Skip to content

Commit

Permalink
feat(test-runner): introduce attachments
Browse files Browse the repository at this point in the history
  • Loading branch information
pavelfeldman committed Jul 16, 2021
1 parent 31572fc commit 93c4e15
Show file tree
Hide file tree
Showing 11 changed files with 48 additions and 18 deletions.
7 changes: 6 additions & 1 deletion src/test/dispatcher.ts
Expand Up @@ -271,7 +271,12 @@ export class Dispatcher {
const { test, result } = this._testById.get(params.testId)!;
result.duration = params.duration;
result.error = params.error;
result.data = params.data;
result.attachments = params.attachments.map(a => ({
name: a.name,
path: a.path,
contentType: a.contentType,
body: a.body ? Buffer.from(a.body, 'base64') : undefined
}));
test.expectedStatus = params.expectedStatus;
test.annotations = params.annotations;
test.timeout = params.timeout;
Expand Down
9 changes: 8 additions & 1 deletion src/test/expect.ts
Expand Up @@ -38,7 +38,7 @@ function toMatchSnapshot(this: ReturnType<Expect['getState']>, received: Buffer
options.threshold = projectThreshold;

const withNegateComparison = this.isNot;
const { pass, message } = compare(
const { pass, message, expectedPath, actualPath, diffPath, mimeType } = compare(
received,
options.name,
testInfo.snapshotPath,
Expand All @@ -47,6 +47,13 @@ function toMatchSnapshot(this: ReturnType<Expect['getState']>, received: Buffer
withNegateComparison,
options
);
const contentType = mimeType || 'application/octet-stream';
if (expectedPath)
testInfo.attachments.push({ name: 'expected', contentType, path: expectedPath });
if (actualPath)
testInfo.attachments.push({ name: 'actual', contentType, path: actualPath });
if (diffPath)
testInfo.attachments.push({ name: 'diff', contentType, path: diffPath });
return { pass, message: () => message };
}

Expand Down
6 changes: 5 additions & 1 deletion src/test/golden.ts
Expand Up @@ -88,7 +88,7 @@ export function compare(
updateSnapshots: UpdateSnapshots,
withNegateComparison: boolean,
options?: { threshold?: number }
): { pass: boolean; message?: string; } {
): { pass: boolean; message?: string; expectedPath?: string, actualPath?: string, diffPath?: string, mimeType?: string } {
const snapshotFile = snapshotPath(name);

if (!fs.existsSync(snapshotFile)) {
Expand Down Expand Up @@ -179,6 +179,10 @@ export function compare(
return {
pass: false,
message: output.join('\n'),
expectedPath,
actualPath,
diffPath,
mimeType
};
}

Expand Down
10 changes: 7 additions & 3 deletions src/test/index.ts
Expand Up @@ -162,17 +162,21 @@ export const test = _baseTest.extend<PlaywrightTestArgs & PlaywrightTestOptions,
const preserveTrace = captureTrace && (trace === 'on' || (testFailed && trace === 'retain-on-failure') || (trace === 'on-first-retry' && testInfo.retry === 1));
if (preserveTrace) {
const tracePath = testInfo.outputPath(`trace.zip`);
testInfo.data.playwrightTrace = tracePath;
await context.tracing.stop({ path: tracePath });
testInfo.attachments.push({ name: 'trace', path: tracePath, contentType: 'application/zip' });
} else if (captureTrace) {
await context.tracing.stop();
}

const captureScreenshots = (screenshot === 'on' || (screenshot === 'only-on-failure' && testFailed));
if (captureScreenshots) {
await Promise.all(allPages.map((page, index) => {
await Promise.all(allPages.map(async (page, index) => {
const screenshotPath = testInfo.outputPath(`test-${testFailed ? 'failed' : 'finished'}-${++index}.png`);
return page.screenshot({ timeout: 5000, path: screenshotPath }).catch(e => {});
try {
await page.screenshot({ timeout: 5000, path: screenshotPath });
testInfo.attachments.push({ name: 'screenshot', path: screenshotPath, contentType: 'image/png' });
} catch {
}
}));
}

Expand Down
2 changes: 1 addition & 1 deletion src/test/ipc.ts
Expand Up @@ -42,7 +42,7 @@ export type TestEndPayload = {
expectedStatus: TestStatus;
annotations: { type: string, description?: string }[];
timeout: number;
data: { [key: string]: any },
attachments: { name: string, path?: string, body?: string, contentType: string }[];
};

export type TestEntry = {
Expand Down
2 changes: 1 addition & 1 deletion src/test/reporter.ts
Expand Up @@ -51,7 +51,7 @@ export interface TestResult {
duration: number;
status?: TestStatus;
error?: TestError;
data: { [key: string]: any };
attachments: { name: string, path?: string, body?: Buffer, contentType: string }[];
stdout: (string | Buffer)[];
stderr: (string | Buffer)[];
}
Expand Down
9 changes: 7 additions & 2 deletions src/test/reporters/json.ts
Expand Up @@ -67,7 +67,7 @@ export interface JSONReportTestResult {
stdout: JSONReportSTDIOEntry[],
stderr: JSONReportSTDIOEntry[],
retry: number;
data: { [key: string]: any },
attachments: { name: string, path?: string, body?: string, contentType: string }[];
}
export type JSONReportSTDIOEntry = { text: string } | { buffer: string };

Expand Down Expand Up @@ -217,7 +217,12 @@ class JSONReporter implements Reporter {
stdout: result.stdout.map(s => stdioEntry(s)),
stderr: result.stderr.map(s => stdioEntry(s)),
retry: result.retry,
data: result.data,
attachments: result.attachments.map(a => ({
name: a.name,
contentType: a.contentType,
path: a.path,
body: a.body?.toString('base64')
})),
};
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/test/test.ts
Expand Up @@ -187,7 +187,7 @@ export class Test extends Base implements reporterTypes.Test {
duration: 0,
stdout: [],
stderr: [],
data: {},
attachments: [],
};
this.results.push(result);
return result;
Expand Down
9 changes: 7 additions & 2 deletions src/test/workerRunner.ts
Expand Up @@ -226,7 +226,7 @@ export class WorkerRunner extends EventEmitter {
retry: entry.retry,
expectedStatus: 'passed',
annotations: [],
data: {},
attachments: [],
duration: 0,
status: 'passed',
stdout: [],
Expand Down Expand Up @@ -477,7 +477,12 @@ function buildTestEndPayload(testId: string, testInfo: TestInfo): TestEndPayload
expectedStatus: testInfo.expectedStatus,
annotations: testInfo.annotations,
timeout: testInfo.timeout,
data: testInfo.data,
attachments: testInfo.attachments.map(a => ({
name: a.name,
contentType: a.contentType,
path: a.path,
body: a.body?.toString('base64')
}))
};
}

Expand Down
6 changes: 3 additions & 3 deletions tests/playwright-test/access-data.spec.ts
Expand Up @@ -83,13 +83,13 @@ test('should report projectName in result', async ({ runInlineTest }) => {
expect(exitCode).toBe(0);
});

test('should access testInfo.data in fixture', async ({ runInlineTest }) => {
test('should access testInfo.attachments in fixture', async ({ runInlineTest }) => {
const { exitCode, report } = await runInlineTest({
'test-data-visible-in-env.spec.ts': `
const test = pwt.test.extend({
foo: async ({}, run, testInfo) => {
await run();
testInfo.data.foo = 'bar';
testInfo.attachments.push({ name: 'foo', body: Buffer.from([1, 2, 3]), contentType: 'application/octet-stream' });
},
});
test('ensure fixture can set data', async ({ foo }) => {
Expand All @@ -98,5 +98,5 @@ test('should access testInfo.data in fixture', async ({ runInlineTest }) => {
});
expect(exitCode).toBe(0);
const test = report.suites[0].specs[0].tests[0];
expect(test.results[0].data).toEqual({ foo: 'bar' });
expect(test.results[0].attachments[0]).toEqual({ name: 'foo', body: 'AQID', contentType: 'application/octet-stream' });
});
4 changes: 2 additions & 2 deletions types/test.d.ts
Expand Up @@ -399,9 +399,9 @@ export interface TestInfo extends WorkerInfo {
annotations: { type: string, description?: string }[];

/**
* Arbitrary data that test fixtures can provide for the test report.
* File attachments for this test.
*/
data: { [key: string]: any };
attachments: { name: string, path?: string, body?: Buffer, contentType: string }[];

/**
* When tests are run multiple times, each run gets a unique `repeatEachIndex`.
Expand Down

0 comments on commit 93c4e15

Please sign in to comment.