fix: render JUnit attachment paths relative to outputFile (#27024)

This commit is contained in:
David Paquette 2023-09-14 13:58:09 -06:00 committed by GitHub
parent 603861c48d
commit 3170963f42
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 48 additions and 6 deletions

View File

@ -31,6 +31,7 @@ class JUnitReporter extends EmptyReporter {
private totalFailures = 0; private totalFailures = 0;
private totalSkipped = 0; private totalSkipped = 0;
private outputFile: string | undefined; private outputFile: string | undefined;
private resolvedOutputFile: string | undefined;
private stripANSIControlSequences = false; private stripANSIControlSequences = false;
constructor(options: { outputFile?: string, stripANSIControlSequences?: boolean } = {}) { constructor(options: { outputFile?: string, stripANSIControlSequences?: boolean } = {}) {
@ -51,6 +52,10 @@ class JUnitReporter extends EmptyReporter {
this.suite = suite; this.suite = suite;
this.timestamp = new Date(); this.timestamp = new Date();
this.startTime = monotonicTime(); this.startTime = monotonicTime();
if (this.outputFile) {
assert(this.config.configFile || path.isAbsolute(this.outputFile), 'Expected fully resolved path if not using config file.');
this.resolvedOutputFile = this.config.configFile ? path.resolve(path.dirname(this.config.configFile), this.outputFile) : this.outputFile;
}
} }
override async onEnd(result: FullResult) { override async onEnd(result: FullResult) {
@ -79,11 +84,9 @@ class JUnitReporter extends EmptyReporter {
serializeXML(root, tokens, this.stripANSIControlSequences); serializeXML(root, tokens, this.stripANSIControlSequences);
const reportString = tokens.join('\n'); const reportString = tokens.join('\n');
if (this.outputFile) { if (this.resolvedOutputFile) {
assert(this.config.configFile || path.isAbsolute(this.outputFile), 'Expected fully resolved path if not using config file.'); await fs.promises.mkdir(path.dirname(this.resolvedOutputFile), { recursive: true });
const outputFile = this.config.configFile ? path.resolve(path.dirname(this.config.configFile), this.outputFile) : this.outputFile; await fs.promises.writeFile(this.resolvedOutputFile, reportString);
await fs.promises.mkdir(path.dirname(outputFile), { recursive: true });
await fs.promises.writeFile(outputFile, reportString);
} else { } else {
console.log(reportString); console.log(reportString);
} }
@ -190,7 +193,14 @@ class JUnitReporter extends EmptyReporter {
for (const attachment of result.attachments) { for (const attachment of result.attachments) {
if (!attachment.path) if (!attachment.path)
continue; continue;
const attachmentPath = path.relative(this.config.rootDir, attachment.path);
let attachmentPath = path.relative(this.config.rootDir, attachment.path);
try {
if (this.resolvedOutputFile)
attachmentPath = path.relative(path.dirname(this.resolvedOutputFile), attachment.path);
} catch {
systemOut.push(`\nWarning: Unable to make attachment path ${attachment.path} relative to report output file ${this.outputFile}`);
}
try { try {
await fs.promises.access(attachment.path); await fs.promises.access(attachment.path);

View File

@ -307,6 +307,38 @@ for (const useIntermediateMergeReport of [false, true] as const) {
expect(result.exitCode).toBe(0); expect(result.exitCode).toBe(0);
}); });
test('should render attachment paths relative to report file when report file name is specified', async ({ runInlineTest }, testInfo) => {
test.skip(useIntermediateMergeReport, 'Blob report hashes attachment paths');
const result = await runInlineTest({
'project/playwright.config.ts': `
module.exports = { reporter: [['junit', { outputFile: '../my-report/junit/a.xml' }]] };
`,
'project/a.test.js': `
import { test, expect } from '@playwright/test';
test.use({ screenshot: 'on' });
test('one', async ({ page }, testInfo) => {
await page.setContent('hello');
const file = testInfo.outputPath('file.txt');
require('fs').writeFileSync(file, 'my file', 'utf8');
testInfo.attachments.push({ name: 'my-file', path: file, contentType: 'text/plain' });
console.log('log here');
});
`,
}, { reporter: '', config: './project/playwright.config.ts' });
expect(result.exitCode).toBe(0);
expect(fs.existsSync(testInfo.outputPath(path.join('my-report', 'junit', 'a.xml')))).toBeTruthy();
const xml = parseXML(fs.readFileSync(testInfo.outputPath(path.join('my-report', 'junit', 'a.xml'))).toString());
const testcase = xml['testsuites']['testsuite'][0]['testcase'][0];
expect(testcase['system-out'].length).toBe(1);
expect(testcase['system-out'][0].trim()).toBe([
`log here`,
`\n[[ATTACHMENT|..${path.sep}..${path.sep}test-results${path.sep}a-one${path.sep}file.txt]]`,
`\n[[ATTACHMENT|..${path.sep}..${path.sep}test-results${path.sep}a-one${path.sep}test-finished-1.png]]`,
].join('\n'));
expect(result.exitCode).toBe(0);
});
function parseXML(xml: string): any { function parseXML(xml: string): any {
let result: any; let result: any;
xml2js.parseString(xml, (err, r) => result = r); xml2js.parseString(xml, (err, r) => result = r);