Skip to content

Commit 137070d

Browse files
authored
Revert "chore: hide setup, store, TestProject.setupMatch, storageStat… (#19756)
…eName (#19442)" This reverts commit 92dd734.
1 parent d912cbf commit 137070d

File tree

14 files changed

+537
-337
lines changed

14 files changed

+537
-337
lines changed

docs/src/test-api/class-testoptions.md

+8
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,14 @@ Learn more about [automatic screenshots](../test-configuration.md#automatic-scre
205205
## property: TestOptions.storageState = %%-js-python-context-option-storage-state-%%
206206
* since: v1.10
207207

208+
## property: TestOptions.storageStateName
209+
* since: v1.29
210+
- type: <[string]>
211+
212+
Name of the [TestStore] entry that should be used to initialize [`property: TestOptions.storageState`]. The value must be
213+
written to the test storage before creation of a browser context that uses it (usually in [`property: TestProject.setupMatch`]). If both
214+
this property and [`property: TestOptions.storageState`] are specified, this property will always take precedence.
215+
208216
## property: TestOptions.testIdAttribute
209217
* since: v1.27
210218

docs/src/test-api/class-testproject.md

+13-1
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ Filter to only run tests with a title matching one of the patterns. For example,
146146
* since: v1.10
147147
- type: ?<[RegExp]|[Array]<[RegExp]>>
148148

149-
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.
149+
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.
150150

151151
`grepInvert` option is also useful for [tagging tests](../test-annotations.md#tag-tests).
152152

@@ -162,6 +162,18 @@ Metadata that will be put directly to the test report serialized as JSON.
162162

163163
Project name is visible in the report and during test execution.
164164

165+
## property: TestProject.setupMatch
166+
* since: v1.29
167+
- type: ?<[string]|[RegExp]|[Array]<[string]|[RegExp]>>
168+
169+
Project setup files that will be executed before all tests in the project.
170+
171+
**Details**
172+
173+
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.
174+
175+
If there is a file that matches both [`property: TestProject.setupMatch`] and [`property: TestProject.testMatch`] filters an error will be thrown.
176+
165177
## property: TestProject.snapshotDir
166178
* since: v1.10
167179
- type: ?<[string]>

docs/src/test-api/class-teststore.md

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# class: TestStore
2+
* since: v1.29
3+
* langs: js
4+
5+
Playwright Test provides a global `store` object for passing values between project setup and tests. It is
6+
an error to call store methods outside of setup and tests.
7+
8+
```js tab=js-js
9+
const { setup, store } = require('@playwright/test');
10+
11+
setup('sign in', async ({ page, context }) => {
12+
// Save signed-in state to an entry named 'github-test-user'.
13+
const contextState = await context.storageState();
14+
await store.set('test-user', contextState)
15+
});
16+
```
17+
18+
```js tab=js-ts
19+
import { setup, store } from '@playwright/test';
20+
21+
setup('sign in', async ({ page, context }) => {
22+
// Save signed-in state to an entry named 'github-test-user'.
23+
const contextState = await context.storageState();
24+
await store.set('test-user', contextState)
25+
});
26+
```
27+
28+
## async method: TestStore.get
29+
* since: v1.29
30+
- returns: <[any]>
31+
32+
Get named item from the store. Returns undefined if there is no value with given name.
33+
34+
### param: TestStore.get.name
35+
* since: v1.29
36+
- `name` <[string]>
37+
38+
Item name.
39+
40+
## async method: TestStore.set
41+
* since: v1.29
42+
43+
Set value to the store.
44+
45+
### param: TestStore.set.name
46+
* since: v1.29
47+
- `name` <[string]>
48+
49+
Item name.
50+
51+
### param: TestStore.set.value
52+
* since: v1.29
53+
- `value` <[any]>
54+
55+
Item value. The value must be serializable to JSON. Passing `undefined` deletes the entry with given name.
56+

packages/playwright-test/src/index.ts

+8-9
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import { rootTestType, _setProjectSetup } from './testType';
2727
export { expect } from './expect';
2828
export { addRunnerPlugin as _addRunnerPlugin } from './plugins';
2929
export const _baseTest: TestType<{}, {}> = rootTestType.test;
30-
export const _store = _baseStore;
30+
export const store = _baseStore;
3131

3232
if ((process as any)['__pw_initiator__']) {
3333
const originalStackTraceLimit = Error.stackTraceLimit;
@@ -47,7 +47,6 @@ type TestFixtures = PlaywrightTestArgs & PlaywrightTestOptions & {
4747
_reuseContext: boolean,
4848
_setupContextOptionsAndArtifacts: void;
4949
_contextFactory: (options?: BrowserContextOptions) => Promise<BrowserContext>;
50-
_storageStateName: string | undefined;
5150
};
5251
type WorkerFixtures = PlaywrightWorkerArgs & PlaywrightWorkerOptions & {
5352
_browserOptions: LaunchOptions;
@@ -144,7 +143,7 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({
144143
permissions: [({ contextOptions }, use) => use(contextOptions.permissions), { option: true }],
145144
proxy: [({ contextOptions }, use) => use(contextOptions.proxy), { option: true }],
146145
storageState: [({ contextOptions }, use) => use(contextOptions.storageState), { option: true }],
147-
_storageStateName: [undefined, { option: true }],
146+
storageStateName: [undefined, { option: true }],
148147
timezoneId: [({ contextOptions }, use) => use(contextOptions.timezoneId), { option: true }],
149148
userAgent: [({ contextOptions }, use) => use(contextOptions.userAgent), { option: true }],
150149
viewport: [({ contextOptions }, use) => use(contextOptions.viewport === undefined ? { width: 1280, height: 720 } : contextOptions.viewport), { option: true }],
@@ -174,7 +173,7 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({
174173
permissions,
175174
proxy,
176175
storageState,
177-
_storageStateName,
176+
storageStateName,
178177
viewport,
179178
timezoneId,
180179
userAgent,
@@ -213,10 +212,10 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({
213212
options.permissions = permissions;
214213
if (proxy !== undefined)
215214
options.proxy = proxy;
216-
if (_storageStateName !== undefined) {
217-
const value = await _store.get(_storageStateName);
215+
if (storageStateName !== undefined) {
216+
const value = await store.get(storageStateName);
218217
if (!value)
219-
throw new Error(`Cannot find value in the _store for _storageStateName: "${_storageStateName}"`);
218+
throw new Error(`Cannot find value in the store for storageStateName: "${storageStateName}"`);
220219
options.storageState = value as any;
221220
} else if (storageState !== undefined) {
222221
options.storageState = storageState;
@@ -629,7 +628,7 @@ function normalizeScreenshotMode(screenshot: PlaywrightWorkerOptions['screenshot
629628
const kTracingStarted = Symbol('kTracingStarted');
630629

631630
export const test = _baseTest.extend<TestFixtures, WorkerFixtures>(playwrightFixtures);
632-
export const _setup = _baseTest.extend<TestFixtures, WorkerFixtures>(playwrightFixtures);
633-
_setProjectSetup(_setup, true);
631+
export const setup = _baseTest.extend<TestFixtures, WorkerFixtures>(playwrightFixtures);
632+
_setProjectSetup(setup, true);
634633

635634
export default test;

packages/playwright-test/src/loader.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ export class Loader {
275275
const outputDir = takeFirst(projectConfig.outputDir, config.outputDir, path.join(throwawayArtifactsPath, 'test-results'));
276276
const snapshotDir = takeFirst(projectConfig.snapshotDir, config.snapshotDir, testDir);
277277
const name = takeFirst(projectConfig.name, config.name, '');
278-
const _setupMatch = takeFirst((projectConfig as any)._setupMatch, []);
278+
const _setupMatch = takeFirst(projectConfig.setupMatch, []);
279279

280280
const defaultSnapshotPathTemplate = '{snapshotDir}/{testFileDir}/{testFileName}-snapshots/{arg}{-projectName}{-snapshotSuffix}{ext}';
281281
const snapshotPathTemplate = takeFirst(projectConfig.snapshotPathTemplate, config.snapshotPathTemplate, defaultSnapshotPathTemplate);
@@ -614,7 +614,7 @@ function validateProject(file: string, project: Project, title: string) {
614614
throw errorWithFile(file, `${title}.testDir must be a string`);
615615
}
616616

617-
for (const prop of ['testIgnore', 'testMatch'] as const) {
617+
for (const prop of ['testIgnore', 'testMatch', 'setupMatch'] as const) {
618618
if (prop in project && project[prop] !== undefined) {
619619
const value = project[prop];
620620
if (Array.isArray(value)) {

packages/playwright-test/src/runner.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -258,13 +258,13 @@ export class Runner {
258258
if (!isTest && !isSetup)
259259
return false;
260260
if (isSetup && isTest)
261-
throw new Error(`File "${file}" matches both '_setup' and 'testMatch' filters in project "${project.name}"`);
261+
throw new Error(`File "${file}" matches both 'setup' and 'testMatch' filters in project "${project.name}"`);
262262
if (fileToProjectName.has(file)) {
263263
if (isSetup) {
264264
if (!setupFiles.has(file))
265-
throw new Error(`File "${file}" matches '_setup' filter in project "${project.name}" and 'testMatch' filter in project "${fileToProjectName.get(file)}"`);
265+
throw new Error(`File "${file}" matches 'setup' filter in project "${project.name}" and 'testMatch' filter in project "${fileToProjectName.get(file)}"`);
266266
} else if (setupFiles.has(file)) {
267-
throw new Error(`File "${file}" matches '_setup' filter in project "${fileToProjectName.get(file)}" and 'testMatch' filter in project "${project.name}"`);
267+
throw new Error(`File "${file}" matches 'setup' filter in project "${fileToProjectName.get(file)}" and 'testMatch' filter in project "${project.name}"`);
268268
}
269269
}
270270
fileToProjectName.set(file, project.name);

packages/playwright-test/src/store.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,11 @@
1616

1717
import fs from 'fs';
1818
import path from 'path';
19+
import type { TestStore } from '../types/test';
1920
import { currentTestInfo } from './globals';
2021
import { sanitizeForFilePath, trimLongString } from './util';
2122

22-
class JsonStore {
23+
class JsonStore implements TestStore {
2324
private _toFilePath(name: string) {
2425
const testInfo = currentTestInfo();
2526
if (!testInfo)

packages/playwright-test/src/testType.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -84,14 +84,14 @@ export class TestTypeImpl {
8484
if (this._projectSetup)
8585
addFatalError(`${title} is called in a file which is not a part of project setup.`, location);
8686
else
87-
addFatalError(`${title} is called in a project setup file (use '_setup' instead of 'test').`, location);
87+
addFatalError(`${title} is called in a project setup file (use 'setup' instead of 'test').`, location);
8888
}
8989
return suite;
9090
}
9191

9292
private _createTest(type: 'default' | 'only' | 'skip' | 'fixme', location: Location, title: string, fn: Function) {
9393
throwIfRunningInsideJest();
94-
const suite = this._currentSuite(location, this._projectSetup ? '_setup()' : 'test()');
94+
const suite = this._currentSuite(location, this._projectSetup ? 'setup()' : 'test()');
9595
if (!suite)
9696
return;
9797
const test = new TestCase(title, fn, this, location);
@@ -150,15 +150,15 @@ export class TestTypeImpl {
150150
}
151151

152152
private _hook(name: 'beforeEach' | 'afterEach' | 'beforeAll' | 'afterAll', location: Location, fn: Function) {
153-
const suite = this._currentSuite(location, `${this._projectSetup ? '_setup' : 'test'}.${name}()`);
153+
const suite = this._currentSuite(location, `${this._projectSetup ? 'setup' : 'test'}.${name}()`);
154154
if (!suite)
155155
return;
156156
suite._hooks.push({ type: name, fn, location });
157157
}
158158

159159
private _configure(location: Location, options: { mode?: 'parallel' | 'serial', retries?: number, timeout?: number }) {
160160
throwIfRunningInsideJest();
161-
const suite = this._currentSuite(location, `${this._projectSetup ? '_setup' : 'test'}.describe.configure()`);
161+
const suite = this._currentSuite(location, `${this._projectSetup ? 'setup' : 'test'}.describe.configure()`);
162162
if (!suite)
163163
return;
164164

@@ -225,7 +225,7 @@ export class TestTypeImpl {
225225
}
226226

227227
private _use(location: Location, fixtures: Fixtures) {
228-
const suite = this._currentSuite(location, `${this._projectSetup ? '_setup' : 'test'}.use()`);
228+
const suite = this._currentSuite(location, `${this._projectSetup ? 'setup' : 'test'}.use()`);
229229
if (!suite)
230230
return;
231231
suite._use.push({ fixtures, location });
@@ -234,7 +234,7 @@ export class TestTypeImpl {
234234
private async _step<T>(location: Location, title: string, body: () => Promise<T>): Promise<T> {
235235
const testInfo = currentTestInfo();
236236
if (!testInfo) {
237-
addFatalError(`${this._projectSetup ? '_setup' : 'test'}.step() can only be called from a test`, location);
237+
addFatalError(`${this._projectSetup ? 'setup' : 'test'}.step() can only be called from a test`, location);
238238
return undefined as any;
239239
}
240240
const step = testInfo._addStep({

packages/playwright-test/types/test.d.ts

+66-2
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,10 @@ export interface FullProject<TestArgs = {}, WorkerArgs = {}> {
193193
/**
194194
* Filter to only run tests with a title **not** matching one of the patterns. This is the opposite of
195195
* [testProject.grep](https://playwright.dev/docs/api/class-testproject#test-project-grep). Also available globally
196-
* and in the [command line](https://playwright.dev/docs/test-cli) with the `--grep-invert` option.
196+
* and in the [command line](https://playwright.dev/docs/test-cli) with the `--grep-invert` option. This filter and its command line
197+
* counterpart also applies to the setup files. If all
198+
* [testProject.setupMatch](https://playwright.dev/docs/api/class-testproject#test-project-setup-match) tests match
199+
* the filter Playwright **will** run all setup files before running the matching tests.
197200
*
198201
* `grepInvert` option is also useful for [tagging tests](https://playwright.dev/docs/test-annotations#tag-tests).
199202
*/
@@ -2826,6 +2829,35 @@ type ConnectOptions = {
28262829
timeout?: number;
28272830
};
28282831

2832+
/**
2833+
* Playwright Test provides a global `store` object for passing values between project setup and tests. It is an error
2834+
* to call store methods outside of setup and tests.
2835+
*
2836+
* ```js
2837+
* import { setup, store } from '@playwright/test';
2838+
*
2839+
* setup('sign in', async ({ page, context }) => {
2840+
* // Save signed-in state to an entry named 'github-test-user'.
2841+
* const contextState = await context.storageState();
2842+
* await store.set('test-user', contextState)
2843+
* });
2844+
* ```
2845+
*
2846+
*/
2847+
export interface TestStore {
2848+
/**
2849+
* Get named item from the store. Returns undefined if there is no value with given name.
2850+
* @param name Item name.
2851+
*/
2852+
get<T>(name: string): Promise<T | undefined>;
2853+
/**
2854+
* Set value to the store.
2855+
* @param name Item name.
2856+
* @param value Item value. The value must be serializable to JSON. Passing `undefined` deletes the entry with given name.
2857+
*/
2858+
set<T>(name: string, value: T | undefined): Promise<void>;
2859+
}
2860+
28292861
/**
28302862
* Playwright Test provides many options to configure test environment, [Browser], [BrowserContext] and more.
28312863
*
@@ -3062,6 +3094,16 @@ export interface PlaywrightTestOptions {
30623094
* Either a path to the file with saved storage, or an object with the following fields:
30633095
*/
30643096
storageState: StorageState | undefined;
3097+
/**
3098+
* Name of the [TestStore] entry that should be used to initialize
3099+
* [testOptions.storageState](https://playwright.dev/docs/api/class-testoptions#test-options-storage-state). The value
3100+
* must be written to the test storage before creation of a browser context that uses it (usually in
3101+
* [testProject.setupMatch](https://playwright.dev/docs/api/class-testproject#test-project-setup-match)). If both this
3102+
* property and
3103+
* [testOptions.storageState](https://playwright.dev/docs/api/class-testoptions#test-options-storage-state) are
3104+
* specified, this property will always take precedence.
3105+
*/
3106+
storageStateName: string | undefined;
30653107
/**
30663108
* Changes the timezone of the context. See
30673109
* [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;
33453387

33463388
export const _baseTest: TestType<{}, {}>;
33473389
export const expect: Expect;
3390+
export const store: TestStore;
33483391

33493392
// This is required to not export everything by default. See https://github.com/Microsoft/TypeScript/issues/19545#issuecomment-340490459
33503393
export {};
@@ -4639,7 +4682,10 @@ interface TestProject {
46394682
/**
46404683
* Filter to only run tests with a title **not** matching one of the patterns. This is the opposite of
46414684
* [testProject.grep](https://playwright.dev/docs/api/class-testproject#test-project-grep). Also available globally
4642-
* and in the [command line](https://playwright.dev/docs/test-cli) with the `--grep-invert` option.
4685+
* and in the [command line](https://playwright.dev/docs/test-cli) with the `--grep-invert` option. This filter and its command line
4686+
* counterpart also applies to the setup files. If all
4687+
* [testProject.setupMatch](https://playwright.dev/docs/api/class-testproject#test-project-setup-match) tests match
4688+
* the filter Playwright **will** run all setup files before running the matching tests.
46434689
*
46444690
* `grepInvert` option is also useful for [tagging tests](https://playwright.dev/docs/test-annotations#tag-tests).
46454691
*/
@@ -4655,6 +4701,24 @@ interface TestProject {
46554701
*/
46564702
name?: string;
46574703

4704+
/**
4705+
* Project setup files that will be executed before all tests in the project.
4706+
*
4707+
* **Details**
4708+
*
4709+
* If project setup fails the tests in this project will be skipped. All project setup files will run in every shard
4710+
* if the project is sharded. [testProject.grep](https://playwright.dev/docs/api/class-testproject#test-project-grep)
4711+
* and [testProject.grepInvert](https://playwright.dev/docs/api/class-testproject#test-project-grep-invert) and their
4712+
* command line counterparts also apply to the setup files. If such filters match only tests in the project,
4713+
* Playwright will run **all** setup files before running the matching tests.
4714+
*
4715+
* If there is a file that matches both
4716+
* [testProject.setupMatch](https://playwright.dev/docs/api/class-testproject#test-project-setup-match) and
4717+
* [testProject.testMatch](https://playwright.dev/docs/api/class-testproject#test-project-test-match) filters an error
4718+
* will be thrown.
4719+
*/
4720+
setupMatch?: string|RegExp|Array<string|RegExp>;
4721+
46584722
/**
46594723
* The base directory, relative to the config file, for snapshot files created with `toMatchSnapshot`. Defaults to
46604724
* [testProject.testDir](https://playwright.dev/docs/api/class-testproject#test-project-test-dir).

0 commit comments

Comments
 (0)