Skip to content

Commit

Permalink
Revert "chore: hide TestProject.setup and TestInfo.storage (#18800)" (#…
Browse files Browse the repository at this point in the history
…18836)

Now that the patch has been cherry-picked to the release branch
reverting it on main.

This reverts commit 6deba5d.
  • Loading branch information
yury-s committed Nov 16, 2022
1 parent 7c65b5d commit 05fb3e6
Show file tree
Hide file tree
Showing 13 changed files with 209 additions and 57 deletions.
36 changes: 36 additions & 0 deletions docs/src/test-api/class-storage.md
@@ -0,0 +1,36 @@
# class: Storage
* since: v1.28
* langs: js

Playwright Test provides a [`method: TestInfo.storage`] object for passing values between project setup and tests.
TODO: examples

## async method: Storage.get
* since: v1.28
- returns: <[any]>

Get named item from the storage. Returns undefined if there is no value with given name.

### param: Storage.get.name
* since: v1.28
- `name` <[string]>

Item name.

## async method: Storage.set
* since: v1.28

Set value to the storage.

### param: Storage.set.name
* since: v1.28
- `name` <[string]>

Item name.

### param: Storage.set.value
* since: v1.28
- `value` <[any]>

Item value. The value must be serializable to JSON. Passing `undefined` deletes the entry with given name.

6 changes: 6 additions & 0 deletions docs/src/test-api/class-testinfo.md
Expand Up @@ -505,6 +505,12 @@ Output written to `process.stderr` or `console.error` during the test execution.

Output written to `process.stdout` or `console.log` during the test execution.

## method: TestInfo.storage
* since: v1.28
- returns: <[Storage]>

Returns a [Storage] instance for the currently running project.

## property: TestInfo.timeout
* since: v1.10
- type: <[int]>
Expand Down
8 changes: 8 additions & 0 deletions docs/src/test-api/class-testoptions.md
Expand Up @@ -202,6 +202,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.28
- type: <[string]>

Name of the [Storage] entry that should be used to initialize [`property: TestOptions.storageState`]. The value must be
written to the storage before creatiion of a browser context that uses it (usually in [`property: TestProject.setup`]). If both
this property and [`property: TestOptions.storageState`] are specified, this property will always take precedence.

## property: TestOptions.testIdAttribute
* since: v1.27

Expand Down
6 changes: 6 additions & 0 deletions docs/src/test-api/class-testproject.md
Expand Up @@ -162,6 +162,12 @@ 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.setup
* since: v1.28
- type: ?<[string]|[RegExp]|[Array]<[string]|[RegExp]>>

Project setup files that would be executed before all tests in the project. 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.snapshotDir
* since: v1.10
- type: ?<[string]>
Expand Down
11 changes: 5 additions & 6 deletions packages/playwright-test/src/index.ts
Expand Up @@ -49,7 +49,6 @@ type TestFixtures = PlaywrightTestArgs & PlaywrightTestOptions & {
};
type WorkerFixtures = PlaywrightWorkerArgs & PlaywrightWorkerOptions & {
_browserOptions: LaunchOptions;
_storageStateName: string | undefined;
_artifactsDir: () => string;
_snapshotSuffix: string;
};
Expand Down Expand Up @@ -152,7 +151,7 @@ export const test = _baseTest.extend<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, scope: 'worker' }],
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 }],
Expand Down Expand Up @@ -182,7 +181,7 @@ export const test = _baseTest.extend<TestFixtures, WorkerFixtures>({
permissions,
proxy,
storageState,
_storageStateName,
storageStateName,
viewport,
timezoneId,
userAgent,
Expand Down Expand Up @@ -221,10 +220,10 @@ export const test = _baseTest.extend<TestFixtures, WorkerFixtures>({
options.permissions = permissions;
if (proxy !== undefined)
options.proxy = proxy;
if (_storageStateName !== undefined) {
const value = await (test.info() as TestInfoImpl)._storage().get(_storageStateName);
if (storageStateName !== undefined) {
const value = await test.info().storage().get(storageStateName);
if (!value)
throw new Error(`Cannot find value in the storage for storageStateName: "${_storageStateName}"`);
throw new Error(`Cannot find value in the storage for storageStateName: "${storageStateName}"`);
options.storageState = value as any;
} else if (storageState !== undefined) {
options.storageState = storageState;
Expand Down
4 changes: 2 additions & 2 deletions packages/playwright-test/src/loader.ts
Expand Up @@ -274,7 +274,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 _setup = takeFirst((projectConfig as any)._setup, []);
const _setup = takeFirst(projectConfig.setup, []);

const defaultSnapshotPathTemplate = '{snapshotDir}/{testFileDir}/{testFileName}-snapshots/{arg}{-projectName}{-snapshotSuffix}{ext}';
const snapshotPathTemplate = takeFirst(projectConfig.snapshotPathTemplate, config.snapshotPathTemplate, defaultSnapshotPathTemplate);
Expand Down Expand Up @@ -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', 'setup'] as const) {
if (prop in project && project[prop] !== undefined) {
const value = project[prop];
if (Array.isArray(value)) {
Expand Down
12 changes: 6 additions & 6 deletions packages/playwright-test/src/testInfo.ts
Expand Up @@ -17,7 +17,7 @@
import fs from 'fs';
import path from 'path';
import { monotonicTime } from 'playwright-core/lib/utils';
import type { TestError, TestInfo, TestStatus } from '../types/test';
import type { Storage, TestError, TestInfo, TestStatus } from '../types/test';
import type { WorkerInitParams } from './ipc';
import type { Loader } from './loader';
import type { TestCase } from './test';
Expand Down Expand Up @@ -60,7 +60,7 @@ export class TestInfoImpl implements TestInfo {
readonly snapshotDir: string;
errors: TestError[] = [];
currentStep: TestStepInternal | undefined;
private readonly _testStorage: JsonStorage;
private readonly _storage: JsonStorage;

get error(): TestError | undefined {
return this.errors[0];
Expand Down Expand Up @@ -108,7 +108,7 @@ export class TestInfoImpl implements TestInfo {
this.expectedStatus = test.expectedStatus;

this._timeoutManager = new TimeoutManager(this.project.timeout);
this._testStorage = new JsonStorage(this);
this._storage = new JsonStorage(this);

this.outputDir = (() => {
const relativeTestFilePath = path.relative(this.project.testDir, test._requireFile.replace(/\.(spec|test)\.(js|ts|mjs)$/, ''));
Expand Down Expand Up @@ -281,12 +281,12 @@ export class TestInfoImpl implements TestInfo {
this._timeoutManager.setTimeout(timeout);
}

_storage() {
return this._testStorage;
storage() {
return this._storage;
}
}

class JsonStorage {
class JsonStorage implements Storage {
constructor(private _testInfo: TestInfoImpl) {
}

Expand Down
38 changes: 38 additions & 0 deletions packages/playwright-test/types/test.d.ts
Expand Up @@ -1849,6 +1849,11 @@ export interface TestInfo {
*/
stdout: Array<string|Buffer>;

/**
* Returns a [Storage] instance for the currently running project.
*/
storage(): Storage;

/**
* Timeout in milliseconds for the currently running test. Zero means no timeout. Learn more about
* [various timeouts](https://playwright.dev/docs/test-timeouts).
Expand Down Expand Up @@ -2777,6 +2782,24 @@ type ConnectOptions = {
timeout?: number;
};

/**
* Playwright Test provides a [testInfo.storage()](https://playwright.dev/docs/api/class-testinfo#test-info-storage) object
* for passing values between project setup and tests. TODO: examples
*/
export interface Storage {
/**
* Get named item from the storage. Returns undefined if there is no value with given name.
* @param name Item name.
*/
get<T>(name: string): Promise<T | undefined>;
/**
* Set value to the storage.
* @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.
*
Expand Down Expand Up @@ -3011,6 +3034,15 @@ 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 [Storage] 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 storage before creatiion of a browser context that uses it (usually in
* [testProject.setup](https://playwright.dev/docs/api/class-testproject#test-project-setup)). 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)
Expand Down Expand Up @@ -4523,6 +4555,12 @@ interface TestProject {
*/
name?: string;

/**
* Project setup files that would be executed before all tests in the project. 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.
*/
setup?: 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).
Expand Down
38 changes: 38 additions & 0 deletions tests/playwright-test/config.spec.ts
Expand Up @@ -480,3 +480,41 @@ test('should have correct types for the config', async ({ runTSC }) => {
});
expect(result.exitCode).toBe(0);
});

test('should throw when project.setup has wrong type', async ({ runInlineTest }) => {
const result = await runInlineTest({
'playwright.config.ts': `
module.exports = {
projects: [
{ name: 'a', setup: 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].setup must be a string or a RegExp`);
});

test('should throw when project.setup has wrong array type', async ({ runInlineTest }) => {
const result = await runInlineTest({
'playwright.config.ts': `
module.exports = {
projects: [
{ name: 'a', setup: [/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].setup[1] must be a string or a RegExp`);
});
28 changes: 14 additions & 14 deletions tests/playwright-test/project-setup.spec.ts
Expand Up @@ -98,7 +98,7 @@ function expectFilesRunBefore(timeline: Timeline, before: string[], after: strin
test('should work for one project', async ({ runGroups }, testInfo) => {
const projectTemplates = {
'a': {
_setup: ['**/*.setup.ts']
setup: ['**/*.setup.ts']
},
};
const configWithFiles = createConfigWithProjects(['a'], testInfo, projectTemplates);
Expand All @@ -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': {
_setup: ['**/*.setup.ts']
setup: ['**/*.setup.ts']
},
'b': {
_setup: /.*b.setup.ts/
setup: /.*b.setup.ts/
},
'c': {
_setup: '**/c.setup.ts'
setup: '**/c.setup.ts'
},
};
const configWithFiles = createConfigWithProjects(['a', 'b', 'c'], testInfo, projectTemplates);
Expand All @@ -134,10 +134,10 @@ test('should work for several projects', async ({ runGroups }, testInfo) => {
test('should stop project if setup fails', async ({ runGroups }, testInfo) => {
const projectTemplates = {
'a': {
_setup: ['**/*.setup.ts']
setup: ['**/*.setup.ts']
},
'b': {
_setup: /.*b.setup.ts/
setup: /.*b.setup.ts/
},
};
const configWithFiles = createConfigWithProjects(['a', 'b', 'c'], testInfo, projectTemplates);
Expand All @@ -162,7 +162,7 @@ test('should run setup in each project shard', async ({ runGroups }, testInfo) =
projects: [
{
name: 'p1',
_setup: /.*.setup.ts/,
setup: /.*.setup.ts/,
},
]
};`,
Expand Down Expand Up @@ -210,12 +210,12 @@ test('should run setup only for projects that have tests in the shard', async ({
projects: [
{
name: 'p1',
_setup: /.*p1.setup.ts$/,
setup: /.*p1.setup.ts$/,
testMatch: /.*a.test.ts/,
},
{
name: 'p2',
_setup: /.*p2.setup.ts$/,
setup: /.*p2.setup.ts$/,
testMatch: /.*b.test.ts/,
},
]
Expand Down Expand Up @@ -265,10 +265,10 @@ test('should run setup only for projects that have tests in the shard', async ({
test('--project only runs setup from that project;', async ({ runGroups }, testInfo) => {
const projectTemplates = {
'a': {
_setup: /.*a.setup.ts/
setup: /.*a.setup.ts/
},
'b': {
_setup: /.*b.setup.ts/
setup: /.*b.setup.ts/
},
};
const configWithFiles = createConfigWithProjects(['a', 'b', 'c'], testInfo, projectTemplates);
Expand All @@ -285,7 +285,7 @@ test('same file cannot be a setup and a test in the same project', async ({ runG
projects: [
{
name: 'p1',
_setup: /.*a.test.ts$/,
setup: /.*a.test.ts$/,
testMatch: /.*a.test.ts$/,
},
]
Expand All @@ -308,12 +308,12 @@ test('same file cannot be a setup and a test in different projects', async ({ ru
projects: [
{
name: 'p1',
_setup: /.*a.test.ts$/,
setup: /.*a.test.ts$/,
testMatch: /.*noMatch.test.ts$/,
},
{
name: 'p2',
_setup: /.*noMatch.test.ts$/,
setup: /.*noMatch.test.ts$/,
testMatch: /.*a.test.ts$/
},
]
Expand Down

0 comments on commit 05fb3e6

Please sign in to comment.