Alois Klink 1cc2f98431
fix: fix GFM markdown output (#1553)
GFM output is currently broken (since v13.2.2),
which prevents tables from being written to markdown.

This is because as of [remark v13.0.0][1], GFM support was added
to the remark-gfm pacakge.

6b5bc2548d
fixed parsing GFM, however, it didn't add GFM support for markdown
output.

[1]: https://github.com/remarkjs/remark/releases/tag/13.0.0

Fixes: f4a46b134f58639f352b3024801c3d98f901d66a
2024-01-30 13:39:01 -05:00

420 lines
11 KiB
JavaScript

/* global jasmine */
import path from 'path';
import os from 'os';
import { exec } from 'child_process';
import tmp from 'tmp';
import fs from 'fs-extra';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
function documentation(args, options, parseJSON) {
if (!options) {
options = {};
}
if (!options.cwd) {
options.cwd = __dirname;
}
options.maxBuffer = 1024 * 1024;
args.unshift('node ' + path.join(__dirname, '..', 'bin', 'documentation.js'));
return new Promise((resolve, reject) => {
exec(args.join(' '), options, function (err, stdout, stderr) {
if (err) {
err.stderr = stderr;
return reject(err);
}
if (parseJSON === false) {
resolve(stdout);
} else {
try {
resolve(JSON.parse(stdout));
} catch (e) {
reject(e);
}
}
});
});
}
function normalize(result) {
result.forEach(function (item) {
item.context.file = '[path]';
});
return result;
}
test('documentation binary', async function () {
const data = await documentation(['build fixture/simple.input.js'], {});
expect(data.length).toBe(1);
});
test.skip('defaults to parsing package.json main', async function () {
const data = await documentation(['build'], {
cwd: path.join(__dirname, '..')
});
expect(data.length).toBeTruthy();
});
test('accepts config file', async function () {
const data = await documentation([
'build fixture/sorting/input.js -c fixture/config.json'
]);
expect(normalize(data)).toMatchSnapshot();
});
test('accepts config file - reports failures', async function () {
try {
await documentation(
['build fixture/sorting/input.js -c fixture/config-bad.yml'],
{},
false
);
} catch (stderr) {
expect(stderr).toMatchSnapshot();
}
});
test('accepts config file - reports parse failures', async function () {
try {
await documentation(
['build fixture/sorting/input.js -c fixture/config-malformed.json'],
{},
false
);
} catch (stderr) {
expect(stderr.stderr.match(/SyntaxError/g)).toBeTruthy();
}
});
test('--shallow option', async function () {
const data = await documentation([
'build --shallow fixture/internal.input.js'
]);
expect(data.length).toBe(0);
});
test('when a file is specified both in a glob and explicitly, it is only documented once', async function () {
const data = await documentation([
'build fixture/simple.input.js fixture/simple.input.*'
]);
expect(data.length).toBe(1);
});
test('extension option', async function () {
const data = await documentation([
'build fixture/extension/index.otherextension ' +
'--requireExtension=otherextension --parseExtension=otherextension'
]);
expect(data.length).toBe(1);
});
test('extension option', function () {
return documentation(['build fixture/extension.jsx']);
});
describe('invalid arguments', function () {
test('bad -f option', async function () {
try {
await documentation(
['build -f DOES-NOT-EXIST fixture/internal.input.js'],
{},
false
);
} catch (err) {
expect(err).toBeTruthy();
}
});
test('html with no destination', async function () {
try {
await documentation(['build -f html fixture/internal.input.js']);
} catch (err) {
expect(
err
.toString()
.match(
/The HTML output mode requires a destination directory set with -o/
)
).toBeTruthy();
}
});
test('bad command', async function () {
try {
await documentation(['-f html fixture/internal.input.js'], {}, false);
} catch (err) {
expect(err.code).toBeTruthy();
}
});
});
const semver =
/\bv?(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-[\da-z-]+(?:\.[\da-z-]+)*)?(?:\+[\da-z-]+(?:\.[\da-z-]+)*)?\b/gi;
test('--config', async function () {
const dst = path.join(os.tmpdir(), (Date.now() + Math.random()).toString());
fs.mkdirSync(dst);
const outputIndex = path.join(dst, 'index.html');
const data = await documentation(
[
'build -c fixture/html/documentation.yml -f html fixture/html/nested.input.js -o ' +
dst
],
{},
false
);
let output = fs.readFileSync(outputIndex, 'utf8');
const version = (await import('../package.json')).default.version;
output = output.replace(new RegExp(version.replace(/\./g, '\\.'), 'g'), '');
expect(output).toMatchSnapshot();
});
test('--version', async function () {
const output = await documentation(['--version'], {}, false);
expect(output).toBeTruthy();
});
describe('lint command', function () {
test('generates lint output', async function () {
try {
await documentation(['lint fixture/lint/lint.input.js'], {}, false);
} catch (err) {
const data = err.stderr.toString().split('\n').slice(2).join('\n');
expect(data).toMatchSnapshot();
}
});
test('generates no output on a good file', async function () {
const data = await documentation(
['lint fixture/simple.input.js'],
{},
false
);
expect(data).toBe('');
});
test('exposes syntax error on a bad file', async function () {
try {
await documentation(
['lint fixture/bad/syntax.input', '--parseExtension input'],
{},
false
);
} catch (err) {
expect(err.code > 0).toBeTruthy();
}
});
test('lint with no inputs', async function () {
try {
await documentation(
['lint'],
{
cwd: path.join(__dirname, 'fixture/bad')
},
false
);
} catch (err) {
expect(err.code > 0).toBeTruthy();
}
});
test('generates lint output with shallow', async function () {
const data = await documentation(
['lint fixture/lint/lint.input.shallow.js --shallow'],
{},
false
);
expect(data).toBe('');
});
});
test('given no files', async function () {
try {
await documentation(['build']);
} catch (err) {
expect(
err
.toString()
.match(
/documentation was given no files and was not run in a module directory/
)
).toBeTruthy();
}
});
test('with an invalid command', async function () {
try {
await documentation(['invalid'], {}, false);
} catch (err) {
expect(err).toBeTruthy();
}
});
test.skip('--access flag', async function () {
const data = await documentation(
['build --shallow fixture/internal.input.js -a public'],
{},
false
);
expect(data).toBe('[]');
});
test.skip('--infer-private flag', async function () {
const data = await documentation(
['build fixture/infer-private.input.js --infer-private ^_'],
{},
false
);
// This uses JSON.parse with a reviver used as a visitor.
JSON.parse(data, function (n, v) {
// Make sure we do not see any names that match `^_`.
if (n === 'name') {
expect(typeof v).toBe('string');
expect(!/_$/.test.skip(v)).toBeTruthy();
}
return v;
});
});
test.skip('write to file', async function () {
const dst = path.join(os.tmpdir(), (Date.now() + Math.random()).toString());
const data = await documentation(
['build --shallow fixture/internal.input.js -o ' + dst],
{},
false
);
expect(data).toBe('');
expect(fs.existsSync(dst)).toBeTruthy();
});
test.skip('write to html', async function () {
const dstDir = path.join(
os.tmpdir(),
(Date.now() + Math.random()).toString()
);
fs.mkdirSync(dstDir);
const data = await documentation(
['build --shallow fixture/internal.input.js -f html -o ' + dstDir],
{},
false
);
expect(data).toBe('');
expect(fs.existsSync(path.join(dstDir, 'index.html'))).toBeTruthy();
});
test.skip('write to html with custom theme', async function () {
const dstDir = path.join(
os.tmpdir(),
(Date.now() + Math.random()).toString()
);
fs.mkdirSync(dstDir);
const data = await documentation(
[
'build -t fixture/custom_theme --shallow fixture/internal.input.js -f html -o ' +
dstDir
],
{},
false
);
expect(data).toBe('');
expect(fs.readFileSync(path.join(dstDir, 'index.html'), 'utf8')).toBeTruthy();
});
test.skip('write to html, highlightAuto', function () {
const fixture = 'fixture/auto_lang_hljs/multilanguage.input.js';
const config = 'fixture/auto_lang_hljs/config.yml';
const dstDir = path.join(
os.tmpdir(),
(Date.now() + Math.random()).toString()
);
fs.mkdirSync(dstDir);
return documentation(
['build --shallow ' + fixture + ' -c ' + config + ' -f html -o ' + dstDir],
{},
false
).then(() => {
const result = fs.readFileSync(path.join(dstDir, 'index.html'), 'utf8');
expect(
result.indexOf('<span class="hljs-number">42</span>') > 0
).toBeTruthy();
expect(
result.indexOf('<span class="hljs-selector-attr">[data-foo]</span>') > 0
).toBeTruthy();
expect(
result.indexOf('<span class="hljs-attr">data-foo</span>') > 0
).toBeTruthy();
});
});
test.skip('fatal error', async function () {
try {
await documentation(
['build --shallow fixture/bad/syntax.input --parseExtension input'],
{},
false
);
} catch (err) {
expect(err.toString().match(/Unexpected token/)).toBeTruthy();
}
});
test('build GFM (e.g. markdown tables) for -f md', async function () {
const data = await documentation(
['build', 'fixture/html/nested.input.js', '--shallow', '-f', 'md'],
{},
false
);
expect(data).toMatchSnapshot();
expect(data).toMatch(
`| Col 1 | Col 2 | Col 3 |
| ----- | ----- | ----- |
| Dat 1 | Dat 2 | Dat 3 |
| Dat 4 | Dat 5 | Dat 6 |`
);
});
test.skip('build --document-exported', async function () {
const data = await documentation(
['build fixture/document-exported.input.js --document-exported -f md'],
{},
false
);
expect(data).toMatchSnapshot();
});
test.skip('build large file without error (no deoptimized styling error)', function () {
const dstFile =
path.join(os.tmpdir(), (Date.now() + Math.random()).toString()) + '.js';
let contents = '';
for (let i = 0; i < 4e4; i++) {
contents += '/* - */\n';
}
fs.writeFileSync(dstFile, contents, 'utf8');
return documentation(['build ' + dstFile], {}, false).then(() => {
fs.unlinkSync(dstFile);
});
});
test.skip('should use browser resolve', async function () {
const data = await documentation(['build fixture/resolve/index.js']);
expect(normalize(data)).toMatchSnapshot();
});
test.skip('should use node resolve', async function () {
const data = await documentation([
'build fixture/resolve/index.js --resolve node'
]);
expect(normalize(data)).toMatchSnapshot();
});