179 lines
7.6 KiB
TypeScript
179 lines
7.6 KiB
TypeScript
import { test, expect } from "../../fixtures/test-base";
|
||
import { MobileGitHubPage } from "../../pages/mobile-github-page";
|
||
import { waitForFiniteAnimations } from "../../helpers/animations";
|
||
|
||
// @covers AC-INTEGRATIONS-GITHUB-MOBILE-001.3
|
||
// @covers AC-INTEGRATIONS-GITHUB-MOBILE-001.4
|
||
// @covers AC-INTEGRATIONS-GITHUB-MOBILE-001.5
|
||
test("phone results wrap long content, expose touch actions, and support every capped page", async ({
|
||
testPage,
|
||
apiClient,
|
||
}) => {
|
||
await apiClient.mockGitHubReset();
|
||
await apiClient.mockGitHubSetUser("test-user");
|
||
await apiClient.mockGitHubAddPRs(
|
||
Array.from({ length: 1050 }, (_, index) => ({
|
||
number: index + 1,
|
||
title: `Result ${index + 1}: A long pull request title that remains readable on a narrow phone screen`,
|
||
state: "open",
|
||
head_branch: `feature/${index}`,
|
||
base_branch: "main",
|
||
author_login: "test-user",
|
||
repo_owner: "a-long-organization-name",
|
||
repo_name: "a-very-long-repository-name-without-any-spaces",
|
||
})),
|
||
);
|
||
// The mock provider returns every seeded PR; model GitHub's bounded page response.
|
||
let resultTotal = 1050;
|
||
await testPage.route("**/api/v1/github/user/prs?**", async (route) => {
|
||
const response = await route.fetch();
|
||
const data = await response.json();
|
||
const params = new URL(route.request().url()).searchParams;
|
||
const page = Number(params.get("page") ?? 1);
|
||
const pageSize = Number(params.get("per_page") ?? 25);
|
||
await route.fulfill({
|
||
response,
|
||
json: {
|
||
...data,
|
||
total_count: resultTotal,
|
||
prs: data.prs.slice(0, resultTotal).slice((page - 1) * pageSize, page * pageSize),
|
||
},
|
||
});
|
||
});
|
||
await testPage.setViewportSize({ width: 320, height: 640 });
|
||
const github = new MobileGitHubPage(testPage);
|
||
await github.goto();
|
||
const query = testPage.getByPlaceholder(/Custom query/);
|
||
await query.fill("is:open");
|
||
await query.press("Enter");
|
||
await expect(testPage.getByTestId("pr-row")).toHaveCount(25);
|
||
const chooser = testPage.locator(
|
||
'button[aria-label="Choose results page"],select[aria-label="Choose results page"]',
|
||
);
|
||
await expect(chooser).toBeVisible();
|
||
expect(await chooser.evaluate((element) => element.tagName)).toBe("BUTTON");
|
||
expect((await chooser.boundingBox())!.height).toBeGreaterThanOrEqual(44);
|
||
await expect(testPage.getByRole("button", { name: "Previous page", exact: true })).toBeDisabled();
|
||
const firstRow = testPage.getByTestId("pr-row").first();
|
||
await expect(firstRow).toBeVisible();
|
||
expect(await firstRow.evaluate((element) => element.scrollWidth <= element.clientWidth)).toBe(
|
||
true,
|
||
);
|
||
const taskAction = firstRow.getByTestId("pr-start-task-trigger");
|
||
expect((await taskAction.boundingBox())!.height).toBeGreaterThanOrEqual(44);
|
||
await taskAction.tap();
|
||
await expect(testPage.getByTestId("pr-start-task-preset").first()).toBeVisible();
|
||
expect(
|
||
(await testPage.getByTestId("pr-start-task-preset").first().boundingBox())!.height,
|
||
).toBeGreaterThanOrEqual(44);
|
||
await testPage.keyboard.press("Escape");
|
||
await chooser.tap();
|
||
const pages = testPage.getByRole("dialog", { name: "Choose results page", exact: true });
|
||
await expect(pages).toBeVisible();
|
||
await waitForFiniteAnimations(pages);
|
||
await expect(pages.getByRole("button", { name: /^Page \d+ of 40$/ })).toHaveCount(40);
|
||
const pageFive = pages.getByRole("button", { name: "Page 5 of 40", exact: true });
|
||
expect((await pageFive.boundingBox())!.height).toBeGreaterThanOrEqual(44);
|
||
await pageFive.tap();
|
||
await expect(pages).toBeHidden();
|
||
await expect(chooser).toBeFocused();
|
||
await expect(testPage.getByText("101–125 of 1000+", { exact: true })).toBeVisible();
|
||
await chooser.tap();
|
||
const lastPage = pages.getByRole("button", { name: "Page 40 of 40", exact: true });
|
||
await lastPage.scrollIntoViewIfNeeded();
|
||
await lastPage.tap();
|
||
await expect(testPage.getByText("976–1000 of 1000+", { exact: true })).toBeVisible();
|
||
await chooser.tap();
|
||
await expect(lastPage).toBeInViewport();
|
||
await expect(lastPage).toHaveAttribute("aria-current", "page");
|
||
await testPage.keyboard.press("Escape");
|
||
await expect(pages).toBeHidden();
|
||
await expect(chooser).toBeFocused();
|
||
await expect(testPage.getByRole("button", { name: "Next page", exact: true })).toBeDisabled();
|
||
resultTotal = 50;
|
||
await testPage.getByRole("button", { name: "Refresh", exact: true }).tap();
|
||
await expect(chooser).toContainText("Page 40 of 2");
|
||
await chooser.tap();
|
||
await expect(pages.getByRole("button", { name: "Done", exact: true })).toBeFocused();
|
||
await pages.getByRole("button", { name: "Page 2 of 2", exact: true }).tap();
|
||
await expect(testPage.getByText("26–50 of 50", { exact: true })).toBeVisible();
|
||
await expect(chooser).toBeFocused();
|
||
expect(
|
||
await testPage.evaluate(() => document.documentElement.scrollWidth <= window.innerWidth),
|
||
).toBe(true);
|
||
await testPage.setViewportSize({ width: 767, height: 640 });
|
||
await expect(chooser).toBeVisible();
|
||
await testPage.setViewportSize({ width: 768, height: 640 });
|
||
await expect(chooser).toBeHidden();
|
||
await expect(github.inlineSidebar).toBeVisible();
|
||
});
|
||
|
||
// @covers AC-INTEGRATIONS-GITHUB-MOBILE-001.2
|
||
// @covers AC-INTEGRATIONS-GITHUB-MOBILE-001.5
|
||
test("long view names do not compress result counts and refresh metadata", async ({
|
||
testPage,
|
||
apiClient,
|
||
seedData,
|
||
}) => {
|
||
await apiClient.mockGitHubReset();
|
||
await apiClient.mockGitHubSetUser("test-user");
|
||
await apiClient.mockGitHubAddPRs([
|
||
{
|
||
number: 1,
|
||
title: "Harbor dashboard",
|
||
state: "open",
|
||
head_branch: "feature/mobile",
|
||
base_branch: "main",
|
||
author_login: "test-user",
|
||
repo_owner: "harbor",
|
||
repo_name: "web",
|
||
},
|
||
]);
|
||
const settings = await apiClient.rawRequest(
|
||
"GET",
|
||
`/api/v1/github/workspace-settings?workspace_id=${seedData.workspaceId}`,
|
||
);
|
||
const baseline = await settings.json();
|
||
const saved = await apiClient.rawRequest("PUT", "/api/v1/github/workspace-settings", {
|
||
workspace_id: seedData.workspaceId,
|
||
saved_presets: [
|
||
{
|
||
id: "long-toolbar-view",
|
||
kind: "pr",
|
||
label: "Harbor navigation improvements ready for mobile review",
|
||
customQuery: "is:open",
|
||
repoFilter: "",
|
||
createdAt: "2026-09-11T00:00:00Z",
|
||
isDefault: true,
|
||
},
|
||
],
|
||
});
|
||
expect(saved.ok).toBe(true);
|
||
try {
|
||
await testPage.setViewportSize({ width: 320, height: 640 });
|
||
const github = new MobileGitHubPage(testPage);
|
||
await github.goto();
|
||
await expect(testPage.getByTestId("pr-row")).toBeVisible();
|
||
const refresh = testPage.getByRole("button", { name: "Refresh", exact: true });
|
||
const viewBounds = (await github.mobileMenuButton.boundingBox())!;
|
||
const refreshBounds = (await refresh.boundingBox())!;
|
||
expect(refreshBounds.y).toBeGreaterThanOrEqual(viewBounds.y + viewBounds.height);
|
||
expect(viewBounds.height).toBeLessThanOrEqual(48);
|
||
await expect(testPage.getByTestId("integration-mobile-result-count")).toHaveText("Result 1");
|
||
expect(
|
||
await testPage.evaluate(() => document.documentElement.scrollWidth <= window.innerWidth),
|
||
).toBe(true);
|
||
await testPage.setViewportSize({ width: 767, height: 640 });
|
||
await expect(testPage.getByTestId("integration-mobile-result-count")).toBeVisible();
|
||
await testPage.setViewportSize({ width: 768, height: 640 });
|
||
await expect(testPage.getByTestId("integration-mobile-result-count")).toBeHidden();
|
||
await expect(github.inlineSidebar).toBeVisible();
|
||
} finally {
|
||
const restored = await apiClient.rawRequest("PUT", "/api/v1/github/workspace-settings", {
|
||
workspace_id: seedData.workspaceId,
|
||
saved_presets: baseline.saved_presets ?? [],
|
||
});
|
||
expect(restored.ok).toBe(true);
|
||
}
|
||
});
|