mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
fix: serialize attachment to base64 in tele reporter (#23590)
This commit is contained in:
parent
a4d361379f
commit
400c7cd529
@ -88,12 +88,14 @@ export type JsonTestResultStart = {
|
||||
startTime: string;
|
||||
};
|
||||
|
||||
export type JsonAttachment = Omit<TestResult['attachments'][0], 'body'> & { base64?: string };
|
||||
|
||||
export type JsonTestResultEnd = {
|
||||
id: string;
|
||||
duration: number;
|
||||
status: TestStatus;
|
||||
errors: TestError[];
|
||||
attachments: TestResult['attachments'];
|
||||
attachments: JsonAttachment[];
|
||||
};
|
||||
|
||||
export type JsonTestStepStart = {
|
||||
@ -228,7 +230,7 @@ export class TeleReporterReceiver {
|
||||
result.status = payload.status;
|
||||
result.statusEx = payload.status;
|
||||
result.errors = payload.errors;
|
||||
result.attachments = payload.attachments;
|
||||
result.attachments = this._parseAttachments(payload.attachments);
|
||||
this._reporter.onTestEnd?.(test, result);
|
||||
}
|
||||
|
||||
@ -321,6 +323,15 @@ export class TeleReporterReceiver {
|
||||
};
|
||||
}
|
||||
|
||||
private _parseAttachments(attachments: JsonAttachment[]): TestResult['attachments'] {
|
||||
return attachments.map(a => {
|
||||
return {
|
||||
...a,
|
||||
body: a.base64 && (globalThis as any).Buffer ? Buffer.from(a.base64, 'base64') : undefined,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private _mergeSuitesInto(jsonSuites: JsonSuite[], parent: TeleSuite) {
|
||||
for (const jsonSuite of jsonSuites) {
|
||||
let targetSuite = parent.suites.find(s => s.title === jsonSuite.title);
|
||||
|
@ -21,7 +21,7 @@ import { mime } from 'playwright-core/lib/utilsBundle';
|
||||
import { Readable } from 'stream';
|
||||
import type { FullConfig, FullResult, TestResult } from '../../types/testReporter';
|
||||
import type { Suite } from '../common/test';
|
||||
import type { JsonEvent } from '../isomorphic/teleReceiver';
|
||||
import type { JsonAttachment, JsonEvent } from '../isomorphic/teleReceiver';
|
||||
import { TeleReporterEmitter } from './teleEmitter';
|
||||
|
||||
|
||||
@ -44,7 +44,7 @@ export class BlobReporter extends TeleReporterEmitter {
|
||||
private _reportFile!: string;
|
||||
|
||||
constructor(options: BlobReporterOptions) {
|
||||
super(message => this._messages.push(message));
|
||||
super(message => this._messages.push(message), false);
|
||||
this._options = options;
|
||||
this._salt = createGuid();
|
||||
|
||||
@ -78,8 +78,8 @@ export class BlobReporter extends TeleReporterEmitter {
|
||||
]);
|
||||
}
|
||||
|
||||
override _serializeAttachments(attachments: TestResult['attachments']): TestResult['attachments'] {
|
||||
return attachments.map(attachment => {
|
||||
override _serializeAttachments(attachments: TestResult['attachments']): JsonAttachment[] {
|
||||
return super._serializeAttachments(attachments).map(attachment => {
|
||||
if (!attachment.path || !fs.statSync(attachment.path).isFile())
|
||||
return attachment;
|
||||
// Add run guid to avoid clashes between shards.
|
||||
|
@ -20,15 +20,17 @@ import type { SuitePrivate } from '../../types/reporterPrivate';
|
||||
import type { FullConfig, FullResult, Location, Reporter, TestError, TestResult, TestStep } from '../../types/testReporter';
|
||||
import { FullConfigInternal, FullProjectInternal } from '../common/config';
|
||||
import type { Suite, TestCase } from '../common/test';
|
||||
import type { JsonConfig, JsonEvent, JsonProject, JsonStdIOType, JsonSuite, JsonTestCase, JsonTestEnd, JsonTestResultEnd, JsonTestResultStart, JsonTestStepEnd, JsonTestStepStart } from '../isomorphic/teleReceiver';
|
||||
import type { JsonAttachment, JsonConfig, JsonEvent, JsonProject, JsonStdIOType, JsonSuite, JsonTestCase, JsonTestEnd, JsonTestResultEnd, JsonTestResultStart, JsonTestStepEnd, JsonTestStepStart } from '../isomorphic/teleReceiver';
|
||||
import { serializeRegexPatterns } from '../isomorphic/teleReceiver';
|
||||
|
||||
export class TeleReporterEmitter implements Reporter {
|
||||
private _messageSink: (message: JsonEvent) => void;
|
||||
private _rootDir!: string;
|
||||
private _receiverIsInBrowser: boolean;
|
||||
|
||||
constructor(messageSink: (message: JsonEvent) => void) {
|
||||
constructor(messageSink: (message: JsonEvent) => void, receiverIsInBrowser: boolean) {
|
||||
this._messageSink = messageSink;
|
||||
this._receiverIsInBrowser = receiverIsInBrowser;
|
||||
}
|
||||
|
||||
onBegin(config: FullConfig, suite: Suite) {
|
||||
@ -199,8 +201,14 @@ export class TeleReporterEmitter implements Reporter {
|
||||
};
|
||||
}
|
||||
|
||||
_serializeAttachments(attachments: TestResult['attachments']): TestResult['attachments'] {
|
||||
return attachments;
|
||||
_serializeAttachments(attachments: TestResult['attachments']): JsonAttachment[] {
|
||||
return attachments.map(a => {
|
||||
return {
|
||||
...a,
|
||||
// There is no Buffer in the browser, so there is no point in sending the data there.
|
||||
base64: (a.body && !this._receiverIsInBrowser) ? a.body.toString('base64') : undefined,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private _serializeStepStart(step: TestStep): JsonTestStepStart {
|
||||
|
@ -158,7 +158,7 @@ class UIMode {
|
||||
}
|
||||
|
||||
private async _listTests() {
|
||||
const listReporter = new TeleReporterEmitter(e => this._dispatchEvent(e.method, e.params));
|
||||
const listReporter = new TeleReporterEmitter(e => this._dispatchEvent(e.method, e.params), true);
|
||||
const reporter = new InternalReporter([listReporter]);
|
||||
this._config.cliListOnly = true;
|
||||
this._config.testIdMatcher = undefined;
|
||||
@ -183,7 +183,7 @@ class UIMode {
|
||||
this._config.testIdMatcher = id => !testIdSet || testIdSet.has(id);
|
||||
|
||||
const reporters = await createReporters(this._config, 'ui');
|
||||
reporters.push(new TeleReporterEmitter(e => this._dispatchEvent(e.method, e.params)));
|
||||
reporters.push(new TeleReporterEmitter(e => this._dispatchEvent(e.method, e.params), true));
|
||||
const reporter = new InternalReporter(reporters);
|
||||
const taskRunner = createTaskRunnerForWatch(this._config, reporter);
|
||||
const testRun = new TestRun(this._config, reporter);
|
||||
|
@ -947,6 +947,59 @@ result.stderr: stderr text
|
||||
`);
|
||||
});
|
||||
|
||||
test('encode inline attachments', async ({ runInlineTest, mergeReports }) => {
|
||||
const reportDir = test.info().outputPath('blob-report');
|
||||
const files = {
|
||||
'echo-reporter.js': `
|
||||
import fs from 'fs';
|
||||
|
||||
class EchoReporter {
|
||||
onTestEnd(test, result) {
|
||||
const attachmentBodies = result.attachments.map(a => a.body?.toString('base64'));
|
||||
result.attachments.forEach(a => console.log(a.body, 'isBuffer', Buffer.isBuffer(a.body)));
|
||||
fs.writeFileSync('log.txt', attachmentBodies.join(','));
|
||||
}
|
||||
}
|
||||
module.exports = EchoReporter;
|
||||
`,
|
||||
'playwright.config.js': `
|
||||
module.exports = {
|
||||
reporter: [['blob']]
|
||||
};
|
||||
`,
|
||||
'a.test.js': `
|
||||
import { test, expect } from '@playwright/test';
|
||||
test('a test', async ({}) => {
|
||||
expect(1 + 1).toBe(2);
|
||||
test.info().attachments.push({
|
||||
name: 'example.txt',
|
||||
contentType: 'text/plain',
|
||||
body: Buffer.from('foo'),
|
||||
});
|
||||
|
||||
test.info().attachments.push({
|
||||
name: 'example.json',
|
||||
contentType: 'application/json',
|
||||
body: Buffer.from(JSON.stringify({ foo: 1 })),
|
||||
});
|
||||
|
||||
test.info().attachments.push({
|
||||
name: 'example-utf16.txt',
|
||||
contentType: 'text/plain, charset=utf16le',
|
||||
body: Buffer.from('utf16 encoded', 'utf16le'),
|
||||
});
|
||||
});
|
||||
`,
|
||||
};
|
||||
|
||||
await runInlineTest(files);
|
||||
|
||||
const { exitCode } = await mergeReports(reportDir, {}, { additionalArgs: ['--reporter', test.info().outputPath('echo-reporter.js')] });
|
||||
expect(exitCode).toBe(0);
|
||||
const log = fs.readFileSync(test.info().outputPath('log.txt')).toString();
|
||||
expect(log).toBe(`Zm9v,eyJmb28iOjF9,dQB0AGYAMQA2ACAAZQBuAGMAbwBkAGUAZAA=`);
|
||||
});
|
||||
|
||||
test('preserve steps in html report', async ({ runInlineTest, mergeReports, showReport, page }) => {
|
||||
const reportDir = test.info().outputPath('blob-report');
|
||||
const files = {
|
||||
|
Loading…
x
Reference in New Issue
Block a user