@qavajs/playwright-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 +70 -0
- package/adapter/PlaywrightWorld.js +19 -0
- package/adapter/PlaywrightWorld.js.map +1 -0
- package/adapter/adapter.spec.js +98 -0
- package/adapter/adapter.spec.js.map +1 -0
- package/adapter/loader.js +39 -0
- package/adapter/loader.js.map +1 -0
- package/index.d.ts +1 -0
- package/index.js +4 -0
- package/package.json +23 -0
- package/src/PlaywrightWorld.ts +25 -0
- package/src/adapter.spec.ts +97 -0
- package/src/loader.ts +35 -0
- package/tsconfig.json +22 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Change Log
|
|
2
|
+
|
|
3
|
+
All notable changes to the "@qavajs/steps-playwright" 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,70 @@
|
|
|
1
|
+
[](https://badge.fury.io/js/@qavajs%2Fplaywright-runner-adapter)
|
|
2
|
+
|
|
3
|
+
# @qavajs/playwright-runner-adapter
|
|
4
|
+
Adapter to run cucumberjs tests via playwright test runner
|
|
5
|
+
|
|
6
|
+
## Installation
|
|
7
|
+
|
|
8
|
+
`npm install @qavajs/playwright-runner-adapter`
|
|
9
|
+
|
|
10
|
+
## Basic Configuration
|
|
11
|
+
|
|
12
|
+
### Create cucumber config file
|
|
13
|
+
Set _paths_ and _require_ properties
|
|
14
|
+
```typescript
|
|
15
|
+
export default {
|
|
16
|
+
paths: ['test/features/*.feature'],
|
|
17
|
+
require: ['test/step_definitions/*.ts']
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
### Set testDir
|
|
21
|
+
Set testDir to adapter
|
|
22
|
+
```typescript
|
|
23
|
+
export default defineConfig({
|
|
24
|
+
testDir: resolve('node_modules/@qavajs/playwright-runner-adapter/adapter')
|
|
25
|
+
});
|
|
26
|
+
```
|
|
27
|
+
### Add cucumber config
|
|
28
|
+
Set CONFIG and PROFILE environment variables e.g via dotenv library
|
|
29
|
+
```dotenv
|
|
30
|
+
CONFIG=test/cucumber.ts
|
|
31
|
+
PROFILE=default
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Advanced Configuration
|
|
35
|
+
### Customizing test instance
|
|
36
|
+
Custom test instance can be passed to world constructor as _test_ property.
|
|
37
|
+
And then fixtures can be connected with world instance via _init_ property.
|
|
38
|
+
```typescript
|
|
39
|
+
import { test as base } from '@playwright/test';
|
|
40
|
+
import { SettingsPage } from './settings-page';
|
|
41
|
+
import { setWorldConstructor } from '@cucumber/cucumber';
|
|
42
|
+
import { PlaywrightWorld } from '@qavajs/playwright-runner-adapter/PlaywrightWorld';
|
|
43
|
+
|
|
44
|
+
type MyFixtures = {
|
|
45
|
+
settingsPage: SettingsPage;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const customTest = base.extend<MyFixtures>({
|
|
49
|
+
settingsPage: async ({ page }, use) => {
|
|
50
|
+
await use(new SettingsPage(page));
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
class ExtendedPlaywrightWorld extends PlaywrightWorld {
|
|
55
|
+
settingsPage: SettingsPage;
|
|
56
|
+
constructor(options: any) {
|
|
57
|
+
super(options);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
test = customTest;
|
|
61
|
+
|
|
62
|
+
// init arrow function connects fixtures with Cucumber world instance
|
|
63
|
+
init = ({ settingsPage }) => {
|
|
64
|
+
this.settingsPage = settingsPage;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PlaywrightWorld = void 0;
|
|
4
|
+
const test_1 = require("@playwright/test");
|
|
5
|
+
class PlaywrightWorld {
|
|
6
|
+
constructor(options) {
|
|
7
|
+
this.test = test_1.test;
|
|
8
|
+
this.init = ({ browser, context, page }) => {
|
|
9
|
+
this.browser = browser;
|
|
10
|
+
this.context = context;
|
|
11
|
+
this.page = page;
|
|
12
|
+
};
|
|
13
|
+
this.log = options.log;
|
|
14
|
+
this.attach = options.attach;
|
|
15
|
+
this.parameters = options.parameters;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
exports.PlaywrightWorld = PlaywrightWorld;
|
|
19
|
+
//# sourceMappingURL=PlaywrightWorld.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PlaywrightWorld.js","sourceRoot":"","sources":["../src/PlaywrightWorld.ts"],"names":[],"mappings":";;;AAAA,2CAA0F;AAE1F,MAAa,eAAe;IAWxB,YAAY,OAAY;QAFxB,SAAI,GAAG,WAAI,CAAC;QAQZ,SAAI,GAAG,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAA6D,EAAE,EAAE;YAC7F,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;YACvB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;YACvB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACrB,CAAC,CAAA;QATG,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;QACvB,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAC7B,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IACzC,CAAC;CAOJ;AAtBD,0CAsBC"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var _a, _b;
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const loader_1 = require("./loader");
|
|
5
|
+
const config = require((_a = process.env.CONFIG) !== null && _a !== void 0 ? _a : 'config.js');
|
|
6
|
+
const profile = (_b = process.env.PROFILE) !== null && _b !== void 0 ? _b : 'default';
|
|
7
|
+
const resolvedConfig = config[profile];
|
|
8
|
+
const features = (0, loader_1.loadFeatures)(resolvedConfig.paths);
|
|
9
|
+
const supportCodeLibrary = (0, loader_1.loadStepDefinitions)(resolvedConfig.require);
|
|
10
|
+
function log(data) {
|
|
11
|
+
console.log(data);
|
|
12
|
+
}
|
|
13
|
+
function attach(body, details) {
|
|
14
|
+
var _a, _b;
|
|
15
|
+
const fileName = (_a = details.fileName) !== null && _a !== void 0 ? _a : 'attachment';
|
|
16
|
+
const contentType = (_b = details.mediaType) !== null && _b !== void 0 ? _b : 'text/plain';
|
|
17
|
+
this.test.info().attach(fileName, { body, contentType });
|
|
18
|
+
}
|
|
19
|
+
const world = new supportCodeLibrary.World({
|
|
20
|
+
log,
|
|
21
|
+
attach
|
|
22
|
+
});
|
|
23
|
+
const test = world.test;
|
|
24
|
+
for (const feature of features) {
|
|
25
|
+
const tests = feature.tests;
|
|
26
|
+
test.describe(feature.feature, async () => {
|
|
27
|
+
for (const beforeAllHook of supportCodeLibrary.beforeTestRunHookDefinitions) {
|
|
28
|
+
test.beforeAll(async () => {
|
|
29
|
+
await beforeAllHook.code.apply({});
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
test.beforeEach(world.init);
|
|
33
|
+
for (const testCase of tests) {
|
|
34
|
+
const tag = testCase.tags.map(tag => tag.name);
|
|
35
|
+
test(testCase.name, { tag }, async () => {
|
|
36
|
+
for (const beforeHook of supportCodeLibrary.beforeTestCaseHookDefinitions) {
|
|
37
|
+
if (beforeHook.appliesToTestCase(testCase)) {
|
|
38
|
+
await test.step('Before', async () => {
|
|
39
|
+
await beforeHook.code.apply(world, [testCase]);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
for (const pickleStep of testCase.steps) {
|
|
44
|
+
await test.step(pickleStep.text, async () => {
|
|
45
|
+
for (const beforeStep of supportCodeLibrary.beforeTestStepHookDefinitions) {
|
|
46
|
+
if (beforeStep.appliesToTestCase(testCase)) {
|
|
47
|
+
await beforeStep.code.apply(world);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const steps = supportCodeLibrary.stepDefinitions
|
|
51
|
+
.filter(stepDefinition => stepDefinition.matchesStepName(pickleStep.text));
|
|
52
|
+
if (steps.length === 0)
|
|
53
|
+
throw new Error(`Step '${pickleStep.text}' is not defined`);
|
|
54
|
+
if (steps.length > 1)
|
|
55
|
+
throw new Error(`'${pickleStep.text}' matches multiple step definitions`);
|
|
56
|
+
const [step] = steps;
|
|
57
|
+
const { parameters } = await step.getInvocationParameters({
|
|
58
|
+
// @ts-ignore
|
|
59
|
+
step: {
|
|
60
|
+
text: pickleStep.text,
|
|
61
|
+
argument: pickleStep.argument
|
|
62
|
+
},
|
|
63
|
+
world
|
|
64
|
+
});
|
|
65
|
+
const result = { status: 'passed' };
|
|
66
|
+
try {
|
|
67
|
+
await step.code.apply(world, parameters);
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
result.status = 'failed';
|
|
71
|
+
result.error = err;
|
|
72
|
+
}
|
|
73
|
+
for (const afterStep of supportCodeLibrary.afterTestStepHookDefinitions) {
|
|
74
|
+
if (afterStep.appliesToTestCase(testCase)) {
|
|
75
|
+
await afterStep.code.apply(world, [{ result }]);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (result.error)
|
|
79
|
+
throw result.error;
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
for (const afterHook of supportCodeLibrary.afterTestCaseHookDefinitions) {
|
|
83
|
+
if (afterHook.appliesToTestCase(testCase)) {
|
|
84
|
+
await test.step('After', async () => {
|
|
85
|
+
await afterHook.code.apply(world, [testCase]);
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
for (const afterAllHook of supportCodeLibrary.afterTestRunHookDefinitions) {
|
|
92
|
+
test.afterAll(async function () {
|
|
93
|
+
await afterAllHook.code.apply({});
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=adapter.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.spec.js","sourceRoot":"","sources":["../src/adapter.spec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.loadStepDefinitions = exports.loadFeatures = void 0;
|
|
4
|
+
const node_fs_1 = require("node:fs");
|
|
5
|
+
const node_path_1 = require("node:path");
|
|
6
|
+
const gherkin_1 = require("@cucumber/gherkin");
|
|
7
|
+
const messages_1 = require("@cucumber/messages");
|
|
8
|
+
const cucumber_1 = require("@cucumber/cucumber");
|
|
9
|
+
const glob_1 = require("glob");
|
|
10
|
+
const PlaywrightWorld_1 = require("./PlaywrightWorld");
|
|
11
|
+
const uuidFn = messages_1.IdGenerator.uuid();
|
|
12
|
+
const builder = new gherkin_1.AstBuilder(uuidFn);
|
|
13
|
+
const matcher = new gherkin_1.GherkinClassicTokenMatcher();
|
|
14
|
+
const parser = new gherkin_1.Parser(builder, matcher);
|
|
15
|
+
function loadFeatures(globPattern) {
|
|
16
|
+
const files = (0, glob_1.globSync)(globPattern);
|
|
17
|
+
return files.map(file => {
|
|
18
|
+
var _a;
|
|
19
|
+
const filePath = (0, node_path_1.resolve)(file);
|
|
20
|
+
const gherkinDocument = parser.parse((0, node_fs_1.readFileSync)(filePath, 'utf-8'));
|
|
21
|
+
return {
|
|
22
|
+
feature: (_a = gherkinDocument.feature) === null || _a === void 0 ? void 0 : _a.name,
|
|
23
|
+
tests: (0, gherkin_1.compile)(gherkinDocument, file, uuidFn)
|
|
24
|
+
};
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
exports.loadFeatures = loadFeatures;
|
|
28
|
+
function loadStepDefinitions(globPattern) {
|
|
29
|
+
cucumber_1.supportCodeLibraryBuilder.reset(process.cwd(), uuidFn);
|
|
30
|
+
cucumber_1.supportCodeLibraryBuilder.methods.setWorldConstructor(PlaywrightWorld_1.PlaywrightWorld);
|
|
31
|
+
const files = (0, glob_1.globSync)(globPattern);
|
|
32
|
+
for (const file of files) {
|
|
33
|
+
const filePath = (0, node_path_1.resolve)(file);
|
|
34
|
+
require(filePath);
|
|
35
|
+
}
|
|
36
|
+
return cucumber_1.supportCodeLibraryBuilder.finalize();
|
|
37
|
+
}
|
|
38
|
+
exports.loadStepDefinitions = loadStepDefinitions;
|
|
39
|
+
//# sourceMappingURL=loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":";;;AAAA,qCAAuC;AACvC,yCAAoC;AACpC,+CAA4F;AAC5F,iDAAiD;AACjD,iDAA+D;AAC/D,+BAAgC;AAChC,uDAAkD;AAElD,MAAM,MAAM,GAAG,sBAAW,CAAC,IAAI,EAAE,CAAA;AACjC,MAAM,OAAO,GAAG,IAAI,oBAAU,CAAC,MAAM,CAAC,CAAA;AACtC,MAAM,OAAO,GAAG,IAAI,oCAA0B,EAAE,CAAC;AACjD,MAAM,MAAM,GAAG,IAAI,gBAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;AAE3C,SAAgB,YAAY,CAAC,WAAqB;IAC9C,MAAM,KAAK,GAAG,IAAA,eAAQ,EAAC,WAAW,CAAC,CAAC;IACpC,OAAO,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE;;QACpB,MAAM,QAAQ,GAAG,IAAA,mBAAO,EAAC,IAAI,CAAC,CAAC;QAC/B,MAAM,eAAe,GAAG,MAAM,CAAC,KAAK,CAAC,IAAA,sBAAY,EAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;QACtE,OAAO;YACH,OAAO,EAAE,MAAA,eAAe,CAAC,OAAO,0CAAE,IAAI;YACtC,KAAK,EAAE,IAAA,iBAAO,EAAC,eAAe,EAAE,IAAI,EAAE,MAAM,CAAC;SAChD,CAAA;IACL,CAAC,CAAC,CAAC;AACP,CAAC;AAVD,oCAUC;AAED,SAAgB,mBAAmB,CAAC,WAAqB;IACrD,oCAAyB,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC,CAAC;IACvD,oCAAyB,CAAC,OAAO,CAAC,mBAAmB,CAAC,iCAAe,CAAC,CAAC;IACvE,MAAM,KAAK,GAAG,IAAA,eAAQ,EAAC,WAAW,CAAC,CAAC;IACpC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACvB,MAAM,QAAQ,GAAG,IAAA,mBAAO,EAAC,IAAI,CAAC,CAAC;QAC/B,OAAO,CAAC,QAAQ,CAAC,CAAC;IACtB,CAAC;IACD,OAAO,oCAAyB,CAAC,QAAQ,EAAE,CAAC;AAChD,CAAC;AATD,kDASC"}
|
package/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { PlaywrightWorld } from './src/PlaywrightWorld';
|
package/index.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@qavajs/playwright-runner-adapter",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "adapter for playwright test runner",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"build": "tsc",
|
|
7
|
+
"test:ui": "playwright test --config test/playwright.config.ts --ui",
|
|
8
|
+
"test:e2e": "playwright test --config test/playwright-e2e.config.ts"
|
|
9
|
+
},
|
|
10
|
+
"author": "Alexandr Galichenko",
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@cucumber/gherkin": "^28.0.0",
|
|
14
|
+
"@cucumber/messages": "^24.1.0",
|
|
15
|
+
"glob": "^10.3.12"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@cucumber/cucumber": "^10.3.2",
|
|
19
|
+
"@playwright/test": "^1.42.1",
|
|
20
|
+
"@types/node": "^20.12.2",
|
|
21
|
+
"typescript": "^5.4.3"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { APIRequestContext, Browser, BrowserContext, Page, test } from '@playwright/test';
|
|
2
|
+
|
|
3
|
+
export class PlaywrightWorld {
|
|
4
|
+
page!: Page;
|
|
5
|
+
context!: BrowserContext;
|
|
6
|
+
browser!: Browser;
|
|
7
|
+
browserName!: string;
|
|
8
|
+
request!: APIRequestContext;
|
|
9
|
+
log!: (data: any) => void;
|
|
10
|
+
attach!: (data: any, details?: { filename?: string, mediaType: string }) => void;
|
|
11
|
+
parameters!: string;
|
|
12
|
+
test = test;
|
|
13
|
+
|
|
14
|
+
constructor(options: any) {
|
|
15
|
+
this.log = options.log;
|
|
16
|
+
this.attach = options.attach;
|
|
17
|
+
this.parameters = options.parameters;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
init = ({ browser, context, page }: { browser: Browser, context: BrowserContext, page: Page }) => {
|
|
21
|
+
this.browser = browser;
|
|
22
|
+
this.context = context;
|
|
23
|
+
this.page = page;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import {loadStepDefinitions, loadFeatures} from './loader';
|
|
2
|
+
|
|
3
|
+
const config = require(process.env.CONFIG ?? 'config.js');
|
|
4
|
+
const profile = process.env.PROFILE ?? 'default';
|
|
5
|
+
|
|
6
|
+
const resolvedConfig = config[profile];
|
|
7
|
+
const features = loadFeatures(resolvedConfig.paths);
|
|
8
|
+
const supportCodeLibrary = loadStepDefinitions(resolvedConfig.require);
|
|
9
|
+
|
|
10
|
+
function log(data: any) {
|
|
11
|
+
console.log(data);
|
|
12
|
+
}
|
|
13
|
+
function attach(this: { test: any }, body: any, details: any) {
|
|
14
|
+
const fileName = details.fileName ?? 'attachment';
|
|
15
|
+
const contentType = details.mediaType ?? 'text/plain';
|
|
16
|
+
this.test.info().attach(fileName, { body, contentType })
|
|
17
|
+
}
|
|
18
|
+
const world = new supportCodeLibrary.World({
|
|
19
|
+
log,
|
|
20
|
+
attach
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const test = world.test;
|
|
24
|
+
|
|
25
|
+
for (const feature of features) {
|
|
26
|
+
const tests = feature.tests;
|
|
27
|
+
test.describe(feature.feature as string, async () => {
|
|
28
|
+
for (const beforeAllHook of supportCodeLibrary.beforeTestRunHookDefinitions) {
|
|
29
|
+
test.beforeAll(async () => {
|
|
30
|
+
await beforeAllHook.code.apply({});
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
test.beforeEach(world.init);
|
|
35
|
+
|
|
36
|
+
for (const testCase of tests) {
|
|
37
|
+
const tag = testCase.tags.map(tag => tag.name);
|
|
38
|
+
|
|
39
|
+
test(testCase.name, {tag}, async () => {
|
|
40
|
+
for (const beforeHook of supportCodeLibrary.beforeTestCaseHookDefinitions) {
|
|
41
|
+
if (beforeHook.appliesToTestCase(testCase)) {
|
|
42
|
+
await test.step('Before', async () => {
|
|
43
|
+
await beforeHook.code.apply(world, [testCase]);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
for (const pickleStep of testCase.steps) {
|
|
48
|
+
await test.step(pickleStep.text, async () => {
|
|
49
|
+
for (const beforeStep of supportCodeLibrary.beforeTestStepHookDefinitions) {
|
|
50
|
+
if (beforeStep.appliesToTestCase(testCase)) {
|
|
51
|
+
await beforeStep.code.apply(world);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const steps = supportCodeLibrary.stepDefinitions
|
|
55
|
+
.filter(stepDefinition => stepDefinition.matchesStepName(pickleStep.text));
|
|
56
|
+
if (steps.length === 0) throw new Error(`Step '${pickleStep.text}' is not defined`);
|
|
57
|
+
if (steps.length > 1) throw new Error(`'${pickleStep.text}' matches multiple step definitions`);
|
|
58
|
+
const [step] = steps;
|
|
59
|
+
const { parameters} = await step.getInvocationParameters({
|
|
60
|
+
// @ts-ignore
|
|
61
|
+
step: {
|
|
62
|
+
text: pickleStep.text,
|
|
63
|
+
argument: pickleStep.argument
|
|
64
|
+
},
|
|
65
|
+
world
|
|
66
|
+
});
|
|
67
|
+
const result: { status: string, error?: any } = { status: 'passed' };
|
|
68
|
+
try {
|
|
69
|
+
await step.code.apply(world, parameters);
|
|
70
|
+
} catch (err) {
|
|
71
|
+
result.status = 'failed';
|
|
72
|
+
result.error = err;
|
|
73
|
+
}
|
|
74
|
+
for (const afterStep of supportCodeLibrary.afterTestStepHookDefinitions) {
|
|
75
|
+
if (afterStep.appliesToTestCase(testCase)) {
|
|
76
|
+
await afterStep.code.apply(world, [{result}]);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (result.error) throw result.error;
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
for (const afterHook of supportCodeLibrary.afterTestCaseHookDefinitions) {
|
|
83
|
+
if (afterHook.appliesToTestCase(testCase)) {
|
|
84
|
+
await test.step('After', async () => {
|
|
85
|
+
await afterHook.code.apply(world, [testCase]);
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
})
|
|
90
|
+
}
|
|
91
|
+
for (const afterAllHook of supportCodeLibrary.afterTestRunHookDefinitions) {
|
|
92
|
+
test.afterAll(async function () {
|
|
93
|
+
await afterAllHook.code.apply({});
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
package/src/loader.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { AstBuilder, compile, GherkinClassicTokenMatcher, Parser } from '@cucumber/gherkin';
|
|
4
|
+
import { IdGenerator } from '@cucumber/messages';
|
|
5
|
+
import { supportCodeLibraryBuilder } from '@cucumber/cucumber';
|
|
6
|
+
import { globSync } from 'glob';
|
|
7
|
+
import {PlaywrightWorld} from './PlaywrightWorld';
|
|
8
|
+
|
|
9
|
+
const uuidFn = IdGenerator.uuid()
|
|
10
|
+
const builder = new AstBuilder(uuidFn)
|
|
11
|
+
const matcher = new GherkinClassicTokenMatcher();
|
|
12
|
+
const parser = new Parser(builder, matcher)
|
|
13
|
+
|
|
14
|
+
export function loadFeatures(globPattern: string[]) {
|
|
15
|
+
const files = globSync(globPattern);
|
|
16
|
+
return files.map(file => {
|
|
17
|
+
const filePath = resolve(file);
|
|
18
|
+
const gherkinDocument = parser.parse(readFileSync(filePath, 'utf-8'));
|
|
19
|
+
return {
|
|
20
|
+
feature: gherkinDocument.feature?.name,
|
|
21
|
+
tests: compile(gherkinDocument, file, uuidFn)
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function loadStepDefinitions(globPattern: string[]) {
|
|
27
|
+
supportCodeLibraryBuilder.reset(process.cwd(), uuidFn);
|
|
28
|
+
supportCodeLibraryBuilder.methods.setWorldConstructor(PlaywrightWorld);
|
|
29
|
+
const files = globSync(globPattern);
|
|
30
|
+
for (const file of files) {
|
|
31
|
+
const filePath = resolve(file);
|
|
32
|
+
require(filePath);
|
|
33
|
+
}
|
|
34
|
+
return supportCodeLibraryBuilder.finalize();
|
|
35
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"include": [
|
|
3
|
+
"src/**/*.ts"
|
|
4
|
+
],
|
|
5
|
+
"exclude": [
|
|
6
|
+
"test/**/*.spec.ts"
|
|
7
|
+
],
|
|
8
|
+
"compilerOptions": {
|
|
9
|
+
"target": "es2017",
|
|
10
|
+
"module": "node16",
|
|
11
|
+
"moduleResolution": "node16",
|
|
12
|
+
"outDir": "./adapter",
|
|
13
|
+
"esModuleInterop": true,
|
|
14
|
+
"sourceMap": true,
|
|
15
|
+
"forceConsistentCasingInFileNames": true,
|
|
16
|
+
"strict": true,
|
|
17
|
+
"skipLibCheck": true,
|
|
18
|
+
"types": [
|
|
19
|
+
"node"
|
|
20
|
+
]
|
|
21
|
+
}
|
|
22
|
+
}
|