wdio-test-ledger-service 9.0.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.
@@ -0,0 +1,47 @@
1
+ name: Publish to NPM
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ release_type:
7
+ description: 'Release type'
8
+ required: true
9
+ type: 'choice'
10
+ default: 'patch'
11
+ options:
12
+ - 'patch'
13
+ - 'minor'
14
+ - 'major'
15
+ env:
16
+ NPM_ACCESS_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }}
17
+
18
+ jobs:
19
+ release:
20
+ runs-on: ubuntu-latest
21
+ steps:
22
+ - uses: actions/checkout@v3
23
+ with:
24
+ ref: 'master'
25
+ fetch-depth: 0
26
+ - uses: actions/setup-node@v3
27
+ with:
28
+ node-version: '20'
29
+
30
+ - name: NPM Setup
31
+ run: |
32
+ npm set registry "https://registry.npmjs.org/"
33
+ npm set //registry.npmjs.org/:_authToken $NPM_ACCESS_TOKEN
34
+ npm whoami
35
+
36
+ - name: Git Setup
37
+ run: |
38
+ git config --global user.email "wpbrock@gmail.com"
39
+ git config --global user.name "Test Reporter Service Release Bot"
40
+
41
+ - name: Install Dependencies
42
+ run: npm ci
43
+
44
+ - run: npm run release -- ${{ github.event.inputs.release_type }}
45
+ env:
46
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
47
+ NPM_ACCESS_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }}
package/README.md ADDED
@@ -0,0 +1,49 @@
1
+ # WebdriverIO Test Reporter Service
2
+
3
+ WDIO service that takes results from [wdio-test-reporter](https://github.com/WillBrock/wdio-test-reporter) and uploads them to [testreporter.io](https://testreporter.io)
4
+
5
+ testreporter.io stores and tracks all test runs with extensive details about each test. This gives you a historical overivew of how your tests are running.
6
+
7
+ Setup is very simple. All you need to do is add the service and reporter to the `services` and `reporters` arrays in your wdio.conf.js file.
8
+
9
+ ## Install the service
10
+
11
+ ```
12
+ npm install wdio-test-reporter-service
13
+ ```
14
+
15
+ ## Add the service to the services array in wdio.conf.js
16
+
17
+ ```
18
+ services: [['test-reporter', {
19
+ reporterOutputDir : `./testreporter`, // This must match the outputDir from the wdio-test-reporter
20
+ apiUrl : `app-api.testreporter.io`, // Defaults to app-api.testreporter.io if none is set
21
+ username : `jenkins@foobar.com`, // app.testreporter.io username
22
+ apiToken : `12345`, // Found in the app.testreporter.io under your proifle section
23
+ projectId : 123, // Only needed if using more than one project
24
+ appVersion : `2.8.10`, // The code version can also be set here
25
+ enableFlaky : 1, // Will mark tests as flaky if it detects them based on previous runs
26
+ }]],
27
+ ```
28
+
29
+ You will create a custom `username` and `apiToken` in the UI under Settings -> Profile -> API Keys
30
+
31
+ ## Add the wdio-test-reporter to the reporters array in wdio.conf.js
32
+
33
+ ```
34
+ npm install wdio-test-reporter
35
+ ```
36
+
37
+ ```
38
+ reporters : [[`test`, {
39
+ outputDir : `./testreporter`
40
+ }]]
41
+ ```
42
+
43
+ ## Environment variables
44
+
45
+ Environment variables can be set when running tests that the server will use to add to the results
46
+
47
+ * `RUN_TITLE` - Title of the test run. This might be something like a Jira issue key. Defaults to a timestamp if not specified
48
+ * `RUN_UUID` - UUID which can be used to directly link to the test run results. e.g. https://app.testreporter.io/runs/c26b23d8-eb9f-4ff4-a884-5cb9f3d3aba5<uuid>
49
+ * `APP_VERSION` - Set the version of app this test run ran against
package/RELEASE.md ADDED
@@ -0,0 +1 @@
1
+ https://www.npmjs.com/package/np
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "wdio-test-ledger-service",
3
+ "version": "9.0.0",
4
+ "description": "WebdriverIO server to send reporter data to testledger.dev",
5
+ "type": "module",
6
+ "main": "src/index.js",
7
+ "engines": {
8
+ "node": "20.x"
9
+ },
10
+ "scripts": {
11
+ "release": "release-it --github.release"
12
+ },
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/WillBrock/wdio-test-ledger-service.git"
16
+ },
17
+ "keywords": [
18
+ "WebdriverIO",
19
+ "tests",
20
+ "test",
21
+ "e2e",
22
+ "wdio",
23
+ "testing",
24
+ "reporter",
25
+ "service"
26
+ ],
27
+ "author": "Will Brock",
28
+ "license": "ISC",
29
+ "bugs": {
30
+ "url": "https://github.com/WillBrock/wdio-test-ledger-service/issues"
31
+ },
32
+ "homepage": "https://github.com/WillBrock/wdio-test-ledger-service#readme",
33
+ "dependencies": {
34
+ "btoa": "^1.2.1",
35
+ "fs-extra": "^11.2.0",
36
+ "node-fetch": "^3.3.2"
37
+ },
38
+ "devDependencies": {
39
+ "release-it": "^17.0.1"
40
+ }
41
+ }
package/src/index.js ADDED
@@ -0,0 +1,206 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import btoa from 'btoa';
4
+ import { SevereServiceError } from 'webdriverio';
5
+
6
+ const api_url = `https://app-api.testreporter.io`;
7
+
8
+ class TestReporterLauncher {
9
+ constructor(options) {
10
+ this.options = options;
11
+
12
+ if(!this.options.reporterOutputDir) {
13
+ throw new SevereServiceError(`No reporterOutputDir specified`)
14
+ }
15
+
16
+ if(!this.options.username) {
17
+ throw new SevereServiceError(`No username specified`)
18
+ }
19
+
20
+ if(!this.options.apiToken) {
21
+ throw new SevereServiceError(`No apiToken specified`)
22
+ }
23
+ }
24
+
25
+ onPrepare() {
26
+ fs.emptyDirSync(this.options.reporterOutputDir);
27
+
28
+ fs.writeFileSync(`${this.options.reporterOutputDir}/trio-onPrepare.txt`, `onPrepare called`, { encoding : `utf-8` });
29
+
30
+ this.start = new Date();
31
+ }
32
+
33
+ async onComplete(exit_code, config) {
34
+ const data = this.buildData(config);
35
+
36
+ try {
37
+ const tmp = await this.post(data);
38
+ fs.writeFileSync(`${this.options.reporterOutputDir}/trio-onComplete-post.txt`, `onComplete-post`, { encoding : `utf-8` });
39
+ }
40
+ catch(e) {
41
+ fs.writeFileSync(`${this.options.reporterOutputDir}/trio-post-error.txt`, e.message, { encoding : `utf-8` });
42
+ }
43
+ }
44
+
45
+ buildData(config) {
46
+ const directory = path.resolve(this.options.reporterOutputDir);
47
+ const files = fs.readdirSync(directory);
48
+ const suite_data = {};
49
+ const all_errors = {};
50
+ const all_hooks = {};
51
+
52
+
53
+ fs.writeFileSync(`${this.options.reporterOutputDir}/trio-skip-passed.txt`, `Value of SKIP_PASSED_UPLOADS: ${process.env.SKIP_PASSED_UPLOADS}`, { encoding : `utf-8` });
54
+ fs.writeFileSync(`${this.options.reporterOutputDir}/trio-buildData.txt`, `Starting buildData`, { encoding : `utf-8` });
55
+
56
+ const data = {
57
+ project_id : this.options.projectId,
58
+ uuid : process.env.RUN_UUID,
59
+ // This is a way to group runs together, for example if you're using sharding
60
+ group_uuid : process.env.GROUP_UUID,
61
+ main_run : Number(process.env.MAIN_RUN),
62
+ title : process.env.RUN_TITLE || this.start,
63
+ // Site the tests were ran on
64
+ site : process.env.SITE,
65
+ build_url : process.env.BUILD_URL,
66
+ run_date : this.start.toISOString(),
67
+ duration : new Date().getTime() - this.start.getTime(),
68
+ version : process.env.APP_VERSION || process.env.CODE_VERSION || this.options.appVersion || `0.0.1`,
69
+ suites_ran : config.suite ? config.suite.join(`, `) : (config.multiRun || config.repeat ? `RepeatRun` : ``),
70
+ issue_user : process.env.ISSUE_USER ?? null,
71
+ issue_summary : process.env.ISSUE_SUMMARY ?? null,
72
+ enable_flaky : Number(process.env.ENABLE_FLAKY) || this.options.enableFlaky || 0,
73
+ passed : 1,
74
+ failed : 0,
75
+ suites : [],
76
+ };
77
+
78
+ for(const file of files) {
79
+ if(!file.match(/test-reporter.log/)) {
80
+ continue;
81
+ }
82
+
83
+ let tmp = false;
84
+ try {
85
+ const filepath = `${directory}/${file}`;
86
+ tmp = fs.readFileSync(filepath, { encoding : `utf8` });
87
+ }
88
+ catch(e) {
89
+ fs.writeFileSync(`${this.options.reporterOutputDir}/trio-readfile-error.txt`, e.message, { encoding : `utf-8` });
90
+ }
91
+
92
+ const identifier = file.match(/wdio-(\d+-\d+)-/)[1];
93
+
94
+ if(!tmp) {
95
+ continue;
96
+ }
97
+
98
+ const content = JSON.parse(tmp);
99
+ const suite_key = btoa(`${identifier}:${content.spec_file}:${content.capabilities}:${content.title}`);
100
+
101
+ if(content.passed && Number(process.env.SKIP_PASSED_UPLOADS) === 1) {
102
+ continue;
103
+ }
104
+
105
+ suite_data[suite_key] = {
106
+ title : content.title,
107
+ spec_file : content.spec_file,
108
+ filepath : content.filepath,
109
+ capabilities : content.capabilities,
110
+ duration : content.duration,
111
+ retries : content.retries || 0,
112
+ passed : content.passed,
113
+ failed : content.failed,
114
+ skipped : content.skipped,
115
+ start : content.start,
116
+ tests : [],
117
+ };
118
+
119
+ for(const test of content.tests) {
120
+ const hook = test.type === `hook`;
121
+ const test_key = btoa(`${identifier}:${content.spec_file}:${content.capabilities}:${content.title}:${test.title}`);
122
+
123
+ if(!all_errors[test_key]) {
124
+ all_errors[test_key] = [];
125
+ }
126
+
127
+ // This will make sure we have stored errors from the same test if it has retried
128
+ all_errors[test_key] = [...all_errors[test_key], ...test.errors];
129
+
130
+ const test_data = {
131
+ title : test.title,
132
+ duration : test.duration,
133
+ passed : test.passed,
134
+ retries : test.retries,
135
+ failed : test.failed,
136
+ skipped : test.skipped,
137
+ errors : all_errors[test_key],
138
+ };
139
+
140
+ suite_data[suite_key].tests.push(test_data);
141
+
142
+ if(hook && !all_hooks[suite_key]) {
143
+ all_hooks[suite_key] = [];
144
+ }
145
+
146
+ if(hook) {
147
+ all_hooks[suite_key].push(test_data)
148
+ }
149
+ }
150
+
151
+ if(all_hooks[suite_key]) {
152
+ suite_data[suite_key].tests = [...suite_data[suite_key].tests, ...all_hooks[suite_key]];
153
+ }
154
+ }
155
+
156
+ const suites = Object.values(suite_data);
157
+ for(const suite of suites) {
158
+ if(!suite.failed) {
159
+ continue;
160
+ }
161
+
162
+ data.failed = 1;
163
+ data.passed = 0;
164
+
165
+ break;
166
+ }
167
+
168
+ fs.writeFileSync(`${this.options.reporterOutputDir}/trio-end-buildData.txt`, `Ending buildData`, { encoding : `utf-8` });
169
+
170
+ data.suites = suites;
171
+
172
+ return data;
173
+ }
174
+
175
+ post(data) {
176
+ return fetch(this.getApiRoute(), {
177
+ method : `POST`,
178
+ headers : {
179
+ 'Content-Type' : `application/json`,
180
+ 'Authorization' : `Basic ${this.getAuthToken()}`,
181
+ },
182
+ body : JSON.stringify(data),
183
+ });
184
+ }
185
+
186
+ getApiUrl() {
187
+ return `https://${this.options.apiUrl?.replace(`https://`, ``) || api_url}`;
188
+ }
189
+
190
+ getApiRoute() {
191
+ return [
192
+ this.getApiUrl(),
193
+ `/runs`,
194
+ ].join(``);
195
+ }
196
+
197
+ getAuthToken() {
198
+ return btoa([
199
+ this.options.username,
200
+ this.options.apiToken,
201
+ ].join(`:`));
202
+ }
203
+ }
204
+
205
+ export default class TestReporterService {};
206
+ export const launcher = TestReporterLauncher;