@ -15,6 +15,14 @@ const mockSetAnswerValue = vi.fn((q: QuestionField, val: unknown) => {
answerMap [ q . id ] = val ;
} ) ;
const httpMocks = vi . hoisted ( ( ) = > ( {
get : vi . fn ( ) ,
} ) ) ;
vi . mock ( "@/lib/http" , ( ) = > ( {
http : { get : httpMocks . get } ,
} ) ) ;
vi . mock ( "@/translations/provider" , ( ) = > ( {
useI18n : ( ) = > ( {
locale : "en" ,
@ -62,8 +70,6 @@ const phoneQuestion2: QuestionField = {
options : [ ] ,
} ;
describe ( "QuestionPhone IP country detection and shimmer" , ( ) = > {
beforeEach ( ( ) = > {
answerMap = { } ;
@ -71,13 +77,47 @@ describe("QuestionPhone IP country detection and shimmer", () => {
localStorage . clear ( ) ;
resetGeoPhoneStateForTesting ( ) ;
vi . restoreAllMocks ( ) ;
httpMocks . get . mockReset ( ) ;
} ) ;
afterEach ( ( ) = > {
cleanup ( ) ;
} ) ;
it ( "renders shimmer on country button while IP request is pending, then shows resolved country code" , async ( ) = > {
it ( "renders single unified shimmer on country button while IP request is pending, then shows resolved country code from Habib user region API" , async ( ) = > {
let resolveRegion ! : ( value : unknown ) = > void ;
const regionPromise = new Promise ( ( resolve ) = > {
resolveRegion = resolve ;
} ) ;
httpMocks . get . mockReturnValue ( regionPromise ) ;
const { container } = render ( < QuestionPhone question = { phoneQuestion1 } / > ) ;
const shimmerElements = container . querySelectorAll ( ".shimmer-bg" ) ;
expect ( shimmerElements . length ) . toBe ( 1 ) ;
const input = screen . getByRole ( "textbox" ) ;
expect ( input . classList . contains ( "shimmer-bg" ) ) . toBe ( false ) ;
await act ( async ( ) = > {
resolveRegion ( {
data : {
country : "Iran" ,
country_code : "IR" ,
} ,
} ) ;
} ) ;
await waitFor ( ( ) = > {
expect ( container . querySelectorAll ( ".shimmer-bg" ) . length ) . toBe ( 0 ) ;
expect ( screen . getByText ( "+98" ) ) . toBeDefined ( ) ;
expect ( screen . getByText ( "🇮🇷" ) ) . toBeDefined ( ) ;
} ) ;
} ) ;
it ( "falls back to secondary fetch when Habib region API fails and shows resolved code" , async ( ) = > {
httpMocks . get . mockRejectedValue ( new Error ( "Network failure" ) ) ;
let resolveIpFetch ! : ( value : unknown ) = > void ;
const ipPromise = new Promise ( ( resolve ) = > {
resolveIpFetch = resolve ;
@ -95,11 +135,6 @@ describe("QuestionPhone IP country detection and shimmer", () => {
const { container } = render ( < QuestionPhone question = { phoneQuestion1 } / > ) ;
const shimmerElements = container . querySelectorAll ( ".shimmer-bg" ) ;
expect ( shimmerElements . length ) . toBeGreaterThan ( 0 ) ;
const input = screen . getByRole ( "textbox" ) ;
expect ( input . classList . contains ( "shimmer-bg" ) ) . toBe ( false ) ;
await act ( async ( ) = > {
resolveIpFetch ( { country_calling_code : "+98" } ) ;
} ) ;
@ -111,7 +146,8 @@ describe("QuestionPhone IP country detection and shimmer", () => {
} ) ;
} ) ;
it ( "shows default country code when IP request fails" , async ( ) = > {
it ( "shows default country code when all IP requests fail" , async ( ) = > {
httpMocks . get . mockRejectedValue ( new Error ( "Network failure" ) ) ;
vi . spyOn ( globalThis , "fetch" ) . mockRejectedValue (
new Error ( "Network failure" ) ,
) ;
@ -128,20 +164,12 @@ describe("QuestionPhone IP country detection and shimmer", () => {
} ) ;
it ( "fetches IP country code only once when multiple fields are rendered and updates both" , async ( ) = > {
let resolveIpFetch ! : ( value : unknown ) = > void ;
const ip Promise = new Promise ( ( resolve ) = > {
resolveIpFetch = resolve ;
let resolveRegion ! : ( value : unknown ) = > void ;
const region Promise = new Promise ( ( resolve ) = > {
resolveRegion = resolve ;
} ) ;
const fetchSpy = vi . spyOn ( globalThis , "fetch" ) . mockImplementation ( ( ) = >
ipPromise . then (
( data ) = >
( {
ok : true ,
json : async ( ) = > data ,
} ) as unknown as Response ,
) ,
) ;
httpMocks . get . mockReturnValue ( regionPromise ) ;
render (
< >
@ -150,10 +178,15 @@ describe("QuestionPhone IP country detection and shimmer", () => {
< / > ,
) ;
expect ( fetchSpy ) . toHaveBeenCalledTimes ( 1 ) ;
expect ( httpMocks . get ) . toHaveBeenCalledTimes ( 1 ) ;
await act ( async ( ) = > {
resolveIpFetch ( { country_calling_code : "+98" } ) ;
resolveRegion ( {
data : {
country : "Iran" ,
country_code : "IR" ,
} ,
} ) ;
} ) ;
await waitFor ( ( ) = > {
@ -170,31 +203,21 @@ describe("QuestionPhone IP country detection and shimmer", () => {
phoneNumber : "2025550143" ,
} ;
const fetchSpy = vi . spyOn ( globalThis , "fetch" ) ;
const { container } = render ( < QuestionPhone question = { phoneQuestion1 } / > ) ;
expect ( container . querySelectorAll ( ".shimmer-bg" ) . length ) . toBe ( 0 ) ;
expect ( screen . getByText ( "+1" ) ) . toBeDefined ( ) ;
expect ( screen . getByDisplayValue ( "2025550143" ) ) . toBeDefined ( ) ;
expect ( fetchSpy ) . not . toHaveBeenCalled ( ) ;
expect ( httpMocks . get ) . not . toHaveBeenCalled ( ) ;
} ) ;
it ( "does not overwrite manual selection when user manually interacts" , async ( ) = > {
let resolveIpFetch ! : ( value : unknown ) = > void ;
const ip Promise = new Promise ( ( resolve ) = > {
resolveIpFetch = resolve ;
let resolveRegion ! : ( value : unknown ) = > void ;
const region Promise = new Promise ( ( resolve ) = > {
resolveRegion = resolve ;
} ) ;
vi . spyOn ( globalThis , "fetch" ) . mockImplementation ( ( ) = >
ipPromise . then (
( data ) = >
( {
ok : true ,
json : async ( ) = > data ,
} ) as unknown as Response ,
) ,
) ;
httpMocks . get . mockReturnValue ( regionPromise ) ;
render ( < QuestionPhone question = { phoneQuestion1 } / > ) ;
@ -202,7 +225,12 @@ describe("QuestionPhone IP country detection and shimmer", () => {
fireEvent . change ( input , { target : { value : "123456" } } ) ;
await act ( async ( ) = > {
resolveIpFetch ( { country_calling_code : "+98" } ) ;
resolveRegion ( {
data : {
country : "Iran" ,
country_code : "IR" ,
} ,
} ) ;
} ) ;
expect ( screen . getByDisplayValue ( "123456" ) ) . toBeDefined ( ) ;