mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-09-26 13:33:27 -04:00
23617f43b4
CLOSES #2290, CLOSES #2338, CLOSES #2186, CLOSES #2305, CLOSES #2332, CLOSES #2320, CLOSES #2344, CLOSES #2291, CLOSES #2335, CLOSES #2340, CLOSES #2385, CLOSES #2352, CLOSES #2378, CLOSES #2403, CLOSES #2404, CLOSES #2405, CLOSES #2367, CLOSES #2037, CLOSES #2177, CLOSES #2368, CLOSES #2382, CLOSES #1951
476 lines
19 KiB
Lua
476 lines
19 KiB
Lua
package.path = "./?.lua;./?/init.lua;" .. package.path
|
|
|
|
local T = require("tests.harness")
|
|
love = love or require("tests.love_stub")
|
|
love.graphics.setLineJoin = love.graphics.setLineJoin or function() end
|
|
love.graphics.newShader = love.graphics.newShader or function() return {} end
|
|
|
|
local Loader = require("src.mods.Loader")
|
|
local CartManifest = require("src.carts.CartManifest")
|
|
local SaveData = require("src.core.SaveData")
|
|
local SaveSerializer = require("src.core.SaveSerializer")
|
|
local GameVersion = require("src.core.GameVersion")
|
|
local Manifest = require("src.mods.Manifest")
|
|
local ModProfile = require("src.mods.ModProfile")
|
|
local LauncherMods = require("src.mods.LauncherMods")
|
|
local LoadOrder = require("src.mods.LoadOrder")
|
|
local Logger = require("src.core.Logger")
|
|
Logger.warn = function() end
|
|
Logger.info = function() end
|
|
|
|
local function names(list) return table.concat(list or {}, ",") end
|
|
|
|
local function memfs(files)
|
|
return {
|
|
files = files,
|
|
read = function(path) return files[path] end,
|
|
write = function(path, content) files[path] = content return true end,
|
|
remove = function(path) files[path] = nil return true end,
|
|
getInfo = function(path)
|
|
if files[path] then return { type = "file" } end
|
|
local prefix = path .. "/"
|
|
for key in pairs(files) do
|
|
if key:sub(1, #prefix) == prefix then return { type = "directory" } end
|
|
end
|
|
return nil
|
|
end,
|
|
load = function(path)
|
|
if not files[path] then return nil, "no file: " .. path end
|
|
return load(files[path], path)
|
|
end,
|
|
createDirectory = function() return true end,
|
|
getDirectoryItems = function(path)
|
|
local seen, items = {}, {}
|
|
local prefix = path .. "/"
|
|
for key in pairs(files) do
|
|
if key:sub(1, #prefix) == prefix then
|
|
local child = key:sub(#prefix + 1):match("^[^/]+")
|
|
if child and not seen[child] then
|
|
seen[child] = true
|
|
items[#items + 1] = child
|
|
end
|
|
end
|
|
end
|
|
table.sort(items)
|
|
return items
|
|
end,
|
|
}
|
|
end
|
|
|
|
local function manifestJson(id, extra)
|
|
return ([[{"id":"%s","name":"%s","version":"1.0.0","entry":"main.lua"%s}]])
|
|
:format(id, id, extra or "")
|
|
end
|
|
|
|
local function entry(label)
|
|
return ([[
|
|
return function(mod)
|
|
mod.content.pokemon:override("PIKACHU", { name = "%s" })
|
|
end
|
|
]]):format(label)
|
|
end
|
|
|
|
local function install(opts, extras)
|
|
extras = extras or {}
|
|
local files = { ["options.lua"] = SaveSerializer.encode(opts or {}) }
|
|
for _, id in ipairs({ "alpha", "beta", "gamma" }) do
|
|
files["mods/" .. id .. "/manifest.json"] = manifestJson(id, extras[id])
|
|
files["mods/" .. id .. "/main.lua"] = entry(id:upper())
|
|
end
|
|
SaveData.resetSlotState()
|
|
GameVersion.set("red")
|
|
return files
|
|
end
|
|
|
|
local function boot(files, mode)
|
|
local data = { pokemon = { PIKACHU = { name = "PIKACHU" } } }
|
|
local loader = Loader.new({ fs = memfs(files) })
|
|
local ok = loader:load(data, mode and { mode = mode } or nil)
|
|
return loader, data, ok
|
|
end
|
|
|
|
|
|
do
|
|
T.check(type(SaveData.defaultOptions().modOrder) == "table",
|
|
"defaultOptions carries a modOrder list")
|
|
T.eq(names(SaveData.modOrder({ modOrder = { "b", 3, "a", "b", "", "c" } })),
|
|
"b,a,c", "modOrder keeps strings once, in order")
|
|
T.eq(#SaveData.modOrder({}), 0, "no modOrder reads as empty")
|
|
local o = SaveData.setModOrder({}, { "x", "x", "y" })
|
|
T.eq(names(o.modOrder), "x,y", "setModOrder stores the sanitized list")
|
|
end
|
|
|
|
|
|
do
|
|
local entries = {
|
|
{ id = "gamma", priority = 0 }, { id = "alpha", priority = 5 },
|
|
{ id = "beta", priority = 0 },
|
|
}
|
|
T.eq(names(LoadOrder.materialize({}, entries)), "beta,gamma,alpha",
|
|
"an empty saved order materializes to (priority, id)")
|
|
T.eq(names(LoadOrder.materialize({ "alpha", "gone" }, entries)),
|
|
"beta,gamma,alpha,gone",
|
|
"unlisted ids merge in by priority, uninstalled saved ids keep their slot")
|
|
T.eq(names(LoadOrder.materialize({ "gamma" }, entries)), "gamma,beta,alpha",
|
|
"an unlisted id tied on priority follows the saved one")
|
|
local grown = {
|
|
{ id = "a", priority = 0 }, { id = "b", priority = 0 }, { id = "c", priority = 0 },
|
|
{ id = "fw", priority = -1000 }, { id = "late", priority = 50 },
|
|
}
|
|
T.eq(names(LoadOrder.materialize({ "b", "a", "c" }, grown)), "fw,b,a,c,late",
|
|
"a mod installed after a reorder still lands by its manifest priority")
|
|
local l = { "a", "b", "c", "d" }
|
|
T.eq(names(LoadOrder.move(l, "b", -1)), "b,a,c,d", "move up one")
|
|
T.eq(names(LoadOrder.move(l, "a", -1)), "a,b,c,d", "move up clamps at the top")
|
|
T.eq(names(LoadOrder.move(l, "d", 1)), "a,b,c,d", "move down clamps at the bottom")
|
|
T.eq(names(LoadOrder.move(l, "c", -math.huge)), "c,a,b,d", "move to top")
|
|
T.eq(names(LoadOrder.move(l, "a", math.huge)), "b,c,d,a", "move to bottom")
|
|
T.eq(names(l), "a,b,c,d", "move never edits its input")
|
|
local full, changed = LoadOrder.moveWithin({ "a", "x", "b", "c" },
|
|
{ "a", "b", "c" }, "b", -1)
|
|
T.check(changed, "moveWithin reports a change")
|
|
T.eq(names(full), "b,x,a,c",
|
|
"moveWithin swaps visible neighbours and keeps hidden ids in their slots")
|
|
end
|
|
|
|
|
|
do
|
|
local files = install()
|
|
local loader, data = boot(files)
|
|
T.eq(names(loader.order), "alpha,beta,gamma",
|
|
"no modOrder keeps the (priority, id) order")
|
|
T.eq(data.pokemon.PIKACHU.name, "GAMMA", "and the last id wins")
|
|
end
|
|
|
|
do
|
|
local files = install({ modOrder = { "gamma", "alpha", "beta" } })
|
|
local loader, data = boot(files)
|
|
T.eq(names(loader.order), "gamma,alpha,beta", "modOrder decides the load order")
|
|
T.eq(data.pokemon.PIKACHU.name, "BETA", "the last mod in the player's order wins")
|
|
end
|
|
|
|
do
|
|
local files = install({ modOrder = { "beta" } })
|
|
local loader = boot(files)
|
|
T.eq(names(loader.order), "beta,alpha,gamma",
|
|
"a partial list leads, the rest follow by priority and id")
|
|
end
|
|
|
|
do
|
|
local files = install({ modOrder = { "nope", "gamma" } })
|
|
local loader, _, ok = boot(files)
|
|
T.check(ok ~= false, "an unknown id in modOrder is not an error")
|
|
T.eq(names(loader.order), "gamma,alpha,beta", "unknown ids are skipped")
|
|
end
|
|
|
|
do
|
|
local files = install({ modOrder = { "alpha", "beta", "gamma" } },
|
|
{ alpha = [[,"priority":9]], gamma = [[,"priority":-4]] })
|
|
local loader = boot(files)
|
|
T.eq(names(loader.order), "alpha,beta,gamma",
|
|
"the player's order outranks manifest priority")
|
|
end
|
|
|
|
do
|
|
local files = install({ modOrder = { "beta", "alpha" } },
|
|
{ gamma = [[,"priority":-1000]] })
|
|
local loader = boot(files)
|
|
T.eq(names(loader.order), "gamma,beta,alpha",
|
|
"a low-priority mod missing from the saved order still loads first")
|
|
end
|
|
|
|
do
|
|
local files = install({ modOrder = { "alpha", "beta", "gamma" } },
|
|
{ alpha = [[,"dependencies":["gamma"] ]] })
|
|
local loader = boot(files)
|
|
T.eq(names(loader.order), "beta,gamma,alpha",
|
|
"a dependency still loads before the mod that needs it")
|
|
end
|
|
|
|
do
|
|
local files = install({ modOrder = { "alpha", "beta", "gamma" } })
|
|
local cart = assert(CartManifest.parse({ id = "cartx", title = "cartx",
|
|
version = "1.0.0", author = "t", shell = "#102030", base = "red",
|
|
seal = "sealed",
|
|
mods = { { id = "alpha", source = "local", version = "1.0.0" },
|
|
{ id = "gamma", source = "local", version = "1.0.0" } },
|
|
load_order = { "gamma", "alpha" } }))
|
|
files["carts/cartx" .. CartManifest.EXT] = CartManifest.encode(cart)
|
|
SaveData.setCart("cartx", "hashx")
|
|
local loader = boot(files)
|
|
T.eq(names(loader.order), "gamma,alpha", "a cart's load_order beats modOrder")
|
|
SaveData.setCart(nil)
|
|
end
|
|
|
|
do
|
|
local files = install({ modOrder = { "gamma", "alpha", "beta" } })
|
|
local loader = boot(files, "disableAll")
|
|
T.eq(loader.playerRank and next(loader.playerRank), nil,
|
|
"arena modes ignore the player's order")
|
|
end
|
|
|
|
|
|
do
|
|
local p = ModProfile.capture({ { id = "a", enabled = true } }, {}, {},
|
|
{ "b", "a", "b" })
|
|
T.eq(names(p.order), "b,a", "capture copies the sanitized order")
|
|
local back = ModProfile.decode(ModProfile.encode({ name = "P", enabled = {},
|
|
options = {}, slots = {}, enabledByVersion = {}, order = { "b", "a" } }))
|
|
T.eq(names(back and back.order), "b,a", "encode/decode round-trips order")
|
|
local bad = SaveSerializer.encode({ format = ModProfile.FORMAT,
|
|
formatVersion = 1, profile = { name = "Q", order = { "x", 4, "x", "y" } } })
|
|
T.eq(names(ModProfile.decode(bad).order), "x,y",
|
|
"decode drops non-strings and duplicates")
|
|
local live = { modOrder = { "z" } }
|
|
T.check(not ModProfile.matchesOrder({ name = "old" }, live),
|
|
"a profile without an order does not match a custom order")
|
|
ModProfile.restoreOrder({ name = "old" }, live)
|
|
T.eq(names(live.modOrder), "", "a profile without an order resets to the default order")
|
|
T.check(ModProfile.matchesOrder({ name = "old" }, live),
|
|
"and matches once the order is back to default")
|
|
ModProfile.restoreOrder(back, live)
|
|
T.eq(names(live.modOrder), "b,a", "restoreOrder writes the profile's order")
|
|
T.check(ModProfile.matchesOrder(back, live), "matchesOrder agrees after restore")
|
|
T.check(not ModProfile.matchesOrder(back, { modOrder = { "a", "b" } }),
|
|
"matchesOrder notices a moved mod")
|
|
end
|
|
|
|
|
|
local function mf(raw) return Manifest.validate(raw) end
|
|
|
|
do
|
|
local manifests = {
|
|
mf({ id = "aaa", name = "A", version = "1.0.0", entry = "m.lua" }),
|
|
mf({ id = "bbb", name = "B", version = "1.0.0", entry = "m.lua",
|
|
dependencies = { "ccc" } }),
|
|
mf({ id = "ccc", name = "C", version = "1.0.0", entry = "m.lua" }),
|
|
}
|
|
local rows = LauncherMods.deriveList(manifests,
|
|
{ mods = {}, modOrder = { "bbb", "ccc", "aaa" } }, "red")
|
|
local by = {}
|
|
for _, r in ipairs(rows) do by[r.id] = r end
|
|
T.eq(by.bbb.loadRank, 1, "deriveList stamps the saved position")
|
|
T.eq(by.aaa.loadRank, 3, "for every row")
|
|
T.eq(by.bbb.orderAfter, "ccc", "a dependency placed later is flagged")
|
|
T.check(type(by.bbb.orderNote) == "string" and by.bbb.orderNote:find("C", 1, true),
|
|
"with a note naming it")
|
|
T.eq(by.ccc.orderNote, nil, "no note when nothing forces a move")
|
|
T.eq(names(LauncherMods.orderedIds(rows, { modOrder = { "ccc" } })),
|
|
"ccc,aaa,bbb", "orderedIds materializes over rows")
|
|
T.eq(names(LauncherMods.moveInOrder({ "a", "b" }, "b", -1)), "b,a",
|
|
"moveInOrder swaps")
|
|
end
|
|
|
|
do
|
|
local fs = love.filesystem
|
|
for _, id in ipairs({ "lo_one", "lo_two", "lo_three" }) do
|
|
fs.write("mods/" .. id .. "/manifest.json", manifestJson(id))
|
|
fs.write("mods/" .. id .. "/main.lua", "return function() end")
|
|
end
|
|
SaveData.saveOptions(SaveData.defaultOptions())
|
|
local ok, at = LauncherMods.moveMod("lo_one", 1)
|
|
T.check(ok, "moveMod moves a mod down")
|
|
T.eq(at, 2, "and reports its new position")
|
|
T.eq(names(SaveData.loadOptions().modOrder), "lo_three,lo_one,lo_two",
|
|
"moveMod persists the whole materialized list")
|
|
T.check(not LauncherMods.moveMod("lo_two", 1), "the last mod cannot move down")
|
|
T.check(not LauncherMods.moveMod("missing_mod", -1), "an unknown id is refused")
|
|
local opts = SaveData.loadOptions()
|
|
opts.safeMode = true
|
|
SaveData.saveOptions(opts)
|
|
T.check(not LauncherMods.moveMod("lo_one", -1), "safe mode refuses a move")
|
|
opts = SaveData.loadOptions()
|
|
opts.safeMode = false
|
|
SaveData.saveOptions(opts)
|
|
fs.write("mods/lo_zero/manifest.json", manifestJson("lo_zero", [[,"priority":-1000]]))
|
|
fs.write("mods/lo_zero/main.lua", "return function() end")
|
|
T.check(LauncherMods.moveMod("lo_two", -1), "a move after a new install")
|
|
T.eq(names(SaveData.loadOptions().modOrder), "lo_zero,lo_three,lo_two,lo_one",
|
|
"the new low-priority mod was placed by priority, not forced last")
|
|
opts = SaveData.loadOptions()
|
|
opts.modProfiles = { { name = "PLAIN", enabled = {}, options = {}, slots = {},
|
|
enabledByVersion = {} } }
|
|
SaveData.saveOptions(opts)
|
|
T.check(LauncherMods.applyProfile("PLAIN"), "applyProfile takes a profile with no order")
|
|
T.eq(#SaveData.loadOptions().modOrder, 0, "and resets the load order to default")
|
|
LauncherMods.moveMod("lo_one", 1)
|
|
T.check(LauncherMods.resetOrder(), "resetOrder clears a saved order")
|
|
T.eq(#SaveData.loadOptions().modOrder, 0, "back to author priority")
|
|
opts = SaveData.loadOptions()
|
|
opts.modProfiles, opts.activeProfile = nil, nil
|
|
SaveData.saveOptions(opts)
|
|
for _, id in ipairs({ "lo_one", "lo_two", "lo_three", "lo_zero" }) do
|
|
fs.remove("mods/" .. id .. "/manifest.json")
|
|
fs.remove("mods/" .. id .. "/main.lua")
|
|
end
|
|
end
|
|
|
|
|
|
do
|
|
local ManagerState = require("src.mods.ManagerState")
|
|
local writes = 0
|
|
local game = {
|
|
save = { options = { mods = {}, modOrder = {} } },
|
|
modStatus = { errors = {}, available = {
|
|
{ id = "ma", name = "MA", enabled = true, state = "loaded", priority = 0 },
|
|
{ id = "mb", name = "MB", enabled = true, state = "loaded", priority = 0 },
|
|
{ id = "mc", name = "MC", enabled = true, state = "loaded", priority = 0 },
|
|
} },
|
|
writeOptions = function() writes = writes + 1 end,
|
|
stack = { pop = function() end, push = function() end },
|
|
}
|
|
local ms = ManagerState.new(game)
|
|
ms:refresh()
|
|
local function labels(rows)
|
|
local out = {}
|
|
for _, r in ipairs(rows) do out[#out + 1] = r.label end
|
|
return table.concat(out, "|")
|
|
end
|
|
local function rowNamed(rows, label)
|
|
for _, r in ipairs(rows) do if r.label == label then return r end end
|
|
end
|
|
local rows = ms:detailRows(ms.byId.ma)
|
|
local orderRow = rowNamed(rows, "< LOAD #1/3 >")
|
|
T.check(orderRow and orderRow.adjust, "the detail screen has one load position row")
|
|
T.eq(labels(rows):find("LOAD EARLIER", 1, true), nil, "no separate earlier/later rows")
|
|
ms.currentMod, ms.screen, ms.scroll = ms.byId.ma, "detail", 1
|
|
for i, r in ipairs(rows) do if r == orderRow then ms.cursor = i end end
|
|
ms:adjustOrTab(1)
|
|
T.eq(names(game.save.options.modOrder), "mb,ma,mc", "RIGHT on the row loads it later")
|
|
T.check(writes > 0, "and persists the options")
|
|
T.check(ms.restartPending, "a moved mod stages a restart")
|
|
rows = ms:detailRows(ms.byId.ma)
|
|
T.check(rowNamed(rows, "< LOAD #2/3 >") ~= nil, "the row follows the move")
|
|
ms:adjustOrTab(-1)
|
|
T.check(not ms.restartPending, "moving back to where it booted clears the restart")
|
|
T.eq(ms.descScroll, 1, "left/right on the row does not scroll the description")
|
|
rowNamed(ms:detailRows(ms.byId.mc), "< LOAD #3/3 >").adjust(-1)
|
|
ms:discardChanges()
|
|
T.eq(names(game.save.options.modOrder), "", "discard restores the boot order")
|
|
T.check(not ms.restartPending, "and clears the pending restart")
|
|
|
|
local Font = require("src.render.Font")
|
|
local busy = { id = "md", name = "MD", enabled = true, state = "loaded", priority = 0,
|
|
github = "someone/md", experimental = true, error = "boom", permissions = { "net" } }
|
|
game.modStatus.available[#game.modStatus.available + 1] = busy
|
|
ms:refresh()
|
|
ms.currentMod, ms.screen, ms.cursor, ms.scroll = ms.byId.md, "detail", 1, 1
|
|
local all = ms:rowsForScreen()
|
|
T.check(#all > 4, "a busy mod has more detail rows than fit above the footer")
|
|
local function drawAll()
|
|
local drawn = {}
|
|
local realDraw, realCode = Font.draw, Font.drawCode
|
|
Font.draw = function(text, x, y) drawn[#drawn + 1] = { text = tostring(text), x = x, y = y } end
|
|
Font.drawCode = function() end
|
|
ms:drawDetail()
|
|
Font.draw, Font.drawCode = realDraw, realCode
|
|
local low, seen = true, {}
|
|
for _, d in ipairs(drawn) do
|
|
if d.x == 32 then
|
|
if d.y >= 15 * 8 then low = false end
|
|
seen[d.text] = true
|
|
end
|
|
end
|
|
return low, seen
|
|
end
|
|
local clear, seen = drawAll()
|
|
T.check(clear, "no detail row draws on the footer")
|
|
T.check(seen["FOR " .. require("src.mods.ModTargets").chip(busy)] == nil,
|
|
"FOR is a header line, not a row")
|
|
T.check(not seen["BACK"], "BACK starts below the scroll window")
|
|
for _ = 1, #all - 1 do ms:moveCursor(1) end
|
|
T.eq(all[ms.cursor].label, "BACK", "the cursor reaches BACK")
|
|
clear, seen = drawAll()
|
|
T.check(clear and seen["BACK"], "and BACK scrolls into view above the footer")
|
|
end
|
|
|
|
|
|
do
|
|
local Kit = require("src.ui.kit.Kit")
|
|
local RomImporter = require("src.import.RomImporter")
|
|
local LauncherView = require("src.import.LauncherView")
|
|
local function window(w, h)
|
|
love.graphics.getDimensions = function() return w, h end
|
|
love.graphics.getPixelDimensions = function() return w, h end
|
|
end
|
|
local manifests = {
|
|
mf({ id = "va", name = "Alpha", version = "1.0.0", entry = "m.lua" }),
|
|
mf({ id = "vb", name = "Beta", version = "1.0.0", entry = "m.lua" }),
|
|
mf({ id = "vc", name = "Gamma", version = "1.0.0", entry = "m.lua" }),
|
|
}
|
|
local rows = LauncherMods.deriveList(manifests,
|
|
{ mods = {}, modOrder = { "vc", "va", "vb" } })
|
|
local seen, labels = {}, {}
|
|
local realButton = Kit.button
|
|
Kit.button = function(x, y, w, h, label, opts)
|
|
if opts and opts.id then
|
|
seen[opts.id] = { x = x, y = y, w = w, h = h, opts = opts }
|
|
labels[opts.id] = label
|
|
end
|
|
return realButton(x, y, w, h, label, opts)
|
|
end
|
|
local texts = {}
|
|
local realText = Kit.text
|
|
Kit.text = function(font, s, ...)
|
|
texts[#texts + 1] = s
|
|
return realText(font, s, ...)
|
|
end
|
|
for _, size in ipairs({ { 1280, 2400 }, { 360, 3200 } }) do
|
|
window(size[1], size[2])
|
|
seen, labels, texts = {}, {}, {}
|
|
local imp = RomImporter.new(function() end, { launcher = true })
|
|
imp.tab = "mods"
|
|
imp.ready = { red = true }
|
|
imp.mods = rows
|
|
imp.modSort = "order"
|
|
local moved
|
|
imp._moveMod = function(_, id, delta) moved = { id, delta } end
|
|
local ok, err = pcall(LauncherView.draw, imp)
|
|
T.check(ok, ("%dx%d draws: %s"):format(size[1], size[2], tostring(err)))
|
|
local tag = size[1] .. "x" .. size[2]
|
|
T.check(seen["mod-up-va"] ~= nil and seen["mod-down-va"] ~= nil,
|
|
tag .. ": up/down buttons are drawn for a visible row")
|
|
T.check(seen["mod-up-vc"] and seen["mod-up-vc"].opts.enabled == false,
|
|
tag .. ": the first mod cannot move up")
|
|
T.check(seen["mod-down-vb"] and seen["mod-down-vb"].opts.enabled == false,
|
|
tag .. ": the last mod cannot move down")
|
|
local found = false
|
|
for _, s in ipairs(texts) do
|
|
if type(s) == "string" and s:find("^#1 Gamma") then found = true end
|
|
end
|
|
T.check(found, tag .. ": the list is sorted by load order with #n badges")
|
|
local det, up = seen["mod-row-va"], seen["mod-up-va"]
|
|
if det and up then
|
|
T.check(det.y == up.y and det.x + det.w <= up.x,
|
|
tag .. ": the move buttons share the Details line, row height unchanged")
|
|
end
|
|
seen["mod-down-va"].opts.action()
|
|
T.eq(moved and moved[1], "va", tag .. ": Down moves that mod")
|
|
T.eq(moved and moved[2], 1, tag .. ": by one slot")
|
|
imp._modActions = "va"
|
|
seen, texts = {}, {}
|
|
imp._uiActions = {}
|
|
ok, err = pcall(LauncherView.draw, imp)
|
|
T.check(ok, tag .. " details modal draws: " .. tostring(err))
|
|
T.check(seen["modact-up"] and seen["modact-down"] and seen["modact-top"]
|
|
and seen["modact-bottom"], tag .. ": Details offers the four Move buttons")
|
|
T.check(seen["modact-top"] and seen["modact-top"].opts.enabled == true,
|
|
tag .. ": the middle mod can move to the top")
|
|
seen["modact-bottom"].opts.action()
|
|
T.eq(moved and moved[2], math.huge, tag .. ": Move to bottom")
|
|
imp._modActions = nil
|
|
imp._sortPopup = "mods"
|
|
seen = {}
|
|
pcall(LauncherView.draw, imp)
|
|
T.check(seen["sortpop-order"] ~= nil, tag .. ": Load order is a MODS sort")
|
|
T.check(seen["sortpop-reset-order"] ~= nil, tag .. ": with a reset button")
|
|
imp._sortPopup = "find"
|
|
seen = {}
|
|
pcall(LauncherView.draw, imp)
|
|
T.check(seen["sortpop-order"] == nil, tag .. ": but not a FIND sort")
|
|
end
|
|
Kit.button, Kit.text = realButton, realText
|
|
end
|
|
|
|
T.finish("mod load order (#2177)")
|