Skip to content

Commit

Permalink
created ci/cd workflow folder with basic pipeline; logout flow WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
bduran04 committed Sep 16, 2024
1 parent 0b0179a commit 954009a
Show file tree
Hide file tree
Showing 8 changed files with 287 additions and 139 deletions.
40 changes: 40 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: CI/CD Pipeline

on:
push:
branches:
- main
pull_request:
branches:
- main

jobs:
build:
runs-on: ubuntu-latest

strategy:
matrix:
node-version: [16.x, 18.x]

steps:
- name: Checkout repository
uses: actions/checkout@v3

- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}

- name: Install dependencies
run: npm install

- name: Run tests
run: npm run test

- name: Build Next.js app
run: npm run build

- name: Deploy to Vercel
run: vercel --prod
env:
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
90 changes: 90 additions & 0 deletions app/__tests__/Dashboard.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// app/dashboard/Dashboard.test.tsx
import React from 'react';
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
import DashboardPage from '../dashboard/page';
import DashboardClient from '../dashboard/DashboardClient';
import { cookies } from 'next/headers';
import { redirect } from 'next/navigation';

// Mock next/headers and next/navigation
jest.mock('next/headers', () => ({
cookies: jest.fn(),
}));

jest.mock('next/navigation', () => ({
redirect: jest.fn(),
}));

// Sample user data for the tests
const mockUserData = {
id: '1',
email: '[email protected]',
fullName: 'Test User',
lastName: 'User',
};

describe('DashboardPage (Server Component)', () => {
it('should redirect if no user data cookie is found', () => {
// Mock cookies to return no data
(cookies as jest.Mock).mockReturnValue({
get: jest.fn().mockReturnValue(undefined),
});

const { container } = render(<DashboardPage />);

// Ensure redirect is called
expect(redirect).toHaveBeenCalledWith('/');
// Ensure the component doesn't render anything (returns null)
expect(container.firstChild).toBeNull();
});

it('should render DashboardClient when user data is found in cookies', () => {
// Mock cookies to return user data
(cookies as jest.Mock).mockReturnValue({
get: jest.fn().mockReturnValue({
value: JSON.stringify(mockUserData),
}),
});

render(<DashboardPage />);

// Ensure DashboardClient is rendered
const greeting = screen.getByText(`Hello ${mockUserData.fullName}!`);
expect(greeting).toBeInTheDocument();
});
});

describe('DashboardClient (Client Component)', () => {
it('renders the user information and ExampleEquations component', () => {
render(<DashboardClient userData={mockUserData} />);

// Check if the user's name is displayed
const greeting = screen.getByText(`Hello ${mockUserData.fullName}!`);
expect(greeting).toBeInTheDocument();

// Instead of matching "ExampleEquations", look for the actual content it renders, like buttons or headers
const exampleEquationText = screen.getByText(/Select an Example/i); // Assuming ExampleEquations renders this text
expect(exampleEquationText).toBeInTheDocument();

// Alternatively, if ExampleEquations renders buttons, use getByRole:
const firstExampleButton = screen.getByRole('button', { name: /x \+ 1 = 2/i });
expect(firstExampleButton).toBeInTheDocument();
});

it('should render correctly when userData is provided', () => {
const mockUserData = {
id: '1',
email: '[email protected]',
fullName: 'Test User',
lastName: 'User'
};

render(<DashboardClient userData={mockUserData} />);

const greeting = screen.getByText(/Hello Test User!/i);
expect(greeting).toBeInTheDocument();
});

});

107 changes: 51 additions & 56 deletions app/__tests__/calcbar.test.tsx
Original file line number Diff line number Diff line change
@@ -1,84 +1,79 @@
import React from 'react';
import { render, fireEvent, screen } from '@testing-library/react';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import '@testing-library/jest-dom';
import Calcbar from '../components/Calcbar';
import mathsteps from 'mathsteps';

jest.mock('mathsteps');
// Type the mock function
jest.mock('mathsteps', () => ({
solveEquation: jest.fn(),
}));

describe('Calcbar', () => {
const mockUserId = 'test-user-id';
// Define the type for steps
interface Step {
newEquation: { ascii: () => string };
changeType: string;
}

describe('Calcbar Component', () => {
beforeEach(() => {
jest.clearAllMocks();
});

it('renders the Calcbar component', () => {
render(<Calcbar userId={''} />);
test('renders correctly with default props', () => {
render(<Calcbar userId="123" />);

expect(screen.getByLabelText(/Enter an algebraic equation/i)).toBeInTheDocument();
expect(screen.getByText(/Solve/i)).toBeInTheDocument();
});

it('displays the solution when a valid equation is solved', () => {
const stepsMock = [
{ newEquation: { ascii: () => 'x = 1' }, changeType: 'SOLVE_EQUATION' },
test('renders example input and solves equation on load', async () => {
const mockSteps: Step[] = [
{ newEquation: { ascii: () => 'x = 2' }, changeType: 'simplify' },
{ newEquation: { ascii: () => 'x = 3' }, changeType: 'simplify' },
];
(mathsteps.solveEquation as jest.Mock).mockReturnValue(stepsMock);

render(<Calcbar userId={''} />);

fireEvent.change(screen.getByLabelText(/Enter an algebraic equation/i), { target: { value: 'x + 1 = 2' } });
fireEvent.click(screen.getByText(/Solve/i));

expect(screen.getByText(/Solution: x = 1/i)).toBeInTheDocument();
(mathsteps.solveEquation as jest.Mock).mockReturnValue(mockSteps);

render(<Calcbar exampleInput="x + 2 = 4" userId="123" />);

// Check that the input field has the example input value
expect(screen.getByLabelText(/Enter an algebraic equation/i)).toHaveValue('x + 2 = 4');

// Wait for the solution to appear
await waitFor(() => expect(screen.getByText(/Solution: x = 3/i)).toBeInTheDocument());

// Check steps
expect(screen.getByText(/Step 1/i)).toBeInTheDocument();
expect(screen.getByText(/simplify/i)).toBeInTheDocument();
});

it('displays "Invalid equation" when an invalid equation is entered', () => {
(mathsteps.solveEquation as jest.Mock).mockImplementation(() => { throw new Error('Invalid equation'); });

render(<Calcbar userId={''} />);
test('handles invalid equation', async () => {
(mathsteps.solveEquation as jest.Mock).mockImplementation(() => { throw new Error(); });

render(<Calcbar userId="123" />);

fireEvent.change(screen.getByLabelText(/Enter an algebraic equation/i), { target: { value: 'invalid equation' } });
fireEvent.click(screen.getByText(/Solve/i));

expect(screen.getByText(/Solution: Invalid equation/i)).toBeInTheDocument();

// Wait for the solution to update
await waitFor(() => expect(screen.getByText(/Invalid equation/i)).toBeInTheDocument());
});

it('displays steps when a valid equation is solved', () => {
const stepsMock = [
{ newEquation: { ascii: () => 'x + 1 = 2' }, changeType: 'ADD_CONSTANT' },
{ newEquation: { ascii: () => 'x = 1' }, changeType: 'SOLVE_EQUATION' },
];
(mathsteps.solveEquation as jest.Mock).mockReturnValue(stepsMock);

render(<Calcbar userId={''} />);
test('calls handleSolve on input change and button click', async () => {
const mockSteps: Step[] = [{ newEquation: { ascii: () => 'x = 2' }, changeType: 'simplify' }];
(mathsteps.solveEquation as jest.Mock).mockReturnValue(mockSteps);

fireEvent.change(screen.getByLabelText(/Enter an algebraic equation/i), { target: { value: 'x + 1 = 2' } });
render(<Calcbar userId="123" />);

fireEvent.change(screen.getByLabelText(/Enter an algebraic equation/i), { target: { value: 'x + 2 = 4' } });
fireEvent.click(screen.getByText(/Solve/i));

expect(screen.getByText(/Steps/i)).toBeInTheDocument();
expect(screen.getByText(/Step 1/i)).toBeInTheDocument();
expect(screen.getAllByText(/x \+ 1 = 2/i)).toHaveLength(1);
expect(screen.getByText(/add constant/i)).toBeInTheDocument();
expect(screen.getByText(/Step 2/i)).toBeInTheDocument();
expect(screen.getAllByText(/x = 1/i)).toHaveLength(2);
expect(screen.getByText(/solve equation/i)).toBeInTheDocument();

await waitFor(() => expect(screen.getByText(/Solution: x = 2/i)).toBeInTheDocument());
});

it('opens the Add to Study Guide modal when AddButton is clicked', () => {
const stepsMock = [
{ newEquation: { ascii: () => 'x = 1' }, changeType: 'SOLVE_EQUATION' },
];
(mathsteps.solveEquation as jest.Mock).mockReturnValue(stepsMock);

render(<Calcbar userId={mockUserId} />);

fireEvent.change(screen.getByLabelText(/Enter an algebraic equation/i), { target: { value: 'x + 1 = 2' } });
fireEvent.click(screen.getByText(/Solve/i));
fireEvent.click(screen.getByText(/Add to Study Guide/i));

expect(screen.getByText(/Add to Study Guide/i)).toBeInTheDocument();
expect(screen.getByLabelText(/New Title/i)).toBeInTheDocument();
expect(screen.getByLabelText(/Existing Title/i)).toBeInTheDocument();
test('does not render AddButton component', async () => {
render(<Calcbar userId="123" exampleInput="x + 2 = 4" />);

// Ensure AddButton is not rendered
expect(screen.queryByText(/AddButton Mock/i)).not.toBeInTheDocument();
});
});
29 changes: 14 additions & 15 deletions app/api/logout/route.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
import { NextRequest, NextResponse } from 'next/server'
import { createClient } from '../../utils/supabase/server'
import { deleteCookie } from '../../utils/supabase/cookies'
import { NextRequest, NextResponse } from 'next/server';
import { createClient } from '../../utils/supabase/server';
import { deleteCookie } from '../../utils/supabase/cookies';

export async function POST(req: NextRequest): Promise<NextResponse> {
const supabase = createClient();

export async function POST(req: NextRequest) {
const supabase = createClient();
const { error } = await supabase.auth.signOut();

const { error } = await supabase.auth.signOut()
if (error) {
console.error('Failed to logout', error);
return NextResponse.redirect(new URL('/error', req.url)); // Redirect to an error page if there's an error
}

if (error) {
return NextResponse.redirect(new URL('/error', req.url))
}
const response = NextResponse.redirect(new URL('/', req.url)); // Redirect to the home page
deleteCookie(response, 'user-data'); // Delete cookies if needed

const response = NextResponse.redirect(new URL('/login', req.url))
// Delete cookies if needed
deleteCookie(response, 'user-data')

return response
}
return response;
}
19 changes: 0 additions & 19 deletions app/auth/confirm/route.ts

This file was deleted.

Loading

0 comments on commit 954009a

Please sign in to comment.