Files
paseo/packages/app/e2e/browser/terminal-tab-title-loading.spec.ts
Mohamed Boudra fdf3b4b47f fix(app): show loading state for restored terminal titles (#4553)
Restored tabs render before the terminal list arrives. Mark their titles as loading until metadata is available so they do not briefly display the generic terminal name.
2026-09-09 11:30:28 +02:00

87 lines
3.9 KiB
TypeScript

import { test, expect, type Page } from "../support/fixtures";
import { clickNewTerminal, gotoWorkspace } from "../support/helpers/launcher";
import { renameModalInput, renameModalSubmit } from "../support/helpers/rename";
import { seedWorkspace, type SeededWorkspace } from "../support/helpers/seed-client";
import { selectWorkspaceInSidebar } from "../support/helpers/sidebar";
function terminalTab(page: Page, terminalId: string) {
// Terminal tab chips have no tab role; their persistent identity survives title loading.
return page.getByTestId(`workspace-tab-terminal_${terminalId}`).filter({ visible: true }).first();
}
async function createNamedTerminal(page: Page, workspace: SeededWorkspace, title: string) {
await gotoWorkspace(page, workspace.workspaceId);
await clickNewTerminal(page);
const list = () =>
workspace.client.listTerminals(workspace.repoPath, undefined, {
workspaceId: workspace.workspaceId,
});
await expect.poll(async () => (await list()).terminals.length).toBe(1);
const terminalId = (await list()).terminals[0]!.id;
await terminalTab(page, terminalId).click({ button: "right" });
await page.getByRole("menuitem", { name: "Rename", exact: true }).click();
const modal = `workspace-tab-rename-modal-terminal-${terminalId}`;
await renameModalInput(page, modal).fill(title);
await renameModalSubmit(page, modal).click();
await expect(renameModalInput(page, modal)).toHaveCount(0);
await expect(terminalTab(page, terminalId)).toHaveText(title);
return terminalId;
}
async function recordRestoredTabText(page: Page, terminalId: string) {
// Install before the next document loads: a final-state assertion would miss the flash.
await page.addInitScript((id) => {
const samples: string[] = [];
Object.assign(window, { terminalTitleSamples: samples });
new MutationObserver(() => {
const tab = document.querySelector(`[data-testid="workspace-tab-terminal_${id}"]`);
if (!tab) return;
const text = (tab.textContent ?? "").trim();
if (samples.at(-1) !== text) samples.push(text);
}).observe(document, { subtree: true, childList: true, characterData: true });
}, terminalId);
}
async function expectRestoredTitle(page: Page, terminalId: string, title: string, stage: string) {
await expect(terminalTab(page, terminalId)).toHaveText(title, { timeout: 30_000 });
const samples = await page.evaluate(
() => (window as unknown as { terminalTitleSamples: string[] }).terminalTitleSamples,
);
await test.info().attach(`${stage}-tab-text`, {
body: JSON.stringify(samples),
contentType: "application/json",
});
expect(samples).toContain(title);
expect(samples).not.toContain("Terminal");
}
test("restored terminal titles do not flash a default name while loading", async ({ page }) => {
const workspace = await seedWorkspace({ repoPrefix: "terminal-title-loading-", git: false });
const other = await seedWorkspace({ repoPrefix: "terminal-title-other-", git: false });
try {
const title = "My named terminal";
const terminalId = await createNamedTerminal(page, workspace, title);
await recordRestoredTabText(page, terminalId);
await test.step("reload with a terminal tab restored", async () => {
await page.reload();
await expectRestoredTitle(page, terminalId, title, "reload");
});
await test.step("first workspace visit in a fresh page session", async () => {
await gotoWorkspace(page, other.workspaceId);
await selectWorkspaceInSidebar(page, workspace.workspaceId);
await expectRestoredTitle(page, terminalId, title, "first-visit");
});
await test.step("return to the workspace with its titles already loaded", async () => {
await selectWorkspaceInSidebar(page, other.workspaceId);
await selectWorkspaceInSidebar(page, workspace.workspaceId);
await expectRestoredTitle(page, terminalId, title, "return-visit");
});
} finally {
await workspace.cleanup();
await other.cleanup();
}
});