Resolve popular-request codes to city/airport names
useCityName was a 'phase 2 stub' that returned the IATA code unchanged, so the popular-requests panel read 'Маршрут: SVO - LED' instead of 'Маршрут: Шереметьево - Санкт-Петербург'. Rewire the hook to read the current locale and look up cityByCode / airportByCode from the loaded dictionaries, falling back to the code only while dictionaries are loading or for unknown codes (matches Angular's DictionariesService.getCityOrAirport). Tests expanded to cover the city/airport resolutions and the loading / unknown fallback paths (5 total, 1258 suite green).
This commit is contained in:
@@ -1,18 +1,74 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
// @vitest-environment jsdom
|
||||
import { describe, it, expect, vi } from "vitest";
|
||||
import { renderHook } from "@testing-library/react";
|
||||
import { useCityName } from "./useDictionaries.js";
|
||||
|
||||
vi.mock("@modern-js/runtime/router", () => ({
|
||||
useParams: () => ({ lang: "ru" }),
|
||||
}));
|
||||
|
||||
const mockDictionariesState = vi.fn();
|
||||
vi.mock("@/shared/dictionaries/useDictionaries.js", () => ({
|
||||
useDictionaries: () => mockDictionariesState(),
|
||||
}));
|
||||
|
||||
describe("useCityName", () => {
|
||||
it("returns the code itself (Phase 2 passthrough)", () => {
|
||||
expect(useCityName("SVO")).toBe("SVO");
|
||||
it("returns the code when dictionaries are still loading", () => {
|
||||
mockDictionariesState.mockReturnValue({
|
||||
dictionaries: null,
|
||||
loading: true,
|
||||
error: null,
|
||||
});
|
||||
const { result } = renderHook(() => useCityName("SVO"));
|
||||
expect(result.current).toBe("SVO");
|
||||
});
|
||||
|
||||
it("returns the code for any input", () => {
|
||||
expect(useCityName("LED")).toBe("LED");
|
||||
expect(useCityName("MOW")).toBe("MOW");
|
||||
expect(useCityName("JFK")).toBe("JFK");
|
||||
it("resolves a city code to the localized name", () => {
|
||||
mockDictionariesState.mockReturnValue({
|
||||
dictionaries: {
|
||||
cityByCode: new Map([["MOW", { name: "Москва" }]]),
|
||||
airportByCode: new Map(),
|
||||
},
|
||||
loading: false,
|
||||
error: null,
|
||||
});
|
||||
const { result } = renderHook(() => useCityName("MOW"));
|
||||
expect(result.current).toBe("Москва");
|
||||
});
|
||||
|
||||
it("resolves an airport code to the localized name", () => {
|
||||
mockDictionariesState.mockReturnValue({
|
||||
dictionaries: {
|
||||
cityByCode: new Map(),
|
||||
airportByCode: new Map([["SVO", { name: "Шереметьево" }]]),
|
||||
},
|
||||
loading: false,
|
||||
error: null,
|
||||
});
|
||||
const { result } = renderHook(() => useCityName("SVO"));
|
||||
expect(result.current).toBe("Шереметьево");
|
||||
});
|
||||
|
||||
it("falls back to the code for unknown lookups", () => {
|
||||
mockDictionariesState.mockReturnValue({
|
||||
dictionaries: {
|
||||
cityByCode: new Map(),
|
||||
airportByCode: new Map(),
|
||||
},
|
||||
loading: false,
|
||||
error: null,
|
||||
});
|
||||
const { result } = renderHook(() => useCityName("ZZZ"));
|
||||
expect(result.current).toBe("ZZZ");
|
||||
});
|
||||
|
||||
it("handles empty string", () => {
|
||||
expect(useCityName("")).toBe("");
|
||||
mockDictionariesState.mockReturnValue({
|
||||
dictionaries: null,
|
||||
loading: true,
|
||||
error: null,
|
||||
});
|
||||
const { result } = renderHook(() => useCityName(""));
|
||||
expect(result.current).toBe("");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,22 +1,31 @@
|
||||
/**
|
||||
* Airport/city dictionary hooks.
|
||||
*
|
||||
* Phase 2 stub: returns the IATA code itself as the city name.
|
||||
* The real implementation will call the customer's dictionary API
|
||||
* (similar to Angular DictionariesService.getCityOrAirport).
|
||||
* Resolves an IATA code to its human-readable city/airport name using the
|
||||
* loaded dictionaries. Falls back to the code itself while dictionaries
|
||||
* are loading or when the code is unknown.
|
||||
*
|
||||
* TODO: Replace passthrough with actual API call once the dictionary
|
||||
* endpoint is provided by the customer. The Angular app loads dictionaries
|
||||
* via networkService.getDictionary('cities') / getDictionary('airports')
|
||||
* and builds an in-memory Map<code, CityModel>.
|
||||
* Angular equivalent: DictionariesService.getCityOrAirport, which walks
|
||||
* a Map<code, CityModel | AirportModel>.
|
||||
*/
|
||||
|
||||
import { useParams } from "@modern-js/runtime/router";
|
||||
import { useDictionaries as useDictionariesState } from "@/shared/dictionaries/useDictionaries.js";
|
||||
|
||||
/**
|
||||
* Returns the city name for a given IATA airport/city code.
|
||||
*
|
||||
* Phase 2: passthrough — returns the code itself.
|
||||
* Returns the city/airport name for a given IATA code. Uses the locale
|
||||
* from the current URL. Returns the code itself until dictionaries load
|
||||
* or if the code isn't in the dictionary (matches Angular's fallback —
|
||||
* see DictionariesService.getCityOrAirport).
|
||||
*/
|
||||
export function useCityName(code: string): string {
|
||||
// TODO: Look up from dictionary cache/API when available
|
||||
const { lang } = useParams<{ lang: string }>();
|
||||
const { dictionaries } = useDictionariesState(lang ?? "ru");
|
||||
if (!code || !dictionaries) return code;
|
||||
const upper = code.toUpperCase();
|
||||
const city = dictionaries.cityByCode.get(upper);
|
||||
if (city) return city.name;
|
||||
const airport = dictionaries.airportByCode.get(upper);
|
||||
if (airport) return airport.name;
|
||||
return code;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user