@zohodesk/testinglibrary 0.1.6-exp.8 → 0.1.7
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/.eslintrc.js +4 -1
- package/build/bdd-framework/cli/commands/test.js +9 -3
- package/build/bdd-framework/cucumber/loadSteps.js +8 -4
- package/build/bdd-framework/gen/fixtures.js +3 -1
- package/build/bdd-framework/gen/formatter.js +24 -8
- package/build/bdd-framework/gen/i18n.js +6 -2
- package/build/bdd-framework/gen/index.js +3 -1
- package/build/bdd-framework/gen/testFile.js +56 -20
- package/build/bdd-framework/gen/testNode.js +11 -5
- package/build/bdd-framework/gen/testPoms.js +15 -5
- package/build/bdd-framework/hooks/scenario.js +23 -9
- package/build/bdd-framework/hooks/worker.js +15 -5
- package/build/bdd-framework/playwright/fixtureParameterNames.js +24 -8
- package/build/bdd-framework/playwright/getLocationInFile.js +7 -5
- package/build/bdd-framework/playwright/testTypeImpl.js +3 -1
- package/build/bdd-framework/playwright/transform.js +6 -2
- package/build/bdd-framework/stepDefinitions/createBdd.js +6 -2
- package/build/bdd-framework/stepDefinitions/decorators/class.js +12 -4
- package/build/bdd-framework/stepDefinitions/decorators/steps.js +6 -2
- package/build/bdd-framework/utils/exit.js +9 -4
- package/build/bdd-framework/utils/index.js +3 -1
- package/build/bdd-framework/utils/logger.js +3 -1
- package/changelog.md +8 -2
- package/npm-shrinkwrap.json +1 -1
- package/package.json +1 -1
package/.eslintrc.js
CHANGED
|
@@ -30,8 +30,12 @@ function readConfigsFromEnv() {
|
|
|
30
30
|
}
|
|
31
31
|
function mergeCliOptions(configs, opts) {
|
|
32
32
|
configs.forEach(config => {
|
|
33
|
-
if ('tags' in opts)
|
|
34
|
-
|
|
33
|
+
if ('tags' in opts) {
|
|
34
|
+
config.tags = opts.tags;
|
|
35
|
+
}
|
|
36
|
+
if ('verbose' in opts) {
|
|
37
|
+
config.verbose = Boolean(opts.verbose);
|
|
38
|
+
}
|
|
35
39
|
});
|
|
36
40
|
}
|
|
37
41
|
function assertConfigsCount(configs) {
|
|
@@ -54,5 +58,7 @@ async function runInWorker(config) {
|
|
|
54
58
|
}
|
|
55
59
|
});
|
|
56
60
|
const [exitCode] = await (0, _events.once)(worker, 'exit');
|
|
57
|
-
if (exitCode)
|
|
61
|
+
if (exitCode) {
|
|
62
|
+
(0, _exit.exit)();
|
|
63
|
+
}
|
|
58
64
|
}
|
|
@@ -25,10 +25,14 @@ function findStepDefinition(supportCodeLibrary, stepText, file) {
|
|
|
25
25
|
const matchedSteps = supportCodeLibrary.stepDefinitions.filter(step => {
|
|
26
26
|
return step.matchesStepName(stepText);
|
|
27
27
|
});
|
|
28
|
-
if (matchedSteps.length === 0)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
if (matchedSteps.length === 0) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (matchedSteps.length > 1) {
|
|
32
|
+
(0, _exit.exit)([`Multiple step definitions matched for text: "${stepText}" (${file})`,
|
|
33
|
+
// todo: print location of every step definition (as in cucumber)
|
|
34
|
+
...matchedSteps.map(s => ` ${s.pattern}`)].join('\n'));
|
|
35
|
+
}
|
|
32
36
|
return matchedSteps[0];
|
|
33
37
|
}
|
|
34
38
|
function hasTsNodeRegister(runConfiguration) {
|
|
@@ -23,7 +23,9 @@ function extractFixtureNames(fn) {
|
|
|
23
23
|
* and extracts fixtures names from it.
|
|
24
24
|
*/
|
|
25
25
|
function extractFixtureNamesFromFnBodyMemo(fn) {
|
|
26
|
-
if (typeof fn !== 'function')
|
|
26
|
+
if (typeof fn !== 'function') {
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
27
29
|
const fnWithFixtures = fn;
|
|
28
30
|
if (!fnWithFixtures[bodyFixturesSymbol]) {
|
|
29
31
|
fnWithFixtures[bodyFixturesSymbol] = extractFixtureNamesFromFnBody(fn).filter(name => !(0, _bddFixtures.isBddAutoInjectFixture)(name));
|
|
@@ -18,7 +18,9 @@ class Formatter {
|
|
|
18
18
|
fileHeader(uri, importTestFrom) {
|
|
19
19
|
const file = (importTestFrom === null || importTestFrom === void 0 ? void 0 : importTestFrom.file) || '@zohodesk/testinglibrary';
|
|
20
20
|
let varName = (importTestFrom === null || importTestFrom === void 0 ? void 0 : importTestFrom.varName) || 'test';
|
|
21
|
-
if (varName !== 'test')
|
|
21
|
+
if (varName !== 'test') {
|
|
22
|
+
varName = `${varName} as test`;
|
|
23
|
+
}
|
|
22
24
|
return [`/** Generated from: ${uri} */`,
|
|
23
25
|
// prettier-ignore
|
|
24
26
|
// this.quoted() is not possible for 'import from' as backticks not parsed
|
|
@@ -26,7 +28,9 @@ class Formatter {
|
|
|
26
28
|
}
|
|
27
29
|
suite(node, children) {
|
|
28
30
|
const firstLine = `test.describe${this.getSubFn(node)}(${this.quoted(node.title)}, () => {`;
|
|
29
|
-
if (!children.length)
|
|
31
|
+
if (!children.length) {
|
|
32
|
+
return [`${firstLine}});`, ''];
|
|
33
|
+
}
|
|
30
34
|
return [firstLine, '', ...children.map(indent), `});`, ''];
|
|
31
35
|
}
|
|
32
36
|
beforeEach(fixtures, children) {
|
|
@@ -38,7 +42,9 @@ class Formatter {
|
|
|
38
42
|
const fixturesStr = [...fixtures].join(', ');
|
|
39
43
|
const title = this.quoted([node.title, ...node.tags].join(' '));
|
|
40
44
|
const firstLine = `test${this.getSubFn(node)}(${title}, async ({ ${fixturesStr} }) => {`;
|
|
41
|
-
if (!children.length)
|
|
45
|
+
if (!children.length) {
|
|
46
|
+
return [`${firstLine}});`, ''];
|
|
47
|
+
}
|
|
42
48
|
return [firstLine, ...children.map(indent), `});`, ''];
|
|
43
49
|
}
|
|
44
50
|
// eslint-disable-next-line max-params
|
|
@@ -82,12 +88,16 @@ class Formatter {
|
|
|
82
88
|
`}[testInfo.titlePath.slice(2).join(${JSON.stringify(TAGS_FIXTURE_TEST_KEY_SEPARATOR)})] || []),`] : [];
|
|
83
89
|
}
|
|
84
90
|
scenarioHookFixtures(fixtureNames) {
|
|
85
|
-
if (!fixtureNames.length)
|
|
91
|
+
if (!fixtureNames.length) {
|
|
92
|
+
return [];
|
|
93
|
+
}
|
|
86
94
|
const fixtures = fixtureNames.join(', ');
|
|
87
95
|
return [`$scenarioHookFixtures: ({ ${fixtures} }, use) => use({ ${fixtures} }),`];
|
|
88
96
|
}
|
|
89
97
|
workerHookFixtures(fixtureNames) {
|
|
90
|
-
if (!fixtureNames.length)
|
|
98
|
+
if (!fixtureNames.length) {
|
|
99
|
+
return [];
|
|
100
|
+
}
|
|
91
101
|
const fixtures = fixtureNames.join(', ');
|
|
92
102
|
const scope = this.quoted('worker');
|
|
93
103
|
return [`$workerHookFixtures: [({ ${fixtures} }, use) => use({ ${fixtures} }), { scope: ${scope} }],`];
|
|
@@ -96,9 +106,15 @@ class Formatter {
|
|
|
96
106
|
return [`$lang: ({}, use) => use(${this.quoted(lang)}),`];
|
|
97
107
|
}
|
|
98
108
|
getSubFn(node) {
|
|
99
|
-
if (node.flags.only)
|
|
100
|
-
|
|
101
|
-
|
|
109
|
+
if (node.flags.only) {
|
|
110
|
+
return '.only';
|
|
111
|
+
}
|
|
112
|
+
if (node.flags.skip) {
|
|
113
|
+
return '.skip';
|
|
114
|
+
}
|
|
115
|
+
if (node.flags.fixme) {
|
|
116
|
+
return '.fixme';
|
|
117
|
+
}
|
|
102
118
|
return '';
|
|
103
119
|
}
|
|
104
120
|
/**
|
|
@@ -23,10 +23,14 @@ function getKeywordsMap(language) {
|
|
|
23
23
|
function handleKeyword(enKeyword, origMap, targetMap) {
|
|
24
24
|
const nativeKeywords = origMap[enKeyword];
|
|
25
25
|
// Array.isArray converts to any[]
|
|
26
|
-
if (typeof nativeKeywords === 'string')
|
|
26
|
+
if (typeof nativeKeywords === 'string') {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
27
29
|
nativeKeywords.forEach(nativeKeyword => {
|
|
28
30
|
nativeKeyword = nativeKeyword.trim();
|
|
29
|
-
if (!nativeKeyword || nativeKeyword === '*')
|
|
31
|
+
if (!nativeKeyword || nativeKeyword === '*') {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
30
34
|
targetMap.set(nativeKeyword, capitalizeFirstLetter(enKeyword));
|
|
31
35
|
});
|
|
32
36
|
}
|
|
@@ -39,7 +39,9 @@ class TestFilesGenerator {
|
|
|
39
39
|
this.logger = new _logger.Logger({
|
|
40
40
|
verbose: config.verbose
|
|
41
41
|
});
|
|
42
|
-
if (config.tags)
|
|
42
|
+
if (config.tags) {
|
|
43
|
+
this.tagsExpression = (0, _tagExpressions.default)(config.tags);
|
|
44
|
+
}
|
|
43
45
|
}
|
|
44
46
|
async generate() {
|
|
45
47
|
await (0, _exit.withExitHandler)(async () => {
|
|
@@ -41,7 +41,9 @@ class TestFile {
|
|
|
41
41
|
const {
|
|
42
42
|
uri
|
|
43
43
|
} = this.options.doc;
|
|
44
|
-
if (!uri)
|
|
44
|
+
if (!uri) {
|
|
45
|
+
throw new Error(`Document without uri`);
|
|
46
|
+
}
|
|
45
47
|
return uri;
|
|
46
48
|
}
|
|
47
49
|
get content() {
|
|
@@ -69,9 +71,11 @@ class TestFile {
|
|
|
69
71
|
}
|
|
70
72
|
save() {
|
|
71
73
|
const dir = _path.default.dirname(this.outputPath);
|
|
72
|
-
if (!_fs.default.existsSync(dir))
|
|
73
|
-
|
|
74
|
-
|
|
74
|
+
if (!_fs.default.existsSync(dir)) {
|
|
75
|
+
_fs.default.mkdirSync(dir, {
|
|
76
|
+
recursive: true
|
|
77
|
+
});
|
|
78
|
+
}
|
|
75
79
|
_fs.default.writeFileSync(this.outputPath, this.content);
|
|
76
80
|
}
|
|
77
81
|
getFileHeader() {
|
|
@@ -87,7 +91,9 @@ class TestFile {
|
|
|
87
91
|
const {
|
|
88
92
|
importTestFrom
|
|
89
93
|
} = this.config;
|
|
90
|
-
if (!importTestFrom)
|
|
94
|
+
if (!importTestFrom) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
91
97
|
const {
|
|
92
98
|
file,
|
|
93
99
|
varName
|
|
@@ -105,7 +111,9 @@ class TestFile {
|
|
|
105
111
|
const {
|
|
106
112
|
feature
|
|
107
113
|
} = this.options.doc;
|
|
108
|
-
if (!feature)
|
|
114
|
+
if (!feature) {
|
|
115
|
+
throw new Error(`Document without feature.`);
|
|
116
|
+
}
|
|
109
117
|
return this.getSuite(feature);
|
|
110
118
|
}
|
|
111
119
|
/**
|
|
@@ -113,15 +121,23 @@ class TestFile {
|
|
|
113
121
|
*/
|
|
114
122
|
getSuite(feature, parent) {
|
|
115
123
|
const node = new _testNode.TestNode(feature, parent);
|
|
116
|
-
if (node.isSkipped())
|
|
124
|
+
if (node.isSkipped()) {
|
|
125
|
+
return this.formatter.suite(node, []);
|
|
126
|
+
}
|
|
117
127
|
const lines = [];
|
|
118
128
|
feature.children.forEach(child => lines.push(...this.getSuiteChild(child, node)));
|
|
119
129
|
return this.formatter.suite(node, lines);
|
|
120
130
|
}
|
|
121
131
|
getSuiteChild(child, parent) {
|
|
122
|
-
if ('rule' in child && child.rule)
|
|
123
|
-
|
|
124
|
-
|
|
132
|
+
if ('rule' in child && child.rule) {
|
|
133
|
+
return this.getSuite(child.rule, parent);
|
|
134
|
+
}
|
|
135
|
+
if (child.background) {
|
|
136
|
+
return this.getBeforeEach(child.background, parent);
|
|
137
|
+
}
|
|
138
|
+
if (child.scenario) {
|
|
139
|
+
return this.getScenarioLines(child.scenario, parent);
|
|
140
|
+
}
|
|
125
141
|
throw new Error(`Empty child: ${JSON.stringify(child)}`);
|
|
126
142
|
}
|
|
127
143
|
getScenarioLines(scenario, parent) {
|
|
@@ -146,7 +162,9 @@ class TestFile {
|
|
|
146
162
|
*/
|
|
147
163
|
getOutlineSuite(scenario, parent) {
|
|
148
164
|
const node = new _testNode.TestNode(scenario, parent);
|
|
149
|
-
if (node.isSkipped())
|
|
165
|
+
if (node.isSkipped()) {
|
|
166
|
+
return this.formatter.suite(node, []);
|
|
167
|
+
}
|
|
150
168
|
const lines = [];
|
|
151
169
|
let exampleIndex = 0;
|
|
152
170
|
scenario.examples.forEach(examples => {
|
|
@@ -168,8 +186,12 @@ class TestFile {
|
|
|
168
186
|
name: title,
|
|
169
187
|
tags: examples.tags
|
|
170
188
|
}, parent);
|
|
171
|
-
if (this.skipByTagsExpression(node))
|
|
172
|
-
|
|
189
|
+
if (this.skipByTagsExpression(node)) {
|
|
190
|
+
return [];
|
|
191
|
+
}
|
|
192
|
+
if (node.isSkipped()) {
|
|
193
|
+
return this.formatter.test(node, new Set(), []);
|
|
194
|
+
}
|
|
173
195
|
this.testNodes.push(node);
|
|
174
196
|
const {
|
|
175
197
|
fixtures,
|
|
@@ -182,8 +204,12 @@ class TestFile {
|
|
|
182
204
|
*/
|
|
183
205
|
getTest(scenario, parent) {
|
|
184
206
|
const node = new _testNode.TestNode(scenario, parent);
|
|
185
|
-
if (this.skipByTagsExpression(node))
|
|
186
|
-
|
|
207
|
+
if (this.skipByTagsExpression(node)) {
|
|
208
|
+
return [];
|
|
209
|
+
}
|
|
210
|
+
if (node.isSkipped()) {
|
|
211
|
+
return this.formatter.test(node, new Set(), []);
|
|
212
|
+
}
|
|
187
213
|
this.testNodes.push(node);
|
|
188
214
|
const {
|
|
189
215
|
fixtures,
|
|
@@ -264,8 +290,12 @@ class TestFile {
|
|
|
264
290
|
}
|
|
265
291
|
// for cucumber-style stepConfig is undefined
|
|
266
292
|
const stepConfig = (0, _stepConfig.getStepConfig)(stepDefinition);
|
|
267
|
-
if (stepConfig !== null && stepConfig !== void 0 && stepConfig.hasCustomTest)
|
|
268
|
-
|
|
293
|
+
if (stepConfig !== null && stepConfig !== void 0 && stepConfig.hasCustomTest) {
|
|
294
|
+
this.hasCustomTest = true;
|
|
295
|
+
}
|
|
296
|
+
if (!(0, _stepConfig.isPlaywrightStyle)(stepConfig)) {
|
|
297
|
+
this.hasCucumberStyle = true;
|
|
298
|
+
}
|
|
269
299
|
const fixtureNames = this.getStepFixtureNames(stepDefinition);
|
|
270
300
|
const line = (0, _stepConfig.isDecorator)(stepConfig) ? '' : this.formatter.step(enKeyword, pickleStep.text, pickleStep.argument, fixtureNames);
|
|
271
301
|
return {
|
|
@@ -296,14 +326,18 @@ class TestFile {
|
|
|
296
326
|
const hasRowId = !outlineExampleRowId || astNodeIds.includes(outlineExampleRowId);
|
|
297
327
|
return hasStepId && hasRowId;
|
|
298
328
|
});
|
|
299
|
-
if (pickleStep)
|
|
329
|
+
if (pickleStep) {
|
|
330
|
+
return pickleStep;
|
|
331
|
+
}
|
|
300
332
|
}
|
|
301
333
|
throw new Error(`Pickle step not found for step: ${step.text}`);
|
|
302
334
|
}
|
|
303
335
|
getStepEnglishKeyword(step) {
|
|
304
336
|
const nativeKeyword = step.keyword.trim();
|
|
305
337
|
const enKeyword = nativeKeyword === '*' ? 'And' : this.getEnglishKeyword(nativeKeyword);
|
|
306
|
-
if (!enKeyword)
|
|
338
|
+
if (!enKeyword) {
|
|
339
|
+
throw new Error(`Keyword not found: ${nativeKeyword}`);
|
|
340
|
+
}
|
|
307
341
|
return enKeyword;
|
|
308
342
|
}
|
|
309
343
|
getStepFixtureNames(stepDefinition) {
|
|
@@ -340,7 +374,9 @@ class TestFile {
|
|
|
340
374
|
exampleRow.cells.forEach((cell, index) => {
|
|
341
375
|
var _examples$tableHeader;
|
|
342
376
|
const colName = (_examples$tableHeader = examples.tableHeader) === null || _examples$tableHeader === void 0 || (_examples$tableHeader = _examples$tableHeader.cells[index]) === null || _examples$tableHeader === void 0 ? void 0 : _examples$tableHeader.value;
|
|
343
|
-
if (colName)
|
|
377
|
+
if (colName) {
|
|
378
|
+
params[colName] = cell.value;
|
|
379
|
+
}
|
|
344
380
|
});
|
|
345
381
|
return (0, _utils.template)(titleFormat, params);
|
|
346
382
|
}
|
|
@@ -39,11 +39,17 @@ class TestNode {
|
|
|
39
39
|
// eslint-disable-next-line complexity
|
|
40
40
|
setFlag(tag) {
|
|
41
41
|
// in case of several system tags, @only takes precendence
|
|
42
|
-
if (tag === '@only')
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
42
|
+
if (tag === '@only') {
|
|
43
|
+
this.flags = {
|
|
44
|
+
only: true
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
if (tag === '@skip' && !this.flags.only) {
|
|
48
|
+
this.flags.skip = true;
|
|
49
|
+
}
|
|
50
|
+
if (tag === '@fixme' && !this.flags.only) {
|
|
51
|
+
this.flags.fixme = true;
|
|
52
|
+
}
|
|
47
53
|
}
|
|
48
54
|
}
|
|
49
55
|
exports.TestNode = TestNode;
|
|
@@ -73,10 +73,14 @@ class TestPoms {
|
|
|
73
73
|
*/
|
|
74
74
|
getResolvedFixtures(pomNode) {
|
|
75
75
|
const usedPom = this.usedPoms.get(pomNode);
|
|
76
|
-
if (usedPom !== null && usedPom !== void 0 && usedPom.fixtures)
|
|
76
|
+
if (usedPom !== null && usedPom !== void 0 && usedPom.fixtures) {
|
|
77
|
+
return usedPom.fixtures;
|
|
78
|
+
}
|
|
77
79
|
// Recursively resolve children fixtures, used in test.
|
|
78
80
|
let childFixtures = [...pomNode.children].map(child => this.getResolvedFixtures(child)).flat();
|
|
79
|
-
if (!usedPom)
|
|
81
|
+
if (!usedPom) {
|
|
82
|
+
return childFixtures;
|
|
83
|
+
}
|
|
80
84
|
if (childFixtures.length) {
|
|
81
85
|
this.verifyChildFixtures(pomNode, usedPom, childFixtures);
|
|
82
86
|
usedPom.fixtures = childFixtures;
|
|
@@ -91,10 +95,14 @@ class TestPoms {
|
|
|
91
95
|
addUsedPom(pomNode, {
|
|
92
96
|
byTag
|
|
93
97
|
}) {
|
|
94
|
-
if (!pomNode)
|
|
98
|
+
if (!pomNode) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
95
101
|
const usedPom = this.usedPoms.get(pomNode);
|
|
96
102
|
if (usedPom) {
|
|
97
|
-
if (byTag && !usedPom.byTag)
|
|
103
|
+
if (byTag && !usedPom.byTag) {
|
|
104
|
+
usedPom.byTag = true;
|
|
105
|
+
}
|
|
98
106
|
} else {
|
|
99
107
|
this.usedPoms.set(pomNode, {
|
|
100
108
|
byTag
|
|
@@ -107,7 +115,9 @@ class TestPoms {
|
|
|
107
115
|
* @fixture:xxx tag provides maximum fixture that can be used in the scenario.
|
|
108
116
|
*/
|
|
109
117
|
verifyChildFixtures(pomNode, usedPom, childFixtures) {
|
|
110
|
-
if (!usedPom.byTag)
|
|
118
|
+
if (!usedPom.byTag) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
111
121
|
const childFixturesBySteps = childFixtures.filter(f => !f.byTag);
|
|
112
122
|
if (childFixturesBySteps.length) {
|
|
113
123
|
(0, _exit.exit)(`Scenario "${this.title}" contains ${childFixturesBySteps.length} step(s)`, `not compatible with required fixture "${pomNode.fixtureName}"`);
|
|
@@ -38,19 +38,29 @@ function hasScenarioHooks() {
|
|
|
38
38
|
async function runScenarioHooks(type, fixtures) {
|
|
39
39
|
let error;
|
|
40
40
|
for (const hook of scenarioHooks) {
|
|
41
|
-
if (hook.type !== type)
|
|
42
|
-
|
|
41
|
+
if (hook.type !== type) {
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (hook.tagsExpression && !hook.tagsExpression.evaluate(fixtures.$tags)) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
43
47
|
const {
|
|
44
48
|
timeout
|
|
45
49
|
} = hook.options;
|
|
46
50
|
try {
|
|
47
51
|
await (0, _utils.callWithTimeout)(() => hook.fn.call(fixtures.$bddWorld, fixtures), timeout, `${type} hook timeout (${timeout} ms)`);
|
|
48
52
|
} catch (e) {
|
|
49
|
-
if (type === 'before')
|
|
50
|
-
|
|
53
|
+
if (type === 'before') {
|
|
54
|
+
throw e;
|
|
55
|
+
}
|
|
56
|
+
if (!error) {
|
|
57
|
+
error = e;
|
|
58
|
+
}
|
|
51
59
|
}
|
|
52
60
|
}
|
|
53
|
-
if (error)
|
|
61
|
+
if (error) {
|
|
62
|
+
throw error;
|
|
63
|
+
}
|
|
54
64
|
}
|
|
55
65
|
function getScenarioHooksFixtures() {
|
|
56
66
|
if (!scenarioHooksFixtures) {
|
|
@@ -68,10 +78,14 @@ function getScenarioHooksFixtures() {
|
|
|
68
78
|
return scenarioHooksFixtures;
|
|
69
79
|
}
|
|
70
80
|
function getOptionsFromArgs(args) {
|
|
71
|
-
if (typeof args[0] === 'string')
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
81
|
+
if (typeof args[0] === 'string') {
|
|
82
|
+
return {
|
|
83
|
+
tags: args[0]
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
if (typeof args[0] === 'object') {
|
|
87
|
+
return args[0];
|
|
88
|
+
}
|
|
75
89
|
return {};
|
|
76
90
|
}
|
|
77
91
|
function getFnFromArgs(args) {
|
|
@@ -30,18 +30,26 @@ function workerHookFactory(type) {
|
|
|
30
30
|
async function runWorkerHooks(type, fixtures) {
|
|
31
31
|
let error;
|
|
32
32
|
for (const hook of workerHooks) {
|
|
33
|
-
if (hook.type !== type)
|
|
33
|
+
if (hook.type !== type) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
34
36
|
const {
|
|
35
37
|
timeout
|
|
36
38
|
} = hook.options;
|
|
37
39
|
try {
|
|
38
40
|
await (0, _utils.callWithTimeout)(() => hook.fn(fixtures), timeout, `${type} hook timeout (${timeout} ms)`);
|
|
39
41
|
} catch (e) {
|
|
40
|
-
if (type === 'beforeAll')
|
|
41
|
-
|
|
42
|
+
if (type === 'beforeAll') {
|
|
43
|
+
throw e;
|
|
44
|
+
}
|
|
45
|
+
if (!error) {
|
|
46
|
+
error = e;
|
|
47
|
+
}
|
|
42
48
|
}
|
|
43
49
|
}
|
|
44
|
-
if (error)
|
|
50
|
+
if (error) {
|
|
51
|
+
throw error;
|
|
52
|
+
}
|
|
45
53
|
}
|
|
46
54
|
function getWorkerHooksFixtures() {
|
|
47
55
|
if (!workerHooksFixtures) {
|
|
@@ -57,7 +65,9 @@ function getWorkerHooksFixtures() {
|
|
|
57
65
|
return workerHooksFixtures;
|
|
58
66
|
}
|
|
59
67
|
function getOptionsFromArgs(args) {
|
|
60
|
-
if (typeof args[0] === 'object')
|
|
68
|
+
if (typeof args[0] === 'object') {
|
|
69
|
+
return args[0];
|
|
70
|
+
}
|
|
61
71
|
return {};
|
|
62
72
|
}
|
|
63
73
|
function getFnFromArgs(args) {
|
|
@@ -11,16 +11,24 @@ exports.fixtureParameterNames = fixtureParameterNames;
|
|
|
11
11
|
/* eslint-disable max-statements, complexity, max-len, max-depth */
|
|
12
12
|
const signatureSymbol = Symbol('signature');
|
|
13
13
|
function fixtureParameterNames(fn) {
|
|
14
|
-
if (typeof fn !== 'function')
|
|
15
|
-
|
|
14
|
+
if (typeof fn !== 'function') {
|
|
15
|
+
return [];
|
|
16
|
+
}
|
|
17
|
+
if (!fn[signatureSymbol]) {
|
|
18
|
+
fn[signatureSymbol] = innerFixtureParameterNames(fn);
|
|
19
|
+
}
|
|
16
20
|
return fn[signatureSymbol];
|
|
17
21
|
}
|
|
18
22
|
function innerFixtureParameterNames(fn) {
|
|
19
23
|
const text = filterOutComments(fn.toString());
|
|
20
24
|
const match = text.match(/(?:async)?(?:\s+function)?[^(]*\(([^)]*)/);
|
|
21
|
-
if (!match)
|
|
25
|
+
if (!match) {
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
22
28
|
const trimmedParams = match[1].trim();
|
|
23
|
-
if (!trimmedParams)
|
|
29
|
+
if (!trimmedParams) {
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
24
32
|
const [firstParam] = splitByComma(trimmedParams);
|
|
25
33
|
if (firstParam[0] !== '{' || firstParam[firstParam.length - 1] !== '}') {
|
|
26
34
|
throw new Error('First argument must use the object destructuring pattern: ' + firstParam + ' ' + fn.toString());
|
|
@@ -40,9 +48,13 @@ function filterOutComments(s) {
|
|
|
40
48
|
let commentState = 'none';
|
|
41
49
|
for (let i = 0; i < s.length; ++i) {
|
|
42
50
|
if (commentState === 'singleline') {
|
|
43
|
-
if (s[i] === '\n')
|
|
51
|
+
if (s[i] === '\n') {
|
|
52
|
+
commentState = 'none';
|
|
53
|
+
}
|
|
44
54
|
} else if (commentState === 'multiline') {
|
|
45
|
-
if (s[i - 1] === '*' && s[i] === '/')
|
|
55
|
+
if (s[i - 1] === '*' && s[i] === '/') {
|
|
56
|
+
commentState = 'none';
|
|
57
|
+
}
|
|
46
58
|
} else if (commentState === 'none') {
|
|
47
59
|
if (s[i] === '/' && s[i + 1] === '/') {
|
|
48
60
|
commentState = 'singleline';
|
|
@@ -67,11 +79,15 @@ function splitByComma(s) {
|
|
|
67
79
|
stack.pop();
|
|
68
80
|
} else if (!stack.length && s[i] === ',') {
|
|
69
81
|
const token = s.substring(start, i).trim();
|
|
70
|
-
if (token)
|
|
82
|
+
if (token) {
|
|
83
|
+
result.push(token);
|
|
84
|
+
}
|
|
71
85
|
start = i + 1;
|
|
72
86
|
}
|
|
73
87
|
}
|
|
74
88
|
const lastToken = s.substring(start).trim();
|
|
75
|
-
if (lastToken)
|
|
89
|
+
if (lastToken) {
|
|
90
|
+
result.push(lastToken);
|
|
91
|
+
}
|
|
76
92
|
return result;
|
|
77
93
|
}
|
|
@@ -23,11 +23,13 @@ function getLocationInFile(filePath) {
|
|
|
23
23
|
const frameFile = frame.getFileName();
|
|
24
24
|
return frameFile === filePath || frameFile === filePathUrl;
|
|
25
25
|
});
|
|
26
|
-
if (!frameInFile)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
if (!frameInFile) {
|
|
27
|
+
return {
|
|
28
|
+
file: '',
|
|
29
|
+
line: 0,
|
|
30
|
+
column: 0
|
|
31
|
+
};
|
|
32
|
+
}
|
|
31
33
|
const frame = sourceMapSupport.wrapCallSite(frameInFile);
|
|
32
34
|
const fileName = frame.getFileName();
|
|
33
35
|
// Node error stacks for modules use file:// urls instead of paths.
|
|
@@ -44,7 +44,9 @@ async function runStepWithCustomLocation(test, stepText, location, body) {
|
|
|
44
44
|
* - test is a result of mergeTests(subtest, ...)
|
|
45
45
|
*/
|
|
46
46
|
function isTestContainsSubtest(test, subtest) {
|
|
47
|
-
if (test === subtest)
|
|
47
|
+
if (test === subtest) {
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
48
50
|
const testFixtures = new Set(getTestFixtures(test).map(f => locationToString(f.location)));
|
|
49
51
|
return getTestFixtures(subtest).every(f => {
|
|
50
52
|
return testFixtures.has(locationToString(f.location));
|
|
@@ -30,13 +30,17 @@ function installTransform() {
|
|
|
30
30
|
function resolveFilename(specifier, parent, ...rest) {
|
|
31
31
|
if (!reverted && parent) {
|
|
32
32
|
const resolved = resolveHook(parent.filename, specifier);
|
|
33
|
-
if (resolved !== undefined)
|
|
33
|
+
if (resolved !== undefined) {
|
|
34
|
+
specifier = resolved;
|
|
35
|
+
}
|
|
34
36
|
}
|
|
35
37
|
return originalResolveFilename.call(this, specifier, parent, ...rest);
|
|
36
38
|
}
|
|
37
39
|
_module.default._resolveFilename = resolveFilename;
|
|
38
40
|
const revertPirates = pirates.addHook((code, filename) => {
|
|
39
|
-
if (!shouldTransform(filename))
|
|
41
|
+
if (!shouldTransform(filename)) {
|
|
42
|
+
return code;
|
|
43
|
+
}
|
|
40
44
|
return transformHook(code, filename);
|
|
41
45
|
}, {
|
|
42
46
|
exts: ['.ts', '.tsx', '.js', '.jsx', '.mjs']
|
|
@@ -20,7 +20,9 @@ var _worker = require("../hooks/worker");
|
|
|
20
20
|
// todo: https://github.com/vitalets/playwright-bdd/issues/46
|
|
21
21
|
let hasCustomTest = exports.hasCustomTest = false;
|
|
22
22
|
function createBdd(customTest) {
|
|
23
|
-
if (!hasCustomTest)
|
|
23
|
+
if (!hasCustomTest) {
|
|
24
|
+
exports.hasCustomTest = hasCustomTest = isCustomTest(customTest);
|
|
25
|
+
}
|
|
24
26
|
const Given = defineStepCtor('Given', hasCustomTest);
|
|
25
27
|
const When = defineStepCtor('When', hasCustomTest);
|
|
26
28
|
const Then = defineStepCtor('Then', hasCustomTest);
|
|
@@ -51,7 +53,9 @@ function defineStepCtor(keyword, hasCustomTest) {
|
|
|
51
53
|
};
|
|
52
54
|
}
|
|
53
55
|
function isCustomTest(customTest) {
|
|
54
|
-
if (!customTest || customTest === _bddFixtures.test)
|
|
56
|
+
if (!customTest || customTest === _bddFixtures.test) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
55
59
|
assertTestHasBddFixtures(customTest);
|
|
56
60
|
return true;
|
|
57
61
|
}
|
|
@@ -41,13 +41,19 @@ function ensureUniqueFixtureName({
|
|
|
41
41
|
fixtureName,
|
|
42
42
|
className
|
|
43
43
|
}) {
|
|
44
|
-
if (!fixtureName)
|
|
44
|
+
if (!fixtureName) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
45
47
|
const existingPom = getPomNodeByFixtureName(fixtureName);
|
|
46
|
-
if (existingPom)
|
|
48
|
+
if (existingPom) {
|
|
49
|
+
(0, _exit.exit)(`Duplicate fixture name "${fixtureName}"`, `defined for classes: ${existingPom.className}, ${className}`);
|
|
50
|
+
}
|
|
47
51
|
}
|
|
48
52
|
function linkParentWithPomNode(Ctor, pomNode) {
|
|
49
53
|
const parentCtor = Object.getPrototypeOf(Ctor);
|
|
50
|
-
if (!parentCtor)
|
|
54
|
+
if (!parentCtor) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
51
57
|
// if parentCtor is not in pomGraph, add it.
|
|
52
58
|
// Case: parent class is not marked with @Fixture, but has decorator steps (base class)
|
|
53
59
|
const parentPomNode = pomGraph.get(parentCtor) || createPomNode(parentCtor, '');
|
|
@@ -55,6 +61,8 @@ function linkParentWithPomNode(Ctor, pomNode) {
|
|
|
55
61
|
}
|
|
56
62
|
function getPomNodeByFixtureName(fixtureName) {
|
|
57
63
|
for (const pomNode of pomGraph.values()) {
|
|
58
|
-
if (pomNode.fixtureName === fixtureName)
|
|
64
|
+
if (pomNode.fixtureName === fixtureName) {
|
|
65
|
+
return pomNode;
|
|
66
|
+
}
|
|
59
67
|
}
|
|
60
68
|
}
|
|
@@ -36,11 +36,15 @@ function createStepDecorator(keyword) {
|
|
|
36
36
|
};
|
|
37
37
|
}
|
|
38
38
|
function linkStepsWithPomNode(Ctor, pomNode) {
|
|
39
|
-
if (!(Ctor !== null && Ctor !== void 0 && Ctor.prototype))
|
|
39
|
+
if (!(Ctor !== null && Ctor !== void 0 && Ctor.prototype)) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
40
42
|
const propertyDescriptors = Object.getOwnPropertyDescriptors(Ctor.prototype);
|
|
41
43
|
return Object.values(propertyDescriptors).forEach(descriptor => {
|
|
42
44
|
const stepConfig = getStepConfigFromMethod(descriptor);
|
|
43
|
-
if (!stepConfig)
|
|
45
|
+
if (!stepConfig) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
44
48
|
stepConfig.pomNode = pomNode;
|
|
45
49
|
decoratedSteps.add(stepConfig);
|
|
46
50
|
});
|
|
@@ -35,8 +35,10 @@ async function withExitHandler(fn) {
|
|
|
35
35
|
return await fn();
|
|
36
36
|
} catch (e) {
|
|
37
37
|
if (e instanceof ExitError) {
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
if (e.message) {
|
|
39
|
+
// eslint-disable-next-line no-console
|
|
40
|
+
console.error(e.message);
|
|
41
|
+
}
|
|
40
42
|
process.exitCode = 1;
|
|
41
43
|
} else {
|
|
42
44
|
throw e;
|
|
@@ -48,8 +50,11 @@ function exit(...messages) {
|
|
|
48
50
|
if (_worker_threads.isMainThread) {
|
|
49
51
|
// use console.error() here instead of logger.error() to have less stack
|
|
50
52
|
// for flushing messages to stderr.
|
|
51
|
-
|
|
52
|
-
if (messages.length)
|
|
53
|
+
|
|
54
|
+
if (messages.length) {
|
|
55
|
+
// eslint-disable-next-line no-console, max-depth
|
|
56
|
+
console.error('Error:', ...messages);
|
|
57
|
+
}
|
|
53
58
|
process.exit(1);
|
|
54
59
|
} else {
|
|
55
60
|
throw new ExitError(messages.join(' '));
|
|
@@ -57,7 +57,9 @@ function getPackageVersion(packageName) {
|
|
|
57
57
|
return packageJson.version || '';
|
|
58
58
|
}
|
|
59
59
|
async function callWithTimeout(fn, timeout, timeoutMsg) {
|
|
60
|
-
if (!timeout)
|
|
60
|
+
if (!timeout) {
|
|
61
|
+
return fn();
|
|
62
|
+
}
|
|
61
63
|
const ac = new AbortController();
|
|
62
64
|
return Promise.race([fn(), setTimeoutPromise(timeout, null, {
|
|
63
65
|
ref: false,
|
|
@@ -13,7 +13,9 @@ class Logger {
|
|
|
13
13
|
this.options = options;
|
|
14
14
|
}
|
|
15
15
|
log(...args) {
|
|
16
|
-
if (this.options.verbose)
|
|
16
|
+
if (this.options.verbose) {
|
|
17
|
+
console.log(...args);
|
|
18
|
+
}
|
|
17
19
|
}
|
|
18
20
|
warn(...args) {
|
|
19
21
|
// using log() to output warnings to stdout, not stderr
|
package/changelog.md
CHANGED
|
@@ -2,9 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
## Framework that abstracts the configuration for playwright and Jest
|
|
4
4
|
|
|
5
|
-
# 0.1.
|
|
5
|
+
# 0.1.7
|
|
6
6
|
**Enhancements**
|
|
7
|
-
- Added option to run teardown logic
|
|
7
|
+
- Added option to run teardown logic.
|
|
8
|
+
- Added support for tag based filtering.
|
|
9
|
+
- Playwright-bdd version updated to 5.6.0.
|
|
10
|
+
- New fixture added to add tag as annotations in test report
|
|
11
|
+
|
|
12
|
+
**Issue Fixes**
|
|
13
|
+
- Edition command option. Fixed the edition tags not generated properly
|
|
8
14
|
|
|
9
15
|
# 0.1.6
|
|
10
16
|
|
package/npm-shrinkwrap.json
CHANGED