import { WidgetEventService } from '../services/WidgetEventService'; import type { WidgetCallbacks } from '../domain'; import type { WidgetStateManager } from '../services/WidgetStateManager'; describe('WidgetEventService', () => { let mockHide: jest.Mock; let mockResize: jest.Mock; let mockUserId: string; let mockTransactionId: string; let mockCallbacks: WidgetCallbacks; let mockStateManager: jest.Mocked; let service: WidgetEventService; beforeEach(() => { jest.clearAllMocks(); mockHide = jest.fn(); mockResize = jest.fn(); mockUserId = 'test-user-123'; mockTransactionId = 'txn-456'; mockCallbacks = { onClosed: jest.fn(), onError: jest.fn(), onPageChanged: jest.fn(), onQuestionAnswered: jest.fn(), onCompleted: jest.fn(), onPartialCompleted: jest.fn(), onResize: jest.fn(), }; mockStateManager = { getLogs: jest.fn(), saveLogs: jest.fn(), incrementAttempt: jest.fn(), resetAttempts: jest.fn(), markTransactionAnswered: jest.fn(), hasAnsweredTransaction: jest.fn(), clearLogs: jest.fn(), hasLogs: jest.fn(), updateTimestamp: jest.fn(), overrideTimestamp: jest.fn(), } as unknown as jest.Mocked; service = new WidgetEventService( mockHide, mockResize, mockUserId, mockTransactionId, mockCallbacks, mockStateManager, ); }); afterEach(() => { jest.clearAllMocks(); jest.restoreAllMocks(); }); it('should handle FORM_CLOSE event correctly', async () => { const result = await service.handleMessage('FORM_CLOSE', true); expect(mockHide).toHaveBeenCalledTimes(1); expect(mockCallbacks.onClosed).toHaveBeenCalledTimes(1); expect(mockStateManager.updateTimestamp).not.toHaveBeenCalled(); expect(result).toEqual({ status: 'success' }); }); it('should handle FORM_RESIZE event correctly', async () => { const result = await service.handleMessage('FORM_RESIZE-350', true); expect(mockResize).toHaveBeenCalledWith('350'); expect(mockCallbacks.onResize).toHaveBeenCalledWith('350'); expect(result).toEqual({ status: 'success' }); }); it('should handle FORM_ERROR event correctly', async () => { const result = await service.handleMessage('FORM_ERROR-Something went wrong', true); expect(mockHide).toHaveBeenCalledTimes(1); expect(mockCallbacks.onError).toHaveBeenCalledWith('Something went wrong'); expect(result).toEqual({ status: 'error', message: 'Something went wrong' }); }); it('should preserve hyphenated FORM_ERROR payloads', async () => { const result = await service.handleMessage('FORM_ERROR-timeout-api-gateway', true); expect(mockHide).toHaveBeenCalledTimes(1); expect(mockCallbacks.onError).toHaveBeenCalledWith('timeout-api-gateway'); expect(result).toEqual({ status: 'error', message: 'timeout-api-gateway' }); }); it('should handle FORM_PAGECHANGED event correctly', async () => { const result = await service.handleMessage('FORM_PAGECHANGED-page2', true); expect(mockCallbacks.onPageChanged).toHaveBeenCalledWith('page2'); expect(result).toEqual({ status: 'success' }); }); it('should handle QUESTION_ANSWERED event correctly', async () => { const result = await service.handleMessage('QUESTION_ANSWERED', true); expect(mockStateManager.resetAttempts).toHaveBeenCalledTimes(1); expect(mockCallbacks.onQuestionAnswered).toHaveBeenCalledTimes(1); expect(result).toEqual({ status: 'success' }); }); it('should handle FORM_COMPLETED event correctly', async () => { const result = await service.handleMessage('FORM_COMPLETED', true); expect(mockStateManager.resetAttempts).toHaveBeenCalledTimes(1); expect(mockStateManager.markTransactionAnswered).toHaveBeenCalledWith('txn-456'); expect(mockStateManager.updateTimestamp).toHaveBeenCalledWith('lastSubmit'); expect(mockCallbacks.onCompleted).toHaveBeenCalledWith('test-user-123'); expect(result).toEqual({ status: 'success' }); }); it('should handle FORM_PARTIALCOMPLETED event correctly', async () => { const result = await service.handleMessage('FORM_PARTIALCOMPLETED', true); expect(mockStateManager.resetAttempts).toHaveBeenCalledTimes(1); expect(mockStateManager.markTransactionAnswered).toHaveBeenCalledWith('txn-456'); expect(mockStateManager.updateTimestamp).toHaveBeenCalledWith('lastPartialSubmit'); expect(mockCallbacks.onPartialCompleted).toHaveBeenCalledWith('test-user-123'); expect(result).toEqual({ status: 'success' }); }); it('should not mark a transaction as answered on QUESTION_ANSWERED', async () => { await service.handleMessage('QUESTION_ANSWERED', true); expect(mockStateManager.markTransactionAnswered).not.toHaveBeenCalled(); }); it('should return error for unknown events', async () => { const result = await service.handleMessage('UNKNOWN_EVENT', true); expect(result).toEqual({ status: 'error', message: 'Unknown event' }); }); it('should return error when FORM_OPENED is sent since it was removed from event handlers', async () => { const result = await service.handleMessage('FORM_OPENED', true); expect(result).toEqual({ status: 'error', message: 'Unknown event' }); }); it('should adapt closeSoluCXWidget to FORM_CLOSE', async () => { const result = await service.handleMessage('closeSoluCXWidget', false); expect(mockHide).toHaveBeenCalledTimes(1); expect(mockCallbacks.onClosed).toHaveBeenCalledTimes(1); expect(mockStateManager.updateTimestamp).not.toHaveBeenCalled(); expect(result).toEqual({ status: 'success' }); }); it('should adapt completeSoluCXWidget to FORM_COMPLETED', async () => { const result = await service.handleMessage('completeSoluCXWidget', false); expect(mockCallbacks.onCompleted).toHaveBeenCalledWith('test-user-123'); expect(result).toEqual({ status: 'success' }); }); it('should adapt partialSoluCXWidget to FORM_PARTIALCOMPLETED', async () => { const result = await service.handleMessage('partialSoluCXWidget', false); expect(mockCallbacks.onPartialCompleted).toHaveBeenCalledWith('test-user-123'); expect(result).toEqual({ status: 'success' }); }); it('should adapt dismissSoluCXWidget to FORM_CLOSE', async () => { const result = await service.handleMessage('dismissSoluCXWidget', false); expect(mockHide).toHaveBeenCalledTimes(1); expect(mockCallbacks.onClosed).toHaveBeenCalledTimes(1); expect(mockStateManager.updateTimestamp).not.toHaveBeenCalled(); expect(result).toEqual({ status: 'success' }); }); it('should adapt errorSoluCXWidget to FORM_ERROR', async () => { const result = await service.handleMessage('errorSoluCXWidget-Network error', false); expect(mockHide).toHaveBeenCalledTimes(1); expect(mockCallbacks.onError).toHaveBeenCalledWith('Network error'); expect(result).toEqual({ status: 'error', message: 'Network error' }); }); it('should adapt resizeSoluCXWidget to FORM_RESIZE', async () => { const result = await service.handleMessage('resizeSoluCXWidget-400', false); expect(mockResize).toHaveBeenCalledWith('400'); expect(mockCallbacks.onResize).toHaveBeenCalledWith('400'); expect(result).toEqual({ status: 'success' }); }); it('should adapt openSoluCXWidget to FORM_OPENED which returns unknown event', async () => { const result = await service.handleMessage('openSoluCXWidget', false); expect(result).toEqual({ status: 'error', message: 'Unknown event' }); }); it('should not call any callback when event is unknown', async () => { await service.handleMessage('SOME_RANDOM_EVENT', true); expect(mockHide).not.toHaveBeenCalled(); expect(mockResize).not.toHaveBeenCalled(); expect(mockCallbacks.onClosed).not.toHaveBeenCalled(); expect(mockCallbacks.onError).not.toHaveBeenCalled(); expect(mockCallbacks.onPageChanged).not.toHaveBeenCalled(); expect(mockCallbacks.onQuestionAnswered).not.toHaveBeenCalled(); expect(mockCallbacks.onPartialCompleted).not.toHaveBeenCalled(); expect(mockCallbacks.onResize).not.toHaveBeenCalled(); }); it('should work without callbacks when none are provided', async () => { const serviceWithoutCallbacks = new WidgetEventService( mockHide, mockResize, mockUserId, mockTransactionId, undefined, mockStateManager, ); const result = await serviceWithoutCallbacks.handleMessage('FORM_CLOSE', true); expect(mockHide).toHaveBeenCalledTimes(1); expect(result).toEqual({ status: 'success' }); }); });