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(); }); });