pagean 6.0.8 → 6.0.9
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 +107 -111
- package/bin/pagean.js +13 -6
- package/bin/pageanrc-lint.js +7 -8
- package/docs/upgrade-guide.md +26 -26
- package/index.js +19 -4
- package/lib/config.js +34 -12
- package/lib/default-config.json +2 -8
- package/lib/external-file-utils.js +6 -3
- package/lib/link-utils.js +23 -19
- package/lib/logger.js +7 -3
- package/lib/reporter.js +4 -2
- package/lib/schema-errors.js +22 -8
- package/lib/test-utils.js +19 -7
- package/lib/tests.js +193 -91
- package/package.json +22 -18
- package/schemas/pageanrc.schema.json +3 -11
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Pagean
|
|
2
2
|
|
|
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).
|
|
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
5
|
## Installation
|
|
6
6
|
|
|
@@ -27,57 +27,57 @@ Options:
|
|
|
27
27
|
-h, --help display help for command
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
Pagean requires a configuration file named, which can be specified via the CLI as detailed above, or use the default file `.pageanrc.json` in the project root.
|
|
30
|
+
Pagean requires a configuration file named, which can be specified via the CLI as detailed above, or use the default file `.pageanrc.json` in the project root. This file provides the URLs to be tested and options to configure the tests and reports. Details on the available tests and the configuration file format are provided below.
|
|
31
31
|
|
|
32
32
|
## Test Cases
|
|
33
33
|
|
|
34
|
-
The tests use [Puppeteer](https://github.com/GoogleChrome/puppeteer) to launch a headless Chrome browser.
|
|
34
|
+
The tests use [Puppeteer](https://github.com/GoogleChrome/puppeteer) to launch a headless Chrome browser. The URLs defined in the configuration file are each loaded once, and after page load the applicable tests are executed. Test results are `passed` or `failed`, but can be configured to report `warning` instead of failure. Only a `failed` test will cause the test process to fail and exit with an error code (a `warning` will not).
|
|
35
35
|
|
|
36
36
|
### Horizontal Scrollbar Test
|
|
37
37
|
|
|
38
|
-
The horizontal scrollbar test fails if the rendered page has a horizontal scrollbar.
|
|
38
|
+
The horizontal scrollbar test fails if the rendered page has a horizontal scrollbar. If a specific browser viewport size is desired for this test, that can be configured in the `puppeteerLaunchOptions`.
|
|
39
39
|
|
|
40
40
|
### Console Output Test
|
|
41
41
|
|
|
42
|
-
The console output test fails if any output is written to the browser console.
|
|
42
|
+
The console output test fails if any output is written to the browser console. An array is included in the report with all entries, as shown below:
|
|
43
43
|
|
|
44
44
|
```json
|
|
45
45
|
[
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
46
|
+
{
|
|
47
|
+
"_args": [],
|
|
48
|
+
"_location": {
|
|
49
|
+
"lineNumber": undefined,
|
|
50
|
+
"url": "https://this.url.does.not.exist/file.js"
|
|
51
|
+
},
|
|
52
|
+
"_text": "Failed to load resource: net::ERR_NAME_NOT_RESOLVED",
|
|
53
|
+
"_type": "error"
|
|
54
|
+
}
|
|
55
55
|
]
|
|
56
56
|
```
|
|
57
57
|
|
|
58
58
|
### Console Error Test
|
|
59
59
|
|
|
60
|
-
The console error test fails if any error is written to the browser console, but is otherwise the same as the console output test.
|
|
60
|
+
The console error test fails if any error is written to the browser console, but is otherwise the same as the console output test. This separation allows for testing for console errors, but allowing any other console output.
|
|
61
61
|
|
|
62
62
|
### Rendered HTML Test
|
|
63
63
|
|
|
64
|
-
The rendered HTML test is intended for cases where content is dynamically created prior to page load (i.e. the `load` event firing).
|
|
64
|
+
The rendered HTML test is intended for cases where content is dynamically created prior to page load (i.e. the `load` event firing). The rendered HTML is returned and checked with [HTML Hint](https://www.npmjs.com/package/htmlhint) and the test fails if any issues are found. An array is included in the report with all HTML Hint issues, as shown below:
|
|
65
65
|
|
|
66
66
|
```json
|
|
67
67
|
[
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
68
|
+
{
|
|
69
|
+
"col": 9,
|
|
70
|
+
"evidence": " <div id=\"div1\"></div>",
|
|
71
|
+
"line": 6,
|
|
72
|
+
"message": "The id value [ div1 ] must be unique.",
|
|
73
|
+
"raw": " id=\"div1\"",
|
|
74
|
+
"rule": {
|
|
75
|
+
"description": "The value of id attributes must be unique.",
|
|
76
|
+
"id": "id-unique",
|
|
77
|
+
"link": "https://github.com/thedaviddias/HTMLHint/wiki/id-unique"
|
|
78
|
+
},
|
|
79
|
+
"type": "error"
|
|
80
|
+
}
|
|
81
81
|
]
|
|
82
82
|
```
|
|
83
83
|
|
|
@@ -87,28 +87,28 @@ Note: This test may not find some errors in the original HTML that are removed/r
|
|
|
87
87
|
|
|
88
88
|
### Page Load Time Test
|
|
89
89
|
|
|
90
|
-
The page load time test fails if the page load time (from start through the `load` event) exceeds the defined threshold in the configuration file (or the default of 2 seconds).
|
|
90
|
+
The page load time test fails if the page load time (from start through the `load` event) exceeds the defined threshold in the configuration file (or the default of 2 seconds). The actual load time is included in the report. Tests will time out at twice the page load time threshold.
|
|
91
91
|
|
|
92
92
|
### External Script Test
|
|
93
93
|
|
|
94
|
-
The external script test is intended to identify any externally loaded javascript files (e.g. loaded from a CDN) and aggregate those files so they can undergo further analysis (e.g. dependency vulnerability scanning). The test is included here since these tests load fully rendered pages, therefore allowing the aggregation of this data for pages generated using any language or framework.
|
|
94
|
+
The external script test is intended to identify any externally loaded javascript files (e.g. loaded from a CDN) and aggregate those files so they can undergo further analysis (e.g. dependency vulnerability scanning). The test is included here since these tests load fully rendered pages, therefore allowing the aggregation of this data for pages generated using any language or framework. By default the test returns a warning if the page includes any javascript files loaded from a different domain than the page (although this could be overridden to fail instead via setting `failWarn: false`, see the Configuration section below). These files are then downloaded and saved in the "pagean-external-files" directory in the project root. Subdirectories are created for each domain, then following the URL path. For example, the following script...
|
|
95
95
|
|
|
96
96
|
```html
|
|
97
97
|
<script src="https://bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js"></script>
|
|
98
98
|
```
|
|
99
99
|
|
|
100
|
-
...will be saved as `./bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js`.
|
|
100
|
+
...will be saved as `./bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js`. The `data` array in the test report includes the original file URL and the local saved filename or applicable error, as shown below.
|
|
101
101
|
|
|
102
102
|
```json
|
|
103
103
|
[
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
104
|
+
{
|
|
105
|
+
"url": "https://code.jquery.com/jquery-3.4.1.slim.min.js",
|
|
106
|
+
"localFile": "pagean-external-scripts/code.jquery.com/jquery-3.4.1.slim.min.js"
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"url": "http://bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js",
|
|
110
|
+
"error": "Request failed with status code 404"
|
|
111
|
+
}
|
|
112
112
|
]
|
|
113
113
|
```
|
|
114
114
|
|
|
@@ -118,8 +118,8 @@ Each external script is saved only once, but will be reported on any page where
|
|
|
118
118
|
|
|
119
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).
|
|
120
120
|
|
|
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).
|
|
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.
|
|
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).
|
|
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
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
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
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.
|
|
@@ -129,18 +129,18 @@ For any failing test, the `data` array in the test report includes the original
|
|
|
129
129
|
|
|
130
130
|
```json
|
|
131
131
|
[
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
132
|
+
{
|
|
133
|
+
"href": "https://about.gitlab.com/not-found",
|
|
134
|
+
"status": 404
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
"href": "http://localhost:8080/brokenLinks.html#notlinked",
|
|
138
|
+
"status": "#notlinked Not Found"
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
"href": "https://this.url.does.not.exist/",
|
|
142
|
+
"status": "ENOTFOUND"
|
|
143
|
+
}
|
|
144
144
|
]
|
|
145
145
|
```
|
|
146
146
|
|
|
@@ -161,19 +161,19 @@ Complete reports for the example case in this project (the tests as specified in
|
|
|
161
161
|
|
|
162
162
|
## Configuration
|
|
163
163
|
|
|
164
|
-
Pagean looks for a configuration file as specified via the CLI, or defaults to a file named `.pageanrc.json` in the project root.
|
|
164
|
+
Pagean looks for a configuration file as specified via the CLI, or defaults to a file named `.pageanrc.json` in the project root. If the configuration file is not found, is not valid JSON, or does not contain any URLs to check the job will fail.
|
|
165
165
|
|
|
166
166
|
Below is an example `.pageanrc.json` file, which is broken into six major properties:
|
|
167
167
|
|
|
168
168
|
- `htmlhintrc`: An optional path to an htmlhintrc file to be used in the rendered HTML test
|
|
169
169
|
- `project`: An optional name of the project, which is included in HTML and JSON reports.
|
|
170
|
-
- `puppeteerLaunchOptions`: An optional set of options to pass to Puppeteer on launch.
|
|
171
|
-
- `reporters`:
|
|
170
|
+
- `puppeteerLaunchOptions`: An optional set of options to pass to Puppeteer on launch. There are no default options. The complete list of available options can be found at https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#puppeteerlaunchoptions.
|
|
171
|
+
- `reporters`: An optional array of reporters indicating the test reports that should be provided. There are three possible options - `cli`, `html`, and `json`. The `cli` option reports all test details to the console, but the final results summary is always output (even with `cli` disabled). If `reporters` is specified, at least one reporter must be included. The default value, as specified below, is all three reporters enabled.
|
|
172
172
|
- `settings`: These settings enable/disable or configure tests, and are applied to all tests overriding the default values.
|
|
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.
|
|
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.
|
|
174
174
|
- The longhand version includes an object for each test. Every test includes two possible properties (some tests include additional settings):
|
|
175
175
|
- `enabled`: A boolean value to enable/disable the test, and some tests include additional settings (default `true` for all tests).
|
|
176
|
-
- `failWarn`: A boolean value causing a failed test to report a warning instead of failure.
|
|
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.
|
|
177
177
|
|
|
178
178
|
The shorthand:
|
|
179
179
|
|
|
@@ -196,67 +196,63 @@ is equivalent to the longhand:
|
|
|
196
196
|
|
|
197
197
|
All available settings with the default values are shown below.
|
|
198
198
|
|
|
199
|
-
- `urls`: An array of URLs to be tested, which must contain at least one value.
|
|
199
|
+
- `urls`: An array of URLs to be tested, which must contain at least one value. Each array entry can either be a URL string, or an object that contains a `url` string and an optional `settings` object. This object can contain any of the `settings` values identified above and will override that setting for testing that URL. The `url` string can be either an actual URL or a local file, as shown in the example below.
|
|
200
200
|
|
|
201
201
|
```json
|
|
202
202
|
{
|
|
203
|
-
|
|
204
|
-
|
|
203
|
+
"puppeteerLaunchOptions": {
|
|
204
|
+
"args": ["--no-sandbox"]
|
|
205
|
+
},
|
|
206
|
+
"reporters": ["cli", "html", "json"],
|
|
207
|
+
"settings": {
|
|
208
|
+
"horizontalScrollbarTest": {
|
|
209
|
+
"enabled": true,
|
|
210
|
+
"failWarn": false
|
|
211
|
+
},
|
|
212
|
+
"consoleOutputTest": {
|
|
213
|
+
"enabled": true,
|
|
214
|
+
"failWarn": false
|
|
215
|
+
},
|
|
216
|
+
"consoleErrorTest": {
|
|
217
|
+
"enabled": true,
|
|
218
|
+
"failWarn": false
|
|
219
|
+
},
|
|
220
|
+
"renderedHtmlTest": {
|
|
221
|
+
"enabled": true,
|
|
222
|
+
"failWarn": false
|
|
223
|
+
},
|
|
224
|
+
"pageLoadTimeTest": {
|
|
225
|
+
"enabled": true,
|
|
226
|
+
"failWarn": false,
|
|
227
|
+
"pageLoadTimeThreshold": 2
|
|
205
228
|
},
|
|
206
|
-
"
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
"json"
|
|
210
|
-
],
|
|
211
|
-
"settings": {
|
|
212
|
-
"horizontalScrollbarTest": {
|
|
213
|
-
"enabled": true,
|
|
214
|
-
"failWarn": false
|
|
215
|
-
},
|
|
216
|
-
"consoleOutputTest": {
|
|
217
|
-
"enabled": true,
|
|
218
|
-
"failWarn": false
|
|
219
|
-
},
|
|
220
|
-
"consoleErrorTest": {
|
|
221
|
-
"enabled": true,
|
|
222
|
-
"failWarn": false
|
|
223
|
-
},
|
|
224
|
-
"renderedHtmlTest": {
|
|
225
|
-
"enabled": true,
|
|
226
|
-
"failWarn": false
|
|
227
|
-
},
|
|
228
|
-
"pageLoadTimeTest": {
|
|
229
|
-
"enabled": true,
|
|
230
|
-
"failWarn": false,
|
|
231
|
-
"pageLoadTimeThreshold": 2
|
|
232
|
-
},
|
|
233
|
-
"externalScriptTest": {
|
|
234
|
-
"enabled": true,
|
|
235
|
-
"failWarn": true
|
|
236
|
-
},
|
|
237
|
-
"brokenLinkTest": {
|
|
238
|
-
"enabled": true,
|
|
239
|
-
"failWarn": false,
|
|
240
|
-
"checkWithBrowser": false,
|
|
241
|
-
"ignoreDuplicates": true,
|
|
242
|
-
"ignoredLinks": []
|
|
243
|
-
}
|
|
229
|
+
"externalScriptTest": {
|
|
230
|
+
"enabled": true,
|
|
231
|
+
"failWarn": true
|
|
244
232
|
},
|
|
245
|
-
"
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
233
|
+
"brokenLinkTest": {
|
|
234
|
+
"enabled": true,
|
|
235
|
+
"failWarn": false,
|
|
236
|
+
"checkWithBrowser": false,
|
|
237
|
+
"ignoreDuplicates": true,
|
|
238
|
+
"ignoredLinks": []
|
|
239
|
+
}
|
|
240
|
+
},
|
|
241
|
+
"urls": [
|
|
242
|
+
"https://gitlab.com/gitlab-ci-utils/pagean/",
|
|
243
|
+
{
|
|
244
|
+
"url": "./tests/test-cases/consoleLog.html",
|
|
245
|
+
"settings": {
|
|
246
|
+
"consoleOutputTest": false
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
]
|
|
254
250
|
}
|
|
255
251
|
```
|
|
256
252
|
|
|
257
253
|
## Docker Images
|
|
258
254
|
|
|
259
|
-
Provided with the Pagean project are Docker images configured to run the tests.
|
|
255
|
+
Provided with the Pagean project are Docker images configured to run the tests. All available image tags can be found in the `gitlab-ci-utils/pagean` repository at https://gitlab.com/gitlab-ci-utils/pagean/container_registry. Details on each release can be found on the [Releases](https://gitlab.com/gitlab-ci-utils/pagean/releases) page.
|
|
260
256
|
|
|
261
257
|
**Note:** Any images in the `gitlab-ci-utils/pagean/tmp` repository are temporary images used during the build process and may be deleted at any point.
|
|
262
258
|
|
|
@@ -280,7 +276,7 @@ pagean:
|
|
|
280
276
|
|
|
281
277
|
### Testing With Static HTTP Server
|
|
282
278
|
|
|
283
|
-
The Docker image shown above includes [`http-server`](https://www.npmjs.com/package/http-server) and [`wait-on`](https://www.npmjs.com/package/wait-on) installed globally to run a local HTTP server for testing static content.
|
|
279
|
+
The Docker image shown above includes [`http-server`](https://www.npmjs.com/package/http-server) and [`wait-on`](https://www.npmjs.com/package/wait-on) installed globally to run a local HTTP server for testing static content. The example job below illustrates how to use this for Pagean tests. The script starts the server in this project's `test-cases` directory and uses `wait-on` to hold the script until the server is running and returns a valid response. The referenced `pageanrc` file is the same as the project default `pageanrc`, but references all test URLs from the local server.
|
|
284
280
|
|
|
285
281
|
```yaml
|
|
286
282
|
pagean:
|
package/bin/pagean.js
CHANGED
|
@@ -9,17 +9,24 @@ const pkg = require('../package.json');
|
|
|
9
9
|
|
|
10
10
|
const defaultConfigFileName = './.pageanrc.json';
|
|
11
11
|
|
|
12
|
-
program
|
|
13
|
-
.
|
|
12
|
+
program
|
|
13
|
+
.version(pkg.version)
|
|
14
|
+
.option(
|
|
15
|
+
'-c, --config <file>',
|
|
14
16
|
'the path to the pagean configuration file',
|
|
15
|
-
defaultConfigFileName
|
|
17
|
+
defaultConfigFileName
|
|
18
|
+
)
|
|
16
19
|
.parse(process.argv);
|
|
17
20
|
const options = program.opts();
|
|
18
21
|
|
|
19
22
|
try {
|
|
20
23
|
const config = getConfig(options.config);
|
|
21
24
|
pagean.executeAllTests(config);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
} catch (error) {
|
|
26
|
+
log({
|
|
27
|
+
message: `Error executing pagean tests\n${error.message}`,
|
|
28
|
+
level: levels.error,
|
|
29
|
+
exitOnError: true,
|
|
30
|
+
errorCode: 1
|
|
31
|
+
});
|
|
25
32
|
}
|
package/bin/pageanrc-lint.js
CHANGED
|
@@ -10,7 +10,8 @@ const pkg = require('../package.json');
|
|
|
10
10
|
|
|
11
11
|
const defaultConfigFileName = './.pageanrc.json';
|
|
12
12
|
|
|
13
|
-
program
|
|
13
|
+
program
|
|
14
|
+
.version(pkg.version)
|
|
14
15
|
.option('-j, --json', 'output JSON with full details')
|
|
15
16
|
.description('Lint a pageanrc file')
|
|
16
17
|
.usage(`[options] [file] (default: "${defaultConfigFileName}")`)
|
|
@@ -34,8 +35,7 @@ const outputJsonResults = (configFileName, lintResults) => {
|
|
|
34
35
|
const outputConsoleResults = (configFileName, lintResults) => {
|
|
35
36
|
if (lintResults.isValid) {
|
|
36
37
|
log({ message: `\n${configFileName}: ${green('valid')}\n` });
|
|
37
|
-
}
|
|
38
|
-
else {
|
|
38
|
+
} else {
|
|
39
39
|
logError(`\n${underline(configFileName)}`, false);
|
|
40
40
|
for (const error of formatErrors(lintResults.errors)) {
|
|
41
41
|
logError(error, false);
|
|
@@ -46,16 +46,15 @@ const outputConsoleResults = (configFileName, lintResults) => {
|
|
|
46
46
|
};
|
|
47
47
|
|
|
48
48
|
try {
|
|
49
|
-
const configFileName =
|
|
49
|
+
const configFileName =
|
|
50
|
+
program.args.length > 0 ? program.args[0] : defaultConfigFileName;
|
|
50
51
|
const lintResults = lintConfigFile(configFileName);
|
|
51
52
|
|
|
52
53
|
if (options.json) {
|
|
53
54
|
outputJsonResults(configFileName, lintResults);
|
|
54
|
-
}
|
|
55
|
-
else {
|
|
55
|
+
} else {
|
|
56
56
|
outputConsoleResults(configFileName, lintResults);
|
|
57
57
|
}
|
|
58
|
-
}
|
|
59
|
-
catch (error) {
|
|
58
|
+
} catch (error) {
|
|
60
59
|
logError(`Error linting pageanrc file\n${error.message}`);
|
|
61
60
|
}
|
package/docs/upgrade-guide.md
CHANGED
|
@@ -26,18 +26,18 @@
|
|
|
26
26
|
|
|
27
27
|
```json
|
|
28
28
|
{
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
29
|
+
"settings": {
|
|
30
|
+
"pageLoadTimeThreshold": 2
|
|
31
|
+
},
|
|
32
|
+
"urls": [
|
|
33
|
+
"https://somewhere.com/",
|
|
34
|
+
{
|
|
35
|
+
"url": "https://somewhere-else.com",
|
|
36
|
+
"settings": {
|
|
37
|
+
"pageLoadTimeThreshold": 3
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
41
|
}
|
|
42
42
|
```
|
|
43
43
|
|
|
@@ -45,21 +45,21 @@
|
|
|
45
45
|
|
|
46
46
|
```json
|
|
47
47
|
{
|
|
48
|
-
|
|
48
|
+
"settings": {
|
|
49
|
+
"pageLoadTimeTest": {
|
|
50
|
+
"pageLoadTimeThreshold": 2
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"urls": [
|
|
54
|
+
"https://somewhere.com/",
|
|
55
|
+
{
|
|
56
|
+
"url": "https://somewhere-else.com",
|
|
57
|
+
"settings": {
|
|
49
58
|
"pageLoadTimeTest": {
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
},
|
|
53
|
-
"urls": [
|
|
54
|
-
"https://somewhere.com/",
|
|
55
|
-
{
|
|
56
|
-
"url": "https://somewhere-else.com",
|
|
57
|
-
"settings": {
|
|
58
|
-
"pageLoadTimeTest": {
|
|
59
|
-
"pageLoadTimeThreshold": 3
|
|
60
|
-
}
|
|
61
|
-
}
|
|
59
|
+
"pageLoadTimeThreshold": 3
|
|
62
60
|
}
|
|
63
|
-
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
]
|
|
64
64
|
}
|
|
65
65
|
```
|
package/index.js
CHANGED
|
@@ -18,6 +18,7 @@ const { createLinkChecker } = require('./lib/link-utils');
|
|
|
18
18
|
* @static
|
|
19
19
|
* @param {object} config The Pagean test configuration.
|
|
20
20
|
*/
|
|
21
|
+
// eslint-disable-next-line max-lines-per-function
|
|
21
22
|
const executeAllTests = async (config) => {
|
|
22
23
|
const logger = testLogger(config);
|
|
23
24
|
const linkChecker = createLinkChecker();
|
|
@@ -29,13 +30,27 @@ const executeAllTests = async (config) => {
|
|
|
29
30
|
const consoleLog = [];
|
|
30
31
|
const page = await browser.newPage();
|
|
31
32
|
// Object property names defined by puppeteer API
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
page.on('console', (message) =>
|
|
34
|
+
consoleLog.push({
|
|
35
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
36
|
+
_type: message._type,
|
|
37
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
38
|
+
_text: message._text,
|
|
39
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
40
|
+
_stackTraceLocations: message._stackTraceLocations
|
|
41
|
+
})
|
|
42
|
+
);
|
|
34
43
|
await page.goto(testUrl.url, { waitUntil: 'load' });
|
|
35
44
|
|
|
36
45
|
const testContext = {
|
|
37
|
-
page,
|
|
38
|
-
|
|
46
|
+
page,
|
|
47
|
+
consoleLog,
|
|
48
|
+
urlSettings: {
|
|
49
|
+
...testUrl.settings,
|
|
50
|
+
htmlHintConfig: config.htmlHintConfig
|
|
51
|
+
},
|
|
52
|
+
logger,
|
|
53
|
+
linkChecker
|
|
39
54
|
};
|
|
40
55
|
for (const test of Object.keys(testFunctions)) {
|
|
41
56
|
await testFunctions[test](testContext);
|
package/lib/config.js
CHANGED
|
@@ -22,8 +22,7 @@ const getUrl = (testUrl) => {
|
|
|
22
22
|
if (typeof testUrl === 'object') {
|
|
23
23
|
rawUrl = testUrl.url;
|
|
24
24
|
urlSettings = testUrl.settings;
|
|
25
|
-
}
|
|
26
|
-
else {
|
|
25
|
+
} else {
|
|
27
26
|
rawUrl = testUrl;
|
|
28
27
|
}
|
|
29
28
|
return { rawUrl, urlSettings };
|
|
@@ -47,27 +46,42 @@ const processAllTestSettings = (settings) => {
|
|
|
47
46
|
return processedSettings;
|
|
48
47
|
};
|
|
49
48
|
|
|
50
|
-
const consolidateTestSettings = (
|
|
49
|
+
const consolidateTestSettings = (
|
|
50
|
+
defaultSettings,
|
|
51
|
+
globalSettings,
|
|
52
|
+
urlSettings
|
|
53
|
+
) => {
|
|
51
54
|
const sanitizedGlobalSettings = globalSettings ? globalSettings : {};
|
|
52
55
|
const sanitizedUrlSettings = urlSettings ? urlSettings : {};
|
|
53
56
|
const processedSettings = {};
|
|
54
57
|
for (const key of Object.keys(defaultSettings)) {
|
|
55
|
-
processedSettings[key] = {
|
|
58
|
+
processedSettings[key] = {
|
|
59
|
+
...defaultSettings[key],
|
|
60
|
+
...sanitizedGlobalSettings[key],
|
|
61
|
+
...sanitizedUrlSettings[key]
|
|
62
|
+
};
|
|
56
63
|
}
|
|
57
64
|
if (processedSettings.brokenLinkTest.ignoredLinks) {
|
|
58
|
-
processedSettings.brokenLinkTest.ignoredLinks =
|
|
65
|
+
processedSettings.brokenLinkTest.ignoredLinks =
|
|
66
|
+
processedSettings.brokenLinkTest.ignoredLinks.map((link) =>
|
|
67
|
+
normalizeLink(link)
|
|
68
|
+
);
|
|
59
69
|
}
|
|
60
70
|
return processedSettings;
|
|
61
71
|
};
|
|
62
72
|
|
|
63
73
|
const processUrls = (config) => {
|
|
64
74
|
const processedConfigSettings = processAllTestSettings(config.settings);
|
|
65
|
-
return config.urls.map(testUrl => {
|
|
75
|
+
return config.urls.map((testUrl) => {
|
|
66
76
|
const { rawUrl, urlSettings } = getUrl(testUrl);
|
|
67
77
|
return {
|
|
68
78
|
url: protocolify(rawUrl),
|
|
69
79
|
rawUrl,
|
|
70
|
-
settings: consolidateTestSettings(
|
|
80
|
+
settings: consolidateTestSettings(
|
|
81
|
+
defaultConfig.settings,
|
|
82
|
+
processedConfigSettings,
|
|
83
|
+
processAllTestSettings(urlSettings)
|
|
84
|
+
)
|
|
71
85
|
};
|
|
72
86
|
});
|
|
73
87
|
};
|
|
@@ -76,11 +90,15 @@ const getHtmlHintConfig = (htmlHintConfigFilename) => {
|
|
|
76
90
|
if (!fs.existsSync(htmlHintConfigFilename)) {
|
|
77
91
|
return;
|
|
78
92
|
}
|
|
79
|
-
return JSON.parse(fs.readFileSync(htmlHintConfigFilename, '
|
|
93
|
+
return JSON.parse(fs.readFileSync(htmlHintConfigFilename, 'utf8'));
|
|
80
94
|
};
|
|
81
95
|
|
|
82
96
|
const validateConfigSchema = (config) => {
|
|
83
|
-
const schema = JSON.parse(
|
|
97
|
+
const schema = JSON.parse(
|
|
98
|
+
fs.readFileSync(
|
|
99
|
+
path.join(__dirname, '../', 'schemas', 'pageanrc.schema.json')
|
|
100
|
+
)
|
|
101
|
+
);
|
|
84
102
|
const ajv = new Ajv({ allErrors: true });
|
|
85
103
|
ajvErrors(ajv);
|
|
86
104
|
const validate = ajv.compile(schema);
|
|
@@ -89,7 +107,7 @@ const validateConfigSchema = (config) => {
|
|
|
89
107
|
};
|
|
90
108
|
|
|
91
109
|
const getConfigFromFile = (configFileName) => {
|
|
92
|
-
return JSON.parse(fs.readFileSync(configFileName, '
|
|
110
|
+
return JSON.parse(fs.readFileSync(configFileName, 'utf8'));
|
|
93
111
|
};
|
|
94
112
|
|
|
95
113
|
/**
|
|
@@ -104,13 +122,17 @@ const processConfig = (configFileName) => {
|
|
|
104
122
|
const config = getConfigFromFile(configFileName);
|
|
105
123
|
const { isValid } = validateConfigSchema(config);
|
|
106
124
|
if (!isValid) {
|
|
107
|
-
throw new TypeError(
|
|
125
|
+
throw new TypeError(
|
|
126
|
+
`File ${configFileName} has an invalid pageanrc schema`
|
|
127
|
+
);
|
|
108
128
|
}
|
|
109
129
|
return {
|
|
110
130
|
project: config.project || '',
|
|
111
131
|
puppeteerLaunchOptions: config.puppeteerLaunchOptions,
|
|
112
132
|
urls: processUrls(config),
|
|
113
|
-
htmlHintConfig: getHtmlHintConfig(
|
|
133
|
+
htmlHintConfig: getHtmlHintConfig(
|
|
134
|
+
config.htmlhintrc || defaultHtmlHintConfigFilename
|
|
135
|
+
),
|
|
114
136
|
reporters: config.reporters ? config.reporters : defaultConfig.reporters
|
|
115
137
|
};
|
|
116
138
|
};
|
package/lib/default-config.json
CHANGED
|
@@ -32,12 +32,6 @@
|
|
|
32
32
|
"ignoreDuplicates": true
|
|
33
33
|
}
|
|
34
34
|
},
|
|
35
|
-
"reporters": [
|
|
36
|
-
|
|
37
|
-
"html",
|
|
38
|
-
"json"
|
|
39
|
-
],
|
|
40
|
-
"urls": [
|
|
41
|
-
"ignored, required to validate schema"
|
|
42
|
-
]
|
|
35
|
+
"reporters": ["cli", "html", "json"],
|
|
36
|
+
"urls": ["ignored, required to validate schema"]
|
|
43
37
|
}
|
|
@@ -39,7 +39,11 @@ const saveExternalScript = async (script) => {
|
|
|
39
39
|
const result = { url: script };
|
|
40
40
|
try {
|
|
41
41
|
const scriptUrl = new URL(script);
|
|
42
|
-
const pathName = path.join(
|
|
42
|
+
const pathName = path.join(
|
|
43
|
+
externalFilePath,
|
|
44
|
+
scriptUrl.hostname,
|
|
45
|
+
scriptUrl.pathname
|
|
46
|
+
);
|
|
43
47
|
if (!fs.existsSync(pathName)) {
|
|
44
48
|
// Axios will throw for any error response
|
|
45
49
|
const response = await axios.get(script);
|
|
@@ -47,8 +51,7 @@ const saveExternalScript = async (script) => {
|
|
|
47
51
|
fs.writeFileSync(pathName, response.data);
|
|
48
52
|
}
|
|
49
53
|
result.localFile = pathName;
|
|
50
|
-
}
|
|
51
|
-
catch (error) {
|
|
54
|
+
} catch (error) {
|
|
52
55
|
result.error = error.message;
|
|
53
56
|
}
|
|
54
57
|
return result;
|