test(web): cover extracted shell contracts
Refs #247 Co-Authored-By: OpenAI Codex <codex@openai.com>
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
import { afterEach, describe, expect, it } from 'vitest';
|
||||
import {
|
||||
routeById,
|
||||
routeFromLocation
|
||||
} from './routes';
|
||||
|
||||
describe('app route matching', () => {
|
||||
afterEach(() => {
|
||||
window.history.replaceState(null, '', '/app');
|
||||
});
|
||||
|
||||
it('returns one stable route object across sibling allowSubPaths locations', () => {
|
||||
const playoutsRoute = routeById.get('playouts');
|
||||
|
||||
window.history.replaceState(null, '', '/app/playouts/20/alternate-schedules');
|
||||
const alternateSchedules = routeFromLocation();
|
||||
|
||||
window.history.replaceState(null, '', '/app/playouts/20/templates');
|
||||
const templates = routeFromLocation();
|
||||
|
||||
expect(alternateSchedules).toBe(playoutsRoute);
|
||||
expect(templates).toBe(playoutsRoute);
|
||||
expect(templates).toBe(alternateSchedules);
|
||||
});
|
||||
|
||||
it('ignores the query string while preserving the route object', () => {
|
||||
const searchRoute = routeById.get('search');
|
||||
|
||||
window.history.replaceState(null, '', '/app/search?query=alpha');
|
||||
const firstQuery = routeFromLocation();
|
||||
|
||||
window.history.replaceState(null, '', '/app/search?query=beta');
|
||||
const secondQuery = routeFromLocation();
|
||||
|
||||
expect(firstQuery).toBe(searchRoute);
|
||||
expect(secondQuery).toBe(searchRoute);
|
||||
});
|
||||
|
||||
it('normalizes a trailing slash and rejects an unowned path', () => {
|
||||
window.history.replaceState(null, '', '/app/channels/');
|
||||
expect(routeFromLocation()).toBe(routeById.get('channels'));
|
||||
|
||||
window.history.replaceState(null, '', '/app/channels/42');
|
||||
expect(routeFromLocation()).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,6 @@
|
||||
import { cleanup, fireEvent, render, screen, waitFor, within } from '@testing-library/react';
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { PrimaryActionProvider, usePrimaryActionHandler } from '../primaryAction';
|
||||
import { SchedulesScreen } from './SchedulesScreen';
|
||||
|
||||
function jsonResponse(body: unknown, status = 200): Response {
|
||||
@@ -157,6 +158,11 @@ async function renderReady(options: MockOptions = {}) {
|
||||
return handle;
|
||||
}
|
||||
|
||||
function SchedulesPrimaryActionButton() {
|
||||
const handler = usePrimaryActionHandler('schedules');
|
||||
return <button type="button" disabled={!handler} onClick={handler}>TopBar action</button>;
|
||||
}
|
||||
|
||||
describe('SchedulesScreen — load', () => {
|
||||
it('renders the lineup for the active schedule', async () => {
|
||||
await renderReady();
|
||||
@@ -438,10 +444,19 @@ describe('SchedulesScreen — schedule CRUD', () => {
|
||||
await waitFor(() => expect(handle.requests.some((r) => r.url === '/api/v1/schedules/99/items')).toBe(true));
|
||||
});
|
||||
|
||||
it('responds to the TopBar primary-action event by opening create', async () => {
|
||||
await renderReady();
|
||||
window.dispatchEvent(new CustomEvent('ctv:primary-action', { detail: 'schedules' }));
|
||||
await waitFor(() => expect(screen.getByRole('dialog')).toBeTruthy());
|
||||
it('registers the TopBar primary action to open create', async () => {
|
||||
mockApi();
|
||||
render(
|
||||
<PrimaryActionProvider>
|
||||
<SchedulesPrimaryActionButton />
|
||||
<SchedulesScreen />
|
||||
</PrimaryActionProvider>
|
||||
);
|
||||
await waitFor(() => expect(screen.getByLabelText('Schedule lineup')).toBeTruthy());
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: 'TopBar action' }));
|
||||
|
||||
expect(screen.getByRole('dialog')).toBeTruthy();
|
||||
expect(within(screen.getByRole('dialog')).getByLabelText('Name')).toBeTruthy();
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user