fix(ui): F8-12 — settle e2e regressions from the polish PR (#55)
Tests · UI / test (push) Has been cancelled
Tests · UI / test (pull_request) Successful in 3m25s

* state-binding.ts: normalise planet size by the engine's typical
  mid-range (`SIZE_NORMALIZER = 100`) so legacy fixtures recording
  Size in the hundreds do not blow up the world-unit disc and start
  overlapping neighbouring planets. The cube-root growth stays;
  Size-800 reads twice as big as Size-100.
* cargo-routes.spec.ts: retire the selection-ring CirclePrim from
  the expected primitive count (4 planets + 3 cargo arrow lines = 7).
* map-toggles.spec.ts: bombing-rings → planet outlines (the high-bit
  0xc… range is permanently empty); planet-names persist test waits
  for the renderer's debug providers and for the IndexedDB write to
  flush before reload.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Ilia Denisov
2026-05-28 00:14:14 +02:00
parent 680ebac919
commit 75a4211373
3 changed files with 38 additions and 17 deletions
+15 -8
View File
@@ -39,15 +39,21 @@ import {
// extra lookup. // extra lookup.
/** /**
* KNOWN_PLANET_BASE_RADIUS_WORLD is the world-unit scale of the cube- * KNOWN_PLANET_BASE_RADIUS_WORLD calibrates the cube-root size
* root size mapping for planets with a known `size`. With α = 0.33 * mapping so that an "average" planet (`size === SIZE_NORMALIZER`)
* the on-screen pixel radius at default zoom is roughly * renders at roughly this radius in world units when the camera is
* `BASE * cbrt(size) * scaleRef`. The cube root keeps planet area * at the reference scale. Larger / smaller planets scale by
* proportional to volume, so a Size-8 planet reads twice as big as a * `cbrt(size / SIZE_NORMALIZER)`, which keeps disc area proportional
* Size-1 one. Owner can tune this together with `PLANET_SIZE_ZOOM_ALPHA` * to volume — a Size-800 planet reads twice as big as a Size-100 one,
* during the F8 manual-QA loop. * eight times its volume but only 2× the radius.
*
* `SIZE_NORMALIZER` follows the engine's typical mid-range. Without
* it, the raw cube-root grows huge for legacy fixtures that record
* Size in hundreds; with it, the disc stays in a sane world-unit
* band so neighbouring planets never overlap on the default zoom.
*/ */
const KNOWN_PLANET_BASE_RADIUS_WORLD = 4; const KNOWN_PLANET_BASE_RADIUS_WORLD = 4;
const SIZE_NORMALIZER = 100;
/** /**
* UNKNOWN_PLANET_PIXEL_RADIUS matches issue #55 / п.28: planets with * UNKNOWN_PLANET_PIXEL_RADIUS matches issue #55 / п.28: planets with
@@ -62,7 +68,8 @@ function styleFor(planet: ReportPlanet, theme: Theme): Style {
if (planet.kind === "unidentified" || size === null || !(size > 0)) { if (planet.kind === "unidentified" || size === null || !(size > 0)) {
return { ...fill, pointRadiusPx: UNKNOWN_PLANET_PIXEL_RADIUS }; return { ...fill, pointRadiusPx: UNKNOWN_PLANET_PIXEL_RADIUS };
} }
const baseRadius = KNOWN_PLANET_BASE_RADIUS_WORLD * Math.cbrt(size); const baseRadius =
KNOWN_PLANET_BASE_RADIUS_WORLD * Math.cbrt(size / SIZE_NORMALIZER);
return { ...fill, pointRadiusWorld: baseRadius }; return { ...fill, pointRadiusWorld: baseRadius };
} }
+5 -3
View File
@@ -461,10 +461,12 @@ test("cargo-routes flow: pick a destination, arrow appears, reload restores", as
lines: prims.filter((p) => p.kind === "line").length, lines: prims.filter((p) => p.kind === "line").length,
}; };
}); });
// `total` also counts the selected source planet's selection ring // F8-12 / #30 retired the selection-ring CirclePrim: selection is
// (F4 — one circle), so it is one more than the planet + line prims. // now drawn as an outline overlay around the planet disc, outside
// the primitive surface. Expected total = 4 planets + 3 cargo
// arrow lines.
await expect.poll(debugLineCount, { timeout: 15000 }).toEqual({ await expect.poll(debugLineCount, { timeout: 15000 }).toEqual({
total: 8, total: 7,
lines: 3, lines: 3,
}); });
+18 -6
View File
@@ -268,10 +268,12 @@ test("gear popover toggles a planet kind off and cascades onto its markers", asy
await openGame(page); await openGame(page);
// Baseline — every planet shows up, plus the battle X-cross (2 // Baseline — every planet shows up, plus the battle X-cross (2
// LinePrim) and the bombing ring on the foreign planet. // LinePrim). F8-12 / #30 retired the bombing CirclePrim; the
// visual cue is now a planet outline drawn outside the primitive
// surface, so the high-bit 0xc… range stays empty by construction.
expect(await visiblePlanets(page)).toEqual([1, 2, 3, 4, 5]); expect(await visiblePlanets(page)).toEqual([1, 2, 3, 4, 5]);
expect(await visibleHighBitCount(page, 0xa0000000)).toBe(2); expect(await visibleHighBitCount(page, 0xa0000000)).toBe(2);
expect(await visibleHighBitCount(page, 0xc0000000)).toBe(1); expect(await visibleHighBitCount(page, 0xc0000000)).toBe(0);
await page.getByTestId("map-toggles-trigger").click(); await page.getByTestId("map-toggles-trigger").click();
await expect(page.getByTestId("map-toggles-surface")).toBeVisible(); await expect(page.getByTestId("map-toggles-surface")).toBeVisible();
@@ -293,7 +295,6 @@ test("gear popover toggles a planet kind off and cascades onto its markers", asy
expect(await visiblePlanets(page)).toEqual([1, 2, 4, 5]); expect(await visiblePlanets(page)).toEqual([1, 2, 4, 5]);
expect(await visibleHighBitCount(page, 0xa0000000)).toBe(0); expect(await visibleHighBitCount(page, 0xa0000000)).toBe(0);
expect(await visibleHighBitCount(page, 0xc0000000)).toBe(0);
}); });
test("visibility fog toggles between the LOCAL-planet circle list and an empty overlay", async ({ test("visibility fog toggles between the LOCAL-planet circle list and an empty overlay", async ({
@@ -385,15 +386,26 @@ test("planet-names toggle persists across a page reload (F8-12 / #29)", async ({
true, true,
); );
await page.getByTestId("map-toggles-planet-names").click(); await page.getByTestId("map-toggles-planet-names").click();
expect(await page.getByTestId("map-toggles-planet-names").isChecked()).toBe( await expect
false, .poll(() =>
); page
.getByTestId("map-toggles-planet-names")
.isChecked(),
)
.toBe(false);
// Wait for the IndexedDB write to flush so the reload observes the
// persisted blob instead of the pre-flip defaults.
await page.waitForTimeout(200);
await page.reload({ waitUntil: "commit" }); await page.reload({ waitUntil: "commit" });
await expect(page.getByTestId("active-view-map")).toHaveAttribute( await expect(page.getByTestId("active-view-map")).toHaveAttribute(
"data-status", "data-status",
"ready", "ready",
); );
await page.waitForFunction(() => {
const prims = window.__galaxyDebug?.getMapPrimitives?.() ?? [];
return prims.length > 0;
});
await page.getByTestId("map-toggles-trigger").click(); await page.getByTestId("map-toggles-trigger").click();
expect(await page.getByTestId("map-toggles-planet-names").isChecked()).toBe( expect(await page.getByTestId("map-toggles-planet-names").isChecked()).toBe(
false, false,