141 lines
8.4 KiB
Python
141 lines
8.4 KiB
Python
"""Dev console confirmation, keyboard isolation, HUD, time controls and saved badge."""
|
|
import pathlib, sys
|
|
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent.parent / 'tools'))
|
|
from use_venv import ensure_playwright; ensure_playwright()
|
|
from browser import launch, consume_gpu_option
|
|
consume_gpu_option()
|
|
|
|
ROOT = pathlib.Path(__file__).resolve().parent.parent
|
|
SHOTS = ROOT / 'shots'
|
|
SHOTS.mkdir(exist_ok=True)
|
|
fails = []
|
|
def check(name, ok):
|
|
print(('PASS ' if ok else 'FAIL ') + name)
|
|
if not ok: fails.append(name)
|
|
|
|
with launch() as (browser, page, url):
|
|
errors = []
|
|
page.on('pageerror', lambda e: errors.append(str(e)))
|
|
page.add_init_script("localStorage.setItem('deepgrain.settings', JSON.stringify({look:'pixel', sv:2}));")
|
|
page.goto(url)
|
|
page.wait_for_function("!$('title').hidden")
|
|
page.evaluate("startNewWorld(424242, 512, 384, 'Console test')")
|
|
page.wait_for_function('GAME.running && !GAME.loading')
|
|
page.evaluate("document.querySelectorAll('.card, .toast').forEach(e => e.remove())")
|
|
page.keyboard.press('`')
|
|
check('backtick opens the first-use warning', page.is_visible('#m-dialog') and not page.is_visible('#dev-console'))
|
|
page.screenshot(path=str(SHOTS / 't66-warning.png'))
|
|
check('warning explains permanent marking and future achievements', 'permanently' in page.inner_text('#dlg-body') and 'achievements' in page.inner_text('#dlg-body'))
|
|
page.get_by_role('button', name='Cancel', exact=True).click()
|
|
check('cancel leaves the world unmarked and console closed', page.evaluate("!devWorld && $('dev-console').hidden && !GAME.paused"))
|
|
page.keyboard.press('`')
|
|
page.get_by_role('button', name='Enable dev console', exact=True).click()
|
|
page.wait_for_function("!$('dev-console').hidden && document.activeElement === $('dev-input')")
|
|
check('accepting marks the world and resumes play', page.evaluate('devWorld && !GAME.paused'))
|
|
check('build material chip stays stable across HUD refreshes', page.evaluate("""() => {
|
|
selectTool(2); renderSelCard();
|
|
const chip = $('selcard').querySelector('[data-wall-picker]');
|
|
renderSelCard();
|
|
const stable = chip === $('selcard').querySelector('[data-wall-picker]');
|
|
selectTool(0); return stable;
|
|
}"""))
|
|
old_money = page.evaluate('money')
|
|
page.fill('#dev-input', 'money 1000'); page.keyboard.press('Enter')
|
|
page.wait_for_function(f"money === {old_money + 1000} && $('wallet').textContent.includes(fmtMoney(money))")
|
|
check('money command changes the wallet HUD', True)
|
|
tool = page.evaluate('GAME.tool')
|
|
page.fill('#dev-input', ''); page.keyboard.type('1234wasd')
|
|
check('typing game keys does not switch tools or hold movement', page.evaluate(f'GAME.tool === {tool} && keys.size === 0'))
|
|
page.keyboard.press('Escape')
|
|
check('Escape closes console without opening pause menu', page.evaluate("$('dev-console').hidden && $('m-pause').hidden && !GAME.paused"))
|
|
page.keyboard.press('`')
|
|
check('marked worlds reopen without the warning', page.evaluate("!$('dev-console').hidden && $('m-dialog').hidden"))
|
|
page.keyboard.press('`')
|
|
check('backtick closes even with input focused', not page.is_visible('#dev-console'))
|
|
page.keyboard.press('`')
|
|
def command(text):
|
|
page.fill('#dev-input', text); page.keyboard.press('Enter')
|
|
command('pause')
|
|
tick = page.evaluate('tick')
|
|
page.wait_for_timeout(150)
|
|
check('dev pause freezes simulation ticks', page.evaluate('tick') == tick)
|
|
position = page.evaluate("[bot.x, bot.y, bot.lamp, GAME.tool, $('bagpanel').hidden]")
|
|
page.fill('#dev-input', 'give copper')
|
|
page.keyboard.press('Tab')
|
|
first = page.input_value('#dev-input')
|
|
page.keyboard.press('Tab')
|
|
check('Tab completes spaced material names and cycles matches', first.startswith('give Copper') and first != page.input_value('#dev-input') and 'Matches:' in page.inner_text('#dev-output'))
|
|
page.keyboard.press('ArrowUp')
|
|
check('Up recalls the newest submitted command', page.input_value('#dev-input') == 'pause')
|
|
page.keyboard.press('ArrowDown')
|
|
check('Down beyond newest returns an empty input', page.input_value('#dev-input') == '')
|
|
page.keyboard.press('Control+l')
|
|
check('Ctrl+L clears output and keeps console focus', page.evaluate("$('dev-output').children.length === 0 && document.activeElement === $('dev-input')"))
|
|
page.wait_for_timeout(100)
|
|
check('Tab, arrows and Ctrl+L leave bot and game controls alone', page.evaluate("[bot.x, bot.y, bot.lamp, GAME.tool, $('bagpanel').hidden]") == position and page.evaluate('keys.size === 0 && !GAME.ctrl'))
|
|
command('give saw 3')
|
|
check('giving a saw explains building and leaves it out of the bag',
|
|
'Saw is built from the build menu, not given.' in page.inner_text('#dev-output')
|
|
and page.evaluate('bag[SAWB] === 0'))
|
|
page.fill('#dev-input', 'give saw'); page.keyboard.press('Tab')
|
|
check('Tab never offers the saw', page.input_value('#dev-input') == 'give saw')
|
|
command('materials saw')
|
|
check('material discovery omits construction pieces', 'No matching materials.' in page.inner_text('#dev-output'))
|
|
command('give sand 7'); command('give copper bar 2'); command('give gate 3')
|
|
check('ordinary grains and bars still arrive while gates are refused',
|
|
page.evaluate('bag[SAND] === 7 && bag[BARCU] === 2 && bag[GATE] === 0'))
|
|
page.screenshot(path=str(SHOTS / 't97-console.png'))
|
|
command('clear')
|
|
command('materials copperore')
|
|
command('help give')
|
|
command('give coperore')
|
|
check('errors are red while results use normal colour', page.evaluate('''() => {
|
|
const rows = $('dev-output').children, error = rows[rows.length - 1];
|
|
return error.classList.contains('dev-error') && getComputedStyle(error).color === 'rgb(239, 106, 85)' && getComputedStyle(error).color !== getComputedStyle(rows[0]).color;
|
|
}'''))
|
|
page.screenshot(path=str(SHOTS / 't94-console.png'))
|
|
command('clear')
|
|
check('clear command empties output', page.evaluate("$('dev-output').children.length === 0"))
|
|
check('submitted history is stored across sessions', page.evaluate("JSON.parse(localStorage.getItem('deepgrain.consoleHistory')).slice(-1)[0] === 'clear'"))
|
|
command('step 5')
|
|
page.wait_for_function(f'tick === {tick + 5} && devSteps === 0')
|
|
page.wait_for_timeout(150)
|
|
check('step advances exactly five ticks then stays paused', page.evaluate('tick') == tick + 5)
|
|
# Drive the actual frame accumulator with controlled elapsed time and no scheduled frames.
|
|
counts = page.evaluate('''() => {
|
|
requestAnimationFrame = () => 0;
|
|
GAME.paused = false; devPaused = false; SET.fpsCap = 0; SET.maxSteps = 3;
|
|
const counts = [];
|
|
for (const speed of [0.5, 1, 2, 8]) {
|
|
devSpeed = speed; acc = 0; const before = tick;
|
|
const t = performance.now(); lastT = t - 34; lastRender = t - 34; frame(t);
|
|
counts.push(tick - before);
|
|
}
|
|
return counts;
|
|
}''')
|
|
check('speed scales fixed ticks and respects maxSteps', counts == [1, 2, 3, 3])
|
|
page.evaluate('devCommand("debt clear"); runDevCommand("money 1"); openNotebook("Claims")')
|
|
page.wait_for_function("$('nb-body').textContent.includes('paid off')")
|
|
check('cleared debt is reflected in Claims', True)
|
|
page.evaluate('closeAllModals(); runDevCommand("debt 123"); openNotebook("Market")')
|
|
check('new debt is reflected in the Market', '$123' in page.inner_text('#nb-body'))
|
|
page.evaluate('closeAllModals()')
|
|
page.evaluate("async () => { await saveWorld('main', true); await renderWorlds(); }")
|
|
page.evaluate("openModal('m-worlds')")
|
|
check('world list entry stores and draws the dev badge', page.locator('#worlds-list .dev-badge').is_visible() and page.inner_text('#worlds-list .dev-badge') == 'Dev console')
|
|
page.screenshot(path=str(SHOTS / 't66-worlds.png'))
|
|
page.evaluate('closeAllModals()')
|
|
check('saving and loading preserves mark and log, resets runtime controls', page.evaluate('''async () => {
|
|
const sv = await worldNewest(GAME.worldId);
|
|
const count = devLog.length;
|
|
await startFromSave(sv);
|
|
return devWorld && devLog.length === count && devSpeed === 1 && !devPaused && !devFree && $('dev-console').hidden;
|
|
}'''))
|
|
page.reload()
|
|
page.wait_for_function("!$('title').hidden")
|
|
check('history is restored on a fresh page', page.evaluate("devInputHistory.items.slice(-1)[0] === 'step 5' && devRecall(devInputHistory, -1) === 'step 5'"))
|
|
check('no browser errors', not errors)
|
|
browser.close()
|
|
|
|
sys.exit(1 if fails else 0)
|