Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 15 additions & 6 deletions crates/diffcore-tauri/ui/tests/e2e/activity-stream.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,11 +169,16 @@ test.describe("AI activity stream", () => {
const panel = page.locator(".panel-right");
const logPanel = page.getByTestId("activity-log-panel");
const latestCard = page.getByTestId("activity-entry").last();
const inspector = page.getByTestId("activity-inspector");

await latestCard.click();
await expect(inspector).toContainText("stdout.command_execution");
await expect(inspector).toContainText("\"path\": \"crates/diffcore-tauri/ui/src/App.tsx\"");
// The dedicated inspector was replaced by inline hint chips on the card;
// the event type and payload are exposed via the chips' title attributes.
await expect(
latestCard.locator('.activity-card-hint[title="stdout.command_execution"]'),
).toBeVisible();
await expect(
latestCard.locator(".activity-card-hint", { hasText: "Payload" }),
).toHaveAttribute("title", /"path": "crates\/diffcore-tauri\/ui\/src\/App\.tsx"/);
await expect(page.getByTestId("activity-panel")).toContainText("Latest stream captured from Codex CLI");

for (const locator of [panel, logPanel, page.locator(".activity-view-switch")]) {
Expand Down Expand Up @@ -230,10 +235,14 @@ test.describe("AI activity stream", () => {
await expect(cards.first()).toContainText("Preparing refinement request");
await expect(cards.last()).toContainText("Refinement rationale");

const heights = await cards.evaluateAll((elements) =>
elements.map((element) => Math.round(element.getBoundingClientRect().height)),
// compact cards: not collapsed, not clipping (overflow: hidden)
const metrics = await cards.evaluateAll((elements) =>
elements.map((element) => [element.clientHeight, element.scrollHeight]),
);
expect(heights.every((height) => height >= 78)).toBeTruthy();
for (const [clientHeight, scrollHeight] of metrics) {
expect(clientHeight).toBeGreaterThanOrEqual(40);
expect(clientHeight).toBeGreaterThanOrEqual(scrollHeight - 1);
}
});

test("surfaces the effective Codex backend in Settings when local auth is available", async ({ page }) => {
Expand Down
9 changes: 7 additions & 2 deletions crates/diffcore-tauri/ui/tests/e2e/bugfixes.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,10 @@ test.describe("Bugfix — Keyboard shortcuts with Monaco focus", () => {
// Replay bar should appear
await expect(page.locator(".replay-bar")).toBeVisible();

// Exit replay
await page.keyboard.press("Escape");
// Exit replay — r toggles replay off. (Escape is deliberately passed
// through to Monaco while the editor has focus, so r is the keyboard
// exit path from Monaco focus.)
await page.keyboard.press("r");
await page.waitForTimeout(300);
await expect(page.locator(".replay-bar")).not.toBeVisible();
});
Expand All @@ -153,6 +155,9 @@ test.describe("Bugfix — Flow graph fullscreen", () => {
test.beforeEach(async ({ page }) => {
await page.goto("/");
await waitForAnalysis(page);
// The flow graph now lives under the Graph subtab in the right panel
await page.locator(".annotation-subtab", { hasText: "Graph" }).click();
await expect(page.locator("[data-testid='flow-graph']")).toBeVisible();
});

test("07 — fullscreen re-centers the graph (not stuck in top-left)", async ({ page }) => {
Expand Down
187 changes: 40 additions & 147 deletions crates/diffcore-tauri/ui/tests/e2e/comment-strip-v2.spec.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
/**
* Comment Strip v2 — Playwright E2E tests.
* Comments v2 — Playwright E2E tests.
*
* Covers:
* - File comment icon in left panel (clickable, with count)
* - Comment strip collapse/expand animation
* - Active comment highlighting in nav pills
* - File path shown on comment cards
* - Scrollability of comment nav and detail when many comments
* - Smooth transitions on collapsible sections
* - Clicking the file comment icon opens the right-panel comments tab
* - Active comment highlighting on comment cards
* - File path shown on comment file-group headers
* - Scrollability of the comments list with many comments
*/
import { test, expect, type Page } from "@playwright/test";

Expand All @@ -29,6 +28,10 @@ async function addCommentViaUI(page: Page, text: string) {
await page.waitForTimeout(300);
}

async function openCommentsTab(page: Page) {
await page.getByTestId("comments-tab").click();
}

// ── File Comment Icon ──

test.describe("File Comment Icon", () => {
Expand Down Expand Up @@ -57,67 +60,19 @@ test.describe("File Comment Icon", () => {
await expect(count).toContainText("2");
});

test("03 — clicking comment icon opens comment strip", async ({ page }) => {
test("03 — clicking comment icon opens the comments tab", async ({ page }) => {
await addCommentViaUI(page, "A comment");

// Collapse the comments strip first
const toggle = page.locator(".comment-strip-toggle");
await toggle.click();
await page.waitForTimeout(300);

// Verify collapsed
const body = page.locator(".comment-strip-body");
await expect(body).toHaveCSS("max-height", "0px");
// Comments tab is not active by default (annotations is)
await expect(page.getByTestId("comments-tab")).toHaveAttribute("aria-selected", "false");

// Click the file comment icon
await page.locator(".file-comment-btn").first().click();
await page.waitForTimeout(400);

// Comments strip should be expanded
await expect(page.locator(".comment-strip-collapsed")).not.toBeVisible();
});
});

// ── Comment Strip Collapse/Expand ──

test.describe("Comment Strip Collapse", () => {
test.beforeEach(async ({ page }) => {
await page.goto("/");
await waitForAnalysis(page);
await addCommentViaUI(page, "Test comment for collapse");
});

test("04 — comment strip has 'Comments' header with count", async ({ page }) => {
const toggle = page.locator(".comment-strip-toggle");
await expect(toggle).toBeVisible();
await expect(toggle).toContainText("Comments");

const count = page.locator(".comment-strip-count").first();
await expect(count).toContainText("1");
});

test("05 — clicking header collapses the comment body", async ({ page }) => {
const toggle = page.locator(".comment-strip-toggle");
await toggle.click();
await page.waitForTimeout(400);

// Body should be collapsed (max-height: 0)
const strip = page.locator(".comment-strip");
await expect(strip).toHaveClass(/comment-strip-collapsed/);
});

test("06 — clicking header again expands the comment body", async ({ page }) => {
const toggle = page.locator(".comment-strip-toggle");

// Collapse
await toggle.click();
await page.waitForTimeout(400);
await expect(page.locator(".comment-strip")).toHaveClass(/comment-strip-collapsed/);

// Expand
await toggle.click();
await page.waitForTimeout(400);
await expect(page.locator(".comment-strip")).not.toHaveClass(/comment-strip-collapsed/);
// Right panel should switch to the comments tab and show the comment
await expect(page.getByTestId("comments-tab")).toHaveAttribute("aria-selected", "true");
await expect(page.locator(".comments-tab-card")).toHaveCount(1);
});
});

Expand All @@ -129,36 +84,29 @@ test.describe("Active Comment Highlight", () => {
await waitForAnalysis(page);
await addCommentViaUI(page, "First comment");
await addCommentViaUI(page, "Second comment");
await openCommentsTab(page);
});

test("07 — clicking nav pill highlights it as active", async ({ page }) => {
const firstPill = page.locator(".comment-strip-nav-item").first();
await firstPill.click();
test("04 — clicking a comment card highlights it as active", async ({ page }) => {
const firstCard = page.locator(".comments-tab-card").first();
await firstCard.click();
await page.waitForTimeout(300);

await expect(firstPill).toHaveClass(/comment-strip-nav-active/);
await expect(firstCard).toHaveClass(/comments-tab-card-active/);
});

test("08 — clicking a different pill moves the active highlight", async ({ page }) => {
const firstPill = page.locator(".comment-strip-nav-item").first();
const secondPill = page.locator(".comment-strip-nav-item").nth(1);

await firstPill.click();
await page.waitForTimeout(300);
await expect(firstPill).toHaveClass(/comment-strip-nav-active/);

await secondPill.click();
await page.waitForTimeout(300);
await expect(secondPill).toHaveClass(/comment-strip-nav-active/);
await expect(firstPill).not.toHaveClass(/comment-strip-nav-active/);
});
test("05 — clicking a different card moves the active highlight", async ({ page }) => {
const firstCard = page.locator(".comments-tab-card").first();
const secondCard = page.locator(".comments-tab-card").nth(1);

test("09 — clicking a comment card highlights it as active", async ({ page }) => {
const firstCard = page.locator(".comment-strip-item").first();
await firstCard.click();
await page.waitForTimeout(300);
await expect(firstCard).toHaveClass(/comments-tab-card-active/);

await expect(firstCard).toHaveClass(/comment-strip-item-active/);
await secondCard.click();
await page.waitForTimeout(300);
await expect(secondCard).toHaveClass(/comments-tab-card-active/);
await expect(firstCard).not.toHaveClass(/comments-tab-card-active/);
});
});

Expand All @@ -170,10 +118,11 @@ test.describe("File Path on Comments", () => {
await waitForAnalysis(page);
});

test("10 — comment card shows file path", async ({ page }) => {
test("06 — comment file group shows file path", async ({ page }) => {
await addCommentViaUI(page, "Comment with path");
await openCommentsTab(page);

const filepath = page.locator(".comment-strip-filepath").first();
const filepath = page.locator(".comments-tab-file-path").first();
await expect(filepath).toBeVisible();
// Should contain a file name (not empty)
const text = await filepath.textContent();
Expand All @@ -183,83 +132,27 @@ test.describe("File Path on Comments", () => {

// ── Scrollability ──

test.describe("Comment Strip Scrollability", () => {
test.describe("Comments List Scrollability", () => {
test.beforeEach(async ({ page }) => {
await page.goto("/");
await waitForAnalysis(page);
});

test("11comment detail panel is scrollable with many comments", async ({ page }) => {
test("07comments list is scrollable with many comments", async ({ page }) => {
// Add many comments to overflow the panel
for (let i = 0; i < 8; i++) {
for (let i = 0; i < 10; i++) {
await addCommentViaUI(page, `Comment number ${i + 1} — this is a longer comment to take up vertical space in the panel`);
}
await openCommentsTab(page);

const detail = page.locator(".comment-strip-detail");
await expect(detail).toBeVisible();
const list = page.locator(".comments-tab-list");
await expect(list).toBeVisible();

// Check that overflow-y is set to auto
await expect(detail).toHaveCSS("overflow-y", "auto");
await expect(list).toHaveCSS("overflow-y", "auto");

// The detail panel should have scroll height larger than client height
const hasScroll = await detail.evaluate((el) => el.scrollHeight > el.clientHeight);
// The list should have scroll height larger than client height
const hasScroll = await list.evaluate((el) => el.scrollHeight > el.clientHeight);
expect(hasScroll).toBe(true);
});

test("12 — comment nav panel is scrollable with many comments", async ({ page }) => {
for (let i = 0; i < 8; i++) {
await addCommentViaUI(page, `Nav scroll test ${i + 1}`);
}

const nav = page.locator(".comment-strip-nav");
await expect(nav).toBeVisible();
await expect(nav).toHaveCSS("overflow-y", "auto");
});
});

// ── Collapsible Sections Transitions ──

test.describe("Smooth Collapsible Sections", () => {
test.beforeEach(async ({ page }) => {
await page.goto("/");
await waitForAnalysis(page);
});

test("13 — flow graph section uses collapsible-body with transition", async ({ page }) => {
const body = page.locator(".flow-graph-section .collapsible-body");
if (await body.isVisible()) {
// Should have transition property set
const transition = await body.evaluate((el) => getComputedStyle(el).transition);
expect(transition).toContain("max-height");
}
});

test("14 — edges section uses collapsible-body with transition", async ({ page }) => {
const body = page.locator(".edges-section .collapsible-body");
if (await body.isVisible()) {
const transition = await body.evaluate((el) => getComputedStyle(el).transition);
expect(transition).toContain("max-height");
}
});

test("15 — edges section starts collapsed", async ({ page }) => {
const section = page.locator(".edges-section");
if (await section.isVisible()) {
await expect(section).toHaveClass(/edges-collapsed/);
}
});

test("16 — edges section expands on toggle click", async ({ page }) => {
const toggle = page.locator(".edges-section .section-toggle");
if (await toggle.isVisible()) {
await toggle.click();
await page.waitForTimeout(400);

const section = page.locator(".edges-section");
await expect(section).not.toHaveClass(/edges-collapsed/);

// Edge items should be visible
await expect(page.locator(".edge-item").first()).toBeVisible();
}
});
});
23 changes: 9 additions & 14 deletions crates/diffcore-tauri/ui/tests/e2e/gutter-comments.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,6 @@ test.describe("Gutter Comment Icons", () => {

console.log("Comments after add:", JSON.stringify(info));
expect(info.total).toBeGreaterThanOrEqual(1);

// Check for comment strip
const stripVisible = await page.locator(".comment-strip").isVisible();
console.log("Comment strip visible:", stripVisible);
});

test("04 — glyph icon appears in editor after code comment added", async ({ page }) => {
Expand Down Expand Up @@ -175,7 +171,7 @@ test.describe("Gutter Comment Icons", () => {
expect(highlights).toBe(4);
});

test("08 — glyph click activates comment in strip and scrolls it into view", async ({ page }) => {
test("08 — glyph click activates comment in comments tab and scrolls it into view", async ({ page }) => {
// Add two comments so we can verify the right one gets activated
await addCodeCommentViaUI(page, "First comment on lines 5-8");

Expand Down Expand Up @@ -203,24 +199,23 @@ test.describe("Gutter Comment Icons", () => {
const glyphs = await page.locator(".comment-glyph-icon").count();
expect(glyphs).toBe(2);

// Comment strip should show 2 items
const items = await page.locator(".comment-strip-item").count();
expect(items).toBe(2);
// Comments tab badge should show 2 — checked without opening the tab, so the
// glyph-click activation assert below stays meaningful
await expect(page.getByTestId("comments-tab").locator(".panel-tab-count")).toHaveText("2");

// Click the first glyph icon
const firstGlyph = page.locator(".comment-glyph-icon").first();
await firstGlyph.click();
await page.waitForTimeout(500);

// The first comment card should be highlighted as active
const activeItems = page.locator(".comment-strip-item-active");
// Glyph click activates the comments tab and highlights the matching card
await expect(page.getByTestId("comments-tab")).toHaveAttribute("aria-selected", "true");
const activeItems = page.locator(".comments-tab-card-active");
expect(await activeItems.count()).toBe(1);

// The comments strip should be expanded (not collapsed)
await expect(page.locator(".comment-strip")).not.toHaveClass(/comment-strip-collapsed/);
await expect(activeItems.first()).toContainText("First comment on lines 5-8");

// Take screenshot for verification
await page.locator(".panel-center").screenshot({
await page.screenshot({
path: path.join(SCREENSHOTS_DIR, "87-glyph-click-activates-comment.png"),
});
});
Expand Down
Loading