the-render-pattern
a component test that renders outside the setup factory or destructures queries from render()
Agent checkadvisory by defaultreacttests
The argument
The Render Pattern
Render inside the setup() factory; query and assert in the test. For a component, render() is the act, but by convention it lives in the arrange factory — the one accepted exception to "the act lives in the test". Query from screen — never destructure queries from render().
import { expect, describe, test, jest } from '@jest/globals';
import { render, screen } from '@testing-library/preact';
import userEvent from '@testing-library/user-event';
import { NotificationBanner } from './NotificationBanner';
// Mocked Imports
// -------------------------
const mockUseAppStore = jest.fn<(selector: (state: unknown) => unknown) => unknown>();
jest.mock('@store/appStore', () => ({
useAppStore: (selector: (state: unknown) => unknown) => mockUseAppStore(selector),
}));
// -------------------------
const setupNotificationBanner = ({ isVisible = true }: { isVisible?: boolean } = {}) => {
const onDismiss = jest.fn<() => void>();
mockUseAppStore.mockReturnValue(isVisible);
render(<NotificationBanner onDismiss={onDismiss} />);
return { onDismiss };
};
describe('NotificationBanner', () => {
test('does not render the banner when not visible', () => {
setupNotificationBanner({ isVisible: false });
const banner = screen.queryByRole('alert');
expect(banner).not.toBeInTheDocument();
});
test('renders the notification message when visible', () => {
setupNotificationBanner({ isVisible: true });
const message = screen.getByText('Action required');
expect(message).toBeInTheDocument();
});
test('calls the dismiss handler when the dismiss button is clicked', async () => {
const { onDismiss } = setupNotificationBanner({ isVisible: true });
const user = userEvent.setup();
const dismissButton = screen.getByRole('button', { name: /dismiss/i });
await user.click(dismissButton);
expect(onDismiss).toHaveBeenCalledTimes(1);
});
});
The proof
failsrc/notifications/NotificationBanner.unit.test.tsx
import { expect, describe, test } from '@jest/globals';
import { render } from '@testing-library/react';
import { NotificationBanner } from './NotificationBanner';
describe('NotificationBanner', () => {
test('renders the notification message when visible', () => {
// render in the test body, and queries destructured from its result
const { getByText } = render(<NotificationBanner isVisible message="Action required" />);
expect(getByText('Action required')).toBeInTheDocument();
});
});
passsrc/notifications/NotificationBanner.unit.test.tsx
import { expect, describe, test } from '@jest/globals';
import { render, screen } from '@testing-library/react';
import { NotificationBanner } from './NotificationBanner';
const setupNotificationBanner = ({ isVisible = true }: { isVisible?: boolean } = {}) => {
render(<NotificationBanner isVisible={isVisible} message="Action required" />);
};
describe('NotificationBanner', () => {
test('renders the notification message when visible', () => {
setupNotificationBanner({ isVisible: true });
const message = screen.getByText('Action required');
expect(message).toBeInTheDocument();
});
});
Turn it down
Both lines go in your lightsout.config.json.
"standards-checks": { "the-render-pattern": "advisory" }"standards-checks": { "the-render-pattern": "off" }