fix(blob): make sure tele reporters do not override ids (#31056)

Fixes https://github.com/microsoft/playwright/issues/31023
This commit is contained in:
Yury Semikhatsky 2024-05-28 16:37:11 -07:00 committed by GitHub
parent 6675652269
commit a11a6d9874
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 45 additions and 12 deletions

View File

@ -32,6 +32,9 @@ export class TeleReporterEmitter implements ReporterV2 {
private _messageSink: (message: teleReceiver.JsonEvent) => void; private _messageSink: (message: teleReceiver.JsonEvent) => void;
private _rootDir!: string; private _rootDir!: string;
private _emitterOptions: TeleReporterEmitterOptions; private _emitterOptions: TeleReporterEmitterOptions;
// In case there is blob reporter and UI mode, make sure one does override
// the id assigned by the other.
private readonly _idSymbol = Symbol('id');
constructor(messageSink: (message: teleReceiver.JsonEvent) => void, options: TeleReporterEmitterOptions = {}) { constructor(messageSink: (message: teleReceiver.JsonEvent) => void, options: TeleReporterEmitterOptions = {}) {
this._messageSink = messageSink; this._messageSink = messageSink;
@ -55,7 +58,7 @@ export class TeleReporterEmitter implements ReporterV2 {
} }
onTestBegin(test: reporterTypes.TestCase, result: reporterTypes.TestResult): void { onTestBegin(test: reporterTypes.TestCase, result: reporterTypes.TestResult): void {
(result as any)[idSymbol] = createGuid(); (result as any)[this._idSymbol] = createGuid();
this._messageSink({ this._messageSink({
method: 'onTestBegin', method: 'onTestBegin',
params: { params: {
@ -82,12 +85,12 @@ export class TeleReporterEmitter implements ReporterV2 {
} }
onStepBegin(test: reporterTypes.TestCase, result: reporterTypes.TestResult, step: reporterTypes.TestStep): void { onStepBegin(test: reporterTypes.TestCase, result: reporterTypes.TestResult, step: reporterTypes.TestStep): void {
(step as any)[idSymbol] = createGuid(); (step as any)[this._idSymbol] = createGuid();
this._messageSink({ this._messageSink({
method: 'onStepBegin', method: 'onStepBegin',
params: { params: {
testId: test.id, testId: test.id,
resultId: (result as any)[idSymbol], resultId: (result as any)[this._idSymbol],
step: this._serializeStepStart(step) step: this._serializeStepStart(step)
} }
}); });
@ -98,7 +101,7 @@ export class TeleReporterEmitter implements ReporterV2 {
method: 'onStepEnd', method: 'onStepEnd',
params: { params: {
testId: test.id, testId: test.id,
resultId: (result as any)[idSymbol], resultId: (result as any)[this._idSymbol],
step: this._serializeStepEnd(step) step: this._serializeStepEnd(step)
} }
}); });
@ -126,7 +129,7 @@ export class TeleReporterEmitter implements ReporterV2 {
const data = isBase64 ? chunk.toString('base64') : chunk; const data = isBase64 ? chunk.toString('base64') : chunk;
this._messageSink({ this._messageSink({
method: 'onStdIO', method: 'onStdIO',
params: { testId: test?.id, resultId: result ? (result as any)[idSymbol] : undefined, type, data, isBase64 } params: { testId: test?.id, resultId: result ? (result as any)[this._idSymbol] : undefined, type, data, isBase64 }
}); });
} }
@ -214,7 +217,7 @@ export class TeleReporterEmitter implements ReporterV2 {
private _serializeResultStart(result: reporterTypes.TestResult): teleReceiver.JsonTestResultStart { private _serializeResultStart(result: reporterTypes.TestResult): teleReceiver.JsonTestResultStart {
return { return {
id: (result as any)[idSymbol], id: (result as any)[this._idSymbol],
retry: result.retry, retry: result.retry,
workerIndex: result.workerIndex, workerIndex: result.workerIndex,
parallelIndex: result.parallelIndex, parallelIndex: result.parallelIndex,
@ -224,7 +227,7 @@ export class TeleReporterEmitter implements ReporterV2 {
private _serializeResultEnd(result: reporterTypes.TestResult): teleReceiver.JsonTestResultEnd { private _serializeResultEnd(result: reporterTypes.TestResult): teleReceiver.JsonTestResultEnd {
return { return {
id: (result as any)[idSymbol], id: (result as any)[this._idSymbol],
duration: result.duration, duration: result.duration,
status: result.status, status: result.status,
errors: result.errors, errors: result.errors,
@ -244,8 +247,8 @@ export class TeleReporterEmitter implements ReporterV2 {
private _serializeStepStart(step: reporterTypes.TestStep): teleReceiver.JsonTestStepStart { private _serializeStepStart(step: reporterTypes.TestStep): teleReceiver.JsonTestStepStart {
return { return {
id: (step as any)[idSymbol], id: (step as any)[this._idSymbol],
parentStepId: (step.parent as any)?.[idSymbol], parentStepId: (step.parent as any)?.[this._idSymbol],
title: step.title, title: step.title,
category: step.category, category: step.category,
startTime: +step.startTime, startTime: +step.startTime,
@ -255,7 +258,7 @@ export class TeleReporterEmitter implements ReporterV2 {
private _serializeStepEnd(step: reporterTypes.TestStep): teleReceiver.JsonTestStepEnd { private _serializeStepEnd(step: reporterTypes.TestStep): teleReceiver.JsonTestStepEnd {
return { return {
id: (step as any)[idSymbol], id: (step as any)[this._idSymbol],
duration: step.duration, duration: step.duration,
error: step.error, error: step.error,
}; };
@ -280,5 +283,3 @@ export class TeleReporterEmitter implements ReporterV2 {
return path.relative(this._rootDir, absolutePath); return path.relative(this._rootDir, absolutePath);
} }
} }
const idSymbol = Symbol('id');

View File

@ -292,6 +292,38 @@ test('should merge blob into blob', async ({ runInlineTest, mergeReports, showRe
} }
}); });
test('should produce consistent step ids', {
annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/31023' },
}, async ({ runInlineTest, mergeReports, showReport, page }) => {
const files = {
'playwright.config.ts': `
module.exports = {
retries: 1,
reporter: [
['blob', { outputFile: 'blob-report/report-1.zip' }],
['blob', { outputFile: 'blob-report/report-2.zip' }]
]
};
`,
'a.test.js': `
import { test, expect } from '@playwright/test';
test('math 1', async ({}) => {
expect(1 + 1).toBe(2);
});
`
};
await runInlineTest(files);
const reportDir = test.info().outputPath('blob-report');
const reportFiles = await fs.promises.readdir(reportDir);
reportFiles.sort();
expect(reportFiles).toEqual(['report-1.zip', 'report-2.zip']);
const { exitCode } = await mergeReports(reportDir, { 'PLAYWRIGHT_HTML_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html,json'] });
expect(exitCode).toBe(0);
await showReport();
await expect(page.locator('.subnav-item:has-text("All") .counter')).toHaveText('2');
await expect(page.locator('.subnav-item:has-text("Passed") .counter')).toHaveText('2');
});
test('be able to merge incomplete shards', async ({ runInlineTest, mergeReports, showReport, page }) => { test('be able to merge incomplete shards', async ({ runInlineTest, mergeReports, showReport, page }) => {
const reportDir = test.info().outputPath('blob-report'); const reportDir = test.info().outputPath('blob-report');
const files = { const files = {