import { Pipeline, Step } from '@ephox/agar'; import { Arr } from '@ephox/katamari'; import { LegacyUnit } from '@ephox/mcagar'; import Serializer from 'tinymce/core/api/dom/Serializer'; import DOMUtils from 'tinymce/core/api/dom/DOMUtils'; import TrimHtml from 'tinymce/core/dom/TrimHtml'; import ViewBlock from '../../module/test/ViewBlock'; import Zwsp from 'tinymce/core/text/Zwsp'; import { UnitTest } from '@ephox/bedrock'; declare const escape: any; UnitTest.asynctest('browser.tinymce.core.dom.SerializerTest', function () { const success = arguments[arguments.length - 2]; const failure = arguments[arguments.length - 1]; const suite = LegacyUnit.createSuite(); const DOM = DOMUtils.DOM; const viewBlock = ViewBlock(); const teardown = function () { viewBlock.update(''); }; const addTeardown = function (steps) { return Arr.bind(steps, function (step) { return [step, Step.sync(teardown)]; }); }; suite.test('Schema rules', function () { let ser = Serializer({ fix_list_elements : true }); ser.setRules('@[id|title|class|style],div,img[src|alt|-style|border],span,hr'); DOM.setHTML('test', 'testtest
'); LegacyUnit.equal( ser.serialize(DOM.get('test'), { getInner: true }), 'test
', 'Global rule' ); ser.setRules('*a[*],em/i[*],strong/b[*i*]'); DOM.setHTML('test', 'testtest2test3'); LegacyUnit.equal(ser.serialize(DOM.get('test')), 'testtest2' + 'test3', 'Wildcard rules'); ser.setRules('br,hr,input[type|name|value],div[id],span[id],strong/b,a,em/i,a[!href|!name],img[src|border=0|title={$uid}]'); DOM.setHTML('test', '

' + 'abc123123linkno'); LegacyUnit.equal(ser.serialize(DOM.get('test')), '


' + 'abc123123link' + 'no
', 'Output name and attribute rules'); ser.setRules('img[src|border=0|alt=]'); DOM.setHTML('test', ''); LegacyUnit.equal(ser.serialize(DOM.get('test')), '', 'Default attribute with empty value'); ser.setRules('img[src|border=0|alt=],div[style|id],*[*]'); DOM.setHTML('test', '
'); LegacyUnit.equal( ser.serialize(DOM.get('test'), { getInner: true }), '
' ); ser = Serializer({ valid_elements : 'img[src|border=0|alt=]', extended_valid_elements : 'div[id],img[src|alt=]' }); DOM.setHTML('test', ''); LegacyUnit.equal( ser.serialize(DOM.get('test')), '
' ); ser = Serializer({ invalid_elements : 'hr,br' }); DOM.setHTML('test', '

'); LegacyUnit.equal( ser.serialize(DOM.get('test'), { getInner: true }), '' ); }); suite.test('allow_unsafe_link_target (default)', function () { const ser = Serializer({ }); DOM.setHTML('test', 'ab'); LegacyUnit.equal( ser.serialize(DOM.get('test'), { getInner: true }), 'ab' ); DOM.setHTML('test', 'ab'); LegacyUnit.equal( ser.serialize(DOM.get('test'), { getInner: true }), 'ab' ); DOM.setHTML('test', 'ab'); LegacyUnit.equal( ser.serialize(DOM.get('test'), { getInner: true }), 'ab' ); DOM.setHTML('test', 'a'); LegacyUnit.equal( ser.serialize(DOM.get('test'), { getInner: true }), 'a' ); DOM.setHTML('test', 'a'); LegacyUnit.equal( ser.serialize(DOM.get('test'), { getInner: true }), 'a' ); }); suite.test('allow_unsafe_link_target (disabled)', function () { const ser = Serializer({ allow_unsafe_link_target: true }); DOM.setHTML('test', 'ab'); LegacyUnit.equal( ser.serialize(DOM.get('test'), { getInner: true }), 'ab' ); }); suite.test('format tree', function () { const ser = Serializer({ }); DOM.setHTML('test', 'a'); LegacyUnit.equal( ser.serialize(DOM.get('test'), { format: 'tree' }).name, 'body' ); }); suite.test('Entity encoding', function () { let ser; ser = Serializer({ entity_encoding : 'numeric' }); DOM.setHTML('test', '<>&" åäö'); LegacyUnit.equal(ser.serialize(DOM.get('test'), { getInner : true }), '<>&" åäö'); ser = Serializer({ entity_encoding : 'named' }); DOM.setHTML('test', '<>&" åäö'); LegacyUnit.equal(ser.serialize(DOM.get('test'), { getInner : true }), '<>&" åäö'); ser = Serializer({ entity_encoding : 'named+numeric', entities : '160,nbsp,34,quot,38,amp,60,lt,62,gt' }); DOM.setHTML('test', '<>&" åäö'); LegacyUnit.equal(ser.serialize(DOM.get('test'), { getInner : true }), '<>&" åäö'); ser = Serializer({ entity_encoding : 'raw' }); DOM.setHTML('test', '<>&" åäö'); LegacyUnit.equal(ser.serialize(DOM.get('test'), { getInner : true }), '<>&"\u00a0\u00e5\u00e4\u00f6'); }); suite.test('Form elements (general)', function () { const ser = Serializer({ fix_list_elements : true }); ser.setRules( 'form[method],label[for],input[type|name|value|checked|disabled|readonly|length|maxlength],select[multiple],' + 'option[value|selected],textarea[name|disabled|readonly]' ); DOM.setHTML('test', ''); LegacyUnit.equal(ser.serialize(DOM.get('test')), ''); DOM.setHTML('test', ''); LegacyUnit.equal(ser.serialize(DOM.get('test')), ''); DOM.setHTML('test', '
'); LegacyUnit.equal(ser.serialize(DOM.get('test')), '
'); DOM.setHTML('test', ''); LegacyUnit.equal(ser.serialize(DOM.get('test')), ''); DOM.setHTML('test', ''); // Edge will add an empty input value so remove that to normalize test since it doesn't break anything LegacyUnit.equal( ser.serialize(DOM.get('test')).replace(/ value=""/g, ''), '' ); }); suite.test('Form elements (checkbox)', function () { const ser = Serializer({ fix_list_elements : true }); ser.setRules('form[method],label[for],input[type|name|value|checked|disabled|readonly|length|maxlength],select[multiple],option[value|selected]'); DOM.setHTML('test', ''); LegacyUnit.equal(ser.serialize(DOM.get('test')), ''); DOM.setHTML('test', ''); LegacyUnit.equal(ser.serialize(DOM.get('test')), ''); DOM.setHTML('test', ''); LegacyUnit.equal(ser.serialize(DOM.get('test')), ''); DOM.setHTML('test', ''); LegacyUnit.equal(ser.serialize(DOM.get('test')), ''); }); suite.test('Form elements (select)', function () { const ser = Serializer({ fix_list_elements : true }); ser.setRules('form[method],label[for],input[type|name|value|checked|disabled|readonly|length|maxlength],select[multiple],option[value|selected]'); DOM.setHTML('test', ''); LegacyUnit.equal(ser.serialize(DOM.get('test')), ''); DOM.setHTML('test', ''); LegacyUnit.equal(ser.serialize(DOM.get('test')), ''); DOM.setHTML('test', ''); LegacyUnit.equal(ser.serialize(DOM.get('test')), ''); DOM.setHTML('test', ''); LegacyUnit.equal(ser.serialize(DOM.get('test')), ''); DOM.setHTML('test', ''); LegacyUnit.equal(ser.serialize(DOM.get('test')), ''); DOM.setHTML('test', ''); LegacyUnit.equal(ser.serialize(DOM.get('test')), ''); DOM.setHTML('test', ''); LegacyUnit.equal(ser.serialize(DOM.get('test')), ''); }); suite.test('List elements', function () { const ser = Serializer({ fix_list_elements : true }); ser.setRules('ul[compact],ol,li'); DOM.setHTML('test', ''); LegacyUnit.equal(ser.serialize(DOM.get('test')), ''); DOM.setHTML('test', ''); LegacyUnit.equal(ser.serialize(DOM.get('test')), ''); DOM.setHTML('test', ''); LegacyUnit.equal(ser.serialize(DOM.get('test')), ''); DOM.setHTML('test', ''); LegacyUnit.equal(ser.serialize(DOM.get('test')), ''); DOM.setHTML('test', '
  1. a
    1. b
    2. c
  2. e
'); LegacyUnit.equal(ser.serialize(DOM.get('test')), '
  1. a
    1. b
    2. c
  2. e
'); }); suite.test('Tables', function () { const ser = Serializer({ fix_list_elements : true }); ser.setRules('table,tr,td[nowrap]'); DOM.setHTML('test', '
'); LegacyUnit.equal(ser.serialize(DOM.get('test')), '
'); DOM.setHTML('test', '
'); LegacyUnit.equal(ser.serialize(DOM.get('test')), '
'); DOM.setHTML('test', '
'); LegacyUnit.equal(ser.serialize(DOM.get('test')), '
'); DOM.setHTML('test', '
'); LegacyUnit.equal(ser.serialize(DOM.get('test')), '
'); }); suite.test('Styles', function () { const ser = Serializer({ fix_list_elements : true }); ser.setRules('*[*]'); DOM.setHTML('test', 'test'); LegacyUnit.equal(ser.serialize(DOM.get('test'), { getInner: true }), 'test'); }); suite.test('Comments', function () { const ser = Serializer({ fix_list_elements : true }); ser.setRules('*[*]'); DOM.setHTML('test', ''); LegacyUnit.equal(ser.serialize(DOM.get('test'), { getInner: true }), ''); }); suite.test('Non HTML elements and attributes', function () { const ser = Serializer({ fix_list_elements : true }); ser.setRules('*[*]'); ser.schema.addValidChildren('+div[prefix:test]'); DOM.setHTML('test', '
test
'); LegacyUnit.equal(ser.serialize(DOM.get('test'), { getInner : true }), '
test
'); DOM.setHTML('test', 'test1Testtest2'); LegacyUnit.equal(ser.serialize(DOM.get('test'), { getInner: true }), 'test1Testtest2'); }); suite.test('Padd empty elements', function () { const ser = Serializer({ fix_list_elements : true }); ser.setRules('#p'); DOM.setHTML('test', '

test

'); LegacyUnit.equal(ser.serialize(DOM.get('test')), '

test

 

'); }); suite.test('Padd empty elements with BR', function () { const ser = Serializer({ padd_empty_with_br: true }); ser.setRules('#p,table,tr,#td,br'); DOM.setHTML('test', '

a

'); LegacyUnit.equal(ser.serialize(DOM.get('test')), '

a


'); DOM.setHTML('test', '

a


'); LegacyUnit.equal(ser.serialize(DOM.get('test')), '

a


'); }); suite.test('Do not padd empty elements with padded children', function () { const ser = Serializer({ fix_list_elements : true }); ser.setRules('#p,#span,b'); DOM.setHTML('test', '

'); LegacyUnit.equal(ser.serialize(DOM.get('test')), '

 

 

'); }); suite.test('Remove empty elements', function () { const ser = Serializer({ fix_list_elements : true }); ser.setRules('-p'); DOM.setHTML('test', '

test

'); LegacyUnit.equal(ser.serialize(DOM.get('test')), '

test

'); }); suite.test('Script with non JS type attribute', function () { const ser = Serializer({ fix_list_elements : true }); ser.setRules('script[type|language|src]'); DOM.setHTML('test', ''); LegacyUnit.equal(ser.serialize(DOM.get('test')).replace(/\r/g, ''), ''); }); suite.test('Script with tags inside a comment with element_format: xhtml', function () { const ser = Serializer({ fix_list_elements : true, element_format: 'xhtml' }); ser.setRules('script[type|language|src]'); DOM.setHTML('test', '// '); LegacyUnit.equal( ser.serialize(DOM.get('test')).replace(/\r/g, ''), '// \n// ]]>' ); }); suite.test('Script with tags inside a comment', function () { const ser = Serializer({ fix_list_elements : true }); ser.setRules('script[type|language|src]'); DOM.setHTML('test', '// '); LegacyUnit.equal( ser.serialize(DOM.get('test')).replace(/\r/g, ''), '// ' ); }); suite.test('Script with less than with element_format: xhtml', function () { const ser = Serializer({ fix_list_elements : true, element_format: 'xhtml' }); ser.setRules('script[type|language|src]'); DOM.setHTML('test', '1 < 2;'); LegacyUnit.equal(ser.serialize(DOM.get('test')).replace(/\r/g, ''), '// '); }); suite.test('Script with less than', function () { const ser = Serializer({ fix_list_elements : true }); ser.setRules('script[type|language|src]'); DOM.setHTML('test', '1 < 2;'); LegacyUnit.equal(ser.serialize(DOM.get('test')).replace(/\r/g, ''), '1 < 2;'); }); suite.test('Script with type attrib and less than with element_format: xhtml', function () { const ser = Serializer({ fix_list_elements : true, element_format: 'xhtml' }); ser.setRules('script[type|language|src]'); DOM.setHTML('test', '1 < 2;'); LegacyUnit.equal(ser.serialize(DOM.get('test')).replace(/\r/g, ''), ''); }); suite.test('Script with whitespace in beginning/end with element_format: xhtml', function () { const ser = Serializer({ fix_list_elements : true, element_format: 'xhtml' }); ser.setRules('script[type|language|src]'); DOM.setHTML('test', '' ); }); suite.test('Script with a HTML comment and less than with element_format: xhtml', function () { const ser = Serializer({ fix_list_elements : true, element_format: 'xhtml' }); ser.setRules('script[type|language|src]'); DOM.setHTML('test', ''); }); suite.test('Script with white space in beginning, comment and less than with element_format: xhtml', function () { const ser = Serializer({ fix_list_elements : true, element_format: 'xhtml' }); ser.setRules('script[type|language|src]'); DOM.setHTML('test', ''); }); suite.test('Script with comments and cdata with element_format: xhtml', function () { const ser = Serializer({ fix_list_elements : true, element_format: 'xhtml' }); ser.setRules('script[type|language|src]'); DOM.setHTML('test', ''); }); suite.test('Script with cdata with element_format: xhtml', function () { const ser = Serializer({ fix_list_elements : true, element_format: 'xhtml' }); ser.setRules('script[type|language|src]'); DOM.setHTML('test', ''); }); suite.test('Script whitespace in beginning/end and cdata with element_format: xhtml', function () { const ser = Serializer({ fix_list_elements : true, element_format: 'xhtml' }); ser.setRules('script[type|language|src]'); DOM.setHTML('test', ''); }); suite.test('Whitespace preserve in pre', function () { const ser = Serializer({ fix_list_elements : true }); ser.setRules('pre'); DOM.setHTML('test', '
  
'); LegacyUnit.equal(ser.serialize(DOM.get('test')), '
  
'); }); suite.test('Script with src attr', function () { const ser = Serializer({ fix_list_elements : true }); ser.setRules('script[type|language|src]'); DOM.setHTML('test', ''); }); suite.test('Script with block comment around cdata with element_format: xhtml', function () { const ser = Serializer({ fix_list_elements : true, element_format: 'xhtml' }); ser.setRules('script[type|language|src]'); DOM.setHTML('test', ''); }); suite.test('Script with html comment and block comment around cdata with element_format: xhtml', function () { const ser = Serializer({ fix_list_elements : true, element_format: 'xhtml' }); ser.setRules('script[type|language|src]'); DOM.setHTML('test', ''); }); suite.test('Script with line comment and html comment with element_format: xhtml', function () { const ser = Serializer({ fix_list_elements : true, element_format: 'xhtml' }); ser.setRules('script[type|language|src]'); DOM.setHTML('test', ''); }); suite.test('Script with block comment around html comment with element_format: xhtml', function () { const ser = Serializer({ fix_list_elements : true, element_format: 'xhtml' }); ser.setRules('script[type|language|src]'); DOM.setHTML('test', ''); }); suite.test('Protected blocks', function () { const ser = Serializer({ fix_list_elements : true }); ser.setRules('noscript[test]'); DOM.setHTML('test', ''); LegacyUnit.equal(ser.serialize(DOM.get('test')), ''); DOM.setHTML('test', ''); LegacyUnit.equal(ser.serialize(DOM.get('test')), ''); DOM.setHTML('test', '
') + '-->'); LegacyUnit.equal(ser.serialize(DOM.get('test')), ''); }); suite.test('Style with whitespace at beginning with element_format: xhtml', function () { const ser = Serializer({ fix_list_elements : true, valid_children: '+body[style]', element_format: 'xhtml' }); ser.setRules('style'); DOM.setHTML('test', ''); LegacyUnit.equal(ser.serialize(DOM.get('test')).replace(/\r/g, ''), ''); }); suite.test('Style with whitespace at beginning', function () { const ser = Serializer({ fix_list_elements : true, valid_children: '+body[style]' }); ser.setRules('style'); DOM.setHTML('test', ''); LegacyUnit.equal(ser.serialize(DOM.get('test')).replace(/\r/g, ''), ''); }); suite.test('Style with cdata with element_format: xhtml', function () { const ser = Serializer({ fix_list_elements : true, valid_children: '+body[style]', element_format: 'xhtml' }); ser.setRules('style'); DOM.setHTML('test', ''); LegacyUnit.equal(ser.serialize(DOM.get('test')).replace(/\r/g, ''), ''); }); suite.test('Style with cdata', function () { const ser = Serializer({ fix_list_elements : true, valid_children: '+body[style]' }); ser.setRules('style'); DOM.setHTML('test', ''); LegacyUnit.equal(ser.serialize(DOM.get('test')).replace(/\r/g, ''), ''); }); suite.test('CDATA', function () { const ser = Serializer({ fix_list_elements : true }); ser.setRules('span'); DOM.setHTML('test', '123abc'); LegacyUnit.equal(ser.serialize(DOM.get('test')), '123]]>abc'); DOM.setHTML('test', '123abc'); LegacyUnit.equal(ser.serialize(DOM.get('test')).replace(/\r/g, ''), '123]]>abc'); }); suite.test('BR at end of blocks', function () { const ser = Serializer({ fix_list_elements : true }); ser.setRules('ul,li,br'); DOM.setHTML('test', ''); LegacyUnit.equal(ser.serialize(DOM.get('test')), ''); }); suite.test('Map elements', function () { const ser = Serializer({ fix_list_elements : true }); ser.setRules('map[id|name],area[shape|coords|href|target|alt]'); DOM.setHTML( 'test', 'sun' ); LegacyUnit.equal( ser.serialize(DOM.get('test')).toLowerCase(), 'sun' ); }); suite.test('Custom elements', function () { const ser = Serializer({ custom_elements: 'custom1,~custom2', valid_elements: 'custom1,custom2' }); document.createElement('custom1'); document.createElement('custom2'); DOM.setHTML('test', '

c1c2

'); LegacyUnit.equal(ser.serialize(DOM.get('test')), 'c1c2'); }); suite.test('Remove internal classes', function () { const ser = Serializer({ valid_elements: 'span[class]' }); DOM.setHTML('test', ''); LegacyUnit.equal(ser.serialize(DOM.get('test')), ''); DOM.setHTML('test', ''); LegacyUnit.equal(ser.serialize(DOM.get('test')), ''); DOM.setHTML('test', ''); LegacyUnit.equal(ser.serialize(DOM.get('test')), ''); DOM.setHTML('test', ''); LegacyUnit.equal(ser.serialize(DOM.get('test')), ''); DOM.setHTML('test', ''); LegacyUnit.equal(ser.serialize(DOM.get('test')), ''); }); suite.test('Restore tabindex', function () { const ser = Serializer({ valid_elements: 'span[tabindex]' }); DOM.setHTML('test', ''); LegacyUnit.equal(ser.serialize(DOM.get('test')), ''); }); suite.test('Trailing BR (IE11)', function () { const ser = Serializer({ valid_elements: 'p,br' }); DOM.setHTML('test', '

a



'); LegacyUnit.equal(ser.serialize(DOM.get('test')), '

a

'); DOM.setHTML('test', 'a

'); LegacyUnit.equal(ser.serialize(DOM.get('test')), 'a'); }); suite.test('addTempAttr', function () { const ser = Serializer({}); ser.addTempAttr('data-x'); ser.addTempAttr('data-y'); DOM.setHTML('test', '

a

'); LegacyUnit.equal(ser.serialize(DOM.get('test'), { getInner: 1 }), '

a

'); LegacyUnit.equal(TrimHtml.trimExternal(ser, '

a

'), '

a

'); }); suite.test('addTempAttr same attr twice', function () { const ser1 = Serializer({}); const ser2 = Serializer({}); ser1.addTempAttr('data-x'); ser2.addTempAttr('data-x'); DOM.setHTML('test', '

a

'); LegacyUnit.equal(ser1.serialize(DOM.get('test'), { getInner: 1 }), '

a

'); LegacyUnit.equal(TrimHtml.trimExternal(ser1, '

a

'), '

a

'); LegacyUnit.equal(ser2.serialize(DOM.get('test'), { getInner: 1 }), '

a

'); LegacyUnit.equal(TrimHtml.trimExternal(ser2, '

a

'), '

a

'); }); suite.test('trim data-mce-bougs="all"', function () { const ser = Serializer({}); DOM.setHTML('test', 'a

b

c'); LegacyUnit.equal(ser.serialize(DOM.get('test'), { getInner: 1 }), 'ac'); LegacyUnit.equal(TrimHtml.trimExternal(ser, 'a

b

c'), 'ac'); }); suite.test('zwsp should not be treated as contents', function () { const ser = Serializer({ }); DOM.setHTML('test', '

' + Zwsp.ZWSP + '

'); LegacyUnit.equal( ser.serialize(DOM.get('test'), { getInner: true }), '

 

' ); }); viewBlock.attach(); viewBlock.get().id = 'test'; Pipeline.async({}, addTeardown(suite.toSteps({})), function () { viewBlock.detach(); success(); }, failure); });