test: encounter characterization (6 tests)
- Encounter.characterization.test.js: createEncounter, path nesting, togglePlayerDisplay on/off, deleteEncounter + clears activeDisplay - testHelpers.js: createEncounterViaUI, selectEncounterByName Locks encounter write paths + payload shapes.
This commit is contained in:
@@ -0,0 +1,122 @@
|
|||||||
|
// Encounter characterization. Lock setDoc path + payload on encounter actions.
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { screen, fireEvent, waitFor } from '@testing-library/react';
|
||||||
|
import '@testing-library/jest-dom';
|
||||||
|
import { getCalls } from './__mocks__/firebase/_mock-db';
|
||||||
|
import { renderApp, createCampaignViaUI, selectCampaignByName, createEncounterViaUI, selectEncounterByName } from './testHelpers';
|
||||||
|
|
||||||
|
function findCall(fn, pathSub) {
|
||||||
|
return getCalls().find(c => c.fn === fn && (pathSub ? c.path.includes(pathSub) : true));
|
||||||
|
}
|
||||||
|
function findCalls(fn, pathSub) {
|
||||||
|
return getCalls().filter(c => c.fn === fn && (pathSub ? c.path.includes(pathSub) : true));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setupCampaignAndEncounter(campName, encName) {
|
||||||
|
await renderApp();
|
||||||
|
await createCampaignViaUI(campName);
|
||||||
|
await selectCampaignByName(campName);
|
||||||
|
await createEncounterViaUI(encName);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('Encounter -> Firebase', () => {
|
||||||
|
test('createEncounter: setDoc with encounter path + payload', async () => {
|
||||||
|
await setupCampaignAndEncounter('Camp E', 'Boss Fight');
|
||||||
|
const call = findCall('setDoc', '/encounters/');
|
||||||
|
expect(call.path).toMatch(/encounters\/.+$/);
|
||||||
|
expect(call.data).toMatchObject({
|
||||||
|
name: 'Boss Fight',
|
||||||
|
participants: [],
|
||||||
|
round: 0,
|
||||||
|
currentTurnParticipantId: null,
|
||||||
|
isStarted: false,
|
||||||
|
isPaused: false,
|
||||||
|
});
|
||||||
|
expect(call.data).toHaveProperty('createdAt');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('createEncounter: path nested under campaign', async () => {
|
||||||
|
await setupCampaignAndEncounter('Camp N', 'Enc N');
|
||||||
|
const call = findCall('setDoc', '/encounters/');
|
||||||
|
expect(call.path).toMatch(/campaigns\/[^/]+\/encounters\//);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('togglePlayerDisplay: setDoc merge on activeDisplay/status', async () => {
|
||||||
|
await setupCampaignAndEncounter('Camp D', 'Enc D');
|
||||||
|
await selectEncounterByName('Enc D');
|
||||||
|
|
||||||
|
// Eye button (icon-only, title attr)
|
||||||
|
const eyeBtn = await screen.findByTitle('Activate for Player Display');
|
||||||
|
fireEvent.click(eyeBtn);
|
||||||
|
|
||||||
|
await waitFor(() => findCall('setDoc', 'activeDisplay/status'));
|
||||||
|
const call = findCall('setDoc', 'activeDisplay/status');
|
||||||
|
// activeDisplay/status setDoc is called with merge option in App
|
||||||
|
expect(call.data).toMatchObject({
|
||||||
|
activeCampaignId: expect.any(String),
|
||||||
|
activeEncounterId: expect.any(String),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('togglePlayerDisplay off: setDoc nulls active ids', async () => {
|
||||||
|
await setupCampaignAndEncounter('Camp O', 'Enc O');
|
||||||
|
await selectEncounterByName('Enc O');
|
||||||
|
|
||||||
|
// turn ON
|
||||||
|
const onBtn = await screen.findByTitle('Activate for Player Display');
|
||||||
|
fireEvent.click(onBtn);
|
||||||
|
await waitFor(() => findCall('setDoc', 'activeDisplay/status'));
|
||||||
|
|
||||||
|
// turn OFF
|
||||||
|
const offBtn = await screen.findByTitle('Deactivate for Player Display');
|
||||||
|
fireEvent.click(offBtn);
|
||||||
|
await waitFor(() => {
|
||||||
|
const calls = findCalls('setDoc', 'activeDisplay/status');
|
||||||
|
const last = calls[calls.length - 1];
|
||||||
|
return last.data.activeCampaignId === null;
|
||||||
|
});
|
||||||
|
const calls = findCalls('setDoc', 'activeDisplay/status');
|
||||||
|
const last = calls[calls.length - 1];
|
||||||
|
expect(last.data).toMatchObject({ activeCampaignId: null, activeEncounterId: null });
|
||||||
|
});
|
||||||
|
|
||||||
|
test('deleteEncounter: deleteDoc on encounter path', async () => {
|
||||||
|
await setupCampaignAndEncounter('Camp X', 'Enc X');
|
||||||
|
await selectEncounterByName('Enc X');
|
||||||
|
|
||||||
|
// trash icon on encounter card
|
||||||
|
const trashBtn = screen.getAllByTitle('Delete Encounter')[0];
|
||||||
|
fireEvent.click(trashBtn);
|
||||||
|
// confirm modal
|
||||||
|
fireEvent.click(await screen.findByRole('button', { name: /Confirm/i }));
|
||||||
|
|
||||||
|
await waitFor(() => findCall('deleteDoc', '/encounters/'));
|
||||||
|
const del = findCall('deleteDoc', '/encounters/');
|
||||||
|
expect(del.path).toMatch(/campaigns\/[^/]+\/encounters\//);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('deleteEncounter clears activeDisplay if it was active', async () => {
|
||||||
|
await setupCampaignAndEncounter('Camp A', 'Enc A');
|
||||||
|
await selectEncounterByName('Enc A');
|
||||||
|
|
||||||
|
// activate display first
|
||||||
|
const onBtn = await screen.findByTitle('Activate for Player Display');
|
||||||
|
fireEvent.click(onBtn);
|
||||||
|
await waitFor(() => findCall('setDoc', 'activeDisplay/status'));
|
||||||
|
|
||||||
|
// delete the active encounter
|
||||||
|
const trashBtn = screen.getAllByTitle('Delete Encounter')[0];
|
||||||
|
fireEvent.click(trashBtn);
|
||||||
|
fireEvent.click(await screen.findByRole('button', { name: /Confirm/i }));
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
const adCalls = findCalls('updateDoc', 'activeDisplay/status');
|
||||||
|
const last = adCalls[adCalls.length - 1];
|
||||||
|
return last.data.activeEncounterId === null;
|
||||||
|
});
|
||||||
|
const adCalls = findCalls('updateDoc', 'activeDisplay/status');
|
||||||
|
const last = adCalls[adCalls.length - 1];
|
||||||
|
expect(last.data).toMatchObject({ activeCampaignId: null, activeEncounterId: null });
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -34,4 +34,23 @@ export async function selectCampaignByName(name) {
|
|||||||
await waitFor(() => screen.getByText(/Managing:/i));
|
await waitFor(() => screen.getByText(/Managing:/i));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Open create-encounter modal, fill name, submit. Assumes campaign selected.
|
||||||
|
export async function createEncounterViaUI(name = 'Test Encounter') {
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: /Create Encounter/i }));
|
||||||
|
await waitFor(() => screen.getByLabelText(/Encounter Name/i));
|
||||||
|
fireEvent.change(screen.getByLabelText(/Encounter Name/i), { target: { value: name } });
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: /^Create$/i }));
|
||||||
|
const { getCalls } = require('./__mocks__/firebase/_mock-db');
|
||||||
|
await waitFor(() => getCalls().find(c => c.fn === 'setDoc' && c.path.includes('/encounters/')));
|
||||||
|
const call = getCalls().find(c => c.fn === 'setDoc' && c.path.includes('/encounters/'));
|
||||||
|
return call.path.split('/').pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Click encounter card by name. Assumes campaign selected.
|
||||||
|
export async function selectEncounterByName(name) {
|
||||||
|
const card = await waitFor(() => screen.getByText(name));
|
||||||
|
fireEvent.click(card);
|
||||||
|
await waitFor(() => screen.getByText(/Managing Encounter:/i));
|
||||||
|
}
|
||||||
|
|
||||||
export { MOCK_DB };
|
export { MOCK_DB };
|
||||||
|
|||||||
Reference in New Issue
Block a user