@reventlessdev/reventless-gwt 1.0.0-alpha.100
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +916 -0
- package/LICENSE +202 -0
- package/bin/reventless-dev.mjs +21 -0
- package/package.json +54 -0
- package/rescript.json +36 -0
- package/src/.gwtignore +10 -0
- package/src/Automation_GWT.res +207 -0
- package/src/Automation_GWT.res.mjs +160 -0
- package/src/Behavior_GWT.res +569 -0
- package/src/Behavior_GWT.res.mjs +523 -0
- package/src/Bind.res +29 -0
- package/src/Bind.res.mjs +22 -0
- package/src/BuildClassifier.res +147 -0
- package/src/BuildClassifier.res.mjs +136 -0
- package/src/Cancellation.res +37 -0
- package/src/Cancellation.res.mjs +46 -0
- package/src/ChildProcess.res +102 -0
- package/src/ChildProcess.res.mjs +107 -0
- package/src/Cli.res +905 -0
- package/src/Cli.res.mjs +952 -0
- package/src/Collector.res +212 -0
- package/src/Collector.res.mjs +218 -0
- package/src/ComponentMeta.res +77 -0
- package/src/ComponentMeta.res.mjs +91 -0
- package/src/ComponentScan.res +103 -0
- package/src/ComponentScan.res.mjs +105 -0
- package/src/Delegate_GWT.res +303 -0
- package/src/Delegate_GWT.res.mjs +372 -0
- package/src/Diff.res +91 -0
- package/src/Diff.res.mjs +98 -0
- package/src/Discovery.res +105 -0
- package/src/Discovery.res.mjs +99 -0
- package/src/DomainDeadCode.res +98 -0
- package/src/DomainDeadCode.res.mjs +72 -0
- package/src/DomainGraph.res +0 -0
- package/src/DomainGraph.res.mjs +0 -0
- package/src/EventMapping_GWT.res +29 -0
- package/src/EventMapping_GWT.res.mjs +44 -0
- package/src/Filter.res +45 -0
- package/src/Filter.res.mjs +47 -0
- package/src/Flow_GWT.res +664 -0
- package/src/Flow_GWT.res.mjs +604 -0
- package/src/FormatterHuman.res +109 -0
- package/src/FormatterHuman.res.mjs +115 -0
- package/src/FormatterJson.res +264 -0
- package/src/FormatterJson.res.mjs +322 -0
- package/src/FormatterJunit.res +93 -0
- package/src/FormatterJunit.res.mjs +90 -0
- package/src/FormatterTap.res +100 -0
- package/src/FormatterTap.res.mjs +131 -0
- package/src/FormatterVsCode.res +352 -0
- package/src/FormatterVsCode.res.mjs +546 -0
- package/src/Hint.res +119 -0
- package/src/Hint.res.mjs +124 -0
- package/src/InboundTranslation_GWT.res +83 -0
- package/src/InboundTranslation_GWT.res.mjs +94 -0
- package/src/JestBind.res +70 -0
- package/src/JestBind.res.mjs +63 -0
- package/src/Loader.res +24 -0
- package/src/Loader.res.mjs +23 -0
- package/src/LocalHost.res +119 -0
- package/src/LocalHost.res.mjs +99 -0
- package/src/Mapping_GWT.res +474 -0
- package/src/Mapping_GWT.res.mjs +322 -0
- package/src/MismatchRender.res +72 -0
- package/src/MismatchRender.res.mjs +127 -0
- package/src/MultiSourceProjection_GWT.res +316 -0
- package/src/MultiSourceProjection_GWT.res.mjs +354 -0
- package/src/OutboundTranslation_GWT.res +238 -0
- package/src/OutboundTranslation_GWT.res.mjs +186 -0
- package/src/Outcome.res +97 -0
- package/src/Outcome.res.mjs +93 -0
- package/src/PackageScan.res +80 -0
- package/src/PackageScan.res.mjs +81 -0
- package/src/PlatformRunner.res +181 -0
- package/src/PlatformRunner.res.mjs +176 -0
- package/src/PlatformScan.res +142 -0
- package/src/PlatformScan.res.mjs +132 -0
- package/src/ProcessManager.res +135 -0
- package/src/ProcessManager.res.mjs +119 -0
- package/src/Projection_GWT.res +312 -0
- package/src/Projection_GWT.res.mjs +332 -0
- package/src/Query_GWT.res +379 -0
- package/src/Query_GWT.res.mjs +367 -0
- package/src/RenderRescript.res +125 -0
- package/src/RenderRescript.res.mjs +141 -0
- package/src/RunWorker.res +24 -0
- package/src/RunWorker.res.mjs +16 -0
- package/src/RunnerTypes.res +68 -0
- package/src/RunnerTypes.res.mjs +60 -0
- package/src/SideEffect_GWT.res +157 -0
- package/src/SideEffect_GWT.res.mjs +113 -0
- package/src/StateChange/ExternalAddCategorySlice.res +25 -0
- package/src/StateChange/ExternalAddCategorySlice.res.mjs +34 -0
- package/src/StateChange/ExternalAddCategorySlice_Behavior.res +19 -0
- package/src/StateChange/ExternalAddCategorySlice_Behavior.res.mjs +46 -0
- package/src/StateChange/WithFixtures.res +22 -0
- package/src/StateChange/WithFixtures.res.mjs +31 -0
- package/src/StateChange/WithFixtures_Behavior.res +17 -0
- package/src/StateChange/WithFixtures_Behavior.res.mjs +37 -0
- package/src/StateChange/WithManualOpen.res +23 -0
- package/src/StateChange/WithManualOpen.res.mjs +31 -0
- package/src/StateChange/WithManualOpen_Behavior.res +17 -0
- package/src/StateChange/WithManualOpen_Behavior.res.mjs +37 -0
- package/src/StubRuntime.res +54 -0
- package/src/StubRuntime.res.mjs +55 -0
- package/src/TestFixtures.res +16 -0
- package/src/TestFixtures.res.mjs +34 -0
- package/src/Watch.res +108 -0
- package/src/Watch.res.mjs +85 -0
- package/src/WatcherProbe.res +56 -0
- package/src/WatcherProbe.res.mjs +59 -0
- package/src/Worker.res +24 -0
- package/src/Worker.res.mjs +9 -0
- package/tests/AutomationGwtTest.res +71 -0
- package/tests/AutomationGwtTest.res.mjs +189 -0
- package/tests/BuildClassifierTest.res +133 -0
- package/tests/BuildClassifierTest.res.mjs +176 -0
- package/tests/CliRunEntryTest.res +57 -0
- package/tests/CliRunEntryTest.res.mjs +65 -0
- package/tests/CliWatchScopeTest.res +62 -0
- package/tests/CliWatchScopeTest.res.mjs +95 -0
- package/tests/ComponentMetaTest.res +67 -0
- package/tests/ComponentMetaTest.res.mjs +71 -0
- package/tests/DelegateGwtTest.res +487 -0
- package/tests/DelegateGwtTest.res.mjs +891 -0
- package/tests/DiscoveryTest.res +42 -0
- package/tests/DiscoveryTest.res.mjs +43 -0
- package/tests/DomainDeadCodeTest.res +119 -0
- package/tests/DomainDeadCodeTest.res.mjs +158 -0
- package/tests/DomainGraphTest.res +448 -0
- package/tests/DomainGraphTest.res.mjs +505 -0
- package/tests/FlowAggregateGwtTest.res +171 -0
- package/tests/FlowAggregateGwtTest.res.mjs +273 -0
- package/tests/FlowCrossPluginGwtTest.res +340 -0
- package/tests/FlowCrossPluginGwtTest.res.mjs +798 -0
- package/tests/FlowGwtTest.res +222 -0
- package/tests/FlowGwtTest.res.mjs +433 -0
- package/tests/FormatterGoldenTest.res +125 -0
- package/tests/FormatterGoldenTest.res.mjs +162 -0
- package/tests/FormatterVsCodeMessageTest.res +41 -0
- package/tests/FormatterVsCodeMessageTest.res.mjs +42 -0
- package/tests/InboundTranslationGwtTest.res +34 -0
- package/tests/InboundTranslationGwtTest.res.mjs +99 -0
- package/tests/LocalHostIntegration.res +70 -0
- package/tests/LocalHostIntegration.res.mjs +64 -0
- package/tests/LocalHostTest.res +71 -0
- package/tests/LocalHostTest.res.mjs +63 -0
- package/tests/MappingGwtTest.res +350 -0
- package/tests/MappingGwtTest.res.mjs +596 -0
- package/tests/OutboundTranslationGwtTest.res +59 -0
- package/tests/OutboundTranslationGwtTest.res.mjs +139 -0
- package/tests/PlatformRunnerTest.res +66 -0
- package/tests/PlatformRunnerTest.res.mjs +85 -0
- package/tests/PlatformScanTest.res +61 -0
- package/tests/PlatformScanTest.res.mjs +56 -0
- package/tests/QueryGwtTest.res +161 -0
- package/tests/QueryGwtTest.res.mjs +314 -0
- package/tests/QueryResolverGwtTest.res +71 -0
- package/tests/QueryResolverGwtTest.res.mjs +136 -0
- package/tests/RunnerUnitTest.res +219 -0
- package/tests/RunnerUnitTest.res.mjs +244 -0
- package/tests/SideEffect/FakeOrderNotification.res +23 -0
- package/tests/SideEffect/FakeOrderNotification.res.mjs +36 -0
- package/tests/SideEffect/FakeOrderNotificationGwtTest.res +35 -0
- package/tests/SideEffect/FakeOrderNotificationGwtTest.res.mjs +73 -0
- package/tests/SideEffect/MockEmail.res +18 -0
- package/tests/SideEffect/MockEmail.res.mjs +47 -0
- package/tests/StateChange/ExternalAddCategorySlice_GWT.res +27 -0
- package/tests/StateChange/ExternalAddCategorySlice_GWT.res.mjs +70 -0
- package/tests/StateChange/WithFixtures_Fixtures.res +9 -0
- package/tests/StateChange/WithFixtures_Fixtures.res.mjs +20 -0
- package/tests/StateChange/WithFixtures_GWT.res +23 -0
- package/tests/StateChange/WithFixtures_GWT.res.mjs +56 -0
- package/tests/StateChange/WithManualOpen_Fixtures.res +7 -0
- package/tests/StateChange/WithManualOpen_Fixtures.res.mjs +20 -0
- package/tests/StateChange/WithManualOpen_GWT.res +17 -0
- package/tests/StateChange/WithManualOpen_GWT.res.mjs +56 -0
- package/tests/StateChangeSliceGwtTest.res +160 -0
- package/tests/StateChangeSliceGwtTest.res.mjs +280 -0
- package/tests/StateViewSliceGwtTest.res +64 -0
- package/tests/StateViewSliceGwtTest.res.mjs +161 -0
- package/tests/WatchDebounceTest.res +106 -0
- package/tests/WatchDebounceTest.res.mjs +128 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as S from "sury/src/S.res.mjs";
|
|
4
|
+
import * as Outcome$ReventlessGwt from "../src/Outcome.res.mjs";
|
|
5
|
+
import * as Automation_GWT$ReventlessGwt from "../src/Automation_GWT.res.mjs";
|
|
6
|
+
|
|
7
|
+
let consumedEventSchema = S.union([
|
|
8
|
+
S.schema(s => ({
|
|
9
|
+
TAG: "OrderPlaced",
|
|
10
|
+
orderId: s.m(S.string),
|
|
11
|
+
shippingAddress: s.m(S.string)
|
|
12
|
+
})),
|
|
13
|
+
S.schema(s => ({
|
|
14
|
+
TAG: "ShipmentCreated",
|
|
15
|
+
orderId: s.m(S.string)
|
|
16
|
+
}))
|
|
17
|
+
]);
|
|
18
|
+
|
|
19
|
+
let todoItemSchema = S.schema(s => ({
|
|
20
|
+
orderId: s.m(S.string),
|
|
21
|
+
shippingAddress: s.m(S.string)
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
let commandSchema = S.schema(s => ({
|
|
25
|
+
TAG: "CreateShipment",
|
|
26
|
+
orderId: s.m(S.string),
|
|
27
|
+
address: s.m(S.string)
|
|
28
|
+
}));
|
|
29
|
+
|
|
30
|
+
function collect(event) {
|
|
31
|
+
if (event.TAG !== "OrderPlaced") {
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
let orderId = event.orderId;
|
|
35
|
+
return [[
|
|
36
|
+
orderId,
|
|
37
|
+
{
|
|
38
|
+
orderId: orderId,
|
|
39
|
+
shippingAddress: event.shippingAddress
|
|
40
|
+
}
|
|
41
|
+
]];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function resolve(event) {
|
|
45
|
+
if (event.TAG === "OrderPlaced") {
|
|
46
|
+
return;
|
|
47
|
+
} else {
|
|
48
|
+
return event.orderId;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function process(_id, item) {
|
|
53
|
+
return [
|
|
54
|
+
item.orderId,
|
|
55
|
+
{
|
|
56
|
+
TAG: "CreateShipment",
|
|
57
|
+
orderId: item.orderId,
|
|
58
|
+
address: item.shippingAddress
|
|
59
|
+
}
|
|
60
|
+
];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function commandAuthorization(param) {
|
|
64
|
+
return "AllowAuthenticated";
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
let ShipOrderSlice = {
|
|
68
|
+
name: "ShipOrder",
|
|
69
|
+
consumedEventSchema: consumedEventSchema,
|
|
70
|
+
todoItemSchema: todoItemSchema,
|
|
71
|
+
commandSchema: commandSchema,
|
|
72
|
+
collect: collect,
|
|
73
|
+
resolve: resolve,
|
|
74
|
+
process: process,
|
|
75
|
+
commandAuthorization: commandAuthorization
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
let include = Automation_GWT$ReventlessGwt.Make(ShipOrderSlice);
|
|
79
|
+
|
|
80
|
+
let describe = include.describe;
|
|
81
|
+
|
|
82
|
+
let test = include.test;
|
|
83
|
+
|
|
84
|
+
let givenEvent = include.givenEvent;
|
|
85
|
+
|
|
86
|
+
let whenCollect = include.whenCollect;
|
|
87
|
+
|
|
88
|
+
let thenTodos = include.thenTodos;
|
|
89
|
+
|
|
90
|
+
let whenResolve = include.whenResolve;
|
|
91
|
+
|
|
92
|
+
let thenResolved = include.thenResolved;
|
|
93
|
+
|
|
94
|
+
let givenTodo = include.givenTodo;
|
|
95
|
+
|
|
96
|
+
let whenProcess = include.whenProcess;
|
|
97
|
+
|
|
98
|
+
let thenCommand = include.thenCommand;
|
|
99
|
+
|
|
100
|
+
let givenEvents = include.givenEvents;
|
|
101
|
+
|
|
102
|
+
let whenSweep = include.whenSweep;
|
|
103
|
+
|
|
104
|
+
let thenCommands = include.thenCommands;
|
|
105
|
+
|
|
106
|
+
let andThenEvents = include.andThenEvents;
|
|
107
|
+
|
|
108
|
+
let thenScenarioTodos = include.thenScenarioTodos;
|
|
109
|
+
|
|
110
|
+
describe("ShipOrder AutomationSlice", () => {
|
|
111
|
+
test("collect: OrderPlaced creates a pending TODO", () => thenTodos(whenCollect(givenEvent({
|
|
112
|
+
TAG: "OrderPlaced",
|
|
113
|
+
orderId: "o1",
|
|
114
|
+
shippingAddress: "1 Main St"
|
|
115
|
+
})), [[
|
|
116
|
+
"o1",
|
|
117
|
+
{
|
|
118
|
+
orderId: "o1",
|
|
119
|
+
shippingAddress: "1 Main St"
|
|
120
|
+
}
|
|
121
|
+
]]));
|
|
122
|
+
test("resolve: ShipmentCreated marks the TODO done", () => thenResolved(whenResolve(givenEvent({
|
|
123
|
+
TAG: "ShipmentCreated",
|
|
124
|
+
orderId: "o1"
|
|
125
|
+
})), "o1"));
|
|
126
|
+
test("process: pending TODO emits CreateShipment", () => thenCommand(whenProcess(givenTodo("o1", {
|
|
127
|
+
orderId: "o1",
|
|
128
|
+
shippingAddress: "1 Main St"
|
|
129
|
+
})), "o1", {
|
|
130
|
+
TAG: "CreateShipment",
|
|
131
|
+
orderId: "o1",
|
|
132
|
+
address: "1 Main St"
|
|
133
|
+
}));
|
|
134
|
+
test("sweep: events → commands, andThenEvents drains todos", () => {
|
|
135
|
+
let s = whenSweep(givenEvents([{
|
|
136
|
+
TAG: "OrderPlaced",
|
|
137
|
+
orderId: "o1",
|
|
138
|
+
shippingAddress: "1 Main St"
|
|
139
|
+
}]));
|
|
140
|
+
let commandsOk = thenCommands(s, [[
|
|
141
|
+
"o1",
|
|
142
|
+
{
|
|
143
|
+
TAG: "CreateShipment",
|
|
144
|
+
orderId: "o1",
|
|
145
|
+
address: "1 Main St"
|
|
146
|
+
}
|
|
147
|
+
]]);
|
|
148
|
+
let drained = andThenEvents(s, [{
|
|
149
|
+
TAG: "ShipmentCreated",
|
|
150
|
+
orderId: "o1"
|
|
151
|
+
}]);
|
|
152
|
+
let todosOk = thenScenarioTodos(drained, []);
|
|
153
|
+
if (commandsOk.TAG === "Ok") {
|
|
154
|
+
if (todosOk.TAG === "Ok") {
|
|
155
|
+
return Outcome$ReventlessGwt.pass;
|
|
156
|
+
} else {
|
|
157
|
+
return todosOk;
|
|
158
|
+
}
|
|
159
|
+
} else {
|
|
160
|
+
return commandsOk;
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
let Spec = include.Spec;
|
|
166
|
+
|
|
167
|
+
let thenNoCommand = include.thenNoCommand;
|
|
168
|
+
|
|
169
|
+
export {
|
|
170
|
+
ShipOrderSlice,
|
|
171
|
+
Spec,
|
|
172
|
+
describe,
|
|
173
|
+
test,
|
|
174
|
+
givenEvent,
|
|
175
|
+
whenCollect,
|
|
176
|
+
thenTodos,
|
|
177
|
+
whenResolve,
|
|
178
|
+
thenResolved,
|
|
179
|
+
givenTodo,
|
|
180
|
+
whenProcess,
|
|
181
|
+
thenCommand,
|
|
182
|
+
thenNoCommand,
|
|
183
|
+
givenEvents,
|
|
184
|
+
whenSweep,
|
|
185
|
+
thenCommands,
|
|
186
|
+
andThenEvents,
|
|
187
|
+
thenScenarioTodos,
|
|
188
|
+
}
|
|
189
|
+
/* consumedEventSchema Not a pure module */
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
// Unit tests for BuildClassifier — the state machine that turns a
|
|
2
|
+
// `rescript build -w` line-stream into buildStart / buildOk / buildFail. The
|
|
3
|
+
// success path is synchronous (the "Finished … compilation" line); the failure
|
|
4
|
+
// path is debounced (no terminator in rescript's error output), so those tests
|
|
5
|
+
// await past the 400ms settle window.
|
|
6
|
+
|
|
7
|
+
open JestGlobals
|
|
8
|
+
|
|
9
|
+
@val external setTimeout: (unit => unit, int) => unit = "setTimeout"
|
|
10
|
+
let delay = (ms: int): promise<unit> =>
|
|
11
|
+
Promise.make((resolve, _reject) => setTimeout(() => resolve(), ms))
|
|
12
|
+
|
|
13
|
+
describe("BuildClassifier", () => {
|
|
14
|
+
testPromise("emits onStart then onOk for a clean incremental compile", async () => {
|
|
15
|
+
let starts = ref(0)
|
|
16
|
+
let oks = ref(0)
|
|
17
|
+
let fails = ref([])
|
|
18
|
+
let feed = BuildClassifier.make({
|
|
19
|
+
onStart: () => starts := starts.contents + 1,
|
|
20
|
+
onOk: _ms => oks := oks.contents + 1,
|
|
21
|
+
onFail: msg => fails := Array.concat(fails.contents, [msg]),
|
|
22
|
+
})
|
|
23
|
+
feed("Parsed 1 source files")
|
|
24
|
+
feed("Compiled 1 modules")
|
|
25
|
+
feed("Finished incremental compilation")
|
|
26
|
+
expect(starts.contents)->toEqual(1)
|
|
27
|
+
expect(oks.contents)->toEqual(1)
|
|
28
|
+
expect(fails.contents->Array.length)->toEqual(0)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
testPromise("emits onFail with the error text after the output settles", async () => {
|
|
32
|
+
let oks = ref(0)
|
|
33
|
+
let fails = ref([])
|
|
34
|
+
let feed = BuildClassifier.make({
|
|
35
|
+
onStart: () => (),
|
|
36
|
+
onOk: _ms => oks := oks.contents + 1,
|
|
37
|
+
onFail: msg => fails := Array.concat(fails.contents, [msg]),
|
|
38
|
+
})
|
|
39
|
+
feed("Parsed 1 source files")
|
|
40
|
+
feed("Compiled 1 modules")
|
|
41
|
+
feed("We've found a bug for you!")
|
|
42
|
+
feed("This has type: string")
|
|
43
|
+
feed("But it's expected to have type: int")
|
|
44
|
+
await delay(550)
|
|
45
|
+
expect(oks.contents)->toEqual(0)
|
|
46
|
+
expect(fails.contents->Array.length)->toEqual(1)
|
|
47
|
+
let msg = fails.contents->Array.getUnsafe(0)
|
|
48
|
+
expect(msg->String.includes("This has type: string"))->toEqual(true)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
testPromise("watchdog fails a build that starts but never terminates", async () => {
|
|
52
|
+
let oks = ref(0)
|
|
53
|
+
let fails = ref([])
|
|
54
|
+
let feed = BuildClassifier.make(~watchdogMs=50, {
|
|
55
|
+
onStart: () => (),
|
|
56
|
+
onOk: _ms => oks := oks.contents + 1,
|
|
57
|
+
onFail: msg => fails := Array.concat(fails.contents, [msg]),
|
|
58
|
+
})
|
|
59
|
+
feed("Parsed 1 source files")
|
|
60
|
+
// No "Finished …" and no error marker — a crashed/hung watcher.
|
|
61
|
+
await delay(120)
|
|
62
|
+
expect(oks.contents)->toEqual(0)
|
|
63
|
+
expect(fails.contents->Array.length)->toEqual(1)
|
|
64
|
+
let msg = fails.contents->Array.getUnsafe(0)
|
|
65
|
+
expect(msg->String.includes("watchdog"))->toEqual(true)
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
testPromise("watchdog does not fire once the build finishes", async () => {
|
|
69
|
+
let oks = ref(0)
|
|
70
|
+
let fails = ref([])
|
|
71
|
+
let feed = BuildClassifier.make(~watchdogMs=50, {
|
|
72
|
+
onStart: () => (),
|
|
73
|
+
onOk: _ms => oks := oks.contents + 1,
|
|
74
|
+
onFail: msg => fails := Array.concat(fails.contents, [msg]),
|
|
75
|
+
})
|
|
76
|
+
feed("Parsed 1 source files")
|
|
77
|
+
feed("Finished incremental compilation")
|
|
78
|
+
await delay(120)
|
|
79
|
+
expect(oks.contents)->toEqual(1)
|
|
80
|
+
expect(fails.contents->Array.length)->toEqual(0)
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
testPromise("errors emitted before the first Parsed line still settle to onFail (B3)", async () => {
|
|
84
|
+
// A crashed toolchain / pnpm resolution failure prints an error block with no
|
|
85
|
+
// preceding "Parsed …" line. The old `compiling && hasError` guard left this
|
|
86
|
+
// pinned forever; now `hasError` alone settles it.
|
|
87
|
+
let oks = ref(0)
|
|
88
|
+
let fails = ref([])
|
|
89
|
+
let feed = BuildClassifier.make({
|
|
90
|
+
onStart: () => (),
|
|
91
|
+
onOk: _ms => oks := oks.contents + 1,
|
|
92
|
+
onFail: msg => fails := Array.concat(fails.contents, [msg]),
|
|
93
|
+
})
|
|
94
|
+
feed("Syntax error: unexpected token")
|
|
95
|
+
feed(" at Foo.res:3:1")
|
|
96
|
+
await delay(550)
|
|
97
|
+
expect(oks.contents)->toEqual(0)
|
|
98
|
+
expect(fails.contents->Array.length)->toEqual(1)
|
|
99
|
+
expect((fails.contents->Array.getUnsafe(0))->String.includes("Syntax error"))->toEqual(true)
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
testPromise("a multi-error build keeps error blocks past the old 20-line cap (B3)", async () => {
|
|
103
|
+
let fails = ref([])
|
|
104
|
+
let feed = BuildClassifier.make({
|
|
105
|
+
onStart: () => (),
|
|
106
|
+
onOk: _ms => (),
|
|
107
|
+
onFail: msg => fails := Array.concat(fails.contents, [msg]),
|
|
108
|
+
})
|
|
109
|
+
feed("Parsed 1 source files")
|
|
110
|
+
feed("We've found a bug for you!")
|
|
111
|
+
// 30 filler lines push a distinctive later error past the old 20-line window.
|
|
112
|
+
for i in 1 to 30 {
|
|
113
|
+
feed("noise line " ++ Int.toString(i))
|
|
114
|
+
}
|
|
115
|
+
feed("Found 2 errors")
|
|
116
|
+
await delay(550)
|
|
117
|
+
expect(fails.contents->Array.length)->toEqual(1)
|
|
118
|
+
// The late "Found 2 errors" line survives (dropped under the old cap of 20).
|
|
119
|
+
expect((fails.contents->Array.getUnsafe(0))->String.includes("Found 2 errors"))->toEqual(true)
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
testPromise("strips ANSI colour codes before matching the finish line", async () => {
|
|
123
|
+
let oks = ref(0)
|
|
124
|
+
let feed = BuildClassifier.make({
|
|
125
|
+
onStart: () => (),
|
|
126
|
+
onOk: _ms => oks := oks.contents + 1,
|
|
127
|
+
onFail: _msg => (),
|
|
128
|
+
})
|
|
129
|
+
feed("Parsed 2 source files")
|
|
130
|
+
feed("\x1b[1;32mFinished incremental compilation\x1b[0m")
|
|
131
|
+
expect(oks.contents)->toEqual(1)
|
|
132
|
+
})
|
|
133
|
+
})
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as BuildClassifier$ReventlessGwt from "../src/BuildClassifier.res.mjs";
|
|
4
|
+
|
|
5
|
+
function delay(ms) {
|
|
6
|
+
return new Promise((resolve, _reject) => {
|
|
7
|
+
setTimeout(() => resolve(), ms);
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
globalThis.describe("BuildClassifier", () => {
|
|
12
|
+
globalThis.test("emits onStart then onOk for a clean incremental compile", async () => {
|
|
13
|
+
let starts = {
|
|
14
|
+
contents: 0
|
|
15
|
+
};
|
|
16
|
+
let oks = {
|
|
17
|
+
contents: 0
|
|
18
|
+
};
|
|
19
|
+
let fails = {
|
|
20
|
+
contents: []
|
|
21
|
+
};
|
|
22
|
+
let feed = BuildClassifier$ReventlessGwt.make(undefined, {
|
|
23
|
+
onStart: () => {
|
|
24
|
+
starts.contents = starts.contents + 1 | 0;
|
|
25
|
+
},
|
|
26
|
+
onOk: _ms => {
|
|
27
|
+
oks.contents = oks.contents + 1 | 0;
|
|
28
|
+
},
|
|
29
|
+
onFail: msg => {
|
|
30
|
+
fails.contents = fails.contents.concat([msg]);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
feed("Parsed 1 source files");
|
|
34
|
+
feed("Compiled 1 modules");
|
|
35
|
+
feed("Finished incremental compilation");
|
|
36
|
+
globalThis.expect(starts.contents).toEqual(1);
|
|
37
|
+
globalThis.expect(oks.contents).toEqual(1);
|
|
38
|
+
globalThis.expect(fails.contents.length).toEqual(0);
|
|
39
|
+
});
|
|
40
|
+
globalThis.test("emits onFail with the error text after the output settles", async () => {
|
|
41
|
+
let oks = {
|
|
42
|
+
contents: 0
|
|
43
|
+
};
|
|
44
|
+
let fails = {
|
|
45
|
+
contents: []
|
|
46
|
+
};
|
|
47
|
+
let feed = BuildClassifier$ReventlessGwt.make(undefined, {
|
|
48
|
+
onStart: () => {},
|
|
49
|
+
onOk: _ms => {
|
|
50
|
+
oks.contents = oks.contents + 1 | 0;
|
|
51
|
+
},
|
|
52
|
+
onFail: msg => {
|
|
53
|
+
fails.contents = fails.contents.concat([msg]);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
feed("Parsed 1 source files");
|
|
57
|
+
feed("Compiled 1 modules");
|
|
58
|
+
feed("We've found a bug for you!");
|
|
59
|
+
feed("This has type: string");
|
|
60
|
+
feed("But it's expected to have type: int");
|
|
61
|
+
await delay(550);
|
|
62
|
+
globalThis.expect(oks.contents).toEqual(0);
|
|
63
|
+
globalThis.expect(fails.contents.length).toEqual(1);
|
|
64
|
+
let msg = fails.contents[0];
|
|
65
|
+
globalThis.expect(msg.includes("This has type: string")).toEqual(true);
|
|
66
|
+
});
|
|
67
|
+
globalThis.test("watchdog fails a build that starts but never terminates", async () => {
|
|
68
|
+
let oks = {
|
|
69
|
+
contents: 0
|
|
70
|
+
};
|
|
71
|
+
let fails = {
|
|
72
|
+
contents: []
|
|
73
|
+
};
|
|
74
|
+
let feed = BuildClassifier$ReventlessGwt.make(50, {
|
|
75
|
+
onStart: () => {},
|
|
76
|
+
onOk: _ms => {
|
|
77
|
+
oks.contents = oks.contents + 1 | 0;
|
|
78
|
+
},
|
|
79
|
+
onFail: msg => {
|
|
80
|
+
fails.contents = fails.contents.concat([msg]);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
feed("Parsed 1 source files");
|
|
84
|
+
await delay(120);
|
|
85
|
+
globalThis.expect(oks.contents).toEqual(0);
|
|
86
|
+
globalThis.expect(fails.contents.length).toEqual(1);
|
|
87
|
+
let msg = fails.contents[0];
|
|
88
|
+
globalThis.expect(msg.includes("watchdog")).toEqual(true);
|
|
89
|
+
});
|
|
90
|
+
globalThis.test("watchdog does not fire once the build finishes", async () => {
|
|
91
|
+
let oks = {
|
|
92
|
+
contents: 0
|
|
93
|
+
};
|
|
94
|
+
let fails = {
|
|
95
|
+
contents: []
|
|
96
|
+
};
|
|
97
|
+
let feed = BuildClassifier$ReventlessGwt.make(50, {
|
|
98
|
+
onStart: () => {},
|
|
99
|
+
onOk: _ms => {
|
|
100
|
+
oks.contents = oks.contents + 1 | 0;
|
|
101
|
+
},
|
|
102
|
+
onFail: msg => {
|
|
103
|
+
fails.contents = fails.contents.concat([msg]);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
feed("Parsed 1 source files");
|
|
107
|
+
feed("Finished incremental compilation");
|
|
108
|
+
await delay(120);
|
|
109
|
+
globalThis.expect(oks.contents).toEqual(1);
|
|
110
|
+
globalThis.expect(fails.contents.length).toEqual(0);
|
|
111
|
+
});
|
|
112
|
+
globalThis.test("errors emitted before the first Parsed line still settle to onFail (B3)", async () => {
|
|
113
|
+
let oks = {
|
|
114
|
+
contents: 0
|
|
115
|
+
};
|
|
116
|
+
let fails = {
|
|
117
|
+
contents: []
|
|
118
|
+
};
|
|
119
|
+
let feed = BuildClassifier$ReventlessGwt.make(undefined, {
|
|
120
|
+
onStart: () => {},
|
|
121
|
+
onOk: _ms => {
|
|
122
|
+
oks.contents = oks.contents + 1 | 0;
|
|
123
|
+
},
|
|
124
|
+
onFail: msg => {
|
|
125
|
+
fails.contents = fails.contents.concat([msg]);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
feed("Syntax error: unexpected token");
|
|
129
|
+
feed(" at Foo.res:3:1");
|
|
130
|
+
await delay(550);
|
|
131
|
+
globalThis.expect(oks.contents).toEqual(0);
|
|
132
|
+
globalThis.expect(fails.contents.length).toEqual(1);
|
|
133
|
+
globalThis.expect(fails.contents[0].includes("Syntax error")).toEqual(true);
|
|
134
|
+
});
|
|
135
|
+
globalThis.test("a multi-error build keeps error blocks past the old 20-line cap (B3)", async () => {
|
|
136
|
+
let fails = {
|
|
137
|
+
contents: []
|
|
138
|
+
};
|
|
139
|
+
let feed = BuildClassifier$ReventlessGwt.make(undefined, {
|
|
140
|
+
onStart: () => {},
|
|
141
|
+
onOk: _ms => {},
|
|
142
|
+
onFail: msg => {
|
|
143
|
+
fails.contents = fails.contents.concat([msg]);
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
feed("Parsed 1 source files");
|
|
147
|
+
feed("We've found a bug for you!");
|
|
148
|
+
for (let i = 1; i <= 30; ++i) {
|
|
149
|
+
feed("noise line " + i.toString());
|
|
150
|
+
}
|
|
151
|
+
feed("Found 2 errors");
|
|
152
|
+
await delay(550);
|
|
153
|
+
globalThis.expect(fails.contents.length).toEqual(1);
|
|
154
|
+
globalThis.expect(fails.contents[0].includes("Found 2 errors")).toEqual(true);
|
|
155
|
+
});
|
|
156
|
+
globalThis.test("strips ANSI colour codes before matching the finish line", async () => {
|
|
157
|
+
let oks = {
|
|
158
|
+
contents: 0
|
|
159
|
+
};
|
|
160
|
+
let feed = BuildClassifier$ReventlessGwt.make(undefined, {
|
|
161
|
+
onStart: () => {},
|
|
162
|
+
onOk: _ms => {
|
|
163
|
+
oks.contents = oks.contents + 1 | 0;
|
|
164
|
+
},
|
|
165
|
+
onFail: _msg => {}
|
|
166
|
+
});
|
|
167
|
+
feed("Parsed 2 source files");
|
|
168
|
+
feed("\x1b[1;32mFinished incremental compilation\x1b[0m");
|
|
169
|
+
globalThis.expect(oks.contents).toEqual(1);
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
export {
|
|
174
|
+
delay,
|
|
175
|
+
}
|
|
176
|
+
/* Not a pure module */
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// Regression tests for the CLI runner's per-test deadline (A2) and the
|
|
2
|
+
// Collector's skip-depth reset (A4). Before A2 a hung test body wedged the whole
|
|
3
|
+
// run — `runEntry` awaited it with no deadline. Before A4 a throwing `xdescribe`
|
|
4
|
+
// left `skipDepth` > 0, silently skipping every subsequently loaded file; the
|
|
5
|
+
// reset lives in `Collector.activate`.
|
|
6
|
+
|
|
7
|
+
open JestGlobals
|
|
8
|
+
|
|
9
|
+
@val external setTimeout: (unit => unit, int) => unit = "setTimeout"
|
|
10
|
+
|
|
11
|
+
let hangingEntry = (~timeout): Collector.entry => {
|
|
12
|
+
id: "hangs",
|
|
13
|
+
name: "hangs forever",
|
|
14
|
+
describePath: [],
|
|
15
|
+
slice: None,
|
|
16
|
+
// A body that never resolves — the pathological hung test.
|
|
17
|
+
body: () => Promise.make((_resolve, _reject) => ()),
|
|
18
|
+
status: Collector.Runnable,
|
|
19
|
+
location: None,
|
|
20
|
+
timeout,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe("Cli.runEntry deadline", () => {
|
|
24
|
+
testPromise("a hung body is reported as a timeout, not a wedge", async () => {
|
|
25
|
+
let r = await Cli.runEntry(hangingEntry(~timeout=Some(50)))
|
|
26
|
+
expect(r.status)->toEqual(RunnerTypes.Fail)
|
|
27
|
+
switch r.mismatch {
|
|
28
|
+
| Some(Outcome.Throw({error})) => expect(error->String.includes("timed out"))->toEqual(true)
|
|
29
|
+
| _ => JsError.throwWithMessage("expected a timeout Throw mismatch")
|
|
30
|
+
}
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
testPromise("a body that resolves within the deadline passes", async () => {
|
|
34
|
+
let entry: Collector.entry = {
|
|
35
|
+
id: "ok",
|
|
36
|
+
name: "resolves fast",
|
|
37
|
+
describePath: [],
|
|
38
|
+
slice: None,
|
|
39
|
+
body: () => Promise.resolve(Outcome.pass),
|
|
40
|
+
status: Collector.Runnable,
|
|
41
|
+
location: None,
|
|
42
|
+
timeout: Some(1000),
|
|
43
|
+
}
|
|
44
|
+
let r = await Cli.runEntry(entry)
|
|
45
|
+
expect(r.status)->toEqual(RunnerTypes.Pass)
|
|
46
|
+
})
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
describe("Collector.activate", () => {
|
|
50
|
+
testSync("resets a leaked skipDepth to zero", () => {
|
|
51
|
+
// Simulate the leak a throwing xdescribe body would leave behind.
|
|
52
|
+
Collector.skipDepth := 3
|
|
53
|
+
Collector.activate()
|
|
54
|
+
expect(Collector.skipDepth.contents)->toEqual(0)
|
|
55
|
+
Collector.deactivate()
|
|
56
|
+
})
|
|
57
|
+
})
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
4
|
+
import * as Cli$ReventlessGwt from "../src/Cli.res.mjs";
|
|
5
|
+
import * as Outcome$ReventlessGwt from "../src/Outcome.res.mjs";
|
|
6
|
+
import * as Collector$ReventlessGwt from "../src/Collector.res.mjs";
|
|
7
|
+
|
|
8
|
+
function hangingEntry(timeout) {
|
|
9
|
+
return {
|
|
10
|
+
id: "hangs",
|
|
11
|
+
name: "hangs forever",
|
|
12
|
+
describePath: [],
|
|
13
|
+
slice: undefined,
|
|
14
|
+
body: () => new Promise((_resolve, _reject) => {}),
|
|
15
|
+
status: "Runnable",
|
|
16
|
+
location: undefined,
|
|
17
|
+
timeout: timeout
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
globalThis.describe("Cli.runEntry deadline", () => {
|
|
22
|
+
globalThis.test("a hung body is reported as a timeout, not a wedge", async () => {
|
|
23
|
+
let r = await Cli$ReventlessGwt.runEntry(hangingEntry(50));
|
|
24
|
+
globalThis.expect(r.status).toEqual("Fail");
|
|
25
|
+
let match = r.mismatch;
|
|
26
|
+
if (match === undefined) {
|
|
27
|
+
return Stdlib_JsError.throwWithMessage("expected a timeout Throw mismatch");
|
|
28
|
+
}
|
|
29
|
+
if (match.TAG !== "Throw") {
|
|
30
|
+
return Stdlib_JsError.throwWithMessage("expected a timeout Throw mismatch");
|
|
31
|
+
}
|
|
32
|
+
globalThis.expect(match.error.includes("timed out")).toEqual(true);
|
|
33
|
+
});
|
|
34
|
+
globalThis.test("a body that resolves within the deadline passes", async () => {
|
|
35
|
+
let entry_describePath = [];
|
|
36
|
+
let entry_body = () => Promise.resolve(Outcome$ReventlessGwt.pass);
|
|
37
|
+
let entry_timeout = 1000;
|
|
38
|
+
let entry = {
|
|
39
|
+
id: "ok",
|
|
40
|
+
name: "resolves fast",
|
|
41
|
+
describePath: entry_describePath,
|
|
42
|
+
slice: undefined,
|
|
43
|
+
body: entry_body,
|
|
44
|
+
status: "Runnable",
|
|
45
|
+
location: undefined,
|
|
46
|
+
timeout: entry_timeout
|
|
47
|
+
};
|
|
48
|
+
let r = await Cli$ReventlessGwt.runEntry(entry);
|
|
49
|
+
globalThis.expect(r.status).toEqual("Pass");
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
globalThis.describe("Collector.activate", () => {
|
|
54
|
+
globalThis.test("resets a leaked skipDepth to zero", () => {
|
|
55
|
+
Collector$ReventlessGwt.skipDepth.contents = 3;
|
|
56
|
+
Collector$ReventlessGwt.activate();
|
|
57
|
+
globalThis.expect(Collector$ReventlessGwt.skipDepth.contents).toEqual(0);
|
|
58
|
+
Collector$ReventlessGwt.deactivate();
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
export {
|
|
63
|
+
hangingEntry,
|
|
64
|
+
}
|
|
65
|
+
/* Not a pure module */
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// Unit tests for the watch re-run scope algebra (B1): a plain edit narrows the
|
|
2
|
+
// re-run to its owning test package, structural/add/full triggers widen to
|
|
3
|
+
// RunAll, and scopes coalesce (RunAll absorbing) while a pass is in flight.
|
|
4
|
+
|
|
5
|
+
open JestGlobals
|
|
6
|
+
|
|
7
|
+
describe("Cli.mergeScope", () => {
|
|
8
|
+
testSync("RunAll absorbs on the left", () =>
|
|
9
|
+
expect(Cli.mergeScope(RunAll, RunPackages(["/p/a"])))->toEqual(Cli.RunAll)
|
|
10
|
+
)
|
|
11
|
+
testSync("RunAll absorbs on the right", () =>
|
|
12
|
+
expect(Cli.mergeScope(RunPackages(["/p/a"]), RunAll))->toEqual(Cli.RunAll)
|
|
13
|
+
)
|
|
14
|
+
testSync("two RunPackages union their dirs, deduplicated", () =>
|
|
15
|
+
expect(Cli.mergeScope(RunPackages(["/p/a", "/p/b"]), RunPackages(["/p/b", "/p/c"])))->toEqual(
|
|
16
|
+
Cli.RunPackages(["/p/a", "/p/b", "/p/c"]),
|
|
17
|
+
)
|
|
18
|
+
)
|
|
19
|
+
testSync("RunAll merged with RunAll stays RunAll", () =>
|
|
20
|
+
expect(Cli.mergeScope(RunAll, RunAll))->toEqual(Cli.RunAll)
|
|
21
|
+
)
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
describe("Cli.pathUnderDir", () => {
|
|
25
|
+
testSync("a file inside the dir is under it", () =>
|
|
26
|
+
expect(Cli.pathUnderDir("/p/pkgA/tests/FooGwt.res.mjs", "/p/pkgA"))->toEqual(true)
|
|
27
|
+
)
|
|
28
|
+
testSync("a sibling with a shared prefix is NOT under it", () =>
|
|
29
|
+
// /p/pkgABC must not be treated as inside /p/pkgA — the trailing separator
|
|
30
|
+
// guards against the bare-prefix false positive.
|
|
31
|
+
expect(Cli.pathUnderDir("/p/pkgABC/tests/FooGwt.res.mjs", "/p/pkgA"))->toEqual(false)
|
|
32
|
+
)
|
|
33
|
+
testSync("an unrelated path is not under it", () =>
|
|
34
|
+
expect(Cli.pathUnderDir("/p/pkgB/tests/BarGwt.res.mjs", "/p/pkgA"))->toEqual(false)
|
|
35
|
+
)
|
|
36
|
+
testSync("a trailing-slash dir still matches", () =>
|
|
37
|
+
expect(Cli.pathUnderDir("/p/pkgA/tests/FooGwt.res.mjs", "/p/pkgA/"))->toEqual(true)
|
|
38
|
+
)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
describe("Cli.scopeSubset", () => {
|
|
42
|
+
let all = [
|
|
43
|
+
"/p/pkgA/tests/FooGwt.res.mjs",
|
|
44
|
+
"/p/pkgA/tests/BarGwt.res.mjs",
|
|
45
|
+
"/p/pkgB/tests/BazGwt.res.mjs",
|
|
46
|
+
]
|
|
47
|
+
testSync("RunAll returns every path", () =>
|
|
48
|
+
expect(Cli.scopeSubset(RunAll, all))->toEqual(all)
|
|
49
|
+
)
|
|
50
|
+
testSync("RunPackages keeps only files under the given dirs", () =>
|
|
51
|
+
expect(Cli.scopeSubset(RunPackages(["/p/pkgA"]), all))->toEqual([
|
|
52
|
+
"/p/pkgA/tests/FooGwt.res.mjs",
|
|
53
|
+
"/p/pkgA/tests/BarGwt.res.mjs",
|
|
54
|
+
])
|
|
55
|
+
)
|
|
56
|
+
testSync("RunPackages over multiple dirs unions their files", () =>
|
|
57
|
+
expect(Cli.scopeSubset(RunPackages(["/p/pkgA", "/p/pkgB"]), all))->toEqual(all)
|
|
58
|
+
)
|
|
59
|
+
testSync("RunPackages with no matching dir yields no files", () =>
|
|
60
|
+
expect(Cli.scopeSubset(RunPackages(["/p/pkgZ"]), all))->toEqual([])
|
|
61
|
+
)
|
|
62
|
+
})
|