Make the network device table sortable by column
Click a header to sort by it (ascending), click again to reverse. Status defaults to known-first (its display string sorts that way naturally, no special-casing needed). IP sorts numerically by octet, not lexically. Ties fall back to IP order so the table doesn't reshuffle mid-poll for devices sharing a sort value (e.g. many unnamed unknowns). Added a Playwright test verifying IP asc/desc and Name asc against real rendered data. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
|
||||
const USERNAME = process.env.LOCAL_USERNAME ?? "admin";
|
||||
const PASSWORD = process.env.LOCAL_PASSWORD ?? "";
|
||||
|
||||
async function login(page: import("@playwright/test").Page) {
|
||||
await page.goto("/");
|
||||
await page.getByPlaceholder("Username").fill(USERNAME);
|
||||
await page.getByPlaceholder("Password").fill(PASSWORD);
|
||||
await page.getByRole("button", { name: "Sign in" }).click();
|
||||
await expect(page.locator(".device-table tbody tr").first()).toBeVisible({ timeout: 15_000 });
|
||||
}
|
||||
|
||||
test("network device table sorts by each column", async ({ page }) => {
|
||||
test.skip(!PASSWORD, "LOCAL_PASSWORD not set");
|
||||
await login(page);
|
||||
|
||||
const ipCells = () => page.locator(".device-table tbody tr td:nth-child(2)").allTextContents();
|
||||
|
||||
// Default: known-first (status ascending).
|
||||
const firstBadge = page.locator(".device-table tbody tr td:first-child .device-badge").first();
|
||||
await expect(firstBadge).toHaveText("known");
|
||||
|
||||
// Click IP header -> ascending numeric IP order.
|
||||
await page.getByRole("button", { name: "IP" }).click();
|
||||
const ascIps = await ipCells();
|
||||
const numeric = (ip: string) => ip.split(".").map(Number);
|
||||
const sortedAsc = [...ascIps].sort((a, b) => {
|
||||
const [an, bn] = [numeric(a), numeric(b)];
|
||||
for (let i = 0; i < 4; i++) if (an[i] !== bn[i]) return an[i] - bn[i];
|
||||
return 0;
|
||||
});
|
||||
expect(ascIps).toEqual(sortedAsc);
|
||||
|
||||
// Click IP header again -> descending.
|
||||
await page.getByRole("button", { name: "IP" }).click();
|
||||
const descIps = await ipCells();
|
||||
expect(descIps).toEqual([...sortedAsc].reverse());
|
||||
|
||||
// Click a different column (Name) resets to ascending on that column.
|
||||
await page.getByRole("button", { name: "Name" }).click();
|
||||
const names = await page.locator(".device-table tbody tr td:nth-child(4)").allTextContents();
|
||||
const sortedNames = [...names].sort((a, b) => a.localeCompare(b));
|
||||
expect(names).toEqual(sortedNames);
|
||||
});
|
||||
Reference in New Issue
Block a user