Files
test-repo/tests_config/run_spec.js
T

358 lines
11 KiB
JavaScript
Raw Normal View History

2017-01-10 12:18:22 -05:00
"use strict";
2017-04-12 13:41:51 -07:00
2020-06-26 20:30:21 +08:00
const { TEST_STANDALONE } = process.env;
const fs = require("fs");
2018-12-08 18:28:29 +08:00
const path = require("path");
const { isCI } = require("ci-info");
2020-06-26 20:30:21 +08:00
const prettier = !TEST_STANDALONE
? require("prettier/local")
: require("prettier/standalone");
2020-05-08 10:56:09 +08:00
const checkParsers = require("./utils/check-parsers");
2020-05-28 01:16:52 +08:00
const visualizeRange = require("./utils/visualize-range");
2020-05-28 09:29:21 +08:00
const createSnapshot = require("./utils/create-snapshot");
const composeOptionsForSnapshot = require("./utils/compose-options-for-snapshot");
const visualizeEndOfLine = require("./utils/visualize-end-of-line");
const consistentEndOfLine = require("./utils/consistent-end-of-line");
const stringifyOptionsForTitle = require("./utils/stringify-options-for-title");
2016-12-23 19:31:38 +00:00
const AST_COMPARE = isCI || process.env.AST_COMPARE;
2020-03-21 19:43:01 +08:00
const DEEP_COMPARE = isCI || process.env.DEEP_COMPARE;
2020-03-24 23:09:16 +08:00
const TEST_CRLF =
(isCI && process.platform === "win32") || process.env.TEST_CRLF;
2018-12-08 18:28:29 +08:00
const CURSOR_PLACEHOLDER = "<|>";
const RANGE_START_PLACEHOLDER = "<<<PRETTIER_RANGE_START>>>";
const RANGE_END_PLACEHOLDER = "<<<PRETTIER_RANGE_END>>>";
2020-03-21 19:43:01 +08:00
// TODO: these test files need fix
const unstableTests = new Map(
[
2020-05-24 21:53:29 +03:00
"js/class-comment/misc.js",
2020-05-08 00:55:46 +08:00
["js/comments/dangling_array.js", (options) => options.semi === false],
["js/comments/jsx.js", (options) => options.semi === false],
"js/comments/return-statement.js",
"js/comments/tagged-template-literal.js",
"js/comments-closure-typecast/iife.js",
2020-05-07 23:54:44 +08:00
"markdown/spec/example-234.md",
"markdown/spec/example-235.md",
2020-05-07 23:05:21 +08:00
"html/multiparser-js/script-tag-escaping.html",
2020-03-21 19:43:01 +08:00
[
2020-05-08 00:55:46 +08:00
"js/multiparser-markdown/codeblock.js",
2020-03-21 22:07:52 +02:00
(options) => options.proseWrap === "always",
2020-03-21 19:43:01 +08:00
],
2020-05-08 00:55:46 +08:00
["js/no-semi/comments.js", (options) => options.semi === false],
["flow/no-semi/comments.js", (options) => options.semi === false],
2020-03-21 22:07:52 +02:00
].map((fixture) => {
2020-03-21 19:43:01 +08:00
const [file, isUnstable = () => true] = Array.isArray(fixture)
? fixture
: [fixture];
return [path.join(__dirname, "../tests/", file), isUnstable];
})
);
2020-08-10 22:17:51 +08:00
const isUnstable = (filename, options) => {
const testFunction = unstableTests.get(filename);
if (!testFunction) {
return false;
}
return testFunction(options);
};
const shouldThrowOnVerify = (filename, options) => {
if (options.parser !== "babel-ts") {
return false;
}
const { disableBabelTS } = options;
if (disableBabelTS === true) {
return true;
}
if (Array.isArray(disableBabelTS) && disableBabelTS.includes(filename)) {
return true;
}
return false;
};
2020-04-14 18:19:13 +08:00
const isTestDirectory = (dirname, name) =>
dirname.startsWith(path.join(__dirname, "../tests", name));
global.run_spec = (fixtures, parsers, options) => {
fixtures = typeof fixtures === "string" ? { dirname: fixtures } : fixtures;
const { dirname } = fixtures;
2020-02-03 07:33:19 +08:00
// `IS_PARSER_INFERENCE_TESTS` mean to test `inferParser` on `standalone`
2020-04-14 18:19:13 +08:00
const IS_PARSER_INFERENCE_TESTS = isTestDirectory(
dirname,
"misc/parser-inference"
2020-04-14 18:19:13 +08:00
);
// `IS_ERROR_TESTS` mean to watch errors like:
// - syntax parser hasn't supported yet
// - syntax errors that should throws
const IS_ERROR_TESTS = isTestDirectory(dirname, "misc/errors");
2020-04-14 18:19:13 +08:00
2020-02-03 07:33:19 +08:00
if (IS_PARSER_INFERENCE_TESTS) {
parsers = [];
} else if (!parsers || !parsers.length) {
throw new Error(`No parsers were specified for ${dirname}`);
}
const snippets = (fixtures.snippets || []).map((test, index) => {
test = typeof test === "string" ? { code: test } : test;
return {
2020-04-25 09:23:34 +08:00
...test,
name: `snippet: ${test.name || `#${index}`}`,
};
});
2018-12-08 18:28:29 +08:00
const files = fs
.readdirSync(dirname, { withFileTypes: true })
.map((file) => {
const basename = file.name;
const filename = path.join(dirname, basename);
if (
path.extname(basename) === ".snap" ||
!file.isFile() ||
basename[0] === "." ||
basename === "jsfmt.spec.js"
) {
return;
}
2020-04-18 21:09:07 +08:00
const text = fs.readFileSync(filename, "utf8");
2020-04-18 21:09:07 +08:00
return {
name: basename,
filename,
code: text,
};
})
.filter(Boolean);
2020-05-21 21:10:00 +08:00
// Make sure tests are in correct location
// only runs on local and one task on CI
if (!isCI || process.env.ENABLE_CODE_COVERAGE) {
checkParsers({ dirname, files }, parsers);
}
2020-05-28 09:29:21 +08:00
const stringifiedOptions = stringifyOptionsForTitle(options);
2020-08-10 22:17:51 +08:00
const [parser, ...verifyParsers] = parsers;
if (parsers.includes("typescript") && !parsers.includes("babel-ts")) {
verifyParsers.push("babel-ts");
}
2020-04-25 09:23:34 +08:00
for (const { name, filename, code, output } of [...files, ...snippets]) {
describe(`${name}${
2020-04-18 21:09:07 +08:00
stringifiedOptions ? ` - ${stringifiedOptions}` : ""
}`, () => {
2020-08-10 22:17:51 +08:00
const formatOptions = {
2020-04-10 19:12:01 +08:00
printWidth: 80,
...options,
2020-08-10 22:17:51 +08:00
filepath: filename,
parser,
2020-04-10 19:12:01 +08:00
};
2020-04-14 18:19:13 +08:00
if (IS_ERROR_TESTS) {
test("error test", () => {
expect(() => {
2020-08-10 22:17:51 +08:00
format(code, formatOptions);
2020-04-14 18:19:13 +08:00
}).toThrowErrorMatchingSnapshot();
});
return;
}
2020-08-10 22:17:51 +08:00
const eolReplacedCode = TEST_CRLF ? code.replace(/\n/g, "\r\n") : code;
const firstFormat = format(eolReplacedCode, formatOptions);
2018-12-08 18:28:29 +08:00
2020-04-10 19:12:01 +08:00
test("format", () => {
2020-08-10 22:17:51 +08:00
// Make sure output has consistent EOL
expect(firstFormat.eolVisualizedOutput).toEqual(
visualizeEndOfLine(consistentEndOfLine(firstFormat.outputWithCursor))
2020-04-10 19:12:01 +08:00
);
2020-08-10 22:17:51 +08:00
2020-04-25 09:23:34 +08:00
if (typeof output === "string") {
2020-08-10 22:17:51 +08:00
expect(firstFormat.output).toEqual(output);
return;
2020-04-25 09:23:34 +08:00
}
2020-08-10 22:17:51 +08:00
const hasEndOfLine = "endOfLine" in formatOptions;
let codeForSnapshot = hasEndOfLine
? code
.replace(RANGE_START_PLACEHOLDER, "")
.replace(RANGE_END_PLACEHOLDER, "")
: firstFormat.inputWithCursor;
let codeOffset = 0;
let resultForSnapshot = firstFormat.outputWithCursor;
const { rangeStart, rangeEnd } = firstFormat.options;
if (typeof rangeStart === "number" || typeof rangeEnd === "number") {
if (TEST_CRLF && hasEndOfLine) {
codeForSnapshot = codeForSnapshot.replace(/\n/g, "\r\n");
}
codeForSnapshot = visualizeRange(codeForSnapshot, {
rangeStart,
rangeEnd,
});
codeOffset = codeForSnapshot.match(/^>?\s+1 \| /)[0].length;
}
// When `TEST_CRLF` and `endOfLine: "auto"`, the eol is always `\r\n`,
// Replace it with guess result from original input
let resultNote = "";
if (formatOptions.endOfLine === "auto") {
const {
guessEndOfLine,
convertEndOfLineToChars,
} = require("../src/common/end-of-line");
const originalAutoResult = convertEndOfLineToChars(
guessEndOfLine(code)
);
if (originalAutoResult !== "\r\n") {
resultNote +=
"** The EOL of output might not real when `TEST_CRLF` **";
if (TEST_CRLF) {
resultForSnapshot = resultForSnapshot.replace(
/\r\n?/g,
originalAutoResult
);
}
}
}
if (hasEndOfLine) {
codeForSnapshot = visualizeEndOfLine(codeForSnapshot);
resultForSnapshot = visualizeEndOfLine(resultForSnapshot);
}
if (resultNote) {
resultForSnapshot = `${resultNote}\n${resultForSnapshot}`;
}
expect(
createSnapshot(
codeForSnapshot,
resultForSnapshot,
composeOptionsForSnapshot(firstFormat.options, parsers),
{ codeOffset }
)
).toMatchSnapshot();
2020-03-21 19:43:01 +08:00
});
2020-08-10 22:17:51 +08:00
for (const parser of verifyParsers) {
const verifyOptions = { ...firstFormat.options, parser };
const runVerifyFormat = () => format(firstFormat.input, verifyOptions);
2020-04-10 19:12:01 +08:00
if (shouldThrowOnVerify(name, verifyOptions)) {
test(`verify (${parser})`, () => {
expect(runVerifyFormat).toThrow(
2020-08-10 22:17:51 +08:00
TEST_STANDALONE ? undefined : SyntaxError
);
});
} else {
const verifyFormat = runVerifyFormat();
test(`verify (${parser})`, () => {
expect(verifyFormat.eolVisualizedOutput).toEqual(
firstFormat.eolVisualizedOutput
);
});
2020-08-10 22:17:51 +08:00
if (AST_COMPARE && verifyFormat.changed && code.trim()) {
test(`verify AST (${parser})`, () => {
const originalAst = parse(verifyFormat.input, verifyOptions);
const formattedAst = parse(verifyFormat.output, verifyOptions);
expect(originalAst).toEqual(formattedAst);
});
}
}
2020-04-10 19:12:01 +08:00
}
2020-08-10 22:17:51 +08:00
const isUnstableTest = isUnstable(filename, formatOptions);
if (
DEEP_COMPARE &&
2020-08-10 22:17:51 +08:00
(firstFormat.changed || isUnstableTest) &&
// No range and cursor
firstFormat.input === eolReplacedCode
) {
test("second format", () => {
2020-08-10 22:17:51 +08:00
const { eolVisualizedOutput: firstOutput, output } = firstFormat;
const { eolVisualizedOutput: secondOutput } = format(
output,
formatOptions
);
if (isUnstableTest) {
// To keep eye on failed tests, this assert never supposed to pass,
// if it fails, just remove the file from `unstableTests`
2020-08-10 22:17:51 +08:00
expect(secondOutput).not.toEqual(firstOutput);
} else {
2020-08-10 22:17:51 +08:00
expect(secondOutput).toEqual(firstOutput);
}
});
}
2020-04-10 19:12:01 +08:00
2020-08-10 22:17:51 +08:00
if (AST_COMPARE && firstFormat.changed) {
test("compare AST", () => {
2020-08-10 22:17:51 +08:00
const { input, output } = firstFormat;
const originalAst = parse(input, formatOptions);
const formattedAst = parse(output, formatOptions);
expect(formattedAst).toEqual(originalAst);
});
2020-04-10 19:12:01 +08:00
}
});
}
2018-12-08 18:28:29 +08:00
};
function parse(source, options) {
return prettier.__debug.parse(source, options, /* massage */ true).ast;
}
2020-08-10 22:17:51 +08:00
function format(text, options) {
options = {
2020-03-21 22:07:52 +02:00
...options,
2020-08-10 22:17:51 +08:00
};
const inputWithCursor = text
.replace(RANGE_START_PLACEHOLDER, (match, offset) => {
options.rangeStart = offset;
return "";
})
.replace(RANGE_END_PLACEHOLDER, (match, offset) => {
options.rangeEnd = offset;
return "";
});
const input = inputWithCursor.replace(CURSOR_PLACEHOLDER, (match, offset) => {
options.cursorOffset = offset;
return "";
2020-01-19 07:23:17 +08:00
});
2018-12-08 18:28:29 +08:00
2020-08-10 22:17:51 +08:00
const result = prettier.formatWithCursor(input, options);
const output = result.formatted;
const outputWithCursor =
options.cursorOffset >= 0
? output.slice(0, result.cursorOffset) +
2018-12-08 18:28:29 +08:00
CURSOR_PLACEHOLDER +
2020-08-10 22:17:51 +08:00
output.slice(result.cursorOffset)
: output;
const eolVisualizedOutput = visualizeEndOfLine(outputWithCursor);
const changed = outputWithCursor !== inputWithCursor;
return {
changed,
options,
input,
inputWithCursor,
output,
outputWithCursor,
eolVisualizedOutput,
};
2018-12-08 18:28:29 +08:00
}