Files
kandev/apps/web/e2e/tests/github/mobile-github-sidebar.spec.ts
T
João Salavisa 5ee5161166 feat(ui): confirm saved view deletion (#3461)
Co-authored-by: Kandev Agent <agent@kandev.dev>
2026-09-07 16:14:58 +01:00

304 lines
11 KiB
TypeScript

import { test, expect } from "../../fixtures/test-base";
import { MobileGitHubPage } from "../../pages/mobile-github-page";
const DEFAULT_REPO = "mobileorg/defaultrepo";
const DEFAULT_REPO_ISSUE = "Mobile issue in saved default repo";
const OTHER_REPO_ISSUE = "Mobile issue outside saved default repo";
const SAVED_QUERY_FILTER = "assignee:@me is:open mobile-saved-default-repo-e2e";
const DEFAULTS_TIMESTAMP = "2026-08-07T00:00:00Z";
test.describe("Mobile /github sidebar", () => {
test.describe("saved default views", () => {
test.afterEach(async ({ testPage, seedData }) => {
const cleanupResponse = await testPage.request.put("/api/v1/github/workspace-settings", {
data: { workspace_id: seedData.workspaceId, saved_presets: [] },
});
expect(cleanupResponse.ok(), "afterEach saved-presets cleanup").toBe(true);
});
test("saved default action is touch-sized, isolated, and persistent", async ({
testPage,
apiClient,
seedData,
}) => {
const savedLabel = "Mobile future issues";
await apiClient.mockGitHubReset();
await apiClient.mockGitHubSetUser("test-user");
await apiClient.mockGitHubAddIssues([
{
number: 190,
title: DEFAULT_REPO_ISSUE,
state: "open",
author_login: "test-user",
repo_owner: "mobileorg",
repo_name: "defaultrepo",
assignees: ["test-user"],
},
]);
const seedResponse = await testPage.request.put("/api/v1/github/workspace-settings", {
data: {
workspace_id: seedData.workspaceId,
saved_presets: [
{
id: "mobile-saved-issue",
kind: "issue",
label: savedLabel,
customQuery: "assignee:@me is:open",
repoFilter: DEFAULT_REPO,
createdAt: DEFAULTS_TIMESTAMP,
isDefault: false,
},
],
},
});
expect(seedResponse.ok()).toBe(true);
const page = new MobileGitHubPage(testPage);
await page.goto();
await page.mobileMenuButton.tap();
await page.mobileSidebar.getByRole("button", { name: "Issues", exact: true }).tap();
await expect(page.toolbarTitle).toContainText("Assigned");
await page.mobileMenuButton.tap();
const defaultAction = page.savedQueryDefaultAction(savedLabel);
const actionBox = await defaultAction.boundingBox();
expect(actionBox).not.toBeNull();
expect(actionBox!.height).toBeGreaterThanOrEqual(44);
expect(actionBox!.width).toBeGreaterThanOrEqual(44);
const setResponse = testPage.waitForResponse(
(response) =>
response.url().includes("/api/v1/github/workspace-settings") &&
response.request().method() === "PUT" &&
response.status() === 200,
);
await defaultAction.tap();
await setResponse;
await expect(page.mobileSidebar).toBeVisible();
await expect(page.toolbarTitle).toContainText("Assigned");
await expect(page.savedQueryDefaultAction(savedLabel, "Clear")).toHaveAttribute(
"aria-pressed",
"true",
);
expect(
await testPage.evaluate(
() => document.documentElement.scrollWidth <= document.documentElement.clientWidth,
),
).toBe(true);
await testPage.reload();
await page.mobileMenuButton.waitFor({ state: "visible" });
await page.mobileMenuButton.tap();
await page.mobileSidebar.getByRole("button", { name: "Issues", exact: true }).tap();
await expect(page.toolbarTitle).toContainText(savedLabel);
await expect(page.repoFilterTrigger).toContainText(DEFAULT_REPO);
});
});
test("hamburger opens sheet and selecting a preset updates the toolbar", async ({
testPage,
apiClient,
}) => {
await apiClient.mockGitHubReset();
await apiClient.mockGitHubSetUser("test-user");
await apiClient.mockGitHubAddPRs([
{
number: 200,
title: "Mobile sidebar PR",
state: "open",
head_branch: "feat/mobile",
base_branch: "main",
author_login: "test-user",
repo_owner: "testorg",
repo_name: "testrepo",
},
]);
const page = new MobileGitHubPage(testPage);
await page.goto();
// On a mobile viewport the inline desktop sidebar is hidden and the
// hamburger menu button is visible.
await expect(page.mobileMenuButton).toBeVisible();
await expect(page.inlineSidebar).toBeHidden();
// Default selection is the first PR preset.
await expect(page.toolbarTitle).toContainText("Review requested");
// Sheet is closed initially.
await expect(page.mobileSidebar).toBeHidden();
// Open the drawer.
await page.mobileMenuButton.tap();
await expect(page.mobileSidebar).toBeVisible();
// Tap a different preset → the drawer closes and the toolbar title
// reflects the new selection.
await page.presetByLabel("Mentions").tap();
await expect(page.mobileSidebar).toBeHidden();
await expect(page.toolbarTitle).toContainText("Mentions");
});
test("repo filter menu can be searched", async ({ testPage, apiClient }) => {
await apiClient.mockGitHubReset();
await apiClient.mockGitHubSetUser("test-user");
await apiClient.mockGitHubAddPRs([
{
number: 210,
title: "Mobile repo search PR",
state: "open",
head_branch: "feat/mobile-search",
base_branch: "main",
author_login: "test-user",
repo_owner: "testorg",
repo_name: "testrepo",
},
{
number: 211,
title: "Mobile repo search second PR",
state: "open",
head_branch: "feat/mobile-other",
base_branch: "main",
author_login: "test-user",
repo_owner: "anotherorg",
repo_name: "secondrepo",
},
]);
const page = new MobileGitHubPage(testPage);
await page.goto();
await page.repoFilterTrigger.tap();
await expect(page.repoSearchInput).toBeVisible();
await page.repoSearchInput.fill("testorg");
await testPage.getByRole("option", { name: "testorg/testrepo" }).tap();
await expect(page.repoFilterTrigger).toContainText("testorg/testrepo");
});
test("saved query defaults to its chosen repository and persists by touch", async ({
testPage,
apiClient,
prCapture,
}) => {
const savedQuery = "Mobile default repo issues";
await apiClient.mockGitHubReset();
await apiClient.mockGitHubSetUser("test-user");
await apiClient.mockGitHubAddIssues([
{
number: 220,
title: DEFAULT_REPO_ISSUE,
state: "open",
author_login: "test-user",
repo_owner: "mobileorg",
repo_name: "defaultrepo",
assignees: ["test-user"],
},
{
number: 221,
title: OTHER_REPO_ISSUE,
state: "open",
author_login: "test-user",
repo_owner: "otherorg",
repo_name: "otherrepo",
assignees: ["test-user"],
},
]);
const page = new MobileGitHubPage(testPage);
await page.goto();
await page.mobileMenuButton.tap();
await page.mobileSidebar.getByRole("button", { name: "Issues", exact: true }).tap();
const queryInput = testPage.getByPlaceholder(/Custom query/);
await queryInput.fill(SAVED_QUERY_FILTER);
await queryInput.press("Enter");
await expect(testPage.getByTestId("issue-row")).toHaveCount(2, { timeout: 15_000 });
await page.mobileMenuButton.tap();
await page.mobileSidebar.getByRole("button", { name: "Save current query" }).tap();
const dialog = testPage.getByRole("dialog", { name: "Save query" });
await dialog.getByLabel("Name").fill(savedQuery);
await expect(page.saveQueryRepoTrigger).toBeVisible();
await expect
.poll(async () => (await page.saveQueryRepoTrigger.boundingBox())?.height ?? 0)
.toBeCloseTo(44, 0);
await page.saveQueryRepoTrigger.tap();
await expect(page.saveQueryRepoDropdown).toBeVisible();
await page.saveQueryRepoDropdown.getByRole("option", { name: DEFAULT_REPO, exact: true }).tap();
const saveResponse = testPage.waitForResponse(
(response) =>
response.url().includes("/api/v1/github/workspace-settings") &&
response.request().method() === "PUT" &&
response.status() === 200,
);
await dialog.getByRole("button", { name: "Save", exact: true }).tap();
await saveResponse;
await expect(page.repoFilterTrigger).toContainText(DEFAULT_REPO);
await expect(page.issueRowByTitle(DEFAULT_REPO_ISSUE)).toBeVisible();
await expect(page.issueRowByTitle(OTHER_REPO_ISSUE)).toHaveCount(0);
await page.repoFilterTrigger.tap();
await testPage
.getByTestId("github-repo-filter-dropdown")
.getByRole("option", { name: "All repos", exact: true })
.tap();
await expect(testPage.getByTestId("issue-row")).toHaveCount(2);
await page.mobileMenuButton.tap();
await page.savedQueryByLabel(savedQuery).tap();
await expect(page.repoFilterTrigger).toContainText(DEFAULT_REPO);
await expect(page.issueRowByTitle(DEFAULT_REPO_ISSUE)).toBeVisible();
await expect(page.issueRowByTitle(OTHER_REPO_ISSUE)).toHaveCount(0);
await testPage.reload();
await page.mobileMenuButton.waitFor({ state: "visible" });
await page.mobileMenuButton.tap();
await page.mobileSidebar.getByRole("button", { name: "Issues", exact: true }).tap();
await page.mobileMenuButton.tap();
await page.savedQueryByLabel(savedQuery).tap();
await expect(page.repoFilterTrigger).toContainText(DEFAULT_REPO);
await expect(page.issueRowByTitle(DEFAULT_REPO_ISSUE)).toBeVisible();
await expect(page.issueRowByTitle(OTHER_REPO_ISSUE)).toHaveCount(0);
expect(
await testPage.evaluate(
() => document.documentElement.scrollWidth <= document.documentElement.clientWidth,
),
).toBe(true);
await page.mobileMenuButton.tap();
const deleteAction = page.mobileSidebar.getByRole("button", {
name: `Delete ${savedQuery} saved query`,
});
const deleteBox = await deleteAction.boundingBox();
expect(deleteBox?.height ?? 0).toBeGreaterThanOrEqual(44);
expect(deleteBox?.width ?? 0).toBeGreaterThanOrEqual(44);
await deleteAction.tap();
const deleteConfirmation = page.mobileSidebar.getByTestId(
"saved-task-view-delete-confirmation",
);
await expect(deleteConfirmation).toHaveAccessibleName(`Delete ${savedQuery}?`);
await expect(testPage.locator('[role="dialog"]:visible')).toHaveCount(1);
await prCapture.screenshot("saved-query-delete-mobile", {
caption: "A saved integration query confirms inline without nesting another sheet.",
});
await deleteConfirmation.getByRole("button", { name: "Cancel" }).tap();
await expect(page.savedQueryByLabel(savedQuery)).toBeVisible();
await deleteAction.tap();
const deleteResponse = testPage.waitForResponse(
(response) =>
response.ok() &&
response.request().method() === "PUT" &&
response.url().includes("/api/v1/github/workspace-settings"),
);
await page.mobileSidebar
.getByTestId("saved-task-view-delete-confirmation")
.getByRole("button", { name: `Delete ${savedQuery}` })
.tap();
await deleteResponse;
await expect(page.savedQueryByLabel(savedQuery)).toHaveCount(0);
await expect(page.mobileSidebar).toBeVisible();
});
});