vest 5.0.0-dev-781e21 → 5.0.2-dev-d315d9

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 (182) hide show
  1. package/LICENSE +2 -2
  2. package/README.md +2 -57
  3. package/dist/cjs/classnames.development.js +18 -16
  4. package/dist/cjs/classnames.production.js +1 -1
  5. package/dist/cjs/enforce/compose.development.js +5 -54
  6. package/dist/cjs/enforce/compose.production.js +1 -1
  7. package/dist/cjs/enforce/compounds.development.js +20 -83
  8. package/dist/cjs/enforce/compounds.production.js +1 -1
  9. package/dist/cjs/enforce/schema.development.js +19 -82
  10. package/dist/cjs/enforce/schema.production.js +1 -1
  11. package/dist/cjs/parser.development.js +12 -9
  12. package/dist/cjs/parser.production.js +1 -1
  13. package/dist/cjs/promisify.development.js +4 -8
  14. package/dist/cjs/promisify.production.js +1 -1
  15. package/dist/cjs/vest.development.js +1287 -1153
  16. package/dist/cjs/vest.production.js +1 -1
  17. package/dist/es/classnames.development.js +20 -18
  18. package/dist/es/classnames.production.js +1 -1
  19. package/dist/es/enforce/compose.development.js +1 -58
  20. package/dist/es/enforce/compose.production.js +1 -1
  21. package/dist/es/enforce/compounds.development.js +2 -90
  22. package/dist/es/enforce/compounds.production.js +1 -1
  23. package/dist/es/enforce/schema.development.js +2 -88
  24. package/dist/es/enforce/schema.production.js +1 -1
  25. package/dist/es/parser.development.js +12 -9
  26. package/dist/es/parser.production.js +1 -1
  27. package/dist/es/promisify.development.js +5 -9
  28. package/dist/es/promisify.production.js +1 -1
  29. package/dist/es/vest.development.js +1286 -1148
  30. package/dist/es/vest.production.js +1 -1
  31. package/dist/umd/classnames.development.js +21 -19
  32. package/dist/umd/classnames.production.js +1 -1
  33. package/dist/umd/enforce/compose.development.js +9 -57
  34. package/dist/umd/enforce/compose.production.js +1 -1
  35. package/dist/umd/enforce/compounds.development.js +32 -94
  36. package/dist/umd/enforce/compounds.production.js +1 -1
  37. package/dist/umd/enforce/schema.development.js +32 -94
  38. package/dist/umd/enforce/schema.production.js +1 -1
  39. package/dist/umd/parser.development.js +16 -13
  40. package/dist/umd/parser.production.js +1 -1
  41. package/dist/umd/promisify.development.js +7 -11
  42. package/dist/umd/promisify.production.js +1 -1
  43. package/dist/umd/vest.development.js +1289 -1155
  44. package/dist/umd/vest.production.js +1 -1
  45. package/package.json +12 -16
  46. package/src/__tests__/__snapshots__/integration.async-tests.test.ts.snap +71 -0
  47. package/src/__tests__/__snapshots__/integration.base.test.ts.snap +71 -0
  48. package/src/__tests__/__snapshots__/integration.stateful-async.test.ts.snap +243 -0
  49. package/src/__tests__/__snapshots__/integration.stateful-tests.test.ts.snap +376 -0
  50. package/src/__tests__/integration.async-tests.test.ts +90 -0
  51. package/src/__tests__/integration.base.test.ts +45 -0
  52. package/src/__tests__/integration.exclusive.test.ts +88 -0
  53. package/src/__tests__/integration.stateful-async.test.ts +137 -0
  54. package/src/__tests__/integration.stateful-tests.test.ts +155 -0
  55. package/src/__tests__/isolate.test.ts +288 -0
  56. package/src/__tests__/state_refill.test.ts +41 -0
  57. package/src/core/SuiteWalker/SuiteWalker.ts +64 -0
  58. package/src/core/SuiteWalker/__tests__/hasRemainingTests.test.ts +130 -0
  59. package/src/core/VestBus/VestBus.ts +78 -0
  60. package/src/core/context/PersistedContext.ts +243 -0
  61. package/src/core/context/SuiteContext.ts +74 -0
  62. package/src/core/isolate/IsolateTest/IsolateTest.ts +213 -0
  63. package/src/core/isolate/IsolateTest/IsolateTestReconciler.ts +156 -0
  64. package/src/core/isolate/IsolateTest/IsolateTestStateMachine.ts +69 -0
  65. package/src/core/isolate/IsolateTest/SimpleStateMachine.ts +43 -0
  66. package/src/core/isolate/IsolateTest/TestWalker.ts +77 -0
  67. package/src/core/isolate/IsolateTypes.ts +10 -0
  68. package/src/core/isolate/isIsolate.ts +6 -0
  69. package/src/core/isolate/isolate.ts +110 -0
  70. package/src/core/isolate/reconciler/Reconciler/Reconciler.ts +123 -0
  71. package/src/core/isolate/reconciler/cancelOverriddenPendingTest.ts +15 -0
  72. package/src/core/isolate/reconciler/isSameProfileTest.ts +12 -0
  73. package/src/core/isolate/walker.ts +127 -0
  74. package/src/core/test/TestTypes.ts +3 -0
  75. package/src/core/test/__tests__/IsolateTest.test.ts +152 -0
  76. package/src/core/test/__tests__/__snapshots__/IsolateTest.test.ts.snap +39 -0
  77. package/src/core/test/__tests__/__snapshots__/memo.test.ts.snap +101 -0
  78. package/src/core/test/__tests__/__snapshots__/test.test.ts.snap +231 -0
  79. package/src/core/test/__tests__/key.test.ts +195 -0
  80. package/src/core/test/__tests__/memo.test.ts +218 -0
  81. package/src/core/test/__tests__/merging_of_previous_test_runs.test.ts +341 -0
  82. package/src/core/test/__tests__/runAsyncTest.test.ts +175 -0
  83. package/src/core/test/__tests__/test.test.ts +226 -0
  84. package/src/core/test/helpers/__tests__/nonMatchingSeverityProfile.test.ts +52 -0
  85. package/src/core/test/helpers/asVestTest.ts +9 -0
  86. package/src/core/test/helpers/matchingFieldName.ts +16 -0
  87. package/src/core/test/helpers/matchingGroupName.ts +12 -0
  88. package/src/core/test/helpers/nonMatchingSeverityProfile.ts +14 -0
  89. package/src/core/test/helpers/shouldUseErrorMessage.ts +9 -0
  90. package/src/core/test/test.memo.ts +81 -0
  91. package/src/core/test/test.ts +64 -0
  92. package/src/core/test/testLevelFlowControl/runTest.ts +86 -0
  93. package/src/core/test/testLevelFlowControl/verifyTestRun.ts +32 -0
  94. package/src/core/test/testObjectIsolate.ts +11 -0
  95. package/src/errors/ErrorStrings.ts +4 -0
  96. package/src/exports/__tests__/classnames.test.ts +92 -0
  97. package/src/exports/__tests__/parser.test.ts +441 -0
  98. package/src/exports/__tests__/promisify.test.ts +77 -0
  99. package/src/exports/classnames.ts +35 -0
  100. package/src/exports/enforce@compose.ts +1 -0
  101. package/src/exports/enforce@compounds.ts +1 -0
  102. package/src/exports/enforce@schema.ts +1 -0
  103. package/src/exports/parser.ts +63 -0
  104. package/src/exports/promisify.ts +18 -0
  105. package/src/hooks/__tests__/__snapshots__/include.test.ts.snap +794 -0
  106. package/src/hooks/__tests__/eager.test.ts +130 -0
  107. package/src/hooks/__tests__/exclusive.test.ts +578 -0
  108. package/src/hooks/__tests__/include.test.ts +431 -0
  109. package/src/hooks/__tests__/optional.test.ts +83 -0
  110. package/src/hooks/__tests__/warn.test.ts +42 -0
  111. package/src/hooks/exclusive.ts +179 -0
  112. package/src/hooks/include.ts +54 -0
  113. package/src/hooks/mode.ts +47 -0
  114. package/src/hooks/optional/OptionalTypes.ts +31 -0
  115. package/src/hooks/optional/optional.ts +69 -0
  116. package/src/hooks/warn.ts +19 -0
  117. package/src/isolates/__tests__/__snapshots__/each.test.ts.snap +3 -0
  118. package/src/isolates/__tests__/__snapshots__/group.test.ts.snap +114 -0
  119. package/src/isolates/__tests__/__snapshots__/omitWhen.test.ts.snap +443 -0
  120. package/src/isolates/__tests__/__snapshots__/skipWhen.test.ts.snap +99 -0
  121. package/src/isolates/__tests__/each.test.ts +35 -0
  122. package/src/isolates/__tests__/group.test.ts +362 -0
  123. package/src/isolates/__tests__/omitWhen.test.ts +246 -0
  124. package/src/isolates/__tests__/skipWhen.test.ts +163 -0
  125. package/src/isolates/each.ts +30 -0
  126. package/src/isolates/group.ts +9 -0
  127. package/src/isolates/omitWhen.ts +41 -0
  128. package/src/isolates/skipWhen.ts +42 -0
  129. package/src/suite/__tests__/__snapshots__/create.test.ts.snap +67 -0
  130. package/src/suite/__tests__/create.test.ts +109 -0
  131. package/src/suite/__tests__/remove.test.ts +50 -0
  132. package/src/suite/__tests__/resetField.test.ts +74 -0
  133. package/src/suite/createSuite.ts +94 -0
  134. package/src/suite/runCallbacks.ts +28 -0
  135. package/src/suiteResult/Severity.ts +15 -0
  136. package/src/suiteResult/SuiteResultTypes.ts +42 -0
  137. package/src/suiteResult/__tests__/done.test.ts +334 -0
  138. package/src/suiteResult/__tests__/produce.test.ts +163 -0
  139. package/src/suiteResult/done/deferDoneCallback.ts +28 -0
  140. package/src/suiteResult/done/shouldSkipDoneRegistration.ts +20 -0
  141. package/src/suiteResult/selectors/__tests__/__snapshots__/collectFailureMessages.test.ts.snap +89 -0
  142. package/src/suiteResult/selectors/__tests__/collectFailureMessages.test.ts +124 -0
  143. package/src/suiteResult/selectors/__tests__/getFailures.test.ts +158 -0
  144. package/src/suiteResult/selectors/__tests__/getFailuresByGroup.test.ts +199 -0
  145. package/src/suiteResult/selectors/__tests__/hasFailures.test.ts +141 -0
  146. package/src/suiteResult/selectors/__tests__/hasFailuresByGroup.test.ts +185 -0
  147. package/src/suiteResult/selectors/__tests__/hasFailuresByTestObject.test.ts +88 -0
  148. package/src/suiteResult/selectors/__tests__/isValid.test.ts +359 -0
  149. package/src/suiteResult/selectors/__tests__/isValidByGroup.test.ts +480 -0
  150. package/src/suiteResult/selectors/collectFailures.ts +43 -0
  151. package/src/suiteResult/selectors/hasFailuresByTestObjects.ts +62 -0
  152. package/src/suiteResult/selectors/produceSuiteSummary.ts +135 -0
  153. package/src/suiteResult/selectors/shouldAddValidProperty.ts +148 -0
  154. package/src/suiteResult/selectors/suiteSelectors.ts +199 -0
  155. package/src/suiteResult/suiteResult.ts +15 -0
  156. package/src/suiteResult/suiteRunResult.ts +43 -0
  157. package/src/vest.ts +36 -0
  158. package/testUtils/TVestMock.ts +5 -0
  159. package/testUtils/__tests__/partition.test.ts +4 -4
  160. package/testUtils/mockThrowError.ts +4 -2
  161. package/testUtils/suiteDummy.ts +2 -1
  162. package/testUtils/testDummy.ts +12 -10
  163. package/testUtils/testPromise.ts +3 -0
  164. package/tsconfig.json +84 -2
  165. package/types/classnames.d.ts +39 -4
  166. package/types/classnames.d.ts.map +1 -0
  167. package/types/enforce/compose.d.ts +2 -126
  168. package/types/enforce/compose.d.ts.map +1 -0
  169. package/types/enforce/compounds.d.ts +2 -136
  170. package/types/enforce/compounds.d.ts.map +1 -0
  171. package/types/enforce/schema.d.ts +2 -144
  172. package/types/enforce/schema.d.ts.map +1 -0
  173. package/types/parser.d.ts +45 -10
  174. package/types/parser.d.ts.map +1 -0
  175. package/types/promisify.d.ts +36 -34
  176. package/types/promisify.d.ts.map +1 -0
  177. package/types/vest.d.ts +169 -224
  178. package/types/vest.d.ts.map +1 -0
  179. package/CHANGELOG.md +0 -87
  180. package/testUtils/expandStateRef.ts +0 -8
  181. package/testUtils/runCreateRef.ts +0 -10
  182. package/testUtils/testObjects.ts +0 -6
@@ -0,0 +1,226 @@
1
+ import { faker } from '@faker-js/faker';
2
+
3
+ import { TestPromise } from '../../../../testUtils/testPromise';
4
+
5
+ import { create, test, warn, enforce, IsolateTest } from 'vest';
6
+
7
+ let testObject: IsolateTest;
8
+
9
+ describe("Test Vest's `test` function", () => {
10
+ describe('test callbacks', () => {
11
+ describe('Warn hook', () => {
12
+ it('Should be marked as warning when the warn hook gets called', () => {
13
+ create(() => {
14
+ testObject = test(faker.random.word(), faker.lorem.sentence(), () => {
15
+ warn();
16
+ });
17
+ })();
18
+ expect(testObject.warns()).toBe(true);
19
+ });
20
+ });
21
+
22
+ describe('Sync', () => {
23
+ it('Should be marked as failed after a thrown error', () => {
24
+ create(() => {
25
+ testObject = test(faker.random.word(), faker.lorem.sentence(), () => {
26
+ throw new Error();
27
+ });
28
+ })();
29
+ expect(testObject.isFailing()).toBe(true);
30
+ // @ts-ignore - very much intentional
31
+ expect(testObject == false).toBe(true); //eslint-disable-line
32
+ });
33
+
34
+ it('Should be marked as failed for an explicit false return', () => {
35
+ create(() => {
36
+ test(faker.random.word(), faker.lorem.sentence(), () => false);
37
+ })();
38
+ expect(testObject.isFailing()).toBe(true);
39
+ // @ts-ignore - very much intentional
40
+ expect(testObject == false).toBe(true); //eslint-disable-line
41
+ });
42
+
43
+ describe('Thrown with a message', () => {
44
+ describe('When field has a message', () => {
45
+ it("Should use field's own message", () => {
46
+ const res = create(() => {
47
+ test('field_with_message', 'some_field_message', () => {
48
+ failWithString();
49
+ });
50
+ test('warning_field_with_message', 'some_field_message', () => {
51
+ warn();
52
+ failWithString();
53
+ });
54
+ })();
55
+
56
+ expect(res.getErrors('field_with_message')).toEqual([
57
+ 'some_field_message',
58
+ ]);
59
+ expect(res.tests['field_with_message'].errors).toEqual([
60
+ 'some_field_message',
61
+ ]);
62
+ expect(res.getWarnings('warning_field_with_message')).toEqual([
63
+ 'some_field_message',
64
+ ]);
65
+ expect(res.tests['warning_field_with_message'].warnings).toEqual([
66
+ 'some_field_message',
67
+ ]);
68
+ });
69
+ });
70
+ describe('When field does not have a message', () => {
71
+ it('Should use message from enforce().message()', () => {
72
+ const res = create(() => {
73
+ test('field_without_message', () => {
74
+ enforce(100).message('some_field_message').equals(0);
75
+ });
76
+ })();
77
+
78
+ expect(res.getErrors('field_without_message')).toEqual([
79
+ 'some_field_message',
80
+ ]);
81
+ });
82
+ it('Should use message from thrown error', () => {
83
+ const res = create(() => {
84
+ test('field_without_message', () => {
85
+ failWithString();
86
+ });
87
+ test('warning_field_without_message', () => {
88
+ warn();
89
+ failWithString();
90
+ });
91
+ })();
92
+
93
+ expect(res.getErrors('field_without_message')).toEqual([
94
+ 'I fail with a message',
95
+ ]);
96
+ expect(res.tests['field_without_message'].errors).toEqual([
97
+ 'I fail with a message',
98
+ ]);
99
+ expect(res.getWarnings('warning_field_without_message')).toEqual([
100
+ 'I fail with a message',
101
+ ]);
102
+ expect(res.tests['warning_field_without_message'].warnings).toEqual(
103
+ ['I fail with a message']
104
+ );
105
+ });
106
+ });
107
+ });
108
+ });
109
+
110
+ describe('async', () => {
111
+ it('Should be marked as failed when a returned promise rejects', () =>
112
+ TestPromise(done => {
113
+ create(() => {
114
+ testObject = test(
115
+ faker.random.word(),
116
+ faker.lorem.sentence(),
117
+ () =>
118
+ new Promise((_, reject) => {
119
+ expect(testObject.isFailing()).toBe(false);
120
+ setTimeout(reject, 300);
121
+ })
122
+ );
123
+ expect(testObject.isFailing()).toBe(false);
124
+ setTimeout(() => {
125
+ expect(testObject.isFailing()).toBe(true);
126
+ done();
127
+ }, 310);
128
+ })();
129
+ }));
130
+ });
131
+ });
132
+
133
+ describe('test params', () => {
134
+ let testObject: IsolateTest;
135
+ it('creates a test without a message and without a key', () => {
136
+ create(() => {
137
+ testObject = test('field_name', () => undefined);
138
+ })();
139
+ expect(testObject.fieldName).toBe('field_name');
140
+ expect(testObject.key).toBeNull();
141
+ expect(testObject.message).toBeUndefined();
142
+ expect(testObject).toMatchSnapshot();
143
+ });
144
+
145
+ it('creates a test without a key', () => {
146
+ create(() => {
147
+ testObject = test('field_name', 'failure message', () => undefined);
148
+ })();
149
+ expect(testObject.fieldName).toBe('field_name');
150
+ expect(testObject.key).toBeNull();
151
+ expect(testObject.message).toBe('failure message');
152
+ expect(testObject).toMatchSnapshot();
153
+ });
154
+
155
+ it('creates a test without a message and with a key', () => {
156
+ create(() => {
157
+ testObject = test('field_name', () => undefined, 'keyboardcat');
158
+ })();
159
+ expect(testObject.fieldName).toBe('field_name');
160
+ expect(testObject.key).toBe('keyboardcat');
161
+ expect(testObject.message).toBeUndefined();
162
+ expect(testObject).toMatchSnapshot();
163
+ });
164
+
165
+ it('creates a test with a message and with a key', () => {
166
+ create(() => {
167
+ testObject = test(
168
+ 'field_name',
169
+ 'failure message',
170
+ () => undefined,
171
+ 'keyboardcat'
172
+ );
173
+ })();
174
+ expect(testObject.fieldName).toBe('field_name');
175
+ expect(testObject.key).toBe('keyboardcat');
176
+ expect(testObject.message).toBe('failure message');
177
+ expect(testObject).toMatchSnapshot();
178
+ });
179
+
180
+ it('throws when field name is not a string', () => {
181
+ const control = jest.fn();
182
+ create(() => {
183
+ // @ts-ignore
184
+ expect(() => test(undefined, () => undefined)).toThrow(
185
+ 'Incompatible params passed to test function. Test fieldName must be a string'
186
+ );
187
+ // @ts-expect-error
188
+ expect(() => test(null, 'error message', () => undefined)).toThrow(
189
+ 'Incompatible params passed to test function. Test fieldName must be a string'
190
+ );
191
+ expect(() =>
192
+ // @ts-expect-error
193
+ test(null, 'error message', () => undefined, 'key')
194
+ ).toThrow(
195
+ 'Incompatible params passed to test function. Test fieldName must be a string'
196
+ );
197
+ control();
198
+ })();
199
+ expect(control).toHaveBeenCalled();
200
+ });
201
+
202
+ it('throws when callback is not a function', () => {
203
+ const control = jest.fn();
204
+ create(() => {
205
+ // @ts-expect-error
206
+ expect(() => test('x')).toThrow(
207
+ 'Incompatible params passed to test function. Test callback must be a function'
208
+ );
209
+ // @ts-expect-error
210
+ expect(() => test('x', 'msg', undefined)).toThrow(
211
+ 'Incompatible params passed to test function. Test callback must be a function'
212
+ );
213
+ // @ts-expect-error
214
+ expect(() => test('x', 'msg', undefined, 'key')).toThrow(
215
+ 'Incompatible params passed to test function. Test callback must be a function'
216
+ );
217
+ control();
218
+ })();
219
+ expect(control).toHaveBeenCalled();
220
+ });
221
+ });
222
+ });
223
+
224
+ function failWithString(msg?: string) {
225
+ throw msg ?? 'I fail with a message';
226
+ }
@@ -0,0 +1,52 @@
1
+ import { IsolateTest } from 'IsolateTest';
2
+ import { IsolateTypes } from 'IsolateTypes';
3
+ import { Severity } from 'Severity';
4
+ import { nonMatchingSeverityProfile } from 'nonMatchingSeverityProfile';
5
+
6
+ describe('nonMatchingSeverityProfile', () => {
7
+ let testObject: IsolateTest;
8
+
9
+ beforeEach(() => {
10
+ testObject = new IsolateTest(IsolateTypes.TEST, {
11
+ fieldName: 'field',
12
+ testFn: jest.fn(),
13
+ });
14
+ });
15
+ describe('When matching', () => {
16
+ describe('When both are warning', () => {
17
+ it('should return false', () => {
18
+ testObject.warn();
19
+ expect(nonMatchingSeverityProfile(Severity.WARNINGS, testObject)).toBe(
20
+ false
21
+ );
22
+ });
23
+ });
24
+
25
+ describe('When both are not warning', () => {
26
+ it('should return false', () => {
27
+ expect(nonMatchingSeverityProfile(Severity.ERRORS, testObject)).toBe(
28
+ false
29
+ );
30
+ });
31
+ });
32
+ });
33
+
34
+ describe('When non matching', () => {
35
+ describe('When test is warning', () => {
36
+ it('should return true', () => {
37
+ testObject.warn();
38
+ expect(nonMatchingSeverityProfile(Severity.ERRORS, testObject)).toBe(
39
+ true
40
+ );
41
+ });
42
+ });
43
+
44
+ describe('When severity is warning', () => {
45
+ it('should return true', () => {
46
+ expect(nonMatchingSeverityProfile(Severity.WARNINGS, testObject)).toBe(
47
+ true
48
+ );
49
+ });
50
+ });
51
+ });
52
+ });
@@ -0,0 +1,9 @@
1
+ import { invariant } from 'vest-utils';
2
+
3
+ import { ErrorStrings } from 'ErrorStrings';
4
+ import { IsolateTest } from 'IsolateTest';
5
+
6
+ export function asVestTest(value: any): IsolateTest {
7
+ invariant(value instanceof IsolateTest, ErrorStrings.EXPECTED_VEST_TEST);
8
+ return value;
9
+ }
@@ -0,0 +1,16 @@
1
+ import { IsolateTest } from 'IsolateTest';
2
+ import { TFieldName } from 'SuiteResultTypes';
3
+
4
+ export function nonMatchingFieldName(
5
+ testObject: IsolateTest,
6
+ fieldName?: TFieldName | void
7
+ ): boolean {
8
+ return !!fieldName && !matchingFieldName(testObject, fieldName);
9
+ }
10
+
11
+ export default function matchingFieldName(
12
+ testObject: IsolateTest,
13
+ fieldName?: TFieldName | void
14
+ ): boolean {
15
+ return !!(fieldName && testObject.fieldName === fieldName);
16
+ }
@@ -0,0 +1,12 @@
1
+ import { bindNot } from 'vest-utils';
2
+
3
+ import { IsolateTest } from 'IsolateTest';
4
+
5
+ export const nonMatchingGroupName = bindNot(matchingGroupName);
6
+
7
+ export function matchingGroupName(
8
+ testObject: IsolateTest,
9
+ groupName: string | void
10
+ ): boolean {
11
+ return testObject.groupName === groupName;
12
+ }
@@ -0,0 +1,14 @@
1
+ import { either } from 'vest-utils';
2
+
3
+ import { IsolateTest } from 'IsolateTest';
4
+ import { Severity } from 'Severity';
5
+
6
+ /**
7
+ * Checks that a given test object matches the currently specified severity level
8
+ */
9
+ export function nonMatchingSeverityProfile(
10
+ severity: Severity,
11
+ testObject: IsolateTest
12
+ ): boolean {
13
+ return either(severity === Severity.WARNINGS, testObject.warns());
14
+ }
@@ -0,0 +1,9 @@
1
+ import { isStringValue, isUndefined } from 'vest-utils';
2
+
3
+ export function shouldUseErrorAsMessage(
4
+ message: string | void,
5
+ error: unknown
6
+ ): error is string {
7
+ // kind of cheating with this safe guard, but it does the job
8
+ return isUndefined(message) && isStringValue(error);
9
+ }
@@ -0,0 +1,81 @@
1
+ import { isNull } from 'vest-utils';
2
+
3
+ import { IsolateTest } from 'IsolateTest';
4
+ import { useCurrentCursor, useSuiteId } from 'PersistedContext';
5
+ import { useTestMemoCache } from 'SuiteContext';
6
+ import { TFieldName } from 'SuiteResultTypes';
7
+ import { TestFn } from 'TestTypes';
8
+ import { VTest } from 'test';
9
+
10
+ export function wrapTestMemo(test: VTest): TestMemo<TFieldName> {
11
+ /**
12
+ * Caches a test result based on the test's dependencies.
13
+ */
14
+ function memo<F extends TFieldName>(
15
+ fieldName: F,
16
+ ...args: ParametersWithoutMessage
17
+ ): IsolateTest;
18
+ function memo<F extends TFieldName>(
19
+ fieldName: F,
20
+ ...args: ParametersWithMessage
21
+ ): IsolateTest;
22
+ function memo<F extends TFieldName>(
23
+ fieldName: F,
24
+ ...args: ParamsOverload
25
+ ): IsolateTest {
26
+ const [deps, testFn, msg] = args.reverse() as [any[], TestFn, string];
27
+
28
+ // Implicit dependency for better specificity
29
+ const dependencies = [useSuiteId(), fieldName, useCurrentCursor()].concat(
30
+ deps
31
+ );
32
+
33
+ return getTestFromCache(dependencies, cacheAction);
34
+
35
+ function cacheAction() {
36
+ return test(fieldName, msg, testFn);
37
+ }
38
+ }
39
+
40
+ return memo;
41
+ }
42
+
43
+ function getTestFromCache(
44
+ dependencies: any[],
45
+ cacheAction: () => IsolateTest
46
+ ): IsolateTest {
47
+ const cache = useTestMemoCache();
48
+
49
+ const cached = cache.get(dependencies);
50
+
51
+ if (isNull(cached)) {
52
+ // cache miss
53
+ return cache(dependencies, cacheAction);
54
+ }
55
+
56
+ const [, cachedValue] = cached;
57
+
58
+ if (cachedValue.isCanceled()) {
59
+ // cache hit, but test is canceled
60
+ cache.invalidate(dependencies);
61
+ return cache(dependencies, cacheAction);
62
+ }
63
+
64
+ IsolateTest.setNode(cachedValue);
65
+
66
+ return cachedValue;
67
+ }
68
+
69
+ type TestMemo<F extends TFieldName> = {
70
+ (fieldName: F, ...args: ParametersWithoutMessage): IsolateTest;
71
+ (fieldName: F, ...args: ParametersWithMessage): IsolateTest;
72
+ };
73
+
74
+ type ParametersWithoutMessage = [test: TestFn, dependencies: unknown[]];
75
+ type ParametersWithMessage = [
76
+ message: string,
77
+ test: TestFn,
78
+ dependencies: unknown[]
79
+ ];
80
+
81
+ type ParamsOverload = ParametersWithoutMessage | ParametersWithMessage;
@@ -0,0 +1,64 @@
1
+ import { assign, invariant, isFunction, isStringValue } from 'vest-utils';
2
+
3
+ import { IsolateTest } from 'IsolateTest';
4
+ import { useEmit } from 'PersistedContext';
5
+ import { useGroupName } from 'SuiteContext';
6
+ import { TFieldName } from 'SuiteResultTypes';
7
+ import { TestFn } from 'TestTypes';
8
+ import { Events } from 'VestBus';
9
+ import { IsolateKey } from 'isolate';
10
+ import { wrapTestMemo } from 'test.memo';
11
+ import { testObjectIsolate } from 'testObjectIsolate';
12
+
13
+ function vestTest<F extends TFieldName>(
14
+ fieldName: F,
15
+ message: string,
16
+ cb: TestFn
17
+ ): IsolateTest;
18
+ function vestTest<F extends TFieldName>(fieldName: F, cb: TestFn): IsolateTest;
19
+ function vestTest<F extends TFieldName>(
20
+ fieldName: F,
21
+ message: string,
22
+ cb: TestFn,
23
+ key: IsolateKey
24
+ ): IsolateTest;
25
+ function vestTest<F extends TFieldName>(
26
+ fieldName: F,
27
+ cb: TestFn,
28
+ key: IsolateKey
29
+ ): IsolateTest;
30
+ function vestTest<F extends TFieldName>(
31
+ fieldName: F,
32
+ ...args:
33
+ | [message: string, cb: TestFn]
34
+ | [cb: TestFn]
35
+ | [message: string, cb: TestFn, key: IsolateKey]
36
+ | [cb: TestFn, key: IsolateKey]
37
+ ): IsolateTest {
38
+ const [message, testFn, key] = (
39
+ isFunction(args[1]) ? args : [undefined, ...args]
40
+ ) as [string | undefined, TestFn, IsolateKey];
41
+
42
+ invariant(isStringValue(fieldName), invalidParamError('fieldName', 'string'));
43
+ invariant(isFunction(testFn), invalidParamError('callback', 'function'));
44
+
45
+ const groupName = useGroupName();
46
+ const emit = useEmit();
47
+
48
+ const testObjectInput = { fieldName, groupName, key, message, testFn };
49
+
50
+ // This invalidates the suite cache.
51
+ emit(Events.TEST_RUN_STARTED);
52
+
53
+ return testObjectIsolate(testObjectInput);
54
+ }
55
+
56
+ export const test = assign(vestTest, {
57
+ memo: wrapTestMemo(vestTest),
58
+ });
59
+
60
+ export type VTest = typeof vestTest;
61
+
62
+ function invalidParamError(name: string, expected: string): string {
63
+ return `Incompatible params passed to test function. Test ${name} must be a ${expected}`;
64
+ }
@@ -0,0 +1,86 @@
1
+ import { isPromise, isStringValue } from 'vest-utils';
2
+
3
+ import { IsolateTest } from 'IsolateTest';
4
+ import { persist, useVestBus } from 'PersistedContext';
5
+ import { SuiteContext } from 'SuiteContext';
6
+ import { TestResult } from 'TestTypes';
7
+ import { Events } from 'VestBus';
8
+ import { verifyTestRun } from 'verifyTestRun';
9
+
10
+ export function attemptRunTestObjectByTier(testObject: IsolateTest) {
11
+ verifyTestRun(testObject);
12
+
13
+ if (testObject.isNonActionable()) {
14
+ // TODO: Need to test that this works as expected
15
+ return;
16
+ }
17
+
18
+ if (testObject.isUntested()) {
19
+ runTest(testObject);
20
+ } else if (testObject.isAsyncTest()) {
21
+ testObject.setPending();
22
+ runAsyncTest(testObject);
23
+ }
24
+ }
25
+
26
+ function runSyncTest(testObject: IsolateTest): TestResult {
27
+ return SuiteContext.run({ currentTest: testObject }, () => testObject.run());
28
+ }
29
+
30
+ /**
31
+ * runs test, if async - adds to pending array
32
+ */
33
+ function runTest(testObject: IsolateTest): void {
34
+ const VestBus = useVestBus();
35
+
36
+ // Run test callback.
37
+ // If a promise is returned, set as async and
38
+ // Move to pending list.
39
+ const result = runSyncTest(testObject);
40
+ try {
41
+ // try catch for safe property access
42
+ // in case object is an enforce chain
43
+ if (isPromise(result)) {
44
+ testObject.asyncTest = result;
45
+ testObject.setPending();
46
+ runAsyncTest(testObject);
47
+ } else {
48
+ VestBus.emit(Events.TEST_COMPLETED, testObject);
49
+ }
50
+ } catch (e) {
51
+ throw new Error(
52
+ `Unexpected error encountered during test registration.
53
+ Test Object: ${JSON.stringify(testObject)}.
54
+ Error: ${e}.`
55
+ );
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Runs async test.
61
+ */
62
+ function runAsyncTest(testObject: IsolateTest): void {
63
+ const { asyncTest, message } = testObject;
64
+
65
+ if (!isPromise(asyncTest)) return;
66
+
67
+ const VestBus = useVestBus();
68
+
69
+ const done = persist(() => {
70
+ VestBus.emit(Events.TEST_COMPLETED, testObject);
71
+ });
72
+ const fail = persist((rejectionMessage?: string) => {
73
+ if (testObject.isCanceled()) {
74
+ return;
75
+ }
76
+
77
+ testObject.message = isStringValue(rejectionMessage)
78
+ ? rejectionMessage
79
+ : message;
80
+ testObject.fail();
81
+
82
+ done();
83
+ });
84
+
85
+ asyncTest.then(done, fail);
86
+ }
@@ -0,0 +1,32 @@
1
+ import { isOptionalFiedApplied } from 'optional';
2
+
3
+ import { IsolateTest } from 'IsolateTest';
4
+ import { isExcluded } from 'exclusive';
5
+ import { shouldSkipBasedOnMode } from 'mode';
6
+ import { withinActiveOmitWhen } from 'omitWhen';
7
+ import { isExcludedIndividually } from 'skipWhen';
8
+
9
+ export function verifyTestRun(testObject: IsolateTest): IsolateTest {
10
+ if (shouldSkipBasedOnMode(testObject)) {
11
+ testObject.skip();
12
+
13
+ return testObject;
14
+ }
15
+
16
+ if (withinActiveOmitWhen() || isOptionalFiedApplied(testObject.fieldName)) {
17
+ testObject.omit();
18
+
19
+ return testObject;
20
+ }
21
+
22
+ if (isExcluded(testObject)) {
23
+ // We're forcing skipping the pending test
24
+ // if we're directly within a skipWhen block
25
+ // This mostly means that we're probably giving
26
+ // up on this async test intentionally.
27
+ testObject.skip(isExcludedIndividually());
28
+ return testObject;
29
+ }
30
+
31
+ return testObject;
32
+ }
@@ -0,0 +1,11 @@
1
+ import { IsolateTest, IsolateTestInput } from 'IsolateTest';
2
+ import { attemptRunTestObjectByTier } from 'runTest';
3
+
4
+ export function testObjectIsolate(
5
+ testObjectInput: IsolateTestInput
6
+ ): IsolateTest {
7
+ return IsolateTest.factory(
8
+ (testObject: IsolateTest) => attemptRunTestObjectByTier(testObject),
9
+ testObjectInput
10
+ );
11
+ }
@@ -0,0 +1,4 @@
1
+ export enum ErrorStrings {
2
+ HOOK_CALLED_OUTSIDE = 'hook called outside of a running suite.',
3
+ EXPECTED_VEST_TEST = 'Expected value to be an instance of IsolateTest',
4
+ }