Rework backend #1
+2
-3
@@ -2151,11 +2151,10 @@ function AdminView({ userId }) {
|
|||||||
if (
|
if (
|
||||||
initialActiveInfo &&
|
initialActiveInfo &&
|
||||||
initialActiveInfo.activeCampaignId &&
|
initialActiveInfo.activeCampaignId &&
|
||||||
campaignsWithDetails.length > 0 &&
|
campaignsWithDetails.length > 0
|
||||||
!selectedCampaignId
|
|
||||||
) {
|
) {
|
||||||
const campaignExists = campaignsWithDetails.some(c => c.id === initialActiveInfo.activeCampaignId);
|
const campaignExists = campaignsWithDetails.some(c => c.id === initialActiveInfo.activeCampaignId);
|
||||||
if (campaignExists) {
|
if (campaignExists && selectedCampaignId !== initialActiveInfo.activeCampaignId) {
|
||||||
setSelectedCampaignId(initialActiveInfo.activeCampaignId);
|
setSelectedCampaignId(initialActiveInfo.activeCampaignId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
// RED test: campaign selection must follow activeDisplay.activeCampaignId changes.
|
||||||
|
// Bug: once selected, new activeDisplay writes ignored (guard `!selectedCampaignId`).
|
||||||
|
// Scenario: replay tool writes activeDisplay to new campaign -> UI must switch.
|
||||||
|
import React from 'react';
|
||||||
|
import { screen, waitFor } from '@testing-library/react';
|
||||||
|
import '@testing-library/jest-dom';
|
||||||
|
import { MOCK_DB } from '../__mocks__/firebase/_mock-db';
|
||||||
|
import { renderApp, createCampaignViaUI, selectCampaignByName } from './testHelpers';
|
||||||
|
|
||||||
|
const PUBLIC_DATA = 'artifacts/ttrpg-initiative-tracker-default/public/data';
|
||||||
|
|
||||||
|
describe('Selection follows activeDisplay (BUG-12)', () => {
|
||||||
|
test('new activeCampaignId switches selection', async () => {
|
||||||
|
await renderApp();
|
||||||
|
const idA = await createCampaignViaUI('Campaign A');
|
||||||
|
const idB = await createCampaignViaUI('Campaign B');
|
||||||
|
|
||||||
|
// seed activeDisplay so useFirestoreDocument has a value to emit
|
||||||
|
const activePath = Object.keys(MOCK_DB._state.docs).find(p => p.includes('/activeDisplay/status'))
|
||||||
|
|| `${PUBLIC_DATA}/activeDisplay/status`;
|
||||||
|
MOCK_DB.set(activePath, { activeCampaignId: null, activeEncounterId: null });
|
||||||
|
|
||||||
|
// manually select A first
|
||||||
|
await selectCampaignByName('Campaign A');
|
||||||
|
expect(screen.getByText(/Managing:/i).textContent).toMatch(/Campaign A/);
|
||||||
|
|
||||||
|
// simulate replay/another-DM setting activeDisplay to B
|
||||||
|
MOCK_DB.merge(activePath, { activeCampaignId: idB });
|
||||||
|
|
||||||
|
// selection should now follow -> Managing: Campaign B
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText(/Managing:/i).textContent).toMatch(/Campaign B/);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('activeDisplay cleared (null) does not force-select', async () => {
|
||||||
|
await renderApp();
|
||||||
|
const idA = await createCampaignViaUI('Persist A');
|
||||||
|
const activePath = Object.keys(MOCK_DB._state.docs).find(p => p.includes('/activeDisplay/status'))
|
||||||
|
|| `${PUBLIC_DATA}/activeDisplay/status`;
|
||||||
|
MOCK_DB.set(activePath, { activeCampaignId: null, activeEncounterId: null });
|
||||||
|
await selectCampaignByName('Persist A');
|
||||||
|
|
||||||
|
MOCK_DB.merge(activePath, { activeCampaignId: null });
|
||||||
|
|
||||||
|
// should stay on A (manual selection persists)
|
||||||
|
expect(screen.getByText(/Managing:/i).textContent).toMatch(/Persist A/);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -33,10 +33,10 @@ export async function createCampaignViaUI(name = 'Test Campaign') {
|
|||||||
await waitFor(() => screen.getByLabelText(/Campaign Name/i));
|
await waitFor(() => screen.getByLabelText(/Campaign Name/i));
|
||||||
fireEvent.change(screen.getByLabelText(/Campaign Name/i), { target: { value: name } });
|
fireEvent.change(screen.getByLabelText(/Campaign Name/i), { target: { value: name } });
|
||||||
fireEvent.click(screen.getByRole('button', { name: /^Create$/i }));
|
fireEvent.click(screen.getByRole('button', { name: /^Create$/i }));
|
||||||
// wait for setDoc recorded
|
// wait for setDoc recorded with this name (latest match)
|
||||||
const { getCalls } = require('../__mocks__/firebase/_mock-db');
|
const { getCalls } = require('../__mocks__/firebase/_mock-db');
|
||||||
await waitFor(() => getCalls().find(c => c.fn === 'setDoc' && c.path.includes('/campaigns/')));
|
await waitFor(() => getCalls().find(c => c.fn === 'setDoc' && c.path.includes('/campaigns/') && c.data.name === name));
|
||||||
const call = getCalls().find(c => c.fn === 'setDoc' && c.path.includes('/campaigns/'));
|
const call = getCalls().filter(c => c.fn === 'setDoc' && c.path.includes('/campaigns/') && c.data.name === name).pop();
|
||||||
return call.path.split('/').pop(); // campaign id
|
return call.path.split('/').pop(); // campaign id
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,8 +54,8 @@ export async function createEncounterViaUI(name = 'Test Encounter') {
|
|||||||
fireEvent.change(screen.getByLabelText(/Encounter Name/i), { target: { value: name } });
|
fireEvent.change(screen.getByLabelText(/Encounter Name/i), { target: { value: name } });
|
||||||
fireEvent.click(screen.getByRole('button', { name: /^Create$/i }));
|
fireEvent.click(screen.getByRole('button', { name: /^Create$/i }));
|
||||||
const { getCalls } = require('../__mocks__/firebase/_mock-db');
|
const { getCalls } = require('../__mocks__/firebase/_mock-db');
|
||||||
await waitFor(() => getCalls().find(c => c.fn === 'setDoc' && c.path.includes('/encounters/')));
|
await waitFor(() => getCalls().find(c => c.fn === 'setDoc' && c.path.includes('/encounters/') && c.data.name === name));
|
||||||
const call = getCalls().find(c => c.fn === 'setDoc' && c.path.includes('/encounters/'));
|
const call = getCalls().filter(c => c.fn === 'setDoc' && c.path.includes('/encounters/') && c.data.name === name).pop();
|
||||||
return call.path.split('/').pop();
|
return call.path.split('/').pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user