@autometa/assertions 1.0.0-rc.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/dist/index.js ADDED
@@ -0,0 +1,809 @@
1
+ import { inspect } from 'node:util';
2
+ import { equals, iterableEquality, subsetEquality } from '@jest/expect-utils';
3
+ import { diff } from 'jest-diff';
4
+ import { printExpected, printReceived } from 'jest-matcher-utils';
5
+
6
+ // src/assertion-error.ts
7
+ var EnsureError = class extends Error {
8
+ constructor(details) {
9
+ const formattedMessage = buildMessage(details);
10
+ super(formattedMessage);
11
+ this.name = "EnsureError";
12
+ this.matcher = details.matcher;
13
+ this.actual = details.actual;
14
+ this.expected = details.expected;
15
+ }
16
+ };
17
+ function buildMessage({ matcher, message, actual, expected, receivedLabel }) {
18
+ const parts = [message];
19
+ const extras = [];
20
+ if (typeof expected !== "undefined" && !containsSection(message, "Expected:")) {
21
+ extras.push(`Expected: ${formatValue(expected)}`);
22
+ }
23
+ if (typeof actual !== "undefined") {
24
+ const label = receivedLabel ? `Received ${receivedLabel}` : "Received";
25
+ if (!containsSection(message, `${label}:`)) {
26
+ extras.push(`${label}: ${formatValue(actual)}`);
27
+ }
28
+ }
29
+ if (!containsSection(message, "Matcher:")) {
30
+ extras.push(`Matcher: ${matcher}`);
31
+ }
32
+ if (extras.length > 0) {
33
+ parts.push("", ...extras);
34
+ }
35
+ return parts.join("\n");
36
+ }
37
+ function formatValue(value) {
38
+ return inspect(value, { depth: 4, maxArrayLength: 10, breakLength: 60, sorted: true });
39
+ }
40
+ function containsSection(message, label) {
41
+ return message.split("\n").some((line) => line.trimStart().startsWith(label));
42
+ }
43
+ var MATCHER_CALL = "ensure(received)";
44
+ var EQUALITY_TESTERS = [iterableEquality];
45
+ var SUBSET_TESTERS = [iterableEquality, subsetEquality];
46
+
47
+ // src/core/context.ts
48
+ function shouldFail(pass, negated) {
49
+ return negated ? pass : !pass;
50
+ }
51
+ function buildFailureMessage(matcher, baseMessage, options = {}) {
52
+ const sections = [`${MATCHER_CALL}.${matcher}(expected)`, baseMessage];
53
+ if (Object.prototype.hasOwnProperty.call(options, "expected")) {
54
+ sections.push(`Expected: ${printExpected(options.expected)}`);
55
+ }
56
+ if (Object.prototype.hasOwnProperty.call(options, "actual")) {
57
+ const label = options.actualLabel ?? "Received";
58
+ sections.push(`${label}: ${printReceived(options.actual)}`);
59
+ }
60
+ if (options.extra) {
61
+ for (const extra of options.extra) {
62
+ if (extra && extra.trim().length > 0) {
63
+ sections.push(extra);
64
+ }
65
+ }
66
+ }
67
+ if (options.diff && options.diff.trim().length > 0) {
68
+ sections.push(options.diff);
69
+ }
70
+ return sections.join("\n\n");
71
+ }
72
+ function formatDiff(expected, actual) {
73
+ const difference = diff(expected, actual, { expand: false });
74
+ return difference && difference.trim().length > 0 ? difference : void 0;
75
+ }
76
+ function formatMissingList(title, values) {
77
+ if (values.length === 0) {
78
+ return "";
79
+ }
80
+ const items = values.map((value) => ` - ${printExpected(value)}`).join("\n");
81
+ return `${title}
82
+ ${items}`;
83
+ }
84
+
85
+ // src/core/predicates.ts
86
+ function isRecord(candidate) {
87
+ return typeof candidate === "object" && candidate !== null;
88
+ }
89
+ function isIterable(candidate) {
90
+ return typeof candidate?.[Symbol.iterator] === "function";
91
+ }
92
+ function hasLengthProperty(candidate) {
93
+ return typeof candidate?.length === "number";
94
+ }
95
+
96
+ // src/matchers/collections.ts
97
+ function assertObjectContaining(ctx, shape) {
98
+ const isObject = isRecord(ctx.value);
99
+ if (!isObject) {
100
+ if (!ctx.negated) {
101
+ ctx.fail("toBeObjectContaining", {
102
+ message: buildFailureMessage("toBeObjectContaining", "Expected value to be an object", {
103
+ actual: ctx.value
104
+ }),
105
+ actual: ctx.value
106
+ });
107
+ }
108
+ return;
109
+ }
110
+ const pass = equals(ctx.value, shape, SUBSET_TESTERS);
111
+ if (shouldFail(pass, ctx.negated)) {
112
+ const baseMessage = ctx.negated ? "Expected object not to match the provided subset" : "Object does not match the provided subset";
113
+ ctx.fail("toBeObjectContaining", {
114
+ message: buildFailureMessage("toBeObjectContaining", baseMessage, {
115
+ expected: shape,
116
+ actual: ctx.value,
117
+ diff: formatDiff(shape, ctx.value)
118
+ }),
119
+ expected: shape,
120
+ actual: ctx.value
121
+ });
122
+ }
123
+ }
124
+ function assertArrayContaining(ctx, expected) {
125
+ if (!Array.isArray(ctx.value)) {
126
+ if (!ctx.negated) {
127
+ ctx.fail("toBeArrayContaining", {
128
+ message: buildFailureMessage("toBeArrayContaining", "Expected value to be an array", {
129
+ actual: ctx.value
130
+ }),
131
+ actual: ctx.value
132
+ });
133
+ }
134
+ return ctx.value;
135
+ }
136
+ const actual = ctx.value;
137
+ const missing = expected.filter(
138
+ (item) => !actual.some((entry) => equals(entry, item, EQUALITY_TESTERS))
139
+ );
140
+ const pass = missing.length === 0;
141
+ if (shouldFail(pass, ctx.negated)) {
142
+ const baseMessage = ctx.negated ? "Expected array not to contain the provided elements" : "Array is missing expected elements";
143
+ ctx.fail("toBeArrayContaining", {
144
+ message: buildFailureMessage("toBeArrayContaining", baseMessage, {
145
+ expected,
146
+ actual,
147
+ extra: [formatMissingList("Missing elements:", missing)],
148
+ diff: formatDiff(expected, actual)
149
+ }),
150
+ expected,
151
+ actual
152
+ });
153
+ }
154
+ return actual;
155
+ }
156
+ function assertContainEqual(ctx, expected) {
157
+ if (!Array.isArray(ctx.value)) {
158
+ if (!ctx.negated) {
159
+ ctx.fail("toContainEqual", {
160
+ message: buildFailureMessage("toContainEqual", "Expected value to be an array", {
161
+ actual: ctx.value
162
+ }),
163
+ actual: ctx.value
164
+ });
165
+ }
166
+ return ctx.value;
167
+ }
168
+ const actual = ctx.value;
169
+ const pass = actual.some((entry) => equals(entry, expected, EQUALITY_TESTERS));
170
+ if (shouldFail(pass, ctx.negated)) {
171
+ const baseMessage = ctx.negated ? "Expected array not to contain the provided element" : "Array is missing the expected element";
172
+ ctx.fail("toContainEqual", {
173
+ message: buildFailureMessage("toContainEqual", baseMessage, {
174
+ expected,
175
+ actual,
176
+ diff: formatDiff(expected, actual)
177
+ }),
178
+ expected,
179
+ actual
180
+ });
181
+ }
182
+ return actual;
183
+ }
184
+ function assertIterableContaining(ctx, expected) {
185
+ if (!isIterable(ctx.value)) {
186
+ if (!ctx.negated) {
187
+ ctx.fail("toBeIterableContaining", {
188
+ message: buildFailureMessage("toBeIterableContaining", "Expected value to be iterable", {
189
+ actual: ctx.value
190
+ }),
191
+ actual: ctx.value
192
+ });
193
+ }
194
+ return ctx.value;
195
+ }
196
+ const entries = Array.from(ctx.value);
197
+ const missing = expected.filter(
198
+ (item) => !entries.some((entry) => equals(entry, item, EQUALITY_TESTERS))
199
+ );
200
+ const pass = missing.length === 0;
201
+ if (shouldFail(pass, ctx.negated)) {
202
+ const baseMessage = ctx.negated ? "Expected iterable not to contain the provided elements" : "Iterable is missing expected elements";
203
+ ctx.fail("toBeIterableContaining", {
204
+ message: buildFailureMessage("toBeIterableContaining", baseMessage, {
205
+ expected,
206
+ actual: entries,
207
+ extra: [formatMissingList("Missing elements:", missing)]
208
+ }),
209
+ expected,
210
+ actual: entries
211
+ });
212
+ }
213
+ return ctx.value;
214
+ }
215
+ function assertHasLength(ctx, expected) {
216
+ if (!hasLengthProperty(ctx.value)) {
217
+ if (!ctx.negated) {
218
+ ctx.fail("toHaveLength", {
219
+ message: buildFailureMessage("toHaveLength", "Expected value to have a numeric length property", {
220
+ actual: ctx.value
221
+ }),
222
+ actual: ctx.value
223
+ });
224
+ }
225
+ return NaN;
226
+ }
227
+ const actualLength = ctx.value.length;
228
+ const pass = actualLength === expected;
229
+ if (shouldFail(pass, ctx.negated)) {
230
+ const baseMessage = ctx.negated ? `Expected length to differ from ${expected}` : "Length does not match expectation";
231
+ ctx.fail("toHaveLength", {
232
+ message: buildFailureMessage("toHaveLength", baseMessage, {
233
+ expected,
234
+ actual: actualLength,
235
+ diff: formatDiff(expected, actualLength)
236
+ }),
237
+ expected,
238
+ actual: actualLength
239
+ });
240
+ }
241
+ return actualLength;
242
+ }
243
+ function assertToBe(ctx, expected) {
244
+ const pass = Object.is(ctx.value, expected);
245
+ if (ctx.negated ? pass : !pass) {
246
+ const baseMessage = ctx.negated ? "Expected values not to be strictly equal" : "Expected values to be strictly equal";
247
+ ctx.fail("toBe", {
248
+ message: buildFailureMessage("toBe", baseMessage, {
249
+ actual: ctx.value,
250
+ expected,
251
+ diff: formatDiff(expected, ctx.value)
252
+ }),
253
+ actual: ctx.value,
254
+ expected
255
+ });
256
+ }
257
+ }
258
+ function assertToEqual(ctx, expected) {
259
+ const pass = equals(ctx.value, expected, EQUALITY_TESTERS);
260
+ if (ctx.negated ? pass : !pass) {
261
+ const baseMessage = ctx.negated ? "Expected values not to be deeply equal" : "Expected values to be deeply equal";
262
+ ctx.fail("toEqual", {
263
+ message: buildFailureMessage("toEqual", baseMessage, {
264
+ actual: ctx.value,
265
+ expected,
266
+ diff: formatDiff(expected, ctx.value)
267
+ }),
268
+ actual: ctx.value,
269
+ expected
270
+ });
271
+ }
272
+ }
273
+ function assertToStrictEqual(ctx, expected) {
274
+ const pass = equals(ctx.value, expected, EQUALITY_TESTERS, true);
275
+ if (ctx.negated ? pass : !pass) {
276
+ const baseMessage = ctx.negated ? "Expected values not to be strictly equal (including prototypes and property definitions)" : "Expected values to be strictly equal (including prototypes and property definitions)";
277
+ ctx.fail("toStrictEqual", {
278
+ message: buildFailureMessage("toStrictEqual", baseMessage, {
279
+ actual: ctx.value,
280
+ expected,
281
+ diff: formatDiff(expected, ctx.value)
282
+ }),
283
+ actual: ctx.value,
284
+ expected
285
+ });
286
+ }
287
+ }
288
+
289
+ // src/matchers/instance.ts
290
+ function assertToBeInstanceOf(ctx, ctor) {
291
+ if (typeof ctor !== "function") {
292
+ ctx.fail("toBeInstanceOf", {
293
+ message: buildFailureMessage("toBeInstanceOf", "Constructor must be a callable function", {
294
+ expected: ctor
295
+ }),
296
+ expected: ctor
297
+ });
298
+ }
299
+ const pass = ctx.value instanceof ctor;
300
+ if (ctx.negated ? pass : !pass) {
301
+ const label = ctor.name || "<anonymous>";
302
+ const baseMessage = ctx.negated ? `Expected value not to be an instance of ${label}` : `Expected value to be an instance of ${label}`;
303
+ ctx.fail("toBeInstanceOf", {
304
+ message: buildFailureMessage("toBeInstanceOf", baseMessage, {
305
+ expected: ctor,
306
+ actual: ctx.value
307
+ }),
308
+ expected: ctor,
309
+ actual: ctx.value
310
+ });
311
+ }
312
+ return ctx.value;
313
+ }
314
+
315
+ // src/matchers/nullish.ts
316
+ var NIL_MESSAGE = "Value is null or undefined";
317
+ function assertToBeDefined(ctx) {
318
+ const isDefined = ctx.value !== null && typeof ctx.value !== "undefined";
319
+ if (ctx.negated ? isDefined : !isDefined) {
320
+ const baseMessage = ctx.negated ? "Expected value to be null or undefined" : NIL_MESSAGE;
321
+ ctx.fail("toBeDefined", {
322
+ message: buildFailureMessage("toBeDefined", baseMessage, {
323
+ actual: ctx.value
324
+ }),
325
+ actual: ctx.value
326
+ });
327
+ }
328
+ return ctx.value;
329
+ }
330
+ function assertToBeUndefined(ctx) {
331
+ const isUndefined = typeof ctx.value === "undefined";
332
+ if (ctx.negated ? isUndefined : !isUndefined) {
333
+ const baseMessage = ctx.negated ? "Expected value not to be undefined" : "Expected value to be undefined";
334
+ ctx.fail("toBeUndefined", {
335
+ message: buildFailureMessage("toBeUndefined", baseMessage, {
336
+ actual: ctx.value
337
+ }),
338
+ actual: ctx.value
339
+ });
340
+ }
341
+ return void 0;
342
+ }
343
+ function assertToBeNull(ctx) {
344
+ const isNull = ctx.value === null;
345
+ if (ctx.negated ? isNull : !isNull) {
346
+ const baseMessage = ctx.negated ? "Expected value not to be null" : "Expected value to be null";
347
+ ctx.fail("toBeNull", {
348
+ message: buildFailureMessage("toBeNull", baseMessage, {
349
+ actual: ctx.value
350
+ }),
351
+ actual: ctx.value
352
+ });
353
+ }
354
+ return null;
355
+ }
356
+
357
+ // src/matchers/numeric.ts
358
+ function requireFiniteNumber(ctx, matcher) {
359
+ const value = ctx.value;
360
+ if (typeof value !== "number" || !Number.isFinite(value)) {
361
+ ctx.fail(matcher, {
362
+ message: buildFailureMessage(
363
+ matcher,
364
+ "Expected value to be a finite number",
365
+ { actual: value }
366
+ ),
367
+ actual: value,
368
+ expected: "finite number"
369
+ });
370
+ }
371
+ return value;
372
+ }
373
+ function requireFiniteExpected(ctx, matcher, expected, name = "expected") {
374
+ if (typeof expected !== "number" || !Number.isFinite(expected)) {
375
+ ctx.fail(matcher, {
376
+ message: buildFailureMessage(
377
+ matcher,
378
+ `Expected ${name} to be a finite number`,
379
+ { actual: expected }
380
+ ),
381
+ actual: expected,
382
+ expected: "finite number"
383
+ });
384
+ }
385
+ }
386
+ function assertToBeGreaterThan(ctx, expected) {
387
+ const actual = requireFiniteNumber(ctx, "toBeGreaterThan");
388
+ requireFiniteExpected(ctx, "toBeGreaterThan", expected);
389
+ const pass = actual > expected;
390
+ if (shouldFail(pass, ctx.negated)) {
391
+ const baseMessage = ctx.negated ? `Expected value not to be greater than ${expected}` : `Expected value to be greater than ${expected}`;
392
+ ctx.fail("toBeGreaterThan", {
393
+ message: buildFailureMessage("toBeGreaterThan", baseMessage, {
394
+ expected,
395
+ actual
396
+ }),
397
+ actual,
398
+ expected
399
+ });
400
+ }
401
+ return actual;
402
+ }
403
+ function assertToBeGreaterThanOrEqual(ctx, expected) {
404
+ const actual = requireFiniteNumber(ctx, "toBeGreaterThanOrEqual");
405
+ requireFiniteExpected(
406
+ ctx,
407
+ "toBeGreaterThanOrEqual",
408
+ expected
409
+ );
410
+ const pass = actual >= expected;
411
+ if (shouldFail(pass, ctx.negated)) {
412
+ const baseMessage = ctx.negated ? `Expected value not to be greater than or equal to ${expected}` : `Expected value to be greater than or equal to ${expected}`;
413
+ ctx.fail("toBeGreaterThanOrEqual", {
414
+ message: buildFailureMessage("toBeGreaterThanOrEqual", baseMessage, {
415
+ expected,
416
+ actual
417
+ }),
418
+ actual,
419
+ expected
420
+ });
421
+ }
422
+ return actual;
423
+ }
424
+ function assertToBeLessThan(ctx, expected) {
425
+ const actual = requireFiniteNumber(ctx, "toBeLessThan");
426
+ requireFiniteExpected(ctx, "toBeLessThan", expected);
427
+ const pass = actual < expected;
428
+ if (shouldFail(pass, ctx.negated)) {
429
+ const baseMessage = ctx.negated ? `Expected value not to be less than ${expected}` : `Expected value to be less than ${expected}`;
430
+ ctx.fail("toBeLessThan", {
431
+ message: buildFailureMessage("toBeLessThan", baseMessage, {
432
+ expected,
433
+ actual
434
+ }),
435
+ actual,
436
+ expected
437
+ });
438
+ }
439
+ return actual;
440
+ }
441
+ function assertToBeLessThanOrEqual(ctx, expected) {
442
+ const actual = requireFiniteNumber(ctx, "toBeLessThanOrEqual");
443
+ requireFiniteExpected(
444
+ ctx,
445
+ "toBeLessThanOrEqual",
446
+ expected
447
+ );
448
+ const pass = actual <= expected;
449
+ if (shouldFail(pass, ctx.negated)) {
450
+ const baseMessage = ctx.negated ? `Expected value not to be less than or equal to ${expected}` : `Expected value to be less than or equal to ${expected}`;
451
+ ctx.fail("toBeLessThanOrEqual", {
452
+ message: buildFailureMessage("toBeLessThanOrEqual", baseMessage, {
453
+ expected,
454
+ actual
455
+ }),
456
+ actual,
457
+ expected
458
+ });
459
+ }
460
+ return actual;
461
+ }
462
+ function assertToBeCloseTo(ctx, expected, precision = 2) {
463
+ const actual = requireFiniteNumber(ctx, "toBeCloseTo");
464
+ requireFiniteExpected(ctx, "toBeCloseTo", expected);
465
+ if (!Number.isInteger(precision) || precision < 0 || precision > 20) {
466
+ ctx.fail("toBeCloseTo", {
467
+ message: buildFailureMessage(
468
+ "toBeCloseTo",
469
+ "Expected precision to be an integer between 0 and 20",
470
+ { actual: precision }
471
+ ),
472
+ actual: precision,
473
+ expected: "integer between 0 and 20"
474
+ });
475
+ }
476
+ const tolerance = Math.pow(10, -precision) / 2;
477
+ const difference = Math.abs(actual - expected);
478
+ const epsilon = Number.EPSILON * Math.max(1, Math.abs(actual), Math.abs(expected)) * 2;
479
+ const pass = difference <= tolerance + epsilon;
480
+ if (shouldFail(pass, ctx.negated)) {
481
+ const baseMessage = ctx.negated ? `Expected value not to be close to ${expected} (precision ${precision})` : `Expected value to be close to ${expected} (precision ${precision})`;
482
+ ctx.fail("toBeCloseTo", {
483
+ message: buildFailureMessage("toBeCloseTo", baseMessage, {
484
+ expected,
485
+ actual,
486
+ extra: [
487
+ `Difference: ${difference}`,
488
+ `Tolerance: ${tolerance}`
489
+ ]
490
+ }),
491
+ actual,
492
+ expected
493
+ });
494
+ }
495
+ return actual;
496
+ }
497
+
498
+ // src/matchers/truthiness.ts
499
+ function assertToBeTruthy(ctx) {
500
+ const pass = Boolean(ctx.value);
501
+ if (ctx.negated ? pass : !pass) {
502
+ const baseMessage = ctx.negated ? "Expected value to be falsy" : "Expected value to be truthy";
503
+ ctx.fail("toBeTruthy", {
504
+ message: buildFailureMessage("toBeTruthy", baseMessage, {
505
+ actual: ctx.value
506
+ }),
507
+ actual: ctx.value
508
+ });
509
+ }
510
+ }
511
+ function assertToBeFalsy(ctx) {
512
+ const pass = !ctx.value;
513
+ if (ctx.negated ? pass : !pass) {
514
+ const baseMessage = ctx.negated ? "Expected value to be truthy" : "Expected value to be falsy";
515
+ ctx.fail("toBeFalsy", {
516
+ message: buildFailureMessage("toBeFalsy", baseMessage, {
517
+ actual: ctx.value
518
+ }),
519
+ actual: ctx.value
520
+ });
521
+ }
522
+ }
523
+
524
+ // src/ensure.ts
525
+ function ensure(value, options = {}) {
526
+ const state = {
527
+ value,
528
+ negated: false,
529
+ ...options.label !== void 0 ? { label: options.label } : {}
530
+ };
531
+ return new EnsureChainImpl(state);
532
+ }
533
+ var EnsureChainImpl = class _EnsureChainImpl {
534
+ constructor(state) {
535
+ this.state = state;
536
+ }
537
+ get value() {
538
+ return this.state.value;
539
+ }
540
+ get not() {
541
+ const toggled = !this.state.negated;
542
+ const nextState = {
543
+ value: this.state.value,
544
+ negated: toggled,
545
+ ...this.state.label !== void 0 ? { label: this.state.label } : {}
546
+ };
547
+ return new _EnsureChainImpl(
548
+ nextState
549
+ );
550
+ }
551
+ toBe(expected) {
552
+ assertToBe(this.createContext(), expected);
553
+ return this;
554
+ }
555
+ toEqual(expected) {
556
+ assertToEqual(this.createContext(), expected);
557
+ return this;
558
+ }
559
+ toStrictEqual(expected) {
560
+ assertToStrictEqual(this.createContext(), expected);
561
+ return this;
562
+ }
563
+ toBeDefined() {
564
+ const defined = assertToBeDefined(this.createContext());
565
+ const next = this.state.negated ? this : this.rewrap(defined);
566
+ return next;
567
+ }
568
+ toBeUndefined() {
569
+ const result = assertToBeUndefined(this.createContext());
570
+ const next = this.state.negated ? this : this.rewrap(result);
571
+ return next;
572
+ }
573
+ toBeNull() {
574
+ const result = assertToBeNull(this.createContext());
575
+ const next = this.state.negated ? this : this.rewrap(result);
576
+ return next;
577
+ }
578
+ toBeTruthy() {
579
+ assertToBeTruthy(this.createContext());
580
+ return this;
581
+ }
582
+ toBeFalsy() {
583
+ assertToBeFalsy(this.createContext());
584
+ return this;
585
+ }
586
+ toBeGreaterThan(expected) {
587
+ const actual = assertToBeGreaterThan(this.createContext(), expected);
588
+ const next = this.state.negated ? this : this.rewrap(actual);
589
+ return next;
590
+ }
591
+ toBeGreaterThanOrEqual(expected) {
592
+ const actual = assertToBeGreaterThanOrEqual(this.createContext(), expected);
593
+ const next = this.state.negated ? this : this.rewrap(actual);
594
+ return next;
595
+ }
596
+ toBeLessThan(expected) {
597
+ const actual = assertToBeLessThan(this.createContext(), expected);
598
+ const next = this.state.negated ? this : this.rewrap(actual);
599
+ return next;
600
+ }
601
+ toBeLessThanOrEqual(expected) {
602
+ const actual = assertToBeLessThanOrEqual(this.createContext(), expected);
603
+ const next = this.state.negated ? this : this.rewrap(actual);
604
+ return next;
605
+ }
606
+ toBeCloseTo(expected, precision) {
607
+ const actual = assertToBeCloseTo(this.createContext(), expected, precision);
608
+ const next = this.state.negated ? this : this.rewrap(actual);
609
+ return next;
610
+ }
611
+ toBeInstanceOf(ctor) {
612
+ const instance = assertToBeInstanceOf(this.createContext(), ctor);
613
+ const next = this.state.negated ? this : this.rewrap(instance);
614
+ return next;
615
+ }
616
+ toBeObjectContaining(shape) {
617
+ assertObjectContaining(this.createContext(), shape);
618
+ return this;
619
+ }
620
+ toBeArrayContaining(expected) {
621
+ const array = assertArrayContaining(this.createContext(), expected);
622
+ const next = this.state.negated ? this : this.rewrap(array);
623
+ return next;
624
+ }
625
+ toContainEqual(expected) {
626
+ const array = assertContainEqual(this.createContext(), expected);
627
+ const next = this.state.negated ? this : this.rewrap(array);
628
+ return next;
629
+ }
630
+ toBeIterableContaining(expected) {
631
+ const iterable = assertIterableContaining(this.createContext(), expected);
632
+ const next = this.state.negated ? this : this.rewrap(iterable);
633
+ return next;
634
+ }
635
+ toHaveLength(expected) {
636
+ assertHasLength(this.createContext(), expected);
637
+ const next = this.state.negated ? this : this.rewrap(this.state.value);
638
+ return next;
639
+ }
640
+ createContext() {
641
+ return {
642
+ value: this.state.value,
643
+ negated: this.state.negated,
644
+ fail: (matcher, details) => this.fail(matcher, details),
645
+ ...this.state.label !== void 0 ? { label: this.state.label } : {}
646
+ };
647
+ }
648
+ rewrap(value) {
649
+ const nextState = {
650
+ value,
651
+ negated: this.state.negated,
652
+ ...this.state.label !== void 0 ? { label: this.state.label } : {}
653
+ };
654
+ return new _EnsureChainImpl(
655
+ nextState
656
+ );
657
+ }
658
+ fail(matcher, details) {
659
+ const errorDetails = {
660
+ matcher,
661
+ message: details.message,
662
+ ...details.actual !== void 0 ? { actual: details.actual } : {},
663
+ ...details.expected !== void 0 ? { expected: details.expected } : {},
664
+ ...this.state.label !== void 0 ? { receivedLabel: this.state.label } : {}
665
+ };
666
+ throw new EnsureError(errorDetails);
667
+ }
668
+ };
669
+
670
+ // src/plugins.ts
671
+ function createEnsureFactory(ensureFn, plugins) {
672
+ const withAlways = (invoker, always) => {
673
+ Object.defineProperty(invoker, "always", {
674
+ value: always,
675
+ enumerable: false,
676
+ configurable: false,
677
+ writable: false
678
+ });
679
+ return invoker;
680
+ };
681
+ const positiveEnsure = withAlways(ensureFn, ensureFn);
682
+ const pluginEntries = Object.keys(plugins).map((key) => {
683
+ const plugin = plugins[key];
684
+ if (!plugin) {
685
+ throw new Error(`Assertion plugin "${String(key)}" is not defined.`);
686
+ }
687
+ const factory = plugin({ ensure: positiveEnsure, isNot: false });
688
+ return [key, factory];
689
+ });
690
+ const negatedEnsureFn = (value, options) => {
691
+ return ensureFn(value, options).not;
692
+ };
693
+ const negativeEnsure = withAlways(negatedEnsureFn, ensureFn);
694
+ const negativePluginEntries = Object.keys(plugins).map((key) => {
695
+ const plugin = plugins[key];
696
+ if (!plugin) {
697
+ throw new Error(`Assertion plugin "${String(key)}" is not defined.`);
698
+ }
699
+ const factory = plugin({ ensure: negativeEnsure, isNot: true });
700
+ return [key, factory];
701
+ });
702
+ return (world) => {
703
+ const facade = (value, options) => ensureFn(value, options);
704
+ Object.defineProperty(facade, "world", {
705
+ value: world,
706
+ enumerable: false,
707
+ configurable: false,
708
+ writable: false
709
+ });
710
+ for (const [key, buildFacet] of pluginEntries) {
711
+ const facet = buildFacet(world);
712
+ Object.defineProperty(facade, key, {
713
+ value: facet,
714
+ enumerable: true,
715
+ configurable: false,
716
+ writable: false
717
+ });
718
+ }
719
+ const notFacade = {};
720
+ for (const [key, buildFacet] of negativePluginEntries) {
721
+ const facet = buildFacet(world);
722
+ Object.defineProperty(notFacade, key, {
723
+ value: facet,
724
+ enumerable: true,
725
+ configurable: false,
726
+ writable: false
727
+ });
728
+ }
729
+ Object.defineProperty(facade, "not", {
730
+ value: notFacade,
731
+ enumerable: true,
732
+ configurable: false,
733
+ writable: false
734
+ });
735
+ return facade;
736
+ };
737
+ }
738
+ function createDefaultEnsureFactory() {
739
+ return createEnsureFactory(ensure, {});
740
+ }
741
+
742
+ // src/plugins/runtime-assertions-plugin.ts
743
+ var runtimeAssertionsPlugin = () => ({ ensure: ensure2 }) => (world) => {
744
+ const defaultTableLabel = "step data table";
745
+ const defaultDocstringLabel = "step docstring";
746
+ function table(shape, options) {
747
+ return world.runtime.getTable(shape, options);
748
+ }
749
+ function consumeTable(shape, options) {
750
+ return world.runtime.consumeTable(shape, options);
751
+ }
752
+ function requireTable(shape, options) {
753
+ const { label, ...tableOptions } = options ?? {};
754
+ const table2 = consumeTable(shape, tableOptions);
755
+ return ensure2.always(table2, {
756
+ label: label ?? `Expected ${shape} ${defaultTableLabel} to be attached to the current step.`
757
+ }).toBeDefined().value;
758
+ }
759
+ function consumeRawTable(options) {
760
+ const tableInstance = consumeTable("headerless", options);
761
+ return tableInstance?.raw();
762
+ }
763
+ function requireRawTable(options) {
764
+ const { label, ...tableOptions } = options ?? {};
765
+ const raw = consumeRawTable(tableOptions);
766
+ return ensure2.always(raw, {
767
+ label: label ?? `Expected ${defaultTableLabel} to be attached to the current step.`
768
+ }).toBeDefined().value;
769
+ }
770
+ return {
771
+ hasTable(options) {
772
+ ensure2(world.runtime.hasTable, {
773
+ label: options?.label ?? `${defaultTableLabel} is present`
774
+ }).toBeTruthy();
775
+ },
776
+ hasDocstring(options) {
777
+ ensure2(world.runtime.hasDocstring, {
778
+ label: options?.label ?? `${defaultDocstringLabel} is present`
779
+ }).toBeTruthy();
780
+ },
781
+ docstring() {
782
+ return world.runtime.getDocstring();
783
+ },
784
+ consumeDocstring() {
785
+ return world.runtime.consumeDocstring();
786
+ },
787
+ requireDocstring(options) {
788
+ const docstring = world.runtime.consumeDocstring();
789
+ return ensure2.always(docstring, {
790
+ label: options?.label ?? `Expected ${defaultDocstringLabel} to be attached to the current step.`
791
+ }).toBeDefined().value;
792
+ },
793
+ table,
794
+ consumeTable,
795
+ requireTable,
796
+ rawTable() {
797
+ return world.runtime.getRawTable();
798
+ },
799
+ consumeRawTable,
800
+ requireRawTable,
801
+ stepMetadata() {
802
+ return world.runtime.getStepMetadata();
803
+ }
804
+ };
805
+ };
806
+
807
+ export { EnsureError, createDefaultEnsureFactory, createEnsureFactory, ensure, runtimeAssertionsPlugin };
808
+ //# sourceMappingURL=out.js.map
809
+ //# sourceMappingURL=index.js.map