Files
test-repo/tests/integration/__tests__/cache.js
T
renovate[bot]andfisker bba5dc430c chore(deps): update dependency file-entry-cache to v10.1.0 (#17438)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: fisker <lionkay@gmail.com>
2025-05-27 09:06:53 +08:00

696 lines
20 KiB
JavaScript

import fs from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
function resolveDir(dir) {
return fileURLToPath(new URL(`../${dir}/`, import.meta.url));
}
const runCliWithoutGitignore = (dir, args, options) =>
runCli(dir, [...args, "--ignore-path", ".prettierignore"], options);
describe("--cache option", () => {
const dir = resolveDir("cli/cache");
const defaultCacheFile = path.join(
dir,
"node_modules/.cache/prettier/.prettier-cache",
);
const nonDefaultCacheFileName = ".non-default-cache-file";
const directoryNameAsCacheFile = "directory-as-cache-file";
const nonDefaultCacheFilePath = path.join(dir, nonDefaultCacheFileName);
const contentA = `function a() {
console.log("this is a.js")
}
`;
const contentB = `function b() {
console.log("this is b.js");
}
`;
const clean = async () => {
await fs.rm(path.join(dir, directoryNameAsCacheFile), {
force: true,
recursive: true,
});
await fs.rm(nonDefaultCacheFilePath, { force: true });
await fs.rm(path.join(dir, "a.js"), { force: true });
await fs.rm(path.join(dir, "b.js"), { force: true });
};
beforeAll(async () => {
await clean();
await fs.mkdir(path.join(dir, directoryNameAsCacheFile));
await fs.writeFile(path.join(dir, "a.js"), contentA);
await fs.writeFile(path.join(dir, "b.js"), contentB);
});
afterEach(async () => {
await fs.rm(path.join(dir, "node_modules"), {
force: true,
recursive: true,
});
await fs.rm(nonDefaultCacheFilePath, { force: true });
await fs.writeFile(path.join(dir, "a.js"), contentA);
await fs.writeFile(path.join(dir, "b.js"), contentB);
});
afterAll(clean);
it("throw error when cache-strategy is invalid", async () => {
const { stderr } = await runCliWithoutGitignore(dir, [
"--cache",
"--cache-strategy",
"invalid",
"*.js",
]);
expect(stderr.trim()).toBe(
'[error] Invalid --cache-strategy value. Expected "content" or "metadata", but received "invalid".',
);
});
it("throws error when use with --stdin-filepath", async () => {
const { stderr } = await runCliWithoutGitignore(
dir,
["--cache", "--stdin-filepath", "foo.js"],
{ input: "const a = a;" },
);
expect(stderr.trim()).toBe(
"[error] `--cache` cannot be used when formatting stdin.",
);
});
it("throws error when use `--cache-strategy` without `--cache`.", async () => {
const { stderr } = await runCliWithoutGitignore(
dir,
["foo.js", "--cache-strategy", "content"],
{
input: "const a = a;",
},
);
expect(stderr.trim()).toBe(
"[error] `--cache-strategy` cannot be used without `--cache`.",
);
});
it("throws error when `--cache-location` is a directory.", async () => {
const { stderr } = await runCliWithoutGitignore(dir, [
"foo.js",
"--cache",
"--cache-location",
directoryNameAsCacheFile,
]);
expect(stderr.trim()).toEqual(
expect.stringMatching(
/\[error\] Resolved --cache-location '.+' is a directory/u,
),
);
});
describe("--cache-strategy metadata", () => {
it("creates default cache file named `node_modules/.cache/prettier/.prettier-cache`", async () => {
await expect(fs.stat(defaultCacheFile)).rejects.toHaveProperty(
"code",
"ENOENT",
);
await runCliWithoutGitignore(dir, [
"--cache",
"--cache-strategy",
"metadata",
"*.js",
]);
await expect(fs.stat(defaultCacheFile)).resolves.not.toThrow();
});
it("doesn't format when cache is available", async () => {
const { stdout: firstStdout } = await runCliWithoutGitignore(dir, [
"--cache",
"--write",
"--cache-strategy",
"metadata",
"*.js",
]);
expect(firstStdout.split("\n")).toEqual(
expect.arrayContaining([
expect.stringMatching(/^a\.js .+ms$/u),
expect.stringMatching(/^b\.js .+ms \(unchanged\)$/u),
]),
);
const { stdout: secondStdout } = await runCliWithoutGitignore(dir, [
"--cache",
"--write",
"--cache-strategy",
"metadata",
"*.js",
]);
expect(secondStdout.split("\n")).toEqual(
expect.arrayContaining([
expect.stringMatching(/^a\.js .+ms \(unchanged\) \(cached\)$/u),
expect.stringMatching(/^b\.js .+ms \(unchanged\) \(cached\)$/u),
]),
);
});
it("re-formats when a file has been updated.", async () => {
const cliArguments = [
"--cache",
"--write",
"--cache-strategy",
"metadata",
"*.js",
];
const { stdout: firstStdout } = await runCliWithoutGitignore(
dir,
cliArguments,
);
expect(firstStdout.split("\n")).toEqual(
expect.arrayContaining([
expect.stringMatching(/^a\.js .+ms$/u),
expect.stringMatching(/^b\.js .+ms \(unchanged\)$/u),
]),
);
// Update `a.js`
await fs.writeFile(path.join(dir, "a.js"), "const a = `a`;");
const { stdout: secondStdout } = await runCliWithoutGitignore(
dir,
cliArguments,
);
expect(secondStdout.split("\n")).toEqual(
// the cache of `b.js` is only available.
expect.arrayContaining([
expect.stringMatching(/^a\.js .+ms$/u),
expect.stringMatching(/^b\.js .+ms \(unchanged\) \(cached\)$/u),
]),
);
});
it("re-formats when timestamp has been updated", async () => {
const cliArguments = [
"--cache",
"--write",
"--cache-strategy",
"metadata",
"*.js",
];
const { stdout: firstStdout } = await runCliWithoutGitignore(
dir,
cliArguments,
);
expect(firstStdout.split("\n")).toEqual(
expect.arrayContaining([
expect.stringMatching(/^a\.js .+ms$/u),
expect.stringMatching(/^b\.js .+ms \(unchanged\)$/u),
]),
);
// update timestamp
const time = new Date();
await fs.utimes(path.join(dir, "a.js"), time, time);
const { stdout: secondStdout } = await runCliWithoutGitignore(
dir,
cliArguments,
);
expect(secondStdout.split("\n")).toEqual(
// the cache of `b.js` is only available.
expect.arrayContaining([
expect.stringMatching(/^a\.js .+ms$/u),
expect.stringMatching(/^b\.js .+ms \(unchanged\) \(cached\)$/u),
]),
);
});
it("re-formats when options has been updated.", async () => {
const { stdout: firstStdout } = await runCliWithoutGitignore(dir, [
"--cache",
"--write",
"--cache-strategy",
"metadata",
"*.js",
]);
expect(firstStdout.split("\n")).toEqual(
expect.arrayContaining([
expect.stringMatching(/^a\.js .+ms$/u),
expect.stringMatching(/^b\.js .+ms \(unchanged\)$/u),
]),
);
const { stdout: secondStdout } = await runCliWithoutGitignore(dir, [
"--cache",
"--cache-strategy",
"metadata",
"--write",
"--trailing-comma",
"all",
"*.js",
]);
expect(secondStdout.split("\n")).toEqual(
expect.arrayContaining([
expect.stringMatching(/^a\.js .+ms$/u),
expect.stringMatching(/^b\.js .+ms \(unchanged\)$/u),
]),
);
});
it("re-formats after execution without write.", async () => {
await runCliWithoutGitignore(dir, [
"--cache",
"--cache-strategy",
"metadata",
"*.js",
]);
const { stdout: secondStdout } = await runCliWithoutGitignore(dir, [
"--write",
"--cache",
"--cache-strategy",
"metadata",
"*.js",
]);
expect(secondStdout.split("\n")).toEqual(
expect.arrayContaining([
expect.stringMatching(/^a\.js .+ms$/u),
expect.stringMatching(/^b\.js .+ms \(unchanged\) \(cached\)$/u),
]),
);
});
it("re-formats when multiple cached files are updated.", async () => {
const cliArguments = [
"--write",
"--cache",
"--cache-strategy",
"metadata",
"*.js",
];
await runCliWithoutGitignore(dir, cliArguments);
// Update `a.js` to unformatted
await fs.writeFile(path.join(dir, "a.js"), "const a = `a`; ");
// Update `b.js` but still formatted
const time = new Date();
await fs.utimes(path.join(dir, "b.js"), time, time);
await runCliWithoutGitignore(dir, [
"--cache",
"--cache-strategy",
"metadata",
"*.js",
]);
const { stdout: thirdStdout } = await runCliWithoutGitignore(
dir,
cliArguments,
);
expect(thirdStdout.split("\n")).toEqual(
expect.arrayContaining([
expect.stringMatching(/^a\.js .+ms$/u),
expect.stringMatching(/^b\.js .+ms \(unchanged\) \(cached\)$/u),
]),
);
});
it("doesn't cache files when write error.", async () => {
const {
stdout: firstStdout,
stderr: firstStderr,
status: firstStatus,
} = await runCliWithoutGitignore(
dir,
["--write", "--cache", "--cache-strategy", "metadata", "*.js"],
{
mockWriteFileErrors: {
"a.js": "EACCES: permission denied.",
},
},
);
expect(firstStatus).toBe(2);
expect(firstStderr).toBe(
'[error] Unable to write file "a.js":\n' +
"[error] EACCES: permission denied. (mocked error)",
);
expect(firstStdout.split("\n")).toEqual(
expect.arrayContaining([
expect.stringMatching(/^a\.js .+ms$/u),
expect.stringMatching(/^b\.js .+ms \(unchanged\)$/u),
]),
);
const { stdout: secondStdout } = await runCliWithoutGitignore(dir, [
"--list-different",
"--cache",
"--cache-strategy",
"metadata",
"*.js",
]);
expect(secondStdout).toBe("a.js");
});
it("removes cache file when run Prettier without `--cache` option", async () => {
await runCliWithoutGitignore(dir, [
"--cache",
"--write",
"--cache-strategy",
"metadata",
"*.js",
]);
await expect(fs.stat(defaultCacheFile)).resolves.not.toThrow();
await runCliWithoutGitignore(dir, ["--write", "*.js"]);
await expect(fs.stat(defaultCacheFile)).rejects.toThrow();
});
});
describe("--cache-strategy content", () => {
it("creates default cache file named `node_modules/.cache/prettier/.prettier-cache`", async () => {
await expect(fs.stat(defaultCacheFile)).rejects.toHaveProperty(
"code",
"ENOENT",
);
await runCliWithoutGitignore(dir, [
"--cache",
"--cache-strategy",
"content",
"*.js",
]);
await expect(fs.stat(defaultCacheFile)).resolves.not.toThrow();
});
it("doesn't format when cache is available", async () => {
const cliArguments = [
"--cache",
"--cache-strategy",
"content",
"--write",
"*.js",
];
const { stdout: firstStdout } = await runCliWithoutGitignore(
dir,
cliArguments,
);
expect(firstStdout.split("\n")).toEqual(
expect.arrayContaining([
expect.stringMatching(/^a\.js .+ms$/u),
expect.stringMatching(/^b\.js .+ms \(unchanged\)$/u),
]),
);
const { stdout: secondStdout } = await runCliWithoutGitignore(
dir,
cliArguments,
);
expect(secondStdout.split("\n")).toEqual(
expect.arrayContaining([
expect.stringMatching(/^a\.js .+ms \(unchanged\) \(cached\)$/u),
expect.stringMatching(/^b\.js .+ms \(unchanged\) \(cached\)$/u),
]),
);
});
it("re-formats when a file has been updated.", async () => {
const cliArguments = [
"--cache",
"--cache-strategy",
"content",
"--write",
"*.js",
];
const { stdout: firstStdout } = await runCliWithoutGitignore(
dir,
cliArguments,
);
expect(firstStdout.split("\n")).toEqual(
expect.arrayContaining([
expect.stringMatching(/^a\.js .+ms$/u),
expect.stringMatching(/^b\.js .+ms \(unchanged\)$/u),
]),
);
// Update `a.js`
await fs.writeFile(path.join(dir, "a.js"), "const a = `a`;");
const { stdout: secondStdout } = await runCliWithoutGitignore(
dir,
cliArguments,
);
expect(secondStdout.split("\n")).toEqual(
// the cache of `b.js` is only available.
expect.arrayContaining([
expect.stringMatching(/^a\.js .+ms$/u),
expect.stringMatching(/^b\.js .+ms \(unchanged\) \(cached\)$/u),
]),
);
});
it("doesn't when timestamp has been updated", async () => {
const cliArguments = [
"--cache",
"--cache-strategy",
"content",
"--write",
"*.js",
];
const { stdout: firstStdout } = await runCliWithoutGitignore(
dir,
cliArguments,
);
expect(firstStdout.split("\n")).toEqual(
expect.arrayContaining([
expect.stringMatching(/^a\.js .+ms$/u),
expect.stringMatching(/^b\.js .+ms \(unchanged\)$/u),
]),
);
// update timestamp
const time = new Date();
await fs.utimes(path.join(dir, "a.js"), time, time);
const { stdout: secondStdout } = await runCliWithoutGitignore(
dir,
cliArguments,
);
expect(secondStdout.split("\n")).toEqual(
expect.arrayContaining([
expect.stringMatching(/^a\.js .+ms \(unchanged\) \(cached\)$/u),
expect.stringMatching(/^b\.js .+ms \(unchanged\) \(cached\)$/u),
]),
);
});
it("re-formats when options has been updated.", async () => {
const { stdout: firstStdout } = await runCliWithoutGitignore(dir, [
"--cache",
"--cache-strategy",
"content",
"--write",
"*.js",
]);
expect(firstStdout.split("\n")).toEqual(
expect.arrayContaining([
expect.stringMatching(/^a\.js .+ms$/u),
expect.stringMatching(/^b\.js .+ms \(unchanged\)$/u),
]),
);
const { stdout: secondStdout } = await runCliWithoutGitignore(dir, [
"--cache",
"--write",
"--cache-strategy",
"content",
"--trailing-comma",
"all",
"*.js",
]);
expect(secondStdout.split("\n")).toEqual(
expect.arrayContaining([
expect.stringMatching(/^a\.js .+ms$/u),
expect.stringMatching(/^b\.js .+ms \(unchanged\)$/u),
]),
);
});
it("re-formats after execution without write.", async () => {
await runCliWithoutGitignore(dir, [
"--cache",
"--cache-strategy",
"content",
"*.js",
]);
const { stdout: secondStdout } = await runCliWithoutGitignore(dir, [
"--write",
"--cache",
"--cache-strategy",
"content",
"*.js",
]);
expect(secondStdout.split("\n")).toEqual(
expect.arrayContaining([
expect.stringMatching(/^a\.js .+ms$/u),
expect.stringMatching(/^b\.js .+ms \(unchanged\) \(cached\)$/u),
]),
);
});
it("re-formats when multiple cached files are updated.", async () => {
const cliArguments = [
"--write",
"--cache",
"--cache-strategy",
"content",
"*.js",
];
await runCliWithoutGitignore(dir, cliArguments);
// Update `a.js` to unformatted
await fs.writeFile(path.join(dir, "a.js"), "const a = `a`; ");
// Update `b.js` but still formatted
const time = new Date();
await fs.utimes(path.join(dir, "b.js"), time, time);
await runCliWithoutGitignore(dir, [
"--cache",
"--cache-strategy",
"content",
"*.js",
]);
const { stdout: thirdStdout } = await runCliWithoutGitignore(
dir,
cliArguments,
);
expect(thirdStdout.split("\n")).toEqual(
expect.arrayContaining([
expect.stringMatching(/^a\.js .+ms$/u),
expect.stringMatching(/^b\.js .+ms \(unchanged\) \(cached\)$/u),
]),
);
});
it("doesn't cache files when write error.", async () => {
const {
stdout: firstStdout,
stderr: firstStderr,
status: firstStatus,
} = await runCliWithoutGitignore(
dir,
["--write", "--cache", "--cache-strategy", "content", "*.js"],
{
mockWriteFileErrors: {
"a.js": "EACCES: permission denied.",
},
},
);
expect(firstStatus).toBe(2);
expect(firstStderr).toBe(
'[error] Unable to write file "a.js":\n' +
"[error] EACCES: permission denied. (mocked error)",
);
expect(firstStdout.split("\n")).toEqual(
expect.arrayContaining([
expect.stringMatching(/^a\.js .+ms$/u),
expect.stringMatching(/^b\.js .+ms \(unchanged\)$/u),
]),
);
const { stdout: secondStdout } = await runCliWithoutGitignore(dir, [
"--list-different",
"--cache",
"--cache-strategy",
"content",
"*.js",
]);
expect(secondStdout).toBe("a.js");
});
it("removes cache file when run Prettier without `--cache` option", async () => {
await runCliWithoutGitignore(dir, ["--cache", "--write", "*.js"]);
await expect(fs.stat(defaultCacheFile)).resolves.not.toThrow();
await runCliWithoutGitignore(dir, ["--write", "*.js"]);
await expect(fs.stat(defaultCacheFile)).rejects.toThrow();
});
});
describe("--cache-location", () => {
it("doesn't create default cache file when `--cache-location` exists", async () => {
await expect(fs.stat(defaultCacheFile)).rejects.toHaveProperty(
"code",
"ENOENT",
);
await runCliWithoutGitignore(dir, [
"--cache",
"--cache-location",
nonDefaultCacheFileName,
"*.js",
]);
await expect(fs.stat(defaultCacheFile)).rejects.toHaveProperty(
"code",
"ENOENT",
);
});
it("throws error for invalid JSON file", async () => {
const { stderr } = await runCliWithoutGitignore(dir, [
"--cache",
"--cache-location",
"a.js",
"*.js",
]);
expect(stderr.trim()).toEqual(
expect.stringMatching(/\[error\] '.+' isn't a valid JSON file/u),
);
});
describe("file", () => {
it("creates the cache file at location specified by `--cache-location`", async () => {
await expect(fs.stat(nonDefaultCacheFilePath)).rejects.toHaveProperty(
"code",
"ENOENT",
);
await runCliWithoutGitignore(dir, [
"--cache",
"--cache-location",
nonDefaultCacheFileName,
"*.js",
]);
await expect(fs.stat(nonDefaultCacheFilePath)).resolves.not.toThrow();
});
it("does'nt format when cache is available", async () => {
const cliArguments = [
"--cache",
"--write",
"--cache-location",
nonDefaultCacheFileName,
"*.js",
];
const { stdout: firstStdout } = await runCliWithoutGitignore(
dir,
cliArguments,
);
expect(firstStdout.split("\n")).toEqual(
expect.arrayContaining([
expect.stringMatching(/^a\.js .+ms$/u),
expect.stringMatching(/^b\.js .+ms \(unchanged\)$/u),
]),
);
const { stdout: secondStdout } = await runCliWithoutGitignore(
dir,
cliArguments,
);
expect(secondStdout.split("\n")).toEqual(
expect.arrayContaining([
expect.stringMatching(/^a\.js .+ms \(unchanged\) \(cached\)$/u),
expect.stringMatching(/^b\.js .+ms \(unchanged\) \(cached\)$/u),
]),
);
});
});
});
});