221 lines
7.4 KiB
JavaScript
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",
|
|
})
|
|
);
|
|
});
|
|
});
|