/** * @jest-environment jsdom */ function setupDOM() { document.body.innerHTML = `
Player X's turn
${Array.from({ length: 9 }, (_, i) => `
`).join('')}
0 0 0 `; } function click(index) { document.querySelectorAll('.cell')[index].dispatchEvent(new MouseEvent('click', { bubbles: true })); } function reset() { document.getElementById('reset').dispatchEvent(new MouseEvent('click', { bubbles: true })); } beforeEach(() => { setupDOM(); jest.resetModules(); require('../public/game.js'); }); describe('initial state', () => { test('all cells empty', () => { document.querySelectorAll('.cell').forEach(c => expect(c.textContent).toBe('')); }); test('status shows X turn', () => { expect(document.getElementById('status').textContent).toBe("Player X's turn"); }); test('scores start at 0', () => { expect(document.getElementById('score-x').textContent).toBe('0'); expect(document.getElementById('score-o').textContent).toBe('0'); expect(document.getElementById('score-draw').textContent).toBe('0'); }); }); describe('turn alternation', () => { test('alternates X and O after each move', () => { click(0); expect(document.getElementById('status').textContent).toBe("Player O's turn"); click(1); expect(document.getElementById('status').textContent).toBe("Player X's turn"); }); test('cell shows correct player mark', () => { click(0); expect(document.querySelectorAll('.cell')[0].textContent).toBe('X'); click(1); expect(document.querySelectorAll('.cell')[1].textContent).toBe('O'); }); test('clicking taken cell does nothing', () => { click(0); click(0); expect(document.getElementById('status').textContent).toBe("Player O's turn"); }); }); describe('X wins', () => { // X: 0,1,2 O: 3,4 beforeEach(() => { click(0); click(3); click(1); click(4); click(2); }); test('status shows X wins', () => { expect(document.getElementById('status').textContent).toBe('Player X wins!'); }); test('winning cells get winner class', () => { const cells = document.querySelectorAll('.cell'); expect(cells[0].classList.contains('winner')).toBe(true); expect(cells[1].classList.contains('winner')).toBe(true); expect(cells[2].classList.contains('winner')).toBe(true); }); test('X score increments', () => { expect(document.getElementById('score-x').textContent).toBe('1'); }); test('no moves accepted after win', () => { click(5); expect(document.querySelectorAll('.cell')[5].textContent).toBe(''); }); }); describe('O wins', () => { // X: 0,6,7 O: 3,4,5 beforeEach(() => { click(0); click(3); click(6); click(4); click(7); click(5); }); test('status shows O wins', () => { expect(document.getElementById('status').textContent).toBe('Player O wins!'); }); test('O score increments', () => { expect(document.getElementById('score-o').textContent).toBe('1'); }); }); describe('draw', () => { // X: 0,2,5,6,7 O: 1,3,4,8 — fills board, no winner beforeEach(() => { click(0); click(1); click(2); click(3); click(5); click(4); click(6); click(8); click(7); }); test('status shows draw', () => { expect(document.getElementById('status').textContent).toBe("It's a draw!"); }); test('draw score increments', () => { expect(document.getElementById('score-draw').textContent).toBe('1'); }); }); describe('reset', () => { test('clears board after game', () => { click(0); click(1); reset(); document.querySelectorAll('.cell').forEach(c => expect(c.textContent).toBe('')); }); test('resets status to X turn', () => { click(0); reset(); expect(document.getElementById('status').textContent).toBe("Player X's turn"); }); test('score persists across reset', () => { click(0); click(3); click(1); click(4); click(2); reset(); expect(document.getElementById('score-x').textContent).toBe('1'); }); test('allows play after reset', () => { click(0); click(1); click(2); click(3); click(4); click(5); click(6); click(7); click(8); reset(); click(0); expect(document.querySelectorAll('.cell')[0].textContent).toBe('X'); }); }); describe('score accumulates across games', () => { test('multiple wins tracked', () => { click(0); click(3); click(1); click(4); click(2); reset(); click(0); click(3); click(1); click(4); click(2); expect(document.getElementById('score-x').textContent).toBe('2'); }); });