chore: use original test ids in html report (#30486)
Fixes https://github.com/microsoft/playwright/issues/30430
This commit is contained in:
parent
67c430435a
commit
4514b7e3ed
|
|
@ -78,11 +78,21 @@ const TestCaseViewLoader: React.FC<{
|
||||||
const testId = searchParams.get('testId');
|
const testId = searchParams.get('testId');
|
||||||
const anchor = (searchParams.get('anchor') || '') as 'video' | 'diff' | '';
|
const anchor = (searchParams.get('anchor') || '') as 'video' | 'diff' | '';
|
||||||
const run = +(searchParams.get('run') || '0');
|
const run = +(searchParams.get('run') || '0');
|
||||||
|
|
||||||
|
const testIdToFileIdMap = React.useMemo(() => {
|
||||||
|
const map = new Map<string, string>();
|
||||||
|
for (const file of report.json().files) {
|
||||||
|
for (const test of file.tests)
|
||||||
|
map.set(test.testId, file.fileId);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}, [report]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
if (!testId || testId === test?.testId)
|
if (!testId || testId === test?.testId)
|
||||||
return;
|
return;
|
||||||
const fileId = testId.split('-')[0];
|
const fileId = testIdToFileIdMap.get(testId);
|
||||||
if (!fileId)
|
if (!fileId)
|
||||||
return;
|
return;
|
||||||
const file = await report.entry(`${fileId}.json`) as TestFile;
|
const file = await report.entry(`${fileId}.json`) as TestFile;
|
||||||
|
|
@ -93,7 +103,7 @@ const TestCaseViewLoader: React.FC<{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
}, [test, report, testId]);
|
}, [test, report, testId, testIdToFileIdMap]);
|
||||||
return <TestCaseView projectNames={report.json().projectNames} test={test} anchor={anchor} run={run}></TestCaseView>;
|
return <TestCaseView projectNames={report.json().projectNames} test={test} anchor={anchor} run={run}></TestCaseView>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -246,7 +246,7 @@ class HtmlBuilder {
|
||||||
}
|
}
|
||||||
const { testFile, testFileSummary } = fileEntry;
|
const { testFile, testFileSummary } = fileEntry;
|
||||||
const testEntries: TestEntry[] = [];
|
const testEntries: TestEntry[] = [];
|
||||||
this._processSuite(fileSuite, fileId, projectSuite.project()!.name, [], testEntries);
|
this._processSuite(fileSuite, projectSuite.project()!.name, [], testEntries);
|
||||||
for (const test of testEntries) {
|
for (const test of testEntries) {
|
||||||
testFile.tests.push(test.testCase);
|
testFile.tests.push(test.testCase);
|
||||||
testFileSummary.tests.push(test.testCaseSummary);
|
testFileSummary.tests.push(test.testCaseSummary);
|
||||||
|
|
@ -346,30 +346,25 @@ class HtmlBuilder {
|
||||||
this._dataZipFile.addBuffer(Buffer.from(JSON.stringify(data)), fileName);
|
this._dataZipFile.addBuffer(Buffer.from(JSON.stringify(data)), fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _processSuite(suite: Suite, fileId: string, projectName: string, path: string[], outTests: TestEntry[]) {
|
private _processSuite(suite: Suite, projectName: string, path: string[], outTests: TestEntry[]) {
|
||||||
const newPath = [...path, suite.title];
|
const newPath = [...path, suite.title];
|
||||||
suite.entries().forEach(e => {
|
suite.entries().forEach(e => {
|
||||||
if (e.type === 'test')
|
if (e.type === 'test')
|
||||||
outTests.push(this._createTestEntry(fileId, e, projectName, newPath));
|
outTests.push(this._createTestEntry(e, projectName, newPath));
|
||||||
else
|
else
|
||||||
this._processSuite(e, fileId, projectName, newPath, outTests);
|
this._processSuite(e, projectName, newPath, outTests);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createTestEntry(fileId: string, test: TestCasePublic, projectName: string, path: string[]): TestEntry {
|
private _createTestEntry(test: TestCasePublic, projectName: string, path: string[]): TestEntry {
|
||||||
const duration = test.results.reduce((a, r) => a + r.duration, 0);
|
const duration = test.results.reduce((a, r) => a + r.duration, 0);
|
||||||
const location = this._relativeLocation(test.location)!;
|
const location = this._relativeLocation(test.location)!;
|
||||||
path = path.slice(1);
|
path = path.slice(1);
|
||||||
|
|
||||||
const [file, ...titles] = test.titlePath();
|
|
||||||
const testIdExpression = `[project=${this._projectId(test.parent)}]${toPosixPath(file)}\x1e${titles.join('\x1e')} (repeat:${test.repeatEachIndex})`;
|
|
||||||
const testId = fileId + '-' + calculateSha1(testIdExpression).slice(0, 20);
|
|
||||||
|
|
||||||
const results = test.results.map(r => this._createTestResult(test, r));
|
const results = test.results.map(r => this._createTestResult(test, r));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
testCase: {
|
testCase: {
|
||||||
testId,
|
testId: test.id,
|
||||||
title: test.title,
|
title: test.title,
|
||||||
projectName,
|
projectName,
|
||||||
location,
|
location,
|
||||||
|
|
@ -383,7 +378,7 @@ class HtmlBuilder {
|
||||||
ok: test.outcome() === 'expected' || test.outcome() === 'flaky',
|
ok: test.outcome() === 'expected' || test.outcome() === 'flaky',
|
||||||
},
|
},
|
||||||
testCaseSummary: {
|
testCaseSummary: {
|
||||||
testId,
|
testId: test.id,
|
||||||
title: test.title,
|
title: test.title,
|
||||||
projectName,
|
projectName,
|
||||||
location,
|
location,
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,32 @@ for (const useIntermediateMergeReport of [false, true] as const) {
|
||||||
await expect(page.locator('.metadata-view')).not.toBeVisible();
|
await expect(page.locator('.metadata-view')).not.toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should allow navigating to testId=test.id', async ({ runInlineTest, page, showReport }) => {
|
||||||
|
const result = await runInlineTest({
|
||||||
|
'a.test.js': `
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
test('passes', async ({ page }) => {
|
||||||
|
console.log('TESTID=' + test.info().testId);
|
||||||
|
await expect(1).toBe(1);
|
||||||
|
});
|
||||||
|
`,
|
||||||
|
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
|
||||||
|
expect(result.exitCode).toBe(0);
|
||||||
|
expect(result.passed).toBe(1);
|
||||||
|
|
||||||
|
await showReport();
|
||||||
|
await page.click('text=passes');
|
||||||
|
await page.locator('text=stdout').click();
|
||||||
|
await expect(page.locator('.attachment-body')).toHaveText(/TESTID=.*/);
|
||||||
|
const idString = await page.locator('.attachment-body').textContent();
|
||||||
|
const testId = idString.match(/TESTID=(.*)/)[1];
|
||||||
|
expect(page.url()).toContain('testId=' + testId);
|
||||||
|
|
||||||
|
// Expect test to be opened.
|
||||||
|
await page.reload();
|
||||||
|
await page.locator('text=stdout').click();
|
||||||
|
await expect(page.locator('.attachment-body')).toHaveText(/TESTID=.*/);
|
||||||
|
});
|
||||||
|
|
||||||
test('should not throw when PW_TEST_HTML_REPORT_OPEN value is invalid', async ({ runInlineTest, page, showReport }, testInfo) => {
|
test('should not throw when PW_TEST_HTML_REPORT_OPEN value is invalid', async ({ runInlineTest, page, showReport }, testInfo) => {
|
||||||
const invalidOption = 'invalid-option';
|
const invalidOption = 'invalid-option';
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue