@reventlessdev/reventless-gwt 1.0.0-alpha.115 → 1.0.0-alpha.116
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 +11 -0
- package/package.json +5 -12
- package/rescript.json +0 -1
- package/bin/reventless-dev.mjs +0 -21
- package/src/Bind.res +0 -29
- package/src/Bind.res.mjs +0 -22
- package/src/BuildClassifier.res +0 -147
- package/src/BuildClassifier.res.mjs +0 -136
- package/src/Cancellation.res +0 -37
- package/src/Cancellation.res.mjs +0 -46
- package/src/ChildProcess.res +0 -102
- package/src/ChildProcess.res.mjs +0 -107
- package/src/Cli.res +0 -889
- package/src/Cli.res.mjs +0 -944
- package/src/Collector.res +0 -222
- package/src/Collector.res.mjs +0 -228
- package/src/ComponentMeta.res +0 -77
- package/src/ComponentMeta.res.mjs +0 -91
- package/src/ComponentScan.res +0 -101
- package/src/ComponentScan.res.mjs +0 -92
- package/src/Discovery.res +0 -102
- package/src/Discovery.res.mjs +0 -86
- package/src/DomainDeadCode.res +0 -98
- package/src/DomainDeadCode.res.mjs +0 -72
- package/src/DomainGraph.res +0 -0
- package/src/DomainGraph.res.mjs +0 -0
- package/src/ExnMessage.res +0 -14
- package/src/ExnMessage.res.mjs +0 -17
- package/src/ExnReflect.mjs +0 -24
- package/src/Filter.res +0 -45
- package/src/Filter.res.mjs +0 -47
- package/src/FormatterHuman.res +0 -109
- package/src/FormatterHuman.res.mjs +0 -115
- package/src/FormatterJson.res +0 -264
- package/src/FormatterJson.res.mjs +0 -322
- package/src/FormatterJunit.res +0 -93
- package/src/FormatterJunit.res.mjs +0 -90
- package/src/FormatterTap.res +0 -100
- package/src/FormatterTap.res.mjs +0 -131
- package/src/FormatterVsCode.res +0 -352
- package/src/FormatterVsCode.res.mjs +0 -546
- package/src/Loader.res +0 -24
- package/src/Loader.res.mjs +0 -23
- package/src/PackageScan.res +0 -80
- package/src/PackageScan.res.mjs +0 -81
- package/src/PlatformRunner.res +0 -191
- package/src/PlatformRunner.res.mjs +0 -178
- package/src/PlatformScan.res +0 -141
- package/src/PlatformScan.res.mjs +0 -124
- package/src/ProcessManager.res +0 -135
- package/src/ProcessManager.res.mjs +0 -119
- package/src/RunWorker.res +0 -24
- package/src/RunWorker.res.mjs +0 -16
- package/src/RunnerTypes.res +0 -68
- package/src/RunnerTypes.res.mjs +0 -60
- package/src/ScanIgnore.res +0 -11
- package/src/ScanIgnore.res.mjs +0 -23
- package/src/Watch.res +0 -102
- package/src/Watch.res.mjs +0 -80
- package/src/WatcherProbe.res +0 -56
- package/src/WatcherProbe.res.mjs +0 -59
- package/src/Worker.res +0 -24
- package/src/Worker.res.mjs +0 -9
- package/tests/BuildClassifierTest.res +0 -133
- package/tests/BuildClassifierTest.res.mjs +0 -176
- package/tests/CliRunEntryTest.res +0 -57
- package/tests/CliRunEntryTest.res.mjs +0 -65
- package/tests/CliWatchScopeTest.res +0 -62
- package/tests/CliWatchScopeTest.res.mjs +0 -95
- package/tests/ComponentMetaTest.res +0 -67
- package/tests/ComponentMetaTest.res.mjs +0 -71
- package/tests/DiscoveryTest.res +0 -42
- package/tests/DiscoveryTest.res.mjs +0 -43
- package/tests/DomainDeadCodeTest.res +0 -119
- package/tests/DomainDeadCodeTest.res.mjs +0 -158
- package/tests/DomainGraphTest.res +0 -448
- package/tests/DomainGraphTest.res.mjs +0 -505
- package/tests/ExnMessageTest.res +0 -42
- package/tests/ExnMessageTest.res.mjs +0 -64
- package/tests/FormatterGoldenTest.res +0 -125
- package/tests/FormatterGoldenTest.res.mjs +0 -162
- package/tests/FormatterVsCodeMessageTest.res +0 -41
- package/tests/FormatterVsCodeMessageTest.res.mjs +0 -42
- package/tests/PlatformRunnerTest.res +0 -66
- package/tests/PlatformRunnerTest.res.mjs +0 -85
- package/tests/PlatformScanTest.res +0 -61
- package/tests/PlatformScanTest.res.mjs +0 -56
- package/tests/RunnerUnitTest.res +0 -219
- package/tests/RunnerUnitTest.res.mjs +0 -244
- package/tests/WatchDebounceTest.res +0 -106
- package/tests/WatchDebounceTest.res.mjs +0 -128
package/tests/RunnerUnitTest.res
DELETED
|
@@ -1,219 +0,0 @@
|
|
|
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
|
-
})
|
|
@@ -1,244 +0,0 @@
|
|
|
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 */
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
// Unit tests for Watch's event coalescing — the part Phase 12 added so a
|
|
2
|
-
// relocation's structural `unlink` survives a debounce window that also carries
|
|
3
|
-
// the trailing `add`/`change` of the same `mv`. The debounce uses real
|
|
4
|
-
// setTimeout(120ms), so these await past the window.
|
|
5
|
-
|
|
6
|
-
open JestGlobals
|
|
7
|
-
|
|
8
|
-
@val external setTimeout: (unit => unit, int) => unit = "setTimeout"
|
|
9
|
-
let delay = (ms: int): promise<unit> =>
|
|
10
|
-
Promise.make((resolve, _reject) => setTimeout(() => resolve(), ms))
|
|
11
|
-
|
|
12
|
-
describe("Watch.isStructuralSource", () => {
|
|
13
|
-
testPromise("an unlink of a .res source is structural", async () =>
|
|
14
|
-
expect(Watch.isStructuralSource(Unlink, "/p/src/Foo/Bar.res"))->toEqual(true)
|
|
15
|
-
)
|
|
16
|
-
testPromise("a generated .res.mjs unlink is NOT structural (ends in .mjs)", async () =>
|
|
17
|
-
expect(Watch.isStructuralSource(Unlink, "/p/src/Foo/Bar.res.mjs"))->toEqual(false)
|
|
18
|
-
)
|
|
19
|
-
testPromise("an add of a .res source is not structural (incremental handles it)", async () =>
|
|
20
|
-
expect(Watch.isStructuralSource(Add, "/p/src/Foo/Bar.res"))->toEqual(false)
|
|
21
|
-
)
|
|
22
|
-
testPromise("a change of a .res source is not structural", async () =>
|
|
23
|
-
expect(Watch.isStructuralSource(Change, "/p/src/Foo/Bar.res"))->toEqual(false)
|
|
24
|
-
)
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
describe("Watch.debounce coalescing", () => {
|
|
28
|
-
testPromise("a burst collapses to one call carrying the strongest event", async () => {
|
|
29
|
-
let calls = ref([])
|
|
30
|
-
let fire = Watch.debounce(40, (e, p) => calls := Array.concat(calls.contents, [(e, p)]))
|
|
31
|
-
// A move/ungroup: the new file is created, an edit lands, then the old file
|
|
32
|
-
// is unlinked — all inside one window. The unlink must win.
|
|
33
|
-
fire(Watch.Change, "/p/src/A.res")
|
|
34
|
-
fire(Watch.Add, "/p/src/new/A.res")
|
|
35
|
-
fire(Watch.Unlink, "/p/src/old/A.res")
|
|
36
|
-
await delay(120)
|
|
37
|
-
expect(calls.contents->Array.length)->toEqual(1)
|
|
38
|
-
let (e, p) = calls.contents->Array.getUnsafe(0)
|
|
39
|
-
expect(e)->toEqual(Watch.Unlink)
|
|
40
|
-
expect(p)->toEqual("/p/src/old/A.res")
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
testPromise("a later weaker event does not mask an earlier unlink", async () => {
|
|
44
|
-
let calls = ref([])
|
|
45
|
-
let fire = Watch.debounce(40, (e, _p) => calls := Array.concat(calls.contents, [e]))
|
|
46
|
-
fire(Watch.Unlink, "/p/src/old/A.res")
|
|
47
|
-
fire(Watch.Change, "/p/src/A.res")
|
|
48
|
-
await delay(120)
|
|
49
|
-
expect(calls.contents)->toEqual([Watch.Unlink])
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
testPromise("interleaved .res.mjs unlinks never surface; each distinct .res source does", async () => {
|
|
53
|
-
// The real directory-mv burst chokidar emits: source AND generated files
|
|
54
|
-
// unlink interleaved. Only `.res` sources are emitted (never a `.res.mjs`),
|
|
55
|
-
// and every *distinct* source is emitted — two moved modules must both drive
|
|
56
|
-
// a rebuild, not collapse to one representative.
|
|
57
|
-
let calls = ref([])
|
|
58
|
-
let fire = Watch.debounce(40, (e, p) => calls := Array.concat(calls.contents, [(e, p)]))
|
|
59
|
-
fire(Watch.Unlink, "/p/src/old/A.res")
|
|
60
|
-
fire(Watch.Unlink, "/p/src/old/A.res.mjs")
|
|
61
|
-
fire(Watch.Unlink, "/p/src/old/A_Behavior.res")
|
|
62
|
-
fire(Watch.Unlink, "/p/src/old/A_Behavior.res.mjs")
|
|
63
|
-
fire(Watch.Add, "/p/src/new/A.res")
|
|
64
|
-
fire(Watch.Add, "/p/src/new/A.res.mjs")
|
|
65
|
-
await delay(120)
|
|
66
|
-
let paths = calls.contents->Array.map(((_, p)) => p)
|
|
67
|
-
expect(calls.contents->Array.every(((e, p)) => Watch.isStructuralSource(e, p)))->toEqual(true)
|
|
68
|
-
expect(paths->Array.includes("/p/src/old/A.res"))->toEqual(true)
|
|
69
|
-
expect(paths->Array.includes("/p/src/old/A_Behavior.res"))->toEqual(true)
|
|
70
|
-
expect(paths->Array.some(p => p->String.endsWith(".mjs")))->toEqual(false)
|
|
71
|
-
expect(calls.contents->Array.length)->toEqual(2)
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
testPromise("a burst spanning two packages emits an unlink per package (A4.1)", async () => {
|
|
75
|
-
// Regression: the old single-`bestPath` debounce clean-rebuilt only one
|
|
76
|
-
// package on a multi-package burst, stranding the other with stale .res.mjs.
|
|
77
|
-
let calls = ref([])
|
|
78
|
-
let fire = Watch.debounce(40, (e, p) => calls := Array.concat(calls.contents, [(e, p)]))
|
|
79
|
-
fire(Watch.Unlink, "/pkgA/src/Foo.res")
|
|
80
|
-
fire(Watch.Unlink, "/pkgB/src/Bar.res")
|
|
81
|
-
await delay(120)
|
|
82
|
-
let paths = calls.contents->Array.map(((_, p)) => p)
|
|
83
|
-
expect(paths->Array.includes("/pkgA/src/Foo.res"))->toEqual(true)
|
|
84
|
-
expect(paths->Array.includes("/pkgB/src/Bar.res"))->toEqual(true)
|
|
85
|
-
expect(calls.contents->Array.length)->toEqual(2)
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
testPromise("a structural signal suppresses a co-occurring plain change", async () => {
|
|
89
|
-
// A structural rebuild does its own re-run, so a plain edit in the same
|
|
90
|
-
// window must not also emit a separate Change (which would double-run).
|
|
91
|
-
let calls = ref([])
|
|
92
|
-
let fire = Watch.debounce(40, (e, _p) => calls := Array.concat(calls.contents, [e]))
|
|
93
|
-
fire(Watch.Change, "/p/src/Edited.res")
|
|
94
|
-
fire(Watch.Unlink, "/p/src/old/Moved.res")
|
|
95
|
-
await delay(120)
|
|
96
|
-
expect(calls.contents)->toEqual([Watch.Unlink])
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
testPromise("a lone change stays a change (incremental re-run)", async () => {
|
|
100
|
-
let calls = ref([])
|
|
101
|
-
let fire = Watch.debounce(40, (e, _p) => calls := Array.concat(calls.contents, [e]))
|
|
102
|
-
fire(Watch.Change, "/p/src/A.res")
|
|
103
|
-
await delay(120)
|
|
104
|
-
expect(calls.contents)->toEqual([Watch.Change])
|
|
105
|
-
})
|
|
106
|
-
})
|