import { describe, it, expect, beforeEach } from 'vitest' import { sanitizeHtml, ETicketEditor, registerEditor } from '../../assets/modules/editor.js' describe('sanitizeHtml', () => { it('keeps allowed tags', () => { const html = '
Hello world
' expect(sanitizeHtml(html)).toBe('Hello world
') }) it('strips disallowed tags but keeps content', () => { const html = 'Citation' expect(sanitizeHtml(html)).toBe('Citation') }) it('returns empty string for empty input', () => { expect(sanitizeHtml('')).toBe('') }) it('strips headings but keeps content', () => { const html = '
Text
' expect(sanitizeHtml(html)).toBe('Text
') }) it('strips onclick from allowed tags', () => { const html = 'Bold' expect(sanitizeHtml(html)).toBe('Bold') }) it('strips onerror from allowed tags', () => { const html = 'Text
' expect(sanitizeHtml(html)).toBe('Text
') }) it('strips class and id from allowed tags', () => { const html = 'Text
' expect(sanitizeHtml(html)).toBe('Text
') }) it('strips data attributes from allowed tags', () => { const html = 'Safe
' expect(sanitizeHtml(html)).toBe('Safe
') }) it('strips img onerror XSS', () => { const html = 'OK
OK
') }) it('ignores comment nodes', () => { const html = 'Before
After
' expect(sanitizeHtml(html)).toBe('Before
After
') }) it('strips href from anchor but keeps text', () => { const html = 'Click' expect(sanitizeHtml(html)).toBe('Click') }) }) function createEditor(innerHtml = '') { registerEditor() document.body.innerHTML = '' const el = document.createElement('e-ticket-editor') el.innerHTML = innerHtml document.body.appendChild(el) el.connectedCallback() return el } describe('ETicketEditor', () => { beforeEach(() => { registerEditor() if (!document.execCommand) { document.execCommand = () => true } }) it('registers the custom element', () => { expect(globalThis.customElements.get('e-ticket-editor')).toBe(ETicketEditor) }) it('hides the textarea and creates toolbar + content area', () => { const editor = createEditor('') const textarea = editor.querySelector('textarea') const toolbar = editor.querySelector('.ete-toolbar') const content = editor.querySelector('.ete-content') expect(textarea.style.display).toBe('none') expect(toolbar).not.toBeNull() expect(content).not.toBeNull() expect(content.getAttribute('contenteditable')).toBe('true') expect(content.innerHTML).toBe('Hello') expect(content.dataset.placeholder).toBe('Ecrivez ici...') }) it('does nothing without a textarea', () => { const editor = createEditor('') expect(editor.querySelector('.ete-toolbar')).toBeNull() expect(editor.querySelector('.ete-content')).toBeNull() }) it('toolbar has buttons', () => { const editor = createEditor() const buttons = editor.querySelectorAll('.ete-btn') expect(buttons.length).toBeGreaterThan(0) }) it('toolbar buttons have aria-label', () => { const editor = createEditor() const buttons = editor.querySelectorAll('.ete-btn') buttons.forEach(btn => { expect(btn.getAttribute('aria-label')).toBeTruthy() }) }) it('toolbar buttons have tabindex=0', () => { const editor = createEditor() const buttons = editor.querySelectorAll('.ete-btn') buttons.forEach(btn => { expect(btn.tabIndex).toBe(0) }) }) it('toolbar has separators', () => { const editor = createEditor() const separators = editor.querySelectorAll('.ete-separator') expect(separators.length).toBeGreaterThan(0) }) it('getHtml returns textarea value', () => { const editor = createEditor('') expect(editor.getHtml()).toBe('Content') }) it('prevents tab key default in content area', () => { const editor = createEditor() const content = editor.querySelector('.ete-content') const event = new KeyboardEvent('keydown', { key: 'Tab', cancelable: true }) content.dispatchEvent(event) expect(event.defaultPrevented).toBe(true) }) it('sanitizeNode handles text nodes and element nodes', () => { const html = 'Hello world plain' const result = sanitizeHtml(html) expect(result).toContain('Hello') expect(result).toContain('world') expect(result).toContain('plain') }) it('allows non-tab keys', () => { const editor = createEditor() const content = editor.querySelector('.ete-content') const event = new KeyboardEvent('keydown', { key: 'Enter', cancelable: true }) content.dispatchEvent(event) expect(event.defaultPrevented).toBe(false) }) it('toolbar button mousedown calls exec and prevents default', () => { const editor = createEditor() const btn = editor.querySelector('.ete-btn') const event = new MouseEvent('mousedown', { cancelable: true, bubbles: true }) btn.dispatchEvent(event) expect(event.defaultPrevented).toBe(true) }) it('syncs content to textarea via _sync', () => { const editor = createEditor() const textarea = editor.querySelector('textarea') editor._content.innerHTML = 'Updated
' editor._sync() expect(textarea.value).toBe('Updated
') }) it('syncs sanitized content to textarea via _sync', () => { const editor = createEditor() const textarea = editor.querySelector('textarea') editor._content.innerHTML = 'Safe
' editor._sync() expect(textarea.value).toBe('StrippedSafe
') }) it('exec with value calls execCommand with value', () => { const editor = createEditor() const btn = editor.querySelectorAll('.ete-btn') const pBtn = Array.from(btn).find(b => b.title === 'Paragraphe') if (pBtn) { const event = new MouseEvent('mousedown', { cancelable: true, bubbles: true }) pBtn.dispatchEvent(event) expect(event.defaultPrevented).toBe(true) } }) it('exec without value calls execCommand without value', () => { const editor = createEditor() const btn = editor.querySelectorAll('.ete-btn') const boldBtn = Array.from(btn).find(b => b.title === 'Gras') if (boldBtn) { const event = new MouseEvent('mousedown', { cancelable: true, bubbles: true }) boldBtn.dispatchEvent(event) expect(event.defaultPrevented).toBe(true) } }) })