• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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