Skip to content

Commit

Permalink
Merge pull request #123 from kristo-baricevic/vitest-branch
Browse files Browse the repository at this point in the history
Vitest installed
  • Loading branch information
kristo-baricevic authored Apr 13, 2024
2 parents 091a576 + 5542a6e commit 83f74d8
Show file tree
Hide file tree
Showing 7 changed files with 6,381 additions and 4,886 deletions.
11,072 changes: 6,246 additions & 4,826 deletions frontend/package-lock.json

Large diffs are not rendered by default.

15 changes: 10 additions & 5 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@
"version": "0.0.0",
"type": "module",
"scripts": {
"test": "vitest",
"test:ui": "vitest --ui",
"dev": "vite --host",
"build": "tsc && vite build",
"lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview"
},
"dependencies": {
"@redux-devtools/extension": "^3.3.0",
"axios": "^1.4.0",
"formik": "^2.4.2",
"jsonwebtoken": "^9.0.2",
"jwt-decode": "^4.0.0",
Expand All @@ -27,10 +28,11 @@
"yup": "^1.2.0"
},
"devDependencies": {
"@babel/preset-env": "^7.24.3",
"@babel/preset-react": "^7.24.1",
"@babel/preset-typescript": "^7.24.1",
"@testing-library/jest-dom": "^6.4.2",
"@testing-library/react": "^15.0.1",
"@testing-library/user-event": "^14.5.2",
"@types/axios": "^0.14.0",
"@types/jest": "^29.5.12",
"@types/jsonwebtoken": "^9.0.6",
"@types/jwt-decode": "^3.1.0",
"@types/node": "^20.10.6",
Expand All @@ -43,16 +45,19 @@
"@typescript-eslint/parser": "^5.59.0",
"@vitejs/plugin-react": "^4.0.0",
"autoprefixer": "^10.4.14",
"axios": "^1.6.8",
"eslint": "^8.38.0",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.3.4",
"jsdom": "^24.0.0",
"postcss": "^8.4.24",
"postcss-nesting": "^12.0.0",
"prettier": "^2.8.8",
"prettier-plugin-tailwindcss": "^0.4.1",
"tailwindcss": "^3.3.2",
"typescript": "^5.1.3",
"vite": "^4.4.9"
"vite": "^4.4.9",
"vitest": "^1.5.0"
}
}
163 changes: 109 additions & 54 deletions frontend/src/pages/PatientManager/NewPatientForm.test.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,22 @@
jest.mock('../../config/envConfig', () => ({
getEnv: () => ({
apiUrl: 'http://localhost:8000/api/listMeds/',
}),
}));

import React from 'react';
// mock React's useState function
jest.mock('react', () => {
const originalReact = jest.requireActual('react'); // Import actual React module
return {
...originalReact, // Spread all original exports
useState: jest.fn(), // Override only useState
};
});


import { render } from '@testing-library/react';
import '@testing-library/jest-dom';
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import NewPatientForm from './NewPatientForm';
import axios from 'axios';

vi.mock('axios');
const mockedAxios = vi.mocked(axios, true);

// Implement a mock function that can handle both overloads
window.scrollTo = vi.fn((...args: [ScrollToOptions?] | [number, number]) => {
if (args.length === 1 && typeof args[0] === 'object') {
console.log(`Scrolling with options`, args[0]);
} else if (typeof args[0] === 'number' && typeof args[1] === 'number') {
console.log(`Scrolling to position (${args[0]}, ${args[1]})`);
}
}) as typeof window.scrollTo;

// Define mock data
export interface PatientInfo {
interface PatientInfo {
ID?: string;
Diagnosis?: string;
OtherDiagnosis?: string;
Expand Down Expand Up @@ -70,46 +65,106 @@ const mockPatientInfo: PatientInfo = {
Reproductive: 'No',
risk_pregnancy: 'No',
};

// Mock functions for setting state
const mockSetPatientInfo = jest.fn();
const mockSetAllPatientInfo = jest.fn();
const mockSetPatientInfo = vi.fn();
const mockSetAllPatientInfo = vi.fn();

// Mock array for allPatientInfo
const mockAllPatientInfo = [mockPatientInfo];
const mockAllPatientInfo = [mockPatientInfo];

// At the top of your test file
// jest.mock('axios');
// import axios from 'axios';
// const mockedAxios = axios as jest.Mocked<typeof axios>;
// Mock localStorage
Object.defineProperty(window, 'localStorage', {
value: {
getItem: vi.fn(() => JSON.stringify([mockPatientInfo])),
setItem: vi.fn(() => null),
},
writable: true
});

const props = {
patientInfo: mockPatientInfo,
setPatientInfo: mockSetPatientInfo,
allPatientInfo: mockAllPatientInfo,
setAllPatientInfo: mockSetAllPatientInfo
};

describe('NewPatientForm useEffect', () => {
it('loads patient info from localStorage on mount', () => {
render(<NewPatientForm {...props} />);
expect(mockSetAllPatientInfo).toHaveBeenCalledWith([mockPatientInfo]);
});
});

describe('NewPatientForm Component', () => {
beforeEach(() => {
// Clear all mocks before each test
jest.clearAllMocks();

// Reset useState mock to a clean state
const originalReact = jest.requireActual('react');
(React.useState as jest.Mock)
.mockImplementation((initial: any) => originalReact.useState(initial));
});
beforeEach(() => {
mockedAxios.post.mockClear();
vi.spyOn(Storage.prototype, 'getItem').mockReturnValue(JSON.stringify([mockPatientInfo]));

// Mocking Axios post request to return specific data needed for the component
mockedAxios.post.mockResolvedValue({
data: {
first: ["MedicationA1", "MedicationA2"],
second: ["MedicationB1", "MedicationB2"],
third: ["MedicationC1", "MedicationC2"]
}
});
});

afterEach(() => {
vi.restoreAllMocks();
});

it('renders without crashing', () => {
render(<NewPatientForm {...props} />);
expect(screen.getByText('Enter Patient Details')).toBeInTheDocument();
});

it('prevents form submission if the diagnosis is "Null"', async () => {
render(<NewPatientForm {...props} />);

// Attempt to submit the form without changing the diagnosis
const submitButton = screen.getByRole('button', { name: 'submit-test' })
fireEvent.click(submitButton);

await waitFor(() => expect(mockedAxios.post).not.toHaveBeenCalled());
});

it('handles patient info on form submit', async () => {
const { getByRole, getByLabelText } = render(<NewPatientForm {...props} />);

fireEvent.change(getByLabelText('Current state'), { target: { value: 'Manic' } });
fireEvent.click(getByRole('button', { name: 'submit-test' }));

await waitFor(() => {
expect(mockedAxios.post).toHaveBeenCalled();
});
});

it('renders without crashing', () => {
(React.useState as jest.Mock)
.mockImplementationOnce(() => [false, jest.fn()]) // Mock for isPressed
.mockImplementationOnce(() => [false, jest.fn()]);
render(<NewPatientForm
patientInfo={mockPatientInfo}
setPatientInfo={mockSetPatientInfo}
allPatientInfo={mockAllPatientInfo}
setAllPatientInfo={mockSetAllPatientInfo}
/>);

// Look for a static element that should always be present
// For example, if your form always renders a submit button, check for that
// expect(screen.getByRole('button', { name: /submit/i })).toBeInTheDocument();
});
});
it('clears the form when the clear button is clicked', async () => {
render(<NewPatientForm {...props} />);
const diagnosisSelect = screen.getByRole('combobox', { name: /current state/i }) as HTMLSelectElement;

fireEvent.change(diagnosisSelect, { target: { value: 'Manic' } });

const clearButton = screen.getByTestId('clear-form-button');
fireEvent.click(clearButton);

await waitFor(() => {
expect(diagnosisSelect).toHaveValue('Null');
});
});

it('handles patient info on form submit', async () => {
render(<NewPatientForm {...props} />);

fireEvent.change(screen.getByLabelText('Current state'), { target: { value: 'Manic' } });
fireEvent.click(screen.getByRole('button', { name: 'submit-test' }));

await waitFor(() => {
expect(mockedAxios.post).toHaveBeenCalledWith(
'http://localhost:3000/chatgpt/list_meds',
{ state: 'Manic' }
);
});
});
});
1 change: 1 addition & 0 deletions frontend/src/testSetup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import '@testing-library/jest-dom'
3 changes: 2 additions & 1 deletion frontend/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"types": ["vitest/globals"],

/* Bundler mode */
"moduleResolution": "bundler",
Expand All @@ -22,6 +23,6 @@
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src", "custom.d.ts", "vite-env.d.ts"],
"include": ["src", "custom.d.ts", "vite-env.d.ts", ".src/testSetup.tsx"],
"references": [{ "path": "./tsconfig.node.json" }]
}
1 change: 1 addition & 0 deletions frontend/vite-env.d.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
/// <reference types="vite/client" />
/// <reference types="vitest" />
12 changes: 12 additions & 0 deletions frontend/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import type { UserConfig as VitestUserConfig } from 'vitest/config';

declare module 'vite' {
export interface UserConfig {
test: VitestUserConfig['test'];
}
}
// https://vitejs.dev/config/
export default defineConfig({
build: {
Expand All @@ -16,4 +22,10 @@ export default defineConfig({
strictPort: true,
port: 3000,
},
test: {
globals: true,
environment: 'jsdom',
setupFiles: './src/testSetup.tsx',
css: true,
},
});

0 comments on commit 83f74d8

Please sign in to comment.