Rework backend #1

Merged
robert merged 86 commits from rework-backend into main 2026-07-01 19:29:34 -04:00
2 changed files with 65 additions and 0 deletions
Showing only changes of commit d979b03f2e - Show all commits
+2
View File
@@ -86,10 +86,12 @@ export function createFirebaseStorage() {
}, },
async setDoc(path, data, opts = {}) { async setDoc(path, data, opts = {}) {
recordAdapterCall({ fn: 'setDoc', path, data, opts });
await setDoc(doc(db, path), data, opts.merge ? { merge: true } : undefined); await setDoc(doc(db, path), data, opts.merge ? { merge: true } : undefined);
}, },
async updateDoc(path, patch) { async updateDoc(path, patch) {
recordAdapterCall({ fn: 'updateDoc', path, patch });
await updateDoc(doc(db, path), patch); await updateDoc(doc(db, path), patch);
}, },
+63
View File
@@ -0,0 +1,63 @@
// 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(<App />);
// 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/i }, { timeout: 3000 });
// toggle ON
fireEvent.click(toggle);
await waitFor(() => {
const writes = getAdapterCalls().filter(
c => c.fn === 'setDoc' && c.path.includes('activeDisplay/status')
);
expect(writes.length).toBeGreaterThan(0);
const last = writes[writes.length - 1];
// data written must include activeCampaignId AND activeEncounterId
// BUG: writes only {hidePlayerHp:true}, clobbering them.
expect(last.data.activeCampaignId).toBe('c1');
expect(last.data.activeEncounterId).toBe('e1');
}, { timeout: 3000 });
});
});