mirror of
https://github.com/block/buzz.git
synced 2026-10-27 04:30:52 +00:00
feat(desktop): make the Projects overview follow the selected section (#6335)
## Summary Part 1 of 4 stacked PRs continuing the Projects work from #6003. - The Projects overview now follows the selected section: the right-hand context pod stays visible across the Projects / Repositories / Reviews / Tasks / Channels tabs and shows section-specific people, stats, and contribution graphs, so it reads as live context rather than a detached summary. - Entity list rows are consolidated onto one compact line (title first, icons after, aligned dates/counts), shared across projects, repositories, PRs, issues, and channel lists via `ProjectEntityListRow`. - New `projectRelatedChannels` helper resolves the channels a project is discussed in for the overview. Follow-ups in this stack: part 2 (selectable workspaces), part 3 (context-aware collaboration), part 4 (navigation & detail-page polish). ## Test plan - [x] Desktop unit tests (`pnpm test`) — pass - [x] TypeScript (`tsc --noEmit`), Biome, clippy — clean via pre-push gate - [x] Playwright specs updated for the new overview behavior (`projects-v3-screenshots.spec.ts`, `sidebar.spec.ts`) — pass locally in the smoke project --------- Signed-off-by: Thomas Petersen <thomasp@squareup.com>
This commit is contained in:
@@ -57,7 +57,7 @@ test("top-level project lists align dates and overflow actions", async ({
|
||||
await page.goto("/", { waitUntil: "domcontentloaded" });
|
||||
await page.getByTestId("open-projects-view").click();
|
||||
await expect(
|
||||
page.getByRole("heading", { level: 1, name: "Projects" }),
|
||||
page.getByRole("heading", { level: 2, name: "Projects", exact: true }),
|
||||
).toBeVisible();
|
||||
|
||||
async function trailingPositions(
|
||||
@@ -103,18 +103,9 @@ test("top-level project lists align dates and overflow actions", async ({
|
||||
await expect(page.getByRole("menuitem", { name: "Local" })).toBeVisible();
|
||||
await page.keyboard.press("Escape");
|
||||
const projectRow = page.locator('[data-testid^="project-row-"]').first();
|
||||
const projectPositions = await trailingPositions(projectRow, {
|
||||
summaryTestId: "projects-row-summary",
|
||||
});
|
||||
// Project rows show the activity bar alone — counts stay in its tooltips.
|
||||
await expect(
|
||||
projectRow
|
||||
.getByTestId("projects-row-summary")
|
||||
.getByTestId("project-activity-bar"),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
projectRow.getByTestId("projects-row-summary"),
|
||||
).not.toContainText("commits");
|
||||
const projectPositions = await trailingPositions(projectRow);
|
||||
await expect(projectRow.getByTestId("projects-row-context")).toBeVisible();
|
||||
await expect(projectRow.getByTestId("projects-row-people")).toBeVisible();
|
||||
|
||||
await page.getByTestId("projects-section-repositories").click();
|
||||
await page.getByRole("button", { name: "Filter repositories" }).click();
|
||||
@@ -126,17 +117,14 @@ test("top-level project lists align dates and overflow actions", async ({
|
||||
await expect(page.getByTestId("repository-row-relay-tools")).toBeVisible();
|
||||
const repositoryRow = page.getByTestId("repository-row-buzz");
|
||||
await expect(
|
||||
repositoryRow.getByTestId("repositories-row-summary"),
|
||||
).toContainText("commits");
|
||||
repositoryRow.getByTestId("repositories-row-project"),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
repositoryRow.getByTestId("repositories-row-branch"),
|
||||
).toContainText("main");
|
||||
// Subtitle is the repository location (owner/repo for Buzz-hosted repos).
|
||||
await expect(repositoryRow.locator("p")).toHaveText(/\/buzz$/);
|
||||
repositoryRow.getByTestId("repositories-row-description"),
|
||||
).toContainText(/Relay, desktop, and mobile|community platform/);
|
||||
const repositoryPositions = await trailingPositions(repositoryRow, {
|
||||
actionName: /More options for/,
|
||||
dateTestId: "repositories-row-date",
|
||||
summaryTestId: "repositories-row-summary",
|
||||
});
|
||||
// No summaryX comparison: repository rows carry text stats next to the bar
|
||||
// while project rows show the bar alone, so the columns differ in width by
|
||||
@@ -219,11 +207,11 @@ test("top-level project lists align dates and overflow actions", async ({
|
||||
.locator('[data-testid^="project-row-"]')
|
||||
.first();
|
||||
await expect(
|
||||
responsiveRepositoryRow.getByTestId("projects-row-summary"),
|
||||
).toBeHidden();
|
||||
responsiveRepositoryRow.getByTestId("projects-row-context"),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
responsiveRepositoryRow.getByTestId("projects-row-people"),
|
||||
).toBeHidden();
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
responsiveRepositoryRow.getByTestId("projects-row-date"),
|
||||
).toBeVisible();
|
||||
@@ -470,7 +458,13 @@ test("multi-repository projects switch the active repository", async ({
|
||||
},
|
||||
);
|
||||
await projectRow.click();
|
||||
await expect(page).toHaveURL(/\/projects\//);
|
||||
await expect(page).not.toHaveURL(/\/projects\//);
|
||||
await expect(projectRow).toHaveAttribute("aria-expanded", "false");
|
||||
await expect(relayToolsRepository).toBeHidden();
|
||||
|
||||
await projectRow.click();
|
||||
await expect(page).not.toHaveURL(/\/projects\//);
|
||||
await expect(projectRow).toHaveAttribute("aria-expanded", "true");
|
||||
await expect(relayToolsRepository).toBeVisible();
|
||||
const projectSidebarMetrics = await sidebarScrollContent.evaluate(
|
||||
(element) => {
|
||||
@@ -496,7 +490,7 @@ test("multi-repository projects switch the active repository", async ({
|
||||
return element.scrollTop;
|
||||
});
|
||||
await projectRow.click();
|
||||
await expect(page).toHaveURL(/\/projects\//);
|
||||
await expect(page).not.toHaveURL(/\/projects\//);
|
||||
await expect(projectRow).toHaveAttribute("aria-expanded", "true");
|
||||
await expect
|
||||
.poll(() =>
|
||||
|
||||
@@ -867,9 +867,11 @@ test("project pull request author rollover stays identity-only", async ({
|
||||
),
|
||||
).toBeVisible();
|
||||
|
||||
const authorLabel = (
|
||||
await author.getByTestId("projects-pr-author-label").innerText()
|
||||
).trim();
|
||||
const authorLabel =
|
||||
(
|
||||
await author.getByTestId("projects-pr-author-label").textContent()
|
||||
)?.trim() ?? "";
|
||||
expect(authorLabel.length).toBeGreaterThan(0);
|
||||
await author.hover();
|
||||
const rollover = page.getByTestId("projects-pr-author-rollover");
|
||||
await expect(rollover).toBeVisible();
|
||||
@@ -898,9 +900,11 @@ test("project issue author rollover matches pull requests", async ({
|
||||
),
|
||||
).toBeVisible();
|
||||
|
||||
const authorLabel = (
|
||||
await author.getByTestId("projects-issue-author-label").innerText()
|
||||
).trim();
|
||||
const authorLabel =
|
||||
(
|
||||
await author.getByTestId("projects-issue-author-label").textContent()
|
||||
)?.trim() ?? "";
|
||||
expect(authorLabel.length).toBeGreaterThan(0);
|
||||
await author.hover();
|
||||
const rollover = page.getByTestId("projects-issue-author-rollover");
|
||||
await expect(rollover).toBeVisible();
|
||||
@@ -993,7 +997,7 @@ test("project overview reports aggregate work-item failures", async ({
|
||||
);
|
||||
});
|
||||
|
||||
test("project overview does not paint a background behind its cards", async ({
|
||||
test("project overview presents collapsible context beside grouped activity", async ({
|
||||
page,
|
||||
}) => {
|
||||
await enableProjectsFeature(page);
|
||||
@@ -1005,16 +1009,138 @@ test("project overview does not paint a background behind its cards", async ({
|
||||
"background-color",
|
||||
"rgba(0, 0, 0, 0)",
|
||||
);
|
||||
await expect(page.getByTestId("projects-overview-layout")).toHaveAttribute(
|
||||
"data-project-context-detached",
|
||||
"true",
|
||||
);
|
||||
const overviewLayout = page.getByTestId("projects-overview-layout");
|
||||
const overviewContentPod = page.getByTestId("projects-overview-content-pod");
|
||||
const appContentSurface = page
|
||||
.locator("[data-buzz-content-surface]")
|
||||
.filter({ has: overviewLayout })
|
||||
.first();
|
||||
await expect(appContentSurface).toHaveCSS(
|
||||
"background-color",
|
||||
"rgba(0, 0, 0, 0)",
|
||||
);
|
||||
await expect(appContentSurface).toHaveCSS("border-radius", "0px");
|
||||
await expect(overviewContentPod).toHaveCSS("border-radius", "16px");
|
||||
await expect(page.getByTestId("projects-workspace-chrome")).toContainText(
|
||||
"Projects",
|
||||
);
|
||||
await expect(page.getByTestId("projects-workspace-chrome")).toContainText(
|
||||
"Activity",
|
||||
);
|
||||
await expect(page.getByTestId("projects-page-tabs")).toBeVisible();
|
||||
await expect(page.getByTestId("projects-page-header")).toContainText(
|
||||
"Welcome to Activity",
|
||||
);
|
||||
await expect(page.getByTestId("projects-activity-search")).toBeVisible();
|
||||
await expect(page.getByTestId("projects-activity-intro")).toContainText(
|
||||
"Keep up with commits, reviews, and tasks",
|
||||
);
|
||||
await expect(
|
||||
page.getByTestId("projects-overview-context-panel"),
|
||||
).toBeVisible();
|
||||
await expect(page.getByTestId("projects-overview-context-title")).toHaveText(
|
||||
"Projects",
|
||||
);
|
||||
expect(
|
||||
Math.round(
|
||||
(await page.getByTestId("projects-overview-context-panel").boundingBox())
|
||||
?.width ?? 0,
|
||||
),
|
||||
).toBe(280);
|
||||
await expect(
|
||||
page.getByTestId("projects-overview-create-project"),
|
||||
).toContainText("Create project");
|
||||
await expect(
|
||||
page
|
||||
.getByTestId("projects-overview-context-panel")
|
||||
.getByTestId("projects-create-menu"),
|
||||
).toBeVisible();
|
||||
await expect(page.getByTestId("projects-overview-stats-pod")).toBeVisible();
|
||||
await expect(
|
||||
page
|
||||
.getByTestId("projects-overview-context-panel")
|
||||
.getByTestId("projects-overview-people"),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page
|
||||
.getByTestId("projects-overview-stats-pod")
|
||||
.getByTestId("projects-overview-people"),
|
||||
).toHaveCount(0);
|
||||
await expect(
|
||||
page
|
||||
.getByTestId("projects-overview-context-panel")
|
||||
.getByTestId("projects-overview-context-title"),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page
|
||||
.getByTestId("projects-overview-context-panel")
|
||||
.getByRole("heading", { name: "Details" }),
|
||||
).toHaveCount(0);
|
||||
await expect(
|
||||
page
|
||||
.getByTestId("projects-overview-context-panel")
|
||||
.getByRole("heading", { name: "Activity" }),
|
||||
).toHaveCount(0);
|
||||
await expect(
|
||||
page
|
||||
.getByTestId("projects-overview-context-panel")
|
||||
.getByTestId("projects-overview-activity"),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByTestId("projects-activity-group").first(),
|
||||
).toContainText(/This week|Last week|Earlier/);
|
||||
const searchBox = await page
|
||||
.getByTestId("projects-activity-search")
|
||||
.boundingBox();
|
||||
const tabMenuBox = await page.getByTestId("projects-page-tabs").boundingBox();
|
||||
const firstTabBox = await page
|
||||
.getByTestId("projects-section-all")
|
||||
.boundingBox();
|
||||
const introBox = await page
|
||||
.getByTestId("projects-activity-intro")
|
||||
.boundingBox();
|
||||
const feedBox = await page
|
||||
.getByTestId("projects-activity-group")
|
||||
.first()
|
||||
.boundingBox();
|
||||
const contentPodBox = await overviewContentPod.boundingBox();
|
||||
expect(searchBox).toBeTruthy();
|
||||
expect(tabMenuBox).toBeTruthy();
|
||||
expect(firstTabBox).toBeTruthy();
|
||||
expect(introBox).toBeTruthy();
|
||||
expect(feedBox).toBeTruthy();
|
||||
expect(contentPodBox).toBeTruthy();
|
||||
expect(searchBox?.x ?? 0).toBeLessThan(firstTabBox?.x ?? 0);
|
||||
expect(Math.abs((searchBox?.y ?? 0) - (firstTabBox?.y ?? 0))).toBeLessThan(8);
|
||||
expect(tabMenuBox?.y ?? 0).toBeLessThan(introBox?.y ?? 0);
|
||||
expect(introBox?.y ?? 0).toBeLessThan(feedBox?.y ?? 0);
|
||||
expect(feedBox?.width ?? 0).toBeLessThan((contentPodBox?.width ?? 0) - 48);
|
||||
const feedCenter = (feedBox?.x ?? 0) + (feedBox?.width ?? 0) / 2;
|
||||
const contentCenter =
|
||||
(contentPodBox?.x ?? 0) + (contentPodBox?.width ?? 0) / 2;
|
||||
expect(Math.abs(feedCenter - contentCenter)).toBeLessThan(24);
|
||||
|
||||
const stats = page.getByTestId("projects-overview-stat");
|
||||
await expect(stats).toHaveCount(4);
|
||||
for (let index = 0; index < 4; index += 1) {
|
||||
await expect(stats.nth(index)).toHaveCSS(
|
||||
"background-color",
|
||||
"rgba(0, 0, 0, 0)",
|
||||
);
|
||||
await expect(stats.nth(index)).toHaveCSS("border-style", "solid");
|
||||
}
|
||||
await expect(stats).toHaveCount(5);
|
||||
await expect(stats.nth(2)).toContainText("Channels");
|
||||
const createBox = await page
|
||||
.getByTestId("projects-overview-create-project")
|
||||
.boundingBox();
|
||||
const lastStatBox = await stats.last().boundingBox();
|
||||
const peopleBox = await page
|
||||
.getByTestId("projects-overview-people")
|
||||
.boundingBox();
|
||||
expect(createBox).toBeTruthy();
|
||||
expect(lastStatBox).toBeTruthy();
|
||||
expect(peopleBox).toBeTruthy();
|
||||
expect(peopleBox?.y ?? 0).toBeGreaterThan(createBox?.y ?? 0);
|
||||
expect(peopleBox?.y ?? 0).toBeGreaterThan(
|
||||
(lastStatBox?.y ?? 0) + (lastStatBox?.height ?? 0) - 1,
|
||||
);
|
||||
|
||||
const activityCards = page.getByTestId("projects-activity-card");
|
||||
await expect(activityCards.first()).toBeVisible();
|
||||
@@ -1026,6 +1152,104 @@ test("project overview does not paint a background behind its cards", async ({
|
||||
);
|
||||
await expect(activityCards.nth(index)).toHaveCSS("border-style", "solid");
|
||||
}
|
||||
|
||||
await page.getByTestId("projects-section-channels").click();
|
||||
await expect(page.getByTestId("projects-page-header")).toContainText(
|
||||
"Channels",
|
||||
);
|
||||
await expect(
|
||||
page.getByTestId("projects-overview-context-panel"),
|
||||
).toBeVisible();
|
||||
await expect(page.getByTestId("projects-overview-context-title")).toHaveText(
|
||||
"Channels",
|
||||
);
|
||||
await expect(
|
||||
page.getByTestId("projects-overview-create-project"),
|
||||
).toHaveCount(0);
|
||||
await expect(page.getByTestId("projects-overview-people")).toHaveCount(0);
|
||||
await expect(page.getByTestId("projects-overview-activity")).toHaveCount(0);
|
||||
await expect(stats).toHaveCount(3);
|
||||
await expect(stats.first()).toContainText("Channels");
|
||||
await expect(page.getByTestId("projects-overview-layout")).toHaveAttribute(
|
||||
"data-project-context-detached",
|
||||
"true",
|
||||
);
|
||||
const channelRows = page.getByTestId("project-channel-row");
|
||||
// One row per project or repository binding: buzz, buzz · relay-tools,
|
||||
// design-system, and relay-tools all bind the shared mock channel.
|
||||
await expect(channelRows).toHaveCount(4);
|
||||
await expect(channelRows.first()).toContainText("#general");
|
||||
const channelsList = page.getByTestId("projects-channels-list");
|
||||
await expect(channelsList).toContainText("buzz");
|
||||
await expect(channelsList).toContainText("relay-tools");
|
||||
await expect(channelsList).toContainText("design-system");
|
||||
await expect(page.getByTestId("project-channel-project")).toHaveCount(4);
|
||||
await expect(page.getByTestId("project-channel-repository")).toHaveCount(4);
|
||||
await page.getByTestId("projects-section-projects").click();
|
||||
await expect(page.getByTestId("projects-page-header")).toContainText(
|
||||
"Projects",
|
||||
);
|
||||
await expect(
|
||||
page.getByTestId("projects-overview-context-panel"),
|
||||
).toBeVisible();
|
||||
await expect(page.getByTestId("projects-overview-context-title")).toHaveText(
|
||||
"Projects",
|
||||
);
|
||||
await expect(
|
||||
page.getByTestId("projects-overview-create-project"),
|
||||
).toBeVisible();
|
||||
await expect(page.getByTestId("projects-overview-people")).toBeVisible();
|
||||
await expect(page.getByTestId("projects-overview-activity")).toBeVisible();
|
||||
await page.getByTestId("projects-section-repositories").click();
|
||||
await expect(page.getByTestId("projects-overview-context-title")).toHaveText(
|
||||
"Repositories",
|
||||
);
|
||||
await expect(
|
||||
page.getByTestId("projects-overview-create-project"),
|
||||
).toHaveCount(0);
|
||||
await expect(stats.nth(1)).toContainText("Active tasks");
|
||||
await expect(page.getByTestId("projects-overview-activity")).toBeVisible();
|
||||
await page.getByTestId("projects-section-issues").click();
|
||||
await expect(page.getByTestId("projects-overview-context-title")).toHaveText(
|
||||
"Tasks",
|
||||
);
|
||||
await expect(
|
||||
page.getByTestId("projects-overview-create-issue"),
|
||||
).toContainText("Create task");
|
||||
await expect(page.getByTestId("projects-overview-activity")).toHaveCount(0);
|
||||
await page.getByTestId("projects-section-prs").click();
|
||||
await expect(page.getByTestId("projects-overview-context-title")).toHaveText(
|
||||
"Reviews",
|
||||
);
|
||||
await expect(
|
||||
page.getByTestId("projects-overview-create-pull-request"),
|
||||
).toContainText("Create review");
|
||||
await expect(page.getByTestId("projects-overview-people")).toBeVisible();
|
||||
await expect(page.getByTestId("projects-overview-activity")).toHaveCount(0);
|
||||
await page.getByTestId("projects-section-all").click();
|
||||
await expect(
|
||||
page.getByTestId("projects-overview-context-panel"),
|
||||
).toBeVisible();
|
||||
await expect(page.getByTestId("projects-overview-context-title")).toHaveText(
|
||||
"Projects",
|
||||
);
|
||||
await expect(page.getByTestId("projects-overview-activity")).toBeVisible();
|
||||
|
||||
await page.getByTestId("projects-overview-create-project").click();
|
||||
await expect(page.getByTestId("create-project-dialog")).toBeVisible();
|
||||
await page.keyboard.press("Escape");
|
||||
await expect(page.getByTestId("create-project-dialog")).toBeHidden();
|
||||
|
||||
await page.getByTestId("projects-overview-context-toggle").click();
|
||||
await expect(
|
||||
page.getByTestId("projects-overview-context-panel"),
|
||||
).not.toBeVisible();
|
||||
await expect(
|
||||
page.getByTestId("projects-overview-layout"),
|
||||
).not.toHaveAttribute("data-project-context-detached", "true");
|
||||
await expect(stats).toHaveCount(0);
|
||||
await expect(activityCards.first()).toBeVisible();
|
||||
await expect(page.getByTestId("projects-create-menu")).toBeVisible();
|
||||
});
|
||||
|
||||
test("repository rows identify their git host", async ({ page }) => {
|
||||
@@ -1081,7 +1305,6 @@ test("project subsections do not paint backgrounds behind list or grid items", a
|
||||
"background-color",
|
||||
"rgba(0, 0, 0, 0)",
|
||||
);
|
||||
await expect(listItems.nth(index)).toHaveCSS("border-style", "solid");
|
||||
}
|
||||
|
||||
await page.getByRole("button", { name: "Grid layout" }).click();
|
||||
@@ -1595,3 +1818,68 @@ test("project task can be created with a category from the tasks header", async
|
||||
]);
|
||||
expect(createdEvent?.tags).toContainEqual(["t", "change-request"]);
|
||||
});
|
||||
|
||||
test("narrow layouts keep section context reachable through a sheet", async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.setViewportSize({ height: 720, width: 900 });
|
||||
await enableProjectsFeature(page);
|
||||
await installMockBridge(page);
|
||||
await page.goto("/", { waitUntil: "domcontentloaded" });
|
||||
await page.getByTestId("open-projects-view").click();
|
||||
|
||||
// Below the detached breakpoint the docked rail never renders, but the
|
||||
// context toggle stays available.
|
||||
await expect(page.getByTestId("projects-page-tabs")).toBeVisible();
|
||||
await expect(page.getByTestId("projects-overview-context-panel")).toHaveCount(
|
||||
0,
|
||||
);
|
||||
const contextToggle = page.getByTestId("projects-overview-context-toggle");
|
||||
await expect(contextToggle).toBeVisible();
|
||||
await expect(contextToggle).toHaveAttribute("aria-pressed", "false");
|
||||
|
||||
// Keyboard journey into the Tasks section: open the sheet from the toggle.
|
||||
await page.getByTestId("projects-section-issues").click();
|
||||
await expect(page.getByTestId("projects-page-header")).toContainText("Tasks");
|
||||
await contextToggle.focus();
|
||||
await page.keyboard.press("Enter");
|
||||
const contextSheet = page.getByTestId("projects-overview-context-sheet");
|
||||
await expect(contextSheet).toBeVisible();
|
||||
await expect(
|
||||
contextSheet.getByTestId("projects-overview-context-title"),
|
||||
).toHaveText("Tasks");
|
||||
await expect(contextToggle).toHaveAttribute("aria-pressed", "true");
|
||||
|
||||
// Escape dismisses the sheet and returns focus to the toggle.
|
||||
await page.keyboard.press("Escape");
|
||||
await expect(contextSheet).toBeHidden();
|
||||
await expect(contextToggle).toBeFocused();
|
||||
await expect(contextToggle).toHaveAttribute("aria-pressed", "false");
|
||||
|
||||
// The same journey follows the Reviews section.
|
||||
await page.getByTestId("projects-section-prs").click();
|
||||
await expect(page.getByTestId("projects-page-header")).toContainText(
|
||||
"Reviews",
|
||||
);
|
||||
await contextToggle.focus();
|
||||
await page.keyboard.press("Enter");
|
||||
await expect(contextSheet).toBeVisible();
|
||||
await expect(
|
||||
contextSheet.getByTestId("projects-overview-context-title"),
|
||||
).toHaveText("Reviews");
|
||||
await page.keyboard.press("Escape");
|
||||
await expect(contextSheet).toBeHidden();
|
||||
|
||||
// Selecting a section inside the sheet navigates and dismisses it: the
|
||||
// Activity context lists every section as a stat row.
|
||||
await page.getByTestId("projects-section-all").click();
|
||||
await contextToggle.focus();
|
||||
await page.keyboard.press("Enter");
|
||||
await expect(contextSheet).toBeVisible();
|
||||
await contextSheet
|
||||
.getByTestId("projects-overview-stat")
|
||||
.filter({ hasText: "Tasks" })
|
||||
.click();
|
||||
await expect(contextSheet).toBeHidden();
|
||||
await expect(page.getByTestId("projects-page-header")).toContainText("Tasks");
|
||||
});
|
||||
|
||||
@@ -18,6 +18,32 @@ async function openBuzzProject(page: import("@playwright/test").Page) {
|
||||
await projectEntry.click();
|
||||
}
|
||||
|
||||
test("projects activity overview screenshot", async ({ page }) => {
|
||||
await page.addInitScript(() => {
|
||||
window.localStorage.setItem("buzz-theme", "light");
|
||||
});
|
||||
await installMockBridge(page);
|
||||
await page.goto("/", { waitUntil: "domcontentloaded" });
|
||||
await page.getByTestId("open-projects-view").click();
|
||||
await expect(page.getByTestId("projects-page-tabs")).toBeVisible();
|
||||
await expect(page.getByTestId("projects-page-header")).toBeVisible();
|
||||
await expect(page.getByTestId("projects-activity-search")).toBeVisible();
|
||||
await expect(page.getByTestId("projects-activity-intro")).toContainText(
|
||||
"Welcome to Activity",
|
||||
);
|
||||
await expect(
|
||||
page.getByTestId("projects-overview-context-panel"),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByTestId("projects-overview-create-project"),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByTestId("projects-activity-group").first(),
|
||||
).toBeVisible();
|
||||
await waitForAnimations(page);
|
||||
await page.screenshot({ path: `${SHOTS}/00-projects-pulse.png` });
|
||||
});
|
||||
|
||||
test("sidebar project add flow browses before creating", async ({ page }) => {
|
||||
await installMockBridge(page);
|
||||
await page.goto("/", { waitUntil: "domcontentloaded" });
|
||||
@@ -473,20 +499,24 @@ test("projects v3 workspace screenshot states", async ({ page }) => {
|
||||
"Hide project context",
|
||||
);
|
||||
await expect(terminalIcon).toBeVisible();
|
||||
// The icon is an inline SVG drawn with currentColor, so it must inherit
|
||||
// the toggle button's text color to stay tinted with button state.
|
||||
expect(
|
||||
await terminalIcon.evaluate((element) => {
|
||||
const style = getComputedStyle(element);
|
||||
return {
|
||||
backgroundColor: style.backgroundColor,
|
||||
maskImage: style.maskImage || style.webkitMaskImage,
|
||||
parentColor: getComputedStyle(element.parentElement ?? element).color,
|
||||
};
|
||||
}),
|
||||
await terminalIcon.evaluate((element) => ({
|
||||
color: getComputedStyle(element).color,
|
||||
strokesCurrentColor: Array.from(element.querySelectorAll("rect")).some(
|
||||
(rect) =>
|
||||
rect.getAttribute("stroke") === "currentColor" ||
|
||||
rect.getAttribute("fill") === "currentColor",
|
||||
),
|
||||
tagName: element.tagName.toLowerCase(),
|
||||
})),
|
||||
).toMatchObject({
|
||||
backgroundColor: await terminalButton.evaluate(
|
||||
color: await terminalButton.evaluate(
|
||||
(element) => getComputedStyle(element).color,
|
||||
),
|
||||
maskImage: expect.not.stringMatching(/^none$/),
|
||||
strokesCurrentColor: true,
|
||||
tagName: "svg",
|
||||
});
|
||||
const [repositoryTabBounds, chatTabBounds] = await Promise.all([
|
||||
repositoryPanelTab.boundingBox(),
|
||||
@@ -511,6 +541,9 @@ test("projects v3 workspace screenshot states", async ({ page }) => {
|
||||
await expect(agentContext).toBeVisible();
|
||||
await expect(agentContext).toContainText("Overview");
|
||||
await expect(agentContext).not.toContainText("Buzz /");
|
||||
// The context rail reveals the chat panel with a width transition; measure
|
||||
// only after it settles or the panel's unclipped box overhangs the rail.
|
||||
await waitForAnimations(page);
|
||||
const [
|
||||
attachedSharedHeaderBackdropBounds,
|
||||
tabMenuHeaderBounds,
|
||||
@@ -1012,25 +1045,33 @@ test("projects v3 work-item list metadata", async ({ page }) => {
|
||||
await page.getByTestId("open-projects-view").click();
|
||||
|
||||
await page.getByTestId("projects-section-prs").click();
|
||||
const reviewTable = page.getByTestId("projects-list-container");
|
||||
await expect(reviewTable.locator("thead")).toHaveClass(/sr-only/);
|
||||
const reviewList = page.getByTestId("projects-list-container");
|
||||
await expect(reviewList).toBeVisible();
|
||||
const pullRequestRow = page.getByTestId(/^projects-pr-row-/).first();
|
||||
await expect(pullRequestRow).toBeVisible();
|
||||
await expect(pullRequestRow).toContainText("Review");
|
||||
await expect(pullRequestRow).toContainText("opened this in");
|
||||
await expect(pullRequestRow).toContainText(/from\s*feature\/mock-2-0/);
|
||||
await expect(pullRequestRow).not.toContainText(/#[0-9a-f]{8}/);
|
||||
await expect(pullRequestRow).toContainText(/relay-tools|buzz|design-system/);
|
||||
await waitForAnimations(page);
|
||||
await page.screenshot({ path: `${SHOTS}/05-pr-list-metadata.png` });
|
||||
|
||||
await page.getByTestId("projects-section-issues").click();
|
||||
const taskTable = page.getByTestId("projects-list-container");
|
||||
await expect(taskTable.locator("thead")).toHaveClass(/sr-only/);
|
||||
const taskList = page.getByTestId("projects-list-container");
|
||||
await expect(taskList).toBeVisible();
|
||||
const issueRow = page.getByTestId(/^projects-issue-row-/).first();
|
||||
await expect(issueRow).toBeVisible();
|
||||
await expect(issueRow).toContainText("Issue");
|
||||
await expect(issueRow).toContainText(/opened this in\s*relay-tools/);
|
||||
await expect(issueRow).not.toContainText(/#[0-9a-f]{8}/);
|
||||
await expect(issueRow).toContainText(/relay-tools|buzz|design-system/);
|
||||
await waitForAnimations(page);
|
||||
await page.screenshot({ path: `${SHOTS}/06-issue-list-metadata.png` });
|
||||
|
||||
await page.getByTestId("projects-section-channels").click();
|
||||
const channelRow = page.getByTestId("project-channel-row").first();
|
||||
await expect(channelRow).toBeVisible();
|
||||
await expect(channelRow).toContainText("#general");
|
||||
await expect(
|
||||
page.getByTestId("project-channel-project").first(),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByTestId("project-channel-repository").first(),
|
||||
).toBeVisible();
|
||||
await waitForAnimations(page);
|
||||
await page.screenshot({ path: `${SHOTS}/07-channels-list.png` });
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user