From 27e54aebec14989a799feed84bee2aee6bae2c27 Mon Sep 17 00:00:00 2001 From: Tuba Date: Wed, 3 Jun 2026 23:10:53 +0530 Subject: [PATCH] test: add error resilience tests for ResumePreviewForm --- ...esumePreviewForm.error-resilience.test.tsx | 177 ++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 components/dashboard/ResumePreviewForm.error-resilience.test.tsx diff --git a/components/dashboard/ResumePreviewForm.error-resilience.test.tsx b/components/dashboard/ResumePreviewForm.error-resilience.test.tsx new file mode 100644 index 000000000..591042047 --- /dev/null +++ b/components/dashboard/ResumePreviewForm.error-resilience.test.tsx @@ -0,0 +1,177 @@ +import { render, screen, fireEvent, waitFor } from '@testing-library/react'; +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import ResumePreviewForm from './ResumePreviewForm'; +import type { ReactNode, HTMLAttributes } from 'react'; +import '@testing-library/jest-dom'; + +const toastMocks = vi.hoisted(() => ({ + error: vi.fn(), + success: vi.fn(), +})); + +vi.mock('sonner', () => ({ + toast: { + error: toastMocks.error, + success: toastMocks.success, + }, +})); + +vi.mock('framer-motion', () => ({ + motion: { + div: ({ children, ...props }: HTMLAttributes & { children?: ReactNode }) => ( +
{children}
+ ), + }, +})); + +const parsed = { + name: 'John Doe', + email: 'john@example.com', + phone: '1234567890', + skills: ['React'], + education: [], + experience: [], +}; + +describe('ResumePreviewForm - Error Resilience', () => { + const onBack = vi.fn(); + const onComplete = vi.fn(); + + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('handles missing required fields gracefully', () => { + render( + + ); + + fireEvent.click(screen.getByText('Save Profile')); + + expect(toastMocks.error).toHaveBeenCalledWith('Name and email are required'); + + expect(onComplete).not.toHaveBeenCalled(); + }); + + it('handles failed API responses with custom error message', async () => { + global.fetch = vi.fn().mockResolvedValue({ + ok: true, + json: async () => ({ + success: false, + error: 'Profile validation failed', + }), + }) as typeof fetch; + + render( + + ); + + fireEvent.click(screen.getByText('Save Profile')); + + await waitFor(() => { + expect(toastMocks.error).toHaveBeenCalledWith('Profile validation failed'); + }); + + expect(onComplete).not.toHaveBeenCalled(); + }); + + it('handles non-ok API responses with fallback error message', async () => { + global.fetch = vi.fn().mockResolvedValue({ + ok: false, + json: async () => ({ + success: false, + }), + }) as typeof fetch; + + render( + + ); + + fireEvent.click(screen.getByText('Save Profile')); + + await waitFor(() => { + expect(toastMocks.error).toHaveBeenCalledWith('Failed to save profile'); + }); + + expect(onComplete).not.toHaveBeenCalled(); + }); + + it('handles network exceptions safely', async () => { + global.fetch = vi.fn().mockRejectedValue(new Error('Network failure')) as typeof fetch; + + render( + + ); + + fireEvent.click(screen.getByText('Save Profile')); + + await waitFor(() => { + expect(toastMocks.error).toHaveBeenCalledWith('Network error. Please try again.'); + }); + + expect(onComplete).not.toHaveBeenCalled(); + }); + + it('recovers correctly after a failed request and allows successful retry', async () => { + global.fetch = vi + .fn() + .mockRejectedValueOnce(new Error('Network failure')) + .mockResolvedValueOnce({ + ok: true, + json: async () => ({ + success: true, + }), + }) as typeof fetch; + + render( + + ); + + fireEvent.click(screen.getByText('Save Profile')); + + await waitFor(() => { + expect(toastMocks.error).toHaveBeenCalledWith('Network error. Please try again.'); + }); + + fireEvent.click(screen.getByText('Save Profile')); + + await waitFor(() => { + expect(toastMocks.success).toHaveBeenCalledWith('Profile saved successfully!'); + expect(onComplete).toHaveBeenCalled(); + }); + }); +});