@actionlogementservices/wdio-test-context 2.0.0 → 2.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/.github/CODEOWNERS +2 -0
- package/.github/workflows/release.yml +92 -0
- package/README.md +180 -119
- package/doc/src_test-context.md +13 -0
- package/package.json +7 -3
- package/src/mail-service.js +9 -2
- package/src/test-context.js +17 -2
- package/src/types.d.ts +7 -0
- package/src/wdio/test-context-worker-service.js +1 -1
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Stages the package on npm when a merged PR brings a new version in package.json.
|
|
2
|
+
# npm authentication relies on npm trusted publishing (OIDC): no npm token is stored in GitHub.
|
|
3
|
+
# The staged version goes public only once a maintainer approves it on npmjs.com with 2FA,
|
|
4
|
+
# then the draft GitHub release has to be published to create the git tag.
|
|
5
|
+
name: Release
|
|
6
|
+
|
|
7
|
+
on:
|
|
8
|
+
push:
|
|
9
|
+
branches: [main]
|
|
10
|
+
workflow_dispatch:
|
|
11
|
+
|
|
12
|
+
# never run two releases at the same time
|
|
13
|
+
concurrency: release
|
|
14
|
+
|
|
15
|
+
permissions:
|
|
16
|
+
contents: write # creates the draft GitHub release
|
|
17
|
+
issues: write # creates the approval reminder issue
|
|
18
|
+
id-token: write # npm trusted publishing
|
|
19
|
+
|
|
20
|
+
jobs:
|
|
21
|
+
release:
|
|
22
|
+
runs-on: ubuntu-latest
|
|
23
|
+
env:
|
|
24
|
+
GH_TOKEN: ${{ github.token }}
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@v5
|
|
27
|
+
|
|
28
|
+
- uses: actions/setup-node@v5
|
|
29
|
+
with:
|
|
30
|
+
node-version: 22
|
|
31
|
+
registry-url: https://registry.npmjs.org
|
|
32
|
+
cache: npm
|
|
33
|
+
|
|
34
|
+
# staged publishing requires npm >= 11.15.0
|
|
35
|
+
- run: npm install --global npm@^11.15.0
|
|
36
|
+
|
|
37
|
+
- name: Check whether the version is already published or staged
|
|
38
|
+
id: version
|
|
39
|
+
run: |
|
|
40
|
+
NAME=$(node -p "require('./package.json').name")
|
|
41
|
+
VERSION=$(node -p "require('./package.json').version")
|
|
42
|
+
PUBLISHED=$(npm view "$NAME@$VERSION" version 2>/dev/null || true)
|
|
43
|
+
echo "name=$NAME" >> "$GITHUB_OUTPUT"
|
|
44
|
+
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
|
45
|
+
if [ "$PUBLISHED" = "$VERSION" ]; then
|
|
46
|
+
echo "publish=false" >> "$GITHUB_OUTPUT"
|
|
47
|
+
echo "::notice::$NAME@$VERSION is already published, nothing to release."
|
|
48
|
+
# a (draft) GitHub release means the version has already been staged and awaits approval
|
|
49
|
+
elif gh release list --limit 100 --json tagName --jq '.[].tagName' | grep -Fxq "$VERSION"; then
|
|
50
|
+
echo "publish=false" >> "$GITHUB_OUTPUT"
|
|
51
|
+
echo "::notice::$NAME@$VERSION is already staged, approve it on npmjs.com."
|
|
52
|
+
else
|
|
53
|
+
echo "publish=true" >> "$GITHUB_OUTPUT"
|
|
54
|
+
fi
|
|
55
|
+
# prereleases (e.g. 5.1.0-0) go to the 'next' dist-tag, like release-it does
|
|
56
|
+
if [[ "$VERSION" == *-* ]]; then
|
|
57
|
+
echo "dist-tag=next" >> "$GITHUB_OUTPUT"
|
|
58
|
+
echo "prerelease=--prerelease" >> "$GITHUB_OUTPUT"
|
|
59
|
+
else
|
|
60
|
+
echo "dist-tag=latest" >> "$GITHUB_OUTPUT"
|
|
61
|
+
echo "prerelease=" >> "$GITHUB_OUTPUT"
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
# no build step: install scripts are skipped (msnodesqlv8 native addon is not needed to lint)
|
|
65
|
+
- if: steps.version.outputs.publish == 'true'
|
|
66
|
+
run: npm ci --ignore-scripts
|
|
67
|
+
|
|
68
|
+
- if: steps.version.outputs.publish == 'true'
|
|
69
|
+
run: npm test
|
|
70
|
+
|
|
71
|
+
- if: steps.version.outputs.publish == 'true'
|
|
72
|
+
run: npm stage publish --tag ${{ steps.version.outputs.dist-tag }}
|
|
73
|
+
|
|
74
|
+
# a draft release creates no git tag: the tag is created when the draft is published
|
|
75
|
+
- name: Create the draft GitHub release and the approval reminder
|
|
76
|
+
if: steps.version.outputs.publish == 'true'
|
|
77
|
+
env:
|
|
78
|
+
NAME: ${{ steps.version.outputs.name }}
|
|
79
|
+
VERSION: ${{ steps.version.outputs.version }}
|
|
80
|
+
run: |
|
|
81
|
+
RELEASE_URL=$(gh release create "$VERSION" --draft --target "$GITHUB_SHA" --title "$VERSION" --generate-notes ${{ steps.version.outputs.prerelease }})
|
|
82
|
+
BODY="$NAME@$VERSION has been staged on npm and awaits your approval.
|
|
83
|
+
|
|
84
|
+
- [ ] Approve it in the **Staged Packages** tab of https://www.npmjs.com/package/$NAME (or \`npm stage list\` / \`npm stage approve <stage-id>\`)
|
|
85
|
+
- [ ] Publish the draft GitHub release, which creates the \`$VERSION\` git tag: $RELEASE_URL
|
|
86
|
+
- [ ] Close this issue
|
|
87
|
+
|
|
88
|
+
If you reject the staged version, delete the draft release and close this issue."
|
|
89
|
+
echo "$BODY" >> "$GITHUB_STEP_SUMMARY"
|
|
90
|
+
# the reminder issue notifies the person who merged (requires issues to be enabled on the repository)
|
|
91
|
+
gh issue create --assignee "$GITHUB_ACTOR" --title "Approve the npm release $VERSION" --body "$BODY" \
|
|
92
|
+
|| echo "::warning::Could not create the reminder issue (are issues enabled on the repository?)"
|
package/README.md
CHANGED
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
# wdio-test-context
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/@actionlogementservices/wdio-test-context)
|
|
4
|
+
|
|
3
5
|
A framework to add test context to webdriverio end to end tests.
|
|
4
6
|
|
|
5
|
-
- It uses JSON files (named
|
|
6
|
-
- It
|
|
7
|
+
- It uses JSON files (named datasets) and an optional javascript function to add dynamic content to your dataset.
|
|
8
|
+
- It generates a random test user (name, gender, disposable email address) for each run.
|
|
9
|
+
- It consumes a disposable mail service (maildrop, YOPmail or Guerrilla Mail) in order to check for new messages.
|
|
10
|
+
- It executes SQL queries on Microsoft SQL Server, publishes AMQP (RabbitMQ) messages and runs VTOM jobs.
|
|
7
11
|
- It provides pdf generation to enable document uploads.
|
|
8
12
|
- It provides an Azure DevOps webdriverio reporter.
|
|
9
|
-
- It generates a screenshot when
|
|
13
|
+
- It generates a screenshot when a test fails.
|
|
14
|
+
|
|
15
|
+
## Prerequisites
|
|
16
|
+
|
|
17
|
+
- Node.js 22 or later.
|
|
18
|
+
- webdriverio 9 (`@wdio/globals` is a peer dependency).
|
|
10
19
|
|
|
11
20
|
## Installation
|
|
12
21
|
|
|
@@ -16,11 +25,24 @@ A framework to add test context to webdriverio end to end tests.
|
|
|
16
25
|
npm install @actionlogementservices/wdio-test-context
|
|
17
26
|
```
|
|
18
27
|
|
|
19
|
-
- Create a `data` folder
|
|
28
|
+
- Create a `data` folder.
|
|
29
|
+
|
|
30
|
+
- Create JSON files in the `data` folder: these are your datasets. The content of the selected dataset is merged into the test context. For instance `data/test.json`:
|
|
20
31
|
|
|
21
|
-
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"company": { "name": "ACME" }
|
|
35
|
+
}
|
|
36
|
+
```
|
|
22
37
|
|
|
23
|
-
- Create a `data/test-context.d.ts
|
|
38
|
+
- Create a `data/test-context.d.ts` file: it adds intellisense when you use your test context data within the spec files.
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
export type TestContextModel = {
|
|
42
|
+
company: { name: string };
|
|
43
|
+
customDate: Date;
|
|
44
|
+
};
|
|
45
|
+
```
|
|
24
46
|
|
|
25
47
|
- Create a `data/test-context.js` file with the following code:
|
|
26
48
|
|
|
@@ -35,10 +57,12 @@ A framework to add test context to webdriverio end to end tests.
|
|
|
35
57
|
/** @type {TestContextModel & TestContext} */
|
|
36
58
|
export const testContext = /** @type {TestContextModel & TestContext} */ (
|
|
37
59
|
new TestContext()
|
|
38
|
-
.setDataGenerator(generateRandomData) //
|
|
39
|
-
.setDefaultDataset('test') // default dataset name when DATASET env var is not specified
|
|
60
|
+
.setDataGenerator(generateRandomData) // add your own dynamic data
|
|
61
|
+
.setDefaultDataset('test') // default dataset name when the DATASET env var is not specified
|
|
62
|
+
.setMailProvider('maildrop') // optional: 'maildrop' (default), 'yopmail' or 'guerrillamail'
|
|
63
|
+
.setSmtpServer({ host: 'smtp.domain.com', port: 25 }) // optional: only to send emails with mailService.sendMessage
|
|
40
64
|
.setEnvironment('local', {
|
|
41
|
-
//define parameters
|
|
65
|
+
// define the parameters of your environment
|
|
42
66
|
frontUrl: 'https://test.lvh.me',
|
|
43
67
|
backOfficeUrl: 'https://test-backoffice.lvh.me:4432',
|
|
44
68
|
sso: true
|
|
@@ -51,17 +75,22 @@ A framework to add test context to webdriverio end to end tests.
|
|
|
51
75
|
);
|
|
52
76
|
```
|
|
53
77
|
|
|
54
|
-
|
|
78
|
+
> **NOTES**
|
|
79
|
+
>
|
|
80
|
+
> - The first environment is the default one when the `TARGET_ENV` env var is not specified.
|
|
81
|
+
> - `setEnvironment` accepts a third `options` argument. With `{ perEnvironmentData: true }`, the datasets of this environment are read from `data/{environment_name}/` instead of `data/`.
|
|
82
|
+
> - The mail provider can be overridden with the `MAILPROVIDER` env var.
|
|
83
|
+
|
|
84
|
+
- Create `data/generate-random-data.js` to add dynamic data, with the following code:
|
|
55
85
|
|
|
56
86
|
```js
|
|
57
87
|
import { faker } from '@faker-js/faker/locale/fr';
|
|
58
|
-
import moment from 'moment';
|
|
59
88
|
|
|
60
89
|
/** @typedef {import('@actionlogementservices/wdio-test-context/types').DataGenerator} DataGenerator */
|
|
61
90
|
|
|
62
91
|
/** @type {DataGenerator} */
|
|
63
92
|
function generateRandomData(_context) {
|
|
64
|
-
//
|
|
93
|
+
// random date
|
|
65
94
|
const customDate = faker.date.between({ from: '1995-01-01', to: '2001-01-01' });
|
|
66
95
|
return {
|
|
67
96
|
customDate
|
|
@@ -71,32 +100,7 @@ A framework to add test context to webdriverio end to end tests.
|
|
|
71
100
|
export { generateRandomData };
|
|
72
101
|
```
|
|
73
102
|
|
|
74
|
-
- Configure
|
|
75
|
-
|
|
76
|
-
> **NOTE**
|
|
77
|
-
>
|
|
78
|
-
> - If you have secrets in your connection string, you need to
|
|
79
|
-
>
|
|
80
|
-
> - create a `.env.{your_environment_name}.local` file (for instance `.env.staging.local`) with your secret configuration parameters.
|
|
81
|
-
> - add the following entries to your `.gitignore`:
|
|
82
|
-
>
|
|
83
|
-
> `.env.local`<br>`.env.*.local`
|
|
84
|
-
|
|
85
|
-
```js
|
|
86
|
-
export const testContext = /** @type {TestContextModel & TestContext} */ (
|
|
87
|
-
new TestContext()
|
|
88
|
-
.setEnvironment(
|
|
89
|
-
'local',
|
|
90
|
-
{
|
|
91
|
-
frontUrl: 'https://test.lvh.me',
|
|
92
|
-
},
|
|
93
|
-
{
|
|
94
|
-
amqp: String.raw`amqps://${process.env.RMQ_USER}:${process.env.RMQ_PWD}@your-host:5671/your-vhost`,
|
|
95
|
-
}
|
|
96
|
-
)
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
- Configure webdriverio: in your wdio.conf.js:
|
|
103
|
+
- Configure webdriverio in your `wdio.conf.js`:
|
|
100
104
|
|
|
101
105
|
```js
|
|
102
106
|
import { AdoReporter, TestContextWorkerService } from '@actionlogementservices/wdio-test-context';
|
|
@@ -110,168 +114,225 @@ A framework to add test context to webdriverio end to end tests.
|
|
|
110
114
|
}
|
|
111
115
|
```
|
|
112
116
|
|
|
117
|
+
The `logLevel` option accepts `none`, `error` (default), `warn`, `info` or `debug`.
|
|
118
|
+
|
|
119
|
+
## Environment variables
|
|
120
|
+
|
|
121
|
+
| Name | Description |
|
|
122
|
+
| --------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
|
|
123
|
+
| `USER_PASSWORD` | **Required.** Password given to the generated test user (`testContext.user.password`). |
|
|
124
|
+
| `TARGET_ENV` | Name of the environment to use. Defaults to the first environment set. |
|
|
125
|
+
| `DATASET` | Name of the dataset (JSON file name without extension). Defaults to `setDefaultDataset` value. |
|
|
126
|
+
| `MAILPROVIDER` | Overrides the disposable mail provider (`maildrop`, `yopmail` or `guerrillamail`). |
|
|
127
|
+
| `EMAIL` | Reuses a previously recorded user (see [Test user](#test-user)) instead of generating a new one. |
|
|
128
|
+
| `DEBUG` | When set, raises the `BasePage` default wait timeout from 20 s to 1 hour, for step by step debugging. |
|
|
129
|
+
| `SYSTEM_ACCESSTOKEN` or `TOKEN` | Azure DevOps reporter: pipeline access token, or personal access token when `TOKEN` is used. |
|
|
130
|
+
| `SYSTEM_TEAMFOUNDATIONCOLLECTIONURI`, `SYSTEM_TEAMPROJECT`, `BUILD_BUILDID` | Azure DevOps reporter: set automatically by Azure Pipelines. |
|
|
131
|
+
|
|
113
132
|
## Notes on secrets
|
|
114
133
|
|
|
115
|
-
|
|
134
|
+
Environment variables are loaded with [dotenv-flow](https://www.npmjs.com/package/dotenv-flow), the environment name (`TARGET_ENV`) being the dotenv-flow `node_env`: `.env`, `.env.local`, `.env.{environment_name}` and `.env.{environment_name}.local` files are read.
|
|
116
135
|
|
|
117
|
-
|
|
136
|
+
If you have **secrets** in your parameter values, you need to:
|
|
137
|
+
|
|
138
|
+
- create a `.env.{environment_name}.local` file (for instance `.env.staging.local`) with your secret configuration parameters:
|
|
118
139
|
|
|
119
140
|
```sh
|
|
120
|
-
|
|
121
|
-
|
|
141
|
+
USER_PASSWORD="MySuperStrongPassword"
|
|
142
|
+
DB_PWD="MySuperStrongPassword"
|
|
143
|
+
RMQ_USER="MyUser"
|
|
144
|
+
RMQ_PWD="MySuperSafeSecret"
|
|
122
145
|
```
|
|
123
146
|
|
|
124
|
-
-
|
|
147
|
+
- reference the secret in `data/test-context.js` with a **template literal** (backticks, not quotes):
|
|
125
148
|
|
|
126
149
|
```js
|
|
127
150
|
.setEnvironment('staging', {
|
|
128
|
-
|
|
151
|
+
sqlBO: `Server=YourServer;Database=YourDatabase;User Id=YourUser;Password=${process.env.DB_PWD};`
|
|
152
|
+
})
|
|
129
153
|
```
|
|
130
154
|
|
|
131
|
-
-
|
|
155
|
+
- add the following entries to your `.gitignore` to prevent storing secret values in your repository:
|
|
132
156
|
|
|
133
157
|
```sh
|
|
134
158
|
.env.local
|
|
135
159
|
.env.*.local
|
|
160
|
+
# recorded test users, including their password
|
|
161
|
+
data/log/
|
|
162
|
+
# error screenshots
|
|
163
|
+
screenshot/
|
|
136
164
|
```
|
|
137
165
|
|
|
138
166
|
## Use the test context in the spec file
|
|
139
167
|
|
|
168
|
+
### Test user
|
|
169
|
+
|
|
170
|
+
- A random user is generated at each run and is available in `testContext.user` (`gender`, `firstname`, `lastname`, `email` on a domain of the mail provider, `password` from the `USER_PASSWORD` env var).
|
|
171
|
+
- Call `testContext.recordTestUser()` (for instance once the user account is created) to append the user to `data/log/users.json`. As it contains the passwords, a `.gitignore` is created in `data/log/` so that the folder is never committed.
|
|
172
|
+
- Run the tests with `EMAIL=<recorded email>` to reuse a recorded user of the current environment, or `EMAIL=last` to reuse the last recorded one.
|
|
173
|
+
|
|
140
174
|
### To access the mail service
|
|
141
175
|
|
|
142
|
-
-
|
|
176
|
+
- Use the `mailService` property:
|
|
143
177
|
|
|
144
178
|
```js
|
|
145
|
-
import { testContext } from '
|
|
179
|
+
import { testContext } from '../data/test-context.js';
|
|
146
180
|
|
|
147
|
-
describe(`[Inbox] - ${testContext.user.email}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
181
|
+
describe(`[Inbox] - ${testContext.user.email}`, () => {
|
|
182
|
+
it('receives a mail with a confirmation code', async () => {
|
|
183
|
+
await browser.url(testContext.getParameter('frontUrl')); // to retrieve parameters
|
|
184
|
+
...
|
|
151
185
|
const from = 'no-reply@domain.com';
|
|
152
|
-
const subject =
|
|
153
|
-
// to wait for email and to access test context data
|
|
186
|
+
const subject = 'Confirmation code';
|
|
187
|
+
// to wait for the email and to access the test context data
|
|
154
188
|
const content = await testContext.mailService.waitForMessage(testContext.user.email, from, subject);
|
|
155
|
-
confirmationCode = getConfirmationCode(content);
|
|
189
|
+
const confirmationCode = getConfirmationCode(content); // your own function
|
|
156
190
|
expect(confirmationCode).toContain('123456');
|
|
157
191
|
});
|
|
158
|
-
}
|
|
192
|
+
});
|
|
159
193
|
```
|
|
160
194
|
|
|
195
|
+
- To send an email, define the smtp server with `setSmtpServer({ host, port, secure })` in `data/test-context.js`, then call `await testContext.mailService.sendMessage(from, to, subject, html)`.
|
|
196
|
+
|
|
161
197
|
### To execute SQL queries
|
|
162
198
|
|
|
163
|
-
- You need to define a **parameter** in `
|
|
199
|
+
- You need to define a **parameter** in `data/test-context.js` that contains the **sql connection string**. In the following example it is named `sqlBO`.
|
|
164
200
|
|
|
165
201
|
```js
|
|
166
202
|
export const testContext = /** @type {TestContextModel & TestContext} */ (
|
|
167
203
|
new TestContext()
|
|
168
|
-
.setEnvironment(
|
|
169
|
-
'
|
|
170
|
-
{
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
)
|
|
204
|
+
.setEnvironment('local', {
|
|
205
|
+
frontUrl: 'https://test.lvh.me',
|
|
206
|
+
sqlBO: String.raw`Driver={ODBC Driver 17 for SQL Server};Server=(localdb)\MSSQLLocalDB;Database=YourDatabase;Trusted_Connection=yes;`
|
|
207
|
+
})
|
|
208
|
+
.initialize()
|
|
209
|
+
);
|
|
175
210
|
```
|
|
176
211
|
|
|
177
212
|
> **NOTES on SQL connection string**
|
|
178
213
|
>
|
|
179
|
-
> - If you plan to use `(localdb)` or **Windows authentication
|
|
180
|
-
> - For Microsoft
|
|
181
|
-
> - You **
|
|
182
|
-
> - In the previous example, `(localdb)` is used with the `ODBC Driver 17 for SQL Server
|
|
214
|
+
> - If you plan to use `(localdb)` or **Windows authentication**, this requires an **ODBC driver** with a specific connection string syntax: `Driver={...}`. You also need to check which Microsoft SQL Server **ODBC driver** is installed on your system with the `ODBC Data Sources` control panel.
|
|
215
|
+
> - For a Microsoft SQL Server instance without Windows authentication, there is no need to use an ODBC driver. In such case remove the `Driver={...}` portion of the connection string.
|
|
216
|
+
> - You **cannot** mix both syntaxes in the same environment.
|
|
217
|
+
> - In the previous example, `(localdb)` is used with the `ODBC Driver 17 for SQL Server`, which is installed.
|
|
183
218
|
|
|
184
|
-
- Somewhere in
|
|
219
|
+
- Somewhere in your spec files:
|
|
185
220
|
|
|
186
221
|
```js
|
|
187
|
-
import { testContext } from '
|
|
222
|
+
import { testContext } from '../data/test-context.js';
|
|
188
223
|
|
|
189
|
-
describe(
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
224
|
+
describe('Before starting the test, we', () => {
|
|
225
|
+
it('check the sql version', async () => {
|
|
226
|
+
const result = await testContext.executeSql('sqlBO', sql => sql.query('SELECT @@Version'));
|
|
227
|
+
...
|
|
228
|
+
});
|
|
194
229
|
});
|
|
195
230
|
```
|
|
196
231
|
|
|
197
|
-
### To publish AMQP
|
|
232
|
+
### To publish AMQP messages
|
|
198
233
|
|
|
199
|
-
- You need to define a **parameter** in `
|
|
234
|
+
- You need to define a **parameter** in `data/test-context.js` that contains the **amqp connection string**. In the following example it is named `amqpBO`.
|
|
200
235
|
|
|
201
236
|
```js
|
|
202
237
|
export const testContext = /** @type {TestContextModel & TestContext} */ (
|
|
203
238
|
new TestContext()
|
|
204
|
-
.setEnvironment(
|
|
205
|
-
'
|
|
206
|
-
{
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
)
|
|
239
|
+
.setEnvironment('local', {
|
|
240
|
+
frontUrl: 'https://test.lvh.me',
|
|
241
|
+
amqpBO: `amqps://${process.env.RMQ_USER}:${process.env.RMQ_PWD}@your-host:5671/your-vhost`
|
|
242
|
+
})
|
|
243
|
+
.initialize()
|
|
244
|
+
);
|
|
211
245
|
```
|
|
212
246
|
|
|
213
|
-
- Somewhere in
|
|
247
|
+
- Somewhere in your spec files:
|
|
214
248
|
|
|
215
249
|
```js
|
|
216
|
-
import { testContext } from '
|
|
217
|
-
|
|
218
|
-
describe(
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
250
|
+
import { testContext } from '../data/test-context.js';
|
|
251
|
+
|
|
252
|
+
describe('Before starting the test, we', () => {
|
|
253
|
+
it('send an amqp message', async () => {
|
|
254
|
+
const result = await testContext.publishMessage(
|
|
255
|
+
'amqpBO',
|
|
256
|
+
'exchange', // your exchange name
|
|
257
|
+
'routingKey', // your routing key
|
|
258
|
+
'type', // your message type
|
|
259
|
+
{ test: true }, // your message payload
|
|
260
|
+
testContext.user.email // correlation id to track your message
|
|
261
|
+
);
|
|
262
|
+
expect(result).toBe(true);
|
|
263
|
+
});
|
|
229
264
|
});
|
|
230
265
|
```
|
|
231
266
|
|
|
232
267
|
### To run a VTOM job
|
|
233
268
|
|
|
234
|
-
- You need to define a **parameter** in `
|
|
269
|
+
- You need to define a **parameter** in `data/test-context.js` that contains the **VTOM configuration**. In the following example it is named `vtomBO`.
|
|
235
270
|
|
|
236
271
|
```js
|
|
237
272
|
export const testContext = /** @type {TestContextModel & TestContext} */ (
|
|
238
273
|
new TestContext()
|
|
239
|
-
.setEnvironment(
|
|
240
|
-
'
|
|
241
|
-
{
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
apiKey: 'abcdef',
|
|
246
|
-
environement: 'DEV'
|
|
247
|
-
},
|
|
274
|
+
.setEnvironment('local', {
|
|
275
|
+
frontUrl: 'https://test.lvh.me',
|
|
276
|
+
vtomBO: {
|
|
277
|
+
url: 'https://...',
|
|
278
|
+
apiKey: process.env.VTOM_API_KEY,
|
|
279
|
+
environment: 'DEV'
|
|
248
280
|
}
|
|
249
|
-
)
|
|
281
|
+
})
|
|
282
|
+
.initialize()
|
|
283
|
+
);
|
|
250
284
|
```
|
|
251
285
|
|
|
252
|
-
- Somewhere in
|
|
286
|
+
- Somewhere in your spec files (the job is stopped, started, then its status is checked every 5 s for 100 s at most):
|
|
253
287
|
|
|
254
288
|
```js
|
|
255
|
-
import { testContext } from '
|
|
256
|
-
|
|
257
|
-
describe(`Before starting the test, we`, () => {
|
|
289
|
+
import { testContext } from '../data/test-context.js';
|
|
258
290
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
291
|
+
describe('Before starting the test, we', () => {
|
|
292
|
+
it('run a vtom job', async () => {
|
|
293
|
+
const result = await testContext.runVtomJob('vtomBO', 'app', 'job');
|
|
294
|
+
expect(result).toBe(true);
|
|
295
|
+
});
|
|
262
296
|
});
|
|
263
297
|
```
|
|
264
298
|
|
|
265
|
-
|
|
299
|
+
### Other helpers
|
|
300
|
+
|
|
301
|
+
- `BasePage`: base class for your page objects (tab management, wait helpers…).
|
|
302
|
+
- `pdfGenerator`: `generatePdf(filename, testContext)` creates a pdf file to upload and returns its path, `deletePdf(filepath)` deletes it.
|
|
303
|
+
|
|
304
|
+
## Corporate CA certificates
|
|
305
|
+
|
|
306
|
+
If your environments use certificates issued by a private certificate authority, put the CA certificates (`*.crt` files) in a `ca` folder at the root of your test project: they are trusted when the test context is initialized.
|
|
266
307
|
|
|
267
|
-
|
|
308
|
+
## Run your tests
|
|
309
|
+
|
|
310
|
+
- Specify the dataset with the `DATASET` env var and the environment with the `TARGET_ENV` env var:
|
|
268
311
|
|
|
269
312
|
```sh
|
|
270
313
|
TARGET_ENV=staging DATASET=full-scenario npx wdio
|
|
271
|
-
or
|
|
314
|
+
# or
|
|
272
315
|
TARGET_ENV=staging DATASET=full-scenario npm test
|
|
273
316
|
```
|
|
274
317
|
|
|
318
|
+
- When a test fails, a screenshot and a text file with the error are saved in `screenshot/{environment_name}/{dataset}/`.
|
|
319
|
+
|
|
320
|
+
- With the `AdoReporter`, the results are published as an Azure DevOps test run at the end of the run. In an Azure Pipelines job, map the access token to the script: `env: { SYSTEM_ACCESSTOKEN: $(System.AccessToken) }`.
|
|
321
|
+
|
|
275
322
|
## Code documentation
|
|
276
323
|
|
|
277
|
-
- Code is [documented here](./doc/toc.md).
|
|
324
|
+
- Code is [documented here](./doc/toc.md) (generated with `npm run doc`).
|
|
325
|
+
|
|
326
|
+
## Release
|
|
327
|
+
|
|
328
|
+
The package is published on npm by the `Release` GitHub workflow, with npm trusted publishing (no npm token is stored in GitHub):
|
|
329
|
+
|
|
330
|
+
1. In your pull request, bump the version with `npm run bump -- <major|minor|patch|prerelease>` (updates `package.json` and `package-lock.json` only).
|
|
331
|
+
1. When the pull request is merged on `main`, the workflow runs the linter and **stages** the new version on npm.
|
|
332
|
+
Prerelease versions (e.g. `2.1.0-0`) are staged with the `next` dist-tag.
|
|
333
|
+
1. The workflow creates a draft GitHub release and an issue assigned to the person who merged, as a reminder to:
|
|
334
|
+
- approve the staged version in the **Staged Packages** tab of the [npm package](https://www.npmjs.com/package/@actionlogementservices/wdio-test-context) (2FA required),
|
|
335
|
+
- publish the draft GitHub release, which creates the git tag,
|
|
336
|
+
- close the issue.
|
|
337
|
+
|
|
338
|
+
If the staged version is rejected, delete the draft release and close the issue.
|
package/doc/src_test-context.md
CHANGED
|
@@ -82,6 +82,19 @@ __*return*__ | [TestContext](src_test-context.md) | **
|
|
|
82
82
|
|
|
83
83
|
---
|
|
84
84
|
|
|
85
|
+
### `setSmtpServer(smtpServer) ► TestContext`
|
|
86
|
+
|
|
87
|
+

|
|
88
|
+
|
|
89
|
+
Defines the **smtp server** used by the mail service to send emails.
|
|
90
|
+
|
|
91
|
+
Parameters | Type | Description
|
|
92
|
+
--- | --- | ---
|
|
93
|
+
__smtpServer__ | `SmtpServer` | *smtp server*
|
|
94
|
+
__*return*__ | [TestContext](src_test-context.md) | **
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
85
98
|
### `setLogLevel(level)`
|
|
86
99
|
|
|
87
100
|

|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@actionlogementservices/wdio-test-context",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "A framework to add test context to webdriverio end to end tests.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -12,7 +12,9 @@
|
|
|
12
12
|
"scripts": {
|
|
13
13
|
"prettier": "prettier \"./src/**/*.js\" --write",
|
|
14
14
|
"eslint": "eslint \"./src/**/*.js\"",
|
|
15
|
-
"doc": "jsdoc -c ./.jsdoc.json"
|
|
15
|
+
"doc": "jsdoc -c ./.jsdoc.json",
|
|
16
|
+
"test": "npm run eslint",
|
|
17
|
+
"bump": "npm version --no-git-tag-version"
|
|
16
18
|
},
|
|
17
19
|
"repository": {
|
|
18
20
|
"type": "git",
|
|
@@ -44,6 +46,7 @@
|
|
|
44
46
|
},
|
|
45
47
|
"dependencies": {
|
|
46
48
|
"@faker-js/faker": "^10.4.0",
|
|
49
|
+
"@wdio/reporter": "^9.27.1",
|
|
47
50
|
"amqplib": "^2.0.1",
|
|
48
51
|
"axios": "^1.16.1",
|
|
49
52
|
"azure-devops-node-api": "^15.1.2",
|
|
@@ -59,7 +62,8 @@
|
|
|
59
62
|
"syswide-cas": "^5.3.0"
|
|
60
63
|
},
|
|
61
64
|
"peerDependencies": {
|
|
62
|
-
"@wdio/globals": "^9.27.1"
|
|
65
|
+
"@wdio/globals": "^9.27.1",
|
|
66
|
+
"webdriverio": "^9.27.1"
|
|
63
67
|
},
|
|
64
68
|
"engines": {
|
|
65
69
|
"node": ">=22"
|
package/src/mail-service.js
CHANGED
|
@@ -11,16 +11,20 @@ import * as provider from './page-objects/index.js';
|
|
|
11
11
|
import { logger } from './logger.js';
|
|
12
12
|
|
|
13
13
|
/** @typedef {import('./types.js').MailProvider} MailProvider */
|
|
14
|
+
/** @typedef {import('./types.js').SmtpServer} SmtpServer */
|
|
14
15
|
|
|
15
16
|
export class MailService {
|
|
16
17
|
/** disposable email provider @type {MailProvider} */ provider;
|
|
18
|
+
/** smtp server used to send emails @type {SmtpServer | undefined} */ smtpServer;
|
|
17
19
|
|
|
18
20
|
/**
|
|
19
21
|
* Implements a service that sends and retrieves emails with a given provider.
|
|
20
22
|
* @param {import('./types.js').MailProviderName} providerName disposable email provider name
|
|
23
|
+
* @param {SmtpServer} [smtpServer] smtp server used to send emails
|
|
21
24
|
*/
|
|
22
|
-
constructor(providerName) {
|
|
25
|
+
constructor(providerName, smtpServer) {
|
|
23
26
|
this.provider = provider[providerName].default;
|
|
27
|
+
this.smtpServer = smtpServer;
|
|
24
28
|
}
|
|
25
29
|
|
|
26
30
|
/**
|
|
@@ -48,7 +52,10 @@ export class MailService {
|
|
|
48
52
|
* @returns {Promise<void>}
|
|
49
53
|
*/
|
|
50
54
|
async sendMessage(from, to, subject, html) {
|
|
51
|
-
|
|
55
|
+
if (!this.smtpServer?.host)
|
|
56
|
+
throw new Error('No smtp server defined! Call setSmtpServer on the test context to send emails.');
|
|
57
|
+
const { host, port, secure } = this.smtpServer;
|
|
58
|
+
const transporter = nodemailer.createTransport({ host, port: port ?? 25, secure: secure ?? false });
|
|
52
59
|
await transporter.sendMail({ from, to, subject, html });
|
|
53
60
|
}
|
|
54
61
|
|
package/src/test-context.js
CHANGED
|
@@ -24,6 +24,7 @@ import { runVtomJob } from './vtom-client.js';
|
|
|
24
24
|
/** @typedef {import('./types.d.ts').MailProviderName} MailProviderName */
|
|
25
25
|
/** @typedef {import('./types.d.ts').DataGenerator} DataGenerator */
|
|
26
26
|
/** @typedef {import('./types.d.ts').LogLevel} LogLevel */
|
|
27
|
+
/** @typedef {import('./types.d.ts').SmtpServer} SmtpServer */
|
|
27
28
|
|
|
28
29
|
export const defaultDatasetName = 'default';
|
|
29
30
|
const defaultRecordedUsersFilename = 'users.json';
|
|
@@ -39,6 +40,7 @@ export class TestContext {
|
|
|
39
40
|
/** Test environments @private @type {Map<string, TestEnvironmentConfiguration>} */
|
|
40
41
|
_environments = new Map();
|
|
41
42
|
/** Mail provider name @private @type {MailProviderName} */ _mailProviderName;
|
|
43
|
+
/** Smtp server @private @type {SmtpServer} */ _smtpServer;
|
|
42
44
|
/** data folder @private @type {string} */ _dataFolder;
|
|
43
45
|
/** log folder @private @type {string} */ _logFolder;
|
|
44
46
|
/** data folder @private @type {string} */ _recordedUsersFilepath;
|
|
@@ -90,6 +92,16 @@ export class TestContext {
|
|
|
90
92
|
return this;
|
|
91
93
|
}
|
|
92
94
|
|
|
95
|
+
/**
|
|
96
|
+
* Defines the **smtp server** used by the mail service to send emails.
|
|
97
|
+
* @param {SmtpServer} smtpServer smtp server
|
|
98
|
+
* @returns {TestContext}
|
|
99
|
+
*/
|
|
100
|
+
setSmtpServer(smtpServer) {
|
|
101
|
+
this._smtpServer = smtpServer;
|
|
102
|
+
return this;
|
|
103
|
+
}
|
|
104
|
+
|
|
93
105
|
/**
|
|
94
106
|
* Sets the logging level.
|
|
95
107
|
* @param {LogLevel} level logging level
|
|
@@ -109,7 +121,7 @@ export class TestContext {
|
|
|
109
121
|
node_env: process.env.TARGET_ENV || this.defaultEnvironmentName
|
|
110
122
|
});
|
|
111
123
|
// email provider
|
|
112
|
-
this.mailService = new MailService(this._mailProviderName ?? defaultMailProviderName);
|
|
124
|
+
this.mailService = new MailService(this._mailProviderName ?? defaultMailProviderName, this._smtpServer);
|
|
113
125
|
// data folder
|
|
114
126
|
this._dataFolder =
|
|
115
127
|
(this.environment.options.perEnvironmentData ?? false)
|
|
@@ -123,7 +135,7 @@ export class TestContext {
|
|
|
123
135
|
// generate random user
|
|
124
136
|
const user = this._generateRandomUser(this.mailService);
|
|
125
137
|
// generate custom data
|
|
126
|
-
const customData = this._dataGenerator(this);
|
|
138
|
+
const customData = this._dataGenerator?.(this) ?? {};
|
|
127
139
|
// load dataset
|
|
128
140
|
const data = JSON.parse(fs.readFileSync(datasetFilepath, 'utf8'));
|
|
129
141
|
// add dataset to test context
|
|
@@ -255,6 +267,9 @@ export class TestContext {
|
|
|
255
267
|
fs.mkdirSync(this._logFolder, { recursive: true });
|
|
256
268
|
fs.writeFileSync(this._recordedUsersFilepath, JSON.stringify([]));
|
|
257
269
|
}
|
|
270
|
+
// recorded users contain passwords: never commit the log folder
|
|
271
|
+
const gitignoreFilepath = path.join(this._logFolder, '.gitignore');
|
|
272
|
+
if (!fs.existsSync(gitignoreFilepath)) fs.writeFileSync(gitignoreFilepath, '*\n');
|
|
258
273
|
const { firstname, lastname, gender, email, password } = this.user;
|
|
259
274
|
const environment = this.environmentName;
|
|
260
275
|
const dataset = this.dataset;
|
package/src/types.d.ts
CHANGED
|
@@ -23,6 +23,13 @@ export type MailProvider = {
|
|
|
23
23
|
/** wdio logic to retieve email content */ getMessageContent: (index: number) => Promise<string>;
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
+
/** Smtp server used to send emails */
|
|
27
|
+
export type SmtpServer = {
|
|
28
|
+
/** host name */ host: string;
|
|
29
|
+
/** port (25 by default) */ port?: number;
|
|
30
|
+
/** use TLS from the start of the connection (false by default) */ secure?: boolean;
|
|
31
|
+
};
|
|
32
|
+
|
|
26
33
|
/** Randomly auto generated user */
|
|
27
34
|
export type TestUser = {
|
|
28
35
|
/** last name */ lastname: string;
|
|
@@ -76,7 +76,7 @@ export default class TestContextWorkerService {
|
|
|
76
76
|
const errorLines = stack?.split('at ') ?? [];
|
|
77
77
|
const errorFile = errorLines
|
|
78
78
|
.find((/** @type {string} */ l) => l.toLowerCase().includes('e2e.js'))
|
|
79
|
-
|
|
79
|
+
?.trim();
|
|
80
80
|
const fileContent = `Test : ${test.fullName}\nErreur : ${message}${
|
|
81
81
|
errorFile ? '\nEmplacement : ' + errorFile : ''
|
|
82
82
|
}`;
|