Test Fixtures and Test Filtering#

Playwright in the PPUX repository provides powerful mechanisms for controlling test execution and organization. This page explains how to use test fixtures and the runOnlyIn function to filter and organize your tests based on environment, team ownership, and other criteria.

Test Fixtures Overview#

Fixtures in Playwright are reusable objects that are automatically created for each test. They can be used to set up the environment, provide test data, or create page objects that tests can interact with. The PPUX repository extends Playwright’s fixture system with additional capabilities.

Here is a basic example of a test using fixtures:

import { test } from "@playwright/test";

test("basic test", async ({ page }) => {
  await page.goto("https://example.com");
  const title = await page.title();
  expect(title).toBe("Example Domain");
});

In the above example, page is a built-in fixture provided by Playwright that represents a browser page.

Test Filtering with runOnlyIn#

The runOnlyIn function is a custom utility that filters tests based on specified conditions such as environment type, run type, geography, and environment tier. This enables precise control over which tests run in different environments.

Basic Usage#

runOnlyIn(
  {
    envTypes: [EnvironmentType.MakerShell],
    runTypes: [RunType.Synthetic],
    runGeos: [RunGeo.USA],
    runEnvironments: [[RunEnvironment.Prod]],
  },
  () => {
    test("test only in specific conditions", async ({ page }) => {
      // Test code that will only run when all constraints are met
    });
  }
);

Test Constraints Configuration#

The runOnlyIn function uses TestConstraints to determine when to execute tests:

1. Environment Types (envTypes)#

Specifies which application environments the test should run against:

envTypes: [EnvironmentType.MakerShell, EnvironmentType.PowerAutomate];

Common values include:

  • MakerShell - PowerApps maker portal

  • PowerAutomate - Flow designer environment

  • PPAC - Power Platform Admin Center

  • CanvasDesigner - Canvas app designer

  • PowerVA - Power Virtual Agents

2. Run Types (runTypes)#

Determines the test execution context:

runTypes: [RunType.Nightly, RunType.Runner];

Available run types:

  • Runner - Critical tests that run frequently (hourly)

  • Nightly - Tests that run once per day

  • Pr - Tests that run during PR validation

  • Smoke - Basic functionality tests

  • Synthetic - Outside-in synthetic monitoring

  • Accessibility - Accessibility compliance tests

  • Performance - Performance measurement tests

  • Disabled - Tests disabled from execution

3. Run Environments (runEnvironments)#

Specifies which tiers the tests should run in:

runEnvironments: [[RunEnvironment.Prod], [RunEnvironment.Test]];

Available environments:

  • Prod - Production environment

  • Preview - Preview environment

  • Test - Test environment

  • Dev - Development environment

  • TIP - Test In Production

  • CI - Continuous Integration environment

4. Run Geographies (runGeos)#

Determines which geographical regions to test in:

runGeos: [RunGeo.USA, RunGeo.Europe];

Common geographies:

  • USA - United States

  • Asia - Asia Pacific

  • Europe - Europe

  • UK - United Kingdom

  • Canada - Canada

  • Japan - Japan

  • France - France

  • Germany - Germany

  • And many others as defined in the RunGeo enum

Test Ownership with setTestFileOwningTeam#

The PPUX repository tracks test ownership using the setTestFileOwningTeam function. This helps with test maintenance, accountability, and organization:

import { setTestFileOwningTeam } from "../../../utils/baseFixture";
import { Team } from "@paeng/playwright-teams-info";

// Set team ownership for all tests in the file
setTestFileOwningTeam(test, Team.MakerShell);

Teams are defined in the Team enum:

export enum Team {
  Activities = "Activities",
  AdminCenter = "AdminCenter",
  // ... more teams
  MakerShell = "MakerShell",
  // ... more teams
}

Note: If your team isn’t listed, you can add it to teams.ts following the team onboarding process.

Complete Example#

Here’s a complete example showing fixtures, filtering, and team ownership:

/*!
 * Copyright (C) Microsoft Corporation. All rights reserved.
 */

import {
  EnvironmentType,
  RunEnvironment,
  RunGeo,
  RunType,
  TimeOut,
} from "@paeng/playwright-solution";
import { Team } from "@paeng/playwright-teams-info";
import { setTestFileOwningTeam } from "../../../utils/baseFixture";
import { runOnlyIn } from "../../../utils/runtimeUtils";
import { MakerPage } from "../pages/maker.page";

// Set ownership for all tests in this file
setTestFileOwningTeam(test, Team.MakerShell);

test.describe.serial("PowerApps Creation Tests", () => {
  runOnlyIn(
    {
      envTypes: [EnvironmentType.MakerShell],
      runTypes: [RunType.Synthetic, RunType.Nightly],
      runGeos: [RunGeo.USA],
      runEnvironments: [[RunEnvironment.Prod]],
    },
    () => {
      let makerPage: MakerPage;

      // Set up the environment before each test
      test.beforeEach(async ({ page }) => {
        await test.step("Launch maker portal", async () => {
          makerPage = new MakerPage(page);
          await makerPage.navigateToMakerPortal();
        });
      });

      // Clean up after each test
      test.afterEach(async ({ page }, testInfo) => {
        console.log(
          `Finished ${testInfo.title} with status ${testInfo.status}`
        );
        if (testInfo.status !== testInfo.expectedStatus) {
          console.log(`Test failed, URL: ${page.url()}`);
        }

        // Clean up any created resources
        await makerPage.deleteTestResources();
      });

      // The actual test
      test("Create and publish a canvas app", async () => {
        await test.step("Create a new canvas app", async () => {
          await makerPage.createNewApp("Canvas");
        });

        await test.step("Add components to the app", async () => {
          await makerPage.addComponent("Button");
          await makerPage.configureComponent("Button", { text: "Click me" });
        });

        await test.step("Save and publish the app", async () => {
          await makerPage.saveApp();
          await makerPage.publishApp();
        });
      });
    }
  );
});

Creating Custom Fixtures#

The PPUX repository allows you to create custom fixtures to enhance test capabilities:

// Define a custom fixture type
interface CustomFixtures {
  authenticatedPage: Page;
  dataContext: TestDataContext;
}

// Create a test with extended fixtures
const customTest = test.extend<CustomFixtures>({
  authenticatedPage: async ({ page }, use) => {
    // Authenticate the page before use
    await page.goto("/");
    await loginPage.login(username, password);
    await page.waitForLoadState("networkidle");

    // Provide the authenticated page to the test
    await use(page);

    // Optional cleanup after the test completes
    await page.evaluate(() => window.localStorage.clear());
  },

  dataContext: async ({}, use) => {
    // Create test data before the test
    const context = new TestDataContext();
    await context.initialize();

    // Provide the data context to the test
    await use(context);

    // Clean up test data after the test completes
    await context.cleanup();
  },
});

// Use the custom test with its fixtures
customTest(
  "test with custom fixtures",
  async ({ authenticatedPage, dataContext }) => {
    // Test code that uses the authenticated page and data context
  }
);

Best Practices#

  1. Group related tests: Use test.describe to group tests with similar setup needs

  2. Filter appropriately: Be specific with runOnlyIn conditions to prevent unnecessary test execution

  3. Set team ownership: Always use setTestFileOwningTeam to clearly indicate responsibility

  4. Create reusable fixtures: Extract common setup into fixtures for better maintenance

  5. Use steps for clarity: Break tests into steps using test.step for better readability

  6. Clean up after tests: Always clean up resources created during tests in afterEach hooks

By following these patterns, you’ll create more maintainable, efficient tests that run only when needed and are clearly owned by the appropriate team.