From e1bb5a5760ddd80a9152e7d616b74a4a3c22faf5 Mon Sep 17 00:00:00 2001 From: sakina-cmpn Date: Wed, 3 Jun 2026 17:43:07 +0530 Subject: [PATCH 1/2] test: add ResumePreviewForm mouse interactivity tests --- ...mePreviewForm.mouse-interactivity.test.tsx | 174 ++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 components/dashboard/ResumePreviewForm.mouse-interactivity.test.tsx diff --git a/components/dashboard/ResumePreviewForm.mouse-interactivity.test.tsx b/components/dashboard/ResumePreviewForm.mouse-interactivity.test.tsx new file mode 100644 index 000000000..868840d16 --- /dev/null +++ b/components/dashboard/ResumePreviewForm.mouse-interactivity.test.tsx @@ -0,0 +1,174 @@ +import { render, screen, fireEvent, waitFor } from '@testing-library/react'; +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import React from 'react'; +import type { HTMLAttributes, ReactNode } from 'react'; +import ResumePreviewForm from './ResumePreviewForm'; +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 - Mouse Interactivity & Touch Propagation', () => { + const onBack = vi.fn(); + const onComplete = vi.fn(); + + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('keeps hover affordances intact while add actions still update the skill list', () => { + render( + + ); + + const addButtons = screen.getAllByRole('button', { name: /^Add$/i }); + const addSkillButton = addButtons[0]; + + fireEvent.mouseEnter(addSkillButton); + fireEvent.mouseLeave(addSkillButton); + fireEvent.click(addSkillButton); + + expect(addSkillButton).toHaveClass('hover:text-emerald-500'); + expect(screen.getAllByRole('textbox')).toHaveLength(4); + }); + + it('exposes hover tooltips on remove controls and removes nested sections on click', () => { + render( + + ); + + const addButtons = screen.getAllByRole('button', { name: /^Add$/i }); + fireEvent.click(addButtons[1]); + fireEvent.click(addButtons[2]); + + const removeSkillButton = screen.getByRole('button', { name: /Remove skill 1/i }); + const removeEducationButton = screen.getByRole('button', { + name: /Remove education entry 1/i, + }); + const removeExperienceButton = screen.getByRole('button', { + name: /Remove experience entry 1/i, + }); + + expect(removeSkillButton).toHaveAttribute('title', 'Remove skill 1'); + expect(removeEducationButton).toHaveAttribute('title', 'Remove education entry 1'); + expect(removeExperienceButton).toHaveAttribute('title', 'Remove experience entry 1'); + + fireEvent.mouseEnter(removeSkillButton); + fireEvent.click(removeSkillButton); + fireEvent.click(removeEducationButton); + fireEvent.click(removeExperienceButton); + + expect(screen.getAllByRole('textbox')).toHaveLength(2); + expect(screen.queryByPlaceholderText('Institution')).not.toBeInTheDocument(); + expect(screen.queryByPlaceholderText('Company')).not.toBeInTheDocument(); + }); + + it('propagates touch events from interactive controls up to parent listeners', () => { + const parentTouchStart = vi.fn(); + const parentTouchEnd = vi.fn(); + + render( +
+ +
+ ); + + const addExperienceButton = screen.getAllByRole('button', { name: /^Add$/i })[2]; + + fireEvent.touchStart(addExperienceButton, { + touches: [{ identifier: 1, clientX: 24, clientY: 64 }], + }); + fireEvent.touchEnd(addExperienceButton, { + changedTouches: [{ identifier: 1, clientX: 24, clientY: 64 }], + }); + fireEvent.click(addExperienceButton); + + expect(parentTouchStart).toHaveBeenCalled(); + expect(parentTouchEnd).toHaveBeenCalled(); + expect(screen.getByPlaceholderText('Company')).toBeInTheDocument(); + }); + + it('transitions the save button into a pending state and completes after the request resolves', async () => { + let resolveFetch: + | ((value: { ok: boolean; json: () => Promise<{ success: boolean }> }) => void) + | undefined; + + const pendingFetch = new Promise<{ ok: boolean; json: () => Promise<{ success: boolean }> }>( + (resolve) => { + resolveFetch = resolve; + } + ); + + vi.stubGlobal('fetch', vi.fn().mockReturnValue(pendingFetch)); + + render( + + ); + + const saveButton = screen.getByRole('button', { name: /Save Profile/i }); + fireEvent.click(saveButton); + + expect(screen.getByRole('button', { name: /Saving.../i })).toBeDisabled(); + + resolveFetch?.({ + ok: true, + json: async () => ({ success: true }), + }); + + await waitFor(() => { + expect(onComplete).toHaveBeenCalledTimes(1); + }); + + expect(toastMocks.success).toHaveBeenCalledWith('Profile saved successfully!'); + }); +}); From 44a84c23004121b2a6ea2bcfbb1dd6a27ad9ca05 Mon Sep 17 00:00:00 2001 From: sakina-cmpn Date: Thu, 4 Jun 2026 11:00:59 +0530 Subject: [PATCH 2/2] test:Updated failing query --- .../dashboard/ResumePreviewForm.mouse-interactivity.test.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/components/dashboard/ResumePreviewForm.mouse-interactivity.test.tsx b/components/dashboard/ResumePreviewForm.mouse-interactivity.test.tsx index 868840d16..440f3874e 100644 --- a/components/dashboard/ResumePreviewForm.mouse-interactivity.test.tsx +++ b/components/dashboard/ResumePreviewForm.mouse-interactivity.test.tsx @@ -79,7 +79,9 @@ describe('ResumePreviewForm - Mouse Interactivity & Touch Propagation', () => { fireEvent.click(addButtons[1]); fireEvent.click(addButtons[2]); - const removeSkillButton = screen.getByRole('button', { name: /Remove skill 1/i }); + const removeSkillButton = screen.getByRole('button', { + name: /^Remove skill \d+$/i, + }); const removeEducationButton = screen.getByRole('button', { name: /Remove education entry 1/i, });