ArchitectureQuick Reference

Quick Reference Card

🚀 Launch Your App (3 Ways)

import { AppProvider, AppType, AppLaunchMode } from '../lib';
 
const provider = new AppProvider(page);
 
await provider.launch({
  app: { id: 'your-app-id-here' },
  type: AppType.Canvas,
  mode: AppLaunchMode.Play,
  baseUrl: 'https://make.powerapps.com',
});

Method 2: By Name

import { PowerAppsPage, AppProvider } from '../lib';
 
const powerApps = new PowerAppsPage(page);
await powerApps.navigateToApps();
 
const provider = new AppProvider(page, powerApps.findApp.bind(powerApps));
 
await provider.launch({
  app: { name: 'My Sales App' },
  type: AppType.Canvas,
  mode: AppLaunchMode.Play,
});

Method 3: Simple String (treated as name)

await provider.launch({
  app: 'My Sales App',
  type: AppType.Canvas,
});

🎮 Interact with Your App

Click a Control

import { CanvasControlType } from '../lib';
 
await provider.click({
  name: 'Submit',
  type: CanvasControlType.Button,
});

Fill a Text Input

await provider.fill(
  {
    name: 'Email',
    type: CanvasControlType.TextInput,
  },
  'user@example.com'
);

Fill Entire Form

await provider.fillForm({
  'First Name': 'John',
  'Last Name': 'Doe',
  Email: 'john.doe@example.com',
  Phone: '555-0123',
});

Get a Control

const submitButton = provider.getControl({
  name: 'Submit',
  type: CanvasControlType.Button,
});
 
await submitButton.click();

✅ Assertions

Assert Control is Visible

await provider.assertVisible({
  name: 'Success Message',
});

Assert Control Text

await provider.assertText(
  {
    name: 'Status Label',
  },
  'Success'
);

📊 Check App State

// Is app ready?
const ready = provider.isReady();
 
// Get current app type
const type = provider.getCurrentAppType(); // AppType.Canvas
 
// Get current app ID
const id = provider.getCurrentAppId();
 
// Get current app URL
const url = provider.getCurrentAppUrl();
 
// Get all launched apps
const apps = provider.getLaunchedApps();
console.log(`Tested ${apps.length} apps`);

🧹 Clean Up

// Close current app
await provider.close();
 
// Reset provider state
provider.reset();
 
// Clear factory cache (between tests)
import { AppLauncherFactory } from '../lib';
AppLauncherFactory.clearCache();

📝 Complete Test Example

import { test, expect } from '@playwright/test';
import { AppProvider, AppType, AppLaunchMode, CanvasControlType } from '../lib';
 
test('Test my Canvas app', async ({ page }) => {
  const provider = new AppProvider(page);
 
  // Launch app
  await provider.launch({
    app: { id: process.env.CANVAS_APP_ID! },
    type: AppType.Canvas,
    mode: AppLaunchMode.Play,
    baseUrl: 'https://make.powerapps.com',
  });
 
  // Interact
  await provider.fillForm({
    Name: 'John Doe',
    Email: 'john@example.com',
  });
 
  await provider.click({
    name: 'Submit',
    type: CanvasControlType.Button,
  });
 
  // Assert
  await provider.assertVisible({ name: 'Success' });
  await provider.assertText({ name: 'Status' }, 'Submitted');
 
  // Clean up
  await provider.close();
});

🎯 App Types

import { AppType } from '../lib';
 
AppType.Canvas; // Canvas Apps
AppType.ModelDriven; // Model Driven Apps
AppType.Portal; // Portal Apps (coming soon)

🔄 Launch Modes

import { AppLaunchMode } from '../lib';
 
AppLaunchMode.Play; // Play mode (runtime)
AppLaunchMode.Edit; // Edit mode (studio)
AppLaunchMode.Preview; // Preview mode

🎨 Canvas Control Types

import { CanvasControlType } from '../lib';
 
CanvasControlType.Button;
CanvasControlType.TextInput;
CanvasControlType.Label;
CanvasControlType.Dropdown;
CanvasControlType.Checkbox;
CanvasControlType.DatePicker;
CanvasControlType.Gallery;
// ... and 20+ more

🗂️ Model Driven Control Types

import { ModelDrivenControlType } from '../lib';
 
ModelDrivenControlType.Button;
ModelDrivenControlType.TextInput;
ModelDrivenControlType.Dropdown;
ModelDrivenControlType.Checkbox;
ModelDrivenControlType.DatePicker;
ModelDrivenControlType.Lookup;
ModelDrivenControlType.NavigationItem;
// ... and more

🔧 Advanced: Factory Pattern

import { AppLauncherFactory, AppType } from '../lib';
 
// Create launcher
const launcher = AppLauncherFactory.createLauncher(page, AppType.Canvas);
 
// Launch by ID
await launcher.launchById('app-id', 'https://make.powerapps.com', AppLaunchMode.Play);
 
// Interact
await launcher.clickControl({ name: 'Submit' });
await launcher.fillControl({ name: 'Email' }, 'test@example.com');
 
// State
const ready = launcher.isAppReady();
const id = launcher.getAppId();
const url = launcher.getAppUrl();
 
// Clean up
await launcher.closeApp();
launcher.reset();

📚 Documentation

DocumentDescription
Getting StartedComplete guide with examples
Architecture OverviewSystem architecture and patterns
Architecture DiagramVisual architecture diagram
API ReferenceComplete API documentation
Example TestsRunnable test examples

🌟 Best Practices

1. Use Environment Variables

const appId = process.env.CANVAS_APP_ID!;
const baseUrl = process.env.BASE_URL || 'https://make.powerapps.com';

2. Launch by ID for Speed

// ✅ Fast
await provider.launch({ app: { id: 'abc-123' }, type: AppType.Canvas });
 
// ❌ Slower (requires navigation and search)
await provider.launch({ app: { name: 'My App' }, type: AppType.Canvas });

3. Proper Setup and Teardown

test.beforeEach(async ({ page }) => {
  provider = new AppProvider(page);
});
 
test.afterEach(async () => {
  await provider.close();
  provider.reset();
});

4. Use test.step() for Organization

await test.step('Launch app', async () => {
  await provider.launch({ ... });
});
 
await test.step('Fill form', async () => {
  await provider.fillForm({ ... });
});
 
await test.step('Verify result', async () => {
  await provider.assertVisible({ ... });
});

❓ Common Scenarios

Test Multiple Apps

// Test Canvas app
await provider.launch({ app: { id: 'canvas-id' }, type: AppType.Canvas });
await provider.click({ name: 'Button1' });
await provider.close();
 
// Test Model Driven app
await provider.launch({ app: { id: 'model-id' }, type: AppType.ModelDriven });
await provider.click({ name: 'Contacts' });
await provider.close();

Error Handling

try {
  await provider.launch({ app: { id: 'bad-id' }, type: AppType.Canvas });
} catch (error) {
  console.error('Launch failed:', error);
}

Custom Timeout

await provider.launch({
  app: { id: 'app-id' },
  type: AppType.Canvas,
  options: {
    timeout: 60000,
    waitForReady: true,
  },
});

🆘 Need Help?


Happy Testing! 🚀