@reventlessdev/reventless-gwt 1.0.0-alpha.225 → 1.0.0-alpha.227
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 +14 -0
- package/package.json +6 -6
- package/src/Hint.res +8 -3
- package/src/Hint.res.mjs +8 -0
- package/src/MultiSourceProjection_GWT.res +4 -0
- package/src/MultiSourceProjection_GWT.res.mjs +8 -4
- package/src/Outcome.res +5 -1
- package/src/Projection_GWT.res +4 -0
- package/src/Projection_GWT.res.mjs +8 -4
- package/tests/HintTest.res +56 -0
- package/tests/HintTest.res.mjs +99 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,20 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# 1.0.0-alpha.227 (2026-09-28)
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* **gwt:** a view's failing scenario points at project, not evolve ([1cb5cd3](https://github.com/ReventlessDev/reventless-core/commit/1cb5cd3651ec9830abac2676142a40d311d0b77c))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# 1.0.0-alpha.226 (2026-09-27)
|
|
14
|
+
|
|
15
|
+
### Features
|
|
16
|
+
|
|
17
|
+
* **gwt:** a view's failing scenario points at project, not evolve ([1cb5cd3](https://github.com/ReventlessDev/reventless-core/commit/1cb5cd3651ec9830abac2676142a40d311d0b77c))
|
|
18
|
+
|
|
19
|
+
|
|
6
20
|
# 1.0.0-alpha.225 (2026-09-25)
|
|
7
21
|
|
|
8
22
|
**Note:** Version bump only for package @reventlessdev/reventless-gwt
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reventlessdev/reventless-gwt",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.227",
|
|
4
4
|
"description": "Given-When-Then DSLs and test harness for Reventless slice testing",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"jest": {
|
|
@@ -14,12 +14,12 @@
|
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"sury": "11.0.0",
|
|
17
|
-
"@reventlessdev/rescript-effect": "0.1.0-alpha.34",
|
|
18
|
-
"@reventlessdev/rescript-node": "2.0.0-alpha.15",
|
|
19
|
-
"@reventlessdev/reventless-core": "3.0.0-alpha.278",
|
|
20
|
-
"@reventlessdev/reventless-infra": "3.0.0-alpha.177",
|
|
21
17
|
"@reventlessdev/rescript-jest": "1.0.0-alpha.11",
|
|
22
|
-
"@reventlessdev/
|
|
18
|
+
"@reventlessdev/rescript-node": "2.0.0-alpha.17",
|
|
19
|
+
"@reventlessdev/reventless-core": "3.0.0-alpha.280",
|
|
20
|
+
"@reventlessdev/reventless-infra": "3.0.0-alpha.179",
|
|
21
|
+
"@reventlessdev/rescript-effect": "0.1.0-alpha.34",
|
|
22
|
+
"@reventlessdev/reventless-spec": "3.0.0-alpha.151"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"rescript": "12.3.1",
|
package/src/Hint.res
CHANGED
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
// and §3.3 (JSON `hint` field).
|
|
7
7
|
//
|
|
8
8
|
// `locus` is a dotted pointer like "CategoryBehavior.decide" filled in by
|
|
9
|
-
// the DSL at `then*` call time when the slice module name is in scope
|
|
10
|
-
//
|
|
11
|
-
//
|
|
9
|
+
// the DSL at `then*` call time when the slice module name is in scope, else a
|
|
10
|
+
// generic pointer. A `StateMismatch` names its fold, so a view's points at
|
|
11
|
+
// `project` rather than `evolve`.
|
|
12
12
|
|
|
13
13
|
type t = {
|
|
14
14
|
locus: string,
|
|
@@ -58,6 +58,11 @@ let forMismatch = (~slice="<slice>", m: Outcome.mismatch): t =>
|
|
|
58
58
|
message,
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
|
+
| StateMismatch({fold: Project}) => {
|
|
62
|
+
locus: `${slice}.project`,
|
|
63
|
+
branch: None,
|
|
64
|
+
message: "project() filed a different row than expected. Check the arm for the event and the key it sets.",
|
|
65
|
+
}
|
|
61
66
|
| StateMismatch(_) => {
|
|
62
67
|
locus: `${slice}.evolve`,
|
|
63
68
|
branch: None,
|
package/src/Hint.res.mjs
CHANGED
|
@@ -48,6 +48,14 @@ function forMismatch(sliceOpt, m) {
|
|
|
48
48
|
message: message
|
|
49
49
|
};
|
|
50
50
|
case "StateMismatch" :
|
|
51
|
+
let match$2 = m.fold;
|
|
52
|
+
if (match$2 !== undefined && match$2 !== "Evolve") {
|
|
53
|
+
return {
|
|
54
|
+
locus: slice + `.project`,
|
|
55
|
+
branch: undefined,
|
|
56
|
+
message: "project() filed a different row than expected. Check the arm for the event and the key it sets."
|
|
57
|
+
};
|
|
58
|
+
}
|
|
51
59
|
return {
|
|
52
60
|
locus: slice + `.evolve`,
|
|
53
61
|
branch: undefined,
|
|
@@ -239,6 +239,7 @@ module Make = (Projection: Reventless.Projection.Mapping): (
|
|
|
239
239
|
} else {
|
|
240
240
|
Outcome.fail(
|
|
241
241
|
StateMismatch({
|
|
242
|
+
fold: Project,
|
|
242
243
|
key: testId.contents,
|
|
243
244
|
expected: Some(JSON.Encode.array(expectedStates->encStates)),
|
|
244
245
|
actual: Some(encStore(store)),
|
|
@@ -257,6 +258,7 @@ module Make = (Projection: Reventless.Projection.Mapping): (
|
|
|
257
258
|
} else {
|
|
258
259
|
Outcome.fail(
|
|
259
260
|
StateMismatch({
|
|
261
|
+
fold: Project,
|
|
260
262
|
key: id,
|
|
261
263
|
expected: Some(JSON.Encode.array(expectedStates->encStates)),
|
|
262
264
|
actual: Some(encStore(store)),
|
|
@@ -272,6 +274,7 @@ module Make = (Projection: Reventless.Projection.Mapping): (
|
|
|
272
274
|
} else {
|
|
273
275
|
Outcome.fail(
|
|
274
276
|
StateMismatch({
|
|
277
|
+
fold: Project,
|
|
275
278
|
key: "<all>",
|
|
276
279
|
expected: Some(encStore(expectedStore)),
|
|
277
280
|
actual: Some(encStore(store)),
|
|
@@ -293,6 +296,7 @@ module Make = (Projection: Reventless.Projection.Mapping): (
|
|
|
293
296
|
} else {
|
|
294
297
|
Outcome.fail(
|
|
295
298
|
StateMismatch({
|
|
299
|
+
fold: Project,
|
|
296
300
|
key: "<any>",
|
|
297
301
|
expected: None,
|
|
298
302
|
actual: Some(encStore(store)),
|
|
@@ -248,7 +248,8 @@ function Make(Projection) {
|
|
|
248
248
|
TAG: "StateMismatch",
|
|
249
249
|
key: testId.contents,
|
|
250
250
|
expected: expectedStates.map(encState),
|
|
251
|
-
actual: encStore(store)
|
|
251
|
+
actual: encStore(store),
|
|
252
|
+
fold: "Project"
|
|
252
253
|
});
|
|
253
254
|
}
|
|
254
255
|
};
|
|
@@ -264,7 +265,8 @@ function Make(Projection) {
|
|
|
264
265
|
TAG: "StateMismatch",
|
|
265
266
|
key: id,
|
|
266
267
|
expected: expectedStates.map(encState),
|
|
267
|
-
actual: encStore(store)
|
|
268
|
+
actual: encStore(store),
|
|
269
|
+
fold: "Project"
|
|
268
270
|
});
|
|
269
271
|
}
|
|
270
272
|
};
|
|
@@ -277,7 +279,8 @@ function Make(Projection) {
|
|
|
277
279
|
TAG: "StateMismatch",
|
|
278
280
|
key: "<all>",
|
|
279
281
|
expected: encStore(expectedStore),
|
|
280
|
-
actual: encStore(store)
|
|
282
|
+
actual: encStore(store),
|
|
283
|
+
fold: "Project"
|
|
281
284
|
});
|
|
282
285
|
}
|
|
283
286
|
};
|
|
@@ -293,7 +296,8 @@ function Make(Projection) {
|
|
|
293
296
|
TAG: "StateMismatch",
|
|
294
297
|
key: "<any>",
|
|
295
298
|
expected: undefined,
|
|
296
|
-
actual: encStore(store)
|
|
299
|
+
actual: encStore(store),
|
|
300
|
+
fold: "Project"
|
|
297
301
|
});
|
|
298
302
|
}
|
|
299
303
|
};
|
package/src/Outcome.res
CHANGED
|
@@ -8,10 +8,14 @@
|
|
|
8
8
|
// shape and §3.3 for the JSON rendering. Stage 7 adds sury-aware rendering and
|
|
9
9
|
// structural `fieldDiff`. Stage 4 added `AppendConditionMismatch`.
|
|
10
10
|
|
|
11
|
+
// The function whose fold produced a state: a behavior's `evolve`, or a view's
|
|
12
|
+
// `project`. Absent means `Evolve`, so a harness that does not say keeps its hint.
|
|
13
|
+
type fold = Evolve | Project
|
|
14
|
+
|
|
11
15
|
type mismatch =
|
|
12
16
|
| EventsMismatch({expected: array<JSON.t>, actual: array<JSON.t>})
|
|
13
17
|
| ErrorMismatch({expected: JSON.t, actual: option<JSON.t>, actualEvents: array<JSON.t>})
|
|
14
|
-
| StateMismatch({key: string, expected: option<JSON.t>, actual: option<JSON.t
|
|
18
|
+
| StateMismatch({key: string, expected: option<JSON.t>, actual: option<JSON.t>, fold?: fold})
|
|
15
19
|
| NoEventExpected({actual: array<JSON.t>})
|
|
16
20
|
| TodoMismatch({expected: array<(string, JSON.t)>, actual: array<(string, JSON.t)>})
|
|
17
21
|
| AppendConditionMismatch({expected: JSON.t, actual: JSON.t})
|
package/src/Projection_GWT.res
CHANGED
|
@@ -243,6 +243,7 @@ module Make = (Spec: Spec, Projection: Projection with module Spec := Spec): (
|
|
|
243
243
|
} else {
|
|
244
244
|
Outcome.fail(
|
|
245
245
|
StateMismatch({
|
|
246
|
+
fold: Project,
|
|
246
247
|
key: testId.contents,
|
|
247
248
|
expected: Some(JSON.Encode.array(expectedStates->encStates)),
|
|
248
249
|
actual: Some(encStore(store)),
|
|
@@ -261,6 +262,7 @@ module Make = (Spec: Spec, Projection: Projection with module Spec := Spec): (
|
|
|
261
262
|
} else {
|
|
262
263
|
Outcome.fail(
|
|
263
264
|
StateMismatch({
|
|
265
|
+
fold: Project,
|
|
264
266
|
key: id,
|
|
265
267
|
expected: Some(JSON.Encode.array(expectedStates->encStates)),
|
|
266
268
|
actual: Some(encStore(store)),
|
|
@@ -276,6 +278,7 @@ module Make = (Spec: Spec, Projection: Projection with module Spec := Spec): (
|
|
|
276
278
|
} else {
|
|
277
279
|
Outcome.fail(
|
|
278
280
|
StateMismatch({
|
|
281
|
+
fold: Project,
|
|
279
282
|
key: "<all>",
|
|
280
283
|
expected: Some(encStore(expectedStore)),
|
|
281
284
|
actual: Some(encStore(store)),
|
|
@@ -296,6 +299,7 @@ module Make = (Spec: Spec, Projection: Projection with module Spec := Spec): (
|
|
|
296
299
|
} else {
|
|
297
300
|
Outcome.fail(
|
|
298
301
|
StateMismatch({
|
|
302
|
+
fold: Project,
|
|
299
303
|
key: "<any>",
|
|
300
304
|
expected: None,
|
|
301
305
|
actual: Some(encStore(store)),
|
|
@@ -231,7 +231,8 @@ function Make(Spec) {
|
|
|
231
231
|
TAG: "StateMismatch",
|
|
232
232
|
key: testId.contents,
|
|
233
233
|
expected: expectedStates.map(encState),
|
|
234
|
-
actual: encStore(store)
|
|
234
|
+
actual: encStore(store),
|
|
235
|
+
fold: "Project"
|
|
235
236
|
});
|
|
236
237
|
}
|
|
237
238
|
};
|
|
@@ -247,7 +248,8 @@ function Make(Spec) {
|
|
|
247
248
|
TAG: "StateMismatch",
|
|
248
249
|
key: id,
|
|
249
250
|
expected: expectedStates.map(encState),
|
|
250
|
-
actual: encStore(store)
|
|
251
|
+
actual: encStore(store),
|
|
252
|
+
fold: "Project"
|
|
251
253
|
});
|
|
252
254
|
}
|
|
253
255
|
};
|
|
@@ -260,7 +262,8 @@ function Make(Spec) {
|
|
|
260
262
|
TAG: "StateMismatch",
|
|
261
263
|
key: "<all>",
|
|
262
264
|
expected: encStore(expectedStore),
|
|
263
|
-
actual: encStore(store)
|
|
265
|
+
actual: encStore(store),
|
|
266
|
+
fold: "Project"
|
|
264
267
|
});
|
|
265
268
|
}
|
|
266
269
|
};
|
|
@@ -276,7 +279,8 @@ function Make(Spec) {
|
|
|
276
279
|
TAG: "StateMismatch",
|
|
277
280
|
key: "<any>",
|
|
278
281
|
expected: undefined,
|
|
279
|
-
actual: encStore(store)
|
|
282
|
+
actual: encStore(store),
|
|
283
|
+
fold: "Project"
|
|
280
284
|
});
|
|
281
285
|
}
|
|
282
286
|
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// A state mismatch's hint names the fold that produced the state: `project` for
|
|
2
|
+
// a view's scenario, `evolve` for a behavior's.
|
|
3
|
+
|
|
4
|
+
open JestGlobals
|
|
5
|
+
|
|
6
|
+
module CategoriesView = {
|
|
7
|
+
module Key = Reventless.Id.StringPure
|
|
8
|
+
let name = "CategoriesView"
|
|
9
|
+
|
|
10
|
+
@schema
|
|
11
|
+
type state = {categoryId: string, name: string}
|
|
12
|
+
|
|
13
|
+
@schema
|
|
14
|
+
type consumedEvent = CategoryAdded({categoryId: string, name: string})
|
|
15
|
+
|
|
16
|
+
let subIdConfig = None
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
module CategoriesViewProjection = {
|
|
20
|
+
module Spec = CategoriesView
|
|
21
|
+
open CategoriesView
|
|
22
|
+
|
|
23
|
+
let project = ({event}: Reventless.StateViewSlice.consumed<consumedEvent>) =>
|
|
24
|
+
switch event {
|
|
25
|
+
| CategoryAdded({categoryId, name}) => [
|
|
26
|
+
Reventless.Projection.Set(categoryId, {categoryId, name}),
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
module View = Projection_GWT.Make(CategoriesView, CategoriesViewProjection)
|
|
32
|
+
|
|
33
|
+
describe("Hint for a StateMismatch", () => {
|
|
34
|
+
testPromise("a view's differing row points at project", async () => {
|
|
35
|
+
let outcome = await View.givenEvents([])
|
|
36
|
+
->View.whenEvent(CategoryAdded({categoryId: "c1", name: "Electronics"}))
|
|
37
|
+
->View.thenStateWithId("c1", {categoryId: "c1", name: "Books"})
|
|
38
|
+
switch outcome {
|
|
39
|
+
| Error(m) =>
|
|
40
|
+
let hint = Hint.forMismatch(~slice=CategoriesView.name, m)
|
|
41
|
+
expect(hint.locus)->toBe("CategoriesView.project")
|
|
42
|
+
expect(hint.message)->toBe(
|
|
43
|
+
"project() filed a different row than expected. Check the arm for the event and the key it sets.",
|
|
44
|
+
)
|
|
45
|
+
| Ok() => JsError.throwWithMessage("expected a StateMismatch")
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
testSync("a behavior's differing state still points at evolve", () => {
|
|
50
|
+
let hint = Hint.forMismatch(
|
|
51
|
+
~slice="AddCategory",
|
|
52
|
+
Outcome.StateMismatch({key: "c1", expected: None, actual: None}),
|
|
53
|
+
)
|
|
54
|
+
expect(hint.locus)->toBe("AddCategory.evolve")
|
|
55
|
+
})
|
|
56
|
+
})
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Sury from "sury";
|
|
4
|
+
import * as Id$Reventless from "@reventlessdev/reventless-spec/src/types/Id.res.mjs";
|
|
5
|
+
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
6
|
+
import * as Hint$ReventlessGwt from "../src/Hint.res.mjs";
|
|
7
|
+
import * as Projection_GWT$ReventlessGwt from "../src/Projection_GWT.res.mjs";
|
|
8
|
+
|
|
9
|
+
let name = "CategoriesView";
|
|
10
|
+
|
|
11
|
+
let stateSchema = Sury.$schema(s => ({
|
|
12
|
+
categoryId: s.m(Sury.string),
|
|
13
|
+
name: s.m(Sury.string)
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
let consumedEventSchema = Sury.$schema(s => ({
|
|
17
|
+
TAG: "CategoryAdded",
|
|
18
|
+
categoryId: s.m(Sury.string),
|
|
19
|
+
name: s.m(Sury.string)
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
let CategoriesView = {
|
|
23
|
+
Key: undefined,
|
|
24
|
+
name: name,
|
|
25
|
+
stateSchema: stateSchema,
|
|
26
|
+
consumedEventSchema: consumedEventSchema,
|
|
27
|
+
subIdConfig: undefined,
|
|
28
|
+
authorization: "AllowAuthenticated",
|
|
29
|
+
visibility: "Public"
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
function project(param) {
|
|
33
|
+
let event = param.event;
|
|
34
|
+
let categoryId = event.categoryId;
|
|
35
|
+
return [{
|
|
36
|
+
TAG: "Set",
|
|
37
|
+
_0: categoryId,
|
|
38
|
+
_1: {
|
|
39
|
+
categoryId: categoryId,
|
|
40
|
+
name: event.name
|
|
41
|
+
}
|
|
42
|
+
}];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
let CategoriesViewProjection = {
|
|
46
|
+
Spec: undefined,
|
|
47
|
+
project: project
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
let View = Projection_GWT$ReventlessGwt.Make({
|
|
51
|
+
name: name,
|
|
52
|
+
stateSchema: stateSchema,
|
|
53
|
+
Key: {
|
|
54
|
+
schema: Id$Reventless.StringPure.schema,
|
|
55
|
+
make: prim => prim,
|
|
56
|
+
makeFromString: prim => prim,
|
|
57
|
+
toString: prim => prim,
|
|
58
|
+
cmp: Id$Reventless.StringPure.cmp
|
|
59
|
+
},
|
|
60
|
+
consumedEventSchema: consumedEventSchema,
|
|
61
|
+
subIdConfig: undefined
|
|
62
|
+
})({
|
|
63
|
+
project: project
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
globalThis.describe("Hint for a StateMismatch", () => {
|
|
67
|
+
globalThis.test("a view's differing row points at project", async () => {
|
|
68
|
+
let outcome = await View.thenStateWithId(View.whenEvent(View.givenEvents([]), {
|
|
69
|
+
TAG: "CategoryAdded",
|
|
70
|
+
categoryId: "c1",
|
|
71
|
+
name: "Electronics"
|
|
72
|
+
}), "c1", {
|
|
73
|
+
categoryId: "c1",
|
|
74
|
+
name: "Books"
|
|
75
|
+
});
|
|
76
|
+
if (outcome.TAG === "Ok") {
|
|
77
|
+
return Stdlib_JsError.throwWithMessage("expected a StateMismatch");
|
|
78
|
+
}
|
|
79
|
+
let hint = Hint$ReventlessGwt.forMismatch(name, outcome._0);
|
|
80
|
+
globalThis.expect(hint.locus).toBe("CategoriesView.project");
|
|
81
|
+
globalThis.expect(hint.message).toBe("project() filed a different row than expected. Check the arm for the event and the key it sets.");
|
|
82
|
+
});
|
|
83
|
+
globalThis.test("a behavior's differing state still points at evolve", () => {
|
|
84
|
+
let hint = Hint$ReventlessGwt.forMismatch("AddCategory", {
|
|
85
|
+
TAG: "StateMismatch",
|
|
86
|
+
key: "c1",
|
|
87
|
+
expected: undefined,
|
|
88
|
+
actual: undefined
|
|
89
|
+
});
|
|
90
|
+
globalThis.expect(hint.locus).toBe("AddCategory.evolve");
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
export {
|
|
95
|
+
CategoriesView,
|
|
96
|
+
CategoriesViewProjection,
|
|
97
|
+
View,
|
|
98
|
+
}
|
|
99
|
+
/* stateSchema Not a pure module */
|