2017-01-10 20:18:22 +03:00
|
|
|
"use strict";
|
2017-04-12 23:41:51 +03:00
|
|
|
|
2017-03-25 18:10:17 +03:00
|
|
|
const fs = require("fs");
|
2018-12-08 13:28:29 +03:00
|
|
|
const path = require("path");
|
2018-09-01 07:11:19 +03:00
|
|
|
const raw = require("jest-snapshot-serializer-raw").wrap;
|
2016-12-23 22:31:38 +03:00
|
|
|
|
2017-05-09 20:32:01 +03:00
|
|
|
const AST_COMPARE = process.env["AST_COMPARE"];
|
2018-06-12 21:05:40 +03:00
|
|
|
const TEST_STANDALONE = process.env["TEST_STANDALONE"];
|
2018-12-08 13:28:29 +03:00
|
|
|
const TEST_CRLF = process.env["TEST_CRLF"];
|
|
|
|
|
|
|
|
const CURSOR_PLACEHOLDER = "<|>";
|
|
|
|
const RANGE_START_PLACEHOLDER = "<<<PRETTIER_RANGE_START>>>";
|
|
|
|
const RANGE_END_PLACEHOLDER = "<<<PRETTIER_RANGE_END>>>";
|
2018-06-12 21:05:40 +03:00
|
|
|
|
|
|
|
const prettier = !TEST_STANDALONE
|
|
|
|
? require("prettier/local")
|
|
|
|
: require("prettier/standalone");
|
2016-12-30 08:01:44 +03:00
|
|
|
|
2018-12-08 13:28:29 +03:00
|
|
|
global.run_spec = (dirname, parsers, options) => {
|
|
|
|
// istanbul ignore next
|
2017-11-30 09:31:52 +03:00
|
|
|
if (!parsers || !parsers.length) {
|
|
|
|
throw new Error(`No parsers were specified for ${dirname}`);
|
|
|
|
}
|
|
|
|
|
2018-12-08 13:28:29 +03:00
|
|
|
fs.readdirSync(dirname).forEach(basename => {
|
|
|
|
const filename = path.join(dirname, basename);
|
2018-06-12 21:05:40 +03:00
|
|
|
|
2017-06-05 22:34:08 +03:00
|
|
|
if (
|
2018-12-08 13:28:29 +03:00
|
|
|
path.extname(basename) === ".snap" ||
|
|
|
|
!fs.lstatSync(filename).isFile() ||
|
|
|
|
basename[0] === "." ||
|
|
|
|
basename === "jsfmt.spec.js"
|
2017-06-05 22:34:08 +03:00
|
|
|
) {
|
2018-12-08 13:28:29 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let rangeStart;
|
|
|
|
let rangeEnd;
|
|
|
|
let cursorOffset;
|
|
|
|
|
|
|
|
const text = fs.readFileSync(filename, "utf8");
|
|
|
|
|
|
|
|
const source = (TEST_CRLF ? text.replace(/\n/g, "\r\n") : text)
|
|
|
|
.replace(RANGE_START_PLACEHOLDER, (match, offset) => {
|
|
|
|
rangeStart = offset;
|
|
|
|
return "";
|
|
|
|
})
|
|
|
|
.replace(RANGE_END_PLACEHOLDER, (match, offset) => {
|
|
|
|
rangeEnd = offset;
|
2018-05-01 15:42:59 +03:00
|
|
|
return "";
|
|
|
|
});
|
|
|
|
|
2018-12-08 13:28:29 +03:00
|
|
|
const input = source.replace(CURSOR_PLACEHOLDER, (match, offset) => {
|
|
|
|
cursorOffset = offset;
|
|
|
|
return "";
|
|
|
|
});
|
|
|
|
|
|
|
|
const baseOptions = Object.assign({ printWidth: 80 }, options, {
|
|
|
|
rangeStart,
|
|
|
|
rangeEnd,
|
|
|
|
cursorOffset
|
|
|
|
});
|
|
|
|
const mainOptions = Object.assign({}, baseOptions, {
|
|
|
|
parser: parsers[0]
|
|
|
|
});
|
|
|
|
|
|
|
|
const hasEndOfLine = "endOfLine" in mainOptions;
|
|
|
|
|
|
|
|
const output = format(input, filename, mainOptions);
|
|
|
|
const visualizedOutput = visualizeEndOfLine(output);
|
|
|
|
|
|
|
|
test(basename, () => {
|
|
|
|
expect(visualizedOutput).toEqual(
|
|
|
|
visualizeEndOfLine(consistentEndOfLine(output))
|
|
|
|
);
|
|
|
|
expect(
|
|
|
|
raw(
|
|
|
|
createSnapshot(
|
|
|
|
hasEndOfLine
|
|
|
|
? visualizeEndOfLine(
|
|
|
|
text
|
|
|
|
.replace(RANGE_START_PLACEHOLDER, "")
|
|
|
|
.replace(RANGE_END_PLACEHOLDER, "")
|
|
|
|
)
|
|
|
|
: source,
|
|
|
|
hasEndOfLine ? visualizedOutput : output,
|
|
|
|
Object.assign({}, baseOptions, { parsers })
|
2018-11-25 11:21:14 +03:00
|
|
|
)
|
2018-12-08 13:28:29 +03:00
|
|
|
)
|
|
|
|
).toMatchSnapshot();
|
|
|
|
});
|
|
|
|
|
|
|
|
for (const parser of parsers.slice(1)) {
|
|
|
|
const verifyOptions = Object.assign({}, baseOptions, { parser });
|
|
|
|
test(`${basename} - ${parser}-verify`, () => {
|
|
|
|
const verifyOutput = format(input, filename, verifyOptions);
|
|
|
|
expect(visualizedOutput).toEqual(visualizeEndOfLine(verifyOutput));
|
2017-05-09 06:23:51 +03:00
|
|
|
});
|
2018-12-08 13:28:29 +03:00
|
|
|
}
|
2017-03-25 18:10:17 +03:00
|
|
|
|
2018-12-08 13:28:29 +03:00
|
|
|
if (AST_COMPARE) {
|
|
|
|
test(`${filename} parse`, () => {
|
|
|
|
const parseOptions = Object.assign({}, mainOptions);
|
|
|
|
delete parseOptions.cursorOffset;
|
|
|
|
|
|
|
|
const originalAst = parse(input, parseOptions);
|
|
|
|
let formattedAst;
|
|
|
|
|
|
|
|
expect(() => {
|
|
|
|
formattedAst = parse(
|
|
|
|
output.replace(CURSOR_PLACEHOLDER, ""),
|
|
|
|
parseOptions
|
|
|
|
);
|
|
|
|
}).not.toThrow();
|
|
|
|
expect(originalAst).toEqual(formattedAst);
|
2017-12-29 00:25:11 +03:00
|
|
|
});
|
2016-12-23 22:31:38 +03:00
|
|
|
}
|
|
|
|
});
|
2018-12-08 13:28:29 +03:00
|
|
|
};
|
2017-12-29 00:25:11 +03:00
|
|
|
|
2018-12-08 13:28:29 +03:00
|
|
|
function parse(source, options) {
|
|
|
|
return prettier.__debug.parse(source, options, /* massage */ true).ast;
|
2016-12-23 22:31:38 +03:00
|
|
|
}
|
|
|
|
|
2018-12-08 13:28:29 +03:00
|
|
|
function format(source, filename, options) {
|
2018-05-01 15:42:59 +03:00
|
|
|
const result = prettier.formatWithCursor(
|
2018-12-08 13:28:29 +03:00
|
|
|
source,
|
|
|
|
Object.assign({ filepath: filename }, options)
|
2017-03-25 18:10:17 +03:00
|
|
|
);
|
2018-11-06 17:47:13 +03:00
|
|
|
|
2018-12-08 13:28:29 +03:00
|
|
|
return options.cursorOffset >= 0
|
|
|
|
? result.formatted.slice(0, result.cursorOffset) +
|
|
|
|
CURSOR_PLACEHOLDER +
|
|
|
|
result.formatted.slice(result.cursorOffset)
|
|
|
|
: result.formatted;
|
2016-12-23 22:31:38 +03:00
|
|
|
}
|
|
|
|
|
2018-12-08 13:28:29 +03:00
|
|
|
function consistentEndOfLine(text) {
|
|
|
|
let firstEndOfLine;
|
|
|
|
return text.replace(/\r\n?|\n/g, endOfLine => {
|
|
|
|
if (!firstEndOfLine) {
|
|
|
|
firstEndOfLine = endOfLine;
|
|
|
|
}
|
|
|
|
return firstEndOfLine;
|
|
|
|
});
|
2018-06-12 21:05:40 +03:00
|
|
|
}
|
|
|
|
|
2018-12-08 13:28:29 +03:00
|
|
|
function visualizeEndOfLine(text) {
|
|
|
|
return text.replace(/\r\n?|\n/g, endOfLine => {
|
|
|
|
switch (endOfLine) {
|
|
|
|
case "\n":
|
|
|
|
return "<LF>\n";
|
|
|
|
case "\r\n":
|
|
|
|
return "<CRLF>\n";
|
|
|
|
case "\r":
|
|
|
|
return "<CR>\n";
|
|
|
|
default:
|
|
|
|
throw new Error(`Unexpected end of line ${JSON.stringify(endOfLine)}`);
|
|
|
|
}
|
|
|
|
});
|
2017-03-25 18:10:17 +03:00
|
|
|
}
|
2018-11-25 11:21:14 +03:00
|
|
|
|
|
|
|
function createSnapshot(input, output, options) {
|
|
|
|
const separatorWidth = 80;
|
|
|
|
const printWidthIndicator =
|
|
|
|
options.printWidth > 0 && Number.isFinite(options.printWidth)
|
|
|
|
? " ".repeat(options.printWidth) + "| printWidth"
|
|
|
|
: [];
|
|
|
|
return []
|
|
|
|
.concat(
|
|
|
|
printSeparator(separatorWidth, "options"),
|
|
|
|
printOptions(
|
|
|
|
omit(
|
|
|
|
options,
|
|
|
|
k => k === "rangeStart" || k === "rangeEnd" || k === "cursorOffset"
|
|
|
|
)
|
|
|
|
),
|
|
|
|
printWidthIndicator,
|
|
|
|
printSeparator(separatorWidth, "input"),
|
|
|
|
input,
|
|
|
|
printSeparator(separatorWidth, "output"),
|
|
|
|
output,
|
|
|
|
printSeparator(separatorWidth)
|
|
|
|
)
|
|
|
|
.join("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
function printSeparator(width, description) {
|
|
|
|
description = description || "";
|
|
|
|
const leftLength = Math.floor((width - description.length) / 2);
|
|
|
|
const rightLength = width - leftLength - description.length;
|
|
|
|
return "=".repeat(leftLength) + description + "=".repeat(rightLength);
|
|
|
|
}
|
|
|
|
|
|
|
|
function printOptions(options) {
|
|
|
|
const keys = Object.keys(options).sort();
|
|
|
|
return keys.map(key => `${key}: ${stringify(options[key])}`).join("\n");
|
|
|
|
function stringify(value) {
|
|
|
|
return value === Infinity
|
|
|
|
? "Infinity"
|
|
|
|
: Array.isArray(value)
|
|
|
|
? `[${value.map(v => JSON.stringify(v)).join(", ")}]`
|
|
|
|
: JSON.stringify(value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function omit(obj, fn) {
|
|
|
|
return Object.keys(obj).reduce((reduced, key) => {
|
|
|
|
const value = obj[key];
|
|
|
|
if (!fn(key, value)) {
|
|
|
|
reduced[key] = value;
|
|
|
|
}
|
|
|
|
return reduced;
|
|
|
|
}, {});
|
|
|
|
}
|