fix(test runner): avoid dependency tracking colliding between esm and cjs (#29994)
When collecting dependencies both from CJS loader and from ESM loader, the latter would overwrite the dependencies set instead of appending. Also make sure cts/cjs/mts/mjs are all supported equally. References #29747.
This commit is contained in:
parent
c7b074d39e
commit
b41b802662
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import util from 'util';
|
import util from 'util';
|
||||||
import { serializeCompilationCache } from '../transform/compilationCache';
|
import { type SerializedCompilationCache, serializeCompilationCache } from '../transform/compilationCache';
|
||||||
import type { ConfigLocation, FullConfigInternal } from './config';
|
import type { ConfigLocation, FullConfigInternal } from './config';
|
||||||
import type { ReporterDescription, TestInfoError, TestStatus } from '../../types/test';
|
import type { ReporterDescription, TestInfoError, TestStatus } from '../../types/test';
|
||||||
|
|
||||||
|
|
@ -43,7 +43,7 @@ export type ConfigCLIOverrides = {
|
||||||
export type SerializedConfig = {
|
export type SerializedConfig = {
|
||||||
location: ConfigLocation;
|
location: ConfigLocation;
|
||||||
configCLIOverrides: ConfigCLIOverrides;
|
configCLIOverrides: ConfigCLIOverrides;
|
||||||
compilationCache: any;
|
compilationCache?: SerializedCompilationCache;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TtyParams = {
|
export type TtyParams = {
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ export type MemoryCache = {
|
||||||
moduleUrl?: string;
|
moduleUrl?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type SerializedCompilationCache = {
|
export type SerializedCompilationCache = {
|
||||||
sourceMaps: [string, string][],
|
sourceMaps: [string, string][],
|
||||||
memoryCache: [string, MemoryCache][],
|
memoryCache: [string, MemoryCache][],
|
||||||
fileDependencies: [string, string[]][],
|
fileDependencies: [string, string[]][],
|
||||||
|
|
@ -158,15 +158,19 @@ export function serializeCompilationCache(): SerializedCompilationCache {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addToCompilationCache(payload: any) {
|
export function addToCompilationCache(payload: SerializedCompilationCache) {
|
||||||
for (const entry of payload.sourceMaps)
|
for (const entry of payload.sourceMaps)
|
||||||
sourceMaps.set(entry[0], entry[1]);
|
sourceMaps.set(entry[0], entry[1]);
|
||||||
for (const entry of payload.memoryCache)
|
for (const entry of payload.memoryCache)
|
||||||
memoryCache.set(entry[0], entry[1]);
|
memoryCache.set(entry[0], entry[1]);
|
||||||
for (const entry of payload.fileDependencies)
|
for (const entry of payload.fileDependencies) {
|
||||||
fileDependencies.set(entry[0], new Set(entry[1]));
|
const existing = fileDependencies.get(entry[0]) || [];
|
||||||
for (const entry of payload.externalDependencies)
|
fileDependencies.set(entry[0], new Set([...entry[1], ...existing]));
|
||||||
externalDependencies.set(entry[0], new Set(entry[1]));
|
}
|
||||||
|
for (const entry of payload.externalDependencies) {
|
||||||
|
const existing = externalDependencies.get(entry[0]) || [];
|
||||||
|
externalDependencies.set(entry[0], new Set([...entry[1], ...existing]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function calculateCachePath(filePath: string, hash: string): string {
|
function calculateCachePath(filePath: string, hash: string): string {
|
||||||
|
|
@ -249,9 +253,9 @@ const kPlaywrightCoveragePrefix = path.resolve(__dirname, '../../../../tests/con
|
||||||
export function belongsToNodeModules(file: string) {
|
export function belongsToNodeModules(file: string) {
|
||||||
if (file.includes(`${path.sep}node_modules${path.sep}`))
|
if (file.includes(`${path.sep}node_modules${path.sep}`))
|
||||||
return true;
|
return true;
|
||||||
if (file.startsWith(kPlaywrightInternalPrefix) && file.endsWith('.js'))
|
if (file.startsWith(kPlaywrightInternalPrefix) && (file.endsWith('.js') || file.endsWith('.mjs')))
|
||||||
return true;
|
return true;
|
||||||
if (file.startsWith(kPlaywrightCoveragePrefix) && file.endsWith('.js'))
|
if (file.startsWith(kPlaywrightCoveragePrefix) && (file.endsWith('.js') || file.endsWith('.mjs')))
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -240,7 +240,7 @@ function installTransform(): () => void {
|
||||||
if (!shouldTransform(filename))
|
if (!shouldTransform(filename))
|
||||||
return code;
|
return code;
|
||||||
return transformHook(code, filename).code;
|
return transformHook(code, filename).code;
|
||||||
}, { exts: ['.ts', '.tsx', '.js', '.jsx', '.mjs'] });
|
}, { exts: ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.mts', '.cjs', '.cts'] });
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
reverted = true;
|
reverted = true;
|
||||||
|
|
|
||||||
|
|
@ -171,7 +171,7 @@ export class TestInfoImpl implements TestInfo {
|
||||||
this._timeoutManager = new TimeoutManager(this.project.timeout);
|
this._timeoutManager = new TimeoutManager(this.project.timeout);
|
||||||
|
|
||||||
this.outputDir = (() => {
|
this.outputDir = (() => {
|
||||||
const relativeTestFilePath = path.relative(this.project.testDir, this._requireFile.replace(/\.(spec|test)\.(js|ts|mjs)$/, ''));
|
const relativeTestFilePath = path.relative(this.project.testDir, this._requireFile.replace(/\.(spec|test)\.(js|ts|jsx|tsx|mjs|mts|cjs|cts)$/, ''));
|
||||||
const sanitizedRelativePath = relativeTestFilePath.replace(process.platform === 'win32' ? new RegExp('\\\\', 'g') : new RegExp('/', 'g'), '-');
|
const sanitizedRelativePath = relativeTestFilePath.replace(process.platform === 'win32' ? new RegExp('\\\\', 'g') : new RegExp('/', 'g'), '-');
|
||||||
const fullTitleWithoutSpec = this.titlePath.slice(1).join(' ');
|
const fullTitleWithoutSpec = this.titlePath.slice(1).join(' ');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -91,8 +91,85 @@ test('should print dependencies in ESM mode', async ({ runInlineTest }) => {
|
||||||
const output = result.output;
|
const output = result.output;
|
||||||
const deps = JSON.parse(output.match(/###(.*)###/)![1]);
|
const deps = JSON.parse(output.match(/###(.*)###/)![1]);
|
||||||
expect(deps).toEqual({
|
expect(deps).toEqual({
|
||||||
'a.test.ts': ['helperA.ts', 'index.mjs'],
|
'a.test.ts': ['helperA.ts'],
|
||||||
'b.test.ts': ['helperA.ts', 'helperB.ts', 'index.mjs'],
|
'b.test.ts': ['helperA.ts', 'helperB.ts'],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should print dependencies in mixed CJS/ESM mode 1', async ({ runInlineTest }) => {
|
||||||
|
const result = await runInlineTest({
|
||||||
|
'package.json': `{ "type": "module" }`,
|
||||||
|
'playwright.config.ts': `
|
||||||
|
import { defineConfig } from '@playwright/test';
|
||||||
|
export default defineConfig({
|
||||||
|
globalTeardown: './globalTeardown.ts',
|
||||||
|
});
|
||||||
|
`,
|
||||||
|
'helperA.cjs': `exports.foo = () => {}`,
|
||||||
|
'helperB.cjs': `require('./helperA');`,
|
||||||
|
'a.test.ts': `
|
||||||
|
import './helperA';
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
test('passes', () => {});
|
||||||
|
`,
|
||||||
|
'b.test.cjs': `
|
||||||
|
require('./helperB');
|
||||||
|
const { test, expect } = require('@playwright/test');
|
||||||
|
test('passes', () => {});
|
||||||
|
`,
|
||||||
|
'globalTeardown.ts': `
|
||||||
|
import { fileDependencies } from 'playwright/lib/internalsForTest';
|
||||||
|
export default () => {
|
||||||
|
console.log('###' + JSON.stringify(fileDependencies()) + '###');
|
||||||
|
};
|
||||||
|
`
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
expect(result.exitCode).toBe(0);
|
||||||
|
expect(result.passed).toBe(2);
|
||||||
|
const output = result.output;
|
||||||
|
const deps = JSON.parse(output.match(/###(.*)###/)![1]);
|
||||||
|
expect(deps).toEqual({
|
||||||
|
'a.test.ts': ['helperA.cjs'],
|
||||||
|
'b.test.cjs': ['helperA.cjs', 'helperB.cjs'],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should print dependencies in mixed CJS/ESM mode 2', async ({ runInlineTest }) => {
|
||||||
|
const result = await runInlineTest({
|
||||||
|
'playwright.config.mts': `
|
||||||
|
import { defineConfig } from '@playwright/test';
|
||||||
|
export default defineConfig({
|
||||||
|
globalTeardown: './globalTeardown.ts',
|
||||||
|
});
|
||||||
|
`,
|
||||||
|
'helperA.cjs': `exports.foo = () => {}`,
|
||||||
|
'helperB.cts': `import './helperA';`,
|
||||||
|
'a.test.mts': `
|
||||||
|
import './helperA';
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
test('passes', () => {});
|
||||||
|
`,
|
||||||
|
'b.test.ts': `
|
||||||
|
import './helperB';
|
||||||
|
const { test, expect } = require('@playwright/test');
|
||||||
|
test('passes', () => {});
|
||||||
|
`,
|
||||||
|
'globalTeardown.ts': `
|
||||||
|
import { fileDependencies } from 'playwright/lib/internalsForTest';
|
||||||
|
export default () => {
|
||||||
|
console.log('###' + JSON.stringify(fileDependencies()) + '###');
|
||||||
|
};
|
||||||
|
`
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
expect(result.exitCode).toBe(0);
|
||||||
|
expect(result.passed).toBe(2);
|
||||||
|
const output = result.output;
|
||||||
|
const deps = JSON.parse(output.match(/###(.*)###/)![1]);
|
||||||
|
expect(deps).toEqual({
|
||||||
|
'a.test.mts': ['helperA.cjs'],
|
||||||
|
'b.test.ts': ['helperA.cjs', 'helperB.cts'],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue