diff --git a/packages/playwright/src/common/config.ts b/packages/playwright/src/common/config.ts index 9b8be8e730..550f17d2f5 100644 --- a/packages/playwright/src/common/config.ts +++ b/packages/playwright/src/common/config.ts @@ -175,6 +175,7 @@ export class FullProjectInternal { this.snapshotPathTemplate = takeFirst(projectConfig.snapshotPathTemplate, config.snapshotPathTemplate, defaultSnapshotPathTemplate); this.project = { + globalTimeout: fullConfig.config.globalTimeout, grep: takeFirst(projectConfig.grep, config.grep, defaultGrep), grepInvert: takeFirst(projectConfig.grepInvert, config.grepInvert, null), outputDir: takeFirst(configCLIOverrides.outputDir, pathResolve(configDir, projectConfig.outputDir), pathResolve(configDir, config.outputDir), path.join(throwawayArtifactsPath, 'test-results')), diff --git a/packages/playwright/src/common/ipc.ts b/packages/playwright/src/common/ipc.ts index c1c7b2da25..bddfb62388 100644 --- a/packages/playwright/src/common/ipc.ts +++ b/packages/playwright/src/common/ipc.ts @@ -129,7 +129,7 @@ export type EnvProducedPayload = [string, string | null][]; export function serializeConfig(config: FullConfigInternal, passCompilationCache: boolean): SerializedConfig { const result: SerializedConfig = { - location: { configDir: config.configDir, resolvedConfigFile: config.config.configFile }, + location: { configDir: config.configDir, resolvedConfigFile: config.config.projects[0]?.configFile }, configCLIOverrides: config.configCLIOverrides, compilationCache: passCompilationCache ? serializeCompilationCache() : undefined, }; diff --git a/packages/playwright/src/reporters/base.ts b/packages/playwright/src/reporters/base.ts index fff2703208..a66b9ea8fa 100644 --- a/packages/playwright/src/reporters/base.ts +++ b/packages/playwright/src/reporters/base.ts @@ -153,7 +153,7 @@ export class BaseReporter implements ReporterV2 { } protected generateStartingMessage() { - const jobs = this.config.metadata.actualWorkers ?? this.config.workers; + const jobs = this.config.metadata.actualWorkers ?? this.config.projects[0].workers; const shardDetails = this.config.shard ? `, shard ${this.config.shard.current} of ${this.config.shard.total}` : ''; if (!this.totalTestCount) return ''; @@ -195,7 +195,7 @@ export class BaseReporter implements ReporterV2 { if (expected) tokens.push(colors.green(` ${expected} passed`) + colors.dim(` (${milliseconds(this.result.duration)})`)); if (this.result.status === 'timedout') - tokens.push(colors.red(` Timed out waiting ${this.config.globalTimeout / 1000}s for the entire test run`)); + tokens.push(colors.red(` Timed out waiting ${this.config.projects[0].globalTimeout / 1000}s for the entire test run`)); if (fatalErrors.length && expected + unexpected.length + interrupted.length + flaky.length > 0) tokens.push(colors.red(` ${fatalErrors.length === 1 ? '1 error was not a part of any test' : fatalErrors.length + ' errors were not a part of any test'}, see above for details`)); diff --git a/packages/playwright/src/reporters/json.ts b/packages/playwright/src/reporters/json.ts index 36ba678258..5ba8791cb0 100644 --- a/packages/playwright/src/reporters/json.ts +++ b/packages/playwright/src/reporters/json.ts @@ -231,8 +231,8 @@ class JSONReporter extends EmptyReporter { async function outputReport(report: JSONReport, config: FullConfig, outputFile: string | undefined) { const reportString = JSON.stringify(report, undefined, 2); if (outputFile) { - assert(config.configFile || path.isAbsolute(outputFile), 'Expected fully resolved path if not using config file.'); - outputFile = config.configFile ? path.resolve(path.dirname(config.configFile), outputFile) : outputFile; + assert(config.projects[0]?.configFile || path.isAbsolute(outputFile), 'Expected fully resolved path if not using config file.'); + outputFile = config.projects[0]?.configFile ? path.resolve(path.dirname(config.projects[0]?.configFile), outputFile) : outputFile; await fs.promises.mkdir(path.dirname(outputFile), { recursive: true }); await fs.promises.writeFile(outputFile, reportString); } else { diff --git a/packages/playwright/src/reporters/junit.ts b/packages/playwright/src/reporters/junit.ts index cc526abb93..1eaa89003a 100644 --- a/packages/playwright/src/reporters/junit.ts +++ b/packages/playwright/src/reporters/junit.ts @@ -55,8 +55,8 @@ class JUnitReporter extends EmptyReporter { this.timestamp = new Date(); this.startTime = monotonicTime(); if (this.outputFile) { - assert(this.config.configFile || path.isAbsolute(this.outputFile), 'Expected fully resolved path if not using config file.'); - this.resolvedOutputFile = this.config.configFile ? path.resolve(path.dirname(this.config.configFile), this.outputFile) : this.outputFile; + assert(this.config.projects[0]?.configFile || path.isAbsolute(this.outputFile), 'Expected fully resolved path if not using config file.'); + this.resolvedOutputFile = this.config.projects[0]?.configFile ? path.resolve(path.dirname(this.config.projects[0]?.configFile), this.outputFile) : this.outputFile; } } diff --git a/packages/playwright/src/runner/loadUtils.ts b/packages/playwright/src/runner/loadUtils.ts index b138b6fae4..d7d8ca76af 100644 --- a/packages/playwright/src/runner/loadUtils.ts +++ b/packages/playwright/src/runner/loadUtils.ts @@ -169,7 +169,7 @@ export async function createRootSuite(testRun: TestRun, errors: TestError[], sho if (config.config.forbidOnly) { const onlyTestsAndSuites = rootSuite._getOnlyItems(); if (onlyTestsAndSuites.length > 0) { - const configFilePath = config.config.configFile ? path.relative(config.config.rootDir, config.config.configFile) : undefined; + const configFilePath = config.config.projects[0]?.configFile ? path.relative(config.config.rootDir, config.config.projects[0]?.configFile) : undefined; errors.push(...createForbidOnlyErrors(onlyTestsAndSuites, config.configCLIOverrides.forbidOnly, configFilePath)); } } diff --git a/packages/playwright/src/runner/runner.ts b/packages/playwright/src/runner/runner.ts index 68110bd5ec..5353f313aa 100644 --- a/packages/playwright/src/runner/runner.ts +++ b/packages/playwright/src/runner/runner.ts @@ -74,7 +74,7 @@ export class Runner { async runAllTests(): Promise { const config = this._config; const listOnly = config.cliListOnly; - const deadline = config.config.globalTimeout ? monotonicTime() + config.config.globalTimeout : 0; + const deadline = config.config.projects[0].globalTimeout ? monotonicTime() + config.config.projects[0].globalTimeout : 0; // Legacy webServer support. webServerPluginsForConfig(config).forEach(p => config.plugins.push({ factory: p })); diff --git a/packages/playwright/src/runner/tasks.ts b/packages/playwright/src/runner/tasks.ts index 46d0ae4181..48e8e786e3 100644 --- a/packages/playwright/src/runner/tasks.ts +++ b/packages/playwright/src/runner/tasks.ts @@ -63,7 +63,7 @@ export class TestRun { } export function createTaskRunner(config: FullConfigInternal, reporter: ReporterV2): TaskRunner { - const taskRunner = new TaskRunner(reporter, config.config.globalTimeout); + const taskRunner = new TaskRunner(reporter, config.config.projects[0].globalTimeout); addGlobalSetupTasks(taskRunner, config); taskRunner.addTask('load tests', createLoadTask('in-process', { filterOnly: true, failOnLoadErrors: true })); addRunTasks(taskRunner, config); @@ -109,14 +109,14 @@ function addRunTasks(taskRunner: TaskRunner, config: FullConfigInternal } export function createTaskRunnerForList(config: FullConfigInternal, reporter: ReporterV2, mode: 'in-process' | 'out-of-process', options: { failOnLoadErrors: boolean }): TaskRunner { - const taskRunner = new TaskRunner(reporter, config.config.globalTimeout); + const taskRunner = new TaskRunner(reporter, config.config.projects[0].globalTimeout); taskRunner.addTask('load tests', createLoadTask(mode, { ...options, filterOnly: false })); taskRunner.addTask('report begin', createReportBeginTask()); return taskRunner; } export function createTaskRunnerForListFiles(config: FullConfigInternal, reporter: ReporterV2): TaskRunner { - const taskRunner = new TaskRunner(reporter, config.config.globalTimeout); + const taskRunner = new TaskRunner(reporter, config.config.projects[0].globalTimeout); taskRunner.addTask('load tests', createListFilesTask()); taskRunner.addTask('report begin', createReportBeginTask()); return taskRunner; @@ -283,7 +283,7 @@ function createPhasesTask(): Task { testRun.phases.push(phase); for (const project of phaseProjects) { const projectSuite = projectToSuite.get(project)!; - const testGroups = createTestGroups(projectSuite, testRun.config.config.workers); + const testGroups = createTestGroups(projectSuite, project.project.workers); phase.projects.push({ project, projectSuite, testGroups }); testGroupsInPhase += testGroups.length; } diff --git a/packages/trace-viewer/src/ui/uiModeFiltersView.tsx b/packages/trace-viewer/src/ui/uiModeFiltersView.tsx index a6cff6ecf8..5d0596bd1d 100644 --- a/packages/trace-viewer/src/ui/uiModeFiltersView.tsx +++ b/packages/trace-viewer/src/ui/uiModeFiltersView.tsx @@ -80,7 +80,7 @@ export const FiltersView: React.FC<{ const copy = new Map(projectFilters); copy.set(projectName, !copy.get(projectName)); setProjectFilters(copy); - const configFile = testModel?.config?.configFile; + const configFile = testModel?.config?.projects[0]?.configFile; if (configFile) settings.setObject(configFile + ':projects', [...copy.entries()].filter(([_, v]) => v).map(([k]) => k)); }}/> diff --git a/packages/trace-viewer/src/ui/uiModeView.tsx b/packages/trace-viewer/src/ui/uiModeView.tsx index 03abb468ab..6a269b5371 100644 --- a/packages/trace-viewer/src/ui/uiModeView.tsx +++ b/packages/trace-viewer/src/ui/uiModeView.tsx @@ -202,7 +202,7 @@ export const UIModeView: React.FC<{}> = ({ return; const { config, rootSuite } = testModel; - const selectedProjects = config.configFile ? settings.getObject(config.configFile + ':projects', undefined) : undefined; + const selectedProjects = config.projects[0]?.configFile ? settings.getObject(config.projects[0]?.configFile + ':projects', undefined) : undefined; const newFilter = new Map(projectFilters); for (const projectName of newFilter.keys()) { if (!rootSuite.suites.find(s => s.title === projectName))