-
Notifications
You must be signed in to change notification settings - Fork 1
π§ͺ add unit tests for milady-root utility #480
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,181 @@ | ||||||||||||||||||||||||||||||||||||||
| import fsSync from "node:fs"; | ||||||||||||||||||||||||||||||||||||||
| import fsAsync from "node:fs/promises"; | ||||||||||||||||||||||||||||||||||||||
| import path from "node:path"; | ||||||||||||||||||||||||||||||||||||||
| import { fileURLToPath } from "node:url"; | ||||||||||||||||||||||||||||||||||||||
| import { beforeEach, describe, expect, it, vi } from "vitest"; | ||||||||||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||||||||||
| resolveMiladyPackageRoot, | ||||||||||||||||||||||||||||||||||||||
| resolveMiladyPackageRootSync, | ||||||||||||||||||||||||||||||||||||||
| } from "./milady-root.ts"; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| vi.mock("node:fs", () => ({ | ||||||||||||||||||||||||||||||||||||||
| default: { | ||||||||||||||||||||||||||||||||||||||
| readFileSync: vi.fn(), | ||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||
| })); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| vi.mock("node:fs/promises", () => ({ | ||||||||||||||||||||||||||||||||||||||
| default: { | ||||||||||||||||||||||||||||||||||||||
| readFile: vi.fn(), | ||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||
| })); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| vi.mock("node:url", () => ({ | ||||||||||||||||||||||||||||||||||||||
| fileURLToPath: vi.fn(), | ||||||||||||||||||||||||||||||||||||||
| })); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| describe("milady-root", () => { | ||||||||||||||||||||||||||||||||||||||
| beforeEach(() => { | ||||||||||||||||||||||||||||||||||||||
| vi.clearAllMocks(); | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| describe("resolveMiladyPackageRoot (async)", () => { | ||||||||||||||||||||||||||||||||||||||
| it("returns null if no candidates provided", async () => { | ||||||||||||||||||||||||||||||||||||||
| const result = await resolveMiladyPackageRoot({}); | ||||||||||||||||||||||||||||||||||||||
| expect(result).toBeNull(); | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| it("resolves to directory with package.json name 'milady' using cwd", async () => { | ||||||||||||||||||||||||||||||||||||||
| const mockCwd = "/home/user/project/sub/dir"; | ||||||||||||||||||||||||||||||||||||||
| const expectedRoot = "/home/user/project"; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| vi.mocked(fsAsync.readFile).mockImplementation(async (filePath) => { | ||||||||||||||||||||||||||||||||||||||
| if (filePath === path.join(expectedRoot, "package.json")) { | ||||||||||||||||||||||||||||||||||||||
| return JSON.stringify({ name: "milady" }); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| return JSON.stringify({ name: "something-else" }); | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| const result = await resolveMiladyPackageRoot({ cwd: mockCwd }); | ||||||||||||||||||||||||||||||||||||||
| expect(result).toBe(expectedRoot); | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| it("resolves using argv1 when inside node_modules/.bin", async () => { | ||||||||||||||||||||||||||||||||||||||
| const mockArgv1 = "/app/node_modules/.bin/run-milady"; | ||||||||||||||||||||||||||||||||||||||
| const expectedRoot = "/app"; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| vi.mocked(fsAsync.readFile).mockImplementation(async (filePath) => { | ||||||||||||||||||||||||||||||||||||||
| if (filePath === path.join(expectedRoot, "package.json")) { | ||||||||||||||||||||||||||||||||||||||
| return JSON.stringify({ name: "milady" }); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| throw new Error("ENOENT"); | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| const result = await resolveMiladyPackageRoot({ argv1: mockArgv1 }); | ||||||||||||||||||||||||||||||||||||||
| expect(result).toBe(expectedRoot); | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| it("resolves using moduleUrl", async () => { | ||||||||||||||||||||||||||||||||||||||
| const mockModuleUrl = "file:///app/src/utils/index.ts"; | ||||||||||||||||||||||||||||||||||||||
| const mockFileUrlPath = "/app/src/utils/index.ts"; | ||||||||||||||||||||||||||||||||||||||
| vi.mocked(fileURLToPath).mockReturnValue(mockFileUrlPath); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| const expectedRoot = "/app"; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| vi.mocked(fsAsync.readFile).mockImplementation(async (filePath) => { | ||||||||||||||||||||||||||||||||||||||
| if (filePath === path.join(expectedRoot, "package.json")) { | ||||||||||||||||||||||||||||||||||||||
| return JSON.stringify({ name: "milady" }); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| return JSON.stringify({ name: "not-milady" }); | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| const result = await resolveMiladyPackageRoot({ | ||||||||||||||||||||||||||||||||||||||
| moduleUrl: mockModuleUrl, | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
| expect(result).toBe(expectedRoot); | ||||||||||||||||||||||||||||||||||||||
| expect(fileURLToPath).toHaveBeenCalledWith(mockModuleUrl); | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| it("returns null if maxDepth is exceeded", async () => { | ||||||||||||||||||||||||||||||||||||||
| const mockCwd = "/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p"; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| vi.mocked(fsAsync.readFile).mockImplementation(async (filePath) => { | ||||||||||||||||||||||||||||||||||||||
| if (filePath === path.join("/", "package.json")) { | ||||||||||||||||||||||||||||||||||||||
| return JSON.stringify({ name: "milady" }); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| return JSON.stringify({ name: "not-milady" }); | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| const result = await resolveMiladyPackageRoot({ cwd: mockCwd }); | ||||||||||||||||||||||||||||||||||||||
| expect(result).toBeNull(); | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| it("handles parsing errors gracefully and continues", async () => { | ||||||||||||||||||||||||||||||||||||||
| const mockCwd = "/home/user/project/sub"; | ||||||||||||||||||||||||||||||||||||||
| const expectedRoot = "/home/user/project"; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| vi.mocked(fsAsync.readFile).mockImplementation(async (filePath) => { | ||||||||||||||||||||||||||||||||||||||
| if (filePath === path.join(mockCwd, "package.json")) { | ||||||||||||||||||||||||||||||||||||||
| return "invalid-json"; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| if (filePath === path.join(expectedRoot, "package.json")) { | ||||||||||||||||||||||||||||||||||||||
| return JSON.stringify({ name: "milady" }); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| throw new Error("ENOENT"); | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| const result = await resolveMiladyPackageRoot({ cwd: mockCwd }); | ||||||||||||||||||||||||||||||||||||||
| expect(result).toBe(expectedRoot); | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| it("returns null if none of the ancestor directories contain milady package.json", async () => { | ||||||||||||||||||||||||||||||||||||||
| const mockCwd = "/home/user/project"; | ||||||||||||||||||||||||||||||||||||||
| vi.mocked(fsAsync.readFile).mockImplementation(async () => { | ||||||||||||||||||||||||||||||||||||||
| return JSON.stringify({ name: "not-milady" }); | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| const result = await resolveMiladyPackageRoot({ cwd: mockCwd }); | ||||||||||||||||||||||||||||||||||||||
| expect(result).toBeNull(); | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| describe("resolveMiladyPackageRootSync (sync)", () => { | ||||||||||||||||||||||||||||||||||||||
| it("returns null if no candidates provided", () => { | ||||||||||||||||||||||||||||||||||||||
| const result = resolveMiladyPackageRootSync({}); | ||||||||||||||||||||||||||||||||||||||
| expect(result).toBeNull(); | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| it("resolves to directory with package.json name 'milady' using cwd", () => { | ||||||||||||||||||||||||||||||||||||||
| const mockCwd = "/home/user/project/sub/dir"; | ||||||||||||||||||||||||||||||||||||||
| const expectedRoot = "/home/user/project"; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| vi.mocked(fsSync.readFileSync).mockImplementation((filePath) => { | ||||||||||||||||||||||||||||||||||||||
| if (filePath === path.join(expectedRoot, "package.json")) { | ||||||||||||||||||||||||||||||||||||||
| return JSON.stringify({ name: "milady" }); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| return JSON.stringify({ name: "something-else" }); | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| const result = resolveMiladyPackageRootSync({ cwd: mockCwd }); | ||||||||||||||||||||||||||||||||||||||
| expect(result).toBe(expectedRoot); | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| it("handles parsing errors gracefully and continues", () => { | ||||||||||||||||||||||||||||||||||||||
| const mockCwd = "/home/user/project/sub"; | ||||||||||||||||||||||||||||||||||||||
| const expectedRoot = "/home/user/project"; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| vi.mocked(fsSync.readFileSync).mockImplementation((filePath) => { | ||||||||||||||||||||||||||||||||||||||
| if (filePath === path.join(mockCwd, "package.json")) { | ||||||||||||||||||||||||||||||||||||||
| throw new Error("ENOENT"); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| if (filePath === path.join(expectedRoot, "package.json")) { | ||||||||||||||||||||||||||||||||||||||
| return JSON.stringify({ name: "milady" }); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| throw new Error("ENOENT"); | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+157
to
+165
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The test case "handles parsing errors gracefully and continues" for the synchronous variant currently simulates a file system error (
Suggested change
|
||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| const result = resolveMiladyPackageRootSync({ cwd: mockCwd }); | ||||||||||||||||||||||||||||||||||||||
| expect(result).toBe(expectedRoot); | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| it("returns null when package.json does not contain a string name", () => { | ||||||||||||||||||||||||||||||||||||||
| const mockCwd = "/home/user/project/sub"; | ||||||||||||||||||||||||||||||||||||||
| vi.mocked(fsSync.readFileSync).mockImplementation((_filePath) => { | ||||||||||||||||||||||||||||||||||||||
| return JSON.stringify({ name: 123 }); | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| const result = resolveMiladyPackageRootSync({ cwd: mockCwd }); | ||||||||||||||||||||||||||||||||||||||
| expect(result).toBeNull(); | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The tests use hardcoded POSIX-style path strings (e.g.,
"/home/user/project") which are then compared against paths generated bypath.joinandpath.resolvein the mock implementations. This will lead to test failures on Windows due to different path separators (\vs/) and drive letters. It is recommended to wrap these path strings inpath.resolve()to ensure they are correctly normalized for the current platform. This pattern should be applied to all similar path definitions in this file.