@koenvanbelle/cypress-soft-assertions 1.0.6 → 1.0.8

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.
Files changed (2) hide show
  1. package/dist/index.js +115 -104
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -9,49 +9,86 @@
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
10
  let softAssertionErrors = [];
11
11
  let isInSoftTest = false;
12
- let originalAssert = null;
12
+ let activeFailHandler = null;
13
+ let activeSoftTestTitle = null;
14
+ let finalizerInstalled = false;
13
15
  /**
14
- * Intercept Chai assertions to capture failures instead of throwing
16
+ * Track a soft assertion failure so it can be reported at test end.
15
17
  */
16
- function setupSoftAssertions() {
17
- if (!originalAssert) {
18
- // Store the original assert function
19
- originalAssert = chai.Assertion.prototype.assert;
18
+ function captureSoftAssertion(error) {
19
+ const message = (error === null || error === void 0 ? void 0 : error.message) || String(error);
20
+ const stack = error === null || error === void 0 ? void 0 : error.stack;
21
+ const lastEntry = softAssertionErrors[softAssertionErrors.length - 1];
22
+ if (lastEntry && lastEntry.message === message && lastEntry.stack === stack) {
23
+ return;
20
24
  }
21
- // Override the assert function to capture errors
22
- chai.Assertion.prototype.assert = function (...args) {
23
- if (isInSoftTest) {
24
- try {
25
- // Call the original assert
26
- originalAssert.apply(this, args);
27
- }
28
- catch (error) {
29
- // Capture the error instead of throwing
30
- softAssertionErrors.push({
31
- message: error.message,
32
- stack: error.stack
33
- });
34
- // Don't throw - let the test continue
25
+ softAssertionErrors.push({
26
+ message,
27
+ stack
28
+ });
29
+ }
30
+ /**
31
+ * Intercept Cypress failures and make assertion failures soft in soft_it() tests.
32
+ */
33
+ function setupSoftAssertions() {
34
+ if (!activeFailHandler) {
35
+ activeFailHandler = (error) => {
36
+ if (!isInSoftTest) {
37
+ throw error;
35
38
  }
39
+ captureSoftAssertion(error);
40
+ return false;
41
+ };
42
+ Cypress.on('fail', activeFailHandler);
43
+ }
44
+ }
45
+ function getTestTitle(test) {
46
+ if (test && typeof test.fullTitle === 'function') {
47
+ return test.fullTitle();
48
+ }
49
+ return String((test === null || test === void 0 ? void 0 : test.title) || '');
50
+ }
51
+ function installFinalizer() {
52
+ if (finalizerInstalled) {
53
+ return;
54
+ }
55
+ afterEach(function () {
56
+ var _a, _b;
57
+ if (!isInSoftTest) {
58
+ return;
36
59
  }
37
- else {
38
- // Normal behavior for regular tests
39
- originalAssert.apply(this, args);
60
+ const currentTest = (this.currentTest || this.test);
61
+ if (!currentTest || getTestTitle(currentTest) !== activeSoftTestTitle) {
62
+ return;
40
63
  }
41
- };
64
+ const finalError = finalizeSoftTest();
65
+ if (!finalError) {
66
+ return;
67
+ }
68
+ currentTest.err = finalError;
69
+ currentTest.state = 'failed';
70
+ const runner = (_b = (_a = Cypress === null || Cypress === void 0 ? void 0 : Cypress.mocha) === null || _a === void 0 ? void 0 : _a.getRunner) === null || _b === void 0 ? void 0 : _b.call(_a);
71
+ if (runner && typeof runner.fail === 'function') {
72
+ runner.fail(currentTest, finalError);
73
+ return;
74
+ }
75
+ throw finalError;
76
+ });
77
+ finalizerInstalled = true;
42
78
  }
43
79
  /**
44
- * Restore original Chai assertion behavior
80
+ * Restore original Chai assertion behavior.
45
81
  */
46
82
  function restoreAssertions() {
47
- if (originalAssert) {
48
- chai.Assertion.prototype.assert = originalAssert;
83
+ if (activeFailHandler) {
84
+ Cypress.off('fail', activeFailHandler);
85
+ activeFailHandler = null;
49
86
  }
50
87
  }
51
88
  /**
52
89
  * Report all collected soft assertion failures
53
90
  */
54
- function reportSoftAssertionFailures() {
91
+ function buildSoftAssertionError() {
55
92
  if (softAssertionErrors.length > 0) {
56
93
  const errorMessages = softAssertionErrors
57
94
  .map((entry, index) => ` ${index + 1}. ${entry.message}`)
@@ -66,13 +103,58 @@ function reportSoftAssertionFailures() {
66
103
  ''
67
104
  ].join('\n');
68
105
  // Clear errors
69
- const errorCount = softAssertionErrors.length;
70
106
  softAssertionErrors = [];
71
- // Throw the aggregated error
72
107
  const error = new Error(finalMessage);
73
108
  error.name = 'SoftAssertionError';
74
- throw error;
109
+ return error;
75
110
  }
111
+ return null;
112
+ }
113
+ /**
114
+ * Cleanup soft assertion state and report accumulated failures.
115
+ */
116
+ function finalizeSoftTest() {
117
+ isInSoftTest = false;
118
+ restoreAssertions();
119
+ activeSoftTestTitle = null;
120
+ return buildSoftAssertionError();
121
+ }
122
+ /**
123
+ * Cleanup soft assertion state without reporting (used on hard failures).
124
+ */
125
+ function abortSoftTest() {
126
+ isInSoftTest = false;
127
+ restoreAssertions();
128
+ activeSoftTestTitle = null;
129
+ }
130
+ /**
131
+ * Create a soft_it variant from a Mocha it function.
132
+ */
133
+ function createSoftIt(baseIt) {
134
+ installFinalizer();
135
+ return function (title, fn) {
136
+ return baseIt(title, function () {
137
+ isInSoftTest = true;
138
+ softAssertionErrors = [];
139
+ activeSoftTestTitle = getTestTitle(this.currentTest || this.test);
140
+ setupSoftAssertions();
141
+ try {
142
+ const result = fn.call(this);
143
+ if (result && typeof result.then === 'function') {
144
+ return result
145
+ .catch((error) => {
146
+ abortSoftTest();
147
+ throw error;
148
+ });
149
+ }
150
+ return result;
151
+ }
152
+ catch (error) {
153
+ abortSoftTest();
154
+ throw error;
155
+ }
156
+ });
157
+ };
76
158
  }
77
159
  /**
78
160
  * soft_it - Define a test where all assertions are soft (non-blocking)
@@ -91,82 +173,11 @@ function reportSoftAssertionFailures() {
91
173
  * cy.get('.city').should('have.text', 'NYC'); // Won't stop if fails
92
174
  * });
93
175
  */
94
- globalThis.soft_it = function (title, fn) {
95
- return it(title, function () {
96
- // Setup soft assertion mode
97
- isInSoftTest = true;
98
- softAssertionErrors = [];
99
- setupSoftAssertions();
100
- // Wrap the test function execution
101
- const executeTest = () => {
102
- try {
103
- const result = fn.call(this);
104
- // Handle async tests (Cypress tests return undefined, but we need to check after cy commands)
105
- if (result && typeof result.then === 'function') {
106
- return result.then(() => {
107
- // Test completed - check for errors at the end
108
- return cy.wrap(null).then(() => {
109
- isInSoftTest = false;
110
- reportSoftAssertionFailures();
111
- });
112
- }, (err) => {
113
- isInSoftTest = false;
114
- restoreAssertions();
115
- throw err;
116
- });
117
- }
118
- // For Cypress tests, we need to hook into the end of the command chain
119
- return cy.wrap(null).then(() => {
120
- isInSoftTest = false;
121
- reportSoftAssertionFailures();
122
- });
123
- }
124
- catch (err) {
125
- isInSoftTest = false;
126
- restoreAssertions();
127
- throw err;
128
- }
129
- };
130
- return executeTest();
131
- });
132
- };
176
+ globalThis.soft_it = createSoftIt(it);
133
177
  /**
134
178
  * soft_it.only - Run only this soft test
135
179
  */
136
- globalThis.soft_it.only = function (title, fn) {
137
- return it.only(title, function () {
138
- isInSoftTest = true;
139
- softAssertionErrors = [];
140
- setupSoftAssertions();
141
- const executeTest = () => {
142
- try {
143
- const result = fn.call(this);
144
- if (result && typeof result.then === 'function') {
145
- return result.then(() => {
146
- return cy.wrap(null).then(() => {
147
- isInSoftTest = false;
148
- reportSoftAssertionFailures();
149
- });
150
- }, (err) => {
151
- isInSoftTest = false;
152
- restoreAssertions();
153
- throw err;
154
- });
155
- }
156
- return cy.wrap(null).then(() => {
157
- isInSoftTest = false;
158
- reportSoftAssertionFailures();
159
- });
160
- }
161
- catch (err) {
162
- isInSoftTest = false;
163
- restoreAssertions();
164
- throw err;
165
- }
166
- };
167
- return executeTest();
168
- });
169
- };
180
+ globalThis.soft_it.only = createSoftIt(it.only);
170
181
  /**
171
182
  * soft_it.skip - Skip this soft test
172
183
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@koenvanbelle/cypress-soft-assertions",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
4
4
  "description": "A Cypress plugin that provides soft_it() for soft assertions - all assertions continue on failure and are reported together",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",