Add release headers service, runtime-aware API header handling, and i18n updates for release configuration

- Implement `releaseHeaders.js` to manage release-related HTTP headers.
- Update requests to include runtime-based release headers.
- Extend i18n phrases across multiple locales for release runtime and configuration labels.
- Enhance unit tests for header building and API availability error handling.
This commit is contained in:
Jeppe Bundgaard
2026-05-20 17:52:54 +02:00
parent 130cc2fc10
commit ea8db593c5
43 changed files with 3191 additions and 171 deletions
+130 -4
View File
@@ -4,8 +4,15 @@ import { mockApi, seedAuthenticatedState } from "./support/network.js";
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 GUARD_TIMEOUT_MS = 30_000;
const unavailableRuntime = {
generated_at: "2026-05-19T09:00:00.000Z",
@@ -141,7 +148,7 @@ test("users assigned to an unconfigured release channel can ignore the guard tem
await page.goto("/user", { waitUntil: "domcontentloaded" });
const guard = page.getByTestId("release-channel-unavailable-page");
await expect(guard).toBeVisible();
await expect(guard).toBeVisible({ timeout: GUARD_TIMEOUT_MS });
await expect(guard).toContainText("Release channel is not ready");
await expect(guard).toContainText("Canary");
await expect(page.getByTestId("release-channel-missing")).toContainText("Release bundle");
@@ -154,6 +161,101 @@ test("users assigned to an unconfigured release channel can ignore the guard tem
await page.evaluate(() => window.localStorage.removeItem("release_channel_unavailable_ignore_until"));
});
test("invalid release runtime JSON shows the unavailable guard instead of crashing bootstrap", async ({ page }) => {
const internalRuntime = {
...unavailableRuntime,
channel: {
...unavailableRuntime.channel,
slug: "internal",
name: "Internal",
default_channel: false,
},
versions: { frontend: null, api: null, bundle_id: null },
availability: {
configured: false,
missing: ["release_runtime"],
status: "unconfigured",
},
};
const consoleErrors = [];
page.on("console", (message) => {
if (message.type() === "error") {
consoleErrors.push(message.text());
}
});
await boot(page, internalRuntime);
await page.addInitScript(() => {
window.localStorage.setItem("release_channel_selected_slug", "internal");
});
await page.route("**/release/runtime**", async (route) => {
await route.fulfill({
status: 200,
contentType: "text/html",
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: '<br /><b>Warning</b> Composer autoload warning {"success":true}',
});
});
await page.goto("/user", { waitUntil: "domcontentloaded" });
const guard = page.getByTestId("release-channel-unavailable-page");
await expect(guard).toBeVisible({ timeout: GUARD_TIMEOUT_MS });
await expect(guard).toContainText("Internal");
await expect(page.getByTestId("release-channel-missing")).toContainText("Release runtime");
expect(consoleErrors.join("\n")).not.toContain("Cannot read properties of null");
});
test("selected channel auth session 404 shows the release channel guard", async ({ page }) => {
const internalRuntime = {
...availableRuntime(),
channel: {
...unavailableRuntime.channel,
slug: "internal",
name: "Internal",
default_channel: false,
},
frontend_base_url: null,
api_base_url: "https://api-v2.truckwash.io/internal/api",
urls: {
frontend_base_url: null,
api_base_url: "https://api-v2.truckwash.io/internal/api",
},
availability: {
configured: true,
missing: [],
status: "ready",
},
};
const channelApiRequests = [];
await boot(page, internalRuntime);
await page.addInitScript(() => {
window.localStorage.setItem("release_channel_selected_slug", "internal");
});
await page.route("**/release/runtime**", async (route) => {
await route.fulfill(json({ data: internalRuntime }));
});
await page.route("https://api-v2.truckwash.io/internal/api/auth/session**", async (route) => {
channelApiRequests.push(route.request().url());
await route.fulfill(json({ data: { message: "Not found" } }, 404));
});
await page.goto("/user", { waitUntil: "domcontentloaded" });
const guard = page.getByTestId("release-channel-unavailable-page");
await expect(guard).toBeVisible({ timeout: GUARD_TIMEOUT_MS });
await expect(guard).toContainText("Internal");
await expect(page.getByTestId("release-channel-missing")).toContainText("API URL");
await expect(page.locator(".swal2-popup")).toHaveCount(0);
await expect.poll(() => channelApiRequests.length).toBeGreaterThan(0);
expect(channelApiRequests.every((url) => url.startsWith("https://api-v2.truckwash.io/internal/api/"))).toBe(true);
});
test("release channel choices show git commit and release time when available", async ({ page }) => {
const runtime = runtimeWithSelectableReleaseDetails();
await boot(page, runtime);
@@ -163,7 +265,7 @@ test("release channel choices show git commit and release time when available",
await page.goto("/user", { waitUntil: "domcontentloaded" });
const guard = page.getByTestId("release-channel-unavailable-page");
await expect(guard).toBeVisible();
await expect(guard).toBeVisible({ timeout: GUARD_TIMEOUT_MS });
const canary = page.getByTestId("release-channel-option-canary");
await expect(canary).toBeVisible();
@@ -190,6 +292,28 @@ test("predefined release channel text is localized on the guard page", async ({
name: "Internal",
description: "Internal staff and superuser validation channel.",
},
versions: {
frontend: {
version_label: "frontend-internal",
commit_sha: "130cc2fc106a1111222233334444555566667777",
deployed_at: "2026-05-20T09:32:00.000Z",
},
api: {
version_label: "api-internal",
commit_sha: "24ac681365511111222233334444555566667777",
deployed_at: "2026-05-20T09:32:00.000Z",
},
bundle_id: 10,
bundle: {
id: 10,
promoted_at: "2026-05-20T09:33:00.000Z",
},
},
availability: {
configured: false,
missing: ["frontend_base_url"],
status: "unconfigured",
},
};
await boot(page, internalRuntime, "da");
@@ -199,10 +323,12 @@ test("predefined release channel text is localized on the guard page", async ({
await page.goto("/user", { waitUntil: "domcontentloaded" });
const guard = page.getByTestId("release-channel-unavailable-page");
await expect(guard).toBeVisible();
await expect(guard).toBeVisible({ timeout: GUARD_TIMEOUT_MS });
await expect(guard).toContainText("Release-kanalen er ikke klar");
await expect(guard).toContainText("Intern");
await expect(guard).toContainText("Intern kanal til medarbejdere");
await expect(page.getByTestId("release-channel-missing")).toContainText("Frontend-URL");
await expect(page.getByTestId("release-channel-missing")).not.toContainText("frontend_base_url");
await expect(guard).not.toContainText("Internal staff");
await expect(guard).not.toContainText(/\binternal\b/);
await expect(guard).not.toContainText(/\p{L}\?\p{L}|\?\p{L}/u);
@@ -218,7 +344,7 @@ test("users assigned to an unconfigured release channel can check again when it
await page.goto("/user", { waitUntil: "domcontentloaded" });
const guard = page.getByTestId("release-channel-unavailable-page");
await expect(guard).toBeVisible();
await expect(guard).toBeVisible({ timeout: GUARD_TIMEOUT_MS });
const currentUrl = page.url();
releaseRuntime = availableRuntime();