@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 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.225",
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/reventless-spec": "3.0.0-alpha.149"
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. If the
10
- // DSL can't provide it (e.g. Projection has no single "locus" function name
11
- // the user controls), `locus` falls back to a generic pointer.
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})
@@ -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 */