Fix test API mismatches: time display tests target InsertOverlay/ TimeDisplay and time_display.gd; E-Talk tests rewritten to target interaction_list.gd _cache_entity_relationship() and _relationship_color state. Phase 2 tests (name, tier hint) marked as skip placeholders. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
299 lines
11 KiB
GDScript
299 lines
11 KiB
GDScript
## Sprint 17 — UX: E-Talk overlay improvement (#537) — Phase 1
|
|
## Tests for relationship color indicator in interaction_list.gd.
|
|
##
|
|
## Spec refs:
|
|
## D-033 (entity color = relationship to player)
|
|
## D-051 (diegetic insert display — insert-only overlay)
|
|
## D-057 (interaction list, z-layer 6)
|
|
##
|
|
## Phase 1 scope (2026-02-24):
|
|
## Color bar only — NPC name and dialogue tier deferred to Phase 2 pending
|
|
## server protocol change (no known_attributes in wire protocol v13).
|
|
##
|
|
## Implementation: client/ui/interaction_list.gd
|
|
## `var _relationship_color: Color = Constants.IMPLANT_TEXT_DIM`
|
|
## `func _cache_entity_relationship() -> void`
|
|
## 3px left-edge bar drawn in _draw() at 85% alpha, called from update_from_state()
|
|
class_name TestETalkOverlaySprint17
|
|
extends GdUnitTestSuite
|
|
|
|
|
|
func before_test() -> void:
|
|
SimBridge.reset_test_state()
|
|
GameState.nearby_interactions = []
|
|
GameState.visible_entities = []
|
|
GameState.player_stance = ""
|
|
|
|
|
|
func after_test() -> void:
|
|
GameState.nearby_interactions = []
|
|
GameState.visible_entities = []
|
|
GameState.player_stance = ""
|
|
|
|
|
|
# -------------------------------------------------------------------------
|
|
# D-033: Constants.color_for_relationship() — palette baseline (pure logic)
|
|
# -------------------------------------------------------------------------
|
|
|
|
func test_relationship_unknown_maps_to_teal() -> void:
|
|
assert_that(Constants.color_for_relationship("Unknown")).is_equal(Constants.ENTITY_COLOR_UNKNOWN)
|
|
|
|
func test_relationship_friendly_maps_to_green() -> void:
|
|
assert_that(Constants.color_for_relationship("Friendly")).is_equal(Constants.ENTITY_COLOR_FRIENDLY)
|
|
|
|
func test_relationship_poi_maps_to_amber() -> void:
|
|
assert_that(Constants.color_for_relationship("PersonOfInterest")).is_equal(Constants.ENTITY_COLOR_POI)
|
|
|
|
func test_relationship_hostile_maps_to_red() -> void:
|
|
assert_that(Constants.color_for_relationship("Hostile")).is_equal(Constants.ENTITY_COLOR_HOSTILE)
|
|
|
|
func test_relationship_known_falls_back_to_unknown_color() -> void:
|
|
# "Known" not matched in color_for_relationship() — falls through _ → ENTITY_COLOR_UNKNOWN
|
|
assert_that(Constants.color_for_relationship("Known")).is_equal(Constants.ENTITY_COLOR_UNKNOWN)
|
|
|
|
func test_relationship_unknown_string_falls_back_to_unknown() -> void:
|
|
assert_that(Constants.color_for_relationship("SomeNewState")).is_equal(Constants.ENTITY_COLOR_UNKNOWN)
|
|
|
|
|
|
# -------------------------------------------------------------------------
|
|
# UIStrings: relationship state labels exist (Phase 2 pre-fixture)
|
|
# -------------------------------------------------------------------------
|
|
|
|
func test_ui_strings_has_relationship_unknown_label() -> void:
|
|
assert_that(UIStrings.has_key("relationship_states.unknown.label")).is_true()
|
|
|
|
func test_ui_strings_has_relationship_known_label() -> void:
|
|
assert_that(UIStrings.has_key("relationship_states.known.label")).is_true()
|
|
|
|
func test_ui_strings_has_relationship_friendly_label() -> void:
|
|
assert_that(UIStrings.has_key("relationship_states.friendly.label")).is_true()
|
|
|
|
func test_ui_strings_has_relationship_poi_label() -> void:
|
|
assert_that(UIStrings.has_key("relationship_states.person_of_interest.label")).is_true()
|
|
|
|
func test_ui_strings_has_relationship_hostile_label() -> void:
|
|
assert_that(UIStrings.has_key("relationship_states.hostile.label")).is_true()
|
|
|
|
|
|
# -------------------------------------------------------------------------
|
|
# _relationship_color initial state
|
|
# -------------------------------------------------------------------------
|
|
|
|
func test_relationship_color_default_is_implant_dim() -> void:
|
|
# Before any update, _relationship_color starts at IMPLANT_TEXT_DIM
|
|
var list = _make_list()
|
|
assert_that(list._relationship_color).is_equal(Constants.IMPLANT_TEXT_DIM)
|
|
list.queue_free()
|
|
|
|
|
|
# -------------------------------------------------------------------------
|
|
# _cache_entity_relationship(): D-033 palette via update_from_state() (#537 core)
|
|
# -------------------------------------------------------------------------
|
|
|
|
func test_cache_relationship_unknown_sets_unknown_color() -> void:
|
|
_setup_npc_interaction(2, "Unknown")
|
|
var list = _make_list()
|
|
list.update_from_state()
|
|
assert_that(list._relationship_color).is_equal(Constants.ENTITY_COLOR_UNKNOWN)
|
|
list.queue_free()
|
|
|
|
func test_cache_relationship_friendly_sets_green() -> void:
|
|
_setup_npc_interaction(2, "Friendly")
|
|
var list = _make_list()
|
|
list.update_from_state()
|
|
assert_that(list._relationship_color).is_equal(Constants.ENTITY_COLOR_FRIENDLY)
|
|
list.queue_free()
|
|
|
|
func test_cache_relationship_poi_sets_amber() -> void:
|
|
_setup_npc_interaction(2, "PersonOfInterest")
|
|
var list = _make_list()
|
|
list.update_from_state()
|
|
assert_that(list._relationship_color).is_equal(Constants.ENTITY_COLOR_POI)
|
|
list.queue_free()
|
|
|
|
func test_cache_relationship_hostile_sets_red() -> void:
|
|
_setup_npc_interaction(2, "Hostile")
|
|
var list = _make_list()
|
|
list.update_from_state()
|
|
assert_that(list._relationship_color).is_equal(Constants.ENTITY_COLOR_HOSTILE)
|
|
list.queue_free()
|
|
|
|
func test_cache_relationship_absent_field_uses_unknown_color() -> void:
|
|
# Entity present but "relationship" key missing → get("relationship", "Unknown") → Unknown color
|
|
GameState.visible_entities = [{
|
|
"entity_id": 2, "x": 12.0, "y": 9.0, "z": 0,
|
|
"kind": {"variant": "Npc", "data": null},
|
|
"visibility": "Forward",
|
|
}]
|
|
GameState.nearby_interactions = [{
|
|
"entity_id": 2, "entity_type": "Npc", "distance": 1,
|
|
"verbs": [{"kind": "Talk", "label": "Talk", "priority": 1, "available": true}],
|
|
}]
|
|
var list = _make_list()
|
|
list.update_from_state()
|
|
assert_that(list._relationship_color).is_equal(Constants.ENTITY_COLOR_UNKNOWN)
|
|
list.queue_free()
|
|
|
|
func test_cache_relationship_entity_not_in_visible_uses_dim_fallback() -> void:
|
|
# Entity in nearby_interactions but NOT in visible_entities → IMPLANT_TEXT_DIM fallback
|
|
GameState.visible_entities = []
|
|
GameState.nearby_interactions = [{
|
|
"entity_id": 2, "entity_type": "Npc", "distance": 1,
|
|
"verbs": [{"kind": "Talk", "label": "Talk", "priority": 1, "available": true}],
|
|
}]
|
|
var list = _make_list()
|
|
list.update_from_state()
|
|
assert_that(list._relationship_color).is_equal(Constants.IMPLANT_TEXT_DIM)
|
|
list.queue_free()
|
|
|
|
func test_cache_relationship_updates_when_relationship_changes() -> void:
|
|
# First call: Unknown
|
|
_setup_npc_interaction(2, "Unknown")
|
|
var list = _make_list()
|
|
list.update_from_state()
|
|
assert_that(list._relationship_color).is_equal(Constants.ENTITY_COLOR_UNKNOWN)
|
|
|
|
# Relationship shifts → re-cache picks up new value
|
|
_setup_npc_interaction(2, "Hostile")
|
|
list.update_from_state()
|
|
assert_that(list._relationship_color).is_equal(Constants.ENTITY_COLOR_HOSTILE)
|
|
list.queue_free()
|
|
|
|
func test_cache_relationship_targets_correct_entity_by_id() -> void:
|
|
# Two entities visible; nearby_interactions[0] is the target (entity 2, Friendly)
|
|
# Entity 3 (Hostile) must not pollute the color
|
|
GameState.visible_entities = [
|
|
{
|
|
"entity_id": 2, "x": 11.0, "y": 9.0, "z": 0,
|
|
"kind": {"variant": "Npc", "data": null},
|
|
"visibility": "Forward", "relationship": "Friendly", "observation": "Visible",
|
|
},
|
|
{
|
|
"entity_id": 3, "x": 12.0, "y": 9.0, "z": 0,
|
|
"kind": {"variant": "Npc", "data": null},
|
|
"visibility": "Forward", "relationship": "Hostile", "observation": "Visible",
|
|
},
|
|
]
|
|
GameState.nearby_interactions = [
|
|
{
|
|
"entity_id": 2, "entity_type": "Npc", "distance": 1,
|
|
"verbs": [{"kind": "Talk", "label": "Talk", "priority": 1, "available": true}],
|
|
},
|
|
{
|
|
"entity_id": 3, "entity_type": "Npc", "distance": 2,
|
|
"verbs": [{"kind": "Talk", "label": "Talk", "priority": 1, "available": true}],
|
|
},
|
|
]
|
|
var list = _make_list()
|
|
list.update_from_state()
|
|
# interaction[0] = entity 2 (Friendly) → green bar
|
|
assert_that(list._relationship_color).is_equal(Constants.ENTITY_COLOR_FRIENDLY)
|
|
list.queue_free()
|
|
|
|
func test_z_layer_is_insert_canvas() -> void:
|
|
# D-049 / D-057: interaction list lives on InsertOverlay (CanvasLayer 10)
|
|
var list = _make_list()
|
|
assert_that(list.get_z_layer()).is_equal(Constants.CANVAS_INSERT)
|
|
list.queue_free()
|
|
|
|
|
|
# -------------------------------------------------------------------------
|
|
# Regression: interaction_list public API unaffected by #537 changes
|
|
# -------------------------------------------------------------------------
|
|
|
|
func test_list_hides_when_no_interactions() -> void:
|
|
GameState.nearby_interactions = []
|
|
var list = _make_list()
|
|
list.update_from_state()
|
|
assert_that(list.is_showing()).is_false()
|
|
list.queue_free()
|
|
|
|
func test_list_shows_on_npc_interaction() -> void:
|
|
_setup_npc_interaction(2, "Unknown")
|
|
var list = _make_list()
|
|
list.update_from_state()
|
|
assert_that(list.is_showing()).is_true()
|
|
list.queue_free()
|
|
|
|
func test_list_get_selected_verb_returns_first_verb() -> void:
|
|
GameState.nearby_interactions = [{
|
|
"entity_id": 2, "entity_type": "Npc", "distance": 1,
|
|
"verbs": [
|
|
{"kind": "Talk", "label": "Talk", "priority": 1, "available": true},
|
|
{"kind": "ExamineNpc", "label": "Look", "priority": 2, "available": true},
|
|
],
|
|
}]
|
|
var list = _make_list()
|
|
assert_that(list.get_selected_verb()).is_equal("Talk")
|
|
list.queue_free()
|
|
|
|
func test_list_get_interaction_target_returns_entity_id() -> void:
|
|
GameState.visible_entities = [{
|
|
"entity_id": 5, "x": 12.0, "y": 9.0, "z": 0,
|
|
"kind": {"variant": "Npc", "data": null},
|
|
"visibility": "Forward", "relationship": "Unknown", "observation": "Visible",
|
|
}]
|
|
GameState.nearby_interactions = [{
|
|
"entity_id": 5, "entity_type": "Npc", "distance": 1,
|
|
"verbs": [{"kind": "Talk", "label": "Talk", "priority": 1, "available": true}],
|
|
}]
|
|
var list = _make_list()
|
|
list.update_from_state()
|
|
assert_that(list.get_interaction_target()).is_equal(5)
|
|
list.queue_free()
|
|
|
|
func test_list_suppressed_when_insert_inactive() -> void:
|
|
_setup_npc_interaction(2, "Unknown")
|
|
var list = _make_list()
|
|
list.set_insert_active(false)
|
|
list.update_from_state()
|
|
assert_that(list.is_showing()).is_false()
|
|
list.queue_free()
|
|
|
|
func test_list_hides_on_empty_verb_list() -> void:
|
|
GameState.nearby_interactions = [{
|
|
"entity_id": 2, "entity_type": "Npc", "distance": 1, "verbs": [],
|
|
}]
|
|
var list = _make_list()
|
|
list.update_from_state()
|
|
assert_that(list.is_showing()).is_false()
|
|
list.queue_free()
|
|
|
|
|
|
# -------------------------------------------------------------------------
|
|
# Phase 2 placeholders — deferred pending server protocol change (no known_attributes in v13)
|
|
# -------------------------------------------------------------------------
|
|
|
|
func skip_test_npc_name_displayed_when_known() -> void:
|
|
pass
|
|
|
|
func skip_test_dialogue_tier_context_hint_for_friendly() -> void:
|
|
pass
|
|
|
|
func skip_test_dialogue_tier_context_hint_for_hostile() -> void:
|
|
pass
|
|
|
|
|
|
# -------------------------------------------------------------------------
|
|
# Helpers
|
|
# -------------------------------------------------------------------------
|
|
|
|
func _make_list() -> Control:
|
|
var scene = load("res://ui/interaction_list.tscn")
|
|
var list = scene.instantiate()
|
|
add_child(list) # _ready() fires here — @onready var _vbox resolves
|
|
return list
|
|
|
|
|
|
## Set up GameState with a single NPC entity + matching interaction for tests.
|
|
func _setup_npc_interaction(entity_id: int, relationship: String) -> void:
|
|
GameState.visible_entities = [{
|
|
"entity_id": entity_id, "x": 12.0, "y": 9.0, "z": 0,
|
|
"kind": {"variant": "Npc", "data": null},
|
|
"visibility": "Forward", "relationship": relationship, "observation": "Visible",
|
|
}]
|
|
GameState.nearby_interactions = [{
|
|
"entity_id": entity_id, "entity_type": "Npc", "distance": 1,
|
|
"verbs": [{"kind": "Talk", "label": "Talk", "priority": 1, "available": true}],
|
|
}]
|