import { describe, it, expect, beforeEach, vi } from 'vitest'; // Stub the Widget component so mountWidget does not boot the WebSocket // transport (jsdom has no WebSocket implementation). We only verify mount // mechanics here — Widget rendering is a separate concern. vi.mock('@/ui/widget', () => ({ Widget: () => null, })); import { mountWidget, applyStyles } from '@/mount'; import type { WidgetConfig } from '@/config'; const baseConfig: WidgetConfig = { botId: 'test-bot', apiKey: 'test-key', serverUrl: 'https://test.local', position: 'bottom-right', shadow: false, }; const TEST_CSS = '.messenzy-widget { color: tomato; }'; beforeEach(() => { document.head.innerHTML = ''; document.body.innerHTML = ''; }); describe('mountWidget — classic mode', () => { it('creates #messenzy-root in document.body when absent', () => { mountWidget(baseConfig, TEST_CSS, 'classic'); const root = document.getElementById('messenzy-root'); expect(root).not.toBeNull(); expect(root?.parentElement).toBe(document.body); }); it('reuses an existing #messenzy-root', () => { const existing = document.createElement('div'); existing.id = 'messenzy-root'; document.body.appendChild(existing); const handle = mountWidget(baseConfig, TEST_CSS, 'classic'); expect(handle.root).toBe(existing); }); it('returns a handle without shadowRoot', () => { const handle = mountWidget(baseConfig, TEST_CSS, 'classic'); expect(handle.shadowRoot).toBeUndefined(); }); it('does not attach a shadow root to the host', () => { mountWidget(baseConfig, TEST_CSS, 'classic'); const root = document.getElementById('messenzy-root'); expect(root?.shadowRoot).toBeNull(); }); }); describe('mountWidget — shadow mode', () => { it('creates the host and attaches an open shadow root', () => { const handle = mountWidget(baseConfig, TEST_CSS, 'shadow'); const root = document.getElementById('messenzy-root'); expect(root).not.toBeNull(); expect(root?.shadowRoot).not.toBeNull(); expect(handle.shadowRoot).toBe(root?.shadowRoot); }); it('reuses an already-attached shadow root (HMR/idempotency)', () => { const existing = document.createElement('div'); existing.id = 'messenzy-root'; const preAttached = existing.attachShadow({ mode: 'open' }); document.body.appendChild(existing); const handle = mountWidget(baseConfig, TEST_CSS, 'shadow'); expect(handle.shadowRoot).toBe(preAttached); }); it('injects styles into the shadow root, not document.head', () => { mountWidget(baseConfig, TEST_CSS, 'shadow'); expect(document.getElementById('messenzy-styles')).toBeNull(); const shadow = document.getElementById('messenzy-root')?.shadowRoot; const styleNode = shadow?.querySelector('style'); expect(styleNode).not.toBeNull(); expect(styleNode?.textContent).toContain('tomato'); }); it('keeps the