@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,219 @@
|
|
|
1
|
+
// Runner-side self-tests. Constructs synthetic `Outcome` values and feeds
|
|
2
|
+
// them through `RenderRescript`, `Diff`, and `FormatterJson` / `FormatterTap`
|
|
3
|
+
// to assert the rendered shapes. This is the unit-test layer — CLI-level
|
|
4
|
+
// integration is covered by running `node bin/reventless-gwt.mjs run tests/`
|
|
5
|
+
// against the DSL worked-example suites.
|
|
6
|
+
|
|
7
|
+
open JestGlobals
|
|
8
|
+
|
|
9
|
+
let encodeString = (s: string): JSON.t => JSON.Encode.string(s)
|
|
10
|
+
|
|
11
|
+
let makeTaggedEvent = (~tag: string, ~payload: JSON.t): JSON.t => {
|
|
12
|
+
let d = Dict.make()
|
|
13
|
+
d->Dict.set("TAG", JSON.Encode.string(tag))
|
|
14
|
+
d->Dict.set("_0", payload)
|
|
15
|
+
JSON.Encode.object(d)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
describe("RenderRescript", () => {
|
|
19
|
+
testPromise("renders tagged variants with BuckleScript representation", async () => {
|
|
20
|
+
let payload = Dict.make()
|
|
21
|
+
payload->Dict.set("name", encodeString("Electronics"))
|
|
22
|
+
let ev = makeTaggedEvent(~tag="CategoryAdded", ~payload=JSON.Encode.object(payload))
|
|
23
|
+
let rendered = RenderRescript.render(ev)
|
|
24
|
+
expect(rendered)->toEqual(`CategoryAdded({name: "Electronics"})`)
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
testPromise("renders payload-less variants as bare strings", async () => {
|
|
28
|
+
let ev = JSON.Encode.string("CategoryArchived")
|
|
29
|
+
expect(RenderRescript.render(ev))->toEqual(`"CategoryArchived"`)
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
testPromise("renders plain records in field order", async () => {
|
|
33
|
+
let d = Dict.make()
|
|
34
|
+
d->Dict.set("id", encodeString("c1"))
|
|
35
|
+
d->Dict.set("count", JSON.Encode.int(3))
|
|
36
|
+
let rendered = RenderRescript.render(JSON.Encode.object(d))
|
|
37
|
+
expect(rendered)->toEqual(`{id: "c1", count: 3}`)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
testPromise("renderMany splits large arrays across lines", async () => {
|
|
41
|
+
let a = makeTaggedEvent(~tag="A", ~payload=JSON.Encode.object(Dict.make()))
|
|
42
|
+
let b = makeTaggedEvent(~tag="B", ~payload=JSON.Encode.object(Dict.make()))
|
|
43
|
+
let rendered = RenderRescript.renderMany([a, b])
|
|
44
|
+
expect(rendered->String.includes("A({})"))->toEqual(true)
|
|
45
|
+
expect(rendered->String.includes("B({})"))->toEqual(true)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
// Inline-record variants (`Name({...})`) are flattened by ReScript: the record
|
|
49
|
+
// fields sit beside TAG, not under `_0`. Regression for the dropped-payload bug
|
|
50
|
+
// where `ProductsNotAvailable({missing: [...]})` rendered as bare
|
|
51
|
+
// `ProductsNotAvailable`, making error diffs read `X != X`.
|
|
52
|
+
testPromise("renders inline-record variants with their flattened payload", async () => {
|
|
53
|
+
let d = Dict.make()
|
|
54
|
+
d->Dict.set("TAG", encodeString("ProductsNotAvailable"))
|
|
55
|
+
d->Dict.set("missing", JSON.Encode.array([encodeString("p1x")]))
|
|
56
|
+
let rendered = RenderRescript.render(JSON.Encode.object(d))
|
|
57
|
+
expect(rendered)->toEqual(`ProductsNotAvailable({missing: ["p1x"]})`)
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
testPromise("renders multi-field inline-record variants", async () => {
|
|
61
|
+
let d = Dict.make()
|
|
62
|
+
d->Dict.set("TAG", encodeString("Conflict"))
|
|
63
|
+
d->Dict.set("expected", JSON.Encode.int(1))
|
|
64
|
+
d->Dict.set("got", JSON.Encode.int(2))
|
|
65
|
+
let rendered = RenderRescript.render(JSON.Encode.object(d))
|
|
66
|
+
expect(rendered)->toEqual(`Conflict({expected: 1, got: 2})`)
|
|
67
|
+
})
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
describe("Diff", () => {
|
|
71
|
+
testPromise("emits one entry per differing leaf", async () => {
|
|
72
|
+
let d1 = Dict.make()
|
|
73
|
+
d1->Dict.set("name", encodeString("A"))
|
|
74
|
+
d1->Dict.set("count", JSON.Encode.int(1))
|
|
75
|
+
let d2 = Dict.make()
|
|
76
|
+
d2->Dict.set("name", encodeString("B"))
|
|
77
|
+
d2->Dict.set("count", JSON.Encode.int(1))
|
|
78
|
+
let diff = Diff.diff(JSON.Encode.object(d1), JSON.Encode.object(d2))
|
|
79
|
+
expect(diff->Array.length)->toEqual(1)
|
|
80
|
+
let first = diff->Array.getUnsafe(0)
|
|
81
|
+
expect(first.path)->toEqual("name")
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
testPromise("emits zero entries when values are identical", async () => {
|
|
85
|
+
let d = Dict.make()
|
|
86
|
+
d->Dict.set("a", JSON.Encode.int(1))
|
|
87
|
+
let left = JSON.Encode.object(d)
|
|
88
|
+
let d2 = Dict.make()
|
|
89
|
+
d2->Dict.set("a", JSON.Encode.int(1))
|
|
90
|
+
let right = JSON.Encode.object(d2)
|
|
91
|
+
expect(Diff.diff(left, right)->Array.length)->toEqual(0)
|
|
92
|
+
})
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
describe("FormatterJson.mismatchJson", () => {
|
|
96
|
+
testPromise("ErrorMismatch carries dual-rendered expected + actualEvents", async () => {
|
|
97
|
+
// Payload-less variants encode to a bare JSON string — matches sury's
|
|
98
|
+
// default representation.
|
|
99
|
+
let expectedError = JSON.Encode.string("CategoryAlreadyExists")
|
|
100
|
+
let actualEvent = {
|
|
101
|
+
let p = Dict.make()
|
|
102
|
+
p->Dict.set("categoryId", encodeString("c1"))
|
|
103
|
+
p->Dict.set("name", encodeString("X"))
|
|
104
|
+
makeTaggedEvent(~tag="CategoryAdded", ~payload=JSON.Encode.object(p))
|
|
105
|
+
}
|
|
106
|
+
let m = Outcome.ErrorMismatch({
|
|
107
|
+
expected: expectedError,
|
|
108
|
+
actual: None,
|
|
109
|
+
actualEvents: [actualEvent],
|
|
110
|
+
})
|
|
111
|
+
let j = FormatterJson.mismatchJson(m, Some("AddCategory"))
|
|
112
|
+
let str = JSON.stringify(j)
|
|
113
|
+
expect(str->String.includes(`"kind":"ErrorMismatch"`))->toEqual(true)
|
|
114
|
+
// Payload-less variant's `type` field carries the constructor name.
|
|
115
|
+
expect(str->String.includes(`"type":"CategoryAlreadyExists"`))->toEqual(true)
|
|
116
|
+
expect(
|
|
117
|
+
str->String.includes(`CategoryAdded({categoryId: \\\"c1\\\", name: \\\"X\\\"})`),
|
|
118
|
+
)->toEqual(true)
|
|
119
|
+
expect(str->String.includes(`"locus":"AddCategory.decide"`))->toEqual(true)
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
testPromise("EventsMismatch includes a fieldDiff derived from payloads", async () => {
|
|
123
|
+
let expected = {
|
|
124
|
+
let p = Dict.make()
|
|
125
|
+
p->Dict.set("name", encodeString("Electronics"))
|
|
126
|
+
[makeTaggedEvent(~tag="Added", ~payload=JSON.Encode.object(p))]
|
|
127
|
+
}
|
|
128
|
+
let actual = {
|
|
129
|
+
let p = Dict.make()
|
|
130
|
+
p->Dict.set("name", encodeString("Books"))
|
|
131
|
+
[makeTaggedEvent(~tag="Added", ~payload=JSON.Encode.object(p))]
|
|
132
|
+
}
|
|
133
|
+
let m = Outcome.EventsMismatch({expected, actual})
|
|
134
|
+
let j = FormatterJson.mismatchJson(m, None)
|
|
135
|
+
let str = JSON.stringify(j)
|
|
136
|
+
expect(str->String.includes(`"fieldDiff"`))->toEqual(true)
|
|
137
|
+
expect(str->String.includes(`"path":"0._0.name"`))->toEqual(true)
|
|
138
|
+
})
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
describe("Cli.parseArgv", () => {
|
|
142
|
+
testPromise("parses run subcommand with --format=json --filter=Foo", async () => {
|
|
143
|
+
let argv = [
|
|
144
|
+
"/bin/node",
|
|
145
|
+
"/path/to/bin",
|
|
146
|
+
"run",
|
|
147
|
+
"--format=json",
|
|
148
|
+
"--filter=Foo",
|
|
149
|
+
"tests/",
|
|
150
|
+
]
|
|
151
|
+
switch Cli.parseArgv(argv) {
|
|
152
|
+
| Ok(opts) => {
|
|
153
|
+
expect(opts.subcommand == Run)->toEqual(true)
|
|
154
|
+
expect(opts.format == Json)->toEqual(true)
|
|
155
|
+
expect(opts.filters)->toEqual(["Foo"])
|
|
156
|
+
expect(opts.roots)->toEqual(["tests/"])
|
|
157
|
+
}
|
|
158
|
+
| Error(msg) => JsError.throwWithMessage("expected Ok, got: " ++ msg)
|
|
159
|
+
}
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
testPromise("discover subcommand defaults to vscode format", async () => {
|
|
163
|
+
let argv = ["/bin/node", "/path/to/bin", "discover"]
|
|
164
|
+
switch Cli.parseArgv(argv) {
|
|
165
|
+
| Ok(opts) => {
|
|
166
|
+
expect(opts.subcommand == Discover)->toEqual(true)
|
|
167
|
+
expect(opts.format == VsCode)->toEqual(true)
|
|
168
|
+
}
|
|
169
|
+
| Error(msg) => JsError.throwWithMessage("expected Ok, got: " ++ msg)
|
|
170
|
+
}
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
testPromise("unknown flag reports an error", async () => {
|
|
174
|
+
let argv = ["/bin/node", "/path/to/bin", "run", "--nonsense"]
|
|
175
|
+
switch Cli.parseArgv(argv) {
|
|
176
|
+
| Ok(_) => JsError.throwWithMessage("expected Error")
|
|
177
|
+
| Error(msg) => expect(msg->String.includes("Unknown flag"))->toEqual(true)
|
|
178
|
+
}
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
testPromise("no path argument defaults to the cwd subtree (auto-discovery)", async () => {
|
|
182
|
+
let argv = ["/bin/node", "/path/to/bin", "watch", "--format=vscode"]
|
|
183
|
+
switch Cli.parseArgv(argv) {
|
|
184
|
+
| Ok(opts) => {
|
|
185
|
+
expect(opts.subcommand == Watch)->toEqual(true)
|
|
186
|
+
expect(opts.roots)->toEqual(["."])
|
|
187
|
+
}
|
|
188
|
+
| Error(msg) => JsError.throwWithMessage("expected Ok, got: " ++ msg)
|
|
189
|
+
}
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
testPromise("platform backend defaults to memory; --backend overrides", async () => {
|
|
193
|
+
switch Cli.parseArgv(["/bin/node", "/path/to/bin", "platform", "--format=vscode"]) {
|
|
194
|
+
| Ok(opts) => {
|
|
195
|
+
expect(opts.subcommand == Platform)->toEqual(true)
|
|
196
|
+
expect(opts.backend)->toEqual("memory")
|
|
197
|
+
}
|
|
198
|
+
| Error(msg) => JsError.throwWithMessage("expected Ok, got: " ++ msg)
|
|
199
|
+
}
|
|
200
|
+
switch Cli.parseArgv(["/bin/node", "/path/to/bin", "platform", "--backend=sqlite:./db?reset"]) {
|
|
201
|
+
| Ok(opts) => expect(opts.backend)->toEqual("sqlite:./db?reset")
|
|
202
|
+
| Error(msg) => JsError.throwWithMessage("expected Ok, got: " ++ msg)
|
|
203
|
+
}
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
testPromise("platform --ui-ports defaults false; flag sets it true", async () => {
|
|
207
|
+
switch Cli.parseArgv(["/bin/node", "/path/to/bin", "platform", "--format=vscode"]) {
|
|
208
|
+
| Ok(opts) => expect(opts.uiPorts)->toEqual(false)
|
|
209
|
+
| Error(msg) => JsError.throwWithMessage("expected Ok, got: " ++ msg)
|
|
210
|
+
}
|
|
211
|
+
switch Cli.parseArgv(["/bin/node", "/path/to/bin", "platform", "--ui-ports"]) {
|
|
212
|
+
| Ok(opts) => {
|
|
213
|
+
expect(opts.subcommand == Platform)->toEqual(true)
|
|
214
|
+
expect(opts.uiPorts)->toEqual(true)
|
|
215
|
+
}
|
|
216
|
+
| Error(msg) => JsError.throwWithMessage("expected Ok, got: " ++ msg)
|
|
217
|
+
}
|
|
218
|
+
})
|
|
219
|
+
})
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
4
|
+
import * as Cli$ReventlessGwt from "../src/Cli.res.mjs";
|
|
5
|
+
import * as Diff$ReventlessGwt from "../src/Diff.res.mjs";
|
|
6
|
+
import * as FormatterJson$ReventlessGwt from "../src/FormatterJson.res.mjs";
|
|
7
|
+
import * as RenderRescript$ReventlessGwt from "../src/RenderRescript.res.mjs";
|
|
8
|
+
|
|
9
|
+
function encodeString(s) {
|
|
10
|
+
return s;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function makeTaggedEvent(tag, payload) {
|
|
14
|
+
let d = {};
|
|
15
|
+
d["TAG"] = tag;
|
|
16
|
+
d["_0"] = payload;
|
|
17
|
+
return d;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
globalThis.describe("RenderRescript", () => {
|
|
21
|
+
globalThis.test("renders tagged variants with BuckleScript representation", async () => {
|
|
22
|
+
let payload = {};
|
|
23
|
+
payload["name"] = "Electronics";
|
|
24
|
+
let ev = makeTaggedEvent("CategoryAdded", payload);
|
|
25
|
+
let rendered = RenderRescript$ReventlessGwt.render(undefined, ev);
|
|
26
|
+
globalThis.expect(rendered).toEqual(`CategoryAdded({name: "Electronics"})`);
|
|
27
|
+
});
|
|
28
|
+
globalThis.test("renders payload-less variants as bare strings", async () => {
|
|
29
|
+
globalThis.expect(RenderRescript$ReventlessGwt.render(undefined, "CategoryArchived")).toEqual(`"CategoryArchived"`);
|
|
30
|
+
});
|
|
31
|
+
globalThis.test("renders plain records in field order", async () => {
|
|
32
|
+
let d = {};
|
|
33
|
+
d["id"] = "c1";
|
|
34
|
+
d["count"] = 3;
|
|
35
|
+
let rendered = RenderRescript$ReventlessGwt.render(undefined, d);
|
|
36
|
+
globalThis.expect(rendered).toEqual(`{id: "c1", count: 3}`);
|
|
37
|
+
});
|
|
38
|
+
globalThis.test("renderMany splits large arrays across lines", async () => {
|
|
39
|
+
let a = makeTaggedEvent("A", {});
|
|
40
|
+
let b = makeTaggedEvent("B", {});
|
|
41
|
+
let rendered = RenderRescript$ReventlessGwt.renderMany([
|
|
42
|
+
a,
|
|
43
|
+
b
|
|
44
|
+
]);
|
|
45
|
+
globalThis.expect(rendered.includes("A({})")).toEqual(true);
|
|
46
|
+
globalThis.expect(rendered.includes("B({})")).toEqual(true);
|
|
47
|
+
});
|
|
48
|
+
globalThis.test("renders inline-record variants with their flattened payload", async () => {
|
|
49
|
+
let d = {};
|
|
50
|
+
d["TAG"] = "ProductsNotAvailable";
|
|
51
|
+
d["missing"] = ["p1x"];
|
|
52
|
+
let rendered = RenderRescript$ReventlessGwt.render(undefined, d);
|
|
53
|
+
globalThis.expect(rendered).toEqual(`ProductsNotAvailable({missing: ["p1x"]})`);
|
|
54
|
+
});
|
|
55
|
+
globalThis.test("renders multi-field inline-record variants", async () => {
|
|
56
|
+
let d = {};
|
|
57
|
+
d["TAG"] = "Conflict";
|
|
58
|
+
d["expected"] = 1;
|
|
59
|
+
d["got"] = 2;
|
|
60
|
+
let rendered = RenderRescript$ReventlessGwt.render(undefined, d);
|
|
61
|
+
globalThis.expect(rendered).toEqual(`Conflict({expected: 1, got: 2})`);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
globalThis.describe("Diff", () => {
|
|
66
|
+
globalThis.test("emits one entry per differing leaf", async () => {
|
|
67
|
+
let d1 = {};
|
|
68
|
+
d1["name"] = "A";
|
|
69
|
+
d1["count"] = 1;
|
|
70
|
+
let d2 = {};
|
|
71
|
+
d2["name"] = "B";
|
|
72
|
+
d2["count"] = 1;
|
|
73
|
+
let diff = Diff$ReventlessGwt.diff(d1, d2);
|
|
74
|
+
globalThis.expect(diff.length).toEqual(1);
|
|
75
|
+
let first = diff[0];
|
|
76
|
+
globalThis.expect(first.path).toEqual("name");
|
|
77
|
+
});
|
|
78
|
+
globalThis.test("emits zero entries when values are identical", async () => {
|
|
79
|
+
let d = {};
|
|
80
|
+
d["a"] = 1;
|
|
81
|
+
let d2 = {};
|
|
82
|
+
d2["a"] = 1;
|
|
83
|
+
globalThis.expect(Diff$ReventlessGwt.diff(d, d2).length).toEqual(0);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
globalThis.describe("FormatterJson.mismatchJson", () => {
|
|
88
|
+
globalThis.test("ErrorMismatch carries dual-rendered expected + actualEvents", async () => {
|
|
89
|
+
let p = {};
|
|
90
|
+
p["categoryId"] = "c1";
|
|
91
|
+
p["name"] = "X";
|
|
92
|
+
let actualEvent = makeTaggedEvent("CategoryAdded", p);
|
|
93
|
+
let m_2 = [actualEvent];
|
|
94
|
+
let m = {
|
|
95
|
+
TAG: "ErrorMismatch",
|
|
96
|
+
expected: "CategoryAlreadyExists",
|
|
97
|
+
actual: undefined,
|
|
98
|
+
actualEvents: m_2
|
|
99
|
+
};
|
|
100
|
+
let j = FormatterJson$ReventlessGwt.mismatchJson(m, "AddCategory");
|
|
101
|
+
let str = JSON.stringify(j);
|
|
102
|
+
globalThis.expect(str.includes(`"kind":"ErrorMismatch"`)).toEqual(true);
|
|
103
|
+
globalThis.expect(str.includes(`"type":"CategoryAlreadyExists"`)).toEqual(true);
|
|
104
|
+
globalThis.expect(str.includes(`CategoryAdded({categoryId: \\\"c1\\\", name: \\\"X\\\"})`)).toEqual(true);
|
|
105
|
+
globalThis.expect(str.includes(`"locus":"AddCategory.decide"`)).toEqual(true);
|
|
106
|
+
});
|
|
107
|
+
globalThis.test("EventsMismatch includes a fieldDiff derived from payloads", async () => {
|
|
108
|
+
let p = {};
|
|
109
|
+
p["name"] = "Electronics";
|
|
110
|
+
let expected = [makeTaggedEvent("Added", p)];
|
|
111
|
+
let p$1 = {};
|
|
112
|
+
p$1["name"] = "Books";
|
|
113
|
+
let actual = [makeTaggedEvent("Added", p$1)];
|
|
114
|
+
let m = {
|
|
115
|
+
TAG: "EventsMismatch",
|
|
116
|
+
expected: expected,
|
|
117
|
+
actual: actual
|
|
118
|
+
};
|
|
119
|
+
let j = FormatterJson$ReventlessGwt.mismatchJson(m, undefined);
|
|
120
|
+
let str = JSON.stringify(j);
|
|
121
|
+
globalThis.expect(str.includes(`"fieldDiff"`)).toEqual(true);
|
|
122
|
+
globalThis.expect(str.includes(`"path":"0._0.name"`)).toEqual(true);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
globalThis.describe("Cli.parseArgv", () => {
|
|
127
|
+
globalThis.test("parses run subcommand with --format=json --filter=Foo", async () => {
|
|
128
|
+
let argv = [
|
|
129
|
+
"/bin/node",
|
|
130
|
+
"/path/to/bin",
|
|
131
|
+
"run",
|
|
132
|
+
"--format=json",
|
|
133
|
+
"--filter=Foo",
|
|
134
|
+
"tests/"
|
|
135
|
+
];
|
|
136
|
+
let opts = Cli$ReventlessGwt.parseArgv(argv);
|
|
137
|
+
if (opts.TAG !== "Ok") {
|
|
138
|
+
return Stdlib_JsError.throwWithMessage("expected Ok, got: " + opts._0);
|
|
139
|
+
}
|
|
140
|
+
let opts$1 = opts._0;
|
|
141
|
+
globalThis.expect(opts$1.subcommand === "Run").toEqual(true);
|
|
142
|
+
globalThis.expect(opts$1.format === "Json").toEqual(true);
|
|
143
|
+
globalThis.expect(opts$1.filters).toEqual(["Foo"]);
|
|
144
|
+
globalThis.expect(opts$1.roots).toEqual(["tests/"]);
|
|
145
|
+
});
|
|
146
|
+
globalThis.test("discover subcommand defaults to vscode format", async () => {
|
|
147
|
+
let argv = [
|
|
148
|
+
"/bin/node",
|
|
149
|
+
"/path/to/bin",
|
|
150
|
+
"discover"
|
|
151
|
+
];
|
|
152
|
+
let opts = Cli$ReventlessGwt.parseArgv(argv);
|
|
153
|
+
if (opts.TAG !== "Ok") {
|
|
154
|
+
return Stdlib_JsError.throwWithMessage("expected Ok, got: " + opts._0);
|
|
155
|
+
}
|
|
156
|
+
let opts$1 = opts._0;
|
|
157
|
+
globalThis.expect(opts$1.subcommand === "Discover").toEqual(true);
|
|
158
|
+
globalThis.expect(opts$1.format === "VsCode").toEqual(true);
|
|
159
|
+
});
|
|
160
|
+
globalThis.test("unknown flag reports an error", async () => {
|
|
161
|
+
let argv = [
|
|
162
|
+
"/bin/node",
|
|
163
|
+
"/path/to/bin",
|
|
164
|
+
"run",
|
|
165
|
+
"--nonsense"
|
|
166
|
+
];
|
|
167
|
+
let msg = Cli$ReventlessGwt.parseArgv(argv);
|
|
168
|
+
if (msg.TAG === "Ok") {
|
|
169
|
+
return Stdlib_JsError.throwWithMessage("expected Error");
|
|
170
|
+
}
|
|
171
|
+
globalThis.expect(msg._0.includes("Unknown flag")).toEqual(true);
|
|
172
|
+
});
|
|
173
|
+
globalThis.test("no path argument defaults to the cwd subtree (auto-discovery)", async () => {
|
|
174
|
+
let argv = [
|
|
175
|
+
"/bin/node",
|
|
176
|
+
"/path/to/bin",
|
|
177
|
+
"watch",
|
|
178
|
+
"--format=vscode"
|
|
179
|
+
];
|
|
180
|
+
let opts = Cli$ReventlessGwt.parseArgv(argv);
|
|
181
|
+
if (opts.TAG !== "Ok") {
|
|
182
|
+
return Stdlib_JsError.throwWithMessage("expected Ok, got: " + opts._0);
|
|
183
|
+
}
|
|
184
|
+
let opts$1 = opts._0;
|
|
185
|
+
globalThis.expect(opts$1.subcommand === "Watch").toEqual(true);
|
|
186
|
+
globalThis.expect(opts$1.roots).toEqual(["."]);
|
|
187
|
+
});
|
|
188
|
+
globalThis.test("platform backend defaults to memory; --backend overrides", async () => {
|
|
189
|
+
let opts = Cli$ReventlessGwt.parseArgv([
|
|
190
|
+
"/bin/node",
|
|
191
|
+
"/path/to/bin",
|
|
192
|
+
"platform",
|
|
193
|
+
"--format=vscode"
|
|
194
|
+
]);
|
|
195
|
+
if (opts.TAG === "Ok") {
|
|
196
|
+
let opts$1 = opts._0;
|
|
197
|
+
globalThis.expect(opts$1.subcommand === "Platform").toEqual(true);
|
|
198
|
+
globalThis.expect(opts$1.backend).toEqual("memory");
|
|
199
|
+
} else {
|
|
200
|
+
Stdlib_JsError.throwWithMessage("expected Ok, got: " + opts._0);
|
|
201
|
+
}
|
|
202
|
+
let opts$2 = Cli$ReventlessGwt.parseArgv([
|
|
203
|
+
"/bin/node",
|
|
204
|
+
"/path/to/bin",
|
|
205
|
+
"platform",
|
|
206
|
+
"--backend=sqlite:./db?reset"
|
|
207
|
+
]);
|
|
208
|
+
if (opts$2.TAG !== "Ok") {
|
|
209
|
+
return Stdlib_JsError.throwWithMessage("expected Ok, got: " + opts$2._0);
|
|
210
|
+
}
|
|
211
|
+
globalThis.expect(opts$2._0.backend).toEqual("sqlite:./db?reset");
|
|
212
|
+
});
|
|
213
|
+
globalThis.test("platform --ui-ports defaults false; flag sets it true", async () => {
|
|
214
|
+
let opts = Cli$ReventlessGwt.parseArgv([
|
|
215
|
+
"/bin/node",
|
|
216
|
+
"/path/to/bin",
|
|
217
|
+
"platform",
|
|
218
|
+
"--format=vscode"
|
|
219
|
+
]);
|
|
220
|
+
if (opts.TAG === "Ok") {
|
|
221
|
+
globalThis.expect(opts._0.uiPorts).toEqual(false);
|
|
222
|
+
} else {
|
|
223
|
+
Stdlib_JsError.throwWithMessage("expected Ok, got: " + opts._0);
|
|
224
|
+
}
|
|
225
|
+
let opts$1 = Cli$ReventlessGwt.parseArgv([
|
|
226
|
+
"/bin/node",
|
|
227
|
+
"/path/to/bin",
|
|
228
|
+
"platform",
|
|
229
|
+
"--ui-ports"
|
|
230
|
+
]);
|
|
231
|
+
if (opts$1.TAG !== "Ok") {
|
|
232
|
+
return Stdlib_JsError.throwWithMessage("expected Ok, got: " + opts$1._0);
|
|
233
|
+
}
|
|
234
|
+
let opts$2 = opts$1._0;
|
|
235
|
+
globalThis.expect(opts$2.subcommand === "Platform").toEqual(true);
|
|
236
|
+
globalThis.expect(opts$2.uiPorts).toEqual(true);
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
export {
|
|
241
|
+
encodeString,
|
|
242
|
+
makeTaggedEvent,
|
|
243
|
+
}
|
|
244
|
+
/* Not a pure module */
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Fixture: a minimal `SideEffect.T` that calls `MockEmail` on `Placed`.
|
|
2
|
+
// Mirrors the aggregate example pattern (`Order_EmailNotification` calling
|
|
3
|
+
// `EmailService.sendOrderConfirmation`) but in-package so the framework's own
|
|
4
|
+
// test suite can exercise it without depending on the example plugin.
|
|
5
|
+
|
|
6
|
+
module Source = {
|
|
7
|
+
let name = "FakeOrder"
|
|
8
|
+
module Id = Reventless.Id.String
|
|
9
|
+
|
|
10
|
+
@schema
|
|
11
|
+
type event =
|
|
12
|
+
| Placed({email: string})
|
|
13
|
+
| Shipped
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let moduleUrl = "test://FakeOrderNotification"
|
|
17
|
+
|
|
18
|
+
let execute = async (id, _meta, event, _queryEngine) =>
|
|
19
|
+
switch event {
|
|
20
|
+
| Source.Placed({email}) =>
|
|
21
|
+
MockEmail.recordSend(~email, ~orderId=Source.Id.toString(id))
|
|
22
|
+
| Source.Shipped => ()
|
|
23
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as S from "sury/src/S.res.mjs";
|
|
4
|
+
import * as Id$Reventless from "@reventlessdev/reventless-spec/src/types/Id.res.mjs";
|
|
5
|
+
import * as MockEmail$ReventlessGwt from "./MockEmail.res.mjs";
|
|
6
|
+
|
|
7
|
+
let eventSchema = S.union([
|
|
8
|
+
S.schema(s => ({
|
|
9
|
+
TAG: "Placed",
|
|
10
|
+
email: s.m(S.string)
|
|
11
|
+
})),
|
|
12
|
+
S.literal("Shipped")
|
|
13
|
+
]);
|
|
14
|
+
|
|
15
|
+
let Source = {
|
|
16
|
+
name: "FakeOrder",
|
|
17
|
+
Id: undefined,
|
|
18
|
+
eventSchema: eventSchema
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
async function execute(id, _meta, event, _queryEngine) {
|
|
22
|
+
if (typeof event !== "object") {
|
|
23
|
+
return;
|
|
24
|
+
} else {
|
|
25
|
+
return MockEmail$ReventlessGwt.recordSend(event.email, Id$Reventless.$$String.toString(id));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let moduleUrl = "test://FakeOrderNotification";
|
|
30
|
+
|
|
31
|
+
export {
|
|
32
|
+
Source,
|
|
33
|
+
moduleUrl,
|
|
34
|
+
execute,
|
|
35
|
+
}
|
|
36
|
+
/* eventSchema Not a pure module */
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Smoke test for SideEffect_GWT. Folder `SideEffect/` triggers the PPX kind
|
|
2
|
+
// inference; filename `FakeOrderNotification_GWT.res` resolves the SE under
|
|
3
|
+
// test to the sibling `FakeOrderNotification.res` (a `SideEffect.T`).
|
|
4
|
+
|
|
5
|
+
@@reventless.gwt
|
|
6
|
+
|
|
7
|
+
describe("FakeOrderNotification SideEffect", () => {
|
|
8
|
+
JestGlobals.beforeEach(MockEmail.reset)
|
|
9
|
+
|
|
10
|
+
test("Placed records a confirmation send", () =>
|
|
11
|
+
givenEventForId(
|
|
12
|
+
Source.Id.makeFromString("o1"),
|
|
13
|
+
Source.Placed({email: "alice@example.com"}),
|
|
14
|
+
)
|
|
15
|
+
->whenExecuted(MockEmail.mock)
|
|
16
|
+
->thenExternalCalls([
|
|
17
|
+
MockEmail.SendConfirmation({email: "alice@example.com", orderId: "o1"}),
|
|
18
|
+
])
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
test("Shipped is a no-op", () =>
|
|
22
|
+
givenEvent(Source.Shipped)
|
|
23
|
+
->whenExecuted(MockEmail.mock)
|
|
24
|
+
->thenNoExternalCalls
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
test("Two distinct Placed events record two calls", async () => {
|
|
28
|
+
let _ =
|
|
29
|
+
await givenEventForId(Source.Id.makeFromString("o1"), Source.Placed({email: "a@a"}))
|
|
30
|
+
->whenExecuted(MockEmail.mock)
|
|
31
|
+
await givenEventForId(Source.Id.makeFromString("o2"), Source.Placed({email: "b@b"}))
|
|
32
|
+
->whenExecuted(MockEmail.mock)
|
|
33
|
+
->thenExternalCallCount(2)
|
|
34
|
+
})
|
|
35
|
+
})
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Id$Reventless from "@reventlessdev/reventless-spec/src/types/Id.res.mjs";
|
|
4
|
+
import * as MockEmail$ReventlessGwt from "./MockEmail.res.mjs";
|
|
5
|
+
import * as SideEffect_GWT$ReventlessGwt from "../../src/SideEffect_GWT.res.mjs";
|
|
6
|
+
import * as FakeOrderNotification$ReventlessGwt from "./FakeOrderNotification.res.mjs";
|
|
7
|
+
|
|
8
|
+
let include = SideEffect_GWT$ReventlessGwt.Make({
|
|
9
|
+
Source: {
|
|
10
|
+
name: FakeOrderNotification$ReventlessGwt.Source.name,
|
|
11
|
+
Id: Id$Reventless.$$String,
|
|
12
|
+
eventSchema: FakeOrderNotification$ReventlessGwt.Source.eventSchema
|
|
13
|
+
},
|
|
14
|
+
moduleUrl: FakeOrderNotification$ReventlessGwt.moduleUrl,
|
|
15
|
+
execute: FakeOrderNotification$ReventlessGwt.execute
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
let describe = include.describe;
|
|
19
|
+
|
|
20
|
+
let test = include.test;
|
|
21
|
+
|
|
22
|
+
let givenEvent = include.givenEvent;
|
|
23
|
+
|
|
24
|
+
let givenEventForId = include.givenEventForId;
|
|
25
|
+
|
|
26
|
+
let whenExecuted = include.whenExecuted;
|
|
27
|
+
|
|
28
|
+
let thenNoExternalCalls = include.thenNoExternalCalls;
|
|
29
|
+
|
|
30
|
+
let thenExternalCalls = include.thenExternalCalls;
|
|
31
|
+
|
|
32
|
+
let thenExternalCallCount = include.thenExternalCallCount;
|
|
33
|
+
|
|
34
|
+
describe("FakeOrderNotification SideEffect", () => {
|
|
35
|
+
globalThis.beforeEach(MockEmail$ReventlessGwt.reset);
|
|
36
|
+
test("Placed records a confirmation send", undefined, () => thenExternalCalls(whenExecuted(givenEventForId(Id$Reventless.$$String.makeFromString("o1"), {
|
|
37
|
+
TAG: "Placed",
|
|
38
|
+
email: "alice@example.com"
|
|
39
|
+
}), MockEmail$ReventlessGwt.mock, undefined), [{
|
|
40
|
+
TAG: "SendConfirmation",
|
|
41
|
+
email: "alice@example.com",
|
|
42
|
+
orderId: "o1"
|
|
43
|
+
}]));
|
|
44
|
+
test("Shipped is a no-op", undefined, () => thenNoExternalCalls(whenExecuted(givenEvent("Shipped"), MockEmail$ReventlessGwt.mock, undefined)));
|
|
45
|
+
test("Two distinct Placed events record two calls", undefined, async () => {
|
|
46
|
+
await whenExecuted(givenEventForId(Id$Reventless.$$String.makeFromString("o1"), {
|
|
47
|
+
TAG: "Placed",
|
|
48
|
+
email: "a@a"
|
|
49
|
+
}), MockEmail$ReventlessGwt.mock, undefined);
|
|
50
|
+
return await thenExternalCallCount(whenExecuted(givenEventForId(Id$Reventless.$$String.makeFromString("o2"), {
|
|
51
|
+
TAG: "Placed",
|
|
52
|
+
email: "b@b"
|
|
53
|
+
}), MockEmail$ReventlessGwt.mock, undefined), 2);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
let SE = include.SE;
|
|
58
|
+
|
|
59
|
+
let givenMeta = include.givenMeta;
|
|
60
|
+
|
|
61
|
+
export {
|
|
62
|
+
SE,
|
|
63
|
+
describe,
|
|
64
|
+
test,
|
|
65
|
+
givenEvent,
|
|
66
|
+
givenEventForId,
|
|
67
|
+
givenMeta,
|
|
68
|
+
whenExecuted,
|
|
69
|
+
thenNoExternalCalls,
|
|
70
|
+
thenExternalCalls,
|
|
71
|
+
thenExternalCallCount,
|
|
72
|
+
}
|
|
73
|
+
/* include Not a pure module */
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Fixture: a mock email service that records every call into a ref. The
|
|
2
|
+
// SideEffect_GWT test reads the recorded calls back via `mock.snapshot()`
|
|
3
|
+
// and asserts against them.
|
|
4
|
+
|
|
5
|
+
@schema
|
|
6
|
+
type call = SendConfirmation({email: string, orderId: string})
|
|
7
|
+
|
|
8
|
+
let calls: ref<array<call>> = ref([])
|
|
9
|
+
|
|
10
|
+
let reset = () => calls := []
|
|
11
|
+
|
|
12
|
+
let recordSend = (~email, ~orderId) =>
|
|
13
|
+
calls := calls.contents->Array.concat([SendConfirmation({email, orderId})])
|
|
14
|
+
|
|
15
|
+
let mock: SideEffect_GWT.mock<call> = {
|
|
16
|
+
snapshot: () => calls.contents,
|
|
17
|
+
encode: c => c->S.reverseConvertToJsonOrThrow(callSchema),
|
|
18
|
+
}
|