Skip to content
Merged
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
8 changes: 8 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"plugins": [],
"printWidth": 100,
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "all"
}
35,211 changes: 18,107 additions & 17,104 deletions frontend/package-lock.json

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"lint": "eslint .",
"lint:scss": "stylelint \"**/*.scss\"",
"lint:all": "npm run lint && npm run lint:scss",
"format": "prettier --write \"src/**/*.{ts,tsx,js,jsx,scss,css,json,md}\"",
"android": "npx cap sync android && npx cap open android",
"ios": "npx cap sync ios && npx cap open ios",
"prepare": "husky"
Expand Down Expand Up @@ -93,7 +94,7 @@
"@typescript-eslint/parser": "8.19.1",
"@vitejs/plugin-legacy": "6.0.0",
"@vitejs/plugin-react": "4.3.4",
"@vitest/coverage-v8": "2.1.9",
"@vitest/coverage-v8": "^3.1.1",
"cypress": "13.17.0",
"eslint": "9.17.0",
"eslint-plugin-react": "7.37.3",
Expand All @@ -112,6 +113,7 @@
"typescript": "5.7.2",
"typescript-eslint": "8.19.1",
"vite": "6.0.7",
"vitest": "2.1.9"
"vitest": "^3.1.1",
"prettier": "^3.0.0"
}
}
3 changes: 1 addition & 2 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const initializeStatusBar = async () => {
try {
if (Capacitor.isPluginAvailable('StatusBar')) {
await StatusBar.setStyle({ style: Style.Light });

if (Capacitor.getPlatform() === 'android') {
// Make status bar transparent on Android
await StatusBar.setBackgroundColor({ color: '#4765ff' });
Expand All @@ -44,7 +44,6 @@ const initializeStatusBar = async () => {
* @returns JSX
*/
const App = (): JSX.Element => {

useEffect(() => {
initializeStatusBar();
}, []);
Expand Down
92 changes: 49 additions & 43 deletions frontend/src/common/api/__tests__/reportService.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { vi, describe, test, expect, beforeEach } from 'vitest';
import { uploadReport, ReportError, fetchLatestReports, fetchAllReports, markReportAsRead } from '../reportService';
import {
uploadReport,
ReportError,
fetchLatestReports,
fetchAllReports,
markReportAsRead,
} from '../reportService';
import { ReportCategory, ReportStatus } from '../../models/medicalReport';
import axios from 'axios';

Expand All @@ -12,13 +18,13 @@ vi.mock('axios', () => ({
post: vi.fn(),
get: vi.fn(),
patch: vi.fn(),
isAxiosError: vi.fn(() => true)
}
isAxiosError: vi.fn(() => true),
},
}));

// Mock dynamic imports to handle the service functions
vi.mock('../reportService', async (importOriginal) => {
const actual = await importOriginal() as typeof ReportServiceModule;
const actual = (await importOriginal()) as typeof ReportServiceModule;

// Create a new object with the same properties as the original
return {
Expand All @@ -38,9 +44,11 @@ vi.mock('../reportService', async (importOriginal) => {
return response.data;
} catch (error) {
// Properly wrap the error in a ReportError
throw new actual.ReportError(error instanceof Error
? `Failed to upload report: ${error.message}`
: 'Failed to upload report');
throw new actual.ReportError(
error instanceof Error
? `Failed to upload report: ${error.message}`
: 'Failed to upload report',
);
}
},

Expand All @@ -50,9 +58,11 @@ vi.mock('../reportService', async (importOriginal) => {
const response = await axios.get(`/api/reports/latest?limit=${limit}`);
return response.data;
} catch (error) {
throw new actual.ReportError(error instanceof Error
? `Failed to fetch latest reports: ${error.message}`
: 'Failed to fetch latest reports');
throw new actual.ReportError(
error instanceof Error
? `Failed to fetch latest reports: ${error.message}`
: 'Failed to fetch latest reports',
);
}
},

Expand All @@ -62,9 +72,11 @@ vi.mock('../reportService', async (importOriginal) => {
const response = await axios.get(`/api/reports`);
return response.data;
} catch (error) {
throw new actual.ReportError(error instanceof Error
? `Failed to fetch all reports: ${error.message}`
: 'Failed to fetch all reports');
throw new actual.ReportError(
error instanceof Error
? `Failed to fetch all reports: ${error.message}`
: 'Failed to fetch all reports',
);
}
},

Expand All @@ -79,10 +91,10 @@ vi.mock('@aws-amplify/auth', () => ({
fetchAuthSession: vi.fn().mockResolvedValue({
tokens: {
idToken: {
toString: () => 'mock-id-token'
}
}
})
toString: () => 'mock-id-token',
},
},
}),
}));

// Mock response data
Expand All @@ -100,7 +112,7 @@ const mockReports = [
status: ReportStatus.UNREAD,
category: ReportCategory.BRAIN,
date: '2024-03-24',
}
},
];

describe('reportService', () => {
Expand All @@ -122,7 +134,7 @@ describe('reportService', () => {
status: ReportStatus.UNREAD,
category: ReportCategory.GENERAL,
date: '2024-05-10',
}
},
});
});

Expand All @@ -147,7 +159,7 @@ describe('reportService', () => {
status: ReportStatus.UNREAD,
category: ReportCategory.HEART,
date: '2024-05-10',
}
},
});

const heartFile = new File(['test'], 'heart-report.pdf', { type: 'application/pdf' });
Expand All @@ -162,7 +174,7 @@ describe('reportService', () => {
status: ReportStatus.UNREAD,
category: ReportCategory.BRAIN,
date: '2024-05-10',
}
},
});

const neuroFile = new File(['test'], 'brain-scan.pdf', { type: 'application/pdf' });
Expand All @@ -179,20 +191,18 @@ describe('reportService', () => {
test('should throw ReportError on upload failure', async () => {
// Mock axios.post to fail
(axios.post as ReturnType<typeof vi.fn>).mockRejectedValueOnce(
new Error('API request failed')
new Error('API request failed'),
);

await expect(uploadReport(mockFile, progressCallback))
.rejects
.toThrow(ReportError);
await expect(uploadReport(mockFile, progressCallback)).rejects.toThrow(ReportError);
});
});

describe('fetchLatestReports', () => {
beforeEach(() => {
// Setup axios mock response
(axios.get as ReturnType<typeof vi.fn>).mockResolvedValue({
data: mockReports.slice(0, 2)
data: mockReports.slice(0, 2),
});
});

Expand All @@ -201,16 +211,18 @@ describe('reportService', () => {

expect(axios.get).toHaveBeenCalled();
expect(reports).toHaveLength(2);
expect(reports[0]).toEqual(expect.objectContaining({
id: expect.any(String),
title: expect.any(String)
}));
expect(reports[0]).toEqual(
expect.objectContaining({
id: expect.any(String),
title: expect.any(String),
}),
);
});

test('should fetch latest reports with custom limit', async () => {
const limit = 1;
(axios.get as ReturnType<typeof vi.fn>).mockResolvedValue({
data: mockReports.slice(0, 1)
data: mockReports.slice(0, 1),
});

const reports = await fetchLatestReports(limit);
Expand All @@ -222,16 +234,14 @@ describe('reportService', () => {
test('should throw ReportError on fetch failure', async () => {
(axios.get as ReturnType<typeof vi.fn>).mockRejectedValue(new Error('Network error'));

await expect(fetchLatestReports())
.rejects
.toThrow(ReportError);
await expect(fetchLatestReports()).rejects.toThrow(ReportError);
});
});

describe('fetchAllReports', () => {
beforeEach(() => {
(axios.get as ReturnType<typeof vi.fn>).mockResolvedValue({
data: mockReports
data: mockReports,
});
});

Expand All @@ -245,21 +255,19 @@ describe('reportService', () => {
test('should throw ReportError on fetch failure', async () => {
(axios.get as ReturnType<typeof vi.fn>).mockRejectedValue(new Error('Network error'));

await expect(fetchAllReports())
.rejects
.toThrow(ReportError);
await expect(fetchAllReports()).rejects.toThrow(ReportError);
});
});

describe('markReportAsRead', () => {
beforeEach(() => {
const updatedReport = {
...mockReports[0],
status: ReportStatus.READ
status: ReportStatus.READ,
};

(axios.patch as ReturnType<typeof vi.fn>).mockResolvedValue({
data: updatedReport
data: updatedReport,
});
});

Expand All @@ -273,9 +281,7 @@ describe('reportService', () => {
test('should throw error when report not found', async () => {
(axios.patch as ReturnType<typeof vi.fn>).mockRejectedValue(new Error('Report not found'));

await expect(markReportAsRead('non-existent-id'))
.rejects
.toThrow(ReportError);
await expect(markReportAsRead('non-existent-id')).rejects.toThrow(ReportError);
});
});
});
8 changes: 4 additions & 4 deletions frontend/src/common/api/useGetCurrentUser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,20 @@ export const useGetCurrentUser = () => {
try {
// Get current user from Cognito
const cognitoUser = await CognitoAuthService.getCurrentUser();

if (!cognitoUser) {
throw new Error('User not found');
}

// Map Cognito user data to our application's user model
const userData = {
username: cognitoUser.username || '',
attributes: {
// Extract whatever attributes are available from the user object
email: cognitoUser.signInDetails?.loginId || '',
}
},
};

return mapCognitoUserToAppUser(userData);
} catch (error) {
console.error('Error getting current user:', error);
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/common/api/useGetUserTokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ export const useGetUserTokens = () => {
try {
// Get tokens from Cognito
const tokens = await CognitoAuthService.getUserTokens();

if (!tokens) {
throw new Error('Tokens not found.');
}

return tokens;
} catch (error) {
console.error('Error getting user tokens:', error);
Expand Down
Loading