import ParserHtml from '../../../../src/parser/model/ParserHtml'; import ParserCss from '../../../../src/parser/model/ParserCss'; import DomComponents from '../../../../src/dom_components'; import Editor from '../../../../src/editor/model/Editor'; import { CSS_BG_OBJ, CSS_BG_STR } from './ParserCss'; describe('ParserHtml', () => { let obj: ReturnType; beforeEach(() => { const em = new Editor({}); const dom = new DomComponents(em); obj = ParserHtml(em, { textTags: ['br', 'b', 'i', 'u'], textTypes: ['text', 'textnode', 'comment'], returnArray: true, }); obj.compTypes = dom.componentTypes; }); test('Simple div node', () => { const str = '
'; const result = [{ tagName: 'div' }]; expect(obj.parse(str).html).toEqual(result); }); test('Simple article node', () => { const str = '
'; const result = [{ tagName: 'article' }]; expect(obj.parse(str).html).toEqual(result); }); test('Node with attributes', () => { const str = '
'; const result = [ { tagName: 'div', classes: ['test2', 'test3'], attributes: { 'data-one': 'test4', id: 'test1', strange: 'test5', }, }, ]; expect(obj.parse(str).html).toEqual(result); }); test('Parse style string to object', () => { const str = 'color:black; width:100px; test:value;'; const result = { color: 'black', width: '100px', test: 'value', }; expect(obj.parseStyle(str)).toEqual(result); }); test('Parse style string with values containing colon to object', () => { const str = 'background-image:url("https://some-website.ex"); test:value;'; const result = { 'background-image': 'url("https://some-website.ex")', test: 'value', }; expect(obj.parseStyle(str)).toEqual(result); }); test('Parse style with multiple values of the same key', () => { expect(obj.parseStyle(CSS_BG_STR)).toEqual(CSS_BG_OBJ); }); test('Parse style with comments', () => { expect(obj.parseStyle('/* color #ffffff; */ width: 100px;')).toEqual({ width: '100px', }); }); test('Parse class string to array', () => { const str = 'test1 test2 test3 test-4'; const result = ['test1', 'test2', 'test3', 'test-4']; expect(obj.parseClass(str)).toEqual(result); }); test('Parse class string to array with special classes', () => { const str = 'test1 test2 test3 test-4 gjs-test'; const result = ['test1', 'test2', 'test3', 'test-4', 'gjs-test']; expect(obj.parseClass(str)).toEqual(result); }); test('Style attribute is isolated', () => { const str = '
'; const result = [ { tagName: 'div', attributes: { id: 'test1' }, style: { color: 'black', width: '100px', test: 'value', }, }, ]; expect(obj.parse(str).html).toEqual(result); }); test('Class attribute is isolated', () => { const str = '
'; const result = [ { tagName: 'div', attributes: { id: 'test1' }, classes: ['test2', 'test3', 'test4'], }, ]; expect(obj.parse(str).html).toEqual(result); }); test('Parse images nodes', () => { const str = ''; const result = [ { tagName: 'img', type: 'image', attributes: { id: 'test1', src: './index.html', }, }, ]; expect(obj.parse(str).html).toEqual(result); }); test('Parse text nodes', () => { const str = '
test2
'; const result = [ { tagName: 'div', attributes: { id: 'test1' }, type: 'text', components: { type: 'textnode', content: 'test2 ' }, }, ]; expect(obj.parse(str).html).toEqual(result); }); test('Parse text with few text tags', () => { const str = '

test2
a b b i u test
'; const result = [ { tagName: 'div', attributes: { id: 'test1' }, type: 'text', components: [ { tagName: 'br' }, { content: ' test2 ', type: 'textnode', tagName: '', }, { tagName: 'br' }, { content: ' a b ', type: 'textnode', tagName: '', }, { components: { type: 'textnode', content: 'b' }, type: 'text', tagName: 'b', }, { content: ' ', type: 'textnode', tagName: '', }, { components: { type: 'textnode', content: 'i' }, tagName: 'i', type: 'text', }, { content: ' ', type: 'textnode', tagName: '', }, { components: { type: 'textnode', content: 'u' }, tagName: 'u', type: 'text', }, { content: ' test ', type: 'textnode', tagName: '', }, ], }, ]; expect(obj.parse(str).html).toEqual(result); }); test('Parse text with few text tags and nested node', () => { const str = '
a b b ic
ABC
i u test
'; const result = [ { tagName: 'div', attributes: { id: 'test1' }, type: 'text', components: [ { content: 'a b ', type: 'textnode', tagName: '', }, { components: { type: 'textnode', content: 'b' }, tagName: 'b', type: 'text', }, { content: ' ', type: 'textnode', tagName: '', }, { components: { type: 'textnode', content: 'i' }, tagName: 'i', type: 'text', }, { content: 'c ', type: 'textnode', tagName: '', }, { tagName: 'div', type: 'text', components: { type: 'textnode', content: 'ABC' }, }, { content: ' ', type: 'textnode', tagName: '', }, { components: { type: 'textnode', content: 'i' }, tagName: 'i', type: 'text', }, { content: ' ', type: 'textnode', tagName: '', }, { components: { type: 'textnode', content: 'u' }, tagName: 'u', type: 'text', }, { content: ' test ', type: 'textnode', tagName: '', }, ], }, ]; expect(obj.parse(str).html).toEqual(result); }); test('Parse text with few text tags and comment', () => { const str = '
Some text
Bold
'; const result = [ { tagName: 'div', attributes: { id: 'test1' }, type: 'text', components: [ { content: 'Some text ', type: 'textnode', tagName: '', }, { tagName: 'br' }, { content: ' comment ', type: 'comment', tagName: '', }, { components: { type: 'textnode', content: 'Bold' }, type: 'text', tagName: 'b', }, ], }, ]; expect(obj.parse(str).html).toEqual(result); }); test('Parse nested nodes', () => { const str = '
Text mid
'; const result = [ { tagName: 'article', attributes: { id: 'test1' }, components: [ { tagName: 'div', }, { content: ' ', type: 'textnode', tagName: '', }, { tagName: 'footer', attributes: { id: 'test2' }, }, { tagName: '', type: 'textnode', content: ' Text mid ', }, { tagName: 'div', attributes: { id: 'last' }, }, ], }, ]; expect(obj.parse(str).html).toEqual(result); }); test('Parse nested text nodes', () => { const str = '
content1
nested
content2
'; const result = [ { tagName: 'div', type: 'text', components: [ { tagName: '', type: 'textnode', content: 'content1 ', }, { tagName: 'div', type: 'text', components: { type: 'textnode', content: 'nested' }, }, { tagName: '', type: 'textnode', content: ' content2', }, ], }, ]; expect(obj.parse(str).html).toEqual(result); }); test('Parse nested span text nodes', () => { const str = '
content1
nested
content2
'; const result = [ { tagName: 'div', components: [ { tagName: '', type: 'textnode', content: 'content1 ', }, { tagName: 'div', components: [ { tagName: 'span', type: 'text', components: { type: 'textnode', content: 'nested' }, }, ], }, { tagName: '', type: 'textnode', content: ' content2', }, ], }, ]; expect(obj.parse(str).html).toEqual(result); }); test('Parse multiple nodes', () => { const str = '
'; const result = [{ tagName: 'div' }, { tagName: 'div' }]; expect(obj.parse(str).html).toEqual(result); }); test('Remove script tags', () => { const str = '
'; const result = [{ tagName: 'div' }, { tagName: 'div' }]; expect(obj.parse(str).html).toEqual(result); }); test('Isolate styles', () => { const str = '
'; const resHtml = [{ tagName: 'div' }, { tagName: 'div' }]; const resCss = [ { selectors: ['a'], style: { color: 'red' }, }, { selectors: ['b'], style: { color: 'blue' }, }, ]; const res = obj.parse(str, ParserCss()); expect(res.html).toEqual(resHtml); expect(res.css).toEqual(resCss); }); test('Respect multiple font-faces contained in styles in html', () => { const str = `
a div
`; const expected = [ { selectors: [], selectorsAdd: '', style: { 'font-family': '"Open Sans"', src: 'url(https://fonts.gstatic.com/s/droidsans/v8/SlGVmQWMvZQIdix7AFxXkHNSbRYXags.woff2)', }, singleAtRule: true, atRuleType: 'font-face', }, { selectors: [], selectorsAdd: '', style: { 'font-family': "'Glyphicons Halflings'", src: 'url(https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/fonts/glyphicons-halflings-regular.eot)', }, singleAtRule: true, atRuleType: 'font-face', }, ]; const res = obj.parse(str, ParserCss()); expect(res.css).toEqual(expected); }); test('Parse nested div with text and spaces', () => { const str = '

TestText

'; const result = [ { tagName: 'div', type: 'text', components: [ { tagName: '', type: 'textnode', content: ' ', }, { tagName: 'p', components: { type: 'textnode', content: 'TestText' }, type: 'text', }, { tagName: '', type: 'textnode', content: ' ', }, ], }, ]; expect(obj.parse(str).html).toEqual(result); }); test('Cleanup useless empty whitespaces', () => { const str = `

TestText

`; const result = [ { tagName: 'div', components: [ { tagName: 'p', components: { type: 'textnode', content: 'TestText' }, type: 'text', }, ], }, ]; expect(obj.parse(str).html).toEqual(result); }); test('Keep meaningful whitespaces', () => { const str = `

A

B

C

 

D

`; const result = [ { tagName: 'div', type: 'text', components: [ { tagName: 'p', components: { type: 'textnode', content: 'A' }, type: 'text', }, { type: 'textnode', content: ' ', tagName: '' }, { tagName: 'p', components: { type: 'textnode', content: 'B' }, type: 'text', }, { type: 'textnode', content: ' ', tagName: '' }, { tagName: 'p', components: { type: 'textnode', content: 'C' }, type: 'text', }, { type: 'textnode', content: ' ', tagName: '' }, { tagName: 'p', components: { type: 'textnode', content: 'D' }, type: 'text', }, ], }, ]; expect(obj.parse(str).html).toEqual(result); }); test('Parse node with model attributes to fetch', () => { const str = '
test2
'; const result = [ { tagName: 'div', draggable: '.myselector', stuff: 'test', attributes: { id: 'test1', 'data-test': 'test-value', }, type: 'text', components: { type: 'textnode', content: 'test2 ' }, }, ]; expect(obj.parse(str).html).toEqual(result); }); test('Parse model attributes with true and false', () => { const str = '
test2
'; const result = [ { tagName: 'div', draggable: true, stuff: false, attributes: { id: 'test1', 'data-test': 'test-value', }, type: 'text', components: { type: 'textnode', content: 'test2 ' }, }, ]; expect(obj.parse(str).html).toEqual(result); }); test('Parse attributes with object inside', () => { const str = '
test2
'; const result = [ { tagName: 'div', type: 'text', test: { prop1: 'value1', prop2: 10, prop3: true, }, components: { type: 'textnode', content: 'test2 ' }, }, ]; expect(obj.parse(str).html).toEqual(result); }); test('Parse attributes with arrays inside', () => { const str = '
test2
'; const result = [ { tagName: 'div', type: 'text', test: ['value1', 'value2'], components: { type: 'textnode', content: 'test2 ' }, }, ]; expect(obj.parse(str).html).toEqual(result); }); test('SVG is properly parsed', () => { const str = `
`; const result = [ { tagName: 'div', components: [ { type: 'svg', tagName: 'svg', attributes: { xmlns: 'http://www.w3.org/2000/svg', viewBox: '0 0 24 24', }, components: [ { tagName: 'linearGradient', attributes: { x1: '0%', y1: '0%' }, type: 'svg-in', }, { tagName: 'path', attributes: { d: 'M13 12h7v1.5h-7m0-4h7V11h-7m0 3.5h7V16h-7m8-12H3c-1.1 0-2 .9-2 2v13c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2m0 15h-9V6h9', }, type: 'svg-in', }, ], }, ], }, ]; expect(obj.parse(str).html).toEqual(result); }); test('