@qavajs/cypress-runner-adapter 0.1.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/CHANGELOG.md +14 -0
- package/LICENSE +21 -0
- package/README.md +38 -0
- package/adapter/index.js +128 -0
- package/adapter.d.ts +1 -0
- package/adapter.js +1 -0
- package/index.d.ts +29 -0
- package/index.js +1 -0
- package/package.json +22 -0
- package/supportCodeLibrary/build_parameter_type.js +14 -0
- package/supportCodeLibrary/data_table.js +52 -0
- package/supportCodeLibrary/definition.js +5 -0
- package/supportCodeLibrary/index.js +96 -0
- package/supportCodeLibrary/pickle_filter.js +17 -0
- package/supportCodeLibrary/sourced_parameter_type_registry.js +20 -0
- package/supportCodeLibrary/step_argument.js +13 -0
- package/supportCodeLibrary/step_definition.js +34 -0
- package/supportCodeLibrary/test_case_hook_definition.js +25 -0
- package/supportCodeLibrary/test_step_hook_definition.js +23 -0
- package/supportCodeLibrary/value_checker.js +14 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Change Log
|
|
2
|
+
|
|
3
|
+
All notable changes to the "@qavajs/cypress-runner-adapter" will be documented in this file.
|
|
4
|
+
|
|
5
|
+
Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.
|
|
6
|
+
|
|
7
|
+
:rocket: - new feature
|
|
8
|
+
:beetle: - bugfix
|
|
9
|
+
:x: - deprecation/removal
|
|
10
|
+
:pencil: - chore
|
|
11
|
+
:microscope: - experimental
|
|
12
|
+
|
|
13
|
+
## [0.1.0]
|
|
14
|
+
- :rocket: initial implementation
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 @qavajs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# @qavajs/cypress-runner-adapter
|
|
2
|
+
Adapter to run cucumberjs tests via cypress test runner
|
|
3
|
+
|
|
4
|
+
## Installation
|
|
5
|
+
|
|
6
|
+
`npm install @qavajs/cypress-runner-adapter`
|
|
7
|
+
|
|
8
|
+
## Basic Configuration
|
|
9
|
+
|
|
10
|
+
```javascript
|
|
11
|
+
const { defineConfig } = require('cypress');
|
|
12
|
+
const cucumber = require('@qavajs/cypress-runner-adapter/adapter');
|
|
13
|
+
|
|
14
|
+
module.exports = defineConfig({
|
|
15
|
+
e2e: {
|
|
16
|
+
specPattern: 'cypress/feature/**/*.feature',
|
|
17
|
+
supportFile: 'cypress/support/e2e.js',
|
|
18
|
+
setupNodeEvents(on, config) {
|
|
19
|
+
on('file:preprocessor', cucumber)
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
`support/e2e.js` is entry point with step definition;
|
|
26
|
+
|
|
27
|
+
```javascript
|
|
28
|
+
import { When, setWorldConstructor } from '@qavajs/cypress-runner-adapter';
|
|
29
|
+
|
|
30
|
+
class World {
|
|
31
|
+
|
|
32
|
+
}
|
|
33
|
+
setWorldConstructor(World);
|
|
34
|
+
|
|
35
|
+
When('open {string} url', function (url) {
|
|
36
|
+
cy.visit(url);
|
|
37
|
+
});
|
|
38
|
+
```
|
package/adapter/index.js
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
const {
|
|
2
|
+
ensureFileSync,
|
|
3
|
+
writeFileSync,
|
|
4
|
+
readFileSync,
|
|
5
|
+
} = require('fs-extra');
|
|
6
|
+
const { randomUUID } = require('node:crypto');
|
|
7
|
+
const { AstBuilder, compile, GherkinClassicTokenMatcher, Parser } = require('@cucumber/gherkin');
|
|
8
|
+
const cyBrowserify = require('@cypress/browserify-preprocessor')()
|
|
9
|
+
|
|
10
|
+
const uuidFn = () => randomUUID();
|
|
11
|
+
const builder = new AstBuilder(uuidFn)
|
|
12
|
+
const matcher = new GherkinClassicTokenMatcher();
|
|
13
|
+
const parser = new Parser(builder, matcher)
|
|
14
|
+
|
|
15
|
+
function adapter(testCases) {
|
|
16
|
+
return `
|
|
17
|
+
const tests = ${JSON.stringify(testCases)};
|
|
18
|
+
|
|
19
|
+
function keyword(step) {
|
|
20
|
+
switch (step.type) {
|
|
21
|
+
case 'Context': return 'Given';
|
|
22
|
+
case 'Action': return 'When';
|
|
23
|
+
case 'Outcome': return 'Then';
|
|
24
|
+
default: return 'Step';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function executeStep(pickle, world) {
|
|
29
|
+
if (pickle.argument && pickle.argument.dataTable) {
|
|
30
|
+
Cypress.log({ displayName: 'DataTable', message: pickle.argument.dataTable })
|
|
31
|
+
}
|
|
32
|
+
if (pickle.argument && pickle.argument.docString) {
|
|
33
|
+
Cypress.log({ displayName: 'Multiline', message: pickle.argument.docString.content })
|
|
34
|
+
}
|
|
35
|
+
const steps = supportCodeLibrary.stepDefinitions
|
|
36
|
+
.filter(stepDefinition => stepDefinition.matchesStepName(pickle.text));
|
|
37
|
+
if (steps.length === 0) throw new Error(\`Step '\${pickle.text}' is not defined\`);
|
|
38
|
+
if (steps.length > 1) throw new Error(\`'\${pickle.text}' matches multiple step definitions\`);
|
|
39
|
+
const [step] = steps;
|
|
40
|
+
const { parameters } = step.getInvocationParameters({
|
|
41
|
+
step: {
|
|
42
|
+
text: pickle.text,
|
|
43
|
+
argument: pickle.argument
|
|
44
|
+
},
|
|
45
|
+
world
|
|
46
|
+
});
|
|
47
|
+
step.code.apply(world, parameters);
|
|
48
|
+
}
|
|
49
|
+
for (const test of tests) {
|
|
50
|
+
describe('Scenario: ' + test.name, { testIsolation: false }, function () {
|
|
51
|
+
const world = new supportCodeLibrary.World();
|
|
52
|
+
let skip = false;
|
|
53
|
+
let result = 'passed';
|
|
54
|
+
afterEach(function() {
|
|
55
|
+
if (this.step) {
|
|
56
|
+
for (const afterStep of supportCodeLibrary.afterTestStepHookDefinitions) {
|
|
57
|
+
if (afterStep.appliesToTestCase(this.step)) {
|
|
58
|
+
afterStep.code.apply(world, [{
|
|
59
|
+
pickle: test,
|
|
60
|
+
pickleStep: this.step,
|
|
61
|
+
gherkinDocument: tests,
|
|
62
|
+
result: this.currentTest.state
|
|
63
|
+
}])
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (this.currentTest.state !== 'passed') {
|
|
68
|
+
skip = true;
|
|
69
|
+
}
|
|
70
|
+
result = this.currentTest.state;
|
|
71
|
+
});
|
|
72
|
+
for (const beforeTest of supportCodeLibrary.beforeTestCaseHookDefinitions) {
|
|
73
|
+
if (beforeTest.appliesToTestCase(test)) {
|
|
74
|
+
it(beforeTest.name, function () {
|
|
75
|
+
if (skip) return this.skip();
|
|
76
|
+
beforeTest.code.apply(world, [{
|
|
77
|
+
pickle: test,
|
|
78
|
+
gherkinDocument: tests,
|
|
79
|
+
willBeRetried: false
|
|
80
|
+
}]);
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
for (const step of test.steps) {
|
|
85
|
+
it(keyword(step) + ': ' + step.text, function () {
|
|
86
|
+
this.step = step;
|
|
87
|
+
if (skip) return this.skip();
|
|
88
|
+
for (const beforeStep of supportCodeLibrary.beforeTestStepHookDefinitions) {
|
|
89
|
+
if (beforeStep.appliesToTestCase(step)) {
|
|
90
|
+
beforeStep.code.apply(world, [{
|
|
91
|
+
pickle: test,
|
|
92
|
+
pickleStep: step,
|
|
93
|
+
gherkinDocument: tests
|
|
94
|
+
}]);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
executeStep(step, world);
|
|
98
|
+
})
|
|
99
|
+
}
|
|
100
|
+
for (const afterTest of supportCodeLibrary.afterTestCaseHookDefinitions) {
|
|
101
|
+
if (afterTest.appliesToTestCase(test)) {
|
|
102
|
+
it(afterTest.name, function () {
|
|
103
|
+
afterTest.code.apply(world, [{
|
|
104
|
+
pickle: test,
|
|
105
|
+
result,
|
|
106
|
+
gherkinDocument: tests,
|
|
107
|
+
willBeRetried: false
|
|
108
|
+
}])
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
`;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
module.exports = async function cucumber(file) {
|
|
118
|
+
const { filePath, outputPath, shouldWatch } = file;
|
|
119
|
+
if (!filePath.endsWith('.feature')) {
|
|
120
|
+
return cyBrowserify(file)
|
|
121
|
+
}
|
|
122
|
+
const gherkinDocument = parser.parse(readFileSync(filePath, 'utf-8'));
|
|
123
|
+
const testCases = compile(gherkinDocument, filePath, uuidFn);
|
|
124
|
+
ensureFileSync(outputPath);
|
|
125
|
+
writeFileSync(outputPath, adapter(testCases), 'utf-8');
|
|
126
|
+
|
|
127
|
+
return outputPath
|
|
128
|
+
}
|
package/adapter.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function (file: any): void;
|
package/adapter.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('./adapter/index');
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
declare type Expression = string | RegExp;
|
|
2
|
+
declare type TestHookOptions = {
|
|
3
|
+
tags?: string,
|
|
4
|
+
name?: string
|
|
5
|
+
};
|
|
6
|
+
declare type StepHookOptions = {
|
|
7
|
+
tags?: string
|
|
8
|
+
};
|
|
9
|
+
declare type ParameterTypeOption = {
|
|
10
|
+
name: string,
|
|
11
|
+
preferForRegexpMatch?: boolean,
|
|
12
|
+
regexp: RegExp,
|
|
13
|
+
transformer?: Function,
|
|
14
|
+
useForSnippets?: boolean
|
|
15
|
+
}
|
|
16
|
+
declare interface IWorld {}
|
|
17
|
+
export function Given(expression: Expression, fn: Function): void;
|
|
18
|
+
export function When(expression: Expression, fn: Function): void;
|
|
19
|
+
export function Then(expression: Expression, fn: Function): void;
|
|
20
|
+
export function Before(fn: Function): void;
|
|
21
|
+
export function Before(options: TestHookOptions, fn: Function): void;
|
|
22
|
+
export function After(fn: Function): void;
|
|
23
|
+
export function After(options: TestHookOptions, fn: Function): void;
|
|
24
|
+
export function BeforeStep(fn: Function): void;
|
|
25
|
+
export function BeforeStep(options: StepHookOptions, fn: Function): void;
|
|
26
|
+
export function AfterStep(fn: Function): void;
|
|
27
|
+
export function AfterStep(options: StepHookOptions, fn: Function): void;
|
|
28
|
+
export function setWorldConstructor(world: IWorld): void;
|
|
29
|
+
export function defineParameterType(option: ParameterTypeOption): void;
|
package/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('./supportCodeLibrary');
|
package/package.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@qavajs/cypress-runner-adapter",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"main": "index.js",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"debug": "cypress open --config-file test/cypress.config.js",
|
|
7
|
+
"test": "cypress run --config-file test/cypress.config.js"
|
|
8
|
+
},
|
|
9
|
+
"author": "Alexandr Galichenko",
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"description": "feature file preprocessor",
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@cucumber/cucumber-expressions": "^17.1.0",
|
|
14
|
+
"@cucumber/gherkin": "^28.0.0",
|
|
15
|
+
"@cucumber/tag-expressions": "^6.1.0",
|
|
16
|
+
"@cypress/browserify-preprocessor": "^3.0.2",
|
|
17
|
+
"fs-extra": "^11.2.0"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"cypress": "^13.11.0"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import {ParameterType} from '@cucumber/cucumber-expressions'
|
|
2
|
+
|
|
3
|
+
export function buildParameterType({ name, regexp, transformer, useForSnippets, preferForRegexpMatch }) {
|
|
4
|
+
if (typeof useForSnippets !== 'boolean') useForSnippets = true
|
|
5
|
+
if (typeof preferForRegexpMatch !== 'boolean') preferForRegexpMatch = false
|
|
6
|
+
return new ParameterType(
|
|
7
|
+
name,
|
|
8
|
+
regexp,
|
|
9
|
+
null,
|
|
10
|
+
transformer,
|
|
11
|
+
useForSnippets,
|
|
12
|
+
preferForRegexpMatch
|
|
13
|
+
)
|
|
14
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export default class Data_table {
|
|
2
|
+
constructor(sourceTable) {
|
|
3
|
+
if (sourceTable instanceof Array) {
|
|
4
|
+
this.rawTable = sourceTable
|
|
5
|
+
} else {
|
|
6
|
+
this.rawTable = sourceTable.rows.map((row) =>
|
|
7
|
+
row.cells.map((cell) => cell.value)
|
|
8
|
+
)
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
hashes() {
|
|
13
|
+
const copy = this.raw()
|
|
14
|
+
const keys = copy[0]
|
|
15
|
+
const valuesArray = copy.slice(1)
|
|
16
|
+
return valuesArray.map((values) => {
|
|
17
|
+
const rowObject = {}
|
|
18
|
+
keys.forEach((key, index) => (rowObject[key] = values[index]))
|
|
19
|
+
return rowObject
|
|
20
|
+
})
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
raw() {
|
|
24
|
+
return this.rawTable.slice(0)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
rows() {
|
|
28
|
+
const copy = this.raw()
|
|
29
|
+
copy.shift()
|
|
30
|
+
return copy
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
rowsHash() {
|
|
34
|
+
const rows = this.raw()
|
|
35
|
+
const everyRowHasTwoColumns = rows.every((row) => row.length === 2)
|
|
36
|
+
if (!everyRowHasTwoColumns) {
|
|
37
|
+
throw new Error(
|
|
38
|
+
'rowsHash can only be called on a data table where all rows have exactly two columns'
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
const result = {}
|
|
42
|
+
rows.forEach((x) => (result[x[0]] = x[1]))
|
|
43
|
+
return result
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
transpose() {
|
|
47
|
+
const transposed = this.rawTable[0].map((x, i) =>
|
|
48
|
+
this.rawTable.map((y) => y[i])
|
|
49
|
+
)
|
|
50
|
+
return new Data_table(transposed)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CucumberExpression,
|
|
3
|
+
RegularExpression,
|
|
4
|
+
} from '@cucumber/cucumber-expressions'
|
|
5
|
+
import StepDefinition from './step_definition';
|
|
6
|
+
import {SourcedParameterTypeRegistry} from './sourced_parameter_type_registry';
|
|
7
|
+
import TestCaseHookDefinition from './test_case_hook_definition';
|
|
8
|
+
import TestStepHookDefinition from './test_step_hook_definition';
|
|
9
|
+
import {buildParameterType} from './build_parameter_type';
|
|
10
|
+
class World {}
|
|
11
|
+
|
|
12
|
+
const supportCodeLibrary = {
|
|
13
|
+
stepDefinitions: [],
|
|
14
|
+
afterTestCaseHookDefinitions: [],
|
|
15
|
+
afterTestRunHookDefinitions: [],
|
|
16
|
+
afterTestStepHookDefinitions: [],
|
|
17
|
+
beforeTestCaseHookDefinitions: [],
|
|
18
|
+
beforeTestRunHookDefinitions: [],
|
|
19
|
+
beforeTestStepHookDefinitions: [],
|
|
20
|
+
World,
|
|
21
|
+
parameterTypeRegistry: new SourcedParameterTypeRegistry()
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
window.supportCodeLibrary = supportCodeLibrary;
|
|
25
|
+
export function defineStep(keyword, pattern, code) {
|
|
26
|
+
const expression = typeof pattern === 'string'
|
|
27
|
+
? new CucumberExpression(pattern, supportCodeLibrary.parameterTypeRegistry)
|
|
28
|
+
: new RegularExpression(pattern, supportCodeLibrary.parameterTypeRegistry);
|
|
29
|
+
supportCodeLibrary.stepDefinitions.push(new StepDefinition({
|
|
30
|
+
keyword,
|
|
31
|
+
expression,
|
|
32
|
+
pattern,
|
|
33
|
+
code
|
|
34
|
+
}));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function When(pattern, code) {
|
|
38
|
+
defineStep('When', pattern, code);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function Then(pattern, code) {
|
|
42
|
+
defineStep('Then', pattern, code);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function Given(pattern, code) {
|
|
46
|
+
defineStep('Given', pattern, code);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function Before(optionsOrCode, code) {
|
|
50
|
+
const defaultOptions = { name: 'Before' };
|
|
51
|
+
const options = code ? Object.assign(defaultOptions, optionsOrCode) : defaultOptions;
|
|
52
|
+
const handler = code ?? optionsOrCode;
|
|
53
|
+
supportCodeLibrary.beforeTestCaseHookDefinitions.push(new TestCaseHookDefinition({
|
|
54
|
+
code: handler,
|
|
55
|
+
options
|
|
56
|
+
}));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function After(optionsOrCode, code) {
|
|
60
|
+
const defaultOptions = { name: 'After' };
|
|
61
|
+
const options = code ? Object.assign(defaultOptions, optionsOrCode) : defaultOptions;
|
|
62
|
+
const handler = code ?? optionsOrCode;
|
|
63
|
+
supportCodeLibrary.afterTestCaseHookDefinitions.push(new TestCaseHookDefinition({
|
|
64
|
+
code: handler,
|
|
65
|
+
options
|
|
66
|
+
}));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function BeforeStep(optionsOrCode, code) {
|
|
70
|
+
const options = code ? optionsOrCode : { name: 'BeforeStep' };
|
|
71
|
+
const handler = code ?? optionsOrCode;
|
|
72
|
+
supportCodeLibrary.beforeTestStepHookDefinitions.push(new TestStepHookDefinition({
|
|
73
|
+
code: handler,
|
|
74
|
+
options
|
|
75
|
+
}));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function AfterStep(optionsOrCode, code) {
|
|
79
|
+
const options = code ? optionsOrCode : { name: 'AfterStep' };
|
|
80
|
+
const handler = code ?? optionsOrCode;
|
|
81
|
+
supportCodeLibrary.afterTestStepHookDefinitions.push(new TestStepHookDefinition({
|
|
82
|
+
code: handler,
|
|
83
|
+
options
|
|
84
|
+
}));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function setWorldConstructor(world) {
|
|
88
|
+
supportCodeLibrary.World = world;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function defineParameterType(options) {
|
|
92
|
+
const parameterType = buildParameterType(options)
|
|
93
|
+
supportCodeLibrary.parameterTypeRegistry.defineSourcedParameterType(parameterType, {})
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import {doesHaveValue, doesNotHaveValue} from './value_checker';
|
|
2
|
+
import parse from '@cucumber/tag-expressions';
|
|
3
|
+
|
|
4
|
+
export class PickleTagFilter {
|
|
5
|
+
constructor(tagExpression) {
|
|
6
|
+
if (doesHaveValue(tagExpression) && tagExpression !== '') {
|
|
7
|
+
this.tagExpressionNode = parse(tagExpression)
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
matchesAllTagExpressions(pickle) {
|
|
12
|
+
if (doesNotHaveValue(this.tagExpressionNode)) {
|
|
13
|
+
return true
|
|
14
|
+
}
|
|
15
|
+
return this.tagExpressionNode.evaluate(pickle.tags.map((x) => x.name))
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ParameterType,
|
|
3
|
+
ParameterTypeRegistry,
|
|
4
|
+
} from '@cucumber/cucumber-expressions'
|
|
5
|
+
|
|
6
|
+
export class SourcedParameterTypeRegistry extends ParameterTypeRegistry {
|
|
7
|
+
parameterTypeToSource = new WeakMap()
|
|
8
|
+
|
|
9
|
+
defineSourcedParameterType(
|
|
10
|
+
parameterType,
|
|
11
|
+
source
|
|
12
|
+
) {
|
|
13
|
+
this.defineParameterType(parameterType)
|
|
14
|
+
this.parameterTypeToSource.set(parameterType, source)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
lookupSource(parameterType) {
|
|
18
|
+
return this.parameterTypeToSource.get(parameterType)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { doesHaveValue } from './value_checker';
|
|
2
|
+
|
|
3
|
+
export function parseStepArgument(
|
|
4
|
+
arg,
|
|
5
|
+
mapping
|
|
6
|
+
) {
|
|
7
|
+
if (doesHaveValue(arg.dataTable)) {
|
|
8
|
+
return mapping.dataTable(arg.dataTable)
|
|
9
|
+
} else if (doesHaveValue(arg.docString)) {
|
|
10
|
+
return mapping.docString(arg.docString)
|
|
11
|
+
}
|
|
12
|
+
throw new Error(`Unknown step argument: ${arg}`)
|
|
13
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import Definition from './definition';
|
|
2
|
+
import { doesHaveValue } from './value_checker';
|
|
3
|
+
import DataTable from './data_table';
|
|
4
|
+
import { parseStepArgument } from './step_argument';
|
|
5
|
+
|
|
6
|
+
export default class StepDefinition extends Definition {
|
|
7
|
+
constructor(data) {
|
|
8
|
+
super(data)
|
|
9
|
+
this.keyword = data.keyword
|
|
10
|
+
this.pattern = data.pattern
|
|
11
|
+
this.expression = data.expression
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
getInvocationParameters({ step, world }) {
|
|
15
|
+
const parameters = this.expression.match(step.text).map((arg) => arg.getValue(world))
|
|
16
|
+
if (doesHaveValue(step.argument)) {
|
|
17
|
+
const argumentParameter = parseStepArgument(step.argument, {
|
|
18
|
+
dataTable: (arg) => new DataTable(arg),
|
|
19
|
+
docString: (arg) => arg.content,
|
|
20
|
+
})
|
|
21
|
+
parameters.push(argumentParameter)
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
getInvalidCodeLengthMessage: () =>
|
|
25
|
+
this.baseGetInvalidCodeLengthMessage(parameters),
|
|
26
|
+
parameters,
|
|
27
|
+
validCodeLengths: [parameters.length, parameters.length + 1],
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
matchesStepName(stepName) {
|
|
32
|
+
return doesHaveValue(this.expression.match(stepName))
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import Definition from './definition';
|
|
2
|
+
import { PickleTagFilter } from './pickle_filter';
|
|
3
|
+
|
|
4
|
+
export default class TestCaseHookDefinition extends Definition {
|
|
5
|
+
|
|
6
|
+
constructor(data) {
|
|
7
|
+
super(data)
|
|
8
|
+
this.name = data.options.name
|
|
9
|
+
this.tagExpression = data.options.tags
|
|
10
|
+
this.pickleTagFilter = new PickleTagFilter(data.options.tags)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
appliesToTestCase(pickle) {
|
|
14
|
+
return this.pickleTagFilter.matchesAllTagExpressions(pickle)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async getInvocationParameters({ hookParameter }) {
|
|
18
|
+
return {
|
|
19
|
+
getInvalidCodeLengthMessage: () =>
|
|
20
|
+
this.buildInvalidCodeLengthMessage('0 or 1', '2'),
|
|
21
|
+
parameters: [hookParameter],
|
|
22
|
+
validCodeLengths: [0, 1, 2],
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import Definition from './definition';
|
|
2
|
+
import {PickleTagFilter} from './pickle_filter';
|
|
3
|
+
|
|
4
|
+
export default class TestStepHookDefinition extends Definition {
|
|
5
|
+
constructor(data) {
|
|
6
|
+
super(data)
|
|
7
|
+
this.tagExpression = data.options.tags
|
|
8
|
+
this.pickleTagFilter = new PickleTagFilter(data.options.tags)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
appliesToTestCase(pickle) {
|
|
12
|
+
return this.pickleTagFilter.matchesAllTagExpressions(pickle)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async getInvocationParameters({ hookParameter }) {
|
|
16
|
+
return {
|
|
17
|
+
getInvalidCodeLengthMessage: () =>
|
|
18
|
+
this.buildInvalidCodeLengthMessage('0 or 1', '2'),
|
|
19
|
+
parameters: [hookParameter],
|
|
20
|
+
validCodeLengths: [0, 1, 2],
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export function doesHaveValue(value) {
|
|
2
|
+
return !doesNotHaveValue(value)
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function doesNotHaveValue(value) {
|
|
6
|
+
return value === null || value === undefined
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function valueOrDefault(value, defaultValue) {
|
|
10
|
+
if (doesHaveValue(value)) {
|
|
11
|
+
return value
|
|
12
|
+
}
|
|
13
|
+
return defaultValue
|
|
14
|
+
}
|