diff --git a/docs/src/auth.md b/docs/src/auth.md index 64e4855ade..1b29164141 100644 --- a/docs/src/auth.md +++ b/docs/src/auth.md @@ -177,7 +177,7 @@ in only once per project and then skip the log in step for all of the tests. Web apps use cookie-based or token-based authentication, where authenticated state is stored as [cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies) or in [local storage](https://developer.mozilla.org/en-US/docs/Web/API/Storage). Playwright provides [browserContext.storageState([options])](https://playwright.dev/docs/api/class-browsercontext#browser-context-storage-state) method that can be used to retrieve storage state from authenticated contexts and then create new contexts with prepopulated state. -You can run authentication steps once during the project [`property: TestProject.setup`] phase and save the context state into [TestStore]. The stored value can later be reused to automatically restore authenticated context state in every test of the project. This way the login will run once per project before all tests. +You can run authentication steps once during the project [`property: TestProject.setupMatch`] phase and save the context state into [TestStore]. The stored value can later be reused to automatically restore authenticated context state in every test of the project. This way the login will run once per project before all tests. Create a setup test that performs login and saves the context state into project store: diff --git a/docs/src/test-api/class-testoptions.md b/docs/src/test-api/class-testoptions.md index dbb94a34a2..7b022addb1 100644 --- a/docs/src/test-api/class-testoptions.md +++ b/docs/src/test-api/class-testoptions.md @@ -207,7 +207,7 @@ Learn more about [automatic screenshots](../test-configuration.md#automatic-scre - 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.setup`]). If both +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 diff --git a/docs/src/test-api/class-testproject.md b/docs/src/test-api/class-testproject.md index eeba8c06ba..bd9d9574ef 100644 --- a/docs/src/test-api/class-testproject.md +++ b/docs/src/test-api/class-testproject.md @@ -148,7 +148,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. This filter and its command line counterpart also applies to the setup files. If all [`property: TestProject.setup`] tests match the filter Playwright **will** run all setup files before running the matching tests. +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). @@ -164,13 +164,17 @@ 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 +## property: TestProject.setupMatch * since: v1.29 - 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.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. +Project setup files that will be executed before all tests in the project. -If there is a file that matches both [`property: TestProject.setup`] and [`property: TestProject.testMatch`] filters an error will be thrown. +**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 diff --git a/packages/playwright-test/src/loader.ts b/packages/playwright-test/src/loader.ts index 71a724b4c9..3675814467 100644 --- a/packages/playwright-test/src/loader.ts +++ b/packages/playwright-test/src/loader.ts @@ -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 _setup = takeFirst(projectConfig.setup, []); + const _setupMatch = takeFirst(projectConfig.setupMatch, []); const defaultSnapshotPathTemplate = '{snapshotDir}/{testFileDir}/{testFileName}-snapshots/{arg}{-projectName}{-snapshotSuffix}{ext}'; const snapshotPathTemplate = takeFirst(projectConfig.snapshotPathTemplate, config.snapshotPathTemplate, defaultSnapshotPathTemplate); @@ -292,7 +292,7 @@ export class Loader { metadata: takeFirst(projectConfig.metadata, config.metadata, undefined), name, testDir, - _setup, + _setupMatch, _respectGitIgnore: respectGitIgnore, snapshotDir, snapshotPathTemplate, @@ -615,7 +615,7 @@ function validateProject(file: string, project: Project, title: string) { throw errorWithFile(file, `${title}.testDir must be a string`); } - for (const prop of ['testIgnore', 'testMatch', 'setup'] 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)) { diff --git a/packages/playwright-test/src/runner.ts b/packages/playwright-test/src/runner.ts index dd7d241f4b..8fb2e239f8 100644 --- a/packages/playwright-test/src/runner.ts +++ b/packages/playwright-test/src/runner.ts @@ -244,7 +244,7 @@ export class Runner { const commandLineFileMatcher = commandLineFileFilters.length ? createFileMatcherFromFilters(commandLineFileFilters) : () => true; for (const project of projects) { const allFiles = await collectFiles(project.testDir, project._respectGitIgnore); - const setupMatch = createFileMatcher(project._setup); + const setupMatch = createFileMatcher(project._setupMatch); const testMatch = createFileMatcher(project.testMatch); const testIgnore = createFileMatcher(project.testIgnore); const testFiles = allFiles.filter(file => { diff --git a/packages/playwright-test/src/types.ts b/packages/playwright-test/src/types.ts index d7023df2ed..608fa22559 100644 --- a/packages/playwright-test/src/types.ts +++ b/packages/playwright-test/src/types.ts @@ -68,7 +68,7 @@ export interface FullProjectInternal extends FullProjectPublic { _fullyParallel: boolean; _expect: Project['expect']; _respectGitIgnore: boolean; - _setup: string | RegExp | (string | RegExp)[]; + _setupMatch: string | RegExp | (string | RegExp)[]; snapshotPathTemplate: string; } diff --git a/packages/playwright-test/types/test.d.ts b/packages/playwright-test/types/test.d.ts index 6628161de4..03a755e72e 100644 --- a/packages/playwright-test/types/test.d.ts +++ b/packages/playwright-test/types/test.d.ts @@ -195,8 +195,8 @@ export interface FullProject { * [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. This filter and its command line * counterpart also applies to the setup files. If all - * [testProject.setup](https://playwright.dev/docs/api/class-testproject#test-project-setup) tests match the filter - * Playwright **will** run all setup files before running the matching tests. + * [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). */ @@ -3111,8 +3111,9 @@ export interface PlaywrightTestOptions { * 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.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 + * [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; @@ -4734,8 +4735,8 @@ interface TestProject { * [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. This filter and its command line * counterpart also applies to the setup files. If all - * [testProject.setup](https://playwright.dev/docs/api/class-testproject#test-project-setup) tests match the filter - * Playwright **will** run all setup files before running the matching tests. + * [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). */ @@ -4752,19 +4753,22 @@ 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. - * [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. + * 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.setup](https://playwright.dev/docs/api/class-testproject#test-project-setup) and + * [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. */ - setup?: string|RegExp|Array; + setupMatch?: string|RegExp|Array; /** * The base directory, relative to the config file, for snapshot files created with `toMatchSnapshot`. Defaults to diff --git a/tests/playwright-test/config.spec.ts b/tests/playwright-test/config.spec.ts index 931c806174..b037f3ab1e 100644 --- a/tests/playwright-test/config.spec.ts +++ b/tests/playwright-test/config.spec.ts @@ -481,12 +481,12 @@ 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 }) => { +test('should throw when project.setupMatch has wrong type', async ({ runInlineTest }) => { const result = await runInlineTest({ 'playwright.config.ts': ` module.exports = { projects: [ - { name: 'a', setup: 100 }, + { name: 'a', setupMatch: 100 }, ], }; `, @@ -497,15 +497,15 @@ test('should throw when project.setup has wrong type', async ({ runInlineTest }) }); expect(result.exitCode).toBe(1); - expect(result.output).toContain(`Error: playwright.config.ts: config.projects[0].setup must be a string or a RegExp`); + expect(result.output).toContain(`Error: playwright.config.ts: config.projects[0].setupMatch must be a string or a RegExp`); }); -test('should throw when project.setup has wrong array type', async ({ runInlineTest }) => { +test('should throw when project.setupMatch has wrong array type', async ({ runInlineTest }) => { const result = await runInlineTest({ 'playwright.config.ts': ` module.exports = { projects: [ - { name: 'a', setup: [/100/, 100] }, + { name: 'a', setupMatch: [/100/, 100] }, ], }; `, @@ -516,5 +516,5 @@ test('should throw when project.setup has wrong array type', async ({ runInlineT }); 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`); + expect(result.output).toContain(`Error: playwright.config.ts: config.projects[0].setupMatch[1] must be a string or a RegExp`); }); diff --git a/tests/playwright-test/project-setup.spec.ts b/tests/playwright-test/project-setup.spec.ts index 3947681fb1..c590b98679 100644 --- a/tests/playwright-test/project-setup.spec.ts +++ b/tests/playwright-test/project-setup.spec.ts @@ -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'] + setupMatch: ['**/*.setup.ts'] }, }; const configWithFiles = createConfigWithProjects(['a'], testInfo, projectTemplates); @@ -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'] + setupMatch: ['**/*.setup.ts'] }, 'b': { - setup: /.*b.setup.ts/ + setupMatch: /.*b.setup.ts/ }, 'c': { - setup: '**/c.setup.ts' + setupMatch: '**/c.setup.ts' }, }; const configWithFiles = createConfigWithProjects(['a', 'b', 'c'], testInfo, projectTemplates); @@ -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'] + setupMatch: ['**/*.setup.ts'] }, 'b': { - setup: /.*b.setup.ts/ + setupMatch: /.*b.setup.ts/ }, }; const configWithFiles = createConfigWithProjects(['a', 'b', 'c'], testInfo, projectTemplates); @@ -162,7 +162,7 @@ test('should run setup in each project shard', async ({ runGroups }, testInfo) = projects: [ { name: 'p1', - setup: /.*.setup.ts/, + setupMatch: /.*.setup.ts/, }, ] };`, @@ -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$/, + setupMatch: /.*p1.setup.ts$/, testMatch: /.*a.test.ts/, }, { name: 'p2', - setup: /.*p2.setup.ts$/, + setupMatch: /.*p2.setup.ts$/, testMatch: /.*b.test.ts/, }, ] @@ -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/ + setupMatch: /.*a.setup.ts/ }, 'b': { - setup: /.*b.setup.ts/ + setupMatch: /.*b.setup.ts/ }, }; const configWithFiles = createConfigWithProjects(['a', 'b', 'c'], testInfo, projectTemplates); @@ -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$/, + setupMatch: /.*a.test.ts$/, testMatch: /.*a.test.ts$/, }, ] @@ -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$/, + setupMatch: /.*a.test.ts$/, testMatch: /.*noMatch.test.ts$/, }, { name: 'p2', - setup: /.*noMatch.test.ts$/, + setupMatch: /.*noMatch.test.ts$/, testMatch: /.*a.test.ts$/ }, ] @@ -336,12 +336,12 @@ test('list-files should enumerate setup files in same group', async ({ runComman projects: [ { name: 'p1', - setup: /.*a..setup.ts$/, + setupMatch: /.*a..setup.ts$/, testMatch: /.*a.test.ts$/, }, { name: 'p2', - setup: /.*b.setup.ts$/, + setupMatch: /.*b.setup.ts$/, testMatch: /.*b.test.ts$/ }, ] @@ -383,12 +383,12 @@ test('test --list should enumerate setup tests as regular ones', async ({ runCom projects: [ { name: 'p1', - setup: /.*a..setup.ts$/, + setupMatch: /.*a..setup.ts$/, testMatch: /.*a.test.ts$/, }, { name: 'p2', - setup: /.*b.setup.ts$/, + setupMatch: /.*b.setup.ts$/, testMatch: /.*b.test.ts$/ }, ] @@ -433,7 +433,7 @@ test('should allow .only in setup files', async ({ runGroups }, testInfo) => { projects: [ { name: 'p1', - setup: /.*.setup.ts/, + setupMatch: /.*.setup.ts/, }, ] };`, @@ -468,7 +468,7 @@ test('should allow describe.only in setup files', async ({ runGroups }, testInfo projects: [ { name: 'p1', - setup: /.*.setup.ts/, + setupMatch: /.*.setup.ts/, }, ] };`, @@ -505,7 +505,7 @@ test('should filter describe line in setup files', async ({ runGroups }, testInf projects: [ { name: 'p1', - setup: /.*.setup.ts/, + setupMatch: /.*.setup.ts/, }, ] };`, @@ -542,7 +542,7 @@ test('should allow .only in both setup and test files', async ({ runGroups }, te projects: [ { name: 'p1', - setup: /.*.setup.ts/, + setupMatch: /.*.setup.ts/, }, ] };`, @@ -574,7 +574,7 @@ test('should run full setup when there is test.only', async ({ runGroups }, test projects: [ { name: 'p1', - setup: /.*.setup.ts/, + setupMatch: /.*.setup.ts/, }, ] };`, @@ -613,11 +613,11 @@ test('should allow filtering setup by file:line', async ({ runGroups }, testInfo projects: [ { name: 'p1', - setup: /.*a.setup.ts/, + setupMatch: /.*a.setup.ts/, }, { name: 'p2', - setup: /.*b.setup.ts/, + setupMatch: /.*b.setup.ts/, }, ] };`, @@ -669,7 +669,7 @@ test('should support filters matching both setup and test', async ({ runGroups } projects: [ { name: 'p1', - setup: /.*.setup.ts/, + setupMatch: /.*.setup.ts/, }, ] };`, @@ -712,12 +712,12 @@ test('should run setup for a project if tests match only in another project', as { name: 'p1', testMatch: /.*a.test.ts/, - setup: /.*a.setup.ts/, + setupMatch: /.*a.setup.ts/, }, { name: 'p2', testMatch: /.*b.test.ts/, - setup: /.*b.setup.ts/, + setupMatch: /.*b.setup.ts/, }, ] };`, @@ -754,7 +754,7 @@ test('should run all setup files if only tests match filter', async ({ runGroups projects: [ { name: 'p1', - setup: /.*.setup.ts/, + setupMatch: /.*.setup.ts/, }, ] };`, @@ -791,7 +791,7 @@ test('should run all setup files if only tests match grep filter', async ({ runG projects: [ { name: 'p1', - setup: /.*.setup.ts/, + setupMatch: /.*.setup.ts/, }, ] };`, @@ -828,7 +828,7 @@ test('should apply project.grep filter to both setup and tests', async ({ runGro projects: [ { name: 'p1', - setup: /.*.setup.ts/, + setupMatch: /.*.setup.ts/, grep: /a.(test|setup).ts.*(test|setup)/, }, ] @@ -893,7 +893,7 @@ test('should prohibit test in setup files', async ({ runGroups }, testInfo) => { projects: [ { name: 'p1', - setup: /.*.setup.ts/, + setupMatch: /.*.setup.ts/, }, ] };`, @@ -915,7 +915,7 @@ test('should prohibit test hooks in setup files', async ({ runGroups }, testInfo projects: [ { name: 'p1', - setup: /.*.setup.ts/, + setupMatch: /.*.setup.ts/, }, ] };`, diff --git a/tests/playwright-test/store.spec.ts b/tests/playwright-test/store.spec.ts index 1a6d491bc4..87a3e18b99 100644 --- a/tests/playwright-test/store.spec.ts +++ b/tests/playwright-test/store.spec.ts @@ -48,7 +48,7 @@ test('should share store state between project setup and tests', async ({ runInl projects: [ { name: 'p1', - setup: /.*store.setup.ts/ + setupMatch: /.*store.setup.ts/ } ] }; @@ -125,11 +125,11 @@ test('should isolate store state between projects', async ({ runInlineTest }) => projects: [ { name: 'p1', - setup: /.*store.setup.ts/ + setupMatch: /.*store.setup.ts/ }, { name: 'p2', - setup: /.*store.setup.ts/ + setupMatch: /.*store.setup.ts/ } ] }; @@ -176,7 +176,7 @@ test('should load context storageState from store', async ({ runInlineTest, serv projects: [ { name: 'p1', - setup: /.*store.setup.ts/ + setupMatch: /.*store.setup.ts/ } ] }; @@ -225,7 +225,7 @@ test('should load storageStateName specified in the project config from store', projects: [ { name: 'p1', - setup: /.*store.setup.ts/, + setupMatch: /.*store.setup.ts/, use: { storageStateName: 'stateInStorage', }, @@ -272,7 +272,7 @@ test('should load storageStateName specified in the global config from store', a projects: [ { name: 'p1', - setup: /.*store.setup.ts/, + setupMatch: /.*store.setup.ts/, } ] };