1
18import { test, expect } from "../fixtures";
19import {
20 addMemberViaSeam,
21 renameWorkspace,
22 deleteWorkspace,
23 listMembers,
24 seamServiceToken,
25} from "../utils/api";
26
27test.describe("Workspace — Phase 2", () => {
28 test("create workspace from sidebar switcher", async ({
29 authPage,
30 freshWorkspace,
31 }) => {
32 const ws = await freshWorkspace("My New Team Space");
33
34
35 await authPage.goto("/");
36 await expect(authPage.getByTestId("sidebar")).toBeVisible();
37 await authPage.getByTestId("workspace-switcher").click();
38 await expect(
39 authPage.getByTestId("workspace-switcher").getByText("My New Team Space"),
40 ).toBeVisible({ timeout: 10_000 });
41
42
43 await authPage.getByText("My New Team Space").click();
44 await authPage.waitForURL(/\/w\
45 });
46
47 test("add member (test seam) + second context; role visible", async ({
48 api,
49 secondUser,
50 freshWorkspace,
51 }) => {
52 const ws = await freshWorkspace("Invited Space");
53
54
55 const { user: second, page: secondPage } = await secondUser();
56
57
58
59
60
61
62
63
64 await addMemberViaSeam(seamServiceToken(), ws.id, second.email, "MEMBER");
65
66
67
68 const members = await listMembers(api, ws.id);
69 const owner = members.find((m) => m.role === "OWNER");
70 expect(owner, "demo user must be OWNER of a workspace they created").toBeTruthy();
71 const joined = members.find((m) => m.email === second.email);
72 expect(joined, "second user must be a member after the seam add").toBeTruthy();
73 expect(joined?.role).toBe("MEMBER");
74 expect(members.length).toBeGreaterThanOrEqual(2);
75
76
77 await secondPage.goto(`/w/${ws.id}/settings`);
78 await expect(
79 secondPage.getByTestId("workspace-settings"),
80 ).toBeVisible({ timeout: 15_000 });
81 });
82
83 test("rename workspace", async ({ authPage, api, freshWorkspace }) => {
84 const ws = await freshWorkspace("Rename Me");
85 const renamed = await renameWorkspace(api, ws.id, "Renamed Space");
86 expect(renamed.name).toBe("Renamed Space");
87
88 await authPage.goto(`/w/${ws.id}`);
89
90 await expect(authPage.getByTestId("sidebar").getByText("Renamed Space")).toBeVisible({
91 timeout: 10_000,
92 });
93 });
94
95 test("delete workspace (owner) — pages removed, switcher updated", async ({
96 authPage,
97 api,
98 freshWorkspace,
99 }) => {
100 const ws = await freshWorkspace("Doomed Space");
101
102 const pageRes = await api.post(`/api/v1/workspaces/${ws.id}/pages`, {
103 data: { title: "Orphan Candidate" },
104 });
105 expect(pageRes.status()).toBe(201);
106
107 await deleteWorkspace(api, ws.id);
108
109
110 const gone = await api.get(`/api/v1/workspaces/${ws.id}`);
111 expect(gone.status()).toBe(404);
112
113 await authPage.goto("/");
114 await authPage.getByTestId("workspace-switcher").click();
115
116 const doomed = authPage.getByTestId("workspace-switcher").getByText("Doomed Space");
117 const deadline = Date.now() + 10_000;
118
119 while (true) {
120 const count = await doomed.count();
121 if (count === 0) break;
122 if (Date.now() > deadline) throw new Error("deleted workspace still in switcher");
123 await authPage.waitForTimeout(500);
124 }
125 });
126
127 test("settings tabs render (profile, billing placeholder, security, integrations)", async ({
128 authPage,
129 freshWorkspace,
130 }) => {
131 const ws = await freshWorkspace("Settings Space");
132 await authPage.goto(`/w/${ws.id}/settings`);
133 await expect(authPage.getByTestId("workspace-settings")).toBeVisible({
134 timeout: 15_000,
135 });
136
137 for (const tab of ["profile", "billing", "security", "integrations"]) {
138 const tabEl = authPage.getByTestId(`settings-tab-${tab}`);
139 await expect(tabEl, `settings tab "${tab}" should render`).toBeVisible();
140 await tabEl.click();
141 }
142
143
144 await expect(authPage.getByTestId("workspace-settings")).toBeVisible();
145 });
146
147 test("member list only shows actual members (tenant isolation)", async ({
148 api,
149 secondUser,
150 freshWorkspace,
151 }) => {
152 const ws = await freshWorkspace("Isolated Space");
153 const { user: outsider, api: outsiderApi } = await secondUser();
154
155
156 const members = await listMembers(api, ws.id);
157 expect(members.some((m) => m.email === outsider.email)).toBe(false);
158
159
160 const denied = await outsiderApi.get(`/api/v1/workspaces/${ws.id}/members`);
161 expect([403, 404]).toContain(denied.status());
162 });
163});
164