feat: lib-core.crafting-display-test v0.1.0 — 8 TAP asserts
Test suite for lib-core.crafting-display v0.1.0. Covers:
1. create returns widget_def with render, handle_input, title set
from opts.
2. register_action + unregister_action round-trip: action-count
goes 0 -> 1 -> 0.
3. Duplicate action label -> Loud-Error matching
'crafting-display.register_action.*duplicate'.
4. Non-function callback -> Loud-Error matching
'crafting-display.register_action.*callback'.
5. Default action-set is empty directly after create.
6. Rows filtered via is_known: only known recipes appear in the
per-frame row-list (one recipe is_known=false is hidden).
7. Per-row availability flag matches can_craft.ok: container with
1x rock makes a recipe needing rock available=true and a
recipe needing missing stick available=false.
8. Resolver overrides (set_icon_resolver / set_label_resolver /
set_summary_resolver) actually take effect — backdoors return
the overridden values.
Uses engine.test.assert with string.find for pattern matching
loud-error messages (engine.test has no assert_match helper).
Per-test isolation via crafting._test_clear_all; composition
templates are defined once at module-scope under namespace
craft_display_test.* (composition has no clear-all hook).
Depends on lib-core.crafting-display 0.1.0, lib-core.crafting
0.1.0, lib-core.panel 0.1.1, lib-core.inventory-list 0.1.0,
lib-core.composition 0.3.0 — all five declared directly per
engine's per-module non-transitive resolver.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
226
init.lua
Normal file
226
init.lua
Normal file
@@ -0,0 +1,226 @@
|
||||
-- lib-core.crafting-display-test — v0.1.0
|
||||
-- 8 assertions covering widget creation, action lifecycle, default-empty,
|
||||
-- row visibility (is_known filter), per-row availability, and resolver overrides.
|
||||
-- Pattern follows P.2.4 Test-Module-Pattern; TAP output via engine.test.*
|
||||
|
||||
local composition = require("lib-core.composition")
|
||||
local inventory = require("lib-core.inventory-list")
|
||||
local crafting = require("lib-core.crafting")
|
||||
local display = require("lib-core.crafting-display")
|
||||
local T = engine.test
|
||||
|
||||
local M = {}
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
-- Template definitions (one-time, shared across tests).
|
||||
-- composition does not expose a clear-all, so we never re-define.
|
||||
-- All template-ids namespaced to this test lib.
|
||||
-- ---------------------------------------------------------------------
|
||||
|
||||
composition.define_template{
|
||||
id = "craft_display_test.container",
|
||||
properties = {
|
||||
sprite_path = "sprites/bag.png",
|
||||
position = {x = 0, y = 0},
|
||||
},
|
||||
tags = {"renderable"},
|
||||
container = { kind = "list" },
|
||||
}
|
||||
|
||||
composition.define_template{
|
||||
id = "craft_display_test.rock",
|
||||
properties = {
|
||||
stack_mode = "individual",
|
||||
sprite_path = "sprites/rock.png",
|
||||
position = {x = 0, y = 0},
|
||||
},
|
||||
tags = {"renderable", "item"},
|
||||
}
|
||||
|
||||
composition.define_template{
|
||||
id = "craft_display_test.stick",
|
||||
properties = {
|
||||
stack_mode = "individual",
|
||||
sprite_path = "sprites/stick.png",
|
||||
position = {x = 0, y = 0},
|
||||
},
|
||||
tags = {"renderable", "item"},
|
||||
}
|
||||
|
||||
composition.define_template{
|
||||
id = "craft_display_test.rock_pick",
|
||||
properties = {
|
||||
stack_mode = "individual",
|
||||
sprite_path = "sprites/pick.png",
|
||||
position = {x = 0, y = 0},
|
||||
},
|
||||
tags = {"renderable", "item"},
|
||||
}
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
-- Fixture helpers
|
||||
-- ---------------------------------------------------------------------
|
||||
|
||||
local function setup_container(items)
|
||||
crafting._test_clear_all()
|
||||
local container = composition.create{ template = "craft_display_test.container" }
|
||||
for _, spec in ipairs(items) do
|
||||
for _ = 1, spec.count do
|
||||
local e = composition.create{ template = spec.template }
|
||||
inventory.add(container, e)
|
||||
end
|
||||
end
|
||||
return container
|
||||
end
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
-- Tests
|
||||
-- ---------------------------------------------------------------------
|
||||
|
||||
function M.run_tests(_ctx)
|
||||
-- ================================================================
|
||||
-- §1: create(container, opts) returns widget_def with render,
|
||||
-- handle_input, title.
|
||||
-- ================================================================
|
||||
crafting._test_clear_all()
|
||||
local container1 = composition.create{ template = "craft_display_test.container" }
|
||||
local widget1 = display.create(container1, { title = "Workbench" })
|
||||
T.assert(widget1 ~= nil
|
||||
and type(widget1.render) == "function"
|
||||
and type(widget1.handle_input) == "function"
|
||||
and widget1.title == "Workbench",
|
||||
"1. create returns widget_def with render, handle_input, title set from opts")
|
||||
|
||||
-- ================================================================
|
||||
-- §2: register_action + unregister_action round-trip (count 0 -> 1 -> 0).
|
||||
-- ================================================================
|
||||
crafting._test_clear_all()
|
||||
local container2 = composition.create{ template = "craft_display_test.container" }
|
||||
local widget2 = display.create(container2)
|
||||
local count_before = #widget2._actions
|
||||
display.register_action(widget2, "Craft", function(_rid, _ctx) end)
|
||||
local count_after_reg = #widget2._actions
|
||||
display.unregister_action(widget2, "Craft")
|
||||
local count_after_unreg = #widget2._actions
|
||||
T.assert(count_before == 0 and count_after_reg == 1 and count_after_unreg == 0,
|
||||
"2. register_action + unregister_action round-trip: count goes 0 -> 1 -> 0")
|
||||
|
||||
-- ================================================================
|
||||
-- §3: Duplicate label -> Loud-Error matching
|
||||
-- 'crafting%-display.register_action.*duplicate'.
|
||||
-- ================================================================
|
||||
crafting._test_clear_all()
|
||||
local container3 = composition.create{ template = "craft_display_test.container" }
|
||||
local widget3 = display.create(container3)
|
||||
display.register_action(widget3, "Craft", function() end)
|
||||
local ok3, err3 = pcall(display.register_action, widget3, "Craft", function() end)
|
||||
T.assert(not ok3 and type(err3) == "string"
|
||||
and string.find(err3, "crafting%-display%.register_action") ~= nil
|
||||
and string.find(err3, "duplicate") ~= nil,
|
||||
"3. duplicate label -> Loud-Error matching 'crafting-display.register_action.*duplicate'")
|
||||
|
||||
-- ================================================================
|
||||
-- §4: Non-function callback -> Loud-Error matching
|
||||
-- 'crafting%-display.register_action.*callback'.
|
||||
-- ================================================================
|
||||
crafting._test_clear_all()
|
||||
local container4 = composition.create{ template = "craft_display_test.container" }
|
||||
local widget4 = display.create(container4)
|
||||
local ok4, err4 = pcall(display.register_action, widget4, "Craft", "not_a_function")
|
||||
T.assert(not ok4 and type(err4) == "string"
|
||||
and string.find(err4, "crafting%-display%.register_action") ~= nil
|
||||
and string.find(err4, "callback") ~= nil,
|
||||
"4. non-function callback -> Loud-Error matching 'crafting-display.register_action.*callback'")
|
||||
|
||||
-- ================================================================
|
||||
-- §5: Default action-set is empty (count=0 directly after create).
|
||||
-- ================================================================
|
||||
crafting._test_clear_all()
|
||||
local container5 = composition.create{ template = "craft_display_test.container" }
|
||||
local widget5 = display.create(container5)
|
||||
T.assert(#widget5._actions == 0,
|
||||
"5. default action-set is empty directly after create")
|
||||
|
||||
-- ================================================================
|
||||
-- §6: Rows filtered via is_known. Two recipes — one with
|
||||
-- is_known=function()->false → only the visible one appears.
|
||||
-- ================================================================
|
||||
local container6 = setup_container{}
|
||||
crafting.define_recipe{
|
||||
id = "visible_r6",
|
||||
inputs = { { template = "craft_display_test.rock", count = 1 } },
|
||||
output = { template = "craft_display_test.rock_pick", count = 1 },
|
||||
name = "Visible Recipe",
|
||||
}
|
||||
crafting.define_recipe{
|
||||
id = "hidden_r6",
|
||||
inputs = { { template = "craft_display_test.rock", count = 1 } },
|
||||
output = { template = "craft_display_test.rock_pick", count = 1 },
|
||||
name = "Hidden Recipe",
|
||||
is_known = function(_) return false end,
|
||||
}
|
||||
local widget6 = display.create(container6)
|
||||
local rows6 = display._test_get_rows(widget6)
|
||||
local found_visible = false
|
||||
local found_hidden = false
|
||||
for _, row in ipairs(rows6) do
|
||||
if row.recipe.id == "visible_r6" then found_visible = true end
|
||||
if row.recipe.id == "hidden_r6" then found_hidden = true end
|
||||
end
|
||||
T.assert(#rows6 == 1 and found_visible and not found_hidden,
|
||||
"6. rows filtered via is_known: only known recipes appear in row-list")
|
||||
|
||||
-- ================================================================
|
||||
-- §7: Per-row availability flag matches can_craft.ok.
|
||||
-- Container with 1x rock → recipe needing rock available=true,
|
||||
-- recipe needing stick (missing) available=false.
|
||||
-- ================================================================
|
||||
local container7 = setup_container{
|
||||
{ template = "craft_display_test.rock", count = 1 },
|
||||
}
|
||||
crafting.define_recipe{
|
||||
id = "have_rock_r7",
|
||||
inputs = { { template = "craft_display_test.rock", count = 1 } },
|
||||
output = { template = "craft_display_test.rock_pick", count = 1 },
|
||||
name = "Rock Recipe",
|
||||
}
|
||||
crafting.define_recipe{
|
||||
id = "need_stick_r7",
|
||||
inputs = { { template = "craft_display_test.stick", count = 1 } },
|
||||
output = { template = "craft_display_test.rock_pick", count = 1 },
|
||||
name = "Stick Recipe",
|
||||
}
|
||||
local widget7 = display.create(container7)
|
||||
local rows7 = display._test_get_rows(widget7)
|
||||
local available_map = {}
|
||||
for _, row in ipairs(rows7) do
|
||||
available_map[row.recipe.id] = row.available
|
||||
end
|
||||
T.assert(#rows7 == 2
|
||||
and available_map["have_rock_r7"] == true
|
||||
and available_map["need_stick_r7"] == false,
|
||||
"7. per-row availability flag matches can_craft.ok")
|
||||
|
||||
-- ================================================================
|
||||
-- §8: Resolver overrides invoked after set_icon/label/summary_resolver.
|
||||
-- ================================================================
|
||||
local container8 = setup_container{}
|
||||
crafting.define_recipe{
|
||||
id = "resolver_r8",
|
||||
inputs = { { template = "craft_display_test.rock", count = 1 } },
|
||||
output = { template = "craft_display_test.rock_pick", count = 1 },
|
||||
name = "Default Name",
|
||||
}
|
||||
local widget8 = display.create(container8)
|
||||
display.set_icon_resolver(widget8, function(_r) return "ICON_X" end)
|
||||
display.set_label_resolver(widget8, function(_r) return "LABEL_X" end)
|
||||
display.set_summary_resolver(widget8, function(_r) return "SUMMARY_X" end)
|
||||
local recipe8 = crafting.get_recipe("resolver_r8")
|
||||
local icon8 = display._test_resolve_icon(widget8, recipe8)
|
||||
local label8 = display._test_resolve_label(widget8, recipe8)
|
||||
local summary8 = display._test_resolve_summary(widget8, recipe8)
|
||||
T.assert(icon8 == "ICON_X" and label8 == "LABEL_X" and summary8 == "SUMMARY_X",
|
||||
"8. resolver overrides (icon/label/summary) invoked after set_*_resolver")
|
||||
end
|
||||
|
||||
return M
|
||||
Reference in New Issue
Block a user