fix(ui): group mobile system metrics (#3880)

Co-authored-by: Kandev Agent <agent@kandev.dev>
This commit is contained in:
João Salavisa
2026-09-23 12:00:02 +01:00
committed by GitHub
parent 31ad30f437
commit 16ca1657ce
11 changed files with 437 additions and 73 deletions
@@ -1,4 +1,10 @@
import { expect, test } from "../../fixtures/test-base";
import { waitForFiniteAnimations } from "../../helpers/animations";
import {
assertNoDocumentHorizontalOverflow,
assertNoElementHorizontalOverflow,
requireBox,
} from "../../helpers/layout-assertions";
import {
captureAppStatusBarSettings,
restoreAppStatusBarSettings,
@@ -29,7 +35,8 @@ test.describe("Mobile resource metrics display", () => {
let statusBarBaseline: AppStatusBarSettingsBaseline;
let globalMetricsBaseline: SystemMetricsGlobalSettings;
test.beforeEach(async ({ apiClient }) => {
test.beforeEach(async ({ apiClient, testPage }) => {
void testPage;
const settings = await apiClient.getUserSettings();
baseline = settings.settings.system_metrics_display as SystemMetricsDisplay;
statusBarBaseline = await captureAppStatusBarSettings(apiClient);
@@ -58,7 +65,7 @@ test.describe("Mobile resource metrics display", () => {
await restoreAppStatusBarSettings(apiClient, statusBarBaseline);
});
test("stacks all enabled metrics inside the Status drawer", async ({ testPage, apiClient }) => {
test("aligns all enabled metrics inside the Status drawer", async ({ testPage, apiClient }) => {
const globalMetricsUpdate = await apiClient.rawRequest(
"PATCH",
"/api/v1/system/metrics/settings",
@@ -71,12 +78,13 @@ test.describe("Mobile resource metrics display", () => {
expect(displayUpdate.ok).toBe(true);
await testPage.goto("/");
await testPage.getByTestId("app-nav-trigger").click();
await testPage.getByTestId("mobile-home-status-button").click();
await testPage.getByTestId("app-nav-trigger").tap();
await testPage.getByTestId("mobile-home-status-button").tap();
const drawer = testPage.getByTestId("app-status-drawer");
const metrics = drawer.getByTestId("app-status-metrics");
await expect(metrics.getByLabel(/^CPU (?!temperature)/)).toBeVisible();
await waitForFiniteAnimations(testPage.getByRole("dialog", { name: "Status", exact: true }));
const geometry = await metrics.evaluate((element) => {
const indicators = Array.from(
element.querySelectorAll<HTMLElement>('span[aria-label]:not([aria-label="Host metrics"])'),
@@ -105,17 +113,88 @@ test.describe("Mobile resource metrics display", () => {
};
});
expect(geometry.rowCount).toBe(3);
expect(geometry.firstRowCount).toBe(2);
expect(geometry.rowCount).toBe(2);
expect(geometry.firstRowCount).toBe(3);
expect(geometry.count).toBe(5);
expect(geometry.contained).toBe(true);
expect(geometry.valuesScrollWidth).toBeLessThanOrEqual(geometry.valuesClientWidth + 1);
for (const label of ["CPU", "Memory", "Disk", "CPU temperature", "System load (1 min)"]) {
const reading = metrics.getByText(label, { exact: true });
await reading.scrollIntoViewIfNeeded();
await expect(reading).toBeInViewport();
}
expect(await drawer.locator("[class*='overflow-y-auto']").count()).toBe(1);
expect(await testPage.evaluate(() => document.documentElement.scrollWidth)).toBe(
await testPage.evaluate(() => document.documentElement.clientWidth),
);
});
test("groups the default readings in one compact card in the phone menu", async ({
testPage,
apiClient,
prCapture,
}) => {
await setAppStatusBarEnabled(apiClient, false);
const globalUpdate = await apiClient.rawRequest("PATCH", "/api/v1/system/metrics/settings", {
...globalMetricsBaseline,
metrics: ALL_HOST_METRICS.slice(0, 3),
});
expect(globalUpdate.ok).toBe(true);
const displayUpdate = await apiClient.rawRequest("PATCH", "/api/v1/user/settings", {
system_metrics_display: { show_in_topbar: true, simplified: false },
});
expect(displayUpdate.ok).toBe(true);
await testPage.emulateMedia({ colorScheme: "dark" });
await testPage.goto("/");
for (const width of [393, 320, 767]) {
await testPage.setViewportSize({ width, height: 851 });
const trigger = testPage.getByTestId("app-nav-trigger");
await trigger.tap();
const menu = testPage.getByTestId("app-nav-sheet");
const metrics = menu.getByTestId("app-status-metrics");
await expect(metrics.getByLabel(/^CPU /)).toBeVisible();
await metrics.scrollIntoViewIfNeeded();
await waitForFiniteAnimations(menu);
const heading = await requireBox(metrics.getByRole("heading"), "metrics heading");
const host = await requireBox(metrics.getByLabel("Host metrics"), "host badge");
expect(
Math.abs(heading.y + heading.height / 2 - host.y - host.height / 2),
).toBeLessThanOrEqual(1);
const boxes = [];
for (const label of ["CPU", "Memory", "Disk"]) {
await expect(metrics.getByText(label, { exact: true })).toBeVisible();
const reading = metrics.getByLabel(new RegExp(`^${label} `));
boxes.push(await requireBox(reading, label));
await assertNoElementHorizontalOverflow(reading, label);
}
for (const box of boxes) {
expect(box.y).toBeCloseTo(boxes[0].y, 0);
expect(box.width).toBeCloseTo(boxes[0].width, 0);
expect(box.height).toBeGreaterThanOrEqual(44);
}
await assertNoElementHorizontalOverflow(metrics);
await assertNoDocumentHorizontalOverflow(testPage);
await expect(menu.locator("nav.overflow-y-auto")).toHaveCount(1);
if (width === 393) {
if (prCapture.capturing) await expect(testPage.getByTestId("toast-message")).toHaveCount(0);
await prCapture.screenshot("system-metrics-menu-mobile", {
caption: "CPU, memory, and disk grouped in the phone menu's built-in System metrics card",
});
}
await testPage.keyboard.press("Escape");
await expect(menu).toBeHidden();
await expect(trigger).toBeFocused();
}
await testPage.setViewportSize({ width: 768, height: 851 });
await expect(testPage.getByTestId("app-nav-trigger")).toBeHidden();
const metrics = testPage.getByTestId("app-status-metrics");
await expect(metrics.getByLabel(/^CPU /)).toBeVisible();
await expect(metrics).toHaveCSS("display", "flex");
await expect(metrics.getByRole("heading")).toHaveCount(0);
await assertNoDocumentHorizontalOverflow(testPage);
});
test("renders simplified metrics in the Status drawer", async ({ testPage }) => {
await testPage.goto("/settings");
await testPage.getByTestId("settings-index").getByRole("link", { name: "Appearance" }).click();
@@ -1,4 +1,5 @@
import { expect, test } from "../../fixtures/test-base";
import { assertNoDocumentHorizontalOverflow, requireBox } from "../../helpers/layout-assertions";
import {
captureAppStatusBarSettings,
restoreAppStatusBarSettings,
@@ -19,7 +20,8 @@ test.describe("Resource metrics display", () => {
let baseline: SystemMetricsDisplay;
let statusBarBaseline: AppStatusBarSettingsBaseline;
test.beforeEach(async ({ apiClient }) => {
test.beforeEach(async ({ apiClient, testPage }) => {
void testPage;
const settings = await apiClient.getUserSettings();
baseline = settings.settings.system_metrics_display as SystemMetricsDisplay;
statusBarBaseline = await captureAppStatusBarSettings(apiClient);
@@ -33,6 +35,31 @@ test.describe("Resource metrics display", () => {
await restoreAppStatusBarSettings(apiClient, statusBarBaseline);
});
test("keeps detailed desktop metrics in a compact inline status bar", async ({
testPage,
apiClient,
prCapture,
}) => {
const update = await apiClient.rawRequest("PATCH", "/api/v1/user/settings", {
system_metrics_display: { show_in_topbar: true, simplified: false },
});
expect(update.ok).toBe(true);
await testPage.emulateMedia({ colorScheme: "dark" });
await testPage.goto("/");
const metrics = testPage.getByTestId("app-status-metrics");
await expect(metrics.getByLabel(/^CPU /)).toBeVisible();
await expect(metrics.getByRole("heading")).toHaveCount(0);
const host = await requireBox(metrics.getByLabel("Host metrics"), "host badge");
const cpu = await requireBox(metrics.getByLabel(/^CPU /), "CPU reading");
expect(Math.abs(host.y + host.height / 2 - cpu.y - cpu.height / 2)).toBeLessThanOrEqual(1);
expect((await requireBox(metrics, "desktop metrics")).height).toBeLessThanOrEqual(24);
await assertNoDocumentHorizontalOverflow(testPage);
if (prCapture.capturing) await expect(testPage.getByTestId("toast-message")).toHaveCount(0);
await prCapture.screenshot("system-metrics-desktop", {
caption: "Desktop retains the compact inline host metrics in the status bar",
});
});
test("renders simplified metrics in the status bar", async ({ testPage }) => {
await testPage.goto("/settings/preferences/appearance");
const showMetrics = testPage.getByRole("switch", { name: "Show host metrics in status bar" });