bupkis 0.7.2 → 0.9.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.
Files changed (215) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +19 -2
  3. package/dist/commonjs/assertion/assertion-async.d.ts.map +1 -1
  4. package/dist/commonjs/assertion/assertion-async.js +37 -7
  5. package/dist/commonjs/assertion/assertion-async.js.map +1 -1
  6. package/dist/commonjs/assertion/assertion-sync.d.ts.map +1 -1
  7. package/dist/commonjs/assertion/assertion-sync.js +32 -8
  8. package/dist/commonjs/assertion/assertion-sync.js.map +1 -1
  9. package/dist/commonjs/assertion/assertion-types.d.ts +37 -31
  10. package/dist/commonjs/assertion/assertion-types.d.ts.map +1 -1
  11. package/dist/commonjs/assertion/assertion-types.js +0 -32
  12. package/dist/commonjs/assertion/assertion-types.js.map +1 -1
  13. package/dist/commonjs/assertion/assertion.d.ts +3 -21
  14. package/dist/commonjs/assertion/assertion.d.ts.map +1 -1
  15. package/dist/commonjs/assertion/assertion.js +42 -27
  16. package/dist/commonjs/assertion/assertion.js.map +1 -1
  17. package/dist/commonjs/assertion/create.d.ts +2 -0
  18. package/dist/commonjs/assertion/create.d.ts.map +1 -1
  19. package/dist/commonjs/assertion/create.js +38 -42
  20. package/dist/commonjs/assertion/create.js.map +1 -1
  21. package/dist/commonjs/assertion/impl/assertion-util.d.ts +16 -4
  22. package/dist/commonjs/assertion/impl/assertion-util.d.ts.map +1 -1
  23. package/dist/commonjs/assertion/impl/assertion-util.js +20 -15
  24. package/dist/commonjs/assertion/impl/assertion-util.js.map +1 -1
  25. package/dist/commonjs/assertion/impl/async-parametric.d.ts +63 -11
  26. package/dist/commonjs/assertion/impl/async-parametric.d.ts.map +1 -1
  27. package/dist/commonjs/assertion/impl/async-parametric.js +89 -52
  28. package/dist/commonjs/assertion/impl/async-parametric.js.map +1 -1
  29. package/dist/commonjs/assertion/impl/async.d.ts +116 -12
  30. package/dist/commonjs/assertion/impl/async.d.ts.map +1 -1
  31. package/dist/commonjs/assertion/impl/async.js +1 -1
  32. package/dist/commonjs/assertion/impl/sync-basic.d.ts.map +1 -1
  33. package/dist/commonjs/assertion/impl/sync-basic.js +6 -4
  34. package/dist/commonjs/assertion/impl/sync-basic.js.map +1 -1
  35. package/dist/commonjs/assertion/impl/sync-collection.d.ts.map +1 -1
  36. package/dist/commonjs/assertion/impl/sync-collection.js +24 -14
  37. package/dist/commonjs/assertion/impl/sync-collection.js.map +1 -1
  38. package/dist/commonjs/assertion/impl/sync-esoteric.d.ts +1 -5
  39. package/dist/commonjs/assertion/impl/sync-esoteric.d.ts.map +1 -1
  40. package/dist/commonjs/assertion/impl/sync-esoteric.js +11 -13
  41. package/dist/commonjs/assertion/impl/sync-esoteric.js.map +1 -1
  42. package/dist/commonjs/assertion/impl/sync-parametric.d.ts +27 -7
  43. package/dist/commonjs/assertion/impl/sync-parametric.d.ts.map +1 -1
  44. package/dist/commonjs/assertion/impl/sync-parametric.js +75 -51
  45. package/dist/commonjs/assertion/impl/sync-parametric.js.map +1 -1
  46. package/dist/commonjs/assertion/impl/sync.d.ts +54 -22
  47. package/dist/commonjs/assertion/impl/sync.d.ts.map +1 -1
  48. package/dist/commonjs/assertion/impl/sync.js +1 -1
  49. package/dist/commonjs/assertion/impl/sync.js.map +1 -1
  50. package/dist/commonjs/assertion/index.d.ts +1 -1
  51. package/dist/commonjs/assertion/index.d.ts.map +1 -1
  52. package/dist/commonjs/assertion/index.js +0 -1
  53. package/dist/commonjs/assertion/index.js.map +1 -1
  54. package/dist/commonjs/assertion/slotify.d.ts +1 -13
  55. package/dist/commonjs/assertion/slotify.d.ts.map +1 -1
  56. package/dist/commonjs/assertion/slotify.js +49 -16
  57. package/dist/commonjs/assertion/slotify.js.map +1 -1
  58. package/dist/commonjs/bootstrap.d.ts +85 -17
  59. package/dist/commonjs/bootstrap.d.ts.map +1 -1
  60. package/dist/commonjs/bootstrap.js +1 -0
  61. package/dist/commonjs/bootstrap.js.map +1 -1
  62. package/dist/commonjs/diff.d.ts +51 -0
  63. package/dist/commonjs/diff.d.ts.map +1 -0
  64. package/dist/commonjs/diff.js +279 -0
  65. package/dist/commonjs/diff.js.map +1 -0
  66. package/dist/commonjs/error.d.ts +37 -18
  67. package/dist/commonjs/error.d.ts.map +1 -1
  68. package/dist/commonjs/error.js +44 -30
  69. package/dist/commonjs/error.js.map +1 -1
  70. package/dist/commonjs/expect.d.ts.map +1 -1
  71. package/dist/commonjs/expect.js +131 -78
  72. package/dist/commonjs/expect.js.map +1 -1
  73. package/dist/commonjs/guards.d.ts +24 -10
  74. package/dist/commonjs/guards.d.ts.map +1 -1
  75. package/dist/commonjs/guards.js +56 -39
  76. package/dist/commonjs/guards.js.map +1 -1
  77. package/dist/commonjs/index.d.ts +85 -17
  78. package/dist/commonjs/index.d.ts.map +1 -1
  79. package/dist/commonjs/internal-schema.d.ts +25 -0
  80. package/dist/commonjs/internal-schema.d.ts.map +1 -0
  81. package/dist/commonjs/internal-schema.js +209 -0
  82. package/dist/commonjs/internal-schema.js.map +1 -0
  83. package/dist/commonjs/schema.d.ts.map +1 -1
  84. package/dist/commonjs/schema.js +3 -2
  85. package/dist/commonjs/schema.js.map +1 -1
  86. package/dist/commonjs/use.js +22 -8
  87. package/dist/commonjs/use.js.map +1 -1
  88. package/dist/commonjs/util.d.ts +1 -0
  89. package/dist/commonjs/util.d.ts.map +1 -1
  90. package/dist/commonjs/util.js +14 -10
  91. package/dist/commonjs/util.js.map +1 -1
  92. package/dist/commonjs/value-to-schema.d.ts +1 -0
  93. package/dist/commonjs/value-to-schema.d.ts.map +1 -1
  94. package/dist/commonjs/value-to-schema.js +19 -12
  95. package/dist/commonjs/value-to-schema.js.map +1 -1
  96. package/dist/esm/assertion/assertion-async.d.ts.map +1 -1
  97. package/dist/esm/assertion/assertion-async.js +37 -7
  98. package/dist/esm/assertion/assertion-async.js.map +1 -1
  99. package/dist/esm/assertion/assertion-sync.d.ts.map +1 -1
  100. package/dist/esm/assertion/assertion-sync.js +32 -8
  101. package/dist/esm/assertion/assertion-sync.js.map +1 -1
  102. package/dist/esm/assertion/assertion-types.d.ts +37 -31
  103. package/dist/esm/assertion/assertion-types.d.ts.map +1 -1
  104. package/dist/esm/assertion/assertion-types.js +1 -31
  105. package/dist/esm/assertion/assertion-types.js.map +1 -1
  106. package/dist/esm/assertion/assertion.d.ts +3 -21
  107. package/dist/esm/assertion/assertion.d.ts.map +1 -1
  108. package/dist/esm/assertion/assertion.js +42 -27
  109. package/dist/esm/assertion/assertion.js.map +1 -1
  110. package/dist/esm/assertion/create.d.ts +2 -0
  111. package/dist/esm/assertion/create.d.ts.map +1 -1
  112. package/dist/esm/assertion/create.js +37 -41
  113. package/dist/esm/assertion/create.js.map +1 -1
  114. package/dist/esm/assertion/impl/assertion-util.d.ts +16 -4
  115. package/dist/esm/assertion/impl/assertion-util.d.ts.map +1 -1
  116. package/dist/esm/assertion/impl/assertion-util.js +20 -15
  117. package/dist/esm/assertion/impl/assertion-util.js.map +1 -1
  118. package/dist/esm/assertion/impl/async-parametric.d.ts +63 -11
  119. package/dist/esm/assertion/impl/async-parametric.d.ts.map +1 -1
  120. package/dist/esm/assertion/impl/async-parametric.js +89 -52
  121. package/dist/esm/assertion/impl/async-parametric.js.map +1 -1
  122. package/dist/esm/assertion/impl/async.d.ts +116 -12
  123. package/dist/esm/assertion/impl/async.d.ts.map +1 -1
  124. package/dist/esm/assertion/impl/async.js +2 -2
  125. package/dist/esm/assertion/impl/async.js.map +1 -1
  126. package/dist/esm/assertion/impl/sync-basic.d.ts.map +1 -1
  127. package/dist/esm/assertion/impl/sync-basic.js +6 -4
  128. package/dist/esm/assertion/impl/sync-basic.js.map +1 -1
  129. package/dist/esm/assertion/impl/sync-collection.d.ts.map +1 -1
  130. package/dist/esm/assertion/impl/sync-collection.js +24 -14
  131. package/dist/esm/assertion/impl/sync-collection.js.map +1 -1
  132. package/dist/esm/assertion/impl/sync-esoteric.d.ts +1 -5
  133. package/dist/esm/assertion/impl/sync-esoteric.d.ts.map +1 -1
  134. package/dist/esm/assertion/impl/sync-esoteric.js +11 -13
  135. package/dist/esm/assertion/impl/sync-esoteric.js.map +1 -1
  136. package/dist/esm/assertion/impl/sync-parametric.d.ts +27 -7
  137. package/dist/esm/assertion/impl/sync-parametric.d.ts.map +1 -1
  138. package/dist/esm/assertion/impl/sync-parametric.js +75 -51
  139. package/dist/esm/assertion/impl/sync-parametric.js.map +1 -1
  140. package/dist/esm/assertion/impl/sync.d.ts +54 -22
  141. package/dist/esm/assertion/impl/sync.d.ts.map +1 -1
  142. package/dist/esm/assertion/impl/sync.js +2 -2
  143. package/dist/esm/assertion/impl/sync.js.map +1 -1
  144. package/dist/esm/assertion/index.d.ts +1 -1
  145. package/dist/esm/assertion/index.d.ts.map +1 -1
  146. package/dist/esm/assertion/index.js +0 -1
  147. package/dist/esm/assertion/index.js.map +1 -1
  148. package/dist/esm/assertion/slotify.d.ts +1 -13
  149. package/dist/esm/assertion/slotify.d.ts.map +1 -1
  150. package/dist/esm/assertion/slotify.js +50 -17
  151. package/dist/esm/assertion/slotify.js.map +1 -1
  152. package/dist/esm/bootstrap.d.ts +85 -17
  153. package/dist/esm/bootstrap.d.ts.map +1 -1
  154. package/dist/esm/bootstrap.js +1 -0
  155. package/dist/esm/bootstrap.js.map +1 -1
  156. package/dist/esm/diff.d.ts +51 -0
  157. package/dist/esm/diff.d.ts.map +1 -0
  158. package/dist/esm/diff.js +273 -0
  159. package/dist/esm/diff.js.map +1 -0
  160. package/dist/esm/error.d.ts +37 -18
  161. package/dist/esm/error.d.ts.map +1 -1
  162. package/dist/esm/error.js +41 -27
  163. package/dist/esm/error.js.map +1 -1
  164. package/dist/esm/expect.d.ts.map +1 -1
  165. package/dist/esm/expect.js +133 -80
  166. package/dist/esm/expect.js.map +1 -1
  167. package/dist/esm/guards.d.ts +24 -10
  168. package/dist/esm/guards.d.ts.map +1 -1
  169. package/dist/esm/guards.js +52 -36
  170. package/dist/esm/guards.js.map +1 -1
  171. package/dist/esm/index.d.ts +85 -17
  172. package/dist/esm/index.d.ts.map +1 -1
  173. package/dist/esm/internal-schema.d.ts +25 -0
  174. package/dist/esm/internal-schema.d.ts.map +1 -0
  175. package/dist/esm/internal-schema.js +203 -0
  176. package/dist/esm/internal-schema.js.map +1 -0
  177. package/dist/esm/schema.d.ts.map +1 -1
  178. package/dist/esm/schema.js +3 -2
  179. package/dist/esm/schema.js.map +1 -1
  180. package/dist/esm/use.js +19 -6
  181. package/dist/esm/use.js.map +1 -1
  182. package/dist/esm/util.d.ts +1 -0
  183. package/dist/esm/util.d.ts.map +1 -1
  184. package/dist/esm/util.js +14 -10
  185. package/dist/esm/util.js.map +1 -1
  186. package/dist/esm/value-to-schema.d.ts +1 -0
  187. package/dist/esm/value-to-schema.d.ts.map +1 -1
  188. package/dist/esm/value-to-schema.js +20 -13
  189. package/dist/esm/value-to-schema.js.map +1 -1
  190. package/package.json +29 -11
  191. package/src/assertion/assertion-async.ts +42 -14
  192. package/src/assertion/assertion-sync.ts +40 -17
  193. package/src/assertion/assertion-types.ts +55 -45
  194. package/src/assertion/assertion.ts +49 -32
  195. package/src/assertion/create.ts +46 -65
  196. package/src/assertion/impl/assertion-util.ts +31 -18
  197. package/src/assertion/impl/async-parametric.ts +93 -52
  198. package/src/assertion/impl/async.ts +2 -2
  199. package/src/assertion/impl/sync-basic.ts +7 -4
  200. package/src/assertion/impl/sync-collection.ts +34 -14
  201. package/src/assertion/impl/sync-esoteric.ts +17 -13
  202. package/src/assertion/impl/sync-parametric.ts +79 -52
  203. package/src/assertion/impl/sync.ts +2 -2
  204. package/src/assertion/index.ts +1 -1
  205. package/src/assertion/slotify.ts +67 -21
  206. package/src/bootstrap.ts +1 -0
  207. package/src/diff.ts +343 -0
  208. package/src/error.ts +66 -31
  209. package/src/expect.ts +195 -129
  210. package/src/guards.ts +74 -48
  211. package/src/internal-schema.ts +246 -0
  212. package/src/schema.ts +4 -2
  213. package/src/use.ts +21 -7
  214. package/src/util.ts +15 -12
  215. package/src/value-to-schema.ts +21 -13
package/src/expect.ts CHANGED
@@ -18,11 +18,12 @@ import {
18
18
  import { createAssertion, createAsyncAssertion } from './assertion/create.js';
19
19
  import {
20
20
  AssertionError,
21
+ AssertionImplementationError,
21
22
  FailAssertionError,
22
23
  NegatedAssertionError,
23
24
  UnknownAssertionError,
24
25
  } from './error.js';
25
- import { isAssertionFailure, isString } from './guards.js';
26
+ import { isString } from './guards.js';
26
27
  import {
27
28
  type Expect,
28
29
  type ExpectAsync,
@@ -181,44 +182,50 @@ export function createExpectAsyncFunction<
181
182
  expect?.assertions.length ?? 0,
182
183
  assertions.length + (expect?.assertions.length ?? 0),
183
184
  );
185
+ /**
186
+ * @function
187
+ */
184
188
  const expectAsyncFunction = async (...args: readonly unknown[]) => {
185
189
  await Promise.resolve();
186
- const [isNegated, processedArgs] = maybeProcessNegation(args);
187
- const candidates: Array<{
188
- assertion: AnyAsyncAssertion;
189
- parseResult: ParsedResult<AssertionParts>;
190
- }> = [];
191
- for (const assertion of [...(expect?.assertions ?? []), ...assertions]) {
192
- const parseResult = await assertion.parseValuesAsync(processedArgs);
193
- const { exactMatch, parsedValues, success } = parseResult;
190
+ const argsMatrix = conjunctify(args);
191
+ for (const args of argsMatrix) {
192
+ const { isNegated, processedArgs } = maybeProcessNegation(args);
193
+ const candidates: Array<{
194
+ assertion: AnyAsyncAssertion;
195
+ parseResult: ParsedResult<AssertionParts>;
196
+ }> = [];
197
+ for (const assertion of [...(expect?.assertions ?? []), ...assertions]) {
198
+ const parseResult = await assertion.parseValuesAsync(processedArgs);
199
+ const { exactMatch, parsedValues, success } = parseResult;
194
200
 
195
- if (success) {
196
- if (exactMatch) {
197
- return executeAsync(
198
- assertion,
199
- parsedValues,
200
- [...args],
201
- expectAsyncFunction,
202
- isNegated,
203
- parseResult,
204
- );
201
+ if (success) {
202
+ if (exactMatch) {
203
+ return executeAsync(
204
+ assertion,
205
+ parsedValues,
206
+ [...args],
207
+ expectAsyncFunction,
208
+ isNegated,
209
+ parseResult,
210
+ );
211
+ }
212
+ candidates.push({ assertion, parseResult });
205
213
  }
206
- candidates.push({ assertion, parseResult });
207
214
  }
215
+ if (candidates.length) {
216
+ const { assertion, parseResult } = candidates[0]!;
217
+ return executeAsync(
218
+ assertion as any,
219
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
220
+ parseResult.parsedValues as any,
221
+ [...args],
222
+ expectAsyncFunction,
223
+ isNegated,
224
+ parseResult,
225
+ );
226
+ }
227
+ throwInvalidParametersError(args);
208
228
  }
209
- if (candidates.length) {
210
- const { assertion, parseResult } = candidates[0]!;
211
- return executeAsync(
212
- assertion as any,
213
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
214
- parseResult.parsedValues as any,
215
- [...args],
216
- expectAsyncFunction,
217
- isNegated,
218
- parseResult,
219
- );
220
- }
221
- throwInvalidParametersError(args);
222
229
  };
223
230
  return expectAsyncFunction;
224
231
  }
@@ -359,44 +366,52 @@ export function createExpectSyncFunction<
359
366
  expect?.assertions.length ?? 0,
360
367
  assertions.length + (expect?.assertions.length ?? 0),
361
368
  );
369
+ /**
370
+ * @function
371
+ */
362
372
  const expectFunction = (...args: readonly unknown[]) => {
363
- const [isNegated, processedArgs] = maybeProcessNegation(args);
364
- const candidates: Array<{
365
- assertion: AnySyncAssertion;
366
- parseResult: ParsedResult<AssertionParts>;
367
- }> = [];
368
- for (const assertion of [...(expect?.assertions ?? []), ...assertions]) {
369
- const parseResult = assertion.parseValues(processedArgs);
370
- const { exactMatch, parsedValues, success } = parseResult;
373
+ const argsMatrix = conjunctify(args);
371
374
 
372
- if (success) {
373
- if (exactMatch) {
374
- return execute(
375
- assertion,
376
- parsedValues,
377
- [...args],
378
- expectFunction,
379
- isNegated,
380
- parseResult,
381
- );
375
+ for (const args of argsMatrix) {
376
+ const { isNegated, processedArgs } = maybeProcessNegation(args);
377
+ const candidates: Array<{
378
+ assertion: AnySyncAssertion;
379
+ parseResult: ParsedResult<AssertionParts>;
380
+ }> = [];
381
+ for (const assertion of [...(expect?.assertions ?? []), ...assertions]) {
382
+ const parseResult = assertion.parseValues(processedArgs);
383
+ const { exactMatch, parsedValues, success } = parseResult;
384
+
385
+ if (success) {
386
+ if (exactMatch) {
387
+ return execute(
388
+ assertion,
389
+ parsedValues,
390
+ [...args],
391
+ expectFunction,
392
+ isNegated,
393
+ parseResult,
394
+ );
395
+ }
396
+ candidates.push({ assertion, parseResult });
382
397
  }
383
- candidates.push({ assertion, parseResult });
384
398
  }
399
+ if (candidates.length) {
400
+ const { assertion, parseResult } = candidates[0]!;
401
+ return execute(
402
+ assertion as any,
403
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
404
+ parseResult.parsedValues as any,
405
+ [...args],
406
+ expectFunction,
407
+ isNegated,
408
+ parseResult,
409
+ );
410
+ }
411
+ throwInvalidParametersError(args);
385
412
  }
386
- if (candidates.length) {
387
- const { assertion, parseResult } = candidates[0]!;
388
- return execute(
389
- assertion as any,
390
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
391
- parseResult.parsedValues as any,
392
- [...args],
393
- expectFunction,
394
- isNegated,
395
- parseResult,
396
- );
397
- }
398
- throwInvalidParametersError(args);
399
413
  };
414
+
400
415
  return expectFunction;
401
416
  }
402
417
 
@@ -406,6 +421,7 @@ export function createExpectSyncFunction<
406
421
  * @privateRemarks
407
422
  * This is here because `Assertion` doesn't know anything about negation and
408
423
  * probably shouldn't.
424
+ * @function
409
425
  * @param assertion - The assertion to execute
410
426
  * @param parsedValues - Parsed values for the assertion
411
427
  * @param args - Original arguments passed to expect
@@ -424,47 +440,44 @@ const execute = <
424
440
  isNegated: boolean,
425
441
  parseResult?: ParsedResult<Parts>,
426
442
  ): void => {
427
- if (!isNegated) {
428
- return assertion.execute(parsedValues, args, stackStartFn, parseResult);
429
- }
443
+ if (isNegated) {
444
+ // negation logic
445
+ try {
446
+ assertion.execute(parsedValues, args, stackStartFn, parseResult);
447
+ } catch (error) {
448
+ // success!
449
+ if (AssertionError.isAssertionError(error)) {
450
+ return;
451
+ }
430
452
 
431
- try {
432
- const result = assertion.execute(
433
- parsedValues,
434
- args,
435
- stackStartFn,
436
- parseResult,
437
- );
438
- if (isAssertionFailure(result)) {
439
- throw new NegatedAssertionError({
440
- actual: result.actual,
441
- expected: result.expected,
442
- message:
443
- result.message ??
444
- `Expected assertion ${assertion} to fail (due to negation), but it passed`,
445
- stackStartFn,
446
- });
453
+ if (AssertionImplementationError.isAssertionImplementationError(error)) {
454
+ throw error;
455
+ }
456
+
457
+ throw new AssertionImplementationError(
458
+ `Assertion ${assertion} threw a non-AssertionError`,
459
+ { cause: error },
460
+ );
447
461
  }
448
- // If we reach here, the assertion passed but we expected it to fail
462
+
463
+ // if we reach here, then the assertion passed when it should have failed, so:
449
464
  throw new NegatedAssertionError({
450
- message: `Expected assertion to fail (due to negation), but it passed: ${assertion}`,
465
+ message: `Expected assertion ${assertion} to fail (due to negation), but it passed`,
451
466
  stackStartFn,
452
467
  });
453
- } catch (error) {
454
- // Check if this is the negation error we just threw
455
- if (NegatedAssertionError.isNegatedAssertionError(error)) {
456
- // This is our negation error, re-throw it
457
- throw error;
458
- }
468
+ } else {
469
+ try {
470
+ assertion.execute(parsedValues, args, stackStartFn, parseResult);
471
+ } catch (error) {
472
+ if (AssertionError.isAssertionError(error)) {
473
+ throw error;
474
+ }
459
475
 
460
- if (AssertionError.isAssertionError(error)) {
461
- // The assertion failed as expected for negation - this is success
462
- return;
476
+ throw new AssertionImplementationError(
477
+ `Assertion ${assertion} threw a non-AssertionError`,
478
+ { cause: error },
479
+ );
463
480
  }
464
-
465
- debug('Non-assertion error thrown during negated assertion: %O', error);
466
- // Re-throw non-assertion errors (like TypeErrors, etc.)
467
- throw error;
468
481
  }
469
482
  };
470
483
 
@@ -474,6 +487,7 @@ const execute = <
474
487
  * @privateRemarks
475
488
  * This is here because `Assertion` doesn't know anything about negation and
476
489
  * probably shouldn't.
490
+ * @function
477
491
  * @param assertion - The assertion to execute
478
492
  * @param parsedValues - Parsed values for the assertion
479
493
  * @param args - Original arguments passed to expectAsync
@@ -492,39 +506,50 @@ const executeAsync = async <
492
506
  isNegated: boolean,
493
507
  parseResult?: ParsedResult<Parts>,
494
508
  ): Promise<void> => {
495
- if (!isNegated) {
496
- return assertion.executeAsync(
497
- parsedValues,
498
- args,
499
- stackStartFn,
500
- parseResult,
501
- );
502
- }
509
+ if (isNegated) {
510
+ // negation logic
511
+ try {
512
+ await assertion.executeAsync(
513
+ parsedValues,
514
+ args,
515
+ stackStartFn,
516
+ parseResult,
517
+ );
518
+ } catch (error) {
519
+ // success!
520
+ if (AssertionError.isAssertionError(error)) {
521
+ return;
522
+ }
523
+
524
+ throw new AssertionImplementationError(
525
+ `Assertion ${assertion} threw a non-AssertionError`,
526
+ { cause: error },
527
+ );
528
+ }
503
529
 
504
- try {
505
- await assertion.executeAsync(parsedValues, args, stackStartFn, parseResult);
506
- // If we reach here, the assertion passed but we expected it to fail
530
+ // if we reach here, then the assertion passed when it should have failed, so:
507
531
  throw new NegatedAssertionError({
508
- message: `Expected assertion to fail (due to negation), but it passed: ${assertion}`,
532
+ message: `Expected assertion ${assertion} to fail (due to negation), but it passed`,
509
533
  stackStartFn,
510
534
  });
511
- } catch (error) {
512
- // Check if this is the negation error we just threw
513
- if (NegatedAssertionError.isNegatedAssertionError(error)) {
514
- // This is our negation error, re-throw it
515
- throw error;
516
- }
535
+ } else {
536
+ try {
537
+ await assertion.executeAsync(
538
+ parsedValues,
539
+ args,
540
+ stackStartFn,
541
+ parseResult,
542
+ );
543
+ } catch (error) {
544
+ if (AssertionError.isAssertionError(error)) {
545
+ throw error;
546
+ }
517
547
 
518
- if (AssertionError.isAssertionError(error)) {
519
- // The assertion failed as expected for negation - this is success
520
- return;
548
+ throw new AssertionImplementationError(
549
+ `Assertion ${assertion} threw a non-AssertionError`,
550
+ { cause: error },
551
+ );
521
552
  }
522
- debug(
523
- 'Non-assertion error thrown during negated async assertion: %O',
524
- error,
525
- );
526
- // Re-throw non-assertion errors (like TypeErrors, etc.)
527
- throw error;
528
553
  }
529
554
  };
530
555
 
@@ -533,14 +558,16 @@ const executeAsync = async <
533
558
  * requested along with arguments stripped of the leading negation (to enable
534
559
  * assertion matching).
535
560
  *
561
+ * @function
536
562
  * @internal
537
563
  */
538
564
  const maybeProcessNegation = (
539
565
  args: readonly unknown[],
540
- ): [isNegated: boolean, processedArgs: readonly unknown[]] => {
566
+ ): { isNegated: boolean; processedArgs: readonly unknown[] } => {
541
567
  let isNegated = false;
542
568
  let processedArgs = args;
543
569
 
570
+ // note: args[1] should always be a string
544
571
  if (args.length >= 2 && isString(args[1])) {
545
572
  const { cleanedPhrase, isNegated: detected } = detectNegation(args[1]);
546
573
  if (detected) {
@@ -548,13 +575,49 @@ const maybeProcessNegation = (
548
575
  processedArgs = [args[0], cleanedPhrase, ...args.slice(2)];
549
576
  }
550
577
  }
551
- return [isNegated, processedArgs];
578
+ return { isNegated, processedArgs };
579
+ };
580
+
581
+ /**
582
+ * Given some args, create a matrix of args based on the presence of the
583
+ * conjunction operator ("and").
584
+ *
585
+ * If no "and" is present, returns the original args wrapped in an array.
586
+ *
587
+ * @function
588
+ */
589
+ const conjunctify = (args: readonly unknown[]): (readonly unknown[])[] => {
590
+ let argsMatrix = [args];
591
+
592
+ if (args.length >= 2) {
593
+ // partition args by the string "and"; there may be multiple "and"s
594
+ const andIndices = args
595
+ .map((arg, index) => (arg === 'and' ? index : -1))
596
+ .filter((index) => index !== -1);
597
+
598
+ if (andIndices.length > 0) {
599
+ const parts: unknown[][] = [];
600
+ let lastIndex = 0;
601
+ for (const andIndex of andIndices) {
602
+ const partsArgs =
603
+ lastIndex > 0
604
+ ? [args[0], ...args.slice(lastIndex, andIndex)]
605
+ : [...args.slice(lastIndex, andIndex)];
606
+ parts.push(partsArgs);
607
+ lastIndex = andIndex + 1;
608
+ }
609
+ parts.push([args[0], ...args.slice(lastIndex)]);
610
+ argsMatrix = parts;
611
+ }
612
+ }
613
+ return argsMatrix;
552
614
  };
553
615
 
554
616
  /**
555
617
  * Throws an error indicating that no valid assertion could be found for the
556
618
  * provided arguments.
557
619
  *
620
+ * @function
558
621
  * @param args The arguments that were passed to the expect function
559
622
  * @internal
560
623
  */
@@ -571,6 +634,7 @@ const throwInvalidParametersError = (args: readonly unknown[]): never => {
571
634
  * Detects if an assertion phrase starts with "not " and returns the cleaned
572
635
  * phrase.
573
636
  *
637
+ * @function
574
638
  * @param phrase - The assertion phrase to check
575
639
  * @returns Object with `isNegated` flag and `cleanedPhrase`
576
640
  */
@@ -594,7 +658,9 @@ const detectNegation = (
594
658
  };
595
659
 
596
660
  /**
597
- * {@inheritdoc FailFn}
661
+ * {@inheritDoc FailFn}
662
+ *
663
+ * @function
598
664
  */
599
665
  const fail: FailFn = (reason?: string): never => {
600
666
  throw new FailAssertionError({ message: reason });