diff --git a/packages/playwright-test/src/cli.ts b/packages/playwright-test/src/cli.ts index abee986ebd..353ff563b9 100644 --- a/packages/playwright-test/src/cli.ts +++ b/packages/playwright-test/src/cli.ts @@ -159,7 +159,7 @@ async function runTests(args: string[], opts: { [key: string]: any }) { configLoader.ignoreProjectDependencies(); const config = configLoader.fullConfig(); - config._internal.testFileFilters = args.map(arg => { + config._internal.cliFileFilters = args.map(arg => { const match = /^(.*?):(\d+):?(\d+)?$/.exec(arg); return { re: forceRegExp(match ? match[1] : arg), @@ -169,9 +169,9 @@ async function runTests(args: string[], opts: { [key: string]: any }) { }); const grepMatcher = opts.grep ? createTitleMatcher(forceRegExp(opts.grep)) : () => true; const grepInvertMatcher = opts.grepInvert ? createTitleMatcher(forceRegExp(opts.grepInvert)) : () => false; - config._internal.testTitleMatcher = (title: string) => !grepInvertMatcher(title) && grepMatcher(title); + config._internal.cliTitleMatcher = (title: string) => !grepInvertMatcher(title) && grepMatcher(title); config._internal.listOnly = !!opts.list; - config._internal.projectFilter = opts.project || undefined; + config._internal.cliProjectFilter = opts.project || undefined; config._internal.passWithNoTests = !!opts.passWithNoTests; const runner = new Runner(config); diff --git a/packages/playwright-test/src/common/compilationCache.ts b/packages/playwright-test/src/common/compilationCache.ts index 20463eb959..44e23a9975 100644 --- a/packages/playwright-test/src/common/compilationCache.ts +++ b/packages/playwright-test/src/common/compilationCache.ts @@ -94,7 +94,12 @@ export function serializeCompilationCache(): any { }; } -export function initializeCompilationCache(payload: any) { +export function clearCompilationCache() { + sourceMaps.clear(); + memoryCache.clear(); +} + +export function addToCompilationCache(payload: any) { for (const entry of payload.sourceMaps) sourceMaps.set(entry[0], entry[1]); for (const entry of payload.memoryCache) diff --git a/packages/playwright-test/src/common/configLoader.ts b/packages/playwright-test/src/common/configLoader.ts index 1a7130e1a9..87ffb6473c 100644 --- a/packages/playwright-test/src/common/configLoader.ts +++ b/packages/playwright-test/src/common/configLoader.ts @@ -450,8 +450,8 @@ export const baseFullConfig: FullConfigInternal = { maxConcurrentTestGroups: 0, ignoreSnapshots: false, plugins: [], - testTitleMatcher: () => true, - testFileFilters: [], + cliTitleMatcher: () => true, + cliFileFilters: [], listOnly: false, } }; diff --git a/packages/playwright-test/src/common/types.ts b/packages/playwright-test/src/common/types.ts index e5453b9533..ae8c712696 100644 --- a/packages/playwright-test/src/common/types.ts +++ b/packages/playwright-test/src/common/types.ts @@ -50,9 +50,9 @@ type ConfigInternal = { webServers: Exclude[]; plugins: TestRunnerPluginRegistration[]; listOnly: boolean; - testFileFilters: TestFileFilter[]; - testTitleMatcher: Matcher; - projectFilter?: string[]; + cliFileFilters: TestFileFilter[]; + cliTitleMatcher: Matcher; + cliProjectFilter?: string[]; passWithNoTests?: boolean; }; diff --git a/packages/playwright-test/src/loader/loaderMain.ts b/packages/playwright-test/src/loader/loaderMain.ts index 9b317a516d..a3bf5ac27a 100644 --- a/packages/playwright-test/src/loader/loaderMain.ts +++ b/packages/playwright-test/src/loader/loaderMain.ts @@ -20,6 +20,7 @@ import { ProcessRunner } from '../common/process'; import type { FullConfigInternal } from '../common/types'; import { loadTestFile } from '../common/testLoader'; import type { TestError } from '../../reporter'; +import { addToCompilationCache, serializeCompilationCache } from '../common/compilationCache'; export class LoaderMain extends ProcessRunner { private _serializedConfig: SerializedConfig; @@ -27,6 +28,7 @@ export class LoaderMain extends ProcessRunner { constructor(serializedConfig: SerializedConfig) { super(); + addToCompilationCache(serializedConfig.compilationCache); this._serializedConfig = serializedConfig; } @@ -39,8 +41,12 @@ export class LoaderMain extends ProcessRunner { async loadTestFile(params: { file: string }) { const testErrors: TestError[] = []; const config = await this._config(); - const rootSuite = await loadTestFile(params.file, config.rootDir, testErrors); - return { rootSuite: rootSuite._deepSerialize(), testErrors }; + const fileSuite = await loadTestFile(params.file, config.rootDir, testErrors); + return { fileSuite: fileSuite._deepSerialize(), testErrors }; + } + + async serializeCompilationCache() { + return serializeCompilationCache(); } } diff --git a/packages/playwright-test/src/runner/loadUtils.ts b/packages/playwright-test/src/runner/loadUtils.ts index e0acf4695e..2700476002 100644 --- a/packages/playwright-test/src/runner/loadUtils.ts +++ b/packages/playwright-test/src/runner/loadUtils.ts @@ -21,15 +21,15 @@ import type { LoaderHost } from './loaderHost'; import { Suite } from '../common/test'; import type { TestCase } from '../common/test'; import type { FullConfigInternal, FullProjectInternal } from '../common/types'; -import { createFileMatcherFromFilters, createTitleMatcher, errorWithFile } from '../util'; +import { createTitleMatcher, errorWithFile } from '../util'; import type { Matcher, TestFileFilter } from '../util'; import { buildProjectsClosure, collectFilesForProject, filterProjects } from './projectUtils'; import { requireOrImport } from '../common/transform'; import { buildFileSuiteForProject, filterByFocusedLine, filterOnly, filterTestsRemoveEmptySuites } from '../common/suiteUtils'; import { filterForShard } from './testGroups'; -export async function loadAllTests(config: FullConfigInternal, errors: TestError[]): Promise { - const projects = filterProjects(config.projects, config._internal.projectFilter); +export async function loadAllTests(config: FullConfigInternal, projectsToIgnore: Set, fileMatcher: Matcher, errors: TestError[]): Promise { + const projects = filterProjects(config.projects, config._internal.cliProjectFilter); let filesToRunByProject = new Map(); let topLevelProjects: FullProjectInternal[]; @@ -41,14 +41,15 @@ export async function loadAllTests(config: FullConfigInternal, errors: TestError // First collect all files for the projects in the command line, don't apply any file filters. const allFilesForProject = new Map(); for (const project of projects) { + if (projectsToIgnore.has(project)) + continue; const files = await collectFilesForProject(project, fsCache); allFilesForProject.set(project, files); } // Filter files based on the file filters, eliminate the empty projects. - const commandLineFileMatcher = config._internal.testFileFilters.length ? createFileMatcherFromFilters(config._internal.testFileFilters) : null; for (const [project, files] of allFilesForProject) { - const filteredFiles = commandLineFileMatcher ? files.filter(commandLineFileMatcher) : files; + const filteredFiles = files.filter(fileMatcher); if (filteredFiles.length) filesToRunByProject.set(project, filteredFiles); } @@ -67,6 +68,9 @@ export async function loadAllTests(config: FullConfigInternal, errors: TestError topLevelProjects = filteredProjectClosure.filter(p => p._internal.type === 'top-level'); dependencyProjects = filteredProjectClosure.filter(p => p._internal.type === 'dependency'); + topLevelProjects = topLevelProjects.filter(p => !projectsToIgnore.has(p)); + dependencyProjects = dependencyProjects.filter(p => !projectsToIgnore.has(p)); + // (Re-)add all files for dependent projects, disregard filters. for (const project of dependencyProjects) { const files = allFilesForProject.get(project) || await collectFilesForProject(project, fsCache); @@ -113,7 +117,7 @@ export async function loadAllTests(config: FullConfigInternal, errors: TestError // Prepend the projects that are dependencies. for (const project of dependencyProjects) { - const projectSuite = await createProjectSuite(fileSuits, project, { testFileFilters: [], testTitleMatcher: undefined }, filesToRunByProject.get(project)!); + const projectSuite = await createProjectSuite(fileSuits, project, { cliFileFilters: [], cliTitleMatcher: undefined }, filesToRunByProject.get(project)!); if (projectSuite) rootSuite._prependSuite(projectSuite); } @@ -121,7 +125,7 @@ export async function loadAllTests(config: FullConfigInternal, errors: TestError return rootSuite; } -async function createProjectSuite(fileSuits: Suite[], project: FullProjectInternal, options: { testFileFilters: TestFileFilter[], testTitleMatcher?: Matcher }, files: string[]): Promise { +async function createProjectSuite(fileSuits: Suite[], project: FullProjectInternal, options: { cliFileFilters: TestFileFilter[], cliTitleMatcher?: Matcher }, files: string[]): Promise { const fileSuitesMap = new Map(); for (const fileSuite of fileSuits) fileSuitesMap.set(fileSuite._requireFile, fileSuite); @@ -140,7 +144,7 @@ async function createProjectSuite(fileSuits: Suite[], project: FullProjectIntern } } // Filter tests to respect line/column filter. - filterByFocusedLine(projectSuite, options.testFileFilters); + filterByFocusedLine(projectSuite, options.cliFileFilters); const grepMatcher = createTitleMatcher(project.grep); const grepInvertMatcher = project.grepInvert ? createTitleMatcher(project.grepInvert) : null; @@ -149,7 +153,7 @@ async function createProjectSuite(fileSuits: Suite[], project: FullProjectIntern const grepTitle = test.titlePath().join(' '); if (grepInvertMatcher?.(grepTitle)) return false; - return grepMatcher(grepTitle) && (!options.testTitleMatcher || options.testTitleMatcher(grepTitle)); + return grepMatcher(grepTitle) && (!options.cliTitleMatcher || options.cliTitleMatcher(grepTitle)); }; if (filterTestsRemoveEmptySuites(projectSuite, titleMatcher)) diff --git a/packages/playwright-test/src/runner/loaderHost.ts b/packages/playwright-test/src/runner/loaderHost.ts index 84bd78d690..399659a6b3 100644 --- a/packages/playwright-test/src/runner/loaderHost.ts +++ b/packages/playwright-test/src/runner/loaderHost.ts @@ -22,6 +22,7 @@ import { loadTestFile } from '../common/testLoader'; import type { LoadError } from '../common/fixtures'; import type { FullConfigInternal } from '../common/types'; import { PoolBuilder } from '../common/poolBuilder'; +import { addToCompilationCache } from '../common/compilationCache'; export abstract class LoaderHost { protected _config: FullConfigInternal; @@ -56,18 +57,20 @@ export class OutOfProcessLoaderHost extends LoaderHost { constructor(config: FullConfigInternal) { super(config); - this._processHost = new ProcessHost(require.resolve('../loaderMain.js'), 'loader'); + this._processHost = new ProcessHost(require.resolve('../loader/loaderMain.js'), 'loader'); this._startPromise = this._processHost.startRunner(serializeConfig(config), true, {}); } async doLoadTestFile(file: string, loadErrors: LoadError[]): Promise { await this._startPromise; const result = await this._processHost.sendMessage({ method: 'loadTestFile', params: { file } }) as any; - loadErrors.push(...result.loadErrors); + loadErrors.push(...result.testErrors); return Suite._deepParse(result.fileSuite); } override async stop() { + const result = await this._processHost.sendMessage({ method: 'serializeCompilationCache' }) as any; + addToCompilationCache(result); await this._processHost.stop(); } } diff --git a/packages/playwright-test/src/runner/tasks.ts b/packages/playwright-test/src/runner/tasks.ts index f1e236a245..7a0b20f4d8 100644 --- a/packages/playwright-test/src/runner/tasks.ts +++ b/packages/playwright-test/src/runner/tasks.ts @@ -28,6 +28,8 @@ import { TaskRunner } from './taskRunner'; import type { Suite } from '../common/test'; import type { FullConfigInternal, FullProjectInternal } from '../common/types'; import { loadAllTests, loadGlobalHook } from './loadUtils'; +import { createFileMatcherFromFilters } from '../util'; +import type { Matcher } from '../util'; const removeFolderAsync = promisify(rimraf); const readDirAsync = promisify(fs.readdir); @@ -106,7 +108,7 @@ function createRemoveOutputDirsTask(): Task { return async ({ config }) => { const outputDirs = new Set(); for (const p of config.projects) { - if (!config._internal.projectFilter || config._internal.projectFilter.includes(p.name)) + if (!config._internal.cliProjectFilter || config._internal.cliProjectFilter.includes(p.name)) outputDirs.add(p.outputDir); } @@ -124,10 +126,12 @@ function createRemoveOutputDirsTask(): Task { }; } -function createLoadTask(): Task { +function createLoadTask(projectsToIgnore = new Set(), additionalFileMatcher?: Matcher): Task { return async (context, errors) => { const { config } = context; - context.rootSuite = await loadAllTests(config, errors); + const cliMatcher = config._internal.cliFileFilters.length ? createFileMatcherFromFilters(config._internal.cliFileFilters) : () => true; + const fileMatcher = (value: string) => cliMatcher(value) && (additionalFileMatcher ? additionalFileMatcher(value) : true); + context.rootSuite = await loadAllTests(config, projectsToIgnore, fileMatcher, errors); // Fail when no tests. if (!context.rootSuite.allTests().length && !config._internal.passWithNoTests && !config.shard) throw new Error(`No tests found`); diff --git a/packages/playwright-test/src/worker/workerMain.ts b/packages/playwright-test/src/worker/workerMain.ts index 3330783dd2..ebd55a6810 100644 --- a/packages/playwright-test/src/worker/workerMain.ts +++ b/packages/playwright-test/src/worker/workerMain.ts @@ -31,7 +31,7 @@ import { ProcessRunner } from '../common/process'; import { loadTestFile } from '../common/testLoader'; import { buildFileSuiteForProject, filterTestsRemoveEmptySuites } from '../common/suiteUtils'; import { PoolBuilder } from '../common/poolBuilder'; -import { initializeCompilationCache } from '../common/compilationCache'; +import { addToCompilationCache } from '../common/compilationCache'; const removeFolderAsync = util.promisify(rimraf); @@ -67,7 +67,7 @@ export class WorkerMain extends ProcessRunner { process.env.TEST_WORKER_INDEX = String(params.workerIndex); process.env.TEST_PARALLEL_INDEX = String(params.parallelIndex); setIsWorkerProcess(); - initializeCompilationCache(params.config.compilationCache); + addToCompilationCache(params.config.compilationCache); this._params = params; this._fixtureRunner = new FixtureRunner();