@fgv/ts-utils-jest 5.0.0-2 → 5.0.0-21

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 (157) hide show
  1. package/.vscode/launch.json +16 -0
  2. package/README.md +11 -0
  3. package/config/api-extractor.json +343 -0
  4. package/config/jest-disable-colors.js +34 -0
  5. package/config/rig.json +16 -0
  6. package/lib/helpers/fsHelpers.test.d.ts +2 -0
  7. package/lib/index.test.d.ts +2 -0
  8. package/lib/matchers/toFail/index.test.d.ts +2 -0
  9. package/lib/matchers/toFail/predicate.test.d.ts +2 -0
  10. package/lib/matchers/toFailTest/index.test.d.ts +2 -0
  11. package/lib/matchers/toFailTest/predicate.test.d.ts +2 -0
  12. package/lib/matchers/toFailTestAndMatchSnapshot/index.js +2 -1
  13. package/lib/matchers/toFailTestAndMatchSnapshot/index.test.d.ts +2 -0
  14. package/lib/matchers/toFailTestAndMatchSnapshot/predicate.test.d.ts +2 -0
  15. package/lib/matchers/toFailTestWith/index.test.d.ts +2 -0
  16. package/lib/matchers/toFailTestWith/predicate.test.d.ts +2 -0
  17. package/lib/matchers/toFailWith/index.test.d.ts +2 -0
  18. package/lib/matchers/toFailWith/predicate.test.d.ts +2 -0
  19. package/lib/matchers/toFailWithDetail/index.test.d.ts +2 -0
  20. package/lib/matchers/toFailWithDetail/predicate.test.d.ts +2 -0
  21. package/lib/matchers/toHaveBeenCalledWithArgumentsMatching/index.test.d.ts +2 -0
  22. package/lib/matchers/toHaveBeenCalledWithArgumentsMatching/predicate.test.d.ts +2 -0
  23. package/lib/matchers/toSucceed/index.test.d.ts +2 -0
  24. package/lib/matchers/toSucceed/predicate.test.d.ts +2 -0
  25. package/lib/matchers/toSucceedAndMatchInlineSnapshot/index.test.d.ts +2 -0
  26. package/lib/matchers/toSucceedAndMatchSnapshot/index.test.d.ts +2 -0
  27. package/lib/matchers/toSucceedAndSatisfy/index.test.d.ts +2 -0
  28. package/lib/matchers/toSucceedAndSatisfy/predicate.test.d.ts +2 -0
  29. package/lib/matchers/toSucceedWith/index.test.d.ts +2 -0
  30. package/lib/matchers/toSucceedWith/predicate.test.d.ts +2 -0
  31. package/lib/matchers/toSucceedWithDetail/index.test.d.ts +2 -0
  32. package/lib/matchers/toSucceedWithDetail/predicate.test.d.ts +2 -0
  33. package/lib/resolvers/resolvers.test.d.ts +2 -0
  34. package/lib/tsdoc-metadata.json +1 -1
  35. package/lib/utils/colorHelpers.d.ts +54 -0
  36. package/lib/utils/colorHelpers.js +84 -0
  37. package/lib/utils/matcherHelpers.js +9 -5
  38. package/package.json +8 -8
  39. package/src/__snapshots__/index.test.ts.snap +24 -0
  40. package/src/helpers/fsHelpers.ts +184 -0
  41. package/src/helpers/index.ts +1 -0
  42. package/src/index.ts +27 -0
  43. package/src/matchers/index.ts +29 -0
  44. package/src/matchers/toFail/__snapshots__/index.test.ts.snap +17 -0
  45. package/src/matchers/toFail/index.ts +45 -0
  46. package/src/matchers/toFail/predicate.ts +7 -0
  47. package/src/matchers/toFailTest/__snapshots__/index.test.ts.snap +15 -0
  48. package/src/matchers/toFailTest/index.ts +48 -0
  49. package/src/matchers/toFailTest/predicate.ts +8 -0
  50. package/src/matchers/toFailTestAndMatchSnapshot/__snapshots__/index.test.ts.snap +14 -0
  51. package/src/matchers/toFailTestAndMatchSnapshot/index.ts +48 -0
  52. package/src/matchers/toFailTestAndMatchSnapshot/predicate.ts +12 -0
  53. package/src/matchers/toFailTestWith/__snapshots__/index.test.ts.snap +34 -0
  54. package/src/matchers/toFailTestWith/index.ts +56 -0
  55. package/src/matchers/toFailTestWith/predicate.ts +24 -0
  56. package/src/matchers/toFailWith/__snapshots__/index.test.ts.snap +25 -0
  57. package/src/matchers/toFailWith/index.ts +52 -0
  58. package/src/matchers/toFailWith/predicate.ts +16 -0
  59. package/src/matchers/toFailWithDetail/__snapshots__/index.test.ts.snap +41 -0
  60. package/src/matchers/toFailWithDetail/index.ts +63 -0
  61. package/src/matchers/toFailWithDetail/predicate.ts +27 -0
  62. package/src/matchers/toHaveBeenCalledWithArgumentsMatching/__snapshots__/index.test.ts.snap +82 -0
  63. package/src/matchers/toHaveBeenCalledWithArgumentsMatching/index.ts +100 -0
  64. package/src/matchers/toHaveBeenCalledWithArgumentsMatching/predicate.ts +18 -0
  65. package/src/matchers/toSucceed/__snapshots__/index.test.ts.snap +17 -0
  66. package/src/matchers/toSucceed/index.ts +45 -0
  67. package/src/matchers/toSucceed/predicate.ts +7 -0
  68. package/src/matchers/toSucceedAndMatchInlineSnapshot/__snapshots__/index.test.ts.snap +15 -0
  69. package/src/matchers/toSucceedAndMatchInlineSnapshot/index.ts +44 -0
  70. package/src/matchers/toSucceedAndMatchSnapshot/__snapshots__/index.test.ts.snap +27 -0
  71. package/src/matchers/toSucceedAndMatchSnapshot/index.ts +47 -0
  72. package/src/matchers/toSucceedAndSatisfy/__snapshots__/index.test.ts.snap +37 -0
  73. package/src/matchers/toSucceedAndSatisfy/index.ts +78 -0
  74. package/src/matchers/toSucceedAndSatisfy/predicate.ts +22 -0
  75. package/src/matchers/toSucceedWith/__snapshots__/index.test.ts.snap +25 -0
  76. package/src/matchers/toSucceedWith/index.ts +58 -0
  77. package/src/matchers/toSucceedWith/predicate.ts +14 -0
  78. package/src/matchers/toSucceedWithDetail/__snapshots__/index.test.ts.snap +41 -0
  79. package/src/matchers/toSucceedWithDetail/index.ts +62 -0
  80. package/src/matchers/toSucceedWithDetail/predicate.ts +23 -0
  81. package/src/resolvers/cli.ts +21 -0
  82. package/src/resolvers/ide.ts +21 -0
  83. package/src/ts-utils.ts +1 -0
  84. package/src/types/index.ts +107 -0
  85. package/src/utils/colorHelpers.ts +82 -0
  86. package/src/utils/matcherHelpers.ts +64 -0
  87. package/src/utils/snapshotResolver.ts +28 -0
  88. package/test/data/testData.json +4 -0
  89. package/CHANGELOG.md +0 -105
  90. package/lib/helpers/fsHelpers.d.ts.map +0 -1
  91. package/lib/helpers/fsHelpers.js.map +0 -1
  92. package/lib/helpers/index.d.ts.map +0 -1
  93. package/lib/helpers/index.js.map +0 -1
  94. package/lib/index.d.ts.map +0 -1
  95. package/lib/index.js.map +0 -1
  96. package/lib/matchers/index.d.ts.map +0 -1
  97. package/lib/matchers/index.js.map +0 -1
  98. package/lib/matchers/toFail/index.d.ts.map +0 -1
  99. package/lib/matchers/toFail/index.js.map +0 -1
  100. package/lib/matchers/toFail/predicate.d.ts.map +0 -1
  101. package/lib/matchers/toFail/predicate.js.map +0 -1
  102. package/lib/matchers/toFailTest/index.d.ts.map +0 -1
  103. package/lib/matchers/toFailTest/index.js.map +0 -1
  104. package/lib/matchers/toFailTest/predicate.d.ts.map +0 -1
  105. package/lib/matchers/toFailTest/predicate.js.map +0 -1
  106. package/lib/matchers/toFailTestAndMatchSnapshot/index.d.ts.map +0 -1
  107. package/lib/matchers/toFailTestAndMatchSnapshot/index.js.map +0 -1
  108. package/lib/matchers/toFailTestAndMatchSnapshot/predicate.d.ts.map +0 -1
  109. package/lib/matchers/toFailTestAndMatchSnapshot/predicate.js.map +0 -1
  110. package/lib/matchers/toFailTestWith/index.d.ts.map +0 -1
  111. package/lib/matchers/toFailTestWith/index.js.map +0 -1
  112. package/lib/matchers/toFailTestWith/predicate.d.ts.map +0 -1
  113. package/lib/matchers/toFailTestWith/predicate.js.map +0 -1
  114. package/lib/matchers/toFailWith/index.d.ts.map +0 -1
  115. package/lib/matchers/toFailWith/index.js.map +0 -1
  116. package/lib/matchers/toFailWith/predicate.d.ts.map +0 -1
  117. package/lib/matchers/toFailWith/predicate.js.map +0 -1
  118. package/lib/matchers/toFailWithDetail/index.d.ts.map +0 -1
  119. package/lib/matchers/toFailWithDetail/index.js.map +0 -1
  120. package/lib/matchers/toFailWithDetail/predicate.d.ts.map +0 -1
  121. package/lib/matchers/toFailWithDetail/predicate.js.map +0 -1
  122. package/lib/matchers/toHaveBeenCalledWithArgumentsMatching/index.d.ts.map +0 -1
  123. package/lib/matchers/toHaveBeenCalledWithArgumentsMatching/index.js.map +0 -1
  124. package/lib/matchers/toHaveBeenCalledWithArgumentsMatching/predicate.d.ts.map +0 -1
  125. package/lib/matchers/toHaveBeenCalledWithArgumentsMatching/predicate.js.map +0 -1
  126. package/lib/matchers/toSucceed/index.d.ts.map +0 -1
  127. package/lib/matchers/toSucceed/index.js.map +0 -1
  128. package/lib/matchers/toSucceed/predicate.d.ts.map +0 -1
  129. package/lib/matchers/toSucceed/predicate.js.map +0 -1
  130. package/lib/matchers/toSucceedAndMatchInlineSnapshot/index.d.ts.map +0 -1
  131. package/lib/matchers/toSucceedAndMatchInlineSnapshot/index.js.map +0 -1
  132. package/lib/matchers/toSucceedAndMatchSnapshot/index.d.ts.map +0 -1
  133. package/lib/matchers/toSucceedAndMatchSnapshot/index.js.map +0 -1
  134. package/lib/matchers/toSucceedAndSatisfy/index.d.ts.map +0 -1
  135. package/lib/matchers/toSucceedAndSatisfy/index.js.map +0 -1
  136. package/lib/matchers/toSucceedAndSatisfy/predicate.d.ts.map +0 -1
  137. package/lib/matchers/toSucceedAndSatisfy/predicate.js.map +0 -1
  138. package/lib/matchers/toSucceedWith/index.d.ts.map +0 -1
  139. package/lib/matchers/toSucceedWith/index.js.map +0 -1
  140. package/lib/matchers/toSucceedWith/predicate.d.ts.map +0 -1
  141. package/lib/matchers/toSucceedWith/predicate.js.map +0 -1
  142. package/lib/matchers/toSucceedWithDetail/index.d.ts.map +0 -1
  143. package/lib/matchers/toSucceedWithDetail/index.js.map +0 -1
  144. package/lib/matchers/toSucceedWithDetail/predicate.d.ts.map +0 -1
  145. package/lib/matchers/toSucceedWithDetail/predicate.js.map +0 -1
  146. package/lib/resolvers/cli.d.ts.map +0 -1
  147. package/lib/resolvers/cli.js.map +0 -1
  148. package/lib/resolvers/ide.d.ts.map +0 -1
  149. package/lib/resolvers/ide.js.map +0 -1
  150. package/lib/ts-utils.d.ts.map +0 -1
  151. package/lib/ts-utils.js.map +0 -1
  152. package/lib/types/index.d.ts.map +0 -1
  153. package/lib/types/index.js.map +0 -1
  154. package/lib/utils/matcherHelpers.d.ts.map +0 -1
  155. package/lib/utils/matcherHelpers.js.map +0 -1
  156. package/lib/utils/snapshotResolver.d.ts.map +0 -1
  157. package/lib/utils/snapshotResolver.js.map +0 -1
@@ -0,0 +1,34 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`.toFailTestWith logs failure correctly when callback fails with .not: toFailTestAndMatchSnapshot 1`] = `
4
+ "expect(callback).not.toFailTestWith(expectedMessage)
5
+ Expected:
6
+ Not failure with /expect/i
7
+ Received: Callback failed with:
8
+ >>>>
9
+ expect(received).toBe(expected) // Object.is equality
10
+
11
+ Expected: false
12
+ Received: true
13
+ <<<<"
14
+ `;
15
+
16
+ exports[`.toFailTestWith logs failure correctly when callback returns an unexpected value: toFailTestAndMatchSnapshot 1`] = `
17
+ "expect(callback).toFailTestWith(expectedMessage)
18
+ Expected:
19
+ Failure with /random text/i
20
+ Received: Callback failed with:
21
+ >>>>
22
+ expect(received).toBe(expected) // Object.is equality
23
+
24
+ Expected: \\"goodbye\\"
25
+ Received: \\"hello\\"
26
+ <<<<"
27
+ `;
28
+
29
+ exports[`.toFailTestWith logs failure correctly when callback succeeds: toFailTestAndMatchSnapshot 1`] = `
30
+ "expect(callback).toFailTestWith(expectedMessage)
31
+ Expected:
32
+ Failure with \\"whatever\\"
33
+ Received: Callback succeeded"
34
+ `;
@@ -0,0 +1,56 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-vars,no-unused-vars */
2
+ import { Failure, Success } from '../../ts-utils';
3
+ import { matcherName, predicate } from './predicate';
4
+
5
+ import { matcherHint } from 'jest-matcher-utils';
6
+ import { printExpectedResult } from '../../utils/matcherHelpers';
7
+
8
+ declare global {
9
+ // eslint-disable-next-line @typescript-eslint/no-namespace
10
+ namespace jest {
11
+ // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars, @typescript-eslint/naming-convention
12
+ interface Matchers<R, T> {
13
+ /**
14
+ * Use .toFailTestWith to test a custom matcher by
15
+ * verifying that a test case fails as expected and
16
+ * reports an error matching a supplied value.
17
+ * @param expected -
18
+ */
19
+ toFailTestWith<T>(expected: string | string[] | RegExp): R;
20
+ }
21
+ }
22
+ }
23
+
24
+ function passMessage<T>(cbResult: Success<string>, expected: RegExp | string | string[]): () => string {
25
+ return () =>
26
+ [
27
+ matcherHint(`.not.${matcherName}`, 'callback', 'expectedMessage'),
28
+ printExpectedResult('failure', false, expected),
29
+ ` Received: Callback failed with:\n>>>>\n${cbResult.value}\n<<<<`
30
+ ].join('\n');
31
+ }
32
+
33
+ function failMessage<T>(cbResult: Failure<string>, expected: RegExp | string | string[]): () => string {
34
+ return () =>
35
+ [
36
+ matcherHint(`${matcherName}`, 'callback', 'expectedMessage'),
37
+ printExpectedResult('failure', true, expected),
38
+ cbResult.message === ''
39
+ ? ' Received: Callback succeeded'
40
+ : ` Received: Callback failed with:\n>>>>\n${cbResult.message}\n<<<<`
41
+ ].join('\n');
42
+ }
43
+
44
+ export default {
45
+ toFailTestWith: function <T>(
46
+ cb: () => void,
47
+ expected: RegExp | string | string[]
48
+ ): jest.CustomMatcherResult {
49
+ const cbResult = predicate(cb, expected);
50
+ if (cbResult.isSuccess()) {
51
+ return { pass: true, message: passMessage(cbResult, expected) };
52
+ }
53
+
54
+ return { pass: false, message: failMessage(cbResult, expected) };
55
+ }
56
+ };
@@ -0,0 +1,24 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-vars,no-unused-vars */
2
+ import { equals } from '@jest/expect-utils';
3
+ import { stringify } from 'jest-matcher-utils';
4
+ import { Result, captureResult, fail, succeed } from '../../ts-utils';
5
+
6
+ export const matcherName: string = 'toFailTestWith';
7
+
8
+ export function predicate<T>(cb: () => void, expected: string | string[] | RegExp): Result<string> {
9
+ const cbResult = captureResult(() => cb());
10
+ if (cbResult.isFailure()) {
11
+ let success = false;
12
+ if (expected instanceof RegExp) {
13
+ success = cbResult.message.match(expected) !== null;
14
+ } else if (typeof expected === 'string') {
15
+ success = cbResult.message === expected;
16
+ } else if (Array.isArray(expected)) {
17
+ success = equals(cbResult.message.split('\n'), expected);
18
+ } else {
19
+ return fail(`Unsupported expected value "${stringify(expected)}" for toFailTestWith`);
20
+ }
21
+ return success ? succeed(cbResult.message) : fail(cbResult.message);
22
+ }
23
+ return fail('');
24
+ }
@@ -0,0 +1,25 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`.toFailWith reports details correctly on a failure due to a success result: toFailTestAndMatchSnapshot 1`] = `
4
+ "expect(received).toFailWith(expected)
5
+ Expected:
6
+ Failure with \\"hello\\"
7
+ Received:
8
+ Success with \\"hello\\""
9
+ `;
10
+
11
+ exports[`.toFailWith reports details correctly on a failure due to non-matching failure value: toFailTestAndMatchSnapshot 1`] = `
12
+ "expect(received).toFailWith(expected)
13
+ Expected:
14
+ Failure with \\"error\\"
15
+ Received:
16
+ Failure with \\"oops\\""
17
+ `;
18
+
19
+ exports[`.toFailWith reports details correctly on a failure with a .not: toFailTestAndMatchSnapshot 1`] = `
20
+ "expect(received).not.toFailWith(expected)
21
+ Expected:
22
+ Not failure with \\"oops\\"
23
+ Received:
24
+ Failure with \\"oops\\""
25
+ `;
@@ -0,0 +1,52 @@
1
+ import { printExpectedResult, printReceivedResult } from '../../utils/matcherHelpers';
2
+ import { matcherName, predicate } from './predicate';
3
+
4
+ import { Result } from '@fgv/ts-utils';
5
+ import { matcherHint } from 'jest-matcher-utils';
6
+
7
+ declare global {
8
+ // eslint-disable-next-line @typescript-eslint/no-namespace
9
+ namespace jest {
10
+ // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars, @typescript-eslint/naming-convention
11
+ interface Matchers<R, T> {
12
+ /**
13
+ * Use .toFailWith to verify that a Result<T> is a failure
14
+ * that matches a supplied string, RegExp or undefined value
15
+ * @param message -
16
+ */
17
+ toFailWith(message: string | RegExp | undefined): R;
18
+ }
19
+ }
20
+ }
21
+
22
+ function passMessage<T>(received: Result<T>, expected: string | RegExp | undefined): () => string {
23
+ return () =>
24
+ [
25
+ matcherHint(`.not.${matcherName}`),
26
+ printExpectedResult('failure', false, expected),
27
+ printReceivedResult(received)
28
+ ].join('\n');
29
+ }
30
+
31
+ function failMessage<T>(received: Result<T>, expected: string | RegExp | undefined): () => string {
32
+ return () =>
33
+ [
34
+ matcherHint(`${matcherName}`),
35
+ printExpectedResult('failure', true, expected),
36
+ printReceivedResult(received)
37
+ ].join('\n');
38
+ }
39
+
40
+ export default {
41
+ toFailWith: function <T>(
42
+ received: Result<T>,
43
+ expected: string | RegExp | undefined
44
+ ): jest.CustomMatcherResult {
45
+ const pass = predicate(received, expected);
46
+ if (pass) {
47
+ return { pass: true, message: passMessage(received, expected) };
48
+ }
49
+
50
+ return { pass: false, message: failMessage(received, expected) };
51
+ }
52
+ };
@@ -0,0 +1,16 @@
1
+ import { Result } from '../../ts-utils';
2
+
3
+ export const matcherName: string = 'toFailWith';
4
+
5
+ export function predicate<T>(received: Result<T>, expected: string | RegExp | undefined): boolean {
6
+ if (received.isFailure()) {
7
+ if (expected === undefined) {
8
+ return received.message === undefined;
9
+ } else if (expected instanceof RegExp) {
10
+ return received.message.match(expected) !== null;
11
+ } else {
12
+ return received.message === expected;
13
+ }
14
+ }
15
+ return false;
16
+ }
@@ -0,0 +1,41 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`.toFailWithDetail reports details correctly on a failure due to a success result: toFailTestAndMatchSnapshot 1`] = `
4
+ "expect(received).toFailWith(expected)
5
+ Expected:
6
+ Failure with \\"hello\\"
7
+ Detail: \\"\\"detail\\"\\"
8
+ Received:
9
+ Success with \\"\\"hello\\"\\"
10
+ Detail: \\"undefined\\""
11
+ `;
12
+
13
+ exports[`.toFailWithDetail reports details correctly on a failure due to non-matching detail value: toFailTestAndMatchSnapshot 1`] = `
14
+ "expect(received).toFailWith(expected)
15
+ Expected:
16
+ Failure with \\"error\\"
17
+ Detail: \\"\\"detail\\"\\"
18
+ Received:
19
+ Failure with \\"error\\"
20
+ Detail: \\"\\"other detail\\"\\""
21
+ `;
22
+
23
+ exports[`.toFailWithDetail reports details correctly on a failure due to non-matching failure value: toFailTestAndMatchSnapshot 1`] = `
24
+ "expect(received).toFailWith(expected)
25
+ Expected:
26
+ Failure with \\"error\\"
27
+ Detail: \\"\\"detail\\"\\"
28
+ Received:
29
+ Failure with \\"oops\\"
30
+ Detail: \\"\\"detail\\"\\""
31
+ `;
32
+
33
+ exports[`.toFailWithDetail reports details correctly on a failure with a .not: toFailTestAndMatchSnapshot 1`] = `
34
+ "expect(received).not.toFailWith(expected)
35
+ Expected:
36
+ Not failure with \\"oops\\"
37
+ Detail: \\"\\"detail\\"\\"
38
+ Received:
39
+ Failure with \\"oops\\"
40
+ Detail: \\"\\"detail\\"\\""
41
+ `;
@@ -0,0 +1,63 @@
1
+ import { DetailedResult, ResultDetailType } from '@fgv/ts-utils';
2
+ import { printExpectedDetailedResult, printReceivedDetailedResult } from '../../utils/matcherHelpers';
3
+ import { matcherName, predicate } from './predicate';
4
+
5
+ import { matcherHint } from 'jest-matcher-utils';
6
+
7
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-unused-vars
8
+ declare global {
9
+ // eslint-disable-next-line @typescript-eslint/no-namespace
10
+ namespace jest {
11
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars, @typescript-eslint/naming-convention
12
+ interface Matchers<R, T> {
13
+ /**
14
+ * Use .toFailWithDetail to verify that a DetailedResult<T> is
15
+ * a failure that matches both a supplied expected failure message
16
+ * (string, RegExp or undefined) and a supplied failure detail.
17
+ * @param message -
18
+ */
19
+ toFailWithDetail(message: string | RegExp | undefined, detail: ResultDetailType<T>): R;
20
+ }
21
+ }
22
+ }
23
+
24
+ function passMessage<T, TD>(
25
+ received: DetailedResult<T, TD>,
26
+ expectedMessage: string | RegExp | undefined,
27
+ expectedDetail: TD
28
+ ): () => string {
29
+ return () =>
30
+ [
31
+ matcherHint(`.not.${matcherName}`),
32
+ printExpectedDetailedResult('failure', false, expectedMessage, expectedDetail),
33
+ printReceivedDetailedResult(received)
34
+ ].join('\n');
35
+ }
36
+
37
+ function failMessage<T, TD>(
38
+ received: DetailedResult<T, TD>,
39
+ expectedMessage: string | RegExp | undefined,
40
+ expectedDetail: TD
41
+ ): () => string {
42
+ return () =>
43
+ [
44
+ matcherHint(`${matcherName}`),
45
+ printExpectedDetailedResult('failure', true, expectedMessage, expectedDetail),
46
+ printReceivedDetailedResult(received)
47
+ ].join('\n');
48
+ }
49
+
50
+ export default {
51
+ toFailWithDetail: function <T extends DetailedResult<unknown, unknown>>(
52
+ received: T,
53
+ expectedMessage: string | RegExp | undefined,
54
+ expectedDetail: ResultDetailType<T>
55
+ ): jest.CustomMatcherResult {
56
+ const pass = predicate(received, expectedMessage, expectedDetail);
57
+ if (pass) {
58
+ return { pass: true, message: passMessage(received, expectedMessage, expectedDetail) };
59
+ }
60
+
61
+ return { pass: false, message: failMessage(received, expectedMessage, expectedDetail) };
62
+ }
63
+ };
@@ -0,0 +1,27 @@
1
+ import { equals } from '@jest/expect-utils';
2
+ import { DetailedResult } from '../../ts-utils';
3
+
4
+ export const matcherName: string = 'toFailWith';
5
+
6
+ export function predicate<T, TD>(
7
+ received: DetailedResult<T, TD>,
8
+ expectedResult: string | RegExp | undefined,
9
+ expectedDetail: TD
10
+ ): boolean {
11
+ if (received.isFailure()) {
12
+ if (expectedResult === undefined) {
13
+ if (received.message !== undefined) {
14
+ return false;
15
+ }
16
+ } else if (expectedResult instanceof RegExp) {
17
+ if (received.message.match(expectedResult) === null) {
18
+ return false;
19
+ }
20
+ } else if (received.message !== expectedResult) {
21
+ return false;
22
+ }
23
+
24
+ return equals(received.detail, expectedDetail);
25
+ }
26
+ return false;
27
+ }
@@ -0,0 +1,82 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`.toHaveBeenCalledWithArgumentsMatching reports a helpful message if a .not test fails with few calls 1`] = `
4
+ "expect(received).not.toHaveBeenCalledWithArgumentsMatching(expected)
5
+ Expected no call with arguments matching:
6
+ [Any<String>]
7
+ Received (1 total):
8
+ *000: [\\"arg1\\"]"
9
+ `;
10
+
11
+ exports[`.toHaveBeenCalledWithArgumentsMatching reports a helpful message if a .not test fails with many calls "for a call in the middle" 1`] = `
12
+ "expect(received).not.toHaveBeenCalledWithArgumentsMatching(expected)
13
+ Expected no call with arguments matching:
14
+ [\\"arg3\\"]
15
+ Received (7 total):
16
+ 002: [\\"arg2\\"]
17
+ *003: [\\"arg3\\"]
18
+ 004: [\\"arg4\\"]"
19
+ `;
20
+
21
+ exports[`.toHaveBeenCalledWithArgumentsMatching reports a helpful message if a .not test fails with many calls "for the first call" 1`] = `
22
+ "expect(received).not.toHaveBeenCalledWithArgumentsMatching(expected)
23
+ Expected no call with arguments matching:
24
+ [\\"arg0\\"]
25
+ Received (7 total):
26
+ *000: [\\"arg0\\"]
27
+ 001: [\\"arg1\\"]
28
+ 002: [\\"arg2\\"]"
29
+ `;
30
+
31
+ exports[`.toHaveBeenCalledWithArgumentsMatching reports a helpful message if a .not test fails with many calls "for the last call" 1`] = `
32
+ "expect(received).not.toHaveBeenCalledWithArgumentsMatching(expected)
33
+ Expected no call with arguments matching:
34
+ [\\"arg6\\"]
35
+ Received (7 total):
36
+ 004: [\\"arg4\\"]
37
+ 005: [\\"arg5\\"]
38
+ *006: [\\"arg6\\"]"
39
+ `;
40
+
41
+ exports[`.toHaveBeenCalledWithArgumentsMatching reports a helpful message if a .not test fails with many calls "for the second call" 1`] = `
42
+ "expect(received).not.toHaveBeenCalledWithArgumentsMatching(expected)
43
+ Expected no call with arguments matching:
44
+ [StringMatching /1/]
45
+ Received (7 total):
46
+ 000: [\\"arg0\\"]
47
+ *001: [\\"arg1\\"]
48
+ 002: [\\"arg2\\"]"
49
+ `;
50
+
51
+ exports[`.toHaveBeenCalledWithArgumentsMatching reports a helpful message if a .not test fails with many calls "for the second-to-last-call" 1`] = `
52
+ "expect(received).not.toHaveBeenCalledWithArgumentsMatching(expected)
53
+ Expected no call with arguments matching:
54
+ [\\"arg5\\"]
55
+ Received (7 total):
56
+ 004: [\\"arg4\\"]
57
+ *005: [\\"arg5\\"]
58
+ 006: [\\"arg6\\"]"
59
+ `;
60
+
61
+ exports[`.toHaveBeenCalledWithArgumentsMatching reports a helpful message if a call is not matched 1`] = `
62
+ "expect(received).toHaveBeenCalledWithArgumentsMatching(expected)
63
+ Expected call with arguments matching:
64
+ [\\"call7\\"]
65
+ Received (6 total):
66
+ 003: [\\"call4\\"]
67
+ 004: [\\"call5\\"]
68
+ 005: [\\"call6\\"]"
69
+ `;
70
+
71
+ exports[`.toHaveBeenCalledWithArgumentsMatching reports a helpful message if function was not called 1`] = `
72
+ "expect(received).toHaveBeenCalledWithArgumentsMatching(expected)
73
+ Expected call with arguments matching:
74
+ [\\"call7\\"]
75
+ Received (0 total):"
76
+ `;
77
+
78
+ exports[`.toHaveBeenCalledWithArgumentsMatching throws if argument is "an object" 1`] = `"Test error: toHaveBeenCalledWithArgumentsMatching called with other than jest.Mock"`;
79
+
80
+ exports[`.toHaveBeenCalledWithArgumentsMatching throws if argument is "not a mock function" 1`] = `"Test error: toHaveBeenCalledWithArgumentsMatching called with other than jest.Mock"`;
81
+
82
+ exports[`.toHaveBeenCalledWithArgumentsMatching throws if argument is "null" 1`] = `"Test error: toHaveBeenCalledWithArgumentsMatching called with other than jest.Mock"`;
@@ -0,0 +1,100 @@
1
+ import { getTypeOfProperty, getValueOfPropertyOrDefault } from '@fgv/ts-utils';
2
+ import { matcherHint, printExpected, printReceived } from 'jest-matcher-utils';
3
+ import { IMatchedCall, matcherName, predicate } from './predicate';
4
+
5
+ declare global {
6
+ // eslint-disable-next-line @typescript-eslint/no-namespace
7
+ namespace jest {
8
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars, @typescript-eslint/naming-convention
9
+ interface Matchers<R> {
10
+ /**
11
+ * Use .toSucceedWith to verify that a Result<T> is a success
12
+ * and that the result value matches the supplied value
13
+ * @param expected -
14
+ */
15
+ toHaveBeenCalledWithArgumentsMatching(expected: unknown): R;
16
+ }
17
+ }
18
+ }
19
+
20
+ function getRange(length: number, cursor: number): { start: number; end: number } {
21
+ // less than 3 return everything
22
+ if (length < 3) {
23
+ return { start: 0, end: length };
24
+ }
25
+
26
+ if (cursor === 0) {
27
+ return { start: 0, end: 3 };
28
+ } else if (cursor >= length - 1) {
29
+ return { start: length - 3, end: length };
30
+ }
31
+ return { start: cursor - 1, end: cursor + 2 };
32
+ }
33
+
34
+ function formatOneCall(index: number, received: unknown, cursor: boolean): string {
35
+ const indexString = index.toLocaleString([], { maximumFractionDigits: 0, minimumIntegerDigits: 3 });
36
+ const cursorString = cursor ? '*' : ' ';
37
+ return `${cursorString}${indexString}: ${printReceived(received)}`;
38
+ }
39
+
40
+ function formatArgsMessage(received: jest.Mock, cursor?: number): string[] {
41
+ const calls = received.mock.calls;
42
+
43
+ if (calls.length > 0) {
44
+ // if there's no cursor, show the last 3
45
+ const { start, end } = getRange(calls.length, cursor !== undefined ? cursor : calls.length - 1);
46
+ const callsToShow = calls.slice(start, end);
47
+ return callsToShow.map((c, i) => formatOneCall(start + i, c, start + i === cursor));
48
+ }
49
+ return [];
50
+ }
51
+
52
+ function passMessage(received: jest.Mock, expected: unknown, matched: IMatchedCall): () => string {
53
+ return () =>
54
+ [
55
+ matcherHint(`.not.${matcherName}`),
56
+ 'Expected no call with arguments matching:',
57
+ ` ${printExpected(expected)}`,
58
+ `Received (${received.mock.calls.length} total):`,
59
+ ...formatArgsMessage(received, matched.index)
60
+ ].join('\n');
61
+ }
62
+
63
+ function failMessage(received: jest.Mock, expected: unknown): () => string {
64
+ return () =>
65
+ [
66
+ matcherHint(`${matcherName}`),
67
+ 'Expected call with arguments matching:',
68
+ ` ${printExpected(expected)}`,
69
+ `Received (${received.mock.calls.length} total):`,
70
+ ...formatArgsMessage(received)
71
+ ].join('\n');
72
+ }
73
+
74
+ function isMock(received: unknown): received is jest.Mock {
75
+ if (received !== null && !Array.isArray(received)) {
76
+ return (
77
+ getValueOfPropertyOrDefault('_isMockFunction', received as object) === true &&
78
+ getTypeOfProperty('mock', received as object) === 'object'
79
+ );
80
+ }
81
+ return false;
82
+ }
83
+
84
+ export default {
85
+ toHaveBeenCalledWithArgumentsMatching: function (
86
+ received: jest.Mock,
87
+ expected: unknown
88
+ ): jest.CustomMatcherResult {
89
+ if (!isMock(received)) {
90
+ throw new Error('Test error: toHaveBeenCalledWithArgumentsMatching called with other than jest.Mock');
91
+ }
92
+
93
+ const matched = predicate(received, expected);
94
+ if (matched !== undefined) {
95
+ return { pass: true, message: passMessage(received, expected, matched) };
96
+ }
97
+
98
+ return { pass: false, message: failMessage(received, expected) };
99
+ }
100
+ };
@@ -0,0 +1,18 @@
1
+ import { equals } from '@jest/expect-utils';
2
+
3
+ export const matcherName: string = 'toHaveBeenCalledWithArgumentsMatching';
4
+
5
+ export interface IMatchedCall {
6
+ index: number;
7
+ arguments: unknown[];
8
+ }
9
+
10
+ export function predicate(received: jest.Mock, expected: unknown): undefined | IMatchedCall {
11
+ for (let i = 0; i < received.mock.calls.length; i++) {
12
+ const args = received.mock.calls[i];
13
+ if (equals(args, expected)) {
14
+ return { index: i, arguments: args };
15
+ }
16
+ }
17
+ return undefined;
18
+ }
@@ -0,0 +1,17 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`.toSucceed reports details for a failed .not test: toFailTestAndMatchSnapshot 1`] = `
4
+ "expect(received).not.toSucceed(expected)
5
+ Expected:
6
+ Not success
7
+ Received:
8
+ Success with \\"hello\\""
9
+ `;
10
+
11
+ exports[`.toSucceed reports details for a failed test: toFailTestAndMatchSnapshot 1`] = `
12
+ "expect(received).toSucceed(expected)
13
+ Expected:
14
+ Success
15
+ Received:
16
+ Failure with \\"oops\\""
17
+ `;
@@ -0,0 +1,45 @@
1
+ import { printExpectedResult, printReceivedResult } from '../../utils/matcherHelpers';
2
+ import { matcherName, predicate } from './predicate';
3
+
4
+ import { Result } from '@fgv/ts-utils';
5
+ import { matcherHint } from 'jest-matcher-utils';
6
+
7
+ declare global {
8
+ // eslint-disable-next-line @typescript-eslint/no-namespace
9
+ namespace jest {
10
+ // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars, @typescript-eslint/naming-convention
11
+ interface Matchers<R, T> {
12
+ /**
13
+ * Use .toSucceed to verify that a Result<T> is a success
14
+ */
15
+ toSucceed(): R;
16
+ }
17
+ }
18
+ }
19
+
20
+ function passMessage<T>(received: Result<T>): () => string {
21
+ return () =>
22
+ [
23
+ matcherHint(`.not.${matcherName}`),
24
+ printExpectedResult('success', false),
25
+ printReceivedResult(received)
26
+ ].join('\n');
27
+ }
28
+
29
+ function failMessage<T>(received: Result<T>): () => string {
30
+ return () =>
31
+ [matcherHint(`${matcherName}`), printExpectedResult('success', true), printReceivedResult(received)].join(
32
+ '\n'
33
+ );
34
+ }
35
+
36
+ export default {
37
+ toSucceed: function <T>(received: Result<T>): jest.CustomMatcherResult {
38
+ const pass = predicate(received);
39
+ if (pass) {
40
+ return { pass: true, message: passMessage(received) };
41
+ }
42
+
43
+ return { pass: false, message: failMessage(received) };
44
+ }
45
+ };
@@ -0,0 +1,7 @@
1
+ import { Result } from '../../ts-utils';
2
+
3
+ export const matcherName: string = 'toSucceed';
4
+
5
+ export function predicate<T>(received: Result<T>): boolean {
6
+ return received.isSuccess();
7
+ }
@@ -0,0 +1,15 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`.toSucceedAndMatchSnapshot logs details correctly for a failed result: toFailTestAndMatchSnapshot 1`] = `
4
+ "expect(callback).toSucceedAndMatchInlineSnapshot(expected)
5
+ Expected:
6
+ Callback to succeed with a result that matches the snapshot
7
+ Received:
8
+ Failure with \\"oopsy\\""
9
+ `;
10
+
11
+ exports[`.toSucceedAndMatchSnapshot logs details correctly for a failed result: toFailTestAndMatchSnapshot 2`] = `
12
+ "expect(received).not.toMatchInlineSnapshot(properties, snapshot)
13
+
14
+ Matcher error: Snapshot matchers cannot be used with not"
15
+ `;