fix: restoring snapshots that have unrelated root paths
Some checks failed
Release Workflow / determine-release-type (push) Has been cancelled
Release Workflow / checks (push) Has been cancelled
Release Workflow / e2e-tests (push) Has been cancelled
Release Workflow / build-images (push) Has been cancelled
Release Workflow / publish-release (push) Has been cancelled
Some checks failed
Release Workflow / determine-release-type (push) Has been cancelled
Release Workflow / checks (push) Has been cancelled
Release Workflow / e2e-tests (push) Has been cancelled
Release Workflow / build-images (push) Has been cancelled
Release Workflow / publish-release (push) Has been cancelled
This commit is contained in:
parent
63e12868b1
commit
add0f2788f
3 changed files with 79 additions and 2 deletions
|
|
@ -75,4 +75,51 @@ describe("RestoreForm", () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
test("restores the selected full path when the display root is unrelated", async () => {
|
||||
let restoreRequestBody: unknown;
|
||||
|
||||
server.use(
|
||||
http.get("/api/v1/repositories/:shortId/snapshots/:snapshotId/files", () => {
|
||||
return HttpResponse.json({
|
||||
files: [
|
||||
{ name: "project", path: "/mnt/project", type: "dir" },
|
||||
{ name: "a.txt", path: "/mnt/project/a.txt", type: "file" },
|
||||
],
|
||||
});
|
||||
}),
|
||||
http.post("/api/v1/repositories/:shortId/restore", async ({ request }) => {
|
||||
restoreRequestBody = await request.json();
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
message: "Snapshot restored successfully",
|
||||
filesRestored: 1,
|
||||
filesSkipped: 0,
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
render(
|
||||
<RestoreForm
|
||||
repository={fromAny({ shortId: "repo-1", name: "Repo 1" })}
|
||||
snapshotId="snap-1"
|
||||
returnPath="/repositories/repo-1/snap-1"
|
||||
queryBasePath="/mnt/project"
|
||||
displayBasePath="/other/root"
|
||||
/>,
|
||||
);
|
||||
|
||||
const row = await screen.findByRole("button", { name: "mnt" });
|
||||
await userEvent.click(within(row).getByRole("checkbox"));
|
||||
await userEvent.click(screen.getByRole("button", { name: "Restore 1 item" }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(restoreRequestBody).toEqual({
|
||||
snapshotId: "snap-1",
|
||||
include: ["/mnt"],
|
||||
selectedItemKind: "dir",
|
||||
overwrite: "always",
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -134,6 +134,33 @@ describe("SnapshotTreeBrowser", () => {
|
|||
expect(selectedKind === "dir").toBe(true);
|
||||
});
|
||||
|
||||
test("keeps full paths when the display root does not contain the query root", async () => {
|
||||
mockListSnapshotFiles();
|
||||
|
||||
let selectedPaths: Set<string> | undefined;
|
||||
let selectedKind: "file" | "dir" | null = null;
|
||||
|
||||
renderSnapshotTreeBrowser({
|
||||
queryBasePath: "/mnt/project",
|
||||
displayBasePath: "/other/root",
|
||||
withCheckboxes: true,
|
||||
onSelectionChange: (paths) => {
|
||||
selectedPaths = paths;
|
||||
},
|
||||
onSingleSelectionKindChange: (kind) => {
|
||||
selectedKind = kind;
|
||||
},
|
||||
});
|
||||
|
||||
const row = await screen.findByRole("button", { name: "mnt" });
|
||||
const checkbox = within(row).getByRole("checkbox");
|
||||
|
||||
await userEvent.click(checkbox);
|
||||
|
||||
expect(selectedPaths ? Array.from(selectedPaths) : []).toEqual(["/mnt"]);
|
||||
expect(selectedKind === "dir").toBe(true);
|
||||
});
|
||||
|
||||
test("shows selected folder state when full paths are provided from the parent", async () => {
|
||||
mockListSnapshotFiles();
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { listSnapshotFilesOptions } from "~/client/api-client/@tanstack/react-qu
|
|||
import { FileBrowser, type FileBrowserUiProps } from "~/client/components/file-browsers/file-browser";
|
||||
import { useFileBrowser } from "~/client/hooks/use-file-browser";
|
||||
import { parseError } from "~/client/lib/errors";
|
||||
import { normalizeAbsolutePath } from "@zerobyte/core/utils";
|
||||
import { isPathWithin, normalizeAbsolutePath } from "@zerobyte/core/utils";
|
||||
import { logger } from "~/client/lib/logger";
|
||||
|
||||
function createPathPrefixFns(basePath: string) {
|
||||
|
|
@ -48,6 +48,9 @@ export const SnapshotTreeBrowser = (props: SnapshotTreeBrowserProps) => {
|
|||
const queryClient = useQueryClient();
|
||||
const normalizedQueryBasePath = normalizeAbsolutePath(queryBasePath);
|
||||
const normalizedDisplayBasePath = normalizeAbsolutePath(displayBasePath ?? "/");
|
||||
const effectiveDisplayBasePath = isPathWithin(normalizedDisplayBasePath, normalizedQueryBasePath)
|
||||
? normalizedDisplayBasePath
|
||||
: "/";
|
||||
|
||||
const { data, isLoading, error } = useQuery({
|
||||
...listSnapshotFilesOptions({
|
||||
|
|
@ -57,7 +60,7 @@ export const SnapshotTreeBrowser = (props: SnapshotTreeBrowserProps) => {
|
|||
enabled,
|
||||
});
|
||||
|
||||
const displayPathFns = useMemo(() => createPathPrefixFns(normalizedDisplayBasePath), [normalizedDisplayBasePath]);
|
||||
const displayPathFns = useMemo(() => createPathPrefixFns(effectiveDisplayBasePath), [effectiveDisplayBasePath]);
|
||||
|
||||
const displaySelectedPaths = useMemo(() => {
|
||||
if (!selectedPaths) return undefined;
|
||||
|
|
|
|||
Loading…
Reference in a new issue