218 lines
6.8 KiB
JavaScript
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([]);
|
|
});
|