Files
gen1recomp/tests/game3_shop_menu_test.lua
bryanthaboi dee935a4ea CLOSES #2430
2026-09-23 11:58:32 -04:00

231 lines
9.2 KiB
Lua

-- Automated test suite for Game 3 Poké Mart system (shop_menu.lua, marts.lua, bag.lua).
require("tests.game3_cache").requireData("game3_shop_menu_test")
local ShopMenu = require("src.ui.game3.shop_menu")
local Bag = require("src.core.game3.bag")
local ItemsData = require("src.core.game3.items_data")
local Marts = require("src.core.game3.marts")
local Adapters = require("src.core.game3.scripting.adapters")
local Message = require("src.ui.game3.message")
local Stack = require("src.ui.game3.stack")
local function make_input(pressedMap)
pressedMap = pressedMap or {}
return {
wasPressed = function(self, key)
return pressedMap[key] == true
end,
isDown = function(self, key)
return pressedMap[key] == true
end,
}
end
local function assert_eq(a, b, msg)
if a ~= b then
error(string.format("ASSERTION FAILED: %s (expected %s, got %s)", msg or "", tostring(b), tostring(a)), 2)
end
end
local function assert_true(val, msg)
if not val then
error(string.format("ASSERTION FAILED: %s (expected true, got %s)", msg or "", tostring(val)), 2)
end
end
print("=== [TEST 1] Marts data lookup ===")
Marts.ensure()
local viridianItems = Marts.itemsFor(135731380) or Marts.itemsFor("g3:081649b8") or Marts.itemsFor(135678392)
assert_true(viridianItems ~= nil and #viridianItems > 0, "Marts.itemsFor resolved stock list")
print("Resolved mart items count:", #viridianItems)
print("=== [TEST 2] ShopMenu Opening and Root Navigation ===")
local closed = false
local session = {
money = 5000,
bag = Bag.new(),
}
ShopMenu.show({
items = { 4, 13, 19, 20 }, -- POKE_BALL, POTION, ANTIDOTE, PARALYZE_HEAL
session = session,
onClose = function() closed = true end,
})
assert_true(ShopMenu.isOpen(), "Shop is open")
assert_eq(ShopMenu.mode, "root", "Initial mode is root")
assert_eq(ShopMenu.cursor, 1, "Initial cursor on BUY")
-- Navigate root menu
ShopMenu.handleInput(make_input({ down = true }))
assert_eq(ShopMenu.cursor, 2, "Cursor moved to SELL")
ShopMenu.handleInput(make_input({ down = true }))
assert_eq(ShopMenu.cursor, 3, "Cursor moved to SEE YA!")
ShopMenu.handleInput(make_input({ down = true }))
assert_eq(ShopMenu.cursor, 1, "Cursor wrapped back to BUY")
print("=== [TEST 3] Enter Buy Menu & Quantity Fast-Scroll ===")
ShopMenu.handleInput(make_input({ a = true }))
assert_eq(ShopMenu.mode, "buy", "Entered BUY mode")
assert_eq(ShopMenu.cursor, 1, "Cursor at first item (POKE_BALL)")
-- Select Poké Ball (ID 4, price 200)
ShopMenu.handleInput(make_input({ a = true }))
assert_eq(ShopMenu.mode, "buy_qty", "Entered buy_qty mode")
assert_eq(ShopMenu.qty, 1, "Initial quantity is 1")
-- Fast scroll: Right (+10) -> qty = 11
ShopMenu.handleInput(make_input({ right = true }))
assert_eq(ShopMenu.qty, 11, "Fast-scrolled right to 11")
-- Left (-10) -> qty = 1
ShopMenu.handleInput(make_input({ left = true }))
assert_eq(ShopMenu.qty, 1, "Fast-scrolled left to 1")
-- Set qty to 10
ShopMenu.handleInput(make_input({ right = true }))
ShopMenu.handleInput(make_input({ down = true }))
assert_eq(ShopMenu.qty, 10, "Qty set to 10")
print("=== [TEST 4] Buy Confirmation ===")
-- Press A to confirm quantity -> mode is buy_confirm
ShopMenu.handleInput(make_input({ a = true }))
assert_eq(ShopMenu.mode, "buy_confirm", "Entered buy_confirm mode")
assert_eq(ShopMenu.yesNoCursor, 1, "YES selected")
-- Confirm purchase: 10 Poké Balls @ 200 = 2000
ShopMenu.handleInput(make_input({ a = true }))
assert_eq(ShopMenu.mode, "buy_msg", "Entered buy_msg mode with success message")
assert_eq(session.money, 3000, "Money deducted correctly (5000 - 2000 = 3000)")
assert_eq(Bag.get(session.bag, 4), 10, "Bag received 10 Poké Balls")
assert_eq(Bag.get(session.bag, 12), 0, "FRLG gives no Premier Ball bonus (shop.c:978 BuyMenuTryMakePurchase)")
assert_eq(ShopMenu._status, "Here you are!\nThank you!", "Status is gText_HereYouGoThankYou")
-- Dismiss message with A
ShopMenu.handleInput(make_input({ a = true }))
assert_eq(ShopMenu.mode, "buy", "Returned to buy mode")
print("=== [TEST 5] 99 Poké Balls ===")
session.money = 50000
ShopMenu.cursor = 1 -- Poké Ball
ShopMenu.handleInput(make_input({ a = true }))
assert_eq(ShopMenu.mode, "buy_qty", "Entered buy_qty mode")
-- Fast scroll to 99
for _ = 1, 10 do
ShopMenu.handleInput(make_input({ right = true }))
end
assert_eq(ShopMenu.qty, 99, "Qty clamped at 99")
-- Confirm quantity
ShopMenu.handleInput(make_input({ a = true }))
assert_eq(ShopMenu.mode, "buy_confirm", "Entered buy_confirm")
-- Confirm buy
ShopMenu.handleInput(make_input({ a = true }))
assert_eq(session.money, 50000 - (99 * 200), "Money deducted for 99 balls")
assert_eq(Bag.get(session.bag, 4), 109, "Bag has 109 Poké Balls")
assert_eq(Bag.get(session.bag, 12), 0, "no Premier Ball for 99 balls either")
-- Dismiss message
ShopMenu.handleInput(make_input({ a = true }))
assert_eq(ShopMenu.mode, "buy", "Returned to buy mode")
print("=== [TEST 6] 10 Great Balls ===")
-- Add Great Ball (ID 3, price 600) to shop items
ShopMenu._items = { 3, 4 }
ShopMenu.cursor = 1 -- Great Ball
ShopMenu.handleInput(make_input({ a = true }))
assert_eq(ShopMenu.mode, "buy_qty", "Entered buy_qty for Great Ball")
ShopMenu.handleInput(make_input({ right = true })) -- +10 -> 11
ShopMenu.handleInput(make_input({ down = true })) -- -> 10
assert_eq(ShopMenu.qty, 10, "10 Great Balls selected")
ShopMenu.handleInput(make_input({ a = true })) -- Confirm qty
ShopMenu.handleInput(make_input({ a = true })) -- Confirm buy
assert_eq(Bag.get(session.bag, 3), 10, "Bag received 10 Great Balls")
assert_eq(Bag.get(session.bag, 12), 0, "Great Balls yield no Premier Ball")
ShopMenu.handleInput(make_input({ a = true })) -- Dismiss msg
print("=== [TEST 7] Insufficient Funds & Full Bag Handlers ===")
session.money = 100 -- Less than 200 for Poké Ball
ShopMenu.cursor = 2 -- Poké Ball
ShopMenu.handleInput(make_input({ a = true }))
assert_eq(ShopMenu.mode, "buy_msg", "Entered buy_msg for insufficient funds")
assert_eq(ShopMenu._status, "You don't have enough money.", "Error message set")
ShopMenu.handleInput(make_input({ a = true }))
assert_eq(ShopMenu.mode, "buy", "Returned safely to buy mode without soft-lock")
print("=== [TEST 8] 0-Price Sell Filter & Sell Flow ===")
-- Give player items: Master Ball (ID 1, price 0), Oak's Parcel (ID 360, price 0), Potion (ID 13, price 300, sell 150), Antidote (ID 19, price 100, sell 50)
Bag.add(session.bag, 1, 1) -- Master Ball
Bag.add(session.bag, 360, 1) -- Oak's Parcel
Bag.add(session.bag, 13, 5) -- Potion (5)
Bag.add(session.bag, 19, 2) -- Antidote (2)
-- Cancel out of BUY back to ROOT
ShopMenu.handleInput(make_input({ b = true }))
assert_eq(ShopMenu.mode, "root", "Returned to root")
assert_eq(ShopMenu._status, "Is there anything else I can do?", "Clerk greeting updated")
-- Move to SELL and enter
ShopMenu.handleInput(make_input({ down = true }))
assert_eq(ShopMenu.cursor, 2, "Cursor on SELL")
ShopMenu.handleInput(make_input({ a = true }))
local Fade = require("src.ui.game3.fade")
for _ = 1, 240 do
if not Fade.isActive() then break end
Fade.tick(1 / 60)
end
local BagMenu = require("src.ui.game3.bag_menu")
-- pokefirered/src/shop.c:288 CB2_GoToSellMenu
assert_eq(ShopMenu.mode, "sell", "Entered sell mode")
assert_true(BagMenu.isOpen() and BagMenu._location == "shop", "SELL opened the bag in shop mode")
-- pokefirered/src/shop.c:330
BagMenu.close()
assert_eq(ShopMenu.mode, "root", "Returned to root from sell")
assert_eq(ShopMenu._status, "Is there anything else I can do?", "Clerk asks again after selling")
print("=== [TEST 9] Close Poké Mart and Resume VM ===")
-- Exit via SEE YA! (cursor 3)
ShopMenu.cursor = 3
ShopMenu.handleInput(make_input({ a = true }))
assert_false = function(v, m) if v then error(m or "assert false failed") end end
assert_false(ShopMenu.isOpen(), "Shop is closed")
assert_true(closed, "onClose callback fired successfully")
print("=== [TEST 10] Script Adapter openShop Integration ===")
local adapterDone = false
Message.show("Some stayed clerk message...", { stay = true })
assert_true(Message.isOpen(), "Field message was open before openShop")
local adapters = Adapters.host(nil, nil, nil)
adapters.openShop(135731380, function() adapterDone = true end)
assert_false(Message.isOpen(), "Field Message was cleanly closed by openShop")
assert_true(ShopMenu.isOpen(), "ShopMenu is open via adapter")
-- Close shop
ShopMenu.close()
assert_false(ShopMenu.isOpen(), "ShopMenu closed")
assert_true(adapterDone, "Adapter done callback invoked")
print("=== [TEST 11] Shop Camera Mode State ===")
ShopMenu.show({ items = { 4, 13 } })
assert_true(ShopMenu.isOpen(), "Shop open")
assert_false(ShopMenu.isShopCamera(), "Root mode is not shop camera")
local inputA = make_input({ a = true })
ShopMenu.handleInput(inputA)
assert_eq(ShopMenu.mode, "buy", "Switched to buy mode")
assert_true(ShopMenu.isShopCamera(), "Buy mode activates shop camera")
ShopMenu.close()
assert_false(ShopMenu.isShopCamera(), "Closed shop deactivates shop camera")
print("=== [TEST 12] MoneyBox Cleanup on Shop Close ===")
local MoneyBox = require("src.ui.game3.money_box")
MoneyBox.show(19, 1, 1000)
assert_true(MoneyBox.isVisible(), "MoneyBox initially visible")
ShopMenu.show({ items = { 4, 13 } })
assert_false(MoneyBox.isVisible(), "MoneyBox hidden when ShopMenu opens")
ShopMenu.close()
assert_false(MoneyBox.isVisible(), "MoneyBox remains hidden after ShopMenu closes")
print("\nALL 12 POKÉ MART TESTS PASSED!")