- Introduced `.prettierrc.json` to enforce consistent code formatting across the project. - Updated unit and e2e test files to address formatting issues, improve readability, and ensure alignment with the new Prettier configuration.
685 lines
24 KiB
JavaScript
685 lines
24 KiB
JavaScript
// @vitest-environment jsdom
|
|
import { mount } from "@vue/test-utils";
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
import axios from "axios";
|
|
import RequestQueueProgress from "@/components/global/RequestQueueProgress.vue";
|
|
import { SessionUser } from "@/components/session/token/SessionUser.vue";
|
|
import { API_URL } from "@/config.js";
|
|
import {
|
|
__configureRequestQueueForTests,
|
|
__resetRequestQueueForTests,
|
|
enqueueRequest,
|
|
reportComponentMissingPermission,
|
|
requestQueueState,
|
|
} from "@/services/requestQueue.js";
|
|
|
|
const flushMicrotasks = async () => {
|
|
await Promise.resolve();
|
|
await Promise.resolve();
|
|
};
|
|
|
|
const flushManyMicrotasks = async (rounds = 10) => {
|
|
for (let index = 0; index < rounds; index += 1) {
|
|
await Promise.resolve();
|
|
}
|
|
};
|
|
|
|
const createDeferred = () => {
|
|
let resolve;
|
|
let reject;
|
|
const promise = new Promise((res, rej) => {
|
|
resolve = res;
|
|
reject = rej;
|
|
});
|
|
|
|
return { promise, resolve, reject };
|
|
};
|
|
|
|
const triggerShiftTriplePress = async () => {
|
|
window.dispatchEvent(new KeyboardEvent("keydown", { key: "Shift" }));
|
|
window.dispatchEvent(new KeyboardEvent("keydown", { key: "Shift" }));
|
|
window.dispatchEvent(new KeyboardEvent("keydown", { key: "Shift" }));
|
|
await flushMicrotasks();
|
|
};
|
|
|
|
describe("RequestQueueProgress", () => {
|
|
const resetSessionUserState = () => {
|
|
SessionUser.isSubuser.value = false;
|
|
SessionUser.user.display_name.value = null;
|
|
SessionUser.user.customer_number.value = null;
|
|
SessionUser.user.group_id.value = null;
|
|
SessionUser.user.email.value = null;
|
|
SessionUser.user.phone.number.value = null;
|
|
|
|
SessionUser.subuser.id.value = null;
|
|
SessionUser.subuser.username.value = null;
|
|
SessionUser.subuser.name.value = null;
|
|
SessionUser.subuser.email.value = null;
|
|
SessionUser.subuser.grants.value = [];
|
|
SessionUser.subuser.selectedGrantCustomerNumber.value = null;
|
|
};
|
|
|
|
beforeEach(() => {
|
|
vi.useFakeTimers();
|
|
localStorage.clear();
|
|
__resetRequestQueueForTests();
|
|
__configureRequestQueueForTests({ maxConcurrentGet: 1, maxConcurrentOther: 1, spacingMs: 0 });
|
|
vi.stubGlobal(
|
|
"fetch",
|
|
vi.fn(async () => ({ ok: true, status: 200 }))
|
|
);
|
|
resetSessionUserState();
|
|
});
|
|
|
|
afterEach(() => {
|
|
__resetRequestQueueForTests();
|
|
resetSessionUserState();
|
|
localStorage.clear();
|
|
vi.restoreAllMocks();
|
|
vi.unstubAllGlobals();
|
|
vi.useRealTimers();
|
|
});
|
|
|
|
it("is hidden by default and becomes visible + expanded on shift 3 times", async () => {
|
|
const wrapper = mount(RequestQueueProgress);
|
|
|
|
expect(wrapper.find("[data-testid='request-queue-progress']").exists()).toBe(false);
|
|
|
|
const first = createDeferred();
|
|
const second = createDeferred();
|
|
|
|
const requestOne = enqueueRequest(() => first.promise);
|
|
const requestTwo = enqueueRequest(() => second.promise);
|
|
|
|
await flushMicrotasks();
|
|
expect(wrapper.find("[data-testid='request-queue-progress']").exists()).toBe(false);
|
|
|
|
await triggerShiftTriplePress();
|
|
expect(wrapper.get("[data-testid='request-queue-progress']").text()).toContain("1 active, 1 queued");
|
|
expect(wrapper.find("[data-testid='request-queue-progress-details']").exists()).toBe(true);
|
|
|
|
first.resolve({ status: 200 });
|
|
await flushManyMicrotasks();
|
|
|
|
expect(wrapper.get("[data-testid='request-queue-progress']").text()).toContain("Request queue:");
|
|
|
|
second.resolve({ status: 200 });
|
|
await Promise.all([requestOne, requestTwo]);
|
|
await flushManyMicrotasks();
|
|
|
|
expect(wrapper.get("[data-testid='request-queue-progress']").text()).toContain("Request queue complete: 2/2");
|
|
|
|
vi.advanceTimersByTime(2600);
|
|
await flushMicrotasks();
|
|
|
|
expect(wrapper.find("[data-testid='request-queue-progress']").exists()).toBe(true);
|
|
});
|
|
|
|
it("stays hidden until shift 3 times even for small batches", async () => {
|
|
const wrapper = mount(RequestQueueProgress);
|
|
const onlyRequest = createDeferred();
|
|
|
|
const request = enqueueRequest(() => onlyRequest.promise);
|
|
await flushManyMicrotasks();
|
|
|
|
expect(wrapper.find("[data-testid='request-queue-progress']").exists()).toBe(false);
|
|
await triggerShiftTriplePress();
|
|
expect(wrapper.find("[data-testid='request-queue-progress']").exists()).toBe(true);
|
|
|
|
onlyRequest.resolve({ status: 200 });
|
|
await request;
|
|
await flushManyMicrotasks();
|
|
});
|
|
|
|
it("closes when escape is pressed while open", async () => {
|
|
const wrapper = mount(RequestQueueProgress);
|
|
const first = createDeferred();
|
|
const second = createDeferred();
|
|
|
|
const requestOne = enqueueRequest(() => first.promise, { method: "GET", url: "/esc-close-1" });
|
|
const requestTwo = enqueueRequest(() => second.promise, { method: "GET", url: "/esc-close-2" });
|
|
await flushManyMicrotasks();
|
|
|
|
await triggerShiftTriplePress();
|
|
expect(wrapper.find("[data-testid='request-queue-progress']").exists()).toBe(true);
|
|
|
|
window.dispatchEvent(new KeyboardEvent("keydown", { key: "Escape" }));
|
|
await flushManyMicrotasks();
|
|
expect(wrapper.find("[data-testid='request-queue-progress']").exists()).toBe(false);
|
|
|
|
first.resolve({ status: 200 });
|
|
second.resolve({ status: 200 });
|
|
await Promise.all([requestOne, requestTwo]);
|
|
await flushManyMicrotasks();
|
|
});
|
|
|
|
it("dispatches system-search close event on shift 3x for superuser", async () => {
|
|
vi.spyOn(SessionUser, "canAccessSuperUser").mockReturnValue(true);
|
|
const closeEventListener = vi.fn();
|
|
window.addEventListener("system-search:close", closeEventListener);
|
|
|
|
mount(RequestQueueProgress);
|
|
await triggerShiftTriplePress();
|
|
|
|
expect(closeEventListener).toHaveBeenCalled();
|
|
expect(closeEventListener.mock.calls.some(([event]) => event?.type === "system-search:close")).toBe(true);
|
|
|
|
window.removeEventListener("system-search:close", closeEventListener);
|
|
});
|
|
|
|
it("expands upward with active endpoint details, method icon and timer", async () => {
|
|
const wrapper = mount(RequestQueueProgress);
|
|
const first = createDeferred();
|
|
const second = createDeferred();
|
|
|
|
const requestOne = enqueueRequest(() => first.promise, { method: "POST", url: "/orders" });
|
|
const requestTwo = enqueueRequest(() => second.promise, { method: "GET", url: "/order/items" });
|
|
|
|
await flushManyMicrotasks();
|
|
await triggerShiftTriplePress();
|
|
await flushManyMicrotasks();
|
|
|
|
expect(wrapper.find("[data-testid='request-queue-errors-box']").exists()).toBe(true);
|
|
expect(wrapper.find("[data-testid='request-queue-missing-permissions-box']").exists()).toBe(true);
|
|
expect(wrapper.find("[data-testid='request-queue-runtime-box']").exists()).toBe(true);
|
|
expect(wrapper.find("[data-testid='request-queue-user-box']").exists()).toBe(true);
|
|
|
|
const details = wrapper.get("[data-testid='request-queue-progress-details']");
|
|
expect(details.text()).toContain("Active requests");
|
|
expect(details.text()).toContain("POST");
|
|
expect(details.text()).toContain("/orders");
|
|
expect(details.text()).toMatch(/ms|s/);
|
|
expect(details.find(".fa-plus-circle").exists()).toBe(true);
|
|
|
|
const runtimeBox = wrapper.get("[data-testid='request-queue-runtime-box']");
|
|
expect(runtimeBox.text()).toContain("API URL");
|
|
expect(runtimeBox.text()).toContain("Current host");
|
|
expect(runtimeBox.text()).toContain("Environment");
|
|
expect(runtimeBox.text()).toContain("Commit");
|
|
expect(runtimeBox.text()).toContain("Version time");
|
|
expect(runtimeBox.text()).toContain("Outgoing requests");
|
|
expect(runtimeBox.text()).toContain("Ingoing responses");
|
|
expect(runtimeBox.text()).toContain("Outgoing bandwidth");
|
|
expect(runtimeBox.text()).toContain("Ingoing bandwidth");
|
|
|
|
const userBox = wrapper.get("[data-testid='request-queue-user-box']");
|
|
expect(userBox.text()).toContain("User type");
|
|
expect(userBox.text()).toContain("User");
|
|
|
|
first.resolve({ status: 200 });
|
|
second.resolve({ status: 200 });
|
|
await Promise.all([requestOne, requestTwo]);
|
|
await flushManyMicrotasks();
|
|
});
|
|
|
|
it("tracks runtime ingoing/outgoing request counts and bandwidth totals", async () => {
|
|
const wrapper = mount(RequestQueueProgress);
|
|
const first = createDeferred();
|
|
const second = createDeferred();
|
|
|
|
const requestOne = enqueueRequest(() => first.promise, {
|
|
method: "POST",
|
|
url: "/runtime-metrics-a",
|
|
requestData: {
|
|
headers: { "X-Trace": "a" },
|
|
data: { payload: "first" },
|
|
},
|
|
});
|
|
const requestTwo = enqueueRequest(() => second.promise, {
|
|
method: "GET",
|
|
url: "/runtime-metrics-b",
|
|
requestData: {
|
|
params: { page: 1, limit: 10 },
|
|
},
|
|
});
|
|
await flushManyMicrotasks();
|
|
await triggerShiftTriplePress();
|
|
await flushManyMicrotasks();
|
|
|
|
first.resolve({ status: 200, data: { ok: true, item: "a" } });
|
|
second.resolve({ status: 200, data: { ok: true, item: "b" } });
|
|
await Promise.all([requestOne, requestTwo]);
|
|
await flushManyMicrotasks();
|
|
|
|
expect(requestQueueState.networkTotals.outgoingRequests).toBe(2);
|
|
expect(requestQueueState.networkTotals.ingoingResponses).toBe(2);
|
|
expect(requestQueueState.networkTotals.outgoingBytes).toBeGreaterThan(0);
|
|
expect(requestQueueState.networkTotals.ingoingBytes).toBeGreaterThan(0);
|
|
|
|
const runtimeBox = wrapper.get("[data-testid='request-queue-runtime-box']");
|
|
expect(runtimeBox.text()).toContain("Outgoing requests");
|
|
expect(runtimeBox.text()).toContain("Ingoing responses");
|
|
expect(runtimeBox.text()).toContain("Outgoing bandwidth");
|
|
expect(runtimeBox.text()).toContain("Ingoing bandwidth");
|
|
});
|
|
|
|
it("shows subuser details in the user box", async () => {
|
|
SessionUser.isSubuser.value = true;
|
|
SessionUser.subuser.name.value = "Sub User";
|
|
SessionUser.subuser.id.value = 77;
|
|
SessionUser.subuser.email.value = "subuser@example.com";
|
|
SessionUser.subuser.grants.value = [{ billing_customer_number: 12345, permissions: [] }];
|
|
SessionUser.subuser.selectedGrantCustomerNumber.value = 12345;
|
|
|
|
const wrapper = mount(RequestQueueProgress);
|
|
const first = createDeferred();
|
|
const second = createDeferred();
|
|
|
|
const requestOne = enqueueRequest(() => first.promise, { method: "GET", url: "/subuser-check-1" });
|
|
const requestTwo = enqueueRequest(() => second.promise, { method: "GET", url: "/subuser-check-2" });
|
|
await flushManyMicrotasks();
|
|
|
|
await triggerShiftTriplePress();
|
|
await flushManyMicrotasks();
|
|
|
|
const userBox = wrapper.get("[data-testid='request-queue-user-box']");
|
|
expect(userBox.text()).toContain("User type");
|
|
expect(userBox.text()).toContain("Subuser");
|
|
expect(userBox.text()).toContain("Sub User");
|
|
expect(userBox.text()).toContain("subuser@example.com");
|
|
expect(userBox.text()).toContain("12345");
|
|
|
|
first.resolve({ status: 200 });
|
|
second.resolve({ status: 200 });
|
|
await Promise.all([requestOne, requestTwo]);
|
|
await flushManyMicrotasks();
|
|
});
|
|
|
|
it("shows ping and bookings insights in the bottom of the middle panel", async () => {
|
|
const wrapper = mount(RequestQueueProgress);
|
|
const pingRequest = createDeferred();
|
|
const bookingsRequest = createDeferred();
|
|
|
|
const requestOne = enqueueRequest(() => pingRequest.promise, { method: "GET", url: "/ping" });
|
|
const requestTwo = enqueueRequest(() => bookingsRequest.promise, { method: "GET", url: "/order-bookings" });
|
|
await flushManyMicrotasks();
|
|
|
|
await triggerShiftTriplePress();
|
|
await flushManyMicrotasks();
|
|
|
|
pingRequest.resolve({ status: 200 });
|
|
bookingsRequest.resolve({ status: 200 });
|
|
await Promise.all([requestOne, requestTwo]);
|
|
await flushManyMicrotasks();
|
|
|
|
const insights = wrapper.get("[data-testid='request-queue-bottom-request-insights']");
|
|
expect(insights.text()).toContain("Ping");
|
|
expect(insights.text()).toContain("Bookings");
|
|
expect(insights.text()).toContain("ms");
|
|
|
|
const pingInsight = wrapper.get("[data-testid='request-queue-insight-ping']");
|
|
const bookingsInsight = wrapper.get("[data-testid='request-queue-insight-bookings']");
|
|
expect(pingInsight.text()).toMatch(/now|s ago|m ago/);
|
|
expect(bookingsInsight.text()).toMatch(/now|s ago|m ago/);
|
|
});
|
|
|
|
it("does not emit recursive update errors while processing many requests", async () => {
|
|
const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
|
|
mount(RequestQueueProgress);
|
|
await triggerShiftTriplePress();
|
|
await flushManyMicrotasks();
|
|
|
|
const requests = Array.from({ length: 30 }, (_, index) =>
|
|
enqueueRequest(async () => ({ status: 200, data: { index } }), {
|
|
method: "GET",
|
|
url: `/bulk-recursive-check-${index}`,
|
|
})
|
|
);
|
|
|
|
await Promise.all(requests);
|
|
await flushManyMicrotasks(30);
|
|
|
|
const hasRecursiveError = [...errorSpy.mock.calls, ...warnSpy.mock.calls].some((call) =>
|
|
call.join(" ").includes("Maximum recursive updates exceeded")
|
|
);
|
|
expect(hasRecursiveError).toBe(false);
|
|
});
|
|
|
|
it("stores errors with request and response payloads and respects configured error limit", async () => {
|
|
__configureRequestQueueForTests({
|
|
errorHistoryLimit: 1,
|
|
missingPermissionsLimit: 1,
|
|
retryByStatusCode: {},
|
|
payloadMaxChars: 8000,
|
|
});
|
|
const wrapper = mount(RequestQueueProgress);
|
|
|
|
const requestOne = enqueueRequest(
|
|
async () => {
|
|
throw {
|
|
message: "First failure",
|
|
response: { status: 500, data: { reason: "first" } },
|
|
};
|
|
},
|
|
{
|
|
method: "POST",
|
|
url: "/first-error",
|
|
requestData: { data: { requestBody: "first" } },
|
|
}
|
|
).catch((error) => error);
|
|
|
|
const requestTwo = enqueueRequest(
|
|
async () => {
|
|
throw {
|
|
message: "Second failure",
|
|
response: {
|
|
status: 403,
|
|
data: {
|
|
success: false,
|
|
data: {
|
|
message: "Missing permission(s)",
|
|
permissions: ["department_notification_sms_get"],
|
|
},
|
|
meta: [],
|
|
includes: [],
|
|
},
|
|
},
|
|
};
|
|
},
|
|
{
|
|
method: "PATCH",
|
|
url: "/second-error",
|
|
requestData: { data: { requestBody: "second" } },
|
|
}
|
|
).catch((error) => error);
|
|
|
|
await Promise.all([requestOne, requestTwo]);
|
|
await flushManyMicrotasks();
|
|
|
|
expect(requestQueueState.errorRequests.length).toBe(1);
|
|
expect(requestQueueState.errorRequests[0].url).toBe("/second-error");
|
|
expect(requestQueueState.errorRequests[0].requestText).toContain("requestBody");
|
|
expect(requestQueueState.errorRequests[0].responseText).toContain("Missing permission(s)");
|
|
expect(requestQueueState.missingPermissions.length).toBe(1);
|
|
expect(requestQueueState.missingPermissions[0].permission).toBe("department_notification_sms_get");
|
|
|
|
await triggerShiftTriplePress();
|
|
await flushManyMicrotasks();
|
|
|
|
const errorsBox = wrapper.get("[data-testid='request-queue-errors-box']");
|
|
expect(errorsBox.text()).toContain("Errors (1)");
|
|
expect(errorsBox.text()).toContain("/second-error");
|
|
expect(errorsBox.text()).toContain("Request and response data");
|
|
expect(errorsBox.text()).toContain("Request");
|
|
expect(errorsBox.text()).toContain("Response");
|
|
|
|
const missingPermissionsBox = wrapper.get("[data-testid='request-queue-missing-permissions-box']");
|
|
expect(missingPermissionsBox.text()).toContain("department_notification_sms_get");
|
|
|
|
await wrapper.get("[data-testid='request-queue-clear-errors']").trigger("click");
|
|
await flushManyMicrotasks();
|
|
|
|
expect(requestQueueState.errorRequests.length).toBe(0);
|
|
expect(wrapper.get("[data-testid='request-queue-errors-box']").text()).toContain("No stored errors");
|
|
expect(requestQueueState.missingPermissions.length).toBe(1);
|
|
|
|
await wrapper.get("[data-testid='request-queue-clear-permissions']").trigger("click");
|
|
await flushManyMicrotasks();
|
|
|
|
expect(requestQueueState.missingPermissions.length).toBe(0);
|
|
expect(wrapper.get("[data-testid='request-queue-missing-permissions-box']").text()).toContain(
|
|
"No missing permissions detected"
|
|
);
|
|
});
|
|
|
|
it("stays visible after errors occur", async () => {
|
|
__configureRequestQueueForTests({ errorHistoryLimit: 2 });
|
|
const wrapper = mount(RequestQueueProgress);
|
|
|
|
const failed = enqueueRequest(
|
|
async () => {
|
|
throw {
|
|
message: "Failure should keep panel visible",
|
|
response: { status: 500, data: { detail: "boom" } },
|
|
};
|
|
},
|
|
{
|
|
method: "DELETE",
|
|
url: "/error-visible",
|
|
requestData: { data: { id: 123 } },
|
|
}
|
|
).catch((error) => error);
|
|
|
|
await failed;
|
|
await flushManyMicrotasks();
|
|
|
|
expect(wrapper.find("[data-testid='request-queue-progress']").exists()).toBe(false);
|
|
await triggerShiftTriplePress();
|
|
expect(wrapper.find("[data-testid='request-queue-progress']").exists()).toBe(true);
|
|
|
|
vi.advanceTimersByTime(30_000);
|
|
await flushManyMicrotasks();
|
|
|
|
expect(wrapper.find("[data-testid='request-queue-progress']").exists()).toBe(true);
|
|
await flushManyMicrotasks();
|
|
|
|
const errorsBox = wrapper.get("[data-testid='request-queue-errors-box']");
|
|
expect(errorsBox.text()).toContain("Errors");
|
|
expect(errorsBox.text()).toContain("/error-visible");
|
|
});
|
|
|
|
it("stores missing permissions when response payload is stringified JSON", async () => {
|
|
__configureRequestQueueForTests({
|
|
missingPermissionsLimit: 5,
|
|
retryByStatusCode: {},
|
|
});
|
|
|
|
const request = enqueueRequest(
|
|
async () => {
|
|
throw {
|
|
message: "Request failed with status code 403",
|
|
response: {
|
|
status: 403,
|
|
data: JSON.stringify({
|
|
success: false,
|
|
data: {
|
|
message: "Missing permission(s)",
|
|
permissions: ["department_notification_sms_get"],
|
|
},
|
|
meta: [],
|
|
includes: [],
|
|
}),
|
|
},
|
|
};
|
|
},
|
|
{
|
|
method: "GET",
|
|
url: "/notification/sms",
|
|
}
|
|
).catch((error) => error);
|
|
|
|
await request;
|
|
await flushManyMicrotasks();
|
|
|
|
expect(requestQueueState.missingPermissions.length).toBe(1);
|
|
expect(requestQueueState.missingPermissions[0].permission).toBe("department_notification_sms_get");
|
|
});
|
|
|
|
it("stores component permissions with COMPONENT method and keeps them below API entries", async () => {
|
|
const wrapper = mount(RequestQueueProgress);
|
|
|
|
const apiRequest = enqueueRequest(
|
|
async () => {
|
|
throw {
|
|
message: "Request failed with status code 403",
|
|
response: {
|
|
status: 403,
|
|
data: {
|
|
success: false,
|
|
data: {
|
|
message: "Missing permission(s)",
|
|
permissions: ["department_notification_sms_get"],
|
|
},
|
|
},
|
|
},
|
|
};
|
|
},
|
|
{
|
|
method: "GET",
|
|
url: "/notification/sms",
|
|
}
|
|
).catch((error) => error);
|
|
|
|
await apiRequest;
|
|
reportComponentMissingPermission("goals_department_create", {
|
|
source: "DepartmentGoals:CreateGoalButton",
|
|
});
|
|
await flushManyMicrotasks();
|
|
await triggerShiftTriplePress();
|
|
await flushManyMicrotasks();
|
|
|
|
const rows = wrapper
|
|
.get("[data-testid='request-queue-missing-permissions-box']")
|
|
.findAll(".request-queue-progress__list-item--permission")
|
|
.map((item) => item.text());
|
|
|
|
expect(rows.length).toBe(2);
|
|
expect(rows[0]).toContain("department_notification_sms_get");
|
|
expect(rows[0]).toContain("GET");
|
|
expect(rows[1]).toContain("goals_department_create");
|
|
expect(rows[1]).toContain("COMPONENT");
|
|
});
|
|
|
|
it("throttles repeated component permission reports", async () => {
|
|
__configureRequestQueueForTests({
|
|
componentPermissionReportWindowMs: 5000,
|
|
missingPermissionsLimit: 10,
|
|
});
|
|
|
|
reportComponentMissingPermission("goals_department_create", {
|
|
source: "DepartmentGoals:CreateGoalButton",
|
|
});
|
|
reportComponentMissingPermission("goals_department_create", {
|
|
source: "DepartmentGoals:CreateGoalButton",
|
|
});
|
|
await flushManyMicrotasks();
|
|
|
|
expect(requestQueueState.missingPermissions.length).toBe(1);
|
|
expect(requestQueueState.missingPermissions[0].count).toBe(1);
|
|
expect(requestQueueState.missingPermissions[0].method).toBe("COMPONENT");
|
|
|
|
vi.advanceTimersByTime(5100);
|
|
reportComponentMissingPermission("goals_department_create", {
|
|
source: "DepartmentGoals:CreateGoalButton",
|
|
});
|
|
await flushManyMicrotasks();
|
|
|
|
expect(requestQueueState.missingPermissions.length).toBe(1);
|
|
expect(requestQueueState.missingPermissions[0].count).toBe(2);
|
|
});
|
|
|
|
it("shows add button for missing permissions when superuser impersonates a user", async () => {
|
|
localStorage.setItem("superuser_token", "su-token");
|
|
SessionUser.user.group_id.value = 19;
|
|
|
|
const wrapper = mount(RequestQueueProgress);
|
|
const request = enqueueRequest(
|
|
async () => {
|
|
throw {
|
|
message: "Request failed with status code 403",
|
|
response: {
|
|
status: 403,
|
|
data: {
|
|
success: false,
|
|
data: {
|
|
message: "Missing permission(s)",
|
|
permissions: ["department_notification_sms_get"],
|
|
},
|
|
},
|
|
},
|
|
};
|
|
},
|
|
{
|
|
method: "GET",
|
|
url: "/notification/sms",
|
|
}
|
|
).catch((error) => error);
|
|
|
|
await request;
|
|
await flushManyMicrotasks();
|
|
await triggerShiftTriplePress();
|
|
await flushManyMicrotasks();
|
|
|
|
expect(
|
|
wrapper.find("[data-testid='request-queue-permission-action-department_notification_sms_get-GET']").exists()
|
|
).toBe(true);
|
|
});
|
|
|
|
it("shows add button for COMPONENT missing permissions when superuser impersonates a user", async () => {
|
|
localStorage.setItem("superuser_token", "su-token");
|
|
SessionUser.user.group_id.value = 19;
|
|
const wrapper = mount(RequestQueueProgress);
|
|
|
|
reportComponentMissingPermission("goals_department_create", {
|
|
source: "DepartmentGoals:CreateGoalButton",
|
|
});
|
|
await flushManyMicrotasks();
|
|
await triggerShiftTriplePress();
|
|
await flushManyMicrotasks();
|
|
|
|
expect(
|
|
wrapper.find("[data-testid='request-queue-permission-action-goals_department_create-COMPONENT']").exists()
|
|
).toBe(true);
|
|
});
|
|
|
|
it("adds missing permission with superuser token and shows disabled checkmark on success", async () => {
|
|
localStorage.setItem("superuser_token", "su-token");
|
|
SessionUser.user.group_id.value = 19;
|
|
const axiosPostSpy = vi.spyOn(axios, "post").mockResolvedValue({ status: 200, data: {} });
|
|
|
|
const wrapper = mount(RequestQueueProgress);
|
|
const request = enqueueRequest(
|
|
async () => {
|
|
throw {
|
|
message: "Request failed with status code 403",
|
|
response: {
|
|
status: 403,
|
|
data: {
|
|
success: false,
|
|
data: {
|
|
message: "Missing permission(s)",
|
|
permissions: ["department_notification_sms_get"],
|
|
},
|
|
},
|
|
},
|
|
};
|
|
},
|
|
{
|
|
method: "GET",
|
|
url: "/notification/sms",
|
|
}
|
|
).catch((error) => error);
|
|
|
|
await request;
|
|
await flushManyMicrotasks();
|
|
await triggerShiftTriplePress();
|
|
await flushManyMicrotasks();
|
|
|
|
const actionButton = wrapper.get(
|
|
"[data-testid='request-queue-permission-action-department_notification_sms_get-GET']"
|
|
);
|
|
await actionButton.trigger("click");
|
|
await flushManyMicrotasks();
|
|
|
|
expect(axiosPostSpy).toHaveBeenCalledWith(
|
|
`${API_URL}/roles/permissions`,
|
|
{
|
|
group_id: 19,
|
|
permission_id: "department_notification_sms_get",
|
|
},
|
|
{
|
|
headers: {
|
|
Authorization: "Bearer su-token",
|
|
},
|
|
}
|
|
);
|
|
|
|
expect(actionButton.attributes("disabled")).toBeDefined();
|
|
expect(actionButton.find(".fa-check").exists()).toBe(true);
|
|
|
|
axiosPostSpy.mockRestore();
|
|
});
|
|
});
|