Add tests and edit works
This commit is contained in:
parent
bfbba7b5ab
commit
bc4b0e2df7
12 changed files with 2415 additions and 20 deletions
200
tests/components/SaveOptionsModal.test.tsx
Normal file
200
tests/components/SaveOptionsModal.test.tsx
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
import React from 'react';
|
||||
import { render, screen, fireEvent } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { SaveOptionsModal } from '../../components/SaveOptionsModal';
|
||||
|
||||
describe('SaveOptionsModal', () => {
|
||||
const defaultProps = {
|
||||
isOpen: true,
|
||||
onClose: vi.fn(),
|
||||
onSaveNew: vi.fn(),
|
||||
onOverwrite: vi.fn(),
|
||||
isSaving: false,
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('rendering', () => {
|
||||
it('renders nothing when isOpen is false', () => {
|
||||
render(<SaveOptionsModal {...defaultProps} isOpen={false} />);
|
||||
expect(screen.queryByText('Save Quiz')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders modal when isOpen is true', () => {
|
||||
render(<SaveOptionsModal {...defaultProps} />);
|
||||
expect(screen.getByText('Save Quiz')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('displays explanation text', () => {
|
||||
render(<SaveOptionsModal {...defaultProps} />);
|
||||
expect(screen.getByText(/loaded from your library/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows update existing button', () => {
|
||||
render(<SaveOptionsModal {...defaultProps} />);
|
||||
expect(screen.getByText('Update existing quiz')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows save as new button', () => {
|
||||
render(<SaveOptionsModal {...defaultProps} />);
|
||||
expect(screen.getByText('Save as new quiz')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows close button', () => {
|
||||
render(<SaveOptionsModal {...defaultProps} />);
|
||||
const closeButtons = screen.getAllByRole('button');
|
||||
expect(closeButtons.length).toBeGreaterThanOrEqual(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('interactions - happy path', () => {
|
||||
it('calls onOverwrite when update existing is clicked', async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<SaveOptionsModal {...defaultProps} />);
|
||||
|
||||
await user.click(screen.getByText('Update existing quiz'));
|
||||
|
||||
expect(defaultProps.onOverwrite).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('calls onSaveNew when save as new is clicked', async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<SaveOptionsModal {...defaultProps} />);
|
||||
|
||||
await user.click(screen.getByText('Save as new quiz'));
|
||||
|
||||
expect(defaultProps.onSaveNew).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('calls onClose when close button (X) is clicked', async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<SaveOptionsModal {...defaultProps} />);
|
||||
|
||||
const closeButton = screen.getByRole('button', { name: '' });
|
||||
await user.click(closeButton);
|
||||
|
||||
expect(defaultProps.onClose).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('calls onClose when backdrop is clicked', async () => {
|
||||
render(<SaveOptionsModal {...defaultProps} />);
|
||||
|
||||
const backdrop = document.querySelector('.fixed.inset-0');
|
||||
expect(backdrop).toBeInTheDocument();
|
||||
fireEvent.click(backdrop!);
|
||||
|
||||
expect(defaultProps.onClose).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not close when modal content is clicked', async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<SaveOptionsModal {...defaultProps} />);
|
||||
|
||||
await user.click(screen.getByText('Save Quiz'));
|
||||
|
||||
expect(defaultProps.onClose).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('interactions - unhappy path / edge cases', () => {
|
||||
it('disables buttons when isSaving is true', () => {
|
||||
render(<SaveOptionsModal {...defaultProps} isSaving={true} />);
|
||||
|
||||
const updateButton = screen.getByText('Update existing quiz').closest('button');
|
||||
const saveNewButton = screen.getByText('Save as new quiz').closest('button');
|
||||
|
||||
expect(updateButton).toBeDisabled();
|
||||
expect(saveNewButton).toBeDisabled();
|
||||
});
|
||||
|
||||
it('does not call onOverwrite when disabled and clicked', async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<SaveOptionsModal {...defaultProps} isSaving={true} />);
|
||||
|
||||
const button = screen.getByText('Update existing quiz').closest('button')!;
|
||||
await user.click(button);
|
||||
|
||||
expect(defaultProps.onOverwrite).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not call onSaveNew when disabled and clicked', async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<SaveOptionsModal {...defaultProps} isSaving={true} />);
|
||||
|
||||
const button = screen.getByText('Save as new quiz').closest('button')!;
|
||||
await user.click(button);
|
||||
|
||||
expect(defaultProps.onSaveNew).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('handles rapid clicks gracefully', async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<SaveOptionsModal {...defaultProps} />);
|
||||
|
||||
const button = screen.getByText('Update existing quiz');
|
||||
await user.click(button);
|
||||
await user.click(button);
|
||||
await user.click(button);
|
||||
|
||||
expect(defaultProps.onOverwrite).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
|
||||
it('remains functional after re-opening', async () => {
|
||||
const user = userEvent.setup();
|
||||
const { rerender } = render(<SaveOptionsModal {...defaultProps} />);
|
||||
|
||||
await user.click(screen.getByText('Update existing quiz'));
|
||||
expect(defaultProps.onOverwrite).toHaveBeenCalledTimes(1);
|
||||
|
||||
rerender(<SaveOptionsModal {...defaultProps} isOpen={false} />);
|
||||
expect(screen.queryByText('Save Quiz')).not.toBeInTheDocument();
|
||||
|
||||
rerender(<SaveOptionsModal {...defaultProps} isOpen={true} />);
|
||||
await user.click(screen.getByText('Save as new quiz'));
|
||||
expect(defaultProps.onSaveNew).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('accessibility', () => {
|
||||
it('all interactive elements are focusable', () => {
|
||||
render(<SaveOptionsModal {...defaultProps} />);
|
||||
|
||||
const buttons = screen.getAllByRole('button');
|
||||
buttons.forEach(button => {
|
||||
expect(button).not.toHaveAttribute('tabindex', '-1');
|
||||
});
|
||||
});
|
||||
|
||||
it('buttons have descriptive text for screen readers', () => {
|
||||
render(<SaveOptionsModal {...defaultProps} />);
|
||||
|
||||
expect(screen.getByText('Overwrite the original with your changes')).toBeInTheDocument();
|
||||
expect(screen.getByText('Keep the original and create a copy')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('state transitions', () => {
|
||||
it('transitions from not saving to saving correctly', async () => {
|
||||
const { rerender } = render(<SaveOptionsModal {...defaultProps} isSaving={false} />);
|
||||
|
||||
const updateButton = screen.getByText('Update existing quiz').closest('button');
|
||||
expect(updateButton).not.toBeDisabled();
|
||||
|
||||
rerender(<SaveOptionsModal {...defaultProps} isSaving={true} />);
|
||||
expect(updateButton).toBeDisabled();
|
||||
});
|
||||
|
||||
it('transitions from saving back to not saving', async () => {
|
||||
const { rerender } = render(<SaveOptionsModal {...defaultProps} isSaving={true} />);
|
||||
|
||||
const updateButton = screen.getByText('Update existing quiz').closest('button');
|
||||
expect(updateButton).toBeDisabled();
|
||||
|
||||
rerender(<SaveOptionsModal {...defaultProps} isSaving={false} />);
|
||||
expect(updateButton).not.toBeDisabled();
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue