// The others in the project (plan §7.4): another editor's state read with // care, the others by name without this editor, each person once however // many tabs they have, their choices as frames and their windows as dots, // a colour for whoever the dashboard gave none, initials; and this editor's // state told only when it changed — nothing that changes without a cause. import { describe, expect, it } from 'vitest'; import { initialsOf } from '../src/remote/PresenceMarks.js'; import { colorOf, peersOf, peopleOf, PRESENCE_COLORS, readPresence, selectionsOf, watchersOf, windowKey, type PresenceState } from '../src/remote/presence.js'; import { presencePublisher } from '../src/remote/usePresence.js'; const state = (id: string, name: string, selection: string | null = null, windows: string[] = [], color = '#112233'): PresenceState => ({ v: 1, user: { id, name, color }, selection, windows }); describe('the others in the project', () => { it('reads a state of the shape it writes, and leaves out any other', () => { expect(readPresence(state('u', 'Mia', 'd', ['desktop:d']))).toEqual(state('u', 'Mia', 'd', ['desktop:d'])); for (const bad of [ null, 'Mia', { ...state('u', 'Mia'), v: 2 }, { ...state('u', 'Mia'), user: null }, { ...state('', 'Mia') }, { ...state('u', 7 as unknown as string) }, { ...state('u', 'x'.repeat(201)) }, { ...state('u', 'Mia'), user: { id: 'u', name: 'Mia', color: 'c'.repeat(33) } }, { ...state('u', 'Mia'), selection: 5 }, { ...state('u', 'Mia'), windows: 'desktop:d' }, { ...state('u', 'Mia'), windows: [7] }, { ...state('u', 'Mia'), windows: Array.from({ length: 101 }, () => 'w') }, ]) expect(readPresence(bad)).toBeUndefined(); }); it('lists the other editors by name, without this one and without what it cannot read', () => { const states = new Map([ [1, state('me', 'Zoe')], [2, state('b', 'Ben')], [3, { cursor: { x: 1 } }], [4, state('a', 'Ada')], [5, state('b', 'Ben')], ]); expect(peersOf(states, 1).map((peer) => [peer.clientId, peer.state.user.name])).toEqual([ [4, 'Ada'], [2, 'Ben'], [5, 'Ben'], ]); }); it('counts a person with two tabs once, and leaves out this person in another tab', () => { const peers = peersOf( new Map([ [2, state('b', 'Ben')], [3, state('b', 'Ben')], [4, state('me', 'Zoe')], ]), 1, ); expect(peopleOf(peers, 'me')).toEqual([{ id: 'b', name: 'Ben', color: '#112233' }]); expect(peopleOf(peers).map((user) => user.id)).toEqual(['b', 'me']); }); it('gives each chosen device the colours of who chose it, each person once', () => { const peers = peersOf( new Map([ [2, state('b', 'Ben', 'router', [], '#aa0000')], [3, state('b', 'Ben', 'router', [], '#aa0000')], [4, state('a', 'Ada', 'router', [], '#00aa00')], [5, state('c', 'Cem', 'pc', [], '#0000aa')], [6, state('d', 'Dan', null)], ]), 1, ); expect(selectionsOf(peers)).toEqual( new Map([ ['router', ['#00aa00', '#aa0000']], ['pc', ['#0000aa']], ]), ); }); it('gives each open window who has it open, each person once', () => { const desktop = windowKey('desktop', 'pc'); const peers = peersOf( new Map([ [2, state('b', 'Ben', null, [desktop, 'capture'])], [3, state('b', 'Ben', null, [desktop])], [4, state('a', 'Ada', null, [windowKey('sat', 's')])], ]), 1, ); const watchers = watchersOf(peers); expect([...watchers.keys()].sort()).toEqual(['capture', 'desktop:pc', 'sat:s']); expect(watchers.get(desktop)!.map((user) => user.id)).toEqual(['b']); expect([windowKey('nat', 'g'), windowKey('capture', 'ignored')]).toEqual(['nat:g', 'capture']); }); it("takes the dashboard's colour, and gives one by the id to whoever has none", () => { expect(colorOf({ id: 'x', name: 'X', color: '#ABCDEF' })).toBe('#ABCDEF'); const given = colorOf({ id: 'person-1', name: 'X', color: '' }); expect(PRESENCE_COLORS).toContain(given); expect(colorOf({ id: 'person-1', name: 'Y', color: 'blue' })).toBe(given); // Every editor gives the same colour, whatever its version: pinned. expect([colorOf({ id: '0b8f5f2e-1a3c-4d5e-8f90-123456789abc', name: '', color: '' }), colorOf({ id: 'person-2', name: '', color: '' })]).toEqual(['#e67700', '#1971c2']); const all = new Set(Array.from({ length: 40 }, (_, index) => colorOf({ id: `p${index}`, name: '', color: '' }))); expect(all.size).toBeGreaterThan(4); }); it('writes the initials of the first and the last word', () => { expect([initialsOf('Ada Beispiel'), initialsOf('mia'), initialsOf('Jörg Ömer Weiß'), initialsOf(' '), initialsOf('🐧 Pinguin')]).toEqual(['AB', 'M', 'JW', '?', '🐧P']); }); it("tells this editor's state only when it changed", () => { const set: unknown[] = []; const publish = presencePublisher({ setLocalState: (value) => void set.push(value) }, { id: 'me', name: 'Zoe', color: '' }); publish(null, []); publish(null, []); publish('pc', []); publish('pc', []); publish('pc', ['desktop:pc']); expect(set).toEqual([ { v: 1, user: { id: 'me', name: 'Zoe', color: '' }, selection: null, windows: [] }, { v: 1, user: { id: 'me', name: 'Zoe', color: '' }, selection: 'pc', windows: [] }, { v: 1, user: { id: 'me', name: 'Zoe', color: '' }, selection: 'pc', windows: ['desktop:pc'] }, ]); }); });