-- lib-core.inventory-list-display-test — v0.1.0 -- 9 assertions covering widget creation, action lifecycle, resolver -- overrides, and content ordering. -- Pattern follows P.2.4 Test-Module-Pattern; TAP output via engine.test.* local panel = require("lib-core.panel") local composition = require("lib-core.composition") local inventory = require("lib-core.inventory-list") local display = require("lib-core.inventory-list-display") local T = engine.test local M = {} -- ---------------------------------------------------------------- -- Template definitions (shared across all assertions in this run) -- All template IDs are unique to this test lib to avoid clashes. -- ---------------------------------------------------------------- -- Container template: kind="list", no constraints. composition.define_template{ id = "test_backpack", properties = { position = {x = 0, y = 0} }, tags = {"renderable"}, container = { kind = "list" }, } -- Item template: individual stack_mode + Atlas-UV properties for icon tests. composition.define_template{ id = "test_item", properties = { stack_mode = "individual", sprite_atlas = "tex.png", sprite_uv = {x = 0, y = 0, w = 8, h = 8}, position = {x = 0, y = 0}, }, tags = {"renderable", "item"}, } function M.run_tests(ctx) local bp = composition.create{template = "test_backpack"} -- ================================================================ -- 1. create returns widget_def with render + handle_input + title -- ================================================================ local widget = display.create(bp, { title = "Backpack" }) T.assert(widget ~= nil, "create returns non-nil") T.equals(type(widget.render), "function", "widget has render fn") T.equals(type(widget.handle_input), "function", "widget has handle_input") T.equals(widget.title, "Backpack", "title set from opts") -- ================================================================ -- 2. create with non-container entity -> loud-error -- test_item has no container block, so composition.get_container -- returns nil, and display.create loud-errors. -- ================================================================ local plain = composition.create{template = "test_item"} local ok2 = pcall(display.create, plain) T.assert(not ok2, "create(non_container) loud-errors") -- ================================================================ -- 3. register_action + unregister_action round-trip (no error) -- ================================================================ display.register_action(widget, "Drop", function(item, c) end) display.register_action(widget, "Inspect", function(item, c) end) display.unregister_action(widget, "Drop") T.assert(true, "register/unregister round-trip completes without error") -- ================================================================ -- 4. Duplicate action label -> loud-error -- "Drop" was unregistered above; re-register then attempt duplicate. -- ================================================================ display.register_action(widget, "Drop", function() end) local ok4 = pcall(display.register_action, widget, "Drop", function() end) T.assert(not ok4, "duplicate action label loud-errors") -- ================================================================ -- 5. Non-function callback -> loud-error -- ================================================================ local ok5 = pcall(display.register_action, widget, "X", "not_fn") T.assert(not ok5, "non-function callback loud-errors") -- ================================================================ -- 6. Default action-set is empty; unregister of unknown label is no-op -- A fresh widget has no implicit actions. Unregistering a missing -- label must not error (idempotent). -- ================================================================ local widget2 = display.create(composition.create{template = "test_backpack"}) display.unregister_action(widget2, "Drop") -- no-op: not registered T.assert(true, "default action-set empty; unregister of unknown label is no-op") -- ================================================================ -- 7. Rows match inventory.contents order (_test_get_rows) -- ================================================================ local item_a = composition.create{template = "test_item"} local item_b = composition.create{template = "test_item"} inventory.add(bp, item_a) inventory.add(bp, item_b) local rows = display._test_get_rows(widget) T.assert(#rows == 2, "rows reflect inventory.contents count") -- Verify ordering: item_a was added first, must appear first. -- Compare by composition.reg_id (stable identity; entity userdata has no __eq). local rid_a = item_a:get_property("composition.reg_id") local rid_r1 = rows[1]:get_property("composition.reg_id") T.assert(rid_a ~= nil and rid_r1 == rid_a, "rows[1] matches item_a (insertion order preserved)") -- ================================================================ -- 8. Default icon-resolver reads sprite_atlas + sprite_uv from properties -- ================================================================ local icon = display._test_resolve_icon(widget, item_a) T.assert(icon ~= nil, "default icon-resolver returns non-nil table") T.equals(icon.atlas, "tex.png", "icon.atlas matches sprite_atlas property") T.equals(icon.uv.x, 0, "icon.uv.x matches sprite_uv.x property") -- ================================================================ -- 9. Custom icon-resolver invoked after set_icon_resolver -- ================================================================ local custom_called = false display.set_icon_resolver(widget, function(item) custom_called = true return {atlas = "custom.png", uv = {x = 1, y = 2, w = 3, h = 4}} end) local icon2 = display._test_resolve_icon(widget, item_a) T.assert(custom_called, "custom icon-resolver was invoked after set_icon_resolver") T.equals(icon2.atlas, "custom.png", "custom resolver returns custom atlas") end return M