pagean 4.4.2 → 6.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/LICENSE +1 -1
- package/README.md +9 -9
- package/docs/upgrade-guide.md +16 -0
- package/index.js +7 -4
- package/lib/config.js +2 -2
- package/lib/default-config.json +41 -39
- package/lib/externalFileUtils.js +1 -1
- package/lib/linkUtils.js +162 -25
- package/lib/logger.js +1 -1
- package/lib/report-template.handlebars +224 -209
- package/lib/schemaErrors.js +5 -4
- package/lib/testUtils.js +1 -1
- package/lib/tests.js +14 -14
- package/package.json +28 -30
- package/schemas/pageanrc.schema.json +199 -191
- package/.codeclimate.json +0 -8
- package/.depcheckrc.json +0 -3
- package/.dockerignore +0 -12
- package/.eslintrc.json +0 -19
- package/.gitattributes +0 -4
- package/.gitlab/gitlab-releaser.json +0 -16
- package/.htmlhintrc +0 -25
- package/.markdownlint.json +0 -13
- package/.pa11yci.json +0 -5
- package/.pageanrc.json +0 -55
- package/.stylelintrc.json +0 -7
- package/CHANGELOG.md +0 -204
- package/gitlab.pageanrc.json +0 -16
- package/jest.config.json +0 -22
- package/static-server.pageanrc.json +0 -50
- package/tests/__snapshots__/config.test.js.snap +0 -591
- package/tests/__snapshots__/externalFileUtils.test.js.snap +0 -7
- package/tests/__snapshots__/index.test.js.snap +0 -30
- package/tests/__snapshots__/pagean.test.js.snap +0 -82
- package/tests/__snapshots__/pageanrc.test.js.snap +0 -759
- package/tests/__snapshots__/reporter.test.js.snap +0 -509
- package/tests/config.test.js +0 -264
- package/tests/externalFileUtils.test.js +0 -184
- package/tests/index.test.js +0 -181
- package/tests/linkUtils.test.js +0 -290
- package/tests/logger.test.js +0 -324
- package/tests/pagean.test.js +0 -85
- package/tests/pageanrc.test.js +0 -96
- package/tests/reporter.test.js +0 -82
- package/tests/schemaErrors.test.js +0 -88
- package/tests/test-cases/.htmlhintrc +0 -25
- package/tests/test-cases/brokenLinks.html +0 -22
- package/tests/test-cases/consoleLog.html +0 -11
- package/tests/test-cases/duplicateLinks.html +0 -22
- package/tests/test-cases/dynamicContent.html +0 -13
- package/tests/test-cases/externalScripts.html +0 -21
- package/tests/test-cases/horizontalScrollbar.html +0 -13
- package/tests/test-cases/htmlError.html +0 -11
- package/tests/test-cases/noExternalScripts.html +0 -12
- package/tests/test-cases/notDocumentLinks.html +0 -18
- package/tests/test-cases/pagean-results.json +0 -1
- package/tests/test-cases/scriptError404.html +0 -9
- package/tests/test-cases/slowLoad.html +0 -9
- package/tests/test-configs/cli-tests/.pageanrc.json +0 -28
- package/tests/test-configs/cli-tests/all-empty.pageanrc.json +0 -10
- package/tests/test-configs/cli-tests/all-errors.pageanrc.json +0 -69
- package/tests/test-configs/cli-tests/all-fail-cli.pageanrc.json +0 -9
- package/tests/test-configs/cli-tests/no-urls.pageanrc.json +0 -7
- package/tests/test-configs/cli-tests/pass-fail-cli.pageanrc.json +0 -14
- package/tests/test-configs/integration-tests/all-passing-tests.pageanrc.json +0 -63
- package/tests/test-configs/integration-tests/broken-links-error.pageanrc.json +0 -22
- package/tests/test-configs/integration-tests/console-error-reporter-cli-json.pageanrc.json +0 -20
- package/tests/test-configs/integration-tests/console-error-reporter-html.pageanrc.json +0 -19
- package/tests/test-configs/integration-tests/console-error.pageanrc.json +0 -16
- package/tests/test-configs/integration-tests/console-output.pageanrc.json +0 -16
- package/tests/test-configs/integration-tests/external-scripts-error.pageanrc.json +0 -19
- package/tests/test-configs/integration-tests/horizontal-scrollbar.pageanrc.json +0 -16
- package/tests/test-configs/integration-tests/html-error.pageanrc.json +0 -16
- package/tests/test-configs/integration-tests/page-load-time.pageanrc.json +0 -16
- package/tests/test-configs/unit-tests/empty-url-values.pageanrc.json +0 -9
- package/tests/test-configs/unit-tests/empty-urls.pageanrc.json +0 -3
- package/tests/test-configs/unit-tests/empty.pageanrc.json +0 -2
- package/tests/test-configs/unit-tests/global-and-test-specific-settings-shorthand.pageanrc.json +0 -30
- package/tests/test-configs/unit-tests/global-and-test-specific-settings-test-props.pageanrc.json +0 -42
- package/tests/test-configs/unit-tests/global-and-test-specific-settings.pageanrc.json +0 -34
- package/tests/test-configs/unit-tests/global-test-settings.pageanrc.json +0 -15
- package/tests/test-configs/unit-tests/htmlhintrc-invalid.pageanrc.json +0 -7
- package/tests/test-configs/unit-tests/htmlhintrc-valid.pageanrc.json +0 -7
- package/tests/test-configs/unit-tests/ignored-links-denormalized.pageanrc.json +0 -18
- package/tests/test-configs/unit-tests/invalid.pageanrc.json +0 -0
- package/tests/test-configs/unit-tests/no-test-settings.pageanrc.json +0 -6
- package/tests/test-configs/unit-tests/project-no-test-settings.pageanrc.json +0 -7
- package/tests/test-configs/unit-tests/puppeteer-no-test-settings.pageanrc.json +0 -9
- package/tests/test-configs/unit-tests/reporters-empty.pageanrc.json +0 -7
- package/tests/test-configs/unit-tests/reporters-invalid-type.pageanrc.json +0 -9
- package/tests/test-configs/unit-tests/reporters-not-array.pageanrc.json +0 -6
- package/tests/test-configs/unit-tests/reporters-valid.pageanrc.json +0 -9
- package/tests/test-configs/unit-tests/test-specific-settings.pageanrc.json +0 -26
- package/tests/test-configs/unit-tests/url-types.pageanrc.json +0 -10
- package/tests/testUtils.test.js +0 -244
- package/tests/tests.test.js +0 -533
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
Pagean is a web page analysis tool designed to automate tests requiring web pages to be loaded in a browser window (e.g. 404 error loading an external resource, page renders with horizontal scrollbars). The specific tests are outlined below, but are all general tests that do not include any page-specific logic.
|
|
4
4
|
|
|
5
|
-
**Note: As of v4.0.0, this module is being being released as `pagean` and `page-load-tests` has been deprecated. This drove breaking configuration changes that will require updating your project's configuration file. See the [version upgrade guide](https://gitlab.com/gitlab-ci-utils/pagean/-/blob/master/docs/upgrade-guide.md) for complete details.**
|
|
6
|
-
|
|
7
5
|
## Installation
|
|
8
6
|
|
|
9
7
|
Install Pagean globally (as shown below), or locally, via [npm](https://www.npmjs.com/).
|
|
@@ -121,7 +119,11 @@ Each external script is saved only once, but will be reported on any page where
|
|
|
121
119
|
The broken link test checks for broken links on the page. It checks any `<a>` tag on the page with `href` pointing to another location on the current page or another page (i.e. only `http(s)` or `file` protocols).
|
|
122
120
|
|
|
123
121
|
- For links within the page, this test checks for existence of the element on the page, passing if the element exists and failing otherwise (and passing for cases that are always valid, e.g. `#` or `#top` for the current page). It does not check the visibility of the element. Failing tests return a response of "#element Not Found" (where `#element` identifies the specific element).
|
|
124
|
-
- For links to other pages, the test makes a `HEAD` request for that URL and checks the response. If an erroneous response is returned (>= 400 with no execution error) and not code 429 (Too Many Requests), the request is retried with a `GET` request. The test
|
|
122
|
+
- For links to other pages, the test tries to most efficiently confirm whether the target link is valid. It first makes a `HEAD` request for that URL and checks the response. If an erroneous response is returned (>= 400 with no execution error) and not code 429 (Too Many Requests), the request is retried with a `GET` request. The test passes for HTTP responses < 400 and fails otherwise (if HTTP response is >= 400 or another error occurs).
|
|
123
|
+
- This can result in false failure indications, specifically for `file://` links (`404` or `ECONNREFUSED`) or where the browser passes a domain identity with the request (page loads when tested, but `401` response for links to that page). For these cases, or other false failures, the test configuration allows a boolean `checkWithBrowser` option that will instead check links by loading the target in the browser (via `puppeteer`). Note this can increase test execution time, in some cases substantially, due to the time to open a new browser tab and plus load the page and all assets.
|
|
124
|
+
- If the link to another page includes a hash it is removed prior to checking. The test in this case is confirming a valid link, not that the element exists, which is only done for the current page.
|
|
125
|
+
- The test configuration allows an `ignoredLinks` array listing link URLs to ignore for this test. Note this only applies to links to other pages, not links within the page, which are always checked.
|
|
126
|
+
- To optimize performance, link test results are cached and those links are not re-tested for the entire test run (across all tested URLs). The test configuration allows a boolean `ignoreDuplicates` option that can be set to `false` to bypass this behavior and re-test all links. The results for any failed links are included in the reports in any case.
|
|
125
127
|
|
|
126
128
|
For any failing test, the `data` array in the test report includes the original URL and the response code or error as shown below.
|
|
127
129
|
|
|
@@ -142,10 +144,6 @@ For any failing test, the `data` array in the test report includes the original
|
|
|
142
144
|
]
|
|
143
145
|
```
|
|
144
146
|
|
|
145
|
-
The test configuration allows an `ignoredLinks` array listing link URLs to ignore.
|
|
146
|
-
|
|
147
|
-
**Note: This test currently fails with warning by default so that it is not a breaking change, but this will be updated in the next major release.**
|
|
148
|
-
|
|
149
147
|
## Reports
|
|
150
148
|
|
|
151
149
|
Based on the `reporters` configuration, Pagean results may be displayed in the console and saved in two reports in the project root directory (any or all of the three):
|
|
@@ -175,7 +173,7 @@ Below is an example `.pageanrc.json` file, which is broken into six major proper
|
|
|
175
173
|
- The shorthand notation allows easy enabling/disabling of tests. In this format the test name is given with a boolean value to enable or disable the test. In this case any other test-specific settings use the default values.
|
|
176
174
|
- The longhand version includes an object for each test. Every test includes two possible properties (some tests include additional settings):
|
|
177
175
|
- `enabled`: A boolean value to enable/disable the test, and some tests include additional settings (default `true` for all tests).
|
|
178
|
-
- `failWarn`: A boolean value causing a failed test to report a warning instead of failure. A warning result will not cause the test process to fail (exit with an error code). The default value for all tests is `false` except the `externalScriptTest
|
|
176
|
+
- `failWarn`: A boolean value causing a failed test to report a warning instead of failure. A warning result will not cause the test process to fail (exit with an error code). The default value for all tests is `false` except the `externalScriptTest`, as shown below.
|
|
179
177
|
|
|
180
178
|
The shorthand:
|
|
181
179
|
|
|
@@ -238,7 +236,9 @@ All available settings with the default values are shown below.
|
|
|
238
236
|
},
|
|
239
237
|
"brokenLinkTest": {
|
|
240
238
|
"enabled": true,
|
|
241
|
-
"failWarn":
|
|
239
|
+
"failWarn": false,
|
|
240
|
+
"checkWithBrowser": false,
|
|
241
|
+
"ignoreDuplicates": true,
|
|
242
242
|
"ignoredLinks": []
|
|
243
243
|
}
|
|
244
244
|
},
|
package/docs/upgrade-guide.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Version Upgrade Guide
|
|
2
2
|
|
|
3
|
+
## Upgrading from v4.x to v5.0
|
|
4
|
+
|
|
5
|
+
1. Update to a Node.js current or LTS release (`^12.20.0 || ^14.15.0 || >=16.0.0`). Pagean may still work in other releases, but is not specifically tested or supported.
|
|
6
|
+
|
|
7
|
+
2. The Broken Link Test default setting was originally to fail with warning, but this was updated to fail (without warning) to be consistent with the other tests. To return to the previous behavior, update your configuration settings with:
|
|
8
|
+
|
|
9
|
+
```json
|
|
10
|
+
{
|
|
11
|
+
"settings": {
|
|
12
|
+
"brokenLinkTest": {
|
|
13
|
+
"failWarn": true
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
3
19
|
## Upgrading from v3.x (`page-load-tests`) to v4.0 (`pagean`)
|
|
4
20
|
|
|
5
21
|
1. Change the configurations file name from `.pltconfig.json` to `.pageanrc.json`
|
package/index.js
CHANGED
|
@@ -5,10 +5,12 @@ const puppeteer = require('puppeteer');
|
|
|
5
5
|
const testLogger = require('./lib/logger');
|
|
6
6
|
const testReporter = require('./lib/reporter');
|
|
7
7
|
const { ...testFunctions } = require('./lib/tests');
|
|
8
|
+
const { createLinkChecker } = require('./lib/linkUtils');
|
|
8
9
|
|
|
9
10
|
// eslint-disable-next-line max-lines-per-function
|
|
10
|
-
const executeAllTests = async(config) => {
|
|
11
|
+
const executeAllTests = async (config) => {
|
|
11
12
|
const logger = testLogger(config);
|
|
13
|
+
const linkChecker = createLinkChecker();
|
|
12
14
|
const browser = await puppeteer.launch(config.puppeteerLaunchOptions);
|
|
13
15
|
|
|
14
16
|
for (const testUrl of config.urls) {
|
|
@@ -21,7 +23,8 @@ const executeAllTests = async(config) => {
|
|
|
21
23
|
page.on('console', msg => consoleLog.push({ _type: msg._type, _text: msg._text, _stackTraceLocations: msg._stackTraceLocations }));
|
|
22
24
|
await page.goto(testUrl.url, { waitUntil: 'load' });
|
|
23
25
|
|
|
24
|
-
const testContext = { page, consoleLog,
|
|
26
|
+
const testContext = { page, consoleLog,
|
|
27
|
+
urlSettings: { ...testUrl.settings, htmlHintConfig: config.htmlHintConfig }, logger, linkChecker };
|
|
25
28
|
for (const test of Object.keys(testFunctions)) {
|
|
26
29
|
await testFunctions[test](testContext);
|
|
27
30
|
}
|
|
@@ -34,8 +37,8 @@ const executeAllTests = async(config) => {
|
|
|
34
37
|
|
|
35
38
|
if (testResults.summary.failed > 0) {
|
|
36
39
|
// For test harness want process to exit with error code
|
|
37
|
-
// eslint-disable-next-line no-process-exit
|
|
38
|
-
process.exit(
|
|
40
|
+
// eslint-disable-next-line no-process-exit, no-magic-numbers
|
|
41
|
+
process.exit(2);
|
|
39
42
|
}
|
|
40
43
|
};
|
|
41
44
|
|
package/lib/config.js
CHANGED
|
@@ -46,7 +46,7 @@ const consolidateTestSettings = (defaultSettings, globalSettings, urlSettings) =
|
|
|
46
46
|
const sanitizedUrlSettings = urlSettings ? urlSettings : {};
|
|
47
47
|
const processedSettings = {};
|
|
48
48
|
for (const key of Object.keys(defaultSettings)) {
|
|
49
|
-
processedSettings[key] =
|
|
49
|
+
processedSettings[key] = { ...defaultSettings[key], ...sanitizedGlobalSettings[key], ...sanitizedUrlSettings[key] };
|
|
50
50
|
}
|
|
51
51
|
if (processedSettings.brokenLinkTest.ignoredLinks) {
|
|
52
52
|
processedSettings.brokenLinkTest.ignoredLinks = processedSettings.brokenLinkTest.ignoredLinks.map(link => normalizeLink(link));
|
|
@@ -73,7 +73,7 @@ const getHtmlHintConfig = (htmlHintConfigFilename) => {
|
|
|
73
73
|
return JSON.parse(fs.readFileSync(htmlHintConfigFilename, 'utf-8'));
|
|
74
74
|
};
|
|
75
75
|
|
|
76
|
-
const validateConfigSchema = (config) =>{
|
|
76
|
+
const validateConfigSchema = (config) => {
|
|
77
77
|
const schema = JSON.parse(fs.readFileSync(path.join(__dirname, '../', 'schemas', 'pageanrc.schema.json')));
|
|
78
78
|
const ajv = new Ajv({ allErrors: true });
|
|
79
79
|
ajvErrors(ajv);
|
package/lib/default-config.json
CHANGED
|
@@ -1,41 +1,43 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
},
|
|
7
|
-
"consoleOutputTest": {
|
|
8
|
-
"enabled": true,
|
|
9
|
-
"failWarn": false
|
|
10
|
-
},
|
|
11
|
-
"consoleErrorTest": {
|
|
12
|
-
"enabled": true,
|
|
13
|
-
"failWarn": false
|
|
14
|
-
},
|
|
15
|
-
"renderedHtmlTest": {
|
|
16
|
-
"enabled": true,
|
|
17
|
-
"failWarn": false
|
|
18
|
-
},
|
|
19
|
-
"pageLoadTimeTest": {
|
|
20
|
-
"enabled": true,
|
|
21
|
-
"failWarn": false,
|
|
22
|
-
"pageLoadTimeThreshold": 2
|
|
23
|
-
},
|
|
24
|
-
"externalScriptTest": {
|
|
25
|
-
"enabled": true,
|
|
26
|
-
"failWarn": true
|
|
27
|
-
},
|
|
28
|
-
"brokenLinkTest": {
|
|
29
|
-
"enabled": true,
|
|
30
|
-
"failWarn": true
|
|
31
|
-
}
|
|
2
|
+
"settings": {
|
|
3
|
+
"horizontalScrollbarTest": {
|
|
4
|
+
"enabled": true,
|
|
5
|
+
"failWarn": false
|
|
32
6
|
},
|
|
33
|
-
"
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
7
|
+
"consoleOutputTest": {
|
|
8
|
+
"enabled": true,
|
|
9
|
+
"failWarn": false
|
|
10
|
+
},
|
|
11
|
+
"consoleErrorTest": {
|
|
12
|
+
"enabled": true,
|
|
13
|
+
"failWarn": false
|
|
14
|
+
},
|
|
15
|
+
"renderedHtmlTest": {
|
|
16
|
+
"enabled": true,
|
|
17
|
+
"failWarn": false
|
|
18
|
+
},
|
|
19
|
+
"pageLoadTimeTest": {
|
|
20
|
+
"enabled": true,
|
|
21
|
+
"failWarn": false,
|
|
22
|
+
"pageLoadTimeThreshold": 2
|
|
23
|
+
},
|
|
24
|
+
"externalScriptTest": {
|
|
25
|
+
"enabled": true,
|
|
26
|
+
"failWarn": true
|
|
27
|
+
},
|
|
28
|
+
"brokenLinkTest": {
|
|
29
|
+
"enabled": true,
|
|
30
|
+
"failWarn": false,
|
|
31
|
+
"checkWithBrowser": false,
|
|
32
|
+
"ignoreDuplicates": true
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"reporters": [
|
|
36
|
+
"cli",
|
|
37
|
+
"html",
|
|
38
|
+
"json"
|
|
39
|
+
],
|
|
40
|
+
"urls": [
|
|
41
|
+
"ignored, required to validate schema"
|
|
42
|
+
]
|
|
43
|
+
}
|
package/lib/externalFileUtils.js
CHANGED
|
@@ -13,7 +13,7 @@ const shouldSaveFile = (script, page) => {
|
|
|
13
13
|
return scriptUrl.host !== pageUrl.host;
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
-
const saveExternalScript = async(script) => {
|
|
16
|
+
const saveExternalScript = async (script) => {
|
|
17
17
|
const result = { url: script };
|
|
18
18
|
try {
|
|
19
19
|
const scriptUrl = new URL(script);
|
package/lib/linkUtils.js
CHANGED
|
@@ -1,10 +1,23 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* @module linkUtils
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const https = require('https');
|
|
3
8
|
const axios = require('axios');
|
|
4
9
|
const normalizeUrl = require('normalize-url');
|
|
5
10
|
|
|
6
11
|
const msPerSec = 1000;
|
|
7
12
|
const timeoutSeconds = 120;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Enum for HTTP responses
|
|
16
|
+
*
|
|
17
|
+
* @public
|
|
18
|
+
* @readonly
|
|
19
|
+
* @enum {number}
|
|
20
|
+
*/
|
|
8
21
|
const httpResponse = Object.freeze({
|
|
9
22
|
continue: 100,
|
|
10
23
|
ok: 200,
|
|
@@ -16,14 +29,59 @@ const httpResponse = Object.freeze({
|
|
|
16
29
|
|
|
17
30
|
const noRetryResponses = [httpResponse.tooManyRequests];
|
|
18
31
|
|
|
19
|
-
|
|
32
|
+
/**
|
|
33
|
+
* Normalizes a URL with https://www.npmjs.com/package/normalize-url
|
|
34
|
+
* using defaults plus the following overrides:
|
|
35
|
+
* 1. Set default protocol to https if protocol-relative
|
|
36
|
+
* 2. Do not remove any querystring parameters
|
|
37
|
+
* 3. Strip hash from URL
|
|
38
|
+
* 4. Do not strip "www." from the URL
|
|
39
|
+
*
|
|
40
|
+
* @public
|
|
41
|
+
* @param {string} url The URL to normalize
|
|
42
|
+
* @returns {string} The normalized URL
|
|
43
|
+
*/
|
|
44
|
+
const normalizeLink = url => normalizeUrl(url,
|
|
45
|
+
{
|
|
46
|
+
defaultProtocol: 'https:',
|
|
47
|
+
removeQueryParameters: [],
|
|
48
|
+
stripHash: true,
|
|
49
|
+
stripWWW: false
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Checks settings to determine if the provided link should be ignored
|
|
55
|
+
*
|
|
56
|
+
* @private
|
|
57
|
+
* @param {Object} settings Test settings object, which may contain an
|
|
58
|
+
* ignoredLinks array
|
|
59
|
+
* @param {string} link The link to check against the ignore list
|
|
60
|
+
* @returns {boolean} True if the link should be ignored, otherwise false
|
|
61
|
+
*/
|
|
62
|
+
const ignoreLink = (settings, link) => settings.ignoredLinks && settings.ignoredLinks.includes(link);
|
|
20
63
|
|
|
21
|
-
// The ignoredLinks values are normalized when settings are processed and does not need to be repeated here.
|
|
22
|
-
const ignoreLink = (settings, link) => settings.ignoredLinks && settings.ignoredLinks.includes(normalizeLink(link));
|
|
23
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Checks a response to an HTTP request, either a response code or explicit error,
|
|
67
|
+
* to identify any failed responses.
|
|
68
|
+
*
|
|
69
|
+
* @public
|
|
70
|
+
* @param {(string|number)} response The response to an HTTP request to check for failure.
|
|
71
|
+
* @returns {boolean} True if failed, otherwise false
|
|
72
|
+
*/
|
|
24
73
|
const isFailedResponse = response => isNaN(response.status) || response.status >= httpResponse.badRequest;
|
|
25
74
|
|
|
26
|
-
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Checks a Puppeteer page for the element specified in the hash of the provided link.
|
|
78
|
+
*
|
|
79
|
+
* @private
|
|
80
|
+
* @param {Object} page A Puppeteer page object
|
|
81
|
+
* @param {string} link The link to check
|
|
82
|
+
* @returns {(string|number)} The link status (HTTP response code or error)
|
|
83
|
+
*/
|
|
84
|
+
const checkSamePageLink = async (page, link) => {
|
|
27
85
|
const selector = link.slice(page.url().length);
|
|
28
86
|
if (selector === '#' || selector === '#top') {
|
|
29
87
|
return httpResponse.ok;
|
|
@@ -33,14 +91,55 @@ const checkSamePageLink = async(page, link) => {
|
|
|
33
91
|
return element ? httpResponse.ok : `${selector} Not Found`;
|
|
34
92
|
};
|
|
35
93
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Checks the provided link for validity by loading in a Puppeteer page.
|
|
97
|
+
*
|
|
98
|
+
* @private
|
|
99
|
+
* @param {Object} page A Puppeteer page object
|
|
100
|
+
* @param {string} link The link to check
|
|
101
|
+
* @returns {(string|number)} The link status (HTTP response code or error)
|
|
102
|
+
*/
|
|
103
|
+
const checkExternalPageLinkBrowser = async (page, link) => {
|
|
104
|
+
let status;
|
|
105
|
+
try {
|
|
106
|
+
const testPage = await page.browser().newPage();
|
|
107
|
+
const response = await testPage.goto(link);
|
|
108
|
+
status = response.status();
|
|
109
|
+
await testPage.close();
|
|
110
|
+
}
|
|
111
|
+
catch (err) {
|
|
112
|
+
// Errors are returned in the format: "ENOTFOUND at https://this.url.does.not.exist/",
|
|
113
|
+
// so extract error only and remove URL
|
|
114
|
+
status = err.message.replace(/^(.*) at .*$/, '$1');
|
|
115
|
+
}
|
|
116
|
+
return status;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Checks the provided link for validity by requesting with axios. If useGet if false,
|
|
122
|
+
* a HEAD request is made for efficiency. If useGet is true, a full GET request is made.
|
|
123
|
+
*
|
|
124
|
+
* @private
|
|
125
|
+
* @param {Object} page A Puppeteer page object
|
|
126
|
+
* @param {string} link The link to check
|
|
127
|
+
* @param {boolean} [useGet=false] Used to identify the request method to use (HEAD or GET)
|
|
128
|
+
* @returns {(string|number)} The link status (HTTP response code or error)
|
|
129
|
+
*/
|
|
130
|
+
// eslint-disable-next-line sonarjs/cognitive-complexity -- Allow less than 10
|
|
131
|
+
const checkExternalPageLink = async (page, link, useGet = false) => {
|
|
39
132
|
// Get user-agent from page so axios uses the same value for requesting links
|
|
40
133
|
const userAgent = await page.evaluate('navigator.userAgent');
|
|
41
134
|
|
|
42
135
|
try {
|
|
43
136
|
const options = { headers: { 'User-Agent': userAgent }, timeout: timeoutSeconds * msPerSec };
|
|
137
|
+
// Using internal browser property since not exposed
|
|
138
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
139
|
+
if (page.browser()._ignoreHTTPSErrors) {
|
|
140
|
+
const agent = new https.Agent({ rejectUnauthorized: false });
|
|
141
|
+
options.httpsAgent = agent;
|
|
142
|
+
}
|
|
44
143
|
const httpMethod = useGet ? axios.get : axios.head;
|
|
45
144
|
const response = await httpMethod(link, options);
|
|
46
145
|
return response.status;
|
|
@@ -59,27 +158,65 @@ const checkExternalPageLink = async(page, link, useGet = false) => {
|
|
|
59
158
|
}
|
|
60
159
|
};
|
|
61
160
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Factory function returning a linkChecker object with a {@link checkLink}
|
|
164
|
+
* function that caches checked link results.
|
|
165
|
+
*
|
|
166
|
+
* @public
|
|
167
|
+
* @returns {Object}
|
|
168
|
+
*/
|
|
169
|
+
// eslint-disable-next-line max-lines-per-function
|
|
170
|
+
const createLinkChecker = () => {
|
|
171
|
+
const checkedLinks = new Map();
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
/**
|
|
175
|
+
* Checks the provided link for validity using context object for a
|
|
176
|
+
* reference to the Puppeteer page and applicable settings.
|
|
177
|
+
*
|
|
178
|
+
* @public
|
|
179
|
+
* @param {Object} context A Pagean test context object
|
|
180
|
+
* @param {string} link The link to check
|
|
181
|
+
* @returns {(string|number)} The link status (HTTP response code or error)
|
|
182
|
+
*/
|
|
183
|
+
// eslint-disable-next-line sonarjs/cognitive-complexity, max-lines-per-function
|
|
184
|
+
checkLink: async (context, link) => {
|
|
185
|
+
let status = httpResponse.unknownError;
|
|
186
|
+
try {
|
|
187
|
+
// Check all page links first since normalize removes the hash
|
|
188
|
+
if (link.startsWith(`${context.page.url()}#`)) {
|
|
189
|
+
return await checkSamePageLink(context.page, link);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const normalizedLink = normalizeLink(link);
|
|
193
|
+
if (ignoreLink(context.testSettings, normalizedLink)) {
|
|
194
|
+
// Set to a unique status so obvious that it's ignored
|
|
195
|
+
return httpResponse.continue;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (context.testSettings.ignoreDuplicates && checkedLinks.has(link)) {
|
|
199
|
+
return checkedLinks.get(link);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (context.testSettings.checkWithBrowser) {
|
|
203
|
+
status = await checkExternalPageLinkBrowser(context.page, normalizedLink);
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
status = await checkExternalPageLink(context.page, normalizedLink);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
status = httpResponse.unknownError;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
checkedLinks.set(link, status);
|
|
214
|
+
return status;
|
|
74
215
|
}
|
|
75
|
-
}
|
|
76
|
-
catch {
|
|
77
|
-
status = httpResponse.unknownError;
|
|
78
|
-
}
|
|
79
|
-
return status;
|
|
216
|
+
};
|
|
80
217
|
};
|
|
81
218
|
|
|
82
|
-
module.exports.
|
|
219
|
+
module.exports.createLinkChecker = createLinkChecker;
|
|
83
220
|
module.exports.httpResponse = httpResponse;
|
|
84
221
|
module.exports.isFailedResponse = isFailedResponse;
|
|
85
222
|
module.exports.normalizeLink = normalizeLink;
|