playwright-bdd
Version:
BDD Testing with Playwright runner
171 lines • 7.38 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
/**
* Cucumber html reporter.
* Based on: https://github.com/cucumber/cucumber-js/blob/main/src/formatter/html_formatter.ts
* See: https://github.com/cucumber/html-formatter
* See: https://github.com/cucumber/react-components
*/
const node_fs_1 = __importDefault(require("node:fs"));
const promises_1 = require("node:stream/promises");
const node_stream_1 = require("node:stream");
const html_formatter_1 = require("@cucumber/html-formatter");
const node_path_1 = __importDefault(require("node:path"));
const base_1 = __importDefault(require("../base"));
const helpers_1 = require("../attachments/helpers");
const skip_1 = require("../attachments/skip");
const external_1 = require("../attachments/external");
const traceViewer_1 = require("./traceViewer");
const utils_1 = require("../../../utils");
const envelopesTransformer_1 = require("./envelopesTransformer");
// Directory for attahcments, relative to reportDir.
// We store attachments in subdirectory to not accidentally
// bloat the working dir and make it easier to find report html file.
// 'data' name is also used in Playwright HTML reporter.
const ATTACHMENTS_DIR = 'data';
class HtmlReporter extends base_1.default {
constructor(internalOptions, userOptions = {}) {
super(internalOptions);
this.userOptions = userOptions;
this.attachmentsDir = '';
this.attachmentsBaseURL = '';
this.hasTraces = false;
this.receivedTestRunFinished = false;
this.setOutputStream(this.userOptions.outputFile);
if (this.userOptions.externalAttachments) {
this.setupAttachmentsDir();
this.setupAttachmentsBaseURL();
}
this.htmlStream = new html_formatter_1.CucumberHtmlStream();
this.transformStream = this.createTransformStream();
this.htmlStream.pipe(this.transformStream).pipe(this.outputStream);
}
async init() {
const envelopesTransformer = new envelopesTransformer_1.EnvelopesTransformer(this.messagesBuilder);
this.messagesBuilder.getMessages().forEach((origEnvelope) => {
// filter out retries, except the last one
const envelope = envelopesTransformer.transformEnvelope(origEnvelope);
if (!envelope)
return;
if (envelope.testRunFinished)
this.receivedTestRunFinished = true;
if ((0, helpers_1.isAttachmentEnvelope)(envelope)) {
this.handleAttachment(envelope);
}
else {
this.writeEnvelope(envelope);
}
});
}
async finished() {
this.htmlStream.end();
await (0, promises_1.finished)(this.htmlStream);
await (0, promises_1.finished)(this.transformStream);
if (this.hasTraces)
await (0, traceViewer_1.copyTraceViewer)(this.outputDir);
await super.finished();
}
handleAttachment(envelope) {
if ((0, skip_1.shouldSkipAttachment)(envelope, this.userOptions.skipAttachments))
return;
// For now don't externalize text attachments, b/c they are not visible in the report.
// In the future maybe handle separately 'text/x.cucumber.log+plain', 'text/uri-list'.
// See: https://github.com/cucumber/cucumber-js/issues/2430
// See: https://github.com/cucumber/react-components/blob/main/src/components/gherkin/attachment/Attachment.tsx#L32
const isExternalAttachment = this.userOptions.externalAttachments && !(0, external_1.isTextAttachment)(envelope.attachment);
const newAttachment = isExternalAttachment
? (0, external_1.toExternalAttachment)(envelope.attachment, this.attachmentsDir, this.attachmentsBaseURL)
: (0, external_1.toEmbeddedAttachment)(envelope.attachment);
if (isExternalAttachment)
this.handleTraceAttachment(newAttachment);
this.writeEnvelope({
...envelope,
attachment: newAttachment,
});
}
writeEnvelope(envelope) {
this.htmlStream.write(envelope);
}
/**
* If there is trace attachment, copy trace-viewer to the report
* and create additional attachment with trace view link.
* - implementation in PW: https://github.com/microsoft/playwright/blob/release-1.50/packages/playwright/src/reporters/html.ts#L434
* - attachmentsBaseURL should start with http(s) to be able to show traces.
*/
handleTraceAttachment(attachment) {
if ((0, traceViewer_1.isTraceAttachment)(attachment)) {
this.hasTraces = true;
const { testCaseStartedId, testStepId } = attachment;
const href = (0, traceViewer_1.generateTraceUrl)(attachment);
const newEnvelope = (0, traceViewer_1.createViewTraceLinkAttachment)(testCaseStartedId, testStepId, href);
this.writeEnvelope(newEnvelope);
}
}
setupAttachmentsDir() {
(0, utils_1.throwIf)(!this.outputDir, 'Unable to externalize attachments when reporter is not writing to a file');
this.attachmentsDir = node_path_1.default.join(this.outputDir, ATTACHMENTS_DIR);
if (node_fs_1.default.existsSync(this.attachmentsDir))
node_fs_1.default.rmSync(this.attachmentsDir, { recursive: true });
node_fs_1.default.mkdirSync(this.attachmentsDir, { recursive: true });
}
setupAttachmentsBaseURL() {
this.attachmentsBaseURL = removeTrailingSlash(
// don't prepend '/' to ATTACHMENTS_DIR
// to make it work when reporter is opened from file:// protocol.
this.userOptions.attachmentsBaseURL || ATTACHMENTS_DIR);
}
/**
* Special transform stream to inject custom script into the HTML.
*/
createTransformStream() {
return new node_stream_1.Transform({
transform: (chunk, _encoding, callback) => {
const newChunk = this.receivedTestRunFinished ? this.injectCustomAssets(chunk) : chunk;
callback(null, newChunk);
},
});
}
injectCustomAssets(chunk) {
const chunkStr = chunk.toString();
if (chunkStr.includes('</body>')) {
const customAssets = [
cssHideLogForCustomHtml, // prettier-ignore
traceViewer_1.assetsViewTraceLinks,
].join('\n');
return chunkStr.replace('</body>', `${customAssets}</body>`);
}
else {
return chunk;
}
}
}
exports.default = HtmlReporter;
function removeTrailingSlash(url) {
return url.replace(/\/+$/, '');
}
/**
* Custom css to adjust log entries for showing custom HTML:
* - hide LOG label (it's a ::before pseudo-element)
* - hide copy button
* - adjust padding
*/
const cssHideLogForCustomHtml = `
<style>
div:has(> pre > span > [data-custom-html])::before {
display: none !important;
}
div:has(> pre > span > [data-custom-html]) > button {
display: none !important;
}
div:has(> pre > span > [data-custom-html]) {
padding-left: 0 !important;
}
div:has(> pre > span > [data-custom-html]) > pre {
padding: .666em .75em !important;
}
</style>
`;
//# sourceMappingURL=index.js.map