const test = require('node:test'); const assert = require('node:assert/strict'); function loadApi() { let api; assert.doesNotThrow(() => { api = require('../app.js'); }, 'expected app.js to export the Conway helpers'); return api; } test('blinker rotates after one generation', () => { const { createEmptyGrid, stepGrid } = loadApi(); const grid = createEmptyGrid(5, 5); grid[2][1] = 1; grid[2][2] = 1; grid[2][3] = 1; const next = stepGrid(grid); assert.deepEqual(next, [ [0, 0, 0, 0, 0], [0, 0, 1, 0, 0], [0, 0, 1, 0, 0], [0, 0, 1, 0, 0], [0, 0, 0, 0, 0], ]); }); test('block stays stable across generations', () => { const { createEmptyGrid, stepGrid } = loadApi(); const grid = createEmptyGrid(4, 4); grid[1][1] = 1; grid[1][2] = 1; grid[2][1] = 1; grid[2][2] = 1; assert.deepEqual(stepGrid(grid), grid); }); test('stampPattern places the glider cells inside the grid', () => { const { createEmptyGrid, stampPattern, PATTERNS } = loadApi(); const grid = createEmptyGrid(6, 6); stampPattern(grid, PATTERNS.glider, 1, 1); assert.equal(grid[1][2], 1); assert.equal(grid[2][3], 1); assert.equal(grid[3][1], 1); assert.equal(grid[3][2], 1); assert.equal(grid[3][3], 1); }); test('createInitialState seeds the default preset and starts paused', () => { const { createInitialState } = loadApi(); const state = createInitialState({ rows: 20, cols: 20, defaultPattern: 'pulsar', }); assert.equal(state.running, false); assert.equal(state.selectedPattern, 'pulsar'); assert.equal(state.generation, 0); assert.ok(state.liveCount > 0); }); test('toggleCell flips a single cell without mutating the original grid', () => { const { createEmptyGrid, toggleCell } = loadApi(); const grid = createEmptyGrid(3, 3); const next = toggleCell(grid, 1, 1); assert.equal(grid[1][1], 0); assert.equal(next[1][1], 1); }); test('applyPreset replaces the grid, pauses playback, and resets generation', () => { const { applyPreset, createInitialState } = loadApi(); const state = { ...createInitialState({ rows: 30, cols: 30, defaultPattern: 'glider' }), generation: 12, running: true, }; const next = applyPreset(state, 'gosperGliderGun'); assert.equal(next.running, false); assert.equal(next.generation, 0); assert.equal(next.selectedPattern, 'gosperGliderGun'); assert.ok(next.liveCount > 0); }); test('advanceState steps the grid and increments generation', () => { const { advanceState, createEmptyGrid } = loadApi(); const grid = createEmptyGrid(5, 5); grid[2][1] = 1; grid[2][2] = 1; grid[2][3] = 1; const next = advanceState({ cols: 5, generation: 0, grid: grid, liveCount: 3, rows: 5, running: true, selectedPattern: 'custom', speed: 1, }); assert.equal(next.generation, 1); assert.equal(next.liveCount, 3); assert.equal(next.grid[1][2], 1); assert.equal(next.grid[2][2], 1); assert.equal(next.grid[3][2], 1); }); test('randomizeGrid respects probability extremes', () => { const { createEmptyGrid, randomizeGrid } = loadApi(); const grid = createEmptyGrid(2, 3); assert.deepEqual(randomizeGrid(grid, 0), [ [0, 0, 0], [0, 0, 0], ]); assert.deepEqual(randomizeGrid(grid, 1), [ [1, 1, 1], [1, 1, 1], ]); }); test('setSpeed returns a copied state with a readable label', () => { const { createInitialState, getSpeedDelay, getSpeedLabel, setSpeed } = loadApi(); const state = createInitialState({ rows: 10, cols: 10, defaultPattern: 'glider' }); const next = setSpeed(state, 4); assert.notEqual(next, state); assert.equal(next.speed, 4); assert.equal(getSpeedLabel(next.speed), 'Hyper'); assert.equal(getSpeedLabel(1), 'Drift'); assert.equal(getSpeedDelay(1), 520); assert.equal(getSpeedDelay(6), 60); });