Files
pleno-vue/tests/unit/xlvask-autopilot-component.spec.js
T

221 lines
7.4 KiB
JavaScript

// @vitest-environment jsdom
import { flushPromises, shallowMount } from "@vue/test-utils";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { ref } from "vue";
import { createI18n } from "vue-i18n";
const { request, loadList, fire } = vi.hoisted(() => ({
request: vi.fn(),
loadList: vi.fn(),
fire: vi.fn(),
}));
vi.mock("sweetalert2", () => ({ default: { fire } }));
vi.mock("@/components/session/token/SessionUser.vue", () => ({
SessionUser: {
request,
functions: {
currency: { toLocal: (value) => String(value) },
parseErrorMessage: (error) => String(error),
},
objects: {
global: {
language: {
completed: "Completed",
generated: "Generated",
possible_duplicates: "Possible duplicates",
price_match: "Price match",
price_unmatch: "Price mismatch",
regret: "Cancel",
},
},
},
},
}));
vi.mock("@/components/pagination/paginatedList.vue", () => ({
usePaginatedListInstance: () => ({ loadList }),
}));
vi.mock("@/components/pagination/departmentTabs.vue", () => ({
departments: ref([{ id: 1, name: "Hall 1" }]),
getDepartments: vi.fn(),
getDepartmentName: () => "Hall 1",
}));
vi.mock("@/components/displays/department/pos/sync/xlvaskUsageAmountCache.js", () => ({
clearCachedXlvaskUsageAmount: vi.fn(),
getCachedXlvaskUsageAmount: vi.fn(() => null),
setCachedXlvaskUsageAmount: vi.fn(),
}));
import XlvaskUsageOrdersTable from "@/components/displays/department/pos/sync/xlvaskUsageOrdersTable.vue";
const actionableRow = () => ({
id: 8101,
reg_1: "AB12345",
created_at: new Date().toISOString(),
customer_name: "Test customer",
department_id: 1,
lane: 1,
total_net_amount: 125,
import_state: "new",
resolution_state: "needs_review",
certainty: "certain",
planned_action: "attach_order",
automation: {
id: 7101,
status: "suggested",
action: "attach_order",
can_accept: true,
can_deny: true,
can_ignore: true,
can_attach_order: true,
can_create_order: true,
review_eligible: true,
},
});
const mountTable = (props = {}) =>
shallowMount(XlvaskUsageOrdersTable, {
props: {
objects: [actionableRow()],
...props,
},
global: {
plugins: [
createI18n({ legacy: false, locale: "en", missingWarn: false, fallbackWarn: false, messages: { en: {} } }),
],
stubs: {
WhiteBoxCard: {
template: "<section><slot name='header'/><slot name='content'/></section>",
},
OrderItemsTable: true,
},
},
});
describe("XL-Vask automation component authorization", () => {
beforeEach(() => {
request.mockReset();
request.mockResolvedValue({ data: { data: { action_halted: false } } });
loadList.mockReset();
fire.mockReset();
fire.mockResolvedValue({ isConfirmed: true });
});
it("renders evidence read-only when review capability is absent", () => {
const wrapper = mountTable({ allowSelectMultiple: true, allowReviewActions: false });
expect(wrapper.find("input[type='checkbox']").exists()).toBe(false);
expect(wrapper.find("[data-testid='xlvask-automation-accept-8101']").exists()).toBe(false);
expect(wrapper.find(".xlvask-usage-actions-column").exists()).toBe(false);
});
it("renders mutation controls only for an eligible row with review capability", () => {
const wrapper = mountTable({ allowSelectMultiple: true, allowReviewActions: true, allowAdjudicationActions: true });
expect(wrapper.find("input[type='checkbox']").attributes("disabled")).toBeUndefined();
expect(wrapper.find("[data-testid='xlvask-automation-accept-8101']").exists()).toBe(true);
expect(wrapper.find(".xlvask-usage-actions-column").exists()).toBe(true);
});
it("keeps an ineligible row unselectable", () => {
const row = actionableRow();
row.automation.review_eligible = false;
const wrapper = mountTable({ objects: [row], allowSelectMultiple: true, allowReviewActions: true });
expect(wrapper.find("input[type='checkbox']").attributes("disabled")).toBeDefined();
});
it("does not infer eligibility or action permissions from suggested status", async () => {
const row = actionableRow();
delete row.automation.review_eligible;
row.automation.can_accept = false;
row.automation.can_attach_order = false;
row.automation.candidate_orders = [{ order_id: 7001 }];
const wrapper = mountTable({ objects: [row], allowSelectMultiple: true, allowReviewActions: true });
expect(wrapper.find("input[type='checkbox']").attributes("disabled")).toBeDefined();
expect(wrapper.find("[data-testid='xlvask-automation-accept-8101']").exists()).toBe(false);
expect(
wrapper.find("[data-testid='xlvask-automation-candidates-8101'] button").attributes("disabled")
).toBeDefined();
await wrapper.find("[data-testid='xlvask-automation-candidates-8101'] button").trigger("click");
expect(request).not.toHaveBeenCalled();
});
it("makes the department read-only rendering issue no mutation request", async () => {
const wrapper = mountTable({ allowSelectMultiple: true, allowReviewActions: false });
await wrapper.find(".xlvask-usage-registration").trigger("click");
expect(request).not.toHaveBeenCalled();
});
it("submits an explicitly allowed post-action outcome only for policy managers", async () => {
const row = actionableRow();
row.automation = {
id: 7201,
suggestion_id: 7201,
status: "auto_accepted",
adjudication_eligible: true,
allowed_adjudication_outcomes: ["correct", "incorrect", "not_allowed"],
};
const wrapper = mountTable({
objects: [row],
allowReviewActions: false,
allowAdjudicationActions: true,
});
expect(wrapper.find("[data-testid='xlvask-adjudication-correct-8101']").exists()).toBe(true);
expect(wrapper.find("[data-testid='xlvask-adjudication-not_allowed-8101']").exists()).toBe(false);
await wrapper.find("[data-testid='xlvask-adjudication-correct-8101']").trigger("click");
await flushPromises();
expect(request).toHaveBeenCalledWith(
"/modules/xlvask/services/usage/automation/admin/calibrations/labels",
"POST",
{ suggestion_id: 7201, outcome: "correct" }
);
expect(loadList).toHaveBeenCalled();
});
it("does not expose adjudication to an ordinary reviewer", () => {
const row = actionableRow();
row.automation.adjudication_eligible = true;
row.automation.allowed_adjudication_outcomes = ["correct"];
const wrapper = mountTable({
objects: [row],
allowReviewActions: true,
allowAdjudicationActions: false,
});
expect(wrapper.find("[data-testid='xlvask-adjudication-8101']").exists()).toBe(false);
});
it("surfaces every successful adverse adjudication as an automation halt", async () => {
const row = actionableRow();
row.automation = {
id: 7202,
status: "auto_accepted",
adjudication_eligible: true,
allowed_adjudication_outcomes: ["incorrect"],
};
const wrapper = mountTable({
objects: [row],
allowAdjudicationActions: true,
});
await wrapper.find("[data-testid='xlvask-adjudication-incorrect-8101']").trigger("click");
await flushPromises();
expect(fire).toHaveBeenLastCalledWith(
expect.objectContaining({
icon: "warning",
title: "invoicing_period.xlvask_autopilot.adjudication.halted",
})
);
});
});