fix(test runner): make TestCase.id not depend on the path separator (#29010)
Fixes #28991.
This commit is contained in:
parent
ab7d1b5e53
commit
775ef30e43
|
|
@ -52,3 +52,7 @@ export async function copyFileAndMakeWritable(from: string, to: string) {
|
||||||
export function sanitizeForFilePath(s: string) {
|
export function sanitizeForFilePath(s: string) {
|
||||||
return s.replace(/[\x00-\x2C\x2E-\x2F\x3A-\x40\x5B-\x60\x7B-\x7F]+/g, '-');
|
return s.replace(/[\x00-\x2C\x2E-\x2F\x3A-\x40\x5B-\x60\x7B-\x7F]+/g, '-');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function toPosixPath(aPath: string): string {
|
||||||
|
return aPath.split(path.sep).join(path.posix.sep);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { calculateSha1 } from 'playwright-core/lib/utils';
|
import { calculateSha1, toPosixPath } from 'playwright-core/lib/utils';
|
||||||
import type { Suite, TestCase } from './test';
|
import type { Suite, TestCase } from './test';
|
||||||
import type { FullProjectInternal } from './config';
|
import type { FullProjectInternal } from './config';
|
||||||
import type { Matcher, TestFileFilter } from '../util';
|
import type { Matcher, TestFileFilter } from '../util';
|
||||||
|
|
@ -39,9 +39,10 @@ export function filterTestsRemoveEmptySuites(suite: Suite, filter: (test: TestCa
|
||||||
suite._entries = suite._entries.filter(e => entries.has(e)); // Preserve the order.
|
suite._entries = suite._entries.filter(e => entries.has(e)); // Preserve the order.
|
||||||
return !!suite._entries.length;
|
return !!suite._entries.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function bindFileSuiteToProject(project: FullProjectInternal, suite: Suite): Suite {
|
export function bindFileSuiteToProject(project: FullProjectInternal, suite: Suite): Suite {
|
||||||
const relativeFile = path.relative(project.project.testDir, suite.location!.file).split(path.sep).join('/');
|
const relativeFile = path.relative(project.project.testDir, suite.location!.file);
|
||||||
const fileId = calculateSha1(relativeFile).slice(0, 20);
|
const fileId = calculateSha1(toPosixPath(relativeFile)).slice(0, 20);
|
||||||
|
|
||||||
// Clone suite.
|
// Clone suite.
|
||||||
const result = suite._deepClone();
|
const result = suite._deepClone();
|
||||||
|
|
@ -51,7 +52,8 @@ export function bindFileSuiteToProject(project: FullProjectInternal, suite: Suit
|
||||||
result.forEachTest((test, suite) => {
|
result.forEachTest((test, suite) => {
|
||||||
suite._fileId = fileId;
|
suite._fileId = fileId;
|
||||||
// At the point of the query, suite is not yet attached to the project, so we only get file, describe and test titles.
|
// At the point of the query, suite is not yet attached to the project, so we only get file, describe and test titles.
|
||||||
const testIdExpression = `[project=${project.id}]${test.titlePath().join('\x1e')}`;
|
const [file, ...titles] = test.titlePath();
|
||||||
|
const testIdExpression = `[project=${project.id}]${toPosixPath(file)}\x1e${titles.join('\x1e')}`;
|
||||||
const testId = fileId + '-' + calculateSha1(testIdExpression).slice(0, 20);
|
const testId = fileId + '-' + calculateSha1(testIdExpression).slice(0, 20);
|
||||||
test.id = testId;
|
test.id = testId;
|
||||||
test._projectId = project.id;
|
test._projectId = project.id;
|
||||||
|
|
|
||||||
|
|
@ -20,11 +20,10 @@ import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import type { TransformCallback } from 'stream';
|
import type { TransformCallback } from 'stream';
|
||||||
import { Transform } from 'stream';
|
import { Transform } from 'stream';
|
||||||
import { toPosixPath } from './json';
|
|
||||||
import { codeFrameColumns } from '../transform/babelBundle';
|
import { codeFrameColumns } from '../transform/babelBundle';
|
||||||
import type { FullResult, FullConfig, Location, Suite, TestCase as TestCasePublic, TestResult as TestResultPublic, TestStep as TestStepPublic, TestError } from '../../types/testReporter';
|
import type { FullResult, FullConfig, Location, Suite, TestCase as TestCasePublic, TestResult as TestResultPublic, TestStep as TestStepPublic, TestError } from '../../types/testReporter';
|
||||||
import type { SuitePrivate } from '../../types/reporterPrivate';
|
import type { SuitePrivate } from '../../types/reporterPrivate';
|
||||||
import { HttpServer, assert, calculateSha1, copyFileAndMakeWritable, gracefullyProcessExitDoNotHang, removeFolders, sanitizeForFilePath } from 'playwright-core/lib/utils';
|
import { HttpServer, assert, calculateSha1, copyFileAndMakeWritable, gracefullyProcessExitDoNotHang, removeFolders, sanitizeForFilePath, toPosixPath } from 'playwright-core/lib/utils';
|
||||||
import { colors, formatError, formatResultFailure, stripAnsiEscapes } from './base';
|
import { colors, formatError, formatResultFailure, stripAnsiEscapes } from './base';
|
||||||
import { resolveReporterOutputPath } from '../util';
|
import { resolveReporterOutputPath } from '../util';
|
||||||
import type { Metadata } from '../../types/test';
|
import type { Metadata } from '../../types/test';
|
||||||
|
|
|
||||||
|
|
@ -18,15 +18,10 @@ import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import type { FullConfig, TestCase, Suite, TestResult, TestError, TestStep, FullResult, Location, JSONReport, JSONReportSuite, JSONReportSpec, JSONReportTest, JSONReportTestResult, JSONReportTestStep, JSONReportError } from '../../types/testReporter';
|
import type { FullConfig, TestCase, Suite, TestResult, TestError, TestStep, FullResult, Location, JSONReport, JSONReportSuite, JSONReportSpec, JSONReportTest, JSONReportTestResult, JSONReportTestStep, JSONReportError } from '../../types/testReporter';
|
||||||
import { formatError, prepareErrorStack } from './base';
|
import { formatError, prepareErrorStack } from './base';
|
||||||
import { MultiMap } from 'playwright-core/lib/utils';
|
import { MultiMap, assert, toPosixPath } from 'playwright-core/lib/utils';
|
||||||
import { assert } from 'playwright-core/lib/utils';
|
|
||||||
import { getProjectId } from '../common/config';
|
import { getProjectId } from '../common/config';
|
||||||
import EmptyReporter from './empty';
|
import EmptyReporter from './empty';
|
||||||
|
|
||||||
export function toPosixPath(aPath: string): string {
|
|
||||||
return aPath.split(path.sep).join(path.posix.sep);
|
|
||||||
}
|
|
||||||
|
|
||||||
class JSONReporter extends EmptyReporter {
|
class JSONReporter extends EmptyReporter {
|
||||||
config!: FullConfig;
|
config!: FullConfig;
|
||||||
suite!: Suite;
|
suite!: Suite;
|
||||||
|
|
|
||||||
|
|
@ -797,4 +797,30 @@ var import_test = __toModule(require("@playwright/test"));
|
||||||
4 | `);
|
4 | `);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test('should report a stable test.id', async ({ runInlineTest }) => {
|
||||||
|
const result = await runInlineTest({
|
||||||
|
'reporter.ts': `
|
||||||
|
class Reporter {
|
||||||
|
onTestBegin(test) {
|
||||||
|
console.log('\\n%%testbegin-' + test.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default Reporter;
|
||||||
|
`,
|
||||||
|
'playwright.config.ts': `
|
||||||
|
module.exports = { reporter: [[ './reporter.ts' ]] };
|
||||||
|
`,
|
||||||
|
'a.test.ts': `
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
test('example test', async ({}) => {
|
||||||
|
});
|
||||||
|
`
|
||||||
|
}, { reporter: '', workers: 1 });
|
||||||
|
|
||||||
|
expect(result.exitCode).toBe(0);
|
||||||
|
expect(result.outputLines).toEqual([
|
||||||
|
'testbegin-20289bcdad95a5e18c38-8b63c3695b9c8bd62d98',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue