pa11y-ci-reporter-runner 1.0.1 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -71,46 +71,59 @@ These command are all asynchronous and must be completed before another is sent,
71
71
  A complete example is provided below:
72
72
 
73
73
  ```js
74
- const { createRunner, RunnerStates } = require("pa11y-ci-reporter-runner");
74
+ const { createRunner, RunnerStates } = require('pa11y-ci-reporter-runner');
75
75
 
76
- const resultsFileName = "pa11yci-results.json";
77
- const reporterName = "../test-reporter.js";
78
- const reporterOptions = { "isSomething": true };
76
+ const resultsFileName = 'pa11yci-results.json';
77
+ const reporterName = '../test-reporter.js';
78
+ const reporterOptions = { isSomething: true };
79
79
  const config = {
80
- defaults: {
81
- timeout: 30000,
82
- },
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
- ],
80
+ defaults: {
81
+ timeout: 30000
82
+ },
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
+ ]
91
91
  };
92
92
 
93
93
  test('test all reporter functions', async () => {
94
- const runner = createRunner(resultsFileName, reporterName, reporterOptions, config);
94
+ const runner = createRunner(
95
+ resultsFileName,
96
+ reporterName,
97
+ reporterOptions,
98
+ config
99
+ );
95
100
 
96
- await runner.runAll();
101
+ await runner.runAll();
97
102
 
98
- // Test reporter results
103
+ // Test reporter results
99
104
  });
100
105
 
101
106
  test('test reporter at urlResults state', async () => {
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
107
+ const runner = createRunner(
108
+ resultsFileName,
109
+ reporterName,
110
+ reporterOptions,
111
+ config
112
+ );
113
+
114
+ await runner.runUntil(
115
+ RunnerStates.beginUrl,
116
+ 'http://localhost:8080/page1-no-errors.html'
117
+ );
118
+ let currentState = runner.getCurrentState();
119
+ // { state: "beginUrl", url: "http://localhost:8080/page1-no-errors.html" }
120
+ const nextState = runner.getNextState();
121
+ // { state: "urlResults", url: "http://localhost:8080/page1-no-errors.html" }
122
+ await runner.runNext();
123
+ currentState = runner.getCurrentState();
124
+ // { state: "urlResults", url: "http://localhost:8080/page1-no-errors.html" }
125
+
126
+ // Test reporter results
114
127
  });
115
128
  ```
116
129
 
package/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ /* eslint-disable max-lines */
1
2
  'use strict';
2
3
 
3
4
  /**
@@ -21,7 +22,7 @@ const RunnerStates = require('./lib/runner-states');
21
22
  * @param {string} fileName Pa11y CI JSON results file name.
22
23
  * @returns {object} Pa11y CI results.
23
24
  */
24
- const loadPa11yciResults = fileName => {
25
+ const loadPa11yciResults = (fileName) => {
25
26
  if (typeof fileName !== 'string') {
26
27
  throw new TypeError('fileName must be a string');
27
28
  }
@@ -29,8 +30,7 @@ const loadPa11yciResults = fileName => {
29
30
  try {
30
31
  const results = JSON.parse(fs.readFileSync(fileName, 'utf8'));
31
32
  return formatter.convertJsonToResultsObject(results);
32
- }
33
- catch (error) {
33
+ } catch (error) {
34
34
  throw new Error(`Error loading results file - ${error.message}`);
35
35
  }
36
36
  };
@@ -44,16 +44,14 @@ const loadPa11yciResults = fileName => {
44
44
  * @param {object[]} values The urls array from configuration.
45
45
  * @returns {string[]} Array of URL strings.
46
46
  */
47
- const getUrlList = values => {
47
+ const getUrlList = (values) => {
48
48
  const result = [];
49
49
  for (const value of values) {
50
50
  if (typeof value === 'string') {
51
51
  result.push(value);
52
- }
53
- else if (typeof value.url === 'string') {
52
+ } else if (typeof value.url === 'string') {
54
53
  result.push(value.url);
55
- }
56
- else {
54
+ } else {
57
55
  throw new TypeError('invalid url element');
58
56
  }
59
57
  }
@@ -77,9 +75,13 @@ const validateUrls = (results, config) => {
77
75
  }
78
76
  const resultUrls = Object.keys(results.results);
79
77
  const configUrls = getUrlList(config.urls);
80
- if (resultUrls.length !== configUrls.length ||
81
- JSON.stringify(resultUrls.sort()) !== JSON.stringify(configUrls.sort())) {
82
- throw new TypeError('config.urls is specified and does not match results');
78
+ if (
79
+ resultUrls.length !== configUrls.length ||
80
+ JSON.stringify(resultUrls.sort()) !== JSON.stringify(configUrls.sort())
81
+ ) {
82
+ throw new TypeError(
83
+ 'config.urls is specified and does not match results'
84
+ );
83
85
  }
84
86
  };
85
87
 
@@ -90,7 +92,8 @@ const validateUrls = (results, config) => {
90
92
  * @param {object} results Pa11y results for a single URL.
91
93
  * @returns {boolean} True if the results are an execution error.
92
94
  */
93
- const isError = results => results.length === 1 && results[0] instanceof Error;
95
+ const isError = (results) =>
96
+ results.length === 1 && results[0] instanceof Error;
94
97
 
95
98
  /**
96
99
  * Factory to create a pa11y-ci reporter runner that can execute
@@ -105,7 +108,12 @@ const isError = results => results.length === 1 && results[0] instanceof Error;
105
108
  * @returns {object} A Pa11y CI reporter runner.
106
109
  */
107
110
  // eslint-disable-next-line max-lines-per-function
108
- const createRunner = (resultsFileName, reporterName, options = {}, config = {}) => {
111
+ const createRunner = (
112
+ resultsFileName,
113
+ reporterName,
114
+ options = {},
115
+ config = {}
116
+ ) => {
109
117
  const pa11yciResults = loadPa11yciResults(resultsFileName);
110
118
  validateUrls(pa11yciResults, config);
111
119
  const pa11yciConfig = createConfig(config);
@@ -118,7 +126,12 @@ const createRunner = (resultsFileName, reporterName, options = {}, config = {})
118
126
  * @private
119
127
  * @returns {object} The reporter associated with the runner.
120
128
  */
121
- const getReporter = () => reporterBuilder.buildReporter(reporterName, options, pa11yciConfig.defaults);
129
+ const getReporter = () =>
130
+ reporterBuilder.buildReporter(
131
+ reporterName,
132
+ options,
133
+ pa11yciConfig.defaults
134
+ );
122
135
 
123
136
  // Get the initial reporter
124
137
  let reporter = getReporter();
@@ -157,7 +170,13 @@ const createRunner = (resultsFileName, reporterName, options = {}, config = {})
157
170
  const urlConfig = pa11yciConfig.getConfigForUrl(url);
158
171
  await (isError(results)
159
172
  ? reporter.error(results[0], url, urlConfig)
160
- : reporter.results(formatter.getPa11yResultsFromPa11yCiResults(url, pa11yciResults), urlConfig));
173
+ : reporter.results(
174
+ formatter.getPa11yResultsFromPa11yCiResults(
175
+ url,
176
+ pa11yciResults
177
+ ),
178
+ urlConfig
179
+ ));
161
180
  };
162
181
 
163
182
  /**
@@ -180,7 +199,10 @@ const createRunner = (resultsFileName, reporterName, options = {}, config = {})
180
199
 
181
200
  // URLs for the service is always the array of result URLs since they
182
201
  // are used to retrieve the results.
183
- const service = serviceFactory(Object.keys(pa11yciResults.results), actions);
202
+ const service = serviceFactory(
203
+ Object.keys(pa11yciResults.results),
204
+ actions
205
+ );
184
206
 
185
207
  /**
186
208
  * Resets the runner and reporter to the initial states.
package/lib/config.js CHANGED
@@ -37,7 +37,9 @@ const configFactory = (config) => {
37
37
  }
38
38
 
39
39
  // Config URLs are validated against results, if specified, so will find a result.
40
- const result = urls.find(urlObject => urlObject === url || urlObject.url === url);
40
+ const result = urls.find(
41
+ (urlObject) => urlObject === url || urlObject.url === url
42
+ );
41
43
  if (typeof result === 'string') {
42
44
  return defaults;
43
45
  }
package/lib/formatter.js CHANGED
@@ -6,8 +6,10 @@
6
6
  * @module formatter
7
7
  */
8
8
 
9
- const isError = issues => issues.length === 1 && Object.keys(issues[0]).length === 1
10
- && issues[0].message;
9
+ const isError = (issues) =>
10
+ issues.length === 1 &&
11
+ Object.keys(issues[0]).length === 1 &&
12
+ issues[0].message;
11
13
 
12
14
  /**
13
15
  * Converts Pa11y CI JSON output to an equivalent Pa11y CI object,
@@ -18,7 +20,7 @@ const isError = issues => issues.length === 1 && Object.keys(issues[0]).length =
18
20
  * @param {object} jsonResults Pa11y CI JSON results.
19
21
  * @returns {object} The equivalent Pa11y CI object.
20
22
  */
21
- const convertJsonToResultsObject = jsonResults => {
23
+ const convertJsonToResultsObject = (jsonResults) => {
22
24
  const results = {
23
25
  total: jsonResults.total,
24
26
  passes: jsonResults.passes,
@@ -28,7 +30,9 @@ const convertJsonToResultsObject = jsonResults => {
28
30
 
29
31
  for (const url of Object.keys(jsonResults.results)) {
30
32
  const issues = jsonResults.results[url];
31
- const formattedIssues = isError(issues) ? [new Error(issues[0].message)] : issues;
33
+ const formattedIssues = isError(issues)
34
+ ? [new Error(issues[0].message)]
35
+ : issues;
32
36
  results.results[url] = formattedIssues;
33
37
  }
34
38
 
@@ -59,4 +63,5 @@ const getPa11yResultsFromPa11yCiResults = (url, results) => {
59
63
  };
60
64
 
61
65
  module.exports.convertJsonToResultsObject = convertJsonToResultsObject;
62
- module.exports.getPa11yResultsFromPa11yCiResults = getPa11yResultsFromPa11yCiResults;
66
+ module.exports.getPa11yResultsFromPa11yCiResults =
67
+ getPa11yResultsFromPa11yCiResults;
@@ -1,6 +1,5 @@
1
1
  'use strict';
2
2
 
3
-
4
3
  /**
5
4
  * State machine for pa11yci.
6
5
  *
@@ -20,6 +19,10 @@ const pa11yciMachine = createMachine(
20
19
  urlIndex: 0,
21
20
  urls: []
22
21
  },
22
+ // Opting in for v4, will be default in xstate v5
23
+ predictableActionArguments: true,
24
+ // Opting in for v4, will be default in xstate v5
25
+ preserveActionOrder: true,
23
26
  states: {
24
27
  init: {
25
28
  entry: 'setInitialUrlIndex',
@@ -69,8 +72,7 @@ const pa11yciMachine = createMachine(
69
72
  },
70
73
  guards: {
71
74
  hasUrls: (context) => context.urls.length > 0,
72
- isLastUrl: (context) =>
73
- context.urlIndex === context.urls.length - 1
75
+ isLastUrl: (context) => context.urlIndex === context.urls.length - 1
74
76
  }
75
77
  }
76
78
  );
@@ -45,7 +45,7 @@ const StateTypes = Object.freeze({
45
45
  * @param {Array} urls Array of URLs.
46
46
  * @returns {object} The initial state machine context.
47
47
  */
48
- const getInitialContext = urls => ({
48
+ const getInitialContext = (urls) => ({
49
49
  urlIndex: 0,
50
50
  urls
51
51
  });
@@ -57,7 +57,8 @@ const getInitialContext = urls => ({
57
57
  * @param {string} state The state to check.
58
58
  * @returns {boolean} True if the state has an associated url.
59
59
  */
60
- const hasUrl = state => state === RunnerStates.beginUrl || state === RunnerStates.urlResults;
60
+ const hasUrl = (state) =>
61
+ state === RunnerStates.beginUrl || state === RunnerStates.urlResults;
61
62
 
62
63
  /**
63
64
  * Gets the URL for the given machine state.
@@ -66,8 +67,10 @@ const hasUrl = state => state === RunnerStates.beginUrl || state === RunnerState
66
67
  * @param {object} machineState The machine state to check against.
67
68
  * @returns {string} The current URL for the machine state.
68
69
  */
69
- const getUrlForState = machineState => hasUrl(machineState.value)
70
- ? machineState.context.urls[machineState.context.urlIndex] : undefined;
70
+ const getUrlForState = (machineState) =>
71
+ hasUrl(machineState.value)
72
+ ? machineState.context.urls[machineState.context.urlIndex]
73
+ : undefined;
71
74
 
72
75
  /**
73
76
  * Validates that the given state is a valid RunnerStates value. Throws if not.
@@ -92,8 +95,10 @@ const validateRunnerState = (state) => {
92
95
  * @returns {boolean} True if the context matches the target, otherwise false.
93
96
  */
94
97
  const isAtTarget = (machineState, targetState, targetUrl) => {
95
- return machineState.value === targetState &&
96
- (!targetUrl || getUrlForState(machineState) === targetUrl);
98
+ return (
99
+ machineState.value === targetState &&
100
+ (!targetUrl || getUrlForState(machineState) === targetUrl)
101
+ );
97
102
  };
98
103
 
99
104
  /**
@@ -103,7 +108,7 @@ const isAtTarget = (machineState, targetState, targetUrl) => {
103
108
  * @param {object} machineState The machine state.
104
109
  * @returns {object} The machine state summary.
105
110
  */
106
- const getStateSummary = machineState => ({
111
+ const getStateSummary = (machineState) => ({
107
112
  state: machineState.value,
108
113
  url: getUrlForState(machineState)
109
114
  });
@@ -135,10 +140,14 @@ const serviceFactory = (urls, actions) => {
135
140
  */
136
141
  const validateCommandAllowed = () => {
137
142
  if (pendingCommand) {
138
- throw new Error('runner cannot accept a command while another command is pending, await previous command');
143
+ throw new Error(
144
+ 'runner cannot accept a command while another command is pending, await previous command'
145
+ );
139
146
  }
140
147
  if (currentState.value === finalState) {
141
- throw new Error(`runner must be reset before executing any other functions from the ${finalState} state`);
148
+ throw new Error(
149
+ `runner must be reset before executing any other functions from the ${finalState} state`
150
+ );
142
151
  }
143
152
  };
144
153
 
@@ -165,10 +174,12 @@ const serviceFactory = (urls, actions) => {
165
174
  // Check next state and save for reference (machine.transition
166
175
  // is a pure function, so only retrieves the state)
167
176
  if (currentState.value !== finalState) {
168
- nextState = machine.transition(currentState, MachineEvents.NEXT);
177
+ nextState = machine.transition(
178
+ currentState,
179
+ MachineEvents.NEXT
180
+ );
169
181
  }
170
- }
171
- finally {
182
+ } finally {
172
183
  // Ensure pending command is reset in all cases, including on error
173
184
  pendingCommand = false;
174
185
  }
@@ -206,7 +217,8 @@ const serviceFactory = (urls, actions) => {
206
217
  * @param {StateTypes} stateType The state type.
207
218
  * @returns {object} The state object for teh given type.
208
219
  */
209
- const getState = stateType => stateType === StateTypes.current ? currentState : nextState;
220
+ const getState = (stateType) =>
221
+ stateType === StateTypes.current ? currentState : nextState;
210
222
 
211
223
  /**
212
224
  * Common function for executing runUntil and runUntilNext using the
@@ -226,8 +238,10 @@ const serviceFactory = (urls, actions) => {
226
238
  validateRunnerState(targetState);
227
239
 
228
240
  // Run until target or final state is achieved
229
- while (!isAtTarget(getState(stateType), targetState, targetUrl)
230
- && (getState(stateType)).value !== finalState) {
241
+ while (
242
+ !isAtTarget(getState(stateType), targetState, targetUrl) &&
243
+ getState(stateType).value !== finalState
244
+ ) {
231
245
  await sendEvent(MachineEvents.NEXT);
232
246
  }
233
247
 
@@ -235,7 +249,9 @@ const serviceFactory = (urls, actions) => {
235
249
  // not in the results and throw to indicate the command failed.
236
250
  if (!isAtTarget(getState(stateType), targetState, targetUrl)) {
237
251
  const urlString = targetUrl ? ` for targetUrl "${targetUrl}"` : '';
238
- throw new Error(`targetState "${targetState}"${urlString} was not found`);
252
+ throw new Error(
253
+ `targetState "${targetState}"${urlString} was not found`
254
+ );
239
255
  }
240
256
  };
241
257
 
@@ -5,12 +5,11 @@ const path = require('path');
5
5
  const reporterMethods = ['beforeAll', 'begin', 'results', 'error', 'afterAll'];
6
6
  const noop = () => {};
7
7
 
8
- const loadReporter = reporterName => {
8
+ const loadReporter = (reporterName) => {
9
9
  try {
10
10
  // eslint-disable-next-line node/global-require
11
11
  return require(reporterName);
12
- }
13
- catch {
12
+ } catch {
14
13
  // eslint-disable-next-line node/global-require
15
14
  return require(path.resolve(process.cwd(), reporterName));
16
15
  }
@@ -44,8 +43,7 @@ const buildReporter = (reporterName, options, config) => {
44
43
  }
45
44
  }
46
45
  return reporter;
47
- }
48
- catch (error) {
46
+ } catch (error) {
49
47
  throw new Error(`Error loading reporter: ${error.message}`);
50
48
  }
51
49
  };
package/package.json CHANGED
@@ -1,14 +1,17 @@
1
1
  {
2
2
  "name": "pa11y-ci-reporter-runner",
3
- "version": "1.0.1",
3
+ "version": "2.0.2",
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": {
7
- "test": "jest --ci",
7
+ "hooks-pre-commit": "npm run lint && npm run prettier-check",
8
+ "hooks-pre-push": "npm audit --audit-level=critical && npm test",
9
+ "lint": "npm run lint-js && npm run lint-md",
8
10
  "lint-js": "eslint .",
9
11
  "lint-md": "markdownlint **/*.md --ignore node_modules --ignore Archive",
10
- "lint": "npm run lint-js && npm run lint-md",
11
- "push": "npm run lint && npm audit --audit-level=high && npm test"
12
+ "prettier-check": "prettier --check .",
13
+ "prettier-fix": "prettier --write .",
14
+ "test": "jest --ci"
12
15
  },
13
16
  "repository": {
14
17
  "type": "git",
@@ -24,7 +27,7 @@
24
27
  "author": "Aaron Goldenthal <npm@aarongoldenthal.com>",
25
28
  "license": "MIT",
26
29
  "engines": {
27
- "node": "^12.20.0 || ^14.15.0 || >=16.0.0"
30
+ "node": "^14.15.0 || ^16.13.0 || >=18.0.0"
28
31
  },
29
32
  "files": [
30
33
  "index.js",
@@ -35,15 +38,16 @@
35
38
  },
36
39
  "homepage": "https://gitlab.com/gitlab-ci-utils/pa11y-ci-reporter-runner",
37
40
  "devDependencies": {
38
- "@aarongoldenthal/eslint-config-standard": "^12.0.2",
39
- "eslint": "^8.11.0",
40
- "jest": "^27.5.1",
41
- "jest-junit": "^13.0.0",
42
- "markdownlint-cli": "^0.31.1",
43
- "pa11y-ci-reporter-cli-summary": "^1.0.1"
41
+ "@aarongoldenthal/eslint-config-standard": "^16.0.1",
42
+ "eslint": "^8.23.0",
43
+ "jest": "^29.0.1",
44
+ "jest-junit": "^14.0.1",
45
+ "markdownlint-cli": "^0.32.2",
46
+ "pa11y-ci-reporter-cli-summary": "^2.0.1",
47
+ "prettier": "^2.7.1"
44
48
  },
45
49
  "dependencies": {
46
50
  "lodash": "^4.17.21",
47
- "xstate": "^4.30.6"
51
+ "xstate": "^4.33.5"
48
52
  }
49
53
  }