feat(MED-161): add jest for service tests
This commit is contained in:
44
__mocks__/isikukood.ts
Normal file
44
__mocks__/isikukood.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
// Mock for isikukood library to avoid ES module issues in tests
|
||||
|
||||
export enum Gender {
|
||||
MALE = 'male',
|
||||
FEMALE = 'female',
|
||||
}
|
||||
|
||||
export default class Isikukood {
|
||||
private code: string;
|
||||
|
||||
constructor(code: string) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
static validate(code: string): boolean {
|
||||
return true; // Mock always returns true for tests
|
||||
}
|
||||
|
||||
static generate(options?: { gender?: Gender; century?: number }): string {
|
||||
return '39001010002'; // Mock Estonian ID code
|
||||
}
|
||||
|
||||
isValid(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
getGender(): Gender {
|
||||
return Gender.MALE;
|
||||
}
|
||||
|
||||
getBirthDate(): Date {
|
||||
return new Date('1990-01-01');
|
||||
}
|
||||
|
||||
getAge(): number {
|
||||
return 30;
|
||||
}
|
||||
|
||||
getCentury(): number {
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
export { Isikukood };
|
||||
3
__mocks__/server-only.ts
Normal file
3
__mocks__/server-only.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
// Mock for server-only to avoid Next.js server component issues in tests
|
||||
// This module does nothing in tests - it's just a marker for Next.js
|
||||
export {};
|
||||
@@ -833,282 +833,6 @@ const big2: AnalysisTestResponse = {
|
||||
],
|
||||
};
|
||||
|
||||
const big2: AnalysisTestResponse = {
|
||||
"id": 3,
|
||||
"orderedAnalysisElements": [
|
||||
{
|
||||
"analysisIdOriginal": "57021-8",
|
||||
"isWaitingForResults": false,
|
||||
"analysisName": "Hemogramm",
|
||||
"results": {
|
||||
"nestedElements": [
|
||||
{
|
||||
"status": 4,
|
||||
"unit": "g/L",
|
||||
"normLower": 134,
|
||||
"normUpper": 170,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2025-09-12 14:02:03",
|
||||
"responseValue": 150,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"analysisElementOriginalId": "718-7"
|
||||
},
|
||||
{
|
||||
"status": 4,
|
||||
"unit": "%",
|
||||
"normLower": 40,
|
||||
"normUpper": 49,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2025-09-12 14:02:03",
|
||||
"responseValue": 45,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"analysisElementOriginalId": "4544-3"
|
||||
},
|
||||
{
|
||||
"status": 4,
|
||||
"unit": "E9/L",
|
||||
"normLower": 4.1,
|
||||
"normUpper": 9.7,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2025-09-12 14:02:03",
|
||||
"responseValue": 5,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"analysisElementOriginalId": "6690-2"
|
||||
},
|
||||
{
|
||||
"status": 4,
|
||||
"unit": "E12/L",
|
||||
"normLower": 4.5,
|
||||
"normUpper": 5.7,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2025-09-12 14:02:03",
|
||||
"responseValue": 5,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"analysisElementOriginalId": "789-8"
|
||||
},
|
||||
{
|
||||
"status": 4,
|
||||
"unit": "fL",
|
||||
"normLower": 82,
|
||||
"normUpper": 95,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2025-09-12 14:02:04",
|
||||
"responseValue": 85,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"analysisElementOriginalId": "787-2"
|
||||
},
|
||||
{
|
||||
"status": 4,
|
||||
"unit": "pg",
|
||||
"normLower": 28,
|
||||
"normUpper": 33,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2025-09-12 14:02:04",
|
||||
"responseValue": 30,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"analysisElementOriginalId": "785-6"
|
||||
},
|
||||
{
|
||||
"status": 4,
|
||||
"unit": "g/L",
|
||||
"normLower": 322,
|
||||
"normUpper": 356,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2025-09-12 14:02:04",
|
||||
"responseValue": 355,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"analysisElementOriginalId": "786-4"
|
||||
},
|
||||
{
|
||||
"status": 4,
|
||||
"unit": "%",
|
||||
"normLower": 12,
|
||||
"normUpper": 15,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2025-09-12 14:02:04",
|
||||
"responseValue": 15,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"analysisElementOriginalId": "788-0"
|
||||
},
|
||||
{
|
||||
"status": 4,
|
||||
"unit": "E9/L",
|
||||
"normLower": 157,
|
||||
"normUpper": 372,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2025-09-12 14:02:04",
|
||||
"responseValue": 255,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"analysisElementOriginalId": "777-3"
|
||||
},
|
||||
{
|
||||
"status": 4,
|
||||
"unit": "%",
|
||||
"normLower": 0.18,
|
||||
"normUpper": 0.38,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2025-09-12 14:02:04",
|
||||
"responseValue": 0.2,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"analysisElementOriginalId": "51637-7"
|
||||
},
|
||||
{
|
||||
"status": 4,
|
||||
"unit": "fL",
|
||||
"normLower": 9.2,
|
||||
"normUpper": 12.3,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2025-09-12 14:02:04",
|
||||
"responseValue": 10,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"analysisElementOriginalId": "32623-1"
|
||||
},
|
||||
{
|
||||
"status": 4,
|
||||
"unit": "fL",
|
||||
"normLower": 10.1,
|
||||
"normUpper": 16.2,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2025-09-12 14:02:04",
|
||||
"responseValue": 15,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"analysisElementOriginalId": "32207-3"
|
||||
},
|
||||
{
|
||||
"status": 4,
|
||||
"unit": "E9/L",
|
||||
"normLower": 0.01,
|
||||
"normUpper": 0.08,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2025-09-12 14:02:04",
|
||||
"responseValue": 0.05,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"analysisElementOriginalId": "704-7"
|
||||
},
|
||||
{
|
||||
"status": 4,
|
||||
"unit": "E9/L",
|
||||
"normLower": 0.02,
|
||||
"normUpper": 0.4,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2025-09-12 14:02:04",
|
||||
"responseValue": 0.05,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"analysisElementOriginalId": "711-2"
|
||||
},
|
||||
{
|
||||
"status": 4,
|
||||
"unit": "E9/L",
|
||||
"normLower": 1.9,
|
||||
"normUpper": 6.7,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2025-09-12 14:02:04",
|
||||
"responseValue": 5,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"analysisElementOriginalId": "751-8"
|
||||
},
|
||||
{
|
||||
"status": 4,
|
||||
"unit": "E9/L",
|
||||
"normLower": 0.24,
|
||||
"normUpper": 0.8,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2025-09-12 14:02:04",
|
||||
"responseValue": 0.5,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"analysisElementOriginalId": "742-7"
|
||||
},
|
||||
{
|
||||
"status": 4,
|
||||
"unit": "E9/L",
|
||||
"normLower": 1.3,
|
||||
"normUpper": 3.1,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2025-09-12 14:02:04",
|
||||
"responseValue": 1.5,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"analysisElementOriginalId": "731-0"
|
||||
},
|
||||
{
|
||||
"status": 4,
|
||||
"unit": "E9/L",
|
||||
"normLower": 0,
|
||||
"normUpper": 0.03,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2025-09-12 14:02:03",
|
||||
"responseValue": 0,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"analysisElementOriginalId": "51584-1"
|
||||
},
|
||||
{
|
||||
"status": 4,
|
||||
"unit": "%",
|
||||
"normLower": 0,
|
||||
"normUpper": 0.5,
|
||||
"normStatus": 0,
|
||||
"responseTime": "2025-09-12 14:02:04",
|
||||
"responseValue": 0,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"analysisElementOriginalId": "38518-7"
|
||||
},
|
||||
{
|
||||
"status": 4,
|
||||
"unit": "E9/L",
|
||||
"normStatus": 0,
|
||||
"responseTime": "2025-09-12 14:02:04",
|
||||
"responseValue": 0,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"analysisElementOriginalId": "771-6"
|
||||
},
|
||||
{
|
||||
"status": 4,
|
||||
"unit": "/100WBC",
|
||||
"normStatus": 0,
|
||||
"responseTime": "2025-09-12 14:02:04",
|
||||
"responseValue": 0,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"analysisElementOriginalId": "58413-6"
|
||||
}
|
||||
],
|
||||
"unit": null,
|
||||
"normLower": null,
|
||||
"normUpper": null,
|
||||
"normStatus": null,
|
||||
"responseTime": null,
|
||||
"responseValue": null,
|
||||
"normLowerIncluded": false,
|
||||
"normUpperIncluded": false,
|
||||
"responseValueIsNegative": false,
|
||||
"responseValueIsWithinNorm": false,
|
||||
"status": "4",
|
||||
"analysisElementOriginalId": "57021-8",
|
||||
"summary": 'test'
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const analysisResponses: AnalysisTestResponse[] = [
|
||||
empty1,
|
||||
big1,
|
||||
|
||||
66
jest.config.js
Normal file
66
jest.config.js
Normal file
@@ -0,0 +1,66 @@
|
||||
import { pathsToModuleNameMapper } from 'ts-jest';
|
||||
import { readFileSync } from 'fs';
|
||||
|
||||
const tsconfig = JSON.parse(readFileSync('./tsconfig.json', 'utf8'));
|
||||
|
||||
/** @type {import('jest').Config} */
|
||||
export default {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
|
||||
// Handle module resolution for TypeScript paths
|
||||
moduleNameMapper: {
|
||||
...pathsToModuleNameMapper(tsconfig.compilerOptions.paths, {
|
||||
prefix: '<rootDir>/',
|
||||
}),
|
||||
// Mock problematic libraries
|
||||
'^isikukood$': '<rootDir>/__mocks__/isikukood.ts',
|
||||
'^server-only$': '<rootDir>/__mocks__/server-only.ts',
|
||||
},
|
||||
|
||||
// Test file patterns
|
||||
testMatch: [
|
||||
'**/__tests__/**/*.(ts|tsx|js)',
|
||||
'**/*.(test|spec).(ts|tsx|js)'
|
||||
],
|
||||
|
||||
// Setup files
|
||||
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
|
||||
|
||||
// Coverage configuration
|
||||
collectCoverageFrom: [
|
||||
'lib/**/*.{ts,tsx}',
|
||||
'app/**/*.{ts,tsx}',
|
||||
'components/**/*.{ts,tsx}',
|
||||
'!**/*.d.ts',
|
||||
'!**/node_modules/**',
|
||||
'!**/.next/**'
|
||||
],
|
||||
|
||||
// Transform configuration
|
||||
transform: {
|
||||
'^.+\\.(ts|tsx)$': ['ts-jest', {
|
||||
useESM: true,
|
||||
tsconfig: {
|
||||
jsx: 'react-jsx',
|
||||
},
|
||||
}],
|
||||
},
|
||||
|
||||
// Module file extensions
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'],
|
||||
|
||||
// Ignore patterns
|
||||
testPathIgnorePatterns: [
|
||||
'<rootDir>/.next/',
|
||||
'<rootDir>/node_modules/',
|
||||
],
|
||||
|
||||
// Transform ignore patterns for node_modules
|
||||
transformIgnorePatterns: [
|
||||
'node_modules/(?!(.*\\.mjs$))',
|
||||
],
|
||||
|
||||
// ESM support
|
||||
extensionsToTreatAsEsm: ['.ts'],
|
||||
};
|
||||
59
jest.setup.js
Normal file
59
jest.setup.js
Normal file
@@ -0,0 +1,59 @@
|
||||
// Jest setup file for global test configuration
|
||||
|
||||
// Mock Next.js router
|
||||
jest.mock('next/router', () => ({
|
||||
useRouter() {
|
||||
return {
|
||||
route: '/',
|
||||
pathname: '/',
|
||||
query: {},
|
||||
asPath: '/',
|
||||
push: jest.fn(),
|
||||
pop: jest.fn(),
|
||||
reload: jest.fn(),
|
||||
back: jest.fn(),
|
||||
prefetch: jest.fn().mockResolvedValue(undefined),
|
||||
beforePopState: jest.fn(),
|
||||
events: {
|
||||
on: jest.fn(),
|
||||
off: jest.fn(),
|
||||
emit: jest.fn(),
|
||||
},
|
||||
isFallback: false,
|
||||
};
|
||||
},
|
||||
}));
|
||||
|
||||
// Mock Next.js navigation
|
||||
jest.mock('next/navigation', () => ({
|
||||
useRouter() {
|
||||
return {
|
||||
push: jest.fn(),
|
||||
replace: jest.fn(),
|
||||
prefetch: jest.fn(),
|
||||
back: jest.fn(),
|
||||
forward: jest.fn(),
|
||||
refresh: jest.fn(),
|
||||
};
|
||||
},
|
||||
useSearchParams() {
|
||||
return new URLSearchParams();
|
||||
},
|
||||
usePathname() {
|
||||
return '/';
|
||||
},
|
||||
}));
|
||||
|
||||
// Global test utilities
|
||||
global.console = {
|
||||
...console,
|
||||
// Suppress console.log in tests unless needed
|
||||
log: jest.fn(),
|
||||
debug: jest.fn(),
|
||||
info: jest.fn(),
|
||||
warn: jest.fn(),
|
||||
error: jest.fn(),
|
||||
};
|
||||
|
||||
// Set up environment variables for tests
|
||||
process.env.NODE_ENV = 'test';
|
||||
112
lib/services/medipost/medipostPrivateMessage.service.test.ts
Normal file
112
lib/services/medipost/medipostPrivateMessage.service.test.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
import { AnalysisResponseElement } from "~/lib/types/analysis-response-element";
|
||||
import { canCreateAnalysisResponseElement, getAnalysisResponseElementsForGroup } from "./medipostPrivateMessage.service";
|
||||
import { ResponseUuring } from "@/packages/shared/src/types/medipost-analysis";
|
||||
|
||||
type TestExistingElement = Pick<AnalysisResponseElement, 'analysis_element_original_id' | 'status' | 'response_value'>;
|
||||
|
||||
describe('medipostPrivateMessage.service', () => {
|
||||
describe('canCreateAnalysisResponseElement', () => {
|
||||
it('should return true if the analysis response element does not exist', async () => {
|
||||
const existingElements = [] as TestExistingElement[];
|
||||
const groupUuring = {
|
||||
UuringuElement: {
|
||||
UuringOlek: 1,
|
||||
UuringId: '1',
|
||||
},
|
||||
} as const;
|
||||
const responseValue = 1;
|
||||
const log = jest.fn();
|
||||
expect(await canCreateAnalysisResponseElement({ existingElements, groupUuring, responseValue, log })).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false if the analysis response element exists and the status is higher', async () => {
|
||||
const existingElements = [{ analysis_element_original_id: '1', status: '2', response_value: 1 }] as TestExistingElement[];
|
||||
const groupUuring = {
|
||||
UuringuElement: {
|
||||
UuringOlek: 1,
|
||||
UuringId: '1',
|
||||
},
|
||||
} as const;
|
||||
const responseValue = 1;
|
||||
const log = jest.fn();
|
||||
expect(await canCreateAnalysisResponseElement({ existingElements, groupUuring, responseValue, log })).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getAnalysisResponseElementsForGroup', () => {
|
||||
it('should return single new element', async () => {
|
||||
const analysisGroup = {
|
||||
UuringuGruppNimi: '1',
|
||||
Uuring: [
|
||||
{
|
||||
UuringuElement: {
|
||||
UuringOlek: 1,
|
||||
UuringId: '1',
|
||||
UuringuVastus: [{ VastuseVaartus: '1' }],
|
||||
},
|
||||
},
|
||||
] as unknown as ResponseUuring[],
|
||||
} as const;
|
||||
const existingElements = [] as TestExistingElement[];
|
||||
const log = jest.fn();
|
||||
expect(await getAnalysisResponseElementsForGroup({ analysisGroup, existingElements, log }))
|
||||
.toEqual([{
|
||||
analysis_element_original_id: '1',
|
||||
analysis_name: undefined,
|
||||
comment: null,
|
||||
norm_lower: null,
|
||||
norm_lower_included: false,
|
||||
norm_status: undefined,
|
||||
norm_upper: null,
|
||||
norm_upper_included: false,
|
||||
response_time: null,
|
||||
response_value: 1,
|
||||
unit: null,
|
||||
original_response_element: {
|
||||
UuringOlek: 1,
|
||||
UuringId: '1',
|
||||
UuringuVastus: [{ VastuseVaartus: '1' }],
|
||||
},
|
||||
status: '1',
|
||||
}]);
|
||||
});
|
||||
|
||||
it('should return no new element if element already exists in higher status', async () => {
|
||||
const analysisGroup = {
|
||||
UuringuGruppNimi: '1',
|
||||
Uuring: [
|
||||
{
|
||||
UuringuElement: {
|
||||
UuringOlek: 1,
|
||||
UuringId: '1',
|
||||
UuringuVastus: [{ VastuseVaartus: '1' }],
|
||||
},
|
||||
},
|
||||
] as unknown as ResponseUuring[],
|
||||
} as const;
|
||||
const existingElements = [{ analysis_element_original_id: '1', status: '2', response_value: 1 }] as TestExistingElement[];
|
||||
const log = jest.fn();
|
||||
expect(await getAnalysisResponseElementsForGroup({ analysisGroup, existingElements, log }))
|
||||
.toEqual([]);
|
||||
});
|
||||
|
||||
it('should return no new element if element already exists with response value', async () => {
|
||||
const analysisGroup = {
|
||||
UuringuGruppNimi: '1',
|
||||
Uuring: [
|
||||
{
|
||||
UuringuElement: {
|
||||
UuringOlek: 1,
|
||||
UuringId: '1',
|
||||
UuringuVastus: [{ VastuseVaartus: '' }],
|
||||
},
|
||||
},
|
||||
] as unknown as ResponseUuring[],
|
||||
} as const;
|
||||
const existingElements = [{ analysis_element_original_id: '1', status: '1', response_value: 1 }] as TestExistingElement[];
|
||||
const log = jest.fn();
|
||||
expect(await getAnalysisResponseElementsForGroup({ analysisGroup, existingElements, log }))
|
||||
.toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -16,6 +16,10 @@
|
||||
"start": "next start",
|
||||
"start:test": "NODE_ENV=test next start",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch",
|
||||
"test:coverage": "jest --coverage",
|
||||
"test:ci": "jest --ci --coverage --watchAll=false",
|
||||
"supabase": "supabase",
|
||||
"supabase:start": "supabase status || supabase start",
|
||||
"supabase:stop": "supabase stop",
|
||||
@@ -88,6 +92,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@hookform/resolvers": "^5.0.1",
|
||||
"@jest/globals": "^30.1.2",
|
||||
"@kit/eslint-config": "workspace:*",
|
||||
"@kit/prettier-config": "workspace:*",
|
||||
"@kit/tsconfig": "workspace:*",
|
||||
@@ -95,6 +100,7 @@
|
||||
"@medusajs/ui-preset": "latest",
|
||||
"@next/bundle-analyzer": "15.3.2",
|
||||
"@tailwindcss/postcss": "^4.1.10",
|
||||
"@types/jest": "^30.0.0",
|
||||
"@types/jsonwebtoken": "9.0.10",
|
||||
"@types/lodash": "^4.17.17",
|
||||
"@types/node": "^22.15.32",
|
||||
@@ -103,11 +109,14 @@
|
||||
"babel-plugin-react-compiler": "19.1.0-rc.2",
|
||||
"cssnano": "^7.0.7",
|
||||
"dotenv": "^16.5.0",
|
||||
"jest": "^30.1.3",
|
||||
"jest-environment-node": "^30.1.2",
|
||||
"pino-pretty": "13.0.0",
|
||||
"prettier": "^3.5.3",
|
||||
"supabase": "^2.30.4",
|
||||
"tailwindcss": "4.1.7",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"ts-jest": "^29.4.2",
|
||||
"typescript": "^5.8.3",
|
||||
"yup": "^1.6.1"
|
||||
},
|
||||
|
||||
1278
pnpm-lock.yaml
generated
1278
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user