@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,604 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as S from "sury/src/S.res.mjs";
|
|
4
|
+
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
5
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
6
|
+
import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js";
|
|
7
|
+
import * as Primitive_string from "@rescript/runtime/lib/es6/Primitive_string.js";
|
|
8
|
+
import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/components/DcbTag.res.mjs";
|
|
9
|
+
import * as Message$Reventless from "@reventlessdev/reventless-spec/src/types/Message.res.mjs";
|
|
10
|
+
import * as DcbDecode$Reventless from "@reventlessdev/reventless-spec/src/components/DcbDecode.res.mjs";
|
|
11
|
+
import * as Outcome$ReventlessGwt from "./Outcome.res.mjs";
|
|
12
|
+
import * as JestBind$ReventlessGwt from "./JestBind.res.mjs";
|
|
13
|
+
import * as Message$ReventlessCore from "@reventlessdev/reventless-core/src/Message.res.mjs";
|
|
14
|
+
import * as StubRuntime$ReventlessGwt from "./StubRuntime.res.mjs";
|
|
15
|
+
import * as DcbScopeInference$Reventless from "@reventlessdev/reventless-spec/src/components/DcbScopeInference.res.mjs";
|
|
16
|
+
import * as Projection_GWT$ReventlessGwt from "./Projection_GWT.res.mjs";
|
|
17
|
+
|
|
18
|
+
S.enableJson();
|
|
19
|
+
|
|
20
|
+
let emptyState_log = [];
|
|
21
|
+
|
|
22
|
+
let emptyState_lastEvents = [];
|
|
23
|
+
|
|
24
|
+
let emptyState_lastCommands = [];
|
|
25
|
+
|
|
26
|
+
let emptyState_lastPublic = [];
|
|
27
|
+
|
|
28
|
+
let emptyState = {
|
|
29
|
+
log: emptyState_log,
|
|
30
|
+
outcome: Outcome$ReventlessGwt.pass,
|
|
31
|
+
lastEvents: emptyState_lastEvents,
|
|
32
|
+
lastError: undefined,
|
|
33
|
+
lastCommands: emptyState_lastCommands,
|
|
34
|
+
lastPublic: emptyState_lastPublic,
|
|
35
|
+
lastAggregateId: undefined
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
let start = Promise.resolve(emptyState);
|
|
39
|
+
|
|
40
|
+
function recordOutcome(s, o) {
|
|
41
|
+
let match = s.outcome;
|
|
42
|
+
if (match.TAG === "Ok") {
|
|
43
|
+
return {
|
|
44
|
+
log: s.log,
|
|
45
|
+
outcome: o,
|
|
46
|
+
lastEvents: s.lastEvents,
|
|
47
|
+
lastError: s.lastError,
|
|
48
|
+
lastCommands: s.lastCommands,
|
|
49
|
+
lastPublic: s.lastPublic,
|
|
50
|
+
lastAggregateId: s.lastAggregateId
|
|
51
|
+
};
|
|
52
|
+
} else {
|
|
53
|
+
return s;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function entryMatchesQuery(entry, query) {
|
|
58
|
+
if (query.length === 0) {
|
|
59
|
+
return true;
|
|
60
|
+
} else {
|
|
61
|
+
return query.some(qi => {
|
|
62
|
+
let types = qi.eventTypes;
|
|
63
|
+
let typeMatch = types !== undefined ? types.includes(entry.eventType) : true;
|
|
64
|
+
let tags = qi.tags;
|
|
65
|
+
let tagMatch = tags !== undefined ? tags.every(tag => entry.tags.some(et => {
|
|
66
|
+
if (et.key === tag.key) {
|
|
67
|
+
return et.value === tag.value;
|
|
68
|
+
} else {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
})) : true;
|
|
72
|
+
if (typeMatch) {
|
|
73
|
+
return tagMatch;
|
|
74
|
+
} else {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function appendEvents(log, events, schema) {
|
|
82
|
+
return log.concat(events.map(ev => {
|
|
83
|
+
let json = Message$ReventlessCore.encode(ev, schema);
|
|
84
|
+
return {
|
|
85
|
+
eventType: Message$Reventless.variantNameOfJson(json),
|
|
86
|
+
tags: DcbTag$Reventless.extractTagsExpanded(schema, ev),
|
|
87
|
+
json: json,
|
|
88
|
+
aggregateId: undefined
|
|
89
|
+
};
|
|
90
|
+
}));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function appendAggregateEvents(log, id, events, schema) {
|
|
94
|
+
return log.concat(events.map(ev => {
|
|
95
|
+
let json = Message$ReventlessCore.encode(ev, schema);
|
|
96
|
+
return {
|
|
97
|
+
eventType: Message$Reventless.variantNameOfJson(json),
|
|
98
|
+
tags: DcbTag$Reventless.extractTagsExpanded(schema, ev),
|
|
99
|
+
json: json,
|
|
100
|
+
aggregateId: id
|
|
101
|
+
};
|
|
102
|
+
}));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function decodeMatching(log, decoder, query) {
|
|
106
|
+
return Stdlib_Array.filterMap(log, entry => {
|
|
107
|
+
if (!entryMatchesQuery(entry, query)) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
let d = entry.json;
|
|
111
|
+
let data;
|
|
112
|
+
data = typeof d === "object" && d !== null && !Array.isArray(d) ? d : ({});
|
|
113
|
+
return decoder.decode(entry.eventType, data);
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function decodeAggregateMatching(log, id, decoder) {
|
|
118
|
+
return Stdlib_Array.filterMap(log, entry => {
|
|
119
|
+
let eid = entry.aggregateId;
|
|
120
|
+
if (eid === undefined) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (eid !== id) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
let d = entry.json;
|
|
127
|
+
let data;
|
|
128
|
+
data = typeof d === "object" && d !== null && !Array.isArray(d) ? d : ({});
|
|
129
|
+
return decoder.decode(entry.eventType, data);
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function test(name, timeout, body) {
|
|
134
|
+
JestBind$ReventlessGwt.testPromise("Flow", name, timeout, async () => (await body()).outcome);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function CommandStep(Spec) {
|
|
138
|
+
return Behavior => {
|
|
139
|
+
let consumedDecoder = DcbDecode$Reventless.makeDecoder(Spec.consumedEventSchema);
|
|
140
|
+
let consumedEventTypes = consumedDecoder.eventTypes;
|
|
141
|
+
let givenEvents = async (flowP, events) => {
|
|
142
|
+
let s = await flowP;
|
|
143
|
+
return {
|
|
144
|
+
log: appendEvents(s.log, events, Spec.eventSchema),
|
|
145
|
+
outcome: s.outcome,
|
|
146
|
+
lastEvents: s.lastEvents,
|
|
147
|
+
lastError: s.lastError,
|
|
148
|
+
lastCommands: s.lastCommands,
|
|
149
|
+
lastPublic: s.lastPublic,
|
|
150
|
+
lastAggregateId: s.lastAggregateId
|
|
151
|
+
};
|
|
152
|
+
};
|
|
153
|
+
let scopeShape = DcbTag$Reventless.sliceShapeFromSchemas("", Spec.commandSchema, Spec.consumedEventSchema, Spec.eventSchema);
|
|
154
|
+
let set = new Set();
|
|
155
|
+
DcbTag$Reventless.extractCrossPartitionTagKeys(Spec.eventSchema).forEach(k => {
|
|
156
|
+
set.add(k);
|
|
157
|
+
});
|
|
158
|
+
DcbScopeInference$Reventless.crossPartitionForSlice(scopeShape).forEach(k => {
|
|
159
|
+
set.add(k);
|
|
160
|
+
});
|
|
161
|
+
let crossPartitionTagKeys = Array.from(set.values()).toSorted(Primitive_string.compare);
|
|
162
|
+
let tagKeysByEventType = DcbScopeInference$Reventless.infer([scopeShape]).tagKeysByEventType;
|
|
163
|
+
let whenCommand = async (flowP, command) => {
|
|
164
|
+
let s = await flowP;
|
|
165
|
+
let query = DcbTag$Reventless.buildQueryFromCommand(consumedEventTypes, Spec.commandSchema, command, tagKeysByEventType, crossPartitionTagKeys);
|
|
166
|
+
let history = decodeMatching(s.log, consumedDecoder, query);
|
|
167
|
+
let state = Stdlib_Array.reduce(history, Behavior.initialState, Behavior.evolve);
|
|
168
|
+
let events = Behavior.decide(state, command);
|
|
169
|
+
if (events.TAG !== "Ok") {
|
|
170
|
+
return {
|
|
171
|
+
log: s.log,
|
|
172
|
+
outcome: s.outcome,
|
|
173
|
+
lastEvents: [],
|
|
174
|
+
lastError: Message$ReventlessCore.encode(events._0, Spec.errorSchema),
|
|
175
|
+
lastCommands: s.lastCommands,
|
|
176
|
+
lastPublic: s.lastPublic,
|
|
177
|
+
lastAggregateId: undefined
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
let events$1 = events._0;
|
|
181
|
+
return {
|
|
182
|
+
log: appendEvents(s.log, events$1, Spec.eventSchema),
|
|
183
|
+
outcome: s.outcome,
|
|
184
|
+
lastEvents: events$1.map(e => Message$ReventlessCore.encode(e, Spec.eventSchema)),
|
|
185
|
+
lastError: undefined,
|
|
186
|
+
lastCommands: s.lastCommands,
|
|
187
|
+
lastPublic: s.lastPublic,
|
|
188
|
+
lastAggregateId: undefined
|
|
189
|
+
};
|
|
190
|
+
};
|
|
191
|
+
let thenEvents = async (flowP, expected) => {
|
|
192
|
+
let s = await flowP;
|
|
193
|
+
let expectedJson = expected.map(e => Message$ReventlessCore.encode(e, Spec.eventSchema));
|
|
194
|
+
let err = s.lastError;
|
|
195
|
+
let o = err !== undefined ? Outcome$ReventlessGwt.fail({
|
|
196
|
+
TAG: "ErrorMismatch",
|
|
197
|
+
expected: null,
|
|
198
|
+
actual: err,
|
|
199
|
+
actualEvents: s.lastEvents
|
|
200
|
+
}) : (
|
|
201
|
+
Primitive_object.equal(s.lastEvents, expectedJson) ? Outcome$ReventlessGwt.pass : Outcome$ReventlessGwt.fail({
|
|
202
|
+
TAG: "EventsMismatch",
|
|
203
|
+
expected: expectedJson,
|
|
204
|
+
actual: s.lastEvents
|
|
205
|
+
})
|
|
206
|
+
);
|
|
207
|
+
return recordOutcome(s, o);
|
|
208
|
+
};
|
|
209
|
+
let thenEvent = (flowP, event) => thenEvents(flowP, [event]);
|
|
210
|
+
let thenError = async (flowP, expected) => {
|
|
211
|
+
let s = await flowP;
|
|
212
|
+
let expJson = Message$ReventlessCore.encode(expected, Spec.errorSchema);
|
|
213
|
+
let actual = s.lastError;
|
|
214
|
+
let o = actual !== undefined ? (
|
|
215
|
+
Primitive_object.equal(actual, expJson) ? Outcome$ReventlessGwt.pass : Outcome$ReventlessGwt.fail({
|
|
216
|
+
TAG: "ErrorMismatch",
|
|
217
|
+
expected: expJson,
|
|
218
|
+
actual: actual,
|
|
219
|
+
actualEvents: s.lastEvents
|
|
220
|
+
})
|
|
221
|
+
) : Outcome$ReventlessGwt.fail({
|
|
222
|
+
TAG: "ErrorMismatch",
|
|
223
|
+
expected: expJson,
|
|
224
|
+
actual: undefined,
|
|
225
|
+
actualEvents: s.lastEvents
|
|
226
|
+
});
|
|
227
|
+
return recordOutcome(s, o);
|
|
228
|
+
};
|
|
229
|
+
return {
|
|
230
|
+
consumedDecoder: consumedDecoder,
|
|
231
|
+
consumedEventTypes: consumedEventTypes,
|
|
232
|
+
givenEvents: givenEvents,
|
|
233
|
+
scopeShape: scopeShape,
|
|
234
|
+
crossPartitionTagKeys: crossPartitionTagKeys,
|
|
235
|
+
tagKeysByEventType: tagKeysByEventType,
|
|
236
|
+
whenCommand: whenCommand,
|
|
237
|
+
thenEvents: thenEvents,
|
|
238
|
+
thenEvent: thenEvent,
|
|
239
|
+
thenError: thenError
|
|
240
|
+
};
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function AggregateCommandStep(Spec) {
|
|
245
|
+
return Behavior => {
|
|
246
|
+
let aggregateDecoder = DcbDecode$Reventless.makeDecoder(Spec.eventSchema);
|
|
247
|
+
let givenEvents = async (flowP, id, events) => {
|
|
248
|
+
let s = await flowP;
|
|
249
|
+
return {
|
|
250
|
+
log: appendAggregateEvents(s.log, id, events, Spec.eventSchema),
|
|
251
|
+
outcome: s.outcome,
|
|
252
|
+
lastEvents: s.lastEvents,
|
|
253
|
+
lastError: s.lastError,
|
|
254
|
+
lastCommands: s.lastCommands,
|
|
255
|
+
lastPublic: s.lastPublic,
|
|
256
|
+
lastAggregateId: s.lastAggregateId
|
|
257
|
+
};
|
|
258
|
+
};
|
|
259
|
+
let whenCommand = async (flowP, id, command) => {
|
|
260
|
+
let s = await flowP;
|
|
261
|
+
let history = decodeAggregateMatching(s.log, id, aggregateDecoder);
|
|
262
|
+
let state = Stdlib_Array.reduce(history, Behavior.initialState, Behavior.evolve);
|
|
263
|
+
let events = Behavior.decide(state, command);
|
|
264
|
+
if (events.TAG !== "Ok") {
|
|
265
|
+
return {
|
|
266
|
+
log: s.log,
|
|
267
|
+
outcome: s.outcome,
|
|
268
|
+
lastEvents: [],
|
|
269
|
+
lastError: Message$ReventlessCore.encode(events._0, Spec.errorSchema),
|
|
270
|
+
lastCommands: s.lastCommands,
|
|
271
|
+
lastPublic: s.lastPublic,
|
|
272
|
+
lastAggregateId: id
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
let events$1 = events._0;
|
|
276
|
+
return {
|
|
277
|
+
log: appendAggregateEvents(s.log, id, events$1, Spec.eventSchema),
|
|
278
|
+
outcome: s.outcome,
|
|
279
|
+
lastEvents: events$1.map(e => Message$ReventlessCore.encode(e, Spec.eventSchema)),
|
|
280
|
+
lastError: undefined,
|
|
281
|
+
lastCommands: s.lastCommands,
|
|
282
|
+
lastPublic: s.lastPublic,
|
|
283
|
+
lastAggregateId: id
|
|
284
|
+
};
|
|
285
|
+
};
|
|
286
|
+
let thenEvents = async (flowP, expected) => {
|
|
287
|
+
let s = await flowP;
|
|
288
|
+
let expectedJson = expected.map(e => Message$ReventlessCore.encode(e, Spec.eventSchema));
|
|
289
|
+
let err = s.lastError;
|
|
290
|
+
let o = err !== undefined ? Outcome$ReventlessGwt.fail({
|
|
291
|
+
TAG: "ErrorMismatch",
|
|
292
|
+
expected: null,
|
|
293
|
+
actual: err,
|
|
294
|
+
actualEvents: s.lastEvents
|
|
295
|
+
}) : (
|
|
296
|
+
Primitive_object.equal(s.lastEvents, expectedJson) ? Outcome$ReventlessGwt.pass : Outcome$ReventlessGwt.fail({
|
|
297
|
+
TAG: "EventsMismatch",
|
|
298
|
+
expected: expectedJson,
|
|
299
|
+
actual: s.lastEvents
|
|
300
|
+
})
|
|
301
|
+
);
|
|
302
|
+
return recordOutcome(s, o);
|
|
303
|
+
};
|
|
304
|
+
let thenEvent = (flowP, event) => thenEvents(flowP, [event]);
|
|
305
|
+
let thenNoEvent = async flowP => {
|
|
306
|
+
let s = await flowP;
|
|
307
|
+
let err = s.lastError;
|
|
308
|
+
let o = err !== undefined ? Outcome$ReventlessGwt.fail({
|
|
309
|
+
TAG: "ErrorMismatch",
|
|
310
|
+
expected: null,
|
|
311
|
+
actual: err,
|
|
312
|
+
actualEvents: s.lastEvents
|
|
313
|
+
}) : (
|
|
314
|
+
s.lastEvents.length === 0 ? Outcome$ReventlessGwt.pass : Outcome$ReventlessGwt.fail({
|
|
315
|
+
TAG: "NoEventExpected",
|
|
316
|
+
actual: s.lastEvents
|
|
317
|
+
})
|
|
318
|
+
);
|
|
319
|
+
return recordOutcome(s, o);
|
|
320
|
+
};
|
|
321
|
+
let thenError = async (flowP, expected) => {
|
|
322
|
+
let s = await flowP;
|
|
323
|
+
let expJson = Message$ReventlessCore.encode(expected, Spec.errorSchema);
|
|
324
|
+
let actual = s.lastError;
|
|
325
|
+
let o = actual !== undefined ? (
|
|
326
|
+
Primitive_object.equal(actual, expJson) ? Outcome$ReventlessGwt.pass : Outcome$ReventlessGwt.fail({
|
|
327
|
+
TAG: "ErrorMismatch",
|
|
328
|
+
expected: expJson,
|
|
329
|
+
actual: actual,
|
|
330
|
+
actualEvents: s.lastEvents
|
|
331
|
+
})
|
|
332
|
+
) : Outcome$ReventlessGwt.fail({
|
|
333
|
+
TAG: "ErrorMismatch",
|
|
334
|
+
expected: expJson,
|
|
335
|
+
actual: undefined,
|
|
336
|
+
actualEvents: s.lastEvents
|
|
337
|
+
});
|
|
338
|
+
return recordOutcome(s, o);
|
|
339
|
+
};
|
|
340
|
+
return {
|
|
341
|
+
aggregateDecoder: aggregateDecoder,
|
|
342
|
+
givenEvents: givenEvents,
|
|
343
|
+
whenCommand: whenCommand,
|
|
344
|
+
thenEvents: thenEvents,
|
|
345
|
+
thenEvent: thenEvent,
|
|
346
|
+
thenNoEvent: thenNoEvent,
|
|
347
|
+
thenError: thenError
|
|
348
|
+
};
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function AutomationStep(Spec) {
|
|
353
|
+
let consumedDecoder = DcbDecode$Reventless.makeDecoder(Spec.consumedEventSchema);
|
|
354
|
+
let whenReacts = async flowP => {
|
|
355
|
+
let s = await flowP;
|
|
356
|
+
let events = decodeMatching(s.log, consumedDecoder, []);
|
|
357
|
+
let collected = events.map(e => Spec.collect(e)).flat();
|
|
358
|
+
let resolvedIds = Stdlib_Array.filterMap(events, e => Spec.resolve(e));
|
|
359
|
+
let pending = collected.filter(param => !resolvedIds.includes(param[0]));
|
|
360
|
+
let commands = Stdlib_Array.filterMap(pending, param => Spec.process(param[0], param[1]));
|
|
361
|
+
return {
|
|
362
|
+
log: s.log,
|
|
363
|
+
outcome: s.outcome,
|
|
364
|
+
lastEvents: s.lastEvents,
|
|
365
|
+
lastError: s.lastError,
|
|
366
|
+
lastCommands: commands.map(param => Message$ReventlessCore.encode(param[1], Spec.commandSchema)),
|
|
367
|
+
lastPublic: s.lastPublic,
|
|
368
|
+
lastAggregateId: s.lastAggregateId
|
|
369
|
+
};
|
|
370
|
+
};
|
|
371
|
+
let thenIssuesCommands = async (flowP, expected) => {
|
|
372
|
+
let s = await flowP;
|
|
373
|
+
let expJson = expected.map(c => Message$ReventlessCore.encode(c, Spec.commandSchema));
|
|
374
|
+
let o = Primitive_object.equal(s.lastCommands, expJson) ? Outcome$ReventlessGwt.pass : Outcome$ReventlessGwt.fail({
|
|
375
|
+
TAG: "EventsMismatch",
|
|
376
|
+
expected: expJson,
|
|
377
|
+
actual: s.lastCommands
|
|
378
|
+
});
|
|
379
|
+
return recordOutcome(s, o);
|
|
380
|
+
};
|
|
381
|
+
let thenIssuesCommand = (flowP, command) => thenIssuesCommands(flowP, [command]);
|
|
382
|
+
let thenIssuesNoCommand = flowP => thenIssuesCommands(flowP, []);
|
|
383
|
+
return {
|
|
384
|
+
consumedDecoder: consumedDecoder,
|
|
385
|
+
whenReacts: whenReacts,
|
|
386
|
+
thenIssuesCommands: thenIssuesCommands,
|
|
387
|
+
thenIssuesCommand: thenIssuesCommand,
|
|
388
|
+
thenIssuesNoCommand: thenIssuesNoCommand
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function ViewStep(Spec) {
|
|
393
|
+
return Projection => {
|
|
394
|
+
let P = Projection_GWT$ReventlessGwt.Make(Spec)(Projection);
|
|
395
|
+
let consumedDecoder = DcbDecode$Reventless.makeDecoder(Spec.consumedEventSchema);
|
|
396
|
+
let encState = st => Message$ReventlessCore.encode(st, Spec.stateSchema);
|
|
397
|
+
let thenViewStates = async (flowP, id, expected) => {
|
|
398
|
+
let s = await flowP;
|
|
399
|
+
let events = decodeMatching(s.log, consumedDecoder, []);
|
|
400
|
+
let store = await P.givenEvents(events);
|
|
401
|
+
let actual = Stdlib_Option.getOr(store[id], []).map(encState);
|
|
402
|
+
let expectedJson = expected.map(encState);
|
|
403
|
+
let o = Primitive_object.equal(actual, expectedJson) ? Outcome$ReventlessGwt.pass : Outcome$ReventlessGwt.fail({
|
|
404
|
+
TAG: "StateMismatch",
|
|
405
|
+
key: id,
|
|
406
|
+
expected: expectedJson,
|
|
407
|
+
actual: actual
|
|
408
|
+
});
|
|
409
|
+
return recordOutcome(s, o);
|
|
410
|
+
};
|
|
411
|
+
let thenViewState = (flowP, id, state) => thenViewStates(flowP, id, [state]);
|
|
412
|
+
return {
|
|
413
|
+
P: P,
|
|
414
|
+
consumedDecoder: consumedDecoder,
|
|
415
|
+
encState: encState,
|
|
416
|
+
thenViewStates: thenViewStates,
|
|
417
|
+
thenViewState: thenViewState
|
|
418
|
+
};
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function OutboundStep(Spec) {
|
|
423
|
+
let consumedDecoder = DcbDecode$Reventless.makeDecoder(Spec.consumedEventSchema);
|
|
424
|
+
let encItems = arr => arr.map(param => [
|
|
425
|
+
param[0],
|
|
426
|
+
Message$ReventlessCore.encode(param[1], Spec.outboundItemSchema)
|
|
427
|
+
]);
|
|
428
|
+
let thenOutbound = async (flowP, expected) => {
|
|
429
|
+
let s = await flowP;
|
|
430
|
+
let events = decodeMatching(s.log, consumedDecoder, []);
|
|
431
|
+
let collected = events.map(e => Spec.collect(e)).flat();
|
|
432
|
+
let actual = encItems(collected);
|
|
433
|
+
let expectedJson = encItems(expected);
|
|
434
|
+
let o = Primitive_object.equal(actual, expectedJson) ? Outcome$ReventlessGwt.pass : Outcome$ReventlessGwt.fail({
|
|
435
|
+
TAG: "TodoMismatch",
|
|
436
|
+
expected: expectedJson,
|
|
437
|
+
actual: actual
|
|
438
|
+
});
|
|
439
|
+
return recordOutcome(s, o);
|
|
440
|
+
};
|
|
441
|
+
let thenOutboundNothing = flowP => thenOutbound(flowP, []);
|
|
442
|
+
return {
|
|
443
|
+
consumedDecoder: consumedDecoder,
|
|
444
|
+
encItems: encItems,
|
|
445
|
+
thenOutbound: thenOutbound,
|
|
446
|
+
thenOutboundNothing: thenOutboundNothing
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
function sortJson(arr) {
|
|
451
|
+
return arr.toSorted((a, b) => Primitive_string.compare(JSON.stringify(a), JSON.stringify(b)));
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
function ExtensionPointStep(M) {
|
|
455
|
+
let delegateDecoder = DcbDecode$Reventless.makeDecoder(M.Delegate.eventSchema);
|
|
456
|
+
let runMapping = async (sourceId, events) => {
|
|
457
|
+
let f = M.mapOutgoingEvent;
|
|
458
|
+
if (f === undefined) {
|
|
459
|
+
return [];
|
|
460
|
+
}
|
|
461
|
+
let actions = events.map(ev => f(sourceId, ev, StubRuntime$ReventlessGwt.meta, StubRuntime$ReventlessGwt.queryEngine)).flat();
|
|
462
|
+
return (await Promise.all(actions.map(async action => {
|
|
463
|
+
switch (action.TAG) {
|
|
464
|
+
case "PublishEvent" :
|
|
465
|
+
return [action._1];
|
|
466
|
+
case "PublishEventAsync" :
|
|
467
|
+
let match = await action._0;
|
|
468
|
+
return [match[1]];
|
|
469
|
+
case "HandleDirective" :
|
|
470
|
+
return [];
|
|
471
|
+
}
|
|
472
|
+
}))).flat();
|
|
473
|
+
};
|
|
474
|
+
let whenPublishedThrough = async flowP => {
|
|
475
|
+
let s = await flowP;
|
|
476
|
+
let delegateEvents = Stdlib_Array.filterMap(s.lastEvents, json => {
|
|
477
|
+
let data;
|
|
478
|
+
data = typeof json === "object" && json !== null && !Array.isArray(json) ? json : ({});
|
|
479
|
+
return delegateDecoder.decode(Message$Reventless.variantNameOfJson(json), data);
|
|
480
|
+
});
|
|
481
|
+
let sourceId = Stdlib_Option.getOr(s.lastAggregateId, "gwt-id");
|
|
482
|
+
let publicEvents = await runMapping(sourceId, delegateEvents);
|
|
483
|
+
return {
|
|
484
|
+
log: appendEvents(s.log, publicEvents, M.ExtensionPoint.eventSchema),
|
|
485
|
+
outcome: s.outcome,
|
|
486
|
+
lastEvents: s.lastEvents,
|
|
487
|
+
lastError: s.lastError,
|
|
488
|
+
lastCommands: s.lastCommands,
|
|
489
|
+
lastPublic: publicEvents.map(e => Message$ReventlessCore.encode(e, M.ExtensionPoint.eventSchema)),
|
|
490
|
+
lastAggregateId: s.lastAggregateId
|
|
491
|
+
};
|
|
492
|
+
};
|
|
493
|
+
let thenPublicEvents = async (flowP, expected) => {
|
|
494
|
+
let s = await flowP;
|
|
495
|
+
let expJson = expected.map(e => Message$ReventlessCore.encode(e, M.ExtensionPoint.eventSchema));
|
|
496
|
+
let o = Primitive_object.equal(sortJson(s.lastPublic), sortJson(expJson)) ? Outcome$ReventlessGwt.pass : Outcome$ReventlessGwt.fail({
|
|
497
|
+
TAG: "PublishedActionsMismatch",
|
|
498
|
+
expected: expJson,
|
|
499
|
+
actual: s.lastPublic
|
|
500
|
+
});
|
|
501
|
+
return recordOutcome(s, o);
|
|
502
|
+
};
|
|
503
|
+
let thenPublicEvent = (flowP, event) => thenPublicEvents(flowP, [event]);
|
|
504
|
+
return {
|
|
505
|
+
delegateDecoder: delegateDecoder,
|
|
506
|
+
runMapping: runMapping,
|
|
507
|
+
whenPublishedThrough: whenPublishedThrough,
|
|
508
|
+
thenPublicEvents: thenPublicEvents,
|
|
509
|
+
thenPublicEvent: thenPublicEvent
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
function ExtensionStep(M) {
|
|
514
|
+
let epDecoder = DcbDecode$Reventless.makeDecoder(M.ExtensionPoint.eventSchema);
|
|
515
|
+
let encCmd = c => Message$ReventlessCore.encode(c, M.Delegate.commandSchema);
|
|
516
|
+
let whenExtensionReacts = async flowP => {
|
|
517
|
+
let s = await flowP;
|
|
518
|
+
let epEvents = Stdlib_Array.filterMap(s.lastPublic, json => {
|
|
519
|
+
let data;
|
|
520
|
+
data = typeof json === "object" && json !== null && !Array.isArray(json) ? json : ({});
|
|
521
|
+
return epDecoder.decode(Message$Reventless.variantNameOfJson(json), data);
|
|
522
|
+
});
|
|
523
|
+
let actions = epEvents.map(ev => M.mapIncomingEvent("gwt-id", ev, StubRuntime$ReventlessGwt.meta, StubRuntime$ReventlessGwt.pluginDefinition, StubRuntime$ReventlessGwt.queryEngine)).flat();
|
|
524
|
+
let nested = await Promise.all(actions.map(async action => {
|
|
525
|
+
switch (action.TAG) {
|
|
526
|
+
case "PublishAggregateCommand" :
|
|
527
|
+
return [Message$ReventlessCore.encode(action._1, M.Delegate.commandSchema)];
|
|
528
|
+
case "PublishAggregateCommandAsync" :
|
|
529
|
+
let match = await action._0;
|
|
530
|
+
return [Message$ReventlessCore.encode(match[1], M.Delegate.commandSchema)];
|
|
531
|
+
case "PublishAggregateCommandsAsync" :
|
|
532
|
+
return (await action._0).map(param => Message$ReventlessCore.encode(param[1], M.Delegate.commandSchema));
|
|
533
|
+
case "PublishStateChangeSliceCommand" :
|
|
534
|
+
return [Message$ReventlessCore.encode(action._0, M.Delegate.commandSchema)];
|
|
535
|
+
case "PublishStateChangeSliceCommandAsync" :
|
|
536
|
+
let cmd = await action._0;
|
|
537
|
+
return [Message$ReventlessCore.encode(cmd, M.Delegate.commandSchema)];
|
|
538
|
+
case "PublishStateChangeSliceCommandsAsync" :
|
|
539
|
+
return (await action._0).map(encCmd);
|
|
540
|
+
default:
|
|
541
|
+
return [];
|
|
542
|
+
}
|
|
543
|
+
}));
|
|
544
|
+
return {
|
|
545
|
+
log: s.log,
|
|
546
|
+
outcome: s.outcome,
|
|
547
|
+
lastEvents: s.lastEvents,
|
|
548
|
+
lastError: s.lastError,
|
|
549
|
+
lastCommands: nested.flat(),
|
|
550
|
+
lastPublic: s.lastPublic,
|
|
551
|
+
lastAggregateId: s.lastAggregateId
|
|
552
|
+
};
|
|
553
|
+
};
|
|
554
|
+
let thenIssuesCommands = async (flowP, expected) => {
|
|
555
|
+
let s = await flowP;
|
|
556
|
+
let expJson = expected.map(encCmd);
|
|
557
|
+
let o = Primitive_object.equal(sortJson(s.lastCommands), sortJson(expJson)) ? Outcome$ReventlessGwt.pass : Outcome$ReventlessGwt.fail({
|
|
558
|
+
TAG: "PublishedActionsMismatch",
|
|
559
|
+
expected: expJson,
|
|
560
|
+
actual: s.lastCommands
|
|
561
|
+
});
|
|
562
|
+
return recordOutcome(s, o);
|
|
563
|
+
};
|
|
564
|
+
let thenIssuesCommand = (flowP, command) => thenIssuesCommands(flowP, [command]);
|
|
565
|
+
let thenIssuesNoCommand = flowP => thenIssuesCommands(flowP, []);
|
|
566
|
+
return {
|
|
567
|
+
epDecoder: epDecoder,
|
|
568
|
+
encCmd: encCmd,
|
|
569
|
+
whenExtensionReacts: whenExtensionReacts,
|
|
570
|
+
thenIssuesCommands: thenIssuesCommands,
|
|
571
|
+
thenIssuesCommand: thenIssuesCommand,
|
|
572
|
+
thenIssuesNoCommand: thenIssuesNoCommand
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
let EPMapping;
|
|
577
|
+
|
|
578
|
+
let ExtMapping;
|
|
579
|
+
|
|
580
|
+
let describe = JestBind$ReventlessGwt.describe;
|
|
581
|
+
|
|
582
|
+
export {
|
|
583
|
+
EPMapping,
|
|
584
|
+
ExtMapping,
|
|
585
|
+
emptyState,
|
|
586
|
+
start,
|
|
587
|
+
recordOutcome,
|
|
588
|
+
entryMatchesQuery,
|
|
589
|
+
appendEvents,
|
|
590
|
+
appendAggregateEvents,
|
|
591
|
+
decodeMatching,
|
|
592
|
+
decodeAggregateMatching,
|
|
593
|
+
describe,
|
|
594
|
+
test,
|
|
595
|
+
CommandStep,
|
|
596
|
+
AggregateCommandStep,
|
|
597
|
+
AutomationStep,
|
|
598
|
+
ViewStep,
|
|
599
|
+
OutboundStep,
|
|
600
|
+
sortJson,
|
|
601
|
+
ExtensionPointStep,
|
|
602
|
+
ExtensionStep,
|
|
603
|
+
}
|
|
604
|
+
/* Not a pure module */
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// Terminal-coloured human formatter. Shipped as the default output for
|
|
2
|
+
// `reventless-gwt run`. Uses picocolors for ANSI colours — it is a tiny
|
|
3
|
+
// zero-dependency helper that transparently disables colour when stdout
|
|
4
|
+
// is not a TTY.
|
|
5
|
+
|
|
6
|
+
type colour = string => string
|
|
7
|
+
type picocolors = {
|
|
8
|
+
red: colour,
|
|
9
|
+
green: colour,
|
|
10
|
+
yellow: colour,
|
|
11
|
+
cyan: colour,
|
|
12
|
+
gray: colour,
|
|
13
|
+
bold: colour,
|
|
14
|
+
dim: colour,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
@module("picocolors") external pc: picocolors = "default"
|
|
18
|
+
|
|
19
|
+
@val external processStdout: {"write": string => unit} = "process.stdout"
|
|
20
|
+
@val external processCwd: unit => string = "process.cwd"
|
|
21
|
+
@module("node:path") external pathBasename: string => string = "basename"
|
|
22
|
+
@module("node:path") external pathRelative: (string, string) => string = "relative"
|
|
23
|
+
|
|
24
|
+
let write = (s: string) => processStdout["write"](s)
|
|
25
|
+
let writeLine = (s: string) => write(s ++ "\n")
|
|
26
|
+
|
|
27
|
+
let formatFilePath = (abs: string) => {
|
|
28
|
+
let base = pathBasename(abs)
|
|
29
|
+
let name = if String.endsWith(base, ".res.mjs") {
|
|
30
|
+
String.slice(base, ~start=0, ~end=String.length(base) - 8)
|
|
31
|
+
} else {
|
|
32
|
+
base
|
|
33
|
+
}
|
|
34
|
+
let rel = pathRelative(processCwd(), abs)
|
|
35
|
+
`${name} - ${rel}`
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
let formatLocation = (loc: option<Collector.location>) =>
|
|
39
|
+
switch loc {
|
|
40
|
+
| None => ""
|
|
41
|
+
| Some(l) => `${l.file}:${Int.toString(l.line)}`
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Frames one normalized field as a terminal line. Labels are padded so values
|
|
45
|
+
// align at column 12 (`key:`, `expected:`, `actual:`); `error:` is its own
|
|
46
|
+
// shorter form and the `Throw` stack is dumped raw beneath it.
|
|
47
|
+
let renderField = (f: MismatchRender.field) =>
|
|
48
|
+
switch f {
|
|
49
|
+
| Expected(v) => ` expected: ${v}`
|
|
50
|
+
| Actual(v) => ` actual: ${v}`
|
|
51
|
+
| Key(k) => ` key: "${k}"`
|
|
52
|
+
| ExpectedNoEvents => " expected no events"
|
|
53
|
+
| Error(e) => ` error: ${e}`
|
|
54
|
+
| Stack(s) => s
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
let renderMismatch = (m: Outcome.mismatch) =>
|
|
58
|
+
MismatchRender.normalize(m).fields->Array.map(renderField)->Array.join("\n")
|
|
59
|
+
|
|
60
|
+
let emitTest = (t: RunnerTypes.testResult) => {
|
|
61
|
+
let path = t.describePath->Array.join(" > ")
|
|
62
|
+
let full = path == "" ? t.name : `${path} > ${t.name}`
|
|
63
|
+
let loc = formatLocation(t.location)
|
|
64
|
+
let locStr = loc == "" ? "" : ` ${pc.dim(loc)}`
|
|
65
|
+
switch t.status {
|
|
66
|
+
| Pass => writeLine(` ${pc.green("✓")} ${full}${locStr}`)
|
|
67
|
+
| Skip =>
|
|
68
|
+
writeLine(
|
|
69
|
+
` ${pc.yellow("○")} ${full}${locStr} ${pc.dim(
|
|
70
|
+
t.skipReason->Option.getOr("skipped"),
|
|
71
|
+
)}`,
|
|
72
|
+
)
|
|
73
|
+
| Fail => {
|
|
74
|
+
writeLine(` ${pc.red("✗")} ${pc.bold(full)}${locStr}`)
|
|
75
|
+
switch t.mismatch {
|
|
76
|
+
| Some(m) => {
|
|
77
|
+
writeLine("")
|
|
78
|
+
writeLine(renderMismatch(m))
|
|
79
|
+
let hint = Hint.forMismatch(~slice=?t.slice, m)
|
|
80
|
+
writeLine("")
|
|
81
|
+
writeLine(` ${pc.cyan(Hint.format(hint))}`)
|
|
82
|
+
writeLine("")
|
|
83
|
+
}
|
|
84
|
+
| None => ()
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
let emitFile = (f: RunnerTypes.fileResult) => {
|
|
91
|
+
writeLine("")
|
|
92
|
+
writeLine(pc.bold(formatFilePath(f.path)))
|
|
93
|
+
f.tests->Array.forEach(emitTest)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
let emitSummary = (s: RunnerTypes.summary) => {
|
|
97
|
+
writeLine("")
|
|
98
|
+
let line = `Tests: ${Int.toString(s.total)} ${pc.green(
|
|
99
|
+
Int.toString(s.passed) ++ " passed",
|
|
100
|
+
)} ${pc.red(Int.toString(s.failed) ++ " failed")} ${pc.yellow(
|
|
101
|
+
Int.toString(s.skipped) ++ " skipped",
|
|
102
|
+
)} (${Float.toString(s.durationMs)} ms)`
|
|
103
|
+
writeLine(line)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
let emit = (r: RunnerTypes.runResult) => {
|
|
107
|
+
r.files->Array.forEach(emitFile)
|
|
108
|
+
emitSummary(r.summary)
|
|
109
|
+
}
|