chore(blob): drop shard number from report name (#24270)
We store shard number in the report metadata event and then sort shard files by shard number. This guarantees that within each project sharded events will always go in stable order.
This commit is contained in:
parent
b24ec33aa9
commit
d0280ec8c7
|
|
@ -33,6 +33,7 @@ type BlobReporterOptions = {
|
||||||
|
|
||||||
export type BlobReportMetadata = {
|
export type BlobReportMetadata = {
|
||||||
projectSuffix?: string;
|
projectSuffix?: string;
|
||||||
|
shard?: { total: number, current: number };
|
||||||
};
|
};
|
||||||
|
|
||||||
export class BlobReporter extends TeleReporterEmitter {
|
export class BlobReporter extends TeleReporterEmitter {
|
||||||
|
|
@ -48,18 +49,19 @@ export class BlobReporter extends TeleReporterEmitter {
|
||||||
super(message => this._messages.push(message), false);
|
super(message => this._messages.push(message), false);
|
||||||
this._options = options;
|
this._options = options;
|
||||||
this._salt = createGuid();
|
this._salt = createGuid();
|
||||||
|
|
||||||
this._messages.push({
|
|
||||||
method: 'onBlobReportMetadata',
|
|
||||||
params: {
|
|
||||||
projectSuffix: process.env.PWTEST_BLOB_SUFFIX,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override onConfigure(config: FullConfig) {
|
override onConfigure(config: FullConfig) {
|
||||||
this._outputDir = path.resolve(this._options.configDir, this._options.outputDir || 'blob-report');
|
this._outputDir = path.resolve(this._options.configDir, this._options.outputDir || 'blob-report');
|
||||||
this._reportName = this._computeReportName(config);
|
this._reportName = `report-${createGuid()}`;
|
||||||
|
const metadata: BlobReportMetadata = {
|
||||||
|
projectSuffix: process.env.PWTEST_BLOB_SUFFIX,
|
||||||
|
shard: config.shard ? config.shard : undefined,
|
||||||
|
};
|
||||||
|
this._messages.push({
|
||||||
|
method: 'onBlobReportMetadata',
|
||||||
|
params: metadata
|
||||||
|
});
|
||||||
super.onConfigure(config);
|
super.onConfigure(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -109,15 +111,6 @@ export class BlobReporter extends TeleReporterEmitter {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _computeReportName(config: FullConfig) {
|
|
||||||
let shardSuffix = '';
|
|
||||||
if (config.shard) {
|
|
||||||
const paddedNumber = `${config.shard.current}`.padStart(`${config.shard.total}`.length, '0');
|
|
||||||
shardSuffix = `${paddedNumber}-of-${config.shard.total}-`;
|
|
||||||
}
|
|
||||||
return `report-${shardSuffix}${createGuid()}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _startCopyingFile(from: string, to: string) {
|
private _startCopyingFile(from: string, to: string) {
|
||||||
const copyPromise: Promise<void> = fs.promises.copyFile(from, to)
|
const copyPromise: Promise<void> = fs.promises.copyFile(from, to)
|
||||||
.catch(e => { console.error(`Failed to copy file from "${from}" to "${to}": ${e}`); })
|
.catch(e => { console.error(`Failed to copy file from "${from}" to "${to}": ${e}`); })
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import { TeleReporterReceiver } from '../isomorphic/teleReceiver';
|
||||||
import { createReporters } from '../runner/reporters';
|
import { createReporters } from '../runner/reporters';
|
||||||
import { Multiplexer } from './multiplexer';
|
import { Multiplexer } from './multiplexer';
|
||||||
import { ZipFile } from 'playwright-core/lib/utils';
|
import { ZipFile } from 'playwright-core/lib/utils';
|
||||||
|
import type { BlobReportMetadata } from './blob';
|
||||||
|
|
||||||
export async function createMergedReport(config: FullConfigInternal, dir: string, reporterDescriptions: ReporterDescription[], resolvePaths: boolean) {
|
export async function createMergedReport(config: FullConfigInternal, dir: string, reporterDescriptions: ReporterDescription[], resolvePaths: boolean) {
|
||||||
const shardFiles = await sortedShardFiles(dir);
|
const shardFiles = await sortedShardFiles(dir);
|
||||||
|
|
@ -71,14 +72,32 @@ async function extractReportFromZip(file: string): Promise<Buffer> {
|
||||||
throw new Error(`Cannot find *.jsonl file in ${file}`);
|
throw new Error(`Cannot find *.jsonl file in ${file}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function findMetadata(events: JsonEvent[], file: string): BlobReportMetadata {
|
||||||
|
if (events[0]?.method !== 'onBlobReportMetadata')
|
||||||
|
throw new Error(`No metadata event found in ${file}`);
|
||||||
|
return events[0].params;
|
||||||
|
}
|
||||||
|
|
||||||
async function mergeEvents(dir: string, shardReportFiles: string[]) {
|
async function mergeEvents(dir: string, shardReportFiles: string[]) {
|
||||||
const events: JsonEvent[] = [];
|
const events: JsonEvent[] = [];
|
||||||
const configureEvents: JsonEvent[] = [];
|
const configureEvents: JsonEvent[] = [];
|
||||||
const beginEvents: JsonEvent[] = [];
|
const beginEvents: JsonEvent[] = [];
|
||||||
const endEvents: JsonEvent[] = [];
|
const endEvents: JsonEvent[] = [];
|
||||||
|
const shardEvents: { metadata: BlobReportMetadata, parsedEvents: JsonEvent[] }[] = [];
|
||||||
for (const reportFile of shardReportFiles) {
|
for (const reportFile of shardReportFiles) {
|
||||||
const reportJsonl = await extractReportFromZip(path.join(dir, reportFile));
|
const reportJsonl = await extractReportFromZip(path.join(dir, reportFile));
|
||||||
const parsedEvents = parseEvents(reportJsonl);
|
const parsedEvents = parseEvents(reportJsonl);
|
||||||
|
shardEvents.push({
|
||||||
|
metadata: findMetadata(parsedEvents, reportFile),
|
||||||
|
parsedEvents
|
||||||
|
});
|
||||||
|
}
|
||||||
|
shardEvents.sort((a, b) => {
|
||||||
|
const shardA = a.metadata.shard?.current ?? 0;
|
||||||
|
const shardB = b.metadata.shard?.current ?? 0;
|
||||||
|
return shardA - shardB;
|
||||||
|
});
|
||||||
|
for (const { parsedEvents } of shardEvents) {
|
||||||
for (const event of parsedEvents) {
|
for (const event of parsedEvents) {
|
||||||
if (event.method === 'onConfigure')
|
if (event.method === 'onConfigure')
|
||||||
configureEvents.push(event);
|
configureEvents.push(event);
|
||||||
|
|
|
||||||
|
|
@ -130,7 +130,7 @@ test('should call methods in right order', async ({ runInlineTest, mergeReports
|
||||||
await runInlineTest(files, { shard: `3/3` });
|
await runInlineTest(files, { shard: `3/3` });
|
||||||
const reportFiles = await fs.promises.readdir(reportDir);
|
const reportFiles = await fs.promises.readdir(reportDir);
|
||||||
reportFiles.sort();
|
reportFiles.sort();
|
||||||
expect(reportFiles).toEqual([expect.stringMatching(/report-1-of-3.*.zip/), expect.stringMatching(/report-3-of-3.*.zip/), 'resources']);
|
expect(reportFiles).toEqual([expect.stringMatching(/report-.*.zip/), expect.stringMatching(/report-.*.zip/), 'resources']);
|
||||||
const { exitCode, output } = await mergeReports(reportDir, {}, { additionalArgs: ['--reporter', test.info().outputPath('echo-reporter.js')] });
|
const { exitCode, output } = await mergeReports(reportDir, {}, { additionalArgs: ['--reporter', test.info().outputPath('echo-reporter.js')] });
|
||||||
expect(exitCode).toBe(0);
|
expect(exitCode).toBe(0);
|
||||||
const lines = output.split('\n').filter(l => l.trim().length);
|
const lines = output.split('\n').filter(l => l.trim().length);
|
||||||
|
|
@ -194,7 +194,7 @@ test('should merge into html', async ({ runInlineTest, mergeReports, showReport,
|
||||||
await runInlineTest(files, { shard: `${i + 1}/${totalShards}` });
|
await runInlineTest(files, { shard: `${i + 1}/${totalShards}` });
|
||||||
const reportFiles = await fs.promises.readdir(reportDir);
|
const reportFiles = await fs.promises.readdir(reportDir);
|
||||||
reportFiles.sort();
|
reportFiles.sort();
|
||||||
expect(reportFiles).toEqual([expect.stringMatching(/report-1-of-3.*.zip/), expect.stringMatching(/report-2-of-3.*.zip/), expect.stringMatching(/report-3-of-3.*.zip/), 'resources']);
|
expect(reportFiles).toEqual([expect.stringMatching(/report-.*.zip/), expect.stringMatching(/report-.*.zip/), expect.stringMatching(/report-.*.zip/), 'resources']);
|
||||||
const { exitCode, output } = await mergeReports(reportDir, { 'PW_TEST_HTML_REPORT_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html'] });
|
const { exitCode, output } = await mergeReports(reportDir, { 'PW_TEST_HTML_REPORT_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html'] });
|
||||||
expect(exitCode).toBe(0);
|
expect(exitCode).toBe(0);
|
||||||
|
|
||||||
|
|
@ -254,7 +254,7 @@ test('be able to merge incomplete shards', async ({ runInlineTest, mergeReports,
|
||||||
|
|
||||||
const reportFiles = await fs.promises.readdir(reportDir);
|
const reportFiles = await fs.promises.readdir(reportDir);
|
||||||
reportFiles.sort();
|
reportFiles.sort();
|
||||||
expect(reportFiles).toEqual([expect.stringMatching(/report-1-of-3.*.zip/), expect.stringMatching(/report-3-of-3.*.zip/), 'resources']);
|
expect(reportFiles).toEqual([expect.stringMatching(/report-.*.zip/), expect.stringMatching(/report-.*.zip/), 'resources']);
|
||||||
const { exitCode } = await mergeReports(reportDir, { 'PW_TEST_HTML_REPORT_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html'] });
|
const { exitCode } = await mergeReports(reportDir, { 'PW_TEST_HTML_REPORT_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html'] });
|
||||||
expect(exitCode).toBe(0);
|
expect(exitCode).toBe(0);
|
||||||
|
|
||||||
|
|
@ -360,7 +360,7 @@ test('merge into list report by default', async ({ runInlineTest, mergeReports }
|
||||||
await runInlineTest(files, { shard: `${i + 1}/${totalShards}` });
|
await runInlineTest(files, { shard: `${i + 1}/${totalShards}` });
|
||||||
const reportFiles = await fs.promises.readdir(reportDir);
|
const reportFiles = await fs.promises.readdir(reportDir);
|
||||||
reportFiles.sort();
|
reportFiles.sort();
|
||||||
expect(reportFiles).toEqual([expect.stringMatching(/report-1-of-3.*.zip/), expect.stringMatching(/report-2-of-3.*.zip/), expect.stringMatching(/report-3-of-3.*.zip/), 'resources']);
|
expect(reportFiles).toEqual([expect.stringMatching(/report-.*.zip/), expect.stringMatching(/report-.*.zip/), expect.stringMatching(/report-.*.zip/), 'resources']);
|
||||||
const { exitCode, output } = await mergeReports(reportDir, { PW_TEST_DEBUG_REPORTERS: '1', PW_TEST_DEBUG_REPORTERS_PRINT_STEPS: '1', PWTEST_TTY_WIDTH: '80' }, { additionalArgs: ['--reporter', 'list'] });
|
const { exitCode, output } = await mergeReports(reportDir, { PW_TEST_DEBUG_REPORTERS: '1', PW_TEST_DEBUG_REPORTERS_PRINT_STEPS: '1', PWTEST_TTY_WIDTH: '80' }, { additionalArgs: ['--reporter', 'list'] });
|
||||||
expect(exitCode).toBe(0);
|
expect(exitCode).toBe(0);
|
||||||
|
|
||||||
|
|
@ -431,7 +431,7 @@ test('preserve attachments', async ({ runInlineTest, mergeReports, showReport, p
|
||||||
|
|
||||||
const reportFiles = await fs.promises.readdir(reportDir);
|
const reportFiles = await fs.promises.readdir(reportDir);
|
||||||
reportFiles.sort();
|
reportFiles.sort();
|
||||||
expect(reportFiles).toEqual([expect.stringMatching(/report-1-of-2.*.zip/), 'resources']);
|
expect(reportFiles).toEqual([expect.stringMatching(/report-.*.zip/), 'resources']);
|
||||||
const { exitCode } = await mergeReports(reportDir, { 'PW_TEST_HTML_REPORT_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html'] });
|
const { exitCode } = await mergeReports(reportDir, { 'PW_TEST_HTML_REPORT_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html'] });
|
||||||
expect(exitCode).toBe(0);
|
expect(exitCode).toBe(0);
|
||||||
|
|
||||||
|
|
@ -494,7 +494,7 @@ test('generate html with attachment urls', async ({ runInlineTest, mergeReports,
|
||||||
|
|
||||||
const reportFiles = await fs.promises.readdir(reportDir);
|
const reportFiles = await fs.promises.readdir(reportDir);
|
||||||
reportFiles.sort();
|
reportFiles.sort();
|
||||||
expect(reportFiles).toEqual([expect.stringMatching(/report-1-of-2.*.zip/), 'resources']);
|
expect(reportFiles).toEqual([expect.stringMatching(/report-.*.zip/), 'resources']);
|
||||||
const { exitCode } = await mergeReports(reportDir, { 'PW_TEST_HTML_REPORT_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html', '--attachments', 'missing'] });
|
const { exitCode } = await mergeReports(reportDir, { 'PW_TEST_HTML_REPORT_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html', '--attachments', 'missing'] });
|
||||||
expect(exitCode).toBe(0);
|
expect(exitCode).toBe(0);
|
||||||
|
|
||||||
|
|
@ -568,7 +568,7 @@ test('resource names should not clash between runs', async ({ runInlineTest, sho
|
||||||
|
|
||||||
const reportFiles = await fs.promises.readdir(reportDir);
|
const reportFiles = await fs.promises.readdir(reportDir);
|
||||||
reportFiles.sort();
|
reportFiles.sort();
|
||||||
expect(reportFiles).toEqual([expect.stringMatching(/report-1-of-2.*.zip/), expect.stringMatching(/report-2-of-2.*.zip/), 'resources']);
|
expect(reportFiles).toEqual([expect.stringMatching(/report-.*.zip/), expect.stringMatching(/report-.*.zip/), 'resources']);
|
||||||
|
|
||||||
const { exitCode } = await mergeReports(reportDir, {}, { additionalArgs: ['--reporter', 'html'] });
|
const { exitCode } = await mergeReports(reportDir, {}, { additionalArgs: ['--reporter', 'html'] });
|
||||||
expect(exitCode).toBe(0);
|
expect(exitCode).toBe(0);
|
||||||
|
|
@ -643,7 +643,7 @@ test('multiple output reports', async ({ runInlineTest, mergeReports, showReport
|
||||||
|
|
||||||
const reportFiles = await fs.promises.readdir(reportDir);
|
const reportFiles = await fs.promises.readdir(reportDir);
|
||||||
reportFiles.sort();
|
reportFiles.sort();
|
||||||
expect(reportFiles).toEqual([expect.stringMatching(/report-1-of-2.*.zip/), 'resources']);
|
expect(reportFiles).toEqual([expect.stringMatching(/report-.*.zip/), 'resources']);
|
||||||
const { exitCode, output } = await mergeReports(reportDir, { 'PW_TEST_HTML_REPORT_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html,line'] });
|
const { exitCode, output } = await mergeReports(reportDir, { 'PW_TEST_HTML_REPORT_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html,line'] });
|
||||||
expect(exitCode).toBe(0);
|
expect(exitCode).toBe(0);
|
||||||
|
|
||||||
|
|
@ -704,7 +704,7 @@ test('multiple output reports based on config', async ({ runInlineTest, mergeRep
|
||||||
|
|
||||||
const reportFiles = await fs.promises.readdir(reportDir);
|
const reportFiles = await fs.promises.readdir(reportDir);
|
||||||
reportFiles.sort();
|
reportFiles.sort();
|
||||||
expect(reportFiles).toEqual([expect.stringMatching(/report-1-of-2.*.zip/), expect.stringMatching(/report-2-of-2.*.zip/), 'resources']);
|
expect(reportFiles).toEqual([expect.stringMatching(/report-.*.zip/), expect.stringMatching(/report-.*.zip/), 'resources']);
|
||||||
const { exitCode, output } = await mergeReports(reportDir, undefined, { additionalArgs: ['--config', test.info().outputPath('merged/playwright.config.ts')] });
|
const { exitCode, output } = await mergeReports(reportDir, undefined, { additionalArgs: ['--config', test.info().outputPath('merged/playwright.config.ts')] });
|
||||||
expect(exitCode).toBe(0);
|
expect(exitCode).toBe(0);
|
||||||
|
|
||||||
|
|
@ -850,7 +850,7 @@ test('preserve config fields', async ({ runInlineTest, mergeReports }) => {
|
||||||
|
|
||||||
const reportFiles = await fs.promises.readdir(reportDir);
|
const reportFiles = await fs.promises.readdir(reportDir);
|
||||||
reportFiles.sort();
|
reportFiles.sort();
|
||||||
expect(reportFiles).toEqual([expect.stringMatching(/report-1-of-3.*.zip/), expect.stringMatching(/report-3-of-3.*.zip/), 'resources']);
|
expect(reportFiles).toEqual([expect.stringMatching(/report-.*.zip/), expect.stringMatching(/report-.*.zip/), 'resources']);
|
||||||
const { exitCode } = await mergeReports(reportDir, {}, { additionalArgs: ['--reporter', test.info().outputPath('echo-reporter.js'), '-c', test.info().outputPath('merge.config.ts')] });
|
const { exitCode } = await mergeReports(reportDir, {}, { additionalArgs: ['--reporter', test.info().outputPath('echo-reporter.js'), '-c', test.info().outputPath('merge.config.ts')] });
|
||||||
expect(exitCode).toBe(0);
|
expect(exitCode).toBe(0);
|
||||||
const json = JSON.parse(fs.readFileSync(test.info().outputPath('config.json')).toString());
|
const json = JSON.parse(fs.readFileSync(test.info().outputPath('config.json')).toString());
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue