// @vitest-environment jsdom import { act, StrictMode } from 'react' import { createRoot, type Root } from 'react-dom/client' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { findCallerByNumber, type StoredAddress } from '../addresses' import type { CallEvent } from '../swyx/protocol' import { useCallerLookup } from './useCallerLookup' vi.mock('../addresses', () => ({ findCallerByNumber: vi.fn() })) const ringing: CallEvent = { line: 1, callId: 42, event: 'incoming', peerNumber: '+493012345' } const customer: StoredAddress = { number: ringing.peerNumber, name: 'Kunde', role: 'customer', jobIds: [4711] } function deferred() { let resolve!: (value: StoredAddress | null) => void let reject!: (reason: Error) => void const promise = new Promise((yes, no) => { resolve = yes; reject = no }) return { promise, resolve, reject } } describe('useCallerLookup', () => { let root: Root let element: HTMLDivElement let current: ReturnType function Probe({ call }: { call: CallEvent | null }) { current = useCallerLookup(call) return
{current.caller?.name ?? current.caller?.number ?? ''}
} async function render(call: CallEvent | null, strict = false) { await act(async () => { root.render(strict ? : ) }) } beforeEach(() => { vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true) vi.mocked(findCallerByNumber).mockReset() element = document.createElement('div') document.body.append(element) root = createRoot(element) }) afterEach(async () => { await act(async () => root.unmount()) element.remove() vi.unstubAllGlobals() }) it('shows the lookup result even when another snapshot arrives before it', async () => { const request = deferred() vi.mocked(findCallerByNumber).mockReturnValue(request.promise) await render(ringing) await render({ ...ringing, stateText: 'klingelt' }) await render({ ...ringing }) expect(findCallerByNumber).toHaveBeenCalledTimes(1) await act(async () => request.resolve(customer)) expect(element.textContent).toBe('Kunde') expect(current.caller?.jobIds).toEqual([4711]) }) it('ignores the previous lookup when the line is reused, even for the same number', async () => { const previous = deferred(), next = deferred() vi.mocked(findCallerByNumber).mockReturnValueOnce(previous.promise).mockReturnValueOnce(next.promise) await render(ringing) await render({ ...ringing, callId: 43 }) await act(async () => previous.resolve(customer)) expect(current.caller).toBeNull() await act(async () => next.resolve({ ...customer, name: 'Aktueller Kunde' })) expect(element.textContent).toBe('Aktueller Kunde') expect(findCallerByNumber).toHaveBeenCalledTimes(2) }) it('does not open a late popup after ringing ends', async () => { const request = deferred() vi.mocked(findCallerByNumber).mockReturnValue(request.promise) await render(ringing) await render(null) await act(async () => request.resolve(customer)) expect(current.caller).toBeNull() }) it('keeps loaded customer details after ringing ends', async () => { vi.mocked(findCallerByNumber).mockResolvedValue(customer) await render(ringing) await render(null) expect(current.caller).toEqual(customer) }) it('keeps a dismissed popup closed until the next call', async () => { const legacyCall = { ...ringing, callId: undefined } vi.mocked(findCallerByNumber).mockResolvedValue(customer) await render(legacyCall) await act(async () => current.dismissCaller()) await render({ ...legacyCall }) expect(current.caller).toBeNull() expect(findCallerByNumber).toHaveBeenCalledTimes(1) // Auch ältere Tray-Versionen ohne callId dürfen denselben Anschluss erneut melden. await render(null) await render(legacyCall) expect(current.caller).toEqual(customer) expect(findCallerByNumber).toHaveBeenCalledTimes(2) }) it('falls back to the phone number on a lookup failure', async () => { vi.mocked(findCallerByNumber).mockRejectedValue(new Error('Backend unavailable')) await render(ringing) expect(current.caller).toEqual({ number: ringing.peerNumber }) }) it('still loads the caller when React StrictMode restarts the effect', async () => { vi.mocked(findCallerByNumber).mockResolvedValue(customer) await render(ringing, true) expect(element.textContent).toBe('Kunde') }) })