Playwright. Parameters & Requests
Agenda
- Fixtures. Again
- Parameters
- Routing
Fixtures. Again
- merging
- options
- scopes
- best practicies
Merge fixtures
// composite.fixture.ts
import { mergeTests } from '@playwright/test';
import { test as dbTest } from 'database-test-utils';
import { test as a11yTest } from 'a11y-test-utils';
export const test = mergeTests(dbTest, a11yTest);
// composite.fixture.ts
import { test as baseTest } from '@playwright/test';
type Context = {
usePassword(length: number): string
}
export const test = baseTest.extend({
usePassword: async ({}, use) => {
await use((length) => {
return randomPassowrd(length)
})
},
});
// test
test('some test', async ({usePassword}) => {
const user = {
email: 'some@example.com',
password: await usePassword(20),
}
})
Options
import { test as base } from '@playwright/test';
export const test = base.extend({
helperFixture: [async ({}, use) => {
// ...
}, { box: true, title: 'Custom title', timeout: 60000 }],
});
import { test as base } from '@playwright/test';
const test = base.extend({
workerFixture: [async ({ browser }) => {
// workerFixture setup...
await use('workerFixture');
// workerFixture teardown...
}, { scope: 'worker' }],
autoWorkerFixture: [async ({ browser }) => {
// autoWorkerFixture setup...
await use('autoWorkerFixture');
// autoWorkerFixture teardown...
}, { scope: 'worker', auto: true }],
testFixture: [async ({ page, workerFixture }) => {
// testFixture setup...
await use('testFixture');
// testFixture teardown...
}, { scope: 'test' }],
autoTestFixture: [async () => {
// autoTestFixture setup...
await use('autoTestFixture');
// autoTestFixture teardown...
}, { scope: 'test', auto: true }],
});
test.beforeAll(async () => { /* ... */ });
test.beforeEach(async ({ page }) => { /* ... */ });
test('first test', async ({ page }) => { /* ... */ });
test('second test', async ({ testFixture }) => { /* ... */ });
test.afterEach(async () => { /* ... */ });
test.afterAll(async () => { /* ... */ });
Fixtures
- + "
auto + worker"- самые первые - + "worker" - до теста
- + "
auto+test"- перед beforeEach, после after Each (default) - -"worker" - создается перед тестом, удаляется в конце (перед auto+worker)
- -"
auto+test" -
-"worker"если нет других тестов - - "auto+worker"
Parameters
const PARAMS = [
{name: 'qwe'},
{name: 'asd'},
]
for (const param of PARAMS){
test(`test case: ${param.name}`, async ({page}) => {
page.goto('/')
})
}
Parameters
import fs from 'node:fs';
import path from 'node:path';
import { test } from '@playwright/test';
import { parse } from 'csv-parse/sync';
const records = parse(fs.readFileSync(path.join(__dirname, 'input.csv')), {
columns: true,
skip_empty_lines: true
});
// "test_case","some_value","some_other_value"
// "value 1","value 11","foobar1"
for (const record of records) {
test(`foo: ${record.test_case}`, async ({ page }) => {
console.log(record.test_case, record.some_value, record.some_other_value);
});
}
Parameters. Best Practicies
- статическая информация (фаил, константа)
- нет внешних зависимостей (.env)
- повторяемый результат
- boundary values & equivalent classes
Routing
- HTTP Requests
- Proxy
- Extra headers
Routing. Extra headers
import { defineConfig } from '@playwright/test';
export default defineConfig({
use: {
baseURL: 'https://api.github.com',
extraHTTPHeaders: {
'Accept': 'application/vnd.github.v3+json',
'Authorization': `token ${process.env.API_TOKEN}`,
},
}
});
Routing. Proxy
import { defineConfig } from '@playwright/test';
export default defineConfig({
use: {
proxy: {
server: 'http://my-proxy:8080',
username: 'user',
password: 'secret'
},
}
});
Routing. HTTP Requests
test.beforeAll(async ({ request }) => {
// Create a new repository
const response = await request.post('/user/repos', {
data: {
name: 'My repo'
}
});
expect(response.ok()).toBeTruthy();
});
test.afterAll(async ({ request }) => {
// Delete the repository
const response = await request.delete(`/repos/${USER}/${REPO}`);
expect(response.ok()).toBeTruthy();
});
Routing. Notes
- Creates a browser (even for requests only)
- Extra headers can be overwritten by parent
- Can manipulate with req/res
Routing. Manupulate
test('context request will share cookie storage with its browser context',
async ({
page,
context,
}) => {
const response = await context.request.fetch(route.request());
await route.fulfill({
response, // prev response
headers: { ...response.headers(), foo: 'bar' },
});
// OR
await page.route('**/v1/**', route => route.fulfill({ path: 'mock_data.json' }));
})
Auth State. Setup
import { test as setup, expect } from '@playwright/test';
const authFile = 'playwright/.auth/user.json';
setup('authenticate', async ({ page }) => {
await page.goto('https://example.com/login');
await page.getByLabel('Username or email address').fill('username');
await page.getByLabel('Password').fill('password');
await page.getByRole('button', { name: 'Sign in' }).click();
await page.waitForURL('https://github.com/');
await expect(page.getByRole('button', { name: 'View profile and more' })).toBeVisible();
// End of authentication steps.
await page.context().storageState({ path: authFile });
});
Auth State. Setup
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
projects: [
// Setup project
{ name: 'setup', testMatch: /.*\.setup\.ts/ },
{
name: 'chromium',
use: {
...devices['Desktop Firefox'],
// Use prepared auth state.
storageState: 'playwright/.auth/user.json',
},
dependencies: ['setup'],
},
],
});
Best practicies
- routes - для браузеров
- auth state - штука ситуативная
References
pw. Parameters & Requests
By vitalic gorodkov
pw. Parameters & Requests
- 193