Files
settled-reach/client/tests/test_interaction_list.gd
T
jpmschweitzerandClaude Opus 4.6 4eb54a6f7a fix(client): resolve all gdlint warnings — zero warnings policy
Fix 354 gdlint warnings across 65 files: 194 class-definitions-order
(reorder declarations), 138 max-line-length (split long lines),
22 code issues (unused args, no-else-return, naming). Update .gdlintrc
to exclude addons/ and raise max-public-methods for test files.
No logic changes — declaration order, whitespace, and naming only.

Ticket: #783

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 09:59:56 +02:00

352 lines
11 KiB
GDScript

## D-057: Entity interaction vertical list tests (#432).
## Tests multi-verb vertical list rendering, insert styling, z-layer 6,
## verb click dispatch, and D-055 sprint suppression integration.
##
## Also covers stance toggle input encoding (D-053, #439).
## Spec refs: D-057, D-056, D-055, D-053, D-049
class_name TestInteractionList
extends GdUnitTestSuite
# -- Helpers -------------------------------------------------------------------
func _interaction_list_exists() -> bool:
for path in ["res://ui/interaction_list.tscn", "res://ui/entity_interaction_list.tscn",
"res://scenes/interaction_list.tscn"]:
if ResourceLoader.exists(path):
return true
return false
func _make_interaction_list() -> Node:
for path in ["res://ui/interaction_list.tscn", "res://ui/entity_interaction_list.tscn",
"res://scenes/interaction_list.tscn"]:
if ResourceLoader.exists(path):
var scene = load(path)
var node = scene.instantiate()
add_child(node)
return node
return null
func _make_list_or_skip() -> Node:
var list = _make_interaction_list()
if list == null:
push_warning("TestInteractionList: interaction list scene not found — test skipped (awaiting #432)")
return list
# -- Multi-verb rendering (D-057: 2-4 options max) ----------------------------
func test_list_renders_two_verbs() -> void:
var list = _make_list_or_skip()
if list == null:
return
GameState.nearby_interactions = [{
"entity_id": 2, "entity_type": "Npc", "distance": 1,
"verbs": [
{"kind": "Talk", "label": "Talk", "priority": 1, "available": true},
{"kind": "ExamineNpc", "label": "Observe", "priority": 2, "available": true},
],
}]
if list.has_method("update_from_state"):
list.update_from_state()
if list.has_method("get_visible_verb_count"):
assert_that(list.get_visible_verb_count()).is_equal(2)
list.queue_free()
GameState.nearby_interactions = []
func test_list_renders_four_verbs_max() -> void:
var list = _make_list_or_skip()
if list == null:
return
# D-057: "2-4 options max"
GameState.nearby_interactions = [{
"entity_id": 2, "entity_type": "Npc", "distance": 1,
"verbs": [
{"kind": "Talk", "label": "Talk", "priority": 1, "available": true},
{"kind": "ExamineNpc", "label": "Observe", "priority": 2, "available": true},
{"kind": "Confront", "label": "Confront", "priority": 3, "available": true},
{"kind": "Observe", "label": "Look at", "priority": 4, "available": true},
],
}]
if list.has_method("update_from_state"):
list.update_from_state()
if list.has_method("get_visible_verb_count"):
assert_that(list.get_visible_verb_count()).is_less_equal(4)
list.queue_free()
GameState.nearby_interactions = []
func test_list_empty_when_no_interactions() -> void:
var list = _make_list_or_skip()
if list == null:
return
GameState.nearby_interactions = []
if list.has_method("update_from_state"):
list.update_from_state()
if list.has_method("get_visible_verb_count"):
assert_that(list.get_visible_verb_count()).is_equal(0)
list.queue_free()
func test_list_hidden_when_no_interactions() -> void:
var list = _make_list_or_skip()
if list == null:
return
GameState.nearby_interactions = []
if list.has_method("update_from_state"):
list.update_from_state()
if list.has_method("is_showing"):
assert_that(list.is_showing()).is_false()
list.queue_free()
# -- Verb click dispatch (D-057) -----------------------------------------------
func test_get_selected_verb_returns_kind() -> void:
var list = _make_list_or_skip()
if list == null:
return
GameState.nearby_interactions = [{
"entity_id": 2, "entity_type": "Npc", "distance": 1,
"verbs": [
{"kind": "Talk", "label": "Talk", "priority": 1, "available": true},
],
}]
if list.has_method("get_selected_verb"):
# Default selection should be the first (highest priority) verb
assert_that(list.get_selected_verb()).is_equal("Talk")
list.queue_free()
GameState.nearby_interactions = []
func test_get_interaction_target_returns_entity_id() -> void:
var list = _make_list_or_skip()
if list == null:
return
GameState.nearby_interactions = [{
"entity_id": 42, "entity_type": "Npc", "distance": 1,
"verbs": [
{"kind": "Talk", "label": "Talk", "priority": 1, "available": true},
],
}]
if list.has_method("get_interaction_target"):
assert_that(list.get_interaction_target()).is_equal(42)
list.queue_free()
GameState.nearby_interactions = []
# -- Verb priority ordering (D-057: sorted by priority) -----------------------
func test_verbs_sorted_by_priority() -> void:
var list = _make_list_or_skip()
if list == null:
return
GameState.nearby_interactions = [{
"entity_id": 2, "entity_type": "Npc", "distance": 1,
"verbs": [
{"kind": "ExamineNpc", "label": "Observe", "priority": 3, "available": true},
{"kind": "Talk", "label": "Talk", "priority": 1, "available": true},
{"kind": "Confront", "label": "Confront", "priority": 2, "available": true},
],
}]
if list.has_method("update_from_state") and list.has_method("get_verb_labels"):
list.update_from_state()
var labels = list.get_verb_labels()
# Should be sorted: Talk (1), Confront (2), Observe (3)
if labels.size() >= 3:
assert_that(labels[0]).is_equal("Talk")
assert_that(labels[1]).is_equal("Confront")
assert_that(labels[2]).is_equal("Observe")
list.queue_free()
GameState.nearby_interactions = []
# -- D-055: Sprint suppression ------------------------------------------------
func test_sprint_suppresses_interaction_list() -> void:
# D-055: Sprint stance clears interaction buffer — no verbs should show
var list = _make_list_or_skip()
if list == null:
return
# Set up interactions
GameState.nearby_interactions = [{
"entity_id": 2, "entity_type": "Npc", "distance": 1,
"verbs": [
{"kind": "Talk", "label": "Talk", "priority": 1, "available": true},
],
}]
# Set stance to Sprint
GameState.player_stance = "Sprint"
if list.has_method("update_from_state"):
list.update_from_state()
# The list should be hidden when sprinting
if list.has_method("is_showing"):
assert_that(list.is_showing()).is_false()
elif list.has_method("get_visible_verb_count"):
assert_that(list.get_visible_verb_count()).is_equal(0)
list.queue_free()
GameState.nearby_interactions = []
GameState.player_stance = "Walk"
func test_walk_shows_interaction_list() -> void:
var list = _make_list_or_skip()
if list == null:
return
GameState.nearby_interactions = [{
"entity_id": 2, "entity_type": "Npc", "distance": 1,
"verbs": [
{"kind": "Talk", "label": "Talk", "priority": 1, "available": true},
],
}]
GameState.player_stance = "Walk"
if list.has_method("update_from_state"):
list.update_from_state()
if list.has_method("is_showing"):
assert_that(list.is_showing()).is_true()
list.queue_free()
GameState.nearby_interactions = []
func test_careful_shows_interaction_list() -> void:
var list = _make_list_or_skip()
if list == null:
return
GameState.nearby_interactions = [{
"entity_id": 2, "entity_type": "Npc", "distance": 1,
"verbs": [
{"kind": "Talk", "label": "Talk", "priority": 1, "available": true},
],
}]
GameState.player_stance = "Careful"
if list.has_method("update_from_state"):
list.update_from_state()
if list.has_method("is_showing"):
assert_that(list.is_showing()).is_true()
list.queue_free()
GameState.nearby_interactions = []
GameState.player_stance = "Walk"
# -- Z-layer compliance (D-049: interaction list on layer 6) ------------------
func test_interaction_list_z_layer() -> void:
var list = _make_list_or_skip()
if list == null:
return
# D-057: Labels render on insert overlay (CanvasLayer 10)
if list.has_method("get_z_layer"):
assert_that(list.get_z_layer()).is_equal(Constants.CANVAS_INSERT)
list.queue_free()
# -- Diegetic test (D-057: insert off → labels disappear) --------------------
func test_insert_off_hides_interaction_list() -> void:
var list = _make_list_or_skip()
if list == null:
return
# D-057: "If insert is off, labels disappear"
GameState.nearby_interactions = [{
"entity_id": 2, "entity_type": "Npc", "distance": 1,
"verbs": [
{"kind": "Talk", "label": "Talk", "priority": 1, "available": true},
],
}]
if list.has_method("set_insert_active"):
list.set_insert_active(false)
if list.has_method("is_showing"):
assert_that(list.is_showing()).is_false()
list.queue_free()
GameState.nearby_interactions = []
# -- Stance toggle wire mapping (D-053) ----------------------------------------
func test_stance_up_wire_mapping() -> void:
# Verify InputMapper.Action.TOGGLE_STANCE_UP maps to "ToggleStanceUp" wire name
var wire = SimBridge.action_enum_to_wire(InputMapper.Action.TOGGLE_STANCE_UP)
assert_that(wire).is_equal("ToggleStanceUp")
func test_stance_down_wire_mapping() -> void:
var wire = SimBridge.action_enum_to_wire(InputMapper.Action.TOGGLE_STANCE_DOWN)
assert_that(wire).is_equal("ToggleStanceDown")
func test_all_movement_actions_have_wire_mapping() -> void:
# Verify no action produces an empty wire name (except OPEN_MENU which is client-only)
var actions_with_mapping := [
InputMapper.Action.MOVE_NORTH,
InputMapper.Action.MOVE_NORTHEAST,
InputMapper.Action.MOVE_EAST,
InputMapper.Action.MOVE_SOUTHEAST,
InputMapper.Action.MOVE_SOUTH,
InputMapper.Action.MOVE_SOUTHWEST,
InputMapper.Action.MOVE_WEST,
InputMapper.Action.MOVE_NORTHWEST,
InputMapper.Action.INTERACT,
InputMapper.Action.USE_PERCEPTION_MODE,
InputMapper.Action.PAUSE,
InputMapper.Action.TOGGLE_STANCE_UP,
InputMapper.Action.TOGGLE_STANCE_DOWN,
]
for action in actions_with_mapping:
var wire = SimBridge.action_enum_to_wire(action)
assert_that(wire.length()).is_greater(0)
# -- Contradicted entity indicator (D-057 + #422) ----------------------------
func test_contradicted_entity_verbs_decode() -> void:
# #422: NearbyInteraction.contradicted=true should be decodeable
var raw := {
"tick": 1,
"version": Protocol.PROTOCOL_VERSION,
"entities": [],
"nearby_interactions": [{
"entity_id": 2,
"entity_type": "Npc",
"distance": 1,
"verbs": [{"kind": "Talk", "label": "Talk", "priority": 1, "available": true}],
"contradicted": true,
}],
}
var encoded = Messagepack.encode(raw)
var snapshot = Protocol.decode_snapshot(encoded.value)
assert_that(snapshot).is_not_null()
assert_that(snapshot.nearby_interactions.size()).is_equal(1)
# Note: contradicted field may not be decoded yet in protocol.gd
# This test documents the expected behavior for #422 integration
# -- Object type verb sets (D-057 Phase 1) ------------------------------------
func test_object_type_verbs_decode() -> void:
# #421: ObjectType appears in NearbyInteraction.object_type
var raw := {
"tick": 1,
"version": Protocol.PROTOCOL_VERSION,
"entities": [],
"nearby_interactions": [{
"entity_id": 3,
"entity_type": "Object",
"distance": 1,
"verbs": [
{"kind": "Open", "label": "Open", "priority": 1, "available": true},
{"kind": "Search", "label": "Search", "priority": 2, "available": true},
],
"object_type": "Container",
}],
}
var encoded = Messagepack.encode(raw)
var snapshot = Protocol.decode_snapshot(encoded.value)
assert_that(snapshot).is_not_null()
assert_that(snapshot.nearby_interactions.size()).is_equal(1)
assert_that(snapshot.nearby_interactions[0].verbs.size()).is_equal(2)
assert_that(snapshot.nearby_interactions[0].verbs[0].kind).is_equal("Open")
assert_that(snapshot.nearby_interactions[0].verbs[1].kind).is_equal("Search")