@plugjs/expect5 0.4.5 → 0.4.7

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 (35) hide show
  1. package/dist/cli.mjs +1 -1
  2. package/dist/expectation/async.cjs +32 -20
  3. package/dist/expectation/async.cjs.map +1 -1
  4. package/dist/expectation/async.d.ts +51 -11
  5. package/dist/expectation/async.mjs +32 -20
  6. package/dist/expectation/async.mjs.map +1 -1
  7. package/dist/expectation/expect.cjs +3 -3
  8. package/dist/expectation/expect.cjs.map +1 -1
  9. package/dist/expectation/expect.d.ts +3 -3
  10. package/dist/expectation/expect.mjs +4 -4
  11. package/dist/expectation/expect.mjs.map +1 -1
  12. package/dist/expectation/expectations.cjs +27 -44
  13. package/dist/expectation/expectations.cjs.map +1 -1
  14. package/dist/expectation/expectations.d.ts +72 -14
  15. package/dist/expectation/expectations.mjs +27 -44
  16. package/dist/expectation/expectations.mjs.map +1 -1
  17. package/dist/expectation/matchers.cjs +24 -47
  18. package/dist/expectation/matchers.cjs.map +1 -1
  19. package/dist/expectation/matchers.d.ts +136 -93
  20. package/dist/expectation/matchers.mjs +23 -46
  21. package/dist/expectation/matchers.mjs.map +1 -1
  22. package/dist/expectation/types.cjs.map +1 -1
  23. package/dist/expectation/types.d.ts +2 -2
  24. package/dist/expectation/types.mjs.map +1 -1
  25. package/dist/test.cjs +6 -4
  26. package/dist/test.cjs.map +1 -1
  27. package/dist/test.mjs +7 -5
  28. package/dist/test.mjs.map +1 -1
  29. package/package.json +3 -3
  30. package/src/expectation/async.ts +95 -14
  31. package/src/expectation/expect.ts +6 -6
  32. package/src/expectation/expectations.ts +152 -27
  33. package/src/expectation/matchers.ts +207 -125
  34. package/src/expectation/types.ts +2 -2
  35. package/src/test.ts +9 -4
@@ -1,9 +1,9 @@
1
1
  import { AsyncExpectations } from './async'
2
- import { Matchers } from './matchers'
2
+ import { Matcher } from './matchers'
3
3
 
4
4
  export type { AsyncExpectations } from './async'
5
5
  export type { Expectations, NegativeExpectations } from './expectations'
6
- export type { Matchers, NegativeMatchers } from './matchers'
6
+ export type { Matcher as Matchers, NegativeMatchers } from './matchers'
7
7
 
8
8
  /* ========================================================================== *
9
9
  * EXPECT FUNCTION *
@@ -12,7 +12,7 @@ export type { Matchers, NegativeMatchers } from './matchers'
12
12
  /** The `expect` function exposing expectations and matchers */
13
13
  export type Expect = {
14
14
  <T = unknown>(value: T, remarks?: string): AsyncExpectations<T>
15
- } & Omit<Matchers, 'expect'>
15
+ } & Omit<Matcher, 'expect'>
16
16
 
17
17
  /** The `expect` function exposing expectations and matchers */
18
18
  export const expect: Expect = ((value: any, remarks?: string) => {
@@ -20,13 +20,13 @@ export const expect: Expect = ((value: any, remarks?: string) => {
20
20
  }) as Expect
21
21
 
22
22
  /* Inject all our matchers constructors in the `expect` function */
23
- for (const key of Object.getOwnPropertyNames(Matchers.prototype)) {
23
+ for (const key of Object.getOwnPropertyNames(Matcher.prototype)) {
24
24
  if (! key.startsWith('to')) continue
25
25
 
26
- const matcher = (...args: any[]): any => ((new Matchers() as any)[key](...args))
26
+ const matcher = (...args: any[]): any => ((new Matcher() as any)[key](...args))
27
27
  Object.defineProperty(matcher, 'name', { value: key })
28
28
  Object.defineProperty(expect, key, { value: matcher })
29
29
  }
30
30
 
31
31
  /* Inject the negative matcher constructor in the `expect` function */
32
- Object.defineProperty(expect, 'not', { get: () => new Matchers().not })
32
+ Object.defineProperty(expect, 'not', { get: () => new Matcher().not })
@@ -1,6 +1,6 @@
1
1
  import { diff, type Diff } from './diff'
2
2
  import { toInclude, toMatchContents } from './include'
3
- import { type Matchers } from './matchers'
3
+ import { type Matcher } from './matchers'
4
4
  import {
5
5
  ExpectationError,
6
6
  isMatcher,
@@ -28,13 +28,20 @@ export type AssertedType<T, F extends AssertionFunction<any>, R = ReturnType<F>>
28
28
  I : // returns Expectations<something>, use "something"
29
29
  T // returns something else (void), use T
30
30
 
31
- export type InferMatchers<T> =
32
- T extends Matchers<infer V> ? V :
33
- T extends Record<any, any> ? { [ k in keyof T ] : InferMatchers<T[k]> } :
31
+ /** Infer the type of a {@link Matcher} */
32
+ export type InferMatcher<T, M extends Matcher> =
33
+ M extends Matcher<infer I> ?
34
+ unknown extends I ? T : T & I :
35
+ never
36
+
37
+ /** Recursively infer the type of a {@link Matcher} in a `Record` */
38
+ export type InferToEqual<T> =
39
+ T extends Matcher<infer V> ? V :
40
+ T extends Record<any, any> ? { [ k in keyof T ] : InferToEqual<T[k]> } :
34
41
  T
35
42
 
36
43
  /** Simple wrapper defining the _parent_ instance of an {@link Expectations}. */
37
- type ExpectationsParent = {
44
+ export type ExpectationsParent = {
38
45
  /** Parent {@link Expectations} instance */
39
46
  expectations: Expectations,
40
47
  /** Property associating _this_ to the parent */
@@ -79,9 +86,31 @@ export class Expectations<T = unknown> {
79
86
  * BASIC *
80
87
  * ------------------------------------------------------------------------ */
81
88
 
89
+ /**
90
+ * Expects the value to be of the specified _extended_ {@link TypeName type}.
91
+ *
92
+ * Negation: {@link NegativeExpectations.toBeA `not.toBeA(...)`}
93
+ */
94
+ toBeA<Name extends TypeName>(type: Name): Expectations<TypeMappings[Name]>
95
+
96
+ /**
97
+ * Expects the value to be of the specified _extended_ {@link TypeName type},
98
+ * and further validates it with a {@link Matcher}.
99
+ *
100
+ * Negation: {@link NegativeExpectations.toBeA `not.toBeA(...)`}
101
+ */
102
+ toBeA<
103
+ Name extends TypeName,
104
+ Mapped extends TypeMappings[Name],
105
+ Match extends Matcher,
106
+ >(
107
+ type: Name,
108
+ matcher: Match,
109
+ ): Expectations<InferMatcher<Mapped, Match>>
110
+
82
111
  /**
83
112
  * Expects the value to be of the specified _extended_ {@link TypeName type},
84
- * and (if specified) further asserts it with an {@link AssertionFunction}.
113
+ * and further asserts it with an {@link AssertionFunction}.
85
114
  *
86
115
  * Negation: {@link NegativeExpectations.toBeA `not.toBeA(...)`}
87
116
  */
@@ -90,11 +119,20 @@ export class Expectations<T = unknown> {
90
119
  Mapped extends TypeMappings[Name],
91
120
  Assert extends AssertionFunction<Mapped>,
92
121
  >(
93
- type: Name,
94
- assertion?: Assert,
95
- ): Expectations<AssertedType<Mapped, Assert>> {
122
+ type: Name,
123
+ assertion: Assert,
124
+ ): Expectations<AssertedType<Mapped, Assert>>
125
+
126
+ toBeA(
127
+ type: TypeName,
128
+ assertionOrMatcher?: AssertionFunction | Matcher,
129
+ ): Expectations {
96
130
  if (typeOf(this.value) === type) {
97
- if (assertion) assertion(this as Expectations<any>)
131
+ if (isMatcher(assertionOrMatcher)) {
132
+ assertionOrMatcher.expect(this.value)
133
+ } else if (assertionOrMatcher) {
134
+ assertionOrMatcher(this as Expectations<any>)
135
+ }
98
136
  return this as Expectations<any>
99
137
  }
100
138
 
@@ -250,9 +288,32 @@ export class Expectations<T = unknown> {
250
288
 
251
289
  /* ------------------------------------------------------------------------ */
252
290
 
291
+ /**
292
+ * Expects the value to be an instance of the specified {@link Constructor}.
293
+ *
294
+ * Negation: {@link NegativeExpectations.toBeInstanceOf `not.toInstanceOf(...)`}
295
+ */
296
+ toBeInstanceOf<Class extends Constructor>(
297
+ constructor: Class,
298
+ ): Expectations<InstanceType<Class>>
299
+
253
300
  /**
254
301
  * Expects the value to be an instance of the specified {@link Constructor},
255
- * and (if specified) further asserts it with an {@link AssertionFunction}.
302
+ * and further validates it with a {@link Matcher}.
303
+ *
304
+ * Negation: {@link NegativeExpectations.toBeInstanceOf `not.toInstanceOf(...)`}
305
+ */
306
+ toBeInstanceOf<
307
+ Class extends Constructor,
308
+ Match extends Matcher,
309
+ >(
310
+ constructor: Class,
311
+ matcher: Match,
312
+ ): Expectations<InferMatcher<InstanceType<Class>, Match>>
313
+
314
+ /**
315
+ * Expects the value to be an instance of the specified {@link Constructor},
316
+ * and further asserts it with an {@link AssertionFunction}.
256
317
  *
257
318
  * Negation: {@link NegativeExpectations.toBeInstanceOf `not.toInstanceOf(...)`}
258
319
  */
@@ -260,11 +321,20 @@ export class Expectations<T = unknown> {
260
321
  Class extends Constructor,
261
322
  Assert extends AssertionFunction<InstanceType<Class>>,
262
323
  >(
263
- constructor: Class,
264
- assertion?: Assert,
265
- ): Expectations<AssertedType<InstanceType<Class>, Assert>> {
324
+ constructor: Class,
325
+ assertion: Assert,
326
+ ): Expectations<AssertedType<InstanceType<Class>, Assert>>
327
+
328
+ toBeInstanceOf(
329
+ constructor: Constructor,
330
+ assertionOrMatcher?: AssertionFunction | Matcher,
331
+ ): Expectations {
266
332
  if (this.value instanceof constructor) {
267
- if (assertion) assertion(this as Expectations<any>)
333
+ if (isMatcher(assertionOrMatcher)) {
334
+ assertionOrMatcher.expect(this.value)
335
+ } else if (assertionOrMatcher) {
336
+ assertionOrMatcher(this as Expectations<any>)
337
+ }
268
338
  return this as Expectations<any>
269
339
  }
270
340
 
@@ -404,7 +474,7 @@ export class Expectations<T = unknown> {
404
474
  *
405
475
  * Negation: {@link NegativeExpectations.toEqual `not.toEqual(...)`}
406
476
  */
407
- toEqual<Type>(expected: Type): Expectations<InferMatchers<Type>> {
477
+ toEqual<Type>(expected: Type): Expectations<InferToEqual<Type>> {
408
478
  if ((this.value as any) === expected) return this as Expectations<any>
409
479
 
410
480
  const result = diff(this.value, expected)
@@ -443,6 +513,29 @@ export class Expectations<T = unknown> {
443
513
 
444
514
  /* ------------------------------------------------------------------------ */
445
515
 
516
+ /**
517
+ * Expects the value to have the specified _property_.
518
+ *
519
+ * Negation: {@link NegativeExpectations.toHaveProperty `not.toHaveProperty(...)`}
520
+ */
521
+ toHaveProperty<Prop extends string | number | symbol>(
522
+ property: Prop,
523
+ ): Expectations<T & { [keyt in Prop] : unknown }>
524
+
525
+ /**
526
+ * Expects the value to have the specified _property_ and (if specified)
527
+ * further validates its value with a {@link Matcher}.
528
+ *
529
+ * Negation: {@link NegativeExpectations.toHaveProperty `not.toHaveProperty(...)`}
530
+ */
531
+ toHaveProperty<
532
+ Prop extends string | number | symbol,
533
+ Match extends Matcher,
534
+ >(
535
+ property: Prop,
536
+ matcher: Match,
537
+ ): Expectations<T & { [keyt in Prop] : InferMatcher<unknown, Match> }>
538
+
446
539
  /**
447
540
  * Expects the value to have the specified _property_ and (if specified)
448
541
  * further asserts its value with an {@link AssertionFunction}.
@@ -453,9 +546,14 @@ export class Expectations<T = unknown> {
453
546
  Prop extends string | number | symbol,
454
547
  Assert extends AssertionFunction,
455
548
  >(
456
- property: Prop,
457
- assertion?: Assert,
458
- ): Expectations<T & { [keyt in Prop] : AssertedType<unknown, Assert> }> {
549
+ property: Prop,
550
+ assertion: Assert,
551
+ ): Expectations<T & { [keyt in Prop] : AssertedType<unknown, Assert> }>
552
+
553
+ toHaveProperty(
554
+ property: string | number | symbol,
555
+ assertionOrMatcher?: AssertionFunction | Matcher,
556
+ ): Expectations {
459
557
  this.toBeDefined()
460
558
 
461
559
  const propertyValue = (this.value as any)[property]
@@ -464,11 +562,15 @@ export class Expectations<T = unknown> {
464
562
  this._fail(`to have property "${String(property)}"`)
465
563
  }
466
564
 
467
- if (assertion) {
565
+ if (assertionOrMatcher) {
566
+ const parent: ExpectationsParent = { expectations: this, prop: property }
468
567
  try {
469
- const parent: ExpectationsParent = { expectations: this, prop: property }
470
- const expectations = new Expectations(propertyValue, this.remarks, parent)
471
- assertion(expectations)
568
+ if (isMatcher(assertionOrMatcher)) {
569
+ assertionOrMatcher.expect(propertyValue, parent)
570
+ } else if (assertionOrMatcher) {
571
+ const expectations = new Expectations(propertyValue, this.remarks, parent)
572
+ assertionOrMatcher(expectations)
573
+ }
472
574
  } catch (error) {
473
575
  // any caught error difference gets remapped as a property diff
474
576
  if ((error instanceof ExpectationError) && (error.diff)) {
@@ -596,12 +698,31 @@ export class Expectations<T = unknown> {
596
698
  /* ------------------------------------------------------------------------ */
597
699
 
598
700
  /**
599
- * Expects the value to be a `function` throwing, and (if specified) further
600
- * asserts the thrown value with an {@link AssertionFunction}.
701
+ * Expects the value to be a `function` throwing.
601
702
  *
602
703
  * Negation: {@link NegativeExpectations.toThrow `not.toThrow()`}
603
704
  */
604
- toThrow(assert?: AssertionFunction): Expectations<() => any> {
705
+ toThrow(): Expectations<() => any>
706
+
707
+ /**
708
+ * Expects the value to be a `function` throwing, and further validates the
709
+ * thrown value with a {@link Matcher}.
710
+ *
711
+ * Negation: {@link NegativeExpectations.toThrow `not.toThrow()`}
712
+ */
713
+ toThrow(matcher: Matcher): Expectations<() => any>
714
+
715
+ /**
716
+ * Expects the value to be a `function` throwing, and further asserts the
717
+ * thrown value with an {@link AssertionFunction}.
718
+ *
719
+ * Negation: {@link NegativeExpectations.toThrow `not.toThrow()`}
720
+ */
721
+ toThrow(assert: AssertionFunction): Expectations<() => any>
722
+
723
+ toThrow(
724
+ assertionOrMatcher?: AssertionFunction | Matcher,
725
+ ): Expectations<() => any> {
605
726
  const func = this.toBeA('function')
606
727
 
607
728
  let passed = false
@@ -609,7 +730,11 @@ export class Expectations<T = unknown> {
609
730
  func.value()
610
731
  passed = true
611
732
  } catch (thrown) {
612
- if (assert) assert(new Expectations(thrown, this.remarks))
733
+ if (isMatcher(assertionOrMatcher)) {
734
+ assertionOrMatcher.expect(thrown)
735
+ } else if (assertionOrMatcher) {
736
+ assertionOrMatcher(new Expectations(thrown, this.remarks))
737
+ }
613
738
  }
614
739
 
615
740
  if (passed) this._fail('to throw')