@kadconsulting/dry
Version:
KAD Reusable Component Library
237 lines • 9.11 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { render, screen, waitFor, fireEvent } from '@testing-library/react';
import { act } from 'react-dom/test-utils';
import Wizard from './Wizard';
const MockStepComponent = ({ stepState, onStepChange, }) => (_jsxs("div", { children: [_jsx("span", { children: "Step Content" }), _jsx("button", { onClick: () => onStepChange && onStepChange({ test: 'state' }), children: "Update Step State" }), stepState && _jsxs("span", { children: ["Step State: ", JSON.stringify(stepState)] })] }));
const mockSteps = [
{
title: 'Step 1',
text: 'Step 1 Text',
component: _jsx(MockStepComponent, {}),
},
{
title: 'Step 2',
text: 'Step 2 Text',
component: _jsx(MockStepComponent, {}),
},
{
title: 'Step 3',
text: 'Step 3 Text',
component: _jsx(MockStepComponent, {}),
},
];
describe('Wizard', () => {
beforeAll(() => {
window.scrollTo = jest.fn();
});
it('renders with a wizard className', () => {
// ARRANGE
const { container } = render(_jsx(Wizard, { steps: mockSteps }));
// ASSERT
expect(container.firstChild).toHaveClass('wizard');
});
it('renders the first step by default', () => {
// ARRANGE & ACT
render(_jsx(Wizard, { steps: mockSteps }));
// ASSERT
expect(screen.getByText('Step 1 Text')).toBeInTheDocument();
});
it('navigates to the next step when clicking the next button', async () => {
// ARRANGE
render(_jsx(Wizard, { steps: mockSteps }));
// ACT
await act(async () => {
fireEvent.click(screen.getByText('Next'));
});
// ASSERT
expect(screen.getByText('Step 2 Text')).toBeInTheDocument();
});
it('navigates to the previous step when clicking the previous button', async () => {
// ARRANGE
render(_jsx(Wizard, { steps: mockSteps, currentStep: 1 }));
// ACT
await act(async () => {
fireEvent.click(screen.getByText('Previous'));
});
// ASSERT
expect(screen.getByText('Step 1 Text')).toBeInTheDocument();
});
it('disables the previous button on the first step', () => {
// ARRANGE & ACT
render(_jsx(Wizard, { steps: mockSteps }));
// ASSERT
expect(screen.getByText('Previous').closest('button')).toBeDisabled();
});
it('disables the next button on the last step', () => {
// ARRANGE & ACT
render(_jsx(Wizard, { steps: mockSteps, currentStep: 2 }));
// ASSERT
expect(screen.getByText('Next').closest('button')).toBeDisabled();
});
it('calls onStepChange when navigating', async () => {
// ARRANGE
const onStepChange = jest.fn();
render(_jsx(Wizard, { steps: mockSteps, onStepChange: onStepChange }));
// ACT
await act(async () => {
fireEvent.click(screen.getByText('Next'));
});
// ASSERT
await waitFor(() => {
expect(onStepChange).toHaveBeenCalledWith(1);
});
});
it('displays error message when provided', () => {
// ARRANGE & ACT
render(_jsx(Wizard, { steps: mockSteps, error: 'Test error message' }));
// ASSERT
expect(screen.getByText('Test error message')).toBeInTheDocument();
});
it('uses custom button labels when provided', () => {
// ARRANGE & ACT
render(_jsx(Wizard, { steps: mockSteps, nextButtonLabel: 'Continue', previousButtonLabel: 'Back' }));
// ASSERT
expect(screen.getByText('Continue')).toBeInTheDocument();
expect(screen.getByText('Back')).toBeInTheDocument();
});
it('hides progress bar when hideProgressBar is true', () => {
// ARRANGE & ACT
const { container } = render(_jsx(Wizard, { steps: mockSteps, hideProgressBar: true }));
// ASSERT
expect(container.querySelector('.progress-steps')).not.toBeInTheDocument();
});
it('calls onStepChanging before changing steps', async () => {
// ARRANGE
const onStepChanging = jest.fn().mockResolvedValue(true);
render(_jsx(Wizard, { steps: mockSteps, onStepChanging: onStepChanging }));
// ACT
await act(async () => {
fireEvent.click(screen.getByText('Next'));
});
// ASSERT
await waitFor(() => {
expect(onStepChanging).toHaveBeenCalledWith(1);
});
});
it('prevents step change when validate function returns false', async () => {
// ARRANGE
const validateMock = jest.fn().mockReturnValue(false);
const customSteps = [
{ ...mockSteps[0], validate: validateMock },
...mockSteps.slice(1),
];
render(_jsx(Wizard, { steps: customSteps }));
// ACT
await act(async () => {
fireEvent.click(screen.getByText('Next'));
});
// ASSERT
await waitFor(() => {
expect(validateMock).toHaveBeenCalled();
expect(screen.getByText('Step 1 Text')).toBeInTheDocument();
});
});
it('calls onNext and onPrevious functions when provided', async () => {
// ARRANGE
const onNextMock = jest.fn().mockResolvedValue(undefined);
const onPreviousMock = jest.fn().mockResolvedValue(undefined);
const customSteps = [
{ ...mockSteps[0], onNext: onNextMock },
{ ...mockSteps[1], onPrevious: onPreviousMock },
mockSteps[2],
];
render(_jsx(Wizard, { steps: customSteps }));
// ACT & ASSERT for onNext
await act(async () => {
fireEvent.click(screen.getByText('Next'));
});
await waitFor(() => {
expect(onNextMock).toHaveBeenCalled();
});
// ACT & ASSERT for onPrevious
await act(async () => {
fireEvent.click(screen.getByText('Previous'));
});
await waitFor(() => {
expect(onPreviousMock).toHaveBeenCalled();
});
});
it('renders loading state when isLoading is true', () => {
// ARRANGE & ACT
render(_jsx(Wizard, { steps: mockSteps, loading: true }));
// ASSERT
expect(screen.getByLabelText('Loading')).toBeInTheDocument();
});
it('handles steps with noNextButton property', () => {
// ARRANGE
const customSteps = [
{ ...mockSteps[0], noNextButton: true },
...mockSteps.slice(1),
];
// ACT
render(_jsx(Wizard, { steps: customSteps }));
// ASSERT
expect(screen.queryByText('Next')).not.toBeInTheDocument();
});
it('handles steps with removeButtons property', () => {
// ARRANGE
const customSteps = [
{ ...mockSteps[0], removeButtons: true },
...mockSteps.slice(1),
];
// ACT
render(_jsx(Wizard, { steps: customSteps }));
// ASSERT
expect(screen.queryByText('Next')).not.toBeInTheDocument();
expect(screen.queryByText('Previous')).not.toBeInTheDocument();
});
it('handles stepState and onStepChange', async () => {
// ARRANGE
render(_jsx(Wizard, { steps: mockSteps }));
// ACT
await act(async () => {
fireEvent.click(screen.getByText('Update Step State'));
});
// ASSERT
// This test doesn't actually assert anything, but it ensures that
// the component doesn't throw an error when stepState is updated
});
it('handles undefined externalCurrentStep', () => {
// ARRANGE
const { rerender } = render(_jsx(Wizard, { steps: mockSteps }));
// ACT
rerender(_jsx(Wizard, { steps: mockSteps, currentStep: undefined }));
// ASSERT
expect(screen.getByText('Step 1 Text')).toBeInTheDocument();
});
it('handles step validation', async () => {
// ARRANGE
const validateMock = jest.fn().mockReturnValue(true);
const customSteps = [
{ ...mockSteps[0], validate: validateMock },
...mockSteps.slice(1),
];
render(_jsx(Wizard, { steps: customSteps }));
// ACT
await act(async () => {
fireEvent.click(screen.getByText('Next'));
});
// ASSERT
expect(validateMock).toHaveBeenCalled();
expect(screen.getByText('Step 2 Text')).toBeInTheDocument();
});
it('handles steps without onNext or onPrevious', async () => {
// ARRANGE
render(_jsx(Wizard, { steps: mockSteps }));
// ACT & ASSERT
await act(async () => {
fireEvent.click(screen.getByText('Next'));
});
expect(screen.getByText('Step 2 Text')).toBeInTheDocument();
await act(async () => {
fireEvent.click(screen.getByText('Previous'));
});
expect(screen.getByText('Step 1 Text')).toBeInTheDocument();
});
});
//# sourceMappingURL=Wizard.test.js.map