diff --git a/docs/src/test-api/class-testconfig.md b/docs/src/test-api/class-testconfig.md index d013f5e4ea..de11e98575 100644 --- a/docs/src/test-api/class-testconfig.md +++ b/docs/src/test-api/class-testconfig.md @@ -110,9 +110,9 @@ export default defineConfig({ ## property: TestConfig.globalSetup * since: v1.10 -- type: ?<[string]> +- type: ?<[string]|[Array]<[string]>> -Path to the global setup file. This file will be required and run before all the tests. It must export a single function that takes a [FullConfig] argument. +Path to the global setup file. This file will be required and run before all the tests. It must export a single function that takes a [FullConfig] argument. Pass an array for multiple global setup files. Learn more about [global setup and teardown](../test-global-setup-teardown.md). @@ -128,9 +128,9 @@ export default defineConfig({ ## property: TestConfig.globalTeardown * since: v1.10 -- type: ?<[string]> +- type: ?<[string]|[Array]<[string]>> -Path to the global teardown file. This file will be required and run after all the tests. It must export a single function. See also [`property: TestConfig.globalSetup`]. +Path to the global teardown file. This file will be required and run after all the tests. It must export a single function. See also [`property: TestConfig.globalSetup`]. Pass an array for multiple global teardown files. Learn more about [global setup and teardown](../test-global-setup-teardown.md). diff --git a/packages/playwright/src/common/config.ts b/packages/playwright/src/common/config.ts index d7fb499645..759e23f1c3 100644 --- a/packages/playwright/src/common/config.ts +++ b/packages/playwright/src/common/config.ts @@ -57,6 +57,9 @@ export class FullConfigInternal { testIdMatcher?: Matcher; defineConfigWasUsed = false; + globalSetups: string[] = []; + globalTeardowns: string[] = []; + constructor(location: ConfigLocation, userConfig: Config, configCLIOverrides: ConfigCLIOverrides) { if (configCLIOverrides.projects && userConfig.projects) throw new Error(`Cannot use --browser option when configuration file defines projects. Specify browserName in the projects instead.`); @@ -70,13 +73,16 @@ export class FullConfigInternal { const privateConfiguration = (userConfig as any)['@playwright/test']; this.plugins = (privateConfiguration?.plugins || []).map((p: any) => ({ factory: p })); + this.globalSetups = (Array.isArray(userConfig.globalSetup) ? userConfig.globalSetup : [userConfig.globalSetup]).map(s => resolveScript(s, configDir)).filter((script): script is string => !!script); + this.globalTeardowns = (Array.isArray(userConfig.globalTeardown) ? userConfig.globalTeardown : [userConfig.globalTeardown]).map(s => resolveScript(s, configDir)).filter((script): script is string => !!script); + this.config = { configFile: resolvedConfigFile, rootDir: pathResolve(configDir, userConfig.testDir) || configDir, forbidOnly: takeFirst(configCLIOverrides.forbidOnly, userConfig.forbidOnly, false), fullyParallel: takeFirst(configCLIOverrides.fullyParallel, userConfig.fullyParallel, false), - globalSetup: takeFirst(resolveScript(userConfig.globalSetup, configDir), null), - globalTeardown: takeFirst(resolveScript(userConfig.globalTeardown, configDir), null), + globalSetup: takeFirst(...this.globalSetups, null), + globalTeardown: takeFirst(...this.globalTeardowns, null), globalTimeout: takeFirst(configCLIOverrides.globalTimeout, userConfig.globalTimeout, 0), grep: takeFirst(userConfig.grep, defaultGrep), grepInvert: takeFirst(userConfig.grepInvert, null), diff --git a/packages/playwright/src/runner/tasks.ts b/packages/playwright/src/runner/tasks.ts index 77d84419f4..25e33a7f2b 100644 --- a/packages/playwright/src/runner/tasks.ts +++ b/packages/playwright/src/runner/tasks.ts @@ -98,8 +98,11 @@ export function createGlobalSetupTasks(config: FullConfigInternal) { if (!config.configCLIOverrides.preserveOutputDir && !process.env.PW_TEST_NO_REMOVE_OUTPUT_DIRS) tasks.push(createRemoveOutputDirsTask()); tasks.push(...createPluginSetupTasks(config)); - if (config.config.globalSetup || config.config.globalTeardown) - tasks.push(createGlobalSetupTask()); + if (config.globalSetups.length || config.globalTeardowns.length) { + const length = Math.max(config.globalSetups.length, config.globalTeardowns.length); + for (let i = 0; i < length; i++) + tasks.push(createGlobalSetupTask(i, length)); + } return tasks; } @@ -161,15 +164,20 @@ function createPluginBeginTask(plugin: TestRunnerPluginRegistration): Task { +function createGlobalSetupTask(index: number, length: number): Task { let globalSetupResult: any; let globalSetupFinished = false; let teardownHook: any; + + let title = 'global setup'; + if (length > 1) + title += ` (${index + 1}/${length})`; + return { - title: 'global setup', + title, setup: async ({ config }) => { - const setupHook = config.config.globalSetup ? await loadGlobalHook(config, config.config.globalSetup) : undefined; - teardownHook = config.config.globalTeardown ? await loadGlobalHook(config, config.config.globalTeardown) : undefined; + const setupHook = config.globalSetups[index] ? await loadGlobalHook(config, config.globalSetups[index]) : undefined; + teardownHook = config.globalTeardowns[index] ? await loadGlobalHook(config, config.globalTeardowns[index]) : undefined; globalSetupResult = setupHook ? await setupHook(config.config) : undefined; globalSetupFinished = true; }, diff --git a/packages/playwright/types/test.d.ts b/packages/playwright/types/test.d.ts index 400d7cbcf3..e480a90936 100644 --- a/packages/playwright/types/test.d.ts +++ b/packages/playwright/types/test.d.ts @@ -1077,7 +1077,8 @@ interface TestConfig { /** * Path to the global setup file. This file will be required and run before all the tests. It must export a single - * function that takes a [FullConfig](https://playwright.dev/docs/api/class-fullconfig) argument. + * function that takes a [FullConfig](https://playwright.dev/docs/api/class-fullconfig) argument. Pass an array for + * multiple global setup files. * * Learn more about [global setup and teardown](https://playwright.dev/docs/test-global-setup-teardown). * @@ -1093,12 +1094,13 @@ interface TestConfig { * ``` * */ - globalSetup?: string; + globalSetup?: string|Array; /** * Path to the global teardown file. This file will be required and run after all the tests. It must export a single * function. See also - * [testConfig.globalSetup](https://playwright.dev/docs/api/class-testconfig#test-config-global-setup). + * [testConfig.globalSetup](https://playwright.dev/docs/api/class-testconfig#test-config-global-setup). Pass an array + * for multiple global teardown files. * * Learn more about [global setup and teardown](https://playwright.dev/docs/test-global-setup-teardown). * @@ -1114,7 +1116,7 @@ interface TestConfig { * ``` * */ - globalTeardown?: string; + globalTeardown?: string|Array; /** * Maximum time in milliseconds the whole test suite can run. Zero timeout (default) disables this behavior. Useful on