Skip to content

Commit

Permalink
Merge pull request #377 from COS301-SE-2024/feature/testing
Browse files Browse the repository at this point in the history
Feature/testing
  • Loading branch information
ShamaKamina authored Oct 1, 2024
2 parents 727e460 + 6ff9a26 commit a68ab15
Show file tree
Hide file tree
Showing 4 changed files with 371 additions and 0 deletions.
63 changes: 63 additions & 0 deletions frontend/__tests__/components/CompactDowpdown.test.tsx
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();
});
});
76 changes: 76 additions & 0 deletions frontend/__tests__/components/Dropdowsn.test.tsx
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();
});
});
84 changes: 84 additions & 0 deletions frontend/__tests__/components/FilterModal.test.tsx
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 frontend/__tests__/components/NotificationList.test.tsx
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');
});
});
});

0 comments on commit a68ab15

Please sign in to comment.