Files
paseo/packages/app/e2e/browser/command-center-file-search.spec.ts
T
Mohamed BoudraandGitHub a98d154b6b Search workspace files from the command center (#3059)
* feat(command-center): add workspace file search

Use the existing workspace-aware fuzzy search so file lookup follows the file explorer's ignore and hidden-directory rules. Cmd/Ctrl+P opens a removable Files scope while the normal command center also includes file matches.

* fix(command-center): stabilize file search results

* fix(shortcuts): preserve project picker command

* fix(command-center): keep file results while searching

* feat(command-center): integrate the files scope into the search row

The scope was a 24px pill with 12px text sitting inside the 16px input line, so
it read as a badge dropped into the query rather than the scope of the search.

Make it a leading cell of the search row instead: folder icon, input-sized label,
and a hairline divider before the caret. It stretches to the row's cross size and
bleeds through the header padding with negative margins, so its height is driven
entirely by the input and the panel's own radius clips its corner. Toggling the
scope cannot resize the header, which the new spec pins.

* fix(command-center): restore the files scope as a chip

The previous commit turned the scope into a flush leading cell of the header. Put
the chip back: rounded, bordered, folder icon and 14px label, no divider.

It stretches to the row instead of carrying a height, so it is exactly as tall as
the input's line box and cannot resize the header.

* refactor(shortcuts): regroup and reorder the shortcut help sections

Panels was a junk drawer: the command center, file search, the shortcuts sheet
itself, new terminal, the sidebars, settings, focus mode and theme cycling. The
two shortcuts people reach for most, Cmd+K and Cmd+P, sat inside it below three
other sections. Navigation held both workspace and tab movement.

Sections are now General, Projects & Workspaces, Tabs & Panes, Layout, and Agent
Input. General leads with the command center and file search. Tab movement joins
Tabs & Panes, new terminal joins the tabs it creates, and Layout keeps only the
sidebar and focus toggles.

Row order is now data rather than a side effect of binding-definition order, so
the row someone opens the sheet to find sits at the top of its section. A test
fails when a new shortcut has no place in that order instead of letting it sink
to the bottom unnoticed.

* Keep file search failures in view
2026-08-10 17:02:35 +02:00

140 lines
5.2 KiB
TypeScript

import { expect, test } from "../support/fixtures";
import { openCommandCenter } from "../support/helpers/command-center";
import {
delayDirectorySuggestionResponses,
expectOneLineTruncatedFileResult,
expectStableCommandCenterLayout,
failDirectorySuggestionRequests,
startCommandCenterLayoutObservation,
} from "../support/helpers/command-center-file-search";
import { expectFileTabOpen } from "../support/helpers/file-explorer";
import { gotoWorkspace } from "../support/helpers/launcher";
import { seedWorkspace, type SeededWorkspace } from "../support/helpers/seed-client";
const FILE_PATH =
"packages/app/src/command-center/features/workspace-file-search/layout-stability/needle-layout-stability-command-center.tsx";
const FILE_NAME = "needle-layout-stability-command-center.tsx";
const FILE_DIRECTORY =
"packages/app/src/command-center/features/workspace-file-search/layout-stability";
function paseoSizedFiles(): Array<{ path: string; content: string }> {
const files = Array.from({ length: 120 }, (_, index) => ({
path: `packages/app/src/features/feature-${String(index).padStart(3, "0")}/index.ts`,
content: `export const feature${index} = ${index};\n`,
}));
files.push({ path: FILE_PATH, content: "export const layoutNeedle = true;\n" });
return files;
}
async function seedMatchingWorkspaces(count: number): Promise<SeededWorkspace[]> {
return Promise.all(
Array.from({ length: count }, (_, index) =>
seedWorkspace({
repoPrefix: `command-center-file-search-error-${String(index).padStart(2, "0")}-`,
title: `Home failure ${String(index).padStart(2, "0")}`,
}),
),
);
}
test.use({
userAgent:
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 Chrome/145.0 Safari/537.36",
});
test("workspace file search stays geometrically stable through delayed loading and results", async ({
page,
}) => {
test.setTimeout(120_000);
const seeded = await seedWorkspace({
repoPrefix: "command-center-file-search-",
title: "Paseo-shaped file search",
repo: { files: paseoSizedFiles() },
});
try {
await delayDirectorySuggestionResponses(page, 800);
await gotoWorkspace(page, seeded.workspaceId);
await page.keyboard.press("Meta+P");
const panel = page.getByTestId("command-center-panel");
await expect(panel).toBeVisible({ timeout: 30_000 });
await expect(panel.getByRole("button", { name: "Files" })).toBeVisible();
await startCommandCenterLayoutObservation(page);
await panel.getByTestId("command-center-input").fill(FILE_NAME);
const row = panel.getByRole("button", {
name: `${FILE_NAME} ${FILE_DIRECTORY}`,
});
await expect(row).toBeVisible({ timeout: 30_000 });
await expectOneLineTruncatedFileResult(row, {
filename: FILE_NAME,
path: FILE_DIRECTORY,
});
await panel.getByTestId("command-center-input").fill("needle-layout");
await page.waitForTimeout(150);
expect(await row.isVisible()).toBe(true);
await expect(panel.getByTestId("command-center-file-search-loading")).toBeVisible();
await expect(panel.getByText("Searching files...", { exact: true })).toHaveCount(0);
await expect(panel.getByTestId("command-center-file-search-loading")).toBeHidden();
await expectStableCommandCenterLayout(page);
await row.click();
await expectFileTabOpen(page, FILE_PATH);
} finally {
await seeded.cleanup();
}
});
test("dropping the files scope leaves the search row the same height", async ({ page }) => {
const seeded = await seedWorkspace({
repoPrefix: "command-center-file-search-scope-",
title: "Scope chip height",
});
try {
await gotoWorkspace(page, seeded.workspaceId);
await page.keyboard.press("Meta+P");
const panel = page.getByTestId("command-center-panel");
await expect(panel).toBeVisible({ timeout: 30_000 });
const header = panel.getByTestId("command-center-header");
const scoped = await header.boundingBox();
await page.getByTestId("command-center-files-scope").click();
await expect(page.getByTestId("command-center-files-scope")).toHaveCount(0);
const unscoped = await header.boundingBox();
expect(scoped?.height).toBe(unscoped?.height);
} finally {
await seeded.cleanup();
}
});
test("unscoped command search keeps commands visible and reports file-search failures", async ({
page,
}) => {
test.setTimeout(120_000);
const seeded = await seedMatchingWorkspaces(9);
try {
await failDirectorySuggestionRequests(page);
await gotoWorkspace(page, seeded[0].workspaceId);
const panel = await openCommandCenter(page);
await panel.getByTestId("command-center-input").fill("home");
await expect(panel.getByRole("button", { name: "Home", exact: true })).toBeVisible();
await expect(panel.getByRole("button", { name: /Home failure 08/ })).toBeVisible();
await expect(panel.getByTestId("command-center-file-search-error")).toHaveText(
"Error: Test file search transport failure.",
);
await expect(panel.getByTestId("command-center-file-search-error")).toBeInViewport({
ratio: 1,
});
await expect(panel.getByText("No matches", { exact: true })).toHaveCount(0);
} finally {
await Promise.allSettled(seeded.map((workspace) => workspace.cleanup()));
}
});