@koenvanbelle/cypress-soft-assertions 1.0.0

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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,293 @@
1
+ # Cypress Soft Assertions
2
+
3
+ A Cypress plugin that provides `soft_it()` - a drop-in replacement for `it()` that makes all assertions soft. Assertions continue execution on failure, and all failures are aggregated and reported at the end of the test.
4
+
5
+ ## Overview
6
+
7
+ Standard Cypress assertions stop test execution at the first failure. With `soft_it()`:
8
+
9
+ - All assertions run even if some fail
10
+ - See all failures at once - no need to fix one issue and rerun to find the next
11
+ - Drop-in replacement - just change `it()` to `soft_it()`
12
+ - No manual tracking - failures automatically aggregated and reported
13
+
14
+ ## Installation
15
+
16
+ Install the plugin as a development dependency:
17
+
18
+ ```bash
19
+ npm install cypress-soft-assertions --save-dev
20
+ ```
21
+
22
+ or using Yarn:
23
+
24
+ ```bash
25
+ yarn add cypress-soft-assertions --dev
26
+ ```
27
+
28
+ ## Integration
29
+
30
+ ### Step 1: Import the Plugin
31
+
32
+ Add the import to your Cypress support file. This registers the `soft_it()` function globally.
33
+
34
+ **For TypeScript projects** - Edit `cypress/support/e2e.ts` (or `cypress/support/commands.ts`):
35
+
36
+ ```typescript
37
+ import 'cypress-soft-assertions';
38
+ ```
39
+
40
+ **For JavaScript projects** - Edit `cypress/support/e2e.js` (or `cypress/support/commands.js`):
41
+
42
+ ```javascript
43
+ require('cypress-soft-assertions');
44
+ ```
45
+
46
+ ### Step 2: Add Type Definitions (TypeScript only)
47
+
48
+ If you're using TypeScript, ensure your `tsconfig.json` includes the plugin types:
49
+
50
+ ```json
51
+ {
52
+ "compilerOptions": {
53
+ "types": ["cypress", "cypress-soft-assertions"]
54
+ }
55
+ }
56
+ ```
57
+
58
+ Alternatively, add a reference directive at the top of your test files:
59
+
60
+ ```typescript
61
+ /// <reference types="cypress-soft-assertions" />
62
+ ```
63
+
64
+ ### Step 3: Use in Tests
65
+
66
+ Replace `it()` with `soft_it()` in any test where you want soft assertion behavior:
67
+
68
+ ```typescript
69
+ describe('Product Page', () => {
70
+ soft_it('validates all product details', () => {
71
+ cy.visit('https://example.com/product/123');
72
+
73
+ // All these assertions will run even if some fail
74
+ cy.get('.product-name').should('have.text', 'Awesome Product');
75
+ cy.get('.product-price').should('have.text', '$99.99');
76
+ cy.get('.stock-status').should('have.text', 'In Stock');
77
+ Supported Assertion Types
78
+
79
+ `soft_it()` works with all Cypress assertion styles:
80
+
81
+ ```typescript
82
+ soft_it('supports all assertion types', () => {
83
+ cy.visit('/page');
84
+
85
+ // .should() assertions
86
+ cy.get('.title').should('be.visible');
87
+ cy.get('.title').should('have.text', 'Welcome');
88
+
89
+ // .should() with callback
90
+ cy.get('.items').should(($items) => {
91
+ expect($items).to.have.length(5);
92
+ expect($items.first()).to.contain('Item 1');
93
+ });
94
+
95
+ // expect() in .then()
96
+ cy.get('.price').then($el => {
97
+ expect($el.text()).to.equal('$99.99');
98
+ });
99
+
100
+ // Chained assertions
101
+ cy.get('.button')
102
+ .should('be.visible')
103
+ .and('have.class', 'active')
104
+ .and('contain', 'Submit');
105
+ });
106
+ ```
107
+
108
+ ### Mixing soft_it() with Regular it()
109
+
110
+ You can use both `soft_it()` and regular `it()` in the same test suite:
111
+
112
+ ```typescript
113
+ describe('User Profile', () => {
114
+ // Regular test - stops on first failure
115
+ it('loads the page', () => {
116
+ cy.visit('/profile');
117
+ cy.get('h1').should('be.visible');
118
+ });
119
+
120
+ // Soft test - all assertions run
121
+ soft_it('validates profile fields', () => {
122
+ cy.get('.username').should('have.text', 'john_doe');
123
+ cy.get('.email').should('have.text', 'john@example.com');
124
+ cy.get('.member-since').should('contain', '2023');
125
+ cy.get('.posts-count').should('have.text', '42
126
+
127
+ // Chained assertions
128
+ cy.get('.button')
129
+ .should('be.visible')
130
+ .and('have.class', 'active')
131
+ .and('contain', 'Submit');
132
+ });
133
+ ```
134
+
135
+ ### Multiple Tests
136
+
137
+ You can mix `soft_it()` with regular `it()` tests:
138
+
139
+ ```typescript
140
+ describe('User Profile', () => {
141
+ // Regular test - stops on first failure
142
+ it('loads the page', () => {
143
+ cy.visit('/profile');
144
+ cy.get('h1').should('be.visible');
145
+ });
146
+
147
+ // Soft test - all assertions run
148
+ soft_it('validates profile fields', () => {
149
+ cy.get('.username').should('have.text', 'john_doe');
150
+ cy.get('.email').should('have.text', 'john@example.com');
151
+ cy.get('.member-since').should('contain', '2023');
152
+ cy.get('.posts-count').should('have.text', '42');
153
+ });
154
+
155
+ // Another soft test
156
+ soft_it('validates account settings', () => {
157
+ cy.get('[data-test=notifications]').should('be.checked');
158
+ cy.get('[data-test=newsletter]').should('not.be.checked');
159
+ cy.get('[data-test=theme]').should('have.value', 'dark');
160
+ });
161
+ });
162
+ ```
163
+
164
+ ### Using soft_it.only and soft_it.skip
165
+
166
+ Just like regular `it()`:
167
+
168
+ ```typescript
169
+ describe('Test Suite', () => {
170
+ // Run only this test
171
+ soft_it.only('validates important fields', () => {
172
+ cy.get('.field1').should('exist');
173
+ The plugin supports `.only` and `.skip` modifiers just like regular `it()`:
174
+
175
+ ```typescript
176
+ describe('Test Suite', () => {
177
+ // Run only this test
178
+ soft_it.only('validates important fields', () => {
179
+ cy.get('.field1').should('exist');
180
+ cy.get('.field2').should('exist');
181
+ });
182
+
183
+ // Skip this test
184
+ soft_it.skip('validates optional fields', () => {
185
+ cy.get('.optional').should('exist');
186
+ });
187
+ });
188
+ ```
189
+
190
+ ## How It Works
191
+
192
+ `soft_it()` intercepts Chai assertions within the test block and captures failures instead of throwing them immediately. At the end of the test, all captured failures are aggregated and reported in a single error message.
193
+
194
+ The plugin works with:
195
+ - `.should()` assertions
196
+ - `expect()` assertions
197
+ - `assert()` assertions
198
+ - Custom Cypress commands that use assertions
199
+ - Assertion chains (`.and()`)
200
+
201
+ ## Important Notes
202
+
203
+ ### When Assertions Are Reported
204
+
205
+ Failures are reported at the end of the test after all Cypress commands complete. The test will be marked as failed, and all assertion failures will be listed together.
206
+
207
+ ### Regular vs Soft Tests
208
+
209
+ - **Regular `it()`**: Stops at first assertion failure
210
+ - **Soft `soft_it()`**: Runs all assertions, reports all failures at the end
211
+
212
+ ### Non-Assertion Errors
213
+
214
+ Non-assertion errors (network errors, timeouts, command errors) will still stop test execution immediately. `soft_it()` only makes assertions soft.
215
+
216
+ ```typescript
217
+ soft_it('example', () => {
218
+ cy.visit('/page'); // If this fails (timeout, 404), test stops immediately
219
+ cy.get('.missing').should('exist'); // This assertion is soft
220
+ cy.get('.other').should('exist'); // This assertion is soft
221
+ });
222
+ ```
223
+
224
+ ## Best Practices
225
+
226
+ ### Use for Validation-Heavy Tests
227
+
228
+ `soft_it()` is ideal for tests that validate many fields:
229
+
230
+ ```typescript
231
+ soft_it('validates user profile completeness', () => {
232
+ cy.get('.name').should('not.be.empty');
233
+ cy.get('.email').should('match', /@/);
234
+ cy.get('.phone').should('have.length.at.least', 10);
235
+ cy.get('.address').should('not.be.empty');
236
+ cy.get('.city').should('not.be.empty');
237
+ cy.get('.avatar').should('be.visible');
238
+ });
239
+ ```
240
+
241
+ ### Use Regular it() for Critical Setup
242
+
243
+ Use regular `it()` for setup steps that must succeed:
244
+
245
+ ```typescript
246
+ it('logs in successfully', () => {
247
+ cy.visit('/login');
248
+ cy.get('#username').type('user');
249
+ cy.get('#password').type('pass');
250
+ cy.get('button[type=submit]').click();
251
+ cy.url().should('include', '/dashboard');
252
+ });
253
+
254
+ soft_it('validates dashboard widgets', () => {
255
+ cy.get('.widget-1').should('be.visible');
256
+ cy.get('.widget-2').should('be.visible');
257
+ cy.get('.widget-3').should('be.visible');
258
+ });
259
+ ```
260
+
261
+ ### Group Related Validations
262
+
263
+ Use `soft_it()` to group validations of related elements:
264
+
265
+ ```typescript
266
+ soft_it('validates navigation menu', () => {
267
+ cy.get('nav a').eq(0).should('have.text', 'Home');
268
+ cy.get('nav a').eq(1).should('have.text', 'Products');
269
+ cy.get('nav a').eq(2).should('have.text', 'About');
270
+ cy.get('nav a').eq(3).should('have.text', 'Contact');
271
+ });
272
+ ```
273
+
274
+ ## Error Output Format
275
+
276
+ When soft assertions fail, you receive a formatted error report:
277
+
278
+ ```
279
+ ================================================================================
280
+ SOFT ASSERTION FAILURES (4 failed):
281
+ ================================================================================
282
+ 1. expected '<input#email>' to have value 'test@example.com', but the value was 'wrong@example.com'
283
+ 2. expected '<div.order-total>' to contain '$99.99', but it contained '$89.99'
284
+ 3. expected '<div.tax>' to contain '$8.75', but it contained '$7.50'
285
+ 4. expected '<div.grand-total>' to contain '$113.74', but it contained '$102.49'
286
+ ================================================================================
287
+ ```
288
+
289
+ ## License
290
+
291
+ MIT
292
+
293
+ ##
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Cypress Soft Assertions Plugin
3
+ *
4
+ * Provides soft_it() function that wraps Cypress tests to make all assertions soft.
5
+ * Assertions don't stop execution on failure - they continue and all failures are
6
+ * aggregated and reported at the end of the test.
7
+ */
8
+ declare global {
9
+ /**
10
+ * soft_it - Define a test where all assertions are soft (non-blocking)
11
+ *
12
+ * All Cypress assertions (.should(), expect(), assert()) within this test block
13
+ * will continue execution even when they fail. At the end of the test, all failures
14
+ * are collected and reported together in a single error.
15
+ *
16
+ * @param title - Test title
17
+ * @param fn - Test function
18
+ *
19
+ * @example
20
+ * soft_it('validates product page', () => {
21
+ * cy.visit('/product/123');
22
+ * cy.get('.title').should('have.text', 'Product Name');
23
+ * cy.get('.price').should('have.text', '$99.99');
24
+ * cy.get('.stock').should('contain', 'In Stock');
25
+ * // All assertions run, failures reported together at the end
26
+ * });
27
+ */
28
+ function soft_it(title: string, fn: Mocha.Func | Mocha.AsyncFunc): Mocha.Test;
29
+ namespace soft_it {
30
+ /**
31
+ * Run only this test (like it.only)
32
+ */
33
+ function only(title: string, fn: Mocha.Func | Mocha.AsyncFunc): Mocha.Test;
34
+ /**
35
+ * Skip this test (like it.skip)
36
+ */
37
+ function skip(title: string, fn: Mocha.Func | Mocha.AsyncFunc): void;
38
+ }
39
+ }
40
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,173 @@
1
+ "use strict";
2
+ /**
3
+ * Cypress Soft Assertions Plugin
4
+ *
5
+ * Provides soft_it() function that wraps Cypress tests to make all assertions soft.
6
+ * Assertions don't stop execution on failure - they continue and all failures are
7
+ * aggregated and reported at the end of the test.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ let softAssertionErrors = [];
11
+ let isInSoftTest = false;
12
+ let originalAssert = null;
13
+ /**
14
+ * Intercept Chai assertions to capture failures instead of throwing
15
+ */
16
+ function setupSoftAssertions() {
17
+ if (!originalAssert) {
18
+ // Store the original assert function
19
+ originalAssert = chai.Assertion.prototype.assert;
20
+ }
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
35
+ }
36
+ }
37
+ else {
38
+ // Normal behavior for regular tests
39
+ originalAssert.apply(this, args);
40
+ }
41
+ };
42
+ }
43
+ /**
44
+ * Restore original Chai assertion behavior
45
+ */
46
+ function restoreAssertions() {
47
+ if (originalAssert) {
48
+ chai.Assertion.prototype.assert = originalAssert;
49
+ }
50
+ }
51
+ /**
52
+ * Report all collected soft assertion failures
53
+ */
54
+ function reportSoftAssertionFailures() {
55
+ if (softAssertionErrors.length > 0) {
56
+ const errorMessages = softAssertionErrors
57
+ .map((entry, index) => ` ${index + 1}. ${entry.message}`)
58
+ .join('\n');
59
+ const finalMessage = [
60
+ '',
61
+ '='.repeat(80),
62
+ `SOFT ASSERTION FAILURES (${softAssertionErrors.length} failed):`,
63
+ '='.repeat(80),
64
+ errorMessages,
65
+ '='.repeat(80),
66
+ ''
67
+ ].join('\n');
68
+ // Clear errors
69
+ const errorCount = softAssertionErrors.length;
70
+ softAssertionErrors = [];
71
+ // Throw the aggregated error
72
+ const error = new Error(finalMessage);
73
+ error.name = 'SoftAssertionError';
74
+ throw error;
75
+ }
76
+ }
77
+ /**
78
+ * soft_it - Define a test where all assertions are soft (non-blocking)
79
+ *
80
+ * All Cypress assertions (.should(), expect(), assert()) within this test
81
+ * will continue execution on failure. At the end, all failures are reported together.
82
+ *
83
+ * @param title - Test title
84
+ * @param fn - Test function
85
+ *
86
+ * @example
87
+ * soft_it('validates multiple fields', () => {
88
+ * cy.visit('/page');
89
+ * cy.get('.name').should('have.text', 'John'); // Won't stop if fails
90
+ * cy.get('.age').should('have.text', '25'); // Won't stop if fails
91
+ * cy.get('.city').should('have.text', 'NYC'); // Won't stop if fails
92
+ * });
93
+ */
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
+ };
133
+ /**
134
+ * soft_it.only - Run only this soft test
135
+ */
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
+ };
170
+ /**
171
+ * soft_it.skip - Skip this soft test
172
+ */
173
+ globalThis.soft_it.skip = it.skip;
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@koenvanbelle/cypress-soft-assertions",
3
+ "version": "1.0.0",
4
+ "description": "A Cypress plugin that provides soft_it() for soft assertions - all assertions continue on failure and are reported together",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "prepublishOnly": "npm run build",
10
+ "cy:open": "cypress open",
11
+ "cy:run": "cypress run",
12
+ "test": "npm run build && cypress run"
13
+ },
14
+ "keywords": [
15
+ "cypress",
16
+ "soft-assertions",
17
+ "testing",
18
+ "automation",
19
+ "e2e",
20
+ "cypress-plugin",
21
+ "test",
22
+ "assert",
23
+ "assertion"
24
+ ],
25
+ "author": "Koen Van Belle",
26
+ "license": "MIT",
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "https://github.com/vanbellekoen/cypress-soft-assertions"
30
+ },
31
+ "bugs": {
32
+ "url": "https://github.com/vanbellekoen/cypress-soft-assertions/issues"
33
+ },
34
+ "homepage": "https://github.com/vanbellekoen/cypress-soft-assertions#readme",
35
+ "peerDependencies": {
36
+ "cypress": ">=10.0.0"
37
+ },
38
+ "devDependencies": {
39
+ "cypress": "^15.9.0",
40
+ "typescript": "^5.9.3",
41
+ "@types/mocha": "^10.0.10"
42
+ },
43
+ "files": [
44
+ "dist",
45
+ "README.md"
46
+ ]
47
+ }