89 lines
3.9 KiB
TypeScript
89 lines
3.9 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { readFileSync } from "node:fs";
|
|
import test from "node:test";
|
|
import React from "react";
|
|
import { renderToStaticMarkup } from "react-dom/server";
|
|
import {
|
|
LocationSearchCombobox,
|
|
parseLocationSearchResults,
|
|
type ResolvedBirthLocation,
|
|
} from "../src/components/location-search-combobox.tsx";
|
|
|
|
Object.assign(globalThis, { React });
|
|
|
|
const taipei: ResolvedBirthLocation = {
|
|
id: "nominatim:1293250",
|
|
label: "台北市, 台湾",
|
|
placeType: "city",
|
|
provider: "nominatim",
|
|
providerPlaceId: "1293250",
|
|
countryCode: "TW",
|
|
countryName: "台湾",
|
|
admin1: "台北市",
|
|
admin2: "",
|
|
locality: "台北市",
|
|
latitude: 25.0375,
|
|
longitude: 121.5637,
|
|
timezoneId: "Asia/Taipei",
|
|
timezoneOffset: 8,
|
|
timezoneSource: "historical_tzdb",
|
|
};
|
|
|
|
test("location search parser keeps only complete coordinate and timezone results", () => {
|
|
assert.deepEqual(parseLocationSearchResults({
|
|
locations: [
|
|
{ ...taipei, id: undefined, regionName: taipei.admin1, localityName: taipei.locality },
|
|
{ ...taipei, id: "", providerPlaceId: "", label: "invalid" },
|
|
{ ...taipei, id: "mapbox:unknown-time", timezoneOffset: null },
|
|
],
|
|
}), [taipei, { ...taipei, id: "mapbox:unknown-time", timezoneOffset: null }]);
|
|
assert.deepEqual(parseLocationSearchResults({ results: "not-an-array" }), []);
|
|
});
|
|
|
|
test("location combobox exposes accessible search semantics and selected state", () => {
|
|
const emptyMarkup = renderToStaticMarkup(React.createElement(LocationSearchCombobox, {
|
|
value: null,
|
|
birthDate: "1997-08-08",
|
|
birthTime: "06:30",
|
|
onChange: () => undefined,
|
|
}));
|
|
assert.match(emptyMarkup, /role="combobox"/);
|
|
assert.match(emptyMarkup, /aria-autocomplete="list"/);
|
|
assert.match(emptyMarkup, /aria-expanded="false"/);
|
|
assert.match(emptyMarkup, /aria-controls="[^"]+-listbox"/);
|
|
assert.match(emptyMarkup, /输入至少两个字开始搜索/);
|
|
|
|
const selectedMarkup = renderToStaticMarkup(React.createElement(LocationSearchCombobox, {
|
|
value: taipei,
|
|
onChange: () => undefined,
|
|
}));
|
|
assert.match(selectedMarkup, /value="台北市, 台湾"/);
|
|
assert.match(selectedMarkup, /已选择 台北市, 台湾/);
|
|
assert.match(selectedMarkup, /aria-label="清除出生地点"/);
|
|
});
|
|
|
|
test("profile integrates global location persistence while retaining legacy China fallback", () => {
|
|
const pageSource = readFileSync(new URL("../src/app/page.tsx", import.meta.url), "utf8");
|
|
assert.match(pageSource, /birthPlaceProviderId:\s*string/);
|
|
assert.match(pageSource, /birth_place_provider_id:\s*nextProfile\.birthPlaceProviderId/);
|
|
assert.match(pageSource, /timezone_id:\s*nextProfile\.timezoneId/);
|
|
assert.match(pageSource, /function selectedLocationValue/);
|
|
assert.match(pageSource, /legacy-cn:/);
|
|
assert.match(pageSource, /provinceCode:\s*""[\s\S]*cityCode:\s*""[\s\S]*districtCode:\s*""/);
|
|
assert.doesNotMatch(pageSource, /目前先支持中国大陆地区/);
|
|
});
|
|
|
|
test("selecting a location cancels stale searches and keeps the result list outside the onboarding card", () => {
|
|
const componentSource = readFileSync(new URL("../src/components/location-search-combobox.tsx", import.meta.url), "utf8");
|
|
const globalStyles = readFileSync(new URL("../src/app/globals.css", import.meta.url), "utf8");
|
|
assert.match(componentSource, /function choose\(location: ResolvedBirthLocation\) \{\s*requestSequence\.current \+= 1;/);
|
|
assert.match(globalStyles, /\.birth-time-transition-card \{ position: relative; overflow: visible; \}/);
|
|
});
|
|
|
|
test("profile accepts a selected IANA location before a numeric offset is resolved", () => {
|
|
const pageSource = readFileSync(new URL("../src/app/page.tsx", import.meta.url), "utf8");
|
|
assert.match(pageSource, /profile\.timezoneId\.trim\(\)/);
|
|
assert.match(pageSource, /profile\.timezoneOffset === null \|\| Number\.isFinite\(profile\.timezoneOffset\)/);
|
|
assert.match(pageSource, /timezoneId: birthPlace\.timezoneId/);
|
|
});
|