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 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 simulates 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 finer control over execution.
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 has four arguments:
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 four control functions:
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
- - `reset()`: Resets the runner to the `init` state. This can be sent from any state.
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
- defaults: {
68
- timeout: 30000,
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
- const runner = createRunner(resultsFileName, reporterName, reporterOptions, config);
94
+ const runner = createRunner(resultsFileName, reporterName, reporterOptions, config);
82
95
 
83
- await runner.runAll();
96
+ await runner.runAll();
84
97
 
85
- // Test reporter results
98
+ // Test reporter results
86
99
  });
87
100
 
88
101
  test('test reporter at urlResults state', async () => {
89
- const runner = createRunner(resultsFileName, reporterName, reporterOptions, config);
90
-
91
- await runner.runUntil(RunnerStates.beginUrl, 'http://localhost:8080/page1-no-errors.html');
92
- // The runner is now in the beginUrl state for http://localhost:8080/page1-no-errors.html
93
- await runner.runNext();
94
- // The runner is now in the urlResults state for http://localhost:8080/page1-no-errors.html
95
-
96
- // Test reporter results
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 state.
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 no URL is provided
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
- * @interface
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
 
@@ -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',
@@ -1,11 +1,43 @@
1
+ /* eslint-disable max-lines */
1
2
  'use strict';
2
3
 
3
- const { interpret } = require('xstate');
4
- const machine = require('./pa11yci-machine');
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 {RunnerStates} state The state to check.
26
- * @returns {boolean} True if the state has an associated url.
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 {string} state The state to validate.
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 context matches the targetState and either no
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} context The current state machine context.
48
- * @param {string} targetState The target runner state.
49
- * @param {string} [targetUrl] The target URL.
50
- * @returns {boolean} True if the context matches the target, otherwise false.
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 = (context, targetState, targetUrl) => {
53
- return context.state === targetState && (!targetUrl || context.url === targetUrl);
94
+ const isAtTarget = (machineState, targetState, targetUrl) => {
95
+ return machineState.value === targetState &&
96
+ (!targetUrl || getUrlForState(machineState) === targetUrl);
54
97
  };
55
98
 
56
99
  /**
57
- * Factory function that return a pa11yci-runner service.
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
- // Create service from pa11yci-machine with context, setup
74
- // transition event handler, and start service
75
- const service = interpret(machine.withContext(getInitialContext(urls)));
76
- service.onTransition(async (state) => {
77
- // Save the current state and url for use in manual transitions.
78
- // The state machine only increments the url index, so use that
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
- * Validate that a command is allowed in the given state. Throws if invalid.
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 (currentContext.state === finalState) {
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-runner service. Resolves when
111
- * the next state has been reached and the reporter event raised.
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 {string} event The event name to be sent.
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
- return new Promise((resolve, reject) => {
119
- pendingCommand = { resolve, reject };
120
- service.send({ type: event });
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('RESET');
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('NEXT');
199
+ await sendEvent(MachineEvents.NEXT);
145
200
  };
146
201
 
147
202
  /**
148
- * Executes the entire Pa11y CI sequence, calling all reporter functions,
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
- * @public
153
- * @interface
154
- * @param {string} targetState The target state to run to.
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 runUntil = async (targetState, targetUrl) => {
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
- while (!isAtTarget(currentContext, targetState, targetUrl)
162
- && currentContext.state !== finalState) {
163
- await sendEvent('NEXT');
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(currentContext, targetState, targetUrl)) {
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
 
@@ -1,6 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  /**
4
+ * Valid states for the runner.
5
+ *
4
6
  * @module runner-states
5
7
  */
6
8
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pa11y-ci-reporter-runner",
3
- "version": "0.7.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#readme",
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.10.0",
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.3"
47
+ "xstate": "^4.30.6"
48
48
  }
49
49
  }