Skip to content

Commit bf77b52

Browse files
committed
chore: add test cases for ecs
1 parent 5015f99 commit bf77b52

File tree

17 files changed

+298
-24
lines changed

17 files changed

+298
-24
lines changed

.github/workflows/unit-ssr.yml

+3
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ jobs:
3333
- name: Install dependencies
3434
run: pnpm install
3535

36+
- name: Run ECS unit tests
37+
run: pnpm test:ecs
38+
3639
- name: Cov
3740
run: |
3841
xvfb-run -s "-ac -screen 0 1280x1024x16" pnpm cov

__tests__/ecs/app.spec.ts

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { App, DOMAdapter, DefaultPlugins } from '../../packages/ecs/src';
2+
import { NodeJSAdapter } from '../utils';
3+
4+
DOMAdapter.set(NodeJSAdapter);
5+
6+
describe('App', () => {
7+
it('should register custom plugin correctly', async () => {
8+
const app = new App();
9+
10+
const CustomPlugin = jest.fn(() => {});
11+
app.addPlugins(...DefaultPlugins, CustomPlugin);
12+
13+
await app.run();
14+
await app.exit();
15+
16+
expect(CustomPlugin.mock.calls).toHaveLength(1);
17+
});
18+
});

__tests__/ecs/hierarchy.spec.ts

+151
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
import {
2+
App,
3+
Camera,
4+
Canvas,
5+
Children,
6+
Circle,
7+
Commands,
8+
DOMAdapter,
9+
DefaultPlugins,
10+
Entity,
11+
FillSolid,
12+
Grid,
13+
Parent,
14+
Pen,
15+
Plugin,
16+
PreStartUp,
17+
Renderable,
18+
Stroke,
19+
System,
20+
Theme,
21+
Transform,
22+
Visibility,
23+
system,
24+
} from '../../packages/ecs/src';
25+
import { NodeJSAdapter, sleep } from '../utils';
26+
27+
DOMAdapter.set(NodeJSAdapter);
28+
29+
describe('Hierarchy', () => {
30+
it('should create a hierarchy', async () => {
31+
const app = new App();
32+
33+
let canvasEntity: Entity | undefined;
34+
let cameraEntity: Entity | undefined;
35+
let parentEntity: Entity | undefined;
36+
let childEntity: Entity | undefined;
37+
38+
const MyPlugin: Plugin = () => {
39+
system(PreStartUp)(StartUpSystem);
40+
};
41+
42+
class StartUpSystem extends System {
43+
private readonly commands = new Commands(this);
44+
45+
q = this.query(
46+
(q) =>
47+
q.using(
48+
Canvas,
49+
Theme,
50+
Grid,
51+
Camera,
52+
Parent,
53+
Children,
54+
Transform,
55+
Renderable,
56+
FillSolid,
57+
Stroke,
58+
Circle,
59+
Visibility,
60+
).write,
61+
);
62+
63+
initialize(): void {
64+
const $canvas = DOMAdapter.get().createCanvas(
65+
200,
66+
200,
67+
) as HTMLCanvasElement;
68+
69+
const canvas = this.commands.spawn(
70+
new Canvas({
71+
element: $canvas,
72+
width: 200,
73+
height: 200,
74+
devicePixelRatio: 1,
75+
}),
76+
);
77+
78+
const camera = this.commands.spawn(
79+
new Camera({
80+
canvas: canvas.id(),
81+
}),
82+
new Transform(),
83+
);
84+
85+
const parent = this.commands.spawn(
86+
new Transform(),
87+
new Renderable(),
88+
new FillSolid('red'),
89+
new Circle({ cx: 0, cy: 0, r: 100 }),
90+
new Visibility(),
91+
);
92+
93+
camera.appendChild(parent);
94+
95+
const child = this.commands.spawn(
96+
new Transform(),
97+
new Renderable(),
98+
new FillSolid('green'),
99+
new Stroke({
100+
color: 'black',
101+
width: 10,
102+
alignment: 'center',
103+
dasharray: [10, 10],
104+
}),
105+
new Circle({ cx: 0, cy: 0, r: 50 }),
106+
new Visibility(),
107+
);
108+
parent.appendChild(child);
109+
110+
canvasEntity = canvas.id().hold();
111+
cameraEntity = camera.id().hold();
112+
parentEntity = parent.id().hold();
113+
childEntity = child.id().hold();
114+
115+
this.commands.execute();
116+
}
117+
}
118+
119+
app.addPlugins(...DefaultPlugins, MyPlugin);
120+
121+
await app.run();
122+
123+
await sleep(300);
124+
125+
if (canvasEntity && cameraEntity && parentEntity && childEntity) {
126+
const canvas = canvasEntity.read(Canvas);
127+
expect(canvas.devicePixelRatio).toBe(1);
128+
expect(canvas.width).toBe(200);
129+
expect(canvas.height).toBe(200);
130+
expect(canvas.renderer).toBe('webgl');
131+
expect(canvas.pen).toBe(Pen.HAND);
132+
expect(canvas.cameras).toHaveLength(1);
133+
134+
const camera = cameraEntity.read(Camera);
135+
expect(camera.canvas.isSame(canvasEntity)).toBeTruthy();
136+
expect(cameraEntity.read(Parent).children).toHaveLength(1);
137+
expect(
138+
cameraEntity.read(Parent).children[0].isSame(parentEntity),
139+
).toBeTruthy();
140+
141+
const parent = parentEntity.read(Parent);
142+
expect(parent.children).toHaveLength(1);
143+
expect(parent.children[0].isSame(childEntity)).toBeTruthy();
144+
145+
const child = childEntity.read(Children);
146+
expect(child.parent.isSame(parentEntity)).toBeTruthy();
147+
}
148+
149+
await app.exit();
150+
});
151+
});

jest.config.js

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ module.exports = {
88
'<rootDir>/__tests__/**/*/*.spec.+(ts|tsx|js)',
99
'!**/e2e/*.spec.+(ts|tsx|js)',
1010
'!**/ui/*.spec.+(ts|tsx|js)',
11+
'!**/ecs/*.spec.+(ts|tsx|js)',
1112
// '**/ssr/selector.spec.+(ts|tsx|js)',
1213
],
1314
preset: 'ts-jest',

jest.ecs.config.js

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
const esm = [
2+
'd3-*',
3+
'earcut',
4+
'potpack',
5+
'@mapbox',
6+
'roughjs',
7+
'fractional-indexing',
8+
]
9+
.map((d) => `_${d}|${d}`)
10+
.join('|');
11+
12+
module.exports = {
13+
testTimeout: 100000,
14+
testMatch: ['**/ecs/*.spec.+(ts|tsx|js)'],
15+
preset: 'ts-jest',
16+
moduleFileExtensions: ['ts', 'tsx', 'js', 'json'],
17+
modulePathIgnorePatterns: ['dist'],
18+
collectCoverageFrom: ['packages/ecs/src/**/*.ts'],
19+
transform: {
20+
'^.+\\.[tj]s$': [
21+
'ts-jest',
22+
{
23+
isolatedModules: true,
24+
tsconfig: {
25+
allowJs: true,
26+
target: 'esnext',
27+
esModuleInterop: true,
28+
},
29+
},
30+
],
31+
},
32+
transformIgnorePatterns: [`<rootDir>/node_modules/(?!(?:.pnpm/)?(${esm}))`],
33+
};

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"build": "pnpm -r run build",
2424
"deploy": "npx wrangler pages deploy ./packages/site/docs/.vitepress/dist",
2525
"cov": "jest --coverage",
26+
"test:ecs": "jest -c ./jest.ecs.config.js --coverage",
2627
"test:e2e": "npx playwright test",
2728
"dev:e2e": "vite dev",
2829
"test:ui": "npx playwright test -c playwright.config.ui.ts",
@@ -95,4 +96,4 @@
9596
"playwright": "1.49.1"
9697
}
9798
}
98-
}
99+
}

packages/ecs/examples/main.ts

+10
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
Text,
2424
Wireframe,
2525
Opacity,
26+
Visibility,
2627
Canvas,
2728
system,
2829
PreStartUp,
@@ -76,6 +77,7 @@ class StartUpSystem extends System {
7677
Text,
7778
Wireframe,
7879
Opacity,
80+
Visibility,
7981
).write,
8082
);
8183

@@ -102,6 +104,7 @@ class StartUpSystem extends System {
102104
new FillSolid('red'),
103105
new Circle({ cx: 0, cy: 0, r: 100 }),
104106
new Opacity(),
107+
new Visibility(),
105108
);
106109

107110
camera.appendChild(parent);
@@ -118,6 +121,7 @@ class StartUpSystem extends System {
118121
}),
119122
new Wireframe(true),
120123
new Circle({ cx: 0, cy: 0, r: 50 }),
124+
new Visibility(),
121125
);
122126
parent.appendChild(child);
123127

@@ -132,6 +136,7 @@ class StartUpSystem extends System {
132136
offsetY: 10,
133137
}),
134138
new Rect({ x: 0, y: 0, width: 100, height: 100, cornerRadius: 10 }),
139+
new Visibility(),
135140
);
136141
child.appendChild(grandchild);
137142

@@ -153,6 +158,7 @@ class StartUpSystem extends System {
153158
[200, 0],
154159
],
155160
}),
161+
new Visibility(),
156162
);
157163
child.appendChild(polyline);
158164

@@ -171,6 +177,7 @@ class StartUpSystem extends System {
171177
new Path({
172178
d: 'M 0 0 L 100 100 L 200 0 Z',
173179
}),
180+
new Visibility(),
174181
);
175182
child.appendChild(path);
176183

@@ -192,6 +199,7 @@ class StartUpSystem extends System {
192199
}),
193200
new Rough(),
194201
new Rect({ x: 0, y: 0, width: 100, height: 100, cornerRadius: 10 }),
202+
new Visibility(),
195203
);
196204
child.appendChild(rough);
197205

@@ -205,6 +213,7 @@ class StartUpSystem extends System {
205213
}),
206214
new Rough(),
207215
new Circle({ cx: 100, cy: 200, r: 50 }),
216+
new Visibility(),
208217
);
209218
parent.appendChild(roughCircle);
210219

@@ -219,6 +228,7 @@ class StartUpSystem extends System {
219228
fontSize: 24,
220229
fontFamily: 'Arial',
221230
}),
231+
new Visibility(),
222232
);
223233
parent.appendChild(text);
224234

packages/ecs/src/drawcalls/SDF.ts

+3
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,9 @@ export class SDF extends Drawcall {
485485
let type: number = 0;
486486
let cornerRadius = 0;
487487
const zIndex = (globalRenderOrder + (1 / total) * index) / ZINDEX_FACTOR;
488+
489+
console.log(shape.__id, zIndex);
490+
488491
if (shape.has(Circle)) {
489492
const { cx, cy, r } = shape.read(Circle);
490493
position = [cx, cy, zIndex, 0];

packages/ecs/src/plugins/Hierarchy.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
* @see https://github.com/bevyengine/bevy/blob/latest/crates/bevy_hierarchy
33
*/
44

5-
import { component } from '@lastolivegames/becsy';
5+
import { component, system } from '@lastolivegames/becsy';
66
import { Plugin } from './types';
7-
import { Children, Parent } from '../components';
7+
import { Children, Parent, ZIndex } from '../components';
8+
import { ComputeZIndex, PreUpdate } from '../systems';
89

910
// /**
1011
// * @see https://github.com/bevyengine/bevy/blob/latest/crates/bevy_hierarchy/src/events.rs
@@ -27,4 +28,7 @@ import { Children, Parent } from '../components';
2728
export const HierarchyPlugin: Plugin = () => {
2829
component(Parent);
2930
component(Children);
31+
component(ZIndex);
32+
33+
system(PreUpdate)(ComputeZIndex);
3034
};

packages/ecs/src/plugins/Renderer.ts

-2
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ import {
4141
Stroke,
4242
Text,
4343
Wireframe,
44-
ZIndex,
4544
Visibility,
4645
GPUResource,
4746
Name,
@@ -56,7 +55,6 @@ export const RendererPlugin: Plugin = () => {
5655
component(Name);
5756
component(Wireframe);
5857
component(GlobalRenderOrder);
59-
component(ZIndex);
6058
component(Visibility);
6159
/**
6260
* Style

0 commit comments

Comments
 (0)