@jahia/cypress 6.3.1 → 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.
@@ -15,77 +15,51 @@ 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 Fast (Default)
19
- - **Strategy**: `STRATEGY.failFast`
20
- - **Behavior**: Fails immediately when an error or warning is detected
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
- - **Behavior**: Collects errors/warnings during test execution and fails at the end of each test
28
- - **Use Case**: Suitable when you want each test to complete but still get immediate feedback
29
- - **Pros**: Allows full test execution while providing per-test feedback
30
- - **Cons**: If errors occur in hooks (beforeEach/afterEach), remaining spec tests will be skipped
20
+ - **Behavior**: Collects errors/warnings during test execution and fails at the end of the one; the rest of tests will be skipped
21
+ - **Use Case**: Suitable when you want the test to complete but still get immediate feedback
22
+ - **Pros**: Allows test to be executed till the end before providing a report
23
+ - **Cons**: Since the analysis happens in afterEach() hook, the rest of spec will be ignored
31
24
 
32
- ### 3. Fail After All Tests
25
+ ### 2. Fail After All Tests (default)
33
26
  - **Strategy**: `STRATEGY.failAfterAll`
34
- - **Behavior**: Collects all errors/warnings and reports them after the entire test suite completes
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
- | `JS_LOGGER_DISABLED` | boolean | Disables the logger when set to `true` |
32
+ ## Usage
46
33
 
47
- ### Programmatic Configuration
34
+ ### Basic Setup
48
35
 
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.
36
+ #### Enable the Logger for the repo
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:
50
38
 
51
39
  ```typescript
52
- import { jsErrorsLogger } from '@jahia/cypress';
53
-
54
- // Set preferrable error handling strategy
55
- jsErrorsLogger.setStrategy(jsErrorsLogger.STRATEGY.failAfterEach);
40
+ import {jsErrorsLogger} from '@jahia/cypress';
56
41
 
57
- // Define allowed warnings that won't trigger failures
58
- jsErrorsLogger.setAllowedJsWarnings([
59
- 'Warning: React Hook',
60
- 'Warning: componentWillReceiveProps'
61
- ]);
42
+ // Enable and attach JS Errors Logger
43
+ jsErrorsLogger.enable();
62
44
  ```
63
45
 
64
- ## Usage
65
-
66
- ### Basic Setup
67
-
68
- The logger is automatically initialized when jahia-cypress is imported and registered in the Cypress support files. No manual setup is required for basic functionality.
69
-
70
46
  ### Disabling the Logger
71
47
 
72
48
  #### Via Environment Variable
73
49
 
74
50
  ```bash
75
51
  # Disable in CI/CD or specific environments
76
- export JS_LOGGER_DISABLED=true
52
+ export JAHIA_HOOKS_DISABLE_JS_LOGGER="true"
77
53
  ```
78
54
 
79
- #### Temporarily in Tests
55
+ #### Disable for the specific Spec
80
56
 
81
57
  ```typescript
58
+ import {jsErrorsLogger} from '@jahia/cypress';
59
+
82
60
  describe('Tests with disabled JS logger', () => {
83
61
  before(() => {
84
- Cypress.env('JS_LOGGER_DISABLED', true);
85
- });
86
-
87
- after(() => {
88
- Cypress.env('JS_LOGGER_DISABLED', false);
62
+ jsErrorsLogger.disable();
89
63
  });
90
64
 
91
65
  it('should run without JS error monitoring', () => {
@@ -94,35 +68,76 @@ describe('Tests with disabled JS logger', () => {
94
68
  });
95
69
  ```
96
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
+
97
99
  ## Error Reporting Format
98
100
 
99
- ### Single Test Error (failFast/failAfterEach)
101
+ ### Single Test Error (failAfterEach)
100
102
 
101
103
  ```
102
- Error: CONSOLE ERRORS and WARNINGS FOUND:
103
- - TypeError: Cannot read property 'foo' of undefined
104
- - Warning: React Hook useEffect has missing dependency
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
105
113
  ```
106
114
 
107
115
  ### Multiple Test Errors (failAfterAll)
108
116
 
109
117
  ```
110
- Error: CONSOLE ERRORS and WARNINGS FOUND:
118
+ CONSOLE ERRORS and WARNINGS FOUND:
111
119
 
112
- TEST: should load homepage
120
+ ❌️ TEST: Should be authenticated when correct credentials and code are provided: ❌️
121
+ --------------------------------------------------
122
+ URL: http://localhost:8080/jahia/dashboard
113
123
  ISSUES:
114
- - TypeError: Cannot read property 'user' of undefined
115
- - Warning: componentWillMount is deprecated
116
-
117
- TEST: should handle form submission
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
118
132
  ISSUES:
119
- - ReferenceError: handleSubmit is not defined
133
+ - ⚠️ No satisfying version (^3.0.6) of shared module @jahia/react-material found in shared scope default.
134
+ ==================================================
120
135
  ```
121
136
 
122
137
  ## Best Practices
123
138
 
124
139
  ### Development Environment
125
- - Use `STRATEGY.failFast` for immediate feedback during development
140
+ - Use `STRATEGY.failAfterEach` for immediate feedback during development
126
141
  - Configure comprehensive allowed warnings list for known, acceptable warnings
127
142
  - Enable detailed logging for debugging purposes
128
143
 
@@ -140,13 +155,15 @@ ISSUES:
140
155
 
141
156
  ### Methods
142
157
 
143
- | Method | Parameters | Return Type | Description |
144
- |--------|------------|-------------|-------------|
158
+ | Method | Parameters | Return Type | Description |
159
+ |--------|------------|-------------|--------------------------------------------------------|
145
160
  | `setStrategy(strategy)` | STRATEGY enum | void | Sets the error handling strategy |
146
- | `setAllowedJsWarnings(warnings)` | string[] | void | Configures allowed warning messages |
161
+ | `setAllowedJsWarnings(warnings)` | string[] | void | Configures allowed warning messages |
162
+ | `disable()` | - | void | Disables the logger for the current spec |
163
+ | `enable()` | - | void | Enables the logger for the current repo |
147
164
 
148
165
  ### Enums
149
166
 
150
167
  | Enum | Values | Description |
151
168
  |------|--------|-------------|
152
- | `STRATEGY` | `failFast`, `failAfterAll`, `failAfterEach` | Error handling strategies |
169
+ | `STRATEGY` | `failAfterAll`, `failAfterEach` | Error handling strategies |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jahia/cypress",
3
- "version": "6.3.1",
3
+ "version": "6.4.1",
4
4
  "scripts": {
5
5
  "build": "tsc",
6
6
  "lint": "eslint src -c .eslintrc.json --ext .ts --max-warnings=0"
@@ -5,27 +5,63 @@
5
5
  * Provides methods to enable, disable, and check logger status.
6
6
  */
7
7
 
8
- const envVarDisabled = 'JS_LOGGER_DISABLED';
8
+ const envVarDisableAll = 'JAHIA_HOOKS_DISABLE';
9
+ const envVarDisableJsLogger = 'JAHIA_HOOKS_DISABLE_JS_LOGGER';
9
10
  const envVarCollector = '__JS_LOGGER_FAILURES__';
10
11
  const envVarAllowedWarnings = '__JS_LOGGER_ALLOWED_WARNINGS__';
11
12
  const envVarStrategy = '__JS_LOGGER_STRATEGY__';
12
13
 
13
14
  /**
14
15
  * Strategy for handling JavaScript errors and warnings in Cypress tests.
15
- * - failFast: Fail *immediately* when an error is detected.
16
+ *
16
17
  * - failAfterEach: Collect all errors and warnings *during test* execution and fail if any issues are found.
18
+ *
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.
22
+ *
17
23
  * - failAfterAll: Collect all errors and warnings *after all tests* and fail at the end of the test suite.
24
+ *
25
+ * Proc: Allows all tests to run, collects console errors and warnings, and fails the test suite at the end if any issues are found.
26
+ * This is useful for reporting all issues at once after all tests are executed, rather than failing immediately on the first issue.
27
+ * Cons: Reporting might be confusing, e.g. - cypress will report the very last test as failed, while many tests might have issues.
28
+ * This is because the hook is executed after all tests are completed, so the last test is reported as failed.
29
+ */
30
+ enum STRATEGY { failAfterEach, failAfterAll }
31
+
32
+ /**
33
+ * Auxiliary type to represent a single item in the collector.
34
+ * It contains the test title and an array of error or warning messages collected during the test.
35
+ */
36
+ type CollectorItem = {
37
+ url: string; // URL of the current page where the issue was found
38
+ test: string; // The title of the test where the issue was found
39
+ errors: {type: string; msg: string}[]; // Array of error or warning messages collected during the test
40
+ };
41
+
42
+ /**
43
+ * Returns an emoji based on the type of message.
44
+ * @param {string} type
18
45
  */
19
- enum STRATEGY { failFast, failAfterAll, failAfterEach }
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
+ }
20
56
 
21
57
  /**
22
58
  * Returns the current strategy for handling JavaScript errors and warnings in Cypress tests.
23
59
  * @returns {STRATEGY} - The current strategy for handling JavaScript errors and warnings.
24
- * @note be careful with Cypress.env(envVarStrategy), since it might return `0` for `failFast` strategy,
60
+ * @note be careful with Cypress.env(envVarStrategy), since it might return `0` for `failAfterEach` strategy,
25
61
  * which is falsy in JavaScript, so we need to check if the variable is undefined.
26
62
  */
27
63
  function getStrategy(): STRATEGY {
28
- return typeof Cypress.env(envVarStrategy) === 'undefined' ? STRATEGY.failFast : Cypress.env(envVarStrategy);
64
+ return typeof Cypress.env(envVarStrategy) === 'undefined' ? STRATEGY.failAfterAll : Cypress.env(envVarStrategy);
29
65
  }
30
66
 
31
67
  /**
@@ -36,11 +72,29 @@ function getStrategy(): STRATEGY {
36
72
  */
37
73
  function setStrategy(strategy: STRATEGY): void { Cypress.env(envVarStrategy, strategy); }
38
74
 
75
+ /**
76
+ * Returns console issues collected during the test execution.
77
+ * @returns {CollectorItem []} - Array of collected issues, each issue is an object with test title and errors.
78
+ */
79
+ function getCollectedIssues(): CollectorItem [] { return Cypress.env(envVarCollector) || []; }
80
+
81
+ /**
82
+ * Sets the console issues collected during the test execution.
83
+ * @returns {void}
84
+ */
85
+ function setCollectedIssues(items: CollectorItem []): void { Cypress.env(envVarCollector, items); }
86
+
39
87
  /**
40
88
  * Checks if the js errors and warnings logger is disabled.
41
89
  * @returns {boolean} - true if the logger is disabled, false otherwise.
42
90
  */
43
- function isDisabled(): boolean { return Cypress.env(envVarDisabled) === true; }
91
+ function isDisabled(): boolean { return ((Cypress.env(envVarDisableAll) === true) || (Cypress.env(envVarDisableJsLogger) === true)); }
92
+
93
+ /**
94
+ * Returns the list of allowed warnings that will not be reported by the logger.
95
+ * @returns {string[]} - Array of allowed warning messages.
96
+ */
97
+ function getAllowedJsWarnings(): string[] { return Cypress.env(envVarAllowedWarnings) || []; }
44
98
 
45
99
  /**
46
100
  * Sets the list of allowed warnings that will not be reported by the logger.
@@ -50,223 +104,146 @@ function isDisabled(): boolean { return Cypress.env(envVarDisabled) === true; }
50
104
  function setAllowedJsWarnings(warnings: string[]): void { Cypress.env(envVarAllowedWarnings, warnings); }
51
105
 
52
106
  /**
53
- * Attaches custom hooks to Cypress events to monitor and report JavaScript errors and warnings.
54
- * This method is called automatically in registerSupport.ts#registerSupport
55
- * It sets up listeners for console errors and warnings, collects them after each test,
56
- * and throws an error if any issues are found after all tests are executed.
57
- */
58
- function attachHooks(): void {
59
- // Skip hook attachment if the logger is disabled (e.g. from CI/CD pipeline)
60
- if (isDisabled()) { return; }
61
-
62
- switch (getStrategy()) {
63
- case STRATEGY.failAfterAll:
64
- attachFailAfterAll();
65
- break;
66
- case STRATEGY.failAfterEach:
67
- attachFailAfterEach();
68
- break;
69
- default:
70
- attachFailFast();
71
- }
72
- }
73
-
74
- /**
75
- * Custom hook implementation which fails the test after all tests are executed if any JavaScript errors or warnings are found.
76
- *
77
- * Proc: Allows all tests to run, collects console errors and warnings, and fails the test suite at the end if any issues are found.
78
- * This is useful for reporting all issues at once after all tests are executed, rather than failing immediately on the first issue.
79
- * Cons: Reporting might be confusing, e.g. - cypress will report the very last test as failed, while many tests might have issues.
80
- * This is because the hook is executed after all tests are completed, so the last test is reported as failed.
107
+ * Attaches a custom JavaScript interceptor to capture console errors and warnings.
81
108
  */
82
- function attachFailAfterAll(): void {
109
+ function attachJsInterceptor(): void {
83
110
  /**
84
- * Custom 'window:before:load' hook to spy on console errors and warnings
85
- * This hook is executed before the page is loaded, allowing us to capture console messages.
111
+ * Custom 'window:before:load' hook to attach interceptors before the page is loaded,
112
+ * allowing us to spy on console messages.
86
113
  */
87
114
  Cypress.on('window:before:load', window => {
88
- // Skip 'window:before:load' hook if the logger is not enabled
89
- // Double-check in case if functionality was disabled within the test-case code
90
- // to skip js validations for some particular test and enable it afterward
115
+ // Skip 'window:before:load' hook if the logger is disabled
91
116
  if (isDisabled()) { return; }
92
-
117
+ // Spy on console.error and console.warn to capture errors and warnings
93
118
  cy.spy(window.console, 'error').as('errors');
94
119
  cy.spy(window.console, 'warn').as('warnings');
95
120
  });
96
121
 
97
122
  /**
98
- * Custom 'afterEach' hook to collect console errors and warnings
123
+ * Custom 'window:load' hook to collect JavaScript errors and warnings right after the page is loaded.
99
124
  */
100
- afterEach(() => {
101
- // Skip 'afterEach' hook if the logger is not enabled
102
- // Double-check in case if functionality was disabled within the test-case code
103
- // to skip js validations for some particular test and enable it afterward
125
+ Cypress.on('window:load', win => {
126
+ // Skip 'window:load' hook if the logger is disabled
104
127
  if (isDisabled()) { return; }
128
+ // Collect issues immediately after the window is loaded and analyze them
129
+ collectIssues(win);
130
+ });
131
+ }
105
132
 
106
- let consoleIssues = [];
107
- const allowedWarnings = Cypress.env(envVarAllowedWarnings) || [];
108
-
109
- // All errors should be collected
110
- cy.get('@errors')
111
- .invoke('getCalls')
112
- .then(calls => { consoleIssues = calls; });
133
+ /**
134
+ * Collects JavaScript errors and warnings using the spies set up in attachJsInterceptor.
135
+ * @returns {Cypress.Chainable} - Cypress chainable object that resolves when issues are collected.
136
+ */
137
+ function collectIssues(win: Cypress.AUTWindow): Cypress.Chainable {
138
+ const allowedWarnings = getAllowedJsWarnings();
139
+ let consoleIssues: {type: string, msg: string}[] = [];
140
+ const url = win.location.href;
113
141
 
114
- // Only warnings not in the allowed list should be collected
115
- cy.get('@warnings')
116
- .invoke('getCalls')
117
- .each((call: { args: string[]}) => {
118
- call.args.forEach((arg: string) => {
119
- if (!allowedWarnings.some((item: string) => arg.includes(item))) {
120
- consoleIssues.push(arg);
121
- }
142
+ // Look for console errors and warnings, collected by the spies
143
+ return cy.get('@errors')
144
+ .invoke('getCalls')
145
+ .then(errorCalls => {
146
+ // All errors should be collected
147
+ consoleIssues = errorCalls.flatMap((call: { args: unknown[] }) => call.args.map((arg: string) => ({type: 'error', msg: String(arg)})));
148
+ })
149
+ .then(() => {
150
+ // Analyze warnings
151
+ cy.get('@warnings')
152
+ .invoke('getCalls')
153
+ .then(warningCalls => {
154
+ warningCalls.flatMap((call: { args: unknown[] }) => call.args).forEach((arg: string) => {
155
+ // Only warnings not in the allowed list should be collected
156
+ if (!allowedWarnings.some((item: string) => arg.includes(item))) { consoleIssues.push({type: 'warn', msg: String(arg)}); }
157
+ });
122
158
  });
123
- });
124
-
125
- // Collect all issues and store them in the Cypress environment variable for later usage
126
- cy.then(() => {
159
+ })
160
+ .then(() => {
161
+ // Update the Cypress environment variable with the collected issues
127
162
  if (consoleIssues.length > 0) {
128
- Cypress.env(envVarCollector, [...(Cypress.env(envVarCollector) || []), {
129
- test: Cypress.currentTest.title,
130
- errors: consoleIssues
131
- }]);
163
+ setCollectedIssues([
164
+ ...getCollectedIssues(),
165
+ {url: url, test: Cypress.currentTest.title, errors: consoleIssues}
166
+ ]);
132
167
  }
133
168
  });
134
- });
135
-
136
- /**
137
- * Custom 'after' hook to analyze collected errors and warnings after all tests are executed.
138
- */
139
- after(() => {
140
- // Skip 'after' hook if the logger is not enabled
141
- // Double-check in case if functionality was disabled within the test-case code
142
- // to skip js validations for some particular test and enable it afterward
143
- if (isDisabled()) { return; }
144
-
145
- // Analyze collected errors and warnings
146
- const failures = Cypress.env(envVarCollector) || [];
147
- cy.log('[JS ERRORS LOGGER] Analyze collected issues').then(() => {
148
- if (failures.length > 0) {
149
- // Format the error message for each test
150
- const errorMessage = failures.map((failure: { test: string; errors: string[]; }) => {
151
- return `TEST: ${failure.test}\nISSUES:\n${failure.errors.map((e: string) => `- ${e}`).join('\n')}`;
152
- }).join('\n\n');
153
- // Throw an error with the collected issues
154
- throw new Error('CONSOLE ERRORS and WARNINGS FOUND:\n\n' + errorMessage);
155
- } else {
156
- cy.log('[JS ERRORS LOGGER] No console errors or warnings found.');
157
- }
158
- });
159
- });
160
169
  }
161
170
 
162
171
  /**
163
- * Custom hook implementation which fails the test after each test execution if any JavaScript errors or warnings are found.
164
- *
165
- * Proc: Allows each test to run, collects console errors and warnings, and fails the particular test by the end of it's execution
166
- * if any issues are found.
167
- * Cons: If errors or warnings were found during beforeEach or afterEach hook(s), the rest of spec will be ignored.
172
+ * Analyzes collected JavaScript errors and warnings and throws an error if any were found.
168
173
  */
169
- function attachFailAfterEach(): void {
170
- // Double-check in case if functionality was disabled within the test-case code
171
- // to skip js validations for some particular test and enable it afterward
172
- if (isDisabled()) { return; }
174
+ function analyzeIssues(): void {
175
+ cy.then(() => {
176
+ const failures = getCollectedIssues();
173
177
 
174
- /**
175
- * Custom 'window:before:load' hook to spy on console errors and warnings
176
- * This hook is executed before the page is loaded, allowing us to capture console messages.
177
- */
178
- Cypress.on('window:before:load', window => {
179
- cy.spy(window.console, 'error').as('errors');
180
- cy.spy(window.console, 'warn').as('warnings');
181
- });
178
+ if (failures.length > 0) {
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);
182
183
 
183
- /**
184
- * Custom 'afterEach' hook to collect console errors and warnings
185
- */
186
- afterEach(() => {
187
- let consoleIssues = [];
188
- const allowedWarnings = Cypress.env(envVarAllowedWarnings) || [];
184
+ return acc;
185
+ }, {} as Record<string, CollectorItem[]>);
189
186
 
190
- // All errors should be collected
191
- cy.get('@errors')
192
- .invoke('getCalls')
193
- .then(calls => { consoleIssues = calls; });
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');
194
195
 
195
- // Only warnings not in the allowed list should be collected
196
- cy.get('@warnings')
197
- .invoke('getCalls')
198
- .each((call: { args: string[]}) => {
199
- call.args.forEach((arg: string) => {
200
- if (!allowedWarnings.some((item: string) => arg.includes(item))) {
201
- consoleIssues.push(arg);
202
- }
203
- });
204
- });
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)}`;
200
+ }).join('\n\n');
205
201
 
206
- // Analyze collected errors and warnings
207
- cy.log('[JS ERRORS LOGGER] Analyze collected issues').then(() => {
208
- if (consoleIssues.length > 0) {
209
- // Format the error message for each test
210
- const errorMessage = consoleIssues.map((e: string) => `- ${e}`).join('\n');
211
- // Throw an error with the collected issues
212
- throw new Error('CONSOLE ERRORS and WARNINGS FOUND:\n\n' + errorMessage);
213
- } else {
214
- cy.log('[JS ERRORS LOGGER] No console errors or warnings found.');
215
- }
216
- });
202
+ // Reset the collector for the next test run
203
+ setCollectedIssues([]);
204
+
205
+ // Throw an error with the collected issues
206
+ throw new Error('CONSOLE ERRORS and WARNINGS FOUND:\n\n' + errorMessage);
207
+ }
217
208
  });
218
209
  }
219
210
 
220
211
  /**
221
- * Custom hook implementation which fails the test immediately when a JavaScript error or warning is detected.
222
- *
223
- * Proc: Allows each test to run, looks for console errors and warnings, and fails the particular test IMMEDIATELLY
224
- * if any issues are found.
225
- * Cons: If errors or warnings were found during beforeEach or afterEach hook(s), the rest of spec will be ignored.
212
+ * Disables the js errors and warnings logger.
213
+ * @returns {void}
226
214
  */
227
- function attachFailFast(): void {
228
- // Double-check in case if functionality was disabled within the test-case code
229
- // to skip js validations for some particular test and enable it afterward
230
- if (isDisabled()) { return; }
215
+ function disable(): void { Cypress.env(envVarDisableJsLogger, true); }
216
+
217
+ /**
218
+ * Attaches custom hooks to Cypress events to monitor and report JavaScript errors and warnings.
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).
221
+ */
222
+ function enable(): void {
223
+ // Ensure the logger is enabled by default
224
+ Cypress.env(envVarDisableJsLogger, false);
225
+
226
+ // Attach errors and warnings collector
227
+ attachJsInterceptor();
231
228
 
232
229
  /**
233
- * Custom 'window:before:load' hook to spy on console errors and warnings
234
- * This hook is executed before the page is loaded, allowing us to capture console messages.
230
+ * Custom 'afterEach' hook to analyze JavaScript errors and warnings after EACH test execution.
235
231
  */
236
- Cypress.on('window:before:load', window => {
237
- cy.spy(window.console, 'error').as('errors');
238
- cy.spy(window.console, 'warn').as('warnings');
232
+ afterEach(() => {
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();
239
237
  });
240
238
 
241
- Cypress.on('window:load', () => {
242
- // DOM should be loaded and parsed by now, so we can check for console errors and warnings
243
- let consoleIssues = [];
244
- const allowedWarnings = Cypress.env(envVarAllowedWarnings) || [];
245
-
246
- // All errors should be collected
247
- cy.get('@errors')
248
- .invoke('getCalls')
249
- .then(calls => { consoleIssues = calls; });
250
-
251
- // Only warnings not in the allowed list should be collected
252
- cy.get('@warnings')
253
- .invoke('getCalls')
254
- .each((call: { args: string[]}) => {
255
- call.args.forEach((arg: string) => {
256
- if (!allowedWarnings.some((item: string) => arg.includes(item))) {
257
- consoleIssues.push(arg);
258
- }
259
- });
260
- }).then(() => {
261
- if (consoleIssues.length > 0) {
262
- // Format the error message for each test
263
- const errorMessage = consoleIssues.map((e: string) => `- ${e}`).join('\n');
264
- // Throw an error with the collected issues
265
- throw new Error('CONSOLE ERRORS and WARNINGS FOUND:\n' + errorMessage);
266
- } else {
267
- cy.log('[JS ERRORS LOGGER] No console errors or warnings found.');
268
- }
269
- });
239
+ /**
240
+ * Custom 'after' hook to analyze JavaScript errors and warnings after ALL tests execution.
241
+ */
242
+ after(() => {
243
+ // Skip the hook if the logger is disabled or if the strategy is not failAfterAll
244
+ if (isDisabled() || getStrategy() !== STRATEGY.failAfterAll) { return; }
245
+ // Analyze collected errors and warnings
246
+ analyzeIssues();
270
247
  });
271
248
  }
272
249
 
@@ -274,8 +251,11 @@ function attachFailFast(): void {
274
251
  * Exports the jsLogger module with methods to attach hooks, enable/disable logging, and set allowed warnings.
275
252
  */
276
253
  export const jsErrorsLogger = {
277
- attachHooks,
278
254
  setAllowedJsWarnings,
255
+ getAllowedJsWarnings,
279
256
  setStrategy,
257
+ getStrategy,
258
+ enable,
259
+ disable,
280
260
  STRATEGY
281
261
  };