Fix lint error and update tests for WCAG role changes
- Replace el! non-null assertion with optional chaining in useFocusTrap - Update AutodraftSettings tests to query role="radio" instead of role="button" (buttons have an explicit radio role since the WCAG pass) - Update AvailableParticipantsSection watchlist tests to use getByRole/getAllByRole instead of getByTitle/getAllByTitle (watchlist buttons now use aria-label instead of title) https://claude.ai/code/session_01JXajpFxhqLf8aPCncP81k3
This commit is contained in:
parent
e25cba09ac
commit
cbacfa34b3
3 changed files with 38 additions and 38 deletions
|
|
@ -33,10 +33,10 @@ describe('AutodraftSettings Component', () => {
|
||||||
it('renders all four option buttons', () => {
|
it('renders all four option buttons', () => {
|
||||||
render(<AutodraftSettings {...defaultProps} />);
|
render(<AutodraftSettings {...defaultProps} />);
|
||||||
|
|
||||||
expect(screen.getByRole('button', { name: 'Off' })).toBeInTheDocument();
|
expect(screen.getByRole('radio', { name: 'Off' })).toBeInTheDocument();
|
||||||
expect(screen.getByRole('button', { name: 'Next in Queue' })).toBeInTheDocument();
|
expect(screen.getByRole('radio', { name: 'Next in Queue' })).toBeInTheDocument();
|
||||||
expect(screen.getByRole('button', { name: 'All in Queue' })).toBeInTheDocument();
|
expect(screen.getByRole('radio', { name: 'All in Queue' })).toBeInTheDocument();
|
||||||
expect(screen.getByRole('button', { name: 'All Picks' })).toBeInTheDocument();
|
expect(screen.getByRole('radio', { name: 'All Picks' })).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not render a queue-only toggle switch', () => {
|
it('does not render a queue-only toggle switch', () => {
|
||||||
|
|
@ -51,24 +51,24 @@ describe('AutodraftSettings Component', () => {
|
||||||
|
|
||||||
it('marks Off as active (muted border) when isEnabled=false', () => {
|
it('marks Off as active (muted border) when isEnabled=false', () => {
|
||||||
render(<AutodraftSettings {...defaultProps} isEnabled={false} />);
|
render(<AutodraftSettings {...defaultProps} isEnabled={false} />);
|
||||||
expect(screen.getByRole('button', { name: 'Off' }).className).toContain(
|
expect(screen.getByRole('radio', { name: 'Off' }).className).toContain(
|
||||||
'border-l-muted-foreground'
|
'border-l-muted-foreground'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('marks Next in Queue as active when isEnabled=true, mode=next_pick', () => {
|
it('marks Next in Queue as active when isEnabled=true, mode=next_pick', () => {
|
||||||
render(<AutodraftSettings {...defaultProps} isEnabled={true} mode="next_pick" queueOnly={true} />);
|
render(<AutodraftSettings {...defaultProps} isEnabled={true} mode="next_pick" queueOnly={true} />);
|
||||||
expect(screen.getByRole('button', { name: 'Next in Queue' }).className).toContain('bg-electric');
|
expect(screen.getByRole('radio', { name: 'Next in Queue' }).className).toContain('bg-electric');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('marks All in Queue as active when isEnabled=true, mode=while_on, queueOnly=true', () => {
|
it('marks All in Queue as active when isEnabled=true, mode=while_on, queueOnly=true', () => {
|
||||||
render(<AutodraftSettings {...defaultProps} isEnabled={true} mode="while_on" queueOnly={true} />);
|
render(<AutodraftSettings {...defaultProps} isEnabled={true} mode="while_on" queueOnly={true} />);
|
||||||
expect(screen.getByRole('button', { name: 'All in Queue' }).className).toContain('bg-electric');
|
expect(screen.getByRole('radio', { name: 'All in Queue' }).className).toContain('bg-electric');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('marks All Picks as active when isEnabled=true, mode=while_on, queueOnly=false', () => {
|
it('marks All Picks as active when isEnabled=true, mode=while_on, queueOnly=false', () => {
|
||||||
render(<AutodraftSettings {...defaultProps} isEnabled={true} mode="while_on" queueOnly={false} />);
|
render(<AutodraftSettings {...defaultProps} isEnabled={true} mode="while_on" queueOnly={false} />);
|
||||||
expect(screen.getByRole('button', { name: 'All Picks' }).className).toContain('bg-electric');
|
expect(screen.getByRole('radio', { name: 'All Picks' }).className).toContain('bg-electric');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -79,7 +79,7 @@ describe('AutodraftSettings Component', () => {
|
||||||
const onUpdate = vi.fn();
|
const onUpdate = vi.fn();
|
||||||
render(<AutodraftSettings {...defaultProps} onUpdate={onUpdate} />);
|
render(<AutodraftSettings {...defaultProps} onUpdate={onUpdate} />);
|
||||||
|
|
||||||
fireEvent.click(screen.getByRole('button', { name: 'Next in Queue' }));
|
fireEvent.click(screen.getByRole('radio', { name: 'Next in Queue' }));
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(global.fetch).toHaveBeenCalledWith(
|
expect(global.fetch).toHaveBeenCalledWith(
|
||||||
|
|
@ -94,7 +94,7 @@ describe('AutodraftSettings Component', () => {
|
||||||
const onUpdate = vi.fn();
|
const onUpdate = vi.fn();
|
||||||
render(<AutodraftSettings {...defaultProps} onUpdate={onUpdate} />);
|
render(<AutodraftSettings {...defaultProps} onUpdate={onUpdate} />);
|
||||||
|
|
||||||
fireEvent.click(screen.getByRole('button', { name: 'All in Queue' }));
|
fireEvent.click(screen.getByRole('radio', { name: 'All in Queue' }));
|
||||||
|
|
||||||
await waitFor(() => expect(onUpdate).toHaveBeenCalledWith(true, 'while_on', true));
|
await waitFor(() => expect(onUpdate).toHaveBeenCalledWith(true, 'while_on', true));
|
||||||
});
|
});
|
||||||
|
|
@ -103,7 +103,7 @@ describe('AutodraftSettings Component', () => {
|
||||||
const onUpdate = vi.fn();
|
const onUpdate = vi.fn();
|
||||||
render(<AutodraftSettings {...defaultProps} onUpdate={onUpdate} />);
|
render(<AutodraftSettings {...defaultProps} onUpdate={onUpdate} />);
|
||||||
|
|
||||||
fireEvent.click(screen.getByRole('button', { name: 'All Picks' }));
|
fireEvent.click(screen.getByRole('radio', { name: 'All Picks' }));
|
||||||
|
|
||||||
await waitFor(() => expect(onUpdate).toHaveBeenCalledWith(true, 'while_on', false));
|
await waitFor(() => expect(onUpdate).toHaveBeenCalledWith(true, 'while_on', false));
|
||||||
});
|
});
|
||||||
|
|
@ -114,7 +114,7 @@ describe('AutodraftSettings Component', () => {
|
||||||
<AutodraftSettings {...defaultProps} isEnabled={true} mode="next_pick" queueOnly={true} onUpdate={onUpdate} />
|
<AutodraftSettings {...defaultProps} isEnabled={true} mode="next_pick" queueOnly={true} onUpdate={onUpdate} />
|
||||||
);
|
);
|
||||||
|
|
||||||
fireEvent.click(screen.getByRole('button', { name: 'Off' }));
|
fireEvent.click(screen.getByRole('radio', { name: 'Off' }));
|
||||||
|
|
||||||
await waitFor(() => expect(onUpdate).toHaveBeenCalledWith(false, 'next_pick', false));
|
await waitFor(() => expect(onUpdate).toHaveBeenCalledWith(false, 'next_pick', false));
|
||||||
});
|
});
|
||||||
|
|
@ -122,21 +122,21 @@ describe('AutodraftSettings Component', () => {
|
||||||
it('disables all buttons when isMyTurn=true', () => {
|
it('disables all buttons when isMyTurn=true', () => {
|
||||||
render(<AutodraftSettings {...defaultProps} isMyTurn={true} />);
|
render(<AutodraftSettings {...defaultProps} isMyTurn={true} />);
|
||||||
|
|
||||||
expect(screen.getByRole('button', { name: 'Off' })).toBeDisabled();
|
expect(screen.getByRole('radio', { name: 'Off' })).toBeDisabled();
|
||||||
expect(screen.getByRole('button', { name: 'Next in Queue' })).toBeDisabled();
|
expect(screen.getByRole('radio', { name: 'Next in Queue' })).toBeDisabled();
|
||||||
expect(screen.getByRole('button', { name: 'All in Queue' })).toBeDisabled();
|
expect(screen.getByRole('radio', { name: 'All in Queue' })).toBeDisabled();
|
||||||
expect(screen.getByRole('button', { name: 'All Picks' })).toBeDisabled();
|
expect(screen.getByRole('radio', { name: 'All Picks' })).toBeDisabled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not call fetch when a button is clicked during the user\'s turn', () => {
|
it('does not call fetch when a button is clicked during the user\'s turn', () => {
|
||||||
render(<AutodraftSettings {...defaultProps} isMyTurn={true} />);
|
render(<AutodraftSettings {...defaultProps} isMyTurn={true} />);
|
||||||
fireEvent.click(screen.getByRole('button', { name: 'Next in Queue' }));
|
fireEvent.click(screen.getByRole('radio', { name: 'Next in Queue' }));
|
||||||
expect(global.fetch).not.toHaveBeenCalled();
|
expect(global.fetch).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not re-fire when the already-active option is clicked', async () => {
|
it('does not re-fire when the already-active option is clicked', async () => {
|
||||||
render(<AutodraftSettings {...defaultProps} isEnabled={false} />);
|
render(<AutodraftSettings {...defaultProps} isEnabled={false} />);
|
||||||
fireEvent.click(screen.getByRole('button', { name: 'Off' }));
|
fireEvent.click(screen.getByRole('radio', { name: 'Off' }));
|
||||||
expect(global.fetch).not.toHaveBeenCalled();
|
expect(global.fetch).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -149,10 +149,10 @@ describe('AutodraftSettings Component', () => {
|
||||||
(global.fetch as any).mockReturnValueOnce(new Promise((r) => { resolve = r; }));
|
(global.fetch as any).mockReturnValueOnce(new Promise((r) => { resolve = r; }));
|
||||||
|
|
||||||
render(<AutodraftSettings {...defaultProps} />);
|
render(<AutodraftSettings {...defaultProps} />);
|
||||||
fireEvent.click(screen.getByRole('button', { name: 'Next in Queue' }));
|
fireEvent.click(screen.getByRole('radio', { name: 'Next in Queue' }));
|
||||||
|
|
||||||
// Buttons stay enabled — optimistic UI does not block interaction
|
// Buttons stay enabled — optimistic UI does not block interaction
|
||||||
expect(screen.getByRole('button', { name: 'Off' })).not.toBeDisabled();
|
expect(screen.getByRole('radio', { name: 'Off' })).not.toBeDisabled();
|
||||||
|
|
||||||
resolve?.({ ok: true, json: async () => ({ success: true }) });
|
resolve?.({ ok: true, json: async () => ({ success: true }) });
|
||||||
});
|
});
|
||||||
|
|
@ -160,9 +160,9 @@ describe('AutodraftSettings Component', () => {
|
||||||
it('fires a fetch for every rapid click, aborting previous in-flight requests', async () => {
|
it('fires a fetch for every rapid click, aborting previous in-flight requests', async () => {
|
||||||
render(<AutodraftSettings {...defaultProps} />);
|
render(<AutodraftSettings {...defaultProps} />);
|
||||||
|
|
||||||
fireEvent.click(screen.getByRole('button', { name: 'Next in Queue' }));
|
fireEvent.click(screen.getByRole('radio', { name: 'Next in Queue' }));
|
||||||
fireEvent.click(screen.getByRole('button', { name: 'All in Queue' }));
|
fireEvent.click(screen.getByRole('radio', { name: 'All in Queue' }));
|
||||||
fireEvent.click(screen.getByRole('button', { name: 'Off' }));
|
fireEvent.click(screen.getByRole('radio', { name: 'Off' }));
|
||||||
|
|
||||||
await waitFor(() => expect(global.fetch).toHaveBeenCalledTimes(3));
|
await waitFor(() => expect(global.fetch).toHaveBeenCalledTimes(3));
|
||||||
});
|
});
|
||||||
|
|
@ -175,10 +175,10 @@ describe('AutodraftSettings Component', () => {
|
||||||
(global.fetch as any).mockResolvedValueOnce({ ok: false });
|
(global.fetch as any).mockResolvedValueOnce({ ok: false });
|
||||||
|
|
||||||
render(<AutodraftSettings {...defaultProps} isEnabled={false} />);
|
render(<AutodraftSettings {...defaultProps} isEnabled={false} />);
|
||||||
fireEvent.click(screen.getByRole('button', { name: 'Next in Queue' }));
|
fireEvent.click(screen.getByRole('radio', { name: 'Next in Queue' }));
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(screen.getByRole('button', { name: 'Off' }).className).toContain(
|
expect(screen.getByRole('radio', { name: 'Off' }).className).toContain(
|
||||||
'border-l-muted-foreground'
|
'border-l-muted-foreground'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
@ -188,10 +188,10 @@ describe('AutodraftSettings Component', () => {
|
||||||
(global.fetch as any).mockRejectedValueOnce(new Error('Network error'));
|
(global.fetch as any).mockRejectedValueOnce(new Error('Network error'));
|
||||||
|
|
||||||
render(<AutodraftSettings {...defaultProps} isEnabled={false} />);
|
render(<AutodraftSettings {...defaultProps} isEnabled={false} />);
|
||||||
fireEvent.click(screen.getByRole('button', { name: 'Next in Queue' }));
|
fireEvent.click(screen.getByRole('radio', { name: 'Next in Queue' }));
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(screen.getByRole('button', { name: 'Off' }).className).toContain(
|
expect(screen.getByRole('radio', { name: 'Off' }).className).toContain(
|
||||||
'border-l-muted-foreground'
|
'border-l-muted-foreground'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
@ -203,7 +203,7 @@ describe('AutodraftSettings Component', () => {
|
||||||
describe('API payload', () => {
|
describe('API payload', () => {
|
||||||
it('posts correct formData for Next in Queue (next_pick, queueOnly=true)', async () => {
|
it('posts correct formData for Next in Queue (next_pick, queueOnly=true)', async () => {
|
||||||
render(<AutodraftSettings {...defaultProps} />);
|
render(<AutodraftSettings {...defaultProps} />);
|
||||||
fireEvent.click(screen.getByRole('button', { name: 'Next in Queue' }));
|
fireEvent.click(screen.getByRole('radio', { name: 'Next in Queue' }));
|
||||||
|
|
||||||
await waitFor(() => expect(global.fetch).toHaveBeenCalled());
|
await waitFor(() => expect(global.fetch).toHaveBeenCalled());
|
||||||
|
|
||||||
|
|
@ -217,7 +217,7 @@ describe('AutodraftSettings Component', () => {
|
||||||
|
|
||||||
it('posts correct formData for All in Queue (while_on, queueOnly=true)', async () => {
|
it('posts correct formData for All in Queue (while_on, queueOnly=true)', async () => {
|
||||||
render(<AutodraftSettings {...defaultProps} />);
|
render(<AutodraftSettings {...defaultProps} />);
|
||||||
fireEvent.click(screen.getByRole('button', { name: 'All in Queue' }));
|
fireEvent.click(screen.getByRole('radio', { name: 'All in Queue' }));
|
||||||
|
|
||||||
await waitFor(() => expect(global.fetch).toHaveBeenCalled());
|
await waitFor(() => expect(global.fetch).toHaveBeenCalled());
|
||||||
|
|
||||||
|
|
@ -229,7 +229,7 @@ describe('AutodraftSettings Component', () => {
|
||||||
|
|
||||||
it('posts correct formData for All Picks (while_on, queueOnly=false)', async () => {
|
it('posts correct formData for All Picks (while_on, queueOnly=false)', async () => {
|
||||||
render(<AutodraftSettings {...defaultProps} />);
|
render(<AutodraftSettings {...defaultProps} />);
|
||||||
fireEvent.click(screen.getByRole('button', { name: 'All Picks' }));
|
fireEvent.click(screen.getByRole('radio', { name: 'All Picks' }));
|
||||||
|
|
||||||
await waitFor(() => expect(global.fetch).toHaveBeenCalled());
|
await waitFor(() => expect(global.fetch).toHaveBeenCalled());
|
||||||
|
|
||||||
|
|
@ -241,7 +241,7 @@ describe('AutodraftSettings Component', () => {
|
||||||
|
|
||||||
it('posts isEnabled=false for Off', async () => {
|
it('posts isEnabled=false for Off', async () => {
|
||||||
render(<AutodraftSettings {...defaultProps} isEnabled={true} mode="next_pick" queueOnly={true} />);
|
render(<AutodraftSettings {...defaultProps} isEnabled={true} mode="next_pick" queueOnly={true} />);
|
||||||
fireEvent.click(screen.getByRole('button', { name: 'Off' }));
|
fireEvent.click(screen.getByRole('radio', { name: 'Off' }));
|
||||||
|
|
||||||
await waitFor(() => expect(global.fetch).toHaveBeenCalled());
|
await waitFor(() => expect(global.fetch).toHaveBeenCalled());
|
||||||
|
|
||||||
|
|
@ -251,7 +251,7 @@ describe('AutodraftSettings Component', () => {
|
||||||
|
|
||||||
it('passes an AbortSignal to fetch', async () => {
|
it('passes an AbortSignal to fetch', async () => {
|
||||||
render(<AutodraftSettings {...defaultProps} />);
|
render(<AutodraftSettings {...defaultProps} />);
|
||||||
fireEvent.click(screen.getByRole('button', { name: 'Next in Queue' }));
|
fireEvent.click(screen.getByRole('radio', { name: 'Next in Queue' }));
|
||||||
|
|
||||||
await waitFor(() => expect(global.fetch).toHaveBeenCalled());
|
await waitFor(() => expect(global.fetch).toHaveBeenCalled());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -403,8 +403,8 @@ describe("AvailableParticipantsSection", () => {
|
||||||
render(
|
render(
|
||||||
<AvailableParticipantsSection {...defaultProps} hasTeam={false} />
|
<AvailableParticipantsSection {...defaultProps} hasTeam={false} />
|
||||||
);
|
);
|
||||||
expect(screen.queryByTitle("Add to watchlist")).not.toBeInTheDocument();
|
expect(screen.queryByRole('button', { name: "Add to watchlist" })).not.toBeInTheDocument();
|
||||||
expect(screen.queryByTitle("Remove from watchlist")).not.toBeInTheDocument();
|
expect(screen.queryByRole('button', { name: "Remove from watchlist" })).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders EyeOff icon for unwatched participants when hasTeam is true", () => {
|
it("renders EyeOff icon for unwatched participants when hasTeam is true", () => {
|
||||||
|
|
@ -415,7 +415,7 @@ describe("AvailableParticipantsSection", () => {
|
||||||
watchedParticipantIds={new Set<string>()}
|
watchedParticipantIds={new Set<string>()}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
const buttons = screen.getAllByTitle("Add to watchlist");
|
const buttons = screen.getAllByRole('button', { name: "Add to watchlist" });
|
||||||
expect(buttons.length).toBeGreaterThan(0);
|
expect(buttons.length).toBeGreaterThan(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -427,7 +427,7 @@ describe("AvailableParticipantsSection", () => {
|
||||||
watchedParticipantIds={new Set(["1"])}
|
watchedParticipantIds={new Set(["1"])}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
const buttons = screen.getAllByTitle("Remove from watchlist");
|
const buttons = screen.getAllByRole('button', { name: "Remove from watchlist" });
|
||||||
expect(buttons.length).toBeGreaterThan(0);
|
expect(buttons.length).toBeGreaterThan(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -441,7 +441,7 @@ describe("AvailableParticipantsSection", () => {
|
||||||
watchedParticipantIds={new Set<string>()}
|
watchedParticipantIds={new Set<string>()}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
const buttons = screen.getAllByTitle("Add to watchlist");
|
const buttons = screen.getAllByRole('button', { name: "Add to watchlist" });
|
||||||
await user.click(buttons[0]);
|
await user.click(buttons[0]);
|
||||||
expect(onToggleWatchlist).toHaveBeenCalledWith("1");
|
expect(onToggleWatchlist).toHaveBeenCalledWith("1");
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ export function useFocusTrap(ref: RefObject<HTMLElement | null>, active: boolean
|
||||||
|
|
||||||
function onKeyDown(e: KeyboardEvent) {
|
function onKeyDown(e: KeyboardEvent) {
|
||||||
if (e.key !== "Tab") return;
|
if (e.key !== "Tab") return;
|
||||||
const focusables = Array.from(el!.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR));
|
const focusables = Array.from(el?.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR) ?? []);
|
||||||
if (focusables.length === 0) return;
|
if (focusables.length === 0) return;
|
||||||
const firstEl = focusables[0];
|
const firstEl = focusables[0];
|
||||||
const lastEl = focusables[focusables.length - 1];
|
const lastEl = focusables[focusables.length - 1];
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue