Revert "chore: hide setup, store, TestProject.setupMatch, storageStat… (#19756)
…eName (#19442)"
This reverts commit 92dd734e04
.
This commit is contained in:
Родитель
d912cbf115
Коммит
137070d889
|
@ -205,6 +205,14 @@ Learn more about [automatic screenshots](../test-configuration.md#automatic-scre
|
|||
## property: TestOptions.storageState = %%-js-python-context-option-storage-state-%%
|
||||
* since: v1.10
|
||||
|
||||
## property: TestOptions.storageStateName
|
||||
* since: v1.29
|
||||
- type: <[string]>
|
||||
|
||||
Name of the [TestStore] entry that should be used to initialize [`property: TestOptions.storageState`]. The value must be
|
||||
written to the test storage before creation of a browser context that uses it (usually in [`property: TestProject.setupMatch`]). If both
|
||||
this property and [`property: TestOptions.storageState`] are specified, this property will always take precedence.
|
||||
|
||||
## property: TestOptions.testIdAttribute
|
||||
* since: v1.27
|
||||
|
||||
|
|
|
@ -146,7 +146,7 @@ Filter to only run tests with a title matching one of the patterns. For example,
|
|||
* since: v1.10
|
||||
- type: ?<[RegExp]|[Array]<[RegExp]>>
|
||||
|
||||
Filter to only run tests with a title **not** matching one of the patterns. This is the opposite of [`property: TestProject.grep`]. Also available globally and in the [command line](../test-cli.md) with the `--grep-invert` option.
|
||||
Filter to only run tests with a title **not** matching one of the patterns. This is the opposite of [`property: TestProject.grep`]. Also available globally and in the [command line](../test-cli.md) with the `--grep-invert` option. This filter and its command line counterpart also applies to the setup files. If all [`property: TestProject.setupMatch`] tests match the filter Playwright **will** run all setup files before running the matching tests.
|
||||
|
||||
`grepInvert` option is also useful for [tagging tests](../test-annotations.md#tag-tests).
|
||||
|
||||
|
@ -162,6 +162,18 @@ Metadata that will be put directly to the test report serialized as JSON.
|
|||
|
||||
Project name is visible in the report and during test execution.
|
||||
|
||||
## property: TestProject.setupMatch
|
||||
* since: v1.29
|
||||
- type: ?<[string]|[RegExp]|[Array]<[string]|[RegExp]>>
|
||||
|
||||
Project setup files that will be executed before all tests in the project.
|
||||
|
||||
**Details**
|
||||
|
||||
If project setup fails the tests in this project will be skipped. All project setup files will run in every shard if the project is sharded. [`property: TestProject.grep`] and [`property: TestProject.grepInvert`] and their command line counterparts also apply to the setup files. If such filters match only tests in the project, Playwright will run **all** setup files before running the matching tests.
|
||||
|
||||
If there is a file that matches both [`property: TestProject.setupMatch`] and [`property: TestProject.testMatch`] filters an error will be thrown.
|
||||
|
||||
## property: TestProject.snapshotDir
|
||||
* since: v1.10
|
||||
- type: ?<[string]>
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
# class: TestStore
|
||||
* since: v1.29
|
||||
* langs: js
|
||||
|
||||
Playwright Test provides a global `store` object for passing values between project setup and tests. It is
|
||||
an error to call store methods outside of setup and tests.
|
||||
|
||||
```js tab=js-js
|
||||
const { setup, store } = require('@playwright/test');
|
||||
|
||||
setup('sign in', async ({ page, context }) => {
|
||||
// Save signed-in state to an entry named 'github-test-user'.
|
||||
const contextState = await context.storageState();
|
||||
await store.set('test-user', contextState)
|
||||
});
|
||||
```
|
||||
|
||||
```js tab=js-ts
|
||||
import { setup, store } from '@playwright/test';
|
||||
|
||||
setup('sign in', async ({ page, context }) => {
|
||||
// Save signed-in state to an entry named 'github-test-user'.
|
||||
const contextState = await context.storageState();
|
||||
await store.set('test-user', contextState)
|
||||
});
|
||||
```
|
||||
|
||||
## async method: TestStore.get
|
||||
* since: v1.29
|
||||
- returns: <[any]>
|
||||
|
||||
Get named item from the store. Returns undefined if there is no value with given name.
|
||||
|
||||
### param: TestStore.get.name
|
||||
* since: v1.29
|
||||
- `name` <[string]>
|
||||
|
||||
Item name.
|
||||
|
||||
## async method: TestStore.set
|
||||
* since: v1.29
|
||||
|
||||
Set value to the store.
|
||||
|
||||
### param: TestStore.set.name
|
||||
* since: v1.29
|
||||
- `name` <[string]>
|
||||
|
||||
Item name.
|
||||
|
||||
### param: TestStore.set.value
|
||||
* since: v1.29
|
||||
- `value` <[any]>
|
||||
|
||||
Item value. The value must be serializable to JSON. Passing `undefined` deletes the entry with given name.
|
||||
|
|
@ -27,7 +27,7 @@ import { rootTestType, _setProjectSetup } from './testType';
|
|||
export { expect } from './expect';
|
||||
export { addRunnerPlugin as _addRunnerPlugin } from './plugins';
|
||||
export const _baseTest: TestType<{}, {}> = rootTestType.test;
|
||||
export const _store = _baseStore;
|
||||
export const store = _baseStore;
|
||||
|
||||
if ((process as any)['__pw_initiator__']) {
|
||||
const originalStackTraceLimit = Error.stackTraceLimit;
|
||||
|
@ -47,7 +47,6 @@ type TestFixtures = PlaywrightTestArgs & PlaywrightTestOptions & {
|
|||
_reuseContext: boolean,
|
||||
_setupContextOptionsAndArtifacts: void;
|
||||
_contextFactory: (options?: BrowserContextOptions) => Promise<BrowserContext>;
|
||||
_storageStateName: string | undefined;
|
||||
};
|
||||
type WorkerFixtures = PlaywrightWorkerArgs & PlaywrightWorkerOptions & {
|
||||
_browserOptions: LaunchOptions;
|
||||
|
@ -144,7 +143,7 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({
|
|||
permissions: [({ contextOptions }, use) => use(contextOptions.permissions), { option: true }],
|
||||
proxy: [({ contextOptions }, use) => use(contextOptions.proxy), { option: true }],
|
||||
storageState: [({ contextOptions }, use) => use(contextOptions.storageState), { option: true }],
|
||||
_storageStateName: [undefined, { option: true }],
|
||||
storageStateName: [undefined, { option: true }],
|
||||
timezoneId: [({ contextOptions }, use) => use(contextOptions.timezoneId), { option: true }],
|
||||
userAgent: [({ contextOptions }, use) => use(contextOptions.userAgent), { option: true }],
|
||||
viewport: [({ contextOptions }, use) => use(contextOptions.viewport === undefined ? { width: 1280, height: 720 } : contextOptions.viewport), { option: true }],
|
||||
|
@ -174,7 +173,7 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({
|
|||
permissions,
|
||||
proxy,
|
||||
storageState,
|
||||
_storageStateName,
|
||||
storageStateName,
|
||||
viewport,
|
||||
timezoneId,
|
||||
userAgent,
|
||||
|
@ -213,10 +212,10 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({
|
|||
options.permissions = permissions;
|
||||
if (proxy !== undefined)
|
||||
options.proxy = proxy;
|
||||
if (_storageStateName !== undefined) {
|
||||
const value = await _store.get(_storageStateName);
|
||||
if (storageStateName !== undefined) {
|
||||
const value = await store.get(storageStateName);
|
||||
if (!value)
|
||||
throw new Error(`Cannot find value in the _store for _storageStateName: "${_storageStateName}"`);
|
||||
throw new Error(`Cannot find value in the store for storageStateName: "${storageStateName}"`);
|
||||
options.storageState = value as any;
|
||||
} else if (storageState !== undefined) {
|
||||
options.storageState = storageState;
|
||||
|
@ -629,7 +628,7 @@ function normalizeScreenshotMode(screenshot: PlaywrightWorkerOptions['screenshot
|
|||
const kTracingStarted = Symbol('kTracingStarted');
|
||||
|
||||
export const test = _baseTest.extend<TestFixtures, WorkerFixtures>(playwrightFixtures);
|
||||
export const _setup = _baseTest.extend<TestFixtures, WorkerFixtures>(playwrightFixtures);
|
||||
_setProjectSetup(_setup, true);
|
||||
export const setup = _baseTest.extend<TestFixtures, WorkerFixtures>(playwrightFixtures);
|
||||
_setProjectSetup(setup, true);
|
||||
|
||||
export default test;
|
||||
|
|
|
@ -275,7 +275,7 @@ export class Loader {
|
|||
const outputDir = takeFirst(projectConfig.outputDir, config.outputDir, path.join(throwawayArtifactsPath, 'test-results'));
|
||||
const snapshotDir = takeFirst(projectConfig.snapshotDir, config.snapshotDir, testDir);
|
||||
const name = takeFirst(projectConfig.name, config.name, '');
|
||||
const _setupMatch = takeFirst((projectConfig as any)._setupMatch, []);
|
||||
const _setupMatch = takeFirst(projectConfig.setupMatch, []);
|
||||
|
||||
const defaultSnapshotPathTemplate = '{snapshotDir}/{testFileDir}/{testFileName}-snapshots/{arg}{-projectName}{-snapshotSuffix}{ext}';
|
||||
const snapshotPathTemplate = takeFirst(projectConfig.snapshotPathTemplate, config.snapshotPathTemplate, defaultSnapshotPathTemplate);
|
||||
|
@ -614,7 +614,7 @@ function validateProject(file: string, project: Project, title: string) {
|
|||
throw errorWithFile(file, `${title}.testDir must be a string`);
|
||||
}
|
||||
|
||||
for (const prop of ['testIgnore', 'testMatch'] as const) {
|
||||
for (const prop of ['testIgnore', 'testMatch', 'setupMatch'] as const) {
|
||||
if (prop in project && project[prop] !== undefined) {
|
||||
const value = project[prop];
|
||||
if (Array.isArray(value)) {
|
||||
|
|
|
@ -258,13 +258,13 @@ export class Runner {
|
|||
if (!isTest && !isSetup)
|
||||
return false;
|
||||
if (isSetup && isTest)
|
||||
throw new Error(`File "${file}" matches both '_setup' and 'testMatch' filters in project "${project.name}"`);
|
||||
throw new Error(`File "${file}" matches both 'setup' and 'testMatch' filters in project "${project.name}"`);
|
||||
if (fileToProjectName.has(file)) {
|
||||
if (isSetup) {
|
||||
if (!setupFiles.has(file))
|
||||
throw new Error(`File "${file}" matches '_setup' filter in project "${project.name}" and 'testMatch' filter in project "${fileToProjectName.get(file)}"`);
|
||||
throw new Error(`File "${file}" matches 'setup' filter in project "${project.name}" and 'testMatch' filter in project "${fileToProjectName.get(file)}"`);
|
||||
} else if (setupFiles.has(file)) {
|
||||
throw new Error(`File "${file}" matches '_setup' filter in project "${fileToProjectName.get(file)}" and 'testMatch' filter in project "${project.name}"`);
|
||||
throw new Error(`File "${file}" matches 'setup' filter in project "${fileToProjectName.get(file)}" and 'testMatch' filter in project "${project.name}"`);
|
||||
}
|
||||
}
|
||||
fileToProjectName.set(file, project.name);
|
||||
|
|
|
@ -16,10 +16,11 @@
|
|||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import type { TestStore } from '../types/test';
|
||||
import { currentTestInfo } from './globals';
|
||||
import { sanitizeForFilePath, trimLongString } from './util';
|
||||
|
||||
class JsonStore {
|
||||
class JsonStore implements TestStore {
|
||||
private _toFilePath(name: string) {
|
||||
const testInfo = currentTestInfo();
|
||||
if (!testInfo)
|
||||
|
|
|
@ -84,14 +84,14 @@ export class TestTypeImpl {
|
|||
if (this._projectSetup)
|
||||
addFatalError(`${title} is called in a file which is not a part of project setup.`, location);
|
||||
else
|
||||
addFatalError(`${title} is called in a project setup file (use '_setup' instead of 'test').`, location);
|
||||
addFatalError(`${title} is called in a project setup file (use 'setup' instead of 'test').`, location);
|
||||
}
|
||||
return suite;
|
||||
}
|
||||
|
||||
private _createTest(type: 'default' | 'only' | 'skip' | 'fixme', location: Location, title: string, fn: Function) {
|
||||
throwIfRunningInsideJest();
|
||||
const suite = this._currentSuite(location, this._projectSetup ? '_setup()' : 'test()');
|
||||
const suite = this._currentSuite(location, this._projectSetup ? 'setup()' : 'test()');
|
||||
if (!suite)
|
||||
return;
|
||||
const test = new TestCase(title, fn, this, location);
|
||||
|
@ -150,7 +150,7 @@ export class TestTypeImpl {
|
|||
}
|
||||
|
||||
private _hook(name: 'beforeEach' | 'afterEach' | 'beforeAll' | 'afterAll', location: Location, fn: Function) {
|
||||
const suite = this._currentSuite(location, `${this._projectSetup ? '_setup' : 'test'}.${name}()`);
|
||||
const suite = this._currentSuite(location, `${this._projectSetup ? 'setup' : 'test'}.${name}()`);
|
||||
if (!suite)
|
||||
return;
|
||||
suite._hooks.push({ type: name, fn, location });
|
||||
|
@ -158,7 +158,7 @@ export class TestTypeImpl {
|
|||
|
||||
private _configure(location: Location, options: { mode?: 'parallel' | 'serial', retries?: number, timeout?: number }) {
|
||||
throwIfRunningInsideJest();
|
||||
const suite = this._currentSuite(location, `${this._projectSetup ? '_setup' : 'test'}.describe.configure()`);
|
||||
const suite = this._currentSuite(location, `${this._projectSetup ? 'setup' : 'test'}.describe.configure()`);
|
||||
if (!suite)
|
||||
return;
|
||||
|
||||
|
@ -225,7 +225,7 @@ export class TestTypeImpl {
|
|||
}
|
||||
|
||||
private _use(location: Location, fixtures: Fixtures) {
|
||||
const suite = this._currentSuite(location, `${this._projectSetup ? '_setup' : 'test'}.use()`);
|
||||
const suite = this._currentSuite(location, `${this._projectSetup ? 'setup' : 'test'}.use()`);
|
||||
if (!suite)
|
||||
return;
|
||||
suite._use.push({ fixtures, location });
|
||||
|
@ -234,7 +234,7 @@ export class TestTypeImpl {
|
|||
private async _step<T>(location: Location, title: string, body: () => Promise<T>): Promise<T> {
|
||||
const testInfo = currentTestInfo();
|
||||
if (!testInfo) {
|
||||
addFatalError(`${this._projectSetup ? '_setup' : 'test'}.step() can only be called from a test`, location);
|
||||
addFatalError(`${this._projectSetup ? 'setup' : 'test'}.step() can only be called from a test`, location);
|
||||
return undefined as any;
|
||||
}
|
||||
const step = testInfo._addStep({
|
||||
|
|
|
@ -193,7 +193,10 @@ export interface FullProject<TestArgs = {}, WorkerArgs = {}> {
|
|||
/**
|
||||
* Filter to only run tests with a title **not** matching one of the patterns. This is the opposite of
|
||||
* [testProject.grep](https://playwright.dev/docs/api/class-testproject#test-project-grep). Also available globally
|
||||
* and in the [command line](https://playwright.dev/docs/test-cli) with the `--grep-invert` option.
|
||||
* and in the [command line](https://playwright.dev/docs/test-cli) with the `--grep-invert` option. This filter and its command line
|
||||
* counterpart also applies to the setup files. If all
|
||||
* [testProject.setupMatch](https://playwright.dev/docs/api/class-testproject#test-project-setup-match) tests match
|
||||
* the filter Playwright **will** run all setup files before running the matching tests.
|
||||
*
|
||||
* `grepInvert` option is also useful for [tagging tests](https://playwright.dev/docs/test-annotations#tag-tests).
|
||||
*/
|
||||
|
@ -2826,6 +2829,35 @@ type ConnectOptions = {
|
|||
timeout?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Playwright Test provides a global `store` object for passing values between project setup and tests. It is an error
|
||||
* to call store methods outside of setup and tests.
|
||||
*
|
||||
* ```js
|
||||
* import { setup, store } from '@playwright/test';
|
||||
*
|
||||
* setup('sign in', async ({ page, context }) => {
|
||||
* // Save signed-in state to an entry named 'github-test-user'.
|
||||
* const contextState = await context.storageState();
|
||||
* await store.set('test-user', contextState)
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
export interface TestStore {
|
||||
/**
|
||||
* Get named item from the store. 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.
|
||||
* @param name Item name.
|
||||
* @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>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Playwright Test provides many options to configure test environment, [Browser], [BrowserContext] and more.
|
||||
*
|
||||
|
@ -3062,6 +3094,16 @@ export interface PlaywrightTestOptions {
|
|||
* Either a path to the file with saved storage, or an object with the following fields:
|
||||
*/
|
||||
storageState: StorageState | undefined;
|
||||
/**
|
||||
* Name of the [TestStore] entry that should be used to initialize
|
||||
* [testOptions.storageState](https://playwright.dev/docs/api/class-testoptions#test-options-storage-state). The value
|
||||
* must be written to the test storage before creation of a browser context that uses it (usually in
|
||||
* [testProject.setupMatch](https://playwright.dev/docs/api/class-testproject#test-project-setup-match)). If both this
|
||||
* property and
|
||||
* [testOptions.storageState](https://playwright.dev/docs/api/class-testoptions#test-options-storage-state) are
|
||||
* specified, this property will always take precedence.
|
||||
*/
|
||||
storageStateName: string | undefined;
|
||||
/**
|
||||
* Changes the timezone of the context. See
|
||||
* [ICU's metaZones.txt](https://cs.chromium.org/chromium/src/third_party/icu/source/data/misc/metaZones.txt?rcl=faee8bc70570192d82d2978a71e2a615788597d1)
|
||||
|
@ -3345,6 +3387,7 @@ export default test;
|
|||
|
||||
export const _baseTest: TestType<{}, {}>;
|
||||
export const expect: Expect;
|
||||
export const store: TestStore;
|
||||
|
||||
// This is required to not export everything by default. See https://github.com/Microsoft/TypeScript/issues/19545#issuecomment-340490459
|
||||
export {};
|
||||
|
@ -4639,7 +4682,10 @@ interface TestProject {
|
|||
/**
|
||||
* Filter to only run tests with a title **not** matching one of the patterns. This is the opposite of
|
||||
* [testProject.grep](https://playwright.dev/docs/api/class-testproject#test-project-grep). Also available globally
|
||||
* and in the [command line](https://playwright.dev/docs/test-cli) with the `--grep-invert` option.
|
||||
* and in the [command line](https://playwright.dev/docs/test-cli) with the `--grep-invert` option. This filter and its command line
|
||||
* counterpart also applies to the setup files. If all
|
||||
* [testProject.setupMatch](https://playwright.dev/docs/api/class-testproject#test-project-setup-match) tests match
|
||||
* the filter Playwright **will** run all setup files before running the matching tests.
|
||||
*
|
||||
* `grepInvert` option is also useful for [tagging tests](https://playwright.dev/docs/test-annotations#tag-tests).
|
||||
*/
|
||||
|
@ -4655,6 +4701,24 @@ interface TestProject {
|
|||
*/
|
||||
name?: string;
|
||||
|
||||
/**
|
||||
* Project setup files that will be executed before all tests in the project.
|
||||
*
|
||||
* **Details**
|
||||
*
|
||||
* If project setup fails the tests in this project will be skipped. All project setup files will run in every shard
|
||||
* if the project is sharded. [testProject.grep](https://playwright.dev/docs/api/class-testproject#test-project-grep)
|
||||
* and [testProject.grepInvert](https://playwright.dev/docs/api/class-testproject#test-project-grep-invert) and their
|
||||
* command line counterparts also apply to the setup files. If such filters match only tests in the project,
|
||||
* Playwright will run **all** setup files before running the matching tests.
|
||||
*
|
||||
* If there is a file that matches both
|
||||
* [testProject.setupMatch](https://playwright.dev/docs/api/class-testproject#test-project-setup-match) and
|
||||
* [testProject.testMatch](https://playwright.dev/docs/api/class-testproject#test-project-test-match) filters an error
|
||||
* will be thrown.
|
||||
*/
|
||||
setupMatch?: string|RegExp|Array<string|RegExp>;
|
||||
|
||||
/**
|
||||
* The base directory, relative to the config file, for snapshot files created with `toMatchSnapshot`. Defaults to
|
||||
* [testProject.testDir](https://playwright.dev/docs/api/class-testproject#test-project-test-dir).
|
||||
|
|
|
@ -480,3 +480,41 @@ test('should have correct types for the config', async ({ runTSC }) => {
|
|||
});
|
||||
expect(result.exitCode).toBe(0);
|
||||
});
|
||||
|
||||
test('should throw when project.setupMatch has wrong type', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
'playwright.config.ts': `
|
||||
module.exports = {
|
||||
projects: [
|
||||
{ name: 'a', setupMatch: 100 },
|
||||
],
|
||||
};
|
||||
`,
|
||||
'a.test.ts': `
|
||||
const { test } = pwt;
|
||||
test('pass', async () => {});
|
||||
`
|
||||
});
|
||||
|
||||
expect(result.exitCode).toBe(1);
|
||||
expect(result.output).toContain(`Error: playwright.config.ts: config.projects[0].setupMatch must be a string or a RegExp`);
|
||||
});
|
||||
|
||||
test('should throw when project.setupMatch has wrong array type', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
'playwright.config.ts': `
|
||||
module.exports = {
|
||||
projects: [
|
||||
{ name: 'a', setupMatch: [/100/, 100] },
|
||||
],
|
||||
};
|
||||
`,
|
||||
'a.test.ts': `
|
||||
const { test } = pwt;
|
||||
test('pass', async () => {});
|
||||
`
|
||||
});
|
||||
|
||||
expect(result.exitCode).toBe(1);
|
||||
expect(result.output).toContain(`Error: playwright.config.ts: config.projects[0].setupMatch[1] must be a string or a RegExp`);
|
||||
});
|
||||
|
|
|
@ -17,7 +17,7 @@ import type { PlaywrightTestConfig, TestInfo, PlaywrightTestProject } from '@pla
|
|||
import path from 'path';
|
||||
import { test, expect } from './playwright-test-fixtures';
|
||||
|
||||
function createConfigWithProjects(names: string[], testInfo: TestInfo, projectTemplates?: { [name: string]: PlaywrightTestProject & any }): Record<string, string> {
|
||||
function createConfigWithProjects(names: string[], testInfo: TestInfo, projectTemplates?: { [name: string]: PlaywrightTestProject }): Record<string, string> {
|
||||
const config: PlaywrightTestConfig = {
|
||||
projects: names.map(name => ({ ...projectTemplates?.[name], name, testDir: testInfo.outputPath(name) })),
|
||||
};
|
||||
|
@ -28,9 +28,9 @@ function createConfigWithProjects(names: string[], testInfo: TestInfo, projectTe
|
|||
test('${name} test', async () => {
|
||||
await new Promise(f => setTimeout(f, 100));
|
||||
});`;
|
||||
files[`${name}/${name}._setup.ts`] = `
|
||||
const { _setup } = pwt;
|
||||
_setup('${name} _setup', async () => {
|
||||
files[`${name}/${name}.setup.ts`] = `
|
||||
const { setup } = pwt;
|
||||
setup('${name} setup', async () => {
|
||||
await new Promise(f => setTimeout(f, 100));
|
||||
});`;
|
||||
}
|
||||
|
@ -98,15 +98,15 @@ function expectFilesRunBefore(timeline: Timeline, before: string[], after: strin
|
|||
test('should work for one project', async ({ runGroups }, testInfo) => {
|
||||
const projectTemplates = {
|
||||
'a': {
|
||||
_setupMatch: ['**/*._setup.ts']
|
||||
setupMatch: ['**/*.setup.ts']
|
||||
},
|
||||
};
|
||||
const configWithFiles = createConfigWithProjects(['a'], testInfo, projectTemplates);
|
||||
const { exitCode, passed, timeline } = await runGroups(configWithFiles);
|
||||
expect(exitCode).toBe(0);
|
||||
expect(passed).toBe(2);
|
||||
expect(formatTimeline(timeline)).toEqual(`a > a${path.sep}a._setup.ts > a _setup [begin]
|
||||
a > a${path.sep}a._setup.ts > a _setup [end]
|
||||
expect(formatTimeline(timeline)).toEqual(`a > a${path.sep}a.setup.ts > a setup [begin]
|
||||
a > a${path.sep}a.setup.ts > a setup [end]
|
||||
a > a${path.sep}a.spec.ts > a test [begin]
|
||||
a > a${path.sep}a.spec.ts > a test [end]`);
|
||||
});
|
||||
|
@ -114,13 +114,13 @@ a > a${path.sep}a.spec.ts > a test [end]`);
|
|||
test('should work for several projects', async ({ runGroups }, testInfo) => {
|
||||
const projectTemplates = {
|
||||
'a': {
|
||||
_setupMatch: ['**/*._setup.ts']
|
||||
setupMatch: ['**/*.setup.ts']
|
||||
},
|
||||
'b': {
|
||||
_setupMatch: /.*b._setup.ts/
|
||||
setupMatch: /.*b.setup.ts/
|
||||
},
|
||||
'c': {
|
||||
_setupMatch: '**/c._setup.ts'
|
||||
setupMatch: '**/c.setup.ts'
|
||||
},
|
||||
};
|
||||
const configWithFiles = createConfigWithProjects(['a', 'b', 'c'], testInfo, projectTemplates);
|
||||
|
@ -128,22 +128,22 @@ test('should work for several projects', async ({ runGroups }, testInfo) => {
|
|||
expect(exitCode).toBe(0);
|
||||
expect(passed).toBe(6);
|
||||
for (const name of ['a', 'b', 'c'])
|
||||
expectFilesRunBefore(timeline, [`${name}${path.sep}${name}._setup.ts`], [`${name}${path.sep}${name}.spec.ts`]);
|
||||
expectFilesRunBefore(timeline, [`${name}${path.sep}${name}.setup.ts`], [`${name}${path.sep}${name}.spec.ts`]);
|
||||
});
|
||||
|
||||
test('should stop project if _setup fails', async ({ runGroups }, testInfo) => {
|
||||
test('should stop project if setup fails', async ({ runGroups }, testInfo) => {
|
||||
const projectTemplates = {
|
||||
'a': {
|
||||
_setupMatch: ['**/*._setup.ts']
|
||||
setupMatch: ['**/*.setup.ts']
|
||||
},
|
||||
'b': {
|
||||
_setupMatch: /.*b._setup.ts/
|
||||
setupMatch: /.*b.setup.ts/
|
||||
},
|
||||
};
|
||||
const configWithFiles = createConfigWithProjects(['a', 'b', 'c'], testInfo, projectTemplates);
|
||||
configWithFiles[`a/a._setup.ts`] = `
|
||||
const { _setup, expect } = pwt;
|
||||
_setup('a _setup', async () => {
|
||||
configWithFiles[`a/a.setup.ts`] = `
|
||||
const { setup, expect } = pwt;
|
||||
setup('a setup', async () => {
|
||||
expect(1).toBe(2);
|
||||
});`;
|
||||
|
||||
|
@ -152,17 +152,17 @@ test('should stop project if _setup fails', async ({ runGroups }, testInfo) => {
|
|||
expect(passed).toBe(3);
|
||||
expect(skipped).toBe(1); // 1 test from project 'a'
|
||||
for (const name of ['a', 'b'])
|
||||
expectFilesRunBefore(timeline, [`${name}${path.sep}${name}._setup.ts`], [`${name}${path.sep}${name}.spec.ts`]);
|
||||
expectFilesRunBefore(timeline, [`${name}${path.sep}${name}.setup.ts`], [`${name}${path.sep}${name}.spec.ts`]);
|
||||
});
|
||||
|
||||
test('should run _setup in each project shard', async ({ runGroups }, testInfo) => {
|
||||
test('should run setup in each project shard', async ({ runGroups }, testInfo) => {
|
||||
const files = {
|
||||
'playwright.config.ts': `
|
||||
module.exports = {
|
||||
projects: [
|
||||
{
|
||||
name: 'p1',
|
||||
_setupMatch: /.*._setup.ts/,
|
||||
setupMatch: /.*.setup.ts/,
|
||||
},
|
||||
]
|
||||
};`,
|
||||
|
@ -178,44 +178,44 @@ test('should run _setup in each project shard', async ({ runGroups }, testInfo)
|
|||
test('test1', async () => { });
|
||||
test('test2', async () => { });
|
||||
`,
|
||||
'c._setup.ts': `
|
||||
const { _setup } = pwt;
|
||||
_setup('_setup1', async () => { });
|
||||
_setup('_setup2', async () => { });
|
||||
'c.setup.ts': `
|
||||
const { setup } = pwt;
|
||||
setup('setup1', async () => { });
|
||||
setup('setup2', async () => { });
|
||||
`,
|
||||
};
|
||||
|
||||
{ // Shard 1/2
|
||||
const { exitCode, passed, timeline, output } = await runGroups(files, { shard: '1/2' });
|
||||
expect(output).toContain('Running 6 tests using 1 worker, shard 1 of 2');
|
||||
expect(fileNames(timeline)).toEqual(['a.test.ts', 'c._setup.ts']);
|
||||
expectFilesRunBefore(timeline, [`c._setup.ts`], [`a.test.ts`]);
|
||||
expect(fileNames(timeline)).toEqual(['a.test.ts', 'c.setup.ts']);
|
||||
expectFilesRunBefore(timeline, [`c.setup.ts`], [`a.test.ts`]);
|
||||
expect(exitCode).toBe(0);
|
||||
expect(passed).toBe(6);
|
||||
}
|
||||
{ // Shard 2/2
|
||||
const { exitCode, passed, timeline, output } = await runGroups(files, { shard: '2/2' });
|
||||
expect(output).toContain('Running 4 tests using 1 worker, shard 2 of 2');
|
||||
expect(fileNames(timeline)).toEqual(['b.test.ts', 'c._setup.ts']);
|
||||
expectFilesRunBefore(timeline, [`c._setup.ts`], [`b.test.ts`]);
|
||||
expect(fileNames(timeline)).toEqual(['b.test.ts', 'c.setup.ts']);
|
||||
expectFilesRunBefore(timeline, [`c.setup.ts`], [`b.test.ts`]);
|
||||
expect(exitCode).toBe(0);
|
||||
expect(passed).toBe(4);
|
||||
}
|
||||
});
|
||||
|
||||
test('should run _setup only for projects that have tests in the shard', async ({ runGroups }, testInfo) => {
|
||||
test('should run setup only for projects that have tests in the shard', async ({ runGroups }, testInfo) => {
|
||||
const files = {
|
||||
'playwright.config.ts': `
|
||||
module.exports = {
|
||||
projects: [
|
||||
{
|
||||
name: 'p1',
|
||||
_setupMatch: /.*p1._setup.ts$/,
|
||||
setupMatch: /.*p1.setup.ts$/,
|
||||
testMatch: /.*a.test.ts/,
|
||||
},
|
||||
{
|
||||
name: 'p2',
|
||||
_setupMatch: /.*p2._setup.ts$/,
|
||||
setupMatch: /.*p2.setup.ts$/,
|
||||
testMatch: /.*b.test.ts/,
|
||||
},
|
||||
]
|
||||
|
@ -232,60 +232,60 @@ test('should run _setup only for projects that have tests in the shard', async (
|
|||
test('test1', async () => { });
|
||||
test('test2', async () => { });
|
||||
`,
|
||||
'p1._setup.ts': `
|
||||
const { _setup } = pwt;
|
||||
_setup('_setup1', async () => { });
|
||||
_setup('_setup2', async () => { });
|
||||
'p1.setup.ts': `
|
||||
const { setup } = pwt;
|
||||
setup('setup1', async () => { });
|
||||
setup('setup2', async () => { });
|
||||
`,
|
||||
'p2._setup.ts': `
|
||||
const { _setup } = pwt;
|
||||
_setup('_setup3', async () => { });
|
||||
_setup('_setup4', async () => { });
|
||||
'p2.setup.ts': `
|
||||
const { setup } = pwt;
|
||||
setup('setup3', async () => { });
|
||||
setup('setup4', async () => { });
|
||||
`,
|
||||
};
|
||||
|
||||
{ // Shard 1/2
|
||||
const { exitCode, passed, timeline, output } = await runGroups(files, { shard: '1/2' });
|
||||
expect(output).toContain('Running 6 tests using 1 worker, shard 1 of 2');
|
||||
expect(fileNames(timeline)).toEqual(['a.test.ts', 'p1._setup.ts']);
|
||||
expectFilesRunBefore(timeline, [`p1._setup.ts`], [`a.test.ts`]);
|
||||
expect(fileNames(timeline)).toEqual(['a.test.ts', 'p1.setup.ts']);
|
||||
expectFilesRunBefore(timeline, [`p1.setup.ts`], [`a.test.ts`]);
|
||||
expect(exitCode).toBe(0);
|
||||
expect(passed).toBe(6);
|
||||
}
|
||||
{ // Shard 2/2
|
||||
const { exitCode, passed, timeline, output } = await runGroups(files, { shard: '2/2' });
|
||||
expect(output).toContain('Running 4 tests using 1 worker, shard 2 of 2');
|
||||
expect(fileNames(timeline)).toEqual(['b.test.ts', 'p2._setup.ts']);
|
||||
expectFilesRunBefore(timeline, [`p2._setup.ts`], [`b.test.ts`]);
|
||||
expect(fileNames(timeline)).toEqual(['b.test.ts', 'p2.setup.ts']);
|
||||
expectFilesRunBefore(timeline, [`p2.setup.ts`], [`b.test.ts`]);
|
||||
expect(exitCode).toBe(0);
|
||||
expect(passed).toBe(4);
|
||||
}
|
||||
});
|
||||
|
||||
test('--project only runs _setup from that project;', async ({ runGroups }, testInfo) => {
|
||||
test('--project only runs setup from that project;', async ({ runGroups }, testInfo) => {
|
||||
const projectTemplates = {
|
||||
'a': {
|
||||
_setupMatch: /.*a._setup.ts/
|
||||
setupMatch: /.*a.setup.ts/
|
||||
},
|
||||
'b': {
|
||||
_setupMatch: /.*b._setup.ts/
|
||||
setupMatch: /.*b.setup.ts/
|
||||
},
|
||||
};
|
||||
const configWithFiles = createConfigWithProjects(['a', 'b', 'c'], testInfo, projectTemplates);
|
||||
const { exitCode, passed, timeline } = await runGroups(configWithFiles, { project: ['a', 'c'] });
|
||||
expect(exitCode).toBe(0);
|
||||
expect(passed).toBe(3);
|
||||
expect(fileNames(timeline)).toEqual(['a._setup.ts', 'a.spec.ts', 'c.spec.ts']);
|
||||
expect(fileNames(timeline)).toEqual(['a.setup.ts', 'a.spec.ts', 'c.spec.ts']);
|
||||
});
|
||||
|
||||
test('same file cannot be a _setup and a test in the same project', async ({ runGroups }, testInfo) => {
|
||||
test('same file cannot be a setup and a test in the same project', async ({ runGroups }, testInfo) => {
|
||||
const files = {
|
||||
'playwright.config.ts': `
|
||||
module.exports = {
|
||||
projects: [
|
||||
{
|
||||
name: 'p1',
|
||||
_setupMatch: /.*a.test.ts$/,
|
||||
setupMatch: /.*a.test.ts$/,
|
||||
testMatch: /.*a.test.ts$/,
|
||||
},
|
||||
]
|
||||
|
@ -298,22 +298,22 @@ test('same file cannot be a _setup and a test in the same project', async ({ run
|
|||
|
||||
const { exitCode, output } = await runGroups(files);
|
||||
expect(exitCode).toBe(1);
|
||||
expect(output).toContain(`a.test.ts" matches both '_setup' and 'testMatch' filters in project "p1"`);
|
||||
expect(output).toContain(`a.test.ts" matches both 'setup' and 'testMatch' filters in project "p1"`);
|
||||
});
|
||||
|
||||
test('same file cannot be a _setup and a test in different projects', async ({ runGroups }, testInfo) => {
|
||||
test('same file cannot be a setup and a test in different projects', async ({ runGroups }, testInfo) => {
|
||||
const files = {
|
||||
'playwright.config.ts': `
|
||||
module.exports = {
|
||||
projects: [
|
||||
{
|
||||
name: 'p1',
|
||||
_setupMatch: /.*a.test.ts$/,
|
||||
setupMatch: /.*a.test.ts$/,
|
||||
testMatch: /.*noMatch.test.ts$/,
|
||||
},
|
||||
{
|
||||
name: 'p2',
|
||||
_setupMatch: /.*noMatch.test.ts$/,
|
||||
setupMatch: /.*noMatch.test.ts$/,
|
||||
testMatch: /.*a.test.ts$/
|
||||
},
|
||||
]
|
||||
|
@ -326,41 +326,41 @@ test('same file cannot be a _setup and a test in different projects', async ({ r
|
|||
|
||||
const { exitCode, output } = await runGroups(files);
|
||||
expect(exitCode).toBe(1);
|
||||
expect(output).toContain(`a.test.ts" matches '_setup' filter in project "p1" and 'testMatch' filter in project "p2"`);
|
||||
expect(output).toContain(`a.test.ts" matches 'setup' filter in project "p1" and 'testMatch' filter in project "p2"`);
|
||||
});
|
||||
|
||||
test('list-files should enumerate _setup files in same group', async ({ runCommand }, testInfo) => {
|
||||
test('list-files should enumerate setup files in same group', async ({ runCommand }, testInfo) => {
|
||||
const files = {
|
||||
'playwright.config.ts': `
|
||||
module.exports = {
|
||||
projects: [
|
||||
{
|
||||
name: 'p1',
|
||||
_setupMatch: /.*a.._setup.ts$/,
|
||||
setupMatch: /.*a..setup.ts$/,
|
||||
testMatch: /.*a.test.ts$/,
|
||||
},
|
||||
{
|
||||
name: 'p2',
|
||||
_setupMatch: /.*b._setup.ts$/,
|
||||
setupMatch: /.*b.setup.ts$/,
|
||||
testMatch: /.*b.test.ts$/
|
||||
},
|
||||
]
|
||||
};`,
|
||||
'a1._setup.ts': `
|
||||
const { _setup } = pwt;
|
||||
_setup('test1', async () => { });
|
||||
'a1.setup.ts': `
|
||||
const { setup } = pwt;
|
||||
setup('test1', async () => { });
|
||||
`,
|
||||
'a2._setup.ts': `
|
||||
const { _setup } = pwt;
|
||||
_setup('test1', async () => { });
|
||||
'a2.setup.ts': `
|
||||
const { setup } = pwt;
|
||||
setup('test1', async () => { });
|
||||
`,
|
||||
'a.test.ts': `
|
||||
const { test } = pwt;
|
||||
test('test2', async () => { });
|
||||
`,
|
||||
'b._setup.ts': `
|
||||
const { _setup } = pwt;
|
||||
_setup('test3', async () => { });
|
||||
'b.setup.ts': `
|
||||
const { setup } = pwt;
|
||||
setup('test3', async () => { });
|
||||
`,
|
||||
'b.test.ts': `
|
||||
const { test } = pwt;
|
||||
|
@ -372,42 +372,42 @@ test('list-files should enumerate _setup files in same group', async ({ runComma
|
|||
expect(exitCode).toBe(0);
|
||||
const json = JSON.parse(output);
|
||||
expect(json.projects.map(p => p.name)).toEqual(['p1', 'p2']);
|
||||
expect(json.projects[0].files.map(f => path.basename(f))).toEqual(['a.test.ts', 'a1._setup.ts', 'a2._setup.ts']);
|
||||
expect(json.projects[1].files.map(f => path.basename(f))).toEqual(['b._setup.ts', 'b.test.ts']);
|
||||
expect(json.projects[0].files.map(f => path.basename(f))).toEqual(['a.test.ts', 'a1.setup.ts', 'a2.setup.ts']);
|
||||
expect(json.projects[1].files.map(f => path.basename(f))).toEqual(['b.setup.ts', 'b.test.ts']);
|
||||
});
|
||||
|
||||
test('test --list should enumerate _setup tests as regular ones', async ({ runCommand }, testInfo) => {
|
||||
test('test --list should enumerate setup tests as regular ones', async ({ runCommand }, testInfo) => {
|
||||
const files = {
|
||||
'playwright.config.ts': `
|
||||
module.exports = {
|
||||
projects: [
|
||||
{
|
||||
name: 'p1',
|
||||
_setupMatch: /.*a.._setup.ts$/,
|
||||
setupMatch: /.*a..setup.ts$/,
|
||||
testMatch: /.*a.test.ts$/,
|
||||
},
|
||||
{
|
||||
name: 'p2',
|
||||
_setupMatch: /.*b._setup.ts$/,
|
||||
setupMatch: /.*b.setup.ts$/,
|
||||
testMatch: /.*b.test.ts$/
|
||||
},
|
||||
]
|
||||
};`,
|
||||
'a1._setup.ts': `
|
||||
const { _setup } = pwt;
|
||||
_setup('test1', async () => { });
|
||||
'a1.setup.ts': `
|
||||
const { setup } = pwt;
|
||||
setup('test1', async () => { });
|
||||
`,
|
||||
'a2._setup.ts': `
|
||||
const { _setup } = pwt;
|
||||
_setup('test1', async () => { });
|
||||
'a2.setup.ts': `
|
||||
const { setup } = pwt;
|
||||
setup('test1', async () => { });
|
||||
`,
|
||||
'a.test.ts': `
|
||||
const { test } = pwt;
|
||||
test('test2', async () => { });
|
||||
`,
|
||||
'b._setup.ts': `
|
||||
const { _setup } = pwt;
|
||||
_setup('test3', async () => { });
|
||||
'b.setup.ts': `
|
||||
const { setup } = pwt;
|
||||
setup('test3', async () => { });
|
||||
`,
|
||||
'b.test.ts': `
|
||||
const { test } = pwt;
|
||||
|
@ -419,21 +419,21 @@ test('test --list should enumerate _setup tests as regular ones', async ({ runCo
|
|||
expect(exitCode).toBe(0);
|
||||
expect(output).toContain(`Listing tests:
|
||||
[p1] › a.test.ts:6:7 › test2
|
||||
[p1] › a1._setup.ts:5:7 › test1
|
||||
[p1] › a2._setup.ts:5:7 › test1
|
||||
[p2] › b._setup.ts:5:7 › test3
|
||||
[p1] › a1.setup.ts:5:7 › test1
|
||||
[p1] › a2.setup.ts:5:7 › test1
|
||||
[p2] › b.setup.ts:5:7 › test3
|
||||
[p2] › b.test.ts:6:7 › test4
|
||||
Total: 5 tests in 5 files`);
|
||||
});
|
||||
|
||||
test('should allow .only in _setup files', async ({ runGroups }, testInfo) => {
|
||||
test('should allow .only in setup files', async ({ runGroups }, testInfo) => {
|
||||
const files = {
|
||||
'playwright.config.ts': `
|
||||
module.exports = {
|
||||
projects: [
|
||||
{
|
||||
name: 'p1',
|
||||
_setupMatch: /.*._setup.ts/,
|
||||
setupMatch: /.*.setup.ts/,
|
||||
},
|
||||
]
|
||||
};`,
|
||||
|
@ -444,31 +444,31 @@ test('should allow .only in _setup files', async ({ runGroups }, testInfo) => {
|
|||
test('test3', async () => { });
|
||||
test('test4', async () => { });
|
||||
`,
|
||||
'a._setup.ts': `
|
||||
const { _setup } = pwt;
|
||||
_setup.only('_setup1', async () => { });
|
||||
_setup('_setup2', async () => { });
|
||||
_setup.only('_setup3', async () => { });
|
||||
'a.setup.ts': `
|
||||
const { setup } = pwt;
|
||||
setup.only('setup1', async () => { });
|
||||
setup('setup2', async () => { });
|
||||
setup.only('setup3', async () => { });
|
||||
`,
|
||||
};
|
||||
|
||||
const { exitCode, passed, timeline, output } = await runGroups(files);
|
||||
expect(output).toContain('Running 2 tests using 1 worker');
|
||||
expect(output).toContain('[p1] › a._setup.ts:5:14 › _setup1');
|
||||
expect(output).toContain('[p1] › a._setup.ts:7:14 › _setup3');
|
||||
expect(fileNames(timeline)).toEqual(['a._setup.ts']);
|
||||
expect(output).toContain('[p1] › a.setup.ts:5:13 › setup1');
|
||||
expect(output).toContain('[p1] › a.setup.ts:7:13 › setup3');
|
||||
expect(fileNames(timeline)).toEqual(['a.setup.ts']);
|
||||
expect(exitCode).toBe(0);
|
||||
expect(passed).toBe(2);
|
||||
});
|
||||
|
||||
test('should allow describe.only in _setup files', async ({ runGroups }, testInfo) => {
|
||||
test('should allow describe.only in setup files', async ({ runGroups }, testInfo) => {
|
||||
const files = {
|
||||
'playwright.config.ts': `
|
||||
module.exports = {
|
||||
projects: [
|
||||
{
|
||||
name: 'p1',
|
||||
_setupMatch: /.*._setup.ts/,
|
||||
setupMatch: /.*.setup.ts/,
|
||||
},
|
||||
]
|
||||
};`,
|
||||
|
@ -479,33 +479,33 @@ test('should allow describe.only in _setup files', async ({ runGroups }, testInf
|
|||
test('test3', async () => { });
|
||||
test('test4', async () => { });
|
||||
`,
|
||||
'a._setup.ts': `
|
||||
const { _setup } = pwt;
|
||||
_setup.describe.only('main', () => {
|
||||
_setup('_setup1', async () => { });
|
||||
_setup('_setup2', async () => { });
|
||||
'a.setup.ts': `
|
||||
const { setup } = pwt;
|
||||
setup.describe.only('main', () => {
|
||||
setup('setup1', async () => { });
|
||||
setup('setup2', async () => { });
|
||||
});
|
||||
_setup('_setup3', async () => { });
|
||||
setup('setup3', async () => { });
|
||||
`,
|
||||
};
|
||||
|
||||
const { exitCode, passed, timeline, output } = await runGroups(files);
|
||||
expect(output).toContain('Running 2 tests using 1 worker');
|
||||
expect(output).toContain('[p1] › a._setup.ts:6:9 › main › _setup1');
|
||||
expect(output).toContain('[p1] › a._setup.ts:7:9 › main › _setup2');
|
||||
expect(fileNames(timeline)).toEqual(['a._setup.ts']);
|
||||
expect(output).toContain('[p1] › a.setup.ts:6:9 › main › setup1');
|
||||
expect(output).toContain('[p1] › a.setup.ts:7:9 › main › setup2');
|
||||
expect(fileNames(timeline)).toEqual(['a.setup.ts']);
|
||||
expect(exitCode).toBe(0);
|
||||
expect(passed).toBe(2);
|
||||
});
|
||||
|
||||
test('should filter describe line in _setup files', async ({ runGroups }, testInfo) => {
|
||||
test('should filter describe line in setup files', async ({ runGroups }, testInfo) => {
|
||||
const files = {
|
||||
'playwright.config.ts': `
|
||||
module.exports = {
|
||||
projects: [
|
||||
{
|
||||
name: 'p1',
|
||||
_setupMatch: /.*._setup.ts/,
|
||||
setupMatch: /.*.setup.ts/,
|
||||
},
|
||||
]
|
||||
};`,
|
||||
|
@ -516,33 +516,33 @@ test('should filter describe line in _setup files', async ({ runGroups }, testIn
|
|||
test('test3', async () => { });
|
||||
test('test4', async () => { });
|
||||
`,
|
||||
'a._setup.ts': `
|
||||
const { _setup } = pwt;
|
||||
_setup.describe('main', () => {
|
||||
_setup('_setup1', async () => { });
|
||||
_setup('_setup2', async () => { });
|
||||
'a.setup.ts': `
|
||||
const { setup } = pwt;
|
||||
setup.describe('main', () => {
|
||||
setup('setup1', async () => { });
|
||||
setup('setup2', async () => { });
|
||||
});
|
||||
_setup('_setup3', async () => { });
|
||||
setup('setup3', async () => { });
|
||||
`,
|
||||
};
|
||||
|
||||
const { exitCode, passed, timeline, output } = await runGroups(files, undefined, undefined, { additionalArgs: ['a._setup.ts:5'] });
|
||||
const { exitCode, passed, timeline, output } = await runGroups(files, undefined, undefined, { additionalArgs: ['a.setup.ts:5'] });
|
||||
expect(output).toContain('Running 2 tests using 1 worker');
|
||||
expect(output).toContain('[p1] › a._setup.ts:6:9 › main › _setup1');
|
||||
expect(output).toContain('[p1] › a._setup.ts:7:9 › main › _setup2');
|
||||
expect(fileNames(timeline)).toEqual(['a._setup.ts']);
|
||||
expect(output).toContain('[p1] › a.setup.ts:6:9 › main › setup1');
|
||||
expect(output).toContain('[p1] › a.setup.ts:7:9 › main › setup2');
|
||||
expect(fileNames(timeline)).toEqual(['a.setup.ts']);
|
||||
expect(exitCode).toBe(0);
|
||||
expect(passed).toBe(2);
|
||||
});
|
||||
|
||||
test('should allow .only in both _setup and test files', async ({ runGroups }, testInfo) => {
|
||||
test('should allow .only in both setup and test files', async ({ runGroups }, testInfo) => {
|
||||
const files = {
|
||||
'playwright.config.ts': `
|
||||
module.exports = {
|
||||
projects: [
|
||||
{
|
||||
name: 'p1',
|
||||
_setupMatch: /.*._setup.ts/,
|
||||
setupMatch: /.*.setup.ts/,
|
||||
},
|
||||
]
|
||||
};`,
|
||||
|
@ -553,28 +553,28 @@ test('should allow .only in both _setup and test files', async ({ runGroups }, t
|
|||
test('test3', async () => { });
|
||||
test('test4', async () => { });
|
||||
`,
|
||||
'a._setup.ts': `
|
||||
const { _setup } = pwt;
|
||||
_setup.only('_setup1', async () => { });
|
||||
_setup('_setup2', async () => { });
|
||||
_setup('_setup3', async () => { });
|
||||
'a.setup.ts': `
|
||||
const { setup } = pwt;
|
||||
setup.only('setup1', async () => { });
|
||||
setup('setup2', async () => { });
|
||||
setup('setup3', async () => { });
|
||||
`,
|
||||
};
|
||||
|
||||
const { exitCode, output } = await runGroups(files);
|
||||
expect(exitCode).toBe(0);
|
||||
expect(output).toContain('[p1] › a._setup.ts:5:14 › _setup1');
|
||||
expect(output).toContain('[p1] › a.setup.ts:5:13 › setup1');
|
||||
expect(output).toContain('[p1] › a.test.ts:7:12 › test2');
|
||||
});
|
||||
|
||||
test('should run full _setup when there is test.only', async ({ runGroups }, testInfo) => {
|
||||
test('should run full setup when there is test.only', async ({ runGroups }, testInfo) => {
|
||||
const files = {
|
||||
'playwright.config.ts': `
|
||||
module.exports = {
|
||||
projects: [
|
||||
{
|
||||
name: 'p1',
|
||||
_setupMatch: /.*._setup.ts/,
|
||||
setupMatch: /.*.setup.ts/,
|
||||
},
|
||||
]
|
||||
};`,
|
||||
|
@ -585,14 +585,14 @@ test('should run full _setup when there is test.only', async ({ runGroups }, tes
|
|||
test('test3', async () => { });
|
||||
test('test4', async () => { });
|
||||
`,
|
||||
'a._setup.ts': `
|
||||
const { _setup } = pwt;
|
||||
_setup('_setup1', async () => { });
|
||||
_setup('_setup2', async () => { });
|
||||
'a.setup.ts': `
|
||||
const { setup } = pwt;
|
||||
setup('setup1', async () => { });
|
||||
setup('setup2', async () => { });
|
||||
`,
|
||||
'b._setup.ts': `
|
||||
const { _setup } = pwt;
|
||||
_setup('_setup3', async () => { });
|
||||
'b.setup.ts': `
|
||||
const { setup } = pwt;
|
||||
setup('setup3', async () => { });
|
||||
`,
|
||||
};
|
||||
|
||||
|
@ -600,24 +600,24 @@ test('should run full _setup when there is test.only', async ({ runGroups }, tes
|
|||
expect(exitCode).toBe(0);
|
||||
expect(passed).toBe(4);
|
||||
expect(output).toContain('Running 4 tests using 2 workers');
|
||||
expect(output).toContain('[p1] › b._setup.ts:5:7 › _setup3');
|
||||
expect(output).toContain('[p1] › a._setup.ts:5:7 › _setup1');
|
||||
expect(output).toContain('[p1] › a._setup.ts:6:7 › _setup2');
|
||||
expect(output).toContain('[p1] › b.setup.ts:5:7 › setup3');
|
||||
expect(output).toContain('[p1] › a.setup.ts:5:7 › setup1');
|
||||
expect(output).toContain('[p1] › a.setup.ts:6:7 › setup2');
|
||||
expect(output).toContain('[p1] › a.test.ts:6:12 › test1');
|
||||
});
|
||||
|
||||
test('should allow filtering _setup by file:line', async ({ runGroups }, testInfo) => {
|
||||
test('should allow filtering setup by file:line', async ({ runGroups }, testInfo) => {
|
||||
const files = {
|
||||
'playwright.config.ts': `
|
||||
module.exports = {
|
||||
projects: [
|
||||
{
|
||||
name: 'p1',
|
||||
_setupMatch: /.*a._setup.ts/,
|
||||
setupMatch: /.*a.setup.ts/,
|
||||
},
|
||||
{
|
||||
name: 'p2',
|
||||
_setupMatch: /.*b._setup.ts/,
|
||||
setupMatch: /.*b.setup.ts/,
|
||||
},
|
||||
]
|
||||
};`,
|
||||
|
@ -627,14 +627,14 @@ test('should allow filtering _setup by file:line', async ({ runGroups }, testInf
|
|||
test('test2', async () => { });
|
||||
test('test3', async () => { });
|
||||
`,
|
||||
'a._setup.ts': `
|
||||
const { _setup } = pwt;
|
||||
_setup('_setup1', async () => { });
|
||||
_setup('_setup2', async () => { });
|
||||
'a.setup.ts': `
|
||||
const { setup } = pwt;
|
||||
setup('setup1', async () => { });
|
||||
setup('setup2', async () => { });
|
||||
`,
|
||||
'b._setup.ts': `
|
||||
const { _setup } = pwt;
|
||||
_setup('_setup1', async () => { });
|
||||
'b.setup.ts': `
|
||||
const { setup } = pwt;
|
||||
setup('setup1', async () => { });
|
||||
`,
|
||||
'b.test.ts': `
|
||||
const { test } = pwt;
|
||||
|
@ -645,31 +645,31 @@ test('should allow filtering _setup by file:line', async ({ runGroups }, testInf
|
|||
};
|
||||
|
||||
{
|
||||
const { exitCode, passed, output } = await runGroups(files, undefined, undefined, { additionalArgs: ['.*_setup.ts$'] });
|
||||
const { exitCode, passed, output } = await runGroups(files, undefined, undefined, { additionalArgs: ['.*setup.ts$'] });
|
||||
expect(output).toContain('Running 3 tests using 2 workers');
|
||||
expect(output).toContain('[p1] › a._setup.ts:5:7 › _setup1');
|
||||
expect(output).toContain('[p1] › a._setup.ts:6:7 › _setup2');
|
||||
expect(output).toContain('[p2] › b._setup.ts:5:7 › _setup1');
|
||||
expect(output).toContain('[p1] › a.setup.ts:5:7 › setup1');
|
||||
expect(output).toContain('[p1] › a.setup.ts:6:7 › setup2');
|
||||
expect(output).toContain('[p2] › b.setup.ts:5:7 › setup1');
|
||||
expect(exitCode).toBe(0);
|
||||
expect(passed).toBe(3);
|
||||
}
|
||||
{
|
||||
const { exitCode, passed, output } = await runGroups(files, undefined, undefined, { additionalArgs: ['.*a._setup.ts:5'] });
|
||||
const { exitCode, passed, output } = await runGroups(files, undefined, undefined, { additionalArgs: ['.*a.setup.ts:5'] });
|
||||
expect(output).toContain('Running 1 test using 1 worker');
|
||||
expect(output).toContain('[p1] › a._setup.ts:5:7 › _setup1');
|
||||
expect(output).toContain('[p1] › a.setup.ts:5:7 › setup1');
|
||||
expect(exitCode).toBe(0);
|
||||
expect(passed).toBe(1);
|
||||
}
|
||||
});
|
||||
|
||||
test('should support filters matching both _setup and test', async ({ runGroups }, testInfo) => {
|
||||
test('should support filters matching both setup and test', async ({ runGroups }, testInfo) => {
|
||||
const files = {
|
||||
'playwright.config.ts': `
|
||||
module.exports = {
|
||||
projects: [
|
||||
{
|
||||
name: 'p1',
|
||||
_setupMatch: /.*._setup.ts/,
|
||||
setupMatch: /.*.setup.ts/,
|
||||
},
|
||||
]
|
||||
};`,
|
||||
|
@ -679,14 +679,14 @@ test('should support filters matching both _setup and test', async ({ runGroups
|
|||
test('test2', async () => { });
|
||||
test('test3', async () => { });
|
||||
`,
|
||||
'a._setup.ts': `
|
||||
const { _setup } = pwt;
|
||||
_setup('_setup1', async () => { });
|
||||
_setup('_setup2', async () => { });
|
||||
'a.setup.ts': `
|
||||
const { setup } = pwt;
|
||||
setup('setup1', async () => { });
|
||||
setup('setup2', async () => { });
|
||||
`,
|
||||
'b._setup.ts': `
|
||||
const { _setup } = pwt;
|
||||
_setup('_setup1', async () => { });
|
||||
'b.setup.ts': `
|
||||
const { setup } = pwt;
|
||||
setup('setup1', async () => { });
|
||||
`,
|
||||
'b.test.ts': `
|
||||
const { test } = pwt;
|
||||
|
@ -694,17 +694,17 @@ test('should support filters matching both _setup and test', async ({ runGroups
|
|||
`,
|
||||
};
|
||||
|
||||
const { exitCode, output } = await runGroups(files, undefined, undefined, { additionalArgs: ['.*a.(_setup|test).ts$'] });
|
||||
const { exitCode, output } = await runGroups(files, undefined, undefined, { additionalArgs: ['.*a.(setup|test).ts$'] });
|
||||
expect(exitCode).toBe(0);
|
||||
expect(output).toContain('Running 5 tests using 1 worker');
|
||||
expect(output).toContain('[p1] › a._setup.ts:5:7 › _setup1');
|
||||
expect(output).toContain('[p1] › a._setup.ts:6:7 › _setup2');
|
||||
expect(output).toContain('[p1] › a.setup.ts:5:7 › setup1');
|
||||
expect(output).toContain('[p1] › a.setup.ts:6:7 › setup2');
|
||||
expect(output).toContain('[p1] › a.test.ts:6:7 › test1');
|
||||
expect(output).toContain('[p1] › a.test.ts:7:7 › test2');
|
||||
expect(output).toContain('[p1] › a.test.ts:8:7 › test3');
|
||||
});
|
||||
|
||||
test('should run _setup for a project if tests match only in another project', async ({ runGroups }, testInfo) => {
|
||||
test('should run setup for a project if tests match only in another project', async ({ runGroups }, testInfo) => {
|
||||
const files = {
|
||||
'playwright.config.ts': `
|
||||
module.exports = {
|
||||
|
@ -712,12 +712,12 @@ test('should run _setup for a project if tests match only in another project', a
|
|||
{
|
||||
name: 'p1',
|
||||
testMatch: /.*a.test.ts/,
|
||||
_setupMatch: /.*a._setup.ts/,
|
||||
setupMatch: /.*a.setup.ts/,
|
||||
},
|
||||
{
|
||||
name: 'p2',
|
||||
testMatch: /.*b.test.ts/,
|
||||
_setupMatch: /.*b._setup.ts/,
|
||||
setupMatch: /.*b.setup.ts/,
|
||||
},
|
||||
]
|
||||
};`,
|
||||
|
@ -725,13 +725,13 @@ test('should run _setup for a project if tests match only in another project', a
|
|||
const { test } = pwt;
|
||||
test('test1', async () => { });
|
||||
`,
|
||||
'a._setup.ts': `
|
||||
const { _setup } = pwt;
|
||||
_setup('_setup1', async () => { });
|
||||
'a.setup.ts': `
|
||||
const { setup } = pwt;
|
||||
setup('setup1', async () => { });
|
||||
`,
|
||||
'b._setup.ts': `
|
||||
const { _setup } = pwt;
|
||||
_setup('_setup1', async () => { });
|
||||
'b.setup.ts': `
|
||||
const { setup } = pwt;
|
||||
setup('setup1', async () => { });
|
||||
`,
|
||||
'b.test.ts': `
|
||||
const { test } = pwt;
|
||||
|
@ -742,19 +742,19 @@ test('should run _setup for a project if tests match only in another project', a
|
|||
const { exitCode, output } = await runGroups(files, undefined, undefined, { additionalArgs: ['.*a.test.ts$'] });
|
||||
expect(exitCode).toBe(0);
|
||||
expect(output).toContain('Running 3 tests using 2 workers');
|
||||
expect(output).toContain('[p1] › a._setup.ts:5:7 › _setup1');
|
||||
expect(output).toContain('[p1] › a.setup.ts:5:7 › setup1');
|
||||
expect(output).toContain('[p1] › a.test.ts:6:7 › test1');
|
||||
expect(output).toContain('[p2] › b._setup.ts:5:7 › _setup1');
|
||||
expect(output).toContain('[p2] › b.setup.ts:5:7 › setup1');
|
||||
});
|
||||
|
||||
test('should run all _setup files if only tests match filter', async ({ runGroups }, testInfo) => {
|
||||
test('should run all setup files if only tests match filter', async ({ runGroups }, testInfo) => {
|
||||
const files = {
|
||||
'playwright.config.ts': `
|
||||
module.exports = {
|
||||
projects: [
|
||||
{
|
||||
name: 'p1',
|
||||
_setupMatch: /.*._setup.ts/,
|
||||
setupMatch: /.*.setup.ts/,
|
||||
},
|
||||
]
|
||||
};`,
|
||||
|
@ -764,34 +764,34 @@ test('should run all _setup files if only tests match filter', async ({ runGroup
|
|||
test('test2', async () => { });
|
||||
test('test3', async () => { });
|
||||
`,
|
||||
'a._setup.ts': `
|
||||
const { _setup } = pwt;
|
||||
_setup('_setup1', async () => { });
|
||||
_setup('_setup2', async () => { });
|
||||
'a.setup.ts': `
|
||||
const { setup } = pwt;
|
||||
setup('setup1', async () => { });
|
||||
setup('setup2', async () => { });
|
||||
`,
|
||||
'b._setup.ts': `
|
||||
const { _setup } = pwt;
|
||||
_setup('_setup1', async () => { });
|
||||
'b.setup.ts': `
|
||||
const { setup } = pwt;
|
||||
setup('setup1', async () => { });
|
||||
`,
|
||||
};
|
||||
|
||||
const { exitCode, output } = await runGroups(files, undefined, undefined, { additionalArgs: ['a.test.ts:7'] });
|
||||
expect(exitCode).toBe(0);
|
||||
expect(output).toContain('Running 4 tests using 2 workers');
|
||||
expect(output).toContain('[p1] › a._setup.ts:5:7 › _setup1');
|
||||
expect(output).toContain('[p1] › a._setup.ts:6:7 › _setup2');
|
||||
expect(output).toContain('[p1] › b._setup.ts:5:7 › _setup1');
|
||||
expect(output).toContain('[p1] › a.setup.ts:5:7 › setup1');
|
||||
expect(output).toContain('[p1] › a.setup.ts:6:7 › setup2');
|
||||
expect(output).toContain('[p1] › b.setup.ts:5:7 › setup1');
|
||||
expect(output).toContain('[p1] › a.test.ts:7:7 › test2');
|
||||
});
|
||||
|
||||
test('should run all _setup files if only tests match grep filter', async ({ runGroups }, testInfo) => {
|
||||
test('should run all setup files if only tests match grep filter', async ({ runGroups }, testInfo) => {
|
||||
const files = {
|
||||
'playwright.config.ts': `
|
||||
module.exports = {
|
||||
projects: [
|
||||
{
|
||||
name: 'p1',
|
||||
_setupMatch: /.*._setup.ts/,
|
||||
setupMatch: /.*.setup.ts/,
|
||||
},
|
||||
]
|
||||
};`,
|
||||
|
@ -801,35 +801,35 @@ test('should run all _setup files if only tests match grep filter', async ({ run
|
|||
test('test2', async () => { });
|
||||
test('test3', async () => { });
|
||||
`,
|
||||
'a._setup.ts': `
|
||||
const { _setup } = pwt;
|
||||
_setup('_setup1', async () => { });
|
||||
_setup('_setup2', async () => { });
|
||||
'a.setup.ts': `
|
||||
const { setup } = pwt;
|
||||
setup('setup1', async () => { });
|
||||
setup('setup2', async () => { });
|
||||
`,
|
||||
'b._setup.ts': `
|
||||
const { _setup } = pwt;
|
||||
_setup('_setup1', async () => { });
|
||||
'b.setup.ts': `
|
||||
const { setup } = pwt;
|
||||
setup('setup1', async () => { });
|
||||
`,
|
||||
};
|
||||
|
||||
const { exitCode, output } = await runGroups(files, undefined, undefined, { additionalArgs: ['--grep', '.*test2$'] });
|
||||
expect(exitCode).toBe(0);
|
||||
expect(output).toContain('Running 4 tests using 2 workers');
|
||||
expect(output).toContain('[p1] › a._setup.ts:5:7 › _setup1');
|
||||
expect(output).toContain('[p1] › a._setup.ts:6:7 › _setup2');
|
||||
expect(output).toContain('[p1] › b._setup.ts:5:7 › _setup1');
|
||||
expect(output).toContain('[p1] › a.setup.ts:5:7 › setup1');
|
||||
expect(output).toContain('[p1] › a.setup.ts:6:7 › setup2');
|
||||
expect(output).toContain('[p1] › b.setup.ts:5:7 › setup1');
|
||||
expect(output).toContain('[p1] › a.test.ts:7:7 › test2');
|
||||
});
|
||||
|
||||
test('should apply project.grep filter to both _setup and tests', async ({ runGroups }, testInfo) => {
|
||||
test('should apply project.grep filter to both setup and tests', async ({ runGroups }, testInfo) => {
|
||||
const files = {
|
||||
'playwright.config.ts': `
|
||||
module.exports = {
|
||||
projects: [
|
||||
{
|
||||
name: 'p1',
|
||||
_setupMatch: /.*._setup.ts/,
|
||||
grep: /a.(test|_setup).ts.*(test|_setup)/,
|
||||
setupMatch: /.*.setup.ts/,
|
||||
grep: /a.(test|setup).ts.*(test|setup)/,
|
||||
},
|
||||
]
|
||||
};`,
|
||||
|
@ -839,65 +839,65 @@ test('should apply project.grep filter to both _setup and tests', async ({ runGr
|
|||
test('test2', async () => { });
|
||||
test('foo', async () => { });
|
||||
`,
|
||||
'a._setup.ts': `
|
||||
const { _setup } = pwt;
|
||||
_setup('_setup1', async () => { });
|
||||
_setup('_setup2', async () => { });
|
||||
'a.setup.ts': `
|
||||
const { setup } = pwt;
|
||||
setup('setup1', async () => { });
|
||||
setup('setup2', async () => { });
|
||||
`,
|
||||
'b._setup.ts': `
|
||||
const { _setup } = pwt;
|
||||
_setup('_setup1', async () => { });
|
||||
_setup('foo', async () => { });
|
||||
'b.setup.ts': `
|
||||
const { setup } = pwt;
|
||||
setup('setup1', async () => { });
|
||||
setup('foo', async () => { });
|
||||
`,
|
||||
};
|
||||
|
||||
const { exitCode, output } = await runGroups(files);
|
||||
expect(exitCode).toBe(0);
|
||||
expect(output).toContain('[p1] › a._setup.ts:5:7 › _setup1');
|
||||
expect(output).toContain('[p1] › a._setup.ts:6:7 › _setup2');
|
||||
expect(output).toContain('[p1] › a.setup.ts:5:7 › setup1');
|
||||
expect(output).toContain('[p1] › a.setup.ts:6:7 › setup2');
|
||||
expect(output).toContain('[p1] › a.test.ts:6:7 › test1');
|
||||
expect(output).toContain('[p1] › a.test.ts:7:7 › test2');
|
||||
});
|
||||
|
||||
test('should prohibit _setup in test files', async ({ runGroups }, testInfo) => {
|
||||
test('should prohibit setup in test files', async ({ runGroups }, testInfo) => {
|
||||
const files = {
|
||||
'a.test.ts': `
|
||||
const { _setup, test } = pwt;
|
||||
_setup('test1', async () => { });
|
||||
const { setup, test } = pwt;
|
||||
setup('test1', async () => { });
|
||||
test('test2', async () => { });
|
||||
`,
|
||||
};
|
||||
|
||||
const { exitCode, output } = await runGroups(files);
|
||||
expect(exitCode).toBe(1);
|
||||
expect(output).toContain('_setup() is called in a file which is not a part of project setup.');
|
||||
expect(output).toContain('setup() is called in a file which is not a part of project setup.');
|
||||
});
|
||||
|
||||
test('should prohibit _setup hooks in test files', async ({ runGroups }, testInfo) => {
|
||||
test('should prohibit setup hooks in test files', async ({ runGroups }, testInfo) => {
|
||||
const files = {
|
||||
'a.test.ts': `
|
||||
const { _setup } = pwt;
|
||||
_setup.beforeAll(async () => { });
|
||||
const { setup } = pwt;
|
||||
setup.beforeAll(async () => { });
|
||||
`,
|
||||
};
|
||||
|
||||
const { exitCode, output } = await runGroups(files);
|
||||
expect(exitCode).toBe(1);
|
||||
expect(output).toContain('_setup.beforeAll() is called in a file which is not a part of project setup');
|
||||
expect(output).toContain('setup.beforeAll() is called in a file which is not a part of project setup');
|
||||
});
|
||||
|
||||
test('should prohibit test in _setup files', async ({ runGroups }, testInfo) => {
|
||||
test('should prohibit test in setup files', async ({ runGroups }, testInfo) => {
|
||||
const files = {
|
||||
'playwright.config.ts': `
|
||||
module.exports = {
|
||||
projects: [
|
||||
{
|
||||
name: 'p1',
|
||||
_setupMatch: /.*._setup.ts/,
|
||||
setupMatch: /.*.setup.ts/,
|
||||
},
|
||||
]
|
||||
};`,
|
||||
'a._setup.ts': `
|
||||
'a.setup.ts': `
|
||||
const { test } = pwt;
|
||||
test('test1', async () => { });
|
||||
`,
|
||||
|
@ -908,18 +908,18 @@ test('should prohibit test in _setup files', async ({ runGroups }, testInfo) =>
|
|||
expect(output).toContain('test() is called in a project setup file');
|
||||
});
|
||||
|
||||
test('should prohibit test hooks in _setup files', async ({ runGroups }, testInfo) => {
|
||||
test('should prohibit test hooks in setup files', async ({ runGroups }, testInfo) => {
|
||||
const files = {
|
||||
'playwright.config.ts': `
|
||||
module.exports = {
|
||||
projects: [
|
||||
{
|
||||
name: 'p1',
|
||||
_setupMatch: /.*._setup.ts/,
|
||||
setupMatch: /.*.setup.ts/,
|
||||
},
|
||||
]
|
||||
};`,
|
||||
'a._setup.ts': `
|
||||
'a.setup.ts': `
|
||||
const { test } = pwt;
|
||||
test.beforeEach(async () => { });
|
||||
`,
|
||||
|
|
|
@ -16,24 +16,24 @@
|
|||
|
||||
import { expect, test } from './playwright-test-fixtures';
|
||||
|
||||
test('should provide _store fixture', async ({ runInlineTest }) => {
|
||||
test('should provide store fixture', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
'playwright.config.js': `
|
||||
module.exports = {};
|
||||
`,
|
||||
'a.test.ts': `
|
||||
const { test, _store } = pwt;
|
||||
test('should _store number', async ({ }) => {
|
||||
expect(_store).toBeTruthy();
|
||||
expect(await _store.get('number')).toBe(undefined);
|
||||
await _store.set('number', 2022)
|
||||
expect(await _store.get('number')).toBe(2022);
|
||||
const { test, store } = pwt;
|
||||
test('should store number', async ({ }) => {
|
||||
expect(store).toBeTruthy();
|
||||
expect(await store.get('number')).toBe(undefined);
|
||||
await store.set('number', 2022)
|
||||
expect(await store.get('number')).toBe(2022);
|
||||
});
|
||||
test('should _store object', async ({ }) => {
|
||||
expect(_store).toBeTruthy();
|
||||
expect(await _store.get('object')).toBe(undefined);
|
||||
await _store.set('object', { 'a': 2022 })
|
||||
expect(await _store.get('object')).toEqual({ 'a': 2022 });
|
||||
test('should store object', async ({ }) => {
|
||||
expect(store).toBeTruthy();
|
||||
expect(await store.get('object')).toBe(undefined);
|
||||
await store.set('object', { 'a': 2022 })
|
||||
expect(await store.get('object')).toEqual({ 'a': 2022 });
|
||||
});
|
||||
`,
|
||||
}, { workers: 1 });
|
||||
|
@ -41,42 +41,42 @@ test('should provide _store fixture', async ({ runInlineTest }) => {
|
|||
expect(result.passed).toBe(2);
|
||||
});
|
||||
|
||||
test('should share _store state between project setup and tests', async ({ runInlineTest }) => {
|
||||
test('should share store state between project setup and tests', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
'playwright.config.js': `
|
||||
module.exports = {
|
||||
projects: [
|
||||
{
|
||||
name: 'p1',
|
||||
_setupMatch: /.*_store.setup.ts/
|
||||
setupMatch: /.*store.setup.ts/
|
||||
}
|
||||
]
|
||||
};
|
||||
`,
|
||||
'_store.setup.ts': `
|
||||
const { _setup, expect, _store } = pwt;
|
||||
_setup('should initialize _store', async ({ }) => {
|
||||
expect(await _store.get('number')).toBe(undefined);
|
||||
await _store.set('number', 2022)
|
||||
expect(await _store.get('number')).toBe(2022);
|
||||
'store.setup.ts': `
|
||||
const { setup, expect, store } = pwt;
|
||||
setup('should initialize store', async ({ }) => {
|
||||
expect(await store.get('number')).toBe(undefined);
|
||||
await store.set('number', 2022)
|
||||
expect(await store.get('number')).toBe(2022);
|
||||
|
||||
expect(await _store.get('object')).toBe(undefined);
|
||||
await _store.set('object', { 'a': 2022 })
|
||||
expect(await _store.get('object')).toEqual({ 'a': 2022 });
|
||||
expect(await store.get('object')).toBe(undefined);
|
||||
await store.set('object', { 'a': 2022 })
|
||||
expect(await store.get('object')).toEqual({ 'a': 2022 });
|
||||
});
|
||||
`,
|
||||
'a.test.ts': `
|
||||
const { test, _store } = pwt;
|
||||
const { test, store } = pwt;
|
||||
test('should get data from setup', async ({ }) => {
|
||||
expect(await _store.get('number')).toBe(2022);
|
||||
expect(await _store.get('object')).toEqual({ 'a': 2022 });
|
||||
expect(await store.get('number')).toBe(2022);
|
||||
expect(await store.get('object')).toEqual({ 'a': 2022 });
|
||||
});
|
||||
`,
|
||||
'b.test.ts': `
|
||||
const { test, _store } = pwt;
|
||||
const { test, store } = pwt;
|
||||
test('should get data from setup', async ({ }) => {
|
||||
expect(await _store.get('number')).toBe(2022);
|
||||
expect(await _store.get('object')).toEqual({ 'a': 2022 });
|
||||
expect(await store.get('number')).toBe(2022);
|
||||
expect(await store.get('object')).toEqual({ 'a': 2022 });
|
||||
});
|
||||
`,
|
||||
}, { workers: 1 });
|
||||
|
@ -84,25 +84,25 @@ test('should share _store state between project setup and tests', async ({ runIn
|
|||
expect(result.passed).toBe(3);
|
||||
});
|
||||
|
||||
test('should persist _store state between project runs', async ({ runInlineTest }) => {
|
||||
test('should persist store state between project runs', async ({ runInlineTest }) => {
|
||||
const files = {
|
||||
'playwright.config.js': `
|
||||
module.exports = { };
|
||||
`,
|
||||
'a.test.ts': `
|
||||
const { test, _store } = pwt;
|
||||
const { test, store } = pwt;
|
||||
test('should have no data on first run', async ({ }) => {
|
||||
expect(await _store.get('number')).toBe(undefined);
|
||||
await _store.set('number', 2022)
|
||||
expect(await _store.get('object')).toBe(undefined);
|
||||
await _store.set('object', { 'a': 2022 })
|
||||
expect(await store.get('number')).toBe(undefined);
|
||||
await store.set('number', 2022)
|
||||
expect(await store.get('object')).toBe(undefined);
|
||||
await store.set('object', { 'a': 2022 })
|
||||
});
|
||||
`,
|
||||
'b.test.ts': `
|
||||
const { test, _store } = pwt;
|
||||
const { test, store } = pwt;
|
||||
test('should get data from previous run', async ({ }) => {
|
||||
expect(await _store.get('number')).toBe(2022);
|
||||
expect(await _store.get('object')).toEqual({ 'a': 2022 });
|
||||
expect(await store.get('number')).toBe(2022);
|
||||
expect(await store.get('object')).toEqual({ 'a': 2022 });
|
||||
});
|
||||
`,
|
||||
};
|
||||
|
@ -118,46 +118,46 @@ test('should persist _store state between project runs', async ({ runInlineTest
|
|||
}
|
||||
});
|
||||
|
||||
test('should isolate _store state between projects', async ({ runInlineTest }) => {
|
||||
test('should isolate store state between projects', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
'playwright.config.js': `
|
||||
module.exports = {
|
||||
projects: [
|
||||
{
|
||||
name: 'p1',
|
||||
_setupMatch: /.*_store.setup.ts/
|
||||
setupMatch: /.*store.setup.ts/
|
||||
},
|
||||
{
|
||||
name: 'p2',
|
||||
_setupMatch: /.*_store.setup.ts/
|
||||
setupMatch: /.*store.setup.ts/
|
||||
}
|
||||
]
|
||||
};
|
||||
`,
|
||||
'_store.setup.ts': `
|
||||
const { _setup, expect, _store } = pwt;
|
||||
_setup('should initialize _store', async ({ }) => {
|
||||
expect(await _store.get('number')).toBe(undefined);
|
||||
await _store.set('number', 2022)
|
||||
expect(await _store.get('number')).toBe(2022);
|
||||
'store.setup.ts': `
|
||||
const { setup, expect, store } = pwt;
|
||||
setup('should initialize store', async ({ }) => {
|
||||
expect(await store.get('number')).toBe(undefined);
|
||||
await store.set('number', 2022)
|
||||
expect(await store.get('number')).toBe(2022);
|
||||
|
||||
expect(await _store.get('name')).toBe(undefined);
|
||||
await _store.set('name', 'str-' + _setup.info().project.name)
|
||||
expect(await _store.get('name')).toBe('str-' + _setup.info().project.name);
|
||||
expect(await store.get('name')).toBe(undefined);
|
||||
await store.set('name', 'str-' + setup.info().project.name)
|
||||
expect(await store.get('name')).toBe('str-' + setup.info().project.name);
|
||||
});
|
||||
`,
|
||||
'a.test.ts': `
|
||||
const { test, _store } = pwt;
|
||||
const { test, store } = pwt;
|
||||
test('should get data from setup', async ({ }) => {
|
||||
expect(await _store.get('number')).toBe(2022);
|
||||
expect(await _store.get('name')).toBe('str-' + test.info().project.name);
|
||||
expect(await store.get('number')).toBe(2022);
|
||||
expect(await store.get('name')).toBe('str-' + test.info().project.name);
|
||||
});
|
||||
`,
|
||||
'b.test.ts': `
|
||||
const { test, _store } = pwt;
|
||||
const { test, store } = pwt;
|
||||
test('should get data from setup', async ({ }) => {
|
||||
expect(await _store.get('number')).toBe(2022);
|
||||
expect(await _store.get('name')).toBe('str-' + test.info().project.name);
|
||||
expect(await store.get('number')).toBe(2022);
|
||||
expect(await store.get('name')).toBe('str-' + test.info().project.name);
|
||||
});
|
||||
`,
|
||||
}, { workers: 2 });
|
||||
|
@ -165,7 +165,7 @@ test('should isolate _store state between projects', async ({ runInlineTest }) =
|
|||
expect(result.passed).toBe(6);
|
||||
});
|
||||
|
||||
test('should load context storageState from _store', async ({ runInlineTest, server }) => {
|
||||
test('should load context storageState from store', async ({ runInlineTest, server }) => {
|
||||
server.setRoute('/setcookie.html', (req, res) => {
|
||||
res.setHeader('Set-Cookie', ['a=v1']);
|
||||
res.end();
|
||||
|
@ -176,24 +176,24 @@ test('should load context storageState from _store', async ({ runInlineTest, ser
|
|||
projects: [
|
||||
{
|
||||
name: 'p1',
|
||||
_setupMatch: /.*_store.setup.ts/
|
||||
setupMatch: /.*store.setup.ts/
|
||||
}
|
||||
]
|
||||
};
|
||||
`,
|
||||
'_store.setup.ts': `
|
||||
const { _setup, expect, _store } = pwt;
|
||||
_setup('should save storageState', async ({ page, context }) => {
|
||||
expect(await _store.get('user')).toBe(undefined);
|
||||
'store.setup.ts': `
|
||||
const { setup, expect, store } = pwt;
|
||||
setup('should save storageState', async ({ page, context }) => {
|
||||
expect(await store.get('user')).toBe(undefined);
|
||||
await page.goto('${server.PREFIX}/setcookie.html');
|
||||
const state = await page.context().storageState();
|
||||
await _store.set('user', state);
|
||||
await store.set('user', state);
|
||||
});
|
||||
`,
|
||||
'a.test.ts': `
|
||||
const { test } = pwt;
|
||||
test.use({
|
||||
_storageStateName: 'user'
|
||||
storageStateName: 'user'
|
||||
})
|
||||
test('should get data from setup', async ({ page }) => {
|
||||
await page.goto('${server.EMPTY_PAGE}');
|
||||
|
@ -214,7 +214,7 @@ test('should load context storageState from _store', async ({ runInlineTest, ser
|
|||
expect(result.passed).toBe(3);
|
||||
});
|
||||
|
||||
test('should load _storageStateName specified in the project config from _store', async ({ runInlineTest, server }) => {
|
||||
test('should load storageStateName specified in the project config from store', async ({ runInlineTest, server }) => {
|
||||
server.setRoute('/setcookie.html', (req, res) => {
|
||||
res.setHeader('Set-Cookie', ['a=v1']);
|
||||
res.end();
|
||||
|
@ -225,24 +225,24 @@ test('should load _storageStateName specified in the project config from _store'
|
|||
projects: [
|
||||
{
|
||||
name: 'p1',
|
||||
_setupMatch: /.*_store.setup.ts/,
|
||||
setupMatch: /.*store.setup.ts/,
|
||||
use: {
|
||||
_storageStateName: 'stateInStorage',
|
||||
storageStateName: 'stateInStorage',
|
||||
},
|
||||
}
|
||||
]
|
||||
};
|
||||
`,
|
||||
'_store.setup.ts': `
|
||||
const { _setup, expect, _store } = pwt;
|
||||
_setup.use({
|
||||
_storageStateName: ({}, use) => use(undefined),
|
||||
'store.setup.ts': `
|
||||
const { setup, expect, store } = pwt;
|
||||
setup.use({
|
||||
storageStateName: ({}, use) => use(undefined),
|
||||
})
|
||||
_setup('should save storageState', async ({ page, context }) => {
|
||||
expect(await _store.get('stateInStorage')).toBe(undefined);
|
||||
setup('should save storageState', async ({ page, context }) => {
|
||||
expect(await store.get('stateInStorage')).toBe(undefined);
|
||||
await page.goto('${server.PREFIX}/setcookie.html');
|
||||
const state = await page.context().storageState();
|
||||
await _store.set('stateInStorage', state);
|
||||
await store.set('stateInStorage', state);
|
||||
});
|
||||
`,
|
||||
'a.test.ts': `
|
||||
|
@ -258,7 +258,7 @@ test('should load _storageStateName specified in the project config from _store'
|
|||
expect(result.passed).toBe(2);
|
||||
});
|
||||
|
||||
test('should load _storageStateName specified in the global config from _store', async ({ runInlineTest, server }) => {
|
||||
test('should load storageStateName specified in the global config from store', async ({ runInlineTest, server }) => {
|
||||
server.setRoute('/setcookie.html', (req, res) => {
|
||||
res.setHeader('Set-Cookie', ['a=v1']);
|
||||
res.end();
|
||||
|
@ -267,26 +267,26 @@ test('should load _storageStateName specified in the global config from _store',
|
|||
'playwright.config.js': `
|
||||
module.exports = {
|
||||
use: {
|
||||
_storageStateName: 'stateInStorage',
|
||||
storageStateName: 'stateInStorage',
|
||||
},
|
||||
projects: [
|
||||
{
|
||||
name: 'p1',
|
||||
_setupMatch: /.*_store.setup.ts/,
|
||||
setupMatch: /.*store.setup.ts/,
|
||||
}
|
||||
]
|
||||
};
|
||||
`,
|
||||
'_store.setup.ts': `
|
||||
const { _setup, expect, _store } = pwt;
|
||||
_setup.use({
|
||||
_storageStateName: ({}, use) => use(undefined),
|
||||
'store.setup.ts': `
|
||||
const { setup, expect, store } = pwt;
|
||||
setup.use({
|
||||
storageStateName: ({}, use) => use(undefined),
|
||||
})
|
||||
_setup('should save _storageStateName', async ({ page, context }) => {
|
||||
expect(await _store.get('stateInStorage')).toBe(undefined);
|
||||
setup('should save storageStateName', async ({ page, context }) => {
|
||||
expect(await store.get('stateInStorage')).toBe(undefined);
|
||||
await page.goto('${server.PREFIX}/setcookie.html');
|
||||
const state = await page.context().storageState();
|
||||
await _store.set('stateInStorage', state);
|
||||
await store.set('stateInStorage', state);
|
||||
});
|
||||
`,
|
||||
'a.test.ts': `
|
||||
|
@ -302,7 +302,7 @@ test('should load _storageStateName specified in the global config from _store',
|
|||
expect(result.passed).toBe(2);
|
||||
});
|
||||
|
||||
test('should throw on unknown _storageStateName value', async ({ runInlineTest, server }) => {
|
||||
test('should throw on unknown storageStateName value', async ({ runInlineTest, server }) => {
|
||||
const result = await runInlineTest({
|
||||
'playwright.config.js': `
|
||||
module.exports = {
|
||||
|
@ -310,7 +310,7 @@ test('should throw on unknown _storageStateName value', async ({ runInlineTest,
|
|||
{
|
||||
name: 'p1',
|
||||
use: {
|
||||
_storageStateName: 'stateInStorage',
|
||||
storageStateName: 'stateInStorage',
|
||||
},
|
||||
}
|
||||
]
|
||||
|
@ -324,5 +324,5 @@ test('should throw on unknown _storageStateName value', async ({ runInlineTest,
|
|||
}, { workers: 1 });
|
||||
expect(result.exitCode).toBe(1);
|
||||
expect(result.passed).toBe(0);
|
||||
expect(result.output).toContain('Error: Cannot find value in the _store for _storageStateName: "stateInStorage"');
|
||||
expect(result.output).toContain('Error: Cannot find value in the store for storageStateName: "stateInStorage"');
|
||||
});
|
|
@ -188,3 +188,18 @@ test('config should allow void/empty options', async ({ runTSC }) => {
|
|||
});
|
||||
expect(result.exitCode).toBe(0);
|
||||
});
|
||||
|
||||
test('should provide store interface', async ({ runTSC }) => {
|
||||
const result = await runTSC({
|
||||
'a.spec.ts': `
|
||||
const { test, store } = pwt;
|
||||
test('my test', async () => {
|
||||
await store.set('foo', 'bar');
|
||||
const val = await store.get('foo');
|
||||
// @ts-expect-error
|
||||
await store.unknown();
|
||||
});
|
||||
`
|
||||
});
|
||||
expect(result.exitCode).toBe(0);
|
||||
});
|
||||
|
|
|
@ -196,6 +196,11 @@ type ConnectOptions = {
|
|||
timeout?: number;
|
||||
};
|
||||
|
||||
export interface TestStore {
|
||||
get<T>(name: string): Promise<T | undefined>;
|
||||
set<T>(name: string, value: T | undefined): Promise<void>;
|
||||
}
|
||||
|
||||
export interface PlaywrightWorkerOptions {
|
||||
browserName: BrowserName;
|
||||
defaultBrowserType: BrowserName;
|
||||
|
@ -229,6 +234,7 @@ export interface PlaywrightTestOptions {
|
|||
permissions: string[] | undefined;
|
||||
proxy: Proxy | undefined;
|
||||
storageState: StorageState | undefined;
|
||||
storageStateName: string | undefined;
|
||||
timezoneId: string | undefined;
|
||||
userAgent: string | undefined;
|
||||
viewport: ViewportSize | null | undefined;
|
||||
|
@ -351,6 +357,7 @@ export default test;
|
|||
|
||||
export const _baseTest: TestType<{}, {}>;
|
||||
export const expect: Expect;
|
||||
export const store: TestStore;
|
||||
|
||||
// This is required to not export everything by default. See https://github.com/Microsoft/TypeScript/issues/19545#issuecomment-340490459
|
||||
export {};
|
||||
|
|
Загрузка…
Ссылка в новой задаче