mirror of
https://github.com/lordmathis/lemma.git
synced 2025-11-05 15:44:21 +00:00
Simplify AccordionControl tests
This commit is contained in:
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user