Files
OpenFrontIO/tests/UserSettings.test.ts
T
4544e18ce6 refactor(glow): move small-player glow strength into graphics overrides (#4634)
## Summary

Moves the small-player glow strength from a standalone UserSettings key
(`settings.highlightGlowStrength`) into the graphics overrides blob
(`smallPlayerGlow.strength`), and moves its slider from the basic
settings modal into the **Effects** section of the graphics settings
modal.

The pass now reads strength from its live `RenderSettings` slice like
every other graphics knob, flowing through `applyGraphicsOverrides`.
This removes the dedicated push chain (ClientGameRunner listener →
MapRenderer → Renderer → pass setter) and MapRenderer's context-restore
reapply — strength survives WebGL context restores automatically now,
participates in the graphics modal's "Reset to defaults", and shows up
in the render debug GUI.

## Bug fix

The old slider had no visible effect below ~115%: strength only drove
the blur-iteration count (`round(strength ** 2.74)`, floored at 1 pass),
and the composite intensity (`alpha * pulse * sqrt(passes)`) never
factored strength directly — so 10% rendered identical to 100%.

Strength is now a linear opacity fade on a fixed-width aura:

- 100% = the aura's full brightness, 10% = barely visible, 0% = off
- Slider capped at 100% (the >100% aura-widening mechanism became
unreachable and is removed: `passesFor`, `lastPasses`, and the
blur-iteration loop)
- Default is 25%
- The pass clamps persisted values above 1 so stale settings from the
old 500% range can't over-brighten

## Notes

- Reuses the existing `user_setting.highlight_glow_strength_label` /
`highlight_small_players_desc` translation keys — no en.json change
- The legacy `settings.highlightGlowStrength` localStorage key is not
migrated (the slider only shipped in #4588 a few days ago); users reset
to the 25% default

## Test plan

- [x] `tests/GraphicsOverrides.test.ts` — schema validation + apply
coverage for the new override group
- [x] Full suite (171 tests), tsc, eslint all pass

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 19:09:07 -07:00

61 lines
2.3 KiB
TypeScript

import { EFFECTS_KEY, UserSettings } from "../src/core/game/UserSettings";
describe("UserSettings effect selection", () => {
beforeEach(() => {
localStorage.clear();
// UserSettings keeps a static in-memory cache; reset it too so each test
// reads fresh from the (cleared) localStorage.
(
UserSettings as unknown as { cache: Map<string, string | null> }
).cache.clear();
});
it("sets and reads a per-effectType selection", () => {
const s = new UserSettings();
s.setSelectedEffectName("transportShipTrail", "spectrum");
expect(s.getSelectedEffectName("transportShipTrail")).toBe("spectrum");
});
it("returns null when nothing is selected", () => {
expect(
new UserSettings().getSelectedEffectName("transportShipTrail"),
).toBeNull();
});
it("clearing the last selection removes the storage key", () => {
const s = new UserSettings();
s.setSelectedEffectName("transportShipTrail", "spectrum");
s.setSelectedEffectName("transportShipTrail", undefined);
expect(s.getSelectedEffectName("transportShipTrail")).toBeNull();
expect(localStorage.getItem(EFFECTS_KEY)).toBeNull();
});
it("clearing one effectType leaves other types intact", () => {
const s = new UserSettings();
// Seed two types directly (only one real effectType exists today).
localStorage.setItem(
EFFECTS_KEY,
JSON.stringify({ transportShipTrail: "spectrum", future: "x" }),
);
s.setSelectedEffectName("transportShipTrail", undefined);
expect(s.getSelectedEffects()).toEqual({ future: "x" });
});
it("returns an empty map for a corrupt blob", () => {
localStorage.setItem(EFFECTS_KEY, "not json");
expect(new UserSettings().getSelectedEffects()).toEqual({});
});
it("keeps per-nukeType nuke-explosion slots independent", () => {
const s = new UserSettings();
s.setSelectedEffectName("atom", "atom_boom");
s.setSelectedEffectName("hydro", "hydro_boom");
expect(s.getSelectedEffectName("atom")).toBe("atom_boom");
expect(s.getSelectedEffectName("hydro")).toBe("hydro_boom");
// Clearing one bomb's slot leaves the others intact.
s.setSelectedEffectName("atom", undefined);
expect(s.getSelectedEffectName("atom")).toBeNull();
expect(s.getSelectedEffectName("hydro")).toBe("hydro_boom");
});
});