Skip to content

Commit

Permalink
feat: Updated 10 files
Browse files Browse the repository at this point in the history
  • Loading branch information
sweep-ai[bot] committed Apr 26, 2024
1 parent 66cad26 commit 58b4cdc
Show file tree
Hide file tree
Showing 10 changed files with 769 additions and 0 deletions.
74 changes: 74 additions & 0 deletions apps/nextjs/app/api/dfda/[dfdaPath]/route.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { dfdaGET, dfdaPOST } from './route';
import { createMocks } from 'node-mocks-http';
import { mockDeep } from 'jest-mock-extended';
import { prisma } from '@/lib/prisma';

jest.mock('@/lib/prisma');

describe('DFDA API Route', () => {
beforeEach(() => {
jest.clearAllMocks();
});

describe('dfdaGET', () => {
it('should return DFDA data for a valid user', async () => {
const { req, res } = createMocks({
method: 'GET',
query: { dfdaPath: 'validUser' },
});

const mockDfdaData = { id: 1, name: 'Test DFDA' };
(prisma.dFDA.findUnique as jest.Mock).mockResolvedValueOnce(mockDfdaData);

await dfdaGET(req, res);

expect(res._getStatusCode()).toBe(200);
expect(JSON.parse(res._getData())).toEqual(mockDfdaData);
});

it('should return 404 for an invalid user', async () => {
const { req, res } = createMocks({
method: 'GET',
query: { dfdaPath: 'invalidUser' },
});

(prisma.dFDA.findUnique as jest.Mock).mockResolvedValueOnce(null);

await dfdaGET(req, res);

expect(res._getStatusCode()).toBe(404);
});
});

describe('dfdaPOST', () => {
it('should create a new DFDA entry for a valid request', async () => {
const { req, res } = createMocks({
method: 'POST',
query: { dfdaPath: 'newEntry' },
body: { name: 'New DFDA Entry' },
});
req.session = { user: { id: 1 } } as any;

const mockCreatedEntry = { id: 2, name: 'New DFDA Entry' };
(prisma.dFDA.create as jest.Mock).mockResolvedValueOnce(mockCreatedEntry);

await dfdaPOST(req, res);

expect(res._getStatusCode()).toBe(201);
expect(JSON.parse(res._getData())).toEqual(mockCreatedEntry);
});

it('should return 401 for an unauthenticated request', async () => {
const { req, res } = createMocks({
method: 'POST',
query: { dfdaPath: 'newEntry' },
body: { name: 'New DFDA Entry' },
});
req.session = {} as any;

await dfdaPOST(req, res);

expect(res._getStatusCode()).toBe(401);
});
});
});
71 changes: 71 additions & 0 deletions apps/nextjs/app/api/image2measurements/route.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { createMocks } from 'node-mocks-http';
import { mockDeep } from 'jest-mock-extended';
import { NextApiRequest, NextApiResponse } from 'next';
import { POST } from './route';

jest.mock('openai', () => ({
Configuration: jest.fn(),
OpenAIApi: jest.fn(() => ({
createCompletion: jest.fn(),
})),
}));

describe('Image to Measurements API Route', () => {
beforeEach(() => {
jest.clearAllMocks();
});

it('should return a successful response for a valid request', async () => {
const { req, res } = createMocks<NextApiRequest, NextApiResponse>({
method: 'POST',
body: {
file: 'validBase64Image',
prompt: 'Test prompt',
},
});

const mockOpenAIResponse = {
data: {
choices: [
{
text: 'Mock analysis result',
},
],
},
};
jest.spyOn(require('openai'), 'OpenAIApi').mockImplementation(() => ({
createCompletion: jest.fn().mockResolvedValueOnce(mockOpenAIResponse),
}));

await POST(req, res);

expect(res._getStatusCode()).toBe(200);
expect(JSON.parse(res._getData())).toEqual({
success: true,
analysis: 'Mock analysis result',
});
});

it('should return an error response for an invalid request', async () => {
const { req, res } = createMocks<NextApiRequest, NextApiResponse>({
method: 'POST',
body: {
file: 'invalidBase64Image',
prompt: 'Test prompt',
},
});

const mockOpenAIError = new Error('Mock OpenAI error');
jest.spyOn(require('openai'), 'OpenAIApi').mockImplementation(() => ({
createCompletion: jest.fn().mockRejectedValueOnce(mockOpenAIError),
}));

await POST(req, res);

expect(res._getStatusCode()).toBe(500);
expect(JSON.parse(res._getData())).toEqual({
success: false,
message: 'An error occurred while processing the image.',
});
});
});
36 changes: 36 additions & 0 deletions apps/nextjs/app/chat/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,39 @@ const App: React.FC = () => {
}

export default App;

// Unit tests for file handling logic
describe('App component', () => {
beforeEach(() => {
jest.clearAllMocks();
});

it('handleFileChange should update state correctly with a valid file', async () => {
const mockFile = new File(['test'], 'test.png', { type: 'image/png' });
const mockBase64 = 'mockBase64String';

jest.spyOn(URL, 'createObjectURL').mockReturnValueOnce('mockUrl');
jest.spyOn(global, 'convertFileToBase64').mockResolvedValueOnce(mockBase64);

await act(async () => {
await handleFileChange(mockFile);
});

expect(setFile).toHaveBeenCalledWith(mockFile);
expect(setPreview).toHaveBeenCalledWith('mockUrl');
expect(setStatusMessage).toHaveBeenCalledWith('Image selected. Click "Analyze Image" to proceed.');
expect(setUploadProgress).toHaveBeenCalledWith(0);
expect(setBase64Image).toHaveBeenCalledWith(mockBase64);
});

it('handleCapture should call handleFileChange with the captured Blob', async () => {
const mockBlob = new Blob(['test'], { type: 'image/png' });
const mockFile = new File([mockBlob], 'captured_image.png', { type: 'image/png' });

await act(async () => {
await handleCapture(mockBlob);
});

expect(handleFileChange).toHaveBeenCalledWith(mockFile);
});
});
77 changes: 77 additions & 0 deletions apps/nextjs/app/dashboard/image2measurements/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,80 @@ export default function Home() {
</div>
)
}

// Unit tests for image upload and processing flow
describe('Home component', () => {
beforeEach(() => {
jest.clearAllMocks();
});

it('handleFileChange should update state correctly with a valid file', async () => {
const mockFile = new File(['test'], 'test.png', { type: 'image/png' });
const mockEvent = { target: { files: [mockFile] } };

await act(async () => {
await handleFileChange(mockEvent);
});

expect(setImage).toHaveBeenCalledWith(expect.stringContaining('data:image/png;base64,'));
});

it('handleSubmit should send a POST request with the correct data', async () => {
const mockImage = 'mockBase64Image';
const mockResponse = { success: true, analysis: 'Mock analysis result' };

global.fetch = jest.fn().mockResolvedValueOnce({
ok: true,
json: jest.fn().mockResolvedValueOnce(mockResponse),
});

setImage(mockImage);

await act(async () => {
await handleSubmit(new Event('submit'));
});

expect(global.fetch).toHaveBeenCalledWith('/api/image2measurements', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ image: mockImage }),
});

expect(setIsLoading).toHaveBeenCalledWith(true);
expect(setOpenAIResponse).toHaveBeenCalledWith('');
expect(setNutritionData).toHaveBeenCalledWith([]);
expect(setIsLoading).toHaveBeenCalledWith(false);
});

it('handleSubmit should handle API errors correctly', async () => {
const mockImage = 'mockBase64Image';

global.fetch = jest.fn().mockResolvedValueOnce({
ok: false,
status: 500,
});

setImage(mockImage);

await act(async () => {
await handleSubmit(new Event('submit'));
});

expect(global.fetch).toHaveBeenCalledWith('/api/image2measurements', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ image: mockImage }),
});

expect(setIsLoading).toHaveBeenCalledWith(true);
expect(setOpenAIResponse).toHaveBeenCalledWith('');
expect(setNutritionData).toHaveBeenCalledWith([]);
expect(setIsLoading).toHaveBeenCalledWith(false);
expect(console.error).toHaveBeenCalledWith('Error fetching data:', expect.any(Error));
expect(window.alert).toHaveBeenCalledWith('Failed to fetch data.');
});
});
60 changes: 60 additions & 0 deletions apps/nextjs/components/CameraButton.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import React from 'react';
import { render, fireEvent, act } from '@testing-library/react';
import { CameraButton } from './CameraButton';

describe('CameraButton component', () => {
beforeEach(() => {
jest.clearAllMocks();
});

it('should call onCapture with the captured blob when a photo is taken', async () => {
const mockOnCapture = jest.fn();
const mockBlob = new Blob(['test'], { type: 'image/png' });

global.URL.createObjectURL = jest.fn();
global.navigator.mediaDevices.getUserMedia = jest.fn().mockResolvedValueOnce({
getTracks: () => [{ stop: jest.fn() }],
});

const { getByLabelText } = render(<CameraButton onCapture={mockOnCapture} />);
const cameraButton = getByLabelText('Take a photo');

await act(async () => {
fireEvent.click(cameraButton);
});

const videoElement = document.querySelector('video');
const mockCaptureStream = {
getVideoTracks: () => [{ stop: jest.fn() }],
};
videoElement.srcObject = mockCaptureStream;

const canvasElement = document.createElement('canvas');
canvasElement.toBlob = jest.fn().mockImplementationOnce((callback) => {
callback(mockBlob);
});

await act(async () => {
fireEvent.click(cameraButton);
});

expect(mockOnCapture).toHaveBeenCalledWith(mockBlob);
});

it('should handle errors when accessing the camera stream', async () => {
const mockOnCapture = jest.fn();
const mockError = new Error('Camera access denied');

global.navigator.mediaDevices.getUserMedia = jest.fn().mockRejectedValueOnce(mockError);

const { getByLabelText } = render(<CameraButton onCapture={mockOnCapture} />);
const cameraButton = getByLabelText('Take a photo');

await act(async () => {
fireEvent.click(cameraButton);
});

expect(mockOnCapture).not.toHaveBeenCalled();
// You can add more assertions to check error handling behavior
});
});
Loading

0 comments on commit 58b4cdc

Please sign in to comment.