@zohodesk/testinglibrary 0.1.9-exp-actors → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/bdd-framework/cli/commands/env.js +1 -1
- package/build/bdd-framework/cli/commands/export.js +18 -3
- package/build/bdd-framework/decorators.js +2 -2
- package/build/bdd-framework/gen/formatter.js +57 -13
- package/build/bdd-framework/gen/index.js +21 -9
- package/build/bdd-framework/gen/specialTags.js +70 -0
- package/build/bdd-framework/gen/testFile.js +10 -5
- package/build/bdd-framework/gen/testNode.js +4 -29
- package/build/bdd-framework/gen/testPoms.js +1 -1
- package/build/bdd-framework/index.js +1 -1
- package/build/bdd-framework/playwright/testTypeImpl.js +28 -10
- package/build/bdd-framework/playwright/types.js +8 -1
- package/build/bdd-framework/playwright/utils.js +22 -0
- package/build/bdd-framework/reporter/cucumber/base.js +2 -7
- package/build/bdd-framework/reporter/cucumber/html.js +9 -4
- package/build/bdd-framework/reporter/cucumber/messagesBuilder/AttachmentMapper.js +28 -10
- package/build/bdd-framework/reporter/cucumber/messagesBuilder/Builder.js +21 -20
- package/build/bdd-framework/reporter/cucumber/messagesBuilder/Hook.js +3 -3
- package/build/bdd-framework/reporter/cucumber/messagesBuilder/TestCaseRun.js +46 -18
- package/build/bdd-framework/reporter/cucumber/messagesBuilder/TestCaseRunHooks.js +41 -20
- package/build/bdd-framework/reporter/cucumber/messagesBuilder/TestStepAttachments.js +19 -2
- package/build/bdd-framework/reporter/cucumber/messagesBuilder/TestStepRun.js +42 -16
- package/build/bdd-framework/reporter/cucumber/messagesBuilder/{pwUtils.js → pwStepUtils.js} +33 -14
- package/build/bdd-framework/run/StepInvoker.js +8 -7
- package/build/bdd-framework/run/bddData/index.js +59 -0
- package/build/bdd-framework/run/bddData/types.js +5 -0
- package/build/bdd-framework/run/bddFixtures.js +5 -4
- package/build/bdd-framework/run/bddWorld.js +3 -3
- package/build/bdd-framework/run/bddWorldInternal.js +1 -5
- package/build/bdd-framework/snippets/index.js +1 -1
- package/build/bdd-framework/steps/createBdd.js +78 -0
- package/build/bdd-framework/steps/decorators/class.js +68 -0
- package/build/bdd-framework/steps/decorators/steps.js +98 -0
- package/build/bdd-framework/steps/defineStep.js +62 -0
- package/build/bdd-framework/steps/stepConfig.js +24 -0
- package/build/core/playwright/env-initializer.js +17 -2
- package/build/core/playwright/helpers/configFileNameProvider.js +1 -0
- package/build/core/playwright/index.js +14 -71
- package/build/core/playwright/readConfigFile.js +13 -2
- package/build/index.d.ts +2 -21
- package/changelog.md +14 -0
- package/npm-shrinkwrap.json +1 -1
- package/package.json +3 -2
- package/build/bdd-framework/run/bddDataAttachment.js +0 -46
|
@@ -5,8 +5,8 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.AttachmentMapper = void 0;
|
|
7
7
|
var _AutofillMap = require("../../../utils/AutofillMap");
|
|
8
|
-
var
|
|
9
|
-
var
|
|
8
|
+
var _pwStepUtils = require("./pwStepUtils");
|
|
9
|
+
var _stripAnsiEscapes = require("../../../utils/stripAnsiEscapes");
|
|
10
10
|
class AttachmentMapper {
|
|
11
11
|
result;
|
|
12
12
|
stepAttachments = new _AutofillMap.AutofillMap();
|
|
@@ -20,12 +20,14 @@ class AttachmentMapper {
|
|
|
20
20
|
}
|
|
21
21
|
mapAttachments() {
|
|
22
22
|
const allAttachments = this.result.attachments.slice();
|
|
23
|
-
const attachmentSteps = (0,
|
|
23
|
+
const attachmentSteps = (0, _pwStepUtils.collectStepsWithCategory)(this.result, 'attach');
|
|
24
24
|
attachmentSteps.forEach(attachmentStep => {
|
|
25
25
|
this.mapAttachment(attachmentStep, allAttachments);
|
|
26
26
|
});
|
|
27
27
|
this.unusedAttachments.push(...allAttachments);
|
|
28
28
|
this.mapUnusedAttachments();
|
|
29
|
+
this.mapStdoutAttachments('stdout');
|
|
30
|
+
this.mapStdoutAttachments('stderr');
|
|
29
31
|
}
|
|
30
32
|
mapAttachment(attachmentStep, allAttachments) {
|
|
31
33
|
const index = allAttachments.findIndex(a => getAttachmentStepTitle(a.name) === attachmentStep.title);
|
|
@@ -33,9 +35,6 @@ class AttachmentMapper {
|
|
|
33
35
|
throw new Error(`Attachment not found for step: ${attachmentStep.title}`);
|
|
34
36
|
}
|
|
35
37
|
const [foundAttachment] = allAttachments.splice(index, 1);
|
|
36
|
-
if ((0, _bddDataAttachment.isBddDataAttachment)(foundAttachment)) {
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
38
|
const parentStep = attachmentStep.parent;
|
|
40
39
|
// step.parent is empty:
|
|
41
40
|
// - in PW = 1.34 for screenshot attachment
|
|
@@ -49,13 +48,32 @@ class AttachmentMapper {
|
|
|
49
48
|
return;
|
|
50
49
|
}
|
|
51
50
|
// map unused attachments to the 'After Hooks' step
|
|
52
|
-
const afterHooksRoot =
|
|
53
|
-
if (!afterHooksRoot) {
|
|
54
|
-
throw new Error(`Can not find after hooks root to attach unused attachments.`);
|
|
55
|
-
}
|
|
51
|
+
const afterHooksRoot = this.getAfterHooksRoot();
|
|
56
52
|
const stepAttachments = this.stepAttachments.getOrCreate(afterHooksRoot, () => []);
|
|
57
53
|
stepAttachments.push(...this.unusedAttachments);
|
|
58
54
|
}
|
|
55
|
+
mapStdoutAttachments(name) {
|
|
56
|
+
var _this$result$name;
|
|
57
|
+
// map stdout / stderr to the 'After Hooks' step
|
|
58
|
+
if (!((_this$result$name = this.result[name]) !== null && _this$result$name !== void 0 && _this$result$name.length)) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const body = this.result[name].map(s => typeof s === 'string' ? (0, _stripAnsiEscapes.stripAnsiEscapes)(s) : s).join('');
|
|
62
|
+
const afterHooksRoot = this.getAfterHooksRoot();
|
|
63
|
+
const stepAttachments = this.stepAttachments.getOrCreate(afterHooksRoot, () => []);
|
|
64
|
+
stepAttachments.push({
|
|
65
|
+
name,
|
|
66
|
+
contentType: 'text/plain',
|
|
67
|
+
body: Buffer.from(body)
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
getAfterHooksRoot() {
|
|
71
|
+
const afterHooksRoot = (0, _pwStepUtils.getHooksRootPwStep)(this.result, 'after');
|
|
72
|
+
if (!afterHooksRoot) {
|
|
73
|
+
throw new Error(`Can not find after hooks root.`);
|
|
74
|
+
}
|
|
75
|
+
return afterHooksRoot;
|
|
76
|
+
}
|
|
59
77
|
}
|
|
60
78
|
// See: https://github.com/microsoft/playwright/blob/main/packages/playwright/src/worker/testInfo.ts#L413
|
|
61
79
|
exports.AttachmentMapper = AttachmentMapper;
|
|
@@ -43,10 +43,16 @@ class MessagesBuilder {
|
|
|
43
43
|
this.onEndPromise = new Promise(resolve => this.onEndPromiseResolve = resolve);
|
|
44
44
|
}
|
|
45
45
|
onTestEnd(test, result) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
46
|
+
// For skipped tests Playwright doesn't run fixtures
|
|
47
|
+
// and we don't have bddData attachment -> don't know feature uri.
|
|
48
|
+
// Don't add such test run to report.
|
|
49
|
+
if (test.expectedStatus === 'skipped') {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
// Important to create TestCaseRun here,
|
|
53
|
+
// b/c test properties can change after retries (e.g. annotations)
|
|
54
|
+
const testCaseRun = new _TestCaseRun.TestCaseRun(test, result, this.hooks);
|
|
55
|
+
this.testCaseRuns.push(testCaseRun);
|
|
50
56
|
}
|
|
51
57
|
onEnd(fullResult) {
|
|
52
58
|
this.fullResult = fullResult;
|
|
@@ -66,7 +72,7 @@ class MessagesBuilder {
|
|
|
66
72
|
async doBuildMessages() {
|
|
67
73
|
await this.onEndPromise;
|
|
68
74
|
// order here is important
|
|
69
|
-
this.createTestCaseRuns();
|
|
75
|
+
// this.createTestCaseRuns();
|
|
70
76
|
await this.loadFeatures();
|
|
71
77
|
this.createTestCases();
|
|
72
78
|
this.addMeta();
|
|
@@ -100,21 +106,16 @@ class MessagesBuilder {
|
|
|
100
106
|
async loadFeatures() {
|
|
101
107
|
await this.gherkinDocuments.load(this.testCaseRuns);
|
|
102
108
|
}
|
|
103
|
-
createTestCaseRuns() {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
const testCaseRun = new _TestCaseRun.TestCaseRun(test, result, this.hooks);
|
|
115
|
-
this.testCaseRuns.push(testCaseRun);
|
|
116
|
-
});
|
|
117
|
-
}
|
|
109
|
+
// private createTestCaseRuns() {
|
|
110
|
+
// this.onTestEnds.forEach(({ test, result }) => {
|
|
111
|
+
// // For skipped tests Playwright doesn't run fixtures
|
|
112
|
+
// // and we don't have bddData attachment -> don't know feature uri.
|
|
113
|
+
// // Don't add such test run to report.
|
|
114
|
+
// if (test.expectedStatus === 'skipped') return;
|
|
115
|
+
// const testCaseRun = new TestCaseRun(test, result, this.hooks);
|
|
116
|
+
// this.testCaseRuns.push(testCaseRun);
|
|
117
|
+
// });
|
|
118
|
+
// }
|
|
118
119
|
addMeta() {
|
|
119
120
|
this.report.meta = new _Meta.Meta().buildMessage();
|
|
120
121
|
}
|
|
@@ -60,10 +60,10 @@ class Hook {
|
|
|
60
60
|
const uri = file ? _path.default.relative((0, _configDir.getPlaywrightConfigDir)(), file) : undefined;
|
|
61
61
|
return {
|
|
62
62
|
uri,
|
|
63
|
-
location: {
|
|
64
|
-
line
|
|
63
|
+
location: line ? {
|
|
64
|
+
line,
|
|
65
65
|
column
|
|
66
|
-
}
|
|
66
|
+
} : undefined
|
|
67
67
|
};
|
|
68
68
|
}
|
|
69
69
|
}
|
|
@@ -7,11 +7,11 @@ exports.TestCaseRun = void 0;
|
|
|
7
7
|
var _utils = require("../../../utils");
|
|
8
8
|
var _TestStepRun = require("./TestStepRun");
|
|
9
9
|
var _timing = require("./timing");
|
|
10
|
-
var
|
|
11
|
-
var _bddDataAttachment = require("../../../run/bddDataAttachment");
|
|
10
|
+
var _pwStepUtils = require("./pwStepUtils");
|
|
12
11
|
var _AttachmentMapper = require("./AttachmentMapper");
|
|
13
12
|
var _TestCaseRunHooks = require("./TestCaseRunHooks");
|
|
14
13
|
var _Projects = require("./Projects");
|
|
14
|
+
var _bddData = require("../../../run/bddData");
|
|
15
15
|
class TestCaseRun {
|
|
16
16
|
test;
|
|
17
17
|
result;
|
|
@@ -21,6 +21,10 @@ class TestCaseRun {
|
|
|
21
21
|
testCase;
|
|
22
22
|
attachmentMapper;
|
|
23
23
|
projectInfo;
|
|
24
|
+
// collect steps with error and show only these errors in report.
|
|
25
|
+
// it allows to not show the same error on parent steps
|
|
26
|
+
errorSteps = new Set();
|
|
27
|
+
timeoutedStep;
|
|
24
28
|
executedBeforeHooks;
|
|
25
29
|
executedAfterHooks;
|
|
26
30
|
executedSteps;
|
|
@@ -30,7 +34,7 @@ class TestCaseRun {
|
|
|
30
34
|
this.result = result;
|
|
31
35
|
this.hooks = hooks;
|
|
32
36
|
this.id = this.generateTestRunId();
|
|
33
|
-
this.bddData = this.
|
|
37
|
+
this.bddData = this.extractBddData();
|
|
34
38
|
this.projectInfo = (0, _Projects.getProjectInfo)(this.test);
|
|
35
39
|
this.attachmentMapper = new _AttachmentMapper.AttachmentMapper(this.result);
|
|
36
40
|
this.executedSteps = this.fillExecutedSteps();
|
|
@@ -43,20 +47,30 @@ class TestCaseRun {
|
|
|
43
47
|
}
|
|
44
48
|
return this.testCase;
|
|
45
49
|
}
|
|
50
|
+
isTimeouted() {
|
|
51
|
+
return this.result.status === 'timedOut';
|
|
52
|
+
}
|
|
46
53
|
generateTestRunId() {
|
|
47
|
-
return `${this.test.id}-
|
|
54
|
+
return `${this.test.id}-attempt-${this.result.retry}`;
|
|
48
55
|
}
|
|
49
|
-
|
|
50
|
-
const
|
|
56
|
+
extractBddData() {
|
|
57
|
+
const {
|
|
58
|
+
bddData,
|
|
59
|
+
annotationIndex
|
|
60
|
+
} = (0, _bddData.getBddDataFromTest)(this.test);
|
|
51
61
|
if (!bddData) {
|
|
52
|
-
throw new Error(
|
|
62
|
+
throw new Error(`__bddData annotation is not found for test "${this.test.title}".`);
|
|
53
63
|
}
|
|
64
|
+
// remove __bddData annotation from test (mutate)
|
|
65
|
+
this.test.annotations.splice(annotationIndex, 1);
|
|
54
66
|
return bddData;
|
|
55
67
|
}
|
|
56
68
|
fillExecutedSteps() {
|
|
57
|
-
const possiblePwSteps = this.
|
|
69
|
+
const possiblePwSteps = this.getPossiblePlaywrightBddSteps();
|
|
58
70
|
return this.bddData.steps.map(bddDataStep => {
|
|
59
71
|
const pwStep = this.findPlaywrightStep(possiblePwSteps, bddDataStep);
|
|
72
|
+
this.registerErrorStep(pwStep);
|
|
73
|
+
this.registerTimeoutedStep(pwStep);
|
|
60
74
|
return {
|
|
61
75
|
bddDataStep,
|
|
62
76
|
pwStep
|
|
@@ -66,6 +80,23 @@ class TestCaseRun {
|
|
|
66
80
|
fillExecutedHooks(hookType) {
|
|
67
81
|
return new _TestCaseRunHooks.TestCaseRunHooks(this, hookType).fill(this.executedSteps);
|
|
68
82
|
}
|
|
83
|
+
registerErrorStep(pwStep) {
|
|
84
|
+
if (pwStep !== null && pwStep !== void 0 && pwStep.error) {
|
|
85
|
+
this.errorSteps.add(pwStep);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// eslint-disable-next-line complexity
|
|
89
|
+
registerTimeoutedStep(pwStep) {
|
|
90
|
+
if (!pwStep || !this.isTimeouted() || this.timeoutedStep) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const {
|
|
94
|
+
error
|
|
95
|
+
} = pwStep;
|
|
96
|
+
if ((0, _pwStepUtils.isUnknownDuration)(pwStep) || this.result.errors.some(e => e.message === (error === null || error === void 0 ? void 0 : error.message))) {
|
|
97
|
+
this.timeoutedStep = pwStep;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
69
100
|
buildMessages() {
|
|
70
101
|
return [this.buildTestCaseStarted(), ...this.executedBeforeHooks.buildMessages(), ...this.buildStepRuns(), ...this.executedAfterHooks.buildMessages(), this.buildTestCaseFinished()];
|
|
71
102
|
}
|
|
@@ -108,19 +139,16 @@ class TestCaseRun {
|
|
|
108
139
|
};
|
|
109
140
|
}
|
|
110
141
|
findPlaywrightStep(possiblePwSteps, bddDataStep) {
|
|
111
|
-
|
|
142
|
+
return possiblePwSteps.find(pwStep => {
|
|
112
143
|
return pwStep.location && (0, _utils.stringifyLocation)(pwStep.location) === bddDataStep.pwStepLocation;
|
|
113
144
|
});
|
|
114
|
-
if (!pwStep) {
|
|
115
|
-
throw new Error('Playwright step not found for bdd step');
|
|
116
|
-
}
|
|
117
|
-
return pwStep;
|
|
118
145
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
146
|
+
getPossiblePlaywrightBddSteps() {
|
|
147
|
+
// Before we collected only top-level steps and steps from before hooks (as they are background)
|
|
148
|
+
// But it's more reliable to just collect all test.step items b/c some Playwright versions
|
|
149
|
+
// move steps to fixtures (see: https://github.com/microsoft/playwright/issues/30075)
|
|
150
|
+
// Collecting all test.step items should be ok, as later we anyway map them by location.
|
|
151
|
+
return (0, _pwStepUtils.collectStepsWithCategory)(this.result, 'test.step');
|
|
124
152
|
}
|
|
125
153
|
}
|
|
126
154
|
exports.TestCaseRun = TestCaseRun;
|
|
@@ -5,12 +5,12 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.TestCaseRunHooks = void 0;
|
|
7
7
|
var _Hook = require("./Hook");
|
|
8
|
-
var
|
|
8
|
+
var _pwStepUtils = require("./pwStepUtils");
|
|
9
9
|
var _TestStepRun = require("./TestStepRun");
|
|
10
10
|
class TestCaseRunHooks {
|
|
11
11
|
testCaseRun;
|
|
12
12
|
hookType;
|
|
13
|
-
|
|
13
|
+
rootPwStep;
|
|
14
14
|
candidateSteps = [];
|
|
15
15
|
hookSteps = new Set();
|
|
16
16
|
executedHooks = new Map();
|
|
@@ -22,9 +22,10 @@ class TestCaseRunHooks {
|
|
|
22
22
|
this.setRootStep();
|
|
23
23
|
this.setCandidateSteps();
|
|
24
24
|
this.addStepsWithName();
|
|
25
|
-
this.addStepsWithAttachment();
|
|
26
25
|
this.addStepWithError();
|
|
27
|
-
this.
|
|
26
|
+
this.addStepWithTimeout();
|
|
27
|
+
this.addStepsWithAttachment();
|
|
28
|
+
this.excludeMainSteps(mainSteps);
|
|
28
29
|
this.setExecutedHooks();
|
|
29
30
|
return this;
|
|
30
31
|
}
|
|
@@ -33,7 +34,7 @@ class TestCaseRunHooks {
|
|
|
33
34
|
this.testCaseRun.getTestCase().getHooks(this.hookType).forEach(hookInfo => {
|
|
34
35
|
const executedHook = this.executedHooks.get(hookInfo.hook.internalId);
|
|
35
36
|
// todo: if pwStep is not found in this.executedBeforeHooks,
|
|
36
|
-
// it means that this hook comes from another
|
|
37
|
+
// it means that this hook comes from another attempt of this test case.
|
|
37
38
|
// We can stil try to find it in test result, as otherwise it will be marked as skipped,
|
|
38
39
|
// but actually it was executed.
|
|
39
40
|
const testStepRun = new _TestStepRun.TestStepRun(this.testCaseRun, hookInfo.testStep, executedHook === null || executedHook === void 0 ? void 0 : executedHook.pwStep);
|
|
@@ -42,13 +43,13 @@ class TestCaseRunHooks {
|
|
|
42
43
|
return messages;
|
|
43
44
|
}
|
|
44
45
|
setRootStep() {
|
|
45
|
-
this.
|
|
46
|
+
this.rootPwStep = (0, _pwStepUtils.getHooksRootPwStep)(this.testCaseRun.result, this.hookType);
|
|
46
47
|
}
|
|
47
48
|
setCandidateSteps() {
|
|
48
|
-
if (this.
|
|
49
|
-
this.candidateSteps.push(this.
|
|
49
|
+
if (this.rootPwStep) {
|
|
50
|
+
this.candidateSteps.push(this.rootPwStep);
|
|
50
51
|
}
|
|
51
|
-
this.candidateSteps.push(...(0,
|
|
52
|
+
this.candidateSteps.push(...(0, _pwStepUtils.collectStepsDfs)(this.rootPwStep));
|
|
52
53
|
}
|
|
53
54
|
addStepsWithName() {
|
|
54
55
|
this.candidateSteps.forEach(pwStep => {
|
|
@@ -68,25 +69,45 @@ class TestCaseRunHooks {
|
|
|
68
69
|
});
|
|
69
70
|
}
|
|
70
71
|
addStepWithError() {
|
|
71
|
-
const stepWithError = (0,
|
|
72
|
+
const stepWithError = (0, _pwStepUtils.findDeepestStepWithError)(this.rootPwStep);
|
|
72
73
|
if (stepWithError) {
|
|
73
74
|
this.hookSteps.add(stepWithError);
|
|
74
75
|
// in Playwright error is inherited by all parent steps,
|
|
75
76
|
// but we want to show it once (in the deepest step)
|
|
76
|
-
this.
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
77
|
+
this.testCaseRun.registerErrorStep(stepWithError);
|
|
78
|
+
this.testCaseRun.registerTimeoutedStep(stepWithError);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
addStepWithTimeout() {
|
|
82
|
+
if (!this.testCaseRun.isTimeouted()) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
if (this.testCaseRun.timeoutedStep) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const timeoutedStep = this.hookType === 'before' ?
|
|
89
|
+
// Timeouted steps have duration = -1 in PW <= 1.39 and no error field.
|
|
90
|
+
// In PW > 1.39 timeouted steps have '.error' populated
|
|
91
|
+
(0, _pwStepUtils.findDeepestStepWithUnknownDuration)(this.rootPwStep) :
|
|
92
|
+
// Timeouted after hooks don't have duration = -1,
|
|
93
|
+
// so there is no way to find which exactly fixture timed out.
|
|
94
|
+
// We mark root 'After Hooks' step as timeouted.
|
|
95
|
+
this.rootPwStep;
|
|
96
|
+
if (timeoutedStep) {
|
|
97
|
+
this.hookSteps.add(timeoutedStep);
|
|
98
|
+
this.testCaseRun.timeoutedStep = timeoutedStep;
|
|
81
99
|
}
|
|
82
100
|
}
|
|
83
|
-
|
|
84
|
-
// exclude background steps, b/c they are in pickle
|
|
101
|
+
excludeMainSteps(mainSteps) {
|
|
102
|
+
// - exclude background steps, b/c they are in pickle and should not in hooks.
|
|
103
|
+
// - exclude other test.step items that are bdd steps and should not be in hooks.
|
|
85
104
|
// Important to run this fn after this.fillExecutedSteps()
|
|
86
105
|
// as we assume steps are already populated
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
106
|
+
mainSteps.forEach(stepInfo => {
|
|
107
|
+
if (stepInfo.pwStep) {
|
|
108
|
+
this.hookSteps.delete(stepInfo.pwStep);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
90
111
|
}
|
|
91
112
|
setExecutedHooks() {
|
|
92
113
|
this.hookSteps.forEach(pwStep => {
|
|
@@ -7,6 +7,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
7
7
|
exports.TestStepAttachments = void 0;
|
|
8
8
|
var _fs = _interopRequireDefault(require("fs"));
|
|
9
9
|
var messages = _interopRequireWildcard(require("@cucumber/messages"));
|
|
10
|
+
var _path = _interopRequireDefault(require("path"));
|
|
10
11
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
11
12
|
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
12
13
|
/**
|
|
@@ -44,7 +45,23 @@ class TestStepAttachments {
|
|
|
44
45
|
};
|
|
45
46
|
}
|
|
46
47
|
getAttachmentBodyBase64(pwAttachment) {
|
|
47
|
-
|
|
48
|
+
if (pwAttachment.path) {
|
|
49
|
+
if (_fs.default.existsSync(pwAttachment.path)) {
|
|
50
|
+
return _fs.default.readFileSync(pwAttachment.path, 'base64');
|
|
51
|
+
}
|
|
52
|
+
throw createMissingAttachmentError(pwAttachment.path);
|
|
53
|
+
}
|
|
54
|
+
if (pwAttachment.body) {
|
|
55
|
+
return pwAttachment.body.toString('base64');
|
|
56
|
+
}
|
|
57
|
+
throw new Error(`Playwright attachment without path and body`);
|
|
48
58
|
}
|
|
49
59
|
}
|
|
50
|
-
exports.TestStepAttachments = TestStepAttachments;
|
|
60
|
+
exports.TestStepAttachments = TestStepAttachments;
|
|
61
|
+
function createMissingAttachmentError(attachmentPath) {
|
|
62
|
+
const attachmentDir = _path.default.join(_path.default.dirname(attachmentPath));
|
|
63
|
+
const attachmentDirExists = _fs.default.existsSync(attachmentDir);
|
|
64
|
+
const files = attachmentDirExists ? _fs.default.readdirSync(attachmentDir) : [];
|
|
65
|
+
const errorMsg = [`Attachment file is not found:`, attachmentPath, `Attachment dir ${attachmentDirExists ? 'exists' : 'does not exist'}.`, ...(attachmentDirExists ? [`Available files (${files.length}):`, ...files] : []), ''].join('\n');
|
|
66
|
+
return new Error(errorMsg);
|
|
67
|
+
}
|
|
@@ -25,6 +25,9 @@ class TestStepRun {
|
|
|
25
25
|
// prettier-ignore
|
|
26
26
|
...stepAttachments.buildMessages(), this.buildTestStepFinished()];
|
|
27
27
|
}
|
|
28
|
+
isHook() {
|
|
29
|
+
return Boolean(this.testStep.hookId);
|
|
30
|
+
}
|
|
28
31
|
wasExecuted() {
|
|
29
32
|
return Boolean(this.pwStep);
|
|
30
33
|
}
|
|
@@ -45,16 +48,17 @@ class TestStepRun {
|
|
|
45
48
|
};
|
|
46
49
|
}
|
|
47
50
|
buildTestStepFinished() {
|
|
48
|
-
|
|
49
|
-
const error = (_this$pwStep = this.pwStep) === null || _this$pwStep === void 0 ? void 0 : _this$pwStep.error;
|
|
51
|
+
const error = this.getStepError();
|
|
50
52
|
const testStepFinished = {
|
|
51
53
|
testCaseStartedId: this.testCaseRun.id,
|
|
52
54
|
testStepId: this.testStep.id,
|
|
53
55
|
testStepResult: {
|
|
54
56
|
duration: messages.TimeConversion.millisecondsToDuration(this.duration),
|
|
55
57
|
status: this.getStatus(error),
|
|
56
|
-
message
|
|
57
|
-
|
|
58
|
+
// 'message' is deprecated since cucumber 10.4 in favor of 'exception' field
|
|
59
|
+
// See: https://github.com/cucumber/react-components/pull/345
|
|
60
|
+
message: error ? buildErrorMessage(error) : undefined,
|
|
61
|
+
exception: error ? buildException(error) : undefined
|
|
58
62
|
},
|
|
59
63
|
timestamp: (0, _timing.toCucumberTimestamp)(this.startTime.getTime() + this.duration)
|
|
60
64
|
};
|
|
@@ -62,27 +66,49 @@ class TestStepRun {
|
|
|
62
66
|
testStepFinished
|
|
63
67
|
};
|
|
64
68
|
}
|
|
65
|
-
buildException(error) {
|
|
66
|
-
return {
|
|
67
|
-
type: 'Error',
|
|
68
|
-
message: error.message ? (0, _stripAnsiEscapes.stripAnsiEscapes)(error.message) : undefined,
|
|
69
|
-
stackTrace: error.stack ? (0, _stripAnsiEscapes.stripAnsiEscapes)(error.stack) : undefined
|
|
70
|
-
// Use type casting b/c older versions of @cucumber/messages don't have 'stackTrace' field
|
|
71
|
-
// todo: add direct dependency on @cucumber/messages
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
69
|
getStatus(error) {
|
|
75
70
|
switch (true) {
|
|
76
|
-
case !this.wasExecuted():
|
|
77
|
-
return messages.TestStepResultStatus.SKIPPED;
|
|
78
71
|
case Boolean(error):
|
|
79
72
|
return messages.TestStepResultStatus.FAILED;
|
|
73
|
+
// For hooks that were not executted we return PASSED, not SKIPPED.
|
|
74
|
+
// Because these hooks can be from another run attempt of this testCase.
|
|
75
|
+
// If marked as skipped, the whole run is marked as skipped in reporter.
|
|
76
|
+
case !this.isHook() && !this.wasExecuted():
|
|
77
|
+
return messages.TestStepResultStatus.SKIPPED;
|
|
80
78
|
default:
|
|
81
79
|
return messages.TestStepResultStatus.PASSED;
|
|
82
80
|
}
|
|
83
81
|
}
|
|
82
|
+
// eslint-disable-next-line complexity
|
|
83
|
+
getStepError() {
|
|
84
|
+
if (!this.pwStep) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (this.testCaseRun.errorSteps.has(this.pwStep)) {
|
|
88
|
+
return this.pwStep.error;
|
|
89
|
+
}
|
|
90
|
+
if (this.testCaseRun.isTimeouted() && this.pwStep === this.testCaseRun.timeoutedStep) {
|
|
91
|
+
var _this$testCaseRun$res;
|
|
92
|
+
return {
|
|
93
|
+
message: (_this$testCaseRun$res = this.testCaseRun.result.errors) === null || _this$testCaseRun$res === void 0 ? void 0 : _this$testCaseRun$res.map(e => e.message).join('\n\n')
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
84
97
|
}
|
|
85
98
|
exports.TestStepRun = TestStepRun;
|
|
86
|
-
function
|
|
99
|
+
function buildErrorMessage(error) {
|
|
87
100
|
return (0, _stripAnsiEscapes.stripAnsiEscapes)([error.message, error.snippet].filter(Boolean).join('\n'));
|
|
101
|
+
}
|
|
102
|
+
function buildException(error) {
|
|
103
|
+
return {
|
|
104
|
+
type: 'Error',
|
|
105
|
+
message: buildErrorMessage(error),
|
|
106
|
+
// todo: extract only trace?
|
|
107
|
+
stackTrace: error.stack ? extractStackTrace((0, _stripAnsiEscapes.stripAnsiEscapes)(error.stack)) : undefined
|
|
108
|
+
// Use type casting b/c older versions of @cucumber/messages don't have 'stackTrace' field
|
|
109
|
+
// todo: add direct dependency on @cucumber/messages
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function extractStackTrace(errorStack) {
|
|
113
|
+
return errorStack.split('\n').filter(line => line.match(/^\s+at .*/)).join('\n');
|
|
88
114
|
}
|
|
@@ -5,37 +5,53 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.collectStepsDfs = collectStepsDfs;
|
|
7
7
|
exports.collectStepsWithCategory = collectStepsWithCategory;
|
|
8
|
-
exports.
|
|
9
|
-
exports.
|
|
8
|
+
exports.findDeepestStepWithError = findDeepestStepWithError;
|
|
9
|
+
exports.findDeepestStepWithUnknownDuration = findDeepestStepWithUnknownDuration;
|
|
10
|
+
exports.getHooksRootPwStep = getHooksRootPwStep;
|
|
11
|
+
exports.isUnknownDuration = isUnknownDuration;
|
|
10
12
|
/**
|
|
11
13
|
* Utility functions for filtering Playwright steps.
|
|
12
14
|
*/
|
|
13
|
-
// Playwright step categoires, that can be mapped to testStep / hook in Cucumber
|
|
15
|
+
// Playwright step categoires, that can be mapped to testStep / hook in Cucumber messages
|
|
14
16
|
const MEANINGFUL_STEP_CATEGORIES = ['hook', 'fixture', 'test.step'];
|
|
15
17
|
function collectStepsWithCategory(parent, category) {
|
|
16
18
|
const categories = Array.isArray(category) ? category : [category];
|
|
17
19
|
const steps = collectStepsDfs(parent);
|
|
18
20
|
return steps.filter(step => categories.includes(step.category));
|
|
19
21
|
}
|
|
20
|
-
function
|
|
22
|
+
function getHooksRootPwStep(result, type) {
|
|
21
23
|
const rootStepTitle = type === 'before' ? 'Before Hooks' : 'After Hooks';
|
|
22
24
|
return result.steps.find(step => step.category === 'hook' && step.title === rootStepTitle);
|
|
23
25
|
}
|
|
26
|
+
function findDeepestStepWithError(root) {
|
|
27
|
+
if (!root) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
return findDeepestStepWith(root, pwStep => {
|
|
31
|
+
return Boolean(pwStep.error) && MEANINGFUL_STEP_CATEGORIES.includes(pwStep.category);
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
function findDeepestStepWithUnknownDuration(root) {
|
|
35
|
+
if (!root) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
return findDeepestStepWith(root, pwStep => {
|
|
39
|
+
return isUnknownDuration(pwStep) && MEANINGFUL_STEP_CATEGORIES.includes(pwStep.category);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
24
42
|
/**
|
|
25
|
-
*
|
|
43
|
+
* Finds the deepest step that satisfies predicate function.
|
|
26
44
|
*/
|
|
27
|
-
function
|
|
28
|
-
let
|
|
29
|
-
while (
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
});
|
|
33
|
-
if (!nextErrorStep) {
|
|
45
|
+
function findDeepestStepWith(root, predicate) {
|
|
46
|
+
let currentStep = predicate(root) ? root : undefined;
|
|
47
|
+
while (currentStep) {
|
|
48
|
+
const nextStep = currentStep.steps.find(pwStep => predicate(pwStep));
|
|
49
|
+
if (!nextStep) {
|
|
34
50
|
break;
|
|
35
51
|
}
|
|
36
|
-
|
|
52
|
+
currentStep = nextStep;
|
|
37
53
|
}
|
|
38
|
-
return
|
|
54
|
+
return currentStep;
|
|
39
55
|
}
|
|
40
56
|
/**
|
|
41
57
|
* Returns all steps in DFS order.
|
|
@@ -48,4 +64,7 @@ function collectStepsDfs(parent) {
|
|
|
48
64
|
res.push(...collectStepsDfs(step));
|
|
49
65
|
return res;
|
|
50
66
|
}, [])) || [];
|
|
67
|
+
}
|
|
68
|
+
function isUnknownDuration(pwStep) {
|
|
69
|
+
return pwStep.duration === -1;
|
|
51
70
|
}
|
|
@@ -7,7 +7,7 @@ exports.StepInvoker = void 0;
|
|
|
7
7
|
var _loadSteps = require("../cucumber/loadSteps");
|
|
8
8
|
var _getLocationInFile = require("../playwright/getLocationInFile");
|
|
9
9
|
var _testTypeImpl = require("../playwright/testTypeImpl");
|
|
10
|
-
var _defineStep = require("../
|
|
10
|
+
var _defineStep = require("../steps/defineStep");
|
|
11
11
|
var _lang = require("../config/lang");
|
|
12
12
|
/**
|
|
13
13
|
* Class to invoke step in playwright runner.
|
|
@@ -25,17 +25,18 @@ class StepInvoker {
|
|
|
25
25
|
* Invokes particular step.
|
|
26
26
|
* See: https://github.com/cucumber/cucumber-js/blob/main/src/runtime/test_case_runner.ts#L299
|
|
27
27
|
*/
|
|
28
|
-
async invoke(
|
|
28
|
+
async invoke(stepText, argument, stepFixtures) {
|
|
29
|
+
var _this$world$$internal;
|
|
29
30
|
this.world.$internal.currentStepFixtures = stepFixtures || {};
|
|
30
|
-
const stepDefinition = this.getStepDefinition(
|
|
31
|
+
const stepDefinition = this.getStepDefinition(stepText);
|
|
31
32
|
// Get location of step call in generated test file.
|
|
32
33
|
// This call must be exactly here to have correct call stack (before async calls)
|
|
33
34
|
const location = (0, _getLocationInFile.getLocationInFile)(this.world.testInfo.file);
|
|
34
|
-
const stepTitle = this.getStepTitle(
|
|
35
|
+
const stepTitle = this.getStepTitle(stepText);
|
|
35
36
|
const code = (0, _defineStep.getStepCode)(stepDefinition);
|
|
36
|
-
const parameters = await this.getStepParameters(stepDefinition,
|
|
37
|
-
this.world.$internal.
|
|
38
|
-
|
|
37
|
+
const parameters = await this.getStepParameters(stepDefinition, stepText, argument || undefined);
|
|
38
|
+
(_this$world$$internal = this.world.$internal.bddDataManager) === null || _this$world$$internal === void 0 || _this$world$$internal.registerStep(stepDefinition, stepText, location);
|
|
39
|
+
await (0, _testTypeImpl.runStepWithCustomLocation)(this.world.test, stepTitle, location, () => code.apply(this.world, parameters));
|
|
39
40
|
}
|
|
40
41
|
getStepDefinition(text) {
|
|
41
42
|
const stepDefinition = (0, _loadSteps.findStepDefinition)(this.world.options.supportCodeLibrary, text, this.world.testInfo.file);
|