Skip to content

Commit

Permalink
Ported test: arg-parsing
Browse files Browse the repository at this point in the history
- Unknown options cause an error rather than a warning.
- The styling of some errors changed (colored, padded, indented).
- The algorithm used for suggesting flags when unknown ones are encountered changed.
- Deprecated options like "--jsx-bracket-same-line" are no longer recognized.
  • Loading branch information
fabiospampinato committed Sep 13, 2024
1 parent 6afc523 commit d03eb4f
Show file tree
Hide file tree
Showing 15 changed files with 10,615 additions and 440 deletions.
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
bin.ts
__tests__
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
{
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
Expand Down
10,732 changes: 10,294 additions & 438 deletions package-lock.json

Large diffs are not rendered by default.

11 changes: 9 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
"clean": "rm -rf dist",
"compile": "tsc --outDir dist",
"compile:watch": "tsc --outDir dist --watch",
"prepublishOnly": "npm run compile",
"prepublishOnly": "npm run compile && npm run test",
"test": "cross-env NODE_OPTIONS=--experimental-vm-modules jest --runInBand --rootDir test/__tests__",
"typecheck": "tsc --noEmit"
},
"peerDependencies": {
Expand All @@ -47,7 +48,7 @@
"lomemo": "^1.0.0",
"pioppo": "^1.1.1",
"promise-resolve-timeout": "^2.0.0",
"specialist": "^1.4.2",
"specialist": "^1.4.3",
"tiny-editorconfig": "^1.0.0",
"tiny-jsonc": "^1.0.1",
"tiny-readdir-glob": "^1.22.24",
Expand All @@ -58,8 +59,14 @@
"zeptomatch-is-static": "^1.0.0"
},
"devDependencies": {
"@types/jest": "^29.5.12",
"@types/js-yaml": "^4.0.9",
"@types/node": "^20.12.7",
"cross-env": "^7.0.3",
"jest": "^29.7.0",
"jest-snapshot-serializer-ansi": "^2.1.0",
"jest-snapshot-serializer-raw": "^2.0.0",
"nanoexec": "^1.1.0",
"prettier": "3.3.3",
"typescript": "^5.4.5"
}
Expand Down
2 changes: 2 additions & 0 deletions test/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

root = true
Empty file added test/.prettierignore
Empty file.
3 changes: 3 additions & 0 deletions test/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"printWidth": 80
}
1 change: 1 addition & 0 deletions test/__fixtures__/arg-parsing/file.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
console.log("could be single quote and without semi")
2 changes: 2 additions & 0 deletions test/__fixtures__/arg-parsing/number/1/file-in-dir-named-1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
hello(
'prettier')
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
hello(
'prettier')
2 changes: 2 additions & 0 deletions test/__fixtures__/arg-parsing/number/3
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
hello(
'prettier')
2 changes: 2 additions & 0 deletions test/__fixtures__/arg-parsing/number/4.44
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
hello(
'prettier')
89 changes: 89 additions & 0 deletions test/__tests__/__snapshots__/arg-parsing.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`allow overriding flags (stderr) 1`] = `""`;

exports[`allow overriding flags (write) 1`] = `[]`;

exports[`boolean flags do not swallow the next argument (stderr) 1`] = `""`;

exports[`boolean flags do not swallow the next argument (stdout) 1`] = `"console.log('could be single quote and without semi');"`;

exports[`boolean flags do not swallow the next argument (write) 1`] = `[]`;

exports[`deprecated option values are warned (stderr) 1`] = `"[warn] --jsx-bracket-same-line is deprecated."`;

exports[`deprecated option values are warned (stdout) 1`] = `"console.log("could be single quote and without semi");"`;

exports[`deprecated option values are warned (write) 1`] = `[]`;

exports[`negated options work (stderr) 1`] = `""`;

exports[`negated options work (stdout) 1`] = `"console.log("could be single quote and without semi")"`;

exports[`negated options work (write) 1`] = `[]`;

exports[`number file/dir (stdout) 1`] = `"1/file-in-dir-named-1.js"`;

exports[`number file/dir (stdout) 2`] = `"2.2/file-in-dir-named-2.2.js"`;

exports[`number file/dir (stdout) 3`] = `"3"`;

exports[`number file/dir (stdout) 4`] = `"4.44"`;

exports[`number file/dir (stdout) 5`] = `
"1/file-in-dir-named-1.js
2.2/file-in-dir-named-2.2.js
3
4.44"
`;

exports[`options with \`cliName\` should not allow to pass directly (stderr) 1`] = `
"[warn] Ignored unknown option --filepath=file.js.
[error] No parser and no file path given, couldn't infer a parser."
`;

exports[`options with \`cliName\` should not allow to pass directly (stdout) 1`] = `"prettier();"`;

exports[`options with \`cliName\` should not allow to pass directly (stdout) 2`] = `""`;

exports[`unknown negated options are errored (stderr) 1`] = `""`;

exports[`unknown negated options are errored (stdout) 1`] = `
"
Unknown option: "unknown"
"
`;

exports[`unknown negated options are errored (write) 1`] = `[]`;

exports[`unknown negated options are warned (stderr) 1`] = `""`;

exports[`unknown negated options are warned (stdout) 1`] = `
"
Unknown option: "unknown"
"
`;

exports[`unknown negated options are warned (write) 1`] = `[]`;

exports[`unknown options are errored (stderr) 1`] = `""`;

exports[`unknown options are errored (stdout) 1`] = `
"
Unknown option: "unknown"
"
`;

exports[`unknown options are errored (write) 1`] = `[]`;

exports[`unknown options may trigger a suggestion \`_\` (stderr) 1`] = `""`;

exports[`unknown options may trigger a suggestion \`_\` (stdout) 1`] = `
"
Unknown option: "a". Did you mean "v"?
"
`;

exports[`unknown options not suggestion \`_\` (stderr) 1`] = `"[warn] Ignored unknown option -a. Did you mean -c?"`;

exports[`unknown options not suggestion \`_\` (stdout) 1`] = `"console.log("could be single quote and without semi");"`;
118 changes: 118 additions & 0 deletions test/__tests__/arg-parsing.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import {runCli} from "../utils";

describe("boolean flags do not swallow the next argument", () => {
runCli("arg-parsing", [
"--end-of-line",
"lf",
"--single-quote",
"file.js",
]).test({
status: 0,
});
});

describe("negated options work", () => {
runCli("arg-parsing", [
"--end-of-line",
"lf",
"--no-semi",
"file.js",
]).test({
status: 0,
});
});

describe("unknown options are errored", () => {
runCli("arg-parsing", [
"--end-of-line",
"lf",
"file.js",
"--unknown",
]).test({
status: 1,
});
});

describe("unknown negated options are errored", () => {
runCli("arg-parsing", [
"--end-of-line",
"lf",
"file.js",
"--no-unknown",
]).test({
status: 1,
});
});

describe("unknown options may trigger a suggestion `_`", () => {
runCli("arg-parsing", [
"file.js",
"-a",
]).test({
status: 1,
write: [],
});
});

describe("allow overriding flags", () => {
runCli("arg-parsing", [
"--tab-width=1",
"--tab-width=3",
"--parser=babel",
], {
input: "function a() { b }",
}).test({
stdout: "function a() {\n b;\n}",
status: 0,
});
});

describe("number file/dir", () => {
const patterns = ["1", "2.2", "3", "4.44"];
for (const pattern of patterns) {
runCli("arg-parsing/number", [
"--parser=babel",
"--list-different",
pattern,
]).test({
stderr: "",
status: 1,
write: [],
});
}
runCli("arg-parsing/number", [
"--parser=babel",
"--list-different",
...patterns,
]).test({
stderr: "",
status: 1,
write: [],
});
});

describe.skip("options with `cliName` should not allow to pass directly", () => {
// `filepath` can only pass through `--stdin-filepath`
// `plugins` works the same
runCli("arg-parsing", [
"--stdin-filepath",
"file.js",
], {
isTTY: false,
input: "prettier()",
}).test({
status: 0,
stderr: "",
write: [],
});
runCli("arg-parsing", [
"--filepath",
"file.js",
], {
isTTY: false,
input: "prettier()",
}).test({
status: 2,
write: [],
});
});
87 changes: 87 additions & 0 deletions test/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import exec from "nanoexec";
import path from "node:path";
import process from "node:process";

import { expect, test } from "@jest/globals";
import serializerRaw from "jest-snapshot-serializer-raw";
import serializerAnsi from "jest-snapshot-serializer-ansi";

expect.addSnapshotSerializer(serializerRaw);
expect.addSnapshotSerializer(serializerAnsi);

const ROOT_PATH = process.cwd();
const BIN_PATH = path.join(ROOT_PATH, "dist", "bin.js");
const FIXTURES_PATH = path.join(ROOT_PATH, "test", "__fixtures__");

function normalizeOutput(output, options) {
// \r is trimmed from jest snapshots by default;
// manually replacing this character with /*CR*/ to test its true presence
// If ignoreLineEndings is specified, \r is simply deleted instead
// output = output.replace(/\r/gu, options.ignoreLineEndings ? "" : "/*CR*/"); //TODO
output = output.replace(/(\r?\n|\r)$/, "");
return output;
}

async function runCommand(dir, args, options) {
const cwd = path.join(FIXTURES_PATH, dir);
const result = exec("node", [BIN_PATH, ...args], { cwd, stdio: "pipe" });

if (options.input) {
result.process.stdin.write(options.input);
result.process.stdin.end();
}

const status = await result.code;
const stdout = normalizeOutput((await result.stdout).toString());
const stderr = normalizeOutput((await result.stderr).toString());
const write = []; //TODO

return { status, stdout, stderr, write };
}

async function runTest(name, expected, result, options) {
const title = options.title || "";
test(`${title}(${name})`, async () => {
const value = (await result)[name];
if (expected !== undefined) {
if (name === "status" && expected === "non-zero") {
expect(value).not.toBe(0);
} else if (typeof expected === "function") {
expected(value);
} else {
expect(value).toEqual(expected);
}
} else {
expect(value).toMatchSnapshot();
}
});
}

function runCli(dir, args = [], options = {}) {
const result = runCommand(dir, args, options);
return {
get status() {
return result.then(({ status }) => status);
},
get stdout() {
return run().then(({ stdout }) => stdout);
},
get stderr() {
return run().then(({ stderr }) => stderr);
},
get write() {
return run().then(({ write }) => write);
},
test: (tests) => {
for (const name of ["status", "stdout", "stderr", "write"]) {
const expected = tests[name];
runTest(name, expected, result, options);
}
},
then: (onFulfilled, onRejected) => {
return result.then(onFulfilled, onRejected);
},
};
}

export { runCli };

0 comments on commit d03eb4f

Please sign in to comment.