feat(runner): storage fixture (#18522)
This commit is contained in:
Родитель
20f2e0049c
Коммит
45aa82242d
|
@ -128,3 +128,9 @@ test('basic test', async ({ request }) => {
|
|||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
## property: Fixtures.storage
|
||||
* since: v1.28
|
||||
- type: <[Storage]>
|
||||
|
||||
[Storage] is shared between all tests in the same run.
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
# class: Storage
|
||||
* since: v1.28
|
||||
* langs: js
|
||||
|
||||
Playwright Test provides a `storage` fixture for passing values between project setup and tests.
|
||||
TODO: examples
|
||||
|
||||
## method: Storage.get
|
||||
* since: v1.28
|
||||
- returns: <[any]>
|
||||
|
||||
Get named item from the store.
|
||||
|
||||
### param: Storage.get.name
|
||||
* since: v1.28
|
||||
- `name` <[string]>
|
||||
|
||||
Item name.
|
||||
|
||||
## method: Storage.set
|
||||
* since: v1.28
|
||||
|
||||
Set value to the store.
|
||||
|
||||
### param: Storage.set.name
|
||||
* since: v1.28
|
||||
- `name` <[string]>
|
||||
|
||||
Item name.
|
||||
|
||||
### param: Storage.set.value
|
||||
* since: v1.28
|
||||
- `value` <[any]>
|
||||
|
||||
Item value. The value must be serializable to JSON.
|
||||
|
|
@ -16,17 +16,18 @@
|
|||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import type { LaunchOptions, BrowserContextOptions, Page, BrowserContext, Video, APIRequestContext, Tracing } from 'playwright-core';
|
||||
import type { TestType, PlaywrightTestArgs, PlaywrightTestOptions, PlaywrightWorkerArgs, PlaywrightWorkerOptions, TestInfo, VideoMode, TraceMode } from '../types/test';
|
||||
import { rootTestType } from './testType';
|
||||
import type { APIRequestContext, BrowserContext, BrowserContextOptions, LaunchOptions, Page, Tracing, Video } from 'playwright-core';
|
||||
import * as playwrightLibrary from 'playwright-core';
|
||||
import * as outOfProcess from 'playwright-core/lib/outofprocess';
|
||||
import { createGuid, debugMode } from 'playwright-core/lib/utils';
|
||||
import { removeFolders } from 'playwright-core/lib/utils/fileUtils';
|
||||
export { expect } from './expect';
|
||||
export const _baseTest: TestType<{}, {}> = rootTestType.test;
|
||||
export { addRunnerPlugin as _addRunnerPlugin } from './plugins';
|
||||
import * as outOfProcess from 'playwright-core/lib/outofprocess';
|
||||
import * as playwrightLibrary from 'playwright-core';
|
||||
import type { PlaywrightTestArgs, PlaywrightTestOptions, PlaywrightWorkerArgs, PlaywrightWorkerOptions, TestInfo, TestType, TraceMode, VideoMode } from '../types/test';
|
||||
import type { TestInfoImpl } from './testInfo';
|
||||
import { rootTestType } from './testType';
|
||||
import { sanitizeForFilePath, trimLongString } from './util';
|
||||
export { expect } from './expect';
|
||||
export { addRunnerPlugin as _addRunnerPlugin } from './plugins';
|
||||
export const _baseTest: TestType<{}, {}> = rootTestType.test;
|
||||
|
||||
if ((process as any)['__pw_initiator__']) {
|
||||
const originalStackTraceLimit = Error.stackTraceLimit;
|
||||
|
@ -127,7 +128,7 @@ export const test = _baseTest.extend<TestFixtures, WorkerFixtures>({
|
|||
}
|
||||
}, { scope: 'worker', auto: true }],
|
||||
|
||||
browser: [async ({ playwright, browserName }, use) => {
|
||||
browser: [async ({ playwright, browserName }, use, testInfo) => {
|
||||
if (!['chromium', 'firefox', 'webkit'].includes(browserName))
|
||||
throw new Error(`Unexpected browserName "${browserName}", must be one of "chromium", "firefox" or "webkit"`);
|
||||
const browser = await playwright[browserName].launch();
|
||||
|
@ -135,6 +136,35 @@ export const test = _baseTest.extend<TestFixtures, WorkerFixtures>({
|
|||
await browser.close();
|
||||
}, { scope: 'worker', timeout: 0 }],
|
||||
|
||||
storage: [async ({ }, use, testInfo) => {
|
||||
const toFilePath = (name: string) => {
|
||||
const fileName = sanitizeForFilePath(trimLongString(name)) + '.json';
|
||||
return path.join(testInfo.project.outputDir, '.playwright-storage', (testInfo as TestInfoImpl).project._id, fileName);
|
||||
};
|
||||
const storage = {
|
||||
async get<T>(name: string) {
|
||||
const file = toFilePath(name);
|
||||
try {
|
||||
const data = (await fs.promises.readFile(file)).toString('utf-8');
|
||||
return JSON.parse(data) as T;
|
||||
} catch (e) {
|
||||
return undefined;
|
||||
}
|
||||
},
|
||||
async set<T>(name: string, value: T | undefined) {
|
||||
const file = toFilePath(name);
|
||||
if (value === undefined) {
|
||||
await fs.promises.rm(file, { force: true });
|
||||
return;
|
||||
}
|
||||
const data = JSON.stringify(value, undefined, 2);
|
||||
await fs.promises.mkdir(path.dirname(file), { recursive: true });
|
||||
await fs.promises.writeFile(file, data);
|
||||
}
|
||||
};
|
||||
await use(storage);
|
||||
}, { scope: 'worker' }],
|
||||
|
||||
acceptDownloads: [({ contextOptions }, use) => use(contextOptions.acceptDownloads ?? true), { option: true }],
|
||||
bypassCSP: [({ contextOptions }, use) => use(contextOptions.bypassCSP), { option: true }],
|
||||
colorScheme: [({ contextOptions }, use) => use(contextOptions.colorScheme), { option: true }],
|
||||
|
|
|
@ -2668,6 +2668,23 @@ type ConnectOptions = {
|
|||
timeout?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Playwright Test provides a `storage` fixture for passing values between project setup and tests. TODO: examples
|
||||
*/
|
||||
interface Storage {
|
||||
/**
|
||||
* Get named item from the store.
|
||||
* @param name Item name.
|
||||
*/
|
||||
get<T>(name: string): Promise<T | undefined>;
|
||||
/**
|
||||
* Set value to the store.
|
||||
* @param name Item name.
|
||||
* @param value Item value. The value must be serializable to JSON.
|
||||
*/
|
||||
set<T>(name: string, value: T | undefined): Promise<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Playwright Test provides many options to configure test environment, [Browser], [BrowserContext] and more.
|
||||
*
|
||||
|
@ -3004,6 +3021,10 @@ export interface PlaywrightWorkerArgs {
|
|||
* Learn how to [configure browser](https://playwright.dev/docs/test-configuration) and see [available options][TestOptions].
|
||||
*/
|
||||
browser: Browser;
|
||||
/**
|
||||
* [Storage] is shared between all tests in the same run.
|
||||
*/
|
||||
storage: Storage;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -600,3 +600,121 @@ test('should pass fixture defaults to tests', async ({ runInlineTest }) => {
|
|||
expect(result.exitCode).toBe(0);
|
||||
expect(result.passed).toBe(1);
|
||||
});
|
||||
|
||||
test('should provide storage fixture', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
'playwright.config.js': `
|
||||
module.exports = {};
|
||||
`,
|
||||
'a.test.ts': `
|
||||
const { test } = pwt;
|
||||
test('should store number', async ({ storage }) => {
|
||||
expect(storage).toBeTruthy();
|
||||
expect(await storage.get('number')).toBe(undefined);
|
||||
await storage.set('number', 2022)
|
||||
expect(await storage.get('number')).toBe(2022);
|
||||
});
|
||||
test('should store object', async ({ storage }) => {
|
||||
expect(storage).toBeTruthy();
|
||||
expect(await storage.get('object')).toBe(undefined);
|
||||
await storage.set('object', { 'a': 2022 })
|
||||
expect(await storage.get('object')).toEqual({ 'a': 2022 });
|
||||
});
|
||||
`,
|
||||
}, { workers: 1 });
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.passed).toBe(2);
|
||||
});
|
||||
|
||||
|
||||
test('should share storage state between project setup and tests', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
'playwright.config.js': `
|
||||
module.exports = {
|
||||
projects: [
|
||||
{
|
||||
name: 'p1',
|
||||
setup: /.*storage.setup.ts/
|
||||
}
|
||||
]
|
||||
};
|
||||
`,
|
||||
'storage.setup.ts': `
|
||||
const { test, expect } = pwt;
|
||||
test('should initialize storage', async ({ storage }) => {
|
||||
expect(await storage.get('number')).toBe(undefined);
|
||||
await storage.set('number', 2022)
|
||||
expect(await storage.get('number')).toBe(2022);
|
||||
|
||||
expect(await storage.get('object')).toBe(undefined);
|
||||
await storage.set('object', { 'a': 2022 })
|
||||
expect(await storage.get('object')).toEqual({ 'a': 2022 });
|
||||
});
|
||||
`,
|
||||
'a.test.ts': `
|
||||
const { test } = pwt;
|
||||
test('should get data from setup', async ({ storage }) => {
|
||||
expect(await storage.get('number')).toBe(2022);
|
||||
expect(await storage.get('object')).toEqual({ 'a': 2022 });
|
||||
});
|
||||
`,
|
||||
'b.test.ts': `
|
||||
const { test } = pwt;
|
||||
test('should get data from setup', async ({ storage }) => {
|
||||
expect(await storage.get('number')).toBe(2022);
|
||||
expect(await storage.get('object')).toEqual({ 'a': 2022 });
|
||||
});
|
||||
`,
|
||||
}, { workers: 1 });
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.passed).toBe(3);
|
||||
});
|
||||
|
||||
|
||||
test('should isolate storage state between projects', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
'playwright.config.js': `
|
||||
module.exports = {
|
||||
projects: [
|
||||
{
|
||||
name: 'p1',
|
||||
setup: /.*storage.setup.ts/
|
||||
},
|
||||
{
|
||||
name: 'p2',
|
||||
setup: /.*storage.setup.ts/
|
||||
}
|
||||
]
|
||||
};
|
||||
`,
|
||||
'storage.setup.ts': `
|
||||
const { test, expect } = pwt;
|
||||
test('should initialize storage', async ({ storage }, testInfo) => {
|
||||
expect(await storage.get('number')).toBe(undefined);
|
||||
await storage.set('number', 2022)
|
||||
expect(await storage.get('number')).toBe(2022);
|
||||
|
||||
expect(await storage.get('name')).toBe(undefined);
|
||||
await storage.set('name', 'str-' + testInfo.project.name)
|
||||
expect(await storage.get('name')).toBe('str-' + testInfo.project.name);
|
||||
});
|
||||
`,
|
||||
'a.test.ts': `
|
||||
const { test } = pwt;
|
||||
test('should get data from setup', async ({ storage }, testInfo) => {
|
||||
expect(await storage.get('number')).toBe(2022);
|
||||
expect(await storage.get('name')).toBe('str-' + testInfo.project.name);
|
||||
});
|
||||
`,
|
||||
'b.test.ts': `
|
||||
const { test } = pwt;
|
||||
test('should get data from setup', async ({ storage }, testInfo) => {
|
||||
expect(await storage.get('number')).toBe(2022);
|
||||
expect(await storage.get('name')).toBe('str-' + testInfo.project.name);
|
||||
});
|
||||
`,
|
||||
}, { workers: 2 });
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.passed).toBe(6);
|
||||
});
|
||||
|
||||
|
|
|
@ -196,6 +196,11 @@ type ConnectOptions = {
|
|||
timeout?: number;
|
||||
};
|
||||
|
||||
interface Storage {
|
||||
get<T>(name: string): Promise<T | undefined>;
|
||||
set<T>(name: string, value: T | undefined): Promise<void>;
|
||||
}
|
||||
|
||||
export interface PlaywrightWorkerOptions {
|
||||
browserName: BrowserName;
|
||||
defaultBrowserType: BrowserName;
|
||||
|
@ -243,6 +248,7 @@ export interface PlaywrightTestOptions {
|
|||
export interface PlaywrightWorkerArgs {
|
||||
playwright: typeof import('playwright-core');
|
||||
browser: Browser;
|
||||
storage: Storage;
|
||||
}
|
||||
|
||||
export interface PlaywrightTestArgs {
|
||||
|
|
Загрузка…
Ссылка в новой задаче