UNPKG

playwright-bdd

Version:
166 lines 6.77 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.TestCaseRun = void 0; const TestStepRun_1 = require("./TestStepRun"); const timing_1 = require("./timing"); const pwStepUtils_1 = require("./pwStepUtils"); const AttachmentMapper_1 = require("./AttachmentMapper"); const TestCaseRunHooks_1 = require("./TestCaseRunHooks"); const Projects_1 = require("./Projects"); class TestCaseRun { // eslint-disable-next-line max-params constructor(bddTestData, featureUri, test, result, hooks) { this.bddTestData = bddTestData; this.featureUri = featureUri; this.test = test; this.result = result; this.hooks = hooks; // Collect steps with error and show only these errors in report, // it allows to not show the same error of parent steps. // Usually, value contains step.error, but can be customized: // e.g. timeouted step may not have 'error' field. this.errorSteps = new Map(); // root bg steps (can be several for Rules) this.bgRoots = new Set(); this.id = this.generateTestCaseRunId(); this.projectInfo = (0, Projects_1.getProjectInfo)(this.test); // call order is important here this.attachmentMapper = new AttachmentMapper_1.AttachmentMapper(this.result); this.executedBddSteps = this.fillExecutedBddSteps(); this.executedBeforeHooks = this.fillExecutedHooks('before'); this.executedAfterHooks = this.fillExecutedHooks('after'); } getTestCase() { if (!this.testCase) throw new Error(`TestCase is not set.`); return this.testCase; } isTimeouted() { return this.result.status === 'timedOut'; } willBeRetried() { return Boolean(this.result.error && this.result.retry < this.test.retries); } buildMessages() { return [ this.buildTestCaseStarted(), ...this.executedBeforeHooks.buildMessages(), ...this.buildStepRuns(), ...this.executedAfterHooks.buildMessages(), this.buildTestCaseFinished(), ]; } getExecutedHooks(hookType) { return hookType === 'before' ? this.executedBeforeHooks.executedHooks : this.executedAfterHooks.executedHooks; } getStepError(pwStep) { return this.errorSteps.get(pwStep); } generateTestCaseRunId() { return `${this.test.id}-attempt-${this.result.retry}`; } fillExecutedBddSteps() { const possiblePwSteps = this.getPossiblePwSteps(); return this.bddTestData.steps.map((bddStep) => { return this.fillExecutedBddStep(bddStep, possiblePwSteps); }); } // eslint-disable-next-line visual/complexity fillExecutedBddStep(bddStep, possiblePwSteps) { const pwStep = this.findPlaywrightStep(possiblePwSteps, bddStep); if (pwStep?.error) { this.registerErrorStep(pwStep, pwStep.error); } if (this.isTimeouted() && pwStep && (0, pwStepUtils_1.isUnknownDuration)(pwStep)) { this.registerTimeoutedStep(pwStep); } if (pwStep?.parent && bddStep.isBg) { this.bgRoots.add(pwStep.parent); } if (pwStep) { this.attachmentMapper.populateStepAttachments(pwStep); } return { bddStep, pwStep }; } fillExecutedHooks(hookType) { return new TestCaseRunHooks_1.TestCaseRunHooks(this, hookType, this.bgRoots).fill(); } registerErrorStep(pwStep, error) { this.errorSteps.set(pwStep, error); } hasRegisteredError(error) { for (const registeredError of this.errorSteps.values()) { if ((0, pwStepUtils_1.areTestErrorsEqual)(registeredError, error)) return true; } } registerTimeoutedStep(pwStep) { if (this.timeoutedStep) return; this.timeoutedStep = pwStep; // Handle case when timeouted step has duration -1 and no 'error' field, // but it's parent contains actual error. // - timeout in bg step // - timeout in hooks // We register timeouted step with error from parent. if (!pwStep.error && pwStep.parent?.error && !(0, pwStepUtils_1.isTopLevelStep)(pwStep)) { this.registerErrorStep(this.timeoutedStep, pwStep.parent.error); } } getUnprocessedErrors() { return this.result.errors.filter((error) => !this.isProcessedError(error)); } isProcessedError(error) { for (const pwStepError of this.errorSteps.values()) { if ((0, pwStepUtils_1.areTestErrorsEqual)(pwStepError, error)) { return true; } } } buildTestCaseStarted() { const testCaseStarted = { id: this.id, attempt: this.result.retry, testCaseId: this.getTestCase().id, // workerId: 'worker-1' timestamp: (0, timing_1.toCucumberTimestamp)(this.result.startTime.getTime()), }; return { testCaseStarted }; } buildStepRuns() { return this.getTestCase() .getSteps() .reduce((messages, testStep, stepIndex) => { const { pwStep } = this.executedBddSteps[stepIndex] || {}; const testStepRun = new TestStepRun_1.TestStepRun(this, testStep, pwStep); return messages.concat(testStepRun.buildMessages()); }, []); } buildTestCaseFinished() { const { startTime, duration } = this.result; const testCaseFinished = { testCaseStartedId: this.id, willBeRetried: this.willBeRetried(), timestamp: (0, timing_1.toCucumberTimestamp)(startTime.getTime() + duration), }; return { testCaseFinished }; } findPlaywrightStep(possiblePwSteps, bddStep) { return possiblePwSteps.find((pwStep) => { // todo: filter by file earlier? return (pwStep.location?.file === this.test.location.file && pwStep.location?.line === bddStep.pwStepLine); }); } getPossiblePwSteps() { // Before we collected only top-level steps and steps from before hooks (as they are background) // But it's more reliable to just collect all test.step items b/c some Playwright versions // move steps to fixtures (see: https://github.com/microsoft/playwright/issues/30075) // Collecting all test.step items should be ok, as later we anyway map them by location. return (0, pwStepUtils_1.walkSteps)(this.result.steps).filter((pwStep) => pwStep.category === 'test.step'); } } exports.TestCaseRun = TestCaseRun; //# sourceMappingURL=TestCaseRun.js.map