@qavajs/playwright-runner-adapter 2.3.3 → 2.5.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 +39 -0
- package/README.md +198 -11
- package/adapter/PlaywrightWorld.d.ts +8 -6
- package/adapter/PlaywrightWorld.js +21 -8
- package/adapter/PlaywrightWorld.js.map +1 -1
- package/adapter/Template.d.ts +13 -2
- package/adapter/Template.js +57 -8
- package/adapter/Template.js.map +1 -1
- package/adapter/cucumberAdapter.d.ts +7 -0
- package/adapter/cucumberAdapter.js +254 -114
- package/adapter/cucumberAdapter.js.map +1 -1
- package/adapter/filter.d.ts +8 -0
- package/adapter/filter.js +27 -1
- package/adapter/filter.js.map +1 -1
- package/adapter/gherkin.d.ts +45 -0
- package/adapter/gherkin.js +155 -0
- package/adapter/gherkin.js.map +1 -0
- package/adapter/index.d.ts +2 -0
- package/adapter/index.js +2 -0
- package/adapter/index.js.map +1 -1
- package/adapter/js/PlaywrightWorld.d.ts +8 -6
- package/adapter/js/PlaywrightWorld.js +21 -8
- package/adapter/js/Template.d.ts +13 -2
- package/adapter/js/Template.js +57 -8
- package/adapter/js/cucumberAdapter.d.ts +7 -0
- package/adapter/js/cucumberAdapter.js +254 -114
- package/adapter/js/filter.d.ts +8 -0
- package/adapter/js/filter.js +27 -1
- package/adapter/js/gherkin.d.ts +45 -0
- package/adapter/js/gherkin.js +154 -0
- package/adapter/js/index.d.ts +2 -0
- package/adapter/js/index.js +2 -0
- package/adapter/js/loader.d.ts +9 -1
- package/adapter/js/loader.js +35 -15
- package/adapter/js/testDetails.d.ts +28 -0
- package/adapter/js/testDetails.js +78 -0
- package/adapter/js/timeout.d.ts +17 -0
- package/adapter/js/timeout.js +24 -0
- package/adapter/js/types.d.ts +35 -6
- package/adapter/js/withWorld.d.ts +43 -0
- package/adapter/js/withWorld.js +16 -0
- package/adapter/loader.d.ts +9 -1
- package/adapter/loader.js +35 -15
- package/adapter/loader.js.map +1 -1
- package/adapter/testDetails.d.ts +28 -0
- package/adapter/testDetails.js +79 -0
- package/adapter/testDetails.js.map +1 -0
- package/adapter/timeout.d.ts +17 -0
- package/adapter/timeout.js +25 -0
- package/adapter/timeout.js.map +1 -0
- package/adapter/types.d.ts +35 -6
- package/adapter/withWorld.d.ts +43 -0
- package/adapter/withWorld.js +17 -0
- package/adapter/withWorld.js.map +1 -0
- package/package.json +11 -9
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.collectPositions = collectPositions;
|
|
4
|
+
exports.resolveLocation = resolveLocation;
|
|
5
|
+
exports.callAt = callAt;
|
|
6
|
+
exports.stepTitle = stepTitle;
|
|
7
|
+
exports.toPickleArgument = toPickleArgument;
|
|
8
|
+
exports.buildSnippet = buildSnippet;
|
|
9
|
+
exports.formatLocation = formatLocation;
|
|
10
|
+
exports.featureTitle = featureTitle;
|
|
11
|
+
const node_vm_1 = require("node:vm");
|
|
12
|
+
const cucumber_1 = require("@cucumber/cucumber");
|
|
13
|
+
const PREVIEW_LENGTH = 50;
|
|
14
|
+
/**
|
|
15
|
+
* Map ast node ids (scenarios, steps, example rows, rules) of a gherkin document to their positions
|
|
16
|
+
*/
|
|
17
|
+
function collectPositions(gherkinDocument) {
|
|
18
|
+
const positions = new Map();
|
|
19
|
+
const addSteps = (steps) => {
|
|
20
|
+
for (const step of steps)
|
|
21
|
+
positions.set(step.id, step.location);
|
|
22
|
+
};
|
|
23
|
+
const addScenario = (scenario) => {
|
|
24
|
+
positions.set(scenario.id, scenario.location);
|
|
25
|
+
addSteps(scenario.steps);
|
|
26
|
+
for (const examples of scenario.examples) {
|
|
27
|
+
for (const row of examples.tableBody)
|
|
28
|
+
positions.set(row.id, row.location);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
for (const child of gherkinDocument.feature?.children ?? []) {
|
|
32
|
+
if (child.background)
|
|
33
|
+
addSteps(child.background.steps);
|
|
34
|
+
if (child.scenario)
|
|
35
|
+
addScenario(child.scenario);
|
|
36
|
+
if (child.rule) {
|
|
37
|
+
for (const ruleChild of child.rule.children) {
|
|
38
|
+
if (ruleChild.background)
|
|
39
|
+
addSteps(ruleChild.background.steps);
|
|
40
|
+
if (ruleChild.scenario)
|
|
41
|
+
addScenario(ruleChild.scenario);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return positions;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Resolve location of a pickle or pickle step.
|
|
49
|
+
* The last ast node id is used, so scenario outline examples point to their example row.
|
|
50
|
+
*/
|
|
51
|
+
function resolveLocation(positions, file, astNodeIds) {
|
|
52
|
+
for (let i = astNodeIds.length - 1; i >= 0; i--) {
|
|
53
|
+
const position = positions.get(astNodeIds[i]);
|
|
54
|
+
if (position)
|
|
55
|
+
return { file, line: position.line, column: position.column ?? 1 };
|
|
56
|
+
}
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Call function so that the immediate caller frame reports the given source location.
|
|
61
|
+
* Playwright takes test and describe locations from the caller frame,
|
|
62
|
+
* so this makes reports, `--list` and IDE integrations point to the feature file instead of the adapter.
|
|
63
|
+
*/
|
|
64
|
+
function callAt(location, fn, args) {
|
|
65
|
+
if (!location)
|
|
66
|
+
return fn(...args);
|
|
67
|
+
const code = '(function (fn, args) { return fn(...args); })';
|
|
68
|
+
const callColumn = code.indexOf('fn(...args)');
|
|
69
|
+
const caller = (0, node_vm_1.runInThisContext)(code, {
|
|
70
|
+
filename: location.file,
|
|
71
|
+
lineOffset: location.line - 1,
|
|
72
|
+
columnOffset: location.column - 1 - callColumn
|
|
73
|
+
});
|
|
74
|
+
return caller(fn, args);
|
|
75
|
+
}
|
|
76
|
+
function preview(text) {
|
|
77
|
+
const singleLine = text.replace(/\s*\n\s*/g, ' ↵ ').replace(/\s+/g, ' ').trim();
|
|
78
|
+
return singleLine.length > PREVIEW_LENGTH
|
|
79
|
+
? `${singleLine.slice(0, PREVIEW_LENGTH - 1)}…`
|
|
80
|
+
: singleLine;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Build step title for reports including short preview of doc string or data table argument
|
|
84
|
+
*/
|
|
85
|
+
function stepTitle(pickleStep) {
|
|
86
|
+
const argument = pickleStep.argument;
|
|
87
|
+
if (argument?.docString) {
|
|
88
|
+
return `${pickleStep.text} [MultiLine: ${preview(argument.docString.content)}]`;
|
|
89
|
+
}
|
|
90
|
+
if (argument?.dataTable) {
|
|
91
|
+
const rows = argument.dataTable.rows
|
|
92
|
+
.map(row => `| ${row.cells.map(cell => cell.value).join(' | ')} |`)
|
|
93
|
+
.join(' ');
|
|
94
|
+
return `${pickleStep.text} [DataTable: ${preview(rows)}]`;
|
|
95
|
+
}
|
|
96
|
+
return pickleStep.text;
|
|
97
|
+
}
|
|
98
|
+
function isTable(value) {
|
|
99
|
+
return Array.isArray(value) && value.every(row => Array.isArray(row));
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Convert value passed to executeStep into a pickle step argument.
|
|
103
|
+
* Strings become doc strings, `string[][]` and DataTable instances become data tables.
|
|
104
|
+
*/
|
|
105
|
+
function toPickleArgument(value) {
|
|
106
|
+
if (typeof value === 'string') {
|
|
107
|
+
return { argument: { docString: { content: value } } };
|
|
108
|
+
}
|
|
109
|
+
const rows = value instanceof cucumber_1.DataTable ? value.raw() : isTable(value) ? value : undefined;
|
|
110
|
+
if (rows) {
|
|
111
|
+
return {
|
|
112
|
+
argument: {
|
|
113
|
+
dataTable: {
|
|
114
|
+
rows: rows.map(row => ({ cells: row.map(cell => ({ value: String(cell) })) }))
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
return { extra: value };
|
|
120
|
+
}
|
|
121
|
+
const KEYWORD_TYPES = {
|
|
122
|
+
Context: 'precondition',
|
|
123
|
+
Action: 'event',
|
|
124
|
+
Outcome: 'outcome'
|
|
125
|
+
};
|
|
126
|
+
/**
|
|
127
|
+
* Build cucumber-style snippet for undefined step
|
|
128
|
+
*/
|
|
129
|
+
function buildSnippet(supportCodeLibrary, pickleStep) {
|
|
130
|
+
try {
|
|
131
|
+
const { default: SnippetBuilder } = require('@cucumber/cucumber/lib/formatter/step_definition_snippet_builder/index');
|
|
132
|
+
const { default: SnippetSyntax } = require('@cucumber/cucumber/lib/formatter/step_definition_snippet_builder/javascript_snippet_syntax');
|
|
133
|
+
const builder = new SnippetBuilder({
|
|
134
|
+
snippetSyntax: new SnippetSyntax('async-await'),
|
|
135
|
+
parameterTypeRegistry: supportCodeLibrary.parameterTypeRegistry
|
|
136
|
+
});
|
|
137
|
+
return builder.build({
|
|
138
|
+
keywordType: KEYWORD_TYPES[pickleStep.type ?? ''] ?? 'precondition',
|
|
139
|
+
pickleStep
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
return undefined;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
function formatLocation(location) {
|
|
147
|
+
return location ? `${location.file}:${location.line}` : '';
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Feature title used for describe block; falls back to file path for unnamed features
|
|
151
|
+
*/
|
|
152
|
+
function featureTitle(name, uri) {
|
|
153
|
+
return name?.trim() ? name : uri;
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=gherkin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gherkin.js","sourceRoot":"","sources":["../src/gherkin.ts"],"names":[],"mappings":"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"}
|
package/adapter/index.d.ts
CHANGED
package/adapter/index.js
CHANGED
|
@@ -19,4 +19,6 @@ __exportStar(require("./filter"), exports);
|
|
|
19
19
|
__exportStar(require("./Template"), exports);
|
|
20
20
|
__exportStar(require("@cucumber/cucumber"), exports);
|
|
21
21
|
__exportStar(require("./cucumberAdapter"), exports);
|
|
22
|
+
__exportStar(require("./testDetails"), exports);
|
|
23
|
+
__exportStar(require("./withWorld"), exports);
|
|
22
24
|
//# sourceMappingURL=index.js.map
|
package/adapter/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,oDAAkC;AAClC,2CAAyB;AACzB,6CAA2B;AAC3B,qDAAmC;AACnC,oDAAkC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,oDAAkC;AAClC,2CAAyB;AACzB,6CAA2B;AAC3B,qDAAmC;AACnC,oDAAkC;AAAA,gDAA8B;AAChE,8CAA4B"}
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
import { APIRequestContext, Browser, BrowserContext, Page } from '@playwright/test';
|
|
2
|
-
import type { CucumberAdapterConfig, SupportCodeLibrary, WorldOptions } from './types';
|
|
2
|
+
import type { AttachFn, CucumberAdapterConfig, SupportCodeLibrary, WorldOptions } from './types';
|
|
3
3
|
export declare class TestWorld {
|
|
4
4
|
log: (data: unknown) => void;
|
|
5
|
-
attach:
|
|
6
|
-
fileName?: string;
|
|
7
|
-
mediaType?: string;
|
|
8
|
-
}) => Promise<void>;
|
|
5
|
+
attach: AttachFn;
|
|
9
6
|
parameters: unknown;
|
|
10
7
|
config: CucumberAdapterConfig;
|
|
11
8
|
supportCodeLibrary: SupportCodeLibrary;
|
|
12
9
|
test: import("@playwright/test").TestType<import("@playwright/test").PlaywrightTestArgs & import("@playwright/test").PlaywrightTestOptions, import("@playwright/test").PlaywrightWorkerArgs & import("@playwright/test").PlaywrightWorkerOptions>;
|
|
13
10
|
expect: import("@playwright/test").Expect<{}>;
|
|
14
11
|
constructor(options: WorldOptions);
|
|
15
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Execute step by its text
|
|
14
|
+
* @param text - step text
|
|
15
|
+
* @param argument - doc string (`string`), data table (`string[][]` or `DataTable`) or any extra parameter
|
|
16
|
+
*/
|
|
17
|
+
executeStep(this: TestWorld, text: string, argument?: unknown): Promise<void>;
|
|
16
18
|
}
|
|
17
19
|
export declare class PlaywrightWorld extends TestWorld {
|
|
18
20
|
page: Page;
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PlaywrightWorld = exports.TestWorld = void 0;
|
|
4
4
|
const test_1 = require("@playwright/test");
|
|
5
|
+
const gherkin_1 = require("./gherkin");
|
|
6
|
+
const timeout_1 = require("./timeout");
|
|
5
7
|
class TestWorld {
|
|
6
8
|
log;
|
|
7
9
|
attach;
|
|
@@ -17,7 +19,12 @@ class TestWorld {
|
|
|
17
19
|
this.supportCodeLibrary = options.supportCodeLibrary;
|
|
18
20
|
this.config = options.config;
|
|
19
21
|
}
|
|
20
|
-
|
|
22
|
+
/**
|
|
23
|
+
* Execute step by its text
|
|
24
|
+
* @param text - step text
|
|
25
|
+
* @param argument - doc string (`string`), data table (`string[][]` or `DataTable`) or any extra parameter
|
|
26
|
+
*/
|
|
27
|
+
async executeStep(text, argument) {
|
|
21
28
|
const steps = this.supportCodeLibrary.stepDefinitions
|
|
22
29
|
.filter(s => s.matchesStepName(text));
|
|
23
30
|
if (steps.length === 0)
|
|
@@ -27,13 +34,19 @@ class TestWorld {
|
|
|
27
34
|
throw new Error(`Step "${text}" matches multiple step definitions:\n${locations}`);
|
|
28
35
|
}
|
|
29
36
|
const step = steps[0];
|
|
30
|
-
const {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
+
const { argument: pickleArgument, extra } = (0, gherkin_1.toPickleArgument)(argument);
|
|
38
|
+
const location = step.uri ? { file: step.uri, line: step.line, column: 1 } : undefined;
|
|
39
|
+
// reported as a nested step, so failures inside composite steps point to the exact inner step
|
|
40
|
+
await this.test.step((0, gherkin_1.stepTitle)({ text, argument: pickleArgument }), async () => {
|
|
41
|
+
const { parameters } = await step.getInvocationParameters({ step: { text, argument: pickleArgument }, world: this });
|
|
42
|
+
const args = extra === undefined ? parameters : [...parameters, extra];
|
|
43
|
+
try {
|
|
44
|
+
await (0, timeout_1.runWithTimeout)(() => step.code.apply(this, args), (0, timeout_1.resolveTimeout)(step, this.supportCodeLibrary.defaultTimeout), `Step '${text}' timed out after {timeout} ms\n at ${step.uri}:${step.line}`);
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
throw new Error(`Step failed: ${text}`, { cause: err });
|
|
48
|
+
}
|
|
49
|
+
}, { location });
|
|
37
50
|
}
|
|
38
51
|
}
|
|
39
52
|
exports.TestWorld = TestWorld;
|
package/adapter/js/Template.d.ts
CHANGED
|
@@ -1,10 +1,21 @@
|
|
|
1
|
+
export interface TemplateStep {
|
|
2
|
+
text: string;
|
|
3
|
+
argument?: string | string[][];
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Parse template text into steps. Supports data tables (`| a | b |` lines)
|
|
7
|
+
* and doc strings (`"""` or ``` blocks) placed after a step.
|
|
8
|
+
*/
|
|
9
|
+
export declare function parseTemplate(scenario: string): TemplateStep[];
|
|
1
10
|
/**
|
|
2
11
|
* Define template step
|
|
3
|
-
* @param {string} scenario - multiline string with steps
|
|
12
|
+
* @param {string} scenario - multiline string with steps; steps can have data tables and doc strings
|
|
4
13
|
* @example
|
|
5
14
|
* When('I click {string} and verify {string}', Template((locator, expected) => `
|
|
6
15
|
* I click '${locator}'
|
|
7
16
|
* I expect '${locator} > Value' to equal '${expected}'
|
|
17
|
+
* I fill form
|
|
18
|
+
* | name | John |
|
|
8
19
|
* `));
|
|
9
20
|
*/
|
|
10
|
-
export declare function Template(scenario: (...args:
|
|
21
|
+
export declare function Template(scenario: (...args: any[]) => string): (...args: unknown[]) => string;
|
package/adapter/js/Template.js
CHANGED
|
@@ -1,25 +1,74 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseTemplate = parseTemplate;
|
|
3
4
|
exports.Template = Template;
|
|
5
|
+
const DOC_STRING_DELIMITERS = ['"""', '```'];
|
|
6
|
+
function parseRow(line) {
|
|
7
|
+
const content = line.replace(/^\|/, '').replace(/(?<!\\)\|$/, '');
|
|
8
|
+
return content
|
|
9
|
+
.split(/(?<!\\)\|/)
|
|
10
|
+
.map(cell => cell.trim().replace(/\\\|/g, '|'));
|
|
11
|
+
}
|
|
12
|
+
function stripIndent(line, indent) {
|
|
13
|
+
const leading = line.length - line.trimStart().length;
|
|
14
|
+
return line.slice(Math.min(indent, leading));
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Parse template text into steps. Supports data tables (`| a | b |` lines)
|
|
18
|
+
* and doc strings (`"""` or ``` blocks) placed after a step.
|
|
19
|
+
*/
|
|
20
|
+
function parseTemplate(scenario) {
|
|
21
|
+
const lines = scenario.split('\n');
|
|
22
|
+
const steps = [];
|
|
23
|
+
const previousStep = (line) => {
|
|
24
|
+
const step = steps.at(-1);
|
|
25
|
+
if (!step)
|
|
26
|
+
throw new Error(`Template argument "${line.trim()}" must follow a step`);
|
|
27
|
+
return step;
|
|
28
|
+
};
|
|
29
|
+
for (let i = 0; i < lines.length; i++) {
|
|
30
|
+
const line = lines[i];
|
|
31
|
+
const trimmed = line.trim();
|
|
32
|
+
if (!trimmed)
|
|
33
|
+
continue;
|
|
34
|
+
const delimiter = DOC_STRING_DELIMITERS.find(d => trimmed.startsWith(d));
|
|
35
|
+
if (delimiter) {
|
|
36
|
+
const step = previousStep(line);
|
|
37
|
+
const indent = line.indexOf(delimiter);
|
|
38
|
+
const content = [];
|
|
39
|
+
for (i++; i < lines.length && lines[i].trim() !== delimiter; i++) {
|
|
40
|
+
content.push(stripIndent(lines[i], indent));
|
|
41
|
+
}
|
|
42
|
+
step.argument = content.join('\n');
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if (trimmed.startsWith('|')) {
|
|
46
|
+
const step = previousStep(line);
|
|
47
|
+
if (!Array.isArray(step.argument))
|
|
48
|
+
step.argument = [];
|
|
49
|
+
step.argument.push(parseRow(trimmed));
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
steps.push({ text: trimmed });
|
|
53
|
+
}
|
|
54
|
+
return steps;
|
|
55
|
+
}
|
|
4
56
|
/**
|
|
5
57
|
* Define template step
|
|
6
|
-
* @param {string} scenario - multiline string with steps
|
|
58
|
+
* @param {string} scenario - multiline string with steps; steps can have data tables and doc strings
|
|
7
59
|
* @example
|
|
8
60
|
* When('I click {string} and verify {string}', Template((locator, expected) => `
|
|
9
61
|
* I click '${locator}'
|
|
10
62
|
* I expect '${locator} > Value' to equal '${expected}'
|
|
63
|
+
* I fill form
|
|
64
|
+
* | name | John |
|
|
11
65
|
* `));
|
|
12
66
|
*/
|
|
13
67
|
function Template(scenario) {
|
|
14
68
|
return new Proxy(scenario, {
|
|
15
69
|
apply: async function (template, world, args) {
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
.split('\n')
|
|
19
|
-
.map(step => step.trim())
|
|
20
|
-
.filter(Boolean);
|
|
21
|
-
for (const step of steps) {
|
|
22
|
-
await world.executeStep(step);
|
|
70
|
+
for (const step of parseTemplate(template(...args))) {
|
|
71
|
+
await world.executeStep(step.text, step.argument);
|
|
23
72
|
}
|
|
24
73
|
},
|
|
25
74
|
});
|
|
@@ -1,2 +1,9 @@
|
|
|
1
|
+
import type { Pickle } from '@cucumber/messages';
|
|
1
2
|
import type { CucumberAdapterConfig } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Create scenario filter from tag expressions keyed by their source (used in error messages),
|
|
5
|
+
* e.g. `{ 'tags option': config.tags, 'CUCUMBER_TAGS env variable': process.env.CUCUMBER_TAGS }`.
|
|
6
|
+
* All expressions must match.
|
|
7
|
+
*/
|
|
8
|
+
export declare function createTagFilter(sources: Record<string, string | undefined>): (pickle: Pickle) => boolean;
|
|
2
9
|
export declare function defineConfig(config: CucumberAdapterConfig): void;
|