Files
pleno-vue/tests/e2e/release-update-widget.spec.js
T

218 lines
6.8 KiB
JavaScript

import { expect, test } from "@playwright/test";
import { mockApi, seedAuthenticatedState } from "./support/network.js";
const FRONTEND_BASE_URL = "https://static.example.test/releases/stable/frontend";
const LATEST_FRONTEND_COMMIT = "ffffffffffffffffffffffffffffffffffffffff";
const json = (body, status = 200) => ({
status,
contentType: "application/json",
headers: {
"access-control-allow-origin": "*",
"access-control-allow-headers":
"Content-Type, Authorization, X-Customer-Number, X-Release-Trace, X-Release-Channel, X-Frontend-Version, *",
"access-control-allow-methods": "GET, POST, PUT, PATCH, DELETE, OPTIONS",
},
body: JSON.stringify(body),
});
const text = (body, contentType = "text/plain") => ({
status: 200,
contentType,
headers: {
"access-control-allow-origin": "*",
},
body,
});
const releaseRuntime = () => ({
source: "deployment",
generated_at: "2026-05-27T09:30:00.000Z",
trace_id: "trace-release-update-widget",
channel: {
id: 1,
slug: "stable",
name: "Stable",
default_channel: true,
},
available_channels: [
{
channel: {
id: 1,
slug: "stable",
name: "Stable",
default_channel: true,
},
versions: {
frontend: {
version_label: "stable-frontend",
commit_sha: LATEST_FRONTEND_COMMIT,
},
},
availability: {
configured: true,
missing: [],
status: "ready",
},
},
{
channel: {
id: 2,
slug: "beta",
name: "Beta",
default_channel: false,
},
versions: {
frontend: {
version_label: "beta-frontend",
commit_sha: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
},
},
availability: {
configured: false,
missing: ["api_base_url"],
status: "unconfigured",
},
},
],
versions: {
frontend: {
version_label: "stable-frontend",
commit_sha: LATEST_FRONTEND_COMMIT,
},
api: {
version_label: "stable-api",
commit_sha: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
},
bundle_id: 42,
},
frontend_base_url: FRONTEND_BASE_URL,
api_base_url: "https://api-v2.truckwash.io/master/api",
urls: {
frontend_base_url: FRONTEND_BASE_URL,
api_base_url: "https://api-v2.truckwash.io/master/api",
},
availability: {
configured: true,
missing: [],
status: "ready",
},
capture_policy: {
enabled: false,
capture_level: "metadata",
all_failure_metadata: true,
retention_days: 14,
},
});
const releaseManifest = () => ({
schema_version: 1,
build_id: "build-frontend-latest",
commit_sha: LATEST_FRONTEND_COMMIT,
entry: "assets/app.js",
css: ["assets/app.css"],
asset_urls: ["assets/chunk.js"],
asset_hashes: {
"/release-manifest.json": { bytes: 100 },
"/release-entry.json": { bytes: 80 },
"/assets/app.js": { bytes: 120 },
"/assets/app.css": { bytes: 60 },
"/assets/chunk.js": { bytes: 90 },
},
});
async function bootReleaseUpdateScenario(page) {
const frontendAssetRequests = [];
await page.addInitScript(() => {
window.localStorage.setItem("locale", "en");
window.localStorage.removeItem("release_channel_unavailable_ignore_until");
window.localStorage.removeItem("release_channel_switch_notice_seen");
});
await mockApi(page, {
authenticated: true,
permissions: ["superuser", "user"],
sessionData: {
id: 77,
customer_number: 990077,
display_name: "Superuser demo",
runtime_config: {
economic: {
transaction_draft_customer_number: null,
default_distribution_department_id: null,
},
release: releaseRuntime(),
},
},
});
await page.route("**/release/runtime**", async (route) => {
await route.fulfill(json({ data: releaseRuntime() }));
});
await page.route("**/release-manifest.json", async (route) => {
frontendAssetRequests.push(route.request().url());
await route.fulfill(json(releaseManifest()));
});
await page.route("**/release-entry.json", async (route) => {
frontendAssetRequests.push(route.request().url());
await route.fulfill(json({ entry: "assets/app.js", css: ["assets/app.css"] }));
});
await page.route("**/assets/app.css", async (route) => {
frontendAssetRequests.push(route.request().url());
await route.fulfill(text("body {}", "text/css"));
});
await page.route("**/assets/app.js", async (route) => {
frontendAssetRequests.push(route.request().url());
await new Promise((resolve) => setTimeout(resolve, 800));
await route.fulfill(text("console.log('latest release');", "application/javascript"));
});
await page.route(`${FRONTEND_BASE_URL}/assets/chunk.js`, async (route) => {
await route.fulfill(text("console.log('chunk');", "application/javascript"));
});
await seedAuthenticatedState(page, "release-update-widget-token");
return { frontendAssetRequests };
}
test("desktop release navigation does not predownload frontend updates", async ({ page }, testInfo) => {
test.skip(!testInfo.project.name.includes("desktop"), "desktop release navigation coverage");
await page.setViewportSize({ width: 1280, height: 820 });
const { frontendAssetRequests } = await bootReleaseUpdateScenario(page);
await page.goto("/user", { waitUntil: "domcontentloaded" });
await expect(page.getByTestId("release-frontend-version-badge").first()).toBeVisible({ timeout: 30_000 });
await expect(page.getByTestId("release-frontend-version-badge").first()).not.toContainText("->");
await expect(page.getByTestId("release-update-widget")).toHaveCount(0);
await expect(page.locator(".swal2-popup")).toHaveCount(0);
await page.waitForTimeout(1000);
expect(frontendAssetRequests).toEqual([]);
await page.reload({ waitUntil: "domcontentloaded" });
await expect(page.getByTestId("release-update-widget")).toHaveCount(0);
await expect(page.locator(".swal2-popup")).toHaveCount(0);
await page.waitForTimeout(1000);
expect(frontendAssetRequests).toEqual([]);
});
test("mobile navigation keeps frontend update widget hidden above build info", async ({ page }, testInfo) => {
test.skip(!testInfo.project.name.includes("mobile"), "mobile release navigation coverage");
await page.setViewportSize({ width: 390, height: 820 });
const { frontendAssetRequests } = await bootReleaseUpdateScenario(page);
await page.goto("/user", { waitUntil: "domcontentloaded" });
await page.locator(".mobile-navbar-burger[aria-label='menu']").click();
const menu = page.locator(".mobile-menu-open");
await expect(menu).toBeVisible();
await expect(menu.getByTestId("release-update-widget")).toHaveCount(0);
await expect(menu.getByTestId("mobile-build-info")).toBeVisible();
await expect(page.locator(".swal2-popup")).toHaveCount(0);
await page.waitForTimeout(1000);
expect(frontendAssetRequests).toEqual([]);
});