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,213 @@
1
+ import { CB, invariant, isPromise, seq } from 'vest-utils';
2
+
3
+ import { IsolateTestReconciler } from 'IsolateTestReconciler';
4
+ import {
5
+ TestAction,
6
+ TestStatus,
7
+ createTestStateMachine,
8
+ } from 'IsolateTestStateMachine';
9
+ import { IsolateTypes } from 'IsolateTypes';
10
+ import { TFieldName } from 'SuiteResultTypes';
11
+ import { TestFn, AsyncTest, TestResult } from 'TestTypes';
12
+ import { Isolate, IsolateKey } from 'isolate';
13
+ import { shouldUseErrorAsMessage } from 'shouldUseErrorMessage';
14
+
15
+ export type IsolateTestInput = {
16
+ message?: string;
17
+ groupName?: string;
18
+ fieldName: TFieldName;
19
+ testFn: TestFn;
20
+ key?: IsolateKey;
21
+ };
22
+
23
+ export class IsolateTest extends Isolate<IsolateTypes.TEST> {
24
+ children = null;
25
+ fieldName: TFieldName;
26
+ testFn: TestFn;
27
+ groupName?: string;
28
+ message?: string;
29
+ asyncTest?: AsyncTest;
30
+ id = seq();
31
+ severity = TestSeverity.Error;
32
+ private stateMachine = createTestStateMachine();
33
+
34
+ static reconciler = IsolateTestReconciler;
35
+
36
+ constructor(
37
+ type: IsolateTypes.TEST,
38
+ { fieldName, testFn, message, groupName, key = null }: IsolateTestInput
39
+ ) {
40
+ super(type);
41
+
42
+ this.fieldName = fieldName;
43
+ this.testFn = testFn;
44
+
45
+ if (groupName) {
46
+ this.groupName = groupName;
47
+ }
48
+
49
+ if (message) {
50
+ this.message = message;
51
+ }
52
+
53
+ this.setKey(key);
54
+ }
55
+
56
+ static factory<Callback extends CB = CB>(
57
+ callback: Callback,
58
+ data: IsolateTestInput
59
+ ): IsolateTest {
60
+ return super.create(IsolateTypes.TEST, callback, data) as IsolateTest;
61
+ }
62
+
63
+ get status(): TestStatus {
64
+ return this.stateMachine.getState();
65
+ }
66
+
67
+ setStatus(status: TestStatus, payload?: any): void {
68
+ this.stateMachine.transition(status, payload);
69
+ }
70
+
71
+ run(): TestResult {
72
+ let result: TestResult;
73
+ try {
74
+ result = this.testFn();
75
+ } catch (error) {
76
+ if (shouldUseErrorAsMessage(this.message, error)) {
77
+ this.message = error;
78
+ }
79
+ result = false;
80
+ }
81
+
82
+ if (result === false) {
83
+ this.fail();
84
+ }
85
+
86
+ return result;
87
+ }
88
+
89
+ // Selectors
90
+
91
+ warns(): boolean {
92
+ return this.severity === TestSeverity.Warning;
93
+ }
94
+
95
+ isPending(): boolean {
96
+ return this.statusEquals(TestStatus.PENDING);
97
+ }
98
+
99
+ isOmitted(): boolean {
100
+ return this.statusEquals(TestStatus.OMITTED);
101
+ }
102
+
103
+ isUntested(): boolean {
104
+ return this.statusEquals(TestStatus.UNTESTED);
105
+ }
106
+
107
+ isFailing(): boolean {
108
+ return this.statusEquals(TestStatus.FAILED);
109
+ }
110
+
111
+ isCanceled(): boolean {
112
+ return this.statusEquals(TestStatus.CANCELED);
113
+ }
114
+
115
+ isSkipped(): boolean {
116
+ return this.statusEquals(TestStatus.SKIPPED);
117
+ }
118
+
119
+ isPassing(): boolean {
120
+ return this.statusEquals(TestStatus.PASSING);
121
+ }
122
+
123
+ isWarning(): boolean {
124
+ return this.statusEquals(TestStatus.WARNING);
125
+ }
126
+
127
+ hasFailures(): boolean {
128
+ return this.isFailing() || this.isWarning();
129
+ }
130
+
131
+ isNonActionable(): boolean {
132
+ return this.isSkipped() || this.isOmitted() || this.isCanceled();
133
+ }
134
+
135
+ isTested(): boolean {
136
+ return this.hasFailures() || this.isPassing();
137
+ }
138
+
139
+ awaitsResolution(): boolean {
140
+ // Is the test in a state where it can still be run, or complete running
141
+ // and its final status is indeterminate?
142
+ return this.isSkipped() || this.isUntested() || this.isPending();
143
+ }
144
+
145
+ statusEquals(status: TestStatus): boolean {
146
+ return this.status === status;
147
+ }
148
+
149
+ // State modifiers
150
+
151
+ setPending() {
152
+ this.setStatus(TestStatus.PENDING);
153
+ }
154
+
155
+ fail(): void {
156
+ this.setStatus(this.warns() ? TestStatus.WARNING : TestStatus.FAILED);
157
+ }
158
+
159
+ pass(): void {
160
+ this.setStatus(TestStatus.PASSING);
161
+ }
162
+
163
+ warn(): void {
164
+ this.severity = TestSeverity.Warning;
165
+ }
166
+
167
+ skip(force?: boolean): void {
168
+ // Without this force flag, the test will be marked as skipped even if it is pending.
169
+ // This means that it will not be counted in "allIncomplete" and its done callbacks
170
+ // will not be called, or will be called prematurely.
171
+ // What this mostly say is that when we have a pending test for one field, and we then
172
+ // start typing in a different field - the pending test will be canceled, which
173
+ // is usually an unwanted behavior.
174
+ // The only scenario in which we DO want to cancel the async test regardless
175
+ // is when we specifically skip a test with `skipWhen`, which is handled by the
176
+ // "force" boolean flag.
177
+ // I am not a fan of this flag, but it gets the job done.
178
+ this.setStatus(TestStatus.SKIPPED, force);
179
+ }
180
+
181
+ cancel(): void {
182
+ this.setStatus(TestStatus.CANCELED);
183
+ }
184
+
185
+ reset(): void {
186
+ this.stateMachine.transition(TestAction.RESET);
187
+ }
188
+
189
+ omit(): void {
190
+ this.setStatus(TestStatus.OMITTED);
191
+ }
192
+
193
+ valueOf(): boolean {
194
+ return !this.isFailing();
195
+ }
196
+
197
+ isAsyncTest(): boolean {
198
+ return isPromise(this.asyncTest);
199
+ }
200
+
201
+ static is(value: any): value is IsolateTest {
202
+ return value instanceof IsolateTest;
203
+ }
204
+
205
+ static isX(value: any): asserts value is IsolateTest {
206
+ invariant(this.is(value));
207
+ }
208
+ }
209
+
210
+ enum TestSeverity {
211
+ Error = 'error',
212
+ Warning = 'warning',
213
+ }
@@ -0,0 +1,156 @@
1
+ import { isOptionalFiedApplied } from 'optional';
2
+ import { deferThrow, isNullish, invariant } from 'vest-utils';
3
+
4
+ import { IsolateTest } from 'IsolateTest';
5
+ import { IsolateTypes } from 'IsolateTypes';
6
+ import { Reconciler } from 'Reconciler';
7
+ import cancelOverriddenPendingTest from 'cancelOverriddenPendingTest';
8
+ import { isExcluded } from 'exclusive';
9
+ import { isIsolateType } from 'isIsolate';
10
+ import { isSameProfileTest } from 'isSameProfileTest';
11
+ import { Isolate } from 'isolate';
12
+ import { shouldSkipBasedOnMode } from 'mode';
13
+ import { withinActiveOmitWhen } from 'omitWhen';
14
+ import { isExcludedIndividually } from 'skipWhen';
15
+
16
+ export class IsolateTestReconciler extends Reconciler {
17
+ static reconciler(
18
+ currentNode: Isolate,
19
+ historyNode: Isolate | null
20
+ ): Isolate {
21
+ // Start by verifying params
22
+ if (!IsolateTest.is(currentNode)) {
23
+ return currentNode;
24
+ }
25
+
26
+ if (isNullish(historyNode)) {
27
+ return this.handleNoHistoryNode(currentNode);
28
+ }
29
+
30
+ if (!IsolateTest.is(historyNode)) {
31
+ return currentNode;
32
+ }
33
+
34
+ const reconcilerOutput = this.pickNode(historyNode, currentNode);
35
+
36
+ cancelOverriddenPendingTestOnTestReRun(
37
+ reconcilerOutput,
38
+ currentNode,
39
+ historyNode
40
+ );
41
+
42
+ return reconcilerOutput;
43
+ }
44
+
45
+ static nodeReorderDetected(
46
+ newNode: IsolateTest,
47
+ prevNode?: Isolate
48
+ ): boolean {
49
+ return !!IsolateTest.is(prevNode) && !isSameProfileTest(prevNode, newNode);
50
+ }
51
+
52
+ static handleCollision(
53
+ newNode: IsolateTest,
54
+ prevNode?: Isolate
55
+ ): IsolateTest {
56
+ if (newNode.usesKey()) {
57
+ return this.handleIsolateNodeWithKey(newNode) as IsolateTest;
58
+ }
59
+
60
+ if (this.nodeReorderDetected(newNode, prevNode)) {
61
+ return this.onNodeReorder(newNode, prevNode);
62
+ }
63
+
64
+ return (prevNode ? prevNode : newNode) as IsolateTest;
65
+ }
66
+
67
+ static onNodeReorder(newNode: IsolateTest, prevNode?: Isolate): IsolateTest {
68
+ throwTestOrderError(newNode, prevNode);
69
+ this.removeAllNextNodesInIsolate();
70
+ return newNode;
71
+ }
72
+
73
+ static pickNode(historyNode: IsolateTest, currentNode: IsolateTest): Isolate {
74
+ const collisionResult = this.handleCollision(currentNode, historyNode);
75
+
76
+ if (shouldSkipBasedOnMode(currentNode)) {
77
+ return skipTestAndReturn(currentNode);
78
+ }
79
+
80
+ if (shouldOmit(currentNode)) {
81
+ return omitTestAndReturn(currentNode);
82
+ }
83
+
84
+ if (isExcluded(currentNode)) {
85
+ return forceSkipIfInSkipWhen(collisionResult);
86
+ }
87
+
88
+ return currentNode;
89
+ }
90
+
91
+ static handleNoHistoryNode(testNode: IsolateTest): IsolateTest {
92
+ if (testNode.usesKey()) {
93
+ return this.handleIsolateNodeWithKey(testNode) as IsolateTest;
94
+ }
95
+
96
+ return testNode;
97
+ }
98
+ }
99
+
100
+ function cancelOverriddenPendingTestOnTestReRun(
101
+ nextNode: Isolate,
102
+ currentNode: Isolate,
103
+ prevTestObject: IsolateTest
104
+ ) {
105
+ if (nextNode === currentNode && IsolateTest.is(currentNode)) {
106
+ cancelOverriddenPendingTest(prevTestObject, currentNode);
107
+ }
108
+ }
109
+
110
+ function shouldOmit(testObject: IsolateTest): boolean {
111
+ return withinActiveOmitWhen() || isOptionalFiedApplied(testObject.fieldName);
112
+ }
113
+
114
+ function skipTestAndReturn(testNode: IsolateTest): IsolateTest {
115
+ testNode.skip();
116
+ return testNode;
117
+ }
118
+
119
+ function omitTestAndReturn(testNode: IsolateTest): IsolateTest {
120
+ testNode.omit();
121
+ return testNode;
122
+ }
123
+
124
+ function forceSkipIfInSkipWhen(testNode: IsolateTest): IsolateTest {
125
+ // We're forcing skipping the pending test
126
+ // if we're directly within a skipWhen block
127
+ // This mostly means that we're probably giving
128
+ // up on this async test intentionally.
129
+ testNode.skip(isExcludedIndividually());
130
+ return testNode;
131
+ }
132
+
133
+ function throwTestOrderError(
134
+ newNode: IsolateTest,
135
+ prevNode: Isolate | undefined
136
+ ): void {
137
+ if (shouldAllowReorder(newNode)) {
138
+ return;
139
+ }
140
+
141
+ deferThrow(`Vest Critical Error: Tests called in different order than previous run.
142
+ expected: ${newNode.fieldName}
143
+ received: ${IsolateTest.is(prevNode) ? prevNode.fieldName : undefined}
144
+ This can happen on one of two reasons:
145
+ 1. You're using if/else statements to conditionally select tests. Instead, use "skipWhen".
146
+ 2. You are iterating over a list of tests, and their order changed. Use "each" and a custom key prop so that Vest retains their state.`);
147
+ }
148
+
149
+ /**
150
+ * @returns {boolean} Whether or not the current isolate allows tests to be reordered
151
+ */
152
+ function shouldAllowReorder(newNode: IsolateTest): boolean {
153
+ const parent = newNode.parent;
154
+ invariant(parent);
155
+ return isIsolateType(parent, IsolateTypes.EACH);
156
+ }
@@ -0,0 +1,69 @@
1
+ import { StateMachine, TStateMachine } from 'SimpleStateMachine';
2
+
3
+ export enum TestStatus {
4
+ UNTESTED = 'UNTESTED',
5
+ SKIPPED = 'SKIPPED',
6
+ FAILED = 'FAILED',
7
+ WARNING = 'WARNING',
8
+ PASSING = 'PASSING',
9
+ PENDING = 'PENDING',
10
+ CANCELED = 'CANCELED',
11
+ OMITTED = 'OMITTED',
12
+ }
13
+
14
+ export enum TestAction {
15
+ RESET = 'RESET',
16
+ }
17
+
18
+ type TAction = TestAction | TestStatus;
19
+
20
+ export function createTestStateMachine() {
21
+ return StateMachine<TestStatus, TAction>(machine);
22
+ }
23
+
24
+ /* eslint-disable sort-keys */
25
+ const machine: TStateMachine<TestStatus, TAction> = {
26
+ initial: TestStatus.UNTESTED,
27
+ states: {
28
+ [TestStatus.UNTESTED]: {
29
+ [TestStatus.CANCELED]: TestStatus.CANCELED,
30
+ [TestStatus.FAILED]: TestStatus.FAILED,
31
+ [TestStatus.OMITTED]: TestStatus.OMITTED,
32
+ [TestStatus.PASSING]: TestStatus.PASSING,
33
+ [TestStatus.PENDING]: TestStatus.PENDING,
34
+ [TestStatus.SKIPPED]: TestStatus.SKIPPED,
35
+ [TestStatus.WARNING]: TestStatus.WARNING,
36
+ },
37
+ [TestStatus.SKIPPED]: {
38
+ [TestAction.RESET]: TestStatus.UNTESTED,
39
+ },
40
+ [TestStatus.FAILED]: {
41
+ [TestAction.RESET]: TestStatus.UNTESTED,
42
+ },
43
+ [TestStatus.WARNING]: {
44
+ [TestAction.RESET]: TestStatus.UNTESTED,
45
+ },
46
+ [TestStatus.PASSING]: {
47
+ [TestAction.RESET]: TestStatus.UNTESTED,
48
+ },
49
+ [TestStatus.PENDING]: {
50
+ [TestAction.RESET]: TestStatus.UNTESTED,
51
+ [TestStatus.CANCELED]: TestStatus.CANCELED,
52
+ [TestStatus.FAILED]: TestStatus.FAILED,
53
+ [TestStatus.OMITTED]: TestStatus.OMITTED,
54
+ [TestStatus.PASSING]: TestStatus.PASSING,
55
+ [TestStatus.SKIPPED]: [
56
+ TestStatus.SKIPPED,
57
+ (force?: boolean) => force === true,
58
+ ],
59
+ [TestStatus.WARNING]: TestStatus.WARNING,
60
+ },
61
+ [TestStatus.CANCELED]: {
62
+ [TestAction.RESET]: TestStatus.UNTESTED,
63
+ },
64
+ [TestStatus.OMITTED]: {
65
+ [TestAction.RESET]: TestStatus.UNTESTED,
66
+ },
67
+ },
68
+ };
69
+ /* eslint-enable sort-keys */
@@ -0,0 +1,43 @@
1
+ export type TStateMachine<S extends string, A extends string> = {
2
+ initial: S;
3
+ states: {
4
+ [key in S]: {
5
+ [key in A]?: S | [S, (payload?: any) => boolean];
6
+ };
7
+ };
8
+ };
9
+
10
+ export function StateMachine<S extends string, A extends string>(
11
+ machine: TStateMachine<S, A>
12
+ ): { getState: () => S; transition: (action: A, payload?: any) => void } {
13
+ let state = machine.initial;
14
+
15
+ const api = { getState, transition };
16
+
17
+ return api;
18
+
19
+ function getState(): S {
20
+ return state;
21
+ }
22
+
23
+ function transition(action: A, payload?: any): void {
24
+ const transitionTo = machine.states[state][action];
25
+
26
+ let target = transitionTo;
27
+
28
+ if (Array.isArray(target)) {
29
+ const [, conditional] = target;
30
+ if (!conditional(payload)) {
31
+ return;
32
+ }
33
+
34
+ target = target[0];
35
+ }
36
+
37
+ if (!target || target === state) {
38
+ return;
39
+ }
40
+
41
+ state = target as S;
42
+ }
43
+ }
@@ -0,0 +1,77 @@
1
+ import { IsolateTest } from 'IsolateTest';
2
+ import { TFieldName } from 'SuiteResultTypes';
3
+ import { SuiteWalker } from 'SuiteWalker';
4
+ import matchingFieldName from 'matchingFieldName';
5
+
6
+ export class TestWalker {
7
+ static hasNoTests(): boolean {
8
+ return !SuiteWalker.has(IsolateTest.is);
9
+ }
10
+
11
+ static someIncompleteTests(
12
+ predicate: (test: IsolateTest) => boolean
13
+ ): boolean {
14
+ return SuiteWalker.some(isolate => {
15
+ IsolateTest.isX(isolate);
16
+
17
+ return isolate.isPending() && predicate(isolate);
18
+ }, IsolateTest.is);
19
+ }
20
+
21
+ static someTests(predicate: (test: IsolateTest) => boolean): boolean {
22
+ return SuiteWalker.some(isolate => {
23
+ IsolateTest.isX(isolate);
24
+
25
+ return predicate(isolate);
26
+ }, IsolateTest.is);
27
+ }
28
+
29
+ static everyTest(predicate: (test: IsolateTest) => boolean): boolean {
30
+ return SuiteWalker.every(isolate => {
31
+ IsolateTest.isX(isolate);
32
+
33
+ return predicate(isolate);
34
+ }, IsolateTest.is);
35
+ }
36
+
37
+ static walkTests(
38
+ callback: (test: IsolateTest, breakout: () => void) => void
39
+ ): void {
40
+ SuiteWalker.walk((isolate, breakout) => {
41
+ IsolateTest.isX(isolate);
42
+
43
+ callback(isolate, breakout);
44
+ }, IsolateTest.is);
45
+ }
46
+
47
+ static hasRemainingTests(fieldName?: TFieldName): boolean {
48
+ return TestWalker.someIncompleteTests(testObject => {
49
+ if (fieldName) {
50
+ return matchingFieldName(testObject, fieldName);
51
+ }
52
+ return true;
53
+ });
54
+ }
55
+
56
+ static pluckTests(predicate: (test: IsolateTest) => boolean): void {
57
+ SuiteWalker.pluck(isolate => {
58
+ IsolateTest.isX(isolate);
59
+
60
+ return predicate(isolate);
61
+ }, IsolateTest.is);
62
+ }
63
+
64
+ static resetField(fieldName: TFieldName): void {
65
+ TestWalker.walkTests(testObject => {
66
+ if (matchingFieldName(testObject, fieldName)) {
67
+ testObject.reset();
68
+ }
69
+ });
70
+ }
71
+
72
+ static removeTestByFieldName(fieldName: TFieldName): void {
73
+ TestWalker.pluckTests(testObject => {
74
+ return matchingFieldName(testObject, fieldName);
75
+ });
76
+ }
77
+ }
@@ -0,0 +1,10 @@
1
+ export enum IsolateTypes {
2
+ DEFAULT = 'DEFAULT',
3
+ TEST = 'TEST',
4
+ SUITE = 'SUITE',
5
+ EACH = 'EACH',
6
+ GROUP = 'GROUP',
7
+ SKIP_WHEN = 'SKIP_WHEN',
8
+ OMIT_WHEN = 'OMIT_WHEN',
9
+ TEST_MEMO = 'TEST_MEMO',
10
+ }
@@ -0,0 +1,6 @@
1
+ import { IsolateTypes } from 'IsolateTypes';
2
+ import { Isolate } from 'isolate';
3
+
4
+ export function isIsolateType(isolate: Isolate, type: IsolateTypes): boolean {
5
+ return isolate.type === type;
6
+ }
@@ -0,0 +1,110 @@
1
+ import { CB, invariant, isNotNullish, isNullish } from 'vest-utils';
2
+
3
+ import { IsolateTypes } from 'IsolateTypes';
4
+ import {
5
+ useSetNextIsolateChild,
6
+ useSetHistory,
7
+ useIsolate,
8
+ } from 'PersistedContext';
9
+ import { Reconciler } from 'Reconciler';
10
+
11
+ export type IsolateKey = null | string;
12
+
13
+ export class Isolate<T extends IsolateTypes = IsolateTypes, _D = any> {
14
+ children: Isolate[] | null = [];
15
+ keys: Record<string, Isolate> = {};
16
+ parent: Isolate | null = null;
17
+ output?: any;
18
+ key: IsolateKey = null;
19
+ static reconciler = Reconciler;
20
+
21
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
22
+ constructor(public type: T, _data?: _D) {}
23
+
24
+ setParent(parent: Isolate | null): this {
25
+ this.parent = parent;
26
+ return this;
27
+ }
28
+
29
+ saveOutput(output: any): this {
30
+ this.output = output;
31
+ return this;
32
+ }
33
+
34
+ setKey(key: string | null): this {
35
+ this.key = key;
36
+ return this;
37
+ }
38
+
39
+ usesKey(): boolean {
40
+ return isNotNullish(this.key);
41
+ }
42
+
43
+ addChild(child: Isolate): void {
44
+ invariant(this.children);
45
+
46
+ this.children.push(child);
47
+ }
48
+
49
+ removeChild(node: Isolate): void {
50
+ this.children = this.children?.filter(child => child !== node) ?? null;
51
+ }
52
+
53
+ slice(at: number): void {
54
+ if (isNullish(this.children)) {
55
+ return;
56
+ }
57
+
58
+ this.children.length = at;
59
+ }
60
+
61
+ at(at: number): Isolate | null {
62
+ return this.children?.[at] ?? null;
63
+ }
64
+
65
+ cursor(): number {
66
+ return this.children?.length ?? 0;
67
+ }
68
+
69
+ static create<Callback extends CB = CB>(
70
+ type: IsolateTypes,
71
+ callback: Callback,
72
+ data?: any
73
+ ): Isolate {
74
+ return this.createImplementation(type, callback, data);
75
+ }
76
+
77
+ private static createImplementation<Callback extends CB = CB>(
78
+ type: IsolateTypes,
79
+ callback: Callback,
80
+ data?: any
81
+ ): Isolate {
82
+ const parent = useIsolate();
83
+
84
+ const newCreatedNode = new this(type, data).setParent(parent);
85
+
86
+ const [nextIsolateChild, output] = this.reconciler.reconcile(
87
+ newCreatedNode,
88
+ callback
89
+ );
90
+
91
+ nextIsolateChild.saveOutput(output);
92
+
93
+ this.setNode(nextIsolateChild);
94
+
95
+ return nextIsolateChild;
96
+ }
97
+
98
+ static setNode(node: Isolate): void {
99
+ const parent = useIsolate();
100
+ if (parent) {
101
+ useSetNextIsolateChild(node);
102
+ } else {
103
+ useSetHistory(node);
104
+ }
105
+ }
106
+
107
+ static is(node: any): boolean {
108
+ return node instanceof Isolate;
109
+ }
110
+ }