mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
feat(runner): change storage fixture to TestInfo.storage() (#18584)
This commit is contained in:
parent
a9c15a25f8
commit
25dc0bfacb
@ -128,9 +128,3 @@ test('basic test', async ({ request }) => {
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
## property: Fixtures.storage
|
||||
* since: v1.28
|
||||
- type: <[Storage]>
|
||||
|
||||
[Storage] is shared between all tests in the same run.
|
||||
|
@ -2,14 +2,14 @@
|
||||
* since: v1.28
|
||||
* langs: js
|
||||
|
||||
Playwright Test provides a `storage` fixture for passing values between project setup and tests.
|
||||
Playwright Test provides a [`method: TestInfo.storage`] object for passing values between project setup and tests.
|
||||
TODO: examples
|
||||
|
||||
## method: Storage.get
|
||||
## async method: Storage.get
|
||||
* since: v1.28
|
||||
- returns: <[any]>
|
||||
|
||||
Get named item from the store.
|
||||
Get named item from the storage. Returns undefined if there is no value with given name.
|
||||
|
||||
### param: Storage.get.name
|
||||
* since: v1.28
|
||||
@ -17,10 +17,10 @@ Get named item from the store.
|
||||
|
||||
Item name.
|
||||
|
||||
## method: Storage.set
|
||||
## async method: Storage.set
|
||||
* since: v1.28
|
||||
|
||||
Set value to the store.
|
||||
Set value to the storage.
|
||||
|
||||
### param: Storage.set.name
|
||||
* since: v1.28
|
||||
@ -32,5 +32,5 @@ Item name.
|
||||
* since: v1.28
|
||||
- `value` <[any]>
|
||||
|
||||
Item value. The value must be serializable to JSON.
|
||||
Item value. The value must be serializable to JSON. Passing `undefined` deletes the entry with given name.
|
||||
|
||||
|
@ -498,6 +498,12 @@ Output written to `process.stderr` or `console.error` during the test execution.
|
||||
|
||||
Output written to `process.stdout` or `console.log` during the test execution.
|
||||
|
||||
## method: TestInfo.storage
|
||||
* since: v1.28
|
||||
- returns: <[Storage]>
|
||||
|
||||
Returns a [Storage] instance for the currently running project.
|
||||
|
||||
## property: TestInfo.timeout
|
||||
* since: v1.10
|
||||
- type: <[int]>
|
||||
|
@ -24,7 +24,6 @@ import { removeFolders } from 'playwright-core/lib/utils/fileUtils';
|
||||
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;
|
||||
@ -136,35 +135,6 @@ 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 }],
|
||||
@ -216,7 +186,6 @@ export const test = _baseTest.extend<TestFixtures, WorkerFixtures>({
|
||||
baseURL,
|
||||
contextOptions,
|
||||
serviceWorkers,
|
||||
storage,
|
||||
}, use) => {
|
||||
const options: BrowserContextOptions = {};
|
||||
if (acceptDownloads !== undefined)
|
||||
@ -252,7 +221,7 @@ export const test = _baseTest.extend<TestFixtures, WorkerFixtures>({
|
||||
if (storageState !== undefined) {
|
||||
options.storageState = storageState;
|
||||
if (typeof storageState === 'string') {
|
||||
const value = await storage.get(storageState);
|
||||
const value = await test.info().storage().get(storageState);
|
||||
if (value)
|
||||
options.storageState = value as any;
|
||||
}
|
||||
|
@ -121,6 +121,7 @@ export class Loader {
|
||||
config.snapshotDir = path.resolve(configDir, config.snapshotDir);
|
||||
|
||||
this._fullConfig._configDir = configDir;
|
||||
this._fullConfig._storageDir = path.resolve(configDir, '.playwright-storage');
|
||||
this._fullConfig.configFile = this._configFile;
|
||||
this._fullConfig.rootDir = config.testDir || this._configDir;
|
||||
this._fullConfig._globalOutputDir = takeFirst(config.outputDir, throwawayArtifactsPath, baseFullConfig._globalOutputDir);
|
||||
@ -669,6 +670,7 @@ export const baseFullConfig: FullConfigInternal = {
|
||||
_webServers: [],
|
||||
_globalOutputDir: path.resolve(process.cwd()),
|
||||
_configDir: '',
|
||||
_storageDir: '',
|
||||
_maxConcurrentTestGroups: 0,
|
||||
_ignoreSnapshots: false,
|
||||
_workerIsolation: 'isolate-pools',
|
||||
|
@ -16,15 +16,14 @@
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import type { TestError, TestInfo, TestStatus } from '../types/test';
|
||||
import type { FullConfigInternal, FullProjectInternal } from './types';
|
||||
import { monotonicTime } from 'playwright-core/lib/utils';
|
||||
import type { Storage, TestError, TestInfo, TestStatus } from '../types/test';
|
||||
import type { WorkerInitParams } from './ipc';
|
||||
import type { Loader } from './loader';
|
||||
import type { TestCase } from './test';
|
||||
import { TimeoutManager } from './timeoutManager';
|
||||
import type { Annotation, TestStepInternal } from './types';
|
||||
import type { Annotation, FullConfigInternal, FullProjectInternal, TestStepInternal } from './types';
|
||||
import { addSuffixToFilePath, getContainedPath, normalizeAndSaveAttachment, sanitizeForFilePath, serializeError, trimLongString } from './util';
|
||||
import { monotonicTime } from 'playwright-core/lib/utils';
|
||||
|
||||
export class TestInfoImpl implements TestInfo {
|
||||
private _addStepImpl: (data: Omit<TestStepInternal, 'complete'>) => TestStepInternal;
|
||||
@ -62,6 +61,7 @@ export class TestInfoImpl implements TestInfo {
|
||||
readonly snapshotDir: string;
|
||||
errors: TestError[] = [];
|
||||
currentStep: TestStepInternal | undefined;
|
||||
private readonly _storage: JsonStorage;
|
||||
|
||||
get error(): TestError | undefined {
|
||||
return this.errors[0];
|
||||
@ -109,6 +109,7 @@ export class TestInfoImpl implements TestInfo {
|
||||
this.expectedStatus = test.expectedStatus;
|
||||
|
||||
this._timeoutManager = new TimeoutManager(this.project.timeout);
|
||||
this._storage = new JsonStorage(this);
|
||||
|
||||
this.outputDir = (() => {
|
||||
const relativeTestFilePath = path.relative(this.project.testDir, test._requireFile.replace(/\.(spec|test)\.(js|ts|mjs)$/, ''));
|
||||
@ -279,6 +280,41 @@ export class TestInfoImpl implements TestInfo {
|
||||
setTimeout(timeout: number) {
|
||||
this._timeoutManager.setTimeout(timeout);
|
||||
}
|
||||
|
||||
storage() {
|
||||
return this._storage;
|
||||
}
|
||||
}
|
||||
|
||||
class JsonStorage implements Storage {
|
||||
constructor(private _testInfo: TestInfoImpl) {
|
||||
}
|
||||
|
||||
private _toFilePath(name: string) {
|
||||
const fileName = sanitizeForFilePath(trimLongString(name)) + '.json';
|
||||
return path.join(this._testInfo.config._storageDir, this._testInfo.project._id, fileName);
|
||||
}
|
||||
|
||||
async get<T>(name: string) {
|
||||
const file = this._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 = this._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);
|
||||
}
|
||||
}
|
||||
|
||||
class SkipError extends Error {
|
||||
|
@ -45,6 +45,7 @@ export interface TestStepInternal {
|
||||
export interface FullConfigInternal extends FullConfigPublic {
|
||||
_globalOutputDir: string;
|
||||
_configDir: string;
|
||||
_storageDir: string;
|
||||
_maxConcurrentTestGroups: number;
|
||||
_ignoreSnapshots: boolean;
|
||||
_workerIsolation: WorkerIsolation;
|
||||
|
20
packages/playwright-test/types/test.d.ts
vendored
20
packages/playwright-test/types/test.d.ts
vendored
@ -1740,6 +1740,11 @@ export interface TestInfo {
|
||||
*/
|
||||
stdout: Array<string|Buffer>;
|
||||
|
||||
/**
|
||||
* Returns a [Storage] instance for the currently running project.
|
||||
*/
|
||||
storage(): Storage;
|
||||
|
||||
/**
|
||||
* Timeout in milliseconds for the currently running test. Zero means no timeout. Learn more about
|
||||
* [various timeouts](https://playwright.dev/docs/test-timeouts).
|
||||
@ -2693,18 +2698,19 @@ type ConnectOptions = {
|
||||
};
|
||||
|
||||
/**
|
||||
* Playwright Test provides a `storage` fixture for passing values between project setup and tests. TODO: examples
|
||||
* Playwright Test provides a [testInfo.storage()](https://playwright.dev/docs/api/class-testinfo#test-info-storage) object
|
||||
* for passing values between project setup and tests. TODO: examples
|
||||
*/
|
||||
interface Storage {
|
||||
export interface Storage {
|
||||
/**
|
||||
* Get named item from the store.
|
||||
* Get named item from the storage. Returns undefined if there is no value with given name.
|
||||
* @param name Item name.
|
||||
*/
|
||||
get<T>(name: string): Promise<T | undefined>;
|
||||
/**
|
||||
* Set value to the store.
|
||||
* Set value to the storage.
|
||||
* @param name Item name.
|
||||
* @param value Item value. The value must be serializable to JSON.
|
||||
* @param value Item value. The value must be serializable to JSON. Passing `undefined` deletes the entry with given name.
|
||||
*/
|
||||
set<T>(name: string, value: T | undefined): Promise<void>;
|
||||
}
|
||||
@ -3045,10 +3051,6 @@ 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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -23,13 +23,15 @@ test('should provide storage fixture', async ({ runInlineTest }) => {
|
||||
`,
|
||||
'a.test.ts': `
|
||||
const { test } = pwt;
|
||||
test('should store number', async ({ storage }) => {
|
||||
test('should store number', async ({ }) => {
|
||||
const storage = test.info().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 }) => {
|
||||
test('should store object', async ({ }) => {
|
||||
const storage = test.info().storage();
|
||||
expect(storage).toBeTruthy();
|
||||
expect(await storage.get('object')).toBe(undefined);
|
||||
await storage.set('object', { 'a': 2022 })
|
||||
@ -41,7 +43,6 @@ test('should provide storage fixture', async ({ runInlineTest }) => {
|
||||
expect(result.passed).toBe(2);
|
||||
});
|
||||
|
||||
|
||||
test('should share storage state between project setup and tests', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
'playwright.config.js': `
|
||||
@ -56,7 +57,8 @@ test('should share storage state between project setup and tests', async ({ runI
|
||||
`,
|
||||
'storage.setup.ts': `
|
||||
const { test, expect } = pwt;
|
||||
test('should initialize storage', async ({ storage }) => {
|
||||
test('should initialize storage', async ({ }) => {
|
||||
const storage = test.info().storage();
|
||||
expect(await storage.get('number')).toBe(undefined);
|
||||
await storage.set('number', 2022)
|
||||
expect(await storage.get('number')).toBe(2022);
|
||||
@ -68,14 +70,16 @@ test('should share storage state between project setup and tests', async ({ runI
|
||||
`,
|
||||
'a.test.ts': `
|
||||
const { test } = pwt;
|
||||
test('should get data from setup', async ({ storage }) => {
|
||||
test('should get data from setup', async ({ }) => {
|
||||
const storage = test.info().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 }) => {
|
||||
test('should get data from setup', async ({ }) => {
|
||||
const storage = test.info().storage();
|
||||
expect(await storage.get('number')).toBe(2022);
|
||||
expect(await storage.get('object')).toEqual({ 'a': 2022 });
|
||||
});
|
||||
@ -85,6 +89,41 @@ test('should share storage state between project setup and tests', async ({ runI
|
||||
expect(result.passed).toBe(3);
|
||||
});
|
||||
|
||||
test('should persist storage state between project runs', async ({ runInlineTest }) => {
|
||||
const files = {
|
||||
'playwright.config.js': `
|
||||
module.exports = { };
|
||||
`,
|
||||
'a.test.ts': `
|
||||
const { test } = pwt;
|
||||
test('should have no data on first run', async ({ }) => {
|
||||
const storage = test.info().storage();
|
||||
expect(await storage.get('number')).toBe(undefined);
|
||||
await storage.set('number', 2022)
|
||||
expect(await storage.get('object')).toBe(undefined);
|
||||
await storage.set('object', { 'a': 2022 })
|
||||
});
|
||||
`,
|
||||
'b.test.ts': `
|
||||
const { test } = pwt;
|
||||
test('should get data from previous run', async ({ }) => {
|
||||
const storage = test.info().storage();
|
||||
expect(await storage.get('number')).toBe(2022);
|
||||
expect(await storage.get('object')).toEqual({ 'a': 2022 });
|
||||
});
|
||||
`,
|
||||
};
|
||||
{
|
||||
const result = await runInlineTest(files, { grep: 'should have no data on first run' });
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.passed).toBe(1);
|
||||
}
|
||||
{
|
||||
const result = await runInlineTest(files, { grep: 'should get data from previous run' });
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.passed).toBe(1);
|
||||
}
|
||||
});
|
||||
|
||||
test('should isolate storage state between projects', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
@ -104,28 +143,31 @@ test('should isolate storage state between projects', async ({ runInlineTest })
|
||||
`,
|
||||
'storage.setup.ts': `
|
||||
const { test, expect } = pwt;
|
||||
test('should initialize storage', async ({ storage }, testInfo) => {
|
||||
test('should initialize storage', async ({ }) => {
|
||||
const storage = test.info().storage();
|
||||
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);
|
||||
await storage.set('name', 'str-' + test.info().project.name)
|
||||
expect(await storage.get('name')).toBe('str-' + test.info().project.name);
|
||||
});
|
||||
`,
|
||||
'a.test.ts': `
|
||||
const { test } = pwt;
|
||||
test('should get data from setup', async ({ storage }, testInfo) => {
|
||||
test('should get data from setup', async ({ }) => {
|
||||
const storage = test.info().storage();
|
||||
expect(await storage.get('number')).toBe(2022);
|
||||
expect(await storage.get('name')).toBe('str-' + testInfo.project.name);
|
||||
expect(await storage.get('name')).toBe('str-' + test.info().project.name);
|
||||
});
|
||||
`,
|
||||
'b.test.ts': `
|
||||
const { test } = pwt;
|
||||
test('should get data from setup', async ({ storage }, testInfo) => {
|
||||
test('should get data from setup', async ({ }) => {
|
||||
const storage = test.info().storage();
|
||||
expect(await storage.get('number')).toBe(2022);
|
||||
expect(await storage.get('name')).toBe('str-' + testInfo.project.name);
|
||||
expect(await storage.get('name')).toBe('str-' + test.info().project.name);
|
||||
});
|
||||
`,
|
||||
}, { workers: 2 });
|
||||
@ -133,7 +175,6 @@ test('should isolate storage state between projects', async ({ runInlineTest })
|
||||
expect(result.passed).toBe(6);
|
||||
});
|
||||
|
||||
|
||||
test('should load context storageState from storage', async ({ runInlineTest, server }) => {
|
||||
server.setRoute('/setcookie.html', (req, res) => {
|
||||
res.setHeader('Set-Cookie', ['a=v1']);
|
||||
@ -152,7 +193,8 @@ test('should load context storageState from storage', async ({ runInlineTest, se
|
||||
`,
|
||||
'storage.setup.ts': `
|
||||
const { test, expect } = pwt;
|
||||
test('should save storageState', async ({ page, context, storage }, testInfo) => {
|
||||
test('should save storageState', async ({ page, context }) => {
|
||||
const storage = test.info().storage();
|
||||
expect(await storage.get('user')).toBe(undefined);
|
||||
await page.goto('${server.PREFIX}/setcookie.html');
|
||||
const state = await page.context().storageState();
|
||||
@ -164,7 +206,7 @@ test('should load context storageState from storage', async ({ runInlineTest, se
|
||||
test.use({
|
||||
storageState: 'user'
|
||||
})
|
||||
test('should get data from setup', async ({ page }, testInfo) => {
|
||||
test('should get data from setup', async ({ page }) => {
|
||||
await page.goto('${server.EMPTY_PAGE}');
|
||||
const cookies = await page.evaluate(() => document.cookie);
|
||||
expect(cookies).toBe('a=v1');
|
||||
@ -172,7 +214,7 @@ test('should load context storageState from storage', async ({ runInlineTest, se
|
||||
`,
|
||||
'b.test.ts': `
|
||||
const { test } = pwt;
|
||||
test('should not get data from setup if not configured', async ({ page }, testInfo) => {
|
||||
test('should not get data from setup if not configured', async ({ page }) => {
|
||||
await page.goto('${server.EMPTY_PAGE}');
|
||||
const cookies = await page.evaluate(() => document.cookie);
|
||||
expect(cookies).toBe('');
|
||||
@ -207,17 +249,17 @@ test('should load storageState specified in the project config from storage', as
|
||||
test.reset({
|
||||
storageState: 'default'
|
||||
})
|
||||
test('should save storageState', async ({ page, context, storage }, testInfo) => {
|
||||
test('should save storageState', async ({ page, context }) => {
|
||||
const storage = test.info().storage();
|
||||
expect(await storage.get('stateInStorage')).toBe(undefined);
|
||||
await page.goto('${server.PREFIX}/setcookie.html');
|
||||
const state = await page.context().storageState();
|
||||
await storage.set('stateInStorage', state);
|
||||
console.log('project setup state = ' + state);
|
||||
});
|
||||
`,
|
||||
'a.test.ts': `
|
||||
const { test } = pwt;
|
||||
test('should get data from setup', async ({ page, storage }, testInfo) => {
|
||||
test('should get data from setup', async ({ page }) => {
|
||||
await page.goto('${server.EMPTY_PAGE}');
|
||||
const cookies = await page.evaluate(() => document.cookie);
|
||||
expect(cookies).toBe('a=v1');
|
||||
@ -252,17 +294,17 @@ test('should load storageState specified in the global config from storage', asy
|
||||
test.reset({
|
||||
storageState: 'default'
|
||||
})
|
||||
test('should save storageState', async ({ page, context, storage }, testInfo) => {
|
||||
test('should save storageState', async ({ page, context }) => {
|
||||
const storage = test.info().storage();
|
||||
expect(await storage.get('stateInStorage')).toBe(undefined);
|
||||
await page.goto('${server.PREFIX}/setcookie.html');
|
||||
const state = await page.context().storageState();
|
||||
await storage.set('stateInStorage', state);
|
||||
console.log('project setup state = ' + state);
|
||||
});
|
||||
`,
|
||||
'a.test.ts': `
|
||||
const { test } = pwt;
|
||||
test('should get data from setup', async ({ page, storage }, testInfo) => {
|
||||
test('should get data from setup', async ({ page }) => {
|
||||
await page.goto('${server.EMPTY_PAGE}');
|
||||
const cookies = await page.evaluate(() => document.cookie);
|
||||
expect(cookies).toBe('a=v1');
|
||||
|
@ -195,3 +195,18 @@ test('config should allow void/empty options', async ({ runTSC }) => {
|
||||
});
|
||||
expect(result.exitCode).toBe(0);
|
||||
});
|
||||
|
||||
test('should provide storage interface', async ({ runTSC }) => {
|
||||
const result = await runTSC({
|
||||
'a.spec.ts': `
|
||||
const { test } = pwt;
|
||||
test('my test', async () => {
|
||||
await test.info().storage().set('foo', 'bar');
|
||||
const val = await test.info().storage().get('foo');
|
||||
// @ts-expect-error
|
||||
await test.info().storage().unknown();
|
||||
});
|
||||
`
|
||||
});
|
||||
expect(result.exitCode).toBe(0);
|
||||
});
|
||||
|
3
utils/generate_types/overrides-test.d.ts
vendored
3
utils/generate_types/overrides-test.d.ts
vendored
@ -198,7 +198,7 @@ type ConnectOptions = {
|
||||
timeout?: number;
|
||||
};
|
||||
|
||||
interface Storage {
|
||||
export interface Storage {
|
||||
get<T>(name: string): Promise<T | undefined>;
|
||||
set<T>(name: string, value: T | undefined): Promise<void>;
|
||||
}
|
||||
@ -250,7 +250,6 @@ export interface PlaywrightTestOptions {
|
||||
export interface PlaywrightWorkerArgs {
|
||||
playwright: typeof import('playwright-core');
|
||||
browser: Browser;
|
||||
storage: Storage;
|
||||
}
|
||||
|
||||
export interface PlaywrightTestArgs {
|
||||
|
Loading…
x
Reference in New Issue
Block a user