import { expect, tap, webhelpers } from '@push.rocks/tapbundle'; import { DeesWysiwygBlock } from '../ts_web/elements/wysiwyg/dees-wysiwyg-block.js'; import { WysiwygSelection } from '../ts_web/elements/wysiwyg/wysiwyg.selection.js'; tap.test('Shadow DOM containment should work correctly', async () => { console.log('=== Testing Shadow DOM Containment ==='); // Create a WYSIWYG block component const block = await webhelpers.fixture( '' ); // Set the block data block.block = { id: 'test-1', type: 'paragraph', content: 'Hello world test content' }; block.handlers = { onInput: () => {}, onKeyDown: () => {}, onFocus: () => {}, onBlur: () => {}, onCompositionStart: () => {}, onCompositionEnd: () => {} }; await block.updateComplete; // Get the paragraph element inside Shadow DOM const container = block.shadowRoot?.querySelector('.wysiwyg-block-container') as HTMLElement; const paragraphBlock = container?.querySelector('.block.paragraph') as HTMLElement; expect(paragraphBlock).toBeTruthy(); console.log('Found paragraph block:', paragraphBlock); console.log('Paragraph text content:', paragraphBlock.textContent); // Focus the paragraph paragraphBlock.focus(); // Manually set cursor position const textNode = paragraphBlock.firstChild; if (textNode && textNode.nodeType === Node.TEXT_NODE) { const range = document.createRange(); const selection = window.getSelection(); // Set cursor at position 11 (after "Hello world") range.setStart(textNode, 11); range.setEnd(textNode, 11); selection?.removeAllRanges(); selection?.addRange(range); console.log('Set cursor at position 11'); // Test the containment check console.log('\n--- Testing containment ---'); const currentSelection = window.getSelection(); if (currentSelection && currentSelection.rangeCount > 0) { const selRange = currentSelection.getRangeAt(0); console.log('Selection range:', { startContainer: selRange.startContainer, startOffset: selRange.startOffset, containerText: selRange.startContainer.textContent }); // Test regular contains (should fail across Shadow DOM) const regularContains = paragraphBlock.contains(selRange.startContainer); console.log('Regular contains:', regularContains); // Test Shadow DOM-aware contains const shadowDOMContains = WysiwygSelection.containsAcrossShadowDOM(paragraphBlock, selRange.startContainer); console.log('Shadow DOM contains:', shadowDOMContains); // Since we're setting selection within the same shadow DOM, both should be true expect(regularContains).toBeTrue(); expect(shadowDOMContains).toBeTrue(); } // Test getSplitContent console.log('\n--- Testing getSplitContent ---'); const splitResult = block.getSplitContent(); console.log('Split result:', splitResult); expect(splitResult).toBeTruthy(); if (splitResult) { console.log('Before:', JSON.stringify(splitResult.before)); console.log('After:', JSON.stringify(splitResult.after)); // Expected split at position 11 expect(splitResult.before).toEqual('Hello world'); expect(splitResult.after).toEqual(' test content'); } } }); tap.test('Shadow DOM containment across different shadow roots', async () => { console.log('=== Testing Cross Shadow Root Containment ==='); // Create parent component with WYSIWYG editor const parentDiv = document.createElement('div'); parentDiv.innerHTML = ` `; document.body.appendChild(parentDiv); // Wait for components to be ready await new Promise(resolve => setTimeout(resolve, 100)); const wysiwygInput = parentDiv.querySelector('dees-input-wysiwyg') as any; const blockElement = wysiwygInput?.shadowRoot?.querySelector('dees-wysiwyg-block') as DeesWysiwygBlock; if (blockElement) { // Set block data blockElement.block = { id: 'test-2', type: 'paragraph', content: 'Cross shadow DOM test' }; blockElement.handlers = { onInput: () => {}, onKeyDown: () => {}, onFocus: () => {}, onBlur: () => {}, onCompositionStart: () => {}, onCompositionEnd: () => {} }; await blockElement.updateComplete; // Get the paragraph inside the nested shadow DOM const container = blockElement.shadowRoot?.querySelector('.wysiwyg-block-container') as HTMLElement; const paragraphBlock = container?.querySelector('.block.paragraph') as HTMLElement; if (paragraphBlock) { console.log('Found nested paragraph block'); // Focus and set selection paragraphBlock.focus(); const textNode = paragraphBlock.firstChild; if (textNode && textNode.nodeType === Node.TEXT_NODE) { const range = document.createRange(); range.setStart(textNode, 5); range.setEnd(textNode, 5); const selection = window.getSelection(); selection?.removeAllRanges(); selection?.addRange(range); // Test containment from parent's perspective const selRange = selection?.getRangeAt(0); if (selRange) { // This should fail because it crosses shadow DOM boundary const regularContains = wysiwygInput.contains(selRange.startContainer); console.log('Parent regular contains:', regularContains); expect(regularContains).toBeFalse(); // This should work with our Shadow DOM-aware method const shadowDOMContains = WysiwygSelection.containsAcrossShadowDOM(wysiwygInput, selRange.startContainer); console.log('Parent shadow DOM contains:', shadowDOMContains); expect(shadowDOMContains).toBeTrue(); } } } } // Clean up document.body.removeChild(parentDiv); }); export default tap.start();