forked from Creditra/Creditra-Frontend
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathOnboardingFlow.test.tsx
More file actions
145 lines (124 loc) · 5.49 KB
/
Copy pathOnboardingFlow.test.tsx
File metadata and controls
145 lines (124 loc) · 5.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import { render, screen, fireEvent } from '@testing-library/react';
import { expect, test, vi, describe, beforeEach } from 'vitest';
import { OnboardingFlow } from './OnboardingFlow';
// Provide a clean requestAnimationFrame polyfill (jsdom doesn't ship one)
if (typeof globalThis.requestAnimationFrame !== 'function') {
globalThis.requestAnimationFrame = (cb: FrameRequestCallback) =>
setTimeout(cb, 16) as unknown as number;
}
// Mock framer-motion so tests run without a real browser environment
const mockUseReducedMotion = vi.hoisted(() => vi.fn(() => false));
vi.mock('framer-motion', async () => {
const { createElement, Fragment, forwardRef } = await import('react');
return {
motion: {
div: forwardRef(function MotionDiv(
{ children, initial, animate, exit, transition, ...props }: any,
ref: any,
) {
return createElement('div', { ...props, ref }, children);
}),
},
AnimatePresence({ children, mode: _mode }: any) {
return createElement(Fragment, null, children);
},
useReducedMotion(...args: any[]) {
return mockUseReducedMotion(...args);
},
};
});
describe('OnboardingFlow', () => {
const defaultProps = {
isOpen: true,
onComplete: vi.fn(),
onSkip: vi.fn(),
};
beforeEach(() => {
vi.clearAllMocks();
window.localStorage.clear();
mockUseReducedMotion.mockReturnValue(false);
});
test('returns null when isOpen is false', () => {
const { container } = render(
<OnboardingFlow {...defaultProps} isOpen={false} />,
);
expect(container).toBeEmptyDOMElement();
});
test('renders the first step by default', () => {
render(<OnboardingFlow {...defaultProps} />);
expect(screen.getByText('Welcome to Creditra')).toBeInTheDocument();
expect(screen.getByText('Step 1 of 3')).toBeInTheDocument();
});
test('navigates to the next step on Next click', () => {
render(<OnboardingFlow {...defaultProps} />);
fireEvent.click(screen.getByRole('button', { name: 'Next step' }));
expect(screen.getByText('Credit Evaluation')).toBeInTheDocument();
expect(screen.getByText('Step 2 of 3')).toBeInTheDocument();
});
test('navigates back on Back click', () => {
render(<OnboardingFlow {...defaultProps} />);
fireEvent.click(screen.getByRole('button', { name: 'Next step' }));
fireEvent.click(screen.getByRole('button', { name: 'Go back' }));
expect(screen.getByText('Welcome to Creditra')).toBeInTheDocument();
expect(screen.getByText('Step 1 of 3')).toBeInTheDocument();
});
test('back button is disabled on the first step', () => {
render(<OnboardingFlow {...defaultProps} />);
expect(screen.getByRole('button', { name: 'Go back' })).toBeDisabled();
});
test('calls onComplete and persists localStorage on the last step', () => {
const onComplete = vi.fn();
render(<OnboardingFlow {...defaultProps} onComplete={onComplete} />);
fireEvent.click(screen.getByRole('button', { name: 'Next step' }));
fireEvent.click(screen.getByRole('button', { name: 'Next step' }));
fireEvent.click(screen.getByRole('button', { name: 'Get started' }));
expect(onComplete).toHaveBeenCalledTimes(1);
expect(window.localStorage.getItem('onboarding_completed')).toBe('true');
});
test('calls onSkip and persists localStorage when skip is clicked', () => {
const onSkip = vi.fn();
render(<OnboardingFlow {...defaultProps} onSkip={onSkip} />);
fireEvent.click(screen.getByRole('button', { name: 'Skip onboarding' }));
expect(onSkip).toHaveBeenCalledTimes(1);
expect(window.localStorage.getItem('onboarding_completed')).toBe('true');
});
test('renders step indicators for each step', () => {
render(<OnboardingFlow {...defaultProps} />);
const indicators = screen.getAllByRole('listitem');
expect(indicators).toHaveLength(3);
});
test('shows Get Started label on the last step', () => {
render(<OnboardingFlow {...defaultProps} />);
fireEvent.click(screen.getByRole('button', { name: 'Next step' }));
fireEvent.click(screen.getByRole('button', { name: 'Next step' }));
expect(
screen.getByRole('button', { name: 'Get started' }),
).toBeInTheDocument();
});
describe('reduced-motion', () => {
test('renders all steps and supports navigation when reduced motion is active', () => {
mockUseReducedMotion.mockReturnValue(true);
render(<OnboardingFlow {...defaultProps} />);
// First step renders
expect(screen.getByText('Welcome to Creditra')).toBeInTheDocument();
// Navigate forward
fireEvent.click(screen.getByRole('button', { name: 'Next step' }));
expect(screen.getByText('Credit Evaluation')).toBeInTheDocument();
// Navigate backward
fireEvent.click(screen.getByRole('button', { name: 'Go back' }));
expect(screen.getByText('Welcome to Creditra')).toBeInTheDocument();
// Complete the flow
fireEvent.click(screen.getByRole('button', { name: 'Next step' }));
fireEvent.click(screen.getByRole('button', { name: 'Next step' }));
fireEvent.click(screen.getByRole('button', { name: 'Get started' }));
expect(defaultProps.onComplete).toHaveBeenCalledTimes(1);
});
test('supports skip when reduced motion is active', () => {
mockUseReducedMotion.mockReturnValue(true);
const onSkip = vi.fn();
render(<OnboardingFlow {...defaultProps} onSkip={onSkip} />);
fireEvent.click(screen.getByRole('button', { name: 'Skip onboarding' }));
expect(onSkip).toHaveBeenCalledTimes(1);
});
});
});