diff --git a/package.json b/package.json index aa9178a..4682f95 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "all": "run-s lint bundle test-coverage test-perf", "lint": "eslint .", "bundle": "rollup -c", - "test": "mocha test/index.js", + "test": "mocha test/*.js", "test-coverage": "NO_PERF=1 nyc --reporter=lcov --reporter=html npm test", "test-perf": "node test/perf/index.cjs", "prepublishOnly": "run-s bundle test-perf" diff --git a/test/elements.js b/test/elements.js index 517a534..c9a90d0 100644 --- a/test/elements.js +++ b/test/elements.js @@ -8,1571 +8,1574 @@ import test from './test.js'; // // 'http://purl.org/rss/1.0/': 'rss', // // } -test({ - xml: '
', - expect: [ - [ 'openTag', 'div', {}, true ], - [ 'closeTag', 'div', true ], - ], -}); - -test({ - xml: '
', - expect: [ - [ 'openTag', 'div', {}, true ], - [ 'closeTag', 'div', true ], - ], -}); - -test({ - xml: '
', - expect: [ - [ 'openTag', 'div', {}, false ], - [ 'closeTag', 'div', false ], - ] -}); - -test({ - xml: '
', - expect: [ - [ 'openTag', 'DIV', {}, true ], - [ 'closeTag', 'DIV', true ], - ], -}); - -test({ - xml: '', - expect: [ - [ 'openTag', 'dateTime.iso8601', {}, true ], - [ 'closeTag', 'dateTime.iso8601', true ], - ], -}); - -test({ - xml: '
', - expect: [ - [ 'openTag', 'DIV', {}, true ], - [ 'closeTag', 'DIV', true ], - ], -}); - -test({ - xml: '
', - expect: [ - [ 'openTag', 'DIV', {}, false ], - [ 'closeTag', 'DIV', false ], - ] -}); - -test({ - xml: '
', - expect: [ - [ 'openTag', 'DIV' ], - [ 'closeTag', 'DIV' ], - ], -}); - -test({ - xml: '
', - expect: [ - [ 'openTag', 'div' ], - [ 'error', 'close tag' ], - ] -}); - -test({ - xml: '\n\x01asdasd', - expect: [ - [ 'error', 'missing start tag' ] - ] -}); - -test({ - xml: '', - expect: [ - [ 'attention', '', { - data: '', - line: 0, - column: 0 - } ], - ], -}); - -test({ - xml: '', - expect: [ - [ 'comment', ' HELLO ', { - data: '', - expect: [ - [ 'openTag', 'a', {}, false ], - [ 'comment', 'comment text' ], - [ 'closeTag', 'a', false ], - ], -}); - -test({ - xml: '', - expect: [ - [ 'openTag', 'root' ], - [ 'openTag', 'foo' ], - [ 'error', 'unexpected end of file', { data: '', line: 0, column: 11 } ], - ], -}); - -test({ - xml: '', - expect: [ - [ 'openTag', 'root', {}, false ], - [ 'error', 'closing tag mismatch', { data: '', line: 0, column: 6 } ] - ], -}); - -test({ - xml: 'text', - expect: [ - [ 'openTag', 'root' ], - [ 'text', 'text', { - data: 'ext', - line: 0, - column: 10 - } ], - [ 'closeTag', 'root' ], - ], -}); - -// text / before first tag -test({ - xml: 'a', - expect: [ - [ 'warn', 'non-whitespace outside of root node', { data: 'a', line: 0, column: 0 } ], - [ 'openTag', 'root' ], - [ 'closeTag', 'root' ], - ], -}); - -// text / after last tag -test({ - xml: 'a', - expect: [ - [ 'openTag', 'root' ], - [ 'closeTag', 'root' ], - [ 'warn', 'non-whitespace outside of root node', { data: 'a', line: 0, column: 8 } ], - ], -}); - -// text / around child element -test({ - xml: 'ab', - expect: [ - [ 'openTag', 'root' ], - [ 'text', 'a' ], - [ 'openTag', 'child' ], - [ 'closeTag', 'child' ], - [ 'text', 'b' ], - [ 'closeTag', 'root' ], - ], -}); - -// processing instruction + whitespace outside of root node -test({ - xml: '\n\t ', - expect: [ - [ 'question', '' ], - [ 'openTag', 'root' ], - [ 'closeTag', 'root' ] - ], -}); - -// processing instruction + non-whitespace outside of root node -test({ - xml: '\na\n\n', - expect: [ - [ 'question', '' ], - [ 'warn', 'non-whitespace outside of root node' ], - [ 'openTag', 'root' ], - [ 'closeTag', 'root' ] - ], -}); - -// multiple-root elements -test({ - xml: '\n', - expect: [ - [ 'openTag', 'root' ], - [ 'closeTag', 'root' ], - [ 'openTag', 'otherRoot' ], - [ 'closeTag', 'otherRoot' ] - ], -}); - -// multiple-root elements / xmlns -test({ - xml: - '' + - '', - ns: true, - expect: [ - [ 'openTag', 'atom:root' ], - [ 'closeTag', 'atom:root' ], - [ 'openTag', 'ns0:otherRoot' ], - [ 'closeTag', 'ns0:otherRoot' ] - ], -}); - -// attributes / "" -test({ - xml: '', - expect: [ - [ 'openTag', 'root', { LENGTH: 'abc=ABC' }, false ], - [ 'closeTag', 'root', false ], - ], -}); - -// attributes / no xmlns assignment -test({ - xml: '', - expect: [ - [ 'warn', 'illegal declaration of xmlns' ], - [ 'openTag', 'root', { a: 'B' } ], - [ 'closeTag', 'root' ], - ], -}); - -// attributes / '' -test({ - xml: '', - expect: [ - [ 'openTag', 'root', { length: 'abc=abc' }, false ], - [ 'closeTag', 'root', false ], - ], -}); - -// attributes / = inside attribute -test({ - xml: '', - expect: [ - [ 'openTag', 'root', { _abc: 'abc=abc', ':abc': 'abc' }, false ], - [ 'closeTag', 'root', false ], - ], -}); - -// attributes / space between attributes -test({ - xml: '', - expect: [ - [ 'openTag', 'root', { attr1: 'first', attr2: 'second' }, true ], - [ 'closeTag', 'root', true ], - ], -}); - -// attributes / warnings / no space between attributes -test({ - xml: '', - expect: [ - [ 'warn', 'illegal character after attribute end' ], - [ 'warn', 'illegal character after attribute end' ], - [ 'openTag', 'root', { a: 'B' }, true ], - [ 'closeTag', 'root', true ], - ], -}); - -// attributes / warnings / illegal first char -test({ - xml: '', - expect: [ - [ 'warn', 'illegal first char attribute name' ], - [ 'openTag', 'root', { a: 'B' }, true ], - [ 'closeTag', 'root', true ], - ], -}); - -test({ - xml: '', - expect: [ - [ 'warn', 'illegal first char attribute name' ], - [ 'openTag', 'root', { a: 'B' }, true ], - [ 'closeTag', 'root', true ], - ], -}); - -// attributes / warnings / open - close missmatch -test({ - xml: '', - expect: [ - [ 'warn', 'attribute value quote missmatch' ], - [ 'openTag', 'root', { a: 'B' }, true ], - [ 'closeTag', 'root', true ], - ], -}); - -// attributes / warnings / illegal first char attribute name -test({ - xml: '', - expect: [ - [ 'warn', 'illegal first char attribute name' ], - [ 'warn', 'illegal first char attribute name' ], - [ 'openTag', 'root', { attr2: 'second' }, true ], - [ 'closeTag', 'root', true ], - ], -}); - -// attributes / unicode attr value -test({ - xml: '', - expect: [ - [ 'openTag', 'root', { rain: '☂' }, true ], - [ 'closeTag', 'root', true ], - ], -}); - -// attributes / warnings / illegal first char attribute name -test({ - xml: '', - expect: [ - [ 'warn', 'illegal first char attribute name' ], - [ 'openTag', 'root', { attr2: 'second' }, true ], - [ 'closeTag', 'root', true ], - ], -}); - -// attributes / warnings / illegal attribute name char -test({ - xml: '', - expect: [ - [ 'warn', 'illegal attribute name char' ], - [ 'openTag', 'root', { attr2: 'second' }, true ], - [ 'closeTag', 'root', true ], - ], -}); - -test({ - xml: '', - expect: [ - [ 'openTag', 'root', { 'xmlns:color_1-.0': 'http://color' }, true ], - [ 'closeTag', 'root', true ], - ], -}); - -test({ - xml: '', - expect: [ - [ 'openTag', 'root', { 'xmlns:b:c': 'http://color' }, true ], - [ 'closeTag', 'root', true ], - ], -}); - -test({ - xml: '', - expect: [ - [ 'openTag', 'root', { 'color_1-.0': 'green' }, true ], - [ 'closeTag', 'root', true ], - ], -}); - -// attributes / warnings / attribute without value -test({ - xml: '', - expect: [ - [ 'warn', 'missing attribute value' ], - [ 'openTag', 'root', { a: 'B' }, true ], - [ 'closeTag', 'root', true ], - ], -}); - -test({ - xml: '', - expect: [ - [ 'warn', 'missing attribute value' ], - [ 'openTag', 'root', { a: 'B' }, true ], - [ 'closeTag', 'root', true ], - ], -}); - -// attributes / warnings / missing quoting -test({ - xml: '', - expect: [ - [ 'warn', 'missing attribute value quotes' ], - [ 'openTag', 'root', { a: 'B' }, true ], - [ 'closeTag', 'root', true ], - ], -}); - -// attributes / warnings / missing quoting -test({ - xml: '', - expect: [ - [ 'warn', 'missing attribute value quotes' ], - [ 'openTag', 'root', { a: 'B' }, true ], - [ 'closeTag', 'root', true ], - ], -}); - -test({ - xml: '', - expect: [ - [ 'openTag', 'root', { length: '12345' }, false ], - [ 'openTag', 'item', {}, true ], - [ 'closeTag', 'item', true ], - [ 'closeTag', 'root', false ] - ], -}); - -test({ - xml: '', - expect: [ - [ 'openTag', 'r' ], - [ 'cdata', ' this is ' ], - [ 'cdata', ' this is [] ' ], - [ 'closeTag', 'r' ], - ], -}); - -test({ - xml: '', - expect: [ - [ 'openTag', 'r' ], - [ 'cdata', '[[[[[[[[]]]]]]]]' ], - [ 'closeTag', 'r' ], - ], -}); - -test({ - xml: '', - expect: [ - [ 'openTag', 'r' ], - [ 'error', 'unclosed cdata' ], - ], -}); - -test({ - xml: '<![CDATA[ this is ]]>', - expect: [ - [ 'openTag', 'r' ], - [ 'text', '<![CDATA[ this is ]]>' ], - [ 'closeTag', 'r' ], - ], -}); - -test({ - xml: '', - expect: [ - [ 'openTag', 'html' ], - [ 'openTag', 'head' ], - [ 'openTag', 'script' ], - [ 'text', '\'' ], - [ 'openTag', 'div' ], - [ 'text', 'foo' ], - [ 'closeTag', 'div' ], - [ 'error', 'closing tag mismatch' ], - ] -}); - -// namespaces -test({ - xml: '', - ns: true, - expect: [ - [ 'openTag', 'xmlns' ], - [ 'closeTag', 'xmlns' ], - ] -}); - -// no root namespace (broken rss?) -test({ - xml: '' + - '' + - '', - ns: true, - expect: [ - [ 'openTag', 'rss', { 'xmlns:atom': 'http://www.w3.org/2005/Atom', version: '2.0' } ], - [ 'openTag', 'channel' ], - [ 'closeTag', 'channel' ], - [ 'closeTag', 'rss' ] - ] -}); - -test({ - xml: '', - ns: true, - expect: [ - [ 'openTag', 'atom:feed', { id: 'aa', 'media:title': 'bb' } ], - [ 'closeTag', 'atom:feed' ], - ], -}); - -test({ - xml: '', - ns: true, - expect: [ - [ 'openTag', 'atom:feed', { id: 'aa', 'media:title': 'bb' } ], - [ 'closeTag', 'atom:feed' ], - ], -}); - -test({ - xml: '', - ns: true, - expect: [ - [ 'openTag', 'atom:feed', { id: 'aa', 'media:title': 'bb' } ], - [ 'closeTag', 'atom:feed' ], - ], -}); - -test({ - xml: '', - ns: true, - expect: [ - [ 'openTag', 'atom:feed', { id: 'aa', 'title': 'bb' } ], - [ 'closeTag', 'atom:feed' ], - ], -}); - -test({ - xml: 'text', - ns: true, - expect: [ - [ 'openTag', 'atom:feed' ], - [ 'openTag', 'media:title' ], - [ 'text', 'text' ], - [ 'closeTag', 'media:title' ], - [ 'closeTag', 'atom:feed' ], - ], -}); - -test({ - xml: 'text', - ns: true, - expect: [ - [ 'openTag', 'atom:feed' ], - [ 'openTag', 'media:title' ], - [ 'text', 'text' ], - [ 'closeTag', 'media:title' ], - [ 'closeTag', 'atom:feed' ], - ], -}); - -test({ - xml: 'text', - ns: true, - expect: [ - [ 'openTag', 'atom:feed' ], - [ 'openTag', 'atom:title' ], - [ 'text', 'text' ], - [ 'closeTag', 'atom:title' ], - [ 'closeTag', 'atom:feed' ], - ], -}); - -test({ - xml: '<:text/>', - ns: true, - expect: [ - [ 'openTag', 'atom:feed', { id: 'aa', 'media:title': 'bb' } ], - [ 'openTag', 'media:text' ], - [ 'closeTag', 'media:text' ], - [ 'closeTag', 'atom:feed' ], - ], -}); - -// un-registered namespace handling -test({ - xml: '', - ns: true, - expect: [ - [ 'openTag', 'ns0:root', { id: 'aa', 'bar:title': 'bb' } ], - [ 'openTag', 'bar:child' ], - [ 'closeTag', 'bar:child' ], - [ 'closeTag', 'ns0:root' ], - ], -}); - -// context with whitespace -test({ - xml: ( - '\r' + - ' <:text/>\n' + - '' - ), - ns: true, - expect: [ - [ 'openTag', 'atom:feed', { id: 'aa', 'media:title': 'bb' }, false, { - line: 0, - column: 0, - data: '' - } ], - [ 'text', '\r ' ], - [ 'openTag', 'media:text', {}, true, { line: 2, column: 2, data: '<:text/>' } ], - [ 'closeTag', 'media:text', true, { line: 2, column: 2, data: '<:text/>' } ], - [ 'text', '\n' ], - [ 'closeTag', 'atom:feed', false, { - line: 3, - column: 0, - data: '' - } ], - ], -}); - -// context without whitespace -test({ - xml: ( - '' + - '<:text/>' + - '' - ), - ns: true, - expect: [ - [ 'openTag', 'atom:feed', { id: 'aa', 'media:title': 'bb' }, false, { - line: 0, - column: 0, - data: '' - } ], - [ 'openTag', 'media:text', {}, true, { line: 0, column: 101, data: '<:text/>' } ], - [ 'closeTag', 'media:text', true, { line: 0, column: 101, data: '<:text/>' } ], - [ 'closeTag', 'atom:feed', false, { - line: 0, - column: 109, - data: '' - } ], - ], -}); - -// context with anonymous namespace -test({ - xml: ( - '' - ), - ns: true, - expect: [ - [ 'openTag', 'ns0:foo', { id: 'aa', 'that:title': 'bb' }, true, { - line: 0, - column: 0, - data: '' - } ], - [ 'closeTag', 'ns0:foo', true, { - line: 0, - column: 0, - data: '' - } ], - ], -}); - -// nested namespace handling -test({ - xml: ( - '' + - '' + - '' - ), - ns: true, - expect: [ - [ 'openTag', 'ns0:foo', {}, false ], - [ 'openTag', 'ns1:t', { id: 'aa', 'bar:title': 'bb' }, true ], - [ 'closeTag', 'ns1:t', true ], - [ 'closeTag', 'ns0:foo', false ], - ], -}); - -// nested namespace handling -test({ - xml: ( - '' + - '' + - '' + - '' + - '' + - '' - ), - ns: true, - expect: [ - [ 'openTag', 'ns0:foo' ], - [ 'openTag', 'ns1:t' ], - [ 'openTag', 'ns1:n' ], - [ 'closeTag', 'ns1:n' ], - [ 'openTag', 'ns1:n' ], - [ 'closeTag', 'ns1:n' ], - [ 'closeTag', 'ns1:t' ], - [ 'closeTag', 'ns0:foo' ], - ], -}); - -// nested with default namespace -test({ - xml: ( - '' + - '' + - '' + - '' + - '' - ), - ns: true, - expect: [ - [ 'openTag', 'ns0:foo' ], - [ 'openTag', 'ns1:t' ], - [ 'openTag', 'ns1:n', { id: 'b', 'bar:title': 'BAR' } ], - [ 'closeTag', 'ns1:n' ], - [ 'closeTag', 'ns1:t' ], - [ 'closeTag', 'ns0:foo' ], - ], -}); - -// nested with non-default namespace handling -test({ - xml: ( - '' + - '' + - '' + - '' + - '' - ), - ns: true, - expect: [ - [ 'openTag', 'foo:root' ], - [ 'openTag', 'bar:outer' ], - [ 'openTag', 'nested' ], - [ 'closeTag', 'nested' ], - [ 'closeTag', 'bar:outer' ], - [ 'closeTag', 'foo:root' ], - ], -}); - -// nested namespace re-declaration -test({ - xml: ( - '' + - '' + - '' + - '' + - '' - ), - ns: true, - expect: [ - [ 'openTag', 'ns0:foo' ], - [ 'openTag', 'ns1:t' ], - [ 'openTag', 'bar:other', { 'bar:attr': 'BAR' } ], - [ 'closeTag', 'bar:other' ], - [ 'closeTag', 'ns1:t' ], - [ 'closeTag', 'ns0:foo' ], - ], -}); - -// test namespace attribute exposure -test({ - xml: ( - '' - ), - ns: true, - expect: [ - [ 'openTag', 'ns0:foo', { xmlns: 'http://xxx' } ], - [ 'closeTag', 'ns0:foo', false ], - ], -}); - -// test namespace attribute rewrite -test({ - xml: ( - '' - ), - ns: true, - expect: [ - [ 'openTag', 'ns0:foo', { - 'xmlns:a': 'http://www.w3.org/2005/Atom', - 'xmlns': 'http://xxx', - 'atom:xx': 'foo' - } ], - [ 'closeTag', 'ns0:foo', false ], - ], -}); - -// test missing namespace / element -test({ - xml: ( - '' + - '' + - '' - ), - ns: true, - expect: [ - [ 'openTag', 'ns0:foo' ], - [ 'error', 'missing namespace on ', { - data: '', - line: 0, - column: 24 - } ] - ], -}); - -// illegal namespace prefix -test({ - xml: ( - '' - ), - ns: true, - expect: [ - [ 'error', 'invalid nodeName' ], - ], -}); - -// namespace collision -test({ - xml: ( - '' - ), - ns: true, - expect: [ - [ 'openTag', 'ns0:foo' ], - [ 'closeTag', 'ns0:foo' ], - ], -}); - -// anonymous ns prefix collision -test({ - xml: ( - '' - ), - ns: true, - expect: [ - [ 'openTag', 'ns0:foo', { 'ns1:bar': 'BAR' } ], - [ 'closeTag', 'ns0:foo' ], - ], -}); - -// NOT normalize xsi:type -test({ - xml: ( - '' - ), - ns: true, - expect: [ - [ 'openTag', 'ns0:foo', { 'xsi:type': 'Foo' } ], - [ 'closeTag', 'ns0:foo' ], - ], -}); - -// NOT remap xsi:type -test({ - xml: ( - '' - ), - ns: true, - expect: [ - [ 'openTag', 'ns0:foo', { 'o:type': 'Foo' } ], - [ 'closeTag', 'ns0:foo' ], - ], -}); - -// NOT normalize xsi:type -test({ - xml: ( - '' - ), - ns: true, - expect: [ - [ 'openTag', 'ns0:foo', { 'xsi:type': 'Foo' } ], - [ 'closeTag', 'ns0:foo' ], - ], -}); - -// NOT normalize prefixed xsi:type -test({ - xml: ( - '' - ), - ns: true, - expect: [ - [ 'openTag', 'ns0:foo', { 'xsi:type': 'bar:Bar' } ], - [ 'closeTag', 'ns0:foo' ], - ], -}); - -// NOT normalize xsi:type / preserve unknown prefix in value -test({ - xml: ( - '' - ), - ns: true, - expect: [ - [ 'openTag', 'ns0:foo', { 'xsi:type': 'xs:string' } ], - [ 'closeTag', 'ns0:foo' ], - ], -}); - -// NOT normalize nested xsi:type -test({ - xml: ( - '' + - '' + - '' - ), - ns: true, - expect: [ - [ 'openTag', 'ns0:foo' ], - [ 'openTag', 'ns0:bar', { 'xsi:type': 'Bar' } ], - [ 'closeTag', 'ns0:bar' ], - [ 'closeTag', 'ns0:foo' ], - ], -}); - -// normalize nested prefixed xsi:type -test({ - xml: ( - '' + - '' + - '' - ), - ns: true, - expect: [ - [ 'openTag', 'ns0:foo' ], - [ 'openTag', 'ns0:bar', { 'xsi:type': 'bar:Bar' } ], - [ 'closeTag', 'ns0:bar' ], - [ 'closeTag', 'ns0:foo' ], - ], -}); - -// normalize nested xsi:type / preserve unknown prefix in value -test({ - xml: ( - '' + - '' + - '' - ), - ns: true, - expect: [ - [ 'openTag', 'ns0:foo' ], - [ 'openTag', 'ns0:bar', { 'xsi:type': 'xs:string' } ], - [ 'closeTag', 'ns0:bar' ], - [ 'closeTag', 'ns0:foo' ], - ], -}); - -// attributes / missing namespace for prefixed -test({ - xml: ( - '' - ), - ns: true, - expect: [ - [ 'warn', 'missing namespace for prefix ', { - column: 0, - line: 0, - data: '' - } ], - [ 'openTag', 'foo:foo' ], - [ 'closeTag', 'foo:foo' ] - ], -}); - -test({ - xml: ( - '' + - '' + - '' - ), - ns: true, - expect: [ - [ 'openTag', 'ns0:foo' ], - [ 'warn', 'missing namespace for prefix ' ], - [ 'openTag', 'ns0:bar' ], - [ 'closeTag', 'ns0:bar' ], - [ 'closeTag', 'ns0:foo' ] - ], -}); - -// attributes / missing namespace for prefixed / default ns -test({ - xml: ( - '' - ), - ns: true, - expect: [ - [ 'warn', 'missing namespace for prefix ' ], - [ 'openTag', 'ns0:foo' ], - [ 'closeTag', 'ns0:foo' ] - ], -}); - -// whitespace / BOM at start -test({ - xml: '\uFEFF
\uFEFF
', - expect: [ - [ 'openTag', 'div' ], - [ 'text', '\uFEFF' ], - [ 'closeTag', 'div' ], - ], -}); - -// whitespace / _ at start -test({ - xml: ' \uFEFF
', - expect: [ - [ 'openTag', 'div' ], - [ 'closeTag', 'div' ], - ], -}); - -// cyrillic in text -test({ - xml: '

тест

', - expect: [ - [ 'openTag', 'P' ], - [ 'text', 'тест' ], - [ 'closeTag', 'P' ], - ], -}); - -// kanji in attribute value -test({ - xml: '

', - expect: [ - [ 'openTag', 'P', { foo: '误' }, true ], - [ 'closeTag', 'P' ], - ], -}); - -// nested namespace re-declaration -test({ - xml: '' + - '' + - '' + - '' + - '' + - '' + - '' + - '', - ns: true, - expect: [ - [ 'openTag', 'e:root', { 'xmlns:e': 'http://extensions' } ], - [ 'openTag', 'bar:bar', { 'xmlns:bar': 'http://bar' } ], - [ 'openTag', 'other:child', { b: 'B', 'xmlns:other': 'http://other' } ], - [ 'closeTag', 'other:child' ], - [ 'closeTag', 'bar:bar' ], - [ 'openTag', 'ns0:foo', { xmlns: 'http://foo' } ], - [ 'openTag', 'ns0:child', { a: 'A' } ], - [ 'closeTag', 'ns0:child' ], - [ 'closeTag', 'ns0:foo' ], - [ 'closeTag', 'e:root' ] - ], -}); - -// local namespace re-declaration -test({ - xml: '', - ns: true, - expect: [ - [ 'warn', 'attribute already defined' ], - [ 'openTag', 'e:root', { 'xmlns:e': 'http://extensions' } ], - [ 'closeTag', 'e:root' ] - ], -}); - -// local namespace re-declaration / default namespace -test({ - xml: '', - ns: true, - expect: [ - [ 'warn', 'attribute already defined' ], - [ 'openTag', 'ns0:root', { xmlns: 'http://extensions' } ], - [ 'closeTag', 'ns0:root' ] - ], -}); - -// local attribute re-declaration / no ns -test({ - xml: '', - expect: [ - [ 'warn', 'attribute already defined' ], - [ 'openTag', 'root', { a: 'A' } ], - [ 'closeTag', 'root' ] - ], -}); - -// local attribute re-declaration / with namespace -test({ - xml: '', - ns: true, - expect: [ - [ 'warn', 'attribute already defined' ], - [ 'openTag', 'ns0:root', { xmlns: 'http://extensions', a: 'A' } ], - [ 'closeTag', 'ns0:root' ] - ], -}); - -// local attribute re-declaration / with other namespace -test({ - xml: '', - ns: true, - expect: [ - [ 'warn', 'attribute already defined' ], - [ 'openTag', 'ns0:root', { - xmlns: 'http://extensions', - 'xmlns:bar': 'http://bar', - 'bar:a': 'A' - } ], - [ 'closeTag', 'ns0:root' ] - ], -}); - -// Should not throw with '>' in attribute value of regular element (#17) -test({ - xml: '', - ns: true, - expect: [ - [ 'openTag', 'doc', {}, false ], - [ 'openTag', 'element', { id: 'sample>error' }, false ], - [ 'closeTag', 'element', false ], - [ 'closeTag', 'doc', false ], - ], -}); - -test({ - xml: ' \n \n ', - ns: true, - expect: [ - [ 'openTag', 'doc', {}, false ], - [ 'text', ' \n' ], - [ 'openTag', 'element', { id: 'sample>error' }, false ], - [ 'text', ' \n ' ], - [ 'closeTag', 'element', false ], - [ 'closeTag', 'doc', false ], - ], -}); - -// should handle > in attribute name -test({ - xml: 'o="FOO" bar="BAR" />', - ns: true, - expect: [ - [ 'openTag', 'doc', {}, false ], - [ 'warn', 'missing attribute value quotes' ], - [ 'openTag', 'element', {}, false ], - [ 'text', 'o="FOO" bar="BAR" />' ], - [ 'error', 'closing tag mismatch' ], - ], -}); - -// should handle > between attributes -test({ - xml: '> bar="BAR" />', - ns: true, - expect: [ - [ 'openTag', 'doc', {}, false ], - [ 'openTag', 'element', { foo: 'FOO' }, false ], - [ 'text', '> bar="BAR" />' ], - [ 'error', 'closing tag mismatch' ] - ], -}); - - -// should handle > after attribute -test({ - xml: ' bar="BAR" />', - ns: true, - expect: [ - [ 'openTag', 'doc', {}, false ], - [ 'openTag', 'element', { foo: 'FOO' }, false ], - [ 'text', ' bar="BAR" />' ], - [ 'error', 'closing tag mismatch' ] - ], -}); - -// should handle > after tag name -test({ - xml: '>', - ns: true, - expect: [ - [ 'openTag', 'doc', {}, false ], - [ 'openTag', 'element', {}, true ], - [ 'closeTag', 'element', true ], - [ 'text', '>' ], - [ 'closeTag', 'doc', false ] - ], -}); - -// should handle > in tag name -test({ - xml: '/>', - ns: true, - expect: [ - [ 'openTag', 'doc', {}, false ], - [ 'openTag', 'element', {}, false ], - [ 'text', '/>' ], - [ 'error', 'closing tag mismatch' ] - ], -}); - -// should not throw with '>' in attribute value of self-closing element (#17) -test({ - xml: '', - ns: true, - expect: [ - [ 'openTag', 'doc', {}, false ], - [ 'openTag', 'element', { id: 'sample>error' }, true ], - [ 'closeTag', 'element', true ], - [ 'closeTag', 'doc', false ], - ], -}); - -test({ - xml: ' \n ', - ns: true, - expect: [ - [ 'openTag', 'doc', {}, false ], - [ 'text', ' \n' ], - [ 'openTag', 'element', { id: 'sample>error' }, true ], - [ 'closeTag', 'element', true ], - [ 'text', ' ' ], - [ 'closeTag', 'doc', false ], - ], -}); - -// should not throw with '>' in attribute value of self-closing element at the end of the document -test({ - xml: '', - ns: true, - expect: [ - [ 'openTag', 'doc', {}, false ], - [ 'closeTag', 'doc', false ], - [ 'openTag', 'element', { id: 'sample>error' }, true ], - [ 'closeTag', 'element', true ], - ], -}); - -test({ - xml: '\n ', - ns: true, - expect: [ - [ 'openTag', 'doc', {}, false ], - [ 'closeTag', 'doc', false ], - [ 'openTag', 'element', { id: 'sample>error' }, true ], - [ 'closeTag', 'element', true ], - ], -}); - -// should not throw with '>' in attribute value of self-closing element at the end of the document -test({ - xml: '', - ns: true, - expect: [ - [ 'openTag', 'doc', {}, false ], - [ 'closeTag', 'doc', false ], - [ 'comment', ' !>>> ' ], - [ 'openTag', 'element', { id: 'sample>error' }, true ], - [ 'closeTag', 'element', true ], - ] -}); - -test({ - xml: ' ', - ns: true, - expect: [ - [ 'openTag', 'doc', {}, false ], - [ 'closeTag', 'doc', false ], - [ 'comment', ' !>>> ' ], - [ 'openTag', 'element', { id: 'sample>error' }, true ], - [ 'closeTag', 'element', true ], - ] -}); - -// should handle > in content -test({ - xml: ' bar="BAR" />', - ns: true, - expect: [ - [ 'openTag', 'doc', {}, false ], - [ 'openTag', 'element', { foo: 'FO\'O' }, false ], - [ 'text', ' bar="BAR" />' ], - [ 'closeTag', 'element', false ], - [ 'closeTag', 'doc', false ] - ], -}); - -test({ - xml: ' bar="BAR" />', - ns: true, - expect: [ - [ 'openTag', 'doc', {}, false ], - [ 'openTag', 'element', { foo: 'FO"O' }, false ], - [ 'text', ' bar="BAR" />' ], - [ 'closeTag', 'element', false ], - [ 'closeTag', 'doc', false ] - ], -}); - -test({ - xml: ' bar="BAR" />', - ns: true, - expect: [ - [ 'openTag', 'doc', {}, false ], - [ 'openTag', 'element', { foo: "FO'O" }, false ], - [ 'text', ' bar="BAR" />' ], - [ 'error', 'closing tag mismatch' ] - ], -}); - -test({ - xml: ' bar="BAR" />', - ns: true, - expect: [ - [ 'openTag', 'doc', {}, false ], - [ 'openTag', 'element', { foo: 'FO"O' }, false ], - [ 'text', ' bar="BAR" />' ], - [ 'error', 'closing tag mismatch' ] - ], -}); - -test({ - xml: ' bar="BAR" />', - ns: true, - expect: [ - [ 'openTag', 'doc', {}, false ], - [ 'openTag', 'element', { foo: "FO'O" }, false ], - [ 'text', ' bar="BAR" />' ], - [ 'error', 'closing tag mismatch' ] - ], -}); - -test({ - xml: ' bar="BAR" >', - ns: true, - expect: [ - [ 'openTag', 'doc', {}, false ], - [ 'comment', ' foo=\'FO"O\' ' ], - [ 'text', ' bar="BAR" >' ], - [ 'closeTag', 'doc', false ] - ], -}); - -test({ - xml: ' bar="BAR" >', - ns: true, - expect: [ - [ 'openTag', 'doc', {}, false ], - [ 'attention', '' ], - [ 'text', ' bar="BAR" >' ], - [ 'closeTag', 'doc', false ] - ], -}); - -test({ - xml: ' bar="BAR" >', - ns: true, - expect: [ - [ 'openTag', 'doc', {}, false ], - [ 'attention', '' ], - [ 'text', ' bar="BAR" >' ], - [ 'closeTag', 'doc', false ] - ], -}); - -test({ - xml: '', - ns: true, - expect: [ - [ 'openTag', 'doc', {}, false ], - [ 'attention', '' ], - [ 'error', 'unexpected end of file' ] - ], -}); - -test({ - xml: '', - ns: true, - expect: [ - [ 'openTag', 'doc', {}, false ], - [ 'attention', '' ], - [ 'error', 'unexpected end of file' ] - ], -}); - +describe('elements', () => { + + test({ + xml: '

', + expect: [ + [ 'openTag', 'div', {}, true ], + [ 'closeTag', 'div', true ], + ], + }); + + test({ + xml: '
', + expect: [ + [ 'openTag', 'div', {}, true ], + [ 'closeTag', 'div', true ], + ], + }); + + test({ + xml: '
', + expect: [ + [ 'openTag', 'div', {}, false ], + [ 'closeTag', 'div', false ], + ] + }); + + test({ + xml: '
', + expect: [ + [ 'openTag', 'DIV', {}, true ], + [ 'closeTag', 'DIV', true ], + ], + }); + + test({ + xml: '', + expect: [ + [ 'openTag', 'dateTime.iso8601', {}, true ], + [ 'closeTag', 'dateTime.iso8601', true ], + ], + }); + + test({ + xml: '
', + expect: [ + [ 'openTag', 'DIV', {}, true ], + [ 'closeTag', 'DIV', true ], + ], + }); + + test({ + xml: '
', + expect: [ + [ 'openTag', 'DIV', {}, false ], + [ 'closeTag', 'DIV', false ], + ] + }); + + test({ + xml: '
', + expect: [ + [ 'openTag', 'DIV' ], + [ 'closeTag', 'DIV' ], + ], + }); + + test({ + xml: '
', + expect: [ + [ 'openTag', 'div' ], + [ 'error', 'close tag' ], + ] + }); + + test({ + xml: '\n\x01asdasd', + expect: [ + [ 'error', 'missing start tag' ] + ] + }); + + test({ + xml: '', + expect: [ + [ 'attention', '', { + data: '', + line: 0, + column: 0 + } ], + ], + }); + + test({ + xml: '', + expect: [ + [ 'comment', ' HELLO ', { + data: '
', + expect: [ + [ 'openTag', 'a', {}, false ], + [ 'comment', 'comment text' ], + [ 'closeTag', 'a', false ], + ], + }); + + test({ + xml: '', + expect: [ + [ 'openTag', 'root' ], + [ 'openTag', 'foo' ], + [ 'error', 'unexpected end of file', { data: '', line: 0, column: 11 } ], + ], + }); + + test({ + xml: '', + expect: [ + [ 'openTag', 'root', {}, false ], + [ 'error', 'closing tag mismatch', { data: '', line: 0, column: 6 } ] + ], + }); + + test({ + xml: 'text', + expect: [ + [ 'openTag', 'root' ], + [ 'text', 'text', { + data: 'ext', + line: 0, + column: 10 + } ], + [ 'closeTag', 'root' ], + ], + }); + + // text / before first tag + test({ + xml: 'a', + expect: [ + [ 'warn', 'non-whitespace outside of root node', { data: 'a', line: 0, column: 0 } ], + [ 'openTag', 'root' ], + [ 'closeTag', 'root' ], + ], + }); + + // text / after last tag + test({ + xml: 'a', + expect: [ + [ 'openTag', 'root' ], + [ 'closeTag', 'root' ], + [ 'warn', 'non-whitespace outside of root node', { data: 'a', line: 0, column: 8 } ], + ], + }); + + // text / around child element + test({ + xml: 'ab', + expect: [ + [ 'openTag', 'root' ], + [ 'text', 'a' ], + [ 'openTag', 'child' ], + [ 'closeTag', 'child' ], + [ 'text', 'b' ], + [ 'closeTag', 'root' ], + ], + }); + + // processing instruction + whitespace outside of root node + test({ + xml: '\n\t ', + expect: [ + [ 'question', '' ], + [ 'openTag', 'root' ], + [ 'closeTag', 'root' ] + ], + }); + + // processing instruction + non-whitespace outside of root node + test({ + xml: '\na\n\n', + expect: [ + [ 'question', '' ], + [ 'warn', 'non-whitespace outside of root node' ], + [ 'openTag', 'root' ], + [ 'closeTag', 'root' ] + ], + }); + + // multiple-root elements + test({ + xml: '\n', + expect: [ + [ 'openTag', 'root' ], + [ 'closeTag', 'root' ], + [ 'openTag', 'otherRoot' ], + [ 'closeTag', 'otherRoot' ] + ], + }); + + // multiple-root elements / xmlns + test({ + xml: + '' + + '', + ns: true, + expect: [ + [ 'openTag', 'atom:root' ], + [ 'closeTag', 'atom:root' ], + [ 'openTag', 'ns0:otherRoot' ], + [ 'closeTag', 'ns0:otherRoot' ] + ], + }); + + // attributes / "" + test({ + xml: '', + expect: [ + [ 'openTag', 'root', { LENGTH: 'abc=ABC' }, false ], + [ 'closeTag', 'root', false ], + ], + }); + + // attributes / no xmlns assignment + test({ + xml: '', + expect: [ + [ 'warn', 'illegal declaration of xmlns' ], + [ 'openTag', 'root', { a: 'B' } ], + [ 'closeTag', 'root' ], + ], + }); + + // attributes / '' + test({ + xml: '', + expect: [ + [ 'openTag', 'root', { length: 'abc=abc' }, false ], + [ 'closeTag', 'root', false ], + ], + }); + + // attributes / = inside attribute + test({ + xml: '', + expect: [ + [ 'openTag', 'root', { _abc: 'abc=abc', ':abc': 'abc' }, false ], + [ 'closeTag', 'root', false ], + ], + }); + + // attributes / space between attributes + test({ + xml: '', + expect: [ + [ 'openTag', 'root', { attr1: 'first', attr2: 'second' }, true ], + [ 'closeTag', 'root', true ], + ], + }); + + // attributes / warnings / no space between attributes + test({ + xml: '', + expect: [ + [ 'warn', 'illegal character after attribute end' ], + [ 'warn', 'illegal character after attribute end' ], + [ 'openTag', 'root', { a: 'B' }, true ], + [ 'closeTag', 'root', true ], + ], + }); + + // attributes / warnings / illegal first char + test({ + xml: '', + expect: [ + [ 'warn', 'illegal first char attribute name' ], + [ 'openTag', 'root', { a: 'B' }, true ], + [ 'closeTag', 'root', true ], + ], + }); + + test({ + xml: '', + expect: [ + [ 'warn', 'illegal first char attribute name' ], + [ 'openTag', 'root', { a: 'B' }, true ], + [ 'closeTag', 'root', true ], + ], + }); + + // attributes / warnings / open - close missmatch + test({ + xml: '', + expect: [ + [ 'warn', 'attribute value quote missmatch' ], + [ 'openTag', 'root', { a: 'B' }, true ], + [ 'closeTag', 'root', true ], + ], + }); + + // attributes / warnings / illegal first char attribute name + test({ + xml: '', + expect: [ + [ 'warn', 'illegal first char attribute name' ], + [ 'warn', 'illegal first char attribute name' ], + [ 'openTag', 'root', { attr2: 'second' }, true ], + [ 'closeTag', 'root', true ], + ], + }); + + // attributes / unicode attr value + test({ + xml: '', + expect: [ + [ 'openTag', 'root', { rain: '☂' }, true ], + [ 'closeTag', 'root', true ], + ], + }); + + // attributes / warnings / illegal first char attribute name + test({ + xml: '', + expect: [ + [ 'warn', 'illegal first char attribute name' ], + [ 'openTag', 'root', { attr2: 'second' }, true ], + [ 'closeTag', 'root', true ], + ], + }); + + // attributes / warnings / illegal attribute name char + test({ + xml: '', + expect: [ + [ 'warn', 'illegal attribute name char' ], + [ 'openTag', 'root', { attr2: 'second' }, true ], + [ 'closeTag', 'root', true ], + ], + }); + + test({ + xml: '', + expect: [ + [ 'openTag', 'root', { 'xmlns:color_1-.0': 'http://color' }, true ], + [ 'closeTag', 'root', true ], + ], + }); + + test({ + xml: '', + expect: [ + [ 'openTag', 'root', { 'xmlns:b:c': 'http://color' }, true ], + [ 'closeTag', 'root', true ], + ], + }); + + test({ + xml: '', + expect: [ + [ 'openTag', 'root', { 'color_1-.0': 'green' }, true ], + [ 'closeTag', 'root', true ], + ], + }); + + // attributes / warnings / attribute without value + test({ + xml: '', + expect: [ + [ 'warn', 'missing attribute value' ], + [ 'openTag', 'root', { a: 'B' }, true ], + [ 'closeTag', 'root', true ], + ], + }); + + test({ + xml: '', + expect: [ + [ 'warn', 'missing attribute value' ], + [ 'openTag', 'root', { a: 'B' }, true ], + [ 'closeTag', 'root', true ], + ], + }); + + // attributes / warnings / missing quoting + test({ + xml: '', + expect: [ + [ 'warn', 'missing attribute value quotes' ], + [ 'openTag', 'root', { a: 'B' }, true ], + [ 'closeTag', 'root', true ], + ], + }); + + // attributes / warnings / missing quoting + test({ + xml: '', + expect: [ + [ 'warn', 'missing attribute value quotes' ], + [ 'openTag', 'root', { a: 'B' }, true ], + [ 'closeTag', 'root', true ], + ], + }); + + test({ + xml: '', + expect: [ + [ 'openTag', 'root', { length: '12345' }, false ], + [ 'openTag', 'item', {}, true ], + [ 'closeTag', 'item', true ], + [ 'closeTag', 'root', false ] + ], + }); + + test({ + xml: '', + expect: [ + [ 'openTag', 'r' ], + [ 'cdata', ' this is ' ], + [ 'cdata', ' this is [] ' ], + [ 'closeTag', 'r' ], + ], + }); + + test({ + xml: '', + expect: [ + [ 'openTag', 'r' ], + [ 'cdata', '[[[[[[[[]]]]]]]]' ], + [ 'closeTag', 'r' ], + ], + }); + + test({ + xml: '', + expect: [ + [ 'openTag', 'r' ], + [ 'error', 'unclosed cdata' ], + ], + }); + + test({ + xml: '<![CDATA[ this is ]]>', + expect: [ + [ 'openTag', 'r' ], + [ 'text', '<![CDATA[ this is ]]>' ], + [ 'closeTag', 'r' ], + ], + }); + + test({ + xml: '', + expect: [ + [ 'openTag', 'html' ], + [ 'openTag', 'head' ], + [ 'openTag', 'script' ], + [ 'text', '\'' ], + [ 'openTag', 'div' ], + [ 'text', 'foo' ], + [ 'closeTag', 'div' ], + [ 'error', 'closing tag mismatch' ], + ] + }); + + // namespaces + test({ + xml: '', + ns: true, + expect: [ + [ 'openTag', 'xmlns' ], + [ 'closeTag', 'xmlns' ], + ] + }); + + // no root namespace (broken rss?) + test({ + xml: '' + + '' + + '', + ns: true, + expect: [ + [ 'openTag', 'rss', { 'xmlns:atom': 'http://www.w3.org/2005/Atom', version: '2.0' } ], + [ 'openTag', 'channel' ], + [ 'closeTag', 'channel' ], + [ 'closeTag', 'rss' ] + ] + }); + + test({ + xml: '', + ns: true, + expect: [ + [ 'openTag', 'atom:feed', { id: 'aa', 'media:title': 'bb' } ], + [ 'closeTag', 'atom:feed' ], + ], + }); + + test({ + xml: '', + ns: true, + expect: [ + [ 'openTag', 'atom:feed', { id: 'aa', 'media:title': 'bb' } ], + [ 'closeTag', 'atom:feed' ], + ], + }); + + test({ + xml: '', + ns: true, + expect: [ + [ 'openTag', 'atom:feed', { id: 'aa', 'media:title': 'bb' } ], + [ 'closeTag', 'atom:feed' ], + ], + }); + + test({ + xml: '', + ns: true, + expect: [ + [ 'openTag', 'atom:feed', { id: 'aa', 'title': 'bb' } ], + [ 'closeTag', 'atom:feed' ], + ], + }); + + test({ + xml: 'text', + ns: true, + expect: [ + [ 'openTag', 'atom:feed' ], + [ 'openTag', 'media:title' ], + [ 'text', 'text' ], + [ 'closeTag', 'media:title' ], + [ 'closeTag', 'atom:feed' ], + ], + }); + + test({ + xml: 'text', + ns: true, + expect: [ + [ 'openTag', 'atom:feed' ], + [ 'openTag', 'media:title' ], + [ 'text', 'text' ], + [ 'closeTag', 'media:title' ], + [ 'closeTag', 'atom:feed' ], + ], + }); + + test({ + xml: 'text', + ns: true, + expect: [ + [ 'openTag', 'atom:feed' ], + [ 'openTag', 'atom:title' ], + [ 'text', 'text' ], + [ 'closeTag', 'atom:title' ], + [ 'closeTag', 'atom:feed' ], + ], + }); + + test({ + xml: '<:text/>', + ns: true, + expect: [ + [ 'openTag', 'atom:feed', { id: 'aa', 'media:title': 'bb' } ], + [ 'openTag', 'media:text' ], + [ 'closeTag', 'media:text' ], + [ 'closeTag', 'atom:feed' ], + ], + }); + + // un-registered namespace handling + test({ + xml: '', + ns: true, + expect: [ + [ 'openTag', 'ns0:root', { id: 'aa', 'bar:title': 'bb' } ], + [ 'openTag', 'bar:child' ], + [ 'closeTag', 'bar:child' ], + [ 'closeTag', 'ns0:root' ], + ], + }); + + // context with whitespace + test({ + xml: ( + '\r' + + ' <:text/>\n' + + '' + ), + ns: true, + expect: [ + [ 'openTag', 'atom:feed', { id: 'aa', 'media:title': 'bb' }, false, { + line: 0, + column: 0, + data: '' + } ], + [ 'text', '\r ' ], + [ 'openTag', 'media:text', {}, true, { line: 2, column: 2, data: '<:text/>' } ], + [ 'closeTag', 'media:text', true, { line: 2, column: 2, data: '<:text/>' } ], + [ 'text', '\n' ], + [ 'closeTag', 'atom:feed', false, { + line: 3, + column: 0, + data: '' + } ], + ], + }); + + // context without whitespace + test({ + xml: ( + '' + + '<:text/>' + + '' + ), + ns: true, + expect: [ + [ 'openTag', 'atom:feed', { id: 'aa', 'media:title': 'bb' }, false, { + line: 0, + column: 0, + data: '' + } ], + [ 'openTag', 'media:text', {}, true, { line: 0, column: 101, data: '<:text/>' } ], + [ 'closeTag', 'media:text', true, { line: 0, column: 101, data: '<:text/>' } ], + [ 'closeTag', 'atom:feed', false, { + line: 0, + column: 109, + data: '' + } ], + ], + }); + + // context with anonymous namespace + test({ + xml: ( + '' + ), + ns: true, + expect: [ + [ 'openTag', 'ns0:foo', { id: 'aa', 'that:title': 'bb' }, true, { + line: 0, + column: 0, + data: '' + } ], + [ 'closeTag', 'ns0:foo', true, { + line: 0, + column: 0, + data: '' + } ], + ], + }); + + // nested namespace handling + test({ + xml: ( + '' + + '' + + '' + ), + ns: true, + expect: [ + [ 'openTag', 'ns0:foo', {}, false ], + [ 'openTag', 'ns1:t', { id: 'aa', 'bar:title': 'bb' }, true ], + [ 'closeTag', 'ns1:t', true ], + [ 'closeTag', 'ns0:foo', false ], + ], + }); + + // nested namespace handling + test({ + xml: ( + '' + + '' + + '' + + '' + + '' + + '' + ), + ns: true, + expect: [ + [ 'openTag', 'ns0:foo' ], + [ 'openTag', 'ns1:t' ], + [ 'openTag', 'ns1:n' ], + [ 'closeTag', 'ns1:n' ], + [ 'openTag', 'ns1:n' ], + [ 'closeTag', 'ns1:n' ], + [ 'closeTag', 'ns1:t' ], + [ 'closeTag', 'ns0:foo' ], + ], + }); + + // nested with default namespace + test({ + xml: ( + '' + + '' + + '' + + '' + + '' + ), + ns: true, + expect: [ + [ 'openTag', 'ns0:foo' ], + [ 'openTag', 'ns1:t' ], + [ 'openTag', 'ns1:n', { id: 'b', 'bar:title': 'BAR' } ], + [ 'closeTag', 'ns1:n' ], + [ 'closeTag', 'ns1:t' ], + [ 'closeTag', 'ns0:foo' ], + ], + }); + + // nested with non-default namespace handling + test({ + xml: ( + '' + + '' + + '' + + '' + + '' + ), + ns: true, + expect: [ + [ 'openTag', 'foo:root' ], + [ 'openTag', 'bar:outer' ], + [ 'openTag', 'nested' ], + [ 'closeTag', 'nested' ], + [ 'closeTag', 'bar:outer' ], + [ 'closeTag', 'foo:root' ], + ], + }); + + // nested namespace re-declaration + test({ + xml: ( + '' + + '' + + '' + + '' + + '' + ), + ns: true, + expect: [ + [ 'openTag', 'ns0:foo' ], + [ 'openTag', 'ns1:t' ], + [ 'openTag', 'bar:other', { 'bar:attr': 'BAR' } ], + [ 'closeTag', 'bar:other' ], + [ 'closeTag', 'ns1:t' ], + [ 'closeTag', 'ns0:foo' ], + ], + }); + + // test namespace attribute exposure + test({ + xml: ( + '' + ), + ns: true, + expect: [ + [ 'openTag', 'ns0:foo', { xmlns: 'http://xxx' } ], + [ 'closeTag', 'ns0:foo', false ], + ], + }); + + // test namespace attribute rewrite + test({ + xml: ( + '' + ), + ns: true, + expect: [ + [ 'openTag', 'ns0:foo', { + 'xmlns:a': 'http://www.w3.org/2005/Atom', + 'xmlns': 'http://xxx', + 'atom:xx': 'foo' + } ], + [ 'closeTag', 'ns0:foo', false ], + ], + }); + + // test missing namespace / element + test({ + xml: ( + '' + + '' + + '' + ), + ns: true, + expect: [ + [ 'openTag', 'ns0:foo' ], + [ 'error', 'missing namespace on ', { + data: '', + line: 0, + column: 24 + } ] + ], + }); + + // illegal namespace prefix + test({ + xml: ( + '' + ), + ns: true, + expect: [ + [ 'error', 'invalid nodeName' ], + ], + }); + + // namespace collision + test({ + xml: ( + '' + ), + ns: true, + expect: [ + [ 'openTag', 'ns0:foo' ], + [ 'closeTag', 'ns0:foo' ], + ], + }); + + // anonymous ns prefix collision + test({ + xml: ( + '' + ), + ns: true, + expect: [ + [ 'openTag', 'ns0:foo', { 'ns1:bar': 'BAR' } ], + [ 'closeTag', 'ns0:foo' ], + ], + }); + + // NOT normalize xsi:type + test({ + xml: ( + '' + ), + ns: true, + expect: [ + [ 'openTag', 'ns0:foo', { 'xsi:type': 'Foo' } ], + [ 'closeTag', 'ns0:foo' ], + ], + }); + + // NOT remap xsi:type + test({ + xml: ( + '' + ), + ns: true, + expect: [ + [ 'openTag', 'ns0:foo', { 'o:type': 'Foo' } ], + [ 'closeTag', 'ns0:foo' ], + ], + }); + + // NOT normalize xsi:type + test({ + xml: ( + '' + ), + ns: true, + expect: [ + [ 'openTag', 'ns0:foo', { 'xsi:type': 'Foo' } ], + [ 'closeTag', 'ns0:foo' ], + ], + }); + + // NOT normalize prefixed xsi:type + test({ + xml: ( + '' + ), + ns: true, + expect: [ + [ 'openTag', 'ns0:foo', { 'xsi:type': 'bar:Bar' } ], + [ 'closeTag', 'ns0:foo' ], + ], + }); + + // NOT normalize xsi:type / preserve unknown prefix in value + test({ + xml: ( + '' + ), + ns: true, + expect: [ + [ 'openTag', 'ns0:foo', { 'xsi:type': 'xs:string' } ], + [ 'closeTag', 'ns0:foo' ], + ], + }); + + // NOT normalize nested xsi:type + test({ + xml: ( + '' + + '' + + '' + ), + ns: true, + expect: [ + [ 'openTag', 'ns0:foo' ], + [ 'openTag', 'ns0:bar', { 'xsi:type': 'Bar' } ], + [ 'closeTag', 'ns0:bar' ], + [ 'closeTag', 'ns0:foo' ], + ], + }); + + // normalize nested prefixed xsi:type + test({ + xml: ( + '' + + '' + + '' + ), + ns: true, + expect: [ + [ 'openTag', 'ns0:foo' ], + [ 'openTag', 'ns0:bar', { 'xsi:type': 'bar:Bar' } ], + [ 'closeTag', 'ns0:bar' ], + [ 'closeTag', 'ns0:foo' ], + ], + }); + + // normalize nested xsi:type / preserve unknown prefix in value + test({ + xml: ( + '' + + '' + + '' + ), + ns: true, + expect: [ + [ 'openTag', 'ns0:foo' ], + [ 'openTag', 'ns0:bar', { 'xsi:type': 'xs:string' } ], + [ 'closeTag', 'ns0:bar' ], + [ 'closeTag', 'ns0:foo' ], + ], + }); + + // attributes / missing namespace for prefixed + test({ + xml: ( + '' + ), + ns: true, + expect: [ + [ 'warn', 'missing namespace for prefix ', { + column: 0, + line: 0, + data: '' + } ], + [ 'openTag', 'foo:foo' ], + [ 'closeTag', 'foo:foo' ] + ], + }); + + test({ + xml: ( + '' + + '' + + '' + ), + ns: true, + expect: [ + [ 'openTag', 'ns0:foo' ], + [ 'warn', 'missing namespace for prefix ' ], + [ 'openTag', 'ns0:bar' ], + [ 'closeTag', 'ns0:bar' ], + [ 'closeTag', 'ns0:foo' ] + ], + }); + + // attributes / missing namespace for prefixed / default ns + test({ + xml: ( + '' + ), + ns: true, + expect: [ + [ 'warn', 'missing namespace for prefix ' ], + [ 'openTag', 'ns0:foo' ], + [ 'closeTag', 'ns0:foo' ] + ], + }); + + // whitespace / BOM at start + test({ + xml: '\uFEFF
\uFEFF
', + expect: [ + [ 'openTag', 'div' ], + [ 'text', '\uFEFF' ], + [ 'closeTag', 'div' ], + ], + }); + + // whitespace / _ at start + test({ + xml: ' \uFEFF
', + expect: [ + [ 'openTag', 'div' ], + [ 'closeTag', 'div' ], + ], + }); + + // cyrillic in text + test({ + xml: '

тест

', + expect: [ + [ 'openTag', 'P' ], + [ 'text', 'тест' ], + [ 'closeTag', 'P' ], + ], + }); + + // kanji in attribute value + test({ + xml: '

', + expect: [ + [ 'openTag', 'P', { foo: '误' }, true ], + [ 'closeTag', 'P' ], + ], + }); + + // nested namespace re-declaration + test({ + xml: '' + + '' + + '' + + '' + + '' + + '' + + '' + + '', + ns: true, + expect: [ + [ 'openTag', 'e:root', { 'xmlns:e': 'http://extensions' } ], + [ 'openTag', 'bar:bar', { 'xmlns:bar': 'http://bar' } ], + [ 'openTag', 'other:child', { b: 'B', 'xmlns:other': 'http://other' } ], + [ 'closeTag', 'other:child' ], + [ 'closeTag', 'bar:bar' ], + [ 'openTag', 'ns0:foo', { xmlns: 'http://foo' } ], + [ 'openTag', 'ns0:child', { a: 'A' } ], + [ 'closeTag', 'ns0:child' ], + [ 'closeTag', 'ns0:foo' ], + [ 'closeTag', 'e:root' ] + ], + }); + + // local namespace re-declaration + test({ + xml: '', + ns: true, + expect: [ + [ 'warn', 'attribute already defined' ], + [ 'openTag', 'e:root', { 'xmlns:e': 'http://extensions' } ], + [ 'closeTag', 'e:root' ] + ], + }); + + // local namespace re-declaration / default namespace + test({ + xml: '', + ns: true, + expect: [ + [ 'warn', 'attribute already defined' ], + [ 'openTag', 'ns0:root', { xmlns: 'http://extensions' } ], + [ 'closeTag', 'ns0:root' ] + ], + }); + + // local attribute re-declaration / no ns + test({ + xml: '', + expect: [ + [ 'warn', 'attribute already defined' ], + [ 'openTag', 'root', { a: 'A' } ], + [ 'closeTag', 'root' ] + ], + }); + + // local attribute re-declaration / with namespace + test({ + xml: '', + ns: true, + expect: [ + [ 'warn', 'attribute already defined' ], + [ 'openTag', 'ns0:root', { xmlns: 'http://extensions', a: 'A' } ], + [ 'closeTag', 'ns0:root' ] + ], + }); + + // local attribute re-declaration / with other namespace + test({ + xml: '', + ns: true, + expect: [ + [ 'warn', 'attribute already defined' ], + [ 'openTag', 'ns0:root', { + xmlns: 'http://extensions', + 'xmlns:bar': 'http://bar', + 'bar:a': 'A' + } ], + [ 'closeTag', 'ns0:root' ] + ], + }); + + // Should not throw with '>' in attribute value of regular element (#17) + test({ + xml: '', + ns: true, + expect: [ + [ 'openTag', 'doc', {}, false ], + [ 'openTag', 'element', { id: 'sample>error' }, false ], + [ 'closeTag', 'element', false ], + [ 'closeTag', 'doc', false ], + ], + }); + + test({ + xml: ' \n \n ', + ns: true, + expect: [ + [ 'openTag', 'doc', {}, false ], + [ 'text', ' \n' ], + [ 'openTag', 'element', { id: 'sample>error' }, false ], + [ 'text', ' \n ' ], + [ 'closeTag', 'element', false ], + [ 'closeTag', 'doc', false ], + ], + }); + + // should handle > in attribute name + test({ + xml: 'o="FOO" bar="BAR" />', + ns: true, + expect: [ + [ 'openTag', 'doc', {}, false ], + [ 'warn', 'missing attribute value quotes' ], + [ 'openTag', 'element', {}, false ], + [ 'text', 'o="FOO" bar="BAR" />' ], + [ 'error', 'closing tag mismatch' ], + ], + }); + + // should handle > between attributes + test({ + xml: '> bar="BAR" />', + ns: true, + expect: [ + [ 'openTag', 'doc', {}, false ], + [ 'openTag', 'element', { foo: 'FOO' }, false ], + [ 'text', '> bar="BAR" />' ], + [ 'error', 'closing tag mismatch' ] + ], + }); + + + // should handle > after attribute + test({ + xml: ' bar="BAR" />', + ns: true, + expect: [ + [ 'openTag', 'doc', {}, false ], + [ 'openTag', 'element', { foo: 'FOO' }, false ], + [ 'text', ' bar="BAR" />' ], + [ 'error', 'closing tag mismatch' ] + ], + }); + + // should handle > after tag name + test({ + xml: '>', + ns: true, + expect: [ + [ 'openTag', 'doc', {}, false ], + [ 'openTag', 'element', {}, true ], + [ 'closeTag', 'element', true ], + [ 'text', '>' ], + [ 'closeTag', 'doc', false ] + ], + }); + + // should handle > in tag name + test({ + xml: '/>', + ns: true, + expect: [ + [ 'openTag', 'doc', {}, false ], + [ 'openTag', 'element', {}, false ], + [ 'text', '/>' ], + [ 'error', 'closing tag mismatch' ] + ], + }); + + // should not throw with '>' in attribute value of self-closing element (#17) + test({ + xml: '', + ns: true, + expect: [ + [ 'openTag', 'doc', {}, false ], + [ 'openTag', 'element', { id: 'sample>error' }, true ], + [ 'closeTag', 'element', true ], + [ 'closeTag', 'doc', false ], + ], + }); + + test({ + xml: ' \n ', + ns: true, + expect: [ + [ 'openTag', 'doc', {}, false ], + [ 'text', ' \n' ], + [ 'openTag', 'element', { id: 'sample>error' }, true ], + [ 'closeTag', 'element', true ], + [ 'text', ' ' ], + [ 'closeTag', 'doc', false ], + ], + }); + + // should not throw with '>' in attribute value of self-closing element at the end of the document + test({ + xml: '', + ns: true, + expect: [ + [ 'openTag', 'doc', {}, false ], + [ 'closeTag', 'doc', false ], + [ 'openTag', 'element', { id: 'sample>error' }, true ], + [ 'closeTag', 'element', true ], + ], + }); + + test({ + xml: '\n ', + ns: true, + expect: [ + [ 'openTag', 'doc', {}, false ], + [ 'closeTag', 'doc', false ], + [ 'openTag', 'element', { id: 'sample>error' }, true ], + [ 'closeTag', 'element', true ], + ], + }); + + // should not throw with '>' in attribute value of self-closing element at the end of the document + test({ + xml: '', + ns: true, + expect: [ + [ 'openTag', 'doc', {}, false ], + [ 'closeTag', 'doc', false ], + [ 'comment', ' !>>> ' ], + [ 'openTag', 'element', { id: 'sample>error' }, true ], + [ 'closeTag', 'element', true ], + ] + }); + + test({ + xml: ' ', + ns: true, + expect: [ + [ 'openTag', 'doc', {}, false ], + [ 'closeTag', 'doc', false ], + [ 'comment', ' !>>> ' ], + [ 'openTag', 'element', { id: 'sample>error' }, true ], + [ 'closeTag', 'element', true ], + ] + }); + + // should handle > in content + test({ + xml: ' bar="BAR" />', + ns: true, + expect: [ + [ 'openTag', 'doc', {}, false ], + [ 'openTag', 'element', { foo: 'FO\'O' }, false ], + [ 'text', ' bar="BAR" />' ], + [ 'closeTag', 'element', false ], + [ 'closeTag', 'doc', false ] + ], + }); + + test({ + xml: ' bar="BAR" />', + ns: true, + expect: [ + [ 'openTag', 'doc', {}, false ], + [ 'openTag', 'element', { foo: 'FO"O' }, false ], + [ 'text', ' bar="BAR" />' ], + [ 'closeTag', 'element', false ], + [ 'closeTag', 'doc', false ] + ], + }); + + test({ + xml: ' bar="BAR" />', + ns: true, + expect: [ + [ 'openTag', 'doc', {}, false ], + [ 'openTag', 'element', { foo: "FO'O" }, false ], + [ 'text', ' bar="BAR" />' ], + [ 'error', 'closing tag mismatch' ] + ], + }); + + test({ + xml: ' bar="BAR" />', + ns: true, + expect: [ + [ 'openTag', 'doc', {}, false ], + [ 'openTag', 'element', { foo: 'FO"O' }, false ], + [ 'text', ' bar="BAR" />' ], + [ 'error', 'closing tag mismatch' ] + ], + }); + + test({ + xml: ' bar="BAR" />', + ns: true, + expect: [ + [ 'openTag', 'doc', {}, false ], + [ 'openTag', 'element', { foo: "FO'O" }, false ], + [ 'text', ' bar="BAR" />' ], + [ 'error', 'closing tag mismatch' ] + ], + }); + + test({ + xml: ' bar="BAR" >', + ns: true, + expect: [ + [ 'openTag', 'doc', {}, false ], + [ 'comment', ' foo=\'FO"O\' ' ], + [ 'text', ' bar="BAR" >' ], + [ 'closeTag', 'doc', false ] + ], + }); + + test({ + xml: ' bar="BAR" >', + ns: true, + expect: [ + [ 'openTag', 'doc', {}, false ], + [ 'attention', '' ], + [ 'text', ' bar="BAR" >' ], + [ 'closeTag', 'doc', false ] + ], + }); + + test({ + xml: ' bar="BAR" >', + ns: true, + expect: [ + [ 'openTag', 'doc', {}, false ], + [ 'attention', '' ], + [ 'text', ' bar="BAR" >' ], + [ 'closeTag', 'doc', false ] + ], + }); + + test({ + xml: '', + ns: true, + expect: [ + [ 'openTag', 'doc', {}, false ], + [ 'attention', '' ], + [ 'error', 'unexpected end of file' ] + ], + }); + + test({ + xml: '', + ns: true, + expect: [ + [ 'openTag', 'doc', {}, false ], + [ 'attention', '' ], + [ 'error', 'unexpected end of file' ] + ], + }); + +}); \ No newline at end of file diff --git a/test/errors.js b/test/errors.js index 5340ae6..6c9588a 100644 --- a/test/errors.js +++ b/test/errors.js @@ -5,159 +5,157 @@ import { import assert from 'node:assert'; -describe('handler errors', function() { +describe('errors', function() { - var parser; + describe('error handler', () => { - beforeEach(function() { + it('should NOT pass to #onError', function() { - parser = new Parser(); - }); + // given + const parser = new Parser(); + parser.on('error', function(err, getContext) { + assert.ok(false, 'error called'); + }); - it('should NOT pass to #onError', function() { + parser.on('openTag', function() { + throw new Error('foo'); + }); - // given - parser.on('error', function(err, getContext) { - assert.ok(false, 'error called'); - }); + // when + function parse() { + parser.parse(''); + } - parser.on('openTag', function() { - throw new Error('foo'); + // then + assert.throws(parse, /foo/); }); - // when - function parse() { - parser.parse(''); - } - - // then - assert.throws(parse, /foo/); }); -}); + describe('#parse', function() { -describe('#parse', function() { + it('should throw on invalid arg', function() { - it('should throw on invalid arg', function() { + // given + var parser = new Parser(); - // given - var parser = new Parser(); + // when + function parse() { + parser.parse({}); + } - // when - function parse() { - parser.parse({}); - } + // then + assert.throws(parse, /required args /); + }); - // then - assert.throws(parse, /required args /); - }); + it('should throw on XML parse error', function() { - it('should throw on XML parse error', function() { + // given + var parser = new Parser(); - // given - var parser = new Parser(); + // when + function parse() { + parser.parse('', + '', + '', + '', - 'hi', - '', - '' - ].join('\n')); - } + // then + assert.doesNotThrow(parse); + }); - // then - assert.doesNotThrow(parse); }); -}); + describe('#on', function() { -describe('#on', function() { + it('should throw on invalid args', function() { - it('should throw on invalid args', function() { + // given + var parser = new Parser(); - // given - var parser = new Parser(); + // when + function configure() { + parser.on('openTag'); + } - // when - function configure() { - parser.on('openTag'); - } + // then + assert.throws(configure, /required args /); + }); - // then - assert.throws(configure, /required args /); - }); + it('should throw on invalid event', function() { - it('should throw on invalid event', function() { + // given + var parser = new Parser(); - // given - var parser = new Parser(); + // when + function configure() { + parser.on('foo', function() { }); + } - // when - function configure() { - parser.on('foo', function() { }); - } + // then + assert.throws(configure, /unsupported event: foo/); + }); - // then - assert.throws(configure, /unsupported event: foo/); }); -}); + describe('#ns', function() { -describe('#ns', function() { + it('should throw on invalid args', function() { - it('should throw on invalid args', function() { + // given + var parser = new Parser(); - // given - var parser = new Parser(); + // when + function configure() { + parser.ns('bar'); + } - // when - function configure() { - parser.ns('bar'); - } + // then + assert.throws(configure, /required args /); + }); - // then - assert.throws(configure, /required args /); - }); + it('should NOT throw on no args', function() { - it('should NOT throw on no args', function() { + // given + var parser = new Parser(); - // given - var parser = new Parser(); + // when + function configure() { + parser.ns(); + } - // when - function configure() { - parser.ns(); - } + // then + assert.doesNotThrow(configure); + }); - // then - assert.doesNotThrow(configure); }); }); \ No newline at end of file diff --git a/test/index.js b/test/index.js deleted file mode 100644 index 70e87b6..0000000 --- a/test/index.js +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Test suite main. - */ - -describe('document parsing', async function() { - - describe('should parse', async function() { - - return import('./elements.js'); - - }); - - await import('./modes.js'); - - await import('./decode.js'); - -}); - - -describe('errors', async function() { - - await import('./errors.js'); - -}); \ No newline at end of file