1var fs = require('fs'),
2    path = require('path'),
3    parse5 = require('../index'),
4    HTML = require('../lib/common/html');
5
6function addSlashes(str) {
7    return str
8        .replace(/\t/g, '\\t')
9        .replace(/\n/g, '\\n')
10        .replace(/\f/g, '\\f')
11        .replace(/\r/g, '\\r');
12}
13
14function createDiffMarker(markerPosition) {
15    var marker = '';
16
17    for (var i = 0; i < markerPosition - 1; i++)
18        marker += ' ';
19
20    return marker + '^\n';
21}
22
23//NOTE: creates test suites for each available tree adapter.
24exports.generateTestsForEachTreeAdapter = function (moduleExports, ctor) {
25    Object.keys(parse5.TreeAdapters).forEach(function (adapterName) {
26        var tests = {},
27            adapter = parse5.TreeAdapters[adapterName];
28
29        ctor(tests, adapter);
30
31        Object.keys(tests).forEach(function (testName) {
32            moduleExports['Tree adapter: ' + adapterName + ' - ' + testName] = tests[testName];
33        });
34    });
35};
36
37exports.getStringDiffMsg = function (actual, expected) {
38    for (var i = 0; i < expected.length; i++) {
39        if (actual[i] !== expected[i]) {
40            var diffMsg = '\nString differ at index ' + i + '\n';
41
42            var expectedStr = 'Expected: ' + addSlashes(expected.substring(i - 100, i + 1)),
43                expectedDiffMarker = createDiffMarker(expectedStr.length);
44
45            diffMsg += expectedStr + addSlashes(expected.substring(i + 1, i + 20)) + '\n' + expectedDiffMarker;
46
47            var actualStr = 'Actual:   ' + addSlashes(actual.substring(i - 100, i + 1)),
48                actualDiffMarker = createDiffMarker(actualStr.length);
49
50            diffMsg += actualStr + addSlashes(actual.substring(i + 1, i + 20)) + '\n' + actualDiffMarker;
51
52            return diffMsg;
53        }
54    }
55
56    return '';
57};
58
59exports.removeNewLines = function (str) {
60    return str
61        .replace(/\r/g, '')
62        .replace(/\n/g, '');
63};
64
65function normalizeNewLine(str) {
66    return str.replace(/\r\n/g, '\n');
67}
68
69exports.loadSerializationTestData = function (dataDirPath) {
70    var testSetFileDirs = fs.readdirSync(dataDirPath),
71        tests = [];
72
73    testSetFileDirs.forEach(function (dirName) {
74        var srcFilePath = path.join(dataDirPath, dirName, 'src.html'),
75            expectedFilePath = path.join(dataDirPath, dirName, 'expected.html'),
76            src = fs.readFileSync(srcFilePath).toString(),
77            expected = fs.readFileSync(expectedFilePath).toString();
78
79        tests.push({
80            name: dirName,
81            src: normalizeNewLine(src),
82            expected: normalizeNewLine(expected)
83        });
84    });
85
86    return tests;
87};
88
89exports.loadTreeConstructionTestData = function (dataDirs, treeAdapter) {
90    var testIdx = 0,
91        tests = [];
92
93    dataDirs.forEach(function (dataDirPath) {
94        var testSetFileNames = fs.readdirSync(dataDirPath),
95            dirName = path.basename(dataDirPath);
96
97        testSetFileNames.forEach(function (fileName) {
98            var filePath = path.join(dataDirPath, fileName),
99                testSet = fs.readFileSync(filePath).toString(),
100                setName = fileName.replace('.dat', ''),
101                testDescrs = [],
102                curDirective = '',
103                curDescr = null;
104
105            testSet.split(/\r?\n/).forEach(function (line) {
106                if (line === '#data') {
107                    curDescr = {};
108                    testDescrs.push(curDescr);
109                }
110
111                if (line[0] === '#') {
112                    curDirective = line;
113                    curDescr[curDirective] = [];
114                }
115
116                else
117                    curDescr[curDirective].push(line);
118            });
119
120            testDescrs.forEach(function (descr) {
121                var fragmentContextTagName = descr['#document-fragment'] && descr['#document-fragment'].join('');
122
123                tests.push({
124                    idx: ++testIdx,
125                    setName: setName,
126                    dirName: dirName,
127                    input: descr['#data'].join('\r\n'),
128                    expected: descr['#document'].join('\n'),
129                    expectedErrors: descr['#errors'],
130                    disableEntitiesDecoding: !!descr['#disable-html-entities-decoding'],
131                    fragmentContext: fragmentContextTagName &&
132                                     treeAdapter.createElement(fragmentContextTagName, HTML.NAMESPACES.HTML, [])
133                });
134            });
135        });
136    });
137
138    return tests;
139};
140
141exports.serializeToTestDataFormat = function (rootNode, treeAdapter) {
142    function getSerializedTreeIndent(indent) {
143        var str = '|';
144
145        for (var i = 0; i < indent + 1; i++)
146            str += ' ';
147
148        return str;
149    }
150
151    function getElementSerializedNamespaceURI(element) {
152        switch (treeAdapter.getNamespaceURI(element)) {
153            case HTML.NAMESPACES.SVG:
154                return 'svg ';
155            case HTML.NAMESPACES.MATHML:
156                return 'math ';
157            default :
158                return '';
159        }
160    }
161
162    function serializeNodeList(nodes, indent) {
163        var str = '';
164
165        nodes.forEach(function (node) {
166            str += getSerializedTreeIndent(indent);
167
168            if (treeAdapter.isCommentNode(node))
169                str += '<!-- ' + treeAdapter.getCommentNodeContent(node) + ' -->\n';
170
171            else if (treeAdapter.isTextNode(node))
172                str += '"' + treeAdapter.getTextNodeContent(node) + '"\n';
173
174            else if (treeAdapter.isDocumentTypeNode(node)) {
175                var parts = [],
176                    publicId = treeAdapter.getDocumentTypeNodePublicId(node),
177                    systemId = treeAdapter.getDocumentTypeNodeSystemId(node);
178
179                str += '<!DOCTYPE';
180
181                parts.push(treeAdapter.getDocumentTypeNodeName(node) || '');
182
183                if (publicId !== null || systemId !== null) {
184                    parts.push('"' + (publicId || '') + '"');
185                    parts.push('"' + (systemId || '') + '"');
186                }
187
188                parts.forEach(function (part) {
189                    str += ' ' + part;
190                });
191
192                str += '>\n';
193            }
194
195            else {
196                var tn = treeAdapter.getTagName(node);
197
198                str += '<' + getElementSerializedNamespaceURI(node) + tn + '>\n';
199
200                var childrenIndent = indent + 2,
201                    serializedAttrs = [];
202
203                treeAdapter.getAttrList(node).forEach(function (attr) {
204                    var attrStr = getSerializedTreeIndent(childrenIndent);
205
206                    if (attr.prefix)
207                        attrStr += attr.prefix + ' ';
208
209                    attrStr += attr.name + '="' + attr.value + '"\n';
210
211                    serializedAttrs.push(attrStr);
212                });
213
214                str += serializedAttrs.sort().join('');
215
216                if (tn === HTML.TAG_NAMES.TEMPLATE && treeAdapter.getNamespaceURI(node) === HTML.NAMESPACES.HTML) {
217                    str += getSerializedTreeIndent(childrenIndent) + 'content\n';
218                    childrenIndent += 2;
219                    node = treeAdapter.getChildNodes(node)[0];
220                }
221
222                str += serializeNodeList(treeAdapter.getChildNodes(node), childrenIndent);
223            }
224        });
225
226        return str;
227    }
228
229    return serializeNodeList(treeAdapter.getChildNodes(rootNode), 0);
230};
231
232exports.prettyPrintParserAssertionArgs = function (actual, expected) {
233    var msg = '\nExpected:\n';
234    msg += '-----------------\n';
235    msg += expected + '\n';
236    msg += '\nActual:\n';
237    msg += '-----------------\n';
238    msg += actual + '\n';
239
240    return msg;
241};
242