Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions .github/workflows/pr-validation.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,8 @@ jobs:
- name: Build application
run: npm run build

# Future addition when tests are implemented
# - name: Run tests
# run: npm test
- name: Run tests
run: npm test

- name: Scan for vulnerabilities
run: npm audit --production
Expand Down
83 changes: 83 additions & 0 deletions __tests__/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Algorithm Visualizer Tests

This directory contains tests for the Algorithm Visualizer application. The tests are organized to mirror the structure of the source code, making it easy to locate tests for specific components or functionality.

## Directory Structure

```
__tests__/
├── app/ # Tests for page components
│ └── sorting/
│ └── [algorithm]/
│ └── page.test.tsx # Tests for algorithm page
├── components/ # Tests for React components
│ ├── AlgorithmCard.test.tsx
│ └── visualizer/
│ ├── SortingVisualization.test.tsx
│ └── VisualizerControls.test.tsx
├── context/ # Tests for React context
│ └── AlgorithmContext.test.tsx
├── lib/ # Tests for utility functions and algorithms
│ ├── algorithms/
│ │ └── bubbleSort.test.ts
│ └── utils.test.ts
└── utils/ # Test utilities
└── test-utils.tsx # Common test utilities and helpers
```

## Running Tests

You can run the tests using the following npm scripts:

```bash
# Run all tests
npm test

# Run tests in watch mode (useful during development)
npm run test:watch

# Run tests with coverage report
npm run test:coverage
```

## Testing Approach

1. **Unit Tests**: Test individual functions and components in isolation.
2. **Integration Tests**: Test interactions between multiple components.
3. **Mock Dependencies**: External dependencies are mocked to ensure tests are reliable and fast.

## Test Utilities

The `test-utils.tsx` file provides:

- A custom render function that includes the AlgorithmProvider
- Mock implementations for key dependencies
- Helper functions for test setup

## Coverage Reports

After running `npm run test:coverage`, a coverage report will be generated in the `coverage/` directory. This report shows how much of the code is covered by tests and helps identify areas that need more testing.

## Writing New Tests

When adding new features or components, please follow these guidelines for writing tests:

1. Create test files that mirror the structure of the source code.
2. Test both happy paths and edge cases.
3. Mock external dependencies.
4. Keep tests focused on specific behavior.
5. Use descriptive test names that explain what is being tested.

Example:

```typescript
describe('ComponentName', () => {
it('should render correctly with default props', () => {
// Test code here
});

it('should handle edge case X properly', () => {
// Test code here
});
});
```
139 changes: 139 additions & 0 deletions __tests__/app/sorting/[algorithm]/page.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import React from "react";
import { render, screen } from "@testing-library/react";
import AlgorithmPage from "@/app/sorting/[algorithm]/page";
import { AlgorithmProvider } from "@/context/AlgorithmContext";
import { getAlgorithmByName } from "@/lib/algorithms";

// Mock the next/navigation module
jest.mock("next/navigation", () => ({
useParams: jest.fn(() => ({ algorithm: "bubbleSort" })),
notFound: jest.fn(),
}));

// Mock the algorithms module
jest.mock("@/lib/algorithms", () => ({
getAlgorithmByName: jest.fn().mockReturnValue(() => ({
steps: [
{ array: [5, 3, 8], comparing: [], swapped: false, completed: [] },
{ array: [3, 5, 8], comparing: [], swapped: false, completed: [2] },
],
name: "Bubble Sort",
key: "bubbleSort",
category: "sorting",
description: "A simple sorting algorithm",
timeComplexity: "O(n²)",
spaceComplexity: "O(1)",
reference: "https://en.wikipedia.org/wiki/Bubble_sort",
pseudoCode: ["procedure bubbleSort(A: list of sortable items)"],
})),
availableAlgorithms: [
{
name: "Bubble Sort",
key: "bubbleSort",
category: "sorting",
description: "A simple sorting algorithm",
difficulty: "easy",
},
],
}));

// Mock the PageLayout component
jest.mock("@/components/layout/PageLayout", () => {
return ({ children, title, subtitle }: any) => (
<div data-testid="page-layout">
<h1 data-testid="page-title">{title}</h1>
<p data-testid="page-subtitle">{subtitle}</p>
<div data-testid="page-content">{children}</div>
</div>
);
});

// Mock the AlgorithmVisualizer component
jest.mock("@/components/visualizer/AlgorithmVisualizer", () => {
return () => (
<div data-testid="algorithm-visualizer">Algorithm Visualizer Mock</div>
);
});

describe("AlgorithmPage", () => {
beforeEach(() => {
jest.clearAllMocks();
});

it("should render the algorithm page with correct title and description", () => {
render(
<AlgorithmProvider>
<AlgorithmPage />
</AlgorithmProvider>
);

expect(screen.getByTestId("page-title")).toHaveTextContent("Bubble Sort");

Check failure on line 70 in __tests__/app/sorting/[algorithm]/page.test.tsx

View workflow job for this annotation

GitHub Actions / Validate PR

Property 'toHaveTextContent' does not exist on type 'JestMatchers<HTMLElement>'.
expect(screen.getByTestId("page-subtitle")).toHaveTextContent(

Check failure on line 71 in __tests__/app/sorting/[algorithm]/page.test.tsx

View workflow job for this annotation

GitHub Actions / Validate PR

Property 'toHaveTextContent' does not exist on type 'JestMatchers<HTMLElement>'.
"A simple sorting algorithm"
);
});

it("should render the AlgorithmVisualizer component", () => {
render(
<AlgorithmProvider>
<AlgorithmPage />
</AlgorithmProvider>
);

expect(screen.getByTestId("algorithm-visualizer")).toBeInTheDocument();

Check failure on line 83 in __tests__/app/sorting/[algorithm]/page.test.tsx

View workflow job for this annotation

GitHub Actions / Validate PR

Property 'toBeInTheDocument' does not exist on type 'JestMatchers<HTMLElement>'.
});

it("should render algorithm not found message for invalid algorithm", () => {
// Mock the useParams to return an invalid algorithm
require("next/navigation").useParams.mockReturnValue({
algorithm: "invalidAlgorithm",
});

// Mock the getAlgorithmByName to return null for invalid algorithm
require("@/lib/algorithms").getAlgorithmByName.mockReturnValue(null);

// Mock the availableAlgorithms to not include the invalid algorithm
require("@/lib/algorithms").availableAlgorithms = [];

render(
<AlgorithmProvider>
<AlgorithmPage />
</AlgorithmProvider>
);

// Use queryAllByText to handle multiple matches and check the first occurrence
const notFoundElements = screen.queryAllByText("Algorithm Not Found");
expect(notFoundElements.length).toBeGreaterThan(0);
expect(notFoundElements[0]).toBeInTheDocument();

Check failure on line 107 in __tests__/app/sorting/[algorithm]/page.test.tsx

View workflow job for this annotation

GitHub Actions / Validate PR

Property 'toBeInTheDocument' does not exist on type 'JestMatchers<HTMLElement>'.
});

it("should call getAlgorithmByName with the correct algorithm key", () => {
// Reset the useParams mock to return bubbleSort
require("next/navigation").useParams.mockReturnValue({
algorithm: "bubbleSort",
});

// Reset algorithm mock data
require("@/lib/algorithms").availableAlgorithms = [
{
name: "Bubble Sort",
key: "bubbleSort",
category: "sorting",
description: "A simple sorting algorithm",
difficulty: "easy",
},
];

// Clear previous calls
getAlgorithmByName.mockClear();

Check failure on line 128 in __tests__/app/sorting/[algorithm]/page.test.tsx

View workflow job for this annotation

GitHub Actions / Validate PR

Property 'mockClear' does not exist on type '(name: string) => ((array: number[]) => AlgorithmVisualization) | null'.

render(
<AlgorithmProvider>
<AlgorithmPage />
</AlgorithmProvider>
);

// Check that getAlgorithmByName was called with the right algorithm key
expect(getAlgorithmByName).toHaveBeenCalledWith("bubbleSort");
});
});
58 changes: 58 additions & 0 deletions __tests__/components/AlgorithmCard.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// __tests__/components/AlgorithmCard.test.tsx
import React from "react";
import { render, screen } from "@testing-library/react";
import AlgorithmCard from "@/components/AlgorithmCard";
import { AlgorithmInfo } from "@/lib/types";

// Mock the Next.js Link component
jest.mock("next/link", () => {
return ({ children, href }: { children: React.ReactNode; href: string }) => {
return <a href={href}>{children}</a>;
};
});

describe("AlgorithmCard", () => {
const mockAlgorithm: AlgorithmInfo = {
name: "Bubble Sort",
key: "bubbleSort",
category: "sorting",
description:
"A simple sorting algorithm that repeatedly steps through the list.",
difficulty: "easy",
};

it("should render the algorithm name", () => {
render(<AlgorithmCard algorithm={mockAlgorithm} />);
expect(screen.getByText("Bubble Sort")).toBeInTheDocument();

Check failure on line 26 in __tests__/components/AlgorithmCard.test.tsx

View workflow job for this annotation

GitHub Actions / Validate PR

Property 'toBeInTheDocument' does not exist on type 'JestMatchers<HTMLElement>'.
});

it("should render the algorithm description", () => {
render(<AlgorithmCard algorithm={mockAlgorithm} />);
expect(
screen.getByText(
"A simple sorting algorithm that repeatedly steps through the list."
)
).toBeInTheDocument();

Check failure on line 35 in __tests__/components/AlgorithmCard.test.tsx

View workflow job for this annotation

GitHub Actions / Validate PR

Property 'toBeInTheDocument' does not exist on type 'JestMatchers<HTMLElement>'.
});

it("should contain a link to the algorithm visualization page", () => {
render(<AlgorithmCard algorithm={mockAlgorithm} />);
const visualizeLink = screen.getByText("Visualize");
expect(visualizeLink.closest("a")).toHaveAttribute(

Check failure on line 41 in __tests__/components/AlgorithmCard.test.tsx

View workflow job for this annotation

GitHub Actions / Validate PR

Property 'toHaveAttribute' does not exist on type 'JestMatchers<HTMLAnchorElement | null>'.
"href",
"/sorting/bubbleSort"
);
});

it("should contain a link to the difficulty page", () => {
render(<AlgorithmCard algorithm={mockAlgorithm} />);
const difficultyLink = screen.getByText("easy");
expect(difficultyLink.closest("a")).toHaveAttribute("href", "/easy");

Check failure on line 50 in __tests__/components/AlgorithmCard.test.tsx

View workflow job for this annotation

GitHub Actions / Validate PR

Property 'toHaveAttribute' does not exist on type 'JestMatchers<HTMLAnchorElement | null>'.
});

it("should contain a link to the category page", () => {
render(<AlgorithmCard algorithm={mockAlgorithm} />);
const categoryLink = screen.getByText("sorting");
expect(categoryLink.closest("a")).toHaveAttribute("href", "/sorting");

Check failure on line 56 in __tests__/components/AlgorithmCard.test.tsx

View workflow job for this annotation

GitHub Actions / Validate PR

Property 'toHaveAttribute' does not exist on type 'JestMatchers<HTMLAnchorElement | null>'.
});
});
Loading
Loading