playwright-cucumber-steps 0.0.1 → 0.0.2
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/.editorconfig +2 -1
- package/CONTRIBUTING.md +116 -0
- package/README.md +48 -0
- package/custom-types/testing-server.d.ts +12 -0
- package/fixtures.ts +18 -0
- package/index.ts +33 -0
- package/package.json +24 -9
- package/playwright.config.ts +43 -0
- package/tests/features/test1-user.feature +19 -0
- package/tests/page-objects/test1-page.ts +9 -0
- package/tests/testing-server.ts +169 -0
- package/tsconfig.json +23 -0
- package/utils/get-page-objects.ts +100 -0
package/.editorconfig
CHANGED
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# Contributer's Guide
|
|
2
|
+
We welcome contributions - thanks for taking the time to contribute! Here are
|
|
3
|
+
some guidelines to help you get started. These are just guidelines, not rules,
|
|
4
|
+
so use your best judgment and feel free to propose changes to this document in
|
|
5
|
+
a pull request.
|
|
6
|
+
|
|
7
|
+
## Discussion
|
|
8
|
+
While not absolutely mandatory, it could be good if you first open an
|
|
9
|
+
[issue](https://github.com/Marketionist/playwright-cucumber-steps/issues)
|
|
10
|
+
for any bug or feature request. This allows discussion on the proper course of
|
|
11
|
+
action to take before coding begins.
|
|
12
|
+
|
|
13
|
+
## General rules
|
|
14
|
+
Most of the information you need to start contributing code changes can be found
|
|
15
|
+
[here](https://guides.github.com/activities/contributing-to-open-source/).
|
|
16
|
+
In short: fork, make your changes and submit a pull request (PR).
|
|
17
|
+
|
|
18
|
+
## Code Style Guide
|
|
19
|
+
In case your editor does not respect `.editorconfig`, here is a summary of rules:
|
|
20
|
+
|
|
21
|
+
- spacing - use spaces not tabs
|
|
22
|
+
- 4 spaces for `.ts` files
|
|
23
|
+
- 2 spaces for `.json`, `.yml`, `.feature` and other configuration files that start with a `.`
|
|
24
|
+
- semicolons - mandatory
|
|
25
|
+
- quotes - single-quote
|
|
26
|
+
|
|
27
|
+
### Fork
|
|
28
|
+
Fork the project [on Github](https://github.com/Marketionist/playwright-cucumber-steps)
|
|
29
|
+
and check out your copy locally:
|
|
30
|
+
|
|
31
|
+
```shell
|
|
32
|
+
git clone git@github.com:Marketionist/playwright-cucumber-steps.git
|
|
33
|
+
cd playwright-cucumber-steps
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Create your branch
|
|
37
|
+
Create a feature branch and start hacking:
|
|
38
|
+
|
|
39
|
+
```shell
|
|
40
|
+
git checkout -b my-feature-branch origin/main
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
We practice HEAD-based development, which means all changes are applied
|
|
44
|
+
directly on top of main.
|
|
45
|
+
|
|
46
|
+
### Commit
|
|
47
|
+
First make sure git knows your name and email address:
|
|
48
|
+
|
|
49
|
+
```shell
|
|
50
|
+
git config --global user.name 'John Doe'
|
|
51
|
+
git config --global user.email 'john@example.com'
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Writing good commit message is important.** A commit message should be around
|
|
55
|
+
50 characters or less and contain a short description of the change and
|
|
56
|
+
reference issues fixed (if any). Include `Fixes #N`, where _N_ is the issue
|
|
57
|
+
number the commit fixes, if any.
|
|
58
|
+
|
|
59
|
+
### Rebase
|
|
60
|
+
Use `git rebase` (not `git merge`) to sync your work with the core repository
|
|
61
|
+
from time to time:
|
|
62
|
+
|
|
63
|
+
```shell
|
|
64
|
+
git remote add upstream https://github.com/Marketionist/playwright-cucumber-steps.git
|
|
65
|
+
git fetch upstream
|
|
66
|
+
git rebase upstream/main
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Install all dependencies
|
|
70
|
+
```shell
|
|
71
|
+
npm run install:test-dependencies:chrome
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Test
|
|
75
|
+
New features **should have tests**. Look at other tests to see how
|
|
76
|
+
they should be structured.
|
|
77
|
+
|
|
78
|
+
This project makes use of code linting and e2e tests to make sure we don't break
|
|
79
|
+
anything. Before you submit your pull request make sure you pass all the tests:
|
|
80
|
+
|
|
81
|
+
You can run code linting with: `npm run lint`.
|
|
82
|
+
You can run all the e2e tests with: `npm test`.
|
|
83
|
+
|
|
84
|
+
Tests can be executed locally or remotely using Travis CI. Remote tests run is
|
|
85
|
+
triggered by each pull request.
|
|
86
|
+
|
|
87
|
+
### Push
|
|
88
|
+
```shell
|
|
89
|
+
git push origin my-feature-branch
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Go to https://github.com/yourusername/playwright-cucumber-steps and press the
|
|
93
|
+
_Pull request_ link and fill out the form.
|
|
94
|
+
|
|
95
|
+
A good PR comment message can look like this:
|
|
96
|
+
|
|
97
|
+
```text
|
|
98
|
+
Explain PR normatively in one line
|
|
99
|
+
|
|
100
|
+
Details (optional):
|
|
101
|
+
Details of PR message are a few lines of text, explaining things
|
|
102
|
+
in more detail, possibly giving some background about the issue
|
|
103
|
+
being fixed, etc.
|
|
104
|
+
|
|
105
|
+
Fixes #143
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Pull requests are usually reviewed within a few days. If there are comments to
|
|
109
|
+
address, apply your changes in new commits (preferably
|
|
110
|
+
[fixups](http://git-scm.com/docs/git-commit)) and push to the same branch.
|
|
111
|
+
|
|
112
|
+
### Integration
|
|
113
|
+
When code review is complete, a reviewer will take your PR and integrate it to
|
|
114
|
+
playwright-cucumber-steps main branch.
|
|
115
|
+
|
|
116
|
+
That's it! Thanks a lot for your contribution!
|
package/README.md
CHANGED
|
@@ -1,2 +1,50 @@
|
|
|
1
1
|
# playwright-cucumber-steps
|
|
2
2
|
Cucumber steps (step definitions) written with Playwright for end-to-end (e2e) tests
|
|
3
|
+
|
|
4
|
+
[](https://github.com/Marketionist/playwright-cucumber-steps/actions)
|
|
5
|
+
[](https://www.npmjs.com/package/playwright-cucumber-steps)
|
|
6
|
+
[](https://github.com/Marketionist/playwright-cucumber-steps/blob/main/LICENSE)
|
|
7
|
+
|
|
8
|
+
## Supported versions
|
|
9
|
+
<table>
|
|
10
|
+
<thead>
|
|
11
|
+
<tr>
|
|
12
|
+
<th><a href="https://nodejs.org/" rel="nofollow" target="_blank">Node.js</a></th>
|
|
13
|
+
<th><a href="https://github.com/microsoft/playwright" rel="nofollow" target="_blank">Playwright</a></th>
|
|
14
|
+
<th><a href="https://github.com/vitalets/playwright-bdd" rel="nofollow" target="_blank">Playwright-BDD</a></th>
|
|
15
|
+
</tr>
|
|
16
|
+
</thead>
|
|
17
|
+
<tbody>
|
|
18
|
+
<tr>
|
|
19
|
+
<td>>=22.3.x</td>
|
|
20
|
+
<td>>=1.57.0</td>
|
|
21
|
+
<td>>=8.4.2</td>
|
|
22
|
+
</tr>
|
|
23
|
+
</tbody>
|
|
24
|
+
</table>
|
|
25
|
+
|
|
26
|
+
## List of predefined steps
|
|
27
|
+
### Given steps
|
|
28
|
+
1. `I/user go(es) to URL "..."` - open a site (by its URL provided in "" as a
|
|
29
|
+
string - for example: `"https://github.com/Marketionist"`) in the current
|
|
30
|
+
browser window/tab.
|
|
31
|
+
2. `I/user go(es) to "..."."..."` - open a site (by its URL provided in
|
|
32
|
+
**"page"."element"**) in the current browser window/tab.
|
|
33
|
+
- `I/user go(es) to ... from ...` - open a site (by its URL provided in
|
|
34
|
+
**element** from **page**) in the current browser window/tab.
|
|
35
|
+
|
|
36
|
+
### Then steps
|
|
37
|
+
3. `page title should be "..."` - verify that title of the current browser
|
|
38
|
+
window/tab equals to the text (provided in "" as a string).
|
|
39
|
+
4. `page title should contain "..."` - verify that title of the current browser
|
|
40
|
+
window/tab contains the text (provided in "" as a string).
|
|
41
|
+
|
|
42
|
+
## Contributing
|
|
43
|
+
You are welcome to contribute to this repository - please see
|
|
44
|
+
[CONTRIBUTING.md](https://github.com/Marketionist/playwright-cucumber-steps/blob/main/CONTRIBUTING.md)
|
|
45
|
+
to help you get started. It is not set in stone, so you can just create a pull
|
|
46
|
+
request and we will help you refine it along the way.
|
|
47
|
+
|
|
48
|
+
## Thanks
|
|
49
|
+
If this package was helpful to you - please give this repository a **★ Star** on
|
|
50
|
+
[GitHub](https://github.com/Marketionist/playwright-cucumber-steps).
|
package/fixtures.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/* eslint @typescript-eslint/no-explicit-any: off */
|
|
2
|
+
/* eslint no-empty-pattern: off */
|
|
3
|
+
|
|
4
|
+
import { test as base, createBdd } from 'playwright-bdd';
|
|
5
|
+
|
|
6
|
+
interface Fixtures {
|
|
7
|
+
ctx: Record<string, any>
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const test = base.extend<Fixtures>({
|
|
11
|
+
ctx: async ({}, use) => {
|
|
12
|
+
const ctx = {};
|
|
13
|
+
|
|
14
|
+
await use(ctx);
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export const { Given, When, Then, } = createBdd(test);
|
package/index.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/* eslint new-cap: off */ // Disable rule for Given, When, Then
|
|
2
|
+
|
|
3
|
+
// #############################################################################
|
|
4
|
+
|
|
5
|
+
import { expect } from '@playwright/test';
|
|
6
|
+
import { Given, When, Then } from './fixtures.ts';
|
|
7
|
+
import { pageObjects } from './utils/get-page-objects.ts';
|
|
8
|
+
|
|
9
|
+
// #### Given steps ############################################################
|
|
10
|
+
|
|
11
|
+
Given('I/user go(es) to URL {string}', async ({ page, }, url: string) => {
|
|
12
|
+
await page.goto(url);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
Given('I/user go(es) to {string}.{string}', async ({ page, }, pageObject: string, element: string) => {
|
|
16
|
+
await page.goto(pageObjects[pageObject][element]);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
Given('I/user go(es) to {word} from {word}( page)', async ({ page, }, element: string, pageObject: string) => {
|
|
20
|
+
await page.goto(pageObjects[pageObject][element]);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// #### Then steps #############################################################
|
|
24
|
+
|
|
25
|
+
Then('page title should be {string}', async ({ page, }, text: string) => {
|
|
26
|
+
await expect(page).toHaveTitle(text);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
Then('page title should contain {string}', async ({ page, }, text: string) => {
|
|
30
|
+
const regularExpression = new RegExp(`^.*${text}.*$`, 'g');
|
|
31
|
+
|
|
32
|
+
await expect(page).toHaveTitle(regularExpression);
|
|
33
|
+
});
|
package/package.json
CHANGED
|
@@ -1,21 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "playwright-cucumber-steps",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"description": "Cucumber steps (step definitions) written with Playwright for end-to-end (e2e) tests",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"engines": {
|
|
7
|
-
"node": ">=
|
|
7
|
+
"node": ">=22.3.x"
|
|
8
8
|
},
|
|
9
|
+
"type": "module",
|
|
9
10
|
"scripts": {
|
|
10
|
-
"install-
|
|
11
|
-
"test": "",
|
|
11
|
+
"install:test-dependencies:firefox": "PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=true npm install && npx playwright install firefox",
|
|
12
|
+
"install:test-dependencies:chrome": "PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=true npm install && npx playwright install chrome",
|
|
13
|
+
"install:test-dependencies:all": "npm install && npx playwright install",
|
|
14
|
+
"lint": "node ./node_modules/.bin/eslint *.ts --ext .ts,.tsx .",
|
|
15
|
+
"prepare": "husky",
|
|
16
|
+
"start:test-server": "node tests/testing-server.ts",
|
|
17
|
+
"test": "npx bddgen && npx playwright test",
|
|
12
18
|
"patch": "npm version patch -m \"Bump up package version to %s\" && git push && git push origin --tags && npm publish",
|
|
13
19
|
"minor": "npm version minor -m \"Bump up package version to %s\" && git push && git push origin --tags && npm publish",
|
|
14
20
|
"major": "npm version major -m \"Bump up package version to %s\" && git push && git push origin --tags && npm publish"
|
|
15
21
|
},
|
|
16
22
|
"repository": {
|
|
17
23
|
"type": "git",
|
|
18
|
-
"url": "https://github.com/Marketionist/playwright-cucumber-steps"
|
|
24
|
+
"url": "git+https://github.com/Marketionist/playwright-cucumber-steps.git"
|
|
19
25
|
},
|
|
20
26
|
"keywords": [
|
|
21
27
|
"playwright cucumber",
|
|
@@ -40,11 +46,20 @@
|
|
|
40
46
|
"author": "Dmytro Shpakovskyi",
|
|
41
47
|
"license": "MIT",
|
|
42
48
|
"dependencies": {
|
|
43
|
-
"js-automation-tools": "^1.
|
|
49
|
+
"js-automation-tools": "^3.1.3"
|
|
44
50
|
},
|
|
45
51
|
"peerDependencies": {
|
|
46
|
-
"
|
|
47
|
-
"playwright": ">=
|
|
52
|
+
"@playwright/test": ">=1.57.0",
|
|
53
|
+
"playwright-bdd": ">=8.4.2"
|
|
48
54
|
},
|
|
49
|
-
"devDependencies": {
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@playwright/test": "^1.58.0",
|
|
57
|
+
"playwright-bdd": "^8.4.2",
|
|
58
|
+
"@types/node": "^25.0.10",
|
|
59
|
+
"@typescript-eslint/eslint-plugin": "^8.46.2",
|
|
60
|
+
"@typescript-eslint/parser": "^8.46.2",
|
|
61
|
+
"eslint": "^8.57.1",
|
|
62
|
+
"husky": "^9.1.7",
|
|
63
|
+
"node-testing-server": "^1.6.1"
|
|
64
|
+
}
|
|
50
65
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { defineConfig, devices } from '@playwright/test';
|
|
2
|
+
import { defineBddConfig, cucumberReporter } from 'playwright-bdd';
|
|
3
|
+
|
|
4
|
+
const testDir = defineBddConfig({
|
|
5
|
+
features: 'tests/features/*.feature',
|
|
6
|
+
steps: '*.ts',
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
export default defineConfig({
|
|
10
|
+
// Look for test files in the directory, relative to this configuration file
|
|
11
|
+
testDir,
|
|
12
|
+
// Each test is given 30 seconds to finish
|
|
13
|
+
timeout: 30000,
|
|
14
|
+
|
|
15
|
+
// Forbid test.only on CI
|
|
16
|
+
forbidOnly: !!process.env.CI,
|
|
17
|
+
|
|
18
|
+
// One retry for each test
|
|
19
|
+
retries: 1,
|
|
20
|
+
reporter: [
|
|
21
|
+
cucumberReporter('html', {
|
|
22
|
+
outputFile: 'cucumber-report/index.html',
|
|
23
|
+
externalAttachments: true,
|
|
24
|
+
}),
|
|
25
|
+
],
|
|
26
|
+
use: {
|
|
27
|
+
...devices['Desktop Chrome'],
|
|
28
|
+
channel: 'chrome', // or 'chrome-beta'
|
|
29
|
+
headless: true,
|
|
30
|
+
viewport: { width: 1920, height: 1080, },
|
|
31
|
+
ignoreHTTPSErrors: true,
|
|
32
|
+
trace: 'on',
|
|
33
|
+
screenshot: 'on',
|
|
34
|
+
video: 'on-first-retry',
|
|
35
|
+
},
|
|
36
|
+
// Run local server with test pages before starting the tests
|
|
37
|
+
webServer: {
|
|
38
|
+
command: 'npm run start:test-server',
|
|
39
|
+
url: 'http://localhost:8001',
|
|
40
|
+
stdout: 'pipe',
|
|
41
|
+
stderr: 'pipe',
|
|
42
|
+
},
|
|
43
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
@fast @user-steps @part1
|
|
2
|
+
|
|
3
|
+
Feature: Test "user ..." steps - part 1
|
|
4
|
+
As a user of Playwright
|
|
5
|
+
I should be able to use Cucumber
|
|
6
|
+
to run my e2e tests
|
|
7
|
+
|
|
8
|
+
Scenario: 'user goes to URL' should open corresponding page, 'title should contain' should verify the title
|
|
9
|
+
Given user goes to URL "https://www.saucedemo.com"
|
|
10
|
+
Then page title should be "Swag Labs"
|
|
11
|
+
And page title should contain "ag Lab"
|
|
12
|
+
|
|
13
|
+
Scenario: 'user goes to page' should open corresponding page
|
|
14
|
+
Given user goes to "test1-page"."pageTest1"
|
|
15
|
+
Then page title should be "Test1 Page"
|
|
16
|
+
|
|
17
|
+
Scenario: 'user goes to page' should open corresponding page (text style step)
|
|
18
|
+
Given user goes to pageTest1 from test1-page
|
|
19
|
+
Then page title should be "Test1 Page"
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
// Add testing server to provide pages for tests
|
|
2
|
+
// import { NodeTestingServer } from './testing-server.d.ts';
|
|
3
|
+
import { nodeTestingServer } from 'node-testing-server';
|
|
4
|
+
|
|
5
|
+
// Settings for node testing server
|
|
6
|
+
nodeTestingServer.config = {
|
|
7
|
+
hostname: 'localhost',
|
|
8
|
+
port: 8001,
|
|
9
|
+
logsEnabled: 0,
|
|
10
|
+
pages: {
|
|
11
|
+
'/test1.html': `<title>Test1 Page</title><a id="link-test2-page" href="
|
|
12
|
+
http://localhost:8001/test2.html">Test2 page</a>
|
|
13
|
+
<style>
|
|
14
|
+
#block-menu {
|
|
15
|
+
width: 20%;
|
|
16
|
+
height: 20%;
|
|
17
|
+
z-index: 9999;
|
|
18
|
+
background-color: #FFD700;
|
|
19
|
+
}
|
|
20
|
+
</style>
|
|
21
|
+
<script>
|
|
22
|
+
window.onload = function () {
|
|
23
|
+
document.querySelector('h1').addEventListener("mouseover", function () {
|
|
24
|
+
document.getElementById("text-test").innerHTML = 'Test 1 sample text';
|
|
25
|
+
});
|
|
26
|
+
document.querySelector('h1').addEventListener("mouseout", function () {
|
|
27
|
+
document.getElementById("text-test").innerHTML = '';
|
|
28
|
+
});
|
|
29
|
+
document.querySelector('#button-menu-right-click').addEventListener("contextmenu", function () {
|
|
30
|
+
document.getElementById("text-test").innerHTML = '';
|
|
31
|
+
const blockMenuContainer = document.getElementById("block-menu-container");
|
|
32
|
+
const blockMenu = document.createElement('div');
|
|
33
|
+
|
|
34
|
+
blockMenu.setAttribute('id', 'block-menu');
|
|
35
|
+
blockMenu.innerHTML = 'Menu goes here...';
|
|
36
|
+
blockMenuContainer.insertBefore(blockMenu, blockMenuContainer.firstChild);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
</script>
|
|
40
|
+
<h1>Test1 page</h1>
|
|
41
|
+
<p id="text-test"></p>
|
|
42
|
+
<p>
|
|
43
|
+
<label for="image">Upload image:</label>
|
|
44
|
+
<input type="file" name="image" accept="image/png, .jpeg, .jpg, image/gif">
|
|
45
|
+
<input type="submit" value="Upload">
|
|
46
|
+
</p>
|
|
47
|
+
<p id="block-menu-container">
|
|
48
|
+
<button id="button-menu-right-click">Right click menu</button>
|
|
49
|
+
</p>`,
|
|
50
|
+
'/test2.html': `<title>Test2 Page</title>
|
|
51
|
+
<script>
|
|
52
|
+
window.onload = function () {
|
|
53
|
+
document.getElementById("login").addEventListener("click", function () {
|
|
54
|
+
document.getElementById("block-credentials").innerHTML = document
|
|
55
|
+
.getElementById("input-username").value + document
|
|
56
|
+
.getElementById("input-password").value;
|
|
57
|
+
});
|
|
58
|
+
document.getElementById("input-colors").addEventListener("input", function () {
|
|
59
|
+
document.getElementById("block-input-color").innerHTML = document
|
|
60
|
+
.getElementById("input-colors").value;
|
|
61
|
+
});
|
|
62
|
+
document.getElementById("dropdown-colors").addEventListener("change", function () {
|
|
63
|
+
document.getElementById("block-dropdown-color").innerHTML = document
|
|
64
|
+
.getElementById("dropdown-colors").value;
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
</script>
|
|
68
|
+
<h1>Test2 page</h1>
|
|
69
|
+
<p>Credentials are: <span id="block-credentials"></span></p>
|
|
70
|
+
<form>
|
|
71
|
+
Sign in:<br>
|
|
72
|
+
<input id="input-username" type="text" name="input-username" placeholder="Username" value=""><br>
|
|
73
|
+
<input id="input-password" type="password" name="input-password" placeholder="Password" value=""><br>
|
|
74
|
+
</form>
|
|
75
|
+
<button id="login">Sign in</button>
|
|
76
|
+
<p>Typed in input color is: <span id="block-input-color"></span></p>
|
|
77
|
+
<form>
|
|
78
|
+
Colors:<br>
|
|
79
|
+
<input id="input-colors" type="text" value=""><br>
|
|
80
|
+
<input type="submit" value="Submit">
|
|
81
|
+
</form>
|
|
82
|
+
<p>Selected dropdown color is: <span id="block-dropdown-color"></span></p>
|
|
83
|
+
<select id="dropdown-colors" name="colors">
|
|
84
|
+
<option value="default color">Default color</option>
|
|
85
|
+
<option value="black">Black</option>
|
|
86
|
+
<option value="grey">Grey</option>
|
|
87
|
+
<option value="white">White</option>
|
|
88
|
+
<option value="red">Red</option>
|
|
89
|
+
<option value="crimson">Crimson</option>
|
|
90
|
+
<option value="magenta">Magenta</option>
|
|
91
|
+
<option value="blue">Blue</option>
|
|
92
|
+
<option value="aqua">Aqua</option>
|
|
93
|
+
<option value="cyan">Cyan</option>
|
|
94
|
+
<option value="indigo">Indigo</option>
|
|
95
|
+
<option value="green">Green</option>
|
|
96
|
+
<option value="yellow">Yellow</option>
|
|
97
|
+
<option value="Gold">Gold</option>
|
|
98
|
+
<option value="orange">Orange</option>
|
|
99
|
+
</select>`,
|
|
100
|
+
'/test-iframe.html': `<title>Test Page with iframe</title>
|
|
101
|
+
<h1>Test page with iframe</h1>
|
|
102
|
+
<iframe src="test1.html" id="iframe-test1" name="test iframe" width="400" height="300" align="left">
|
|
103
|
+
<p>Your browser does not support iframes</p>
|
|
104
|
+
</iframe>`,
|
|
105
|
+
'/test-alert.html': `<title>Test Page with alert</title>
|
|
106
|
+
<script>
|
|
107
|
+
window.onload = function () {
|
|
108
|
+
document.getElementById("button-launch-alert").addEventListener("click", function () {
|
|
109
|
+
let alertStatus;
|
|
110
|
+
if (confirm("Accept (OK) or Dismiss (Cancel) - press a button!") == true) {
|
|
111
|
+
alertStatus = "Alert was accepted!";
|
|
112
|
+
} else {
|
|
113
|
+
alertStatus = "Alert was canceled!";
|
|
114
|
+
}
|
|
115
|
+
document.getElementById("block-alert-status").innerHTML = alertStatus;
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
</script>
|
|
119
|
+
<h1>Test page with alert</h1>
|
|
120
|
+
<button id="button-launch-alert">Launch alert</button>
|
|
121
|
+
<p id="block-alert-status"></p>`,
|
|
122
|
+
'/test-loader.html': `<title>Test Page with loader</title>
|
|
123
|
+
<style>
|
|
124
|
+
#loader {
|
|
125
|
+
width: 70%;
|
|
126
|
+
height: 70%;
|
|
127
|
+
position: fixed;
|
|
128
|
+
z-index: 9999;
|
|
129
|
+
background-color: #FFD700;
|
|
130
|
+
}
|
|
131
|
+
</style>
|
|
132
|
+
<script>
|
|
133
|
+
function insertAfter(referenceNode, newNode) {
|
|
134
|
+
referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
|
|
135
|
+
}
|
|
136
|
+
function showLoader (idValue, timeToSpin = 5000) {
|
|
137
|
+
const blockLoader = document.createElement('div');
|
|
138
|
+
|
|
139
|
+
blockLoader.setAttribute('id', idValue);
|
|
140
|
+
document.body.insertBefore(blockLoader, document.body.firstChild);
|
|
141
|
+
|
|
142
|
+
setTimeout(function () {
|
|
143
|
+
document.getElementById(idValue).remove();
|
|
144
|
+
}, timeToSpin);
|
|
145
|
+
}
|
|
146
|
+
function showContentWithDelay (timeDelay = 5000) {
|
|
147
|
+
const title = document.querySelector('h1');
|
|
148
|
+
let blockContent = document.createElement('p');
|
|
149
|
+
blockContent.setAttribute('id', 'block-content');
|
|
150
|
+
blockContent.innerHTML = 'This is a test content on a page with loader';
|
|
151
|
+
|
|
152
|
+
setTimeout(function () {
|
|
153
|
+
insertAfter(title, blockContent);
|
|
154
|
+
}, timeDelay);
|
|
155
|
+
}
|
|
156
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
157
|
+
const timeout1 = 6000;
|
|
158
|
+
const timeout2 = 8000;
|
|
159
|
+
|
|
160
|
+
showLoader('loader', timeout1);
|
|
161
|
+
showContentWithDelay(timeout2);
|
|
162
|
+
});
|
|
163
|
+
</script>
|
|
164
|
+
<h1>Test page with loader</h1>`,
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
// Start node testing server
|
|
169
|
+
await nodeTestingServer.start();
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "es2021",
|
|
4
|
+
"module": "nodenext",
|
|
5
|
+
"outDir": "./dist",
|
|
6
|
+
"types": ["node", "@playwright/test"],
|
|
7
|
+
"strict": true,
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"forceConsistentCasingInFileNames": true,
|
|
11
|
+
"useUnknownInCatchVariables": false,
|
|
12
|
+
"noEmit": true,
|
|
13
|
+
"allowImportingTsExtensions": true,
|
|
14
|
+
"resolveJsonModule": true,
|
|
15
|
+
},
|
|
16
|
+
"include": [
|
|
17
|
+
"**/*.ts"
|
|
18
|
+
],
|
|
19
|
+
"exclude": [
|
|
20
|
+
"node_modules",
|
|
21
|
+
"dist"
|
|
22
|
+
]
|
|
23
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// #############################################################################
|
|
2
|
+
|
|
3
|
+
import { readdir, stat } from 'node:fs';
|
|
4
|
+
import { promisify } from 'node:util';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = path.dirname(__filename);
|
|
10
|
+
|
|
11
|
+
const spacesToIndent = 4;
|
|
12
|
+
|
|
13
|
+
const readdirP = promisify(readdir);
|
|
14
|
+
const statP = promisify(stat);
|
|
15
|
+
|
|
16
|
+
async function _readDirectory (
|
|
17
|
+
directory: string, allFiles?: string[]
|
|
18
|
+
): Promise<string[]> {
|
|
19
|
+
const files = (await readdirP(directory)).map((filePath) => {
|
|
20
|
+
return path.join(directory, filePath);
|
|
21
|
+
});
|
|
22
|
+
const allFilesPaths = allFiles ?? [];
|
|
23
|
+
|
|
24
|
+
allFilesPaths.push(...files);
|
|
25
|
+
await Promise.all(
|
|
26
|
+
files.map(async (f) => {
|
|
27
|
+
return (await statP(f)).isDirectory() && _readDirectory(f, allFilesPaths);
|
|
28
|
+
})
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
return allFilesPaths;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function readDirectories (directories: string[]): Promise<string[]> {
|
|
35
|
+
const allFilesPaths: string[] = [];
|
|
36
|
+
|
|
37
|
+
(await Promise.all(
|
|
38
|
+
directories.map(async (dir) => {
|
|
39
|
+
const files = await _readDirectory(dir);
|
|
40
|
+
|
|
41
|
+
return files;
|
|
42
|
+
})
|
|
43
|
+
)).map((value) => {
|
|
44
|
+
allFilesPaths.push(...value);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
return allFilesPaths;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const isCalledExternally = __dirname.includes('node_modules');
|
|
51
|
+
const pOFolderPath = process.env.PO_FOLDER_PATH!;
|
|
52
|
+
|
|
53
|
+
const pageObjectsFolderPathes = 'PO_FOLDER_PATH' in process.env ?
|
|
54
|
+
pOFolderPath.replace(/\s+/g, '').split(',') :
|
|
55
|
+
[path.join('tests', 'page-objects'),];
|
|
56
|
+
|
|
57
|
+
const fullPageObjectsFolderPathes = isCalledExternally ?
|
|
58
|
+
pageObjectsFolderPathes.map((pageObjectsFolderPath) => {
|
|
59
|
+
return path.join(__dirname, '..', '..', '..', pageObjectsFolderPath);
|
|
60
|
+
}) :
|
|
61
|
+
pageObjectsFolderPathes.map((pageObjectsFolderPath) => {
|
|
62
|
+
return path.join(__dirname, '..', pageObjectsFolderPath);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Require all Page Object files in directory
|
|
66
|
+
type PageObject = Record<string, Record<string, string>>;
|
|
67
|
+
const pageObjects: PageObject = {};
|
|
68
|
+
|
|
69
|
+
async function requirePageObjects () {
|
|
70
|
+
const allPageObjectFiles = await readDirectories(
|
|
71
|
+
fullPageObjectsFolderPathes);
|
|
72
|
+
const allRequiredPageObjects = allPageObjectFiles.filter(
|
|
73
|
+
(value) => {
|
|
74
|
+
return value.includes('.ts');
|
|
75
|
+
}
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
await Promise.all(
|
|
79
|
+
allRequiredPageObjects.map(async (file) => {
|
|
80
|
+
const fileName = path.basename(file, '.ts');
|
|
81
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
82
|
+
const fileContent: PageObject = await import(file);
|
|
83
|
+
|
|
84
|
+
pageObjects[fileName] = fileContent.default;
|
|
85
|
+
|
|
86
|
+
return file;
|
|
87
|
+
})
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
console.log(
|
|
91
|
+
'\nPage Objects from PO_FOLDER_PATH:',
|
|
92
|
+
`\n${JSON.stringify(pageObjects, null, spacesToIndent)}\n\n`
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
return pageObjects;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
await requirePageObjects();
|
|
99
|
+
|
|
100
|
+
export { pageObjects };
|