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 portalPowerAutomate- Flow designer environmentPPAC- Power Platform Admin CenterCanvasDesigner- Canvas app designerPowerVA- 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 dayPr- Tests that run during PR validationSmoke- Basic functionality testsSynthetic- Outside-in synthetic monitoringAccessibility- Accessibility compliance testsPerformance- Performance measurement testsDisabled- 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 environmentPreview- Preview environmentTest- Test environmentDev- Development environmentTIP- Test In ProductionCI- Continuous Integration environment
4. Run Geographies (runGeos)#
Determines which geographical regions to test in:
runGeos: [RunGeo.USA, RunGeo.Europe];
Common geographies:
USA- United StatesAsia- Asia PacificEurope- EuropeUK- United KingdomCanada- CanadaJapan- JapanFrance- FranceGermany- GermanyAnd many others as defined in the
RunGeoenum
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#
Group related tests: Use
test.describeto group tests with similar setup needsFilter appropriately: Be specific with
runOnlyInconditions to prevent unnecessary test executionSet team ownership: Always use
setTestFileOwningTeamto clearly indicate responsibilityCreate reusable fixtures: Extract common setup into fixtures for better maintenance
Use steps for clarity: Break tests into steps using
test.stepfor better readabilityClean up after tests: Always clean up resources created during tests in
afterEachhooks
By following these patterns, you’ll create more maintainable, efficient tests that run only when needed and are clearly owned by the appropriate team.