Code on a computer screen representing automated browser testing
James Ryan 2 min read

Get Started with Playwright for Model Driven Apps

As a Power Platform consultant, knowing how to run automated testing can help you stand out.

What You'll Learn Today

Today we're learning browser automation, not testing. We'll understand:

  • Browser lifecycle management
  • JavaScript execution bridge between Node.js and browser
  • Model Driven App integration basics
  • Why testing frameworks exist

In the next lesson we'll start to learn testing with @playwright/test.

Understanding Playwright's Architecture

Your Computer (Node.js) Browser (Chrome/Edge/Firefox) ┌─────────────────┐ ┌─────────────────┐ │ │ │ │ │ Your Script │ Controls │ Web Page │ │ (TypeScript) ├────────────►│ (MDA) │ │ │ │ │ │ playwright │ │ window.Xrm │ │ - browser │ │ DOM │ │ - page │ │ JavaScript │ │ │ │ │ └─────────────────┘ └─────────────────┘ Node.js Browser

Key Insight: Your test code runs in Node.js, but page.evaluate() lets you run code inside the browser.

The Code

typescript
// day1-concepts.ts import { chromium, Browser, Page, BrowserContext } from 'playwright';  declare global {     interface Window {         Xrm: any;     } }  /**  * Core Playwright Concepts with Model Driven Apps  */  // Your MDA URL - MUST include /main.aspx to load the actual app const MDA_URL = 'https://YOUR-ORG.crm.dynamics.com/main.aspx?appid=YOUR-APP-ID';  async function learnPlaywright() {     let browser: Browser | null = null;     let context: BrowserContext | null = null;     let page: Page | null = null;      try {         // 1. BROWSER LIFECYCLE: Launch → Context → Page         browser = await chromium.launch({             headless: false,             slowMo: 100,         });          context = await browser.newContext({             viewport: { width: 1920, height: 1080 }         });          page = await context.newPage();          // 2. NAVIGATION         await page.goto(D365_URL);         console.log('Navigated to:', page.url());          // 3. WAITING for JavaScript conditions         console.log('Please login manually...');         await page.waitForFunction(             () => typeof window.Xrm !== 'undefined',             { timeout: 120000 }         );         console.log('Login successful - Xrm found');          // 4. EXECUTING JAVASCRIPT - the bridge between Node.js and browser         const pageInfo = await page.evaluate(() => {             return {                 title: document.title,                 url: window.location.href,                 hasXrm: typeof window.Xrm !== 'undefined'             };         });         console.log('Page info:', pageInfo);          // 5. INTERACTING WITH MDA         const userInfo = await page.evaluate(() => {             const xrm = window.Xrm;             const context = xrm.Utility.getGlobalContext();             return {                 userName: context.userSettings.userName,                 orgName: context.organizationSettings.uniqueName,             };         });         console.log('User:', userInfo.userName);         console.log('Org:', userInfo.orgName);          // Navigate using Xrm Web API         console.log('Opening Account form...');         await page.evaluate(() => {             return window.Xrm.Navigation.openForm({                 entityName: "account",                 useQuickCreateForm: false             });         });          // Wait for form to load         await page.waitForFunction(             () => window.Xrm?.Page?.data !== undefined,             { timeout: 10000 }         );          // Check what form we're on         const formInfo = await page.evaluate(() => {             const xrm = window.Xrm;             return {                 entityName: xrm.Page.data.entity.getEntityName(),                 formType: xrm.Page.ui.getFormType(),             };         });         console.log('Opened form:', formInfo);          // 6. SCREENSHOT         await page.screenshot({ path: 'day1-screenshot.png' });         console.log('Screenshot saved');      } finally {         // CLEANUP: Always close resources in reverse order         if (page) await page.close();         if (context) await context.close();         if (browser) await browser.close();     } }  // Run it learnPlaywright()     .then(() => console.log('✅ Complete!'))     .catch(console.error);

Core Concepts Explained

1. Browser Lifecycle

browser → context → page → close()

  • Browser: The actual browser process (Chrome, Firefox, etc.)
  • Context: Isolated browser session (cookies, storage)
  • Page: A tab within the context

2. The page.evaluate() Bridge

javascript
// Your Node.js code const result = await page.evaluate(() => {     // This runs in the browser     return window.someValue; }); This is THE most important concept in Playwright!

3. Waiting Strategies

  • ❌ waitForTimeout() - Brittle, avoid
  • ✅ waitForSelector() - Better, waits for elements
  • ✅ waitForFunction() - Best, waits for any condition

4. Everything is Async

javascript
await page.goto();        // Async await page.click();       // Async await page.evaluate();    // Async Always use await with Playwright methods!

5. Model Driven App Integration

A Model Driven App that is launched in a browser is just website with a window.Xrm JavaScript object. Once typeof window.Xrm !== 'undefined', you can use the Xrm Web API through Playwright.

What We're Missing

  • Assertions: We're just logging, not testing
  • Organization: Everything in one function
  • Reusability: Copy-paste for multiple scenarios
  • Standards: No structure or patterns

Setup & Run

npm init -y npm install playwright typescript ts-node @types/node npx playwright install

Update the D365 URL with your organization: const D365_URL = 'https://YOUR-ORG.crm.dynamics.com/main.aspx?appid=YOUR-APP-ID';

Run the script: npx ts-node day1-concepts.ts

Next Step: Real Testing

The next post will introduce:

  • @playwright/test framework (automated browser management)
  • expect() assertions (real testing)
  • Page Object Model (organization)
  • AAA pattern (structure)
  • Standards compliance (professional approach)

Key Takeaway

A Model Driven App that is launched in a browser is just website. Playwright is just Node.js controlling a browser. Everything else is patterns and frameworks!

You now understand what browser automation is. Next up we'll learn how to test efficiently.

No comments yet