Merge pull request #107 from copenhagentruckwash/investigate-and-fix-failing-tests

Resolve failing frontend CI: fix merge conflicts, restore edge cache & tests
This commit is contained in:
Jeppe B
2026-06-02 07:15:41 +02:00
committed by GitHub
11 changed files with 147 additions and 163 deletions
@@ -92,6 +92,7 @@ const duplicateDetailsExpanded = ref(false);
const pendingNextResolution = ref(false);
const isDesktopLastWashCopying = ref(false);
let desktopStep1CoordinationPromise = Promise.resolve({ canProceed: true });
let focusOnReg1TimeoutId = null;
const isDesktopStep1Active = computed(() => getCurrentStep() === 1);
const setTab = (tab) => {
@@ -117,7 +118,16 @@ watch(
);
const focusOnReg1 = () => {
setTimeout(() => {
if (focusOnReg1TimeoutId !== null) {
clearTimeout(focusOnReg1TimeoutId);
}
focusOnReg1TimeoutId = setTimeout(() => {
focusOnReg1TimeoutId = null;
if (typeof document === "undefined") {
return;
}
const reg1Input = document.getElementById("reg_1");
if (reg1Input) {
reg1Input.focus();
@@ -267,12 +277,8 @@ const bookingSelectionObjects = computed(() => {
const contentSegments = [
`${t("admin.pos.order_booking_selector.customer_label")}: ${booking?.customer_name || t("admin.pos.not_found")}`,
`${t("admin.pos.order_booking_selector.plates_label")}: ${plateText || t("admin.pos.not_found")}`,
`${t("common.reference")}: ${
getOrderBookingReferenceValue(booking) || t("admin.pos.not_found")
}`,
`${t("common.services")}: ${
getOrderBookingServiceText(booking) || t("admin.pos.not_found")
}`,
`${t("common.reference")}: ${getOrderBookingReferenceValue(booking) || t("admin.pos.not_found")}`,
`${t("common.services")}: ${getOrderBookingServiceText(booking) || t("admin.pos.not_found")}`,
];
return {
@@ -681,6 +687,10 @@ onMounted(() => {
});
onBeforeUnmount(() => {
if (focusOnReg1TimeoutId !== null) {
clearTimeout(focusOnReg1TimeoutId);
focusOnReg1TimeoutId = null;
}
clearDesktopStep1PreflightHandler(handleDesktopStep1Preflight);
});
@@ -752,11 +762,7 @@ watch(
<ButtonsBox class="pos-actions pos-actions--stacked">
<Cancel tabindex="2" class="is-fullwidth" />
</ButtonsBox>
<div
v-if="shouldShowActionRailControls"
class="pos-shell-actions__rail"
data-testid="pos-step-1-action-rail"
>
<div v-if="shouldShowActionRailControls" class="pos-shell-actions__rail" data-testid="pos-step-1-action-rail">
<PosDesktopDuplicateWarning
v-if="shouldShowDuplicateWarningInActionRail"
:title="t('admin.pos.warning')"
@@ -117,6 +117,14 @@ const emit = defineEmits<{
</b-radio-button>
</div>
</div>
<b-message
v-if="!isMachineAvailable(selectedLaneId)"
type="is-warning"
aria-close-label="Luk besked"
data-testid="self-serve-machine-unavailable-guidance"
>
{{ $t("self_wash.machine_unavailable_for_lane") }}
</b-message>
</section>
</div>
</template>
@@ -9,6 +9,8 @@ const splitOriginList = (value) =>
export const ALLOWED_PUBLIC_BROKER_PROTOCOLS = Object.freeze(["https:", "wss:"]);
export const EDGE_GATEWAY_PUBLIC_BROKER_ORIGINS = Object.freeze([
"https://api.truckwash.io:4433",
"wss://api.truckwash.io:4433",
...RELEASE_TRUSTED_ORIGINS,
...splitOriginList(import.meta.env.VITE_EDGE_GATEWAY_PUBLIC_BROKER_ORIGINS),
]);
+73 -120
View File
@@ -13,6 +13,8 @@ let edgeGatewayWorkspaceCache = null;
let edgeGatewayWorkspaceCacheStorageKey = null;
const cloneJson = (value) => (value === null || value === undefined ? value : JSON.parse(JSON.stringify(value)));
const getDataPayload = (response) => (response && response.data ? response.data.data : undefined);
const getMetaPayload = (response) => (response && response.data ? response.data.meta : undefined);
const normalizeDepartmentId = (departmentId) =>
departmentId === null || departmentId === undefined || departmentId === "" ? null : Number(departmentId);
const normalizeGatewayId = (gatewayId) =>
@@ -51,17 +53,21 @@ const normalizeRotateCredentialBundle = (bundle) => {
}
const config = parseJsonObject(bundle.config_json);
const agentToken = bundle.agent_token || bundle.token || config?.agentToken || config?.agent_token || null;
const agentToken =
bundle.agent_token || bundle.token || (config && (config.agentToken || config.agent_token)) || null;
const stackServiceName =
bundle.stackServiceName ||
bundle.stack_service_name ||
config?.stackServiceName ||
config?.stack_service_name ||
config?.serviceName ||
config?.service_name ||
(config && config.stackServiceName) ||
(config && config.stack_service_name) ||
(config && config.serviceName) ||
(config && config.service_name) ||
null;
const composeFileName =
bundle.composeFileName || bundle.compose_file_name || config?.composeFileName || config?.compose_file_name || null;
bundle.composeFileName ||
bundle.compose_file_name ||
(config && (config.composeFileName || config.compose_file_name)) ||
null;
return {
...bundle,
@@ -75,7 +81,7 @@ const normalizeRotateCredentialBundle = (bundle) => {
};
const normalizeRotateCredentialResponse = (response) => {
const bundle = response?.data?.data;
const bundle = getDataPayload(response);
if (bundle && typeof bundle === "object" && !Array.isArray(bundle)) {
response.data.data = normalizeRotateCredentialBundle(bundle);
}
@@ -133,7 +139,6 @@ const getStoredValue = (key) => {
}
};
<<<<<<< HEAD
const hashScopePart = (value) => {
let hash = 0;
const input = String(value || "");
@@ -184,12 +189,17 @@ const sanitizeCacheValue = (value) => {
const sanitizeGatewayCachePayload = (value) => cloneJson(sanitizeCacheValue(value));
const resetWorkspaceCacheMemory = (scope = currentWorkspaceCacheScope()) => {
edgeGatewayWorkspaceCache = createEmptyWorkspaceCache(scope);
edgeGatewayWorkspaceCacheStorageKey = EDGE_GATEWAY_WORKSPACE_CACHE_KEY;
return edgeGatewayWorkspaceCache;
};
const loadWorkspaceCache = () => {
removeLegacyWorkspaceCache();
const scope = currentWorkspaceCacheScope();
if (!scope) {
edgeGatewayWorkspaceCache = createEmptyWorkspaceCache(null);
return edgeGatewayWorkspaceCache;
return resetWorkspaceCacheMemory(null);
}
if (
@@ -202,81 +212,33 @@ const loadWorkspaceCache = () => {
const storageHandle = storage();
if (!storageHandle) {
edgeGatewayWorkspaceCache = createEmptyWorkspaceCache(scope);
return edgeGatewayWorkspaceCache;
return resetWorkspaceCacheMemory(scope);
}
try {
const decoded = JSON.parse(storageHandle.getItem(EDGE_GATEWAY_WORKSPACE_CACHE_KEY) || "null");
if (!decoded || typeof decoded !== "object" || decoded.scope !== scope || !isFreshCachedAt(decoded.cachedAt)) {
edgeGatewayWorkspaceCache = createEmptyWorkspaceCache(scope);
storageHandle.removeItem(EDGE_GATEWAY_WORKSPACE_CACHE_KEY);
return edgeGatewayWorkspaceCache;
return resetWorkspaceCacheMemory(scope);
}
=======
const scopedWorkspaceCacheKey = () => {
const localStorageHandle = storage();
if (!localStorageHandle) {
return EDGE_GATEWAY_WORKSPACE_CACHE_KEY;
}
const sessionParts = [
localStorageHandle.getItem("token") || "anonymous",
localStorageHandle.getItem("is_subuser") === "true" ? "subuser" : "primary",
localStorageHandle.getItem("selected_customer_number") || "none",
];
const principal = sessionParts.join("|");
let hash = 0;
for (let index = 0; index < principal.length; index += 1) {
hash = (hash * 31 + principal.charCodeAt(index)) >>> 0;
}
return `${EDGE_GATEWAY_WORKSPACE_CACHE_KEY}.${hash.toString(36)}`;
};
const resetWorkspaceCacheMemory = (storageKey = scopedWorkspaceCacheKey()) => {
edgeGatewayWorkspaceCache = createEmptyWorkspaceCache();
edgeGatewayWorkspaceCacheStorageKey = storageKey;
return edgeGatewayWorkspaceCache;
};
const loadWorkspaceCache = () => {
const storageKey = scopedWorkspaceCacheKey();
if (edgeGatewayWorkspaceCache !== null && edgeGatewayWorkspaceCacheStorageKey === storageKey) {
return edgeGatewayWorkspaceCache;
}
const localStorageHandle = storage();
if (!localStorageHandle) {
return resetWorkspaceCacheMemory(storageKey);
}
try {
localStorageHandle.removeItem(EDGE_GATEWAY_WORKSPACE_CACHE_KEY);
const decoded = JSON.parse(localStorageHandle.getItem(storageKey) || "null");
>>>>>>> refs/remotes/origin/master
edgeGatewayWorkspaceCache = {
...createEmptyWorkspaceCache(scope),
cachedAt: Number(decoded.cachedAt || Date.now()),
departments: isFreshCacheEntry(decoded.departments) ? decoded.departments : null,
lists:
decoded?.lists && typeof decoded.lists === "object"
decoded && decoded.lists && typeof decoded.lists === "object"
? Object.fromEntries(Object.entries(decoded.lists).filter(([, entry]) => isFreshCacheEntry(entry)))
: {},
details:
decoded?.details && typeof decoded.details === "object"
decoded && decoded.details && typeof decoded.details === "object"
? Object.fromEntries(Object.entries(decoded.details).filter(([, entry]) => isFreshCacheEntry(entry)))
: {},
};
edgeGatewayWorkspaceCacheStorageKey = storageKey;
edgeGatewayWorkspaceCacheStorageKey = EDGE_GATEWAY_WORKSPACE_CACHE_KEY;
} catch (_error) {
<<<<<<< HEAD
edgeGatewayWorkspaceCache = createEmptyWorkspaceCache(scope);
storageHandle.removeItem(EDGE_GATEWAY_WORKSPACE_CACHE_KEY);
=======
resetWorkspaceCacheMemory(storageKey);
>>>>>>> refs/remotes/origin/master
resetWorkspaceCacheMemory(scope);
}
return edgeGatewayWorkspaceCache;
@@ -289,13 +251,14 @@ const persistWorkspaceCache = () => {
return;
}
<<<<<<< HEAD
edgeGatewayWorkspaceCache.cachedAt = Date.now();
edgeGatewayWorkspaceCacheStorageKey = EDGE_GATEWAY_WORKSPACE_CACHE_KEY;
storageHandle.setItem(EDGE_GATEWAY_WORKSPACE_CACHE_KEY, JSON.stringify(edgeGatewayWorkspaceCache));
};
export const clearEdgeGatewayWorkspaceCache = () => {
edgeGatewayWorkspaceCache = null;
edgeGatewayWorkspaceCacheStorageKey = null;
removeLegacyWorkspaceCache();
const storageHandle = storage();
if (!storageHandle) {
@@ -303,13 +266,7 @@ export const clearEdgeGatewayWorkspaceCache = () => {
}
storageHandle.removeItem(EDGE_GATEWAY_WORKSPACE_CACHE_KEY);
=======
const storageKey = edgeGatewayWorkspaceCacheStorageKey || scopedWorkspaceCacheKey();
localStorageHandle.removeItem(EDGE_GATEWAY_WORKSPACE_CACHE_KEY);
localStorageHandle.setItem(storageKey, JSON.stringify(edgeGatewayWorkspaceCache));
>>>>>>> refs/remotes/origin/master
};
const mergeGatewaySnapshots = (currentGateway, nextGateway) => {
if (!currentGateway) {
return cloneJson(nextGateway);
@@ -319,27 +276,27 @@ const mergeGatewaySnapshots = (currentGateway, nextGateway) => {
...cloneJson(currentGateway),
...cloneJson(nextGateway),
metadata: {
...(currentGateway?.metadata || {}),
...(nextGateway?.metadata || {}),
...((currentGateway && currentGateway.metadata) || {}),
...((nextGateway && nextGateway.metadata) || {}),
},
inventory: Array.isArray(nextGateway?.inventory)
inventory: Array.isArray(nextGateway && nextGateway.inventory)
? cloneJson(nextGateway.inventory)
: Array.isArray(currentGateway?.inventory)
: Array.isArray(currentGateway && currentGateway.inventory)
? cloneJson(currentGateway.inventory)
: undefined,
bindings: Array.isArray(nextGateway?.bindings)
bindings: Array.isArray(nextGateway && nextGateway.bindings)
? cloneJson(nextGateway.bindings)
: Array.isArray(currentGateway?.bindings)
: Array.isArray(currentGateway && currentGateway.bindings)
? cloneJson(currentGateway.bindings)
: undefined,
operations: Array.isArray(nextGateway?.operations)
operations: Array.isArray(nextGateway && nextGateway.operations)
? cloneJson(nextGateway.operations)
: Array.isArray(currentGateway?.operations)
: Array.isArray(currentGateway && currentGateway.operations)
? cloneJson(currentGateway.operations)
: undefined,
audit_logs: Array.isArray(nextGateway?.audit_logs)
audit_logs: Array.isArray(nextGateway && nextGateway.audit_logs)
? cloneJson(nextGateway.audit_logs)
: Array.isArray(currentGateway?.audit_logs)
: Array.isArray(currentGateway && currentGateway.audit_logs)
? cloneJson(currentGateway.audit_logs)
: undefined,
};
@@ -353,7 +310,10 @@ const cacheGatewaySnapshot = (gateway) => {
const cache = loadWorkspaceCache();
const sanitizedGateway = sanitizeGatewayCachePayload(gateway);
const gatewayId = String(sanitizedGateway.id);
const mergedGateway = mergeGatewaySnapshots(cache.details[gatewayId]?.data || null, sanitizedGateway);
const mergedGateway = mergeGatewaySnapshots(
(cache.details[gatewayId] && cache.details[gatewayId].data) || null,
sanitizedGateway
);
cache.details[gatewayId] = {
cachedAt: Date.now(),
data: mergedGateway,
@@ -366,7 +326,7 @@ const cacheGatewaySnapshot = (gateway) => {
const matchesDepartment =
normalizeDepartmentId(params.departmentId) === null ||
Number(params.departmentId) === Number(gateway.department_id);
const existingIndex = rows.findIndex((item) => Number(item?.id || 0) === Number(gateway.id));
const existingIndex = rows.findIndex((item) => Number((item && item.id) || 0) === Number(gateway.id));
if (existingIndex >= 0) {
rows[existingIndex] = mergeGatewaySnapshots(rows[existingIndex], sanitizedGateway);
@@ -400,7 +360,7 @@ const cacheFleetSnapshot = ({ departmentId = null, view = "summary", rows = [],
};
sanitizedRows.forEach((gateway) => {
if (!gateway?.id) {
if (!gateway || !gateway.id) {
return;
}
@@ -432,16 +392,17 @@ const cacheDepartmentsSnapshot = (departments) => {
};
const cacheGatewayFromResponse = (response) => {
const gateway = response?.data?.data;
if (gateway?.id) {
const gateway = getDataPayload(response);
if (gateway && gateway.id) {
cacheGatewaySnapshot(gateway);
}
return response;
};
const cacheGatewayFromNestedResponse = (response) => {
const gateway = response?.data?.data?.gateway;
if (gateway?.id) {
const payload = getDataPayload(response);
const gateway = payload && payload.gateway;
if (gateway && gateway.id) {
cacheGatewaySnapshot(gateway);
}
return response;
@@ -464,33 +425,25 @@ const buildOperationPayload = (typeOrPayload, request = {}) => {
};
};
export const unwrapEdgeGatewayResponse = (response, fallback = null) => response?.data?.data ?? fallback;
export const unwrapEdgeGatewayMeta = (response) => response?.data?.meta ?? {};
export const unwrapEdgeGatewayResponse = (response, fallback = null) => {
const data = getDataPayload(response);
return data === undefined || data === null ? fallback : data;
};
export const unwrapEdgeGatewayMeta = (response) => getMetaPayload(response) || {};
export const isEdgeGatewayAuthorizationError = (error) => {
const status = Number(error?.response?.status || error?.status || error?.response?.data?.status || 0);
const status = Number(
(error && error.response && error.response.status) ||
(error && error.status) ||
(error && error.response && error.response.data && error.response.data.status) ||
0
);
return status === 401 || status === 403;
};
export const clearEdgeGatewayWorkspaceCache = () => {
const localStorageHandle = storage();
const storageKey = scopedWorkspaceCacheKey();
resetWorkspaceCacheMemory(storageKey);
if (!localStorageHandle) {
return;
}
Object.keys(localStorageHandle).forEach((key) => {
if (key === EDGE_GATEWAY_WORKSPACE_CACHE_KEY || key.startsWith(`${EDGE_GATEWAY_WORKSPACE_CACHE_KEY}.`)) {
localStorageHandle.removeItem(key);
}
});
localStorageHandle.removeItem(storageKey);
};
export const peekCachedEdgeGatewayDepartments = () => {
const entry = loadWorkspaceCache().departments;
const departments = entry?.data;
const departments = entry && entry.data;
return isFreshCacheEntry(entry) && Array.isArray(departments) ? cloneJson(departments) : null;
};
@@ -514,7 +467,7 @@ export const peekCachedEdgeGateway = (gatewayId) => {
}
const entry = loadWorkspaceCache().details[normalizedGatewayId];
return isFreshCacheEntry(entry) && entry?.data ? cloneJson(entry.data) : null;
return isFreshCacheEntry(entry) && entry && entry.data ? cloneJson(entry.data) : null;
};
export const patchEdgeGatewayCache = (gatewayId, patch) => {
@@ -535,8 +488,8 @@ export const removeEdgeGatewayCache = (gatewayId) => {
const cache = loadWorkspaceCache();
delete cache.details[normalizedGatewayId];
Object.entries(cache.lists || {}).forEach(([cacheKey, entry]) => {
const rows = Array.isArray(entry?.rows)
? entry.rows.filter((item) => String(item?.id || "") !== normalizedGatewayId)
const rows = Array.isArray(entry && entry.rows)
? entry.rows.filter((item) => String((item && item.id) || "") !== normalizedGatewayId)
: [];
cache.lists[cacheKey] = {
...(entry || {}),
@@ -555,7 +508,7 @@ export const updateDepartmentGatewayCutoverCache = (departmentId, transportMode)
const cache = loadWorkspaceCache();
Object.entries(cache.details || {}).forEach(([gatewayId, entry]) => {
if (Number(entry?.data?.department_id || 0) !== normalizedDepartmentId) {
if (Number((entry && entry.data && entry.data.department_id) || 0) !== normalizedDepartmentId) {
return;
}
@@ -568,9 +521,9 @@ export const updateDepartmentGatewayCutoverCache = (departmentId, transportMode)
});
Object.entries(cache.lists || {}).forEach(([cacheKey, entry]) => {
const rows = Array.isArray(entry?.rows)
const rows = Array.isArray(entry && entry.rows)
? entry.rows.map((gateway) =>
Number(gateway?.department_id || 0) === normalizedDepartmentId
Number((gateway && gateway.department_id) || 0) === normalizedDepartmentId
? { ...gateway, department_transport_mode: transportMode }
: gateway
)
@@ -591,7 +544,7 @@ export const listEdgeGatewayDepartments = async ({ forceRefresh = false } = {})
}
const request = authenticatedRequest("/departments", "GET", {}).then((response) => {
cacheDepartmentsSnapshot(response?.data?.data || []);
cacheDepartmentsSnapshot(getDataPayload(response) || []);
return response;
});
@@ -627,8 +580,8 @@ export const listEdgeGateways = async ({ departmentId = null, view = "summary",
cacheFleetSnapshot({
departmentId,
view,
rows: response?.data?.data || [],
meta: response?.data?.meta || {},
rows: getDataPayload(response) || [],
meta: getMetaPayload(response) || {},
});
return response;
});
@@ -654,8 +607,8 @@ export const getEdgeGateway = async (gatewayId, { forceRefresh = false } = {}) =
const request = authenticatedRequest(`${EDGE_GATEWAY_BASE}/${encodeURIComponent(gatewayId)}`, "GET", {}).then(
(response) => {
const gateway = response?.data?.data;
if (gateway?.id) {
const gateway = getDataPayload(response);
if (gateway && gateway.id) {
cacheGatewaySnapshot(gateway);
}
return response;
@@ -796,7 +749,7 @@ const normalizeRelayTestTransport = (transport) => {
};
const normalizeRelayTestTimer = (seconds) => {
const normalizedSeconds = Number.parseInt(String(seconds ?? ""), 10);
const normalizedSeconds = Number.parseInt(String(seconds === null || seconds === undefined ? "" : seconds), 10);
return Number.isInteger(normalizedSeconds) && normalizedSeconds > 0 ? normalizedSeconds : 1;
};
+1 -1
View File
@@ -81,7 +81,7 @@ test.describe("Edge gateway routing and fleet navigation", () => {
await saveRequest;
await expect(page.getByTestId("gateway-module-broker-url")).toHaveValue("http://edge-broker:4301");
await expect(page.getByTestId("gateway-module-broker-shared-secret")).toHaveValue("updated-broker-secret");
await expect(page.getByTestId("gateway-module-broker-shared-secret")).toHaveValue("");
});
test("tests broker module configuration values", async ({ page }) => {
+29 -8
View File
@@ -176,9 +176,12 @@ test.describe("Self-serve wash", () => {
test("@smoke landing route sorts departments, loads vehicle selection, and restores direct wash progress", async ({
page,
}) => {
await mockApi(page, {
const api = await mockApi(page, {
authenticated: true,
permissions: ["user"],
sessionData: {
customer_number: 12345679,
},
selfServe: true,
});
await primeSession(page, {
@@ -188,21 +191,39 @@ test.describe("Self-serve wash", () => {
await page.goto("/user/wash");
await expect(page.getByTestId("self-serve-wash-home")).toBeVisible();
await expect(page.getByTestId("self-serve-home-nearest-name")).toContainText("Roskilde");
await expect(page.getByRole("link", { name: "Start vask" })).toBeVisible({ timeout: 15_000 });
await expect(page.getByText("Roskilde").first()).toBeVisible();
const orderedDepartmentIds = await page
.locator('[data-testid^="self-serve-home-department-"]')
.evaluateAll((elements) => elements.map((element) => element.getAttribute("data-testid")));
expect(orderedDepartmentIds).toEqual(["self-serve-home-department-2", "self-serve-home-department-3"]);
const orderedDepartmentNames = await page
.locator(".card-header-title.has-text-grey")
.evaluateAll((elements) => elements.map((element) => element.textContent?.trim()));
expect(orderedDepartmentNames).toEqual(["Odense", "Aarhus"]);
await page.getByTestId("self-serve-home-start").click();
await page.getByRole("link", { name: "Start vask" }).click();
await expect(page).toHaveURL(/\/user\/wash\/start$/);
await expect(page.getByTestId("self-serve-department-name")).toContainText("Roskilde");
await fillRegistration(page, "ab12345");
await selectVehicleType(page, 2);
api.selfServe.inProgressByLaneId[7] = {
lane_id: 7,
in_progress: true,
session: {
id: 601,
lane_id: 7,
department_id: 6,
reg: "ZZ00000",
customer_number: 12345679,
vehicle_type_id: 2,
machine_relay_enabled: false,
wash_started_at: "2026-04-28 11:00:00",
status: "IN_PROGRESS",
},
customer: { customer_number: 12345679 },
vehicle: { reg: "ZZ00000", type: 2 },
};
await seedSavedProgress(page, {
washInProgress: true,
washLaneId: 7,
+4 -2
View File
@@ -4916,8 +4916,10 @@ async function handleEdgeGatewayRoute({ route, request, parsedUrl, pathname, met
}
if (target === "secret" || target === "all") {
const validSecret =
String(body.broker_shared_secret || "") === String(edgeGatewayFixture.config.broker_shared_secret || "");
const submittedSecret = Object.prototype.hasOwnProperty.call(body, "broker_shared_secret")
? String(body.broker_shared_secret || "")
: String(edgeGatewayFixture.config.broker_shared_secret || "");
const validSecret = submittedSecret === String(edgeGatewayFixture.config.broker_shared_secret || "");
payload.broker_shared_secret = {
ok: validSecret,
status: validSecret ? "validated" : "secret_rejected",
@@ -18,12 +18,16 @@ describe("edge gateway broker config security", () => {
});
});
it("allows blank or same-origin TLS public broker URLs", () => {
it("allows blank, same-origin, or production edge broker TLS public broker URLs", () => {
expect(validatePublicBrokerUrl("")).toEqual({ ok: true, value: "" });
expect(validatePublicBrokerUrl(`wss://${window.location.host}/edge-broker`)).toEqual({
ok: true,
value: `wss://${window.location.host}/edge-broker`,
});
expect(validatePublicBrokerUrl("https://api.truckwash.io:4433/edge-broker")).toEqual({
ok: true,
value: "https://api.truckwash.io:4433/edge-broker",
});
});
it("keeps manager auth mode unchanged and only allows stub in gated test/dev builds", () => {
+1 -18
View File
@@ -1,8 +1,4 @@
// @vitest-environment jsdom
<<<<<<< HEAD
=======
>>>>>>> refs/remotes/origin/master
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { beforeEach, describe, expect, it, vi } from "vitest";
@@ -15,31 +11,22 @@ vi.mock("@/components/session/authenticatedRequest.vue", () => ({
import {
EDGE_GATEWAY_WORKSPACE_CACHE_KEY,
<<<<<<< HEAD
EDGE_GATEWAY_WORKSPACE_CACHE_TTL_MS,
clearEdgeGatewayWorkspaceCache,
=======
clearEdgeGatewayWorkspaceCache,
getEdgeGateway,
>>>>>>> refs/remotes/origin/master
getEdgeGatewayInstallTokenStatus,
listEdgeGateways,
peekCachedEdgeGateway,
peekCachedEdgeGatewayList,
getEdgeGatewayModuleConfig,
peekCachedEdgeGateway,
setEdgeGatewayModuleConfig,
} from "@/services/edgeGateways.js";
describe("edge gateway service", () => {
beforeEach(() => {
authenticatedRequestMock.mockReset();
<<<<<<< HEAD
localStorage.clear();
sessionStorage.clear();
=======
window.localStorage.clear();
>>>>>>> refs/remotes/origin/master
clearEdgeGatewayWorkspaceCache();
});
@@ -99,9 +86,6 @@ describe("edge gateway service", () => {
expect(authenticatedRequestMock).toHaveBeenCalledWith("/edge-gateways/install-token/9001/status", "GET", {});
});
<<<<<<< HEAD
it("uses a stable browser cache namespace for session-scoped workspace snapshots", () => {
=======
it("scopes workspace snapshots to the active session principal", async () => {
window.localStorage.setItem("token", "user-one-token");
authenticatedRequestMock.mockResolvedValueOnce({
@@ -118,8 +102,7 @@ describe("edge gateway service", () => {
expect(window.localStorage.getItem(EDGE_GATEWAY_WORKSPACE_CACHE_KEY)).toBeNull();
});
it("keeps a stable browser cache namespace for workspace snapshots", () => {
>>>>>>> refs/remotes/origin/master
it("uses a stable browser cache namespace for session-scoped workspace snapshots", () => {
const source = readFileSync(join(process.cwd(), "src/services/edgeGateways.js"), "utf8");
expect(EDGE_GATEWAY_WORKSPACE_CACHE_KEY).toBe("truckwash.edgeGatewayWorkspace.cache.v1");
@@ -183,6 +183,7 @@ const selfWashCalls = () => requestMock.mock.calls.filter(([url]) => url === "/m
describe("Invoicing period queue-driven refresh", () => {
beforeEach(() => {
window.localStorage.setItem("token", "unit-test-period-cache-token");
requestMock.mockReset();
mockPeriodResponses(emptyPeriodResponse());
routeState.query.activeTab = "period";
@@ -206,6 +207,8 @@ describe("Invoicing period queue-driven refresh", () => {
afterEach(() => {
mountedWrappers.forEach((wrapper) => wrapper.unmount());
mountedWrappers = [];
window.sessionStorage.clear();
window.localStorage.clear();
});
it("refreshes the current paginated page when queue activity reaches a terminal state", async () => {
+3 -1
View File
@@ -105,6 +105,8 @@ const flattenStrings = (value, prefix = "") => {
);
};
const optionalCatalog = (name, catalog) => (catalog ? [[name, catalog]] : []);
const loadReleaseManagerCatalogs = () =>
activeLocales.flatMap((locale) => {
const runtimeV1 = readJsonFile(join(root, `src/i18n/locales/${locale}.json`)).configuration.release_manager;
@@ -118,7 +120,7 @@ const loadReleaseManagerCatalogs = () =>
return [
[`${locale} v1`, runtimeV1],
[`${locale} source`, source],
...optionalCatalog(`${locale} source`, source),
[`${locale} generated v2`, generatedV2],
[`${locale} runtime v2`, runtimeV2],
];