@jahia/cypress 6.4.0 → 6.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,21 +1,11 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Module for monitoring and reporting JavaScript errors and warnings in Cypress tests.
|
|
3
|
-
* Provides methods to enable, disable, and check logger status.
|
|
4
|
-
*/
|
|
5
1
|
/**
|
|
6
2
|
* Strategy for handling JavaScript errors and warnings in Cypress tests.
|
|
7
3
|
*
|
|
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
4
|
* - failAfterEach: Collect all errors and warnings *during test* execution and fail if any issues are found.
|
|
15
5
|
*
|
|
16
|
-
* Proc: Allows each test to run, collects console errors and warnings,
|
|
17
|
-
* if any issues are found.
|
|
18
|
-
* Cons:
|
|
6
|
+
* Proc: Allows each test to run, collects console errors and warnings,
|
|
7
|
+
* and fails the particular test by the end of its execution if any issues are found.
|
|
8
|
+
* Cons: Since the analysis happens in afterEach() hook, the rest of spec will be ignored.
|
|
19
9
|
*
|
|
20
10
|
* - failAfterAll: Collect all errors and warnings *after all tests* and fail at the end of the test suite.
|
|
21
11
|
*
|
|
@@ -25,14 +15,13 @@
|
|
|
25
15
|
* This is because the hook is executed after all tests are completed, so the last test is reported as failed.
|
|
26
16
|
*/
|
|
27
17
|
declare enum STRATEGY {
|
|
28
|
-
|
|
29
|
-
failAfterAll = 1
|
|
30
|
-
failAfterEach = 2
|
|
18
|
+
failAfterEach = 0,
|
|
19
|
+
failAfterAll = 1
|
|
31
20
|
}
|
|
32
21
|
/**
|
|
33
22
|
* Returns the current strategy for handling JavaScript errors and warnings in Cypress tests.
|
|
34
23
|
* @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 `
|
|
24
|
+
* @note be careful with Cypress.env(envVarStrategy), since it might return `0` for `failAfterEach` strategy,
|
|
36
25
|
* which is falsy in JavaScript, so we need to check if the variable is undefined.
|
|
37
26
|
*/
|
|
38
27
|
declare function getStrategy(): STRATEGY;
|
|
@@ -61,9 +50,8 @@ declare function setAllowedJsWarnings(warnings: string[]): void;
|
|
|
61
50
|
declare function disable(): void;
|
|
62
51
|
/**
|
|
63
52
|
* 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.
|
|
53
|
+
* It sets up listeners for console errors and warnings, collects them for each visited URL in each test,
|
|
54
|
+
* and throws an error if any issues are found after each or all tests are executed (depending on the strategy chosen).
|
|
67
55
|
*/
|
|
68
56
|
declare function enable(): void;
|
|
69
57
|
/**
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/* eslint-disable brace-style */
|
|
3
3
|
/* eslint-disable max-statements-per-line */
|
|
4
|
-
/**
|
|
5
|
-
* Module for monitoring and reporting JavaScript errors and warnings in Cypress tests.
|
|
6
|
-
* Provides methods to enable, disable, and check logger status.
|
|
7
|
-
*/
|
|
8
4
|
var __spreadArray = (this && this.__spreadArray) || function (to, from) {
|
|
9
5
|
for (var i = 0, il = from.length, j = to.length; i < il; i++, j++)
|
|
10
6
|
to[j] = from[i];
|
|
@@ -12,6 +8,10 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from) {
|
|
|
12
8
|
};
|
|
13
9
|
exports.__esModule = true;
|
|
14
10
|
exports.jsErrorsLogger = void 0;
|
|
11
|
+
/**
|
|
12
|
+
* Module for monitoring and reporting JavaScript errors and warnings in Cypress tests.
|
|
13
|
+
* Provides methods to enable, disable, and check logger status.
|
|
14
|
+
*/
|
|
15
15
|
var envVarDisableAll = 'JAHIA_HOOKS_DISABLE';
|
|
16
16
|
var envVarDisableJsLogger = 'JAHIA_HOOKS_DISABLE_JS_LOGGER';
|
|
17
17
|
var envVarCollector = '__JS_LOGGER_FAILURES__';
|
|
@@ -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,40 +96,55 @@ 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() {
|
|
95
|
-
|
|
96
|
-
|
|
101
|
+
/**
|
|
102
|
+
* Custom 'window:before:load' hook to attach interceptors before the page is loaded and spy on console messages.
|
|
103
|
+
*/
|
|
104
|
+
cy.on('window:before:load', function (window) {
|
|
105
|
+
// Skip 'window:before:load' hook if the logger is disabled
|
|
97
106
|
if (isDisabled()) {
|
|
98
107
|
return;
|
|
99
108
|
}
|
|
100
|
-
// Spy on console.error and console.warn to capture errors and warnings
|
|
109
|
+
// Spy on console.error and console.warn methods to capture errors and warnings
|
|
101
110
|
cy.spy(window.console, 'error').as('errors');
|
|
102
111
|
cy.spy(window.console, 'warn').as('warnings');
|
|
103
112
|
});
|
|
113
|
+
/**
|
|
114
|
+
* Custom 'window:load' hook to collect JavaScript errors and warnings right after the page is loaded.
|
|
115
|
+
*/
|
|
116
|
+
cy.on('window:load', function (win) {
|
|
117
|
+
// Skip 'window:load' hook if the logger is disabled
|
|
118
|
+
if (isDisabled()) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
// Collect errors and warnings after the page is fully loaded
|
|
122
|
+
collectIssues(win);
|
|
123
|
+
});
|
|
104
124
|
}
|
|
105
125
|
/**
|
|
106
126
|
* Collects JavaScript errors and warnings using the spies set up in attachJsInterceptor.
|
|
107
127
|
* @returns {Cypress.Chainable} - Cypress chainable object that resolves when issues are collected.
|
|
108
128
|
*/
|
|
109
|
-
function collectIssues() {
|
|
110
|
-
var allowedWarnings = getAllowedJsWarnings();
|
|
129
|
+
function collectIssues(win) {
|
|
111
130
|
var consoleIssues = [];
|
|
112
131
|
// Look for console errors and warnings, collected by the spies
|
|
113
132
|
return cy.get('@errors')
|
|
114
133
|
.invoke('getCalls')
|
|
115
134
|
.then(function (errorCalls) {
|
|
116
135
|
// All errors should be collected
|
|
117
|
-
consoleIssues = errorCalls.flatMap(function (call) { return call.args; });
|
|
118
|
-
|
|
119
|
-
|
|
136
|
+
consoleIssues = errorCalls.flatMap(function (call) { return call.args.map(function (arg) { return ({ type: 'error', msg: String(arg) }); }); });
|
|
137
|
+
})
|
|
138
|
+
.then(function () {
|
|
139
|
+
// Analyze warnings - return the chain to maintain proper async flow
|
|
140
|
+
return cy.get('@warnings')
|
|
120
141
|
.invoke('getCalls')
|
|
121
142
|
.then(function (warningCalls) {
|
|
143
|
+
var allowedWarnings = getAllowedJsWarnings();
|
|
122
144
|
warningCalls.flatMap(function (call) { return call.args; }).forEach(function (arg) {
|
|
123
145
|
// Only warnings not in the allowed list should be collected
|
|
124
146
|
if (!allowedWarnings.some(function (item) { return arg.includes(item); })) {
|
|
125
|
-
consoleIssues.push(arg);
|
|
147
|
+
consoleIssues.push({ type: 'warn', msg: String(arg) });
|
|
126
148
|
}
|
|
127
149
|
});
|
|
128
150
|
});
|
|
@@ -131,25 +153,33 @@ function collectIssues() {
|
|
|
131
153
|
// Update the Cypress environment variable with the collected issues
|
|
132
154
|
if (consoleIssues.length > 0) {
|
|
133
155
|
setCollectedIssues(__spreadArray(__spreadArray([], getCollectedIssues()), [
|
|
134
|
-
{ test: Cypress.currentTest.title, errors: consoleIssues }
|
|
156
|
+
{ url: win.location.href, test: Cypress.currentTest.title, errors: consoleIssues }
|
|
135
157
|
]));
|
|
136
158
|
}
|
|
137
|
-
})
|
|
138
|
-
.then(function () {
|
|
139
|
-
// Return a Cypress chainable object to allow chaining
|
|
140
|
-
return cy.wrap(null, { log: false });
|
|
141
159
|
});
|
|
142
160
|
}
|
|
143
161
|
/**
|
|
144
162
|
* Analyzes collected JavaScript errors and warnings and throws an error if any were found.
|
|
145
163
|
*/
|
|
146
164
|
function analyzeIssues() {
|
|
147
|
-
cy.then(function () {
|
|
148
|
-
var failures = getCollectedIssues();
|
|
165
|
+
return cy.wrap(getCollectedIssues()).then(function (failures) {
|
|
149
166
|
if (failures.length > 0) {
|
|
150
|
-
//
|
|
151
|
-
var
|
|
152
|
-
|
|
167
|
+
// Group all issues by test title
|
|
168
|
+
var groupedByTest = failures.reduce(function (acc, failure) {
|
|
169
|
+
acc[failure.test] = acc[failure.test] || [];
|
|
170
|
+
acc[failure.test].push(failure);
|
|
171
|
+
return acc;
|
|
172
|
+
}, {});
|
|
173
|
+
// Format the error message for each test with its collected issues
|
|
174
|
+
var errorMessage = Object.entries(groupedByTest).map(function (_a) {
|
|
175
|
+
var test = _a[0], items = _a[1];
|
|
176
|
+
var urlsAndErrors = items.map(function (item) {
|
|
177
|
+
return "URL: " + item.url + "\nISSUES:\n" + item.errors.map(function (e) { return "- " + (e.type === 'warn' ? getEmoji('warn') : getEmoji('error')) + " " + e.msg; }).join('\n');
|
|
178
|
+
}).join('\n\n');
|
|
179
|
+
// Return the formatted message for the test;
|
|
180
|
+
// Intentionally use fixed-width (50 chars) separators for better readability,
|
|
181
|
+
// when the message might be wrapped
|
|
182
|
+
return getEmoji('error') + "\uFE0F TEST: " + test.trim() + " " + getEmoji('error') + "\uFE0F\n" + '-'.repeat(50) + "\n" + urlsAndErrors + "\n" + '='.repeat(50);
|
|
153
183
|
}).join('\n\n');
|
|
154
184
|
// Reset the collector for the next test run
|
|
155
185
|
setCollectedIssues([]);
|
|
@@ -165,61 +195,41 @@ function analyzeIssues() {
|
|
|
165
195
|
function disable() { Cypress.env(envVarDisableJsLogger, true); }
|
|
166
196
|
/**
|
|
167
197
|
* 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.
|
|
198
|
+
* It sets up listeners for console errors and warnings, collects them for each visited URL in each test,
|
|
199
|
+
* and throws an error if any issues are found after each or all tests are executed (depending on the strategy chosen).
|
|
171
200
|
*/
|
|
172
201
|
function enable() {
|
|
173
|
-
// Ensure the logger is enabled
|
|
202
|
+
// Ensure the logger is enabled
|
|
174
203
|
Cypress.env(envVarDisableJsLogger, false);
|
|
175
|
-
// Attach errors and warnings collector
|
|
176
|
-
attachJsInterceptor();
|
|
177
204
|
/**
|
|
178
|
-
*
|
|
179
|
-
*
|
|
205
|
+
* Attach Cypress hooks forconsole messages collecting before EACH test execution.
|
|
206
|
+
* Use 'beforeEach' hook and local (cy) context instead of global (Cypress) one
|
|
207
|
+
* to ensure proper async flow and avoid events and hooks flakiness.
|
|
180
208
|
*/
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
if (isDisabled()) {
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
// Depending on the strategy, collect issues and analyze them
|
|
187
|
-
// If the strategy is failFast, issues will be collected in 'window:load'
|
|
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
|
-
}
|
|
209
|
+
before(function () {
|
|
210
|
+
attachJsInterceptor();
|
|
199
211
|
});
|
|
200
212
|
/**
|
|
201
|
-
* Custom '
|
|
213
|
+
* Custom 'afterEach' hook to analyze JavaScript errors and warnings after EACH test execution.
|
|
202
214
|
*/
|
|
203
|
-
|
|
204
|
-
// Skip the hook if the logger is disabled or if the strategy is not
|
|
205
|
-
|
|
206
|
-
if (isDisabled() || getStrategy() !== STRATEGY.failAfterAll) {
|
|
215
|
+
afterEach(function () {
|
|
216
|
+
// Skip the hook if the logger is disabled or if the strategy is not failAfterEach
|
|
217
|
+
if (isDisabled() || (getStrategy() !== STRATEGY.failAfterEach)) {
|
|
207
218
|
return;
|
|
208
219
|
}
|
|
209
220
|
// Analyze collected errors and warnings
|
|
210
221
|
analyzeIssues();
|
|
211
222
|
});
|
|
212
223
|
/**
|
|
213
|
-
* Custom '
|
|
214
|
-
* Applicable only for the failFast strategy.
|
|
224
|
+
* Custom 'after' hook to analyze JavaScript errors and warnings after ALL tests execution.
|
|
215
225
|
*/
|
|
216
|
-
|
|
217
|
-
// Skip the hook if the logger is disabled or if the strategy is not
|
|
218
|
-
if (isDisabled() || getStrategy() !== STRATEGY.
|
|
226
|
+
after(function () {
|
|
227
|
+
// Skip the hook if the logger is disabled or if the strategy is not failAfterAll
|
|
228
|
+
if (isDisabled() || (getStrategy() !== STRATEGY.failAfterAll)) {
|
|
219
229
|
return;
|
|
220
230
|
}
|
|
221
|
-
//
|
|
222
|
-
|
|
231
|
+
// Analyze collected errors and warnings
|
|
232
|
+
analyzeIssues();
|
|
223
233
|
});
|
|
224
234
|
}
|
|
225
235
|
/**
|
package/docs/js-errors-logger.md
CHANGED
|
@@ -15,53 +15,59 @@ 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
|
-
|
|
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.
|
|
31
|
+
- **Hint:** To make reporting less confusing, dummy test can be added by the end of the spec to provide more clarity on why the spec failed, e.g:
|
|
50
32
|
|
|
51
33
|
```typescript
|
|
52
|
-
|
|
34
|
+
describe('Tests for the UI module', () => {
|
|
35
|
+
it('Should validate flow A', () => { ... });
|
|
53
36
|
|
|
54
|
-
|
|
55
|
-
jsErrorsLogger.enable();
|
|
37
|
+
it('Should validate flow B', () => { ... });
|
|
56
38
|
|
|
57
|
-
|
|
58
|
-
|
|
39
|
+
it('Should validate flow C', () => { ... });
|
|
40
|
+
...
|
|
59
41
|
|
|
60
|
-
//
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
'
|
|
64
|
-
|
|
42
|
+
// Dummy test to fail if any errors or warnings appear in the browser console,
|
|
43
|
+
// providing clearer insight into execution and failure reasons.
|
|
44
|
+
// Analysis itself will happen inside jsErrorsLogger module (if one is enabled).
|
|
45
|
+
it('Should ensure errors and warnings absense in browser console', () => {
|
|
46
|
+
cy.log('Analyze console messages');
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
Say, there were JavaScript errors and warnings in all tests. Without that dummy test, the very last test in spec will be marked by Cypress as failed, even though it might pass:
|
|
51
|
+
```
|
|
52
|
+
✓ Should validate flow A
|
|
53
|
+
✓ Should validate flow B
|
|
54
|
+
- Should validate flow C
|
|
55
|
+
... <errors/warnings list for each visited url, grouped by test> ...
|
|
56
|
+
```
|
|
57
|
+
But with that dummy test it will be much clearer what exactly happened:
|
|
58
|
+
```
|
|
59
|
+
✓ Should validate flow A
|
|
60
|
+
✓ Should validate flow B
|
|
61
|
+
✓ Should validate flow C
|
|
62
|
+
- Should ensure errors and warnings absence in browser console during spec execution
|
|
63
|
+
... <errors/warnings list for each visited url, grouped by test> ...
|
|
64
|
+
```
|
|
65
|
+
And in case of JavaScript errors and warnings absense (and all tests passed) it will also be much clearer - what validations were performed:
|
|
66
|
+
```
|
|
67
|
+
✓ Should validate flow A
|
|
68
|
+
✓ Should validate flow B
|
|
69
|
+
✓ Should validate flow C
|
|
70
|
+
✓ Should ensure errors and warnings absense in browser console during spec execution
|
|
65
71
|
```
|
|
66
72
|
|
|
67
73
|
## Usage
|
|
@@ -72,11 +78,10 @@ jsErrorsLogger.setAllowedJsWarnings([
|
|
|
72
78
|
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
79
|
|
|
74
80
|
```typescript
|
|
75
|
-
import {
|
|
81
|
+
import {jsErrorsLogger} from '@jahia/cypress';
|
|
76
82
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
});
|
|
83
|
+
// Enable and attach JS Errors Logger
|
|
84
|
+
jsErrorsLogger.enable();
|
|
80
85
|
```
|
|
81
86
|
|
|
82
87
|
### Disabling the Logger
|
|
@@ -91,7 +96,7 @@ export JAHIA_HOOKS_DISABLE_JS_LOGGER="true"
|
|
|
91
96
|
#### Disable for the specific Spec
|
|
92
97
|
|
|
93
98
|
```typescript
|
|
94
|
-
import {
|
|
99
|
+
import {jsErrorsLogger} from '@jahia/cypress';
|
|
95
100
|
|
|
96
101
|
describe('Tests with disabled JS logger', () => {
|
|
97
102
|
before(() => {
|
|
@@ -104,35 +109,76 @@ describe('Tests with disabled JS logger', () => {
|
|
|
104
109
|
});
|
|
105
110
|
```
|
|
106
111
|
|
|
112
|
+
## Configuration
|
|
113
|
+
|
|
114
|
+
### Environment Variables
|
|
115
|
+
|
|
116
|
+
| Variable | Type | Description |
|
|
117
|
+
|---------------------------------|------|-------------|
|
|
118
|
+
| `JAHIA_HOOKS_DISABLE_JS_LOGGER` | boolean | Disables the logger when set to `true` |
|
|
119
|
+
|
|
120
|
+
### Programmatic Configuration
|
|
121
|
+
|
|
122
|
+
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.
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
import {jsErrorsLogger} from '@jahia/cypress';
|
|
126
|
+
|
|
127
|
+
// Enable and attach JS Errors Logger
|
|
128
|
+
jsErrorsLogger.enable();
|
|
129
|
+
|
|
130
|
+
// Set preferrable error handling strategy
|
|
131
|
+
jsErrorsLogger.setStrategy(jsErrorsLogger.STRATEGY.failAfterAll);
|
|
132
|
+
|
|
133
|
+
// Define allowed warnings that won't trigger failures
|
|
134
|
+
jsErrorsLogger.setAllowedJsWarnings([
|
|
135
|
+
'Warning: React Hook',
|
|
136
|
+
'Warning: componentWillReceiveProps'
|
|
137
|
+
]);
|
|
138
|
+
```
|
|
139
|
+
|
|
107
140
|
## Error Reporting Format
|
|
108
141
|
|
|
109
|
-
### Single Test Error (
|
|
142
|
+
### Single Test Error (failAfterEach)
|
|
110
143
|
|
|
111
144
|
```
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
145
|
+
CONSOLE ERRORS and WARNINGS FOUND:
|
|
146
|
+
|
|
147
|
+
❌️ TEST: Should be authenticated when correct credentials and code are provided: ❌️
|
|
148
|
+
--------------------------------------------------
|
|
149
|
+
URL: http://localhost:8080/jahia/dashboard
|
|
150
|
+
ISSUES:
|
|
151
|
+
- ⚠️ Unsatisfied version 5.0.1 from @jahia/jcontent of shared singleton module redux (required ^4.0.5)
|
|
152
|
+
- ⚠️ Unsatisfied version 9.2.0 from @jahia/jcontent of shared singleton module react-redux (required ^8.0.5)
|
|
153
|
+
- ❌️ TypeError: Cannot read property 'user' of undefined
|
|
115
154
|
```
|
|
116
155
|
|
|
117
156
|
### Multiple Test Errors (failAfterAll)
|
|
118
157
|
|
|
119
158
|
```
|
|
120
|
-
|
|
159
|
+
CONSOLE ERRORS and WARNINGS FOUND:
|
|
121
160
|
|
|
122
|
-
TEST:
|
|
161
|
+
❌️ TEST: Should be authenticated when correct credentials and code are provided: ❌️
|
|
162
|
+
--------------------------------------------------
|
|
163
|
+
URL: http://localhost:8080/jahia/dashboard
|
|
123
164
|
ISSUES:
|
|
124
|
-
-
|
|
125
|
-
-
|
|
126
|
-
|
|
127
|
-
|
|
165
|
+
- ⚠️ No satisfying version (^1.11.9) of shared module dayjs found in shared scope default.
|
|
166
|
+
- ⚠️ No satisfying version (^3.0.6) of shared module @jahia/react-material found in shared scope default.
|
|
167
|
+
- ❌️ TypeError: Cannot read property 'user' of undefined
|
|
168
|
+
==================================================
|
|
169
|
+
|
|
170
|
+
❌️ TEST: Should be authenticated on a specific site when correct credentials and code are provided: ❌️
|
|
171
|
+
--------------------------------------------------
|
|
172
|
+
URL: http://localhost:8080/jahia/admin
|
|
128
173
|
ISSUES:
|
|
129
|
-
-
|
|
174
|
+
- ⚠️ No satisfying version (^3.0.6) of shared module @jahia/react-material found in shared scope default.
|
|
175
|
+
==================================================
|
|
130
176
|
```
|
|
131
177
|
|
|
132
178
|
## Best Practices
|
|
133
179
|
|
|
134
180
|
### Development Environment
|
|
135
|
-
- Use `STRATEGY.
|
|
181
|
+
- Use `STRATEGY.failAfterEach` for immediate feedback during development
|
|
136
182
|
- Configure comprehensive allowed warnings list for known, acceptable warnings
|
|
137
183
|
- Enable detailed logging for debugging purposes
|
|
138
184
|
|
|
@@ -161,4 +207,4 @@ ISSUES:
|
|
|
161
207
|
|
|
162
208
|
| Enum | Values | Description |
|
|
163
209
|
|------|--------|-------------|
|
|
164
|
-
| `STRATEGY` | `
|
|
210
|
+
| `STRATEGY` | `failAfterAll`, `failAfterEach` | Error handling strategies |
|
package/package.json
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* eslint-disable brace-style */
|
|
2
2
|
/* eslint-disable max-statements-per-line */
|
|
3
|
+
|
|
3
4
|
/**
|
|
4
5
|
* Module for monitoring and reporting JavaScript errors and warnings in Cypress tests.
|
|
5
6
|
* Provides methods to enable, disable, and check logger status.
|
|
@@ -14,17 +15,11 @@ const envVarStrategy = '__JS_LOGGER_STRATEGY__';
|
|
|
14
15
|
/**
|
|
15
16
|
* Strategy for handling JavaScript errors and warnings in Cypress tests.
|
|
16
17
|
*
|
|
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
18
|
* - failAfterEach: Collect all errors and warnings *during test* execution and fail if any issues are found.
|
|
24
19
|
*
|
|
25
|
-
* Proc: Allows each test to run, collects console errors and warnings,
|
|
26
|
-
* if any issues are found.
|
|
27
|
-
* Cons:
|
|
20
|
+
* Proc: Allows each test to run, collects console errors and warnings,
|
|
21
|
+
* and fails the particular test by the end of its execution if any issues are found.
|
|
22
|
+
* Cons: Since the analysis happens in afterEach() hook, the rest of spec will be ignored.
|
|
28
23
|
*
|
|
29
24
|
* - failAfterAll: Collect all errors and warnings *after all tests* and fail at the end of the test suite.
|
|
30
25
|
*
|
|
@@ -33,25 +28,41 @@ const envVarStrategy = '__JS_LOGGER_STRATEGY__';
|
|
|
33
28
|
* Cons: Reporting might be confusing, e.g. - cypress will report the very last test as failed, while many tests might have issues.
|
|
34
29
|
* This is because the hook is executed after all tests are completed, so the last test is reported as failed.
|
|
35
30
|
*/
|
|
36
|
-
enum STRATEGY {
|
|
31
|
+
enum STRATEGY { failAfterEach, failAfterAll }
|
|
37
32
|
|
|
38
33
|
/**
|
|
39
34
|
* Auxiliary type to represent a single item in the collector.
|
|
40
35
|
* It contains the test title and an array of error or warning messages collected during the test.
|
|
41
36
|
*/
|
|
42
37
|
type CollectorItem = {
|
|
38
|
+
url: string; // URL of the current page where the issue was found
|
|
43
39
|
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
|
|
40
|
+
errors: {type: string; msg: string}[]; // Array of error or warning messages collected during the test
|
|
45
41
|
};
|
|
46
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Returns an emoji based on the type of message.
|
|
45
|
+
* @param {string} type
|
|
46
|
+
*/
|
|
47
|
+
function getEmoji(type: string): string {
|
|
48
|
+
switch (type) {
|
|
49
|
+
case 'warn':
|
|
50
|
+
return '⚠️';
|
|
51
|
+
case 'error':
|
|
52
|
+
return '❌️';
|
|
53
|
+
default:
|
|
54
|
+
return '';
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
47
58
|
/**
|
|
48
59
|
* Returns the current strategy for handling JavaScript errors and warnings in Cypress tests.
|
|
49
60
|
* @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 `
|
|
61
|
+
* @note be careful with Cypress.env(envVarStrategy), since it might return `0` for `failAfterEach` strategy,
|
|
51
62
|
* which is falsy in JavaScript, so we need to check if the variable is undefined.
|
|
52
63
|
*/
|
|
53
64
|
function getStrategy(): STRATEGY {
|
|
54
|
-
return typeof Cypress.env(envVarStrategy) === 'undefined' ? STRATEGY.
|
|
65
|
+
return typeof Cypress.env(envVarStrategy) === 'undefined' ? STRATEGY.failAfterAll : Cypress.env(envVarStrategy);
|
|
55
66
|
}
|
|
56
67
|
|
|
57
68
|
/**
|
|
@@ -95,41 +106,53 @@ function setAllowedJsWarnings(warnings: string[]): void { Cypress.env(envVarAllo
|
|
|
95
106
|
|
|
96
107
|
/**
|
|
97
108
|
* 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
109
|
*/
|
|
100
110
|
function attachJsInterceptor(): void {
|
|
101
|
-
|
|
102
|
-
|
|
111
|
+
/**
|
|
112
|
+
* Custom 'window:before:load' hook to attach interceptors before the page is loaded and spy on console messages.
|
|
113
|
+
*/
|
|
114
|
+
cy.on('window:before:load', window => {
|
|
115
|
+
// Skip 'window:before:load' hook if the logger is disabled
|
|
103
116
|
if (isDisabled()) { return; }
|
|
104
|
-
|
|
105
|
-
// Spy on console.error and console.warn to capture errors and warnings
|
|
117
|
+
// Spy on console.error and console.warn methods 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
|
+
cy.on('window:load', win => {
|
|
126
|
+
// Skip 'window:load' hook if the logger is disabled
|
|
127
|
+
if (isDisabled()) { return; }
|
|
128
|
+
// Collect errors and warnings after the page is fully loaded
|
|
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 {
|
|
116
|
-
|
|
117
|
-
let consoleIssues: string[] = [];
|
|
137
|
+
function collectIssues(win: Cypress.AUTWindow): Cypress.Chainable {
|
|
138
|
+
let consoleIssues: {type: string, msg: string}[] = [];
|
|
118
139
|
|
|
119
140
|
// Look for console errors and warnings, collected by the spies
|
|
120
141
|
return cy.get('@errors')
|
|
121
142
|
.invoke('getCalls')
|
|
122
143
|
.then(errorCalls => {
|
|
123
144
|
// All errors should be collected
|
|
124
|
-
consoleIssues = errorCalls.flatMap((call: { args:
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
145
|
+
consoleIssues = errorCalls.flatMap((call: { args: unknown[] }) => call.args.map((arg: string) => ({type: 'error', msg: String(arg)})));
|
|
146
|
+
})
|
|
147
|
+
.then(() => {
|
|
148
|
+
// Analyze warnings - return the chain to maintain proper async flow
|
|
149
|
+
return cy.get('@warnings')
|
|
128
150
|
.invoke('getCalls')
|
|
129
151
|
.then(warningCalls => {
|
|
130
|
-
|
|
152
|
+
const allowedWarnings = getAllowedJsWarnings();
|
|
153
|
+
warningCalls.flatMap((call: { args: unknown[] }) => call.args).forEach((arg: string) => {
|
|
131
154
|
// Only warnings not in the allowed list should be collected
|
|
132
|
-
if (!allowedWarnings.some((item: string) => arg.includes(item))) { consoleIssues.push(arg); }
|
|
155
|
+
if (!allowedWarnings.some((item: string) => arg.includes(item))) { consoleIssues.push({type: 'warn', msg: String(arg)}); }
|
|
133
156
|
});
|
|
134
157
|
});
|
|
135
158
|
})
|
|
@@ -138,27 +161,41 @@ function collectIssues(): Cypress.Chainable {
|
|
|
138
161
|
if (consoleIssues.length > 0) {
|
|
139
162
|
setCollectedIssues([
|
|
140
163
|
...getCollectedIssues(),
|
|
141
|
-
{test: Cypress.currentTest.title, errors: consoleIssues}
|
|
164
|
+
{url: win.location.href, test: Cypress.currentTest.title, errors: consoleIssues}
|
|
142
165
|
]);
|
|
143
166
|
}
|
|
144
|
-
})
|
|
145
|
-
.then(() => {
|
|
146
|
-
// Return a Cypress chainable object to allow chaining
|
|
147
|
-
return cy.wrap(null, {log: false});
|
|
148
167
|
});
|
|
149
168
|
}
|
|
150
169
|
|
|
151
170
|
/**
|
|
152
171
|
* Analyzes collected JavaScript errors and warnings and throws an error if any were found.
|
|
153
172
|
*/
|
|
154
|
-
function analyzeIssues():
|
|
155
|
-
cy.
|
|
156
|
-
const failures = getCollectedIssues();
|
|
173
|
+
function analyzeIssues(): Cypress.Chainable {
|
|
174
|
+
return cy.wrap(getCollectedIssues()).then(failures => {
|
|
157
175
|
if (failures.length > 0) {
|
|
158
|
-
//
|
|
159
|
-
const
|
|
160
|
-
|
|
176
|
+
// Group all issues by test title
|
|
177
|
+
const groupedByTest = failures.reduce((acc: Record<string, CollectorItem[]>, failure) => {
|
|
178
|
+
acc[failure.test] = acc[failure.test] || [];
|
|
179
|
+
acc[failure.test].push(failure);
|
|
180
|
+
|
|
181
|
+
return acc;
|
|
182
|
+
}, {} as Record<string, CollectorItem[]>);
|
|
183
|
+
|
|
184
|
+
// Format the error message for each test with its collected issues
|
|
185
|
+
const errorMessage = Object.entries(groupedByTest).map(([test, items]) => {
|
|
186
|
+
const urlsAndErrors = items.map(item =>
|
|
187
|
+
`URL: ${item.url}\nISSUES:\n${item.errors.map((e: {
|
|
188
|
+
type: string;
|
|
189
|
+
msg: string
|
|
190
|
+
}) => `- ${e.type === 'warn' ? getEmoji('warn') : getEmoji('error')} ${e.msg}`).join('\n')}`
|
|
191
|
+
).join('\n\n');
|
|
192
|
+
|
|
193
|
+
// Return the formatted message for the test;
|
|
194
|
+
// Intentionally use fixed-width (50 chars) separators for better readability,
|
|
195
|
+
// when the message might be wrapped
|
|
196
|
+
return `${getEmoji('error')}️ TEST: ${test.trim()} ${getEmoji('error')}️\n${'-'.repeat(50)}\n${urlsAndErrors}\n${'='.repeat(50)}`;
|
|
161
197
|
}).join('\n\n');
|
|
198
|
+
|
|
162
199
|
// Reset the collector for the next test run
|
|
163
200
|
setCollectedIssues([]);
|
|
164
201
|
|
|
@@ -176,60 +213,40 @@ function disable(): void { Cypress.env(envVarDisableJsLogger, true); }
|
|
|
176
213
|
|
|
177
214
|
/**
|
|
178
215
|
* 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.
|
|
216
|
+
* It sets up listeners for console errors and warnings, collects them for each visited URL in each test,
|
|
217
|
+
* and throws an error if any issues are found after each or all tests are executed (depending on the strategy chosen).
|
|
182
218
|
*/
|
|
183
219
|
function enable(): void {
|
|
184
|
-
// Ensure the logger is enabled
|
|
220
|
+
// Ensure the logger is enabled
|
|
185
221
|
Cypress.env(envVarDisableJsLogger, false);
|
|
186
222
|
|
|
187
|
-
// Attach errors and warnings collector
|
|
188
|
-
attachJsInterceptor();
|
|
189
|
-
|
|
190
223
|
/**
|
|
191
|
-
*
|
|
192
|
-
*
|
|
224
|
+
* Attach Cypress hooks forconsole messages collecting before EACH test execution.
|
|
225
|
+
* Use 'beforeEach' hook and local (cy) context instead of global (Cypress) one
|
|
226
|
+
* to ensure proper async flow and avoid events and hooks flakiness.
|
|
193
227
|
*/
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
if (isDisabled()) { return; }
|
|
197
|
-
|
|
198
|
-
// Depending on the strategy, collect issues and analyze them
|
|
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
|
-
}
|
|
228
|
+
before(() => {
|
|
229
|
+
attachJsInterceptor();
|
|
209
230
|
});
|
|
210
231
|
|
|
211
232
|
/**
|
|
212
|
-
* Custom '
|
|
233
|
+
* Custom 'afterEach' hook to analyze JavaScript errors and warnings after EACH test execution.
|
|
213
234
|
*/
|
|
214
|
-
|
|
215
|
-
// Skip the hook if the logger is disabled or if the strategy is not
|
|
216
|
-
|
|
217
|
-
if (isDisabled() || getStrategy() !== STRATEGY.failAfterAll) { return; }
|
|
218
|
-
|
|
235
|
+
afterEach(() => {
|
|
236
|
+
// Skip the hook if the logger is disabled or if the strategy is not failAfterEach
|
|
237
|
+
if (isDisabled() || (getStrategy() !== STRATEGY.failAfterEach)) { return; }
|
|
219
238
|
// Analyze collected errors and warnings
|
|
220
239
|
analyzeIssues();
|
|
221
240
|
});
|
|
222
241
|
|
|
223
242
|
/**
|
|
224
|
-
* Custom '
|
|
225
|
-
* Applicable only for the failFast strategy.
|
|
243
|
+
* Custom 'after' hook to analyze JavaScript errors and warnings after ALL tests execution.
|
|
226
244
|
*/
|
|
227
|
-
|
|
228
|
-
// Skip the hook if the logger is disabled or if the strategy is not
|
|
229
|
-
if (isDisabled() || getStrategy() !== STRATEGY.
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
collectIssues().then(() => analyzeIssues());
|
|
245
|
+
after(() => {
|
|
246
|
+
// Skip the hook if the logger is disabled or if the strategy is not failAfterAll
|
|
247
|
+
if (isDisabled() || (getStrategy() !== STRATEGY.failAfterAll)) { return; }
|
|
248
|
+
// Analyze collected errors and warnings
|
|
249
|
+
analyzeIssues();
|
|
233
250
|
});
|
|
234
251
|
}
|
|
235
252
|
|