Dev console: name suggestions, materials list, history, Tab completion, help, clear (t94)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-28 23:35:42 -04:00
co-authored by Claude Opus 5.5
parent 780216b174
commit 1db265a018
8 changed files with 210 additions and 39 deletions
+18 -1
View File
@@ -44,7 +44,7 @@ for (const text of ['money', 'money set', 'money Infinity', 'money 1e309', 'mone
check('bad arguments produce a helpful error: ' + text, output.length > 0 && !output[0].startsWith('>'));
}
check('invalid commands do not mutate or log', beforeBad === run('JSON.stringify([money, debt, bagTotal, player, devLog, devSpeed, devSteps])'));
check('help lists every command', command('help').length === 13);
check('help lists every command', command('help').length === 15);
run('money = 17; bag.fill(0); bagTotal = 0; bagAdd(RSTONE, 32, true); bagAdd(LCLAY, 100, true);');
check('normal build costs are checked', run('!canAfford([[CASH, 100]], 1)'));
command('free on');
@@ -72,4 +72,21 @@ run('alloc(240,160)');
check('a new world starts unmarked without a dev log', run('!devWorld && devLog.length === 0'));
check('give turns rock into its rubble and says so', run(`(() => { enableDevConsole(); const before = bag[RBAS]; const out = devCommand('give basalt 50').join(' '); return bag[RBAS] === before + 50 && bag[BASALT] === 0 && /Basalt rubble/.test(out); })()`));
check('give refuses things that cannot be in a bag', run(`(() => { enableDevConsole(); const out = devCommand('give steam 5').join(' '); return /cannot go in the bag/.test(out) && bag[STEAM] === 0; })()`));
check('material matching ignores spaces and case', run("devMaterial('CoPpErOrE') === CUORE && devMaterial('copper bar') === BARCU"));
check('unknown material suggests close giveable names', /Did you mean:.*Copper ore/i.test(command('give coperore')[0]));
check('materials lists giveable names with internal names', command('materials').some(s => /Copper ore.*CUORE/.test(s)) && !command('materials').some(s => /STEAM/.test(s)));
check('materials filtering ignores spaces and case', command('materials CoPpErOrE').every(s => /Copper ore/i.test(s)) && command('materials copperore').some(s => /CUORE/.test(s)));
check('help for a command has usage and example', command('help give').some(s => s.startsWith('Usage: give')) && command('help give').some(s => s.startsWith('Example: give copper ore')));
check('all commands have detailed help', run("Object.keys(DEV_COMMANDS).every(k => devCommand('help ' + k).some(s => s.startsWith('Example:')))"));
check('errors carry explicit status while results do not', command('give no-such-material').error === true && !command('materials').error && command('money nope').error === true);
check('clear requests output clearing without changing world', command('clear').clear === true);
check('command completion cycles and wraps', run("(() => { const a = devComplete('s'), b = devComplete(a.text, a), c = devComplete(b.text, b); return a.matches.length === 2 && a.text === 'speed' && b.text === 'step' && c.text === a.text; })()"));
check('completion keeps spaced materials and amount', run("devComplete('give copperore 25').matches.includes('give Copper ore 25')"));
check('completion supports internal names', run("devComplete('give cuor').text === 'give Copper ore'"));
check('completion cycles ambiguous materials', run("(() => { const a = devComplete('give copper'), b = devComplete(a.text, a); return a.matches.length > 1 && a.text !== b.text; })()"));
check('completion supports keywords and help commands', run("devComplete('free o').matches.length === 2 && devComplete('money s').text === 'money set' && devComplete('debt c').text === 'debt clear' && devComplete('weather r').text === 'weather rain' && devComplete('help gi').text === 'help give'"));
check('completion leaves unmatched input intact', run("devComplete('give zzzzz').text === 'give zzzzz'"));
check('history validates stored data', run("devHistory(null).items.length === 0 && devHistory(['ok', 5, '', ' ']).items.join() === 'ok'"));
check('history caps at 50 and navigates to empty after newest', run("(() => { const h = devHistory([]); for (let i=0; i<55; i++) devRemember(h, 'money ' + i); devRemember(h, ' '); const newest = devRecall(h, -1); for (let i=0;i<60;i++) devRecall(h,-1); const oldest = devRecall(h,-1); for(let i=0;i<60;i++) devRecall(h,1); return h.items.length === 50 && newest === 'money 54' && oldest === 'money 5' && devRecall(h,1) === ''; })()"));
done();
+28
View File
@@ -59,6 +59,31 @@ with launch() as (browser, page, url):
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('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)
@@ -93,6 +118,9 @@ with launch() as (browser, page, url):
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()