asserts.js (5623B)
1 /* Any copyright is dedicated to the Public Domain. 2 * http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 /** 7 * @todo Bug 2005403 8 * Move this to main assert module, could look like: 9 * await Assert.continueOnAssert(async function() { 10 * Assert.ok(); 11 * await Assert.rejects(); 12 * ... 13 * }); 14 */ 15 function SoftAssert() { 16 this.failures = []; 17 } 18 19 SoftAssert.prototype._recordFailure = function (err, context) { 20 this.failures.push({ 21 message: err.message, 22 name: err.name, 23 stack: err.stack, 24 context, 25 }); 26 }; 27 28 SoftAssert.prototype.throws = function ( 29 block, 30 expectedError, 31 message = "throws failed" 32 ) { 33 try { 34 Assert.throws(block, expectedError, message); 35 36 this._recordFailure(new Error("Did not throw"), { 37 type: "throws", 38 expectedError, 39 message, 40 }); 41 } catch {} 42 }; 43 44 SoftAssert.prototype.ok = function (condition, message = "ok failed") { 45 try { 46 Assert.ok(condition, message); 47 } catch (err) { 48 this._recordFailure(err, { type: "ok", condition, message }); 49 } 50 }; 51 52 SoftAssert.prototype.equal = function ( 53 actual, 54 expected, 55 message = "equal failed" 56 ) { 57 try { 58 Assert.equal(actual, expected, message); 59 } catch (err) { 60 this._recordFailure(err, { type: "equal", actual, expected, message }); 61 } 62 }; 63 64 SoftAssert.prototype.notEqual = function ( 65 actual, 66 expected, 67 message = "notEqual failed" 68 ) { 69 try { 70 Assert.notEqual(actual, expected, message); 71 } catch (err) { 72 this._recordFailure(err, { type: "notEqual", actual, expected, message }); 73 } 74 }; 75 76 SoftAssert.prototype.deepEqual = function ( 77 actual, 78 expected, 79 message = "deepEqual failed" 80 ) { 81 try { 82 Assert.deepEqual(actual, expected, message); 83 } catch (err) { 84 this._recordFailure(err, { type: "deepEqual", actual, expected, message }); 85 } 86 }; 87 88 SoftAssert.prototype.notDeepEqual = function ( 89 actual, 90 expected, 91 message = "notDeepEqual failed" 92 ) { 93 try { 94 Assert.notDeepEqual(actual, expected, message); 95 } catch (err) { 96 this._recordFailure(err, { 97 type: "notDeepEqual", 98 actual, 99 expected, 100 message, 101 }); 102 } 103 }; 104 105 SoftAssert.prototype.strictEqual = function ( 106 actual, 107 expected, 108 message = "strictEqual failed" 109 ) { 110 try { 111 Assert.strictEqual(actual, expected, message); 112 } catch (err) { 113 this._recordFailure(err, { 114 type: "strictEqual", 115 actual, 116 expected, 117 message, 118 }); 119 } 120 }; 121 122 SoftAssert.prototype.notStrictEqual = function ( 123 actual, 124 expected, 125 message = "notStrictEqual failed" 126 ) { 127 try { 128 Assert.notStrictEqual(actual, expected, message); 129 } catch (err) { 130 this._recordFailure(err, { 131 type: "notStrictEqual", 132 actual, 133 expected, 134 message, 135 }); 136 } 137 }; 138 139 SoftAssert.prototype.rejects = async function ( 140 actual, 141 expected, 142 message = "rejects failed" 143 ) { 144 try { 145 await Assert.rejects(actual, expected, message); 146 } catch (err) { 147 this._recordFailure(err, { type: "rejects", actual, expected, message }); 148 } 149 }; 150 151 SoftAssert.prototype.greater = function ( 152 actual, 153 expected, 154 message = "greater failed" 155 ) { 156 try { 157 Assert.greater(actual, expected, message); 158 } catch (err) { 159 this._recordFailure(err, { type: "greater", actual, expected, message }); 160 } 161 }; 162 163 SoftAssert.prototype.greaterOrEqual = function ( 164 actual, 165 expected, 166 message = "greaterOrEqual failed" 167 ) { 168 try { 169 Assert.greaterOrEqual(actual, expected, message); 170 } catch (err) { 171 this._recordFailure(err, { 172 type: "greaterOrEqual", 173 actual, 174 expected, 175 message, 176 }); 177 } 178 }; 179 180 SoftAssert.prototype.less = function ( 181 actual, 182 expected, 183 message = "less failed" 184 ) { 185 try { 186 Assert.less(actual, expected, message); 187 } catch (err) { 188 this._recordFailure(err, { type: "less", actual, expected, message }); 189 } 190 }; 191 192 SoftAssert.prototype.lessOrEqual = function ( 193 actual, 194 expected, 195 message = "lessOrEqual failed" 196 ) { 197 try { 198 Assert.lessOrEqual(actual, expected, message); 199 } catch (err) { 200 this._recordFailure(err, { 201 type: "lessOrEqual", 202 actual, 203 expected, 204 message, 205 }); 206 } 207 }; 208 209 // Call this at the end to fail the test if any soft asserts failed. 210 SoftAssert.prototype.assertAll = function (label = "Soft assertion failures") { 211 if (!this.failures.length) { 212 return; 213 } 214 215 let details = `${label} (${this.failures.length}):\n`; 216 for (let i = 0; i < this.failures.length; i++) { 217 const f = this.failures[i]; 218 details += `\n[${i + 1}] ${f.message}\n`; 219 if (f.context) { 220 details += ` Context: ${JSON.stringify(f.context)}\n`; 221 } 222 if (f.stack) { 223 details += ` Stack: ${f.stack}\n`; 224 } 225 } 226 227 // One combined failure at the end: 228 throw new Error(details); 229 }; 230 231 function softAssertions(Assert) { 232 /** 233 * Provides a way to execute multiple assertions without exiting 234 * a test after the first assertion failure. All assertions in the 235 * provided callback will run even if some assertions fail. If an 236 * assertion fails, the test will fail and provide details on the 237 * failed assertion(s). 238 * 239 * @example 240 * Assert.withSoftAssertions(function(soft) { 241 * // Use usual Assert functions via `soft` reference: 242 * soft.equal(conversation.id.length, 12); 243 * soft.ok(Array.isArray(conversation.messages)); 244 * soft.ok(!isNaN(conversation.createdDate)); 245 * }); 246 * 247 * @param {Function} tests - A callback that acceps a `soft` object to run assertions with. 248 */ 249 Assert.withSoftAssertions = function (tests) { 250 const soft = new SoftAssert(); 251 tests(soft); 252 soft.assertAll(); 253 }; 254 } 255 256 softAssertions(Assert);