Skip to content

Commit cd3d7db

Browse files
committed
test
1 parent ebe18ca commit cd3d7db

2 files changed

Lines changed: 884 additions & 5 deletions

File tree

test/unit/core/class/html.spec.js

Lines changed: 322 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -535,28 +535,345 @@ describe('HTML', () => {
535535
expect(result).toBe(true);
536536
expect(editor.ui.showToast).toHaveBeenCalledWith(editor.lang.message_copy_success, expect.anything());
537537
});
538-
538+
539539
it('should fail if content is not valid', async () => {
540540
const result = await html.copy({}); // invalid object
541541
expect(result).toBe(false);
542542
});
543-
543+
544544
it('should handle clipboard error (return false)', async () => {
545545
jest.spyOn(clipboard, 'write').mockResolvedValue(false);
546546
editor.ui.showToast = jest.fn();
547-
547+
548548
const result = await html.copy('text');
549549
expect(result).toBe(false);
550550
expect(editor.ui.showToast).toHaveBeenCalledWith(editor.lang.message_copy_fail, expect.anything(), 'error');
551551
});
552-
552+
553553
it('should handle exception in copy', async () => {
554554
jest.spyOn(clipboard, 'write').mockRejectedValue(new Error('fail'));
555555
console.error = jest.fn(); // suppress console error
556556
editor.ui.showToast = jest.fn();
557-
557+
558558
const result = await html.copy('text');
559559
expect(result).toBe(false);
560560
});
561561
});
562+
563+
/**
564+
* ============================================================
565+
* STRICT DOM VERIFICATION TESTS FOR HTML CLASS
566+
* These tests verify exact DOM output to catch regressions
567+
* during refactoring.
568+
* ============================================================
569+
*/
570+
describe('Strict DOM verification - insert', () => {
571+
it('should produce exact HTML when inserting text at cursor', () => {
572+
wysiwyg.innerHTML = '<p>Hello World</p>';
573+
const textNode = wysiwyg.querySelector('p').firstChild;
574+
editor.selection.setRange(textNode, 5, textNode, 5);
575+
576+
const insertText = document.createTextNode(' Beautiful');
577+
html.insertNode(insertText);
578+
579+
expect(wysiwyg.innerHTML).toBe('<p>Hello Beautiful World</p>');
580+
});
581+
582+
it('should produce exact HTML when inserting span at cursor', () => {
583+
wysiwyg.innerHTML = '<p>Plain text here</p>';
584+
const textNode = wysiwyg.querySelector('p').firstChild;
585+
editor.selection.setRange(textNode, 6, textNode, 6);
586+
587+
const span = document.createElement('span');
588+
span.style.color = 'red';
589+
span.textContent = 'colored';
590+
html.insertNode(span);
591+
592+
expect(wysiwyg.innerHTML).toBe('<p>Plain <span style="color: red;">colored</span>text here</p>');
593+
});
594+
595+
it('should produce exact HTML when replacing selected text', () => {
596+
wysiwyg.innerHTML = '<p>Replace this word</p>';
597+
const textNode = wysiwyg.querySelector('p').firstChild;
598+
editor.selection.setRange(textNode, 8, textNode, 12);
599+
600+
const replacement = document.createTextNode('that');
601+
html.insertNode(replacement);
602+
603+
expect(wysiwyg.innerHTML).toBe('<p>Replace that word</p>');
604+
});
605+
606+
it('should insert strong element correctly', () => {
607+
wysiwyg.innerHTML = '<p>Normal text</p>';
608+
const textNode = wysiwyg.querySelector('p').firstChild;
609+
editor.selection.setRange(textNode, 7, textNode, 7);
610+
611+
const strong = document.createElement('strong');
612+
strong.textContent = 'BOLD';
613+
html.insertNode(strong);
614+
615+
expect(wysiwyg.querySelector('strong')).toBeTruthy();
616+
expect(wysiwyg.querySelector('strong').textContent).toBe('BOLD');
617+
});
618+
});
619+
620+
describe('Strict DOM verification - insertNode with block elements', () => {
621+
it('should split paragraph when inserting block element', () => {
622+
wysiwyg.innerHTML = '<p>Before After</p>';
623+
const textNode = wysiwyg.querySelector('p').firstChild;
624+
editor.selection.setRange(textNode, 7, textNode, 7);
625+
626+
const newP = document.createElement('p');
627+
newP.textContent = 'Middle';
628+
html.insertNode(newP);
629+
630+
// Should have multiple paragraphs now
631+
const paragraphs = wysiwyg.querySelectorAll('p');
632+
expect(paragraphs.length).toBeGreaterThanOrEqual(2);
633+
expect(wysiwyg.textContent).toContain('Before');
634+
expect(wysiwyg.textContent).toContain('Middle');
635+
expect(wysiwyg.textContent).toContain('After');
636+
});
637+
638+
it('should insert div element splitting content', () => {
639+
wysiwyg.innerHTML = '<p>Start End</p>';
640+
const textNode = wysiwyg.querySelector('p').firstChild;
641+
editor.selection.setRange(textNode, 6, textNode, 6);
642+
643+
const div = document.createElement('div');
644+
div.textContent = 'Inserted Block';
645+
html.insertNode(div);
646+
647+
expect(wysiwyg.querySelector('div')).toBeTruthy();
648+
expect(wysiwyg.textContent).toContain('Inserted Block');
649+
});
650+
});
651+
652+
describe('Strict DOM verification - remove', () => {
653+
it('should remove selected text exactly', () => {
654+
wysiwyg.innerHTML = '<p>Remove middle text</p>';
655+
const textNode = wysiwyg.querySelector('p').firstChild;
656+
editor.selection.setRange(textNode, 7, textNode, 14);
657+
658+
html.remove();
659+
660+
expect(wysiwyg.innerHTML).toBe('<p>Remove text</p>');
661+
});
662+
663+
it('should remove entire word when selected', () => {
664+
wysiwyg.innerHTML = '<p>Hello World</p>';
665+
const textNode = wysiwyg.querySelector('p').firstChild;
666+
editor.selection.setRange(textNode, 6, textNode, 11);
667+
668+
html.remove();
669+
670+
expect(wysiwyg.innerHTML).toBe('<p>Hello </p>');
671+
});
672+
673+
it('should handle removing formatted text', () => {
674+
wysiwyg.innerHTML = '<p>Text <strong>Bold</strong> More</p>';
675+
const strongText = wysiwyg.querySelector('strong').firstChild;
676+
editor.selection.setRange(strongText, 0, strongText, 4);
677+
678+
html.remove();
679+
680+
// Bold text should be removed, strong tag may or may not remain
681+
expect(wysiwyg.textContent).toContain('Text');
682+
expect(wysiwyg.textContent).toContain('More');
683+
expect(wysiwyg.textContent).not.toContain('Bold');
684+
});
685+
686+
it('should remove across multiple elements', () => {
687+
wysiwyg.innerHTML = '<p><strong>Bold</strong><em>Italic</em></p>';
688+
const boldText = wysiwyg.querySelector('strong').firstChild;
689+
const italicText = wysiwyg.querySelector('em').firstChild;
690+
editor.selection.setRange(boldText, 2, italicText, 3);
691+
692+
html.remove();
693+
694+
// Should have removed "ld" from Bold and "Ita" from Italic
695+
expect(wysiwyg.textContent).toBe('Bolic');
696+
});
697+
});
698+
699+
describe('Strict DOM verification - get/set', () => {
700+
it('should get exact HTML content', () => {
701+
wysiwyg.innerHTML = '<p>Test <strong>content</strong></p>';
702+
703+
const result = html.get();
704+
705+
expect(result).toBe('<p>Test <strong>content</strong></p>');
706+
});
707+
708+
it('should set HTML content exactly', () => {
709+
html.set('<p>New <em>content</em></p>');
710+
711+
expect(wysiwyg.innerHTML).toBe('<p>New <em>content</em></p>');
712+
});
713+
714+
it('should handle empty content', () => {
715+
html.set('');
716+
717+
// Empty content results in empty string (actual behavior)
718+
expect(wysiwyg.innerHTML).toBeDefined();
719+
});
720+
721+
it('should preserve complex nested structure', () => {
722+
const complexHTML = '<p><strong><em>Bold Italic</em></strong></p><ul><li>Item 1</li><li>Item 2</li></ul>';
723+
html.set(complexHTML);
724+
725+
expect(wysiwyg.querySelector('strong em')).toBeTruthy();
726+
expect(wysiwyg.querySelectorAll('li').length).toBe(2);
727+
});
728+
});
729+
730+
describe('Strict DOM verification - clean', () => {
731+
it('should clean HTML removing extra whitespace', () => {
732+
const dirty = ' <p> Text with spaces </p> ';
733+
const cleaned = html.clean(dirty);
734+
735+
// Should be trimmed and normalized
736+
expect(cleaned).not.toMatch(/^\s+/);
737+
expect(cleaned).not.toMatch(/\s+$/);
738+
});
739+
740+
it('should preserve necessary structure', () => {
741+
const input = '<p><strong>Bold</strong> <em>Italic</em></p>';
742+
const cleaned = html.clean(input);
743+
744+
expect(cleaned).toContain('<strong>');
745+
expect(cleaned).toContain('<em>');
746+
});
747+
748+
it('should remove script tags', () => {
749+
const malicious = '<p>Safe</p><script>alert("xss")</script>';
750+
const cleaned = html.clean(malicious);
751+
752+
expect(cleaned).not.toContain('<script>');
753+
expect(cleaned).toContain('Safe');
754+
});
755+
756+
it('should handle inline styles (clean may strip styles based on config)', () => {
757+
const styled = '<p><span style="color: red;">Red Text</span></p>';
758+
const cleaned = html.clean(styled);
759+
760+
// clean() may strip inline styles based on editor configuration
761+
// Text content should be preserved
762+
expect(cleaned).toContain('Red Text');
763+
});
764+
});
765+
766+
describe('Strict DOM verification - list operations', () => {
767+
it('should insert into list item correctly', () => {
768+
wysiwyg.innerHTML = '<ul><li>Item one</li><li>Item two</li></ul>';
769+
const li = wysiwyg.querySelector('li');
770+
const textNode = li.firstChild;
771+
editor.selection.setRange(textNode, 5, textNode, 5);
772+
773+
const span = document.createElement('span');
774+
span.textContent = ' inserted';
775+
html.insertNode(span);
776+
777+
expect(li.textContent).toContain('inserted');
778+
});
779+
780+
it('should handle inserting list into paragraph', () => {
781+
wysiwyg.innerHTML = '<p>Before list</p>';
782+
const textNode = wysiwyg.querySelector('p').firstChild;
783+
editor.selection.setRange(textNode, 11, textNode, 11);
784+
785+
const ul = document.createElement('ul');
786+
ul.innerHTML = '<li>New item</li>';
787+
html.insertNode(ul);
788+
789+
expect(wysiwyg.querySelector('ul')).toBeTruthy();
790+
expect(wysiwyg.querySelector('li').textContent).toBe('New item');
791+
});
792+
});
793+
794+
describe('Strict DOM verification - table operations', () => {
795+
it('should insert text into table cell', () => {
796+
wysiwyg.innerHTML = '<table><tbody><tr><td>Cell</td></tr></tbody></table>';
797+
const cell = wysiwyg.querySelector('td');
798+
const textNode = cell.firstChild;
799+
editor.selection.setRange(textNode, 4, textNode, 4);
800+
801+
const insertText = document.createTextNode(' content');
802+
html.insertNode(insertText);
803+
804+
expect(cell.textContent).toBe('Cell content');
805+
});
806+
807+
it('should handle removing text from table cell', () => {
808+
wysiwyg.innerHTML = '<table><tbody><tr><td>Remove this</td></tr></tbody></table>';
809+
const cell = wysiwyg.querySelector('td');
810+
const textNode = cell.firstChild;
811+
editor.selection.setRange(textNode, 7, textNode, 11);
812+
813+
html.remove();
814+
815+
expect(cell.textContent).toBe('Remove ');
816+
});
817+
});
818+
819+
describe('Strict DOM verification - special characters', () => {
820+
it('should handle HTML entities correctly', () => {
821+
html.set('<p>Test &amp; verify &lt;tag&gt;</p>');
822+
823+
expect(wysiwyg.textContent).toBe('Test & verify <tag>');
824+
});
825+
826+
it('should handle unicode characters', () => {
827+
html.set('<p>한글 테스트 🎉</p>');
828+
829+
expect(wysiwyg.textContent).toBe('한글 테스트 🎉');
830+
});
831+
832+
it('should preserve whitespace in preformatted blocks', () => {
833+
const preContent = '<pre> indented\n more indent</pre>';
834+
html.set(preContent);
835+
836+
const pre = wysiwyg.querySelector('pre');
837+
expect(pre).toBeTruthy();
838+
expect(pre.textContent).toContain(' indented');
839+
});
840+
});
841+
842+
describe('Strict DOM verification - edge cases', () => {
843+
it('should handle empty selection insert', () => {
844+
wysiwyg.innerHTML = '<p>Text</p>';
845+
const p = wysiwyg.querySelector('p');
846+
editor.selection.setRange(p, 0, p, 0);
847+
848+
const span = document.createElement('span');
849+
span.textContent = 'Start';
850+
html.insertNode(span);
851+
852+
expect(wysiwyg.textContent).toContain('Start');
853+
});
854+
855+
it('should handle insert at end of content', () => {
856+
wysiwyg.innerHTML = '<p>Content</p>';
857+
const textNode = wysiwyg.querySelector('p').firstChild;
858+
editor.selection.setRange(textNode, 7, textNode, 7);
859+
860+
const endText = document.createTextNode(' End');
861+
html.insertNode(endText);
862+
863+
expect(wysiwyg.querySelector('p').textContent).toBe('Content End');
864+
});
865+
866+
it('should handle nested inline elements', () => {
867+
wysiwyg.innerHTML = '<p><strong><em>Nested</em></strong></p>';
868+
const emText = wysiwyg.querySelector('em').firstChild;
869+
editor.selection.setRange(emText, 3, emText, 3);
870+
871+
const u = document.createElement('u');
872+
u.textContent = '-inserted-';
873+
html.insertNode(u);
874+
875+
expect(wysiwyg.querySelector('u')).toBeTruthy();
876+
expect(wysiwyg.textContent).toContain('-inserted-');
877+
});
878+
});
562879
});

0 commit comments

Comments
 (0)