import { afterEach, describe, expect, it, vi } from 'vitest'; const { createConsumer, createSubscription, getCookie, subscriptionState } = vi.hoisted(() => { const subscriptionState = { callbacks: null }; return { createConsumer: vi.fn(), createSubscription: vi.fn((_identifier, callbacks) => { subscriptionState.callbacks = callbacks; return { updatePresence: vi.fn() }; }), getCookie: vi.fn(), subscriptionState, }; }); vi.mock('@rails/actioncable', () => ({ createConsumer })); vi.mock('js-cookie', () => ({ default: { get: getCookie } })); import BaseActionCableConnector from '../BaseActionCableConnector'; describe('BaseActionCableConnector', () => { afterEach(() => { vi.clearAllMocks(); vi.useRealTimers(); subscriptionState.callbacks = null; BaseActionCableConnector.isDisconnected = false; }); it('authenticates a widget socket and RoomChannel with its pubsub token', () => { vi.useFakeTimers(); getCookie.mockReturnValue(undefined); createConsumer.mockReturnValue({ subscriptions: { create: createSubscription }, }); new BaseActionCableConnector( { $store: { getters: {} } }, 'visitor token' ); expect(createConsumer).toHaveBeenCalledWith( `${window.location.origin}/cable?pubsub_token=visitor%20token` ); expect(createSubscription).toHaveBeenCalledWith( expect.objectContaining({ channel: 'RoomChannel', pubsub_token: 'visitor token', }), expect.any(Object) ); }); it('continues consuming widget events after ActionCable reconnects', () => { vi.useFakeTimers(); getCookie.mockReturnValue(undefined); const isOpen = vi.fn(() => true); createConsumer.mockReturnValue({ connection: { isOpen }, subscriptions: { create: createSubscription }, }); const connector = new BaseActionCableConnector( { $store: { getters: {} } }, 'visitor-token' ); const onMessage = vi.fn(); connector.events['message.created'] = onMessage; connector.onDisconnected = vi.fn(); connector.onReconnect = vi.fn(); subscriptionState.callbacks.received({ event: 'message.created', data: { id: 1, content: 'before reconnect' }, }); subscriptionState.callbacks.disconnected(); vi.advanceTimersByTime(1000); subscriptionState.callbacks.received({ event: 'message.created', data: { id: 2, content: 'after reconnect' }, }); expect(connector.onDisconnected).toHaveBeenCalledOnce(); expect(connector.onReconnect).toHaveBeenCalledOnce(); expect(onMessage).toHaveBeenNthCalledWith(1, { id: 1, content: 'before reconnect', }); expect(onMessage).toHaveBeenNthCalledWith(2, { id: 2, content: 'after reconnect', }); }); });