@jahia/cypress 6.4.0 → 6.4.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.
|
@@ -5,17 +5,11 @@
|
|
|
5
5
|
/**
|
|
6
6
|
* Strategy for handling JavaScript errors and warnings in Cypress tests.
|
|
7
7
|
*
|
|
8
|
-
* - failFast: Fail *immediately* when an error is detected.
|
|
9
|
-
*
|
|
10
|
-
* Proc: Allows each test to run, looks for console errors and warnings, and fails the particular test IMMEDIATELY
|
|
11
|
-
* if any issues are found.
|
|
12
|
-
* Cons: If errors or warnings were found during beforeEach or afterEach hook(s), the rest of spec will be ignored.
|
|
13
|
-
*
|
|
14
8
|
* - failAfterEach: Collect all errors and warnings *during test* execution and fail if any issues are found.
|
|
15
9
|
*
|
|
16
|
-
* Proc: Allows each test to run, collects console errors and warnings,
|
|
17
|
-
* if any issues are found.
|
|
18
|
-
* Cons:
|
|
10
|
+
* Proc: Allows each test to run, collects console errors and warnings,
|
|
11
|
+
* and fails the particular test by the end of its execution if any issues are found.
|
|
12
|
+
* Cons: Since the analysis happens in afterEach() hook, the rest of spec will be ignored.
|
|
19
13
|
*
|
|
20
14
|
* - failAfterAll: Collect all errors and warnings *after all tests* and fail at the end of the test suite.
|
|
21
15
|
*
|
|
@@ -25,14 +19,13 @@
|
|
|
25
19
|
* This is because the hook is executed after all tests are completed, so the last test is reported as failed.
|
|
26
20
|
*/
|
|
27
21
|
declare enum STRATEGY {
|
|
28
|
-
|
|
29
|
-
failAfterAll = 1
|
|
30
|
-
failAfterEach = 2
|
|
22
|
+
failAfterEach = 0,
|
|
23
|
+
failAfterAll = 1
|
|
31
24
|
}
|
|
32
25
|
/**
|
|
33
26
|
* Returns the current strategy for handling JavaScript errors and warnings in Cypress tests.
|
|
34
27
|
* @returns {STRATEGY} - The current strategy for handling JavaScript errors and warnings.
|
|
35
|
-
* @note be careful with Cypress.env(envVarStrategy), since it might return `0` for `
|
|
28
|
+
* @note be careful with Cypress.env(envVarStrategy), since it might return `0` for `failAfterEach` strategy,
|
|
36
29
|
* which is falsy in JavaScript, so we need to check if the variable is undefined.
|
|
37
30
|
*/
|
|
38
31
|
declare function getStrategy(): STRATEGY;
|
|
@@ -61,9 +54,8 @@ declare function setAllowedJsWarnings(warnings: string[]): void;
|
|
|
61
54
|
declare function disable(): void;
|
|
62
55
|
/**
|
|
63
56
|
* Attaches custom hooks to Cypress events to monitor and report JavaScript errors and warnings.
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
* and throws an error if any issues are found after all tests are executed.
|
|
57
|
+
* It sets up listeners for console errors and warnings, collects them for each visited URL in each test,
|
|
58
|
+
* and throws an error if any issues are found after each or all tests are executed (depending on the strategy chosen).
|
|
67
59
|
*/
|
|
68
60
|
declare function enable(): void;
|
|
69
61
|
/**
|
|
@@ -20,17 +20,11 @@ var envVarStrategy = '__JS_LOGGER_STRATEGY__';
|
|
|
20
20
|
/**
|
|
21
21
|
* Strategy for handling JavaScript errors and warnings in Cypress tests.
|
|
22
22
|
*
|
|
23
|
-
* - failFast: Fail *immediately* when an error is detected.
|
|
24
|
-
*
|
|
25
|
-
* Proc: Allows each test to run, looks for console errors and warnings, and fails the particular test IMMEDIATELY
|
|
26
|
-
* if any issues are found.
|
|
27
|
-
* Cons: If errors or warnings were found during beforeEach or afterEach hook(s), the rest of spec will be ignored.
|
|
28
|
-
*
|
|
29
23
|
* - failAfterEach: Collect all errors and warnings *during test* execution and fail if any issues are found.
|
|
30
24
|
*
|
|
31
|
-
* Proc: Allows each test to run, collects console errors and warnings,
|
|
32
|
-
* if any issues are found.
|
|
33
|
-
* Cons:
|
|
25
|
+
* Proc: Allows each test to run, collects console errors and warnings,
|
|
26
|
+
* and fails the particular test by the end of its execution if any issues are found.
|
|
27
|
+
* Cons: Since the analysis happens in afterEach() hook, the rest of spec will be ignored.
|
|
34
28
|
*
|
|
35
29
|
* - failAfterAll: Collect all errors and warnings *after all tests* and fail at the end of the test suite.
|
|
36
30
|
*
|
|
@@ -41,18 +35,31 @@ var envVarStrategy = '__JS_LOGGER_STRATEGY__';
|
|
|
41
35
|
*/
|
|
42
36
|
var STRATEGY;
|
|
43
37
|
(function (STRATEGY) {
|
|
44
|
-
STRATEGY[STRATEGY["
|
|
38
|
+
STRATEGY[STRATEGY["failAfterEach"] = 0] = "failAfterEach";
|
|
45
39
|
STRATEGY[STRATEGY["failAfterAll"] = 1] = "failAfterAll";
|
|
46
|
-
STRATEGY[STRATEGY["failAfterEach"] = 2] = "failAfterEach";
|
|
47
40
|
})(STRATEGY || (STRATEGY = {}));
|
|
41
|
+
/**
|
|
42
|
+
* Returns an emoji based on the type of message.
|
|
43
|
+
* @param {string} type
|
|
44
|
+
*/
|
|
45
|
+
function getEmoji(type) {
|
|
46
|
+
switch (type) {
|
|
47
|
+
case 'warn':
|
|
48
|
+
return '⚠️';
|
|
49
|
+
case 'error':
|
|
50
|
+
return '❌️';
|
|
51
|
+
default:
|
|
52
|
+
return '';
|
|
53
|
+
}
|
|
54
|
+
}
|
|
48
55
|
/**
|
|
49
56
|
* Returns the current strategy for handling JavaScript errors and warnings in Cypress tests.
|
|
50
57
|
* @returns {STRATEGY} - The current strategy for handling JavaScript errors and warnings.
|
|
51
|
-
* @note be careful with Cypress.env(envVarStrategy), since it might return `0` for `
|
|
58
|
+
* @note be careful with Cypress.env(envVarStrategy), since it might return `0` for `failAfterEach` strategy,
|
|
52
59
|
* which is falsy in JavaScript, so we need to check if the variable is undefined.
|
|
53
60
|
*/
|
|
54
61
|
function getStrategy() {
|
|
55
|
-
return typeof Cypress.env(envVarStrategy) === 'undefined' ? STRATEGY.
|
|
62
|
+
return typeof Cypress.env(envVarStrategy) === 'undefined' ? STRATEGY.failAfterAll : Cypress.env(envVarStrategy);
|
|
56
63
|
}
|
|
57
64
|
/**
|
|
58
65
|
* Sets the strategy for handling JavaScript errors and warnings in Cypress tests.
|
|
@@ -89,11 +96,14 @@ function getAllowedJsWarnings() { return Cypress.env(envVarAllowedWarnings) || [
|
|
|
89
96
|
function setAllowedJsWarnings(warnings) { Cypress.env(envVarAllowedWarnings, warnings); }
|
|
90
97
|
/**
|
|
91
98
|
* Attaches a custom JavaScript interceptor to capture console errors and warnings.
|
|
92
|
-
* This interceptor is executed before the page is loaded, allowing us to spy on console messages.
|
|
93
99
|
*/
|
|
94
100
|
function attachJsInterceptor() {
|
|
101
|
+
/**
|
|
102
|
+
* Custom 'window:before:load' hook to attach interceptors before the page is loaded,
|
|
103
|
+
* allowing us to spy on console messages.
|
|
104
|
+
*/
|
|
95
105
|
Cypress.on('window:before:load', function (window) {
|
|
96
|
-
// Skip 'window:before:load' hook if the logger is
|
|
106
|
+
// Skip 'window:before:load' hook if the logger is disabled
|
|
97
107
|
if (isDisabled()) {
|
|
98
108
|
return;
|
|
99
109
|
}
|
|
@@ -101,20 +111,34 @@ function attachJsInterceptor() {
|
|
|
101
111
|
cy.spy(window.console, 'error').as('errors');
|
|
102
112
|
cy.spy(window.console, 'warn').as('warnings');
|
|
103
113
|
});
|
|
114
|
+
/**
|
|
115
|
+
* Custom 'window:load' hook to collect JavaScript errors and warnings right after the page is loaded.
|
|
116
|
+
*/
|
|
117
|
+
Cypress.on('window:load', function (win) {
|
|
118
|
+
// Skip 'window:load' hook if the logger is disabled
|
|
119
|
+
if (isDisabled()) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
// Collect issues immediately after the window is loaded and analyze them
|
|
123
|
+
collectIssues(win);
|
|
124
|
+
});
|
|
104
125
|
}
|
|
105
126
|
/**
|
|
106
127
|
* Collects JavaScript errors and warnings using the spies set up in attachJsInterceptor.
|
|
107
128
|
* @returns {Cypress.Chainable} - Cypress chainable object that resolves when issues are collected.
|
|
108
129
|
*/
|
|
109
|
-
function collectIssues() {
|
|
130
|
+
function collectIssues(win) {
|
|
110
131
|
var allowedWarnings = getAllowedJsWarnings();
|
|
111
132
|
var consoleIssues = [];
|
|
133
|
+
var url = win.location.href;
|
|
112
134
|
// Look for console errors and warnings, collected by the spies
|
|
113
135
|
return cy.get('@errors')
|
|
114
136
|
.invoke('getCalls')
|
|
115
137
|
.then(function (errorCalls) {
|
|
116
138
|
// All errors should be collected
|
|
117
|
-
consoleIssues = errorCalls.flatMap(function (call) { return call.args; });
|
|
139
|
+
consoleIssues = errorCalls.flatMap(function (call) { return call.args.map(function (arg) { return ({ type: 'error', msg: String(arg) }); }); });
|
|
140
|
+
})
|
|
141
|
+
.then(function () {
|
|
118
142
|
// Analyze warnings
|
|
119
143
|
cy.get('@warnings')
|
|
120
144
|
.invoke('getCalls')
|
|
@@ -122,7 +146,7 @@ function collectIssues() {
|
|
|
122
146
|
warningCalls.flatMap(function (call) { return call.args; }).forEach(function (arg) {
|
|
123
147
|
// Only warnings not in the allowed list should be collected
|
|
124
148
|
if (!allowedWarnings.some(function (item) { return arg.includes(item); })) {
|
|
125
|
-
consoleIssues.push(arg);
|
|
149
|
+
consoleIssues.push({ type: 'warn', msg: String(arg) });
|
|
126
150
|
}
|
|
127
151
|
});
|
|
128
152
|
});
|
|
@@ -131,13 +155,9 @@ function collectIssues() {
|
|
|
131
155
|
// Update the Cypress environment variable with the collected issues
|
|
132
156
|
if (consoleIssues.length > 0) {
|
|
133
157
|
setCollectedIssues(__spreadArray(__spreadArray([], getCollectedIssues()), [
|
|
134
|
-
{ test: Cypress.currentTest.title, errors: consoleIssues }
|
|
158
|
+
{ url: url, test: Cypress.currentTest.title, errors: consoleIssues }
|
|
135
159
|
]));
|
|
136
160
|
}
|
|
137
|
-
})
|
|
138
|
-
.then(function () {
|
|
139
|
-
// Return a Cypress chainable object to allow chaining
|
|
140
|
-
return cy.wrap(null, { log: false });
|
|
141
161
|
});
|
|
142
162
|
}
|
|
143
163
|
/**
|
|
@@ -147,9 +167,22 @@ function analyzeIssues() {
|
|
|
147
167
|
cy.then(function () {
|
|
148
168
|
var failures = getCollectedIssues();
|
|
149
169
|
if (failures.length > 0) {
|
|
150
|
-
//
|
|
151
|
-
var
|
|
152
|
-
|
|
170
|
+
// Group all issues by test title
|
|
171
|
+
var groupedByTest = failures.reduce(function (acc, failure) {
|
|
172
|
+
acc[failure.test] = acc[failure.test] || [];
|
|
173
|
+
acc[failure.test].push(failure);
|
|
174
|
+
return acc;
|
|
175
|
+
}, {});
|
|
176
|
+
// Format the error message for each test with its collected issues
|
|
177
|
+
var errorMessage = Object.entries(groupedByTest).map(function (_a) {
|
|
178
|
+
var test = _a[0], items = _a[1];
|
|
179
|
+
var urlsAndErrors = items.map(function (item) {
|
|
180
|
+
return "URL: " + item.url + "\nISSUES:\n" + item.errors.map(function (e) { return "- " + (e.type === 'warn' ? getEmoji('warn') : getEmoji('error')) + " " + e.msg; }).join('\n');
|
|
181
|
+
}).join('\n\n');
|
|
182
|
+
// Return the formatted message for the test;
|
|
183
|
+
// Intentionally use fixed-width (50 chars) separators for better readability,
|
|
184
|
+
// when the message might be wrapped
|
|
185
|
+
return getEmoji('error') + "\uFE0F TEST: " + test.trim() + " " + getEmoji('error') + "\uFE0F\n" + '-'.repeat(50) + "\n" + urlsAndErrors + "\n" + '='.repeat(50);
|
|
153
186
|
}).join('\n\n');
|
|
154
187
|
// Reset the collector for the next test run
|
|
155
188
|
setCollectedIssues([]);
|
|
@@ -165,9 +198,8 @@ function analyzeIssues() {
|
|
|
165
198
|
function disable() { Cypress.env(envVarDisableJsLogger, true); }
|
|
166
199
|
/**
|
|
167
200
|
* Attaches custom hooks to Cypress events to monitor and report JavaScript errors and warnings.
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
* and throws an error if any issues are found after all tests are executed.
|
|
201
|
+
* It sets up listeners for console errors and warnings, collects them for each visited URL in each test,
|
|
202
|
+
* and throws an error if any issues are found after each or all tests are executed (depending on the strategy chosen).
|
|
171
203
|
*/
|
|
172
204
|
function enable() {
|
|
173
205
|
// Ensure the logger is enabled by default
|
|
@@ -175,52 +207,27 @@ function enable() {
|
|
|
175
207
|
// Attach errors and warnings collector
|
|
176
208
|
attachJsInterceptor();
|
|
177
209
|
/**
|
|
178
|
-
* Custom 'afterEach' hook to
|
|
179
|
-
* The behavior of this hook depends on the strategy set for the logger.
|
|
210
|
+
* Custom 'afterEach' hook to analyze JavaScript errors and warnings after EACH test execution.
|
|
180
211
|
*/
|
|
181
212
|
afterEach(function () {
|
|
182
|
-
// Skip the hook if the logger is disabled
|
|
183
|
-
if (isDisabled()) {
|
|
213
|
+
// Skip the hook if the logger is disabled or if the strategy is not failAfterEach
|
|
214
|
+
if (isDisabled() || getStrategy() !== STRATEGY.failAfterEach) {
|
|
184
215
|
return;
|
|
185
216
|
}
|
|
186
|
-
//
|
|
187
|
-
|
|
188
|
-
if (getStrategy() === STRATEGY.failAfterEach) {
|
|
189
|
-
// Collect issues after each test and analyze them immediately
|
|
190
|
-
collectIssues().then(function () { return analyzeIssues(); });
|
|
191
|
-
}
|
|
192
|
-
else if (getStrategy() === STRATEGY.failAfterAll) {
|
|
193
|
-
// Collect issues after each test, but analyze them only after all tests are executed
|
|
194
|
-
collectIssues();
|
|
195
|
-
}
|
|
196
|
-
else {
|
|
197
|
-
// Do nothing for failFast strategy, issues will be collected and analyzed in 'window:load' hook
|
|
198
|
-
}
|
|
217
|
+
// Analyze collected errors and warnings
|
|
218
|
+
analyzeIssues();
|
|
199
219
|
});
|
|
200
220
|
/**
|
|
201
|
-
* Custom 'after' hook to analyze
|
|
221
|
+
* Custom 'after' hook to analyze JavaScript errors and warnings after ALL tests execution.
|
|
202
222
|
*/
|
|
203
223
|
after(function () {
|
|
204
224
|
// Skip the hook if the logger is disabled or if the strategy is not failAfterAll
|
|
205
|
-
// This hook is only relevant for the failAfterAll strategy, where we analyze issues after
|
|
206
225
|
if (isDisabled() || getStrategy() !== STRATEGY.failAfterAll) {
|
|
207
226
|
return;
|
|
208
227
|
}
|
|
209
228
|
// Analyze collected errors and warnings
|
|
210
229
|
analyzeIssues();
|
|
211
230
|
});
|
|
212
|
-
/**
|
|
213
|
-
* Custom 'window:load' hook to collect JavaScript errors and warnings right after the page is loaded.
|
|
214
|
-
* Applicable only for the failFast strategy.
|
|
215
|
-
*/
|
|
216
|
-
Cypress.on('window:load', function () {
|
|
217
|
-
// Skip the hook if the logger is disabled or if the strategy is not failFast
|
|
218
|
-
if (isDisabled() || getStrategy() !== STRATEGY.failFast) {
|
|
219
|
-
return;
|
|
220
|
-
}
|
|
221
|
-
// Collect issues immediately after the window is loaded and analyze them
|
|
222
|
-
collectIssues().then(function () { return analyzeIssues(); });
|
|
223
|
-
});
|
|
224
231
|
}
|
|
225
232
|
/**
|
|
226
233
|
* Exports the jsLogger module with methods to attach hooks, enable/disable logging, and set allowed warnings.
|
package/docs/js-errors-logger.md
CHANGED
|
@@ -15,55 +15,20 @@ The JavaScript Errors Logger is a comprehensive monitoring and reporting module
|
|
|
15
15
|
|
|
16
16
|
The logger supports three distinct strategies for handling JavaScript errors and warnings:
|
|
17
17
|
|
|
18
|
-
### 1. Fail
|
|
19
|
-
- **Strategy**: `STRATEGY.failFast`
|
|
20
|
-
- **Behavior**: Fails immediately when an error or warning is detected; the rest of tests will be executed
|
|
21
|
-
- **Use Case**: Best for development environments where immediate feedback is crucial
|
|
22
|
-
- **Pros**: Quick identification of issues
|
|
23
|
-
- **Cons**: May stop test execution on first error, preventing discovery of additional issues
|
|
24
|
-
|
|
25
|
-
### 2. Fail After Each Test
|
|
18
|
+
### 1. Fail After Each Test
|
|
26
19
|
- **Strategy**: `STRATEGY.failAfterEach`
|
|
27
20
|
- **Behavior**: Collects errors/warnings during test execution and fails at the end of the one; the rest of tests will be skipped
|
|
28
21
|
- **Use Case**: Suitable when you want the test to complete but still get immediate feedback
|
|
29
22
|
- **Pros**: Allows test to be executed till the end before providing a report
|
|
30
|
-
- **Cons**:
|
|
23
|
+
- **Cons**: Since the analysis happens in afterEach() hook, the rest of spec will be ignored
|
|
31
24
|
|
|
32
|
-
###
|
|
25
|
+
### 2. Fail After All Tests (default)
|
|
33
26
|
- **Strategy**: `STRATEGY.failAfterAll`
|
|
34
27
|
- **Behavior**: Collects all errors/warnings and reports them after the entire test suite completes; the last test will be marked as failed
|
|
35
28
|
- **Use Case**: Ideal for CI/CD environments where you want a complete test run overview
|
|
36
29
|
- **Pros**: Complete test suite execution with comprehensive error reporting
|
|
37
30
|
- **Cons**: Error reporting may be confusing as the last test will be marked as failed, since the errors analysis and reporting is done in after() hook
|
|
38
31
|
|
|
39
|
-
## Configuration
|
|
40
|
-
|
|
41
|
-
### Environment Variables
|
|
42
|
-
|
|
43
|
-
| Variable | Type | Description |
|
|
44
|
-
|---------------------------------|------|-------------|
|
|
45
|
-
| `JAHIA_HOOKS_DISABLE_JS_LOGGER` | boolean | Disables the logger when set to `true` |
|
|
46
|
-
|
|
47
|
-
### Programmatic Configuration
|
|
48
|
-
|
|
49
|
-
It is **strongly** recommended to add custom configuration in project's common files, e.g. `tests/cypress/support/e2e.js` to have it applied to all test-cases within the project.
|
|
50
|
-
|
|
51
|
-
```typescript
|
|
52
|
-
import { jsErrorsLogger } from '@jahia/cypress';
|
|
53
|
-
|
|
54
|
-
// Attach Logger
|
|
55
|
-
jsErrorsLogger.enable();
|
|
56
|
-
|
|
57
|
-
// Set preferrable error handling strategy
|
|
58
|
-
jsErrorsLogger.setStrategy(jsErrorsLogger.STRATEGY.failFast);
|
|
59
|
-
|
|
60
|
-
// Define allowed warnings that won't trigger failures
|
|
61
|
-
jsErrorsLogger.setAllowedJsWarnings([
|
|
62
|
-
'Warning: React Hook',
|
|
63
|
-
'Warning: componentWillReceiveProps'
|
|
64
|
-
]);
|
|
65
|
-
```
|
|
66
|
-
|
|
67
32
|
## Usage
|
|
68
33
|
|
|
69
34
|
### Basic Setup
|
|
@@ -72,11 +37,10 @@ jsErrorsLogger.setAllowedJsWarnings([
|
|
|
72
37
|
This call should only be used in `tests/cypress/support/e2e.js`. Add the following code in the repo where functionality should be used:
|
|
73
38
|
|
|
74
39
|
```typescript
|
|
75
|
-
import {
|
|
40
|
+
import {jsErrorsLogger} from '@jahia/cypress';
|
|
76
41
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
});
|
|
42
|
+
// Enable and attach JS Errors Logger
|
|
43
|
+
jsErrorsLogger.enable();
|
|
80
44
|
```
|
|
81
45
|
|
|
82
46
|
### Disabling the Logger
|
|
@@ -91,7 +55,7 @@ export JAHIA_HOOKS_DISABLE_JS_LOGGER="true"
|
|
|
91
55
|
#### Disable for the specific Spec
|
|
92
56
|
|
|
93
57
|
```typescript
|
|
94
|
-
import {
|
|
58
|
+
import {jsErrorsLogger} from '@jahia/cypress';
|
|
95
59
|
|
|
96
60
|
describe('Tests with disabled JS logger', () => {
|
|
97
61
|
before(() => {
|
|
@@ -104,35 +68,76 @@ describe('Tests with disabled JS logger', () => {
|
|
|
104
68
|
});
|
|
105
69
|
```
|
|
106
70
|
|
|
71
|
+
## Configuration
|
|
72
|
+
|
|
73
|
+
### Environment Variables
|
|
74
|
+
|
|
75
|
+
| Variable | Type | Description |
|
|
76
|
+
|---------------------------------|------|-------------|
|
|
77
|
+
| `JAHIA_HOOKS_DISABLE_JS_LOGGER` | boolean | Disables the logger when set to `true` |
|
|
78
|
+
|
|
79
|
+
### Programmatic Configuration
|
|
80
|
+
|
|
81
|
+
It is **strongly** recommended to add custom configuration in project's common files, e.g. `tests/cypress/support/e2e.js` to have it applied to all test-cases within the project.
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
import {jsErrorsLogger} from '@jahia/cypress';
|
|
85
|
+
|
|
86
|
+
// Enable and attach JS Errors Logger
|
|
87
|
+
jsErrorsLogger.enable();
|
|
88
|
+
|
|
89
|
+
// Set preferrable error handling strategy
|
|
90
|
+
jsErrorsLogger.setStrategy(jsErrorsLogger.STRATEGY.failAfterAll);
|
|
91
|
+
|
|
92
|
+
// Define allowed warnings that won't trigger failures
|
|
93
|
+
jsErrorsLogger.setAllowedJsWarnings([
|
|
94
|
+
'Warning: React Hook',
|
|
95
|
+
'Warning: componentWillReceiveProps'
|
|
96
|
+
]);
|
|
97
|
+
```
|
|
98
|
+
|
|
107
99
|
## Error Reporting Format
|
|
108
100
|
|
|
109
|
-
### Single Test Error (
|
|
101
|
+
### Single Test Error (failAfterEach)
|
|
110
102
|
|
|
111
103
|
```
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
104
|
+
CONSOLE ERRORS and WARNINGS FOUND:
|
|
105
|
+
|
|
106
|
+
❌️ TEST: Should be authenticated when correct credentials and code are provided: ❌️
|
|
107
|
+
--------------------------------------------------
|
|
108
|
+
URL: http://localhost:8080/jahia/dashboard
|
|
109
|
+
ISSUES:
|
|
110
|
+
- ⚠️ Unsatisfied version 5.0.1 from @jahia/jcontent of shared singleton module redux (required ^4.0.5)
|
|
111
|
+
- ⚠️ Unsatisfied version 9.2.0 from @jahia/jcontent of shared singleton module react-redux (required ^8.0.5)
|
|
112
|
+
- ❌️ TypeError: Cannot read property 'user' of undefined
|
|
115
113
|
```
|
|
116
114
|
|
|
117
115
|
### Multiple Test Errors (failAfterAll)
|
|
118
116
|
|
|
119
117
|
```
|
|
120
|
-
|
|
118
|
+
CONSOLE ERRORS and WARNINGS FOUND:
|
|
121
119
|
|
|
122
|
-
TEST:
|
|
120
|
+
❌️ TEST: Should be authenticated when correct credentials and code are provided: ❌️
|
|
121
|
+
--------------------------------------------------
|
|
122
|
+
URL: http://localhost:8080/jahia/dashboard
|
|
123
123
|
ISSUES:
|
|
124
|
-
-
|
|
125
|
-
-
|
|
126
|
-
|
|
127
|
-
|
|
124
|
+
- ⚠️ No satisfying version (^1.11.9) of shared module dayjs found in shared scope default.
|
|
125
|
+
- ⚠️ No satisfying version (^3.0.6) of shared module @jahia/react-material found in shared scope default.
|
|
126
|
+
- ❌️ TypeError: Cannot read property 'user' of undefined
|
|
127
|
+
==================================================
|
|
128
|
+
|
|
129
|
+
❌️ TEST: Should be authenticated on a specific site when correct credentials and code are provided: ❌️
|
|
130
|
+
--------------------------------------------------
|
|
131
|
+
URL: http://localhost:8080/jahia/admin
|
|
128
132
|
ISSUES:
|
|
129
|
-
-
|
|
133
|
+
- ⚠️ No satisfying version (^3.0.6) of shared module @jahia/react-material found in shared scope default.
|
|
134
|
+
==================================================
|
|
130
135
|
```
|
|
131
136
|
|
|
132
137
|
## Best Practices
|
|
133
138
|
|
|
134
139
|
### Development Environment
|
|
135
|
-
- Use `STRATEGY.
|
|
140
|
+
- Use `STRATEGY.failAfterEach` for immediate feedback during development
|
|
136
141
|
- Configure comprehensive allowed warnings list for known, acceptable warnings
|
|
137
142
|
- Enable detailed logging for debugging purposes
|
|
138
143
|
|
|
@@ -161,4 +166,4 @@ ISSUES:
|
|
|
161
166
|
|
|
162
167
|
| Enum | Values | Description |
|
|
163
168
|
|------|--------|-------------|
|
|
164
|
-
| `STRATEGY` | `
|
|
169
|
+
| `STRATEGY` | `failAfterAll`, `failAfterEach` | Error handling strategies |
|
package/package.json
CHANGED
|
@@ -14,17 +14,11 @@ const envVarStrategy = '__JS_LOGGER_STRATEGY__';
|
|
|
14
14
|
/**
|
|
15
15
|
* Strategy for handling JavaScript errors and warnings in Cypress tests.
|
|
16
16
|
*
|
|
17
|
-
* - failFast: Fail *immediately* when an error is detected.
|
|
18
|
-
*
|
|
19
|
-
* Proc: Allows each test to run, looks for console errors and warnings, and fails the particular test IMMEDIATELY
|
|
20
|
-
* if any issues are found.
|
|
21
|
-
* Cons: If errors or warnings were found during beforeEach or afterEach hook(s), the rest of spec will be ignored.
|
|
22
|
-
*
|
|
23
17
|
* - failAfterEach: Collect all errors and warnings *during test* execution and fail if any issues are found.
|
|
24
18
|
*
|
|
25
|
-
* Proc: Allows each test to run, collects console errors and warnings,
|
|
26
|
-
* if any issues are found.
|
|
27
|
-
* Cons:
|
|
19
|
+
* Proc: Allows each test to run, collects console errors and warnings,
|
|
20
|
+
* and fails the particular test by the end of its execution if any issues are found.
|
|
21
|
+
* Cons: Since the analysis happens in afterEach() hook, the rest of spec will be ignored.
|
|
28
22
|
*
|
|
29
23
|
* - failAfterAll: Collect all errors and warnings *after all tests* and fail at the end of the test suite.
|
|
30
24
|
*
|
|
@@ -33,25 +27,41 @@ const envVarStrategy = '__JS_LOGGER_STRATEGY__';
|
|
|
33
27
|
* Cons: Reporting might be confusing, e.g. - cypress will report the very last test as failed, while many tests might have issues.
|
|
34
28
|
* This is because the hook is executed after all tests are completed, so the last test is reported as failed.
|
|
35
29
|
*/
|
|
36
|
-
enum STRATEGY {
|
|
30
|
+
enum STRATEGY { failAfterEach, failAfterAll }
|
|
37
31
|
|
|
38
32
|
/**
|
|
39
33
|
* Auxiliary type to represent a single item in the collector.
|
|
40
34
|
* It contains the test title and an array of error or warning messages collected during the test.
|
|
41
35
|
*/
|
|
42
36
|
type CollectorItem = {
|
|
37
|
+
url: string; // URL of the current page where the issue was found
|
|
43
38
|
test: string; // The title of the test where the issue was found
|
|
44
|
-
errors: string[]; // Array of error or warning messages collected during the test
|
|
39
|
+
errors: {type: string; msg: string}[]; // Array of error or warning messages collected during the test
|
|
45
40
|
};
|
|
46
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Returns an emoji based on the type of message.
|
|
44
|
+
* @param {string} type
|
|
45
|
+
*/
|
|
46
|
+
function getEmoji(type: string): string {
|
|
47
|
+
switch (type) {
|
|
48
|
+
case 'warn':
|
|
49
|
+
return '⚠️';
|
|
50
|
+
case 'error':
|
|
51
|
+
return '❌️';
|
|
52
|
+
default:
|
|
53
|
+
return '';
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
47
57
|
/**
|
|
48
58
|
* Returns the current strategy for handling JavaScript errors and warnings in Cypress tests.
|
|
49
59
|
* @returns {STRATEGY} - The current strategy for handling JavaScript errors and warnings.
|
|
50
|
-
* @note be careful with Cypress.env(envVarStrategy), since it might return `0` for `
|
|
60
|
+
* @note be careful with Cypress.env(envVarStrategy), since it might return `0` for `failAfterEach` strategy,
|
|
51
61
|
* which is falsy in JavaScript, so we need to check if the variable is undefined.
|
|
52
62
|
*/
|
|
53
63
|
function getStrategy(): STRATEGY {
|
|
54
|
-
return typeof Cypress.env(envVarStrategy) === 'undefined' ? STRATEGY.
|
|
64
|
+
return typeof Cypress.env(envVarStrategy) === 'undefined' ? STRATEGY.failAfterAll : Cypress.env(envVarStrategy);
|
|
55
65
|
}
|
|
56
66
|
|
|
57
67
|
/**
|
|
@@ -95,41 +105,55 @@ function setAllowedJsWarnings(warnings: string[]): void { Cypress.env(envVarAllo
|
|
|
95
105
|
|
|
96
106
|
/**
|
|
97
107
|
* Attaches a custom JavaScript interceptor to capture console errors and warnings.
|
|
98
|
-
* This interceptor is executed before the page is loaded, allowing us to spy on console messages.
|
|
99
108
|
*/
|
|
100
109
|
function attachJsInterceptor(): void {
|
|
110
|
+
/**
|
|
111
|
+
* Custom 'window:before:load' hook to attach interceptors before the page is loaded,
|
|
112
|
+
* allowing us to spy on console messages.
|
|
113
|
+
*/
|
|
101
114
|
Cypress.on('window:before:load', window => {
|
|
102
|
-
// Skip 'window:before:load' hook if the logger is
|
|
115
|
+
// Skip 'window:before:load' hook if the logger is disabled
|
|
103
116
|
if (isDisabled()) { return; }
|
|
104
|
-
|
|
105
117
|
// Spy on console.error and console.warn to capture errors and warnings
|
|
106
118
|
cy.spy(window.console, 'error').as('errors');
|
|
107
119
|
cy.spy(window.console, 'warn').as('warnings');
|
|
108
120
|
});
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Custom 'window:load' hook to collect JavaScript errors and warnings right after the page is loaded.
|
|
124
|
+
*/
|
|
125
|
+
Cypress.on('window:load', win => {
|
|
126
|
+
// Skip 'window:load' hook if the logger is disabled
|
|
127
|
+
if (isDisabled()) { return; }
|
|
128
|
+
// Collect issues immediately after the window is loaded and analyze them
|
|
129
|
+
collectIssues(win);
|
|
130
|
+
});
|
|
109
131
|
}
|
|
110
132
|
|
|
111
133
|
/**
|
|
112
134
|
* Collects JavaScript errors and warnings using the spies set up in attachJsInterceptor.
|
|
113
135
|
* @returns {Cypress.Chainable} - Cypress chainable object that resolves when issues are collected.
|
|
114
136
|
*/
|
|
115
|
-
function collectIssues(): Cypress.Chainable {
|
|
137
|
+
function collectIssues(win: Cypress.AUTWindow): Cypress.Chainable {
|
|
116
138
|
const allowedWarnings = getAllowedJsWarnings();
|
|
117
|
-
let consoleIssues: string[] = [];
|
|
139
|
+
let consoleIssues: {type: string, msg: string}[] = [];
|
|
140
|
+
const url = win.location.href;
|
|
118
141
|
|
|
119
142
|
// Look for console errors and warnings, collected by the spies
|
|
120
143
|
return cy.get('@errors')
|
|
121
144
|
.invoke('getCalls')
|
|
122
145
|
.then(errorCalls => {
|
|
123
146
|
// All errors should be collected
|
|
124
|
-
consoleIssues = errorCalls.flatMap((call: { args:
|
|
125
|
-
|
|
147
|
+
consoleIssues = errorCalls.flatMap((call: { args: unknown[] }) => call.args.map((arg: string) => ({type: 'error', msg: String(arg)})));
|
|
148
|
+
})
|
|
149
|
+
.then(() => {
|
|
126
150
|
// Analyze warnings
|
|
127
151
|
cy.get('@warnings')
|
|
128
152
|
.invoke('getCalls')
|
|
129
153
|
.then(warningCalls => {
|
|
130
|
-
warningCalls.flatMap((call: { args:
|
|
154
|
+
warningCalls.flatMap((call: { args: unknown[] }) => call.args).forEach((arg: string) => {
|
|
131
155
|
// Only warnings not in the allowed list should be collected
|
|
132
|
-
if (!allowedWarnings.some((item: string) => arg.includes(item))) { consoleIssues.push(arg); }
|
|
156
|
+
if (!allowedWarnings.some((item: string) => arg.includes(item))) { consoleIssues.push({type: 'warn', msg: String(arg)}); }
|
|
133
157
|
});
|
|
134
158
|
});
|
|
135
159
|
})
|
|
@@ -138,13 +162,9 @@ function collectIssues(): Cypress.Chainable {
|
|
|
138
162
|
if (consoleIssues.length > 0) {
|
|
139
163
|
setCollectedIssues([
|
|
140
164
|
...getCollectedIssues(),
|
|
141
|
-
{test: Cypress.currentTest.title, errors: consoleIssues}
|
|
165
|
+
{url: url, test: Cypress.currentTest.title, errors: consoleIssues}
|
|
142
166
|
]);
|
|
143
167
|
}
|
|
144
|
-
})
|
|
145
|
-
.then(() => {
|
|
146
|
-
// Return a Cypress chainable object to allow chaining
|
|
147
|
-
return cy.wrap(null, {log: false});
|
|
148
168
|
});
|
|
149
169
|
}
|
|
150
170
|
|
|
@@ -154,11 +174,31 @@ function collectIssues(): Cypress.Chainable {
|
|
|
154
174
|
function analyzeIssues(): void {
|
|
155
175
|
cy.then(() => {
|
|
156
176
|
const failures = getCollectedIssues();
|
|
177
|
+
|
|
157
178
|
if (failures.length > 0) {
|
|
158
|
-
//
|
|
159
|
-
const
|
|
160
|
-
|
|
179
|
+
// Group all issues by test title
|
|
180
|
+
const groupedByTest = failures.reduce((acc: Record<string, CollectorItem[]>, failure) => {
|
|
181
|
+
acc[failure.test] = acc[failure.test] || [];
|
|
182
|
+
acc[failure.test].push(failure);
|
|
183
|
+
|
|
184
|
+
return acc;
|
|
185
|
+
}, {} as Record<string, CollectorItem[]>);
|
|
186
|
+
|
|
187
|
+
// Format the error message for each test with its collected issues
|
|
188
|
+
const errorMessage = Object.entries(groupedByTest).map(([test, items]) => {
|
|
189
|
+
const urlsAndErrors = items.map(item =>
|
|
190
|
+
`URL: ${item.url}\nISSUES:\n${item.errors.map((e: {
|
|
191
|
+
type: string;
|
|
192
|
+
msg: string
|
|
193
|
+
}) => `- ${e.type === 'warn' ? getEmoji('warn') : getEmoji('error')} ${e.msg}`).join('\n')}`
|
|
194
|
+
).join('\n\n');
|
|
195
|
+
|
|
196
|
+
// Return the formatted message for the test;
|
|
197
|
+
// Intentionally use fixed-width (50 chars) separators for better readability,
|
|
198
|
+
// when the message might be wrapped
|
|
199
|
+
return `${getEmoji('error')}️ TEST: ${test.trim()} ${getEmoji('error')}️\n${'-'.repeat(50)}\n${urlsAndErrors}\n${'='.repeat(50)}`;
|
|
161
200
|
}).join('\n\n');
|
|
201
|
+
|
|
162
202
|
// Reset the collector for the next test run
|
|
163
203
|
setCollectedIssues([]);
|
|
164
204
|
|
|
@@ -176,9 +216,8 @@ function disable(): void { Cypress.env(envVarDisableJsLogger, true); }
|
|
|
176
216
|
|
|
177
217
|
/**
|
|
178
218
|
* Attaches custom hooks to Cypress events to monitor and report JavaScript errors and warnings.
|
|
179
|
-
*
|
|
180
|
-
*
|
|
181
|
-
* and throws an error if any issues are found after all tests are executed.
|
|
219
|
+
* It sets up listeners for console errors and warnings, collects them for each visited URL in each test,
|
|
220
|
+
* and throws an error if any issues are found after each or all tests are executed (depending on the strategy chosen).
|
|
182
221
|
*/
|
|
183
222
|
function enable(): void {
|
|
184
223
|
// Ensure the logger is enabled by default
|
|
@@ -188,49 +227,24 @@ function enable(): void {
|
|
|
188
227
|
attachJsInterceptor();
|
|
189
228
|
|
|
190
229
|
/**
|
|
191
|
-
* Custom 'afterEach' hook to
|
|
192
|
-
* The behavior of this hook depends on the strategy set for the logger.
|
|
230
|
+
* Custom 'afterEach' hook to analyze JavaScript errors and warnings after EACH test execution.
|
|
193
231
|
*/
|
|
194
232
|
afterEach(() => {
|
|
195
|
-
// Skip the hook if the logger is disabled
|
|
196
|
-
if (isDisabled()) { return; }
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
// If the strategy is failFast, issues will be collected in 'window:load'
|
|
200
|
-
if (getStrategy() === STRATEGY.failAfterEach) {
|
|
201
|
-
// Collect issues after each test and analyze them immediately
|
|
202
|
-
collectIssues().then(() => analyzeIssues());
|
|
203
|
-
} else if (getStrategy() === STRATEGY.failAfterAll) {
|
|
204
|
-
// Collect issues after each test, but analyze them only after all tests are executed
|
|
205
|
-
collectIssues();
|
|
206
|
-
} else {
|
|
207
|
-
// Do nothing for failFast strategy, issues will be collected and analyzed in 'window:load' hook
|
|
208
|
-
}
|
|
233
|
+
// Skip the hook if the logger is disabled or if the strategy is not failAfterEach
|
|
234
|
+
if (isDisabled() || getStrategy() !== STRATEGY.failAfterEach) { return; }
|
|
235
|
+
// Analyze collected errors and warnings
|
|
236
|
+
analyzeIssues();
|
|
209
237
|
});
|
|
210
238
|
|
|
211
239
|
/**
|
|
212
|
-
* Custom 'after' hook to analyze
|
|
240
|
+
* Custom 'after' hook to analyze JavaScript errors and warnings after ALL tests execution.
|
|
213
241
|
*/
|
|
214
242
|
after(() => {
|
|
215
243
|
// Skip the hook if the logger is disabled or if the strategy is not failAfterAll
|
|
216
|
-
// This hook is only relevant for the failAfterAll strategy, where we analyze issues after
|
|
217
244
|
if (isDisabled() || getStrategy() !== STRATEGY.failAfterAll) { return; }
|
|
218
|
-
|
|
219
245
|
// Analyze collected errors and warnings
|
|
220
246
|
analyzeIssues();
|
|
221
247
|
});
|
|
222
|
-
|
|
223
|
-
/**
|
|
224
|
-
* Custom 'window:load' hook to collect JavaScript errors and warnings right after the page is loaded.
|
|
225
|
-
* Applicable only for the failFast strategy.
|
|
226
|
-
*/
|
|
227
|
-
Cypress.on('window:load', () => {
|
|
228
|
-
// Skip the hook if the logger is disabled or if the strategy is not failFast
|
|
229
|
-
if (isDisabled() || getStrategy() !== STRATEGY.failFast) { return; }
|
|
230
|
-
|
|
231
|
-
// Collect issues immediately after the window is loaded and analyze them
|
|
232
|
-
collectIssues().then(() => analyzeIssues());
|
|
233
|
-
});
|
|
234
248
|
}
|
|
235
249
|
|
|
236
250
|
/**
|