Skip to content

Instantly share code, notes, and snippets.

@graffhyrum
Last active June 4, 2024 23:09
Show Gist options
  • Save graffhyrum/a378ce1bfe5c813d62efae7288e805b6 to your computer and use it in GitHub Desktop.
Save graffhyrum/a378ce1bfe5c813d62efae7288e805b6 to your computer and use it in GitHub Desktop.
Micro Playwright config example
import { test as base } from 'playwright/types/test';
import { loginPage } from './loginPage';
import { getUser, type User } from './user';
import type { TestableEnvironment } from './params';
export type Fixtures = {
thisEnvironment: TestableEnvironment;
loginPage: ReturnType<typeof loginPage>;
};
export type WorkerFixtures = {
user: User;
};
export const test = base.extend<Fixtures, WorkerFixtures>({
thisEnvironment: ['production', { option: true }],
loginPage: async ({ page, thisEnvironment }, use) => {
// The test will now have a POM instance pre-configured to the environment.
await use(loginPage(page, thisEnvironment));
},
user: [
// worker fixtures have the workerInfo object available
async ({}, use, workerInfo) => {
await use(getUser(workerInfo.parallelIndex, 'user', 'password'));
},
{ scope: 'worker' },
],
});
import { devices } from '@playwright/test';
import type {
TestableBrowser,
TestableEnvironment,
TestableResolution,
} from './params';
import {
testableBrowsers,
testableEnvironments,
testableResolutions,
} from './params';
/**
* Define a function to create a matrix of tests
* This function will create a test for each combination of environment, browser, and resolution
* We can also pass in the specific environments, browsers, and resolutions to test
* @param overrides
*/
export function getProjects(overrides?: {
environmentsToTest?: TestableEnvironment[];
browsersToTest?: TestableBrowser[];
resolutionsToTest?: TestableResolution[];
}) {
const { environmentsToTest, browsersToTest, resolutionsToTest } =
overrides || {};
const environmentsInRun = environmentsToTest || testableEnvironments;
const browsersInRun =
browsersToTest || (Object.keys(testableBrowsers) as TestableBrowser[]);
const resolutionsInRun = resolutionsToTest || testableResolutions;
const projects = [];
for (const env of environmentsInRun) {
for (const browser of browsersInRun) {
for (const resolution of resolutionsInRun) {
projects.push({
name: `${env} ${browser} ${resolution}`,
use: {
thisEnvironment: env, // this is the environment fixture
viewport: resolution,
...devices[testableBrowsers[browser]],
},
});
}
}
}
return projects;
}
// We parameterize the POMs to allow for different configurations
import type { Page } from '@playwright/test';
import type { TestableEnvironment } from './params';
export const loginPage = (page: Page, env: TestableEnvironment) => {
// Dynamically set the URL of this page by environment. This could also be done
// at the test level via `baseUrl` in the test options.
const url =
env === 'production' ? 'https://example.com' : `https://${env}.example.com`;
return {
login: async (username: string, password: string) => {
await page.goto(url);
await page.fill('input[name="username"]', username);
await page.fill('input[name="password"]', password);
await page.click('button[type="submit"]');
},
};
};
import type { devices } from '@playwright/test';
import { type PlaywrightTestOptions } from '@playwright/test';
export const testableEnvironments = [
'production',
'staging',
'development',
] as const;
export type TestableEnvironment = (typeof testableEnvironments)[number];
export const testableBrowsers = {
chromium: 'Desktop Chrome',
firefox: 'Desktop Firefox',
webkit: 'Desktop Safari',
} as const satisfies {
[key: string]: keyof typeof devices;
};
export type TestableBrowser = keyof typeof testableBrowsers;
export const testableResolutions = [
{
width: 1920,
height: 1080,
},
{
width: 1280,
height: 720,
},
] as const satisfies Array<PlaywrightTestOptions['viewport']>;
export type TestableResolution = (typeof testableResolutions)[number];
import { defineConfig } from '@playwright/test';
import { devices } from '@playwright/test';
import { getProjects } from './getProjects';
import type { Fixtures, WorkerFixtures } from './fixtures';
// We now define our config to crate a combinatorial matrix of tests
export default defineConfig<Fixtures, WorkerFixtures>({
// other config parameters...
workers: 4, // Four sets of users will be created and assigned to each worker
use: {
...devices['Desktop Chrome'],
},
// This will create a test for each combination of environment, browser, and resolution
// Logic is complex, so we abstracted it to a function
projects: getProjects(),
});

These files are an illustration of the benefits available in playwright test projects. It leverages parallelization and composed fixtures to create a matrix of tests for different combinations of test environments, browsers, and resolutions. The following is a brief description of the files in their execution order:

  1. playwright.config.ts: This is the entry point for the Playwright tests. It defines the configuration for the tests and uses the getProjects function to generate a matrix of tests for different combinations of environments, browsers, and resolutions.

  2. getProjects.ts: This file defines a function getProjects that generates a matrix of tests for different combinations of environments, browsers, and resolutions. The function can be customized with specific environments, browsers, and resolutions to test.

  3. params.ts: This file defines several constants and types related to the testable environments, browsers, and resolutions. It also defines the types TestableEnvironment, TestableBrowser, and TestableResolution.

  4. fixtures.ts: This file defines fixtures for the tests. Fixtures are reusable test components that can be used across multiple tests. In this case, the fixtures include a TestableEnvironment and a loginPage. There's also a worker fixture for a User.

  5. loginPage.ts: This file exports a function that returns an object with a login method. The login method navigates to a URL (based on the environment), fills in the username and password fields, and clicks the submit button.

  6. user.ts: This file defines a User type and a getUser function. The getUser function creates a user with a unique username by appending an index to the provided username.

  7. test.spec.ts: This file contains an example test that uses the loginPage and user fixtures. The test logs in with the user's username and password.

import { test } from './fixtures';
test('example test', async ({
loginPage, // This is automatically configured to the environment
user, // This is automatically set based on which worker runs this test
}) => {
await loginPage.login(user.username, user.password);
});
export type User = {
username: string;
password: string;
};
// We define a factory function to create users
export function getUser(
index: number,
username: string,
password: string,
): User {
// We append the index to the username to ensure uniqueness
const indexedUsername = `${username}_${index}`;
// Implementation details here, retrieve from db, create endpoint, cache, etc.
return {
username: indexedUsername,
password,
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment