-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'develop' into feature/resolutons_fix
- Loading branch information
Showing
11 changed files
with
484 additions
and
24 deletions.
There are no files selected for viewing
154 changes: 154 additions & 0 deletions
154
frontend/__tests__/components/ChangePasswordForm.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
import React from "react"; | ||
import { describe, expect } from "@jest/globals"; | ||
import { render, fireEvent, waitFor, screen } from "@testing-library/react"; | ||
import ChangePasswordForm from "@/components/ChangePasswordForm/ChangePasswordForm"; | ||
import { useUser } from "@/lib/contexts/UserContext"; | ||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; | ||
|
||
jest.mock("@supabase/supabase-js", () => ({ | ||
createClient: jest.fn().mockReturnValue({ | ||
auth: { | ||
signIn: jest.fn().mockResolvedValue({ | ||
user: { id: "user-id" }, | ||
session: "session-token", | ||
error: null, | ||
}), | ||
}, | ||
from: jest.fn(() => ({ | ||
select: jest.fn().mockResolvedValue({ data: [], error: null }), | ||
insert: jest.fn().mockResolvedValue({ data: [], error: null }), | ||
})), | ||
}), | ||
})); | ||
|
||
jest.mock("@/lib/contexts/UserContext", () => ({ | ||
useUser: jest.fn(), | ||
})); | ||
|
||
jest.mock("@/lib/api/updateProfile", () => ({ | ||
changePassword: jest.fn(), | ||
})); | ||
|
||
const renderWithClient = (ui: React.ReactNode) => { | ||
const testQueryClient = new QueryClient({ | ||
defaultOptions: { | ||
queries: { | ||
retry: false, | ||
}, | ||
}, | ||
}); | ||
return render( | ||
<QueryClientProvider client={testQueryClient}>{ui}</QueryClientProvider> | ||
); | ||
}; | ||
|
||
describe("ChangePasswordForm", () => { | ||
beforeEach(() => { | ||
jest.clearAllMocks(); | ||
jest.spyOn(console, "error").mockImplementation(() => {}); | ||
(useUser as jest.Mock).mockReturnValue({ | ||
user: { | ||
user_id: "user123", | ||
email_address: "user@example.com", | ||
username: "user123", | ||
fullname: "User Fullname", | ||
image_url: "http://example.com/image.jpg", | ||
bio: "User biography", | ||
is_owner: true, | ||
total_issues: 10, | ||
resolved_issues: 5, | ||
access_token: "access_token_value", | ||
}, | ||
}); | ||
}); | ||
|
||
afterEach(() => { | ||
(console.error as jest.Mock).mockRestore(); | ||
}); | ||
|
||
it("renders the form correctly", () => { | ||
renderWithClient(<ChangePasswordForm />); | ||
|
||
expect(screen.getByRole("heading", { name: "Change Password" })).toBeInTheDocument(); | ||
expect(screen.getByLabelText("Current Password")).toBeInTheDocument(); | ||
expect(screen.getByLabelText("New Password")).toBeInTheDocument(); | ||
expect(screen.getByLabelText("Confirm New Password")).toBeInTheDocument(); | ||
expect(screen.getByRole("button", { name: "Change Password" })).toBeInTheDocument(); | ||
}); | ||
|
||
it("displays an error when passwords don't match", async () => { | ||
renderWithClient(<ChangePasswordForm />); | ||
|
||
fireEvent.change(screen.getByLabelText("Current Password"), { target: { value: "oldpass" } }); | ||
fireEvent.change(screen.getByLabelText("New Password"), { target: { value: "newpass" } }); | ||
fireEvent.change(screen.getByLabelText("Confirm New Password"), { target: { value: "differentpass" } }); | ||
|
||
fireEvent.click(screen.getByRole("button", { name: "Change Password" })); | ||
|
||
await waitFor(() => { | ||
expect(screen.getByText("New passwords do not match.")).toBeInTheDocument(); | ||
}); | ||
}); | ||
|
||
it("displays an error when new password is too short", async () => { | ||
renderWithClient(<ChangePasswordForm />); | ||
|
||
fireEvent.change(screen.getByLabelText("Current Password"), { target: { value: "oldpass" } }); | ||
fireEvent.change(screen.getByLabelText("New Password"), { target: { value: "short" } }); | ||
fireEvent.change(screen.getByLabelText("Confirm New Password"), { target: { value: "short" } }); | ||
|
||
fireEvent.click(screen.getByRole("button", { name: "Change Password" })); | ||
|
||
await waitFor(() => { | ||
expect(screen.getByText("Password must be at least 8 characters long.")).toBeInTheDocument(); | ||
}); | ||
}); | ||
|
||
it("calls changePassword function when form is submitted correctly", async () => { | ||
const { changePassword } = require("@/lib/api/updateProfile"); | ||
changePassword.mockResolvedValue({}); | ||
renderWithClient(<ChangePasswordForm />); | ||
|
||
fireEvent.change(screen.getByLabelText("Current Password"), { target: { value: "oldpass" } }); | ||
fireEvent.change(screen.getByLabelText("New Password"), { target: { value: "newpassword" } }); | ||
fireEvent.change(screen.getByLabelText("Confirm New Password"), { target: { value: "newpassword" } }); | ||
|
||
fireEvent.click(screen.getByRole("button", { name: "Change Password" })); | ||
|
||
await waitFor(() => { | ||
expect(changePassword).toHaveBeenCalledWith({ currentPassword: "oldpass", newPassword: "newpassword" }); | ||
}); | ||
}); | ||
|
||
it("displays success message when password is changed successfully", async () => { | ||
const { changePassword } = require("@/lib/api/updateProfile"); | ||
changePassword.mockResolvedValue({}); | ||
renderWithClient(<ChangePasswordForm />); | ||
|
||
fireEvent.change(screen.getByLabelText("Current Password"), { target: { value: "oldpass" } }); | ||
fireEvent.change(screen.getByLabelText("New Password"), { target: { value: "newpassword" } }); | ||
fireEvent.change(screen.getByLabelText("Confirm New Password"), { target: { value: "newpassword" } }); | ||
|
||
fireEvent.click(screen.getByRole("button", { name: "Change Password" })); | ||
|
||
await waitFor(() => { | ||
expect(screen.getByText("Password changed successfully! You will need to log in again.")).toBeInTheDocument(); | ||
}); | ||
}); | ||
|
||
it("displays error message when changePassword function fails", async () => { | ||
const { changePassword } = require("@/lib/api/updateProfile"); | ||
changePassword.mockRejectedValue({ message: "Current password is incorrect" }); | ||
renderWithClient(<ChangePasswordForm />); | ||
|
||
fireEvent.change(screen.getByLabelText("Current Password"), { target: { value: "wrongpass" } }); | ||
fireEvent.change(screen.getByLabelText("New Password"), { target: { value: "newpassword" } }); | ||
fireEvent.change(screen.getByLabelText("Confirm New Password"), { target: { value: "newpassword" } }); | ||
|
||
fireEvent.click(screen.getByRole("button", { name: "Change Password" })); | ||
|
||
await waitFor(() => { | ||
expect(screen.getByText("Current password is incorrect")).toBeInTheDocument(); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
55 changes: 55 additions & 0 deletions
55
frontend/__tests__/components/deleteConfirmation/DeleteConfirmation.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import React from 'react'; | ||
import { render, screen, fireEvent } from '@testing-library/react'; | ||
import DeleteConfirmation from '@/components/DeleteConfirmation/DeleteConfirmation'; | ||
|
||
describe('DeleteConfirmation', () => { | ||
const mockOnConfirm = jest.fn(); | ||
const mockOnCancel = jest.fn(); | ||
|
||
beforeEach(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
it('renders correctly', () => { | ||
render( | ||
<DeleteConfirmation | ||
organizationName="Test Org" | ||
onConfirm={mockOnConfirm} | ||
onCancel={mockOnCancel} | ||
/> | ||
); | ||
|
||
expect(screen.getByText('Delete Organization')).toBeInTheDocument(); | ||
expect(screen.getByText('Are you sure you want to delete Test Org? This action cannot be undone.')).toBeInTheDocument(); | ||
expect(screen.getByRole('button', { name: 'Cancel' })).toBeInTheDocument(); | ||
expect(screen.getByRole('button', { name: 'Delete' })).toBeInTheDocument(); | ||
}); | ||
|
||
it('calls onCancel when Cancel button is clicked', () => { | ||
render( | ||
<DeleteConfirmation | ||
organizationName="Test Org" | ||
onConfirm={mockOnConfirm} | ||
onCancel={mockOnCancel} | ||
/> | ||
); | ||
|
||
fireEvent.click(screen.getByRole('button', { name: 'Cancel' })); | ||
expect(mockOnCancel).toHaveBeenCalledTimes(1); | ||
expect(mockOnConfirm).not.toHaveBeenCalled(); | ||
}); | ||
|
||
it('calls onConfirm when Delete button is clicked', () => { | ||
render( | ||
<DeleteConfirmation | ||
organizationName="Test Org" | ||
onConfirm={mockOnConfirm} | ||
onCancel={mockOnCancel} | ||
/> | ||
); | ||
|
||
fireEvent.click(screen.getByRole('button', { name: 'Delete' })); | ||
expect(mockOnConfirm).toHaveBeenCalledTimes(1); | ||
expect(mockOnCancel).not.toHaveBeenCalled(); | ||
}); | ||
}); |
112 changes: 112 additions & 0 deletions
112
frontend/__tests__/components/deleteConfirmation/TypedDeleteConfirmation.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
import React from 'react'; | ||
import { render, screen, fireEvent } from '@testing-library/react'; | ||
import userEvent from '@testing-library/user-event'; | ||
import TypedDeleteConfirmation from '@/components/DeleteConfirmation/TypedDeleteConfirmation'; | ||
|
||
// Mock the UI components | ||
jest.mock("@/components/ui/input", () => ({ | ||
Input: (props: any) => <input {...props} />, | ||
})); | ||
|
||
jest.mock("@/components/ui/button", () => ({ | ||
Button: (props: any) => <button {...props}>{props.children}</button>, | ||
})); | ||
|
||
jest.mock("@/components/ui/alert-dialog", () => ({ | ||
AlertDialog: ({ children, open }: any) => open ? <div>{children}</div> : null, | ||
AlertDialogContent: ({ children }: any) => <div>{children}</div>, | ||
AlertDialogHeader: ({ children }: any) => <div>{children}</div>, | ||
AlertDialogTitle: ({ children }: any) => <h2>{children}</h2>, | ||
AlertDialogDescription: ({ children }: any) => <p>{children}</p>, | ||
AlertDialogFooter: ({ children }: any) => <div>{children}</div>, | ||
})); | ||
|
||
describe('TypedDeleteConfirmation', () => { | ||
const mockOnConfirm = jest.fn(); | ||
const mockOnClose = jest.fn(); | ||
|
||
const defaultProps = { | ||
isOpen: true, | ||
onClose: mockOnClose, | ||
onConfirm: mockOnConfirm, | ||
itemName: 'Test Item', | ||
itemType: 'Organization', | ||
}; | ||
|
||
beforeEach(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
it('renders correctly when open', () => { | ||
render(<TypedDeleteConfirmation {...defaultProps} />); | ||
|
||
expect(screen.getByText('Delete Organization')).toBeInTheDocument(); | ||
expect(screen.getByText(/This action cannot be undone/)).toBeInTheDocument(); | ||
expect(screen.getByPlaceholderText('delete Test Item')).toBeInTheDocument(); | ||
expect(screen.getByRole('button', { name: 'Cancel' })).toBeInTheDocument(); | ||
expect(screen.getByRole('button', { name: 'Delete' })).toBeInTheDocument(); | ||
}); | ||
|
||
it('does not render when closed', () => { | ||
render(<TypedDeleteConfirmation {...defaultProps} isOpen={false} />); | ||
|
||
expect(screen.queryByText('Delete Organization')).not.toBeInTheDocument(); | ||
}); | ||
|
||
it('disables Delete button when input is incorrect', async () => { | ||
render(<TypedDeleteConfirmation {...defaultProps} />); | ||
|
||
const deleteButton = screen.getByRole('button', { name: 'Delete' }); | ||
expect(deleteButton).toBeDisabled(); | ||
|
||
await userEvent.type(screen.getByPlaceholderText('delete Test Item'), 'incorrect input'); | ||
expect(deleteButton).toBeDisabled(); | ||
}); | ||
|
||
it('enables Delete button when input is correct', async () => { | ||
render(<TypedDeleteConfirmation {...defaultProps} />); | ||
|
||
const deleteButton = screen.getByRole('button', { name: 'Delete' }); | ||
expect(deleteButton).toBeDisabled(); | ||
|
||
await userEvent.type(screen.getByPlaceholderText('delete Test Item'), 'delete Test Item'); | ||
expect(deleteButton).toBeEnabled(); | ||
}); | ||
|
||
it('calls onClose when Cancel button is clicked', () => { | ||
render(<TypedDeleteConfirmation {...defaultProps} />); | ||
|
||
fireEvent.click(screen.getByRole('button', { name: 'Cancel' })); | ||
expect(mockOnClose).toHaveBeenCalledTimes(1); | ||
expect(mockOnConfirm).not.toHaveBeenCalled(); | ||
}); | ||
|
||
it('calls onConfirm when Delete button is clicked with correct input', async () => { | ||
render(<TypedDeleteConfirmation {...defaultProps} />); | ||
|
||
await userEvent.type(screen.getByPlaceholderText('delete Test Item'), 'delete Test Item'); | ||
fireEvent.click(screen.getByRole('button', { name: 'Delete' })); | ||
expect(mockOnConfirm).toHaveBeenCalledTimes(1); | ||
expect(mockOnClose).not.toHaveBeenCalled(); | ||
}); | ||
|
||
it('is case-insensitive for input matching', async () => { | ||
render(<TypedDeleteConfirmation {...defaultProps} />); | ||
|
||
await userEvent.type(screen.getByPlaceholderText('delete Test Item'), 'DELETE TEST ITEM'); | ||
expect(screen.getByRole('button', { name: 'Delete' })).toBeEnabled(); | ||
}); | ||
|
||
it('resets input when closed and reopened', () => { | ||
const { rerender } = render(<TypedDeleteConfirmation {...defaultProps} />); | ||
|
||
const input = screen.getByPlaceholderText('delete Test Item'); | ||
fireEvent.change(input, { target: { value: 'delete Test Item' } }); | ||
expect(input).toHaveValue('delete Test Item'); | ||
|
||
rerender(<TypedDeleteConfirmation {...defaultProps} isOpen={false} />); | ||
rerender(<TypedDeleteConfirmation {...defaultProps} isOpen={true} />); | ||
|
||
|
||
}); | ||
}); |
Oops, something went wrong.