Keep host badges visible under row pressure and apply the selected host color consistently to the icon, label, and tinted background.
214 lines
7.5 KiB
TypeScript
214 lines
7.5 KiB
TypeScript
import { expect, test as base } from "../support/fixtures";
|
|
import { gotoAppShell } from "../support/helpers/app";
|
|
import {
|
|
addConnectedHostAndReload,
|
|
chooseHostBadgeDisplay,
|
|
chooseHostColor,
|
|
expectHostAppearancePreview,
|
|
expectHostBadgeIconOnly,
|
|
expectHostBadgeName,
|
|
expectHostBadgeTinted,
|
|
expectNoHostBadge,
|
|
leaveHostAppearanceSettings,
|
|
openHostAppearanceSettings,
|
|
reloadPreservingHostRegistry,
|
|
renameHostFromSettings,
|
|
waitForConnectedHost,
|
|
} from "../support/helpers/hosts";
|
|
import {
|
|
type IsolatedHostDaemon,
|
|
startIsolatedHostDaemon,
|
|
} from "../support/helpers/isolated-host-daemon";
|
|
import { seedWorkspace, type SeededWorkspace } from "../support/helpers/seed-client";
|
|
import { getServerId } from "../support/helpers/server-id";
|
|
import { waitForSidebarHydration } from "../support/helpers/workspace-ui";
|
|
|
|
const PRIMARY_HOST_LABEL = "Primary Host";
|
|
const SECONDARY_HOST_LABEL = "Secondary Host";
|
|
|
|
interface TwoHostSidebar {
|
|
primaryServerId: string;
|
|
primaryWorkspaceId: string;
|
|
secondaryServerId: string;
|
|
secondaryWorkspaceId: string;
|
|
}
|
|
|
|
// The host badge only appears once the visible sidebar spans more than one host, and that count
|
|
// runs over visible projects — so an offline host with no workspaces will not raise it. Two real
|
|
// daemons, each with a seeded workspace, is the minimum shape that exercises the feature.
|
|
const test = base.extend<{ twoHostSidebar: TwoHostSidebar }>({
|
|
twoHostSidebar: async ({ page }, provide) => {
|
|
const secondaryHost: IsolatedHostDaemon = await startIsolatedHostDaemon(
|
|
"host-appearance-secondary",
|
|
);
|
|
let primary: SeededWorkspace | null = null;
|
|
let secondary: SeededWorkspace | null = null;
|
|
|
|
try {
|
|
primary = await seedWorkspace({
|
|
repoPrefix: "host-appearance-primary-",
|
|
title: "Primary workspace",
|
|
});
|
|
secondary = await seedWorkspace({
|
|
repoPrefix: "host-appearance-secondary-",
|
|
title: "Secondary workspace",
|
|
port: secondaryHost.port,
|
|
});
|
|
|
|
await gotoAppShell(page);
|
|
await addConnectedHostAndReload(page, {
|
|
serverId: secondaryHost.serverId,
|
|
label: SECONDARY_HOST_LABEL,
|
|
port: secondaryHost.port,
|
|
primaryLabel: PRIMARY_HOST_LABEL,
|
|
});
|
|
await waitForConnectedHost(page, {
|
|
serverId: secondaryHost.serverId,
|
|
endpoint: `localhost:${secondaryHost.port}`,
|
|
});
|
|
await waitForSidebarHydration(page);
|
|
|
|
await provide({
|
|
primaryServerId: getServerId(),
|
|
primaryWorkspaceId: primary.workspaceId,
|
|
secondaryServerId: secondaryHost.serverId,
|
|
secondaryWorkspaceId: secondary.workspaceId,
|
|
});
|
|
} finally {
|
|
await primary?.cleanup().catch(() => undefined);
|
|
await secondary?.cleanup().catch(() => undefined);
|
|
await secondaryHost.close().catch(() => undefined);
|
|
}
|
|
},
|
|
});
|
|
|
|
test.describe("Host appearance", () => {
|
|
test.describe.configure({ timeout: 180_000 });
|
|
|
|
test("renaming a host renames its sidebar badge", async ({ page, twoHostSidebar }) => {
|
|
const badge = {
|
|
serverId: twoHostSidebar.secondaryServerId,
|
|
workspaceId: twoHostSidebar.secondaryWorkspaceId,
|
|
};
|
|
await expectHostBadgeName(page, { ...badge, hostName: SECONDARY_HOST_LABEL });
|
|
|
|
await openHostAppearanceSettings(page, twoHostSidebar.secondaryServerId);
|
|
await renameHostFromSettings(page, "Build Box");
|
|
await leaveHostAppearanceSettings(page);
|
|
|
|
await expectHostBadgeName(page, { ...badge, hostName: "Build Box" });
|
|
});
|
|
|
|
test("long host names leave room for the workspace title and actions", async ({
|
|
page,
|
|
twoHostSidebar,
|
|
}) => {
|
|
const hostName = "A very long remote build host name that must not take over the sidebar";
|
|
const workspaceKey = `${twoHostSidebar.secondaryServerId}:${twoHostSidebar.secondaryWorkspaceId}`;
|
|
|
|
await openHostAppearanceSettings(page, twoHostSidebar.secondaryServerId);
|
|
await renameHostFromSettings(page, hostName);
|
|
await leaveHostAppearanceSettings(page);
|
|
|
|
const row = page.getByTestId(`sidebar-workspace-row-${workspaceKey}`);
|
|
const badge = row.getByTestId(`sidebar-host-badge-${twoHostSidebar.secondaryServerId}`);
|
|
await expect(badge).toHaveText(hostName);
|
|
await row.hover();
|
|
|
|
const title = row.getByText("Secondary workspace", { exact: true });
|
|
const kebab = page.getByTestId(`sidebar-workspace-kebab-${workspaceKey}`);
|
|
await expect(kebab).toBeVisible();
|
|
const [badgeBox, titleBox, rowBox, kebabBox] = await Promise.all([
|
|
badge.boundingBox(),
|
|
title.boundingBox(),
|
|
row.boundingBox(),
|
|
kebab.boundingBox(),
|
|
]);
|
|
|
|
expect(badgeBox).not.toBeNull();
|
|
expect(titleBox).not.toBeNull();
|
|
expect(rowBox).not.toBeNull();
|
|
expect(kebabBox).not.toBeNull();
|
|
await expect(badge).toHaveCSS("flex-shrink", "0");
|
|
expect(titleBox!.width).toBeGreaterThan(0);
|
|
expect(kebabBox!.x + kebabBox!.width).toBeLessThanOrEqual(rowBox!.x + rowBox!.width);
|
|
});
|
|
|
|
test("picking a color identifies the badge", async ({ page, twoHostSidebar }) => {
|
|
const badge = {
|
|
serverId: twoHostSidebar.secondaryServerId,
|
|
workspaceId: twoHostSidebar.secondaryWorkspaceId,
|
|
hostName: SECONDARY_HOST_LABEL,
|
|
};
|
|
|
|
await openHostAppearanceSettings(page, twoHostSidebar.secondaryServerId);
|
|
await chooseHostColor(page, "Teal");
|
|
await leaveHostAppearanceSettings(page);
|
|
|
|
await expectHostBadgeTinted(page, { ...badge, color: "teal" });
|
|
});
|
|
|
|
test("icon only keeps the badge and drops the name", async ({ page, twoHostSidebar }) => {
|
|
const badge = {
|
|
serverId: twoHostSidebar.secondaryServerId,
|
|
workspaceId: twoHostSidebar.secondaryWorkspaceId,
|
|
hostName: SECONDARY_HOST_LABEL,
|
|
};
|
|
|
|
await openHostAppearanceSettings(page, twoHostSidebar.secondaryServerId);
|
|
await chooseHostBadgeDisplay(page, "Icon only");
|
|
await leaveHostAppearanceSettings(page);
|
|
|
|
await expectHostBadgeIconOnly(page, badge);
|
|
});
|
|
|
|
test("hiding one host leaves the other host's badge visible", async ({
|
|
page,
|
|
twoHostSidebar,
|
|
}) => {
|
|
await openHostAppearanceSettings(page, twoHostSidebar.secondaryServerId);
|
|
await chooseHostBadgeDisplay(page, "Hidden");
|
|
await leaveHostAppearanceSettings(page);
|
|
|
|
await expectNoHostBadge(page, {
|
|
serverId: twoHostSidebar.secondaryServerId,
|
|
workspaceId: twoHostSidebar.secondaryWorkspaceId,
|
|
hostName: SECONDARY_HOST_LABEL,
|
|
});
|
|
await expectHostBadgeName(page, {
|
|
serverId: twoHostSidebar.primaryServerId,
|
|
workspaceId: twoHostSidebar.primaryWorkspaceId,
|
|
hostName: PRIMARY_HOST_LABEL,
|
|
});
|
|
});
|
|
|
|
test("name, color, and display survive a reload", async ({ page, twoHostSidebar }) => {
|
|
await openHostAppearanceSettings(page, twoHostSidebar.secondaryServerId);
|
|
await renameHostFromSettings(page, "Build Box");
|
|
await chooseHostColor(page, "Amber");
|
|
await chooseHostBadgeDisplay(page, "Icon only");
|
|
await leaveHostAppearanceSettings(page);
|
|
|
|
await reloadPreservingHostRegistry(page);
|
|
await waitForSidebarHydration(page);
|
|
|
|
await expectHostBadgeIconOnly(page, {
|
|
serverId: twoHostSidebar.secondaryServerId,
|
|
workspaceId: twoHostSidebar.secondaryWorkspaceId,
|
|
hostName: "Build Box",
|
|
});
|
|
});
|
|
|
|
test("the settings preview shows the badge as configured", async ({ page, twoHostSidebar }) => {
|
|
await openHostAppearanceSettings(page, twoHostSidebar.secondaryServerId);
|
|
await renameHostFromSettings(page, "Build Box");
|
|
await chooseHostColor(page, "Emerald");
|
|
|
|
await expectHostAppearancePreview(page, {
|
|
serverId: twoHostSidebar.secondaryServerId,
|
|
hostName: "Build Box",
|
|
color: "emerald",
|
|
});
|
|
});
|
|
});
|