import { registerAllListeners } from '../../src/router'; import { MessageType } from '../../src/models/process.model'; // Mocks TokenService jest.mock('../../src/services/token', () => { class TokenServiceMock { static async getInstance() { return new TokenServiceMock(); } async generateSessionToken(origin: string) { return { accessToken: `access-${origin}`, refreshToken: `refresh-${origin}` }; } async validateToken(token: string, origin: string) { return true; } async refreshAccessToken(refreshToken: string, origin: string) { return `new-access-${origin}`; } } return { __esModule: true, default: TokenServiceMock }; }); // Mocks Services jest.mock('../../src/services/service', () => { class ServicesMock { static instance: any; static async getInstance() { if (!ServicesMock.instance) ServicesMock.instance = new ServicesMock(); return ServicesMock.instance; } isPaired() { return true; } async confirmPairing() { /* no-op */ } getPairingProcessId() { return 'pair-123'; } async getProcesses() { return { 'p1': { id: 'p1' } as any }; } async getMyProcesses() { return ['p1', 'p2']; } async getProcess(id: string) { return { states: [{ state_id: 'a'.repeat(64), public_data: {}, pcd_commitment: {}, validation_tokens: [] }] } as any; } async decryptAttribute() { return null; } decodeValue(v: any) { return v; } getHashForFile() { return 'hash-abc'; } getMerkleProofForFile() { return { proof: true } as any; } validateMerkleProof() { return true; } async createPrdUpdate() { return {}; } async approveChange() { return { updated_process: { id: 'p1' } }; } async updateProcess() { return { updated_process: { id: 'p1' } }; } async createProcess() { return { updated_process: { process_id: 'pid', current_process: { states: [{ state_id: 'a'.repeat(64) }] } } } as any; } async handleApiReturn() { /* no-op */ } } return { __esModule: true, default: ServicesMock }; }); // Mocks ModalService (used by REQUEST_LINK) jest.mock('../../src/services/modal.service', () => { class ModalServiceMock { static async getInstance() { return new ModalServiceMock(); } async showConfirmationModal() { return true; } } return { __esModule: true, default: ModalServiceMock }; }); describe('postMessage interfaces (registerAllListeners)', () => { const ORIGIN = 'https://host.test'; let postSpy: jest.SpyInstance; beforeAll(async () => { postSpy = jest.spyOn(window.parent, 'postMessage'); await registerAllListeners(); }); afterEach(() => { postSpy.mockClear(); }); afterAll(() => { postSpy.mockRestore(); }); function dispatch(data: any) { window.dispatchEvent(new MessageEvent('message', { data, origin: ORIGIN })); } async function waitForLastCall(expectedType?: string, prevCount?: number) { const start = Date.now(); const timeoutMs = 1000; // eslint-disable-next-line no-constant-condition while (true) { const calls = postSpy.mock.calls; const countOk = prevCount === undefined ? calls.length > 0 : calls.length > (prevCount || 0); if (countOk) { const last = calls[calls.length - 1] as any; const payload = last && last[0]; if (!expectedType || (payload && payload.type === expectedType)) { return last; } } if (Date.now() - start > timeoutMs) throw new Error('Timeout waiting for postMessage'); await new Promise(r => setTimeout(r, 10)); } } it('REQUEST_LINK -> LINK_ACCEPTED avec tokens', async () => { const prev = postSpy.mock.calls.length; dispatch({ type: MessageType.REQUEST_LINK, messageId: 'm1' }); const [payload, targetOrigin] = (await waitForLastCall(MessageType.LINK_ACCEPTED, prev)) as any; expect(targetOrigin).toBe(ORIGIN); expect(payload.type).toBe(MessageType.LINK_ACCEPTED); expect(payload.accessToken).toContain('access-'); expect(payload.refreshToken).toContain('refresh-'); expect(payload.messageId).toBe('m1'); }); it('VALIDATE_TOKEN -> VALIDATE_TOKEN avec isValid=true', async () => { const prev = postSpy.mock.calls.length; dispatch({ type: MessageType.VALIDATE_TOKEN, accessToken: 'a', refreshToken: 'r', messageId: 'm2' }); const [payload, targetOrigin] = (await waitForLastCall(MessageType.VALIDATE_TOKEN, prev)) as any; expect(targetOrigin).toBe(ORIGIN); expect(payload.type).toBe(MessageType.VALIDATE_TOKEN); expect(payload.isValid).toBe(true); expect(payload.messageId).toBe('m2'); }); it('RENEW_TOKEN -> RENEW_TOKEN avec nouveau accessToken', async () => { const prev = postSpy.mock.calls.length; dispatch({ type: MessageType.RENEW_TOKEN, refreshToken: 'r', messageId: 'm3' }); const [payload, targetOrigin] = (await waitForLastCall(MessageType.RENEW_TOKEN, prev)) as any; expect(targetOrigin).toBe(ORIGIN); expect(payload.type).toBe(MessageType.RENEW_TOKEN); expect(typeof payload.accessToken).toBe('string'); expect(payload.refreshToken).toBe('r'); expect(payload.messageId).toBe('m3'); }); it('GET_PAIRING_ID -> GET_PAIRING_ID avec userPairingId', async () => { const prev = postSpy.mock.calls.length; dispatch({ type: MessageType.GET_PAIRING_ID, accessToken: 'a', messageId: 'm4' }); const [payload, targetOrigin] = (await waitForLastCall(MessageType.GET_PAIRING_ID, prev)) as any; expect(targetOrigin).toBe(ORIGIN); expect(payload.type).toBe(MessageType.GET_PAIRING_ID); expect(payload.userPairingId).toBe('pair-123'); expect(payload.messageId).toBe('m4'); }); it('GET_MY_PROCESSES -> GET_MY_PROCESSES avec liste', async () => { const prev = postSpy.mock.calls.length; dispatch({ type: MessageType.GET_MY_PROCESSES, accessToken: 'a', messageId: 'm5' }); const [payload, targetOrigin] = (await waitForLastCall(MessageType.GET_MY_PROCESSES, prev)) as any; expect(targetOrigin).toBe(ORIGIN); expect(payload.type).toBe(MessageType.GET_MY_PROCESSES); expect(Array.isArray(payload.myProcesses)).toBe(true); expect(payload.messageId).toBe('m5'); }); it('HASH_VALUE -> VALUE_HASHED avec hash', async () => { const prev = postSpy.mock.calls.length; dispatch({ type: MessageType.HASH_VALUE, accessToken: 'a', commitedIn: 'c', label: 'L', fileBlob: { type: 'application/octet-stream', data: new Uint8Array([1]) }, messageId: 'm6' }); const [payload, targetOrigin] = (await waitForLastCall(MessageType.VALUE_HASHED, prev)) as any; expect(targetOrigin).toBe(ORIGIN); expect(payload.type).toBe(MessageType.VALUE_HASHED); expect(payload.hash).toBe('hash-abc'); expect(payload.messageId).toBe('m6'); }); });