Files
settled-reach/client/tests/test_step_canvas_legend.gd
T
jpmschweitzerandClaude Fable 5 a4d9a4fa09 fix(ui): PR #217 review fixes — derived legend offset, 960x540 goldens, non-overlap tests
The legend's reposition() now derives its Y from the header panel's
MEASURED bottom plus a named 12px gap (new accessors on the viewer;
deferred recompute so it reads settled layout, refreshed on header
content change) — the old hardcoded 60.0 sat 17px inside the wrapped
header's real 77px bottom, fusing the panels. Pixel-proven at the
evidence center: bottom=77.0, legend top=89.0, gap=12.0 exact. All 13
goldens regenerated at the DOCUMENTED 960x540 (the 1280x720 rider was
my own CLI flag, not a config; PIL-verified) with two-run byte
stability. Two structural tests pin the header/legend relationship
(T-1192 precedent), proven failing-first against the old constant
(-17.0px reported), and the legacy test that pinned the literal 60.0
now asserts the derived relationship so it cannot re-enforce drift.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-26 15:13:11 +02:00

123 lines
5.5 KiB
GDScript

## T-1182 tests: StepCanvasLegend — smoke coverage (PR #203 review, Hoshe
## note). Not the retired atlas_window_legend.gd's own richer suite (that
## file never had a dedicated test — its coverage lived entirely inside
## AtlasWindowViewer's own suites, now retired); this pins the panel's basic
## lifecycle/content contract directly.
class_name TestStepCanvasLegend
extends GdUnitTestSuite
const LegendScript := preload("res://ui/implant/apps/atlas/step_canvas/step_canvas_legend.gd")
const StepCanvasTransport := preload("res://ui/implant/apps/atlas/step_canvas/step_canvas_transport.gd")
## Same Tier-2/3 isolation as test_step_canvas_viewer.gd's _make_viewer()
## (T-1193 first slice): the default disk-cache root is machine-shared, and a
## warm cache delivers canvases into tests entered on real body ids.
const TEST_DISK_CACHE_ROOT := "user://test_step_canvas_legend_cache/"
func _make_viewer() -> StepCanvasViewer:
var v: StepCanvasViewer = auto_free(StepCanvasViewer.new())
v.disk_cache_root_override = TEST_DISK_CACHE_ROOT
return v
func test_legend_starts_hidden_before_refresh() -> void:
var v: StepCanvasViewer = _make_viewer()
add_child(v)
var legend = LegendScript.new(v)
auto_free(legend)
assert_bool(legend.visible).is_false()
func test_refresh_makes_the_legend_visible_and_populates_content() -> void:
var v: StepCanvasViewer = _make_viewer()
add_child(v)
v.enter({"body_id": "GJ380c", "body_radius_km": 6238.4}, {})
var legend = LegendScript.new(v)
add_child(legend)
legend.refresh()
assert_bool(legend.visible).is_true()
assert_int(legend.get_implant_children().size()).override_failure_message(
"refresh() must populate at least the always-on morphology/glaciation sections"
).is_greater(0)
## refresh() is a no-op (does not crash) when the legend has no viewer —
## defensive guard against a construction-order mistake.
func test_refresh_without_a_viewer_does_not_crash() -> void:
var legend = LegendScript.new(null)
auto_free(legend)
legend.refresh()
assert_bool(legend.visible).is_false()
## Toggling a gen_* overlay on changes the legend's content (the active
## toggle section appears) without crashing — the same "no active toggle by
## default, one section per active toggle" contract the retired
## atlas_window_legend.gd's own refresh() doc described.
func test_refresh_reflects_the_active_overlay_toggle() -> void:
var v: StepCanvasViewer = _make_viewer()
add_child(v)
v.enter({"body_id": "GJ380c", "body_radius_km": 6238.4}, {})
var legend = LegendScript.new(v)
add_child(legend)
legend.refresh()
var rows_before: int = legend.get_implant_children().size()
v.set_overlay_visible("gen_dw_temp", true)
legend.refresh()
var rows_after: int = legend.get_implant_children().size()
assert_int(rows_after).override_failure_message(
"activating a toggle overlay must add its own legend section"
).is_greater(rows_before)
## T-1197 PR #217 review (both reviewers, pixel-proven): this used to assert
## a hardcoded Vector2(PANEL_MARGIN, 60.0) — the same "60.0" constant whose
## drift out of sync with the header panel's REAL grown footprint (once
## step_canvas_viewer.gd wrapped the header in its own ImplantPanel) caused
## the two panels to fuse into one double-height box. The fix removed that
## hardcoded Y from production code (step_canvas_legend.gd's reposition() now
## derives it from StepCanvasViewer.get_header_panel_bottom_y() +
## get_header_legend_gap_px()) — this test must assert the SAME derived
## relationship, not a second copy of the old magic number, or it would go on
## silently enforcing the exact drift-prone shape production code just
## stopped doing (test_step_canvas_viewer.gd's
## test_legend_panel_never_overlaps_the_header_panel_vertically/
## test_legend_panel_leaves_a_real_gap_below_the_header_panel are the fuller
## non-overlap/gap proof against the REAL viewer-owned legend; this one stays
## a narrow smoke check on a standalone legend instance, matching this file's
## own "smoke coverage" scope note at the top).
func test_reposition_derives_y_from_the_headers_measured_bottom_plus_gap() -> void:
var v: StepCanvasViewer = _make_viewer()
add_child(v)
var legend = LegendScript.new(v)
auto_free(legend)
legend.reposition()
var expected_y: float = v.get_header_panel_bottom_y() + v.get_header_legend_gap_px()
assert_that(legend.position).is_equal(Vector2(LegendScript.PANEL_MARGIN, expected_y))
## T-1192 review fix: RESERVED_COLUMN_PX is now a direct read of
## StepCanvasTransport.LEGEND_COLUMN_PX (not an independently-typed
## literal), so a const-vs-const cross-pin test would be structurally
## unable to fail — it is the SAME value by construction. What's still
## worth proving directly is the geometric guarantee that constant is FOR:
## the legend's own ACTUAL laid-out right edge (`reposition()`'s position.x
## + the panel's real minimum width) must land at or before the reserved
## column, with room to spare — never right up against it, and certainly
## never past it into where the canvas is centered from.
func test_legend_actual_right_edge_stays_inside_the_reserved_column() -> void:
var v: StepCanvasViewer = _make_viewer()
add_child(v)
var legend = LegendScript.new(v)
auto_free(legend)
legend.reposition()
var actual_right_edge: float = legend.position.x + legend.custom_minimum_size.x
assert_float(actual_right_edge).override_failure_message(
"the legend's real laid-out right edge must stay inside the column"
+ " StepCanvasViewer reserves for it, with margin to spare"
).is_less(StepCanvasTransport.LEGEND_COLUMN_PX)