pa11y-ci-reporter-runner 0.7.0 → 1.0.1
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/README.md +43 -26
- package/index.js +63 -7
- package/lib/pa11yci-machine.js +10 -0
- package/lib/pa11yci-service.js +184 -65
- package/lib/runner-states.js +2 -0
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Pa11y CI Reporter Runner
|
|
2
2
|
|
|
3
|
-
Pa11y CI Reporter Runner is designed to facilitate testing of [Pa11y CI reporters](https://github.com/pa11y/pa11y-ci#write-a-custom-reporter). Given a Pa11y CI JSON results file and optional configuration it
|
|
3
|
+
Pa11y CI Reporter Runner is designed to facilitate testing of [Pa11y CI reporters](https://github.com/pa11y/pa11y-ci#write-a-custom-reporter). Given a Pa11y CI JSON results file and optional configuration it performs the Pa11y CI calls to the reporter, including proper transformation of results and configuration data. Functionally, it's an emulation of the Pa11y CI side of the reporter interface with explicit control over execution of each step.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -12,7 +12,7 @@ npm install pa11y-ci-reporter-runner
|
|
|
12
12
|
|
|
13
13
|
## Usage
|
|
14
14
|
|
|
15
|
-
Pa11y CI Reporter Runner exports a factory function that creates a reporter runner. This function
|
|
15
|
+
Pa11y CI Reporter Runner exports a factory function `createRunner` that creates a reporter runner. This function takes four arguments:
|
|
16
16
|
|
|
17
17
|
- `resultsFileName`: Path to a Pa11y CI JSON results file.
|
|
18
18
|
- `reporterName`: Name of the reporter to execute. Can be an npm module (e.g. `pa11y-ci-reporter-html`) or a path to a reporter file.
|
|
@@ -24,6 +24,8 @@ Pa11y CI Reporter Runner exports a factory function that creates a reporter runn
|
|
|
24
24
|
Pa11y CI Reporter Runner emulates calls from Pa11y CI to the given reporter for the given results file. There are five distinct states for the runner, as shown below:
|
|
25
25
|
|
|
26
26
|
```mermaid
|
|
27
|
+
%% It's unfortunate that mermaid charts don't render on npmjs.com,
|
|
28
|
+
%% see the README in the repository to view the flowchart
|
|
27
29
|
flowchart LR;
|
|
28
30
|
init-->beforeAll;
|
|
29
31
|
beforeAll-->beginUrl;
|
|
@@ -40,16 +42,27 @@ flowchart LR;
|
|
|
40
42
|
|
|
41
43
|
When calling reporter functions, the runner transforms the Pa11y CI results and configuration data to provide the appropriate arguments. For example, the `results` function is called with the results as returned from Pa11y (slightly different than those returned from Pa11y CI) and the consolidated configuration for the analysis of that URL.
|
|
42
44
|
|
|
45
|
+
The runner state can be obtained via the following runner functions:
|
|
46
|
+
|
|
47
|
+
- `getCurrentState()`: The current state of the runner.
|
|
48
|
+
- `getNextState()`: The next state of the runner (i.e. the state that will be obtained by calling the `runNext()` function).
|
|
49
|
+
|
|
50
|
+
Both functions return an object with the following properties:
|
|
51
|
+
|
|
52
|
+
- `state`: The current runner state (any state value shown above)
|
|
53
|
+
- `url`: The current URL for any state with an applicable URL (`beginUrl` and `urlResults`), otherwise `undefined`.
|
|
54
|
+
|
|
43
55
|
### Runner Execution
|
|
44
56
|
|
|
45
|
-
The reporter runner has
|
|
57
|
+
The reporter runner has five control functions:
|
|
46
58
|
|
|
47
59
|
- `runAll()`: Simulates Pa11y CI running the analysis from the provided JSON results file from the current state through the end, calling all associated reporter functions.
|
|
48
60
|
- `runNext()`: Simulates Pa11y CI running through the next state from the provided JSON results file, calling the associated reporter function as noted above.
|
|
49
61
|
- `runUntil(targetState, targetUrl)`: Simulates Pa11y CI running the analysis from the provided JSON results file from the current state through the specified state/URL, calling the associated reporter functions as noted above. An error will be thrown if the end of the results are reached and the target was not found. This function takes the following arguments:
|
|
50
|
-
- `targetState`: The target state of the runner (any state above except `init`).
|
|
62
|
+
- `targetState`: The target state of the runner (any state above except `init`, or any valid value of the `RunnerStates` enum).
|
|
51
63
|
- `targetUrl`: An optional target URL. If no URL is specified, the runner will stop at the first instance of the target state.
|
|
52
|
-
- `
|
|
64
|
+
- `runUntilNext(targetState, targetUrl)`: Provides the same functionality as `runUntil`, but execution ends at the state prior to the target state/URL, so that the target will execute if `runNext()` is subsequently called.
|
|
65
|
+
- `reset()`: Resets the runner to the `init` state and re-initializes the reporter. This can be sent from any state.
|
|
53
66
|
|
|
54
67
|
These command are all asynchronous and must be completed before another is sent, otherwise an error will be thrown. In addition, once a run has been completed and the runner is in the `afterAll` state it must be `reset` before accepting any run command.
|
|
55
68
|
|
|
@@ -64,36 +77,40 @@ const resultsFileName = "pa11yci-results.json";
|
|
|
64
77
|
const reporterName = "../test-reporter.js";
|
|
65
78
|
const reporterOptions = { "isSomething": true };
|
|
66
79
|
const config = {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
},
|
|
70
|
-
urls: [
|
|
71
|
-
"http://localhost:8080/page1-with-errors.html",
|
|
72
|
-
"http://localhost:8080/page1-no-errors.html",
|
|
73
|
-
{
|
|
74
|
-
url: "https://pa11y.org/timed-out.html",
|
|
75
|
-
timeout: 50,
|
|
80
|
+
defaults: {
|
|
81
|
+
timeout: 30000,
|
|
76
82
|
},
|
|
77
|
-
|
|
83
|
+
urls: [
|
|
84
|
+
"http://localhost:8080/page1-with-errors.html",
|
|
85
|
+
"http://localhost:8080/page1-no-errors.html",
|
|
86
|
+
{
|
|
87
|
+
url: "https://pa11y.org/timed-out.html",
|
|
88
|
+
timeout: 50,
|
|
89
|
+
},
|
|
90
|
+
],
|
|
78
91
|
};
|
|
79
92
|
|
|
80
93
|
test('test all reporter functions', async () => {
|
|
81
|
-
|
|
94
|
+
const runner = createRunner(resultsFileName, reporterName, reporterOptions, config);
|
|
82
95
|
|
|
83
|
-
|
|
96
|
+
await runner.runAll();
|
|
84
97
|
|
|
85
|
-
|
|
98
|
+
// Test reporter results
|
|
86
99
|
});
|
|
87
100
|
|
|
88
101
|
test('test reporter at urlResults state', async () => {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
102
|
+
const runner = createRunner(resultsFileName, reporterName, reporterOptions, config);
|
|
103
|
+
|
|
104
|
+
await runner.runUntil(RunnerStates.beginUrl, 'http://localhost:8080/page1-no-errors.html');
|
|
105
|
+
let currentState = runner.getCurrentState();
|
|
106
|
+
// { state: "beginUrl", url: "http://localhost:8080/page1-no-errors.html" }
|
|
107
|
+
const nextState = runner.getNextState();
|
|
108
|
+
// { state: "urlResults", url: "http://localhost:8080/page1-no-errors.html" }
|
|
109
|
+
await runner.runNext();
|
|
110
|
+
currentState = runner.getCurrentState();
|
|
111
|
+
// { state: "urlResults", url: "http://localhost:8080/page1-no-errors.html" }
|
|
112
|
+
|
|
113
|
+
// Test reporter results
|
|
97
114
|
});
|
|
98
115
|
```
|
|
99
116
|
|
package/index.js
CHANGED
|
@@ -107,16 +107,26 @@ const isError = results => results.length === 1 && results[0] instanceof Error;
|
|
|
107
107
|
// eslint-disable-next-line max-lines-per-function
|
|
108
108
|
const createRunner = (resultsFileName, reporterName, options = {}, config = {}) => {
|
|
109
109
|
const pa11yciResults = loadPa11yciResults(resultsFileName);
|
|
110
|
-
|
|
111
110
|
validateUrls(pa11yciResults, config);
|
|
112
|
-
|
|
113
111
|
const pa11yciConfig = createConfig(config);
|
|
114
|
-
const reporter = reporterBuilder.buildReporter(reporterName, options, pa11yciConfig.defaults);
|
|
115
112
|
const urls = config.urls || Object.keys(pa11yciResults.results);
|
|
116
113
|
|
|
114
|
+
/**
|
|
115
|
+
* Create a new reporter with the given options and config
|
|
116
|
+
* (encapsulated as a function for consistency).
|
|
117
|
+
*
|
|
118
|
+
* @private
|
|
119
|
+
* @returns {object} The reporter associated with the runner.
|
|
120
|
+
*/
|
|
121
|
+
const getReporter = () => reporterBuilder.buildReporter(reporterName, options, pa11yciConfig.defaults);
|
|
122
|
+
|
|
123
|
+
// Get the initial reporter
|
|
124
|
+
let reporter = getReporter();
|
|
125
|
+
|
|
117
126
|
/**
|
|
118
127
|
* Implements the runner beforeAll event, calling reporter.beforeAll.
|
|
119
128
|
*
|
|
129
|
+
* @async
|
|
120
130
|
* @private
|
|
121
131
|
*/
|
|
122
132
|
const beforeAll = async () => {
|
|
@@ -126,6 +136,7 @@ const createRunner = (resultsFileName, reporterName, options = {}, config = {})
|
|
|
126
136
|
/**
|
|
127
137
|
* Implements the runner beginUrl event, calling reporter.begin.
|
|
128
138
|
*
|
|
139
|
+
* @async
|
|
129
140
|
* @private
|
|
130
141
|
* @param {string} url The url being analyzed.
|
|
131
142
|
*/
|
|
@@ -137,6 +148,7 @@ const createRunner = (resultsFileName, reporterName, options = {}, config = {})
|
|
|
137
148
|
* Implements the runner urlResults event, calling reporter.results or
|
|
138
149
|
* reporter.error as appropriate based on the results.
|
|
139
150
|
*
|
|
151
|
+
* @async
|
|
140
152
|
* @private
|
|
141
153
|
* @param {string} url The url being analyzed.
|
|
142
154
|
*/
|
|
@@ -151,6 +163,7 @@ const createRunner = (resultsFileName, reporterName, options = {}, config = {})
|
|
|
151
163
|
/**
|
|
152
164
|
* Implements the runner afterAll event, calling reporter.afterAll.
|
|
153
165
|
*
|
|
166
|
+
* @async
|
|
154
167
|
* @private
|
|
155
168
|
*/
|
|
156
169
|
const afterAll = async () => {
|
|
@@ -170,18 +183,21 @@ const createRunner = (resultsFileName, reporterName, options = {}, config = {})
|
|
|
170
183
|
const service = serviceFactory(Object.keys(pa11yciResults.results), actions);
|
|
171
184
|
|
|
172
185
|
/**
|
|
173
|
-
* Resets the runner to the initial
|
|
186
|
+
* Resets the runner and reporter to the initial states.
|
|
174
187
|
*
|
|
188
|
+
* @async
|
|
175
189
|
* @public
|
|
176
190
|
* @instance
|
|
177
191
|
*/
|
|
178
192
|
const reset = async () => {
|
|
179
193
|
await service.reset();
|
|
194
|
+
reporter = getReporter();
|
|
180
195
|
};
|
|
181
196
|
|
|
182
197
|
/**
|
|
183
198
|
* Executes the entire Pa11y CI sequence, calling all reporter functions.
|
|
184
199
|
*
|
|
200
|
+
* @async
|
|
185
201
|
* @public
|
|
186
202
|
* @instance
|
|
187
203
|
*/
|
|
@@ -193,6 +209,7 @@ const createRunner = (resultsFileName, reporterName, options = {}, config = {})
|
|
|
193
209
|
* Executes the next event in the Pa11y CI sequence, calling the
|
|
194
210
|
* appropriate reporter function.
|
|
195
211
|
*
|
|
212
|
+
* @async
|
|
196
213
|
* @public
|
|
197
214
|
* @instance
|
|
198
215
|
*/
|
|
@@ -202,11 +219,12 @@ const createRunner = (resultsFileName, reporterName, options = {}, config = {})
|
|
|
202
219
|
|
|
203
220
|
/**
|
|
204
221
|
* Executes the entire Pa11y CI sequence, calling all reporter functions,
|
|
205
|
-
* until the specified state and optional URL are reached. If
|
|
222
|
+
* until the specified current state and optional URL are reached. If a URL is not
|
|
206
223
|
* specified, the run completes on the first occurrence of the target state.
|
|
207
224
|
*
|
|
225
|
+
* @async
|
|
208
226
|
* @public
|
|
209
|
-
* @
|
|
227
|
+
* @instance
|
|
210
228
|
* @param {string} targetState The target state to run to.
|
|
211
229
|
* @param {string} [targetUrl] The target URL to run to.
|
|
212
230
|
*/
|
|
@@ -214,11 +232,49 @@ const createRunner = (resultsFileName, reporterName, options = {}, config = {})
|
|
|
214
232
|
await service.runUntil(targetState, targetUrl);
|
|
215
233
|
};
|
|
216
234
|
|
|
235
|
+
/**
|
|
236
|
+
* Executes the entire Pa11y CI sequence, calling all reporter functions,
|
|
237
|
+
* until the specified next state and optional URL are reached. If a URL is not
|
|
238
|
+
* specified, the run completes on the first occurrence of the target state.
|
|
239
|
+
*
|
|
240
|
+
* @async
|
|
241
|
+
* @public
|
|
242
|
+
* @instance
|
|
243
|
+
* @param {string} targetState The target state to run to.
|
|
244
|
+
* @param {string} [targetUrl] The target URL to run to.
|
|
245
|
+
*/
|
|
246
|
+
const runUntilNext = async (targetState, targetUrl) => {
|
|
247
|
+
await service.runUntilNext(targetState, targetUrl);
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Get the current state (state, url).
|
|
252
|
+
*
|
|
253
|
+
* @public
|
|
254
|
+
* @instance
|
|
255
|
+
* @returns {object} The current state.
|
|
256
|
+
*/
|
|
257
|
+
// eslint-disable-next-line prefer-destructuring -- required for jsdoc
|
|
258
|
+
const getCurrentState = service.getCurrentState;
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Get the next state (state, url).
|
|
262
|
+
*
|
|
263
|
+
* @public
|
|
264
|
+
* @instance
|
|
265
|
+
* @returns {object} The next state.
|
|
266
|
+
*/
|
|
267
|
+
// eslint-disable-next-line prefer-destructuring -- required for jsdoc
|
|
268
|
+
const getNextState = service.getNextState;
|
|
269
|
+
|
|
217
270
|
return {
|
|
271
|
+
getCurrentState,
|
|
272
|
+
getNextState,
|
|
218
273
|
reset,
|
|
219
274
|
runAll,
|
|
220
275
|
runNext,
|
|
221
|
-
runUntil
|
|
276
|
+
runUntil,
|
|
277
|
+
runUntilNext
|
|
222
278
|
};
|
|
223
279
|
};
|
|
224
280
|
|
package/lib/pa11yci-machine.js
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* State machine for pa11yci.
|
|
6
|
+
*
|
|
7
|
+
* @module pa11yci-machine
|
|
8
|
+
*/
|
|
9
|
+
|
|
3
10
|
const { createMachine, assign } = require('xstate');
|
|
4
11
|
|
|
12
|
+
/**
|
|
13
|
+
* State machine for pa11yci.
|
|
14
|
+
*/
|
|
5
15
|
const pa11yciMachine = createMachine(
|
|
6
16
|
{
|
|
7
17
|
id: 'pa11yci-runner',
|
package/lib/pa11yci-service.js
CHANGED
|
@@ -1,11 +1,43 @@
|
|
|
1
|
+
/* eslint-disable max-lines */
|
|
1
2
|
'use strict';
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Service that interprets pa11yci-machine.
|
|
6
|
+
*
|
|
7
|
+
* @module pa11yci-service
|
|
8
|
+
*/
|
|
5
9
|
|
|
10
|
+
const machine = require('./pa11yci-machine');
|
|
6
11
|
const RunnerStates = require('./runner-states');
|
|
12
|
+
|
|
7
13
|
const finalState = RunnerStates.afterAll;
|
|
8
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Enum for machine events.
|
|
17
|
+
*
|
|
18
|
+
* @enum {string}
|
|
19
|
+
* @private
|
|
20
|
+
* @readonly
|
|
21
|
+
* @static
|
|
22
|
+
*/
|
|
23
|
+
const MachineEvents = Object.freeze({
|
|
24
|
+
NEXT: 'NEXT',
|
|
25
|
+
RESET: 'RESET'
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Enum for runner service states.
|
|
30
|
+
*
|
|
31
|
+
* @enum {string}
|
|
32
|
+
* @private
|
|
33
|
+
* @readonly
|
|
34
|
+
* @static
|
|
35
|
+
*/
|
|
36
|
+
const StateTypes = Object.freeze({
|
|
37
|
+
current: 'current',
|
|
38
|
+
next: 'next'
|
|
39
|
+
});
|
|
40
|
+
|
|
9
41
|
/**
|
|
10
42
|
* Gets the initial pa11yci-runner state machine context given an array of URLs.
|
|
11
43
|
*
|
|
@@ -19,19 +51,29 @@ const getInitialContext = urls => ({
|
|
|
19
51
|
});
|
|
20
52
|
|
|
21
53
|
/**
|
|
22
|
-
* Checks the state to determine whether it has an associated URL.
|
|
54
|
+
* Checks the runner state to determine whether it has an associated URL.
|
|
23
55
|
*
|
|
24
56
|
* @private
|
|
25
|
-
* @param {
|
|
26
|
-
* @returns {boolean}
|
|
57
|
+
* @param {string} state The state to check.
|
|
58
|
+
* @returns {boolean} True if the state has an associated url.
|
|
27
59
|
*/
|
|
28
60
|
const hasUrl = state => state === RunnerStates.beginUrl || state === RunnerStates.urlResults;
|
|
29
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Gets the URL for the given machine state.
|
|
64
|
+
*
|
|
65
|
+
* @private
|
|
66
|
+
* @param {object} machineState The machine state to check against.
|
|
67
|
+
* @returns {string} The current URL for the machine state.
|
|
68
|
+
*/
|
|
69
|
+
const getUrlForState = machineState => hasUrl(machineState.value)
|
|
70
|
+
? machineState.context.urls[machineState.context.urlIndex] : undefined;
|
|
71
|
+
|
|
30
72
|
/**
|
|
31
73
|
* Validates that the given state is a valid RunnerStates value. Throws if not.
|
|
32
74
|
*
|
|
33
75
|
* @private
|
|
34
|
-
* @param
|
|
76
|
+
* @param {string} state The state to validate.
|
|
35
77
|
*/
|
|
36
78
|
const validateRunnerState = (state) => {
|
|
37
79
|
if (!Object.keys(RunnerStates).includes(state)) {
|
|
@@ -40,21 +82,34 @@ const validateRunnerState = (state) => {
|
|
|
40
82
|
};
|
|
41
83
|
|
|
42
84
|
/**
|
|
43
|
-
* Checks if the
|
|
85
|
+
* Checks if the machine state matches the targetState and either no
|
|
44
86
|
* targetUrl was specified or the context matches the targetUrl.
|
|
45
87
|
*
|
|
46
88
|
* @private
|
|
47
|
-
* @param {object}
|
|
48
|
-
* @param {
|
|
49
|
-
* @param {string}
|
|
50
|
-
* @returns {boolean}
|
|
89
|
+
* @param {object} machineState The machine state to check against.
|
|
90
|
+
* @param {RunnerStates} targetState The target runner state.
|
|
91
|
+
* @param {string} [targetUrl] The target URL.
|
|
92
|
+
* @returns {boolean} True if the context matches the target, otherwise false.
|
|
51
93
|
*/
|
|
52
|
-
const isAtTarget = (
|
|
53
|
-
return
|
|
94
|
+
const isAtTarget = (machineState, targetState, targetUrl) => {
|
|
95
|
+
return machineState.value === targetState &&
|
|
96
|
+
(!targetUrl || getUrlForState(machineState) === targetUrl);
|
|
54
97
|
};
|
|
55
98
|
|
|
56
99
|
/**
|
|
57
|
-
*
|
|
100
|
+
* Gets a summary of the machine state (state, url).
|
|
101
|
+
*
|
|
102
|
+
* @private
|
|
103
|
+
* @param {object} machineState The machine state.
|
|
104
|
+
* @returns {object} The machine state summary.
|
|
105
|
+
*/
|
|
106
|
+
const getStateSummary = machineState => ({
|
|
107
|
+
state: machineState.value,
|
|
108
|
+
url: getUrlForState(machineState)
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Factory function that returns a pa11yci-runner service.
|
|
58
113
|
*
|
|
59
114
|
* @public
|
|
60
115
|
* @static
|
|
@@ -65,35 +120,16 @@ const isAtTarget = (context, targetState, targetUrl) => {
|
|
|
65
120
|
// eslint-disable-next-line max-lines-per-function
|
|
66
121
|
const serviceFactory = (urls, actions) => {
|
|
67
122
|
let pendingCommand;
|
|
68
|
-
const currentContext = {
|
|
69
|
-
state: undefined,
|
|
70
|
-
url: undefined
|
|
71
|
-
};
|
|
72
123
|
|
|
73
|
-
//
|
|
74
|
-
//
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
// to get the url from the original urls array.
|
|
80
|
-
currentContext.state = state.value;
|
|
81
|
-
currentContext.url = hasUrl(currentContext.state)
|
|
82
|
-
? urls[state.context.urlIndex] : undefined;
|
|
83
|
-
|
|
84
|
-
if (pendingCommand) {
|
|
85
|
-
// Do not take any action in init state
|
|
86
|
-
if (currentContext.state !== 'init') {
|
|
87
|
-
await actions[currentContext.state](currentContext.url);
|
|
88
|
-
}
|
|
89
|
-
pendingCommand.resolve();
|
|
90
|
-
pendingCommand = undefined;
|
|
91
|
-
}
|
|
92
|
-
});
|
|
93
|
-
service.start();
|
|
124
|
+
// Implement custom xstate interpreter, which allows for tracking for current
|
|
125
|
+
// and the next state, which is required for some control functions.
|
|
126
|
+
const pa11yMachine = machine.withContext(getInitialContext(urls));
|
|
127
|
+
let currentState = pa11yMachine.initialState;
|
|
128
|
+
// machine.transition is a pure function, so only retrieves the state
|
|
129
|
+
let nextState = machine.transition(currentState, MachineEvents.NEXT);
|
|
94
130
|
|
|
95
131
|
/**
|
|
96
|
-
*
|
|
132
|
+
* Validates that a command is allowed in the given state. Throws if invalid.
|
|
97
133
|
*
|
|
98
134
|
* @private
|
|
99
135
|
*/
|
|
@@ -101,80 +137,163 @@ const serviceFactory = (urls, actions) => {
|
|
|
101
137
|
if (pendingCommand) {
|
|
102
138
|
throw new Error('runner cannot accept a command while another command is pending, await previous command');
|
|
103
139
|
}
|
|
104
|
-
if (
|
|
140
|
+
if (currentState.value === finalState) {
|
|
105
141
|
throw new Error(`runner must be reset before executing any other functions from the ${finalState} state`);
|
|
106
142
|
}
|
|
107
143
|
};
|
|
108
144
|
|
|
109
145
|
/**
|
|
110
|
-
* Sends the specified event to the pa11yci-
|
|
111
|
-
* the
|
|
146
|
+
* Sends the specified event to the pa11yci-machine and executes
|
|
147
|
+
* the applicable action for that state.
|
|
112
148
|
*
|
|
149
|
+
* @async
|
|
113
150
|
* @private
|
|
114
|
-
* @param {
|
|
115
|
-
* @returns {Promise<void>} Promise that indicates a pending state change.
|
|
151
|
+
* @param {MachineEvents} event The event name to be sent.
|
|
116
152
|
*/
|
|
117
|
-
const sendEvent = (event) => {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
153
|
+
const sendEvent = async (event) => {
|
|
154
|
+
try {
|
|
155
|
+
// Track pending command to block other commands
|
|
156
|
+
pendingCommand = true;
|
|
157
|
+
|
|
158
|
+
// Send event to the machine and executes the action for the
|
|
159
|
+
// current state (except in init, which has no action)
|
|
160
|
+
currentState = machine.transition(currentState, event);
|
|
161
|
+
if (currentState.value !== 'init') {
|
|
162
|
+
await actions[currentState.value](getUrlForState(currentState));
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Check next state and save for reference (machine.transition
|
|
166
|
+
// is a pure function, so only retrieves the state)
|
|
167
|
+
if (currentState.value !== finalState) {
|
|
168
|
+
nextState = machine.transition(currentState, MachineEvents.NEXT);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
finally {
|
|
172
|
+
// Ensure pending command is reset in all cases, including on error
|
|
173
|
+
pendingCommand = false;
|
|
174
|
+
}
|
|
122
175
|
};
|
|
123
176
|
|
|
124
177
|
/**
|
|
125
178
|
* Resets the service to the init state.
|
|
126
179
|
*
|
|
180
|
+
* @async
|
|
127
181
|
* @public
|
|
128
182
|
* @instance
|
|
129
183
|
*/
|
|
130
184
|
const reset = async () => {
|
|
131
|
-
await sendEvent(
|
|
185
|
+
await sendEvent(MachineEvents.RESET);
|
|
132
186
|
};
|
|
133
187
|
|
|
134
188
|
/**
|
|
135
189
|
* Executes the next event in the Pa11y CI sequence, calling the
|
|
136
190
|
* appropriate reporter function.
|
|
137
191
|
*
|
|
192
|
+
* @async
|
|
138
193
|
* @public
|
|
139
194
|
* @instance
|
|
140
195
|
*/
|
|
141
196
|
const runNext = async () => {
|
|
142
197
|
validateCommandAllowed();
|
|
143
198
|
|
|
144
|
-
await sendEvent(
|
|
199
|
+
await sendEvent(MachineEvents.NEXT);
|
|
145
200
|
};
|
|
146
201
|
|
|
147
202
|
/**
|
|
148
|
-
*
|
|
149
|
-
* until the specified state and optional URL are reached. If no URL is provided
|
|
150
|
-
* specified, the run completes on the first occurrence of the target state.
|
|
203
|
+
* Gets the state for the given state type (current or next).
|
|
151
204
|
*
|
|
152
|
-
* @
|
|
153
|
-
* @
|
|
154
|
-
* @
|
|
155
|
-
* @param {string} [targetUrl] The target URL to run to.
|
|
205
|
+
* @private
|
|
206
|
+
* @param {StateTypes} stateType The state type.
|
|
207
|
+
* @returns {object} The state object for teh given type.
|
|
156
208
|
*/
|
|
157
|
-
const
|
|
209
|
+
const getState = stateType => stateType === StateTypes.current ? currentState : nextState;
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Common function for executing runUntil and runUntilNext using the
|
|
213
|
+
* specified state type to check for completion. Executes the entire
|
|
214
|
+
* Pa11y CI sequence, calling all reporter functions, until the
|
|
215
|
+
* specified state and optional URL are reached. If a URL is not specified,
|
|
216
|
+
* the run completes on the first occurrence of the target state.
|
|
217
|
+
*
|
|
218
|
+
* @async
|
|
219
|
+
* @private
|
|
220
|
+
* @param {StateTypes} stateType The state type.
|
|
221
|
+
* @param {RunnerStates} targetState The target state to run to.
|
|
222
|
+
* @param {string} [targetUrl] The target URL to run to.
|
|
223
|
+
*/
|
|
224
|
+
const runUntilInternal = async (stateType, targetState, targetUrl) => {
|
|
158
225
|
validateCommandAllowed();
|
|
159
226
|
validateRunnerState(targetState);
|
|
160
227
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
228
|
+
// Run until target or final state is achieved
|
|
229
|
+
while (!isAtTarget(getState(stateType), targetState, targetUrl)
|
|
230
|
+
&& (getState(stateType)).value !== finalState) {
|
|
231
|
+
await sendEvent(MachineEvents.NEXT);
|
|
164
232
|
}
|
|
165
233
|
|
|
166
234
|
// If the finalState is reached and not at target then it is
|
|
167
235
|
// not in the results and throw to indicate the command failed.
|
|
168
|
-
if (!isAtTarget(
|
|
236
|
+
if (!isAtTarget(getState(stateType), targetState, targetUrl)) {
|
|
169
237
|
const urlString = targetUrl ? ` for targetUrl "${targetUrl}"` : '';
|
|
170
238
|
throw new Error(`targetState "${targetState}"${urlString} was not found`);
|
|
171
239
|
}
|
|
172
240
|
};
|
|
173
241
|
|
|
242
|
+
/**
|
|
243
|
+
* Executes the entire Pa11y CI sequence, calling all reporter functions,
|
|
244
|
+
* until the specified current state and optional URL are reached. If a URL is not
|
|
245
|
+
* specified, the run completes on the first occurrence of the target state.
|
|
246
|
+
*
|
|
247
|
+
* @async
|
|
248
|
+
* @public
|
|
249
|
+
* @instance
|
|
250
|
+
* @param {RunnerStates} targetState The target state to run to.
|
|
251
|
+
* @param {string} [targetUrl] The target URL to run to.
|
|
252
|
+
*/
|
|
253
|
+
const runUntil = async (targetState, targetUrl) => {
|
|
254
|
+
await runUntilInternal(StateTypes.current, targetState, targetUrl);
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Executes the entire Pa11y CI sequence, calling all reporter functions,
|
|
259
|
+
* until the specified next state and optional URL are reached. If a URL is not
|
|
260
|
+
* specified, the run completes on the first occurrence of the target state.
|
|
261
|
+
*
|
|
262
|
+
* @async
|
|
263
|
+
* @public
|
|
264
|
+
* @instance
|
|
265
|
+
* @param {RunnerStates} targetState The target state to run to.
|
|
266
|
+
* @param {string} [targetUrl] The target URL to run to.
|
|
267
|
+
*/
|
|
268
|
+
const runUntilNext = async (targetState, targetUrl) => {
|
|
269
|
+
await runUntilInternal(StateTypes.next, targetState, targetUrl);
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Get the current state (state, url).
|
|
274
|
+
*
|
|
275
|
+
* @public
|
|
276
|
+
* @instance
|
|
277
|
+
* @returns {object} The current state.
|
|
278
|
+
*/
|
|
279
|
+
const getCurrentState = () => getStateSummary(currentState);
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Get the next state (state, url).
|
|
283
|
+
*
|
|
284
|
+
* @public
|
|
285
|
+
* @instance
|
|
286
|
+
* @returns {object} The next state.
|
|
287
|
+
*/
|
|
288
|
+
const getNextState = () => getStateSummary(nextState);
|
|
289
|
+
|
|
174
290
|
return {
|
|
291
|
+
getCurrentState,
|
|
292
|
+
getNextState,
|
|
175
293
|
reset,
|
|
176
294
|
runNext,
|
|
177
|
-
runUntil
|
|
295
|
+
runUntil,
|
|
296
|
+
runUntilNext
|
|
178
297
|
};
|
|
179
298
|
};
|
|
180
299
|
|
package/lib/runner-states.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pa11y-ci-reporter-runner",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Pa11y CI Reporter Runner is designed to facilitate testing of Pa11y CI reporters. Given a Pa11y CI JSON results file and optional configuration it simulates the Pa11y CI calls to the reporter.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -33,10 +33,10 @@
|
|
|
33
33
|
"bugs": {
|
|
34
34
|
"url": "https://gitlab.com/gitlab-ci-utils/pa11y-ci-reporter-runner/issues"
|
|
35
35
|
},
|
|
36
|
-
"homepage": "https://gitlab.com/gitlab-ci-utils/pa11y-ci-reporter-runner
|
|
36
|
+
"homepage": "https://gitlab.com/gitlab-ci-utils/pa11y-ci-reporter-runner",
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@aarongoldenthal/eslint-config-standard": "^12.0.2",
|
|
39
|
-
"eslint": "^8.
|
|
39
|
+
"eslint": "^8.11.0",
|
|
40
40
|
"jest": "^27.5.1",
|
|
41
41
|
"jest-junit": "^13.0.0",
|
|
42
42
|
"markdownlint-cli": "^0.31.1",
|
|
@@ -44,6 +44,6 @@
|
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"lodash": "^4.17.21",
|
|
47
|
-
"xstate": "^4.30.
|
|
47
|
+
"xstate": "^4.30.6"
|
|
48
48
|
}
|
|
49
49
|
}
|