Files
gen1recomp/tests/drivers/save_editor_mobile_redesign.lua
T
bryanthaboi 20ab97abf8 save editor
2026-10-01 21:31:40 -04:00

314 lines
11 KiB
Lua

local U = require("tests.drivers.util")
return function(game)
U.wait(20)
package.path = package.path .. ";./tools/save-editor/?.lua;./tools/save-editor/panels/?.lua"
local App = require("tools.save-editor.App")
local Ops = require("Ops")
local Gen = require("Gen")
local SD = require("src.core.SaveData")
local Chooser = require("Chooser")
local Kit = require("Kit")
local Paint = require("src.ui.kit.Button")
local realPaint, navigationRects = Paint.draw, {}
local labelFailures, labelChecks = {}, 0
local function labelCheck(ok, message)
if not ok then
labelFailures[#labelFailures + 1] = message
end
assert(ok, message)
end
Paint.draw = function(owner, x, y, w, h, label, opts, hot, focused)
if owner == Kit and label and label ~= "" then
labelCheck(opts.labelLayout, "missing complete button label " .. label)
local layout = opts.labelLayout
labelCheck(opts.fullLabel, "truncated button label " .. label)
labelCheck(layout.width <= layout.available + 1, "button word does not fit " .. label)
labelCheck(layout.height <= h - 4 * Kit.scale + 1, "button label does not fit " .. label)
labelCheck(table.concat(layout.lines, " ") == label, "button text shortened " .. label)
labelChecks = labelChecks + 1
end
if owner == Kit and opts.id and opts.id:match("^navigate%-") then
navigationRects[opts.id:sub(10)] = { x = x, y = y, w = w, h = h }
end
return realPaint(owner, x, y, w, h, label, opts, hot, focused)
end
local version = os.getenv("POKEPORT_VERSION") or "red"
local path = os.tmpname() .. "-editor-preview.lua"
local seed = Gen.newGame(version)
local f = assert(io.open(path, "wb"))
f:write(SD.encode(seed))
f:close()
App.load(path, { version = version, embedded = true })
local S = App.getState()
assert(S.save and not S.loadError, S.status)
Ops.partyAdd(S)
Ops.selectParty(S, 1)
Ops.addToBag(S, S.cat.items[1])
Ops.addToBag(S, S.cat.items[2])
Ops.boxAdd(S)
Ops.selectParty(S, 1)
local dim, safe = love.graphics.getDimensions, love.window.getSafeArea
local dir = assert(os.getenv("POKEPORT_SHOT_DIR"))
-- Check rendered pixels with MSAA disabled, not only the options passed
-- to the painter. Both bright fills must leave all four corner cutouts
-- and have a smooth arc, including small icon actions and hover states.
local function cornerProbe(w, h, opts, hot)
local canvas = love.graphics.newCanvas(w + 16, h + 16, { msaa = 0 })
love.graphics.push("all")
love.graphics.setCanvas(canvas)
love.graphics.origin()
love.graphics.setScissor()
love.graphics.setShader()
love.graphics.clear(0, 0, 0, 0)
Kit.beginFrame(hot and 20 or -1, hot and 20 or -1, false, 0)
Kit.button(8, 8, w, h, "", opts)
Kit.endFrame()
love.graphics.setCanvas()
love.graphics.pop()
local pixels = canvas:newImageData()
canvas:release()
local function alpha(x, y)
local _, _, _, a = pixels:getPixel(x, y)
return a
end
local rounded = alpha(10, 10) < 0.02
and alpha(w + 5, 10) < 0.02
and alpha(10, h + 5) < 0.02
and alpha(w + 5, h + 5) < 0.02
assert(alpha(8 + math.floor(w / 2), 8 + math.floor(h / 2)) > 0.3, "missing button body")
local smooth = false
for py = 8, 21 do
for px = 8, 21 do
local a = alpha(px, py)
smooth = smooth or (a > 0.02 and a < 0.98)
end
end
pixels:release()
return rounded, smooth
end
local control = require("Theme").CONTROL
local newRadius = control.radius
control.radius = 8
local oldRounded = cornerProbe(100, 44, { kind = "accent" }, false)
control.radius = newRadius
assert(not oldRounded, "old corner regression was not reproduced")
local paints = {
{ kind = "accent" },
{ kind = "good" },
{ kind = "primary" },
{ kind = "ghost" },
{ kind = "danger" },
{ face = "selection", active = true },
{ face = "selection", active = false },
{ face = "invert" },
{ kind = "accent", enabled = false },
{ kind = "good", enabled = false },
}
local cornerChecks = 0
for _, size in ipairs({ { 44, 44 }, { 100, 44 }, { 320, 44 }, { 160, 64 } }) do
for _, opts in ipairs(paints) do
for _, hot in ipairs({ false, true }) do
local rounded, smooth = cornerProbe(size[1], size[2], opts, hot)
assert(rounded, "squared button corner: " .. (opts.kind or opts.face))
if opts.enabled ~= false then
assert(smooth, "aliased button arc: " .. (opts.kind or opts.face))
end
cornerChecks = cornerChecks + 1
end
end
end
print("PASS rendered corner checks: " .. cornerChecks .. " button variants, old shape reproduced")
local function shot(name, W, H, settle)
local canvas = love.graphics.newCanvas(W, H)
love.graphics.getDimensions = function()
return W, H
end
love.window.getSafeArea = function()
return 0, 0, W, H
end
love.graphics.push("all")
love.graphics.setCanvas(canvas)
love.graphics.origin()
love.graphics.setScissor()
love.graphics.setShader()
require("src.render.GameViewport").reset()
love.graphics.clear(0, 0, 0, 1)
App.draw()
if settle then
settle()
end
App.draw()
love.graphics.setCanvas()
love.graphics.pop()
local bytes = canvas:newImageData():encode("png"):getString()
local out = assert(io.open(dir .. "/" .. version .. "-" .. name .. ".png", "wb"))
out:write(bytes)
out:close()
canvas:release()
love.graphics.getDimensions, love.window.getSafeArea = dim, safe
print("PASS captured " .. name)
U.wait(1)
end
local function popupShot(name, W, H, key)
Chooser.close(S)
navigationRects = {}
shot(name, W, H, function()
local rect = assert(navigationRects[key], "missing dropdown " .. key)
App.mousepressed(rect.x + rect.w / 2, rect.y + rect.h / 2, 1)
App.draw()
assert(S.navPopup and S.navPopup.key == key, "dropdown did not open " .. key)
end)
Chooser.close(S)
end
for _, tab in ipairs({ "party", "boxes", "items", "events", "dex", "map", "trainer", "legality" }) do
S.tab = tab
S.pageScroll = {}
S.inspectorScroll = 0
S.monSection = "main"
S.mobileInspector = true
shot(tab, 360, 640)
shot("full-label-" .. tab, 320, 568)
end
S.tab = "dex"
S.pageScroll = {}
shot("pokedex-full-labels", 390, 844)
shot("pokedex-small-phone", 320, 568)
popupShot("pokedex-actions", 390, 844, "dexActions")
popupShot("pokedex-actions-small-phone", 320, 568, "dexActions")
-- Audit complete labels on armed confirmations and every inventory mode,
-- including the state whose longer label used to overflow a small chip.
S.tab = "items"
for _, view in ipairs({ "bag", "pc", "wallet", "badges" }) do
S.itemView = view
shot("full-label-items-" .. view, 320, 568)
end
S.itemView = "bag"
local bagId = require("src.inventory.Bag").order(S.save, S.data)[1]
if bagId then
Ops.arm(S, "bag-drop-" .. tostring(bagId))
shot("full-label-item-confirmation", 320, 568)
Ops.disarm(S)
end
S.tab = "boxes"
for _, view in ipairs({ "storage", "party" }) do
S.boxView = view
shot("full-label-boxes-" .. view, 320, 568)
end
S.boxView = "storage"
Ops.arm(S, "box-release")
shot("full-label-box-confirmation", 320, 568)
Ops.disarm(S)
if Gen.ofState(S) == 3 and S.game3Events and S.game3Events.story[1] then
local flag = S.game3Events.story[1]
Gen.setFlag(S.save, flag.name or flag.id, true)
S.tab = "events"
shot("selected-events", 390, 844)
end
S.tab = "map"
for _, section in ipairs({ "maps", "view", "spawn" }) do
S.mapSection = section
shot("map-" .. section, 360, 640)
shot("map-" .. section .. "-landscape", 640, 360)
end
S.mapSection, S.mapFocused = "view", true
shot("map-focus-landscape", 640, 360)
S.mapFocused = false
S.tab = "party"
S.mobileInspector = false
shot("party-roster", 360, 640)
S.mobileInspector = true
for _, section in ipairs({ "stats", "moves", "origin", "extras", "checks" }) do
S.monSection = section
S.inspectorScroll = 0
shot(section, 390, 844)
shot("full-label-" .. section, 320, 568)
end
if Gen.ofState(S) == 3 then
for _, choice in ipairs({
{ "main", "nature" },
{ "origin", "language" },
{ "origin", "pokeball" },
{ "origin", "metGame" },
}) do
S.monSection, S.propertyChoice, S.inspectorScroll = choice[1], choice[2], 300
shot("full-label-choice-" .. choice[2], 320, 568)
end
S.monSection, S.propertyChoice, S.inspectorScroll = "origin", "language", 500
shot("language-choices", 390, 844, function()
S.inspectorScroll = 500
end)
S.propertyChoice, S.inspectorScroll = nil, 0
end
S.monSection = "main"
shot("selected-buttons", 390, 844)
shot("rounded-buttons", 390, 844)
S.tab = "dex"
shot("rounded-actions", 390, 844)
S.tab = "party"
shot("desktop", 1280, 800)
shot("landscape", 640, 360)
popupShot("tab-menu", 360, 640, "tab")
popupShot("page-popup-desktop", 1280, 800, "tab")
popupShot("page-popup-landscape", 640, 360, "tab")
popupShot("section-popup", 390, 844, "monSection")
popupShot("section-popup-desktop", 1280, 800, "monSection")
for _, entry in ipairs({
{ "items", "itemView" },
{ "boxes", "boxView" },
{ "events", "eventsTab" },
{ "map", "mapSection" },
}) do
S.tab = entry[1]
S.pageScroll = {}
popupShot(entry[1] .. "-popup", 390, 844, entry[2])
end
S.tab = "party"
S.chromeMenu = true
shot("more-menu", 360, 640)
S.chromeMenu = false
for _, picker in ipairs({ "species", "move", "held" }) do
if picker == "species" then
Ops.openSpeciesPicker(S, require("Kit"))
elseif picker == "move" then
Ops.openMovePicker(S, require("Kit"), 1)
else
Ops.openItemPicker(S, require("Kit"), "held")
end
shot("picker-" .. picker, 360, 640)
Ops.closeSpeciesPicker(S, require("Kit"))
Ops.closeMovePicker(S, require("Kit"))
Ops.closeItemPicker(S, require("Kit"))
end
assert(App.save(), "scratch save writes for saved-button preview")
shot("saved-button", 390, 844)
S.allowSave = false
shot("locked-save-button", 390, 844)
S.allowSave = true
local Motion = require("Motion")
local Transition = require("src.ui.kit.Transition")
local oldClock = love.timer.getTime
local clock = 100
love.timer.getTime = function()
return clock
end
Transition.armed = true
Transition.reduceMotion = false
Motion.change(S, "monSection", "stats", 1)
clock = 100.045
shot("section-slide", 390, 844)
clock = 100.20
Motion.update()
require("Kit").blockClicks = false
Motion.change(S, "tab", "items", 1)
clock = 100.245
shot("tab-slide", 390, 844)
Motion.reset()
love.timer.getTime = oldClock
assert(#labelFailures == 0, table.concat(labelFailures, "\n"))
print("PASS complete button labels: " .. labelChecks .. " real-font layouts audited")
App.unload()
Paint.draw = realPaint
os.remove(path)
love.event.quit(0)
end