@koenvanbelle/cypress-soft-assertions 1.0.7 → 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +70 -98
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -9,8 +9,9 @@
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
10
  let softAssertionErrors = [];
11
11
  let isInSoftTest = false;
12
- let originalAssert = null;
13
- const retryWindows = new Map();
12
+ let activeFailHandler = null;
13
+ let activeSoftTestTitle = null;
14
+ let finalizerInstalled = false;
14
15
  /**
15
16
  * Track a soft assertion failure so it can be reported at test end.
16
17
  */
@@ -26,98 +27,89 @@ function captureSoftAssertion(error) {
26
27
  stack
27
28
  });
28
29
  }
29
- /**
30
- * Read command metadata from Cypress internals in a defensive way.
31
- */
32
- function getCurrentCommand() {
30
+ function getCurrentCommandName() {
31
+ var _a;
33
32
  const state = cy === null || cy === void 0 ? void 0 : cy.state;
34
33
  if (typeof state !== 'function') {
35
- return null;
34
+ return '';
36
35
  }
37
- return state('current') || null;
38
- }
39
- function getCommandProp(command, prop) {
40
- if (!command) {
41
- return undefined;
36
+ const current = state('current');
37
+ if (!current) {
38
+ return '';
42
39
  }
43
- if (typeof command.get === 'function') {
44
- return command.get(prop);
40
+ if (typeof current.get === 'function') {
41
+ return String(current.get('name') || '');
45
42
  }
46
- if (command.attributes && prop in command.attributes) {
47
- return command.attributes[prop];
48
- }
49
- return command[prop];
43
+ return String(current.name || ((_a = current.attributes) === null || _a === void 0 ? void 0 : _a.name) || '');
50
44
  }
51
- /**
52
- * Build retry context for the currently running Cypress command.
53
- */
54
- function getAssertionContext() {
55
- const command = getCurrentCommand();
56
- const commandId = String(getCommandProp(command, 'id') || getCommandProp(command, 'chainerId') || '');
57
- const commandName = String(getCommandProp(command, 'name') || '');
58
- const timeout = Number(getCommandProp(command, 'timeout')) || Number(Cypress.config('defaultCommandTimeout'));
59
- const isRetriable = commandName === 'should' || commandName === 'and';
60
- return {
61
- commandId,
62
- timeout,
63
- isRetriable,
64
- };
45
+ function isRetryTimeoutError(error) {
46
+ const message = String((error === null || error === void 0 ? void 0 : error.message) || '');
47
+ return /Timed out retrying after/i.test(message);
65
48
  }
66
- function formatSoftAssertionError(error, timeout, isRetriable) {
67
- const message = String((error === null || error === void 0 ? void 0 : error.message) || error);
68
- if (isRetriable && !/Timed out retrying after/i.test(message)) {
69
- return {
70
- ...error,
71
- message: `Timed out retrying after ${timeout}ms: ${message}`,
72
- };
73
- }
74
- return error;
49
+ function isRetriableAssertionCommand(commandName) {
50
+ return commandName === 'should' || commandName === 'and';
75
51
  }
76
52
  /**
77
- * Intercept Chai assertions and make them soft in soft_it() tests.
53
+ * Intercept Cypress failures and make assertion failures soft in soft_it() tests.
78
54
  */
79
55
  function setupSoftAssertions() {
80
- if (!originalAssert) {
81
- originalAssert = chai.Assertion.prototype.assert;
56
+ if (!activeFailHandler) {
57
+ activeFailHandler = (error) => {
58
+ if (!isInSoftTest) {
59
+ throw error;
60
+ }
61
+ const commandName = getCurrentCommandName();
62
+ if (isRetriableAssertionCommand(commandName) && !isRetryTimeoutError(error)) {
63
+ // Let Cypress continue polling/retrying for retriable assertions.
64
+ throw error;
65
+ }
66
+ captureSoftAssertion(error);
67
+ return false;
68
+ };
69
+ Cypress.on('fail', activeFailHandler);
70
+ }
71
+ }
72
+ function getTestTitle(test) {
73
+ if (test && typeof test.fullTitle === 'function') {
74
+ return test.fullTitle();
82
75
  }
83
- chai.Assertion.prototype.assert = function (...args) {
76
+ return String((test === null || test === void 0 ? void 0 : test.title) || '');
77
+ }
78
+ function installFinalizer() {
79
+ if (finalizerInstalled) {
80
+ return;
81
+ }
82
+ afterEach(function () {
83
+ var _a, _b;
84
84
  if (!isInSoftTest) {
85
- return originalAssert.apply(this, args);
85
+ return;
86
86
  }
87
- const context = getAssertionContext();
88
- try {
89
- const result = originalAssert.apply(this, args);
90
- if (context.commandId) {
91
- retryWindows.delete(context.commandId);
92
- }
93
- return result;
87
+ const currentTest = (this.currentTest || this.test);
88
+ if (!currentTest || getTestTitle(currentTest) !== activeSoftTestTitle) {
89
+ return;
94
90
  }
95
- catch (rawError) {
96
- const error = formatSoftAssertionError(rawError, context.timeout, context.isRetriable);
97
- if (context.isRetriable && context.commandId) {
98
- const currentWindow = retryWindows.get(context.commandId) || {
99
- startedAt: Date.now(),
100
- timeout: context.timeout,
101
- };
102
- retryWindows.set(context.commandId, currentWindow);
103
- const elapsed = Date.now() - currentWindow.startedAt;
104
- if (elapsed < currentWindow.timeout) {
105
- throw rawError;
106
- }
107
- retryWindows.delete(context.commandId);
108
- }
109
- captureSoftAssertion(error);
91
+ const finalError = finalizeSoftTest();
92
+ if (!finalError) {
110
93
  return;
111
94
  }
112
- };
95
+ currentTest.err = finalError;
96
+ currentTest.state = 'failed';
97
+ 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);
98
+ if (runner && typeof runner.fail === 'function') {
99
+ runner.fail(currentTest, finalError);
100
+ return;
101
+ }
102
+ throw finalError;
103
+ });
104
+ finalizerInstalled = true;
113
105
  }
114
106
  /**
115
107
  * Restore original Chai assertion behavior.
116
108
  */
117
109
  function restoreAssertions() {
118
- retryWindows.clear();
119
- if (originalAssert) {
120
- chai.Assertion.prototype.assert = originalAssert;
110
+ if (activeFailHandler) {
111
+ Cypress.off('fail', activeFailHandler);
112
+ activeFailHandler = null;
121
113
  }
122
114
  }
123
115
  /**
@@ -151,6 +143,7 @@ function buildSoftAssertionError() {
151
143
  function finalizeSoftTest() {
152
144
  isInSoftTest = false;
153
145
  restoreAssertions();
146
+ activeSoftTestTitle = null;
154
147
  return buildSoftAssertionError();
155
148
  }
156
149
  /**
@@ -159,52 +152,31 @@ function finalizeSoftTest() {
159
152
  function abortSoftTest() {
160
153
  isInSoftTest = false;
161
154
  restoreAssertions();
155
+ activeSoftTestTitle = null;
162
156
  }
163
157
  /**
164
158
  * Create a soft_it variant from a Mocha it function.
165
159
  */
166
160
  function createSoftIt(baseIt) {
161
+ installFinalizer();
167
162
  return function (title, fn) {
168
163
  return baseIt(title, function () {
169
164
  isInSoftTest = true;
170
165
  softAssertionErrors = [];
166
+ activeSoftTestTitle = getTestTitle(this.currentTest || this.test);
171
167
  setupSoftAssertions();
172
168
  try {
173
169
  const result = fn.call(this);
174
170
  if (result && typeof result.then === 'function') {
175
171
  return result
176
172
  .catch((error) => {
177
- if ((error === null || error === void 0 ? void 0 : error.name) === 'AssertionError') {
178
- captureSoftAssertion(error);
179
- return;
180
- }
181
173
  abortSoftTest();
182
174
  throw error;
183
- })
184
- .then(() => cy.wrap(null).then(() => {
185
- const finalError = finalizeSoftTest();
186
- if (finalError) {
187
- throw finalError;
188
- }
189
- }));
175
+ });
190
176
  }
191
- return cy.wrap(null).then(() => {
192
- const finalError = finalizeSoftTest();
193
- if (finalError) {
194
- throw finalError;
195
- }
196
- });
177
+ return result;
197
178
  }
198
179
  catch (error) {
199
- if ((error === null || error === void 0 ? void 0 : error.name) === 'AssertionError') {
200
- captureSoftAssertion(error);
201
- return cy.wrap(null).then(() => {
202
- const finalError = finalizeSoftTest();
203
- if (finalError) {
204
- throw finalError;
205
- }
206
- });
207
- }
208
180
  abortSoftTest();
209
181
  throw error;
210
182
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@koenvanbelle/cypress-soft-assertions",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
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",