|
|
|
@@ -243,7 +243,7 @@ describe("AddLaneModal — picking an existing branch (issue #34)", () => {
|
|
|
|
|
await user.click(screen.getByRole("button", { name: "Existing branch" }));
|
|
|
|
|
const select = await screen.findByLabelText("Branch to check out");
|
|
|
|
|
await user.selectOptions(select, "feat/other");
|
|
|
|
|
await user.click(screen.getByRole("button", { name: "Add lane" }));
|
|
|
|
|
await user.click(screen.getByRole("button", { name: "Add workspace" }));
|
|
|
|
|
|
|
|
|
|
await waitFor(() =>
|
|
|
|
|
expect(api.lanes.worktree).toHaveBeenCalledWith(
|
|
|
|
@@ -287,7 +287,7 @@ describe("AddLaneModal — picking an existing branch (issue #34)", () => {
|
|
|
|
|
"Automatic — named from the title"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
await user.click(screen.getByRole("button", { name: "Add lane" }));
|
|
|
|
|
await user.click(screen.getByRole("button", { name: "Add workspace" }));
|
|
|
|
|
await waitFor(() =>
|
|
|
|
|
expect(api.lanes.worktree).toHaveBeenCalledWith(
|
|
|
|
|
expect.objectContaining({ branch: undefined, base: "main" })
|
|
|
|
@@ -301,7 +301,7 @@ describe("AddLaneModal — worktree mode (default)", () => {
|
|
|
|
|
// the server name it" (issue #34).
|
|
|
|
|
it("disables confirm until a repo, a title and a resolved branch list are all present", () => {
|
|
|
|
|
renderModal();
|
|
|
|
|
expect(screen.getByRole("button", { name: "Add lane" })).toBeDisabled();
|
|
|
|
|
expect(screen.getByRole("button", { name: "Add workspace" })).toBeDisabled();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("looks up branches once the repo path settles, and shows them as a picker", async () => {
|
|
|
|
@@ -337,7 +337,7 @@ describe("AddLaneModal — worktree mode (default)", () => {
|
|
|
|
|
await waitFor(() => expect(api.lanes.branches).toHaveBeenCalled());
|
|
|
|
|
expect(await screen.findByText(/Not a git repository/)).toBeInTheDocument();
|
|
|
|
|
expect(screen.queryByLabelText("Branch to fork from")).toBeNull();
|
|
|
|
|
expect(screen.getByRole("button", { name: "Add lane" })).toBeDisabled();
|
|
|
|
|
expect(screen.getByRole("button", { name: "Add workspace" })).toBeDisabled();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("refuses a worktree of a repo with no commits and steers to Repo mode", async () => {
|
|
|
|
@@ -355,11 +355,11 @@ describe("AddLaneModal — worktree mode (default)", () => {
|
|
|
|
|
expect(await screen.findByText(/commit first, then publish/i)).toBeInTheDocument();
|
|
|
|
|
await user.type(screen.getByLabelText("Title"), "demo");
|
|
|
|
|
// There is nothing to branch from, so the worktree cannot be created.
|
|
|
|
|
expect(screen.getByRole("button", { name: "Add lane" })).toBeDisabled();
|
|
|
|
|
expect(screen.getByRole("button", { name: "Add workspace" })).toBeDisabled();
|
|
|
|
|
|
|
|
|
|
await user.click(screen.getByRole("button", { name: "Use as a Repo lane" }));
|
|
|
|
|
await user.click(screen.getByRole("button", { name: "Use as a Repo workspace" }));
|
|
|
|
|
expect(screen.getByRole("button", { name: "Repo" })).toHaveAttribute("aria-pressed", "true");
|
|
|
|
|
expect(screen.getByRole("button", { name: "Add lane" })).toBeEnabled();
|
|
|
|
|
expect(screen.getByRole("button", { name: "Add workspace" })).toBeEnabled();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("submits through the worktree provisioning endpoint, with the typed branch name", async () => {
|
|
|
|
@@ -375,7 +375,7 @@ describe("AddLaneModal — worktree mode (default)", () => {
|
|
|
|
|
const user = userEvent.setup();
|
|
|
|
|
|
|
|
|
|
await fillWorktreeForm(user, { title: "New feature", branch: "feat/new-feature" });
|
|
|
|
|
await user.click(screen.getByRole("button", { name: "Add lane" }));
|
|
|
|
|
await user.click(screen.getByRole("button", { name: "Add workspace" }));
|
|
|
|
|
|
|
|
|
|
await waitFor(() => {
|
|
|
|
|
expect(api.lanes.worktree).toHaveBeenCalledWith({
|
|
|
|
@@ -407,7 +407,7 @@ describe("AddLaneModal — worktree mode (default)", () => {
|
|
|
|
|
const user = userEvent.setup();
|
|
|
|
|
|
|
|
|
|
await fillWorktreeForm(user, { title: "New feature" });
|
|
|
|
|
await user.click(screen.getByRole("button", { name: "Add lane" }));
|
|
|
|
|
await user.click(screen.getByRole("button", { name: "Add workspace" }));
|
|
|
|
|
|
|
|
|
|
expect(await screen.findByText("EWORKTREEDIRCOLLISION")).toBeInTheDocument();
|
|
|
|
|
expect(onClose).not.toHaveBeenCalled();
|
|
|
|
@@ -432,7 +432,7 @@ describe("AddLaneModal — worktree mode (default)", () => {
|
|
|
|
|
const user = userEvent.setup();
|
|
|
|
|
|
|
|
|
|
await fillWorktreeForm(user);
|
|
|
|
|
await user.click(screen.getByRole("button", { name: "Add lane" }));
|
|
|
|
|
await user.click(screen.getByRole("button", { name: "Add workspace" }));
|
|
|
|
|
|
|
|
|
|
await waitFor(() => expect(api.lanes.worktree).toHaveBeenCalled());
|
|
|
|
|
await waitFor(() => expect(api.lanes.profileInit).toHaveBeenCalledWith(42));
|
|
|
|
@@ -457,7 +457,7 @@ describe("AddLaneModal — worktree mode (default)", () => {
|
|
|
|
|
const user = userEvent.setup();
|
|
|
|
|
|
|
|
|
|
await fillWorktreeForm(user, { title: "demo2" });
|
|
|
|
|
await user.click(screen.getByRole("button", { name: "Add lane" }));
|
|
|
|
|
await user.click(screen.getByRole("button", { name: "Add workspace" }));
|
|
|
|
|
|
|
|
|
|
await waitFor(() => expect(onAdded).toHaveBeenCalled());
|
|
|
|
|
expect(onClose).not.toHaveBeenCalled();
|
|
|
|
@@ -478,14 +478,14 @@ describe("AddLaneModal — worktree mode (default)", () => {
|
|
|
|
|
const user = userEvent.setup();
|
|
|
|
|
|
|
|
|
|
await fillWorktreeForm(user, { title: "demo3" });
|
|
|
|
|
await user.click(screen.getByRole("button", { name: "Add lane" }));
|
|
|
|
|
await user.click(screen.getByRole("button", { name: "Add workspace" }));
|
|
|
|
|
|
|
|
|
|
// A failure is the one case worth reading before the dialog goes away.
|
|
|
|
|
expect(await screen.findByText("Setup")).toBeInTheDocument();
|
|
|
|
|
expect(onClose).not.toHaveBeenCalled();
|
|
|
|
|
// And then the primary button must close, not sit there disabled: the lane
|
|
|
|
|
// already exists, so "Add lane" has nothing left to do.
|
|
|
|
|
expect(screen.queryByRole("button", { name: "Add lane" })).toBeNull();
|
|
|
|
|
expect(screen.queryByRole("button", { name: "Add workspace" })).toBeNull();
|
|
|
|
|
await user.click(screen.getByRole("button", { name: "Close" }));
|
|
|
|
|
expect(onClose).toHaveBeenCalled();
|
|
|
|
|
});
|
|
|
|
@@ -510,7 +510,7 @@ describe("AddLaneModal — worktree mode (default)", () => {
|
|
|
|
|
const user = userEvent.setup();
|
|
|
|
|
|
|
|
|
|
await fillWorktreeForm(user, { title: "demo4" });
|
|
|
|
|
await user.click(screen.getByRole("button", { name: "Add lane" }));
|
|
|
|
|
await user.click(screen.getByRole("button", { name: "Add workspace" }));
|
|
|
|
|
|
|
|
|
|
await waitFor(() => expect(onAdded).toHaveBeenCalled());
|
|
|
|
|
expect(await screen.findByText(/auto-setup was skipped/)).toBeInTheDocument();
|
|
|
|
@@ -525,12 +525,12 @@ describe("AddLaneModal — repo mode (adopt)", () => {
|
|
|
|
|
const user = userEvent.setup();
|
|
|
|
|
await user.click(screen.getByRole("button", { name: "Repo" }));
|
|
|
|
|
|
|
|
|
|
expect(screen.getByRole("button", { name: "Add lane" })).toBeDisabled();
|
|
|
|
|
expect(screen.getByRole("button", { name: "Add workspace" })).toBeDisabled();
|
|
|
|
|
const dirField = screen.getByLabelText("Directory");
|
|
|
|
|
await focusField(user, dirField);
|
|
|
|
|
await user.type(dirField, "/Users/tester/projects/repo");
|
|
|
|
|
|
|
|
|
|
expect(screen.getByRole("button", { name: "Add lane" })).toBeEnabled();
|
|
|
|
|
expect(screen.getByRole("button", { name: "Add workspace" })).toBeEnabled();
|
|
|
|
|
expect(screen.queryByLabelText("Branch to fork from")).toBeNull();
|
|
|
|
|
expect(screen.queryByLabelText("New branch name")).toBeNull();
|
|
|
|
|
expect(api.lanes.branches).not.toHaveBeenCalled();
|
|
|
|
@@ -551,7 +551,7 @@ describe("AddLaneModal — repo mode (adopt)", () => {
|
|
|
|
|
await focusField(user, dirField);
|
|
|
|
|
await user.type(dirField, "/Users/tester/projects/repo");
|
|
|
|
|
await user.type(screen.getByLabelText("Title"), "main repo");
|
|
|
|
|
await user.click(screen.getByRole("button", { name: "Add lane" }));
|
|
|
|
|
await user.click(screen.getByRole("button", { name: "Add workspace" }));
|
|
|
|
|
|
|
|
|
|
await waitFor(() =>
|
|
|
|
|
expect(api.lanes.ensure).toHaveBeenCalledWith({
|
|
|
|
@@ -582,9 +582,11 @@ describe("AddLaneModal — repo mode (adopt)", () => {
|
|
|
|
|
const dirField = screen.getByLabelText("Directory");
|
|
|
|
|
await focusField(user, dirField);
|
|
|
|
|
await user.type(dirField, "/Users/tester/projects/repo");
|
|
|
|
|
await user.click(screen.getByRole("button", { name: "Add lane" }));
|
|
|
|
|
await user.click(screen.getByRole("button", { name: "Add workspace" }));
|
|
|
|
|
|
|
|
|
|
expect(await screen.findByText(/already has a lane.*#3.*SG - Services/)).toBeInTheDocument();
|
|
|
|
|
expect(
|
|
|
|
|
await screen.findByText(/already has a workspace.*#3.*SG - Services/)
|
|
|
|
|
).toBeInTheDocument();
|
|
|
|
|
// The existing lane is selected so the user sees it, but nothing claims a new one.
|
|
|
|
|
expect(onAdded).toHaveBeenCalledWith(expect.objectContaining({ id: 3 }));
|
|
|
|
|
expect(onClose).not.toHaveBeenCalled();
|
|
|
|
@@ -625,7 +627,7 @@ describe("AddLaneModal — folder browse", () => {
|
|
|
|
|
|
|
|
|
|
expect(screen.queryByRole("dialog", { name: "Browse for a folder" })).toBeNull();
|
|
|
|
|
expect(
|
|
|
|
|
screen.getByRole("dialog", { name: "Create a lane from a working directory" })
|
|
|
|
|
screen.getByRole("dialog", { name: "Create a workspace from a working directory" })
|
|
|
|
|
).toBeInTheDocument();
|
|
|
|
|
expect(onClose).not.toHaveBeenCalled();
|
|
|
|
|
});
|
|
|
|
@@ -655,7 +657,7 @@ describe("AddLaneModal — AI Router", () => {
|
|
|
|
|
const repoField = screen.getByLabelText("Directory");
|
|
|
|
|
await focusField(user, repoField);
|
|
|
|
|
await user.type(repoField, "/Users/tester/projects/repo");
|
|
|
|
|
await user.click(screen.getByRole("button", { name: "Add lane" }));
|
|
|
|
|
await user.click(screen.getByRole("button", { name: "Add workspace" }));
|
|
|
|
|
|
|
|
|
|
await waitFor(() => expect(api.lanes.routerApply).toHaveBeenCalledWith(9, "native"));
|
|
|
|
|
});
|
|
|
|
@@ -674,7 +676,7 @@ describe("AddLaneModal — AI Router", () => {
|
|
|
|
|
await focusField(user, repoField);
|
|
|
|
|
await user.type(repoField, "/Users/tester/projects/repo");
|
|
|
|
|
await user.click(await screen.findByRole("radio", { name: "AI Router" }));
|
|
|
|
|
await user.click(screen.getByRole("button", { name: "Add lane" }));
|
|
|
|
|
await user.click(screen.getByRole("button", { name: "Add workspace" }));
|
|
|
|
|
|
|
|
|
|
await waitFor(() => expect(api.lanes.routerApply).toHaveBeenCalledWith(9, "9router"));
|
|
|
|
|
});
|
|
|
|
@@ -703,7 +705,7 @@ describe("AddLaneModal — AI Router", () => {
|
|
|
|
|
const repoField = screen.getByLabelText("Directory");
|
|
|
|
|
await focusField(user, repoField);
|
|
|
|
|
await user.type(repoField, "/Users/tester/projects/repo");
|
|
|
|
|
await user.click(screen.getByRole("button", { name: "Add lane" }));
|
|
|
|
|
await user.click(screen.getByRole("button", { name: "Add workspace" }));
|
|
|
|
|
|
|
|
|
|
await waitFor(() => expect(api.lanes.ensure).toHaveBeenCalled());
|
|
|
|
|
});
|
|
|
|
@@ -726,7 +728,7 @@ describe("AddLaneModal — a created lane is never lost to a setup failure", ()
|
|
|
|
|
const repoField = screen.getByLabelText("Directory");
|
|
|
|
|
await focusField(user, repoField);
|
|
|
|
|
await user.type(repoField, "/Users/tester/projects/repo");
|
|
|
|
|
await user.click(screen.getByRole("button", { name: "Add lane" }));
|
|
|
|
|
await user.click(screen.getByRole("button", { name: "Add workspace" }));
|
|
|
|
|
|
|
|
|
|
await waitFor(() => expect(onAdded).toHaveBeenCalled());
|
|
|
|
|
// …and the failure is still surfaced, on a modal left open.
|
|
|
|
@@ -792,7 +794,7 @@ describe("AddLaneModal — tool selection", () => {
|
|
|
|
|
const picker = await screen.findByRole("button", { name: /Claude Code.*Default/ });
|
|
|
|
|
expect(picker).toHaveAttribute("aria-expanded", "false");
|
|
|
|
|
await user.click(picker);
|
|
|
|
|
const list = screen.getByRole("group", { name: /Which CLI drives this lane/ });
|
|
|
|
|
const list = screen.getByRole("group", { name: /Which CLI drives this workspace/ });
|
|
|
|
|
expect(
|
|
|
|
|
within(list)
|
|
|
|
|
.getAllByRole("button")
|
|
|
|
@@ -802,7 +804,7 @@ describe("AddLaneModal — tool selection", () => {
|
|
|
|
|
// The picked tool replaces the default on the button, and the list closes.
|
|
|
|
|
expect(screen.getByRole("button", { name: "Codex", expanded: false })).toBeInTheDocument();
|
|
|
|
|
expect(screen.queryByRole("button", { name: "Gemini" })).toBeNull();
|
|
|
|
|
await user.click(screen.getByRole("button", { name: /add lane|create/i }));
|
|
|
|
|
await user.click(screen.getByRole("button", { name: /add workspace|create/i }));
|
|
|
|
|
|
|
|
|
|
await waitFor(() =>
|
|
|
|
|
expect(api.lanes.ensure).toHaveBeenCalledWith(expect.objectContaining({ tool: "codex" }))
|
|
|
|
@@ -842,7 +844,7 @@ describe("AddLaneModal — tool selection", () => {
|
|
|
|
|
const dirField = screen.getByLabelText("Directory");
|
|
|
|
|
await focusField(user, dirField);
|
|
|
|
|
await user.type(dirField, "/tmp/demo");
|
|
|
|
|
await user.click(screen.getByRole("button", { name: /add lane|create/i }));
|
|
|
|
|
await user.click(screen.getByRole("button", { name: /add workspace|create/i }));
|
|
|
|
|
|
|
|
|
|
await waitFor(() =>
|
|
|
|
|
expect(api.lanes.ensure).toHaveBeenCalledWith(expect.objectContaining({ tool: "claude" }))
|
|
|
|
|