@latticexyz/recs 2.0.0-next.2 → 2.0.0-next.4

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.
@@ -0,0 +1,139 @@
1
+ import { defineComponent, removeComponent, setComponent, withValue } from "./Component";
2
+ import { Type, UpdateType } from "./constants";
3
+ import { createEntity } from "./Entity";
4
+ import { Has } from "./Query";
5
+ import { defineEnterSystem, defineExitSystem, defineSystem, defineUpdateSystem } from "./System";
6
+ import { Component, Entity, World } from "./types";
7
+ import { createWorld } from "./World";
8
+
9
+ describe("System", () => {
10
+ let world: World;
11
+
12
+ beforeEach(() => {
13
+ world = createWorld();
14
+ });
15
+
16
+ describe("Systems", () => {
17
+ let Position: Component<{ x: number; y: number }>;
18
+ let entity: Entity;
19
+
20
+ beforeEach(() => {
21
+ Position = defineComponent(world, { x: Type.Number, y: Type.Number });
22
+ entity = createEntity(world, [withValue(Position, { x: 1, y: 2 })]);
23
+ });
24
+
25
+ it("defineSystem should rerun the system if the query result changes (enter, update, exit)", () => {
26
+ const mock = jest.fn();
27
+ defineSystem(world, [Has(Position)], mock);
28
+
29
+ setComponent(Position, entity, { x: 2, y: 3 });
30
+
31
+ expect(mock).toHaveBeenCalledTimes(2);
32
+ expect(mock).toHaveBeenCalledWith({
33
+ entity,
34
+ component: Position,
35
+ value: [
36
+ { x: 2, y: 3 },
37
+ { x: 1, y: 2 },
38
+ ],
39
+ type: UpdateType.Update,
40
+ });
41
+
42
+ setComponent(Position, entity, { x: 3, y: 3 });
43
+ expect(mock).toHaveBeenCalledTimes(3);
44
+ expect(mock).toHaveBeenCalledWith({
45
+ entity,
46
+ component: Position,
47
+ value: [
48
+ { x: 3, y: 3 },
49
+ { x: 2, y: 3 },
50
+ ],
51
+ type: UpdateType.Update,
52
+ });
53
+
54
+ removeComponent(Position, entity);
55
+ expect(mock).toHaveBeenCalledTimes(4);
56
+ expect(mock).toHaveBeenCalledWith({
57
+ entity,
58
+ component: Position,
59
+ value: [undefined, { x: 3, y: 3 }],
60
+ type: UpdateType.Exit,
61
+ });
62
+ });
63
+
64
+ it("defineUpdateSystem should rerun the system if the component value of an entity matchign the query changes", () => {
65
+ const mock = jest.fn();
66
+ defineUpdateSystem(world, [Has(Position)], mock);
67
+
68
+ // The entity already had a position when the system was created and the system runs on init,
69
+ // so this position update is an update
70
+ setComponent(Position, entity, { x: 2, y: 3 });
71
+ expect(mock).toHaveBeenCalledTimes(1);
72
+
73
+ setComponent(Position, entity, { x: 2, y: 3 });
74
+ expect(mock).toHaveBeenCalledTimes(2);
75
+ expect(mock).toHaveBeenCalledWith({
76
+ entity,
77
+ component: Position,
78
+ value: [
79
+ { x: 2, y: 3 },
80
+ { x: 2, y: 3 },
81
+ ],
82
+ type: UpdateType.Update,
83
+ });
84
+
85
+ // Setting the same value again should rerun the system
86
+ setComponent(Position, entity, { x: 2, y: 3 });
87
+ expect(mock).toHaveBeenCalledTimes(3);
88
+
89
+ setComponent(Position, entity, { x: 3, y: 3 });
90
+ expect(mock).toHaveBeenCalledTimes(4);
91
+ });
92
+
93
+ it("defineEnterSystem should rerun once with entities matching the query for the first time", () => {
94
+ const CanMove = defineComponent(world, { value: Type.Boolean });
95
+ const mock = jest.fn();
96
+
97
+ defineEnterSystem(world, [Has(CanMove)], mock);
98
+
99
+ const entity1 = createEntity(world, [withValue(CanMove, { value: true })]);
100
+
101
+ expect(mock).toHaveBeenCalledTimes(1);
102
+ expect(mock).toHaveBeenCalledWith(
103
+ expect.objectContaining({ entity: entity1, component: CanMove, value: [{ value: true }, undefined] })
104
+ );
105
+
106
+ const entity2 = createEntity(world, [withValue(CanMove, { value: true })]);
107
+ expect(mock).toHaveBeenCalledTimes(2);
108
+ expect(mock).toHaveBeenCalledWith(
109
+ expect.objectContaining({ entity: entity2, component: CanMove, value: [{ value: true }, undefined] })
110
+ );
111
+ });
112
+
113
+ it("defineExitSystem should rerun once with entities not matching the query anymore", () => {
114
+ const CanMove = defineComponent(world, { value: Type.Boolean });
115
+
116
+ const mock = jest.fn();
117
+ defineExitSystem(world, [Has(CanMove)], mock);
118
+
119
+ const entity1 = createEntity(world, [withValue(CanMove, { value: true })]);
120
+ const entity2 = createEntity(world);
121
+ setComponent(CanMove, entity2, { value: true });
122
+
123
+ expect(mock).toHaveBeenCalledTimes(0);
124
+
125
+ removeComponent(CanMove, entity1);
126
+
127
+ expect(mock).toHaveBeenCalledTimes(1);
128
+ expect(mock).toHaveBeenCalledWith(
129
+ expect.objectContaining({ entity: entity1, component: CanMove, value: [undefined, { value: true }] })
130
+ );
131
+
132
+ removeComponent(CanMove, entity2);
133
+ expect(mock).toHaveBeenCalledTimes(2);
134
+ expect(mock).toHaveBeenCalledWith(
135
+ expect.objectContaining({ entity: entity2, component: CanMove, value: [undefined, { value: true }] })
136
+ );
137
+ });
138
+ });
139
+ });
@@ -0,0 +1,79 @@
1
+ import { arrayToIterator } from "@latticexyz/utils";
2
+ import { Subject } from "rxjs";
3
+ import { defineComponent, setComponent } from "./Component";
4
+ import { Type } from "./constants";
5
+ import { createEntity } from "./Entity";
6
+ import { World, AnyComponent, EntitySymbol } from "./types";
7
+ import { createWorld, getEntityComponents } from "./World";
8
+
9
+ describe("World", () => {
10
+ describe("createWorld", () => {
11
+ let world: World;
12
+
13
+ beforeEach(() => {
14
+ world = createWorld();
15
+ });
16
+
17
+ describe("registerEntity", () => {
18
+ it("should add the entitiy to the world's entities", () => {
19
+ expect([...world.getEntities()].length).toBe(0);
20
+
21
+ world.registerEntity();
22
+ expect([...world.getEntities()].length).toBe(1);
23
+ expect([...world.getEntities()][0]).not.toBe(undefined);
24
+ });
25
+ });
26
+
27
+ describe("registerComponent", () => {
28
+ let rawComponent: AnyComponent;
29
+
30
+ beforeEach(() => {
31
+ rawComponent = {
32
+ id: "some-id",
33
+ values: {
34
+ value: new Map<EntitySymbol, number>(),
35
+ },
36
+ update$: new Subject(),
37
+ schema: { value: Type.Number },
38
+ metadata: {},
39
+ entities: () => arrayToIterator([]),
40
+ world,
41
+ };
42
+ });
43
+
44
+ it("should add the component to the world's components", () => {
45
+ expect(world.components.length).toBe(0);
46
+ world.registerComponent(rawComponent);
47
+ expect(world.components.length).toBe(1);
48
+ expect(world.components.includes(rawComponent)).toBe(true);
49
+ });
50
+
51
+ describe("getEntitiyComponents", () => {
52
+ it("should return the set of components of a given entity", () => {
53
+ const components = new Set<AnyComponent>([
54
+ defineComponent(world, { value: Type.Number }),
55
+ defineComponent(world, { value: Type.Number }),
56
+ defineComponent(world, { value: Type.Number }),
57
+ ]);
58
+ const entity = createEntity(world);
59
+ const value = 1;
60
+
61
+ for (const component of components) {
62
+ setComponent(component, entity, { value });
63
+ }
64
+
65
+ const received = getEntityComponents(world, entity);
66
+ expect(received.length).toEqual(components.size);
67
+
68
+ for (const comp of received) {
69
+ expect(components.has(comp)).toBe(true);
70
+ }
71
+
72
+ for (const comp of components) {
73
+ expect(received.includes(comp)).toBe(true);
74
+ }
75
+ });
76
+ });
77
+ });
78
+ });
79
+ });
@@ -0,0 +1,9 @@
1
+ export enum ActionState {
2
+ Requested = "Requested",
3
+ Executing = "Executing",
4
+ WaitingForTxEvents = "WaitingForTxEvents",
5
+ Complete = "Complete",
6
+ Failed = "Failed",
7
+ Cancelled = "Cancelled",
8
+ TxReduced = "TxReduced",
9
+ }