Skip to content

Commit

Permalink
test: migrate plugin-options-string tests
Browse files Browse the repository at this point in the history
Copies the tests from prettier.

Notable changes:

- A failed plugin load throws an uncaught exception before this change.
  This catches it and logs an error to stderr
- A `snapshot-diff` package was being used to create a `git diff` like
  diff as a jest snapshot. We can pick the unique lines out instead of
using this

Skipped tests:

- `--help {option}` is not supported in prettier CLI
- `--config-path` with stdin is not yet supported (see prettier#21)

Notable hiccups:

- Parsing argv seems to fall over on things like `--plugin=--foo--`, so
  the test has been changed for now to use `--plugin=totally-invalid`
  • Loading branch information
43081j committed Feb 15, 2025
1 parent ada6549 commit 68a212f
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 4 deletions.
17 changes: 15 additions & 2 deletions src/bin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import { bin, color, exit, parseArgv } from "specialist";
import { PRETTIER_VERSION } from "./constants.js";
import { getPlugin, isBoolean, isNumber, isIntegerInRange, isString } from "./utils.js";
import { normalizeOptions, normalizeFormatOptions, normalizePluginOptions } from "./utils.js";
import type { Bin, PluginsOptions } from "./types.js";
import type { Bin, PluginsOptions, PrettierPlugin } from "./types.js";
import Logger from "./logger.js";

const makeBin = (): Bin => {
return (
Expand Down Expand Up @@ -204,10 +205,18 @@ const makePluggableBin = async (): Promise<Bin> => {
const pluginsDefaultOptions: PluginsOptions = {};
const pluginsNames = formatOptions.plugins || [];
const optionsNames: string[] = [];
const failedPlugins: string[] = [];

for (let i = 0, l = pluginsNames.length; i < l; i++) {
const pluginName = pluginsNames[i];
const plugin = await getPlugin(pluginName);
let plugin: PrettierPlugin;

try {
plugin = await getPlugin(pluginName);
} catch {
failedPlugins.push(pluginName);
continue;
}

for (const option in plugin.options) {
optionsNames.push(option);
Expand Down Expand Up @@ -257,6 +266,10 @@ const makePluggableBin = async (): Promise<Bin> => {
bin = bin.action(async (options, files) => {
const { run } = await import("./index.js");
const baseOptions = await normalizeOptions(options, files);
const stderr = new Logger(baseOptions.logLevel, "stderr");
for (const pluginName of failedPlugins) {
stderr.warn(`Cannot find package '${pluginName}'`);
}
const pluginsCustomOptions = normalizePluginOptions(options, optionsNames);
return run(baseOptions, pluginsDefaultOptions, pluginsCustomOptions);
});
Expand Down
14 changes: 12 additions & 2 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,19 @@ function getPluginVersion(name: string): string | null {
}
}

function getPlugins(names: string[]): PromiseMaybe<PrettierPlugin[]> {
async function getPlugins(names: string[]): Promise<PrettierPlugin[]> {
if (!names.length) return [];
return Promise.all(names.map(getPlugin));
return (
await Promise.all(
names.map(async (name) => {
try {
return await getPlugin(name);
} catch {
return null;
}
}),
)
).filter((plugin): plugin is PrettierPlugin => plugin !== null);
}

const getPluginsBuiltin = once(async (): Promise<PrettierPlugin[]> => {
Expand Down
4 changes: 4 additions & 0 deletions test/__fixtures__/plugin-options-string/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"plugins": ["./plugin.cjs"],
"fooString": "baz"
}
31 changes: 31 additions & 0 deletions test/__fixtures__/plugin-options-string/plugin.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"use strict";

module.exports = {
languages: [
{
name: "foo",
parsers: ["foo-parser"],
extensions: [".foo"],
since: "1.0.0",
},
],
options: {
fooString: {
type: "string",
default: "bar",
description: "foo description",
},
},
parsers: {
"foo-parser": {
parse: (text) => ({ text }),
astFormat: "foo-ast",
},
},
printers: {
"foo-ast": {
print: (path, options) =>
options.fooString ? `foo:${options.fooString}` : path.getValue().text,
},
},
};
18 changes: 18 additions & 0 deletions test/__tests__/__snapshots__/plugin-options-string.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`show detailed external option with \`--help foo-string\` (stderr) 1`] = `""`;

exports[`show detailed external option with \`--help foo-string\` (stdout) 1`] = `
"--foo-string <string>
foo description
Default: bar"
`;
exports[`show detailed external option with \`--help foo-string\` (write) 1`] = `[]`;
exports[`show external options with \`--help\` 1`] = `
" --foo-string <value> foo description
Defaults to "bar""
`;
72 changes: 72 additions & 0 deletions test/__tests__/plugin-options-string.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { runCli } from "../utils";

test("show external options with `--help`", async () => {
const originalStdout = await runCli("plugin-options-string", ["--help"])
.stdout;
const pluggedStdout = await runCli("plugin-options-string", [
"--help",
"--plugin=./plugin.cjs",
]).stdout;
const originalLines = originalStdout.split("\n");
const pluggedLines = pluggedStdout.split("\n");
const differentLines = pluggedLines.filter((line) =>
!originalLines.includes(line));
expect(differentLines.join("\n")).toMatchSnapshot();
});

// Note (43081j); we don't currently support `--help {optionName}`
describe.skip("show detailed external option with `--help foo-string`", () => {
runCli("plugin-options-string", [
"--plugin=./plugin.cjs",
"--help",
"foo-string",
]).test({
status: 0,
});
});

describe("external options from CLI should work", () => {
runCli(
"plugin-options-string",
[
"--plugin=./plugin.cjs",
"--stdin-filepath",
"example.foo",
"--foo-string",
"baz",
],
{ input: "hello-world" },
).test({
stdout: "foo:baz",
stderr: "",
status: 0,
write: [],
});
});

// TODO (43081j): this won't work until we fix #21
describe.skip("external options from config file should work", () => {
runCli(
"plugin-options-string",
["--config-path=./config.json", "--stdin-filepath", "example.foo"],
{ input: "hello-world" },
).test({
stdout: "foo:baz",
stderr: "",
status: 0,
write: [],
});
});

describe("Non exists plugin", () => {
runCli(
"plugin-options-string",
["--plugin=totally-invalid", "--stdin-filepath", "example.foo"],
{ input: "hello-world" },
).test({
stdout: "",
stderr: expect.stringMatching(/Cannot find package 'totally-invalid'/u),
status: 1,
write: [],
});
});

0 comments on commit 68a212f

Please sign in to comment.