UNPKG

@serenity-js/playwright-test

Version:

Serenity/JS test runner adapter for Playwright Test, combining Playwright's developer experience with the advanced reporting and automation capabilities of Serenity/JS

147 lines 7.83 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.PlaywrightEventBuffer = void 0; const node_path_1 = __importDefault(require("node:path")); const core_1 = require("@serenity-js/core"); const events_1 = require("@serenity-js/core/lib/events"); const io_1 = require("@serenity-js/core/lib/io"); const model_1 = require("@serenity-js/core/lib/model"); const WorkerEventStreamReader_1 = require("../api/WorkerEventStreamReader"); const WorkerEventStreamWriter_1 = require("../api/WorkerEventStreamWriter"); const events_2 = require("../events"); const PlaywrightErrorParser_1 = require("./PlaywrightErrorParser"); class PlaywrightEventBuffer { errorParser = new PlaywrightErrorParser_1.PlaywrightErrorParser(); eventStreamReader = new WorkerEventStreamReader_1.WorkerEventStreamReader(); eventFactory; events = new Map(); deferredSceneFinishedEvents = new Map(); configure(config) { this.eventFactory = new events_2.EventFactory(io_1.Path.from(config.rootDir)); } appendTestStart(test, result) { this.events.set(this.sceneId(test, result).value, this.eventFactory.createSceneStartEvents(test, result)); } appendRetryableSceneEvents(test, result) { const sceneId = this.sceneId(test, result); const sceneEndTime = new core_1.Timestamp(result.startTime).plus(core_1.Duration.ofMilliseconds(result.duration)); this.events.get(sceneId.value).push(new events_1.RetryableSceneDetected(sceneId, sceneEndTime)); if (result.retry > 0 || result.status !== 'passed') { this.events.get(sceneId.value).push(new events_1.SceneTagged(sceneId, new model_1.ArbitraryTag('retried'), // todo: replace with a dedicated tag sceneEndTime)); } } deferAppendingSceneFinishedEvent(test, result) { const sceneId = this.sceneId(test, result); const scenarioOutcome = this.outcomeFrom(test, result); this.deferredSceneFinishedEvents.set(sceneId.value, { event: this.eventFactory.createSceneFinishedEvent(test, result, scenarioOutcome), outputDirectory: test.parent.project().outputDir, workerIndex: result.workerIndex, }); } determineScenarioOutcome(worstInteractionOutcome, scenarioOutcome) { if (worstInteractionOutcome instanceof model_1.ExecutionFailedWithAssertionError) { return worstInteractionOutcome; } return worstInteractionOutcome.isWorseThan(scenarioOutcome) ? worstInteractionOutcome : scenarioOutcome; } appendCrashedWorkerEvents(test, result) { const workerStreamId = WorkerEventStreamWriter_1.WorkerEventStreamWriter.workerStreamIdFor(result.workerIndex).value; const sceneId = this.sceneId(test, result); this.events.get(sceneId.value).push(...this.readEventStream(test.parent.project().outputDir, workerStreamId, sceneId.value)); } appendSceneEvents(test, result) { const sceneId = this.sceneId(test, result); this.events.get(sceneId.value).push(...this.readEventStream(test.parent.project().outputDir, sceneId.value)); } readEventStream(outputDirectory, streamId, expectedSceneId = streamId) { const pathToEventStreamFile = node_path_1.default.join(outputDirectory, 'serenity', streamId, 'events.ndjson'); if (this.eventStreamReader.hasStream(pathToEventStreamFile)) { return this.eventStreamReader.read(pathToEventStreamFile, (event) => { // re-attach events from orphaned beforeAll to the test case const hasSceneId = event.value['sceneId'] !== undefined; const isAttachedToScene = event.value['sceneId'] === expectedSceneId; if (hasSceneId && !isAttachedToScene) { event.value['sceneId'] = expectedSceneId; } return event; }); } return []; } appendSceneFinishedEvent(test, result) { const sceneId = this.sceneId(test, result); const worstInteractionOutcome = this.determineWorstInteractionOutcome(this.events.get(sceneId.value)); const scenarioOutcome = this.determineScenarioOutcome(worstInteractionOutcome, this.outcomeFrom(test, result)); this.events.get(sceneId.value).push(this.eventFactory.createSceneFinishedEvent(test, result, scenarioOutcome)); } flush(test, result) { const sceneId = this.sceneId(test, result); const events = this.events.get(sceneId.value); if (!events) { throw new core_1.LogicError(`No events found for test: ${sceneId.value}`); } this.events.delete(sceneId.value); return events; } flushAllDeferred() { const allEvents = []; for (const [testId, events] of this.events.entries()) { const scenarioEvents = []; scenarioEvents.push(...events); if (this.deferredSceneFinishedEvents.has(testId)) { const lastRecordedEvent = scenarioEvents.at(-1); const deferredSceneFinished = this.deferredSceneFinishedEvents.get(testId); const eventStream = this.readEventStream(deferredSceneFinished.outputDirectory, deferredSceneFinished.event.sceneId.value); const firstEventSinceLastIndex = eventStream.findIndex(event => lastRecordedEvent.equals(event)); const eventsSinceLast = firstEventSinceLastIndex === -1 ? eventStream : eventStream.slice(firstEventSinceLastIndex); scenarioEvents.push(...eventsSinceLast); const worstInteractionOutcome = this.determineWorstInteractionOutcome(scenarioEvents); const sceneFinishedEvent = new events_1.SceneFinished(deferredSceneFinished.event.sceneId, deferredSceneFinished.event.details, this.determineScenarioOutcome(worstInteractionOutcome, deferredSceneFinished.event.outcome), deferredSceneFinished.event.timestamp); scenarioEvents.push(sceneFinishedEvent); } allEvents.push(...scenarioEvents); } this.events.clear(); this.deferredSceneFinishedEvents.clear(); return allEvents; } determineWorstInteractionOutcome(events) { let worstInteractionOutcome = new model_1.ExecutionSuccessful(); for (const event of events) { if (event instanceof events_1.InteractionFinished && event.outcome.isWorseThan(worstInteractionOutcome)) { worstInteractionOutcome = event.outcome; } } return worstInteractionOutcome; } outcomeFrom(test, result) { const outcome = test.outcome(); if (outcome === 'skipped') { return new model_1.ExecutionSkipped(); } if (outcome === 'unexpected' && result.status === 'passed') { return new model_1.ExecutionFailedWithError(new core_1.LogicError(`Scenario expected to fail, but ${result.status}`)); } if (['failed', 'interrupted', 'timedOut'].includes(result.status)) { if (test.retries > result.retry) { return new model_1.ExecutionIgnored(this.errorParser.errorFrom(result.error)); } return new model_1.ExecutionFailedWithError(this.errorParser.errorFrom(result.error)); } return new model_1.ExecutionSuccessful(); } sceneId(test, result) { return events_2.PlaywrightSceneId.from(test.parent.project()?.name, test, result); } } exports.PlaywrightEventBuffer = PlaywrightEventBuffer; //# sourceMappingURL=PlaywrightEventBuffer.js.map