cherry-pick(#26413): fix(merge): allow reports with same name as input (#26417)

This commit is contained in:
Yury Semikhatsky 2023-08-10 14:08:01 -07:00 committed by GitHub
parent eed73de573
commit 9a5172e6c1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 72 additions and 1 deletions

View file

@ -110,15 +110,17 @@ async function extractAndParseReports(dir: string, shardFiles: string[], interna
const shardEvents: { file: string, localPath: string, metadata: BlobReportMetadata, parsedEvents: JsonEvent[] }[] = []; const shardEvents: { file: string, localPath: string, metadata: BlobReportMetadata, parsedEvents: JsonEvent[] }[] = [];
await fs.promises.mkdir(path.join(dir, 'resources'), { recursive: true }); await fs.promises.mkdir(path.join(dir, 'resources'), { recursive: true });
const reportNames = new UniqueFileNameGenerator();
for (const file of shardFiles) { for (const file of shardFiles) {
const absolutePath = path.join(dir, file); const absolutePath = path.join(dir, file);
printStatus(`extracting: ${relativeFilePath(absolutePath)}`); printStatus(`extracting: ${relativeFilePath(absolutePath)}`);
const zipFile = new ZipFile(absolutePath); const zipFile = new ZipFile(absolutePath);
const entryNames = await zipFile.entries(); const entryNames = await zipFile.entries();
for (const entryName of entryNames.sort()) { for (const entryName of entryNames.sort()) {
const fileName = path.join(dir, entryName); let fileName = path.join(dir, entryName);
const content = await zipFile.read(entryName); const content = await zipFile.read(entryName);
if (entryName.endsWith('.jsonl')) { if (entryName.endsWith('.jsonl')) {
fileName = reportNames.makeUnique(fileName);
const parsedEvents = parseCommonEvents(content); const parsedEvents = parseCommonEvents(content);
// Passing reviver to JSON.parse doesn't work, as the original strings // Passing reviver to JSON.parse doesn't work, as the original strings
// keep beeing used. To work around that we traverse the parsed events // keep beeing used. To work around that we traverse the parsed events
@ -285,6 +287,27 @@ function printStatusToStdout(message: string) {
process.stdout.write(`${message}\n`); process.stdout.write(`${message}\n`);
} }
class UniqueFileNameGenerator {
private _usedNames = new Set<string>();
makeUnique(name: string): string {
if (!this._usedNames.has(name)) {
this._usedNames.add(name);
return name;
}
const extension = path.extname(name);
name = name.substring(0, name.length - extension.length);
let index = 0;
while (true) {
const candidate = `${name}-${++index}${extension}`;
if (!this._usedNames.has(candidate)) {
this._usedNames.add(candidate);
return candidate;
}
}
}
}
class IdsPatcher { class IdsPatcher {
constructor(private _stringPool: StringInternPool, private _reportName: string | undefined, private _salt: string) { constructor(private _stringPool: StringInternPool, private _reportName: string | undefined, private _salt: string) {
} }

View file

@ -1324,3 +1324,51 @@ test('merge-reports should throw if report version is from the future', async ({
expect(output).toContain(`Error: Blob report report-2.zip was created with a newer version of Playwright.`); expect(output).toContain(`Error: Blob report report-2.zip was created with a newer version of Playwright.`);
}); });
test('should merge blob reports with same name', async ({ runInlineTest, mergeReports, showReport, page }) => {
const files = {
'playwright.config.ts': `
module.exports = {
retries: 1,
reporter: 'blob'
};
`,
'a.test.js': `
import { test, expect } from '@playwright/test';
test('math 1', async ({}) => {
expect(1 + 1).toBe(2);
});
test('failing 1', async ({}) => {
expect(1).toBe(2);
});
test('flaky 1', async ({}) => {
expect(test.info().retry).toBe(1);
});
test.skip('skipped 1', async ({}) => {});
`,
'b.test.js': `
import { test, expect } from '@playwright/test';
test('math 2', async ({}) => {
expect(1 + 1).toBe(2);
});
test('failing 2', async ({}) => {
expect(1).toBe(2);
});
test.skip('skipped 2', async ({}) => {});
`
};
await runInlineTest(files);
const reportZip = test.info().outputPath('blob-report', 'report.zip');
const allReportsDir = test.info().outputPath('all-blob-reports');
await fs.promises.cp(reportZip, path.join(allReportsDir, 'report-1.zip'));
await fs.promises.cp(reportZip, path.join(allReportsDir, 'report-2.zip'));
const { exitCode } = await mergeReports(allReportsDir, { 'PW_TEST_HTML_REPORT_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html'] });
expect(exitCode).toBe(0);
await showReport();
await expect(page.locator('.subnav-item:has-text("All") .counter')).toHaveText('14');
await expect(page.locator('.subnav-item:has-text("Passed") .counter')).toHaveText('4');
await expect(page.locator('.subnav-item:has-text("Failed") .counter')).toHaveText('4');
await expect(page.locator('.subnav-item:has-text("Flaky") .counter')).toHaveText('2');
await expect(page.locator('.subnav-item:has-text("Skipped") .counter')).toHaveText('4');
});