import { Assert, describe, it } from '@ephox/bedrock-client'; import { Arr } from '@ephox/katamari'; import type { SugarElement } from 'ephox/sugar/api/node/SugarElement'; import * as SugarFragment from 'ephox/sugar/api/node/SugarFragment'; import * as SugarNode from 'ephox/sugar/api/node/SugarNode'; import * as Attribute from 'ephox/sugar/api/properties/Attribute'; import * as TextContent from 'ephox/sugar/api/properties/TextContent'; import * as Traverse from 'ephox/sugar/api/search/Traverse'; describe('browser.sugar.node.SugarFragmentTest', () => { it('TINY-13526: should return fragment', () => { const html = '
content
'; const fragment = SugarFragment.fromHtml(html); Assert.eq('Should be fragment', SugarNode.isDocumentFragment(fragment), true); }); it('TINY-13526: should create fragment from single element', () => { const html = '
content
'; const fragment = SugarFragment.fromHtml(html); const children = Traverse.children(fragment); Assert.eq('Should have 1 child', children.length, 1); Assert.eq('Should be div', SugarNode.name(children[0]), 'div'); Assert.eq('Content should match', TextContent.get(children[0]), 'content'); }); it('TINY-13526: should create fragment from multiple sibling elements', () => { const html = 'Cell 1Cell 2Cell 3'; const fragment = SugarFragment.fromHtml(html); const children = Traverse.children(fragment); Assert.eq('Should have 3 children', children.length, 3); Arr.each(children, (child, i) => { Assert.eq('Should be td', SugarNode.name(child), 'td'); Assert.eq('Content should match', TextContent.get(child), `Cell ${i + 1}`); }); }); it('TINY-13526: should create fragment from nested elements', () => { const html = '

Paragraph 1

Paragraph 2

'; const fragment = SugarFragment.fromHtml(html); const children = Traverse.children(fragment); Assert.eq('Should have 1 root child', children.length, 1); Assert.eq('Should be div', SugarNode.name(children[0]), 'div'); const nestedChildren = Traverse.children(children[0]); Assert.eq('Should have 2 nested children', nestedChildren.length, 2); Assert.eq('First nested should be p', SugarNode.name(nestedChildren[0]), 'p'); Assert.eq('Second nested should be p', SugarNode.name(nestedChildren[1]), 'p'); }); it('TINY-13526: should create fragment from text and elements mixed', () => { const html = 'Text beforespan contentText after'; const fragment = SugarFragment.fromHtml(html); const children = Traverse.children(fragment); Assert.eq('Should have 3 children', children.length, 3); Assert.eq('First should be text', SugarNode.isText(children[0]), true); Assert.eq('Second should be span', SugarNode.name(children[1]), 'span'); Assert.eq('Third should be text', SugarNode.isText(children[2]), true); }); it('TINY-13526: should create fragment from empty string', () => { const html = ''; const fragment = SugarFragment.fromHtml(html); const children = Traverse.children(fragment); Assert.eq('Should have no children', children.length, 0); }); it('TINY-13526: should create fragment with attributes preserved', () => { const html = '
content
'; const fragment = SugarFragment.fromHtml(html); const children = Traverse.children(fragment); Assert.eq('Should have 1 child', children.length, 1); Assert.eq('Should have class', Attribute.get(children[0] as SugarElement, 'class'), 'test'); Assert.eq('Should have id', Attribute.get(children[0] as SugarElement, 'id'), 'myid'); Assert.eq('Should have data attr', Attribute.get(children[0] as SugarElement, 'data-custom'), 'value'); }); it('TINY-13526: should handle rwo without table structure', () => { const html = 'ABCD'; const fragment = SugarFragment.fromHtml(html); const children = Traverse.children(fragment); Assert.eq('Should have 2 rows', children.length, 2); Arr.each(children, (row) => { Assert.eq('Should be tr', SugarNode.name(row), 'tr'); const cells = Traverse.children(row); Assert.eq('Should have 2 cells', cells.length, 2); }); }); it('TINY-13526: should handle self-closing tags', () => { const html = 'test
'; const fragment = SugarFragment.fromHtml(html); const children = Traverse.children(fragment); Assert.eq('Should have 3 children', children.length, 3); Assert.eq('First should be img', SugarNode.name(children[0]), 'img'); Assert.eq('Second should be br', SugarNode.name(children[1]), 'br'); Assert.eq('Third should be input', SugarNode.name(children[2]), 'input'); }); it('TINY-13526: should handle whitespace-only content', () => { const html = ' \n\t '; const fragment = SugarFragment.fromHtml(html); const children = Traverse.children(fragment); Assert.eq('Should have text node', children.length > 0, true); }); it('TINY-13526: should create fragment with custom scope document', () => { const customDoc = document.implementation.createHTMLDocument('test'); const html = '
scoped content
'; const fragment = SugarFragment.fromHtml(html, customDoc); const children = Traverse.children(fragment); Assert.eq('Should have 1 child', children.length, 1); Assert.eq('Should belong to custom document', children[0].dom.ownerDocument, customDoc); }); it('TINY-13526: should handle malformed HTML', () => { const html = '

unclosed'; const fragment = SugarFragment.fromHtml(html); const children = Traverse.children(fragment); Assert.eq('Should still create fragment', children.length > 0, true); const firstChild = children[0]; Assert.eq('First child should be div', SugarNode.name(firstChild), 'div'); const nestedChildren = Traverse.children(firstChild); Assert.eq('Div should have 1 child', nestedChildren.length, 1); Assert.eq('Nested child should be p', SugarNode.name(nestedChildren[0]), 'p'); Assert.eq('Paragraph content should match', TextContent.get(nestedChildren[0]), 'unclosed'); }); it('TINY-13526: should handle script tags', () => { const html = '

before
after
'; const fragment = SugarFragment.fromHtml(html); const children = Traverse.children(fragment); Assert.eq('Should have 3 children', children.length, 3); Assert.eq('Middle should be script', SugarNode.name(children[1]), 'script'); }); it('TINY-13526: should handle comments', () => { const html = '
content
more'; const fragment = SugarFragment.fromHtml(html); const children = Traverse.children(fragment); Assert.eq('Should include comment node', children.length, 3); Assert.eq('Middle should be comment', SugarNode.isComment(children[1]), true); }); it('TINY-13526: should handle HTML entities', () => { const html = '
<span>&©
'; const fragment = SugarFragment.fromHtml(html); const children = Traverse.children(fragment); Assert.eq('Should decode entities', TextContent.get(children[0]), '&©'); }); it('TINY-13526: should handle special characters', () => { const html = '
Hello "world" & \'test\'
'; const fragment = SugarFragment.fromHtml(html); const children = Traverse.children(fragment); Assert.eq('Should preserve special chars', TextContent.get(children[0]), 'Hello "world" & \'test\''); }); it('TINY-13526: should handle inline styles', () => { const html = '
styled
'; const fragment = SugarFragment.fromHtml(html); const children = Traverse.children(fragment); Assert.eq('Should preserve style attribute', Attribute.has(children[0], 'style'), true); }); });