mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
chore: respect deps when watching files (#20695)
This commit is contained in:
parent
430d08f4fb
commit
361ea949aa
@ -32,7 +32,7 @@ const cacheDir = process.env.PWTEST_CACHE_DIR || path.join(os.tmpdir(), 'playwri
|
||||
|
||||
const sourceMaps: Map<string, string> = new Map();
|
||||
const memoryCache = new Map<string, MemoryCache>();
|
||||
const fileDependencies = new Map<string, string[]>();
|
||||
const fileDependencies = new Map<string, Set<string>>();
|
||||
|
||||
Error.stackTraceLimit = 200;
|
||||
|
||||
@ -92,7 +92,7 @@ export function serializeCompilationCache(): any {
|
||||
return {
|
||||
sourceMaps: [...sourceMaps.entries()],
|
||||
memoryCache: [...memoryCache.entries()],
|
||||
fileDependencies: [...fileDependencies.entries()],
|
||||
fileDependencies: [...fileDependencies.entries()].map(([filename, deps]) => ([filename, [...deps]])),
|
||||
};
|
||||
}
|
||||
|
||||
@ -107,7 +107,7 @@ export function addToCompilationCache(payload: any) {
|
||||
for (const entry of payload.memoryCache)
|
||||
memoryCache.set(entry[0], entry[1]);
|
||||
for (const entry of payload.fileDependencies)
|
||||
fileDependencies.set(entry[0], entry[1]);
|
||||
fileDependencies.set(entry[0], new Set(entry[1]));
|
||||
}
|
||||
|
||||
function calculateCachePath(content: string, filePath: string, isModule: boolean): string {
|
||||
@ -134,8 +134,11 @@ export function stopCollectingFileDeps(filename: string) {
|
||||
if (!depsCollector)
|
||||
return;
|
||||
depsCollector.delete(filename);
|
||||
const deps = [...depsCollector!].filter(f => !belongsToNodeModules(f));
|
||||
fileDependencies.set(filename, deps);
|
||||
for (const dep of depsCollector) {
|
||||
if (belongsToNodeModules(dep))
|
||||
depsCollector.delete(dep);
|
||||
}
|
||||
fileDependencies.set(filename, depsCollector);
|
||||
depsCollector = undefined;
|
||||
}
|
||||
|
||||
@ -147,6 +150,14 @@ export function fileDependenciesForTest() {
|
||||
return fileDependencies;
|
||||
}
|
||||
|
||||
export function collectAffectedTestFiles(dependency: string, testFileCollector: Set<string>) {
|
||||
testFileCollector.add(dependency);
|
||||
for (const [testFile, deps] of fileDependencies) {
|
||||
if (deps.has(dependency))
|
||||
testFileCollector.add(testFile);
|
||||
}
|
||||
}
|
||||
|
||||
// These two are only used in the dev mode, they are specifically excluding
|
||||
// files from packages/playwright*. In production mode, node_modules covers
|
||||
// that.
|
||||
|
||||
@ -221,10 +221,8 @@ function installTransform(): () => void {
|
||||
}
|
||||
|
||||
const collectCJSDependencies = (module: Module, dependencies: Set<string>) => {
|
||||
if (dependencies.has(module.filename))
|
||||
return;
|
||||
module.children.forEach(child => {
|
||||
if (!belongsToNodeModules(child.filename)) {
|
||||
if (!belongsToNodeModules(child.filename) && !dependencies.has(child.filename)) {
|
||||
dependencies.add(child.filename);
|
||||
collectCJSDependencies(child, dependencies);
|
||||
}
|
||||
|
||||
@ -19,6 +19,6 @@ import { fileDependenciesForTest } from './common/compilationCache';
|
||||
|
||||
export function fileDependencies() {
|
||||
return Object.fromEntries([...fileDependenciesForTest().entries()].map(entry => (
|
||||
[path.basename(entry[0]), entry[1].map(f => path.basename(f))]
|
||||
[path.basename(entry[0]), [...entry[1]].map(f => path.basename(f)).sort()]
|
||||
)));
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@ import type { Matcher } from '../util';
|
||||
import { createTaskRunnerForWatch } from './tasks';
|
||||
import type { TaskRunnerState } from './tasks';
|
||||
import { buildProjectsClosure, filterProjects } from './projectUtils';
|
||||
import { clearCompilationCache } from '../common/compilationCache';
|
||||
import { clearCompilationCache, collectAffectedTestFiles } from '../common/compilationCache';
|
||||
import type { FullResult, TestCase } from 'packages/playwright-test/reporter';
|
||||
import chokidar from 'chokidar';
|
||||
import { WatchModeReporter } from './reporters';
|
||||
@ -149,14 +149,19 @@ ${colors.dim('press')} ${colors.bold('h')} ${colors.dim('to show help, press')}
|
||||
}
|
||||
}
|
||||
|
||||
async function runChangedTests(config: FullConfigInternal, failedTestIds: Set<string>, projectClosure: FullProjectInternal[], files: Set<string>) {
|
||||
async function runChangedTests(config: FullConfigInternal, failedTestIdCollector: Set<string>, projectClosure: FullProjectInternal[], changedFiles: Set<string>) {
|
||||
const commandLineFileMatcher = config._internal.cliFileFilters.length ? createFileMatcherFromFilters(config._internal.cliFileFilters) : () => true;
|
||||
|
||||
// Resolve files that depend on the changed files.
|
||||
const testFiles = new Set<string>();
|
||||
for (const file of changedFiles)
|
||||
collectAffectedTestFiles(file, testFiles);
|
||||
|
||||
// Collect projects with changes.
|
||||
const filesByProject = new Map<FullProjectInternal, string[]>();
|
||||
for (const project of projectClosure) {
|
||||
const projectFiles: string[] = [];
|
||||
for (const file of files) {
|
||||
for (const file of testFiles) {
|
||||
if (!file.startsWith(project.testDir))
|
||||
continue;
|
||||
if (project._internal.type === 'dependency' || commandLineFileMatcher(file))
|
||||
@ -174,11 +179,11 @@ async function runChangedTests(config: FullConfigInternal, failedTestIds: Set<st
|
||||
|
||||
// If there are affected dependency projects, do the full run, respect the original CLI.
|
||||
// if there are no affected dependency projects, intersect CLI with dirty files
|
||||
const additionalFileMatcher = affectsAnyDependency ? () => true : (file: string) => files.has(file);
|
||||
return await runTests(config, failedTestIds, projectsToIgnore, additionalFileMatcher);
|
||||
const additionalFileMatcher = affectsAnyDependency ? () => true : (file: string) => testFiles.has(file);
|
||||
return await runTests(config, failedTestIdCollector, projectsToIgnore, additionalFileMatcher);
|
||||
}
|
||||
|
||||
async function runTests(config: FullConfigInternal, failedTestIds: Set<string>, projectsToIgnore?: Set<FullProjectInternal>, additionalFileMatcher?: Matcher) {
|
||||
async function runTests(config: FullConfigInternal, failedTestIdCollector: Set<string>, projectsToIgnore?: Set<FullProjectInternal>, additionalFileMatcher?: Matcher) {
|
||||
const reporter = new Multiplexer([new WatchModeReporter()]);
|
||||
const taskRunner = createTaskRunnerForWatch(config, reporter, projectsToIgnore, additionalFileMatcher);
|
||||
const context: TaskRunnerState = {
|
||||
@ -194,9 +199,9 @@ async function runTests(config: FullConfigInternal, failedTestIds: Set<string>,
|
||||
let hasFailedTests = false;
|
||||
for (const test of context.rootSuite?.allTests() || []) {
|
||||
if (test.outcome() === 'expected') {
|
||||
failedTestIds.delete(test.id);
|
||||
failedTestIdCollector.delete(test.id);
|
||||
} else {
|
||||
failedTestIds.add(test.id);
|
||||
failedTestIdCollector.add(test.id);
|
||||
hasFailedTests = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,13 +24,14 @@ test('should print dependencies in CJS mode', async ({ runInlineTest }) => {
|
||||
globalTeardown: './globalTeardown.ts',
|
||||
});
|
||||
`,
|
||||
'helper.ts': `export function foo() {}`,
|
||||
'helperA.ts': `export function foo() {}`,
|
||||
'helperB.ts': `import './helperA';`,
|
||||
'a.test.ts': `
|
||||
import './helper';
|
||||
import './helperA';
|
||||
pwt.test('passes', () => {});
|
||||
`,
|
||||
'b.test.ts': `
|
||||
import './helper';
|
||||
import './helperB';
|
||||
pwt.test('passes', () => {});
|
||||
`,
|
||||
'globalTeardown.ts': `
|
||||
@ -46,8 +47,8 @@ test('should print dependencies in CJS mode', async ({ runInlineTest }) => {
|
||||
const output = stripAnsi(result.output);
|
||||
const deps = JSON.parse(output.match(/###(.*)###/)![1]);
|
||||
expect(deps).toEqual({
|
||||
'a.test.ts': ['helper.ts'],
|
||||
'b.test.ts': ['helper.ts'],
|
||||
'a.test.ts': ['helperA.ts'],
|
||||
'b.test.ts': ['helperA.ts', 'helperB.ts'],
|
||||
});
|
||||
});
|
||||
|
||||
@ -61,13 +62,14 @@ test('should print dependencies in ESM mode', async ({ runInlineTest, nodeVersio
|
||||
globalTeardown: './globalTeardown.ts',
|
||||
});
|
||||
`,
|
||||
'helper.ts': `export function foo() {}`,
|
||||
'helperA.ts': `export function foo() {}`,
|
||||
'helperB.ts': `import './helperA.js';`,
|
||||
'a.test.ts': `
|
||||
import './helper.js';
|
||||
import './helperA.js';
|
||||
pwt.test('passes', () => {});
|
||||
`,
|
||||
'b.test.ts': `
|
||||
import './helper.js';
|
||||
import './helperB.js';
|
||||
pwt.test('passes', () => {});
|
||||
`,
|
||||
'globalTeardown.ts': `
|
||||
@ -83,7 +85,7 @@ test('should print dependencies in ESM mode', async ({ runInlineTest, nodeVersio
|
||||
const output = stripAnsi(result.output);
|
||||
const deps = JSON.parse(output.match(/###(.*)###/)![1]);
|
||||
expect(deps).toEqual({
|
||||
'a.test.ts': ['helper.ts'],
|
||||
'b.test.ts': ['helper.ts'],
|
||||
'a.test.ts': ['helperA.ts'],
|
||||
'b.test.ts': ['helperA.ts', 'helperB.ts'],
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user