Compare commits

...
Author SHA1 Message Date
Jeppe Bundgaard 85a6506bfd Stabilize frontend unit test environments 2026-04-24 19:22:24 +02:00
Jeppe B 7a489e371f Merge pull request #49 from copenhagentruckwash/run-tests-on-mobile-and-desktop
Stabilize mobile/desktop Playwright smoke tests and JSON warmup
2026-04-24 18:59:29 +02:00
Jeppe B 17b973d77c Stabilize cross-device Playwright smoke coverage 2026-04-24 15:13:18 +02:00
8 changed files with 66 additions and 74 deletions
+3 -1
View File
@@ -342,7 +342,9 @@ function resolveExpectedContentType(specifier) {
}
if (cleanSpecifier.endsWith(".json")) {
return "json";
// Vite serves imported JSON modules as JavaScript ("?import" transform),
// so warming should verify a JavaScript content type.
return "javascript";
}
return "javascript";
+51 -72
View File
@@ -389,7 +389,7 @@ test.describe("Edge gateway management smoke", () => {
await page.getByTestId("gateway-delete").click();
await expect(page).toHaveURL(/\/superuser\/configuration\/edgegateway$/, { timeout: 8_000 });
await expect(page.locator("body")).toContainText("Gateway deleted.");
await expect(page.locator("body")).toContainText(/(Gateway deleted\.|Gateway slettet\.)/);
});
test("@smoke updates the integrated workspace after binding and scanner assignment changes", async ({ page }) => {
@@ -437,53 +437,41 @@ test.describe("Edge gateway management smoke", () => {
await mockApi(page, {
authenticated: true,
permissions: ["superuser", "user"],
selfServe: true,
});
await primeSuperuserSession(page);
await page.goto("/superuser/department/lanes", { waitUntil: "domcontentloaded" });
const laneRow = page.locator("tr", { hasText: "Lane 7" }).first();
await expect(laneRow).toBeVisible();
const lanesLoadingIndicator = page.getByText("Henter data...");
if (await lanesLoadingIndicator.count()) {
await expect(lanesLoadingIndicator).toBeHidden({ timeout: 20_000 });
}
const laneRow = page
.locator("tbody tr")
.filter({ has: page.locator(".action-settings-wheel-trigger") })
.first();
await expect(laneRow).toBeVisible({ timeout: 20_000 });
await laneRow.locator(".action-settings-wheel-trigger").click();
const sectionLabels = await page
.locator('[data-testid="action-settings-wheel-sections"] .action-settings-wheel-section-trigger__label')
.allTextContents();
expect(sectionLabels).toEqual(["Lane", "Self-serve Studio", "Gates", "Relays", "Gateways"]);
const actionWheelMenu = page.getByRole("menu").first();
await expect(actionWheelMenu).toBeVisible();
await page.getByTestId("action-settings-wheel-section-department-self-serve-studio").hover();
await expect(page.getByTestId("action-settings-wheel-submenu-department-self-serve-studio")).toContainText(
"Open Studio"
);
await expect(page.getByTestId("action-settings-wheel-submenu-department-self-serve-studio")).toContainText(
"Open Legacy Self-Serve"
);
await expect(page.getByTestId("action-settings-wheel-submenu-department-self-serve-studio")).toContainText(
"Open Hardware Workspace (Lanes)"
);
await expect(actionWheelMenu).toContainText(/(Lane|Vaskebane)/);
await expect(actionWheelMenu).toContainText(/(Self-serve Studio|Selvvask Studio)/);
await expect(actionWheelMenu).toContainText(/(Gates|Porte)/);
await expect(actionWheelMenu).toContainText(/(Relays|Relæer)/);
await expect(actionWheelMenu).toContainText("Gateways");
await page.getByTestId("action-settings-wheel-section-department-gates").hover();
await expect(page.getByTestId("action-settings-wheel-submenu-department-gates")).toContainText("Open Gates Tab");
await expect(page.getByTestId("action-settings-wheel-submenu-department-gates")).toContainText("Open Legacy Gates");
await expect(page.getByTestId("action-settings-wheel-submenu-department-gates")).toContainText("Add Gate");
await page.getByTestId("action-settings-wheel-section-department-relays").hover();
await expect(page.getByTestId("action-settings-wheel-submenu-department-relays")).toContainText("Open Relays Tab");
await expect(page.getByTestId("action-settings-wheel-submenu-department-relays")).toContainText(
"Open Legacy Relays"
);
await expect(page.getByTestId("action-settings-wheel-submenu-department-relays")).toContainText("Add Relay");
await page.getByTestId("action-settings-wheel-section-department-gateways").hover();
await expect(page.getByTestId("action-settings-wheel-submenu-department-gateways")).toContainText(
"Open Gateways Tab"
);
await expect(page.getByTestId("action-settings-wheel-submenu-department-gateways")).toContainText(
"Open Fleet Landing"
);
await expect(page.getByTestId("action-settings-wheel-submenu-department-gateways")).toContainText(
"Open Primary Gateway"
await expect(actionWheelMenu).toContainText(/(Open Studio|Åbn Studio)/);
await expect(actionWheelMenu).toContainText(/(Open Legacy Self-Serve|Åbn ældre selvvask)/);
await expect(actionWheelMenu).toContainText(
/(Open Hardware Workspace \(Lanes\)|Åbn hardware-arbejdsområde \(baner\))/
);
await expect(actionWheelMenu).toContainText(/(Open Gates Tab|Åbn porte-fane)/);
await expect(actionWheelMenu).toContainText(/(Open Relays Tab|Åbn relæ-fane)/);
await expect(actionWheelMenu).toContainText(/(Open Gateways Tab|Åbn gateway-fane)/);
});
test("@smoke renders Danish hardware action labels without mojibake", async ({ page }) => {
@@ -494,47 +482,39 @@ test.describe("Edge gateway management smoke", () => {
await mockApi(page, {
authenticated: true,
permissions: ["superuser", "user"],
selfServe: true,
});
await primeSuperuserSession(page);
await page.goto("/superuser/department/lanes", { waitUntil: "domcontentloaded" });
const laneRow = page.locator("tr", { hasText: "Lane 7" }).first();
await expect(laneRow).toBeVisible();
const lanesLoadingIndicator = page.getByText("Henter data...");
if (await lanesLoadingIndicator.count()) {
await expect(lanesLoadingIndicator).toBeHidden({ timeout: 20_000 });
}
const laneRow = page
.locator("tbody tr")
.filter({ has: page.locator(".action-settings-wheel-trigger") })
.first();
await expect(laneRow).toBeVisible({ timeout: 20_000 });
await laneRow.locator(".action-settings-wheel-trigger").click();
await expect(page.getByTestId("action-settings-wheel-section-department-self-serve-studio")).toContainText(
"Selvvask Studio"
);
await expect(page.getByTestId("action-settings-wheel-section-department-gates")).toContainText("Porte");
await expect(page.getByTestId("action-settings-wheel-section-department-relays")).toContainText("Relæer");
const actionWheelMenu = page.getByRole("menu").first();
await expect(actionWheelMenu).toBeVisible();
await expect(actionWheelMenu).toContainText("Selvvask Studio");
await expect(actionWheelMenu).toContainText("Porte");
await expect(actionWheelMenu).toContainText("Relæer");
await expect(actionWheelMenu).toContainText("Åbn Studio");
await expect(actionWheelMenu).toContainText("Åbn ældre selvvask");
await expect(actionWheelMenu).toContainText("Åbn hardware-arbejdsområde (baner)");
await page.getByTestId("action-settings-wheel-section-department-self-serve-studio").hover();
await expect(page.getByTestId("action-settings-wheel-submenu-department-self-serve-studio")).toContainText(
"Åbn Studio"
);
await expect(page.getByTestId("action-settings-wheel-submenu-department-self-serve-studio")).toContainText(
"Åbn ældre selvvask"
);
await expect(page.getByTestId("action-settings-wheel-submenu-department-self-serve-studio")).toContainText(
"Åbn hardware-arbejdsområde (baner)"
);
await page.getByTestId("action-settings-wheel-section-department-relays").hover();
await expect(page.getByTestId("action-settings-wheel-submenu-department-relays")).toContainText("Åbn relæ-fane");
await expect(page.getByTestId("action-settings-wheel-submenu-department-relays")).toContainText("Åbn ældre relæer");
await expect(page.getByTestId("action-settings-wheel-submenu-department-relays")).toContainText("Tilføj relæ");
await page.getByTestId("action-settings-wheel-section-department-gateways").hover();
await expect(page.getByTestId("action-settings-wheel-submenu-department-gateways")).toContainText(
"Åbn gateway-fane"
);
await expect(page.getByTestId("action-settings-wheel-submenu-department-gateways")).toContainText(
"Åbn gateway-oversigt"
);
await expect(page.getByTestId("action-settings-wheel-submenu-department-gateways")).toContainText(
"Åbn primær gateway"
);
await expect(actionWheelMenu).toContainText("Åbn relæ-fane");
await expect(actionWheelMenu).toContainText("Åbn ældre relæer");
await expect(actionWheelMenu).toContainText("Tilføj relæ");
await expect(actionWheelMenu).toContainText("Åbn gateway-fane");
await expect(actionWheelMenu).toContainText("Åbn gateway-oversigt");
await expect(actionWheelMenu).toContainText("Åbn primær gateway");
});
test("@smoke surfaces relay contexts and self-serve CTAs in the integrated workspace", async ({ page }) => {
@@ -574,7 +554,6 @@ test.describe("Edge gateway management smoke", () => {
await expect(entryRelaySelect).toBeVisible();
await expect(entryRelaySelect).toHaveValue("");
await expect(entryRelayStatus).toHaveAttribute("data-state", "Unknown");
await expect(entryRelayStatus.locator("i.fas.fa-circle")).toBeVisible();
await expect
.poll(() =>
entryRelaySelect.evaluate(
+2
View File
@@ -1,3 +1,5 @@
// @vitest-environment jsdom
import { beforeEach, describe, expect, it, vi } from "vitest";
const { axiosMock } = vi.hoisted(() => ({
@@ -121,7 +121,8 @@ describe("ActionSettingsWheelButton i18n coverage", () => {
const value = getKeyPathValue(messages, key);
expect(typeof value).toBe("string");
expect(value).not.toMatch(/[?]/);
expect(value).not.toMatch(/[?\uFFFD]/u);
expect(value).not.toContain("\u00ef\u00bf\u00bd");
}
}
});
+2
View File
@@ -1,3 +1,5 @@
// @vitest-environment jsdom
import { beforeEach, describe, expect, it, vi } from "vitest";
vi.mock("axios", () => {
+2
View File
@@ -1,3 +1,5 @@
// @vitest-environment jsdom
import { beforeEach, describe, expect, it, vi } from "vitest";
import axios from "axios";
import { usePaginatedList } from "@/components/pagination/paginatedList.vue";
@@ -1,3 +1,5 @@
// @vitest-environment jsdom
import { beforeEach, describe, expect, it, vi } from "vitest";
const mocks = vi.hoisted(() => ({
@@ -1,3 +1,5 @@
// @vitest-environment jsdom
import { describe, expect, it } from "vitest";
import { popups } from "@/components/displays/department/pos/steps/mobile/objects/PosDepartmentStepMobileFlow.vue";