Skip to content

Commit 12e6ae7

Browse files
committed
test: add core unit tests for coordinate validation and search logic
1 parent 7b7ace7 commit 12e6ae7

22 files changed

Lines changed: 7981 additions & 2677 deletions

File tree

__mocks__/fileMock.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = 'test-file-stub';

__mocks__/leaflet.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module.exports = {
2+
map: jest.fn(() => ({ remove: jest.fn(), setView: jest.fn() })),
3+
tileLayer: jest.fn(() => ({ addTo: jest.fn() })),
4+
marker: jest.fn(() => ({ addTo: jest.fn(), bindPopup: jest.fn() })),
5+
icon: jest.fn(() => ({})),
6+
latLng: jest.fn((lat, lng) => ({ lat, lng })),
7+
CRS: { EPSG3857: {} },
8+
};

app/layout.tsx

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// app/layout.tsx
22
import type { Metadata } from "next";
33
import "./globals.css";
4+
import Link from "next/link";
45

56
import { Geist, Geist_Mono } from "next/font/google";
67

@@ -32,30 +33,33 @@ export default function RootLayout({
3233
<div className="max-w-7xl mx-auto px-4 py-4 flex justify-between items-center">
3334
<h1 className="text-xl font-bold text-gray-900">EvoMap</h1>
3435
<nav className="space-x-4">
35-
<a href="/" className="text-gray-700 hover:text-blue-600">
36+
<Link href="/" className="text-gray-700 hover:text-blue-600">
3637
Home
37-
</a>
38-
<a href="/map" className="text-gray-700 hover:text-blue-600">
38+
</Link>
39+
<Link href="/map" className="text-gray-700 hover:text-blue-600">
3940
Map
40-
</a>
41-
<a href="/test" className="text-gray-700 hover:text-blue-600">
41+
</Link>
42+
<Link href="/test" className="text-gray-700 hover:text-blue-600">
4243
Test
43-
</a>
44-
<a
44+
</Link>
45+
<Link
4546
href="/phylogenetic"
4647
className="text-gray-700 hover:text-blue-600"
4748
>
4849
Phylogenetic
49-
</a>
50-
<a href="/search" className="text-gray-700 hover:text-blue-600">
50+
</Link>
51+
<Link
52+
href="/search"
53+
className="text-gray-700 hover:text-blue-600"
54+
>
5155
Search
52-
</a>
53-
<a
56+
</Link>
57+
<Link
5458
href="/dashboard"
5559
className="text-gray-700 hover:text-blue-600"
5660
>
5761
Dashboard
58-
</a>
62+
</Link>
5963
</nav>
6064
</div>
6165
</header>

app/map/page.tsx

Lines changed: 15 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
"use client";
2-
import { useState } from "react";
2+
33
import dynamic from "next/dynamic";
4+
import { mockOutbreakData } from "../../src/lib/data/mockData";
45

56
// Dynamically import GlobalMap to avoid SSR issues with Leaflet
6-
const DemoGlobalMap = dynamic(
7+
const GlobalMap = dynamic(
78
() => import("../../src/components/maps/GlobalMap"),
89
{
910
ssr: false,
@@ -12,33 +13,23 @@ const DemoGlobalMap = dynamic(
1213
Loading map...
1314
</div>
1415
),
15-
},
16+
}
1617
);
1718

18-
export default function MapPage() {
19-
const [error] = useState<string | null>(null);
20-
21-
if (error) {
22-
return (
23-
<div className="max-w-7xl mx-auto py-8">
24-
<h2 className="text-2xl font-bold mb-4">Global Outbreak Map</h2>
25-
<div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded">
26-
Error loading map: {error}
27-
</div>
28-
</div>
29-
);
30-
}
19+
// Flatten mockOutbreakData for deployment
20+
const outbreaksForMap = mockOutbreakData.map((o) => ({
21+
id: o.id,
22+
name: o.pathogen || "Unknown",
23+
lat: o.location.coordinates[1], // latitude is second in [lng, lat]
24+
lng: o.location.coordinates[0], // longitude is first
25+
country: o.location.country,
26+
}));
3127

28+
export default function MapPage() {
3229
return (
33-
<div className="max-w-7xl mx-auto py-8">
30+
<div className="max-w-7xl mx-auto py-8 px-4">
3431
<h2 className="text-2xl font-bold mb-4">Global Outbreak Map</h2>
35-
<div className="bg-blue-50 p-4 rounded mb-4">
36-
<p className="text-sm text-blue-800">
37-
Interactive map showing outbreak locations worldwide. Use the time
38-
slider to filter by date.
39-
</p>
40-
</div>
41-
<DemoGlobalMap />
32+
<GlobalMap outbreaks={outbreaksForMap} />
4233
</div>
4334
);
4435
}

app/map/page.tsx.backup

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
"use client";
2+
3+
import dynamic from "next/dynamic";
4+
import { mockOutbreakData } from "../../src/lib/data/mockData";
5+
6+
// Dynamically import GlobalMap to avoid SSR issues with Leaflet
7+
const DemoGlobalMap = dynamic(
8+
() => import("../../src/components/maps/GlobalMap"),
9+
{
10+
ssr: false,
11+
loading: () => (
12+
<div className="w-full h-[600px] bg-gray-200 flex items-center justify-center">
13+
Loading map...
14+
</div>
15+
),
16+
}
17+
);
18+
19+
// Flatten mockOutbreakData for deployment
20+
const outbreaksForMap = mockOutbreakData.map((o) => ({
21+
id: o.id,
22+
name: o.pathogen || "Unknown",
23+
lat: o.location.coordinates[0],
24+
lng: o.location.coordinates[1],
25+
country: o.location.country,
26+
}));
27+
28+
export default function MapPage() {
29+
return (
30+
<div className="max-w-7xl mx-auto py-8">
31+
<h2 className="text-2xl font-bold mb-4">Global Outbreak Map</h2>
32+
<DemoGlobalMap outbreaks={outbreaksForMap} />
33+
</div>
34+
);
35+
}

evomap/app/map/page.tsx

Lines changed: 0 additions & 28 deletions
This file was deleted.

evomap/src/components/maps/GlobalMap.tsx

Lines changed: 9 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,14 @@
33
import React from "react";
44
import { MapContainer, TileLayer, Marker, Popup } from "react-leaflet";
55
import "leaflet/dist/leaflet.css";
6-
import "@/lib/leaflet-config";
7-
import { Outbreak } from "../../lib/types";
6+
7+
interface Outbreak {
8+
id: string;
9+
name: string;
10+
lat: number;
11+
lng: number;
12+
country: string;
13+
}
814

915
interface GlobalMapProps {
1016
outbreaks: Outbreak[];
@@ -28,30 +34,7 @@ const GlobalMap: React.FC<GlobalMapProps> = ({ outbreaks }) => {
2834
<Popup>
2935
<div className="p-2">
3036
<h3 className="font-bold text-lg">{outbreak.name}</h3>
31-
<p className="text-sm text-gray-600">
32-
{outbreak.location.city}, {outbreak.location.country}
33-
</p>
34-
<div className="mt-2">
35-
<span
36-
className={`px-2 py-1 rounded text-xs ${
37-
outbreak.severity === "critical"
38-
? "bg-red-100 text-red-800"
39-
: outbreak.severity === "high"
40-
? "bg-orange-100 text-orange-800"
41-
: outbreak.severity === "medium"
42-
? "bg-yellow-100 text-yellow-800"
43-
: "bg-green-100 text-green-800"
44-
}`}
45-
>
46-
{outbreak.severity.toUpperCase()}
47-
</span>
48-
</div>
49-
<p className="mt-2 text-sm">{outbreak.description}</p>
50-
<div className="mt-2 text-xs text-gray-500">
51-
<p>Cases: {outbreak.cases.toLocaleString()}</p>
52-
<p>Mortality Rate: {outbreak.mortality_rate}%</p>
53-
<p>Status: {outbreak.status}</p>
54-
</div>
37+
<p className="text-sm text-gray-600">{outbreak.country}</p>
5538
</div>
5639
</Popup>
5740
</Marker>

evomap/src/hooks/useOutbreaks.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useEffect, useState } from "react";
2-
import { Outbreak } from "@/lib/types";
2+
import { Outbreak } from "../lib/types";
33

44
export function useOutbreaks() {
55
const [outbreaks, setOutbreaks] = useState<Outbreak[]>([]);

evomap/src/hooks/usePhylogeneticData.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import useSWR from "swr";
2-
import { PhylogeneticNode } from "@/lib/types";
2+
import { PhylogeneticNode } from "../lib/types";
33

44
const fetcher = (url: string) => fetch(url).then((res) => res.json());
55

jest.config.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import type { Config } from 'jest';
2+
3+
const config: Config = {
4+
testEnvironment: 'jsdom',
5+
transform: {
6+
'^.+\\.(ts|tsx)$': ['ts-jest', { tsconfig: { jsx: 'react-jsx' } }],
7+
},
8+
moduleNameMapper: {
9+
'^@/(.*)$': '<rootDir>/src/$1',
10+
'\\.(css|less|scss|png|jpg|webp|svg)$': '<rootDir>/__mocks__/fileMock.js',
11+
},
12+
setupFilesAfterEnv: ['@testing-library/jest-dom'],
13+
testMatch: ['**/src/__tests__/**/*.test.ts', '**/src/tests/**/*.test.tsx'],
14+
};
15+
16+
export default config;

0 commit comments

Comments
 (0)