Simplify AccordionControl tests

This commit is contained in:
2025-07-01 20:40:04 +02:00
parent 1a7ddc1a97
commit 15486c584a

View File

@@ -1,10 +1,5 @@
import { describe, it, expect, vi } from 'vitest';
import {
render as rtlRender,
screen,
fireEvent,
waitFor,
} from '@testing-library/react';
import { render as rtlRender, screen, fireEvent } from '@testing-library/react';
import React from 'react';
import { MantineProvider, Accordion } from '@mantine/core';
import AccordionControl from './AccordionControl';
@@ -23,26 +18,15 @@ const render = (ui: React.ReactElement) => {
const AccordionWrapper: React.FC<{
children: React.ReactNode;
defaultValue?: string[];
multiple?: boolean;
}> = ({ children, defaultValue = ['test'], multiple = true }) => (
<Accordion defaultValue={defaultValue} multiple={multiple}>
}> = ({ children, defaultValue = ['test'] }) => (
<Accordion defaultValue={defaultValue} multiple>
<Accordion.Item value="test">{children}</Accordion.Item>
</Accordion>
);
describe('AccordionControl', () => {
describe('Component Rendering', () => {
it('renders children correctly', () => {
render(
<AccordionWrapper>
<AccordionControl>Test Content</AccordionControl>
</AccordionWrapper>
);
expect(screen.getByText('Test Content')).toBeInTheDocument();
});
it('renders as a Title with order 4', () => {
describe('Normal Operation', () => {
it('renders children as Title with order 4', () => {
render(
<AccordionWrapper>
<AccordionControl>Settings Title</AccordionControl>
@@ -50,11 +34,10 @@ describe('AccordionControl', () => {
);
const title = screen.getByRole('heading', { level: 4 });
expect(title).toBeInTheDocument();
expect(title).toHaveTextContent('Settings Title');
});
it('renders with complex children structure', () => {
it('renders complex children correctly', () => {
render(
<AccordionWrapper>
<AccordionControl>
@@ -65,9 +48,24 @@ describe('AccordionControl', () => {
expect(screen.getByTestId('complex-child')).toBeInTheDocument();
expect(screen.getByText('Complex')).toBeInTheDocument();
expect(screen.getByText('Content')).toBeInTheDocument();
});
it('functions as accordion control', () => {
render(
<AccordionWrapper defaultValue={[]}>
<AccordionControl>Toggle Section</AccordionControl>
<Accordion.Panel>Hidden Content</Accordion.Panel>
</AccordionWrapper>
);
const control = screen.getByRole('button');
fireEvent.click(control);
expect(screen.getByText('Hidden Content')).toBeInTheDocument();
});
});
describe('Edge Cases', () => {
it('handles empty children gracefully', () => {
render(
<AccordionWrapper>
@@ -77,241 +75,9 @@ describe('AccordionControl', () => {
const title = screen.getByRole('heading', { level: 4 });
expect(title).toBeInTheDocument();
expect(title).toBeEmptyDOMElement();
});
it('renders multiple text nodes correctly', () => {
render(
<AccordionWrapper>
<AccordionControl>First Text Second Text</AccordionControl>
</AccordionWrapper>
);
const title = screen.getByRole('heading', { level: 4 });
expect(title).toHaveTextContent('First Text Second Text');
});
it('preserves React component structure in children', () => {
render(
<AccordionWrapper>
<AccordionControl>
<div data-testid="nested-div">Nested Content</div>
</AccordionControl>
</AccordionWrapper>
);
expect(screen.getByTestId('nested-div')).toBeInTheDocument();
expect(screen.getByText('Nested Content')).toBeInTheDocument();
});
it('renders with mixed string and element children', () => {
render(
<AccordionWrapper>
<AccordionControl>
Text before <strong>bold text</strong> and after
</AccordionControl>
</AccordionWrapper>
);
const title = screen.getByRole('heading', { level: 4 });
expect(title).toHaveTextContent('Text before bold text and after');
expect(title.querySelector('strong')).toHaveTextContent('bold text');
});
});
describe('Content Variations', () => {
it.each([
['Simple text', 'Simple text'],
['Text with numbers 123', 'Text with numbers 123'],
['Special chars !@#$%', 'Special chars !@#$%'],
['Unicode characters 测试', 'Unicode characters 测试'],
['Very long text '.repeat(10), 'Very long text '.repeat(10)],
])('renders various content types: %s', (content, expected) => {
render(
<AccordionWrapper>
<AccordionControl>{content}</AccordionControl>
</AccordionWrapper>
);
const title = screen.getByRole('heading', { level: 4 });
expect(title).toHaveTextContent(expected.trim());
});
it('renders with nested React elements', () => {
render(
<AccordionWrapper>
<AccordionControl>
<div>
<span>Nested</span>
<em>Elements</em>
</div>
</AccordionControl>
</AccordionWrapper>
);
expect(screen.getByText('Nested')).toBeInTheDocument();
expect(screen.getByText('Elements')).toBeInTheDocument();
});
});
describe('Accordion Integration', () => {
it('functions as accordion control within accordion context', () => {
render(
<AccordionWrapper defaultValue={[]}>
<AccordionControl>Collapsible Section</AccordionControl>
<Accordion.Panel>Panel Content</Accordion.Panel>
</AccordionWrapper>
);
const control = screen.getByRole('button');
expect(control).toBeInTheDocument();
expect(control).toHaveTextContent('Collapsible Section');
});
it('supports accordion expansion and collapse', async () => {
render(
<AccordionWrapper defaultValue={[]}>
<AccordionControl>Toggle Section</AccordionControl>
<Accordion.Panel>Hidden Content</Accordion.Panel>
</AccordionWrapper>
);
const control = screen.getByRole('button');
// Click to expand
fireEvent.click(control);
// Wait for content to become visible
await waitFor(() => {
expect(screen.getByText('Hidden Content')).toBeInTheDocument();
});
// Click to collapse
fireEvent.click(control);
// For collapse, let's just verify the control is clickable rather than testing visibility
// since Mantine's accordion behavior can vary
expect(control).toBeInTheDocument();
});
it('works with multiple accordion items', () => {
render(
<Accordion multiple>
<Accordion.Item value="first">
<AccordionControl>First Section</AccordionControl>
<Accordion.Panel>First Content</Accordion.Panel>
</Accordion.Item>
<Accordion.Item value="second">
<AccordionControl>Second Section</AccordionControl>
<Accordion.Panel>Second Content</Accordion.Panel>
</Accordion.Item>
</Accordion>
);
expect(screen.getByText('First Section')).toBeInTheDocument();
expect(screen.getByText('Second Section')).toBeInTheDocument();
});
});
describe('Accessibility', () => {
it('provides proper semantic structure', () => {
render(
<AccordionWrapper>
<AccordionControl>Accessible Title</AccordionControl>
</AccordionWrapper>
);
const title = screen.getByRole('heading', { level: 4 });
expect(title).toHaveTextContent('Accessible Title');
// Should be within a button (accordion control)
const button = screen.getByRole('button');
expect(button).toContainElement(title);
});
it('supports keyboard navigation', () => {
render(
<AccordionWrapper defaultValue={[]}>
<AccordionControl>Keyboard Test</AccordionControl>
<Accordion.Panel>Panel Content</Accordion.Panel>
</AccordionWrapper>
);
const control = screen.getByRole('button');
// Should be focusable
control.focus();
expect(control).toHaveFocus();
// Should respond to Enter key
fireEvent.keyDown(control, { key: 'Enter' });
expect(screen.getByText('Panel Content')).toBeInTheDocument();
});
it('has proper ARIA attributes', () => {
render(
<AccordionWrapper>
<AccordionControl>ARIA Test</AccordionControl>
<Accordion.Panel>Panel Content</Accordion.Panel>
</AccordionWrapper>
);
const control = screen.getByRole('button');
// Accordion controls should have proper ARIA attributes
expect(control).toHaveAttribute('aria-expanded');
expect(control).toHaveAttribute('aria-controls');
});
});
describe('Error Handling', () => {
it('handles null children gracefully', () => {
render(
<AccordionWrapper>
<AccordionControl>{null}</AccordionControl>
</AccordionWrapper>
);
const title = screen.getByRole('heading', { level: 4 });
expect(title).toBeInTheDocument();
});
it('handles undefined children gracefully', () => {
render(
<AccordionWrapper>
<AccordionControl>{undefined}</AccordionControl>
</AccordionWrapper>
);
const title = screen.getByRole('heading', { level: 4 });
expect(title).toBeInTheDocument();
});
it('handles boolean children gracefully', () => {
render(
<AccordionWrapper>
<AccordionControl>{true}</AccordionControl>
</AccordionWrapper>
);
const title = screen.getByRole('heading', { level: 4 });
expect(title).toBeInTheDocument();
});
it('handles array of children gracefully', () => {
render(
<AccordionWrapper>
<AccordionControl>{['First', 'Second', 'Third']}</AccordionControl>
</AccordionWrapper>
);
const title = screen.getByRole('heading', { level: 4 });
expect(title).toHaveTextContent('FirstSecondThird');
});
});
describe('Component Props and Behavior', () => {
it('passes through all children props correctly', () => {
it('passes through children props correctly', () => {
const mockClickHandler = vi.fn();
render(
@@ -328,27 +94,21 @@ describe('AccordionControl', () => {
fireEvent.click(innerButton);
expect(mockClickHandler).toHaveBeenCalled();
});
});
it('maintains component identity across re-renders', () => {
const { rerender } = render(
describe('Accessibility', () => {
it('provides proper semantic structure', () => {
render(
<AccordionWrapper>
<AccordionControl>Initial Content</AccordionControl>
<AccordionControl>Accessible Title</AccordionControl>
</AccordionWrapper>
);
const initialTitle = screen.getByRole('heading', { level: 4 });
expect(initialTitle).toHaveTextContent('Initial Content');
const title = screen.getByRole('heading', { level: 4 });
const button = screen.getByRole('button');
rerender(
<TestWrapper>
<AccordionWrapper>
<AccordionControl>Updated Content</AccordionControl>
</AccordionWrapper>
</TestWrapper>
);
const updatedTitle = screen.getByRole('heading', { level: 4 });
expect(updatedTitle).toHaveTextContent('Updated Content');
expect(title).toHaveTextContent('Accessible Title');
expect(button).toContainElement(title);
});
});
});