// Display guard: starting encounter does NOT steal display from another live encounter. // Ending encounter does NOT clear display if different encounter showing. import React from 'react'; import { screen, fireEvent, waitFor } from '@testing-library/react'; import '@testing-library/jest-dom'; import { getCalls, MOCK_DB } from '../__mocks__/firebase/_mock-db'; import { setupReady, addMonsterViaUI, startCombatViaUI } from './testHelpers'; const DISPLAY_PATH = 'artifacts/ttrpg-initiative-tracker-default/public/data/activeDisplay/status'; function findCallActiveDisplay(fn) { return getCalls().filter(c => c.fn === fn && c.path.includes('activeDisplay/status')); } describe('Display guard', () => { test('startEncounter: claims display when slot empty', async () => { await setupReady('GuardCamp1', 'GuardEnc1'); await addMonsterViaUI('Goblin', 10, 5); await startCombatViaUI(); const adCalls = findCallActiveDisplay('setDoc'); const last = adCalls[adCalls.length - 1]; expect(last).toBeTruthy(); expect(last.data.activeEncounterId).toBeTruthy(); }); test('startEncounter: does NOT steal display from another live encounter', async () => { // Pre-seed display as busy with a DIFFERENT encounter MOCK_DB.set(DISPLAY_PATH, { activeCampaignId: 'other-camp', activeEncounterId: 'other-enc', }); await setupReady('GuardCamp2', 'GuardEnc2'); await addMonsterViaUI('Orc', 15, 8); await startCombatViaUI(); // Combat started on encounter doc const encCalls = getCalls().filter(c => c.fn === 'updateDoc' && c.path.includes('/encounters/')); expect(encCalls.some(c => c.data.isStarted === true)).toBe(true); // But display setDoc should NOT have been called to claim this encounter const adClaims = findCallActiveDisplay('setDoc').filter( c => c.data && c.data.activeEncounterId && c.data.activeEncounterId !== 'other-enc' ); expect(adClaims).toHaveLength(0); }); test('endEncounter: clears display when ending the displayed encounter', async () => { await setupReady('GuardCamp3', 'GuardEnc3'); await addMonsterViaUI('Wolf', 8, 3); await startCombatViaUI(); fireEvent.click(screen.getByRole('button', { name: /End Combat/i })); fireEvent.click(await screen.findByRole('button', { name: /Confirm/i })); await waitFor(() => { const adCalls = findCallActiveDisplay('setDoc'); const last = adCalls[adCalls.length - 1]; return last && last.data && last.data.activeCampaignId === null; }); const adCalls = findCallActiveDisplay('setDoc'); const last = adCalls[adCalls.length - 1]; expect(last.data).toMatchObject({ activeCampaignId: null, activeEncounterId: null }); }); test('endEncounter: does NOT clear display when different encounter is live', async () => { await setupReady('GuardCamp4', 'GuardEnc4'); await addMonsterViaUI('Bat', 4, 2); await startCombatViaUI(); // Now hijack display to a different encounter (simulating eyeball toggle elsewhere) MOCK_DB.set(DISPLAY_PATH, { activeCampaignId: 'different-camp', activeEncounterId: 'different-enc', }); fireEvent.click(screen.getByRole('button', { name: /End Combat/i })); fireEvent.click(await screen.findByRole('button', { name: /Confirm/i })); // Wait for encounter to end await waitFor(() => { const encCalls = getCalls().filter(c => c.fn === 'updateDoc' && c.path.includes('/encounters/')); const last = encCalls[encCalls.length - 1]; return last && last.data.isStarted === false; }); // Display should NOT have been cleared const clearCalls = findCallActiveDisplay('setDoc').filter( c => c.data && c.data.activeCampaignId === null ); expect(clearCalls).toHaveLength(0); }); });