1'use strict'; 2 3const assert = require('assert'); 4const fs = require('fs'); 5const path = require('path'); 6const { escapeString } = require('../../packages/parse5/lib/serializer'); 7const parse5 = require('../../packages/parse5/lib'); 8const { 9 removeNewLines, 10 getSubstringByLineCol, 11 getStringDiffMsg, 12 normalizeNewLine, 13 generateTestsForEachTreeAdapter 14} = require('./common'); 15 16function walkTree(document, treeAdapter, handler) { 17 for (let stack = treeAdapter.getChildNodes(document).slice(); stack.length; ) { 18 const node = stack.shift(); 19 const children = treeAdapter.getChildNodes(node); 20 21 handler(node); 22 23 if (children && children.length) { 24 stack = children.concat(stack); 25 } 26 } 27} 28 29function assertLocation(loc, expected, html, lines) { 30 //Offsets 31 let actual = html.substring(loc.startOffset, loc.endOffset); 32 33 expected = removeNewLines(expected); 34 actual = removeNewLines(actual); 35 36 assert.strictEqual(expected, actual, getStringDiffMsg(actual, expected)); 37 38 //Line/col 39 actual = getSubstringByLineCol(lines, loc); 40 actual = removeNewLines(actual); 41 42 assert.strictEqual(actual, expected, getStringDiffMsg(actual, expected)); 43} 44 45//NOTE: Based on the idea that the serialized fragment starts with the startTag 46function assertStartTagLocation(location, serializedNode, html, lines) { 47 const length = location.startTag.endOffset - location.startTag.startOffset; 48 const expected = serializedNode.substring(0, length); 49 50 assertLocation(location.startTag, expected, html, lines); 51} 52 53//NOTE: Based on the idea that the serialized fragment ends with the endTag 54function assertEndTagLocation(location, serializedNode, html, lines) { 55 const length = location.endTag.endOffset - location.endTag.startOffset; 56 const expected = serializedNode.slice(-length); 57 58 assertLocation(location.endTag, expected, html, lines); 59} 60 61function assertAttrsLocation(location, serializedNode, html, lines) { 62 location.attrs.forEach(attr => { 63 const expected = serializedNode.slice(attr.startOffset, attr.endOffset); 64 65 assertLocation(attr, expected, html, lines); 66 }); 67} 68 69function assertNodeLocation(location, serializedNode, html, lines) { 70 const expected = removeNewLines(serializedNode); 71 72 assertLocation(location, expected, html, lines); 73} 74 75function loadParserLocationInfoTestData() { 76 const dataDirPath = path.join(__dirname, '../data/location-info'); 77 const testSetFileDirs = fs.readdirSync(dataDirPath); 78 const tests = []; 79 80 testSetFileDirs.forEach(dirName => { 81 const dataFilePath = path.join(dataDirPath, dirName, 'data.html'); 82 const data = fs.readFileSync(dataFilePath).toString(); 83 84 tests.push({ 85 name: dirName, 86 data: normalizeNewLine(data) 87 }); 88 }); 89 90 return tests; 91} 92 93module.exports = function generateLocationInfoParserTests(moduleExports, prefix, parse) { 94 generateTestsForEachTreeAdapter(moduleExports, (_test, treeAdapter) => { 95 loadParserLocationInfoTestData().forEach(test => { 96 const testName = `Location info (Parser) - ${test.name}`; 97 98 //NOTE: How it works: we parse document with the location info. 99 //Then for each node in the tree we run serializer and compare results with the substring 100 //obtained via location info from the expected serialization results. 101 _test[testName] = async function() { 102 const serializerOpts = { treeAdapter: treeAdapter }; 103 const html = escapeString(test.data); 104 const lines = html.split(/\r?\n/g); 105 106 const parserOpts = { 107 treeAdapter: treeAdapter, 108 sourceCodeLocationInfo: true 109 }; 110 111 const parsingResult = await parse(html, parserOpts); 112 const document = parsingResult.node; 113 114 walkTree(document, treeAdapter, node => { 115 const location = treeAdapter.getNodeSourceCodeLocation(node); 116 117 if (location) { 118 const fragment = treeAdapter.createDocumentFragment(); 119 120 treeAdapter.appendChild(fragment, node); 121 122 const serializedNode = parse5.serialize(fragment, serializerOpts); 123 124 assertNodeLocation(location, serializedNode, html, lines); 125 126 if (location.startTag) { 127 assertStartTagLocation(location, serializedNode, html, lines); 128 } 129 130 if (location.endTag) { 131 assertEndTagLocation(location, serializedNode, html, lines); 132 } 133 134 if (location.attrs) { 135 assertAttrsLocation(location, serializedNode, html, lines); 136 } 137 } 138 }); 139 }; 140 }); 141 }); 142}; 143 144module.exports.assertStartTagLocation = assertStartTagLocation; 145module.exports.assertNodeLocation = assertNodeLocation; 146