133 lines
4.7 KiB
TypeScript
133 lines
4.7 KiB
TypeScript
import { expect } from "@playwright/test";
|
|
import { test } from "../../fixtures/test-base";
|
|
import { SessionPage } from "../../pages/session-page";
|
|
|
|
const TARGET_TITLE = "Mobile inactive summary target";
|
|
|
|
test.describe("Mobile task status summary", () => {
|
|
test("keeps pending CI yellow in native task switcher status updates", async ({
|
|
testPage,
|
|
apiClient,
|
|
seedData,
|
|
prCapture,
|
|
}) => {
|
|
test.setTimeout(90_000);
|
|
|
|
const stepOptions = {
|
|
workflow_id: seedData.workflowId,
|
|
workflow_step_id: seedData.startStepId,
|
|
};
|
|
const navTask = await apiClient.seedTask(
|
|
seedData.workspaceId,
|
|
"Mobile summary navigation",
|
|
stepOptions,
|
|
);
|
|
const targetTask = await apiClient.seedTask(seedData.workspaceId, TARGET_TITLE, {
|
|
...stepOptions,
|
|
state: "IN_PROGRESS",
|
|
});
|
|
await apiClient.seedTaskSession(navTask.task_id, {
|
|
state: "WAITING_FOR_INPUT",
|
|
agentProfileId: seedData.agentProfileId,
|
|
});
|
|
const targetSession = await apiClient.seedTaskSession(targetTask.task_id, {
|
|
state: "WAITING_FOR_INPUT",
|
|
agentProfileId: seedData.agentProfileId,
|
|
});
|
|
|
|
await apiClient.mockGitHubAssociateTaskPR({
|
|
workspace_id: seedData.workspaceId,
|
|
task_id: targetTask.task_id,
|
|
owner: "kandev-e2e",
|
|
repo: "mobile-summary-fixtures",
|
|
pr_number: 43,
|
|
pr_url: "https://github.test/kandev-e2e/mobile-summary-fixtures/pull/43",
|
|
pr_title: "Mobile bounded summary fixture",
|
|
head_branch: "feature/mobile-summary",
|
|
base_branch: "main",
|
|
author_login: "e2e",
|
|
state: "open",
|
|
review_state: "approved",
|
|
checks_state: "pending",
|
|
mergeable_state: "blocked",
|
|
required_reviews: 1,
|
|
checks_total: 2,
|
|
checks_passing: 1,
|
|
});
|
|
|
|
await testPage.goto(`/t/${navTask.task_id}`);
|
|
const session = new SessionPage(testPage);
|
|
await session.waitForLoad();
|
|
await testPage.getByTestId("mobile-session-menu").click();
|
|
const sheet = testPage.getByRole("dialog");
|
|
const targetRow = sheet.getByTestId("sidebar-task-item").filter({
|
|
hasText: TARGET_TITLE,
|
|
});
|
|
await expect(targetRow).toBeVisible({ timeout: 15_000 });
|
|
|
|
await apiClient.seedTaskSession(targetTask.task_id, {
|
|
sessionId: targetSession.session_id,
|
|
state: "RUNNING",
|
|
});
|
|
const runningStatus = targetRow.getByTestId("task-state-running");
|
|
await expect(runningStatus).toBeVisible({ timeout: 15_000 });
|
|
await expect
|
|
.poll(async () =>
|
|
runningStatus.evaluate((element) => ({
|
|
tagName: element.tagName,
|
|
svgAnimated: element.querySelector("svg")?.classList.contains("animate-spin") ?? false,
|
|
})),
|
|
)
|
|
.toEqual({ tagName: "SPAN", svgAnimated: false });
|
|
|
|
await apiClient.seedSessionMessage(targetSession.session_id, {
|
|
type: "clarification_request",
|
|
content: "Choose a mobile-safe approach",
|
|
});
|
|
await expect(targetRow.getByTestId("task-state-waiting-for-input")).toBeVisible({
|
|
timeout: 15_000,
|
|
});
|
|
|
|
await apiClient.seedTaskSession(targetTask.task_id, {
|
|
sessionId: targetSession.session_id,
|
|
state: "WAITING_FOR_INPUT",
|
|
metadata: {
|
|
last_agent_error: {
|
|
message: "Mobile inactive agent failed",
|
|
occurred_at: new Date().toISOString(),
|
|
},
|
|
},
|
|
});
|
|
await expect(targetRow.getByTestId("task-agent-error-icon")).toBeVisible({ timeout: 15_000 });
|
|
|
|
const prIcon = targetRow.getByTestId(`pr-task-icon-${targetTask.task_id}`);
|
|
await expect(prIcon).toHaveAttribute("data-pr-state", "Open", { timeout: 15_000 });
|
|
await expect(prIcon).toHaveClass(/text-yellow-500/);
|
|
await prCapture.screenshot("mobile-task-switcher-pending-ci", {
|
|
caption: "Mobile task switcher keeps pending CI yellow after reload",
|
|
});
|
|
|
|
const viewport = testPage.viewportSize();
|
|
expect(viewport).not.toBeNull();
|
|
expect(
|
|
await testPage.evaluate(
|
|
() => document.documentElement.scrollWidth <= document.documentElement.clientWidth,
|
|
),
|
|
).toBe(true);
|
|
expect(viewport!.width).toBeGreaterThan(0);
|
|
|
|
// The passive PR icon must not steal the row's native touch target.
|
|
await targetRow.tap();
|
|
await expect(testPage).toHaveURL(new RegExp(`/t/${targetTask.task_id}$`));
|
|
await session.waitForLoad();
|
|
await expect(session.prStatusChip()).toBeVisible({ timeout: 15_000 });
|
|
await session.tapPRStatusChip();
|
|
const author = session.prStatusChipDrawer().getByTestId("pr-popover-author");
|
|
await expect(author).toBeInViewport({ ratio: 1 });
|
|
await expect(author).toHaveText("by e2e");
|
|
await prCapture.screenshot("mobile-task-status-summary-author", {
|
|
caption: "Mobile PR status drawer with the linked author identity",
|
|
});
|
|
});
|
|
});
|