Mechanical reformat — no logic changes. 398 files changed, 19125 insertions(+), 16457 deletions(-). Prettier 3.8.4 with .prettierrc (singleQuote:true, semi:true, tabWidth:2, trailingComma:all, printWidth:100). Isolated per D-13-08 for reviewability.
114 lines
4.6 KiB
TypeScript
114 lines
4.6 KiB
TypeScript
/**
|
|
* AppNav persistence test — verifies AppNav renders on ALL routes (/calendar, /lists).
|
|
*
|
|
* FIX 3: AppNav must be a persistent app-shell element outside <Routes> so it
|
|
* survives route transitions. Before the fix, AppNav was rendered INSIDE
|
|
* CalendarShell, which caused it to unmount when navigating to /lists.
|
|
*
|
|
* These tests render the full App component (same as the user experiences) and
|
|
* verify the "FamilySync" brand text (present in both PhoneNav and DesktopNav)
|
|
* is visible on both the /calendar and /lists routes.
|
|
*/
|
|
|
|
import React from 'react';
|
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
import { render, screen } from '@testing-library/react';
|
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
|
|
// ── Module mocks ────────────────────────────────────────────────────────────
|
|
|
|
vi.mock('@schedule-x/react', () => ({
|
|
useCalendarApp: vi.fn(() => ({})),
|
|
ScheduleXCalendar: () => <div data-testid="schedule-x-calendar" />,
|
|
}));
|
|
|
|
vi.mock('@schedule-x/events-service', () => ({
|
|
createEventsServicePlugin: vi.fn(() => ({
|
|
name: 'events-service',
|
|
set: vi.fn(),
|
|
get: vi.fn(),
|
|
getAll: vi.fn(() => []),
|
|
add: vi.fn(),
|
|
remove: vi.fn(),
|
|
update: vi.fn(),
|
|
})),
|
|
}));
|
|
|
|
vi.mock('@schedule-x/event-modal', () => ({
|
|
createEventModalPlugin: vi.fn(() => ({ name: 'event-modal' })),
|
|
}));
|
|
|
|
vi.mock('../api/client.js', () => ({
|
|
fetchMe: vi.fn(),
|
|
fetchEvents: vi.fn(),
|
|
}));
|
|
|
|
vi.mock('../api/listsClient.js', () => ({
|
|
fetchLists: vi.fn().mockResolvedValue({ lists: [] }),
|
|
deleteList: vi.fn(),
|
|
}));
|
|
|
|
vi.mock('../lib/hydrateEvents.js', async (importOriginal) => {
|
|
const actual = await importOriginal<typeof import('../lib/hydrateEvents.js')>();
|
|
return { ...actual, hydrateEvents: vi.fn(actual.hydrateEvents) };
|
|
});
|
|
|
|
// ── Imports (after mocks) ───────────────────────────────────────────────────
|
|
|
|
import { fetchMe, fetchEvents } from '../api/client.js';
|
|
import type { Mock } from 'vitest';
|
|
|
|
// ── Helpers ─────────────────────────────────────────────────────────────────
|
|
|
|
function makeQueryClient() {
|
|
return new QueryClient({
|
|
defaultOptions: { queries: { retry: false, staleTime: 0 } },
|
|
});
|
|
}
|
|
|
|
// ── Tests ────────────────────────────────────────────────────────────────────
|
|
|
|
describe('AppNav persistence across routes (FIX 3)', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
sessionStorage.clear();
|
|
(fetchMe as Mock).mockResolvedValue({
|
|
user: { id: 1, displayName: 'Lucas', color: '#4A90D9' },
|
|
});
|
|
(fetchEvents as Mock).mockResolvedValue({ occurrences: [] });
|
|
});
|
|
|
|
it('renders AppNav "FamilySync" brand on /lists route (FIX 3: nav persists across routes)', async () => {
|
|
// This test verifies the fix: AppNav is a persistent app-shell element that
|
|
// renders on ALL routes, not only inside CalendarShell (/calendar).
|
|
// It renders the app shell layout (AppNav + Routes) with MemoryRouter at /lists
|
|
// and asserts AppNav's brand text is visible — which would fail if AppNav were
|
|
// only inside CalendarShell (which unmounts on /lists).
|
|
const { AppNav } = await import('./AppNav.js');
|
|
const { Routes, Route, MemoryRouter } = await import('react-router');
|
|
const client = makeQueryClient();
|
|
|
|
// Simulate the app-shell layout: AppNav is OUTSIDE Routes (persistent), and
|
|
// Routes renders the /lists page. The 'FamilySync' brand in AppNav must be visible
|
|
// on the /lists route — not just on /calendar.
|
|
render(
|
|
<QueryClientProvider client={client}>
|
|
<MemoryRouter initialEntries={['/lists']}>
|
|
{/* AppNav at app-shell level — sibling of Routes, not inside any Route */}
|
|
<AppNav members={[]} currentUserColor="#4A90D9" currentUserName="Lucas" />
|
|
<Routes>
|
|
<Route path="/lists" element={<div>Lists page</div>} />
|
|
</Routes>
|
|
</MemoryRouter>
|
|
</QueryClientProvider>,
|
|
);
|
|
|
|
// FamilySync brand text from AppNav must be present while the /lists route renders
|
|
const brand = screen.queryAllByText('FamilySync');
|
|
expect(brand.length).toBeGreaterThan(0);
|
|
|
|
// The Lists page route content also renders
|
|
expect(screen.getByText('Lists page')).toBeDefined();
|
|
});
|
|
});
|