Skip to content

Commit 3d46814

Browse files
amal66claude
andcommitted
fix(e2e): realign the suite with the olp UI refresh (PRs Open-Legal-Products#215/Open-Legal-Products#216)
The olp UI sync (b3166dd "sync olp UI as source of truth") and the liquid- surface refinements reshaped several elements the Playwright suite asserts on. None of it is a regression — the specs were describing the old shape. Six specs across four files were failing on main; all six now pass. * The assistant/project chat input placeholder is "How can I help?", not "Ask a question about your documents..." (ChatInput.tsx / TRChatInput.tsx). Fixed in chat-management (rename/delete/project-assistant) and critical-path. * The project assistant empty state replaced the "+ Create New" text link with a PillButton reading "Create" (ProjectAssistantTable). critical-path and chat-management now target getByRole("button", { name: "Create" }). * The sidebar chat row's active marker is APP_SURFACE_ACTIVE_CLASS ("bg-app-surface-active"), not the old "bg-gray-200/60"; and the row wrapper is now h-8, not h-9 (SidebarChatItem.tsx). The rename and delete tests locate the active/first row accordingly. * The documents-toolbar folder-create button is now "Folder" (a TabPillButton wired to the root createFolderAction in ProjectDocumentsView), not "Add Subfolder"; it still opens the autofocused "Folder name" root input. * NewTRModal's footer submit and the tabular page's own CTA both read "Create", so the create-review spec now scopes to the modal submit (button[name="modalAction"][value="create-review"]) to avoid a strict-mode ambiguity. Verified: full root Playwright suite green (27 passed) against the local demo stack (Supabase + MinIO, demo model, e2e@mike.local). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CEguyEgXa9JjCciXCcVemC
1 parent b3166dd commit 3d46814

4 files changed

Lines changed: 30 additions & 19 deletions

File tree

e2e/chat-management.spec.ts

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ test("rename chat: sidebar rename interaction updates the title", async ({ page
124124

125125
// ── Step 1: create a new chat ─────────────────────────────────────────────────
126126
await page.goto("/assistant");
127-
const textarea = page.getByPlaceholder("Ask a question about your documents...");
127+
const textarea = page.getByPlaceholder("How can I help?");
128128
await expect(textarea).toBeVisible({ timeout: 10_000 });
129129
// Pick the keyless demo model so the submit isn't blocked by the
130130
// ApiKeyMissingModal (no provider key is configured in this run).
@@ -155,13 +155,13 @@ test("rename chat: sidebar rename interaction updates the title", async ({ page
155155

156156
// ── Step 4: locate the active chat item ──────────────────────────────────────
157157
// SidebarChatItem.tsx renders a `div.group.relative` wrapper for each chat.
158-
// When isActive=true the wrapper class includes "bg-gray-200/60" as a
159-
// standalone Tailwind class. Inactive items have "hover:bg-gray-100" (a
160-
// different token), so matching the "bg-gray-200/60" class distinguishes the
161-
// active item. Use an attribute-substring match ([class*=]) to avoid having
162-
// to CSS-escape the "/" in the Tailwind class name.
158+
// When isActive=true the wrapper carries APP_SURFACE_ACTIVE_CLASS
159+
// ("bg-app-surface-active"); inactive items carry APP_SURFACE_HOVER_CLASS
160+
// ("hover:bg-app-surface-hover", a different token), so matching
161+
// "bg-app-surface-active" distinguishes the active item. (The olp liquid-
162+
// surface refresh renamed the old "bg-gray-200/60" active token.)
163163
const activeItem = page
164-
.locator('div.group.relative[class*="bg-gray-200/60"]')
164+
.locator('div.group.relative[class*="bg-app-surface-active"]')
165165
.first();
166166

167167
// The active item's trigger is already opacity-100, but hover is harmless and
@@ -214,7 +214,7 @@ test("delete chat: sidebar delete action removes the chat from history", async (
214214

215215
// ── Step 1: create a new chat ─────────────────────────────────────────────────
216216
await page.goto("/assistant");
217-
const textarea = page.getByPlaceholder("Ask a question about your documents...");
217+
const textarea = page.getByPlaceholder("How can I help?");
218218
await expect(textarea).toBeVisible({ timeout: 10_000 });
219219
// ── Step 2: create the chat, riding out transient gateway 502s ───────────────
220220
// ChatInput.handleSubmit creates the chat (saveChat → POST /chat/create) then
@@ -276,7 +276,7 @@ test("delete chat: sidebar delete action removes the chat from history", async (
276276
// just-created chat is active and prepended, so it is the first row; rename it
277277
// via the same three-dot menu the rename test exercises.
278278
const uniqueTitle = `Delete Target ${Date.now()}`;
279-
const firstRow = page.locator("div.group.relative.h-9.rounded-md").first();
279+
const firstRow = page.locator("div.group.relative.h-8.rounded-md").first();
280280
await firstRow.hover();
281281
await firstRow.locator("button").last().click();
282282
await page.getByRole("menuitem", { name: "Rename" }).click();
@@ -290,7 +290,7 @@ test("delete chat: sidebar delete action removes the chat from history", async (
290290
await expect(targetTitle).toBeVisible({ timeout: 10_000 });
291291
// The row wrapper that contains that title button (for reaching its menu).
292292
const targetRow = page
293-
.locator("div.group.relative.h-9.rounded-md")
293+
.locator("div.group.relative.h-8.rounded-md")
294294
.filter({ has: targetTitle });
295295

296296
// ── Step 5-7: delete that specific chat, riding out flaky Supabase 500s ──────
@@ -397,7 +397,9 @@ test("project assistant: create a new chat and submit a question", async ({ page
397397
// found". A genuinely broken assistant tab never shows the button on any
398398
// attempt, so the final assertion still fails.
399399
const assistantUrl = page.url() + "/assistant";
400-
const createNewBtn = page.getByText("+ Create New");
400+
// The olp UI replaced the old "+ Create New" text link with a PillButton
401+
// reading "Create" in the ProjectAssistantTable empty state.
402+
const createNewBtn = page.getByRole("button", { name: "Create", exact: true });
401403
const projectNotFound = page.getByText("Project not found");
402404
const TAB_ATTEMPTS = 4;
403405
for (let attempt = 0; attempt < TAB_ATTEMPTS; attempt++) {
@@ -452,7 +454,7 @@ test("project assistant: create a new chat and submit a question", async ({ page
452454
// ── Step 9: assert the ChatInput textarea is visible ─────────────────────────
453455
// ProjectAssistantChatPage renders <ChatInput> in the right "Project Assistant"
454456
// panel (line 1221-1229 of the chat page component).
455-
const chatInput = page.getByPlaceholder("Ask a question about your documents...");
457+
const chatInput = page.getByPlaceholder("How can I help?");
456458
await expect(chatInput).toBeVisible({ timeout: 10_000 });
457459

458460
// ── Step 10-11: pick an available model, submit a question, assert it clears ──

e2e/critical-path.spec.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -143,9 +143,11 @@ test("create project, upload PDF, ask a question and receive a response", async
143143
the "Assistant" item in the sidebar nav. The workspace fetches
144144
getProject() on mount and does NOT retry, so under the local-Supabase load
145145
the page can land on a permanent "Project not found" or a slow skeleton;
146-
re-navigate until the assistant tab's "+ Create New" affordance renders. */
146+
re-navigate until the assistant tab's empty-state "Create" affordance
147+
renders. (The olp UI replaced the old "+ Create New" text link with a
148+
PillButton reading "Create" — ProjectAssistantTable empty state.) */
147149
const projectUrl = page.url().split("?")[0];
148-
const createNew = page.getByText("+ Create New");
150+
const createNew = page.getByRole("button", { name: "Create", exact: true });
149151
for (let attempt = 1; attempt <= 6; attempt++) {
150152
await page.goto(`${projectUrl}/assistant`);
151153
await page
@@ -163,9 +165,7 @@ test("create project, upload PDF, ask a question and receive a response", async
163165
await page.waitForLoadState("networkidle", { timeout: 20_000 }).catch(() => {});
164166

165167
/* ── Step 7: select the keyless demo model, type a question, submit ───── */
166-
const chatInput = page.getByPlaceholder(
167-
"Ask a question about your documents...",
168-
);
168+
const chatInput = page.getByPlaceholder("How can I help?");
169169
await expect(chatInput).toBeVisible({ timeout: 10_000 });
170170

171171
/* The default Gemini model has no key configured, so submitting it would be

e2e/project-management.spec.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,11 @@ test("create a folder inside a project", { timeout: 60_000 }, async ({ page }) =
291291
* "Add Subfolder") only appears once the project has loaded, so reload until
292292
* it does.
293293
*/
294-
const addSubfolderBtn = page.getByRole("button", { name: "Add Subfolder" });
294+
/* The olp UI renamed the documents-toolbar folder-create button from
295+
"Add Subfolder" to "Folder" (a TabPillButton wired to the root
296+
createFolderAction — ProjectDocumentsView). Clicking it still renders the
297+
autofocused "Folder name" input at root level (creatingIn === null). */
298+
const addSubfolderBtn = page.getByRole("button", { name: "Folder" });
295299
await waitForProjectLoaded(page, addSubfolderBtn);
296300

297301
/* Confirm the uploaded document rendered, i.e. the project is non-empty and

e2e/tabular-reviews.spec.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,12 @@ async function createReview(
133133
const respP = page
134134
.waitForResponse(isCreateReviewPost, { timeout: 30_000 })
135135
.catch(() => null);
136-
await page.getByRole("button", { name: "Create", exact: true }).click();
136+
// The modal footer's submit button and the page's own "Create" CTA both
137+
// read "Create"; scope to the modal's submit (button[name="modalAction"]
138+
// value="create-review") to avoid a strict-mode ambiguity.
139+
await page
140+
.locator('button[name="modalAction"][value="create-review"]')
141+
.click();
137142
const resp = await respP;
138143
if (resp && resp.ok()) {
139144
review = (await resp.json()) as { id: string };

0 commit comments

Comments
 (0)