1'use strict'; 2 3const common = require('../common'); 4// The doctool currently uses js-yaml from the tool/node_modules/eslint/ tree. 5try { 6 require('../../tools/node_modules/eslint/node_modules/js-yaml'); 7} catch { 8 common.skip('missing js-yaml (eslint not present)'); 9} 10 11const assert = require('assert'); 12const { readFileSync } = require('fs'); 13const fixtures = require('../common/fixtures'); 14const { replaceLinks } = require('../../tools/doc/markdown.js'); 15const html = require('../../tools/doc/html.js'); 16const path = require('path'); 17 18module.paths.unshift( 19 path.join(__dirname, '..', '..', 'tools', 'doc', 'node_modules')); 20const unified = require('unified'); 21const markdown = require('remark-parse'); 22const remark2rehype = require('remark-rehype'); 23const raw = require('rehype-raw'); 24const htmlStringify = require('rehype-stringify'); 25 26// Test links mapper is an object of the following structure: 27// { 28// [filename]: { 29// [link definition identifier]: [url to the linked resource] 30// } 31// } 32const testLinksMapper = { 33 'foo': { 34 'command line options': 'cli.html#cli-options', 35 'web server': 'example.html' 36 } 37}; 38 39function toHTML({ input, filename, nodeVersion, versions }) { 40 const content = unified() 41 .use(replaceLinks, { filename, linksMapper: testLinksMapper }) 42 .use(markdown) 43 .use(html.firstHeader) 44 .use(html.preprocessText, { nodeVersion }) 45 .use(html.preprocessElements, { filename }) 46 .use(html.buildToc, { filename, apilinks: {} }) 47 .use(remark2rehype, { allowDangerousHTML: true }) 48 .use(raw) 49 .use(htmlStringify) 50 .processSync(input); 51 52 return html.toHTML({ input, content, filename, nodeVersion, versions }); 53} 54 55// Test data is a list of objects with two properties. 56// The file property is the file path. 57// The html property is some HTML which will be generated by the doctool. 58// This HTML will be stripped of all whitespace because we don't currently 59// have an HTML parser. 60const testData = [ 61 { 62 file: fixtures.path('order_of_end_tags_5873.md'), 63 html: '<h3>Static method: Buffer.from(array) <span> ' + 64 '<a class="mark" href="#foo_static_method_buffer_from_array" ' + 65 'id="foo_static_method_buffer_from_array">#</a> </span> </h3>' + 66 '<ul><li><code>array</code><a ' + 67 'href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/' + 68 'Reference/Global_Objects/Array" class="type"><Array></a></li></ul>' 69 }, 70 { 71 file: fixtures.path('doc_with_yaml.md'), 72 html: '<h1>Sample Markdown with YAML info' + 73 '<span><a class="mark" href="#foo_sample_markdown_with_yaml_info" ' + 74 ' id="foo_sample_markdown_with_yaml_info">#</a></span></h1>' + 75 '<h2>Foobar<span><a class="mark" href="#foo_foobar" ' + 76 'id="foo_foobar">#</a></span></h2>' + 77 '<div class="api_metadata"><span>Added in: v1.0.0</span></div> ' + 78 '<p>Describe <code>Foobar</code> in more detail here.</p>' + 79 '<h2>Foobar II<span><a class="mark" href="#foo_foobar_ii" ' + 80 'id="foo_foobar_ii">#</a></span></h2><div class="api_metadata">' + 81 '<details class="changelog"><summary>History</summary>' + 82 '<table><tbody><tr><th>Version</th><th>Changes</th></tr>' + 83 '<tr><td>v5.3.0, v4.2.0</td>' + 84 '<td><p><span>Added in: v5.3.0, v4.2.0</span></p></td></tr>' + 85 '<tr><td>v4.2.0</td><td><p>The <code>error</code> parameter can now be' + 86 'an arrow function.</p></td></tr></tbody></table></details></div> ' + 87 '<p>Describe <code>Foobar II</code> in more detail here.' + 88 '<a href="http://man7.org/linux/man-pages/man1/fg.1.html"><code>fg(1)' + 89 '</code></a></p><h2>Deprecated thingy<span><a class="mark" ' + 90 'href="#foo_deprecated_thingy" id="foo_deprecated_thingy">#</a>' + 91 '</span></h2><div class="api_metadata"><span>Added in: v1.0.0</span>' + 92 '<span>Deprecated since: v2.0.0</span></div><p>Describe ' + 93 '<code>Deprecated thingy</code> in more detail here.' + 94 '<a href="http://man7.org/linux/man-pages/man1/fg.1p.html"><code>fg(1p)' + 95 '</code></a></p><h2>Something<span><a class="mark" href="#foo_something' + 96 '" id="foo_something">#</a></span></h2> ' + 97 '<!-- This is not a metadata comment --> ' + 98 '<p>Describe <code>Something</code> in more detail here. </p>' 99 }, 100 { 101 file: fixtures.path('sample_document.md'), 102 html: '<ol><li>fish</li><li>fish</li></ol>' + 103 '<ul><li>Red fish</li><li>Blue fish</li></ul>', 104 }, 105 { 106 file: fixtures.path('altdocs.md'), 107 html: '<li><a href="https://nodejs.org/docs/latest-v8.x/api/foo.html">8.x', 108 }, 109 { 110 file: fixtures.path('document_with_links.md'), 111 html: '<h1>Usage and Example<span><a class="mark"' + 112 'href="#foo_usage_and_example" id="foo_usage_and_example">#</a>' + 113 '</span></h1><h2>Usage<span><a class="mark" href="#foo_usage"' + 114 'id="foo_usage">#</a></span></h2><p><code>node \\[options\\] index.js' + 115 '</code></p><p>Please see the<a href="cli.html#cli-options">' + 116 'Command Line Options</a>document for more information.</p><h2>' + 117 'Example<span><a class="mark" href="#foo_example" id="foo_example">' + 118 '#</a></span></h2><p>An example of a<a href="example.html">' + 119 'webserver</a>written with Node.js which responds with<code>' + 120 '\'Hello, World!\'</code>:</p><h2>See also<span><a class="mark"' + 121 'href="#foo_see_also" id="foo_see_also">#</a></span></h2><p>Check' + 122 'out also<a href="https://nodejs.org/">this guide</a></p>' 123 }, 124 { 125 file: fixtures.path('document_with_special_heading.md'), 126 html: '<title>Sample markdown with special heading |', 127 } 128]; 129 130const spaces = /\s/g; 131const versions = [ 132 { num: '10.x', lts: true }, 133 { num: '9.x' }, 134 { num: '8.x' }, 135 { num: '7.x' }, 136 { num: '6.x' }, 137 { num: '5.x' }, 138 { num: '4.x' }, 139 { num: '0.12.x' }, 140 { num: '0.10.x' }]; 141 142testData.forEach(({ file, html }) => { 143 // Normalize expected data by stripping whitespace. 144 const expected = html.replace(spaces, ''); 145 146 const input = readFileSync(file, 'utf8'); 147 148 const output = toHTML({ input, 149 filename: 'foo', 150 nodeVersion: process.version, 151 versions }); 152 153 const actual = output.replace(spaces, ''); 154 // Assert that the input stripped of all whitespace contains the 155 // expected markup. 156 assert(actual.includes(expected), 157 `ACTUAL: ${actual}\nEXPECTED: ${expected}`); 158}); 159