@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,316 @@
|
|
|
1
|
+
open ReventlessCore
|
|
2
|
+
|
|
3
|
+
module type T = {
|
|
4
|
+
type sourceEvent
|
|
5
|
+
type targetState
|
|
6
|
+
|
|
7
|
+
let describe: (string, unit => unit) => unit
|
|
8
|
+
let describeWithId: (string, string, unit => unit) => unit
|
|
9
|
+
let test: (string, ~timeout: int=?, unit => promise<Outcome.outcome>) => unit
|
|
10
|
+
|
|
11
|
+
type store = dict<array<targetState>>
|
|
12
|
+
type storeThunk = unit => promise<store>
|
|
13
|
+
|
|
14
|
+
let givenEvents: array<sourceEvent> => promise<store>
|
|
15
|
+
let givenEventsWithTime: array<(string, sourceEvent)> => promise<store>
|
|
16
|
+
let whenEvent: (promise<store>, sourceEvent) => storeThunk
|
|
17
|
+
let whenEvents: (promise<store>, array<sourceEvent>) => storeThunk
|
|
18
|
+
let whenEventWithTime: (promise<store>, string, sourceEvent) => storeThunk
|
|
19
|
+
let whenEventsWithTime: (promise<store>, array<(string, sourceEvent)>) => storeThunk
|
|
20
|
+
let thenStates: (storeThunk, array<targetState>) => promise<Outcome.outcome>
|
|
21
|
+
let thenStatesWithId: (storeThunk, string, array<targetState>) => promise<Outcome.outcome>
|
|
22
|
+
let thenAllStates: (storeThunk, store) => promise<Outcome.outcome>
|
|
23
|
+
let thenState: (storeThunk, targetState) => promise<Outcome.outcome>
|
|
24
|
+
let thenStateWithId: (storeThunk, string, targetState) => promise<Outcome.outcome>
|
|
25
|
+
let thenNoState: storeThunk => promise<Outcome.outcome>
|
|
26
|
+
let thenThrow: storeThunk => promise<Outcome.outcome>
|
|
27
|
+
let thenFail: storeThunk => promise<Outcome.outcome>
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let handleActions = Projection.handleActions // create alias to avoid shadowing of same named modules
|
|
31
|
+
|
|
32
|
+
module Make = (Projection: Reventless.Projection.Mapping): (
|
|
33
|
+
T with type sourceEvent := Projection.sourceEvent and type targetState := Projection.targetState
|
|
34
|
+
) => {
|
|
35
|
+
S.enableJson()
|
|
36
|
+
|
|
37
|
+
let testId = ref(TestFixtures.id)
|
|
38
|
+
let meta = ref(TestFixtures.meta)
|
|
39
|
+
|
|
40
|
+
let describe = JestBind.describe
|
|
41
|
+
let describeWithId = (description, id, fn) => {
|
|
42
|
+
testId := id
|
|
43
|
+
JestBind.describe(description, fn)
|
|
44
|
+
}
|
|
45
|
+
let test = (name, ~timeout=?, body) =>
|
|
46
|
+
JestBind.testPromise(~slice=Projection.sourceName, name, ~timeout?, body)
|
|
47
|
+
|
|
48
|
+
type store = dict<array<Projection.targetState>>
|
|
49
|
+
type storeThunk = unit => promise<store>
|
|
50
|
+
|
|
51
|
+
let getSubId = state => Projection.subIdConfig->Option.map(({getSubId}) => state->getSubId)
|
|
52
|
+
let hasSubId = (state, subId) => state->getSubId->Option.getOrThrow == subId
|
|
53
|
+
let states = (store, id) => store->Dict.get(id)->Option.getOr([])
|
|
54
|
+
let setStates = (store, id, states) => store->Dict.set(id, states)
|
|
55
|
+
let updateState = (store, id, subId, newState) =>
|
|
56
|
+
store->states(id)->Array.map(state => state->hasSubId(subId) ? newState : state)
|
|
57
|
+
let addState = (store, id, newState) => {
|
|
58
|
+
let (updatedStates, newStates) = switch newState->getSubId {
|
|
59
|
+
| Some(subId) =>
|
|
60
|
+
store->states(id)->Array.some(state => state->hasSubId(subId))
|
|
61
|
+
? (store->updateState(id, subId, newState), [])
|
|
62
|
+
: (store->states(id), [newState])
|
|
63
|
+
| None => (store->states(id), [newState])
|
|
64
|
+
}
|
|
65
|
+
store->Dict.set(id, Array.concat(updatedStates, newStates))
|
|
66
|
+
}
|
|
67
|
+
let deleteStates = (store, id) => store->Dict.set(id, [])
|
|
68
|
+
let deleteSubState = (store, id, subId, getSubId) =>
|
|
69
|
+
store->Dict.set(
|
|
70
|
+
id,
|
|
71
|
+
store
|
|
72
|
+
->Dict.get(id)
|
|
73
|
+
->Option.map(states => states->Array.filter(state => state->getSubId != subId))
|
|
74
|
+
->Option.getOr([]),
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
let load = (store, id) => store->states(id)->Ok->Promise.resolve
|
|
78
|
+
let save = (store, id, state, saveMode: QueryDb.saveMode, _ttl) =>
|
|
79
|
+
switch (store->states(id), saveMode) {
|
|
80
|
+
| (_, Any)
|
|
81
|
+
| ([], Init)
|
|
82
|
+
| ([_], Overwrite) =>
|
|
83
|
+
store->setStates(id, [state])
|
|
84
|
+
Ok()->Promise.resolve
|
|
85
|
+
| _ => Error(ReventlessInfra.QueryDb.StaleState)->Promise.resolve
|
|
86
|
+
}
|
|
87
|
+
let saveBatch = (store, batch) => {
|
|
88
|
+
batch->Array.forEach(((id, state, _ttl)) => store->addState(id, state))
|
|
89
|
+
Ok()->Promise.resolve
|
|
90
|
+
}
|
|
91
|
+
let delete = (store, id, subId) =>
|
|
92
|
+
switch (subId, Projection.subIdConfig) {
|
|
93
|
+
| (None, _) =>
|
|
94
|
+
store->deleteStates(id)
|
|
95
|
+
Ok()->Promise.resolve
|
|
96
|
+
| (Some((_, subId)), Some({Reventless.ReadModel.getSubId: getSubId})) =>
|
|
97
|
+
store->deleteSubState(id, subId, getSubId)
|
|
98
|
+
Ok()->Promise.resolve
|
|
99
|
+
| _ => Ok()->Promise.resolve
|
|
100
|
+
}
|
|
101
|
+
let deleteBatch = (store, ids) => {
|
|
102
|
+
ids->Array.forEach(((id, subId)) =>
|
|
103
|
+
switch (subId, Projection.subIdConfig) {
|
|
104
|
+
| (None, _) => store->deleteStates(id)
|
|
105
|
+
| (Some((_, subId)), Some({Reventless.ReadModel.getSubId: getSubId})) =>
|
|
106
|
+
store->deleteSubState(id, subId, getSubId)
|
|
107
|
+
| _ => ()
|
|
108
|
+
}
|
|
109
|
+
)
|
|
110
|
+
Ok()->Promise.resolve
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
let handleActions = (actions, operations) =>
|
|
114
|
+
actions->handleActions(operations, Projection.subIdConfig)
|
|
115
|
+
|
|
116
|
+
let sortStore = store =>
|
|
117
|
+
switch Projection.subIdConfig {
|
|
118
|
+
| None => store
|
|
119
|
+
| Some(_) =>
|
|
120
|
+
Dict.mapValues(store, states =>
|
|
121
|
+
states->Array.toSorted((state1, state2) =>
|
|
122
|
+
String.compare(state1->getSubId->Option.getUnsafe, state2->getSubId->Option.getUnsafe)
|
|
123
|
+
)
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
let update = async (store, events') => {
|
|
128
|
+
await events'
|
|
129
|
+
->Array.map(event' => event'->Projection.project)
|
|
130
|
+
->handleActions({
|
|
131
|
+
load: load(store, ...),
|
|
132
|
+
loadStream: id => store->states(id)->Stream.fromIterable,
|
|
133
|
+
save: save(store, ...),
|
|
134
|
+
saveBatch: saveBatch(store, ...),
|
|
135
|
+
count: async (_, _, _) => Ok(0),
|
|
136
|
+
delete: delete(store, ...),
|
|
137
|
+
deleteBatch: deleteBatch(store, ...),
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
store->sortStore
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
let givenEvents = events =>
|
|
144
|
+
Dict.make()->update(
|
|
145
|
+
events->Array.map(event => {Message.id: testId.contents, meta: meta.contents, event}),
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
let givenEventsWithTime = events =>
|
|
149
|
+
Dict.make()->update(
|
|
150
|
+
events->Array.map(((time, event)) => {
|
|
151
|
+
Message.id: testId.contents,
|
|
152
|
+
meta: {...meta.contents, time},
|
|
153
|
+
event,
|
|
154
|
+
}),
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
let whenEvent = (store, event): storeThunk =>
|
|
158
|
+
() =>
|
|
159
|
+
(
|
|
160
|
+
async () =>
|
|
161
|
+
await (await store)->update([{Message.id: testId.contents, meta: meta.contents, event}])
|
|
162
|
+
)()
|
|
163
|
+
let whenEvents = (store, events): storeThunk =>
|
|
164
|
+
() =>
|
|
165
|
+
(
|
|
166
|
+
async () =>
|
|
167
|
+
await (await store)->update(
|
|
168
|
+
events->Array.map(event => {Message.id: testId.contents, meta: meta.contents, event}),
|
|
169
|
+
)
|
|
170
|
+
)()
|
|
171
|
+
let whenEventWithTime = (store, time, event): storeThunk =>
|
|
172
|
+
() =>
|
|
173
|
+
(
|
|
174
|
+
async () =>
|
|
175
|
+
await (await store)->update([
|
|
176
|
+
{Message.id: testId.contents, meta: {...meta.contents, time}, event},
|
|
177
|
+
])
|
|
178
|
+
)()
|
|
179
|
+
let whenEventsWithTime = (store, events): storeThunk =>
|
|
180
|
+
() =>
|
|
181
|
+
(
|
|
182
|
+
async () =>
|
|
183
|
+
await (await store)->update(
|
|
184
|
+
events->Array.map(((time, event)) => {
|
|
185
|
+
Message.id: testId.contents,
|
|
186
|
+
meta: {...meta.contents, time},
|
|
187
|
+
event,
|
|
188
|
+
}),
|
|
189
|
+
)
|
|
190
|
+
)()
|
|
191
|
+
|
|
192
|
+
let encState = (s: Projection.targetState) => s->Message.encode(Projection.targetStateSchema)
|
|
193
|
+
let encStates = (arr: array<Projection.targetState>) => arr->Array.map(encState)
|
|
194
|
+
|
|
195
|
+
// State equality via encoded JSON — handles ReScript structural comparison
|
|
196
|
+
// nuances when states contain types whose `==` is not meaningful.
|
|
197
|
+
let stateEq = (a: Projection.targetState, b: Projection.targetState) =>
|
|
198
|
+
JSON.stringify(encState(a)) == JSON.stringify(encState(b))
|
|
199
|
+
let statesEq = (a, b) =>
|
|
200
|
+
Array.length(a) == Array.length(b) &&
|
|
201
|
+
Array.zip(a, b)->Array.every(((x, y)) => stateEq(x, y))
|
|
202
|
+
let storeEq = (a: store, b: store) => {
|
|
203
|
+
let ka = a->Dict.keysToArray->Array.toSorted(String.compare)
|
|
204
|
+
let kb = b->Dict.keysToArray->Array.toSorted(String.compare)
|
|
205
|
+
ka == kb &&
|
|
206
|
+
ka->Array.every(key =>
|
|
207
|
+
statesEq(a->Dict.get(key)->Option.getOr([]), b->Dict.get(key)->Option.getOr([]))
|
|
208
|
+
)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
let encStore = (s: store) => {
|
|
212
|
+
let obj = Dict.make()
|
|
213
|
+
s
|
|
214
|
+
->Dict.toArray
|
|
215
|
+
->Array.forEach(((k, v)) => obj->Dict.set(k, JSON.Encode.array(v->encStates)))
|
|
216
|
+
JSON.Encode.object(obj)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
let thenStates = async (thunk, expectedStates) => {
|
|
220
|
+
let store = await thunk()
|
|
221
|
+
let keys = store->Dict.keysToArray
|
|
222
|
+
let actualId = keys->Array.get(0)
|
|
223
|
+
let actualStates = store->Dict.valuesToArray->Array.get(0)->Option.getOr([])
|
|
224
|
+
if (
|
|
225
|
+
keys->Array.length == 1 &&
|
|
226
|
+
actualId == Some(testId.contents) &&
|
|
227
|
+
statesEq(actualStates, expectedStates)
|
|
228
|
+
) {
|
|
229
|
+
Outcome.pass
|
|
230
|
+
} else {
|
|
231
|
+
Outcome.fail(
|
|
232
|
+
StateMismatch({
|
|
233
|
+
key: testId.contents,
|
|
234
|
+
expected: Some(JSON.Encode.array(expectedStates->encStates)),
|
|
235
|
+
actual: Some(encStore(store)),
|
|
236
|
+
}),
|
|
237
|
+
)
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
let thenStatesWithId = async (thunk, id, expectedStates) => {
|
|
242
|
+
let store = await thunk()
|
|
243
|
+
let keys = store->Dict.keysToArray
|
|
244
|
+
let actualId = keys->Array.get(0)
|
|
245
|
+
let actualStates = store->Dict.valuesToArray->Array.get(0)->Option.getOr([])
|
|
246
|
+
if keys->Array.length == 1 && actualId == Some(id) && statesEq(actualStates, expectedStates) {
|
|
247
|
+
Outcome.pass
|
|
248
|
+
} else {
|
|
249
|
+
Outcome.fail(
|
|
250
|
+
StateMismatch({
|
|
251
|
+
key: id,
|
|
252
|
+
expected: Some(JSON.Encode.array(expectedStates->encStates)),
|
|
253
|
+
actual: Some(encStore(store)),
|
|
254
|
+
}),
|
|
255
|
+
)
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
let thenAllStates = async (thunk, expectedStore: store) => {
|
|
260
|
+
let store = await thunk()
|
|
261
|
+
if storeEq(store, expectedStore) {
|
|
262
|
+
Outcome.pass
|
|
263
|
+
} else {
|
|
264
|
+
Outcome.fail(
|
|
265
|
+
StateMismatch({
|
|
266
|
+
key: "<all>",
|
|
267
|
+
expected: Some(encStore(expectedStore)),
|
|
268
|
+
actual: Some(encStore(store)),
|
|
269
|
+
}),
|
|
270
|
+
)
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
let thenState = (thunk, expectedState) => thenStates(thunk, [expectedState])
|
|
275
|
+
|
|
276
|
+
let thenStateWithId = (thunk, id, expectedState) =>
|
|
277
|
+
thenStatesWithId(thunk, id, [expectedState])
|
|
278
|
+
|
|
279
|
+
let thenNoState = async thunk => {
|
|
280
|
+
let store = await thunk()
|
|
281
|
+
let total =
|
|
282
|
+
store->Dict.valuesToArray->Array.reduce(0, (acc, states) => acc + states->Array.length)
|
|
283
|
+
if total == 0 {
|
|
284
|
+
Outcome.pass
|
|
285
|
+
} else {
|
|
286
|
+
Outcome.fail(
|
|
287
|
+
StateMismatch({
|
|
288
|
+
key: "<any>",
|
|
289
|
+
expected: None,
|
|
290
|
+
actual: Some(encStore(store)),
|
|
291
|
+
}),
|
|
292
|
+
)
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
let thenThrow = async thunk =>
|
|
297
|
+
switch await thunk() {
|
|
298
|
+
| _ =>
|
|
299
|
+
Outcome.fail(
|
|
300
|
+
Throw({
|
|
301
|
+
error: "Expected thunk to throw but it returned normally",
|
|
302
|
+
stack: "",
|
|
303
|
+
}),
|
|
304
|
+
)
|
|
305
|
+
| exception _ => Outcome.pass
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
let thenFail = async thunk =>
|
|
309
|
+
switch await thunk() {
|
|
310
|
+
| _ =>
|
|
311
|
+
Outcome.fail(
|
|
312
|
+
Throw({error: "Expected failure but thunk returned normally", stack: ""}),
|
|
313
|
+
)
|
|
314
|
+
| exception _ => Outcome.pass
|
|
315
|
+
}
|
|
316
|
+
}
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as S from "sury/src/S.res.mjs";
|
|
4
|
+
import * as Stdlib_Dict from "@rescript/runtime/lib/es6/Stdlib_Dict.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 Stream from "effect/Stream";
|
|
8
|
+
import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js";
|
|
9
|
+
import * as Primitive_string from "@rescript/runtime/lib/es6/Primitive_string.js";
|
|
10
|
+
import * as Outcome$ReventlessGwt from "./Outcome.res.mjs";
|
|
11
|
+
import * as JestBind$ReventlessGwt from "./JestBind.res.mjs";
|
|
12
|
+
import * as Message$ReventlessCore from "@reventlessdev/reventless-core/src/Message.res.mjs";
|
|
13
|
+
import * as Projection$ReventlessCore from "@reventlessdev/reventless-core/src/Projection.res.mjs";
|
|
14
|
+
import * as TestFixtures$ReventlessGwt from "./TestFixtures.res.mjs";
|
|
15
|
+
|
|
16
|
+
function Make(Projection) {
|
|
17
|
+
S.enableJson();
|
|
18
|
+
let testId = {
|
|
19
|
+
contents: TestFixtures$ReventlessGwt.id
|
|
20
|
+
};
|
|
21
|
+
let meta = {
|
|
22
|
+
contents: TestFixtures$ReventlessGwt.meta
|
|
23
|
+
};
|
|
24
|
+
let describeWithId = (description, id, fn) => {
|
|
25
|
+
testId.contents = id;
|
|
26
|
+
JestBind$ReventlessGwt.describe(description, fn);
|
|
27
|
+
};
|
|
28
|
+
let test = (name, timeout, body) => JestBind$ReventlessGwt.testPromise(Projection.sourceName, name, timeout, body);
|
|
29
|
+
let getSubId = state => Stdlib_Option.map(Projection.subIdConfig, param => param.getSubId(state));
|
|
30
|
+
let hasSubId = (state, subId) => Stdlib_Option.getOrThrow(getSubId(state), undefined) === subId;
|
|
31
|
+
let states = (store, id) => Stdlib_Option.getOr(store[id], []);
|
|
32
|
+
let setStates = (store, id, states) => {
|
|
33
|
+
store[id] = states;
|
|
34
|
+
};
|
|
35
|
+
let updateState = (store, id, subId, newState) => states(store, id).map(state => {
|
|
36
|
+
if (hasSubId(state, subId)) {
|
|
37
|
+
return newState;
|
|
38
|
+
} else {
|
|
39
|
+
return state;
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
let deleteStates = (store, id) => {
|
|
43
|
+
store[id] = [];
|
|
44
|
+
};
|
|
45
|
+
let deleteSubState = (store, id, subId, getSubId) => {
|
|
46
|
+
store[id] = Stdlib_Option.getOr(Stdlib_Option.map(store[id], states => states.filter(state => Primitive_object.notequal(getSubId(state), subId))), []);
|
|
47
|
+
};
|
|
48
|
+
let update = async (store, events$p) => {
|
|
49
|
+
let actions = events$p.map(event$p => Projection.project(event$p));
|
|
50
|
+
await Projection$ReventlessCore.handleActions(actions, {
|
|
51
|
+
load: extra => Promise.resolve({
|
|
52
|
+
TAG: "Ok",
|
|
53
|
+
_0: states(store, extra)
|
|
54
|
+
}),
|
|
55
|
+
loadStream: id => Stream.fromIterable(states(store, id)),
|
|
56
|
+
save: (extra, extra$1, extra$2, extra$3) => {
|
|
57
|
+
let match = states(store, extra);
|
|
58
|
+
let exit = 0;
|
|
59
|
+
switch (extra$2) {
|
|
60
|
+
case "Init" :
|
|
61
|
+
case "Overwrite" :
|
|
62
|
+
exit = 2;
|
|
63
|
+
break;
|
|
64
|
+
case "Any" :
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
if (exit === 2) {
|
|
68
|
+
let len = match.length;
|
|
69
|
+
if (len !== 1) {
|
|
70
|
+
if (len !== 0) {
|
|
71
|
+
return Promise.resolve({
|
|
72
|
+
TAG: "Error",
|
|
73
|
+
_0: "StaleState"
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
if (extra$2 !== "Init") {
|
|
77
|
+
return Promise.resolve({
|
|
78
|
+
TAG: "Error",
|
|
79
|
+
_0: "StaleState"
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
} else if (extra$2 === "Init") {
|
|
83
|
+
return Promise.resolve({
|
|
84
|
+
TAG: "Error",
|
|
85
|
+
_0: "StaleState"
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
setStates(store, extra, [extra$1]);
|
|
90
|
+
return Promise.resolve({
|
|
91
|
+
TAG: "Ok",
|
|
92
|
+
_0: undefined
|
|
93
|
+
});
|
|
94
|
+
},
|
|
95
|
+
saveBatch: extra => {
|
|
96
|
+
extra.forEach(param => {
|
|
97
|
+
let id = param[0];
|
|
98
|
+
let newState = param[1];
|
|
99
|
+
let subId = getSubId(newState);
|
|
100
|
+
let match = subId !== undefined ? (
|
|
101
|
+
states(store, id).some(state => hasSubId(state, subId)) ? [
|
|
102
|
+
updateState(store, id, subId, newState),
|
|
103
|
+
[]
|
|
104
|
+
] : [
|
|
105
|
+
states(store, id),
|
|
106
|
+
[newState]
|
|
107
|
+
]
|
|
108
|
+
) : [
|
|
109
|
+
states(store, id),
|
|
110
|
+
[newState]
|
|
111
|
+
];
|
|
112
|
+
store[id] = match[0].concat(match[1]);
|
|
113
|
+
});
|
|
114
|
+
return Promise.resolve({
|
|
115
|
+
TAG: "Ok",
|
|
116
|
+
_0: undefined
|
|
117
|
+
});
|
|
118
|
+
},
|
|
119
|
+
count: async (param, param$1, param$2) => ({
|
|
120
|
+
TAG: "Ok",
|
|
121
|
+
_0: 0
|
|
122
|
+
}),
|
|
123
|
+
delete: (extra, extra$1) => {
|
|
124
|
+
let match = Projection.subIdConfig;
|
|
125
|
+
if (extra$1 !== undefined) {
|
|
126
|
+
if (match !== undefined) {
|
|
127
|
+
deleteSubState(store, extra, extra$1[1], match.getSubId);
|
|
128
|
+
return Promise.resolve({
|
|
129
|
+
TAG: "Ok",
|
|
130
|
+
_0: undefined
|
|
131
|
+
});
|
|
132
|
+
} else {
|
|
133
|
+
return Promise.resolve({
|
|
134
|
+
TAG: "Ok",
|
|
135
|
+
_0: undefined
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
} else {
|
|
139
|
+
deleteStates(store, extra);
|
|
140
|
+
return Promise.resolve({
|
|
141
|
+
TAG: "Ok",
|
|
142
|
+
_0: undefined
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
deleteBatch: extra => {
|
|
147
|
+
extra.forEach(param => {
|
|
148
|
+
let subId = param[1];
|
|
149
|
+
let id = param[0];
|
|
150
|
+
let match = Projection.subIdConfig;
|
|
151
|
+
if (subId !== undefined) {
|
|
152
|
+
if (match !== undefined) {
|
|
153
|
+
return deleteSubState(store, id, subId[1], match.getSubId);
|
|
154
|
+
} else {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
} else {
|
|
158
|
+
return deleteStates(store, id);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
return Promise.resolve({
|
|
162
|
+
TAG: "Ok",
|
|
163
|
+
_0: undefined
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
}, Projection.subIdConfig);
|
|
167
|
+
if (Projection.subIdConfig !== undefined) {
|
|
168
|
+
return Stdlib_Dict.mapValues(store, states => states.toSorted((state1, state2) => Primitive_string.compare(getSubId(state1), getSubId(state2))));
|
|
169
|
+
} else {
|
|
170
|
+
return store;
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
let givenEvents = events => update({}, events.map(event => ({
|
|
174
|
+
id: testId.contents,
|
|
175
|
+
meta: meta.contents,
|
|
176
|
+
event: event
|
|
177
|
+
})));
|
|
178
|
+
let givenEventsWithTime = events => update({}, events.map(param => {
|
|
179
|
+
let newrecord = {...meta.contents};
|
|
180
|
+
return {
|
|
181
|
+
id: testId.contents,
|
|
182
|
+
meta: (newrecord.time = param[0], newrecord),
|
|
183
|
+
event: param[1]
|
|
184
|
+
};
|
|
185
|
+
}));
|
|
186
|
+
let whenEvent = (store, event) => (() => (async () => await update(await store, [{
|
|
187
|
+
id: testId.contents,
|
|
188
|
+
meta: meta.contents,
|
|
189
|
+
event: event
|
|
190
|
+
}]))());
|
|
191
|
+
let whenEvents = (store, events) => (() => (async () => await update(await store, events.map(event => ({
|
|
192
|
+
id: testId.contents,
|
|
193
|
+
meta: meta.contents,
|
|
194
|
+
event: event
|
|
195
|
+
}))))());
|
|
196
|
+
let whenEventWithTime = (store, time, event) => (() => (async () => {
|
|
197
|
+
let newrecord = {...meta.contents};
|
|
198
|
+
return await update(await store, [{
|
|
199
|
+
id: testId.contents,
|
|
200
|
+
meta: (newrecord.time = time, newrecord),
|
|
201
|
+
event: event
|
|
202
|
+
}]);
|
|
203
|
+
})());
|
|
204
|
+
let whenEventsWithTime = (store, events) => (() => (async () => await update(await store, events.map(param => {
|
|
205
|
+
let newrecord = {...meta.contents};
|
|
206
|
+
return {
|
|
207
|
+
id: testId.contents,
|
|
208
|
+
meta: (newrecord.time = param[0], newrecord),
|
|
209
|
+
event: param[1]
|
|
210
|
+
};
|
|
211
|
+
})))());
|
|
212
|
+
let encState = s => Message$ReventlessCore.encode(s, Projection.targetStateSchema);
|
|
213
|
+
let statesEq = (a, b) => {
|
|
214
|
+
if (a.length === b.length) {
|
|
215
|
+
return Stdlib_Array.zip(a, b).every(param => {
|
|
216
|
+
let a = param[0];
|
|
217
|
+
let b = param[1];
|
|
218
|
+
return JSON.stringify(Message$ReventlessCore.encode(a, Projection.targetStateSchema)) === JSON.stringify(Message$ReventlessCore.encode(b, Projection.targetStateSchema));
|
|
219
|
+
});
|
|
220
|
+
} else {
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
let storeEq = (a, b) => {
|
|
225
|
+
let ka = Object.keys(a).toSorted(Primitive_string.compare);
|
|
226
|
+
let kb = Object.keys(b).toSorted(Primitive_string.compare);
|
|
227
|
+
if (Primitive_object.equal(ka, kb)) {
|
|
228
|
+
return ka.every(key => statesEq(Stdlib_Option.getOr(a[key], []), Stdlib_Option.getOr(b[key], [])));
|
|
229
|
+
} else {
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
let encStore = s => {
|
|
234
|
+
let obj = {};
|
|
235
|
+
Object.entries(s).forEach(param => {
|
|
236
|
+
obj[param[0]] = param[1].map(encState);
|
|
237
|
+
});
|
|
238
|
+
return obj;
|
|
239
|
+
};
|
|
240
|
+
let thenStates = async (thunk, expectedStates) => {
|
|
241
|
+
let store = await thunk();
|
|
242
|
+
let keys = Object.keys(store);
|
|
243
|
+
let actualId = keys[0];
|
|
244
|
+
let actualStates = Stdlib_Option.getOr(Object.values(store)[0], []);
|
|
245
|
+
if (keys.length === 1 && Primitive_object.equal(actualId, testId.contents) && statesEq(actualStates, expectedStates)) {
|
|
246
|
+
return Outcome$ReventlessGwt.pass;
|
|
247
|
+
} else {
|
|
248
|
+
return Outcome$ReventlessGwt.fail({
|
|
249
|
+
TAG: "StateMismatch",
|
|
250
|
+
key: testId.contents,
|
|
251
|
+
expected: expectedStates.map(encState),
|
|
252
|
+
actual: encStore(store)
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
let thenStatesWithId = async (thunk, id, expectedStates) => {
|
|
257
|
+
let store = await thunk();
|
|
258
|
+
let keys = Object.keys(store);
|
|
259
|
+
let actualId = keys[0];
|
|
260
|
+
let actualStates = Stdlib_Option.getOr(Object.values(store)[0], []);
|
|
261
|
+
if (keys.length === 1 && Primitive_object.equal(actualId, id) && statesEq(actualStates, expectedStates)) {
|
|
262
|
+
return Outcome$ReventlessGwt.pass;
|
|
263
|
+
} else {
|
|
264
|
+
return Outcome$ReventlessGwt.fail({
|
|
265
|
+
TAG: "StateMismatch",
|
|
266
|
+
key: id,
|
|
267
|
+
expected: expectedStates.map(encState),
|
|
268
|
+
actual: encStore(store)
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
let thenAllStates = async (thunk, expectedStore) => {
|
|
273
|
+
let store = await thunk();
|
|
274
|
+
if (storeEq(store, expectedStore)) {
|
|
275
|
+
return Outcome$ReventlessGwt.pass;
|
|
276
|
+
} else {
|
|
277
|
+
return Outcome$ReventlessGwt.fail({
|
|
278
|
+
TAG: "StateMismatch",
|
|
279
|
+
key: "<all>",
|
|
280
|
+
expected: encStore(expectedStore),
|
|
281
|
+
actual: encStore(store)
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
let thenState = (thunk, expectedState) => thenStates(thunk, [expectedState]);
|
|
286
|
+
let thenStateWithId = (thunk, id, expectedState) => thenStatesWithId(thunk, id, [expectedState]);
|
|
287
|
+
let thenNoState = async thunk => {
|
|
288
|
+
let store = await thunk();
|
|
289
|
+
let total = Stdlib_Array.reduce(Object.values(store), 0, (acc, states) => acc + states.length | 0);
|
|
290
|
+
if (total === 0) {
|
|
291
|
+
return Outcome$ReventlessGwt.pass;
|
|
292
|
+
} else {
|
|
293
|
+
return Outcome$ReventlessGwt.fail({
|
|
294
|
+
TAG: "StateMismatch",
|
|
295
|
+
key: "<any>",
|
|
296
|
+
expected: undefined,
|
|
297
|
+
actual: encStore(store)
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
let thenThrow = async thunk => {
|
|
302
|
+
let val;
|
|
303
|
+
try {
|
|
304
|
+
val = await thunk();
|
|
305
|
+
} catch (exn) {
|
|
306
|
+
return Outcome$ReventlessGwt.pass;
|
|
307
|
+
}
|
|
308
|
+
return Outcome$ReventlessGwt.fail({
|
|
309
|
+
TAG: "Throw",
|
|
310
|
+
error: "Expected thunk to throw but it returned normally",
|
|
311
|
+
stack: ""
|
|
312
|
+
});
|
|
313
|
+
};
|
|
314
|
+
let thenFail = async thunk => {
|
|
315
|
+
let val;
|
|
316
|
+
try {
|
|
317
|
+
val = await thunk();
|
|
318
|
+
} catch (exn) {
|
|
319
|
+
return Outcome$ReventlessGwt.pass;
|
|
320
|
+
}
|
|
321
|
+
return Outcome$ReventlessGwt.fail({
|
|
322
|
+
TAG: "Throw",
|
|
323
|
+
error: "Expected failure but thunk returned normally",
|
|
324
|
+
stack: ""
|
|
325
|
+
});
|
|
326
|
+
};
|
|
327
|
+
return {
|
|
328
|
+
describe: JestBind$ReventlessGwt.describe,
|
|
329
|
+
describeWithId: describeWithId,
|
|
330
|
+
test: test,
|
|
331
|
+
givenEvents: givenEvents,
|
|
332
|
+
givenEventsWithTime: givenEventsWithTime,
|
|
333
|
+
whenEvent: whenEvent,
|
|
334
|
+
whenEvents: whenEvents,
|
|
335
|
+
whenEventWithTime: whenEventWithTime,
|
|
336
|
+
whenEventsWithTime: whenEventsWithTime,
|
|
337
|
+
thenStates: thenStates,
|
|
338
|
+
thenStatesWithId: thenStatesWithId,
|
|
339
|
+
thenAllStates: thenAllStates,
|
|
340
|
+
thenState: thenState,
|
|
341
|
+
thenStateWithId: thenStateWithId,
|
|
342
|
+
thenNoState: thenNoState,
|
|
343
|
+
thenThrow: thenThrow,
|
|
344
|
+
thenFail: thenFail
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
let handleActions = Projection$ReventlessCore.handleActions;
|
|
349
|
+
|
|
350
|
+
export {
|
|
351
|
+
handleActions,
|
|
352
|
+
Make,
|
|
353
|
+
}
|
|
354
|
+
/* S Not a pure module */
|