Files
TREK/client/tests/helpers/atlas.ts
T

150 lines
5.0 KiB
TypeScript

import { vi } from 'vitest';
import type { AtlasController } from '../../src/mobile/screens/atlas/atlasController';
import type { AtlasData, BucketItem, CountryDetail } from '../../src/pages/atlas/atlasModel';
/**
* Fixtures for the atlas surfaces. Desktop page, mobile screen, bucket sheet
* and country popup all consume the same useAtlas() controller, so tests build
* one object here instead of mounting the hook with a Leaflet map behind it.
*
* The state setters are spies that also write the new value back onto the
* controller (including updater callbacks), so a test can assert both "the
* setter was called" and "this is the state it produced".
*/
export type VisitedRegions = Record<string, { code: string; name: string; placeCount: number; manuallyMarked?: boolean }[]>;
const echoT = (key: string, params?: Record<string, string | number>): string =>
params ? `${key}:${Object.values(params).join(',')}` : key;
export function buildAtlasData(over: Partial<AtlasData> = {}): AtlasData {
return {
countries: [{ code: 'FR', tripCount: 2, placeCount: 5, firstVisit: '2023-01-01', lastVisit: '2024-06-01' }],
stats: { totalTrips: 3, totalPlaces: 10, totalCountries: 1, totalDays: 14, totalCities: 3 },
mostVisited: null,
continents: { Europe: 1 },
lastTrip: { id: 7, title: 'Paris Trip', countryCode: 'FR' },
nextTrip: null,
streak: 2,
firstYear: 2022,
tripsThisYear: 1,
...over,
};
}
export function buildBucketItem(over: Partial<BucketItem> = {}): BucketItem {
return {
id: 1,
name: 'Kyoto',
lat: null,
lng: null,
country_code: 'JP',
notes: null,
target_date: null,
...over,
};
}
export function buildCountryDetail(over: Partial<CountryDetail> = {}): CountryDetail {
return {
places: [],
trips: [],
manually_marked: false,
...over,
};
}
/** state field → its setter on the controller. */
const SETTERS: Record<string, string> = {
data: 'setData',
visitedRegions: 'setVisitedRegions',
bucketList: 'setBucketList',
confirmAction: 'setConfirmAction',
bucketForm: 'setBucketForm',
bucketSearch: 'setBucketSearch',
bucketSearchResults: 'setBucketSearchResults',
showBucketAdd: 'setShowBucketAdd',
bucketTab: 'setBucketTab',
bucketMonth: 'setBucketMonth',
bucketYear: 'setBucketYear',
bucketPoiMonth: 'setBucketPoiMonth',
bucketPoiYear: 'setBucketPoiYear',
atlas_country_search: 'set_atlas_country_search',
atlas_country_results: 'set_atlas_country_results',
atlas_country_open: 'set_atlas_country_open',
};
const COUNTRY_NAMES: Record<string, string> = {
AR: 'Argentina', DE: 'Germany', ES: 'Spain', FR: 'France', GR: 'Greece',
IT: 'Italy', JP: 'Japan', PE: 'Peru', PT: 'Portugal', US: 'United States',
};
export function buildAtlasController(over: Record<string, unknown> = {}): AtlasController {
const base: Record<string, unknown> = {
t: echoT,
language: 'en',
navigate: vi.fn(() => undefined),
resolveName: (code: string) => COUNTRY_NAMES[code] ?? code,
dark: false,
loading: false,
mapRef: { current: null },
regionTooltipRef: { current: null },
panelRef: { current: null },
glareRef: { current: null },
borderGlareRef: { current: null },
handlePanelMouseMove: vi.fn(() => undefined),
handlePanelMouseLeave: vi.fn(() => undefined),
data: buildAtlasData(),
stats: buildAtlasData().stats,
countries: buildAtlasData().countries,
selectedCountry: null,
countryDetail: null,
loadCountryDetail: vi.fn(async () => undefined),
handleUnmarkCountry: vi.fn(() => undefined),
select_country_from_search: vi.fn(() => undefined),
visitedRegions: {} as VisitedRegions,
atlas_country_search: '',
atlas_country_results: [],
atlas_country_open: false,
atlas_country_options: [],
confirmAction: null,
executeConfirmAction: vi.fn(async () => undefined),
bucketMonth: 0,
bucketYear: 0,
bucketList: [] as BucketItem[],
bucketTab: 'stats',
showBucketAdd: false,
bucketForm: { name: '', notes: '', lat: '', lng: '', target_date: '' },
handleAddBucketItem: vi.fn(async () => undefined),
handleDeleteBucketItem: vi.fn(async () => undefined),
handleBucketPoiSearch: vi.fn(async () => undefined),
handleSelectBucketPoi: vi.fn(() => undefined),
bucketSearchResults: [],
bucketPoiMonth: 0,
bucketPoiYear: 0,
bucketSearching: false,
bucketSearch: '',
};
const ctrl: Record<string, unknown> = { ...base, ...over };
for (const [field, setter] of Object.entries(SETTERS)) {
if (setter in over) continue;
ctrl[setter] = vi.fn((next: unknown) => {
ctrl[field] = typeof next === 'function' ? (next as (prev: unknown) => unknown)(ctrl[field]) : next;
});
}
// stats/countries mirror data unless a test pinned them explicitly.
const data = ctrl.data as AtlasData | null;
if (!('stats' in over)) ctrl.stats = data?.stats ?? { totalTrips: 0, totalPlaces: 0, totalCountries: 0, totalDays: 0 };
if (!('countries' in over)) ctrl.countries = data?.countries ?? [];
return ctrl as unknown as AtlasController;
}