ac29dca865
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Has been skipped
CI / integration (pull_request) Has been skipped
CI / ui (pull_request) Successful in 41s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 57s
- Highlight tracks the last move overall (not the last word): a trailing pass/exchange now highlights nothing, so the board no longer lights up the opponent's old word after our own empty move. - Make the highlight event-driven: refreshed only on a real game event (open/refresh, opponent move, our own committed move) and dismissed the moment composing starts, so recalling a just-placed tile never re-triggers it. - Localize non-play move-history labels via new move.* catalog keys (pass/exchange/resign/timeout); the label printed the raw English action. - Clamp the zoomed board's pan at its edge (overscroll-behavior: none), removing the native rubber-band past the content. Tests: lastMoveCells unit coverage (trailing pass/exchange -> empty), i18n RU label assertions, an e2e overscroll-contract check on the zoomed viewport.
72 lines
2.1 KiB
TypeScript
72 lines
2.1 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import { lastMoveCells, replay } from './board';
|
|
import type { MoveRecord } from './model';
|
|
|
|
function play(tiles: { row: number; col: number; letter: string; blank: boolean }[]): MoveRecord {
|
|
return {
|
|
player: 0,
|
|
action: 'play',
|
|
dir: 'H',
|
|
mainRow: tiles[0].row,
|
|
mainCol: tiles[0].col,
|
|
tiles,
|
|
words: [],
|
|
count: 0,
|
|
score: 0,
|
|
total: 0,
|
|
};
|
|
}
|
|
|
|
function simple(action: 'pass' | 'exchange'): MoveRecord {
|
|
return { player: 1, action, dir: '', mainRow: 0, mainCol: 0, tiles: [], words: [], count: 0, score: 0, total: 0 };
|
|
}
|
|
|
|
const pass = simple('pass');
|
|
|
|
describe('board replay', () => {
|
|
it('places play tiles and ignores non-play moves', () => {
|
|
const moves = [
|
|
play([
|
|
{ row: 7, col: 7, letter: 'A', blank: false },
|
|
{ row: 7, col: 8, letter: 'B', blank: true },
|
|
]),
|
|
pass,
|
|
play([{ row: 8, col: 7, letter: 'C', blank: false }]),
|
|
];
|
|
const b = replay(moves);
|
|
expect(b[7][7]?.letter).toBe('A');
|
|
expect(b[7][8]?.blank).toBe(true);
|
|
expect(b[8][7]?.letter).toBe('C');
|
|
expect(b[0][0]).toBeNull();
|
|
expect(b.length).toBe(15);
|
|
expect(b[0].length).toBe(15);
|
|
});
|
|
});
|
|
|
|
describe('lastMoveCells', () => {
|
|
it('returns the last move cells when the last move is a play', () => {
|
|
const moves = [
|
|
pass,
|
|
play([
|
|
{ row: 7, col: 7, letter: 'A', blank: false },
|
|
{ row: 7, col: 8, letter: 'B', blank: false },
|
|
]),
|
|
];
|
|
expect(lastMoveCells(moves)).toEqual(new Set(['7,7', '7,8']));
|
|
});
|
|
|
|
it('highlights nothing when the last move is a pass after a play', () => {
|
|
const moves = [play([{ row: 7, col: 7, letter: 'A', blank: false }]), pass];
|
|
expect(lastMoveCells(moves).size).toBe(0);
|
|
});
|
|
|
|
it('highlights nothing when the last move is an exchange', () => {
|
|
const moves = [play([{ row: 7, col: 7, letter: 'A', blank: false }]), simple('exchange')];
|
|
expect(lastMoveCells(moves).size).toBe(0);
|
|
});
|
|
|
|
it('highlights nothing for an empty journal', () => {
|
|
expect(lastMoveCells([]).size).toBe(0);
|
|
});
|
|
});
|