1'use strict'; 2 3const assert = require('assert'); 4const parse5 = require('../lib'); 5const generateLocationInfoParserTests = require('../../../test/utils/generate-location-info-parser-tests'); 6const { 7 assertStartTagLocation, 8 assertNodeLocation 9} = require('../../../test/utils/generate-location-info-parser-tests'); 10const { generateTestsForEachTreeAdapter, treeAdapters } = require('../../../test/utils/common'); 11 12generateLocationInfoParserTests(module.exports, 'Parser', (input, opts) => ({ 13 node: parse5.parse(input, opts) 14})); 15 16generateTestsForEachTreeAdapter(module.exports, (_test, treeAdapter) => { 17 _test['Regression - Incorrect LocationInfo.endOffset for implicitly closed <p> element (GH-109)'] = function() { 18 const html = '<p>1<p class="2">3'; 19 20 const opts = { 21 treeAdapter: treeAdapter, 22 sourceCodeLocationInfo: true 23 }; 24 25 const fragment = parse5.parseFragment(html, opts); 26 const firstP = treeAdapter.getChildNodes(fragment)[0]; 27 const firstPLocation = treeAdapter.getNodeSourceCodeLocation(firstP); 28 29 assert.strictEqual(html.substring(firstPLocation.startOffset, firstPLocation.endOffset), '<p>1'); 30 }; 31 32 _test['Regression - Incorrect LocationInfo.endOffset for element with closing tag (GH-159)'] = function() { 33 const html = '<i>1</i>2'; 34 35 const opts = { 36 treeAdapter: treeAdapter, 37 sourceCodeLocationInfo: true 38 }; 39 40 const fragment = parse5.parseFragment(html, opts); 41 const firstChild = treeAdapter.getChildNodes(fragment)[0]; 42 const location = treeAdapter.getNodeSourceCodeLocation(firstChild); 43 44 assert.strictEqual(html.substring(location.startOffset, location.endOffset), '<i>1</i>'); 45 }; 46 47 _test['Regression - Location info not exposed with parseFragment (GH-82)'] = function() { 48 const html = '<html><head></head><body>foo</body></html>'; 49 50 const opts = { 51 treeAdapter: treeAdapter, 52 sourceCodeLocationInfo: true 53 }; 54 55 const fragment = parse5.parseFragment(html, opts); 56 const firstChild = treeAdapter.getChildNodes(fragment)[0]; 57 58 assert.ok(treeAdapter.getNodeSourceCodeLocation(firstChild)); 59 }; 60 61 _test['Regression - location info mixin error when parsing <template> elements (GH-90)'] = function() { 62 const html = '<template>hello</template>'; 63 64 const opts = { 65 treeAdapter: treeAdapter, 66 sourceCodeLocationInfo: true 67 }; 68 69 assert.doesNotThrow(() => { 70 parse5.parseFragment(html, opts); 71 }); 72 }; 73 74 _test['Regression - location info not attached for empty attributes (GH-96)'] = function() { 75 const html = '<div test-attr></div>'; 76 77 const opts = { 78 treeAdapter: treeAdapter, 79 sourceCodeLocationInfo: true 80 }; 81 82 const fragment = parse5.parseFragment(html, opts); 83 const firstChild = treeAdapter.getChildNodes(fragment)[0]; 84 85 assert.ok(treeAdapter.getNodeSourceCodeLocation(firstChild).attrs['test-attr']); 86 }; 87 88 _test['Regression - location line incorrect when a character is unconsumed (GH-151)'] = function() { 89 const html = [ 90 '<html><body><script>', 91 ' var x = window.scrollY <', 92 ' 100;', 93 '</script>', 94 '</body></html>' 95 ].join('\n'); 96 97 const opts = { 98 treeAdapter: treeAdapter, 99 sourceCodeLocationInfo: true 100 }; 101 102 const document = parse5.parse(html, opts); 103 const htmlEl = treeAdapter.getChildNodes(document)[0]; 104 const bodyEl = treeAdapter.getChildNodes(htmlEl)[1]; 105 const scriptEl = treeAdapter.getChildNodes(bodyEl)[0]; 106 const scriptLocation = treeAdapter.getNodeSourceCodeLocation(scriptEl); 107 108 assert.strictEqual(treeAdapter.getTagName(scriptEl), 'script'); 109 assert.equal(scriptLocation.endTag.startLine, 4); 110 }; 111 112 _test['Regression - location.startTag should be available if end tag is missing (GH-181)'] = function() { 113 const html = '<p>test'; 114 115 const opts = { 116 treeAdapter: treeAdapter, 117 sourceCodeLocationInfo: true 118 }; 119 120 const fragment = parse5.parseFragment(html, opts); 121 const p = treeAdapter.getChildNodes(fragment)[0]; 122 const location = treeAdapter.getNodeSourceCodeLocation(p); 123 124 assertNodeLocation(location, html, html, [html]); 125 assertStartTagLocation(location, html, html, [html]); 126 127 assert.ok(!location.endTag); 128 }; 129}); 130 131exports['Updating node source code location (GH-314)'] = function() { 132 const sourceCodeLocationSetter = { 133 setNodeSourceCodeLocation(node, location) { 134 if (location === null) { 135 node.sourceCodeLocation = null; 136 } else { 137 node.sourceCodeLocation = { 138 start: { 139 line: location.startLine, 140 column: location.startCol, 141 offset: location.startOffset 142 }, 143 end: { 144 line: location.endLine, 145 column: location.endCol, 146 offset: location.endOffset 147 } 148 }; 149 } 150 }, 151 updateNodeSourceCodeLocation(node, endLocation) { 152 node.sourceCodeLocation = { 153 start: node.sourceCodeLocation.start, 154 end: { 155 line: endLocation.endLine, 156 column: endLocation.endCol, 157 offset: endLocation.endOffset 158 } 159 }; 160 } 161 }; 162 const adapter = Object.assign(treeAdapters.default, sourceCodeLocationSetter); 163 const document = parse5.parse('<!doctype><body>Testing location</body>', { adapter, sourceCodeLocationInfo: true }); 164 const [doctype, html] = document.childNodes; 165 const [head, body] = html.childNodes; 166 const [text] = body.childNodes; 167 168 assert.deepEqual(doctype.sourceCodeLocation, { 169 start: { line: 1, column: 1, offset: 0 }, 170 end: { line: 1, column: 11, offset: 10 } 171 }); 172 assert.strictEqual(html.sourceCodeLocation, null); 173 assert.strictEqual(head.sourceCodeLocation, null); 174 assert.deepEqual(body.sourceCodeLocation, { 175 start: { line: 1, column: 11, offset: 10 }, 176 end: { line: 1, column: 40, offset: 39 } 177 }); 178 assert.deepEqual(text.sourceCodeLocation, { 179 start: { line: 1, column: 17, offset: 16 }, 180 end: { line: 1, column: 33, offset: 32 } 181 }); 182}; 183