chore(blob): change file name to report{-suffix}{-shard}.zip (#24592)

This commit is contained in:
Yury Semikhatsky 2023-08-03 13:23:30 -07:00 committed by GitHub
parent be1e8e061e
commit 8e2f33673b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 47 additions and 32 deletions

View File

@ -49,3 +49,7 @@ export async function copyFileAndMakeWritable(from: string, to: string) {
await fs.promises.copyFile(from, to);
await fs.promises.chmod(to, 0o664);
}
export function sanitizeForFilePath(s: string) {
return s.replace(/[\x00-\x2C\x2E-\x2F\x3A-\x40\x5B-\x60\x7B-\x7F]+/g, '-');
}

View File

@ -19,10 +19,10 @@ import type { Page as PageEx } from 'playwright-core/lib/client/page';
import type { Locator as LocatorEx } from 'playwright-core/lib/client/locator';
import { currentTestInfo, currentExpectTimeout } from '../common/globals';
import type { ImageComparatorOptions, Comparator } from 'playwright-core/lib/utils';
import { getComparator } from 'playwright-core/lib/utils';
import { getComparator, sanitizeForFilePath } from 'playwright-core/lib/utils';
import type { PageScreenshotOptions } from 'playwright-core/types/types';
import {
addSuffixToFilePath, serializeError, sanitizeForFilePath,
addSuffixToFilePath, serializeError,
trimLongString, callLogText,
expectTypes } from '../util';
import { colors } from 'playwright-core/lib/utilsBundle';

View File

@ -16,7 +16,7 @@
import fs from 'fs';
import path from 'path';
import { ManualPromise, calculateSha1, createGuid, removeFolders } from 'playwright-core/lib/utils';
import { ManualPromise, calculateSha1, createGuid, removeFolders, sanitizeForFilePath } from 'playwright-core/lib/utils';
import { mime } from 'playwright-core/lib/utilsBundle';
import { Readable } from 'stream';
import type { EventEmitter } from 'events';
@ -44,6 +44,7 @@ export class BlobReporter extends TeleReporterEmitter {
private readonly _attachments: { originalPath: string, zipEntryPath: string }[] = [];
private readonly _options: BlobReporterOptions;
private readonly _salt: string;
private _reportName!: string;
constructor(options: BlobReporterOptions) {
super(message => this._messages.push(message), false);
@ -62,6 +63,7 @@ export class BlobReporter extends TeleReporterEmitter {
params: metadata
});
this._reportName = this._computeReportName(config);
super.onConfigure(config);
}
@ -73,16 +75,14 @@ export class BlobReporter extends TeleReporterEmitter {
await removeFolders([outputDir]);
await fs.promises.mkdir(outputDir, { recursive: true });
const reportName = `report-${createGuid()}`;
const zipFile = new yazl.ZipFile();
const zipFinishPromise = new ManualPromise<undefined>();
const finishPromise = zipFinishPromise.catch(e => {
throw new Error(`Failed to write report ${reportName + '.zip'}: ` + e.message);
throw new Error(`Failed to write report ${this._reportName + '.zip'}: ` + e.message);
});
(zipFile as any as EventEmitter).on('error', error => zipFinishPromise.reject(error));
const zipFileName = path.join(outputDir, reportName + '.zip');
const zipFileName = path.join(outputDir, this._reportName + '.zip');
zipFile.outputStream.pipe(fs.createWriteStream(zipFileName)).on('close', () => {
zipFinishPromise.resolve(undefined);
}).on('error', error => zipFinishPromise.reject(error));
@ -95,12 +95,23 @@ export class BlobReporter extends TeleReporterEmitter {
const lines = this._messages.map(m => JSON.stringify(m) + '\n');
const content = Readable.from(lines);
zipFile.addReadStream(content, reportName + '.jsonl');
zipFile.addReadStream(content, this._reportName + '.jsonl');
zipFile.end();
await finishPromise;
}
private _computeReportName(config: FullConfig) {
let reportName = 'report';
if (process.env.PWTEST_BLOB_SUFFIX)
reportName += sanitizeForFilePath(process.env.PWTEST_BLOB_SUFFIX);
if (config.shard) {
const paddedNumber = `${config.shard.current}`.padStart(`${config.shard.total}`.length, '0');
reportName += `-${paddedNumber}`;
}
return reportName;
}
override _serializeAttachments(attachments: TestResult['attachments']): JsonAttachment[] {
return super._serializeAttachments(attachments).map(attachment => {
if (!attachment.path)

View File

@ -20,11 +20,11 @@ import path from 'path';
import type { TransformCallback } from 'stream';
import { Transform } from 'stream';
import type { FullConfig, Suite } from '../../types/testReporter';
import { HttpServer, assert, calculateSha1, copyFileAndMakeWritable, gracefullyProcessExitDoNotHang, removeFolders } from 'playwright-core/lib/utils';
import { HttpServer, assert, calculateSha1, copyFileAndMakeWritable, gracefullyProcessExitDoNotHang, removeFolders, sanitizeForFilePath } from 'playwright-core/lib/utils';
import type { JsonAttachment, JsonReport, JsonSuite, JsonTestCase, JsonTestResult, JsonTestStep } from './raw';
import RawReporter from './raw';
import { stripAnsiEscapes } from './base';
import { resolveReporterOutputPath, sanitizeForFilePath } from '../util';
import { resolveReporterOutputPath } from '../util';
import type { Metadata } from '../../types/test';
import type { ZipFile } from 'playwright-core/lib/zipBundle';
import { yazl } from 'playwright-core/lib/zipBundle';

View File

@ -229,7 +229,7 @@ function mergeEndEvents(endEvents: JsonEvent[]): JsonEvent {
async function sortedShardFiles(dir: string) {
const files = await fs.promises.readdir(dir);
return files.filter(file => file.startsWith('report-') && file.endsWith('.zip')).sort();
return files.filter(file => file.startsWith('report') && file.endsWith('.zip')).sort();
}
function printStatusToStdout(message: string) {

View File

@ -17,8 +17,7 @@
import fs from 'fs';
import path from 'path';
import type { FullConfig, Location, Suite, TestCase, TestResult, TestStatus, TestStep } from '../../types/testReporter';
import { assert } from 'playwright-core/lib/utils';
import { sanitizeForFilePath } from '../util';
import { assert, sanitizeForFilePath } from 'playwright-core/lib/utils';
import { formatResultFailure } from './base';
import { toPosixPath, serializePatterns } from './json';
import { MultiMap } from 'playwright-core/lib/utils';

View File

@ -23,7 +23,7 @@ import url from 'url';
import { colors, debug, minimatch, parseStackTraceLine } from 'playwright-core/lib/utilsBundle';
import type { TestInfoError } from './../types/test';
import type { Location } from './../types/testReporter';
import { calculateSha1, isRegExp, isString } from 'playwright-core/lib/utils';
import { calculateSha1, isRegExp, isString, sanitizeForFilePath } from 'playwright-core/lib/utils';
import type { RawStack } from 'playwright-core/lib/utils';
const PLAYWRIGHT_TEST_PATH = path.join(__dirname, '..');
@ -195,10 +195,6 @@ export function expectTypes(receiver: any, types: string[], matcherName: string)
}
}
export function sanitizeForFilePath(s: string) {
return s.replace(/[\x00-\x2C\x2E-\x2F\x3A-\x40\x5B-\x60\x7B-\x7F]+/g, '-');
}
export function trimLongString(s: string, length = 100) {
if (s.length <= length)
return s;

View File

@ -16,14 +16,14 @@
import fs from 'fs';
import path from 'path';
import { MaxTime, captureRawStack, createAfterActionTraceEventForStep, createBeforeActionTraceEventForStep, monotonicTime, zones } from 'playwright-core/lib/utils';
import { MaxTime, captureRawStack, createAfterActionTraceEventForStep, createBeforeActionTraceEventForStep, monotonicTime, zones, sanitizeForFilePath } from 'playwright-core/lib/utils';
import type { TestInfoError, TestInfo, TestStatus, FullProject, FullConfig } from '../../types/test';
import type { AttachmentPayload, StepBeginPayload, StepEndPayload, WorkerInitParams } from '../common/ipc';
import type { TestCase } from '../common/test';
import { TimeoutManager } from './timeoutManager';
import type { Annotation, FullConfigInternal, FullProjectInternal } from '../common/config';
import type { Location } from '../../types/testReporter';
import { getContainedPath, normalizeAndSaveAttachment, sanitizeForFilePath, serializeError, trimLongString } from '../util';
import { getContainedPath, normalizeAndSaveAttachment, serializeError, trimLongString } from '../util';
import type * as trace from '@trace/trace';
export interface TestStepInternal {

View File

@ -381,7 +381,7 @@ test('merge into list report by default', async ({ runInlineTest, mergeReports }
await runInlineTest(files, { shard: `${i + 1}/${totalShards}` }, { PWTEST_BLOB_DO_NOT_REMOVE: '1' });
const reportFiles = await fs.promises.readdir(reportDir);
reportFiles.sort();
expect(reportFiles).toEqual([expect.stringMatching(/report-.*.zip/), expect.stringMatching(/report-.*.zip/), expect.stringMatching(/report-.*.zip/)]);
expect(reportFiles).toEqual(['report-1.zip', 'report-2.zip', 'report-3.zip']);
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);
@ -460,7 +460,7 @@ test('should print progress', async ({ runInlineTest, mergeReports }) => {
await runInlineTest(files, { shard: `2/2` }, { PWTEST_BLOB_DO_NOT_REMOVE: '1' });
const reportFiles = await fs.promises.readdir(reportDir);
reportFiles.sort();
expect(reportFiles).toEqual([expect.stringMatching(/report-.*.zip/), expect.stringMatching(/report-.*.zip/)]);
expect(reportFiles).toEqual(['report-1.zip', 'report-2.zip']);
const { exitCode, output } = await mergeReports(reportDir, { PW_TEST_HTML_REPORT_OPEN: 'never' }, { additionalArgs: ['--reporter', 'html'] });
expect(exitCode).toBe(0);
@ -511,7 +511,7 @@ test('preserve attachments', async ({ runInlineTest, mergeReports, showReport, p
const reportFiles = await fs.promises.readdir(reportDir);
reportFiles.sort();
expect(reportFiles).toEqual([expect.stringMatching(/report-.*.zip/)]);
expect(reportFiles).toEqual(['report-1.zip']);
const { exitCode } = await mergeReports(reportDir, { 'PW_TEST_HTML_REPORT_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html'] });
expect(exitCode).toBe(0);
@ -574,7 +574,7 @@ test('generate html with attachment urls', async ({ runInlineTest, mergeReports,
const reportFiles = await fs.promises.readdir(reportDir);
reportFiles.sort();
expect(reportFiles).toEqual([expect.stringMatching(/report-.*.zip/)]);
expect(reportFiles).toEqual(['report-1.zip']);
const { exitCode } = await mergeReports(reportDir, { 'PW_TEST_HTML_REPORT_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html'] });
expect(exitCode).toBe(0);
@ -648,7 +648,7 @@ test('resource names should not clash between runs', async ({ runInlineTest, sho
const reportFiles = await fs.promises.readdir(reportDir);
reportFiles.sort();
expect(reportFiles).toEqual([expect.stringMatching(/report-.*.zip/), expect.stringMatching(/report-.*.zip/)]);
expect(reportFiles).toEqual(['report-1.zip', 'report-2.zip']);
const { exitCode } = await mergeReports(reportDir, {}, { additionalArgs: ['--reporter', 'html'] });
expect(exitCode).toBe(0);
@ -723,7 +723,7 @@ test('multiple output reports', async ({ runInlineTest, mergeReports, showReport
const reportFiles = await fs.promises.readdir(reportDir);
reportFiles.sort();
expect(reportFiles).toEqual([expect.stringMatching(/report-.*.zip/)]);
expect(reportFiles).toEqual(['report-1.zip']);
const { exitCode, output } = await mergeReports(reportDir, { 'PW_TEST_HTML_REPORT_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html,line'] });
expect(exitCode).toBe(0);
@ -784,7 +784,7 @@ test('multiple output reports based on config', async ({ runInlineTest, mergeRep
const reportFiles = await fs.promises.readdir(reportDir);
reportFiles.sort();
expect(reportFiles).toEqual([expect.stringMatching(/report-.*.zip/), expect.stringMatching(/report-.*.zip/)]);
expect(reportFiles).toEqual(['report-1.zip', 'report-2.zip']);
const { exitCode, output } = await mergeReports(reportDir, undefined, { additionalArgs: ['--config', test.info().outputPath('merged/playwright.config.ts')] });
expect(exitCode).toBe(0);
@ -799,7 +799,7 @@ test('multiple output reports based on config', async ({ runInlineTest, mergeRep
// Check report presence.
const mergedBlobReportFiles = await fs.promises.readdir(test.info().outputPath('merged/merged-blob'));
expect(mergedBlobReportFiles).toEqual([expect.stringMatching(/report.*.zip/)]);
expect(mergedBlobReportFiles).toEqual(['report.zip']);
});
test('onError in the report', async ({ runInlineTest, mergeReports, showReport, page }) => {
@ -930,7 +930,7 @@ test('preserve config fields', async ({ runInlineTest, mergeReports }) => {
const reportFiles = await fs.promises.readdir(reportDir);
reportFiles.sort();
expect(reportFiles).toEqual([expect.stringMatching(/report-.*.zip/), expect.stringMatching(/report-.*.zip/)]);
expect(reportFiles).toEqual(['report-1.zip', 'report-3.zip']);
const { exitCode } = await mergeReports(reportDir, {}, { additionalArgs: ['--reporter', test.info().outputPath('echo-reporter.js'), '-c', test.info().outputPath('merge.config.ts')] });
expect(exitCode).toBe(0);
const json = JSON.parse(fs.readFileSync(test.info().outputPath('config.json')).toString());
@ -1087,7 +1087,7 @@ test('preserve steps in html report', async ({ runInlineTest, mergeReports, show
await runInlineTest(files);
const reportFiles = await fs.promises.readdir(reportDir);
reportFiles.sort();
expect(reportFiles).toEqual([expect.stringMatching(/report-.*.zip/)]);
expect(reportFiles).toEqual(['report.zip']);
// Run merger in a different directory to make sure relative paths will not be resolved
// relative to the current directory.
const mergeCwd = test.info().outputPath('foo');
@ -1146,7 +1146,12 @@ test('custom project suffix', async ({ runInlineTest, mergeReports }) => {
`,
};
await runInlineTest(files, undefined, { PWTEST_BLOB_SUFFIX: '-suffix' });
await runInlineTest(files, { shard: `1/2` }, { PWTEST_BLOB_SUFFIX: '-suffix', PWTEST_BLOB_DO_NOT_REMOVE: '1' });
await runInlineTest(files, { shard: `2/2` }, { PWTEST_BLOB_SUFFIX: '-suffix', PWTEST_BLOB_DO_NOT_REMOVE: '1' });
const reportFiles = await fs.promises.readdir(reportDir);
reportFiles.sort();
expect(reportFiles).toEqual(['report-suffix-1.zip', 'report-suffix-2.zip']);
const { exitCode, output } = await mergeReports(reportDir, {}, { additionalArgs: ['--reporter', test.info().outputPath('echo-reporter.js')] });
expect(exitCode).toBe(0);
@ -1235,7 +1240,7 @@ test('blob-report should include version', async ({ runInlineTest }) => {
};
await runInlineTest(files);
const reportFiles = await fs.promises.readdir(reportDir);
expect(reportFiles).toEqual([expect.stringMatching(/report-.*.zip/)]);
expect(reportFiles).toEqual(['report.zip']);
await extractZip(test.info().outputPath('blob-report', reportFiles[0]), { dir: test.info().outputPath('blob-report') });
const reportFile = test.info().outputPath('blob-report', reportFiles[0].replace(/\.zip$/, '.jsonl'));