-- lib-core.notify-display-test v0.1.0 — 10 assertions -- Exercises: attach_mode (log/toast/detail), validation, toast lifecycle, -- widget factories, detail overwrite, detach cleanup, widget-channel tracking. local notify = require("lib-core.notify") local panel = require("lib-core.panel") local display = require("lib-core.notify-display") local T = engine.test local M = {} -- Helper: create a fresh set of test channels and reset all state local function setup() notify._test_clear_all() -- panel has no _test_clear_all; unregister any widgets we might have registered pcall(panel.unregister, "game-log") pcall(panel.unregister, "detail-panel") pcall(panel.unregister, "log-a") pcall(panel.unregister, "log-b") display._test_reset() end function M.run_tests(_ctx) -- ================================================================== -- 1. attach_mode("log") succeeds with valid panel_widget_id -- ================================================================== setup() notify.create_channel{ id = "log-ch1", filter = {"event"}, display_mode = "log" } local ok_log = pcall(display.attach_mode, "log", { panel_widget_id = "game-log" }) T.assert(ok_log, "attach_mode('log', {panel_widget_id='game-log'}) succeeds") -- ================================================================== -- 2. attach_mode("toast") with invalid position → loud-error -- ================================================================== setup() local ok_bad_pos = pcall(display.attach_mode, "toast", { position = "invalid" }) T.assert(not ok_bad_pos, "attach_mode('toast') with invalid position raises an error") -- ================================================================== -- 3. attach_mode("detail") with valid panel_widget_id succeeds -- ================================================================== setup() notify.create_channel{ id = "det-ch", filter = {"detail-ev"}, display_mode = "detail" } local ok_detail = pcall(display.attach_mode, "detail", { panel_widget_id = "detail-panel" }) T.assert(ok_detail, "attach_mode('detail', {panel_widget_id='detail-panel'}) succeeds") -- ================================================================== -- 4. attach toast → post → _test_get_active_toasts returns 1 entry -- ================================================================== setup() notify.create_channel{ id = "toast-ch", filter = {"hud"}, display_mode = "toast" } display.attach_mode("toast", { position = "top-right", default_ttl = 10.0 }) notify.post{ tags = {"hud"}, text = "alert!" } local toasts = display._test_get_active_toasts() T.equals(#toasts, 1, "post to toast-channel produces 1 active toast") T.equals(toasts[1].msg.text, "alert!", "toast message text is correct") -- ================================================================== -- 5. update(dt) past TTL → toast removed -- ================================================================== -- Test 5: TTL expiry via engine.time monkey-patch. -- INTENTIONALLY no setup() call — depends on the toast posted in test 4 -- (active_toasts is preserved across tests when setup is skipped). -- Reuse state from test 4; toasts[1].spawn_time is 0 (engine.time absent in test) -- Force-expire by calling update with a dt larger than the spawn_time offset. -- spawn_time = 0, ttl = 10.0; now = 0 → not expired. -- We monkey-patch engine.time to return a future timestamp. local saved_engine_time = engine.time engine.time = { now = function() return 100.0 end } display.update(0) engine.time = saved_engine_time toasts = display._test_get_active_toasts() T.equals(#toasts, 0, "update() past TTL removes expired toasts") -- ================================================================== -- 6. create_log_widget returns table with render / handle_input / title -- ================================================================== local log_widget = display.create_log_widget({ widget_id = "game-log", title = "Events" }) T.assert(type(log_widget) == "table", "create_log_widget returns a table") T.assert(type(log_widget.render) == "function","log widget_def has render function") T.assert(type(log_widget.handle_input) == "function", "log widget_def has handle_input function") T.equals(log_widget.title, "Events", "log widget title is set from opts") -- ================================================================== -- 7. create_detail_widget + post → _test_get_last_detail returns msg -- ================================================================== setup() notify.create_channel{ id = "dw-ch", filter = {"detail-ev"}, display_mode = "detail" } local det_widget = display.create_detail_widget({ widget_id = "detail-panel", title = "Info" }) T.assert(type(det_widget) == "table", "create_detail_widget returns a table") T.assert(type(det_widget.render) == "function","detail widget_def has render function") -- Register widget first so that panel.open inside the subscribe callback succeeds panel.register("detail-panel", det_widget) display.attach_mode("detail", { panel_widget_id = "detail-panel" }) notify.post{ tags = {"detail-ev"}, text = "important!" } local last = display._test_get_last_detail() T.assert(last ~= nil, "_test_get_last_detail is set after detail post") T.equals(last.text, "important!", "detail message text is correct") T.assert(panel.is_open(), "panel is open after detail message auto-open") -- ================================================================== -- 8. Two log-channels mapped to same panel_widget_id → 2 entries tracked -- ================================================================== setup() notify.create_channel{ id = "log-a", filter = {"ev-a"}, display_mode = "log" } notify.create_channel{ id = "log-b", filter = {"ev-b"}, display_mode = "log" } display.attach_mode("log", { panel_widget_id = "game-log" }) local tracked = display._test_get_widget_channels("game-log") T.equals(#tracked, 2, "two log-channels both tracked under same panel_widget_id") -- ================================================================== -- 9. detach_mode("toast") removes toast-channel subscriptions -- ================================================================== setup() notify.create_channel{ id = "toast-a", filter = {"t"}, display_mode = "toast" } display.attach_mode("toast", {}) local count_before = display._test_subscriber_count() display.detach_mode("toast") local count_after = display._test_subscriber_count() T.assert(count_before > count_after, "detach_mode('toast') reduces subscriber count") T.equals(count_after, 0, "subscriber count is 0 after detach_mode('toast')") -- ================================================================== -- 10. Detail overwrite: post msg-A then msg-B → last_detail == B -- ================================================================== setup() notify.create_channel{ id = "ow-ch", filter = {"ow"}, display_mode = "detail" } local ow_widget = display.create_detail_widget({ widget_id = "detail-panel", title = "D" }) panel.register("detail-panel", ow_widget) display.attach_mode("detail", { panel_widget_id = "detail-panel" }) notify.post{ tags = {"ow"}, text = "A" } notify.post{ tags = {"ow"}, text = "B" } local overwritten = display._test_get_last_detail() T.equals(overwritten.text, "B", "second detail post overwrites first (_test_get_last_detail.text == 'B')") end return M