forked from farhoodlabs/paperclip
32605b71ad
## Summary - Removes the amber "Planning" pill from inbox / issue-list rows in `IssueRow` - Updates the focused IssueRow test to assert the badge is no longer rendered - Per [PAP-9691](https://paperclip.ing/PAP/issues/PAP-9691): user just doesn't want to see the badge in list rows The underlying `issue.workMode === "planning"` data, the issue detail composer toggle, and the server/plugin/heartbeat work-mode contract introduced in #5353 are all untouched. Planning mode still functions; the list-row indicator is just gone. ## Test plan - [x] `pnpm exec vitest run --project @paperclipai/ui ui/src/components/IssueRow.test.tsx` (11 passed) - [ ] Visual: open `/PAP/inbox` with a planning-mode issue assigned and confirm no amber Planning pill on the row 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Paperclip <noreply@paperclip.ing> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
158 lines
6.8 KiB
TypeScript
158 lines
6.8 KiB
TypeScript
import { expect, test } from "@playwright/test";
|
|
|
|
const SKIP_LLM = process.env.PAPERCLIP_E2E_SKIP_LLM !== "false";
|
|
|
|
const AGENT_NAME = "CEO";
|
|
const TASK_TITLE = "PAP-3413 planning mode evidence";
|
|
|
|
test("captures planning mode UI for desktop and mobile", async ({ page }) => {
|
|
const timestamp = Date.now();
|
|
const companyName = `PAP-3413-${timestamp}`;
|
|
const screenshotDir = "test-results/planning-mode";
|
|
|
|
await page.goto("/onboarding");
|
|
await expect(page.locator("h3", { hasText: "Name your company" })).toBeVisible({ timeout: 5_000 });
|
|
|
|
await page.locator('input[placeholder="Acme Corp"]').fill(companyName);
|
|
await page.getByRole("button", { name: "Next" }).click();
|
|
|
|
await expect(page.locator("h3", { hasText: "Create your first agent" })).toBeVisible({ timeout: 30_000 });
|
|
await expect(page.locator('input[placeholder="CEO"]')).toHaveValue(AGENT_NAME);
|
|
await page.getByRole("button", { name: "Next" }).click();
|
|
|
|
await expect(page.locator("h3", { hasText: "Give it something to do" })).toBeVisible({ timeout: 30_000 });
|
|
const baseUrl = page.url().split("/").slice(0, 3).join("/");
|
|
|
|
if (SKIP_LLM) {
|
|
const companiesAfterAgentRes = await page.request.get(`${baseUrl}/api/companies`);
|
|
expect(companiesAfterAgentRes.ok()).toBe(true);
|
|
const companiesAfterAgent = await companiesAfterAgentRes.json();
|
|
const companyAfterAgent = companiesAfterAgent.find((c: { name: string }) => c.name === companyName);
|
|
expect(companyAfterAgent).toBeTruthy();
|
|
|
|
const agentsAfterCreateRes = await page.request.get(`${baseUrl}/api/companies/${companyAfterAgent.id}/agents`);
|
|
expect(agentsAfterCreateRes.ok()).toBe(true);
|
|
const agentsAfterCreate = await agentsAfterCreateRes.json();
|
|
const ceoAgentAfterCreate = agentsAfterCreate.find((a: { name: string }) => a.name === AGENT_NAME);
|
|
expect(ceoAgentAfterCreate).toBeTruthy();
|
|
|
|
const disableWakeRes = await page.request.patch(
|
|
`${baseUrl}/api/agents/${ceoAgentAfterCreate.id}?companyId=${encodeURIComponent(companyAfterAgent.id)}`,
|
|
{
|
|
data: {
|
|
runtimeConfig: {
|
|
heartbeat: {
|
|
enabled: false,
|
|
intervalSec: 300,
|
|
wakeOnDemand: false,
|
|
cooldownSec: 10,
|
|
maxConcurrentRuns: 5,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
);
|
|
expect(disableWakeRes.ok()).toBe(true);
|
|
}
|
|
|
|
const taskTitleInput = page.locator('input[placeholder="e.g. Research competitor pricing"]');
|
|
await taskTitleInput.clear();
|
|
await taskTitleInput.fill(TASK_TITLE);
|
|
await page.getByRole("button", { name: "Next" }).click();
|
|
|
|
await expect(page.locator("h3", { hasText: "Ready to launch" })).toBeVisible({ timeout: 30_000 });
|
|
await page.getByRole("button", { name: "Create & Open Issue" }).click();
|
|
await expect(page).toHaveURL(/\/issues\//, { timeout: 30_000 });
|
|
|
|
const openedIssueUrl = page.url();
|
|
const openedIssueIdentifier = openedIssueUrl.split("/").filter(Boolean).pop();
|
|
const baseOrigin = new URL(openedIssueUrl).origin;
|
|
const companyRes = await page.request.get(`${baseOrigin}/api/companies`);
|
|
expect(companyRes.ok()).toBe(true);
|
|
const companies = await companyRes.json();
|
|
const company = companies.find((c: { name: string }) => c.name === companyName);
|
|
expect(company).toBeTruthy();
|
|
const issueRes = await page.request.get(`${baseOrigin}/api/companies/${company.id}/issues`);
|
|
expect(issueRes.ok()).toBe(true);
|
|
const issues = await issueRes.json();
|
|
const planningSeedIssue = issues.find(
|
|
(candidate: { id: string; identifier?: string; title: string }) =>
|
|
candidate.identifier === openedIssueIdentifier || candidate.id === openedIssueIdentifier || candidate.title === TASK_TITLE,
|
|
);
|
|
expect(planningSeedIssue).toBeTruthy();
|
|
|
|
const issue = planningSeedIssue;
|
|
const issueIdentifier = issue.identifier ?? issue.id;
|
|
const issuePath = `/${company.issuePrefix ?? company.id}/issues/${issueIdentifier}`;
|
|
const companyPrefix = company.issuePrefix ?? company.id;
|
|
const issueLinkSelector = `a[href$="/issues/${issueIdentifier}"]`;
|
|
|
|
const setMode = async (mode: "standard" | "planning") => {
|
|
const patchRes = await page.request.patch(`${baseOrigin}/api/issues/${issue.id}`, {
|
|
data: { workMode: mode },
|
|
});
|
|
expect(patchRes.ok()).toBe(true);
|
|
await expect
|
|
.poll(async () => {
|
|
const currentRes = await page.request.get(`${baseOrigin}/api/issues/${issue.id}`);
|
|
expect(currentRes.ok()).toBe(true);
|
|
const current = await currentRes.json();
|
|
return current.workMode;
|
|
}, { timeout: 10_000 })
|
|
.toBe(mode);
|
|
};
|
|
|
|
await setMode("planning");
|
|
|
|
await page.goto(issuePath);
|
|
await expect(page.getByText("Planning").first()).toBeVisible();
|
|
await expect(page.getByTestId("issue-chat-composer")).toHaveAttribute("data-pending-work-mode", "planning");
|
|
const desktopPlanningToggle = page.getByTestId("issue-chat-composer-work-mode-toggle");
|
|
await expect(desktopPlanningToggle).toBeVisible();
|
|
await expect(desktopPlanningToggle).toHaveAttribute("data-pending-work-mode", "planning");
|
|
await expect(desktopPlanningToggle).toHaveAttribute("aria-pressed", "true");
|
|
|
|
await page.screenshot({
|
|
path: `${screenshotDir}/desktop-planning-detail-${timestamp}.png`,
|
|
fullPage: true,
|
|
});
|
|
|
|
await page.goto(`/${companyPrefix}/issues`);
|
|
await expect(page.locator(issueLinkSelector)).toBeVisible();
|
|
await expect(page.locator(issueLinkSelector)).not.toContainText("Planning");
|
|
await page.screenshot({
|
|
path: `${screenshotDir}/desktop-planning-row-${timestamp}.png`,
|
|
fullPage: true,
|
|
});
|
|
|
|
await page.goto(issuePath);
|
|
await page.getByTestId("issue-chat-composer-work-mode-toggle").click();
|
|
await expect(page.getByTestId("issue-chat-composer")).toHaveAttribute("data-pending-work-mode", "standard");
|
|
await expect(page.getByTestId("issue-chat-composer-work-mode-toggle")).toBeHidden();
|
|
await page.screenshot({
|
|
path: `${screenshotDir}/desktop-standard-toggle-${timestamp}.png`,
|
|
fullPage: true,
|
|
});
|
|
|
|
await setMode("planning");
|
|
await page.setViewportSize({ width: 390, height: 844 });
|
|
await page.goto(issuePath);
|
|
await expect(page.getByText("Planning").first()).toBeVisible();
|
|
const mobilePlanningToggle = page.getByTestId("issue-chat-composer-work-mode-toggle");
|
|
await expect(mobilePlanningToggle).toBeVisible();
|
|
await expect(mobilePlanningToggle).toHaveAttribute("data-pending-work-mode", "planning");
|
|
await expect(mobilePlanningToggle).toHaveAttribute("aria-pressed", "true");
|
|
await page.screenshot({
|
|
path: `${screenshotDir}/mobile-planning-detail-${timestamp}.png`,
|
|
fullPage: true,
|
|
});
|
|
|
|
await page.goto(`/${companyPrefix}/issues`);
|
|
await expect(page.locator(issueLinkSelector)).toBeVisible();
|
|
await expect(page.locator(issueLinkSelector)).not.toContainText("Planning");
|
|
await page.screenshot({
|
|
path: `${screenshotDir}/mobile-planning-row-${timestamp}.png`,
|
|
fullPage: true,
|
|
});
|
|
});
|