diff --git a/src/ui/city-autocomplete/CityAutocomplete.test.tsx b/src/ui/city-autocomplete/CityAutocomplete.test.tsx index 51eec265..65f265d2 100644 --- a/src/ui/city-autocomplete/CityAutocomplete.test.tsx +++ b/src/ui/city-autocomplete/CityAutocomplete.test.tsx @@ -150,6 +150,60 @@ describe("CityAutocomplete", () => { expect(container.querySelector(".city-autocomplete__input--has-error")).toBeTruthy(); }); + it("auto-commits a typed city name that exactly matches a dictionary entry", () => { + const onChange = vi.fn(); + render( + , + ); + fireEvent.change(screen.getByTestId("test-input"), { + target: { value: "Москва" }, + }); + expect(onChange).toHaveBeenCalledWith("MOW"); + }); + + it("auto-commits a typed airport name to its owning city code", () => { + const onChange = vi.fn(); + render( + , + ); + fireEvent.change(screen.getByTestId("test-input"), { + target: { value: "Шереметьево" }, + }); + expect(onChange).toHaveBeenCalledWith("MOW"); + }); + + it("does not auto-commit partial typed text", () => { + const onChange = vi.fn(); + render( + , + ); + fireEvent.change(screen.getByTestId("test-input"), { + target: { value: "Мос" }, + }); + expect(onChange).not.toHaveBeenCalled(); + }); + it("does not open popup when dictionaries is null", () => { render( = ({ return () => document.removeEventListener("mousedown", handler); }, []); + // Auto-commit free-typed text that exactly matches a city or airport + // name. Without this, a user who types "Москва" and clicks the search + // button without picking from the dropdown silently fails: the + // parent-held `value` (city code) stays empty while the AutoComplete's + // local input holds the typed string. Exact name match (ru + en) is + // safe — partial text is ignored so the picker still wins for + // disambiguation. + useEffect(() => { + if (!dictionaries || value) return; + if (typeof inputValue !== "string") return; + const trimmed = inputValue.trim(); + if (!trimmed) return; + const lc = trimmed.toLowerCase(); + const cityMatch = dictionaries.cities.find( + (c) => c.name.toLowerCase() === lc, + ); + if (cityMatch) { + onChange(cityMatch.code); + return; + } + const airportMatch = dictionaries.airports.find( + (a) => a.name.toLowerCase() === lc, + ); + if (airportMatch) { + onChange(airportMatch.city_code.toUpperCase()); + } + }, [inputValue, dictionaries, value, onChange]); + const handleComplete = useCallback( (event: AutoCompleteCompleteEvent) => { if (!dictionaries) {