@reventlessdev/reventless-gwt 1.0.0-alpha.74
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 +698 -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 +603 -0
- package/src/Behavior_GWT.res.mjs +426 -0
- package/src/Bind.res +29 -0
- package/src/Bind.res.mjs +22 -0
- package/src/BuildClassifier.res +137 -0
- package/src/BuildClassifier.res.mjs +136 -0
- package/src/Cancellation.res +31 -0
- package/src/Cancellation.res.mjs +45 -0
- package/src/ChildProcess.res +84 -0
- package/src/ChildProcess.res.mjs +92 -0
- package/src/Cli.res +671 -0
- package/src/Cli.res.mjs +754 -0
- package/src/Collector.res +201 -0
- package/src/Collector.res.mjs +215 -0
- package/src/ComponentMeta.res +104 -0
- package/src/ComponentMeta.res.mjs +120 -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 +103 -0
- package/src/DomainDeadCode.res +93 -0
- package/src/DomainDeadCode.res.mjs +69 -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 +36 -0
- package/src/Filter.res.mjs +40 -0
- package/src/Flow_GWT.res +664 -0
- package/src/Flow_GWT.res.mjs +604 -0
- package/src/FormatterHuman.res +138 -0
- package/src/FormatterHuman.res.mjs +122 -0
- package/src/FormatterJson.res +257 -0
- package/src/FormatterJson.res.mjs +320 -0
- package/src/FormatterJunit.res +93 -0
- package/src/FormatterJunit.res.mjs +90 -0
- package/src/FormatterTap.res +139 -0
- package/src/FormatterTap.res.mjs +149 -0
- package/src/FormatterVsCode.res +362 -0
- package/src/FormatterVsCode.res.mjs +564 -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 +135 -0
- package/src/LocalHost.res.mjs +127 -0
- package/src/Mapping_GWT.res +474 -0
- package/src/Mapping_GWT.res.mjs +322 -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 +161 -0
- package/src/Outcome.res.mjs +140 -0
- package/src/PackageScan.res +80 -0
- package/src/PackageScan.res.mjs +81 -0
- package/src/PlatformRunner.res +160 -0
- package/src/PlatformRunner.res.mjs +155 -0
- package/src/PlatformScan.res +138 -0
- package/src/PlatformScan.res.mjs +131 -0
- package/src/ProcessManager.res +91 -0
- package/src/ProcessManager.res.mjs +80 -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/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 +53 -0
- package/src/StubRuntime.res.mjs +54 -0
- package/src/TestFixtures.res +16 -0
- package/src/TestFixtures.res.mjs +34 -0
- package/src/Watch.res +92 -0
- package/src/Watch.res.mjs +83 -0
- package/src/WatcherProbe.res +48 -0
- package/src/WatcherProbe.res.mjs +46 -0
- package/tests/AutomationGwtTest.res +71 -0
- package/tests/AutomationGwtTest.res.mjs +189 -0
- package/tests/BuildClassifierTest.res +94 -0
- package/tests/BuildClassifierTest.res.mjs +132 -0
- package/tests/ComponentMetaTest.res +52 -0
- package/tests/ComponentMetaTest.res.mjs +57 -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 +116 -0
- package/tests/DomainDeadCodeTest.res.mjs +155 -0
- package/tests/DomainGraphTest.res +365 -0
- package/tests/DomainGraphTest.res.mjs +413 -0
- package/tests/FlowAggregateGwtTest.res +169 -0
- package/tests/FlowAggregateGwtTest.res.mjs +269 -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 +432 -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 +98 -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 +348 -0
- package/tests/MappingGwtTest.res.mjs +592 -0
- package/tests/OutboundTranslationGwtTest.res +59 -0
- package/tests/OutboundTranslationGwtTest.res.mjs +138 -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 +78 -0
- package/tests/WatchDebounceTest.res.mjs +97 -0
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as S from "sury/src/S.res.mjs";
|
|
4
|
+
import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
|
|
5
|
+
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
6
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
7
|
+
import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js";
|
|
8
|
+
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
9
|
+
import * as Outcome$ReventlessGwt from "./Outcome.res.mjs";
|
|
10
|
+
import * as JestBind$ReventlessGwt from "./JestBind.res.mjs";
|
|
11
|
+
|
|
12
|
+
function FromReadModel(Spec) {
|
|
13
|
+
return {
|
|
14
|
+
name: Spec.name,
|
|
15
|
+
stateSchema: Spec.stateSchema,
|
|
16
|
+
config: Spec.config,
|
|
17
|
+
subIdConfig: Spec.subIdConfig
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function FromStateViewSlice(Spec) {
|
|
22
|
+
return {
|
|
23
|
+
name: Spec.name,
|
|
24
|
+
stateSchema: Spec.stateSchema,
|
|
25
|
+
config: Spec.config,
|
|
26
|
+
subIdConfig: Spec.subIdConfig
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function Make(Spec) {
|
|
31
|
+
S.enableJson();
|
|
32
|
+
let test = (name, body) => JestBind$ReventlessGwt.test(Spec.name, name, body);
|
|
33
|
+
let encState = s => S.reverseConvertToJsonOrThrow(s, Spec.stateSchema);
|
|
34
|
+
let givenStore = pairs => pairs.map(param => ({
|
|
35
|
+
id: param[0],
|
|
36
|
+
subId: undefined,
|
|
37
|
+
state: param[1]
|
|
38
|
+
}));
|
|
39
|
+
let givenCompositeStore = triples => triples.map(param => ({
|
|
40
|
+
id: param[0],
|
|
41
|
+
subId: param[1],
|
|
42
|
+
state: param[2]
|
|
43
|
+
}));
|
|
44
|
+
let whenQueryById = (s, id) => Stdlib_Array.findMap(s, r => {
|
|
45
|
+
if (r.id === id) {
|
|
46
|
+
return Primitive_option.some(r.state);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
let whenQueryByCompositeId = (s, param) => {
|
|
50
|
+
let subId = param.subId;
|
|
51
|
+
let id = param.id;
|
|
52
|
+
if (Spec.subIdConfig !== undefined) {
|
|
53
|
+
return {
|
|
54
|
+
TAG: "Ok",
|
|
55
|
+
_0: Stdlib_Array.findMap(s, r => {
|
|
56
|
+
if (r.id === id && Primitive_object.equal(r.subId, subId)) {
|
|
57
|
+
return Primitive_option.some(r.state);
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
};
|
|
61
|
+
} else {
|
|
62
|
+
return {
|
|
63
|
+
TAG: "Error",
|
|
64
|
+
_0: {
|
|
65
|
+
TAG: "QueryRowsMismatch",
|
|
66
|
+
expected: [`composite-id lookup requires subIdConfig = Some({subIdField, getSubId}) in ` + Spec.name + `.config`],
|
|
67
|
+
actual: []
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
let indexCovers = (indexName, field) => Spec.config.indexes.some(idx => {
|
|
73
|
+
if (idx.index !== indexName) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
if (Primitive_object.equal(idx.idField, field)) {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
let fields = idx.pkFields;
|
|
80
|
+
if (fields !== undefined) {
|
|
81
|
+
return fields.includes(field);
|
|
82
|
+
} else {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
let whenQuery = (s, args) => {
|
|
87
|
+
let name = args.index;
|
|
88
|
+
let indexError = name !== undefined && !indexCovers(name, args.by) ? ({
|
|
89
|
+
TAG: "QueryRowsMismatch",
|
|
90
|
+
expected: [`index "` + name + `" covering field "` + args.by + `" is missing from ` + Spec.name + `.config.indexes`],
|
|
91
|
+
actual: []
|
|
92
|
+
}) : undefined;
|
|
93
|
+
if (indexError !== undefined) {
|
|
94
|
+
return {
|
|
95
|
+
TAG: "Error",
|
|
96
|
+
_0: indexError
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
let field = args.by;
|
|
100
|
+
let expected = args.value;
|
|
101
|
+
let matches = s.filter(r => {
|
|
102
|
+
let json = S.reverseConvertToJsonOrThrow(r.state, Spec.stateSchema);
|
|
103
|
+
let v = Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(json), d => d[field]);
|
|
104
|
+
if (v === undefined) {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
let actualStr;
|
|
108
|
+
switch (typeof v) {
|
|
109
|
+
case "boolean" :
|
|
110
|
+
actualStr = v ? "true" : "false";
|
|
111
|
+
break;
|
|
112
|
+
case "string" :
|
|
113
|
+
actualStr = v;
|
|
114
|
+
break;
|
|
115
|
+
case "number" :
|
|
116
|
+
actualStr = v.toString();
|
|
117
|
+
break;
|
|
118
|
+
default:
|
|
119
|
+
actualStr = JSON.stringify(v);
|
|
120
|
+
}
|
|
121
|
+
return actualStr === expected;
|
|
122
|
+
});
|
|
123
|
+
let p = args.filter;
|
|
124
|
+
let afterFilter = p !== undefined ? matches.filter(r => p(r.state)) : matches;
|
|
125
|
+
let n = args.limit;
|
|
126
|
+
let afterLimit = n !== undefined ? afterFilter.slice(0, n) : afterFilter;
|
|
127
|
+
return {
|
|
128
|
+
TAG: "Ok",
|
|
129
|
+
_0: afterLimit.map(r => r.state)
|
|
130
|
+
};
|
|
131
|
+
};
|
|
132
|
+
let thenRow = (actual, expected) => {
|
|
133
|
+
if (Primitive_object.equal(actual, expected)) {
|
|
134
|
+
return Outcome$ReventlessGwt.pass;
|
|
135
|
+
}
|
|
136
|
+
let actualJson = Stdlib_Option.mapOr(actual, [], s => [S.reverseConvertToJsonOrThrow(s, Spec.stateSchema)]);
|
|
137
|
+
let expectedJson = Stdlib_Option.mapOr(expected, [], s => [S.reverseConvertToJsonOrThrow(s, Spec.stateSchema)]);
|
|
138
|
+
return Outcome$ReventlessGwt.fail({
|
|
139
|
+
TAG: "QueryRowsMismatch",
|
|
140
|
+
expected: expectedJson,
|
|
141
|
+
actual: actualJson
|
|
142
|
+
});
|
|
143
|
+
};
|
|
144
|
+
let thenRows = (result, expected) => {
|
|
145
|
+
if (result.TAG !== "Ok") {
|
|
146
|
+
return Outcome$ReventlessGwt.fail(result._0);
|
|
147
|
+
}
|
|
148
|
+
let actual = result._0;
|
|
149
|
+
if (Primitive_object.equal(actual, expected)) {
|
|
150
|
+
return Outcome$ReventlessGwt.pass;
|
|
151
|
+
} else {
|
|
152
|
+
return Outcome$ReventlessGwt.fail({
|
|
153
|
+
TAG: "QueryRowsMismatch",
|
|
154
|
+
expected: expected.map(encState),
|
|
155
|
+
actual: actual.map(encState)
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
let thenRowCount = (result, expected) => {
|
|
160
|
+
if (result.TAG !== "Ok") {
|
|
161
|
+
return Outcome$ReventlessGwt.fail(result._0);
|
|
162
|
+
}
|
|
163
|
+
let actual = result._0;
|
|
164
|
+
if (actual.length === expected) {
|
|
165
|
+
return Outcome$ReventlessGwt.pass;
|
|
166
|
+
} else {
|
|
167
|
+
return Outcome$ReventlessGwt.fail({
|
|
168
|
+
TAG: "QueryRowsMismatch",
|
|
169
|
+
expected: [`row count = ` + expected.toString()],
|
|
170
|
+
actual: [`row count = ` + actual.length.toString()]
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
let thenRowFromComposite = (result, expected) => {
|
|
175
|
+
if (result.TAG === "Ok") {
|
|
176
|
+
return thenRow(result._0, expected);
|
|
177
|
+
} else {
|
|
178
|
+
return Outcome$ReventlessGwt.fail(result._0);
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
return {
|
|
182
|
+
Spec: Spec,
|
|
183
|
+
describe: JestBind$ReventlessGwt.describe,
|
|
184
|
+
test: test,
|
|
185
|
+
givenStore: givenStore,
|
|
186
|
+
givenCompositeStore: givenCompositeStore,
|
|
187
|
+
whenQueryById: whenQueryById,
|
|
188
|
+
whenQueryByCompositeId: whenQueryByCompositeId,
|
|
189
|
+
whenQuery: whenQuery,
|
|
190
|
+
thenRow: thenRow,
|
|
191
|
+
thenRows: thenRows,
|
|
192
|
+
thenRowCount: thenRowCount,
|
|
193
|
+
thenRowFromComposite: thenRowFromComposite
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function MakeResolver(Primary) {
|
|
198
|
+
return Target => {
|
|
199
|
+
S.enableJson();
|
|
200
|
+
let sliceName = Primary.name + `→` + Target.name;
|
|
201
|
+
let test = (name, body) => JestBind$ReventlessGwt.test(sliceName, name, body);
|
|
202
|
+
let givenStores = (primary, target) => ({
|
|
203
|
+
primary: primary,
|
|
204
|
+
target: target
|
|
205
|
+
});
|
|
206
|
+
let encTarget = s => S.reverseConvertToJsonOrThrow(s, Target.stateSchema);
|
|
207
|
+
let encTargets = arr => arr.map(encTarget);
|
|
208
|
+
let resolverFor = field => Primary.config.idResolvers.find(r => r.source.idField === field);
|
|
209
|
+
let resolverManyFor = field => Primary.config.idsResolvers.find(r => r.source.idsField === field);
|
|
210
|
+
let missing = msg => ({
|
|
211
|
+
TAG: "QueryRowsMismatch",
|
|
212
|
+
expected: [msg],
|
|
213
|
+
actual: []
|
|
214
|
+
});
|
|
215
|
+
let readField = (state, field) => Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(S.reverseConvertToJsonOrThrow(state, Primary.stateSchema)), d => d[field]);
|
|
216
|
+
let jsonToStr = v => {
|
|
217
|
+
switch (typeof v) {
|
|
218
|
+
case "string" :
|
|
219
|
+
return v;
|
|
220
|
+
case "number" :
|
|
221
|
+
return v.toString();
|
|
222
|
+
default:
|
|
223
|
+
return JSON.stringify(v);
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
let lookupTarget = (target, fkId) => Stdlib_Array.findMap(target, param => {
|
|
227
|
+
if (Primitive_object.equal(param[0], fkId)) {
|
|
228
|
+
return Primitive_option.some(param[1]);
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
let whenResolve = (scenario, field, primaryId) => {
|
|
232
|
+
let match = resolverFor(field);
|
|
233
|
+
if (match === undefined) {
|
|
234
|
+
return {
|
|
235
|
+
TAG: "Error",
|
|
236
|
+
_0: {
|
|
237
|
+
TAG: "QueryRowsMismatch",
|
|
238
|
+
expected: [`@resolves on field "` + field + `" is missing from ` + Primary.name + `.config.idResolvers`],
|
|
239
|
+
actual: []
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
let pstate = Stdlib_Array.findMap(scenario.primary, param => {
|
|
244
|
+
if (param[0] === primaryId) {
|
|
245
|
+
return Primitive_option.some(param[1]);
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
if (pstate === undefined) {
|
|
249
|
+
return {
|
|
250
|
+
TAG: "Ok",
|
|
251
|
+
_0: undefined
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
let fk = readField(Primitive_option.valFromOption(pstate), field);
|
|
255
|
+
if (fk !== undefined) {
|
|
256
|
+
return {
|
|
257
|
+
TAG: "Ok",
|
|
258
|
+
_0: lookupTarget(scenario.target, jsonToStr(fk))
|
|
259
|
+
};
|
|
260
|
+
} else {
|
|
261
|
+
return {
|
|
262
|
+
TAG: "Ok",
|
|
263
|
+
_0: undefined
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
let whenResolveMany = (scenario, field, primaryId) => {
|
|
268
|
+
let match = resolverManyFor(field);
|
|
269
|
+
if (match === undefined) {
|
|
270
|
+
return {
|
|
271
|
+
TAG: "Error",
|
|
272
|
+
_0: {
|
|
273
|
+
TAG: "QueryRowsMismatch",
|
|
274
|
+
expected: [`@resolvesMany on field "` + field + `" is missing from ` + Primary.name + `.config.idsResolvers`],
|
|
275
|
+
actual: []
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
let pstate = Stdlib_Array.findMap(scenario.primary, param => {
|
|
280
|
+
if (param[0] === primaryId) {
|
|
281
|
+
return Primitive_option.some(param[1]);
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
if (pstate === undefined) {
|
|
285
|
+
return {
|
|
286
|
+
TAG: "Ok",
|
|
287
|
+
_0: []
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
let match$1 = readField(Primitive_option.valFromOption(pstate), field);
|
|
291
|
+
if (match$1 !== undefined) {
|
|
292
|
+
if (Array.isArray(match$1)) {
|
|
293
|
+
return {
|
|
294
|
+
TAG: "Ok",
|
|
295
|
+
_0: Stdlib_Array.filterMap(match$1.map(jsonToStr), fkId => lookupTarget(scenario.target, fkId))
|
|
296
|
+
};
|
|
297
|
+
} else {
|
|
298
|
+
return {
|
|
299
|
+
TAG: "Ok",
|
|
300
|
+
_0: []
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
} else {
|
|
304
|
+
return {
|
|
305
|
+
TAG: "Ok",
|
|
306
|
+
_0: []
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
};
|
|
310
|
+
let thenResolved = (result, expected) => {
|
|
311
|
+
if (result.TAG !== "Ok") {
|
|
312
|
+
return Outcome$ReventlessGwt.fail(result._0);
|
|
313
|
+
}
|
|
314
|
+
let actual = result._0;
|
|
315
|
+
if (Primitive_object.equal(actual, expected)) {
|
|
316
|
+
return Outcome$ReventlessGwt.pass;
|
|
317
|
+
} else {
|
|
318
|
+
return Outcome$ReventlessGwt.fail({
|
|
319
|
+
TAG: "QueryRowsMismatch",
|
|
320
|
+
expected: Stdlib_Option.mapOr(expected, [], s => [S.reverseConvertToJsonOrThrow(s, Target.stateSchema)]),
|
|
321
|
+
actual: Stdlib_Option.mapOr(actual, [], s => [S.reverseConvertToJsonOrThrow(s, Target.stateSchema)])
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
let thenResolvedMany = (result, expected) => {
|
|
326
|
+
if (result.TAG !== "Ok") {
|
|
327
|
+
return Outcome$ReventlessGwt.fail(result._0);
|
|
328
|
+
}
|
|
329
|
+
let actual = result._0;
|
|
330
|
+
if (Primitive_object.equal(actual, expected)) {
|
|
331
|
+
return Outcome$ReventlessGwt.pass;
|
|
332
|
+
} else {
|
|
333
|
+
return Outcome$ReventlessGwt.fail({
|
|
334
|
+
TAG: "QueryRowsMismatch",
|
|
335
|
+
expected: expected.map(encTarget),
|
|
336
|
+
actual: actual.map(encTarget)
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
return {
|
|
341
|
+
describe: JestBind$ReventlessGwt.describe,
|
|
342
|
+
sliceName: sliceName,
|
|
343
|
+
test: test,
|
|
344
|
+
givenStores: givenStores,
|
|
345
|
+
encTarget: encTarget,
|
|
346
|
+
encTargets: encTargets,
|
|
347
|
+
resolverFor: resolverFor,
|
|
348
|
+
resolverManyFor: resolverManyFor,
|
|
349
|
+
missing: missing,
|
|
350
|
+
readField: readField,
|
|
351
|
+
jsonToStr: jsonToStr,
|
|
352
|
+
lookupTarget: lookupTarget,
|
|
353
|
+
whenResolve: whenResolve,
|
|
354
|
+
whenResolveMany: whenResolveMany,
|
|
355
|
+
thenResolved: thenResolved,
|
|
356
|
+
thenResolvedMany: thenResolvedMany
|
|
357
|
+
};
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
export {
|
|
362
|
+
FromReadModel,
|
|
363
|
+
FromStateViewSlice,
|
|
364
|
+
Make,
|
|
365
|
+
MakeResolver,
|
|
366
|
+
}
|
|
367
|
+
/* S Not a pure module */
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// Renders a JSON value (produced by sury `Message.encode`) into ReScript
|
|
2
|
+
// source syntax — e.g. the JSON `{"TAG":"CategoryAdded","_0":{"name":"X"}}`
|
|
3
|
+
// becomes the string `CategoryAdded({name: "X"})`.
|
|
4
|
+
//
|
|
5
|
+
// A fully schema-driven renderer would walk `S.t<'a>` and recover every
|
|
6
|
+
// constructor name / record field type. Here we lean on sury's default JS
|
|
7
|
+
// representation instead:
|
|
8
|
+
//
|
|
9
|
+
// - Payload-less variants encode to a bare string ("VariantName").
|
|
10
|
+
// - Single-argument variants encode to `{TAG: "Name", _0: x}`.
|
|
11
|
+
// - Multi-argument variants encode to `{TAG: "Name", _0: x, _1: y, ...}`.
|
|
12
|
+
// - Inline-record variants (`Name({a, b})`) are FLATTENED by ReScript: the
|
|
13
|
+
// record fields sit beside TAG as `{TAG: "Name", a: ..., b: ...}`, NOT under
|
|
14
|
+
// `_0`. These render back as `Name({a: ..., b: ...})`.
|
|
15
|
+
// - Plain records encode to `{field: value, ...}`.
|
|
16
|
+
// - Arrays/tuples encode to JSON arrays.
|
|
17
|
+
//
|
|
18
|
+
// That's enough to produce readable failure diagnostics for every GWT DSL
|
|
19
|
+
// shipped so far. A later pass can replace this with a sury-schema walker
|
|
20
|
+
// for the last 10% of cases (abstract or sealed types, custom schemas).
|
|
21
|
+
|
|
22
|
+
let escapeString = (s: string) => {
|
|
23
|
+
let escaped =
|
|
24
|
+
s
|
|
25
|
+
->String.replaceAll("\\", "\\\\")
|
|
26
|
+
->String.replaceAll("\"", "\\\"")
|
|
27
|
+
->String.replaceAll("\n", "\\n")
|
|
28
|
+
->String.replaceAll("\r", "\\r")
|
|
29
|
+
->String.replaceAll("\t", "\\t")
|
|
30
|
+
"\"" ++ escaped ++ "\""
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let indent = (level: int) => String.repeat(" ", level)
|
|
34
|
+
|
|
35
|
+
let rec render = (~level=0, j: JSON.t): string =>
|
|
36
|
+
switch j {
|
|
37
|
+
| Null => "None"
|
|
38
|
+
| Boolean(true) => "true"
|
|
39
|
+
| Boolean(false) => "false"
|
|
40
|
+
| String(s) => escapeString(s)
|
|
41
|
+
| Number(n) => Float.toString(n)
|
|
42
|
+
| Array(arr) => renderArray(~level, arr)
|
|
43
|
+
| Object(dict) => renderObject(~level, dict)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
and renderArray = (~level, arr: array<JSON.t>) =>
|
|
47
|
+
switch arr {
|
|
48
|
+
| [] => "[]"
|
|
49
|
+
| _ =>
|
|
50
|
+
let inner = arr->Array.map(render(~level=level + 1, _))->Array.join(", ")
|
|
51
|
+
"[" ++ inner ++ "]"
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
and renderRecord = (~level, keys: array<string>, dict: Dict.t<JSON.t>) => {
|
|
55
|
+
let fields =
|
|
56
|
+
keys
|
|
57
|
+
->Array.map(k => {
|
|
58
|
+
let v = dict->Dict.getUnsafe(k)
|
|
59
|
+
k ++ ": " ++ render(~level=level + 1, v)
|
|
60
|
+
})
|
|
61
|
+
->Array.join(", ")
|
|
62
|
+
"{" ++ fields ++ "}"
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
and renderObject = (~level, dict: Dict.t<JSON.t>) => {
|
|
66
|
+
let keys = dict->Dict.keysToArray
|
|
67
|
+
switch dict->Dict.get("TAG") {
|
|
68
|
+
| Some(tagJson) =>
|
|
69
|
+
let tagName = switch tagJson {
|
|
70
|
+
| String(s) => s
|
|
71
|
+
| _ => "?"
|
|
72
|
+
}
|
|
73
|
+
// Positional args are `_0`, `_1`, …; an inline-record variant instead
|
|
74
|
+
// flattens its record fields beside TAG (no `_`-prefixed keys).
|
|
75
|
+
let positional = keys->Array.filter(k => k != "TAG" && String.startsWith(k, "_"))
|
|
76
|
+
let inlineFields = keys->Array.filter(k => k != "TAG" && !String.startsWith(k, "_"))
|
|
77
|
+
switch positional {
|
|
78
|
+
| [] =>
|
|
79
|
+
switch inlineFields {
|
|
80
|
+
| [] => tagName
|
|
81
|
+
| _ => tagName ++ "(" ++ renderRecord(~level, inlineFields, dict) ++ ")"
|
|
82
|
+
}
|
|
83
|
+
| ["_0"] =>
|
|
84
|
+
switch dict->Dict.get("_0") {
|
|
85
|
+
| Some(p) => tagName ++ "(" ++ render(~level=level + 1, p) ++ ")"
|
|
86
|
+
| None => tagName
|
|
87
|
+
}
|
|
88
|
+
| _ =>
|
|
89
|
+
let ordered = positional->Array.toSorted((a, b) => {
|
|
90
|
+
let parseTail = k =>
|
|
91
|
+
String.slice(k, ~start=1, ~end=String.length(k))
|
|
92
|
+
->Int.fromString
|
|
93
|
+
->Option.getOr(0)
|
|
94
|
+
Int.compare(parseTail(a), parseTail(b))
|
|
95
|
+
})
|
|
96
|
+
let parts =
|
|
97
|
+
ordered
|
|
98
|
+
->Array.map(k => render(~level=level + 1, dict->Dict.getUnsafe(k)))
|
|
99
|
+
->Array.join(", ")
|
|
100
|
+
tagName ++ "(" ++ parts ++ ")"
|
|
101
|
+
}
|
|
102
|
+
| None =>
|
|
103
|
+
switch keys {
|
|
104
|
+
| [] => "{}"
|
|
105
|
+
| _ => renderRecord(~level, keys, dict)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
let renderMany = (arr: array<JSON.t>): string =>
|
|
111
|
+
switch arr {
|
|
112
|
+
| [] => "[]"
|
|
113
|
+
| [one] => "[" ++ render(one) ++ "]"
|
|
114
|
+
| _ =>
|
|
115
|
+
let inner =
|
|
116
|
+
arr->Array.map(v => indent(1) ++ render(~level=1, v))->Array.join(",\n")
|
|
117
|
+
"[\n" ++ inner ++ ",\n]"
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Render an optional JSON as either a ReScript value or the sentinel "None".
|
|
121
|
+
let renderOption = (o: option<JSON.t>): string =>
|
|
122
|
+
switch o {
|
|
123
|
+
| Some(j) => render(j)
|
|
124
|
+
| None => "None"
|
|
125
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Stdlib_Int from "@rescript/runtime/lib/es6/Stdlib_Int.js";
|
|
4
|
+
import * as Primitive_int from "@rescript/runtime/lib/es6/Primitive_int.js";
|
|
5
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
6
|
+
|
|
7
|
+
function escapeString(s) {
|
|
8
|
+
let escaped = s.replaceAll("\\", "\\\\").replaceAll("\"", "\\\"").replaceAll("\n", "\\n").replaceAll("\r", "\\r").replaceAll("\t", "\\t");
|
|
9
|
+
return "\"" + escaped + "\"";
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function indent(level) {
|
|
13
|
+
return " ".repeat(level);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function render(levelOpt, j) {
|
|
17
|
+
let level = levelOpt !== undefined ? levelOpt : 0;
|
|
18
|
+
if (j === null) {
|
|
19
|
+
return "None";
|
|
20
|
+
}
|
|
21
|
+
if (Array.isArray(j)) {
|
|
22
|
+
return renderArray(level, j);
|
|
23
|
+
}
|
|
24
|
+
switch (typeof j) {
|
|
25
|
+
case "boolean" :
|
|
26
|
+
if (j) {
|
|
27
|
+
return "true";
|
|
28
|
+
} else {
|
|
29
|
+
return "false";
|
|
30
|
+
}
|
|
31
|
+
case "string" :
|
|
32
|
+
return escapeString(j);
|
|
33
|
+
case "number" :
|
|
34
|
+
return j.toString();
|
|
35
|
+
case "object" :
|
|
36
|
+
return renderObject(level, j);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function renderArray(level, arr) {
|
|
41
|
+
if (arr.length === 0) {
|
|
42
|
+
return "[]";
|
|
43
|
+
}
|
|
44
|
+
let inner = arr.map(__x => render(level + 1 | 0, __x)).join(", ");
|
|
45
|
+
return "[" + inner + "]";
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function renderRecord(level, keys, dict) {
|
|
49
|
+
let fields = keys.map(k => {
|
|
50
|
+
let v = dict[k];
|
|
51
|
+
return k + ": " + render(level + 1 | 0, v);
|
|
52
|
+
}).join(", ");
|
|
53
|
+
return "{" + fields + "}";
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function renderObject(level, dict) {
|
|
57
|
+
let keys = Object.keys(dict);
|
|
58
|
+
let tagJson = dict["TAG"];
|
|
59
|
+
if (tagJson === undefined) {
|
|
60
|
+
if (keys.length !== 0) {
|
|
61
|
+
return renderRecord(level, keys, dict);
|
|
62
|
+
} else {
|
|
63
|
+
return "{}";
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
let tagName;
|
|
67
|
+
tagName = typeof tagJson === "string" ? tagJson : "?";
|
|
68
|
+
let positional = keys.filter(k => {
|
|
69
|
+
if (k !== "TAG") {
|
|
70
|
+
return k.startsWith("_");
|
|
71
|
+
} else {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
let inlineFields = keys.filter(k => {
|
|
76
|
+
if (k !== "TAG") {
|
|
77
|
+
return !k.startsWith("_");
|
|
78
|
+
} else {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
let len = positional.length;
|
|
83
|
+
if (len !== 1) {
|
|
84
|
+
if (len === 0) {
|
|
85
|
+
if (inlineFields.length !== 0) {
|
|
86
|
+
return tagName + "(" + renderRecord(level, inlineFields, dict) + ")";
|
|
87
|
+
} else {
|
|
88
|
+
return tagName;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
} else {
|
|
92
|
+
let match = positional[0];
|
|
93
|
+
if (match === "_0") {
|
|
94
|
+
let p = dict["_0"];
|
|
95
|
+
if (p !== undefined) {
|
|
96
|
+
return tagName + "(" + render(level + 1 | 0, p) + ")";
|
|
97
|
+
} else {
|
|
98
|
+
return tagName;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
let ordered = positional.toSorted((a, b) => {
|
|
103
|
+
let parseTail = k => Stdlib_Option.getOr(Stdlib_Int.fromString(k.slice(1, k.length), undefined), 0);
|
|
104
|
+
return Primitive_int.compare(parseTail(a), parseTail(b));
|
|
105
|
+
});
|
|
106
|
+
let parts = ordered.map(k => render(level + 1 | 0, dict[k])).join(", ");
|
|
107
|
+
return tagName + "(" + parts + ")";
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function renderMany(arr) {
|
|
111
|
+
let len = arr.length;
|
|
112
|
+
if (len !== 1) {
|
|
113
|
+
if (len === 0) {
|
|
114
|
+
return "[]";
|
|
115
|
+
}
|
|
116
|
+
let inner = arr.map(v => " ".repeat(1) + render(1, v)).join(",\n");
|
|
117
|
+
return "[\n" + inner + ",\n]";
|
|
118
|
+
}
|
|
119
|
+
let one = arr[0];
|
|
120
|
+
return "[" + render(undefined, one) + "]";
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function renderOption(o) {
|
|
124
|
+
if (o !== undefined) {
|
|
125
|
+
return render(undefined, o);
|
|
126
|
+
} else {
|
|
127
|
+
return "None";
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export {
|
|
132
|
+
escapeString,
|
|
133
|
+
indent,
|
|
134
|
+
render,
|
|
135
|
+
renderArray,
|
|
136
|
+
renderRecord,
|
|
137
|
+
renderObject,
|
|
138
|
+
renderMany,
|
|
139
|
+
renderOption,
|
|
140
|
+
}
|
|
141
|
+
/* No side effect */
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// Shared data types between the runner loop and formatters.
|
|
2
|
+
|
|
3
|
+
type testStatus = Pass | Fail | Skip
|
|
4
|
+
|
|
5
|
+
type testResult = {
|
|
6
|
+
id: string,
|
|
7
|
+
name: string,
|
|
8
|
+
describePath: array<string>,
|
|
9
|
+
status: testStatus,
|
|
10
|
+
durationMs: float,
|
|
11
|
+
location: option<Collector.location>,
|
|
12
|
+
slice: option<string>,
|
|
13
|
+
mismatch: option<Outcome.mismatch>,
|
|
14
|
+
skipReason: option<string>,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
type fileResult = {
|
|
18
|
+
path: string,
|
|
19
|
+
tests: array<testResult>,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
type summary = {
|
|
23
|
+
total: int,
|
|
24
|
+
passed: int,
|
|
25
|
+
failed: int,
|
|
26
|
+
skipped: int,
|
|
27
|
+
files: int,
|
|
28
|
+
durationMs: float,
|
|
29
|
+
startedAt: string,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
type runResult = {
|
|
33
|
+
summary: summary,
|
|
34
|
+
files: array<fileResult>,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let statusString = (s: testStatus) =>
|
|
38
|
+
switch s {
|
|
39
|
+
| Pass => "pass"
|
|
40
|
+
| Fail => "fail"
|
|
41
|
+
| Skip => "skip"
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
let summaryOf = (files: array<fileResult>, durationMs: float, startedAt: string): summary => {
|
|
45
|
+
let total = ref(0)
|
|
46
|
+
let passed = ref(0)
|
|
47
|
+
let failed = ref(0)
|
|
48
|
+
let skipped = ref(0)
|
|
49
|
+
files->Array.forEach(f =>
|
|
50
|
+
f.tests->Array.forEach(t => {
|
|
51
|
+
total := total.contents + 1
|
|
52
|
+
switch t.status {
|
|
53
|
+
| Pass => passed := passed.contents + 1
|
|
54
|
+
| Fail => failed := failed.contents + 1
|
|
55
|
+
| Skip => skipped := skipped.contents + 1
|
|
56
|
+
}
|
|
57
|
+
})
|
|
58
|
+
)
|
|
59
|
+
{
|
|
60
|
+
total: total.contents,
|
|
61
|
+
passed: passed.contents,
|
|
62
|
+
failed: failed.contents,
|
|
63
|
+
skipped: skipped.contents,
|
|
64
|
+
files: files->Array.length,
|
|
65
|
+
durationMs,
|
|
66
|
+
startedAt,
|
|
67
|
+
}
|
|
68
|
+
}
|