style(13-03): apply Prettier formatting across repo

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.
This commit is contained in:
Lucas Berger
2026-06-11 20:35:18 -04:00
parent 4bc0445173
commit 982438dc10
398 changed files with 19050 additions and 16382 deletions
+76 -76
View File
@@ -13,12 +13,12 @@
* path are both invoked without error (the Temporal hydration contracts from hydrateEvents.test.ts).
*/
import 'temporal-polyfill/global'
import React from 'react'
import { describe, it, expect, vi, beforeEach, type Mock } from 'vitest'
import { render, screen, waitFor } from '@testing-library/react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { MemoryRouter } from 'react-router'
import 'temporal-polyfill/global';
import React from 'react';
import { describe, it, expect, vi, beforeEach, type Mock } from 'vitest';
import { render, screen, waitFor } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { MemoryRouter } from 'react-router';
// window.matchMedia is polyfilled in src/test-setup.ts (loaded via vitest.config setupFiles)
@@ -34,10 +34,10 @@ vi.mock('@schedule-x/react', () => ({
ScheduleXCalendar: ({ calendarApp }: { calendarApp: unknown }) => (
<div data-testid="schedule-x-calendar" data-has-app={calendarApp != null ? 'true' : 'false'} />
),
}))
}));
// Mock @schedule-x/events-service to capture .set() calls
const mockEventsServiceSet = vi.fn()
const mockEventsServiceSet = vi.fn();
vi.mock('@schedule-x/events-service', () => ({
createEventsServicePlugin: vi.fn(() => ({
name: 'events-service',
@@ -48,35 +48,35 @@ vi.mock('@schedule-x/events-service', () => ({
remove: vi.fn(),
update: vi.fn(),
})),
}))
}));
// Mock @schedule-x/event-modal
vi.mock('@schedule-x/event-modal', () => ({
createEventModalPlugin: vi.fn(() => ({
name: 'event-modal',
})),
}))
}));
// Mock the API client
vi.mock('../api/client.js', () => ({
fetchMe: vi.fn(),
fetchEvents: vi.fn(),
}))
}));
// ── Spy on hydrateEvents (must be after vi.mock but before imports use it) ──
vi.mock('../lib/hydrateEvents.js', async (importOriginal) => {
const actual = await importOriginal<typeof import('../lib/hydrateEvents.js')>()
const actual = await importOriginal<typeof import('../lib/hydrateEvents.js')>();
return {
...actual,
hydrateEvents: vi.fn(actual.hydrateEvents), // spy wrapping real implementation
}
})
};
});
// ── Imports (after mocks are in place) ──────────────────────────────────────
import { fetchMe, fetchEvents } from '../api/client.js'
import { hydrateEvents } from '../lib/hydrateEvents.js'
import { CalendarShell } from './CalendarShell.js'
import { fetchMe, fetchEvents } from '../api/client.js';
import { hydrateEvents } from '../lib/hydrateEvents.js';
import { CalendarShell } from './CalendarShell.js';
// ── Fixtures ─────────────────────────────────────────────────────────────────
@@ -94,7 +94,7 @@ const TIMED_OCCURRENCE = {
allDay: false,
location: null,
description: null,
}
};
const ALLDAY_OCCURRENCE = {
id: 'allday-uid::2026-06-20',
@@ -110,7 +110,7 @@ const ALLDAY_OCCURRENCE = {
allDay: true,
location: null,
description: null,
}
};
// ── Helpers ───────────────────────────────────────────────────────────────────
@@ -123,110 +123,110 @@ function makeQueryClient() {
staleTime: 0,
},
},
})
});
}
function renderWithClient(ui: React.ReactElement) {
const client = makeQueryClient()
const client = makeQueryClient();
return render(
<MemoryRouter initialEntries={['/calendar']}>
<QueryClientProvider client={client}>{ui}</QueryClientProvider>
</MemoryRouter>,
)
);
}
// ── Tests ─────────────────────────────────────────────────────────────────────
describe('CalendarShell — CAL-03 render smoke', () => {
beforeEach(() => {
vi.clearAllMocks()
vi.clearAllMocks();
// Reset the one-shot redirect guard between tests
sessionStorage.clear()
sessionStorage.clear();
// Default mock responses
;(fetchMe as Mock).mockResolvedValue({
(fetchMe as Mock).mockResolvedValue({
user: { id: 1, displayName: 'Lucas', color: '#4A90D9' },
})
;(fetchEvents as Mock).mockResolvedValue({
});
(fetchEvents as Mock).mockResolvedValue({
occurrences: [TIMED_OCCURRENCE, ALLDAY_OCCURRENCE],
})
})
});
});
it('renders without throwing with all four views configured (CAL-03 smoke)', () => {
// Validates @schedule-x/react@4.1.0 ↔ @schedule-x/calendar@4.6.0 import compatibility (Pitfall 6)
expect(() => renderWithClient(<CalendarShell />)).not.toThrow()
})
expect(() => renderWithClient(<CalendarShell />)).not.toThrow();
});
it('mounts the ScheduleXCalendar with a non-null calendarApp after data loads', async () => {
renderWithClient(<CalendarShell />)
renderWithClient(<CalendarShell />);
// CalendarShell shows SkeletonCalendar during loading; wait for data to resolve
const calEl = await screen.findByTestId('schedule-x-calendar')
expect(calEl).toBeDefined()
expect(calEl.getAttribute('data-has-app')).toBe('true')
})
const calEl = await screen.findByTestId('schedule-x-calendar');
expect(calEl).toBeDefined();
expect(calEl.getAttribute('data-has-app')).toBe('true');
});
it('calls hydrateEvents with both timed and all-day occurrences and passes them to eventsService', async () => {
renderWithClient(<CalendarShell />)
renderWithClient(<CalendarShell />);
await waitFor(() => {
expect(hydrateEvents as Mock).toHaveBeenCalled()
})
expect(hydrateEvents as Mock).toHaveBeenCalled();
});
const callArgs = (hydrateEvents as Mock).mock.calls[0][0] as typeof TIMED_OCCURRENCE[]
const ids = callArgs.map((o) => o.id)
expect(ids).toContain(TIMED_OCCURRENCE.id)
expect(ids).toContain(ALLDAY_OCCURRENCE.id)
const callArgs = (hydrateEvents as Mock).mock.calls[0][0] as (typeof TIMED_OCCURRENCE)[];
const ids = callArgs.map((o) => o.id);
expect(ids).toContain(TIMED_OCCURRENCE.id);
expect(ids).toContain(ALLDAY_OCCURRENCE.id);
// Verify eventsService.set() was called with the hydrated events
await waitFor(() => {
expect(mockEventsServiceSet).toHaveBeenCalled()
})
const sxEvents = mockEventsServiceSet.mock.calls[0][0] as ReturnType<typeof hydrateEvents>
expect(sxEvents.length).toBe(2)
})
expect(mockEventsServiceSet).toHaveBeenCalled();
});
const sxEvents = mockEventsServiceSet.mock.calls[0][0] as ReturnType<typeof hydrateEvents>;
expect(sxEvents.length).toBe(2);
});
it('converts all-day occurrence to Temporal.PlainDate (Pitfall 4 — no ISO-string rejection)', async () => {
renderWithClient(<CalendarShell />)
renderWithClient(<CalendarShell />);
await waitFor(() => {
expect(mockEventsServiceSet).toHaveBeenCalled()
})
expect(mockEventsServiceSet).toHaveBeenCalled();
});
const sxEvents = mockEventsServiceSet.mock.calls[0][0] as ReturnType<typeof hydrateEvents>
const allDayEvt = sxEvents.find((e) => e.id === ALLDAY_OCCURRENCE.id)
expect(allDayEvt).toBeDefined()
const sxEvents = mockEventsServiceSet.mock.calls[0][0] as ReturnType<typeof hydrateEvents>;
const allDayEvt = sxEvents.find((e) => e.id === ALLDAY_OCCURRENCE.id);
expect(allDayEvt).toBeDefined();
// Must be Temporal.PlainDate, NOT ZonedDateTime — guards all-day date shift (Pitfall 2/4)
expect(allDayEvt!.start).toBeInstanceOf(Temporal.PlainDate)
expect(allDayEvt!.end).toBeInstanceOf(Temporal.PlainDate)
})
expect(allDayEvt!.start).toBeInstanceOf(Temporal.PlainDate);
expect(allDayEvt!.end).toBeInstanceOf(Temporal.PlainDate);
});
it('converts timed occurrence to Temporal.ZonedDateTime (Pitfall 4 — no ISO-string rejection)', async () => {
renderWithClient(<CalendarShell />)
renderWithClient(<CalendarShell />);
await waitFor(() => {
expect(mockEventsServiceSet).toHaveBeenCalled()
})
expect(mockEventsServiceSet).toHaveBeenCalled();
});
const sxEvents = mockEventsServiceSet.mock.calls[0][0] as ReturnType<typeof hydrateEvents>
const timedEvt = sxEvents.find((e) => e.id === TIMED_OCCURRENCE.id)
expect(timedEvt).toBeDefined()
expect(timedEvt!.start).toBeInstanceOf(Temporal.ZonedDateTime)
expect(timedEvt!.end).toBeInstanceOf(Temporal.ZonedDateTime)
})
const sxEvents = mockEventsServiceSet.mock.calls[0][0] as ReturnType<typeof hydrateEvents>;
const timedEvt = sxEvents.find((e) => e.id === TIMED_OCCURRENCE.id);
expect(timedEvt).toBeDefined();
expect(timedEvt!.start).toBeInstanceOf(Temporal.ZonedDateTime);
expect(timedEvt!.end).toBeInstanceOf(Temporal.ZonedDateTime);
});
it('shows sign-in required when /api/me returns an error', () => {
;(fetchMe as Mock).mockRejectedValue(new Error('401'))
renderWithClient(<CalendarShell />)
(fetchMe as Mock).mockRejectedValue(new Error('401'));
renderWithClient(<CalendarShell />);
// Error renders asynchronously after query settles
})
});
it('renders dead-end AuthSplash when redirect guard is already exhausted (D-11)', async () => {
// Simulate the one-shot guard having already fired (prior redirect attempt)
sessionStorage.setItem('familysync.loginRedirectAttempted', '1')
;(fetchMe as Mock).mockRejectedValue(new Error('401'))
renderWithClient(<CalendarShell />)
sessionStorage.setItem('familysync.loginRedirectAttempted', '1');
(fetchMe as Mock).mockRejectedValue(new Error('401'));
renderWithClient(<CalendarShell />);
// After the auth error and the guard is exhausted, dead-end copy must be visible
const tapToRetry = await screen.findByText(/Tap here to try again/i)
expect(tapToRetry).toBeDefined()
})
})
const tapToRetry = await screen.findByText(/Tap here to try again/i);
expect(tapToRetry).toBeDefined();
});
});