49f87c4da3
Whole tree from parallel sessions, plus the version bump: - Clone: a queue with live progress (Add workspace and extra folders), clones run under nice/ionice so the machine stays responsive. - Settings → AI tools → Session defaults: a new session's default permission mode / bypass per account. - Chat lists the background subagents that have not reported back yet. - A file pasted or dropped into a claude pane goes in as an @ mention. - Changes panel: say why amend/undo is refused on a pushed commit. - openapi.yaml regenerated (Artifacts, Host, resumeById).
80 lines
2.6 KiB
TypeScript
80 lines
2.6 KiB
TypeScript
/**
|
|
* @file Tests for CloneProgress: a queued clone shows its place in the
|
|
* server's clone queue, a running one git's phase, percent and speed with a
|
|
* bar, and nothing shows before the server knows the token. Also that
|
|
* `newCloneToken` makes a token the server accepts.
|
|
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
|
|
*/
|
|
|
|
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
import { render, screen } from "@testing-library/react";
|
|
import "../../../i18n";
|
|
|
|
vi.mock("../../../lib/api", () => ({
|
|
api: { lanes: { cloneProgress: vi.fn() } },
|
|
}));
|
|
|
|
import { api, type CloneProgressView } from "../../../lib/api";
|
|
import { CloneProgress, newCloneToken } from "../CloneProgress";
|
|
|
|
const base: CloneProgressView = {
|
|
state: "running",
|
|
position: null,
|
|
running: 2,
|
|
waiting: 1,
|
|
limit: 2,
|
|
phase: null,
|
|
label: null,
|
|
percent: null,
|
|
detail: null,
|
|
};
|
|
|
|
describe("CloneProgress", () => {
|
|
// Braces: a function returned from beforeEach is run as its teardown.
|
|
beforeEach(() => {
|
|
vi.mocked(api.lanes.cloneProgress).mockReset();
|
|
});
|
|
|
|
it("shows the clone's place in the queue while it waits", async () => {
|
|
vi.mocked(api.lanes.cloneProgress).mockResolvedValue({
|
|
...base,
|
|
state: "queued",
|
|
position: 1,
|
|
});
|
|
render(<CloneProgress token="t" />);
|
|
expect(await screen.findByTestId("clone-progress")).toHaveTextContent(
|
|
"Waiting in the clone queue — #1 of 1 (2/2 clones running)"
|
|
);
|
|
});
|
|
|
|
it("shows git's phase, percent and speed with a bar while it runs", async () => {
|
|
vi.mocked(api.lanes.cloneProgress).mockResolvedValue({
|
|
...base,
|
|
phase: "receiving",
|
|
label: "Receiving objects",
|
|
percent: 45,
|
|
detail: "450/1000, 120.50 MiB | 5.20 MiB/s",
|
|
});
|
|
render(<CloneProgress token="t" />);
|
|
const line = await screen.findByTestId("clone-progress");
|
|
expect(line).toHaveTextContent("Downloading 45%");
|
|
expect(line).toHaveTextContent("120.50 MiB | 5.20 MiB/s");
|
|
expect(screen.getByRole("progressbar")).toHaveAttribute("aria-valuenow", "45");
|
|
});
|
|
|
|
it("shows nothing while the server does not know the token yet", async () => {
|
|
vi.mocked(api.lanes.cloneProgress).mockImplementation(() =>
|
|
Promise.reject(Object.assign(new Error("no such clone"), { code: "ENOTFOUND" }))
|
|
);
|
|
render(<CloneProgress token="t" />);
|
|
await new Promise((r) => setTimeout(r, 20));
|
|
expect(screen.queryByTestId("clone-progress")).toBeNull();
|
|
});
|
|
|
|
it("makes tokens the server accepts", () => {
|
|
const a = newCloneToken();
|
|
expect(a).toMatch(/^[A-Za-z0-9_-]{16,64}$/);
|
|
expect(newCloneToken()).not.toBe(a);
|
|
});
|
|
});
|