mirror of
https://gh-proxy.org/https://github.com/klinsmaya/multica.git
synced 2026-11-08 01:16:45 +08:00
* feat: share runtimes with selected workspace members Co-authored-by: multica-agent <[email protected]> * refactor(runtimes): one-click private/public and popover member picker Private and Public apply immediately again (restrictions still go through the reviewed revocation dialog). Specific members opens a draft popover with search, Cancel and Save; selected recipients are summarized under the scope control with an Edit entry point. The editor moves to its own runtime-access-editor component. Co-authored-by: multica-agent <[email protected]> --------- Co-authored-by: multica-agent <[email protected]>
61 lines
3.1 KiB
TypeScript
61 lines
3.1 KiB
TypeScript
import { test, expect } from "@playwright/test";
|
|
import { TestApiClient } from "./fixtures";
|
|
|
|
test("runtime owner can save specific members and revoke their access", async ({ page }, testInfo) => {
|
|
const api = new TestApiClient();
|
|
const member = new TestApiClient();
|
|
const run = `${Date.now()}-${process.pid}`;
|
|
await api.login(`runtime-owner-${run}@localhost`, "Runtime Owner");
|
|
const workspace = await api.ensureWorkspace("Runtime sharing", `runtime-sharing-${run}`);
|
|
await api.markUserOnboarded();
|
|
try {
|
|
await member.login(`runtime-member-${run}@localhost`, "Alice");
|
|
const memberId = await api.addMemberByEmail(`runtime-member-${run}@localhost`);
|
|
const runtimeId = await api.seedRuntime("Studio Mac");
|
|
await page.addInitScript((token) => {
|
|
localStorage.setItem("multica_token", token!);
|
|
localStorage.setItem("multica:chat:isOpen", "false");
|
|
}, api.getToken());
|
|
await page.setViewportSize({ width: 1280, height: 900 });
|
|
await page.goto(`/${workspace.slug}/runtimes/${runtimeId}`);
|
|
await page.getByRole("row", { name: /Studio Mac Built-in/ }).click();
|
|
const scope = (name: string) => page.getByRole("button", { name, exact: true });
|
|
const patched = () => page.waitForResponse((response) => response.url().endsWith(`/api/runtimes/${runtimeId}`) && response.request().method() === "PATCH");
|
|
await expect(scope("Private")).toHaveAttribute("aria-pressed", "true");
|
|
await page.screenshot({ path: testInfo.outputPath("runtime-private.png"), fullPage: true });
|
|
|
|
// Choosing members opens a draft picker; nothing persists until Save.
|
|
await scope("Members").click();
|
|
await expect(page.getByRole("button", { name: "Save", exact: true })).toBeDisabled();
|
|
await page.getByRole("checkbox", { name: "Alice" }).check();
|
|
await page.screenshot({ path: testInfo.outputPath("runtime-member-picker.png"), fullPage: true });
|
|
const saved = patched();
|
|
await page.getByRole("button", { name: "Save", exact: true }).click();
|
|
expect((await saved).status()).toBe(200);
|
|
await page.reload();
|
|
await expect(scope("Members")).toHaveAttribute("aria-pressed", "true");
|
|
await expect(page.getByRole("button", { name: /Alice.*Edit/ })).toBeVisible();
|
|
await page.screenshot({ path: testInfo.outputPath("runtime-shared.png"), fullPage: true });
|
|
|
|
// Private applies in one click.
|
|
const revoked = patched();
|
|
await scope("Private").click();
|
|
expect((await revoked).status()).toBe(200);
|
|
await page.reload();
|
|
await expect(scope("Private")).toHaveAttribute("aria-pressed", "true");
|
|
|
|
await scope("Members").click();
|
|
await page.getByRole("checkbox", { name: "Alice" }).check();
|
|
const sharedAgain = patched();
|
|
await page.getByRole("button", { name: "Save", exact: true }).click();
|
|
expect((await sharedAgain).status()).toBe(200);
|
|
await api.removeMember(memberId);
|
|
await expect(scope("Private")).toHaveAttribute("aria-pressed", "true", { timeout: 15000 });
|
|
await api.addMemberByEmail(`runtime-member-${run}@localhost`);
|
|
await page.reload();
|
|
await expect(scope("Private")).toHaveAttribute("aria-pressed", "true");
|
|
} finally {
|
|
await api.deleteWorkspace();
|
|
}
|
|
});
|