281 lines
10 KiB
JavaScript
281 lines
10 KiB
JavaScript
// @vitest-environment jsdom
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
import {
|
|
RELEASE_RUNTIME_GLOBAL_KEY,
|
|
RELEASE_SOURCE_OVERRIDE_STORAGE_KEY,
|
|
bootstrapReleaseApp,
|
|
fetchReleaseRuntime,
|
|
isReleaseSourceOverrideAvailable,
|
|
RELEASE_SOURCE_MODES,
|
|
resolveReleaseSourceMode,
|
|
runtimeApiUrl,
|
|
setReleaseSourceOverride,
|
|
shouldLoadRemoteRelease,
|
|
} from "@/services/releaseBootstrap.js";
|
|
import { DEFAULT_STABLE_API_URL } from "@/config.js";
|
|
|
|
describe("release bootstrap", () => {
|
|
beforeEach(() => {
|
|
localStorage.clear();
|
|
delete window[RELEASE_RUNTIME_GLOBAL_KEY];
|
|
});
|
|
|
|
afterEach(() => {
|
|
localStorage.clear();
|
|
delete window[RELEASE_RUNTIME_GLOBAL_KEY];
|
|
document.head.innerHTML = "";
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
it("includes selected release channel in the runtime request", () => {
|
|
localStorage.setItem("release_channel_selected_slug", "Canary Preview!");
|
|
|
|
expect(runtimeApiUrl("https://api.truckwash.io")).toBe(
|
|
"https://api.truckwash.io/release/runtime?release_channel=canary-preview"
|
|
);
|
|
});
|
|
|
|
it("uses the control API for selected stable runtime requests", () => {
|
|
localStorage.setItem("release_channel_selected_slug", "stable");
|
|
|
|
expect(runtimeApiUrl("https://api.truckwash.io")).toBe(
|
|
"https://api.truckwash.io/release/runtime?release_channel=stable"
|
|
);
|
|
});
|
|
|
|
it("uses api-v2 master as the production default API", () => {
|
|
expect(DEFAULT_STABLE_API_URL).toBe("https://api-v2.truckwash.io/master/api");
|
|
expect(runtimeApiUrl(DEFAULT_STABLE_API_URL)).toBe("https://api-v2.truckwash.io/master/api/release/runtime");
|
|
});
|
|
|
|
it("resolves local dev API runtime requests against the current origin", () => {
|
|
expect(runtimeApiUrl("/api")).toBe(`${window.location.origin}/api/release/runtime`);
|
|
});
|
|
|
|
it("attaches release trace, channel, and frontend headers to runtime requests", async () => {
|
|
localStorage.setItem("release_trace_id", "trace-runtime");
|
|
const fetchFn = vi.fn(async () => ({
|
|
ok: true,
|
|
json: async () => ({ data: { channel: { slug: "internal" } } }),
|
|
}));
|
|
|
|
await fetchReleaseRuntime({ fetchFn, apiBaseUrl: "https://api.truckwash.io", selectedChannel: "Internal" });
|
|
|
|
const [url, options] = fetchFn.mock.calls[0];
|
|
expect(url).toBe("https://api.truckwash.io/release/runtime?release_channel=internal");
|
|
expect(options.headers).toMatchObject({
|
|
"X-Release-Trace": "trace-runtime",
|
|
"X-Release-Channel": "internal",
|
|
"X-Frontend-Version": expect.any(String),
|
|
});
|
|
});
|
|
|
|
it("loads the local app for the default channel", async () => {
|
|
const loadLocalApp = vi.fn(async () => ({ local: true }));
|
|
const fetchFn = vi.fn(async () => ({
|
|
ok: true,
|
|
json: async () => ({
|
|
data: {
|
|
channel: { slug: "stable", default_channel: true },
|
|
availability: { configured: true },
|
|
},
|
|
}),
|
|
}));
|
|
|
|
await bootstrapReleaseApp({ loadLocalApp, fetchFn, sourceMode: "deployment" });
|
|
|
|
expect(loadLocalApp).toHaveBeenCalledTimes(1);
|
|
expect(window[RELEASE_RUNTIME_GLOBAL_KEY].channel.slug).toBe("stable");
|
|
expect(window[RELEASE_RUNTIME_GLOBAL_KEY].source).toBe("deployment");
|
|
});
|
|
|
|
it("loads the regular frontend while keeping dynamic API runtime in local source mode", async () => {
|
|
localStorage.setItem("release_channel_selected_slug", "internal");
|
|
const loadLocalApp = vi.fn(async () => ({ local: true }));
|
|
const fetchFn = vi.fn(async () => ({
|
|
ok: true,
|
|
json: async () => ({
|
|
data: {
|
|
channel: { slug: "internal", default_channel: false },
|
|
availability: { configured: true },
|
|
urls: {
|
|
api_base_url: "https://api-v2.truckwash.io/internal/api",
|
|
},
|
|
},
|
|
}),
|
|
}));
|
|
|
|
await bootstrapReleaseApp({ loadLocalApp, fetchFn, sourceMode: "local" });
|
|
|
|
expect(fetchFn).toHaveBeenCalledTimes(1);
|
|
expect(loadLocalApp).toHaveBeenCalledTimes(1);
|
|
expect(window[RELEASE_RUNTIME_GLOBAL_KEY]).toMatchObject({
|
|
source: "deployment",
|
|
requested_source: "local",
|
|
channel: { slug: "internal", default_channel: false },
|
|
urls: { api_base_url: "https://api-v2.truckwash.io/internal/api" },
|
|
availability: { configured: true },
|
|
});
|
|
});
|
|
|
|
it("falls back to local API runtime only when local source runtime resolution fails", async () => {
|
|
vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
const loadLocalApp = vi.fn(async () => ({ local: true }));
|
|
const fetchFn = vi.fn(async () => ({
|
|
ok: false,
|
|
status: 503,
|
|
json: async () => ({}),
|
|
}));
|
|
|
|
await bootstrapReleaseApp({ loadLocalApp, fetchFn, sourceMode: "local" });
|
|
|
|
expect(fetchFn).toHaveBeenCalledTimes(1);
|
|
expect(loadLocalApp).toHaveBeenCalledTimes(1);
|
|
expect(window[RELEASE_RUNTIME_GLOBAL_KEY]).toMatchObject({
|
|
source: "local",
|
|
requested_source: "local",
|
|
channel: { slug: "stable", default_channel: true },
|
|
api_base_url: "/api",
|
|
urls: { api_base_url: "/api" },
|
|
availability: { configured: false, missing: ["release_runtime"], status: "unconfigured" },
|
|
});
|
|
});
|
|
|
|
it("allows storage to steer auto source mode without overriding explicit build source mode", () => {
|
|
localStorage.setItem(RELEASE_SOURCE_OVERRIDE_STORAGE_KEY, "deployment");
|
|
|
|
expect(resolveReleaseSourceMode("auto")).toBe("deployment");
|
|
expect(resolveReleaseSourceMode("local")).toBe("local");
|
|
expect(resolveReleaseSourceMode("local", { allowStorageOverride: true })).toBe("deployment");
|
|
});
|
|
|
|
it("sets only supported release source overrides", () => {
|
|
expect(setReleaseSourceOverride(RELEASE_SOURCE_MODES.LOCAL)).toBe("local");
|
|
expect(localStorage.getItem(RELEASE_SOURCE_OVERRIDE_STORAGE_KEY)).toBe("local");
|
|
|
|
expect(setReleaseSourceOverride("deployment")).toBe("deployment");
|
|
expect(localStorage.getItem(RELEASE_SOURCE_OVERRIDE_STORAGE_KEY)).toBe("deployment");
|
|
|
|
expect(setReleaseSourceOverride("auto")).toBe("auto");
|
|
expect(localStorage.getItem(RELEASE_SOURCE_OVERRIDE_STORAGE_KEY)).toBe("auto");
|
|
|
|
expect(setReleaseSourceOverride("invalid-source")).toBe("");
|
|
expect(localStorage.getItem(RELEASE_SOURCE_OVERRIDE_STORAGE_KEY)).toBe("auto");
|
|
});
|
|
|
|
it("reports storage source overrides only for local dev builds without an explicit source", () => {
|
|
expect(isReleaseSourceOverrideAvailable({ isDev: true, releaseSourceEnv: "" })).toBe(true);
|
|
expect(isReleaseSourceOverrideAvailable({ isDev: false, releaseSourceEnv: "" })).toBe(false);
|
|
expect(isReleaseSourceOverrideAvailable({ isDev: true, releaseSourceEnv: "deployment" })).toBe(false);
|
|
expect(isReleaseSourceOverrideAvailable({ isDev: true, releaseSourceEnv: "local" })).toBe(false);
|
|
});
|
|
|
|
it("loads the local unavailable app state when selected release runtime returns invalid JSON", async () => {
|
|
vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
localStorage.setItem("release_channel_selected_slug", "internal");
|
|
const loadLocalApp = vi.fn(async () => ({ local: true }));
|
|
const fetchFn = vi.fn(async () => ({
|
|
ok: true,
|
|
text: async () => "<br /><b>Warning</b>Composer autoload warning",
|
|
}));
|
|
|
|
await bootstrapReleaseApp({ loadLocalApp, fetchFn, sourceMode: "deployment" });
|
|
|
|
expect(loadLocalApp).toHaveBeenCalledTimes(1);
|
|
expect(window[RELEASE_RUNTIME_GLOBAL_KEY]).toMatchObject({
|
|
channel: { slug: "internal", default_channel: false },
|
|
availability: {
|
|
configured: false,
|
|
missing: ["release_runtime"],
|
|
status: "unconfigured",
|
|
},
|
|
source: "local",
|
|
requested_source: "deployment",
|
|
});
|
|
expect(shouldLoadRemoteRelease(null)).toBe(false);
|
|
});
|
|
|
|
it("does not load remote frontend releases", () => {
|
|
const runtime = {
|
|
channel: { slug: "canary", default_channel: false },
|
|
availability: { configured: true },
|
|
urls: {
|
|
frontend_base_url: "https://api-v2.truckwash.io/canary/frontend",
|
|
},
|
|
};
|
|
|
|
expect(shouldLoadRemoteRelease(runtime)).toBe(false);
|
|
});
|
|
|
|
it("loads the regular frontend for non-default channels while keeping dynamic API runtime", async () => {
|
|
const runtime = {
|
|
channel: { slug: "canary", default_channel: false },
|
|
availability: { configured: true },
|
|
urls: {
|
|
frontend_base_url: "https://api-v2.truckwash.io/canary/frontend",
|
|
api_base_url: "https://api-v2.truckwash.io/canary/api",
|
|
},
|
|
};
|
|
const loadLocalApp = vi.fn(async () => ({ local: true }));
|
|
const fetchFn = vi.fn().mockResolvedValueOnce({
|
|
ok: true,
|
|
json: async () => ({ data: runtime }),
|
|
});
|
|
const importModule = vi.fn(async (url) => ({ url }));
|
|
const originalHref = window.location.href;
|
|
|
|
await bootstrapReleaseApp({ loadLocalApp, fetchFn, importModule, documentRef: document, sourceMode: "deployment" });
|
|
|
|
expect(loadLocalApp).toHaveBeenCalledTimes(1);
|
|
expect(importModule).not.toHaveBeenCalled();
|
|
expect(document.querySelector("link")).toBeNull();
|
|
expect(window[RELEASE_RUNTIME_GLOBAL_KEY]).toMatchObject({
|
|
source: "deployment",
|
|
requested_source: "deployment",
|
|
channel: { slug: "canary", default_channel: false },
|
|
urls: {
|
|
api_base_url: "https://api-v2.truckwash.io/canary/api",
|
|
},
|
|
});
|
|
expect(window.location.href).toBe(originalHref);
|
|
});
|
|
|
|
it("does not request release entry assets during bootstrap", async () => {
|
|
const loadLocalApp = vi.fn(async () => ({ local: true }));
|
|
const runtime = {
|
|
channel: { slug: "canary", default_channel: false },
|
|
availability: { configured: true },
|
|
frontend_base_url: "https://api-v2.truckwash.io/canary/frontend",
|
|
};
|
|
const fetchFn = vi
|
|
.fn()
|
|
.mockResolvedValueOnce({
|
|
ok: true,
|
|
json: async () => ({ data: runtime }),
|
|
})
|
|
.mockResolvedValueOnce({
|
|
ok: true,
|
|
json: async () => ({
|
|
entry: "assets/index-canary.js",
|
|
css: ["assets/index-canary.css"],
|
|
}),
|
|
});
|
|
|
|
await bootstrapReleaseApp({
|
|
loadLocalApp,
|
|
fetchFn,
|
|
importModule: vi.fn(),
|
|
documentRef: document,
|
|
sourceMode: "deployment",
|
|
});
|
|
|
|
expect(loadLocalApp).toHaveBeenCalledTimes(1);
|
|
expect(fetchFn).toHaveBeenCalledTimes(1);
|
|
expect(window[RELEASE_RUNTIME_GLOBAL_KEY].source).toBe("deployment");
|
|
expect(window[RELEASE_RUNTIME_GLOBAL_KEY].requested_source).toBe("deployment");
|
|
expect(window[RELEASE_RUNTIME_GLOBAL_KEY].availability).toMatchObject({
|
|
configured: true,
|
|
});
|
|
});
|
|
});
|