playwright-bdd
Version:
BDD Testing with Playwright runner
152 lines • 7.23 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.TestStepRun = void 0;
const messages = __importStar(require("@cucumber/messages"));
const timing_1 = require("./timing");
const TestStepAttachments_1 = require("./TestStepAttachments");
const pwStepUtils_1 = require("./pwStepUtils");
const Exception_1 = require("./Exception");
/**
* Run of messages.TestStep from hook or scenario.
*/
class TestStepRun {
constructor(testCaseRun, testStep, pwStep) {
this.testCaseRun = testCaseRun;
this.testStep = testStep;
this.pwStep = pwStep;
}
buildMessages() {
const stepAttachments = new TestStepAttachments_1.TestStepAttachments(this.testCaseRun, this.testStep, this.pwStep);
return [
this.buildTestStepStarted(), // prettier-ignore
...stepAttachments.buildMessages(),
this.buildTestStepFinished(),
];
}
isHook() {
return Boolean(this.testStep.hookId);
}
wasExecuted() {
return Boolean(this.pwStep);
}
get startTime() {
return this.wasExecuted() ? this.pwStep.startTime : this.testCaseRun.result.startTime;
}
get duration() {
return this.wasExecuted() ? this.pwStep.duration : 0;
}
buildTestStepStarted() {
const testStepStarted = {
testCaseStartedId: this.testCaseRun.id,
testStepId: this.testStep.id,
timestamp: (0, timing_1.toCucumberTimestamp)(this.startTime.getTime()),
};
return { testStepStarted };
}
buildTestStepFinished() {
const error = this.getStepError();
const status = this.getStatus(error);
const exception = isStepFailed(status) && error ? (0, Exception_1.buildException)(error) : undefined;
const testStepFinished = {
testCaseStartedId: this.testCaseRun.id,
testStepId: this.testStep.id,
testStepResult: {
duration: messages.TimeConversion.millisecondsToDuration(this.duration),
status,
// There are two fields for error info:
// - 'message': string message + stack.
// https://github.com/cucumber/messages/blob/main/jsonschema/messages.md#teststepresultmessage
// - 'exception': added in cucumber 10.4, contains structured error details.
// https://github.com/cucumber/messages/blob/main/jsonschema/messages.md#exception
//
// Different Cucumber reporters handle these fields differently:
// - html reporter: shows only exception.stackTrace,
// then (exception.type + exception.message), then result.message.
// https://github.com/cucumber/react-components/blob/main/src/components/results/FailedResult.tsx#L10
// - json reporter: shows only result.message
// https://github.com/cucumber/cucumber-js/blob/main/src/formatter/json_formatter.ts#L270
// - junit reporter: shows exception.type, exception.message and (exception.stackTrace or result.message)
// https://github.com/cucumber/junit-xml-formatter/blob/main/javascript/src/JUnitXmlPrinter.ts#L89
// https://github.com/cucumber/junit-xml-formatter/blob/main/javascript/src/JUnitXmlPrinter.ts#L179
//
// So we should set both fields carefully:
// - result.message = exception.stackTrace to show full error details in json reporter
//
// See also: https://github.com/cucumber/react-components/pull/345
exception,
message: exception?.stackTrace,
},
timestamp: (0, timing_1.toCucumberTimestamp)(this.startTime.getTime() + this.duration),
};
return { testStepFinished };
}
getStatus(error) {
switch (true) {
// When calling test.skip(), it actually throws an error
case (0, pwStepUtils_1.isSkippedError)(error):
return messages.TestStepResultStatus.SKIPPED;
case Boolean(error):
return messages.TestStepResultStatus.FAILED;
// Steps (and hooks) that have no corresponding Playwright step were not executed
// in this particular attempt and should be reported as SKIPPED.
//
// For scenario steps this happens when a prior step failed — subsequent steps have no pwStep.
//
// For BEFORE_TEST_RUN hooks (BeforeAll) this can happen on a retry: PW runs BeforeAll only
// once per worker (credited to the first scenario). If scenario B fails and is retried, its
// attempt 1 runs BeforeAll in the new worker (has a real pwStep). When TestCase.addHooks()
// processes attempt 1, it adds BeforeAll to the shared TestCase used for all attempts.
// Then buildMessages() for attempt 0 finds BeforeAll in the TestCase but has no matching
// pwStep (BeforeAll was credited to scenario A in the original worker). Emitting SKIPPED is
// correct: the hook was a declared prerequisite, but was already handled by a prior scenario
// in the same worker and therefore did not execute as part of this attempt.
case !this.wasExecuted():
return messages.TestStepResultStatus.SKIPPED;
default:
return messages.TestStepResultStatus.PASSED;
}
}
getStepError() {
if (this.pwStep) {
return this.testCaseRun.getStepError(this.pwStep);
}
}
}
exports.TestStepRun = TestStepRun;
function isStepFailed(status) {
return status === messages.TestStepResultStatus.FAILED;
}
//# sourceMappingURL=TestStepRun.js.map