test: e2e jump helper prefers undiscovered systems — fixes exhausted-territory ping-pong
CI / build + unit + e2e (pull_request) Successful in 2m30s
CI / build + unit + e2e (pull_request) Successful in 2m30s
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
import { expect, type Page } from '@playwright/test';
|
||||
|
||||
// Shared e2e helpers — kept out of the specs so both files can reuse them.
|
||||
|
||||
interface WorldSystemDto { id: number; name: string; x: number; y: number; discovered: boolean; current: boolean }
|
||||
interface WorldState { systems: WorldSystemDto[]; edges: [number, number][]; turn: number }
|
||||
|
||||
// Jump to an adjacent system, preferring one that is still undiscovered. Every jump into an
|
||||
// undiscovered system generates 4 fresh neighbours (lib/game/world.ts), so fresh territory is
|
||||
// always available — picking the FIRST edge instead ping-pongs between exhausted systems, because
|
||||
// edges come back in insertion order and the oldest route out of a system is usually the one that
|
||||
// led here. Falls back to any non-current adjacent system if none is undiscovered.
|
||||
export async function jumpToFreshSystem(page: Page): Promise<WorldState> {
|
||||
const world = (await (await page.request.get('/api/world')).json()) as WorldState;
|
||||
const currentId = world.systems.find((s) => s.current)!.id;
|
||||
|
||||
const adjacentIds = new Set<number>();
|
||||
for (const [a, b] of world.edges) {
|
||||
if (a === currentId) adjacentIds.add(b);
|
||||
else if (b === currentId) adjacentIds.add(a);
|
||||
}
|
||||
const candidates = [...adjacentIds].map((id) => world.systems.find((s) => s.id === id)!);
|
||||
const target = candidates.find((s) => !s.discovered) ?? candidates[0];
|
||||
expect(target).toBeDefined(); // every system has at least one route out (ring generation)
|
||||
|
||||
const res = await page.request.post('/api/jump', { data: { systemId: target.id } });
|
||||
expect(res.status()).toBe(200);
|
||||
return (await res.json()) as WorldState;
|
||||
}
|
||||
+2
-5
@@ -1,4 +1,5 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
import { jumpToFreshSystem } from './helpers';
|
||||
|
||||
// Mirrors the /api/world + /api/system response shapes (lib/game/save.ts) — kept local so the spec stays self-contained.
|
||||
interface WorldSystemDto { id: number; name: string; x: number; y: number; discovered: boolean; current: boolean }
|
||||
@@ -218,11 +219,7 @@ test('SKILL TREE tab researches WEAPONS SYSTEMS and unlocks the gated WEAPONS bu
|
||||
if (unexplored.length > 0) {
|
||||
expect((await page.request.post('/api/explore', { data: { planetId: unexplored[0].id } })).status()).toBe(200);
|
||||
} else {
|
||||
const world = (await (await page.request.get('/api/world')).json()) as WorldState;
|
||||
const currentId = world.systems.find((s) => s.current)!.id;
|
||||
const edge = world.edges.find(([a, b]) => a === currentId || b === currentId)!;
|
||||
const targetId = edge[0] === currentId ? edge[1] : edge[0];
|
||||
expect((await page.request.post('/api/jump', { data: { systemId: targetId } })).status()).toBe(200);
|
||||
await jumpToFreshSystem(page); // prefer undiscovered territory — first-edge ping-pongs between exhausted systems
|
||||
}
|
||||
ship = (await (await page.request.get('/api/ship')).json()) as ShipState;
|
||||
}
|
||||
|
||||
+5
-15
@@ -1,6 +1,7 @@
|
||||
import { execSync } from 'node:child_process';
|
||||
import path from 'node:path';
|
||||
import { expect, test } from '@playwright/test';
|
||||
import { jumpToFreshSystem } from './helpers';
|
||||
|
||||
// Repo root — docker-compose.yml lives here; `docker compose restart` runs on
|
||||
// this host (the test runner), browsers stay remote. CI points E2E_COMPOSE_ARGS at the
|
||||
@@ -8,9 +9,7 @@ import { expect, test } from '@playwright/test';
|
||||
const repoRoot = path.join(__dirname, '..');
|
||||
const composeArgs = process.env.E2E_COMPOSE_ARGS ?? '';
|
||||
|
||||
// Mirrors the /api/world + /api/system response shapes (lib/game/save.ts) — kept local so the spec stays self-contained.
|
||||
interface WorldSystemDto { id: number; name: string; x: number; y: number; discovered: boolean; current: boolean }
|
||||
interface WorldState { systems: WorldSystemDto[]; edges: [number, number][]; turn: number }
|
||||
// Mirrors the /api/system response shape (lib/game/save.ts) — kept local so the spec stays self-contained.
|
||||
interface PlanetDto { id: number; name: string; type: string; hazard: boolean; explored: boolean; parent: number | null }
|
||||
interface SystemState { systemId: number; systemName: string; planets: PlanetDto[]; inventory: { kind: string; name: string; qty: number }[]; turn: number }
|
||||
interface CrewDto { id: number; name: string; role: string; traitGood: string; traitBad: string; origin: string }
|
||||
@@ -28,22 +27,13 @@ test('app survives a container restart (stack + volume persistence)', async ({ p
|
||||
expect(before.version).toMatch(/^\d+\.\d+\.\d+$/);
|
||||
|
||||
// Game-state baseline: jump once so there is real progress that must survive the restart.
|
||||
const worldBefore = (await (await page.request.get('/api/world')).json()) as WorldState;
|
||||
const currentId = worldBefore.systems.find((s) => s.current)!.id;
|
||||
const edge = worldBefore.edges.find(([a, b]) => a === currentId || b === currentId)!;
|
||||
const targetId = edge[0] === currentId ? edge[1] : edge[0];
|
||||
const jumpRes = await page.request.post('/api/jump', { data: { systemId: targetId } });
|
||||
expect(jumpRes.status()).toBe(200);
|
||||
await jumpToFreshSystem(page);
|
||||
|
||||
// Exploration baseline: scan one body so explored state + inventory must survive too.
|
||||
// (Earlier specs may have exhausted the current system, so jump until an unscanned body shows up.)
|
||||
let sysBefore = (await (await page.request.get('/api/system')).json()) as SystemState;
|
||||
for (let i = 0; !sysBefore.planets.some((p) => !p.explored) && i < 20; i++) {
|
||||
const world = (await (await page.request.get('/api/world')).json()) as WorldState;
|
||||
const curId = world.systems.find((s) => s.current)!.id;
|
||||
const e = world.edges.find(([a, b]) => a === curId || b === curId)!;
|
||||
const tId = e[0] === curId ? e[1] : e[0];
|
||||
expect((await page.request.post('/api/jump', { data: { systemId: tId } })).status()).toBe(200);
|
||||
await jumpToFreshSystem(page); // prefer undiscovered territory — first-edge ping-pongs between exhausted systems
|
||||
sysBefore = (await (await page.request.get('/api/system')).json()) as SystemState;
|
||||
}
|
||||
const targetPlanet = sysBefore.planets.find((p) => !p.explored)!;
|
||||
@@ -84,7 +74,7 @@ test('app survives a container restart (stack + volume persistence)', async ({ p
|
||||
expect(after!.version).toBe(before.version); // same build, not a re-seeded stack
|
||||
|
||||
// Game state survived: same turn counter and current system as before the restart.
|
||||
const worldAfter = (await (await page.request.get('/api/world')).json()) as WorldState;
|
||||
const worldAfter = (await (await page.request.get('/api/world')).json()) as { turn: number; systems: { id: number; current: boolean }[] };
|
||||
expect(worldAfter.turn).toBe(sysExplored.turn);
|
||||
expect(worldAfter.systems.find((s) => s.current)?.id).toBe(sysExplored.systemId);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user