Files
galaxy-game/ui/frontend/tests/wasm-core-calc.test.ts
T
Ilia Denisov b1b87c8521
Tests · Go / test (push) Successful in 2m26s
Tests · UI / test (push) Successful in 2m26s
feat(ui-calculator): input validation, load caps, ceil display, modernization layout
- custom load capped at cargo capacity (error when exceeded); full load shows the cargo capacity; zero cargo pins load to empty and disables the toggle

- per-input red border + tooltip for every invalid value (blocks, techs, load, MAT, modernization target); no value may be negative; locking a speed is disabled when drive is zero

- display every computed number (results + goal-seek back-solved input) rounded up to 3 decimals via a shared pkg/calc Ceil3 bridged to wasm; engine keeps its own round-to-nearest util.Fixed*

- modernization total upgrade cost spans two columns (single line)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 21:24:40 +02:00

80 lines
2.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Smoke test for the Phase 30 calculator bridge against the real
// (TinyGo-built) core.wasm. The calc-model and component suites use a
// fake Core; this file boots the actual WASM module to confirm every new
// function is registered in `ui/wasm/main.go` and marshals correctly —
// including the object return of `produceShipsInTurn` and the `null`
// infeasible result of the solvers. Requires `make wasm` to have run.
import { beforeAll, describe, expect, test } from "vitest";
import type { Core } from "../src/platform/core/index";
import { loadWasmCoreForTest } from "./setup-wasm";
let core: Core;
beforeAll(async () => {
core = await loadWasmCoreForTest();
});
describe("WasmCore calculator bridge (Phase 30)", () => {
test("combat results", () => {
expect(core.effectiveAttack({ weapons: 15, weaponsTech: 1.5 })).toBeCloseTo(
22.5,
9,
);
expect(
core.effectiveDefence({ shields: 20, shieldsTech: 1, fullMass: 45 }),
).toBeCloseTo((20 / Math.cbrt(45)) * Math.cbrt(30), 6);
expect(
core.bombingPower({ weapons: 30, weaponsTech: 1, armament: 3, number: 1 }),
).toBeCloseTo(139.29503, 3);
});
test("planet build", () => {
expect(
core.shipBuildCost({ shipMass: 10, material: 3, resources: 0.5 }),
).toBeCloseTo(114, 9);
const r = core.produceShipsInTurn({
productionAvailable: 100,
material: 100,
resources: 10,
shipMass: 1,
});
expect(r).toEqual({
ships: 10,
materialLeft: 90,
productionUsed: 0,
progress: 0,
});
});
test("goal-seek solvers, including infeasible", () => {
expect(
core.weaponsForAttack({ targetAttack: 30, weaponsTech: 1.5 }),
).toBeCloseTo(20, 9);
expect(
core.cargoForEmptyMass({ targetEmptyMass: 42, restMass: 30 }),
).toBeCloseTo(12, 9);
expect(
core.loadForFullMass({ targetFullMass: 65, emptyMass: 45, cargoTech: 1 }),
).toBeCloseTo(20, 9);
const shields = core.shieldsForDefence({
targetDefence: 5,
shieldsTech: 1,
restMass: 40,
});
expect(shields).not.toBeNull();
expect(shields as number).toBeGreaterThan(0);
// Speed at/above the stripped-hull ceiling (20 × driveTech) is
// unreachable: the bridge returns null.
expect(
core.driveForSpeed({ targetSpeed: 100, driveTech: 1.2, restMass: 35 }),
).toBeNull();
});
test("ceil3 rounds up to three decimals", () => {
expect(core.ceil3({ value: 5.0003 })).toBeCloseTo(5.001, 9);
expect(core.ceil3({ value: 4.2761 })).toBeCloseTo(4.277, 9);
expect(core.ceil3({ value: 5 })).toBeCloseTo(5, 9);
});
});