Skip to content

Commit 29df9b9

Browse files
committed
test: reorganize test file layout into correct categories
1 parent 84075fd commit 29df9b9

14 files changed

Lines changed: 227 additions & 222 deletions
Lines changed: 31 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,16 @@
11
import { describe, expect, test } from "bun:test";
2-
import { PLAYER_GEOMETRY, PLAYER_CONFIG, WORLD } from "../../src/constants.ts";
2+
import { PLAYER_GEOMETRY, WORLD } from "../../src/constants.ts";
33
import type { LevelEntitySpec } from "../../src/entities/types.ts";
44
import {
55
buildWorld,
66
createPlayer,
77
createPlayerOnFloor,
88
makeInput,
9-
step,
109
stepOnce,
1110
withFloor,
12-
} from "./harness.ts";
13-
14-
describe("Dash tech", () => {
15-
test("dash enters state immediately and commits direction after the frozen startup", () => {
16-
const specs: LevelEntitySpec[] = [];
17-
withFloor(specs, 20);
18-
const world = buildWorld(specs);
19-
const player = createPlayerOnFloor(world, 104, 20);
20-
21-
stepOnce(player, makeInput());
22-
const press = stepOnce(player, makeInput({ x: -1, dashPressed: true }));
23-
expect(press.snapshot.state).toBe("dash");
24-
expect(press.snapshot.vx).toBe(0);
25-
expect(press.effects.some((effect) => effect.type === "dash_begin")).toBeTrue();
26-
expect(press.effects.some((effect) => effect.type === "dash_start")).toBeFalse();
27-
28-
for (let frame = 0; frame < 3; frame++) {
29-
const startup = stepOnce(player, makeInput({ x: 1 }));
30-
expect(startup.snapshot.state).toBe("dash");
31-
expect(startup.snapshot.vx).toBe(0);
32-
expect(startup.effects.some((effect) => effect.type === "dash_start")).toBeFalse();
33-
}
34-
35-
const commit = stepOnce(player, makeInput({ x: 1 }));
36-
const dashStart = commit.effects.find((effect) => effect.type === "dash_start");
37-
expect(dashStart).toBeTruthy();
38-
expect(dashStart?.dirX).toBeGreaterThan(0);
39-
expect(commit.snapshot.vx).toBeGreaterThan(0);
40-
});
41-
42-
test("dash trail effects follow the canonical start, 0.08s, end schedule with no freeze-start trails", () => {
43-
const specs: LevelEntitySpec[] = [];
44-
withFloor(specs, 20);
45-
const world = buildWorld(specs);
46-
const player = createPlayerOnFloor(world, 104, 20);
47-
48-
stepOnce(player, makeInput());
49-
50-
const press = stepOnce(player, makeInput({ x: 1, dashPressed: true }));
51-
expect(press.effects.some((effect) => effect.type === "dash_trail")).toBeFalse();
52-
53-
for (let frame = 0; frame < 3; frame++) {
54-
const frozen = stepOnce(player, makeInput({ x: 1 }));
55-
expect(frozen.effects.some((effect) => effect.type === "dash_trail")).toBeFalse();
56-
}
57-
58-
const trailFrames: number[] = [];
59-
const trailXs: number[] = [];
60-
for (let frame = 0; frame < 20; frame++) {
61-
const result = stepOnce(player, makeInput({ x: 1 }));
62-
const trail = result.effects.find((effect) => effect.type === "dash_trail");
63-
if (trail) {
64-
trailFrames.push(frame);
65-
trailXs.push(trail.trailX ?? NaN);
66-
}
67-
}
68-
69-
expect(trailFrames).toEqual([0, 5, 10]);
70-
expect(trailXs[0]).toBeLessThan(trailXs[1]);
71-
expect(trailXs[1]).toBeLessThan(trailXs[2]);
72-
});
11+
} from "../support/harness.ts";
7312

13+
describe("Checklist dash tech", () => {
7414
test("superdash gives 260 horizontal speed and full jump height", () => {
7515
const specs: LevelEntitySpec[] = [];
7616
withFloor(specs, 20);
@@ -91,39 +31,6 @@ describe("Dash tech", () => {
9131
expect(jump.snapshot.vy).toBeCloseTo(-105, 5);
9232
});
9333

94-
test("jump resolves before dash motion on the coroutine commit frame", () => {
95-
const specs: LevelEntitySpec[] = [];
96-
withFloor(specs, 20);
97-
const world = buildWorld(specs);
98-
const probe = createPlayerOnFloor(world, 104, 20);
99-
100-
stepOnce(probe, makeInput());
101-
stepOnce(probe, makeInput({ x: 1, y: 1, dashPressed: true }));
102-
103-
let commitFrame = -1;
104-
for (let frame = 0; frame < 10; frame++) {
105-
const result = stepOnce(probe, makeInput({ x: 1, y: 1 }));
106-
if (result.effects.some((effect) => effect.type === "dash_start")) {
107-
commitFrame = frame;
108-
break;
109-
}
110-
}
111-
112-
expect(commitFrame).toBeGreaterThanOrEqual(0);
113-
114-
const player = createPlayerOnFloor(world, 104, 20);
115-
stepOnce(player, makeInput());
116-
stepOnce(player, makeInput({ x: 1, y: 1, dashPressed: true }));
117-
step(player, makeInput({ x: 1, y: 1 }), commitFrame);
118-
const jump = stepOnce(player, makeInput({ x: 1, y: 1, jump: true, jumpPressed: true }));
119-
120-
expect(jump.effects.some((effect) => effect.type === "super")).toBeTrue();
121-
expect(jump.effects.some((effect) => effect.type === "hyper")).toBeFalse();
122-
expect(jump.snapshot.state).toBe("normal");
123-
expect(jump.snapshot.vx).toBeCloseTo(260, 5);
124-
expect(jump.snapshot.vy).toBeCloseTo(-105, 5);
125-
});
126-
12734
test("hyperdash gives 325 horizontal speed and half jump height", () => {
12835
const specs: LevelEntitySpec[] = [];
12936
withFloor(specs, 20);
@@ -150,11 +57,7 @@ describe("Dash tech", () => {
15057
const world = buildWorld(specs);
15158
const startX = 104;
15259
const startY = 20 * WORLD.tile - 24;
153-
const probe = createPlayer(
154-
world,
155-
startX,
156-
startY,
157-
);
60+
const probe = createPlayer(world, startX, startY);
15861

15962
stepOnce(probe, makeInput());
16063
stepOnce(probe, makeInput({ x: 1, y: 1, dashPressed: true }));
@@ -331,13 +234,32 @@ describe("Dash tech", () => {
331234
expect(wallbounce.snapshot.vy).toBeCloseTo(-160, 5);
332235
});
333236

334-
test("reference dash constants stay aligned with the Celeste player source", () => {
335-
expect(PLAYER_CONFIG.dash.speed).toBe(240);
336-
expect(PLAYER_CONFIG.dash.endSpeed).toBe(160);
337-
expect(PLAYER_CONFIG.dash.duration).toBe(0.15);
338-
expect(PLAYER_CONFIG.dash.freezeTime).toBe(0.05);
339-
expect(PLAYER_CONFIG.dash.cooldown).toBe(0.2);
340-
expect(PLAYER_CONFIG.dash.refillCooldown).toBe(0.1);
341-
expect(PLAYER_CONFIG.dash.attackTime).toBe(0.3);
237+
test("spiked wallbounce is survivable when the wallbounce moves away from the spikes", () => {
238+
const specs: LevelEntitySpec[] = [];
239+
withFloor(specs, 20);
240+
for (let row = 10; row <= 20; row++) {
241+
specs.push({ kind: "solidTile", col: 15, row });
242+
specs.push({ kind: "spike", col: 15, row, dir: "right" });
243+
}
244+
const world = buildWorld(specs);
245+
const player = createPlayer(
246+
world,
247+
15 * WORLD.tile - PLAYER_GEOMETRY.hitboxW * 0.5 - 1,
248+
20 * WORLD.tile,
249+
);
250+
251+
stepOnce(player, makeInput());
252+
stepOnce(player, makeInput({ y: -1, dashPressed: true }));
253+
stepOnce(player, makeInput({ y: -1 }));
254+
stepOnce(player, makeInput({ y: -1 }));
255+
stepOnce(player, makeInput({ y: -1 }));
256+
stepOnce(player, makeInput({ y: -1 }));
257+
stepOnce(player, makeInput({ y: -1 }));
258+
const wallbounce = stepOnce(player, makeInput({ x: 1, y: -1, jump: true, jumpPressed: true }));
259+
260+
expect(wallbounce.effects.some((effect) => effect.type === "wall_jump")).toBeTrue();
261+
expect(
262+
world.collidesWithSpike(player.getHurtboxBounds(), wallbounce.snapshot.vx, wallbounce.snapshot.vy),
263+
).toBeNull();
342264
});
343265
});
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { describe, expect, test } from "bun:test";
2+
import { PLAYER_GEOMETRY, WORLD } from "../../src/constants.ts";
3+
import type { LevelEntitySpec } from "../../src/entities/types.ts";
4+
import {
5+
buildWorld,
6+
createPlayer,
7+
makeInput,
8+
stepOnce,
9+
withFloor,
10+
} from "../support/harness.ts";
11+
12+
describe("Checklist dashless tech", () => {
13+
test("climbhop transitions from climb to normal with climbhop Y speed", () => {
14+
const specs: LevelEntitySpec[] = [];
15+
withFloor(specs, 20);
16+
for (let row = 15; row <= 19; row++) {
17+
specs.push({ kind: "solidTile", col: 10, row });
18+
}
19+
const world = buildWorld(specs);
20+
const player = createPlayer(
21+
world,
22+
10 * WORLD.tile - PLAYER_GEOMETRY.hitboxW * 0.5 - 1,
23+
18 * WORLD.tile + PLAYER_GEOMETRY.hitboxH,
24+
);
25+
26+
let climbedOut = false;
27+
for (let frame = 0; frame < 400; frame++) {
28+
const result = stepOnce(player, makeInput({ grab: true, y: -1 }));
29+
if (frame > 10 && result.snapshot.state !== "climb") {
30+
climbedOut = true;
31+
expect(result.snapshot.vy).toBeCloseTo(-120, 5);
32+
break;
33+
}
34+
}
35+
36+
expect(climbedOut).toBeTrue();
37+
});
38+
39+
test("wallboost refunds climbjump stamina when pressing away in the boost window", () => {
40+
const specs: LevelEntitySpec[] = [];
41+
withFloor(specs, 26);
42+
for (let row = 8; row <= 25; row++) {
43+
specs.push({ kind: "solidTile", col: 10, row });
44+
}
45+
const world = buildWorld(specs);
46+
const player = createPlayer(
47+
world,
48+
10 * WORLD.tile - PLAYER_GEOMETRY.hitboxW * 0.5 - 1,
49+
20 * WORLD.tile + PLAYER_GEOMETRY.hitboxH,
50+
);
51+
52+
stepOnce(player, makeInput({ grab: true }));
53+
expect(player.getSnapshot().state).toBe("climb");
54+
55+
stepOnce(player, makeInput({ grab: true, jump: true, jumpPressed: true, x: 0 }));
56+
expect(player.getSnapshot().stamina).toBeCloseTo(82.5, 5);
57+
58+
stepOnce(player, makeInput({ x: -1 }));
59+
const wallboost = player.getSnapshot();
60+
expect(wallboost.stamina).toBeCloseTo(110, 5);
61+
expect(wallboost.vx).toBeCloseTo(-125.6666666667, 5);
62+
});
63+
});
Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { describe, expect, test } from "bun:test";
2-
import { WORLD } from "../../src/constants.ts";
2+
import { PLAYER_GEOMETRY, WORLD } from "../../src/constants.ts";
33
import type { LevelEntitySpec } from "../../src/entities/types.ts";
44
import {
55
buildWorld,
@@ -8,9 +8,29 @@ import {
88
makeInput,
99
stepOnce,
1010
withFloor,
11-
} from "./harness.ts";
11+
} from "../support/harness.ts";
12+
13+
describe("Checklist mechanics", () => {
14+
test("directional spikes are safe only while moving in the direction they point", () => {
15+
const specs: LevelEntitySpec[] = [
16+
{ kind: "spike", col: 15, row: 19, dir: "right" },
17+
{ kind: "solidTile", col: 13, row: 20 },
18+
{ kind: "solidTile", col: 14, row: 20 },
19+
{ kind: "solidTile", col: 15, row: 20 },
20+
];
21+
const world = buildWorld(specs);
22+
const hurtbox = {
23+
x: 15 * WORLD.tile - PLAYER_GEOMETRY.hitboxW + 1,
24+
y: 19 * WORLD.tile,
25+
w: PLAYER_GEOMETRY.hitboxW,
26+
h: PLAYER_GEOMETRY.hurtboxH,
27+
};
28+
29+
expect(world.collidesWithSpike(hurtbox, 90, 0)).toBeNull();
30+
expect(world.collidesWithSpike(hurtbox, 0, 0)).not.toBeNull();
31+
expect(world.collidesWithSpike(hurtbox, -90, 0)).not.toBeNull();
32+
});
1233

13-
describe("Core mechanics", () => {
1434
test("coyote jump remains available for 5 ticks at 60 Hz and expires on the 6th", () => {
1535
const specs: LevelEntitySpec[] = [];
1636
withFloor(specs, 20, 0, 9);
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { describe, expect, test } from "bun:test";
22
import { PLAYER_CONFIG } from "../../src/constants.ts";
3-
import { buildWorld, createPlayer, DT, makeInput } from "./harness.ts";
3+
import { buildWorld, createPlayer, DT, makeInput } from "../support/harness.ts";
44

5-
describe("Refill tech", () => {
5+
describe("Refill entity", () => {
66
test("consuming a refill contributes a 0.05s scene freeze", () => {
77
const world = buildWorld([
88
{ kind: "refill", x: 96, y: 89.5, type: "max" },
Lines changed: 2 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -8,35 +8,9 @@ import {
88
makeInput,
99
stepOnce,
1010
withFloor,
11-
} from "./harness.ts";
12-
13-
describe("Climb and dashless tech", () => {
14-
test("climbhop transitions from climb to normal with climbhop Y speed", () => {
15-
const specs: LevelEntitySpec[] = [];
16-
withFloor(specs, 20);
17-
for (let row = 15; row <= 19; row++) {
18-
specs.push({ kind: "solidTile", col: 10, row });
19-
}
20-
const world = buildWorld(specs);
21-
const player = createPlayer(
22-
world,
23-
10 * WORLD.tile - PLAYER_GEOMETRY.hitboxW * 0.5 - 1,
24-
18 * WORLD.tile + PLAYER_GEOMETRY.hitboxH,
25-
);
26-
27-
let climbedOut = false;
28-
for (let frame = 0; frame < 400; frame++) {
29-
const result = stepOnce(player, makeInput({ grab: true, y: -1 }));
30-
if (frame > 10 && result.snapshot.state !== "climb") {
31-
climbedOut = true;
32-
expect(result.snapshot.vy).toBeCloseTo(-120, 5);
33-
break;
34-
}
35-
}
36-
37-
expect(climbedOut).toBeTrue();
38-
});
11+
} from "../support/harness.ts";
3912

13+
describe("Climb behavior", () => {
4014
test("climbing into ledge-top spikes stalls instead of climbhopping", () => {
4115
const specs: LevelEntitySpec[] = [];
4216
withFloor(specs, 20);
@@ -80,31 +54,6 @@ describe("Climb and dashless tech", () => {
8054
expect(player.climbHopBlockedCheck()).toBeTrue();
8155
});
8256

83-
test("wallboost refunds climbjump stamina when pressing away in the boost window", () => {
84-
const specs: LevelEntitySpec[] = [];
85-
withFloor(specs, 26);
86-
for (let row = 8; row <= 25; row++) {
87-
specs.push({ kind: "solidTile", col: 10, row });
88-
}
89-
const world = buildWorld(specs);
90-
const player = createPlayer(
91-
world,
92-
10 * WORLD.tile - PLAYER_GEOMETRY.hitboxW * 0.5 - 1,
93-
20 * WORLD.tile + PLAYER_GEOMETRY.hitboxH,
94-
);
95-
96-
stepOnce(player, makeInput({ grab: true }));
97-
expect(player.getSnapshot().state).toBe("climb");
98-
99-
stepOnce(player, makeInput({ grab: true, jump: true, jumpPressed: true, x: 0 }));
100-
expect(player.getSnapshot().stamina).toBeCloseTo(82.5, 5);
101-
102-
stepOnce(player, makeInput({ x: -1 }));
103-
const wallboost = player.getSnapshot();
104-
expect(wallboost.stamina).toBeCloseTo(110, 5);
105-
expect(wallboost.vx).toBeCloseTo(-125.6666666667, 5);
106-
});
107-
10857
test("tired state uses the strict threshold and wallboost stamina grace", () => {
10958
const world = buildWorld([]);
11059
const player = createPlayer(world, 0, 0) as unknown as {

0 commit comments

Comments
 (0)