22de5009ff
- Artifacts (/artifacts): claude.ai pages sessions publish (captured from the Artifact tool's hook), pinned workspace files, skills and plugins, each kept as a snapshot; shared ones appear in a Hub and a skill or plugin installs into the viewer's own account (server/lib/artifacts.js, /api/artifacts, docs/plans/artifacts/). - Workspace extra folders: clone one or more related repos beside the workspace and add them in one step (POST /api/lanes/:id/sources/clone). - A pane that ended by itself while tmux kept running is no longer offered for resume (tmux.serverStartedAt in reconcileOrphans). - Changes panel: a folder that is not a git repo opens on Files, and its files open from there. - Plus the parallel sessions' work in this tree: File Explorer button in the panel header, user delete, RSA SSH keys, chat/terminal and pet fixes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
62 lines
2.5 KiB
TypeScript
62 lines
2.5 KiB
TypeScript
/**
|
|
* @file Tests for CloneSourcesForm: every URL in the box is cloned in turn as
|
|
* an extra folder, a host that wants a login opens the login row, and Retry
|
|
* re-runs only the failed URLs with that login.
|
|
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
|
|
*/
|
|
|
|
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
import { render, screen, waitFor } from "@testing-library/react";
|
|
import userEvent from "@testing-library/user-event";
|
|
import "../../../i18n";
|
|
|
|
vi.mock("../../../lib/api", () => ({
|
|
api: { lanes: { cloneFolder: vi.fn() } },
|
|
}));
|
|
|
|
import { api, type LaneSourcesView } from "../../../lib/api";
|
|
import { CloneSourcesForm } from "../CloneSourcesForm";
|
|
|
|
const view = { sources: [] } as unknown as LaneSourcesView & { path: string };
|
|
|
|
describe("CloneSourcesForm", () => {
|
|
beforeEach(() => vi.mocked(api.lanes.cloneFolder).mockReset());
|
|
|
|
it("clones each URL, asks for a login where needed, and retries only that one", async () => {
|
|
const user = userEvent.setup();
|
|
const onAdded = vi.fn();
|
|
vi.mocked(api.lanes.cloneFolder).mockImplementation(async (_id, body) => {
|
|
if (body?.url.includes("private") && !body.credentials)
|
|
throw Object.assign(new Error("login"), {
|
|
code: "ECREDENTIALS",
|
|
auth: { protocol: "https", host: "private.test" },
|
|
});
|
|
return view;
|
|
});
|
|
render(<CloneSourcesForm laneId={3} parent="/w" onAdded={onAdded} />);
|
|
|
|
await user.type(
|
|
screen.getByTestId("workspace-clone-input"),
|
|
"https://h/a.git\nhttps://private.test/b.git\nhttps://h/a.git"
|
|
);
|
|
await user.click(screen.getByTestId("workspace-clone-run"));
|
|
|
|
await waitFor(() => expect(api.lanes.cloneFolder).toHaveBeenCalledTimes(2));
|
|
expect(onAdded).toHaveBeenCalledTimes(1);
|
|
expect(await screen.findByText(/private\.test needs a login/)).toBeInTheDocument();
|
|
// Only what is left stays in the box.
|
|
expect(screen.getByTestId("workspace-clone-input")).toHaveValue("https://private.test/b.git");
|
|
|
|
await user.type(screen.getByLabelText("Username"), "dev");
|
|
await user.type(screen.getByLabelText("Password or access token"), "tok");
|
|
await user.click(screen.getByRole("button", { name: "Retry the failed ones" }));
|
|
|
|
await waitFor(() => expect(onAdded).toHaveBeenCalledTimes(2));
|
|
expect(api.lanes.cloneFolder).toHaveBeenLastCalledWith(3, {
|
|
url: "https://private.test/b.git",
|
|
credentials: { username: "dev", password: "tok", remember: true },
|
|
});
|
|
expect(screen.getByTestId("workspace-clone-input")).toHaveValue("");
|
|
});
|
|
});
|