@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
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
// Unit tests for the watch re-run scope algebra (B1): a plain edit narrows the
|
|
2
|
-
// re-run to its owning test package, structural/add/full triggers widen to
|
|
3
|
-
// RunAll, and scopes coalesce (RunAll absorbing) while a pass is in flight.
|
|
4
|
-
|
|
5
|
-
open JestGlobals
|
|
6
|
-
|
|
7
|
-
describe("Cli.mergeScope", () => {
|
|
8
|
-
testSync("RunAll absorbs on the left", () =>
|
|
9
|
-
expect(Cli.mergeScope(RunAll, RunPackages(["/p/a"])))->toEqual(Cli.RunAll)
|
|
10
|
-
)
|
|
11
|
-
testSync("RunAll absorbs on the right", () =>
|
|
12
|
-
expect(Cli.mergeScope(RunPackages(["/p/a"]), RunAll))->toEqual(Cli.RunAll)
|
|
13
|
-
)
|
|
14
|
-
testSync("two RunPackages union their dirs, deduplicated", () =>
|
|
15
|
-
expect(Cli.mergeScope(RunPackages(["/p/a", "/p/b"]), RunPackages(["/p/b", "/p/c"])))->toEqual(
|
|
16
|
-
Cli.RunPackages(["/p/a", "/p/b", "/p/c"]),
|
|
17
|
-
)
|
|
18
|
-
)
|
|
19
|
-
testSync("RunAll merged with RunAll stays RunAll", () =>
|
|
20
|
-
expect(Cli.mergeScope(RunAll, RunAll))->toEqual(Cli.RunAll)
|
|
21
|
-
)
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
describe("Cli.pathUnderDir", () => {
|
|
25
|
-
testSync("a file inside the dir is under it", () =>
|
|
26
|
-
expect(Cli.pathUnderDir("/p/pkgA/tests/FooGwt.res.mjs", "/p/pkgA"))->toEqual(true)
|
|
27
|
-
)
|
|
28
|
-
testSync("a sibling with a shared prefix is NOT under it", () =>
|
|
29
|
-
// /p/pkgABC must not be treated as inside /p/pkgA — the trailing separator
|
|
30
|
-
// guards against the bare-prefix false positive.
|
|
31
|
-
expect(Cli.pathUnderDir("/p/pkgABC/tests/FooGwt.res.mjs", "/p/pkgA"))->toEqual(false)
|
|
32
|
-
)
|
|
33
|
-
testSync("an unrelated path is not under it", () =>
|
|
34
|
-
expect(Cli.pathUnderDir("/p/pkgB/tests/BarGwt.res.mjs", "/p/pkgA"))->toEqual(false)
|
|
35
|
-
)
|
|
36
|
-
testSync("a trailing-slash dir still matches", () =>
|
|
37
|
-
expect(Cli.pathUnderDir("/p/pkgA/tests/FooGwt.res.mjs", "/p/pkgA/"))->toEqual(true)
|
|
38
|
-
)
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
describe("Cli.scopeSubset", () => {
|
|
42
|
-
let all = [
|
|
43
|
-
"/p/pkgA/tests/FooGwt.res.mjs",
|
|
44
|
-
"/p/pkgA/tests/BarGwt.res.mjs",
|
|
45
|
-
"/p/pkgB/tests/BazGwt.res.mjs",
|
|
46
|
-
]
|
|
47
|
-
testSync("RunAll returns every path", () =>
|
|
48
|
-
expect(Cli.scopeSubset(RunAll, all))->toEqual(all)
|
|
49
|
-
)
|
|
50
|
-
testSync("RunPackages keeps only files under the given dirs", () =>
|
|
51
|
-
expect(Cli.scopeSubset(RunPackages(["/p/pkgA"]), all))->toEqual([
|
|
52
|
-
"/p/pkgA/tests/FooGwt.res.mjs",
|
|
53
|
-
"/p/pkgA/tests/BarGwt.res.mjs",
|
|
54
|
-
])
|
|
55
|
-
)
|
|
56
|
-
testSync("RunPackages over multiple dirs unions their files", () =>
|
|
57
|
-
expect(Cli.scopeSubset(RunPackages(["/p/pkgA", "/p/pkgB"]), all))->toEqual(all)
|
|
58
|
-
)
|
|
59
|
-
testSync("RunPackages with no matching dir yields no files", () =>
|
|
60
|
-
expect(Cli.scopeSubset(RunPackages(["/p/pkgZ"]), all))->toEqual([])
|
|
61
|
-
)
|
|
62
|
-
})
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
-
|
|
3
|
-
import * as Cli$ReventlessGwt from "../src/Cli.res.mjs";
|
|
4
|
-
|
|
5
|
-
globalThis.describe("Cli.mergeScope", () => {
|
|
6
|
-
globalThis.test("RunAll absorbs on the left", () => {
|
|
7
|
-
globalThis.expect(Cli$ReventlessGwt.mergeScope("RunAll", {
|
|
8
|
-
TAG: "RunPackages",
|
|
9
|
-
_0: ["/p/a"]
|
|
10
|
-
})).toEqual("RunAll");
|
|
11
|
-
});
|
|
12
|
-
globalThis.test("RunAll absorbs on the right", () => {
|
|
13
|
-
globalThis.expect(Cli$ReventlessGwt.mergeScope({
|
|
14
|
-
TAG: "RunPackages",
|
|
15
|
-
_0: ["/p/a"]
|
|
16
|
-
}, "RunAll")).toEqual("RunAll");
|
|
17
|
-
});
|
|
18
|
-
globalThis.test("two RunPackages union their dirs, deduplicated", () => {
|
|
19
|
-
globalThis.expect(Cli$ReventlessGwt.mergeScope({
|
|
20
|
-
TAG: "RunPackages",
|
|
21
|
-
_0: [
|
|
22
|
-
"/p/a",
|
|
23
|
-
"/p/b"
|
|
24
|
-
]
|
|
25
|
-
}, {
|
|
26
|
-
TAG: "RunPackages",
|
|
27
|
-
_0: [
|
|
28
|
-
"/p/b",
|
|
29
|
-
"/p/c"
|
|
30
|
-
]
|
|
31
|
-
})).toEqual({
|
|
32
|
-
TAG: "RunPackages",
|
|
33
|
-
_0: [
|
|
34
|
-
"/p/a",
|
|
35
|
-
"/p/b",
|
|
36
|
-
"/p/c"
|
|
37
|
-
]
|
|
38
|
-
});
|
|
39
|
-
});
|
|
40
|
-
globalThis.test("RunAll merged with RunAll stays RunAll", () => {
|
|
41
|
-
globalThis.expect(Cli$ReventlessGwt.mergeScope("RunAll", "RunAll")).toEqual("RunAll");
|
|
42
|
-
});
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
globalThis.describe("Cli.pathUnderDir", () => {
|
|
46
|
-
globalThis.test("a file inside the dir is under it", () => {
|
|
47
|
-
globalThis.expect(Cli$ReventlessGwt.pathUnderDir("/p/pkgA/tests/FooGwt.res.mjs", "/p/pkgA")).toEqual(true);
|
|
48
|
-
});
|
|
49
|
-
globalThis.test("a sibling with a shared prefix is NOT under it", () => {
|
|
50
|
-
globalThis.expect(Cli$ReventlessGwt.pathUnderDir("/p/pkgABC/tests/FooGwt.res.mjs", "/p/pkgA")).toEqual(false);
|
|
51
|
-
});
|
|
52
|
-
globalThis.test("an unrelated path is not under it", () => {
|
|
53
|
-
globalThis.expect(Cli$ReventlessGwt.pathUnderDir("/p/pkgB/tests/BarGwt.res.mjs", "/p/pkgA")).toEqual(false);
|
|
54
|
-
});
|
|
55
|
-
globalThis.test("a trailing-slash dir still matches", () => {
|
|
56
|
-
globalThis.expect(Cli$ReventlessGwt.pathUnderDir("/p/pkgA/tests/FooGwt.res.mjs", "/p/pkgA/")).toEqual(true);
|
|
57
|
-
});
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
globalThis.describe("Cli.scopeSubset", () => {
|
|
61
|
-
let all = [
|
|
62
|
-
"/p/pkgA/tests/FooGwt.res.mjs",
|
|
63
|
-
"/p/pkgA/tests/BarGwt.res.mjs",
|
|
64
|
-
"/p/pkgB/tests/BazGwt.res.mjs"
|
|
65
|
-
];
|
|
66
|
-
globalThis.test("RunAll returns every path", () => {
|
|
67
|
-
globalThis.expect(Cli$ReventlessGwt.scopeSubset("RunAll", all)).toEqual(all);
|
|
68
|
-
});
|
|
69
|
-
globalThis.test("RunPackages keeps only files under the given dirs", () => {
|
|
70
|
-
globalThis.expect(Cli$ReventlessGwt.scopeSubset({
|
|
71
|
-
TAG: "RunPackages",
|
|
72
|
-
_0: ["/p/pkgA"]
|
|
73
|
-
}, all)).toEqual([
|
|
74
|
-
"/p/pkgA/tests/FooGwt.res.mjs",
|
|
75
|
-
"/p/pkgA/tests/BarGwt.res.mjs"
|
|
76
|
-
]);
|
|
77
|
-
});
|
|
78
|
-
globalThis.test("RunPackages over multiple dirs unions their files", () => {
|
|
79
|
-
globalThis.expect(Cli$ReventlessGwt.scopeSubset({
|
|
80
|
-
TAG: "RunPackages",
|
|
81
|
-
_0: [
|
|
82
|
-
"/p/pkgA",
|
|
83
|
-
"/p/pkgB"
|
|
84
|
-
]
|
|
85
|
-
}, all)).toEqual(all);
|
|
86
|
-
});
|
|
87
|
-
globalThis.test("RunPackages with no matching dir yields no files", () => {
|
|
88
|
-
globalThis.expect(Cli$ReventlessGwt.scopeSubset({
|
|
89
|
-
TAG: "RunPackages",
|
|
90
|
-
_0: ["/p/pkgZ"]
|
|
91
|
-
}, all)).toEqual([]);
|
|
92
|
-
});
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
/* Not a pure module */
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
// Unit tests for ComponentMeta — the pure folder-convention → {kind, name}
|
|
2
|
-
// derivation behind the `component` field on file items and the `components`
|
|
3
|
-
// inventory. No I/O; path strings only.
|
|
4
|
-
|
|
5
|
-
open JestGlobals
|
|
6
|
-
|
|
7
|
-
describe("ComponentMeta.componentOfTestFile", () => {
|
|
8
|
-
testPromise("derives kind + name from a slice GWT test path", async () => {
|
|
9
|
-
let c = ComponentMeta.componentOfTestFile(
|
|
10
|
-
"/repo/catalog/tests/Product/StateChangeSlice/AddProduct_GWT.res.mjs",
|
|
11
|
-
)
|
|
12
|
-
expect(c)->toEqual(Some({ComponentMeta.kind: "StateChangeSlice", name: "AddProduct"}))
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
testPromise("derives a ReadModel component", async () => {
|
|
16
|
-
let c = ComponentMeta.componentOfTestFile(
|
|
17
|
-
"/repo/catalog/tests/CatalogActivity/ReadModel/CatalogActivity_GWT.res.mjs",
|
|
18
|
-
)
|
|
19
|
-
expect(c)->toEqual(Some({ComponentMeta.kind: "ReadModel", name: "CatalogActivity"}))
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
testPromise("strips the GwtTest marker shape too", async () => {
|
|
23
|
-
let c = ComponentMeta.componentOfTestFile("/repo/x/tests/Order/Aggregate/OrderGwtTest.res.mjs")
|
|
24
|
-
expect(c)->toEqual(Some({ComponentMeta.kind: "Aggregate", name: "Order"}))
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
testPromise("returns None outside a recognised kind folder", async () => {
|
|
28
|
-
let c = ComponentMeta.componentOfTestFile("/repo/x/tests/Helpers/Thing_GWT.res.mjs")
|
|
29
|
-
expect(c)->toEqual(None)
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
testPromise("recognises a plural folder spelling with the canonical kind (C2)", async () => {
|
|
33
|
-
// The generator accepts `Aggregates/`; previously the gwt discovery only knew
|
|
34
|
-
// the singular `Aggregate/` and silently returned None. Now it derives from
|
|
35
|
-
// the shared vocabulary and reports the canonical singular kind.
|
|
36
|
-
let c = ComponentMeta.componentOfTestFile("/repo/x/tests/Order/Aggregates/Order_GWT.res.mjs")
|
|
37
|
-
expect(c)->toEqual(Some({ComponentMeta.kind: "Aggregate", name: "Order"}))
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
testPromise("recognises a short slice folder spelling (StateChange) too (C2)", async () => {
|
|
41
|
-
let c = ComponentMeta.componentOfTestFile(
|
|
42
|
-
"/repo/catalog/tests/Product/StateChange/AddProduct_GWT.res.mjs",
|
|
43
|
-
)
|
|
44
|
-
expect(c)->toEqual(Some({ComponentMeta.kind: "StateChangeSlice", name: "AddProduct"}))
|
|
45
|
-
})
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
describe("ComponentMeta.componentOfSrcFile", () => {
|
|
49
|
-
testPromise("collapses a spec file to its component", async () => {
|
|
50
|
-
let c = ComponentMeta.componentOfSrcFile("/repo/catalog/src/Product/Aggregate/Product.res")
|
|
51
|
-
expect(c)->toEqual(Some({ComponentMeta.kind: "Aggregate", name: "Product"}))
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
testPromise("collapses a behavior body file to the same component", async () => {
|
|
55
|
-
let c = ComponentMeta.componentOfSrcFile(
|
|
56
|
-
"/repo/catalog/src/Product/Aggregate/Product_Behavior.res",
|
|
57
|
-
)
|
|
58
|
-
expect(c)->toEqual(Some({ComponentMeta.kind: "Aggregate", name: "Product"}))
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
testPromise("strips the longest body suffix first (ExtensionPointMapping)", async () => {
|
|
62
|
-
let c = ComponentMeta.componentOfSrcFile(
|
|
63
|
-
"/repo/catalog/src/Products/ExtensionPoint/Products_ExtensionPointMapping.res",
|
|
64
|
-
)
|
|
65
|
-
expect(c)->toEqual(Some({ComponentMeta.kind: "ExtensionPoint", name: "Products"}))
|
|
66
|
-
})
|
|
67
|
-
})
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
-
|
|
3
|
-
import * as ComponentMeta$ReventlessGwt from "../src/ComponentMeta.res.mjs";
|
|
4
|
-
|
|
5
|
-
globalThis.describe("ComponentMeta.componentOfTestFile", () => {
|
|
6
|
-
globalThis.test("derives kind + name from a slice GWT test path", async () => {
|
|
7
|
-
let c = ComponentMeta$ReventlessGwt.componentOfTestFile("/repo/catalog/tests/Product/StateChangeSlice/AddProduct_GWT.res.mjs");
|
|
8
|
-
globalThis.expect(c).toEqual({
|
|
9
|
-
kind: "StateChangeSlice",
|
|
10
|
-
name: "AddProduct"
|
|
11
|
-
});
|
|
12
|
-
});
|
|
13
|
-
globalThis.test("derives a ReadModel component", async () => {
|
|
14
|
-
let c = ComponentMeta$ReventlessGwt.componentOfTestFile("/repo/catalog/tests/CatalogActivity/ReadModel/CatalogActivity_GWT.res.mjs");
|
|
15
|
-
globalThis.expect(c).toEqual({
|
|
16
|
-
kind: "ReadModel",
|
|
17
|
-
name: "CatalogActivity"
|
|
18
|
-
});
|
|
19
|
-
});
|
|
20
|
-
globalThis.test("strips the GwtTest marker shape too", async () => {
|
|
21
|
-
let c = ComponentMeta$ReventlessGwt.componentOfTestFile("/repo/x/tests/Order/Aggregate/OrderGwtTest.res.mjs");
|
|
22
|
-
globalThis.expect(c).toEqual({
|
|
23
|
-
kind: "Aggregate",
|
|
24
|
-
name: "Order"
|
|
25
|
-
});
|
|
26
|
-
});
|
|
27
|
-
globalThis.test("returns None outside a recognised kind folder", async () => {
|
|
28
|
-
let c = ComponentMeta$ReventlessGwt.componentOfTestFile("/repo/x/tests/Helpers/Thing_GWT.res.mjs");
|
|
29
|
-
globalThis.expect(c).toEqual(undefined);
|
|
30
|
-
});
|
|
31
|
-
globalThis.test("recognises a plural folder spelling with the canonical kind (C2)", async () => {
|
|
32
|
-
let c = ComponentMeta$ReventlessGwt.componentOfTestFile("/repo/x/tests/Order/Aggregates/Order_GWT.res.mjs");
|
|
33
|
-
globalThis.expect(c).toEqual({
|
|
34
|
-
kind: "Aggregate",
|
|
35
|
-
name: "Order"
|
|
36
|
-
});
|
|
37
|
-
});
|
|
38
|
-
globalThis.test("recognises a short slice folder spelling (StateChange) too (C2)", async () => {
|
|
39
|
-
let c = ComponentMeta$ReventlessGwt.componentOfTestFile("/repo/catalog/tests/Product/StateChange/AddProduct_GWT.res.mjs");
|
|
40
|
-
globalThis.expect(c).toEqual({
|
|
41
|
-
kind: "StateChangeSlice",
|
|
42
|
-
name: "AddProduct"
|
|
43
|
-
});
|
|
44
|
-
});
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
globalThis.describe("ComponentMeta.componentOfSrcFile", () => {
|
|
48
|
-
globalThis.test("collapses a spec file to its component", async () => {
|
|
49
|
-
let c = ComponentMeta$ReventlessGwt.componentOfSrcFile("/repo/catalog/src/Product/Aggregate/Product.res");
|
|
50
|
-
globalThis.expect(c).toEqual({
|
|
51
|
-
kind: "Aggregate",
|
|
52
|
-
name: "Product"
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
globalThis.test("collapses a behavior body file to the same component", async () => {
|
|
56
|
-
let c = ComponentMeta$ReventlessGwt.componentOfSrcFile("/repo/catalog/src/Product/Aggregate/Product_Behavior.res");
|
|
57
|
-
globalThis.expect(c).toEqual({
|
|
58
|
-
kind: "Aggregate",
|
|
59
|
-
name: "Product"
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
globalThis.test("strips the longest body suffix first (ExtensionPointMapping)", async () => {
|
|
63
|
-
let c = ComponentMeta$ReventlessGwt.componentOfSrcFile("/repo/catalog/src/Products/ExtensionPoint/Products_ExtensionPointMapping.res");
|
|
64
|
-
globalThis.expect(c).toEqual({
|
|
65
|
-
kind: "ExtensionPoint",
|
|
66
|
-
name: "Products"
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
/* Not a pure module */
|
package/tests/DiscoveryTest.res
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
// Unit test for the `.gwtignore` subtree prune in `Discovery.discover`. Builds a
|
|
2
|
-
// throwaway tree under the OS temp dir: a `keep/` directory with a GWT test, and
|
|
3
|
-
// a `skip/` directory carrying a `.gwtignore` sentinel alongside its own GWT
|
|
4
|
-
// test. Discovery must return the former and prune the latter — the guarantee
|
|
5
|
-
// the codegen golden fixtures rely on (they ship compiled `*_GWT.res.mjs` on
|
|
6
|
-
// disk that must never surface as live tests).
|
|
7
|
-
|
|
8
|
-
open JestGlobals
|
|
9
|
-
|
|
10
|
-
@module("node:os") external tmpdir: unit => string = "tmpdir"
|
|
11
|
-
@module("node:path") external join: (string, string) => string = "join"
|
|
12
|
-
|
|
13
|
-
type mkdirOpts = {recursive: bool}
|
|
14
|
-
@module("node:fs/promises") external mkdir: (string, mkdirOpts) => promise<Nullable.t<string>> = "mkdir"
|
|
15
|
-
@module("node:fs/promises") external writeFile: (string, string) => promise<unit> = "writeFile"
|
|
16
|
-
|
|
17
|
-
type rmOpts = {recursive: bool, force: bool}
|
|
18
|
-
@module("node:fs/promises") external rm: (string, rmOpts) => promise<unit> = "rm"
|
|
19
|
-
|
|
20
|
-
let endsWith = (paths: array<string>, suffix: string) =>
|
|
21
|
-
paths->Array.some(p => String.endsWith(p, suffix))
|
|
22
|
-
|
|
23
|
-
describe("Discovery .gwtignore prune", () => {
|
|
24
|
-
testPromise("keeps sibling GWT tests but prunes a subtree carrying .gwtignore", async () => {
|
|
25
|
-
let root = join(tmpdir(), "reventless-gwt-gwtignore-test")
|
|
26
|
-
let _ = await rm(root, {recursive: true, force: true})
|
|
27
|
-
let keep = join(root, "keep")
|
|
28
|
-
let skip = join(root, "skip")
|
|
29
|
-
let _ = await mkdir(keep, {recursive: true})
|
|
30
|
-
let _ = await mkdir(skip, {recursive: true})
|
|
31
|
-
let _ = await writeFile(join(keep, "Keep_GWT.res.mjs"), "")
|
|
32
|
-
let _ = await writeFile(join(skip, "Skip_GWT.res.mjs"), "")
|
|
33
|
-
let _ = await writeFile(join(skip, ".gwtignore"), "")
|
|
34
|
-
|
|
35
|
-
let found = await Discovery.discover([root])
|
|
36
|
-
|
|
37
|
-
expect(found->endsWith("keep/Keep_GWT.res.mjs"))->toBe(true)
|
|
38
|
-
expect(found->endsWith("skip/Skip_GWT.res.mjs"))->toBe(false)
|
|
39
|
-
|
|
40
|
-
let _ = await rm(root, {recursive: true, force: true})
|
|
41
|
-
})
|
|
42
|
-
})
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
-
|
|
3
|
-
import * as Nodeos from "node:os";
|
|
4
|
-
import * as Nodepath from "node:path";
|
|
5
|
-
import * as Promises from "node:fs/promises";
|
|
6
|
-
import * as Discovery$ReventlessGwt from "../src/Discovery.res.mjs";
|
|
7
|
-
|
|
8
|
-
function endsWith(paths, suffix) {
|
|
9
|
-
return paths.some(p => p.endsWith(suffix));
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
globalThis.describe("Discovery .gwtignore prune", () => {
|
|
13
|
-
globalThis.test("keeps sibling GWT tests but prunes a subtree carrying .gwtignore", async () => {
|
|
14
|
-
let root = Nodepath.join(Nodeos.tmpdir(), "reventless-gwt-gwtignore-test");
|
|
15
|
-
await Promises.rm(root, {
|
|
16
|
-
recursive: true,
|
|
17
|
-
force: true
|
|
18
|
-
});
|
|
19
|
-
let keep = Nodepath.join(root, "keep");
|
|
20
|
-
let skip = Nodepath.join(root, "skip");
|
|
21
|
-
await Promises.mkdir(keep, {
|
|
22
|
-
recursive: true
|
|
23
|
-
});
|
|
24
|
-
await Promises.mkdir(skip, {
|
|
25
|
-
recursive: true
|
|
26
|
-
});
|
|
27
|
-
await Promises.writeFile(Nodepath.join(keep, "Keep_GWT.res.mjs"), "");
|
|
28
|
-
await Promises.writeFile(Nodepath.join(skip, "Skip_GWT.res.mjs"), "");
|
|
29
|
-
await Promises.writeFile(Nodepath.join(skip, ".gwtignore"), "");
|
|
30
|
-
let found = await Discovery$ReventlessGwt.discover([root]);
|
|
31
|
-
globalThis.expect(endsWith(found, "keep/Keep_GWT.res.mjs")).toBe(true);
|
|
32
|
-
globalThis.expect(endsWith(found, "skip/Skip_GWT.res.mjs")).toBe(false);
|
|
33
|
-
await Promises.rm(root, {
|
|
34
|
-
recursive: true,
|
|
35
|
-
force: true
|
|
36
|
-
});
|
|
37
|
-
});
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
export {
|
|
41
|
-
endsWith,
|
|
42
|
-
}
|
|
43
|
-
/* Not a pure module */
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
// Unit tests for DomainDeadCode.analyze — orphan-event reachability over synthetic
|
|
2
|
-
// pluginStructures. Pure (no reventless-local import), so it runs under Jest. The
|
|
3
|
-
// real end-to-end pass over the example plugins is exercised by the LocalHost
|
|
4
|
-
// integration test; here we drive the analysis with hand-built fixtures to pin the
|
|
5
|
-
// orphan / not-orphan boundary precisely.
|
|
6
|
-
|
|
7
|
-
open JestGlobals
|
|
8
|
-
|
|
9
|
-
// ── terse builders for the verbose pluginStructure sub-records ──
|
|
10
|
-
let writable = (~name, ~produces=[], ~consumes=[], ~linkedViews=[]): Reventless.Plugin.writableDef => {
|
|
11
|
-
name,
|
|
12
|
-
commands: [],
|
|
13
|
-
producedEventTypes: produces,
|
|
14
|
-
consumedEventTypes: consumes,
|
|
15
|
-
linkedViews,
|
|
16
|
-
consistencyRead: None,
|
|
17
|
-
events: [],
|
|
18
|
-
chapter: None,
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
let queryable = (~name, ~consumes=[]): Reventless.Plugin.queryableDef => {
|
|
22
|
-
name,
|
|
23
|
-
queryField: "",
|
|
24
|
-
schema: "",
|
|
25
|
-
consumedEventTypes: consumes,
|
|
26
|
-
linkedWriteSide: [],
|
|
27
|
-
labelField: "id",
|
|
28
|
-
searchableFields: [],
|
|
29
|
-
statusField: None,
|
|
30
|
-
visibility: None,
|
|
31
|
-
chapter: None,
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
let automation = (~name, ~consumes=[]): Reventless.Plugin.automationSliceDef => {
|
|
35
|
-
name,
|
|
36
|
-
consumedEventTypes: consumes,
|
|
37
|
-
producedCommandTypes: [],
|
|
38
|
-
targetName: "",
|
|
39
|
-
chapter: None,
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
let structure = (
|
|
43
|
-
~aggregates=[],
|
|
44
|
-
~readModels=[],
|
|
45
|
-
~stateViewSlices=[],
|
|
46
|
-
~stateChangeSlices=[],
|
|
47
|
-
~automationSlices=[],
|
|
48
|
-
~outboundTranslationSlices=[],
|
|
49
|
-
~extensions=[],
|
|
50
|
-
): Reventless.Plugin.pluginStructure => {
|
|
51
|
-
readModels,
|
|
52
|
-
stateViewSlices,
|
|
53
|
-
stateChangeSlices,
|
|
54
|
-
aggregates,
|
|
55
|
-
automationSlices,
|
|
56
|
-
outboundTranslationSlices,
|
|
57
|
-
inboundTranslationSlices: [],
|
|
58
|
-
extensions,
|
|
59
|
-
extensionPoints: None,
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
describe("DomainDeadCode.analyze — orphan events", () => {
|
|
63
|
-
testPromise("flags a produced event no component consumes", async () => {
|
|
64
|
-
let shop = structure(
|
|
65
|
-
~aggregates=[writable(~name="Order", ~produces=["Shop.Placed", "Shop.Archived"])],
|
|
66
|
-
~readModels=[queryable(~name="Orders", ~consumes=["Shop.Placed"])],
|
|
67
|
-
)
|
|
68
|
-
let findings = DomainDeadCode.analyze(~structures=[("Shop", shop)])
|
|
69
|
-
expect(findings->Array.map(f => f.detail))->toEqual(["Shop.Archived"])
|
|
70
|
-
let f = findings->Array.getUnsafe(0)
|
|
71
|
-
expect((f.kind, f.pluginName, f.componentName))->toEqual(("OrphanEvent", "Shop", "Order"))
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
testPromise("a consumer in ANY plugin clears the orphan (cross-plugin)", async () => {
|
|
75
|
-
let shop = structure(~aggregates=[writable(~name="Order", ~produces=["Shop.Placed"])])
|
|
76
|
-
let analytics = structure(~automationSlices=[automation(~name="OnPlaced", ~consumes=["Shop.Placed"])])
|
|
77
|
-
let findings = DomainDeadCode.analyze(
|
|
78
|
-
~structures=[("Shop", shop), ("Analytics", analytics)]
|
|
79
|
-
)
|
|
80
|
-
expect(findings->Array.length)->toBe(0)
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
testPromise("consumption via a state-view slice and a state-change DCB read both count", async () => {
|
|
84
|
-
let s = structure(
|
|
85
|
-
~aggregates=[writable(~name="Order", ~produces=["Shop.Placed", "Shop.Shipped"])],
|
|
86
|
-
~stateViewSlices=[queryable(~name="OrderView", ~consumes=["Shop.Placed"])],
|
|
87
|
-
~stateChangeSlices=[writable(~name="Ship", ~consumes=["Shop.Shipped"])],
|
|
88
|
-
)
|
|
89
|
-
let findings = DomainDeadCode.analyze(~structures=[("Shop", s)])
|
|
90
|
-
expect(findings->Array.length)->toBe(0)
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
testPromise("a linked classic read model clears all the aggregate's events (rule 2)", async () => {
|
|
94
|
-
// Classic aggregate → read-model link lives on the producer (`linkedViews`); the
|
|
95
|
-
// read model's consumedEventTypes is empty. The aggregate must not be flagged.
|
|
96
|
-
let shop = structure(
|
|
97
|
-
~aggregates=[
|
|
98
|
-
writable(~name="Order", ~produces=["Shop.Placed", "Shop.Archived"], ~linkedViews=["Orders"]),
|
|
99
|
-
],
|
|
100
|
-
~readModels=[queryable(~name="Orders")],
|
|
101
|
-
)
|
|
102
|
-
let findings = DomainDeadCode.analyze(~structures=[("Shop", shop)])
|
|
103
|
-
expect(findings->Array.length)->toBe(0)
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
testPromise("an aggregate with no view and no consumer is fully orphaned", async () => {
|
|
107
|
-
let shop = structure(~aggregates=[writable(~name="Audit", ~produces=["Shop.Logged"])])
|
|
108
|
-
let findings = DomainDeadCode.analyze(~structures=[("Shop", shop)])
|
|
109
|
-
expect(findings->Array.map(f => f.detail))->toEqual(["Shop.Logged"])
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
testPromise("state-change slices are scanned as producers too", async () => {
|
|
113
|
-
let s = structure(
|
|
114
|
-
~stateChangeSlices=[writable(~name="Reserve", ~produces=["Shop.Reserved"])],
|
|
115
|
-
)
|
|
116
|
-
let findings = DomainDeadCode.analyze(~structures=[("Shop", s)])
|
|
117
|
-
expect(findings->Array.map(f => (f.componentName, f.detail)))->toEqual([("Reserve", "Shop.Reserved")])
|
|
118
|
-
})
|
|
119
|
-
})
|
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
-
|
|
3
|
-
import * as DomainDeadCode$ReventlessGwt from "../src/DomainDeadCode.res.mjs";
|
|
4
|
-
|
|
5
|
-
function writable(name, producesOpt, consumesOpt, linkedViewsOpt) {
|
|
6
|
-
let produces = producesOpt !== undefined ? producesOpt : [];
|
|
7
|
-
let consumes = consumesOpt !== undefined ? consumesOpt : [];
|
|
8
|
-
let linkedViews = linkedViewsOpt !== undefined ? linkedViewsOpt : [];
|
|
9
|
-
return {
|
|
10
|
-
name: name,
|
|
11
|
-
commands: [],
|
|
12
|
-
producedEventTypes: produces,
|
|
13
|
-
consumedEventTypes: consumes,
|
|
14
|
-
linkedViews: linkedViews,
|
|
15
|
-
consistencyRead: undefined,
|
|
16
|
-
events: [],
|
|
17
|
-
chapter: undefined
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function queryable(name, consumesOpt) {
|
|
22
|
-
let consumes = consumesOpt !== undefined ? consumesOpt : [];
|
|
23
|
-
return {
|
|
24
|
-
name: name,
|
|
25
|
-
queryField: "",
|
|
26
|
-
schema: "",
|
|
27
|
-
consumedEventTypes: consumes,
|
|
28
|
-
linkedWriteSide: [],
|
|
29
|
-
labelField: "id",
|
|
30
|
-
searchableFields: [],
|
|
31
|
-
statusField: undefined,
|
|
32
|
-
visibility: undefined,
|
|
33
|
-
chapter: undefined
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function automation(name, consumesOpt) {
|
|
38
|
-
let consumes = consumesOpt !== undefined ? consumesOpt : [];
|
|
39
|
-
return {
|
|
40
|
-
name: name,
|
|
41
|
-
consumedEventTypes: consumes,
|
|
42
|
-
producedCommandTypes: [],
|
|
43
|
-
targetName: "",
|
|
44
|
-
chapter: undefined
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function structure(aggregatesOpt, readModelsOpt, stateViewSlicesOpt, stateChangeSlicesOpt, automationSlicesOpt, outboundTranslationSlicesOpt, extensionsOpt) {
|
|
49
|
-
let aggregates = aggregatesOpt !== undefined ? aggregatesOpt : [];
|
|
50
|
-
let readModels = readModelsOpt !== undefined ? readModelsOpt : [];
|
|
51
|
-
let stateViewSlices = stateViewSlicesOpt !== undefined ? stateViewSlicesOpt : [];
|
|
52
|
-
let stateChangeSlices = stateChangeSlicesOpt !== undefined ? stateChangeSlicesOpt : [];
|
|
53
|
-
let automationSlices = automationSlicesOpt !== undefined ? automationSlicesOpt : [];
|
|
54
|
-
let outboundTranslationSlices = outboundTranslationSlicesOpt !== undefined ? outboundTranslationSlicesOpt : [];
|
|
55
|
-
let extensions = extensionsOpt !== undefined ? extensionsOpt : [];
|
|
56
|
-
return {
|
|
57
|
-
readModels: readModels,
|
|
58
|
-
stateViewSlices: stateViewSlices,
|
|
59
|
-
stateChangeSlices: stateChangeSlices,
|
|
60
|
-
aggregates: aggregates,
|
|
61
|
-
automationSlices: automationSlices,
|
|
62
|
-
outboundTranslationSlices: outboundTranslationSlices,
|
|
63
|
-
inboundTranslationSlices: [],
|
|
64
|
-
extensions: extensions,
|
|
65
|
-
extensionPoints: undefined
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
globalThis.describe("DomainDeadCode.analyze — orphan events", () => {
|
|
70
|
-
globalThis.test("flags a produced event no component consumes", async () => {
|
|
71
|
-
let shop = structure([writable("Order", [
|
|
72
|
-
"Shop.Placed",
|
|
73
|
-
"Shop.Archived"
|
|
74
|
-
], undefined, undefined)], [queryable("Orders", ["Shop.Placed"])], undefined, undefined, undefined, undefined, undefined);
|
|
75
|
-
let findings = DomainDeadCode$ReventlessGwt.analyze([[
|
|
76
|
-
"Shop",
|
|
77
|
-
shop
|
|
78
|
-
]]);
|
|
79
|
-
globalThis.expect(findings.map(f => f.detail)).toEqual(["Shop.Archived"]);
|
|
80
|
-
let f = findings[0];
|
|
81
|
-
globalThis.expect([
|
|
82
|
-
f.kind,
|
|
83
|
-
f.pluginName,
|
|
84
|
-
f.componentName
|
|
85
|
-
]).toEqual([
|
|
86
|
-
"OrphanEvent",
|
|
87
|
-
"Shop",
|
|
88
|
-
"Order"
|
|
89
|
-
]);
|
|
90
|
-
});
|
|
91
|
-
globalThis.test("a consumer in ANY plugin clears the orphan (cross-plugin)", async () => {
|
|
92
|
-
let shop = structure([writable("Order", ["Shop.Placed"], undefined, undefined)], undefined, undefined, undefined, undefined, undefined, undefined);
|
|
93
|
-
let analytics = structure(undefined, undefined, undefined, undefined, [automation("OnPlaced", ["Shop.Placed"])], undefined, undefined);
|
|
94
|
-
let findings = DomainDeadCode$ReventlessGwt.analyze([
|
|
95
|
-
[
|
|
96
|
-
"Shop",
|
|
97
|
-
shop
|
|
98
|
-
],
|
|
99
|
-
[
|
|
100
|
-
"Analytics",
|
|
101
|
-
analytics
|
|
102
|
-
]
|
|
103
|
-
]);
|
|
104
|
-
globalThis.expect(findings.length).toBe(0);
|
|
105
|
-
});
|
|
106
|
-
globalThis.test("consumption via a state-view slice and a state-change DCB read both count", async () => {
|
|
107
|
-
let s = structure([writable("Order", [
|
|
108
|
-
"Shop.Placed",
|
|
109
|
-
"Shop.Shipped"
|
|
110
|
-
], undefined, undefined)], undefined, [queryable("OrderView", ["Shop.Placed"])], [writable("Ship", undefined, ["Shop.Shipped"], undefined)], undefined, undefined, undefined);
|
|
111
|
-
let findings = DomainDeadCode$ReventlessGwt.analyze([[
|
|
112
|
-
"Shop",
|
|
113
|
-
s
|
|
114
|
-
]]);
|
|
115
|
-
globalThis.expect(findings.length).toBe(0);
|
|
116
|
-
});
|
|
117
|
-
globalThis.test("a linked classic read model clears all the aggregate's events (rule 2)", async () => {
|
|
118
|
-
let shop = structure([writable("Order", [
|
|
119
|
-
"Shop.Placed",
|
|
120
|
-
"Shop.Archived"
|
|
121
|
-
], undefined, ["Orders"])], [queryable("Orders", undefined)], undefined, undefined, undefined, undefined, undefined);
|
|
122
|
-
let findings = DomainDeadCode$ReventlessGwt.analyze([[
|
|
123
|
-
"Shop",
|
|
124
|
-
shop
|
|
125
|
-
]]);
|
|
126
|
-
globalThis.expect(findings.length).toBe(0);
|
|
127
|
-
});
|
|
128
|
-
globalThis.test("an aggregate with no view and no consumer is fully orphaned", async () => {
|
|
129
|
-
let shop = structure([writable("Audit", ["Shop.Logged"], undefined, undefined)], undefined, undefined, undefined, undefined, undefined, undefined);
|
|
130
|
-
let findings = DomainDeadCode$ReventlessGwt.analyze([[
|
|
131
|
-
"Shop",
|
|
132
|
-
shop
|
|
133
|
-
]]);
|
|
134
|
-
globalThis.expect(findings.map(f => f.detail)).toEqual(["Shop.Logged"]);
|
|
135
|
-
});
|
|
136
|
-
globalThis.test("state-change slices are scanned as producers too", async () => {
|
|
137
|
-
let s = structure(undefined, undefined, undefined, [writable("Reserve", ["Shop.Reserved"], undefined, undefined)], undefined, undefined, undefined);
|
|
138
|
-
let findings = DomainDeadCode$ReventlessGwt.analyze([[
|
|
139
|
-
"Shop",
|
|
140
|
-
s
|
|
141
|
-
]]);
|
|
142
|
-
globalThis.expect(findings.map(f => [
|
|
143
|
-
f.componentName,
|
|
144
|
-
f.detail
|
|
145
|
-
])).toEqual([[
|
|
146
|
-
"Reserve",
|
|
147
|
-
"Shop.Reserved"
|
|
148
|
-
]]);
|
|
149
|
-
});
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
export {
|
|
153
|
-
writable,
|
|
154
|
-
queryable,
|
|
155
|
-
automation,
|
|
156
|
-
structure,
|
|
157
|
-
}
|
|
158
|
-
/* Not a pure module */
|