pagean 6.0.7 → 7.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.
package/lib/tests.js CHANGED
@@ -7,9 +7,9 @@
7
7
  */
8
8
  const { HTMLHint } = require('htmlhint');
9
9
 
10
- const { testResultStates, pageanTest } = require('./testUtils');
11
- const fileUtils = require('./externalFileUtils');
12
- const { isFailedResponse } = require('./linkUtils');
10
+ const { testResultStates, pageanTest } = require('./test-utils');
11
+ const fileUtils = require('./external-file-utils');
12
+ const { isFailedResponse } = require('./link-utils');
13
13
 
14
14
  const msPerSec = 1000;
15
15
 
@@ -20,15 +20,25 @@ const msPerSec = 1000;
20
20
  * @param {object} context Test execution context.
21
21
  */
22
22
  const horizontalScrollbarTest = async (context) => {
23
- // eslint-disable-next-line no-shadow -- less intuitive
24
- await pageanTest('should not have a horizontal scrollbar', async (context) => {
25
- // istanbul ignore next: injects script causing puppeteer error, see #48
26
- const scrollbar = await context.page.evaluate(() => {
27
- document.scrollingElement.scrollLeft = 1;
28
- return document.scrollingElement.scrollLeft === 1;
29
- });
30
- return { result: scrollbar === false ? testResultStates.passed : testResultStates.failed };
31
- }, context, 'horizontalScrollbarTest');
23
+ await pageanTest(
24
+ 'should not have a horizontal scrollbar',
25
+ // eslint-disable-next-line no-shadow -- less intuitive
26
+ async (context) => {
27
+ // istanbul ignore next: injects script causing puppeteer error, see #48
28
+ const scrollbar = await context.page.evaluate(() => {
29
+ document.scrollingElement.scrollLeft = 1;
30
+ return document.scrollingElement.scrollLeft === 1;
31
+ });
32
+ return {
33
+ result:
34
+ scrollbar === false
35
+ ? testResultStates.passed
36
+ : testResultStates.failed
37
+ };
38
+ },
39
+ context,
40
+ 'horizontalScrollbarTest'
41
+ );
32
42
  };
33
43
 
34
44
  /**
@@ -38,14 +48,24 @@ const horizontalScrollbarTest = async (context) => {
38
48
  * @param {object} context Test execution context.
39
49
  */
40
50
  const consoleOutputTest = (context) => {
41
- // eslint-disable-next-line no-shadow -- less intuitive
42
- pageanTest('should not have console output', (context) => {
43
- const testResult = { result: context.consoleLog.length === 0 ? testResultStates.passed : testResultStates.failed };
44
- if (testResult.result === testResultStates.failed) {
45
- testResult.data = context.consoleLog;
46
- }
47
- return testResult;
48
- }, context, 'consoleOutputTest');
51
+ pageanTest(
52
+ 'should not have console output',
53
+ // eslint-disable-next-line no-shadow -- less intuitive
54
+ (context) => {
55
+ const testResult = {
56
+ result:
57
+ context.consoleLog.length === 0
58
+ ? testResultStates.passed
59
+ : testResultStates.failed
60
+ };
61
+ if (testResult.result === testResultStates.failed) {
62
+ testResult.data = context.consoleLog;
63
+ }
64
+ return testResult;
65
+ },
66
+ context,
67
+ 'consoleOutputTest'
68
+ );
49
69
  };
50
70
 
51
71
  /**
@@ -55,17 +75,27 @@ const consoleOutputTest = (context) => {
55
75
  * @param {object} context Test execution context.
56
76
  */
57
77
  const consoleErrorTest = (context) => {
58
- // eslint-disable-next-line no-shadow -- less intuitive
59
- pageanTest('should not have console errors', (context) => {
60
- // Object property names defined by puppeteer API
61
- // eslint-disable-next-line no-underscore-dangle
62
- const browserErrorLog = context.consoleLog.filter(log => log._type === 'error');
63
- const testResult = { result: browserErrorLog.length === 0 ? testResultStates.passed : testResultStates.failed };
64
- if (testResult.result === testResultStates.failed) {
65
- testResult.data = browserErrorLog;
66
- }
67
- return testResult;
68
- }, context, 'consoleErrorTest');
78
+ pageanTest(
79
+ 'should not have console errors',
80
+ // eslint-disable-next-line no-shadow -- less intuitive
81
+ (context) => {
82
+ const browserErrorLog = context.consoleLog.filter(
83
+ (log) => log.type === 'error'
84
+ );
85
+ const testResult = {
86
+ result:
87
+ browserErrorLog.length === 0
88
+ ? testResultStates.passed
89
+ : testResultStates.failed
90
+ };
91
+ if (testResult.result === testResultStates.failed) {
92
+ testResult.data = browserErrorLog;
93
+ }
94
+ return testResult;
95
+ },
96
+ context,
97
+ 'consoleErrorTest'
98
+ );
69
99
  };
70
100
 
71
101
  /**
@@ -75,16 +105,29 @@ const consoleErrorTest = (context) => {
75
105
  * @param {object} context Test execution context.
76
106
  */
77
107
  const renderedHtmlTest = async (context) => {
78
- // eslint-disable-next-line no-shadow -- less intuitive
79
- await pageanTest('should have valid rendered HTML', async (context) => {
80
- const html = await context.page.content();
81
- const lintResults = HTMLHint.verify(html, context.urlSettings.htmlHintConfig);
82
- const testResult = { result: lintResults.length === 0 ? testResultStates.passed : testResultStates.failed };
83
- if (testResult.result === testResultStates.failed) {
84
- testResult.data = lintResults;
85
- }
86
- return testResult;
87
- }, context, 'renderedHtmlTest');
108
+ await pageanTest(
109
+ 'should have valid rendered HTML',
110
+ // eslint-disable-next-line no-shadow -- less intuitive
111
+ async (context) => {
112
+ const html = await context.page.content();
113
+ const lintResults = HTMLHint.verify(
114
+ html,
115
+ context.urlSettings.htmlHintConfig
116
+ );
117
+ const testResult = {
118
+ result:
119
+ lintResults.length === 0
120
+ ? testResultStates.passed
121
+ : testResultStates.failed
122
+ };
123
+ if (testResult.result === testResultStates.failed) {
124
+ testResult.data = lintResults;
125
+ }
126
+ return testResult;
127
+ },
128
+ context,
129
+ 'renderedHtmlTest'
130
+ );
88
131
  };
89
132
 
90
133
  /**
@@ -93,23 +136,40 @@ const renderedHtmlTest = async (context) => {
93
136
  * @static
94
137
  * @param {object} context Test execution context.
95
138
  */
139
+ // eslint-disable-next-line max-lines-per-function
96
140
  const pageLoadTimeTest = async (context) => {
97
141
  const testSettingName = 'pageLoadTimeTest';
98
- // eslint-disable-next-line no-shadow -- less intuitive
99
- await pageanTest('should load page within timeout', async (context) => {
100
- const { pageLoadTimeThreshold } = context.testSettings;
101
- const name = `should load page within ${pageLoadTimeThreshold} sec`;
102
- // istanbul ignore next: injects script causing puppeteer error, see #48
103
- const performanceTiming = JSON.parse(await context.page.evaluate(
104
- () => JSON.stringify(window.performance)
105
- ));
106
- const loadTimeSec = (performanceTiming.timing.loadEventEnd - performanceTiming.timing.navigationStart) / msPerSec;
107
- const testResult = { name, result: loadTimeSec < pageLoadTimeThreshold ? testResultStates.passed : testResultStates.failed };
108
- if (testResult.result === testResultStates.failed) {
109
- testResult.data = { pageLoadTime: loadTimeSec };
110
- }
111
- return testResult;
112
- }, context, testSettingName);
142
+ await pageanTest(
143
+ 'should load page within timeout',
144
+ // eslint-disable-next-line no-shadow -- less intuitive
145
+ async (context) => {
146
+ const { pageLoadTimeThreshold } = context.testSettings;
147
+ const name = `should load page within ${pageLoadTimeThreshold} sec`;
148
+ // istanbul ignore next: injects script causing puppeteer error, see #48
149
+ const performanceTiming = JSON.parse(
150
+ await context.page.evaluate(() =>
151
+ JSON.stringify(window.performance)
152
+ )
153
+ );
154
+ const loadTimeSec =
155
+ (performanceTiming.timing.loadEventEnd -
156
+ performanceTiming.timing.navigationStart) /
157
+ msPerSec;
158
+ const testResult = {
159
+ name,
160
+ result:
161
+ loadTimeSec < pageLoadTimeThreshold
162
+ ? testResultStates.passed
163
+ : testResultStates.failed
164
+ };
165
+ if (testResult.result === testResultStates.failed) {
166
+ testResult.data = { pageLoadTime: loadTimeSec };
167
+ }
168
+ return testResult;
169
+ },
170
+ context,
171
+ testSettingName
172
+ );
113
173
  };
114
174
 
115
175
  /**
@@ -119,23 +179,42 @@ const pageLoadTimeTest = async (context) => {
119
179
  * @static
120
180
  * @param {object} context Test execution context.
121
181
  */
182
+ // eslint-disable-next-line max-lines-per-function
122
183
  const externalScriptTest = async (context) => {
123
- // eslint-disable-next-line no-shadow -- less intuitive
124
- await pageanTest('should not have external scripts', async (context) => {
125
- // istanbul ignore next: injects script causing puppeteer error, see #48
126
- const scripts = await context.page.evaluate(() => {
127
- return [...document.querySelectorAll('script[src]')].map(s => s.src);
128
- });
184
+ await pageanTest(
185
+ 'should not have external scripts',
186
+ // eslint-disable-next-line no-shadow -- less intuitive
187
+ async (context) => {
188
+ // istanbul ignore next: injects script causing puppeteer error, see #48
189
+ const scripts = await context.page.evaluate(() => {
190
+ return [...document.querySelectorAll('script[src]')].map(
191
+ (s) => s.src
192
+ );
193
+ });
129
194
 
130
- const pageUrl = context.page.url();
131
- const externalScripts = scripts.filter(script => fileUtils.shouldSaveFile(script, pageUrl));
132
- const scriptResults = await Promise.all(externalScripts.map(script => fileUtils.saveExternalScript(script)));
133
- const testResult = { result: scriptResults.length > 0 ? testResultStates.failed : testResultStates.passed };
134
- if (testResult.result === testResultStates.failed) {
135
- testResult.data = scriptResults;
136
- }
137
- return testResult;
138
- }, context, 'externalScriptTest');
195
+ const pageUrl = context.page.url();
196
+ const externalScripts = scripts.filter((script) =>
197
+ fileUtils.shouldSaveFile(script, pageUrl)
198
+ );
199
+ const scriptResults = await Promise.all(
200
+ externalScripts.map((script) =>
201
+ fileUtils.saveExternalScript(script)
202
+ )
203
+ );
204
+ const testResult = {
205
+ result:
206
+ scriptResults.length > 0
207
+ ? testResultStates.failed
208
+ : testResultStates.passed
209
+ };
210
+ if (testResult.result === testResultStates.failed) {
211
+ testResult.data = scriptResults;
212
+ }
213
+ return testResult;
214
+ },
215
+ context,
216
+ 'externalScriptTest'
217
+ );
139
218
  };
140
219
 
141
220
  /**
@@ -144,33 +223,54 @@ const externalScriptTest = async (context) => {
144
223
  * @static
145
224
  * @param {object} context Test execution context.
146
225
  */
226
+ // eslint-disable-next-line max-lines-per-function
147
227
  const brokenLinkTest = async (context) => {
148
- // eslint-disable-next-line no-shadow -- less intuitive
149
- await pageanTest('should not have broken links', async (context) => {
150
- // istanbul ignore next: injects script causing puppeteer error, see #48
151
- const links = await context.page.evaluate(() => {
152
- return [...document.querySelectorAll('a[href]')].map(a => a.href);
153
- });
228
+ await pageanTest(
229
+ 'should not have broken links',
230
+ // eslint-disable-next-line no-shadow -- less intuitive
231
+ async (context) => {
232
+ // istanbul ignore next: injects script causing puppeteer error, see #48
233
+ const links = await context.page.evaluate(() => {
234
+ return [...document.querySelectorAll('a[href]')].map(
235
+ (a) => a.href
236
+ );
237
+ });
154
238
 
155
- // All links are returned from puppeteer as absolute links, so this filters out
156
- // javascript and other values and leaves only pages to request.
157
- const httpLinks = links.filter(link => link.match(/(http(s?)|file):\/\//));
158
- // Reduce to unique page links so only checked once
159
- const uniqueHttpLinks = [...new Set(httpLinks)];
239
+ // All links are returned from puppeteer as absolute links, so this filters out
240
+ // javascript and other values and leaves only pages to request.
241
+ const httpLinks = links.filter((link) =>
242
+ link.match(/(http(s?)|file):\/\//)
243
+ );
244
+ // Reduce to unique page links so only checked once
245
+ const uniqueHttpLinks = [...new Set(httpLinks)];
160
246
 
161
- // Check each link includes check against ignored list, and if not checks
162
- // both links within the page as well as to other pages
163
- const linkResponses = await Promise.all(uniqueHttpLinks.map(async (link) =>
164
- ({ href: link, status: await context.linkChecker.checkLink(context, link) })));
247
+ // Check each link includes check against ignored list, and if not checks
248
+ // both links within the page as well as to other pages
249
+ const linkResponses = await Promise.all(
250
+ uniqueHttpLinks.map(async (link) => ({
251
+ href: link,
252
+ status: await context.linkChecker.checkLink(context, link)
253
+ }))
254
+ );
165
255
 
166
- // Returned results includes status for all links, so filter down to only failed
167
- const failedLinkResponses = linkResponses.filter(result => isFailedResponse(result));
168
- const testResult = { result: failedLinkResponses.length > 0 ? testResultStates.failed : testResultStates.passed };
169
- if (testResult.result === testResultStates.failed) {
170
- testResult.data = failedLinkResponses;
171
- }
172
- return testResult;
173
- }, context, 'brokenLinkTest');
256
+ // Returned results includes status for all links, so filter down to only failed
257
+ const failedLinkResponses = linkResponses.filter((result) =>
258
+ isFailedResponse(result)
259
+ );
260
+ const testResult = {
261
+ result:
262
+ failedLinkResponses.length > 0
263
+ ? testResultStates.failed
264
+ : testResultStates.passed
265
+ };
266
+ if (testResult.result === testResultStates.failed) {
267
+ testResult.data = failedLinkResponses;
268
+ }
269
+ return testResult;
270
+ },
271
+ context,
272
+ 'brokenLinkTest'
273
+ );
174
274
  };
175
275
 
176
276
  module.exports.consoleErrorTest = consoleErrorTest;
package/package.json CHANGED
@@ -1,22 +1,25 @@
1
1
  {
2
2
  "name": "pagean",
3
- "version": "6.0.7",
3
+ "version": "7.0.0",
4
4
  "description": "Pagean is a web page analysis tool designed to automate tests requiring web pages to be loaded in a browser window (e.g. horizontal scrollbar, console errors)",
5
5
  "bin": {
6
6
  "pagean": "./bin/pagean.js",
7
7
  "pageanrc-lint": "./bin/pageanrc-lint.js"
8
8
  },
9
9
  "scripts": {
10
- "start": "node ./bin/pagean.js",
11
- "start-lint": "node ./bin/pageanrc-lint.js",
12
- "start-lint-all": "npm run start-lint && npm run start-lint -c static-server.pageanrc.json && npm run start-lint -c ./lib/default-config.json",
13
- "test": "jest --ci --config jest.config.json",
10
+ "hooks-pre-commit": "npm run lint && npm run prettier-check",
11
+ "hooks-pre-push": "npm audit --audit-level=high && npm test",
12
+ "lint": "npm run lint-css && npm run lint-html && npm run lint-js && npm run lint-md",
14
13
  "lint-css": "stylelint ./lib/report-template.handlebars",
15
14
  "lint-html": "htmlhint ./lib/report-template.handlebars",
16
15
  "lint-js": "eslint \"**/*.js\"",
17
16
  "lint-md": "markdownlint **/*.md --ignore node_modules",
18
- "lint": "npm run lint-css && npm run lint-html && npm run lint-js && npm run lint-md",
19
- "push": "npm run start-lint-all && npm audit --audit-level=critical"
17
+ "prettier-check": "prettier --check .",
18
+ "prettier-fix": "prettier --write .",
19
+ "start": "node ./bin/pagean.js",
20
+ "start-lint": "node ./bin/pageanrc-lint.js",
21
+ "start-lint-all": "npm run start-lint && npm run start-lint -c static-server.pageanrc.json && npm run start-lint -c ./lib/default-config.json",
22
+ "test": "jest --ci --config jest.config.json"
20
23
  },
21
24
  "repository": {
22
25
  "type": "git",
@@ -34,7 +37,7 @@
34
37
  "author": "Aaron Goldenthal <npm@aarongoldenthal.com>",
35
38
  "license": "MIT",
36
39
  "engines": {
37
- "node": "^12.20.0 || ^14.15.0 || >=16.0.0"
40
+ "node": "^14.15.0 || ^16.13.0 || >=18.0.0"
38
41
  },
39
42
  "files": [
40
43
  "index.js",
@@ -46,29 +49,30 @@
46
49
  "bugs": {
47
50
  "url": "https://gitlab.com/gitlab-ci-utils/pagean/issues"
48
51
  },
49
- "homepage": "https://gitlab.com/gitlab-ci-utils/pagean#readme",
52
+ "homepage": "https://gitlab.com/gitlab-ci-utils/pagean",
50
53
  "devDependencies": {
51
- "@aarongoldenthal/eslint-config-standard": "^11.0.0",
52
- "@aarongoldenthal/stylelint-config-standard": "^7.0.0",
54
+ "@aarongoldenthal/eslint-config-standard": "^15.0.0",
55
+ "@aarongoldenthal/stylelint-config-standard": "^9.0.0",
53
56
  "bin-tester": "^2.0.1",
54
- "eslint": "^8.8.0",
55
- "jest": "^27.5.1",
56
- "jest-junit": "^13.0.0",
57
+ "eslint": "^8.17.0",
58
+ "jest": "^28.1.1",
59
+ "jest-junit": "^13.2.0",
57
60
  "markdownlint-cli": "^0.31.1",
61
+ "prettier": "^2.7.0",
58
62
  "strip-ansi": "^6.0.1",
59
- "stylelint": "^14.5.0"
63
+ "stylelint": "^14.9.1"
60
64
  },
61
65
  "dependencies": {
62
- "ajv": "^8.10.0",
66
+ "ajv": "^8.11.0",
63
67
  "ajv-errors": "^3.0.0",
64
- "axios": "^0.25.0",
65
- "ci-logger": "^4.0.1",
66
- "commander": "^9.0.0",
68
+ "axios": "^0.27.2",
69
+ "ci-logger": "^5.0.0",
70
+ "commander": "^9.3.0",
67
71
  "handlebars": "^4.7.7",
68
- "htmlhint": "^1.1.2",
72
+ "htmlhint": "^1.1.4",
69
73
  "kleur": "^4.1.4",
70
74
  "normalize-url": "^6.1.0",
71
75
  "protocolify": "^3.0.0",
72
- "puppeteer": "^13.3.1"
76
+ "puppeteer": "^14.4.0"
73
77
  }
74
78
  }
@@ -22,11 +22,7 @@
22
22
  "description": "Reporters to use when returning the results of the Pagean tests.",
23
23
  "type": "array",
24
24
  "items": {
25
- "enum": [
26
- "cli",
27
- "html",
28
- "json"
29
- ]
25
+ "enum": ["cli", "html", "json"]
30
26
  },
31
27
  "minItems": 1
32
28
  },
@@ -60,9 +56,7 @@
60
56
  "not": true,
61
57
  "errorMessage": "must NOT contain additional properties: ${0#}"
62
58
  },
63
- "required": [
64
- "url"
65
- ],
59
+ "required": ["url"],
66
60
  "errorMessage": {
67
61
  "required": "must have required property 'url'"
68
62
  }
@@ -77,9 +71,7 @@
77
71
  "not": true,
78
72
  "errorMessage": "must NOT contain additional properties: ${0#}"
79
73
  },
80
- "required": [
81
- "urls"
82
- ],
74
+ "required": ["urls"],
83
75
  "definitions": {
84
76
  "detailedSetting": {
85
77
  "description": "The complete set of test-specific settings for most tests.",