feat: delete project sync
This commit is contained in:
parent
4c67ec6209
commit
658b9b7758
|
|
@ -86,8 +86,137 @@ test("login loads backend projects list on /projects", async ({ context, page })
|
|||
await page.getByRole("button", { name: "Sign in" }).click();
|
||||
|
||||
await expect(page).toHaveURL(/\/projects/);
|
||||
await page.waitForResponse((response) =>
|
||||
response.url().includes("/api/editor/projects"),
|
||||
);
|
||||
await expect(page.getByText("Backend Project Alpha")).toBeVisible();
|
||||
});
|
||||
|
||||
test("deleting project from /projects sends cloud DELETE request", async ({
|
||||
context,
|
||||
page,
|
||||
}) => {
|
||||
await page.addInitScript(() => {
|
||||
window.localStorage.setItem("vibecut_workspace_id", "ws-1");
|
||||
});
|
||||
|
||||
await context.addCookies([
|
||||
{
|
||||
name: "refresh_token",
|
||||
value: "e2e-refresh-token",
|
||||
domain: "127.0.0.1",
|
||||
path: "/",
|
||||
httpOnly: false,
|
||||
secure: false,
|
||||
sameSite: "Lax",
|
||||
},
|
||||
]);
|
||||
|
||||
await page.route("**/api/auth/user/refresh", async (route) => {
|
||||
await route.fulfill({
|
||||
status: 401,
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({
|
||||
status: "error",
|
||||
message: "Invalid refresh token",
|
||||
statusCode: 401,
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
await page.route("**/api/auth/user/login", async (route) => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({
|
||||
token: "backend-jwt-token",
|
||||
user: {
|
||||
id: "u-1",
|
||||
username: "john",
|
||||
email: "john@example.com",
|
||||
},
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
const deleteRequestPromise = page.waitForRequest(
|
||||
(request) =>
|
||||
request.method() === "DELETE" &&
|
||||
request.url().includes("/api/editor/projects/proj-remote-1"),
|
||||
);
|
||||
|
||||
await page.route("**/api/editor/projects/*", async (route) => {
|
||||
const workspaceHeader = route.request().headers()["x-workspace-id"];
|
||||
if (!workspaceHeader) {
|
||||
await route.fulfill({
|
||||
status: 400,
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({
|
||||
message: "x-workspace-id header is required",
|
||||
error: "Bad Request",
|
||||
statusCode: 400,
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (route.request().method() === "DELETE") {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({ ok: true }),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await route.fulfill({ status: 405 });
|
||||
});
|
||||
|
||||
await page.route("**/api/editor/projects**", async (route) => {
|
||||
const workspaceHeader = route.request().headers()["x-workspace-id"];
|
||||
if (!workspaceHeader) {
|
||||
await route.fulfill({
|
||||
status: 400,
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({
|
||||
message: "x-workspace-id header is required",
|
||||
error: "Bad Request",
|
||||
statusCode: 400,
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({
|
||||
items: [
|
||||
{
|
||||
id: "proj-remote-1",
|
||||
workspaceId: "ws-1",
|
||||
ownerId: 1,
|
||||
name: "Backend Project Alpha",
|
||||
version: 4,
|
||||
updatedAt: "2026-02-09T12:00:00.000Z",
|
||||
createdAt: "2026-02-08T12:00:00.000Z",
|
||||
},
|
||||
],
|
||||
total: 1,
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
await page.goto("/auth/login");
|
||||
|
||||
await page.getByLabel("Username").fill("john");
|
||||
await page.getByLabel("Password").fill("12345678");
|
||||
await page.getByRole("button", { name: "Sign in" }).click();
|
||||
|
||||
await expect(page).toHaveURL(/\/projects/);
|
||||
await expect(page.getByText("Backend Project Alpha")).toBeVisible();
|
||||
|
||||
await page.getByRole("button", { name: "Project menu" }).first().click();
|
||||
await page.getByRole("menuitem", { name: "Delete" }).click();
|
||||
await page.getByPlaceholder("DELETE").fill("DELETE");
|
||||
await page.getByRole("button", { name: "Delete project" }).click();
|
||||
|
||||
await deleteRequestPromise;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -307,8 +307,33 @@ export class ProjectManager {
|
|||
if (uniqueIds.length === 0) return;
|
||||
|
||||
try {
|
||||
const token = authSession.getToken();
|
||||
const deletedProjectIds =
|
||||
token === null
|
||||
? uniqueIds
|
||||
: (
|
||||
await Promise.all(
|
||||
uniqueIds.map(async (id) => {
|
||||
try {
|
||||
await editorCloudApi.deleteProject({ projectId: id, token });
|
||||
return id;
|
||||
} catch (error) {
|
||||
console.error(`Failed to delete cloud project ${id}:`, error);
|
||||
return null;
|
||||
}
|
||||
}),
|
||||
)
|
||||
).filter((id): id is string => id !== null);
|
||||
|
||||
if (deletedProjectIds.length === 0) {
|
||||
toast.error("Failed to delete projects", {
|
||||
description: "Cloud delete failed. Please try again.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
uniqueIds.map((id) =>
|
||||
deletedProjectIds.map((id) =>
|
||||
Promise.all([
|
||||
storageService.deleteProjectMedia({ projectId: id }),
|
||||
storageService.deleteProject({ id }),
|
||||
|
|
@ -316,10 +341,13 @@ export class ProjectManager {
|
|||
),
|
||||
);
|
||||
|
||||
const idSet = new Set(uniqueIds);
|
||||
const idSet = new Set(deletedProjectIds);
|
||||
this.savedProjects = this.savedProjects.filter(
|
||||
(project) => !idSet.has(project.id),
|
||||
);
|
||||
deletedProjectIds.forEach((id) => {
|
||||
this.remoteVersionByProjectId.delete(id);
|
||||
});
|
||||
|
||||
const shouldClearActive =
|
||||
this.active && idSet.has(this.active.metadata.id);
|
||||
|
|
@ -331,8 +359,18 @@ export class ProjectManager {
|
|||
}
|
||||
|
||||
this.notify();
|
||||
|
||||
if (deletedProjectIds.length < uniqueIds.length) {
|
||||
toast.error("Some projects were not deleted", {
|
||||
description:
|
||||
"Cloud sync failed for some selected projects. Please try again.",
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to delete projects:", error);
|
||||
toast.error("Failed to delete projects", {
|
||||
description: error instanceof Error ? error.message : "Please try again",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@ const requestWithAuthRetry = async <T>({
|
|||
body,
|
||||
}: {
|
||||
path: string;
|
||||
method: "GET" | "PUT" | "POST";
|
||||
method: "GET" | "PUT" | "POST" | "DELETE";
|
||||
token: string;
|
||||
body?: unknown;
|
||||
}): Promise<T> => {
|
||||
|
|
@ -277,6 +277,18 @@ export const editorCloudApi = {
|
|||
token,
|
||||
body: payload,
|
||||
}),
|
||||
deleteProject: ({
|
||||
projectId,
|
||||
token,
|
||||
}: {
|
||||
projectId: string;
|
||||
token: string;
|
||||
}) =>
|
||||
requestWithAuthRetry<void>({
|
||||
path: `/editor/projects/${projectId}`,
|
||||
method: "DELETE",
|
||||
token,
|
||||
}),
|
||||
createFileUpload: ({
|
||||
token,
|
||||
payload,
|
||||
|
|
|
|||
Loading…
Reference in New Issue