From 604b3f3f0e3170bc6c333e68ce03ed3890e80617 Mon Sep 17 00:00:00 2001 From: Surajshivam-123 Date: Wed, 19 Aug 2026 00:01:28 +0530 Subject: [PATCH 1/4] feat: add password confirmation and visibility toggle in create album dialog --- .../components/Albums/CreateAlbumDialog.tsx | 127 +++++++++-- .../__tests__/CreateAlbumDialog.test.tsx | 210 ++++++++++++++++++ 2 files changed, 316 insertions(+), 21 deletions(-) create mode 100644 frontend/src/components/Albums/__tests__/CreateAlbumDialog.test.tsx diff --git a/frontend/src/components/Albums/CreateAlbumDialog.tsx b/frontend/src/components/Albums/CreateAlbumDialog.tsx index ca045d9ad..34a0663d4 100644 --- a/frontend/src/components/Albums/CreateAlbumDialog.tsx +++ b/frontend/src/components/Albums/CreateAlbumDialog.tsx @@ -16,6 +16,7 @@ import { CreateAlbumDialogProps } from '@/types/Album'; import { usePictoMutation } from '@/hooks/useQueryExtension'; import { createAlbum } from '@/api/api-functions'; import { useMutationFeedback } from '@/hooks/useMutationFeedback'; +import { Eye, EyeOff } from 'lucide-react'; export const CreateAlbumDialog: React.FC = ({ isOpen, @@ -27,11 +28,14 @@ export const CreateAlbumDialog: React.FC = ({ description: '', is_locked: false, password: '', + confirmPassword: '', }); + const [showPassword, setShowPassword] = useState(false); + const [showConfirmPassword, setShowConfirmPassword] = useState(false); const [errors, setErrors] = useState>({}); const createAlbumMutation = usePictoMutation({ - mutationFn: createAlbum, + mutationFn: (data: Parameters[0]) => createAlbum(data), }); useMutationFeedback(createAlbumMutation, { @@ -55,8 +59,15 @@ export const CreateAlbumDialog: React.FC = ({ newErrors.name = 'Album name is required'; } - if (formData.is_locked && !formData.password.trim()) { - newErrors.password = 'Password is required for locked albums'; + if (formData.is_locked) { + if (!formData.password.trim()) { + newErrors.password = 'Password is required for locked albums'; + } + if (!formData.confirmPassword.trim()) { + newErrors.confirmPassword = 'Confirm password is required'; + } else if (formData.password !== formData.confirmPassword) { + newErrors.confirmPassword = 'Passwords do not match'; + } } setErrors(newErrors); @@ -88,7 +99,10 @@ export const CreateAlbumDialog: React.FC = ({ description: '', is_locked: false, password: '', + confirmPassword: '', }); + setShowPassword(false); + setShowConfirmPassword(false); setErrors({}); onClose(); }; @@ -161,24 +175,95 @@ export const CreateAlbumDialog: React.FC = ({ {/* Password Field (shown only if locked) */} {formData.is_locked && ( -
- - - setFormData({ ...formData, password: e.target.value }) - } - className={errors.password ? 'border-destructive' : ''} - /> - {errors.password && ( -

{errors.password}

- )} -
+ <> +
+ +
+ + setFormData({ ...formData, password: e.target.value }) + } + className={ + errors.password ? 'border-destructive pr-10' : 'pr-10' + } + /> + +
+ {errors.password && ( +

+ {errors.password} +

+ )} +
+ + {/* Confirm Password Field */} +
+ +
+ + setFormData({ + ...formData, + confirmPassword: e.target.value, + }) + } + className={ + errors.confirmPassword + ? 'border-destructive pr-10' + : 'pr-10' + } + /> + +
+ {errors.confirmPassword && ( +

+ {errors.confirmPassword} +

+ )} +
+ )} diff --git a/frontend/src/components/Albums/__tests__/CreateAlbumDialog.test.tsx b/frontend/src/components/Albums/__tests__/CreateAlbumDialog.test.tsx new file mode 100644 index 000000000..fd677da85 --- /dev/null +++ b/frontend/src/components/Albums/__tests__/CreateAlbumDialog.test.tsx @@ -0,0 +1,210 @@ +import userEvent from '@testing-library/user-event'; +import { render, screen, waitFor } from '@/test-utils'; +import { createAlbum } from '@/api/api-functions'; +import { CreateAlbumDialog } from '../CreateAlbumDialog'; + +jest.mock('@/api/api-functions', () => ({ + createAlbum: jest.fn(), +})); + +const mockCreateAlbum = createAlbum as jest.Mock; + +const renderDialog = ( + isOpen = true, + onClose = jest.fn(), + onSuccess = jest.fn(), +) => + render( + , + ); + +describe('CreateAlbumDialog', () => { + beforeEach(() => { + jest.clearAllMocks(); + mockCreateAlbum.mockResolvedValue({ + success: true, + data: { id: 'a1', name: 'New Album' }, + }); + }); + + it('renders the dialog when isOpen is true', () => { + renderDialog(true); + expect( + screen.getByRole('heading', { name: /create new album/i }), + ).toBeInTheDocument(); + expect(screen.getByLabelText(/album name/i)).toBeInTheDocument(); + expect(screen.getByLabelText(/description/i)).toBeInTheDocument(); + expect(screen.getByLabelText(/lock album/i)).toBeInTheDocument(); + expect(screen.queryByLabelText(/^password/i)).not.toBeInTheDocument(); + expect( + screen.queryByLabelText(/confirm password/i), + ).not.toBeInTheDocument(); + }); + + it('shows error if album name is empty on submission', async () => { + const user = userEvent.setup(); + renderDialog(); + + await user.click(screen.getByRole('button', { name: /create album/i })); + + expect(screen.getByText('Album name is required')).toBeInTheDocument(); + expect(mockCreateAlbum).not.toHaveBeenCalled(); + }); + + it('reveals password and confirm password inputs when lock album is toggled on', async () => { + const user = userEvent.setup(); + renderDialog(); + + expect(screen.queryByLabelText(/^password/i)).not.toBeInTheDocument(); + + await user.click(screen.getByLabelText(/lock album/i)); + + expect(screen.getByLabelText(/^password \*/i)).toBeInTheDocument(); + expect(screen.getByLabelText(/confirm password \*/i)).toBeInTheDocument(); + }); + + it('toggles password and confirm password visibility', async () => { + const user = userEvent.setup(); + renderDialog(); + + await user.click(screen.getByLabelText(/lock album/i)); + + const passwordInput = screen.getByLabelText(/^password \*/i); + const confirmPasswordInput = screen.getByLabelText(/confirm password \*/i); + const togglePasswordBtn = screen.getByRole('button', { + name: /show password/i, + }); + const toggleConfirmBtn = screen.getByRole('button', { + name: /show confirm password/i, + }); + + expect(passwordInput).toHaveAttribute('type', 'password'); + expect(confirmPasswordInput).toHaveAttribute('type', 'password'); + + // Toggle password visibility + await user.click(togglePasswordBtn); + expect(passwordInput).toHaveAttribute('type', 'text'); + expect( + screen.getByRole('button', { name: /hide password/i }), + ).toBeInTheDocument(); + + await user.click(screen.getByRole('button', { name: /hide password/i })); + expect(passwordInput).toHaveAttribute('type', 'password'); + + // Toggle confirm password visibility + await user.click(toggleConfirmBtn); + expect(confirmPasswordInput).toHaveAttribute('type', 'text'); + expect( + screen.getByRole('button', { name: /hide confirm password/i }), + ).toBeInTheDocument(); + + await user.click( + screen.getByRole('button', { name: /hide confirm password/i }), + ); + expect(confirmPasswordInput).toHaveAttribute('type', 'password'); + }); + + it('validates password requirement when lock album is enabled', async () => { + const user = userEvent.setup(); + renderDialog(); + + await user.type(screen.getByLabelText(/album name/i), 'Family Trip'); + await user.click(screen.getByLabelText(/lock album/i)); + + // Submit with both password and confirm password empty + await user.click(screen.getByRole('button', { name: /create album/i })); + + expect( + screen.getByText('Password is required for locked albums'), + ).toBeInTheDocument(); + expect( + screen.getByText('Confirm password is required'), + ).toBeInTheDocument(); + expect(mockCreateAlbum).not.toHaveBeenCalled(); + }); + + it('validates that confirm password is required if password is entered', async () => { + const user = userEvent.setup(); + renderDialog(); + + await user.type(screen.getByLabelText(/album name/i), 'Family Trip'); + await user.click(screen.getByLabelText(/lock album/i)); + await user.type(screen.getByLabelText(/^password \*/i), 'securepass123'); + + await user.click(screen.getByRole('button', { name: /create album/i })); + + expect( + screen.getByText('Confirm password is required'), + ).toBeInTheDocument(); + expect(mockCreateAlbum).not.toHaveBeenCalled(); + }); + + it('validates that passwords must match', async () => { + const user = userEvent.setup(); + renderDialog(); + + await user.type(screen.getByLabelText(/album name/i), 'Family Trip'); + await user.click(screen.getByLabelText(/lock album/i)); + await user.type(screen.getByLabelText(/^password \*/i), 'securepass123'); + await user.type( + screen.getByLabelText(/confirm password \*/i), + 'differentpass', + ); + + await user.click(screen.getByRole('button', { name: /create album/i })); + + expect(screen.getByText('Passwords do not match')).toBeInTheDocument(); + expect(mockCreateAlbum).not.toHaveBeenCalled(); + }); + + it('submits locked album successfully when passwords match', async () => { + const user = userEvent.setup(); + renderDialog(true, jest.fn(), jest.fn()); + + await user.type(screen.getByLabelText(/album name/i), 'Secret Album'); + await user.type(screen.getByLabelText(/description/i), 'Secret photos'); + await user.click(screen.getByLabelText(/lock album/i)); + await user.type(screen.getByLabelText(/^password \*/i), 'secret123'); + await user.type(screen.getByLabelText(/confirm password \*/i), 'secret123'); + + await user.click(screen.getByRole('button', { name: /create album/i })); + + await waitFor(() => { + expect(mockCreateAlbum).toHaveBeenCalledWith({ + name: 'Secret Album', + description: 'Secret photos', + is_locked: true, + password: 'secret123', + }); + }); + }); + + it('submits unlocked album without password payload', async () => { + const user = userEvent.setup(); + renderDialog(); + + await user.type(screen.getByLabelText(/album name/i), 'Public Album'); + await user.click(screen.getByRole('button', { name: /create album/i })); + + await waitFor(() => { + expect(mockCreateAlbum).toHaveBeenCalledWith({ + name: 'Public Album', + is_locked: false, + }); + }); + }); + + it('calls onClose when Cancel button is clicked', async () => { + const user = userEvent.setup(); + const handleClose = jest.fn(); + renderDialog(true, handleClose); + + await user.click(screen.getByRole('button', { name: /cancel/i })); + + expect(handleClose).toHaveBeenCalledTimes(1); + }); +}); From 83004b4035d2c58e67bbfcb29dce32ef49a98822 Mon Sep 17 00:00:00 2001 From: Surajshivam-123 Date: Wed, 19 Aug 2026 00:49:27 +0530 Subject: [PATCH 2/4] feat: add password confirmation, visibility toggles, and state cleanup to create album dialog --- .../components/Albums/CreateAlbumDialog.tsx | 88 +++++++++++++++---- .../__tests__/CreateAlbumDialog.test.tsx | 81 ++++++++++++++++- 2 files changed, 149 insertions(+), 20 deletions(-) diff --git a/frontend/src/components/Albums/CreateAlbumDialog.tsx b/frontend/src/components/Albums/CreateAlbumDialog.tsx index 34a0663d4..6d9af2be3 100644 --- a/frontend/src/components/Albums/CreateAlbumDialog.tsx +++ b/frontend/src/components/Albums/CreateAlbumDialog.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { Dialog, DialogContent, @@ -34,6 +34,25 @@ export const CreateAlbumDialog: React.FC = ({ const [showConfirmPassword, setShowConfirmPassword] = useState(false); const [errors, setErrors] = useState>({}); + const resetForm = () => { + setFormData({ + name: '', + description: '', + is_locked: false, + password: '', + confirmPassword: '', + }); + setShowPassword(false); + setShowConfirmPassword(false); + setErrors({}); + }; + + useEffect(() => { + if (!isOpen) { + resetForm(); + } + }, [isOpen]); + const createAlbumMutation = usePictoMutation({ mutationFn: (data: Parameters[0]) => createAlbum(data), }); @@ -94,21 +113,12 @@ export const CreateAlbumDialog: React.FC = ({ }; const handleClose = () => { - setFormData({ - name: '', - description: '', - is_locked: false, - password: '', - confirmPassword: '', - }); - setShowPassword(false); - setShowConfirmPassword(false); - setErrors({}); + resetForm(); onClose(); }; return ( - + !open && handleClose()}>
@@ -131,10 +141,18 @@ export const CreateAlbumDialog: React.FC = ({ onChange={(e) => setFormData({ ...formData, name: e.target.value }) } + aria-invalid={!!errors.name} + aria-describedby={errors.name ? 'name-error' : undefined} className={errors.name ? 'border-destructive' : ''} /> {errors.name && ( -

{errors.name}

+ )} @@ -167,9 +185,23 @@ export const CreateAlbumDialog: React.FC = ({ - setFormData({ ...formData, is_locked: checked }) - } + onCheckedChange={(checked) => { + setFormData((prev) => ({ + ...prev, + is_locked: checked, + ...(checked ? {} : { password: '', confirmPassword: '' }), + })); + if (!checked) { + setShowPassword(false); + setShowConfirmPassword(false); + setErrors((prev) => { + const updated = { ...prev }; + delete updated.password; + delete updated.confirmPassword; + return updated; + }); + } + }} /> @@ -189,6 +221,10 @@ export const CreateAlbumDialog: React.FC = ({ onChange={(e) => setFormData({ ...formData, password: e.target.value }) } + aria-invalid={!!errors.password} + aria-describedby={ + errors.password ? 'password-error' : undefined + } className={ errors.password ? 'border-destructive pr-10' : 'pr-10' } @@ -200,6 +236,7 @@ export const CreateAlbumDialog: React.FC = ({ aria-label={ showPassword ? 'Hide password' : 'Show password' } + aria-controls="password" > {showPassword ? ( @@ -209,7 +246,11 @@ export const CreateAlbumDialog: React.FC = ({ {errors.password && ( -

+

)} @@ -232,6 +273,12 @@ export const CreateAlbumDialog: React.FC = ({ confirmPassword: e.target.value, }) } + aria-invalid={!!errors.confirmPassword} + aria-describedby={ + errors.confirmPassword + ? 'confirm-password-error' + : undefined + } className={ errors.confirmPassword ? 'border-destructive pr-10' @@ -249,6 +296,7 @@ export const CreateAlbumDialog: React.FC = ({ ? 'Hide confirm password' : 'Show confirm password' } + aria-controls="confirm-password" > {showConfirmPassword ? ( @@ -258,7 +306,11 @@ export const CreateAlbumDialog: React.FC = ({ {errors.confirmPassword && ( -

+

)} diff --git a/frontend/src/components/Albums/__tests__/CreateAlbumDialog.test.tsx b/frontend/src/components/Albums/__tests__/CreateAlbumDialog.test.tsx index fd677da85..9e29b0c72 100644 --- a/frontend/src/components/Albums/__tests__/CreateAlbumDialog.test.tsx +++ b/frontend/src/components/Albums/__tests__/CreateAlbumDialog.test.tsx @@ -7,7 +7,7 @@ jest.mock('@/api/api-functions', () => ({ createAlbum: jest.fn(), })); -const mockCreateAlbum = createAlbum as jest.Mock; +const mockCreateAlbum = jest.mocked(createAlbum); const renderDialog = ( isOpen = true, @@ -27,7 +27,16 @@ describe('CreateAlbumDialog', () => { jest.clearAllMocks(); mockCreateAlbum.mockResolvedValue({ success: true, - data: { id: 'a1', name: 'New Album' }, + message: 'Album created', + data: { + id: 'a1', + name: 'New Album', + description: null, + is_locked: false, + created_at: new Date().toISOString(), + updated_at: new Date().toISOString(), + image_count: 0, + }, }); }); @@ -51,7 +60,10 @@ describe('CreateAlbumDialog', () => { await user.click(screen.getByRole('button', { name: /create album/i })); + const nameInput = screen.getByLabelText(/album name \*/i); expect(screen.getByText('Album name is required')).toBeInTheDocument(); + expect(nameInput).toHaveAttribute('aria-invalid', 'true'); + expect(nameInput).toHaveAttribute('aria-describedby', 'name-error'); expect(mockCreateAlbum).not.toHaveBeenCalled(); }); @@ -84,6 +96,11 @@ describe('CreateAlbumDialog', () => { expect(passwordInput).toHaveAttribute('type', 'password'); expect(confirmPasswordInput).toHaveAttribute('type', 'password'); + expect(togglePasswordBtn).toHaveAttribute('aria-controls', 'password'); + expect(toggleConfirmBtn).toHaveAttribute( + 'aria-controls', + 'confirm-password', + ); // Toggle password visibility await user.click(togglePasswordBtn); @@ -118,12 +135,22 @@ describe('CreateAlbumDialog', () => { // Submit with both password and confirm password empty await user.click(screen.getByRole('button', { name: /create album/i })); + const passwordInput = screen.getByLabelText(/^password \*/i); + const confirmPasswordInput = screen.getByLabelText(/confirm password \*/i); + expect( screen.getByText('Password is required for locked albums'), ).toBeInTheDocument(); expect( screen.getByText('Confirm password is required'), ).toBeInTheDocument(); + expect(passwordInput).toHaveAttribute('aria-invalid', 'true'); + expect(passwordInput).toHaveAttribute('aria-describedby', 'password-error'); + expect(confirmPasswordInput).toHaveAttribute('aria-invalid', 'true'); + expect(confirmPasswordInput).toHaveAttribute( + 'aria-describedby', + 'confirm-password-error', + ); expect(mockCreateAlbum).not.toHaveBeenCalled(); }); @@ -161,6 +188,56 @@ describe('CreateAlbumDialog', () => { expect(mockCreateAlbum).not.toHaveBeenCalled(); }); + it('clears password fields and visibility when lock toggle is turned off', async () => { + const user = userEvent.setup(); + renderDialog(); + + await user.type(screen.getByLabelText(/album name/i), 'Family Trip'); + await user.click(screen.getByLabelText(/lock album/i)); + await user.type(screen.getByLabelText(/^password \*/i), 'secret123'); + await user.type(screen.getByLabelText(/confirm password \*/i), 'secret123'); + + // Toggle off lock + await user.click(screen.getByLabelText(/lock album/i)); + expect(screen.queryByLabelText(/^password/i)).not.toBeInTheDocument(); + + // Toggle on lock again — fields should be empty + await user.click(screen.getByLabelText(/lock album/i)); + expect(screen.getByLabelText(/^password \*/i)).toHaveValue(''); + expect(screen.getByLabelText(/confirm password \*/i)).toHaveValue(''); + }); + + it('resets all form state and sensitive data when closed and reopened', async () => { + const user = userEvent.setup(); + const { rerender } = renderDialog(true); + + await user.type(screen.getByLabelText(/album name/i), 'Secret Trip'); + await user.click(screen.getByLabelText(/lock album/i)); + await user.type(screen.getByLabelText(/^password \*/i), 'secret123'); + await user.type(screen.getByLabelText(/confirm password \*/i), 'secret123'); + + // Simulate closing externally + rerender( + , + ); + + // Reopen dialog + rerender( + , + ); + + expect(screen.getByLabelText(/album name \*/i)).toHaveValue(''); + expect(screen.queryByLabelText(/^password/i)).not.toBeInTheDocument(); + }); + it('submits locked album successfully when passwords match', async () => { const user = userEvent.setup(); renderDialog(true, jest.fn(), jest.fn()); From 25cdc3e03ca7b72ad452264b7da0649a57ad2c3b Mon Sep 17 00:00:00 2001 From: Surajshivam-123 Date: Wed, 19 Aug 2026 00:57:44 +0530 Subject: [PATCH 3/4] test: verify visibility and validation error cleanup in create album dialog reset tests --- .../__tests__/CreateAlbumDialog.test.tsx | 89 +++++++++++++++++-- 1 file changed, 82 insertions(+), 7 deletions(-) diff --git a/frontend/src/components/Albums/__tests__/CreateAlbumDialog.test.tsx b/frontend/src/components/Albums/__tests__/CreateAlbumDialog.test.tsx index 9e29b0c72..8c5efa04a 100644 --- a/frontend/src/components/Albums/__tests__/CreateAlbumDialog.test.tsx +++ b/frontend/src/components/Albums/__tests__/CreateAlbumDialog.test.tsx @@ -188,33 +188,85 @@ describe('CreateAlbumDialog', () => { expect(mockCreateAlbum).not.toHaveBeenCalled(); }); - it('clears password fields and visibility when lock toggle is turned off', async () => { + it('clears password fields, visibility, and validation errors when lock toggle is turned off', async () => { const user = userEvent.setup(); renderDialog(); await user.type(screen.getByLabelText(/album name/i), 'Family Trip'); await user.click(screen.getByLabelText(/lock album/i)); await user.type(screen.getByLabelText(/^password \*/i), 'secret123'); - await user.type(screen.getByLabelText(/confirm password \*/i), 'secret123'); + await user.type( + screen.getByLabelText(/confirm password \*/i), + 'mismatchedpass', + ); + + // Show both passwords + await user.click(screen.getByRole('button', { name: /show password/i })); + await user.click( + screen.getByRole('button', { name: /show confirm password/i }), + ); + expect(screen.getByLabelText(/^password \*/i)).toHaveAttribute( + 'type', + 'text', + ); + expect(screen.getByLabelText(/confirm password \*/i)).toHaveAttribute( + 'type', + 'text', + ); + + // Trigger password validation errors + await user.click(screen.getByRole('button', { name: /create album/i })); + expect(screen.getByText('Passwords do not match')).toBeInTheDocument(); // Toggle off lock await user.click(screen.getByLabelText(/lock album/i)); expect(screen.queryByLabelText(/^password/i)).not.toBeInTheDocument(); + expect( + screen.queryByText('Passwords do not match'), + ).not.toBeInTheDocument(); - // Toggle on lock again — fields should be empty + // Toggle on lock again — fields should be empty, type="password", and have no errors await user.click(screen.getByLabelText(/lock album/i)); - expect(screen.getByLabelText(/^password \*/i)).toHaveValue(''); - expect(screen.getByLabelText(/confirm password \*/i)).toHaveValue(''); + const passwordInput = screen.getByLabelText(/^password \*/i); + const confirmPasswordInput = screen.getByLabelText(/confirm password \*/i); + expect(passwordInput).toHaveValue(''); + expect(passwordInput).toHaveAttribute('type', 'password'); + expect(passwordInput).not.toHaveAttribute('aria-invalid', 'true'); + expect(confirmPasswordInput).toHaveValue(''); + expect(confirmPasswordInput).toHaveAttribute('type', 'password'); + expect(confirmPasswordInput).not.toHaveAttribute('aria-invalid', 'true'); + expect( + screen.queryByText('Passwords do not match'), + ).not.toBeInTheDocument(); + expect( + screen.getByRole('button', { name: /show password/i }), + ).toBeInTheDocument(); + expect( + screen.getByRole('button', { name: /show confirm password/i }), + ).toBeInTheDocument(); }); - it('resets all form state and sensitive data when closed and reopened', async () => { + it('resets all form state, visibility, and validation errors when closed and reopened', async () => { const user = userEvent.setup(); const { rerender } = renderDialog(true); await user.type(screen.getByLabelText(/album name/i), 'Secret Trip'); await user.click(screen.getByLabelText(/lock album/i)); await user.type(screen.getByLabelText(/^password \*/i), 'secret123'); - await user.type(screen.getByLabelText(/confirm password \*/i), 'secret123'); + await user.type( + screen.getByLabelText(/confirm password \*/i), + 'mismatchedpass', + ); + + // Show both passwords + await user.click(screen.getByRole('button', { name: /show password/i })); + await user.click( + screen.getByRole('button', { name: /show confirm password/i }), + ); + + // Trigger validation error + await user.click(screen.getByRole('button', { name: /create album/i })); + expect(screen.getByText('Passwords do not match')).toBeInTheDocument(); // Simulate closing externally rerender( @@ -235,7 +287,30 @@ describe('CreateAlbumDialog', () => { ); expect(screen.getByLabelText(/album name \*/i)).toHaveValue(''); + expect( + screen.queryByText('Passwords do not match'), + ).not.toBeInTheDocument(); expect(screen.queryByLabelText(/^password/i)).not.toBeInTheDocument(); + + // Enable locking and assert visibility and error state are reset + await user.click(screen.getByLabelText(/lock album/i)); + const passwordInput = screen.getByLabelText(/^password \*/i); + const confirmPasswordInput = screen.getByLabelText(/confirm password \*/i); + expect(passwordInput).toHaveValue(''); + expect(passwordInput).toHaveAttribute('type', 'password'); + expect(passwordInput).not.toHaveAttribute('aria-invalid', 'true'); + expect(confirmPasswordInput).toHaveValue(''); + expect(confirmPasswordInput).toHaveAttribute('type', 'password'); + expect(confirmPasswordInput).not.toHaveAttribute('aria-invalid', 'true'); + expect( + screen.queryByText('Passwords do not match'), + ).not.toBeInTheDocument(); + expect( + screen.getByRole('button', { name: /show password/i }), + ).toBeInTheDocument(); + expect( + screen.getByRole('button', { name: /show confirm password/i }), + ).toBeInTheDocument(); }); it('submits locked album successfully when passwords match', async () => { From 0488a987c6f6a9e5a72b43622da2ec94b2368e9b Mon Sep 17 00:00:00 2001 From: Surajshivam-123 Date: Wed, 19 Aug 2026 01:07:30 +0530 Subject: [PATCH 4/4] improve:Removed Unnecessary Comments --- frontend/src/components/Albums/CreateAlbumDialog.tsx | 1 - .../Albums/__tests__/CreateAlbumDialog.test.tsx | 12 ------------ 2 files changed, 13 deletions(-) diff --git a/frontend/src/components/Albums/CreateAlbumDialog.tsx b/frontend/src/components/Albums/CreateAlbumDialog.tsx index 6d9af2be3..0cb9a1b71 100644 --- a/frontend/src/components/Albums/CreateAlbumDialog.tsx +++ b/frontend/src/components/Albums/CreateAlbumDialog.tsx @@ -256,7 +256,6 @@ export const CreateAlbumDialog: React.FC = ({ )} - {/* Confirm Password Field */}