// BUG-4 repro: toggling hidePlayerHp must not clobber activeDisplay doc. // setDoc = replace (contract). {merge:true} arg ignored. // Toggling hide-HP writes {hidePlayerHp:X} alone → activeCampaignId + activeEncounterId → null. // Display reads null → "Game Session Paused". Recover requires re-activating encounter. // Fix: use updateDoc (patch), not setDoc. import React from 'react'; import { render, waitFor, screen, fireEvent } from '@testing-library/react'; import '@testing-library/jest-dom'; import App from '../App'; import { MOCK_DB } from '../__mocks__/firebase/_mock-db'; import { getAdapterCalls, resetAdapterCalls } from '../storage/firebase'; import { selectCampaignByName } from './testHelpers'; function seedAdminWithActiveEncounter() { const campaignPath = 'artifacts/ttrpg-initiative-tracker-default/public/data/campaigns/c1'; const encounterPath = 'artifacts/ttrpg-initiative-tracker-default/public/data/campaigns/c1/encounters/e1'; const activeDisplayPath = 'artifacts/ttrpg-initiative-tracker-default/public/data/activeDisplay/status'; MOCK_DB.set(campaignPath, { name: 'Camp', playerDisplayBackgroundUrl: '' }); MOCK_DB.set(encounterPath, { name: 'Enc', participants: [], isStarted: true }); // active encounter set, HP NOT hidden MOCK_DB.set(activeDisplayPath, { activeCampaignId: 'c1', activeEncounterId: 'e1', hidePlayerHp: false, }); } describe('BUG-4: hide-player-HP toggle preserves activeDisplay', () => { beforeEach(() => { window.history.replaceState({}, '', '/'); global.alert = jest.fn(); resetAdapterCalls(); }); test('toggling hidePlayerHp does NOT clear activeCampaignId/activeEncounterId', async () => { seedAdminWithActiveEncounter(); render(); // wait for admin to mount + load active display await waitFor(() => screen.getByText('Camp'), { timeout: 3000 }); await selectCampaignByName('Camp'); // find the hide-player-HP toggle (role switch) const toggle = await screen.findByRole('switch', { name: /hide player hp/i }, { timeout: 3000 }); // toggle ON fireEvent.click(toggle); await waitFor(() => { const writes = getAdapterCalls().filter( c => c.fn === 'updateDoc' && c.path.includes('activeDisplay/status') ); expect(writes.length).toBeGreaterThan(0); const last = writes[writes.length - 1]; // patch must NOT clobber activeCampaignId/activeEncounterId. // BUG: setDoc replace writes only {hidePlayerHp:true} → clobbers. // Fix: updateDoc patch — other fields untouched. expect(last.patch.hidePlayerHp).toBe(true); }, { timeout: 3000 }); }); });