- Replaced repetitive department module URL logic with `buildDepartmentModulePath` utility in `DepartmentModulesDisplay.vue`. - Enhanced layouts and responsive behavior across components, including `PosDepartmentStep1MobileTransactionHistory.vue` and `DepartmentDailyReport.vue`. - Removed unused `SuperuserInvoicingLocalStore.vue` and refactored to `SuperuserInvoicingLocalStore.ts`. - Updated media query handling and card layout adjustments in `DepartmentDailyReport.vue`. - Added new Playwright configurations (`playwright.prod.config.ts` and `playwright.live.config.ts`) for e2e release workflows. - Extended e2e and unit test coverage for mobile POS and department modules.
180 lines
8.4 KiB
JavaScript
180 lines
8.4 KiB
JavaScript
import { expect, test } from "@playwright/test";
|
|
import { mockApi, seedAuthenticatedState } from "./support/network.js";
|
|
|
|
async function primeSuperuserSession(page) {
|
|
const token = "superuser-edge-gateway-token";
|
|
await seedAuthenticatedState(page, token);
|
|
await page.goto("/login");
|
|
await page.evaluate(async (sessionToken) => {
|
|
const sessionModule = await import("/src/components/session/token/SessionUser.vue");
|
|
window.localStorage.setItem("token", sessionToken);
|
|
sessionModule.SessionUser.token.value = sessionToken;
|
|
sessionModule.SessionUser.authenticated.value = true;
|
|
sessionModule.SessionUser.permissions.value = ["superuser", "user"];
|
|
sessionModule.SessionUser.initiated.value = true;
|
|
}, token);
|
|
}
|
|
|
|
test.describe("Edge gateway management smoke", () => {
|
|
test("@smoke manages installers, bindings, queued updates, and shell approval", async ({ page }) => {
|
|
await mockApi(page, {
|
|
authenticated: true,
|
|
permissions: ["superuser", "user"],
|
|
});
|
|
await primeSuperuserSession(page);
|
|
|
|
await page.goto("/superuser/gateways");
|
|
|
|
await expect(page).toHaveURL(/\/superuser\/gateways$/);
|
|
await expect(page.locator("body")).toContainText("Edge gateways");
|
|
await expect(page.locator("body")).toContainText("Valgt gateway");
|
|
await expect(page.locator("body")).toContainText("CPH Edge 01");
|
|
await expect(page.locator("body")).toContainText("Shelly Plus 2PM");
|
|
await expect(page.locator("body")).toContainText("Latens");
|
|
await expect(page.locator("body")).toContainText("184 ms");
|
|
await expect(page.locator("body")).toContainText("CPU");
|
|
await expect(page.locator("body")).toContainText("27%");
|
|
await expect(page.locator("body")).toContainText("RAM");
|
|
await expect(page.locator("body")).toContainText("61%");
|
|
await expect(page.locator("body")).toContainText("Disk");
|
|
await expect(page.locator("body")).toContainText("58%");
|
|
|
|
const bindingRows = page.locator(".edge-table--editor tbody tr");
|
|
const primaryBindingRow = bindingRows.nth(0);
|
|
const legacyBindingRow = bindingRows.nth(1);
|
|
const primaryDeviceSelect = primaryBindingRow.locator("select").first();
|
|
const primaryChannelSelect = primaryBindingRow.locator("select").nth(1);
|
|
const primaryIpInput = primaryBindingRow.locator('input[type="text"]').nth(1);
|
|
|
|
const primaryOnlineOption = primaryDeviceSelect.locator("option").nth(1);
|
|
await expect(primaryOnlineOption).toContainText("Shelly Plus 2PM");
|
|
await expect(primaryOnlineOption).toContainText("shelly-plus-01");
|
|
await expect(primaryOnlineOption).toContainText("10.1.0.31");
|
|
await expect(primaryOnlineOption).toContainText("2 kanaler");
|
|
await expect(primaryOnlineOption).toContainText("Gen 2");
|
|
|
|
const primaryOfflineOption = primaryDeviceSelect.locator("option").nth(2);
|
|
await expect(primaryOfflineOption).toContainText("Shelly Mini 1");
|
|
await expect(primaryOfflineOption).toContainText("shelly-mini-offline");
|
|
await expect(primaryOfflineOption).toContainText("10.1.0.34");
|
|
await expect(primaryOfflineOption).toContainText("1 kanal");
|
|
await expect(primaryOfflineOption).toContainText("Gen 3");
|
|
await expect(primaryOfflineOption).toContainText("Offline");
|
|
|
|
const staleOption = legacyBindingRow.locator("select").first().locator("option").last();
|
|
await expect(staleOption).toContainText("Ukendt Shelly-device");
|
|
await expect(staleOption).toContainText("shelly-missing-legacy");
|
|
await expect(staleOption).toContainText("10.1.0.99");
|
|
await expect(staleOption).toContainText("2 kanaler");
|
|
await expect(staleOption).toContainText("Mangler i discovery");
|
|
|
|
await primaryChannelSelect.selectOption("1");
|
|
await primaryDeviceSelect.selectOption("shelly-mini-offline");
|
|
await expect(primaryIpInput).toHaveValue("10.1.0.34");
|
|
await expect(primaryChannelSelect).toHaveValue("0");
|
|
|
|
const relayBindingInput = primaryBindingRow.locator('input[type="text"]').first();
|
|
await relayBindingInput.fill("M-7-CANARY");
|
|
await page.getByRole("button", { name: "Gem bindinger" }).click();
|
|
await expect(relayBindingInput).toHaveValue("M-7-CANARY");
|
|
await expect(primaryDeviceSelect).toHaveValue("shelly-mini-offline");
|
|
await expect(primaryIpInput).toHaveValue("10.1.0.34");
|
|
await expect(primaryChannelSelect).toHaveValue("0");
|
|
|
|
await page.getByLabel("Installer afdeling").selectOption("1");
|
|
await page.locator('input[placeholder="F.eks. Roskilde Pi 01"]').fill("Canary Pi");
|
|
await page.getByRole("button", { name: /installer/i }).click();
|
|
await expect(page.getByLabel("Installationskommando")).toHaveValue(/curl -fsSL/);
|
|
|
|
await page.getByRole("button", { name: /discovery/i }).click();
|
|
await expect(page.locator("body")).toContainText("Discovery er");
|
|
await expect(page.locator("body")).toContainText("Afventer");
|
|
await expect(page.locator("body")).toContainText("shelly-plus-new");
|
|
|
|
await page.locator('input[placeholder="F.eks. 1.3.0"]').fill("1.3.0");
|
|
await page.getByRole("button", { name: /opdatering/i }).click();
|
|
await expect(page.locator("body")).toContainText("Opdatering er");
|
|
await expect(page.locator("body")).toContainText("1.3.0");
|
|
await expect(page.locator("body")).toContainText("COMPLETED");
|
|
|
|
await page.getByRole("button", { name: /root shell/i }).click();
|
|
await page.getByRole("textbox", { name: /begrundelse/i }).fill("Investigate offline relay");
|
|
await page.getByRole("button", { name: "Godkend root shell" }).click();
|
|
|
|
const terminalBuffer = page.locator('[aria-label="Root shell buffer"]');
|
|
await expect(page.locator("body")).toContainText("Live");
|
|
await expect(terminalBuffer).toContainText("[Live shell forbundet]");
|
|
await expect(terminalBuffer).toContainText("root@pi:~#");
|
|
|
|
const terminalSurface = page.locator('[aria-label="Root shell terminal"]');
|
|
await terminalSurface.focus();
|
|
await terminalSurface.press("l");
|
|
await terminalSurface.press("s");
|
|
await terminalSurface.press("Enter");
|
|
await expect(terminalBuffer).toContainText("agent.mjs");
|
|
|
|
await page.getByRole("button", { name: "Terminate session" }).click();
|
|
await expect(page.locator("body")).toContainText("Lukket");
|
|
await expect(terminalBuffer).toContainText("Session lukket");
|
|
});
|
|
|
|
test("@smoke shows a shell connection error when the browser socket fails before open", async ({ page }) => {
|
|
await mockApi(page, {
|
|
authenticated: true,
|
|
permissions: ["superuser", "user"],
|
|
edgeGateways: {
|
|
shellSocket: {
|
|
mode: "fail-before-open",
|
|
},
|
|
},
|
|
});
|
|
await primeSuperuserSession(page);
|
|
|
|
await page.goto("/superuser/gateways");
|
|
await page.getByRole("button", { name: /root shell/i }).click();
|
|
await page.getByRole("textbox", { name: /begrundelse/i }).fill("Test broken shell");
|
|
await page.getByRole("button", { name: "Godkend root shell" }).click();
|
|
|
|
await expect(page.locator("body")).toContainText("Fejl");
|
|
await expect(page.getByRole("button", { name: "Terminate session" })).toBeDisabled();
|
|
await expect(page.locator('[aria-label="Root shell buffer"]')).toContainText(
|
|
"gateway-shell fejlede"
|
|
);
|
|
});
|
|
|
|
test("@smoke applies department cutover mode from department page", async ({ page }) => {
|
|
await mockApi(page, {
|
|
authenticated: true,
|
|
permissions: ["superuser", "user"],
|
|
});
|
|
await primeSuperuserSession(page);
|
|
|
|
await page.goto("/superuser/departments/1/gateways");
|
|
|
|
await expect(page).toHaveURL(/\/superuser\/departments\/1\/gateways$/);
|
|
await expect(page.locator("body")).toContainText("Gateway for afdelingen");
|
|
await page.getByLabel("Transporttilstand").selectOption("cloud");
|
|
await page.getByRole("button", { name: "Anvend cutover" }).click();
|
|
await expect(page.getByLabel("Transporttilstand")).toHaveValue("cloud");
|
|
await expect(page.locator("body")).toContainText("Cloud fallback");
|
|
});
|
|
|
|
test("@smoke keeps discovery working when the broker is reported disconnected", async ({ page }) => {
|
|
await mockApi(page, {
|
|
authenticated: true,
|
|
permissions: ["superuser", "user"],
|
|
edgeGateways: {
|
|
brokerConnected: false,
|
|
},
|
|
});
|
|
await primeSuperuserSession(page);
|
|
|
|
await page.goto("/superuser/gateways");
|
|
|
|
await expect(page.locator("body")).toContainText("Broker frakoblet");
|
|
await page.getByRole("button", { name: /discovery/i }).click();
|
|
await expect(page.locator("body")).toContainText("Discovery er");
|
|
await expect(page.locator("body")).toContainText("shelly-plus-new");
|
|
});
|
|
});
|