Browse Source
feat(geo): integrate Flutter bridge get_location action for IP-based country and city detection
master
feat(geo): integrate Flutter bridge get_location action for IP-based country and city detection
master
3 changed files with 481 additions and 134 deletions
@ -0,0 +1,168 @@ |
|||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; |
|||
import { |
|||
getUserGeoRegion, |
|||
getStoredUserGeoRegion, |
|||
setStoredUserGeoRegion, |
|||
resetUserGeoRegionForTesting, |
|||
} from "./geo-region"; |
|||
import { http } from "./http"; |
|||
|
|||
vi.mock("./http", () => ({ |
|||
http: { |
|||
get: vi.fn(), |
|||
}, |
|||
})); |
|||
|
|||
describe("geo-region", () => { |
|||
const originalHabibApp = window.HabibApp; |
|||
const originalAddFlutterResponseListener = window.addFlutterResponseListener; |
|||
|
|||
beforeEach(() => { |
|||
vi.restoreAllMocks(); |
|||
localStorage.clear(); |
|||
resetUserGeoRegionForTesting(); |
|||
}); |
|||
|
|||
afterEach(() => { |
|||
window.HabibApp = originalHabibApp; |
|||
window.addFlutterResponseListener = originalAddFlutterResponseListener; |
|||
resetUserGeoRegionForTesting(); |
|||
}); |
|||
|
|||
describe("getStoredUserGeoRegion / setStoredUserGeoRegion", () => { |
|||
it("returns null when nothing is stored", () => { |
|||
expect(getStoredUserGeoRegion()).toBeNull(); |
|||
}); |
|||
|
|||
it("persists region and retrieves from memory and localStorage", () => { |
|||
const sample = { |
|||
ip: "1.2.3.4", |
|||
city: "Tehran", |
|||
country: "Iran", |
|||
countryCode: "IR", |
|||
phoneCode: "+98", |
|||
}; |
|||
setStoredUserGeoRegion(sample); |
|||
expect(getStoredUserGeoRegion()).toEqual(sample); |
|||
expect(localStorage.getItem("user_geo_region")).toBe( |
|||
JSON.stringify(sample), |
|||
); |
|||
expect(localStorage.getItem("geoIPPhoneCode")).toBe("+98"); |
|||
}); |
|||
}); |
|||
|
|||
describe("getUserGeoRegion", () => { |
|||
it("returns stored region immediately if not forced", async () => { |
|||
const cached = { |
|||
city: "London", |
|||
country: "United Kingdom", |
|||
countryCode: "GB", |
|||
phoneCode: "+44", |
|||
}; |
|||
setStoredUserGeoRegion(cached); |
|||
|
|||
const result = await getUserGeoRegion(false); |
|||
expect(result).toEqual(cached); |
|||
expect(http.get).not.toHaveBeenCalled(); |
|||
}); |
|||
|
|||
it("fetches via Flutter bridge when in Flutter WebView", async () => { |
|||
let listenerCallback: ((event: any) => void) | undefined; |
|||
window.addFlutterResponseListener = vi.fn().mockImplementation((cb) => { |
|||
listenerCallback = cb; |
|||
return () => {}; |
|||
}); |
|||
|
|||
const mockPostMessage = vi.fn().mockImplementation(() => { |
|||
setTimeout(() => { |
|||
listenerCallback?.({ |
|||
action: "get_location", |
|||
success: true, |
|||
data: { |
|||
ip: "5.6.7.8", |
|||
city: "Isfahan", |
|||
country: "Iran", |
|||
country_code: "IR", |
|||
}, |
|||
}); |
|||
}, 10); |
|||
}); |
|||
|
|||
window.HabibApp = { postMessage: mockPostMessage }; |
|||
|
|||
const result = await getUserGeoRegion(true); |
|||
|
|||
expect(mockPostMessage).toHaveBeenCalledWith( |
|||
JSON.stringify({ action: "get_location" }), |
|||
); |
|||
expect(result).toEqual({ |
|||
ip: "5.6.7.8", |
|||
city: "Isfahan", |
|||
country: "Iran", |
|||
countryCode: "IR", |
|||
phoneCode: "+98", |
|||
}); |
|||
expect(getStoredUserGeoRegion()?.countryCode).toBe("IR"); |
|||
}); |
|||
|
|||
it("fetches from HTTP backend when in standard browser", async () => { |
|||
delete (window as any).HabibApp; |
|||
|
|||
(http.get as any).mockResolvedValueOnce({ |
|||
data: { |
|||
ip: "10.0.0.1", |
|||
city: "Shiraz", |
|||
country: "Iran", |
|||
country_code: "IR", |
|||
}, |
|||
}); |
|||
|
|||
const result = await getUserGeoRegion(true); |
|||
|
|||
expect(http.get).toHaveBeenCalledWith("/account/auth/user/region/", { |
|||
timeout: 4000, |
|||
}); |
|||
expect(result).toEqual({ |
|||
ip: "10.0.0.1", |
|||
city: "Shiraz", |
|||
country: "Iran", |
|||
countryCode: "IR", |
|||
phoneCode: "+98", |
|||
}); |
|||
}); |
|||
|
|||
it("falls back to HTTP backend if Flutter bridge returns failure", async () => { |
|||
let listenerCallback: ((event: any) => void) | undefined; |
|||
window.addFlutterResponseListener = vi.fn().mockImplementation((cb) => { |
|||
listenerCallback = cb; |
|||
return () => {}; |
|||
}); |
|||
|
|||
const mockPostMessage = vi.fn().mockImplementation(() => { |
|||
setTimeout(() => { |
|||
listenerCallback?.({ |
|||
action: "get_location", |
|||
success: false, |
|||
}); |
|||
}, 10); |
|||
}); |
|||
|
|||
window.HabibApp = { postMessage: mockPostMessage }; |
|||
|
|||
(http.get as any).mockResolvedValueOnce({ |
|||
data: { |
|||
ip: "192.168.1.1", |
|||
city: "Mashhad", |
|||
country: "Iran", |
|||
country_code: "IR", |
|||
}, |
|||
}); |
|||
|
|||
const result = await getUserGeoRegion(true); |
|||
expect(result.city).toBe("Mashhad"); |
|||
expect(result.countryCode).toBe("IR"); |
|||
expect(result.country).toBe("Iran"); |
|||
expect(result.phoneCode).toBe("+98"); |
|||
}); |
|||
}); |
|||
}); |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue