aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/packages/create-turbo/__tests__
diff options
context:
space:
mode:
author简律纯 <hsiangnianian@outlook.com>2023-04-28 01:36:44 +0800
committer简律纯 <hsiangnianian@outlook.com>2023-04-28 01:36:44 +0800
commitdd84b9d64fb98746a230cd24233ff50a562c39c9 (patch)
treeb583261ef00b3afe72ec4d6dacb31e57779a6faf /packages/create-turbo/__tests__
parent0b46fcd72ac34382387b2bcf9095233efbcc52f4 (diff)
downloadHydroRoll-dd84b9d64fb98746a230cd24233ff50a562c39c9.tar.gz
HydroRoll-dd84b9d64fb98746a230cd24233ff50a562c39c9.zip
Diffstat (limited to 'packages/create-turbo/__tests__')
-rw-r--r--packages/create-turbo/__tests__/examples.test.ts134
-rw-r--r--packages/create-turbo/__tests__/git.test.ts239
-rw-r--r--packages/create-turbo/__tests__/index.test.ts90
-rw-r--r--packages/create-turbo/__tests__/isFolderEmpty.test.ts41
-rw-r--r--packages/create-turbo/__tests__/isWritable.test.ts35
-rw-r--r--packages/create-turbo/__tests__/test-utils.ts34
6 files changed, 573 insertions, 0 deletions
diff --git a/packages/create-turbo/__tests__/examples.test.ts b/packages/create-turbo/__tests__/examples.test.ts
new file mode 100644
index 0000000..20d4464
--- /dev/null
+++ b/packages/create-turbo/__tests__/examples.test.ts
@@ -0,0 +1,134 @@
+import got from "got";
+import * as Got from "got";
+import { isUrlOk, getRepoInfo, hasRepo } from "../src/utils/examples";
+
+jest.mock("got", () => ({
+ __esModule: true,
+ ...jest.requireActual("got"),
+}));
+
+describe("examples", () => {
+ describe("isUrlOk", () => {
+ it("returns true if url returns 200", async () => {
+ const mockGot = jest
+ .spyOn(got, "head")
+ .mockReturnValue({ statusCode: 200 } as any);
+
+ const url = "https://github.com/vercel/turbo/";
+ const result = await isUrlOk(url);
+ expect(result).toBe(true);
+
+ expect(mockGot).toHaveBeenCalledWith(url);
+ mockGot.mockRestore();
+ });
+
+ it("returns false if url returns status != 200", async () => {
+ const mockGot = jest
+ .spyOn(got, "head")
+ .mockReturnValue({ statusCode: 401 } as any);
+
+ const url = "https://not-github.com/vercel/turbo/";
+ const result = await isUrlOk(url);
+ expect(result).toBe(false);
+
+ expect(mockGot).toHaveBeenCalledWith(url);
+ mockGot.mockRestore();
+ });
+ });
+
+ describe("getRepoInfo", () => {
+ test.each([
+ {
+ repoUrl: "https://github.com/vercel/turbo/",
+ examplePath: undefined,
+ defaultBranch: "main",
+ expectBranchLookup: true,
+ expected: {
+ username: "vercel",
+ name: "turbo",
+ branch: "main",
+ filePath: "",
+ },
+ },
+ {
+ repoUrl:
+ "https://github.com/vercel/turbo/tree/canary/examples/kitchen-sink",
+ examplePath: undefined,
+ defaultBranch: "canary",
+ expectBranchLookup: false,
+ expected: {
+ username: "vercel",
+ name: "turbo",
+ branch: "canary",
+ filePath: "examples/kitchen-sink",
+ },
+ },
+ {
+ repoUrl: "https://github.com/vercel/turbo/tree/tek/test-branch/",
+ examplePath: "examples/basic",
+ defaultBranch: "canary",
+ expectBranchLookup: false,
+ expected: {
+ username: "vercel",
+ name: "turbo",
+ branch: "tek/test-branch",
+ filePath: "examples/basic",
+ },
+ },
+ ])(
+ "retrieves repo info for $repoUrl and $examplePath",
+ async ({
+ repoUrl,
+ examplePath,
+ defaultBranch,
+ expectBranchLookup,
+ expected,
+ }) => {
+ const mockGot = jest.spyOn(Got, "default").mockReturnValue({
+ body: JSON.stringify({ default_branch: defaultBranch }),
+ } as any);
+
+ const url = new URL(repoUrl);
+ const result = await getRepoInfo(url, examplePath);
+ expect(result).toMatchObject(expected);
+
+ if (result && expectBranchLookup) {
+ expect(mockGot).toHaveBeenCalledWith(
+ `https://api.github.com/repos/${result.username}/${result.name}`
+ );
+ }
+
+ mockGot.mockRestore();
+ }
+ );
+ });
+
+ describe("hasRepo", () => {
+ test.each([
+ {
+ repoInfo: {
+ username: "vercel",
+ name: "turbo",
+ branch: "main",
+ filePath: "",
+ },
+ expected: true,
+ expectedUrl:
+ "https://api.github.com/repos/vercel/turbo/contents/package.json?ref=main",
+ },
+ ])(
+ "checks repo at $expectedUrl",
+ async ({ expected, repoInfo, expectedUrl }) => {
+ const mockGot = jest
+ .spyOn(got, "head")
+ .mockReturnValue({ statusCode: 200 } as any);
+
+ const result = await hasRepo(repoInfo);
+ expect(result).toBe(expected);
+
+ expect(mockGot).toHaveBeenCalledWith(expectedUrl);
+ mockGot.mockRestore();
+ }
+ );
+ });
+});
diff --git a/packages/create-turbo/__tests__/git.test.ts b/packages/create-turbo/__tests__/git.test.ts
new file mode 100644
index 0000000..27ac118
--- /dev/null
+++ b/packages/create-turbo/__tests__/git.test.ts
@@ -0,0 +1,239 @@
+import path from "path";
+import {
+ DEFAULT_IGNORE,
+ GIT_REPO_COMMAND,
+ HG_REPO_COMMAND,
+ isInGitRepository,
+ isInMercurialRepository,
+ tryGitInit,
+} from "../src/utils/git";
+import childProcess from "child_process";
+import { setupTestFixtures } from "@turbo/test-utils";
+
+describe("git", () => {
+ // just to make sure this doesn't get lost
+ it("default .gitignore includes .turbo", async () => {
+ expect(DEFAULT_IGNORE).toContain(".turbo");
+ });
+
+ describe("isInGitRepository", () => {
+ it("returns true when in a repo", async () => {
+ const mockExecSync = jest
+ .spyOn(childProcess, "execSync")
+ .mockReturnValue("true");
+
+ const result = isInGitRepository();
+ expect(result).toBe(true);
+
+ expect(mockExecSync).toHaveBeenCalledWith(GIT_REPO_COMMAND, {
+ stdio: "ignore",
+ });
+ mockExecSync.mockRestore();
+ });
+
+ it("returns false when not in a repo", async () => {
+ const mockExecSync = jest
+ .spyOn(childProcess, "execSync")
+ .mockImplementation(() => {
+ throw new Error(
+ "fatal: not a git repository (or any of the parent directories): .git"
+ );
+ });
+
+ const result = isInGitRepository();
+ expect(result).toBe(false);
+
+ expect(mockExecSync).toHaveBeenCalledWith(GIT_REPO_COMMAND, {
+ stdio: "ignore",
+ });
+ mockExecSync.mockRestore();
+ });
+
+ it("returns false on error", async () => {
+ const mockExecSync = jest
+ .spyOn(childProcess, "execSync")
+ .mockImplementation(() => {
+ throw new Error("unknown error");
+ });
+
+ const result = isInGitRepository();
+ expect(result).toBe(false);
+
+ expect(mockExecSync).toHaveBeenCalledWith(GIT_REPO_COMMAND, {
+ stdio: "ignore",
+ });
+ mockExecSync.mockRestore();
+ });
+ });
+
+ describe("isInMercurialRepository", () => {
+ it("returns true when in a repo", async () => {
+ const mockExecSync = jest
+ .spyOn(childProcess, "execSync")
+ .mockReturnValue("true");
+
+ const result = isInMercurialRepository();
+ expect(result).toBe(true);
+
+ expect(mockExecSync).toHaveBeenCalledWith(HG_REPO_COMMAND, {
+ stdio: "ignore",
+ });
+ mockExecSync.mockRestore();
+ });
+
+ it("returns false when not in a repo", async () => {
+ const mockExecSync = jest
+ .spyOn(childProcess, "execSync")
+ .mockImplementation(() => {
+ throw new Error("abort: no repository found (.hg not found)");
+ });
+
+ const result = isInMercurialRepository();
+ expect(result).toBe(false);
+
+ expect(mockExecSync).toHaveBeenCalledWith(HG_REPO_COMMAND, {
+ stdio: "ignore",
+ });
+ mockExecSync.mockRestore();
+ });
+
+ it("returns false on error", async () => {
+ const mockExecSync = jest
+ .spyOn(childProcess, "execSync")
+ .mockImplementation(() => {
+ throw new Error("unknown error");
+ });
+
+ const result = isInMercurialRepository();
+ expect(result).toBe(false);
+
+ expect(mockExecSync).toHaveBeenCalledWith(HG_REPO_COMMAND, {
+ stdio: "ignore",
+ });
+ mockExecSync.mockRestore();
+ });
+ });
+
+ describe("tryGitInit", () => {
+ const { useFixture } = setupTestFixtures({
+ directory: path.join(__dirname, "../"),
+ });
+
+ it("inits a repo succesfully", async () => {
+ const { root } = useFixture({ fixture: `git` });
+ const mockExecSync = jest
+ .spyOn(childProcess, "execSync")
+ .mockReturnValueOnce("git version 2.38.1")
+ .mockImplementationOnce(() => {
+ throw new Error(
+ "fatal: not a git repository (or any of the parent directories): .git"
+ );
+ })
+ .mockImplementationOnce(() => {
+ throw new Error("abort: no repository found (.hg not found)");
+ })
+ .mockReturnValue("success");
+
+ const result = tryGitInit(root, "test commit");
+ expect(result).toBe(true);
+
+ const calls = [
+ "git --version",
+ "git init",
+ "git checkout -b main",
+ "git add -A",
+ 'git commit -m "test commit"',
+ ];
+ expect(mockExecSync).toHaveBeenCalledTimes(calls.length + 2);
+ calls.forEach((call) => {
+ expect(mockExecSync).toHaveBeenCalledWith(call, {
+ stdio: "ignore",
+ });
+ });
+ mockExecSync.mockRestore();
+ });
+
+ it("skips init if already in a repo", async () => {
+ const { root } = useFixture({ fixture: `git` });
+ const mockExecSync = jest
+ .spyOn(childProcess, "execSync")
+ .mockReturnValueOnce("git version 2.38.1")
+ .mockReturnValueOnce("true")
+ .mockReturnValue("success");
+
+ const result = tryGitInit(root, "test commit");
+ expect(result).toBe(false);
+
+ const calls = ["git --version"];
+
+ // 1 call for git --version, 1 call for isInGitRepository
+ expect(mockExecSync).toHaveBeenCalledTimes(calls.length + 1);
+ calls.forEach((call) => {
+ expect(mockExecSync).toHaveBeenCalledWith(call, {
+ stdio: "ignore",
+ });
+ });
+ mockExecSync.mockRestore();
+ });
+
+ it("returns false on unexpected error", async () => {
+ const { root } = useFixture({ fixture: `git` });
+ const mockExecSync = jest
+ .spyOn(childProcess, "execSync")
+ .mockImplementationOnce(() => {
+ throw new Error("fatal: unknown command git");
+ });
+
+ const result = tryGitInit(root, "test commit");
+ expect(result).toBe(false);
+
+ const calls = ["git --version"];
+
+ expect(mockExecSync).toHaveBeenCalledTimes(calls.length);
+ calls.forEach((call) => {
+ expect(mockExecSync).toHaveBeenCalledWith(call, {
+ stdio: "ignore",
+ });
+ });
+ mockExecSync.mockRestore();
+ });
+
+ it("cleans up from partial init on failure", async () => {
+ const { root } = useFixture({ fixture: `git` });
+ const mockExecSync = jest
+ .spyOn(childProcess, "execSync")
+ .mockReturnValueOnce("git version 2.38.1")
+ .mockImplementationOnce(() => {
+ throw new Error(
+ "fatal: not a git repository (or any of the parent directories): .git"
+ );
+ })
+ .mockImplementationOnce(() => {
+ throw new Error("abort: no repository found (.hg not found)");
+ })
+ .mockReturnValueOnce("success")
+ .mockReturnValueOnce("success")
+ .mockImplementationOnce(() => {
+ throw new Error("fatal: could not add files");
+ });
+
+ const result = tryGitInit(root, "test commit");
+ expect(result).toBe(false);
+
+ const calls = [
+ "git --version",
+ "git init",
+ "git checkout -b main",
+ "git add -A",
+ ];
+
+ expect(mockExecSync).toHaveBeenCalledTimes(calls.length + 2);
+ calls.forEach((call) => {
+ expect(mockExecSync).toHaveBeenCalledWith(call, {
+ stdio: "ignore",
+ });
+ });
+ mockExecSync.mockRestore();
+ });
+ });
+});
diff --git a/packages/create-turbo/__tests__/index.test.ts b/packages/create-turbo/__tests__/index.test.ts
new file mode 100644
index 0000000..641b193
--- /dev/null
+++ b/packages/create-turbo/__tests__/index.test.ts
@@ -0,0 +1,90 @@
+import path from "path";
+import chalk from "chalk";
+import childProcess from "child_process";
+import { setupTestFixtures, spyConsole } from "@turbo/test-utils";
+import { create } from "../src/commands/create";
+import type { CreateCommandArgument } from "../src/commands/create/types";
+import { turboGradient } from "../src/logger";
+import type { PackageManager } from "@turbo/workspaces";
+
+// imports for mocks
+import * as createProject from "../src/commands/create/createProject";
+import * as turboWorkspaces from "@turbo/workspaces";
+import { getWorkspaceDetailsMockReturnValue } from "./test-utils";
+
+jest.mock("@turbo/workspaces", () => ({
+ __esModule: true,
+ ...jest.requireActual("@turbo/workspaces"),
+}));
+
+describe("create-turbo", () => {
+ const { useFixture } = setupTestFixtures({
+ directory: path.join(__dirname, "../"),
+ });
+
+ const mockConsole = spyConsole();
+
+ test.each<{ packageManager: PackageManager }>([
+ { packageManager: "yarn" },
+ { packageManager: "npm" },
+ { packageManager: "pnpm" },
+ ])(
+ "outputs expected console messages when using $packageManager",
+ async ({ packageManager }) => {
+ const { root } = useFixture({ fixture: `create-turbo` });
+
+ const availableScripts = ["build", "test", "dev"];
+
+ const mockCreateProject = jest
+ .spyOn(createProject, "createProject")
+ .mockResolvedValue({
+ cdPath: "",
+ hasPackageJson: true,
+ availableScripts,
+ });
+
+ const mockGetWorkspaceDetails = jest
+ .spyOn(turboWorkspaces, "getWorkspaceDetails")
+ .mockResolvedValue(
+ getWorkspaceDetailsMockReturnValue({
+ root,
+ packageManager,
+ })
+ );
+
+ const mockExecSync = jest
+ .spyOn(childProcess, "execSync")
+ .mockImplementation(() => {
+ return "success";
+ });
+
+ await create(
+ root as CreateCommandArgument,
+ packageManager as CreateCommandArgument,
+ {
+ skipInstall: true,
+ example: "default",
+ }
+ );
+
+ const expected = `${chalk.bold(
+ turboGradient(">>> Success!")
+ )} Created a new Turborepo at "${path.relative(process.cwd(), root)}".`;
+
+ expect(mockConsole.log).toHaveBeenCalledWith(expected);
+ expect(mockConsole.log).toHaveBeenCalledWith(
+ "Inside that directory, you can run several commands:"
+ );
+
+ availableScripts.forEach((script) => {
+ expect(mockConsole.log).toHaveBeenCalledWith(
+ chalk.cyan(` ${packageManager} run ${script}`)
+ );
+ });
+
+ mockCreateProject.mockRestore();
+ mockGetWorkspaceDetails.mockRestore();
+ mockExecSync.mockRestore();
+ }
+ );
+});
diff --git a/packages/create-turbo/__tests__/isFolderEmpty.test.ts b/packages/create-turbo/__tests__/isFolderEmpty.test.ts
new file mode 100644
index 0000000..66b2310
--- /dev/null
+++ b/packages/create-turbo/__tests__/isFolderEmpty.test.ts
@@ -0,0 +1,41 @@
+import fs from "fs-extra";
+import path from "path";
+import { isFolderEmpty } from "../src/utils/isFolderEmpty";
+import { setupTestFixtures } from "@turbo/test-utils";
+
+describe("isFolderEmpty", () => {
+ const { useFixture } = setupTestFixtures({
+ directory: path.join(__dirname, "../"),
+ });
+
+ it("correctly identifies an empty directory", async () => {
+ const { root } = useFixture({ fixture: `is-folder-empty` });
+ const result = isFolderEmpty(root);
+ expect(result.isEmpty).toEqual(true);
+ expect(result.conflicts).toEqual([]);
+ });
+
+ it("correctly identifies a directory with non-conflicting files", async () => {
+ const { root } = useFixture({ fixture: `is-folder-empty` });
+ fs.writeFileSync(path.join(root, "LICENSE"), "MIT");
+ const result = isFolderEmpty(root);
+ expect(result.isEmpty).toEqual(true);
+ expect(result.conflicts).toEqual([]);
+ });
+
+ it("correctly identifies a directory non-conflicting files (intelliJ)", async () => {
+ const { root } = useFixture({ fixture: `is-folder-empty` });
+ fs.writeFileSync(path.join(root, "intellij-idea-config.iml"), "{}");
+ const result = isFolderEmpty(root);
+ expect(result.isEmpty).toEqual(true);
+ expect(result.conflicts).toEqual([]);
+ });
+
+ it("correctly identifies a directory conflicting files", async () => {
+ const { root } = useFixture({ fixture: `is-folder-empty` });
+ fs.writeFileSync(path.join(root, "README.md"), "my cool project");
+ const result = isFolderEmpty(root);
+ expect(result.isEmpty).toEqual(false);
+ expect(result.conflicts).toEqual(["README.md"]);
+ });
+});
diff --git a/packages/create-turbo/__tests__/isWritable.test.ts b/packages/create-turbo/__tests__/isWritable.test.ts
new file mode 100644
index 0000000..b06670b
--- /dev/null
+++ b/packages/create-turbo/__tests__/isWritable.test.ts
@@ -0,0 +1,35 @@
+import path from "path";
+import { isWriteable } from "../src/utils/isWriteable";
+import { setupTestFixtures } from "@turbo/test-utils";
+import fs from "fs-extra";
+
+describe("isWriteable", () => {
+ const { useFixture } = setupTestFixtures({
+ directory: path.join(__dirname, "../"),
+ });
+
+ it("correctly identifies a writeable directory", async () => {
+ const { root } = useFixture({ fixture: `is-writeable` });
+ const result = await isWriteable(root);
+ expect(result).toEqual(true);
+ });
+
+ it("correctly identifies a non-writeable directory", async () => {
+ const { root } = useFixture({ fixture: `is-writeable` });
+ const result = await isWriteable(path.join(root, "does-not-exist"));
+ expect(result).toEqual(false);
+ });
+
+ it("returns false on unexpected failure", async () => {
+ const { root } = useFixture({ fixture: `is-writeable` });
+ const mockFsAccess = jest
+ .spyOn(fs, "access")
+ .mockRejectedValue(new Error("unknown error"));
+
+ const result = await isWriteable(root);
+ expect(result).toEqual(false);
+ expect(mockFsAccess).toHaveBeenCalledWith(root, fs.constants.W_OK);
+
+ mockFsAccess.mockRestore();
+ });
+});
diff --git a/packages/create-turbo/__tests__/test-utils.ts b/packages/create-turbo/__tests__/test-utils.ts
new file mode 100644
index 0000000..fa6c204
--- /dev/null
+++ b/packages/create-turbo/__tests__/test-utils.ts
@@ -0,0 +1,34 @@
+import path from "path";
+import { PackageManager } from "@turbo/workspaces";
+
+export function getWorkspaceDetailsMockReturnValue({
+ root,
+ packageManager = "npm",
+}: {
+ root: string;
+ packageManager: PackageManager;
+}) {
+ return {
+ name: "mock-project",
+ packageManager,
+ paths: {
+ root,
+ packageJson: path.join(root, "package.json"),
+ lockfile: path.join(root, "yarn.lock"),
+ nodeModules: path.join(root, "node_modules"),
+ },
+ workspaceData: {
+ globs: ["packages/*"],
+ workspaces: [
+ {
+ name: "packages/mock-package",
+ paths: {
+ root: path.join(root, "packages/mock-package"),
+ packageJson: path.join(root, "packages/mock-package/package.json"),
+ nodeModules: path.join(root, "packages/mock-package/node_modules"),
+ },
+ },
+ ],
+ },
+ };
+}