import { describe, expect, it } from 'vitest'; import { gamePhase, groupGames, isMyTurn, shouldBlink } from './lobbysort'; import type { GameView, Seat } from './model'; const ME = 'me'; const seat = (s: number, accountId: string): Seat => ({ seat: s, accountId, displayName: accountId, score: 0, hintsUsed: 0, isWinner: false, }); function game(id: string, status: GameView['status'], toMove: number, lastActivityUnix: number): GameView { return { id, variant: 'scrabble_en', dictVersion: 'v1', vsAi: false, unreadChat: false, status, players: 2, toMove, turnTimeoutSecs: 0, multipleWordsPerTurn: true, moveCount: 0, endReason: '', lastActivityUnix, seats: [seat(0, ME), seat(1, 'opp')], }; } describe('groupGames', () => { it('partitions into your-turn, their-turn and finished', () => { const g = groupGames( [ game('a', 'active', 0, 100), // toMove 0 == my seat -> my turn game('b', 'active', 1, 100), // their turn game('c', 'finished', 0, 100), ], ME, ); expect(g.yourTurn.map((x) => x.id)).toEqual(['a']); expect(g.theirTurn.map((x) => x.id)).toEqual(['b']); expect(g.finished.map((x) => x.id)).toEqual(['c']); }); it('orders your-turn oldest-first, the other two newest-first', () => { const g = groupGames( [ game('y_new', 'active', 0, 200), game('y_old', 'active', 0, 100), game('t_new', 'active', 1, 200), game('t_old', 'active', 1, 100), game('f_new', 'finished', 0, 200), game('f_old', 'finished', 0, 100), ], ME, ); expect(g.yourTurn.map((x) => x.id)).toEqual(['y_old', 'y_new']); expect(g.theirTurn.map((x) => x.id)).toEqual(['t_new', 't_old']); expect(g.finished.map((x) => x.id)).toEqual(['f_new', 'f_old']); }); it('isMyTurn is false for a finished game even at my seat', () => { expect(isMyTurn(game('x', 'finished', 0, 0), ME)).toBe(false); expect(isMyTurn(game('x', 'active', 0, 0), ME)).toBe(true); expect(isMyTurn(game('x', 'active', 1, 0), ME)).toBe(false); }); it('treats an open game (awaiting an opponent) as in progress, not finished', () => { const g = groupGames( [ game('open_mine', 'open', 0, 100), // my turn while waiting game('open_wait', 'open', 1, 100), // the empty seat's turn ], ME, ); expect(g.yourTurn.map((x) => x.id)).toEqual(['open_mine']); expect(g.theirTurn.map((x) => x.id)).toEqual(['open_wait']); expect(g.finished).toEqual([]); expect(isMyTurn(game('x', 'open', 0, 0), ME)).toBe(true); }); }); describe('gamePhase', () => { it('maps each game to its lobby bucket (open folds into mine/theirs like active)', () => { expect(gamePhase(game('x', 'active', 0, 0), ME)).toBe('mine'); expect(gamePhase(game('x', 'active', 1, 0), ME)).toBe('theirs'); expect(gamePhase(game('x', 'open', 0, 0), ME)).toBe('mine'); expect(gamePhase(game('x', 'open', 1, 0), ME)).toBe('theirs'); expect(gamePhase(game('x', 'finished', 0, 0), ME)).toBe('finished'); }); }); describe('shouldBlink', () => { it('blinks on a transition into mine or finished', () => { expect(shouldBlink('theirs', 'mine')).toBe(true); expect(shouldBlink('mine', 'finished')).toBe(true); expect(shouldBlink('theirs', 'finished')).toBe(true); }); it('stays quiet on a transition into theirs, a no-op, or a first sighting', () => { expect(shouldBlink('mine', 'theirs')).toBe(false); expect(shouldBlink('mine', 'mine')).toBe(false); expect(shouldBlink(undefined, 'mine')).toBe(false); expect(shouldBlink(undefined, 'finished')).toBe(false); }); });