-
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 pull request #377 from COS301-SE-2024/feature/testing
Feature/testing
- Loading branch information
Showing
4 changed files
with
371 additions
and
0 deletions.
There are no files selected for viewing
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,63 @@ | ||
import React from 'react'; | ||
import { describe, expect } from "@jest/globals"; | ||
import { render, fireEvent, screen } from '@testing-library/react'; | ||
import '@testing-library/jest-dom'; | ||
import CompactDropdown from '@/components/Dropdown/CompactDropdown'; | ||
|
||
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 }), | ||
})), | ||
}), | ||
})); | ||
|
||
const mockOptions = { | ||
group: 'Test Group', | ||
items: [ | ||
{ value: 'option1', label: 'Option 1' }, | ||
{ value: 'option2', label: 'Option 2' }, | ||
], | ||
}; | ||
|
||
describe('CompactDropdown', () => { | ||
it('renders correctly', () => { | ||
const onChange = jest.fn(); | ||
render(<CompactDropdown options={mockOptions} value="" onChange={onChange} />); | ||
expect(screen.getByRole('combobox')).toBeInTheDocument(); | ||
}); | ||
|
||
it('displays the selected option', () => { | ||
const onChange = jest.fn(); | ||
render(<CompactDropdown options={mockOptions} value="option1" onChange={onChange} />); | ||
expect(screen.getByRole('combobox')).toHaveTextContent('Option 1'); | ||
}); | ||
|
||
// it('calls onChange when an option is selected', () => { | ||
// const onChange = jest.fn(); | ||
// render(<CompactDropdown options={mockOptions} value="" onChange={onChange} />); | ||
// fireEvent.click(screen.getByRole('combobox')); | ||
// fireEvent.click(screen.getByText('Option 2')); | ||
// expect(onChange).toHaveBeenCalledWith('option2'); | ||
// }); | ||
|
||
it('displays placeholder when no option is selected', () => { | ||
const onChange = jest.fn(); | ||
render(<CompactDropdown options={mockOptions} value="" onChange={onChange} placeholder="Select..." />); | ||
expect(screen.getByRole('combobox')).toHaveTextContent('Select...'); | ||
}); | ||
|
||
it('disables the dropdown when disabled prop is true', () => { | ||
const onChange = jest.fn(); | ||
render(<CompactDropdown options={mockOptions} value="" onChange={onChange} disabled={true} />); | ||
expect(screen.getByRole('combobox')).toBeDisabled(); | ||
}); | ||
}); |
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,76 @@ | ||
import React from 'react'; | ||
import { describe, expect } from "@jest/globals"; | ||
import { render, fireEvent, screen } from '@testing-library/react'; | ||
import '@testing-library/jest-dom'; | ||
import Dropdown from '@/components/Dropdown/Dropdown'; | ||
|
||
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 }), | ||
})), | ||
}), | ||
})); | ||
|
||
const mockOptions = { | ||
group: 'Test Group', | ||
items: [ | ||
{ value: 'option1', label: 'Option 1', emoji: '😀' }, | ||
{ value: 'option2', label: 'Option 2', emoji: '😎' }, | ||
], | ||
}; | ||
|
||
describe('Dropdown', () => { | ||
it('renders correctly', () => { | ||
const onChange = jest.fn(); | ||
render(<Dropdown options={mockOptions} value="" onChange={onChange} />); | ||
expect(screen.getByRole('combobox')).toBeInTheDocument(); | ||
}); | ||
|
||
it('displays the selected option', () => { | ||
const onChange = jest.fn(); | ||
render(<Dropdown options={mockOptions} value="option1" onChange={onChange} />); | ||
expect(screen.getByRole('combobox')).toHaveTextContent('Option 1'); | ||
}); | ||
|
||
// it('calls onChange when an option is selected', () => { | ||
// const onChange = jest.fn(); | ||
// render(<Dropdown options={mockOptions} value="" onChange={onChange} />); | ||
// fireEvent.click(screen.getByRole('combobox')); | ||
// fireEvent.click(screen.getByText('Option 2')); | ||
// expect(onChange).toHaveBeenCalledWith('option2'); | ||
// }); | ||
|
||
// it('renders search input when showSearch is true', () => { | ||
// const onChange = jest.fn(); | ||
// render(<Dropdown options={mockOptions} value="" onChange={onChange} showSearch={true} />); | ||
// fireEvent.click(screen.getByRole('combobox')); | ||
// expect(screen.getByPlaceholderText('Search options...')).toBeInTheDocument(); | ||
// }); | ||
|
||
it('renders compact version correctly', () => { | ||
const onChange = jest.fn(); | ||
render(<Dropdown options={mockOptions} value="" onChange={onChange} compact={true} />); | ||
expect(screen.getByText('Set Mood')).toBeInTheDocument(); | ||
}); | ||
|
||
it('displays emoji in compact mode', () => { | ||
const onChange = jest.fn(); | ||
render(<Dropdown options={mockOptions} value="option1" onChange={onChange} compact={true} />); | ||
expect(screen.getByText('😀')).toBeInTheDocument(); | ||
}); | ||
|
||
it('disables the dropdown when disabled prop is true', () => { | ||
const onChange = jest.fn(); | ||
render(<Dropdown options={mockOptions} value="" onChange={onChange} disabled={true} />); | ||
expect(screen.getByRole('combobox')).toBeDisabled(); | ||
}); | ||
}); |
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,84 @@ | ||
import React from 'react'; | ||
import { describe, expect } from "@jest/globals"; | ||
import { render, fireEvent, waitFor, screen } from '@testing-library/react'; | ||
import '@testing-library/jest-dom'; | ||
import FilterModal from '@/components/FilterModal/FilterModal'; | ||
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(() => ({ | ||
user: { id: '1', name: 'Test User' }, | ||
})), | ||
})); | ||
|
||
jest.mock('@/lib/api/dotVisualization', () => ({ | ||
dotVisualization: jest.fn(() => Promise.resolve([ | ||
{ location_id: 1, suburb: 'Test Suburb', city: 'Test City', province: 'Test Province' }, | ||
])), | ||
})); | ||
|
||
describe('FilterModal', () => { | ||
const queryClient = new QueryClient({ | ||
defaultOptions: { | ||
queries: { | ||
retry: false, | ||
}, | ||
}, | ||
}); | ||
|
||
const defaultProps = { | ||
sortBy: '', | ||
setSortBy: jest.fn(), | ||
filter: '', | ||
setFilter: jest.fn(), | ||
location: null, | ||
setLocation: jest.fn(), | ||
onClose: jest.fn(), | ||
}; | ||
|
||
const renderWithQueryClient = (ui: React.ReactElement) => { | ||
return render( | ||
<QueryClientProvider client={queryClient}> | ||
{ui} | ||
</QueryClientProvider> | ||
); | ||
}; | ||
|
||
it('renders correctly', () => { | ||
renderWithQueryClient(<FilterModal {...defaultProps} />); | ||
expect(screen.getByText('Filters')).toBeInTheDocument(); | ||
}); | ||
|
||
|
||
|
||
|
||
|
||
it('calls onClose when Apply Filters button is clicked', () => { | ||
renderWithQueryClient(<FilterModal {...defaultProps} />); | ||
fireEvent.click(screen.getByText('Apply Filters')); | ||
expect(defaultProps.onClose).toHaveBeenCalled(); | ||
}); | ||
|
||
it('renders location dropdown', async () => { | ||
renderWithQueryClient(<FilterModal {...defaultProps} />); | ||
expect(await screen.findByText('All Locations')).toBeInTheDocument(); | ||
}); | ||
|
||
|
||
}); |
148 changes: 148 additions & 0 deletions
148
frontend/__tests__/components/NotificationList.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,148 @@ | ||
import React from 'react'; | ||
import { render, screen, waitFor } from '@testing-library/react'; | ||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; | ||
import NotificationsList from '../../components/Notifications/NotificationsList'; | ||
import { useUser } from "@/lib/contexts/UserContext"; | ||
import { subscribe } from "@/lib/api/subscription"; | ||
import { ThemeProvider } from 'next-themes'; | ||
|
||
// Mock the modules | ||
jest.mock("@/lib/contexts/UserContext", () => ({ | ||
useUser: jest.fn(), | ||
})); | ||
|
||
jest.mock("@/lib/api/subscription", () => ({ | ||
subscribe: jest.fn(), | ||
})); | ||
|
||
jest.mock('next/link', () => { | ||
return ({ children, href }: { children: React.ReactNode; href: string }) => ( | ||
<a href={href}>{children}</a> | ||
); | ||
}); | ||
|
||
// Mock Supabase | ||
jest.mock('@supabase/supabase-js', () => ({ | ||
createClient: jest.fn(() => ({ | ||
auth: { | ||
getSession: jest.fn().mockResolvedValue({ data: { session: null }, error: null }), | ||
}, | ||
})), | ||
})); | ||
|
||
// Mock environment variables | ||
process.env.NEXT_PUBLIC_SUPABASE_URL = 'https://example.supabase.co'; | ||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY = 'example-anon-key'; | ||
|
||
const mockUseUser = useUser as jest.Mock; | ||
const mockSubscribe = subscribe as jest.Mock; | ||
|
||
describe('NotificationsList Component', () => { | ||
let queryClient: QueryClient; | ||
|
||
beforeEach(() => { | ||
queryClient = new QueryClient(); | ||
mockUseUser.mockReturnValue({ user: { user_id: 'test-user' } }); | ||
process.env.NEXT_PUBLIC_BACKEND_URL = 'http://test-backend.com'; | ||
}); | ||
|
||
const renderComponent = () => { | ||
return render( | ||
<QueryClientProvider client={queryClient}> | ||
<ThemeProvider> | ||
<NotificationsList /> | ||
</ThemeProvider> | ||
</QueryClientProvider> | ||
); | ||
}; | ||
|
||
// it('displays loading indicator when fetching data', async () => { | ||
// mockSubscribe.mockReturnValue(new Promise(() => {})); // Never resolves | ||
// renderComponent(); | ||
|
||
// expect(await screen.findByRole('status')).toBeInTheDocument(); | ||
// }); | ||
|
||
it('displays error message when no notifications are available', async () => { | ||
mockSubscribe.mockResolvedValue(null); | ||
renderComponent(); | ||
|
||
await waitFor(() => { | ||
expect(screen.getByText('No Notifications')).toBeInTheDocument(); | ||
expect(screen.getByText('You currently have no notifications. Once you receive notifications, they will appear here')).toBeInTheDocument(); | ||
}); | ||
}); | ||
|
||
it('renders notifications correctly', async () => { | ||
const mockNotifications = [ | ||
{ type: 'reaction', content: 'reacted', issue_id: '123', created_at: new Date().toISOString() }, | ||
{ type: 'comment', content: 'New comment', issue_id: '456', created_at: new Date().toISOString() }, | ||
{ type: 'points', content: 'You earned 10 points', created_at: new Date().toISOString() }, | ||
{ type: 'resolution', content: 'Issue resolved', issue_id: '789', created_at: new Date().toISOString() }, | ||
{ type: 'issue', content: 'New issue reported', issue_id: '101', created_at: new Date().toISOString() }, | ||
]; | ||
|
||
mockSubscribe.mockResolvedValue(mockNotifications); | ||
renderComponent(); | ||
|
||
await waitFor(() => { | ||
expect(screen.getByText('Someone reacted to an Issue you are involved in.')).toBeInTheDocument(); | ||
expect(screen.getByText('New comment on an issue you\'re following: "New comment"')).toBeInTheDocument(); | ||
expect(screen.getByText('You earned 10 points')).toBeInTheDocument(); | ||
expect(screen.getByText('New resolution logged: Issue resolved')).toBeInTheDocument(); | ||
expect(screen.getByText('New issue reported: New issue reported')).toBeInTheDocument(); | ||
}); | ||
}); | ||
|
||
it('handles suspension message for external resolution rejection', async () => { | ||
const mockNotifications = [ | ||
{ type: 'points', content: 'Your external resolution rejected', created_at: new Date().toISOString() }, | ||
]; | ||
|
||
mockSubscribe.mockResolvedValue(mockNotifications); | ||
renderComponent(); | ||
|
||
await waitFor(() => { | ||
expect(screen.getByText(/Your external resolution rejected/)).toBeInTheDocument(); | ||
expect(screen.getByText(/You will be suspended from resolving until/)).toBeInTheDocument(); | ||
}); | ||
}); | ||
|
||
// it('renders correct icons for different notification types', async () => { | ||
// const mockNotifications = [ | ||
// { type: 'reaction', content: 'reacted', issue_id: '123', created_at: new Date().toISOString() }, | ||
// { type: 'comment', content: 'New comment', issue_id: '456', created_at: new Date().toISOString() }, | ||
// { type: 'resolution', content: 'Issue resolved', issue_id: '789', created_at: new Date().toISOString() }, | ||
// { type: 'issue', content: 'New issue', issue_id: '101', created_at: new Date().toISOString() }, | ||
// { type: 'points', content: 'You earned 10 points', created_at: new Date().toISOString() }, | ||
// ]; | ||
|
||
// mockSubscribe.mockResolvedValue(mockNotifications); | ||
// renderComponent(); | ||
|
||
// await waitFor(() => { | ||
// expect(screen.getAllByRole('img', { hidden: true })).toHaveLength(5); | ||
// }); | ||
// }); | ||
|
||
it('renders correct links for different notification types', async () => { | ||
const mockNotifications = [ | ||
{ type: 'reaction', content: 'reacted', issue_id: '123', created_at: new Date().toISOString() }, | ||
{ type: 'comment', content: 'New comment', issue_id: '456', created_at: new Date().toISOString() }, | ||
{ type: 'resolution', content: 'Issue resolved', issue_id: '789', created_at: new Date().toISOString() }, | ||
{ type: 'issue', content: 'New issue', issue_id: '101', created_at: new Date().toISOString() }, | ||
{ type: 'points', content: 'You earned 10 points', created_at: new Date().toISOString() }, | ||
]; | ||
|
||
mockSubscribe.mockResolvedValue(mockNotifications); | ||
renderComponent(); | ||
|
||
await waitFor(() => { | ||
expect(screen.getByText('Someone reacted to an Issue you are involved in.').closest('a')).toHaveAttribute('href', '/issues/123'); | ||
expect(screen.getByText('New comment on an issue you\'re following: "New comment"').closest('a')).toHaveAttribute('href', '/issues/456'); | ||
expect(screen.getByText('New resolution logged: Issue resolved').closest('a')).toHaveAttribute('href', '/issues/789'); | ||
expect(screen.getByText('New issue reported: New issue').closest('a')).toHaveAttribute('href', '/issues/101'); | ||
expect(screen.getByText('You earned 10 points').closest('a')).toHaveAttribute('href', '/leaderboard'); | ||
}); | ||
}); | ||
}); |