Files
pleno-vue/tests/unit/orders-table.spec.js
T

429 lines
12 KiB
JavaScript

// @vitest-environment jsdom
import { defineComponent, h, ref } from "vue";
import { flushPromises, mount } from "@vue/test-utils";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
const getSingleMock = vi.hoisted(() => vi.fn());
const sessionState = vi.hoisted(() => ({
canAccessSuperUser: false,
}));
vi.mock("vue-i18n", () => ({
useI18n: () => ({
t: (key) => key,
}),
createI18n: () => ({
global: {
t: (key) => key,
},
install: () => {},
}),
}));
vi.mock("vue-router", () => ({
useRoute: () => ({
path: "/admin/12/modules/pos/orders",
fullPath: "/admin/12/modules/pos/orders",
name: "posorders",
params: {
departmentId: "12",
},
}),
}));
vi.mock("sweetalert2", () => ({
default: {
fire: vi.fn(() => Promise.resolve()),
close: vi.fn(),
},
}));
vi.mock("@/components/shop/POSDepartmentProcess.vue", () => ({
downloadAttachment: vi.fn(),
}));
vi.mock("@/components/pagination/departmentTabs.vue", () => ({
departments: ref([{ id: 12, name: "Demo" }]),
getDepartments: vi.fn(),
isLoading: ref(false),
getDepartmentName: (departmentId) => (Number(departmentId) === 12 ? "Demo" : `Department ${departmentId}`),
}));
vi.mock("@/components/displays/PopperDefault.vue", () => ({
showPopper: vi.fn(),
removePopperIfOpen: vi.fn(),
popperBox: vi.fn(),
}));
vi.mock("@/components/pagination/paginatedList.vue", () => ({
orderBy: ref("id"),
orderDirection: ref("asc"),
setOrder: vi.fn(),
loadList: vi.fn(),
}));
vi.mock(
"@/views/dashboards/superUserDashboard/InvoicingBillingPeriod/imports/InvoicingBillingPeriodImportInvoiceQueue.vue",
() => ({
invoiceQueue: {
invoiceCollectionQueueInProgress: ref([]),
invoiceCollectionQueueFailed: ref([]),
invoiceCollectionQueueSuccess: ref([]),
invoiceCollectionQueue: ref([]),
collectionQueueLog: ref([]),
addInvoiceCollectionsToQueue: vi.fn(),
processInvoiceCollectionQueue: vi.fn(),
retryInvoiceCollection: vi.fn(),
},
})
);
vi.mock("@/components/session/token/SessionUser.vue", () => ({
SessionUser: {
canAccessDepartment: vi.fn(() => false),
canAccessAdmin: vi.fn(() => false),
canAccessSuperUser: vi.fn(() => sessionState.canAccessSuperUser),
hasPermission: vi.fn(() => false),
hasAttribute: vi.fn(() => false),
objects: {
orders: {
columns: {
id: { label: "ID" },
customer_id: { label: "Customer" },
cashier_id: { label: "Cashier" },
department_id: { label: "Department" },
reference: { label: "Reference" },
notes: { label: "Notes" },
po: { label: "PO" },
created_at: { label: "Created" },
},
meta: {
labels: {
single: "Order",
multiple: "Orders",
entries: "Orders",
},
},
showEditObjectFieldForm: vi.fn(() => Promise.resolve()),
},
vehicles: {
meta: {
labels: {
multiple: "Vehicles",
},
},
},
departments: {
meta: {
labels: {
single: "department",
},
},
},
collectedOrderInvoices: {
get: {
single: getSingleMock,
},
},
global: {
language: {
showing: "Showing",
showing_of_separator: "of",
unselect: "Unselect",
select: "Select",
all: "All",
invoice: "Invoice",
collection: "Collection",
yes: "Yes",
no: "No",
handheld_pending_order: "Pending handheld",
confirmation_needed: "Confirmation needed",
},
system_user_ids: [],
},
},
functions: {
ucFirst: (value) => {
const normalized = String(value || "");
return normalized.charAt(0).toUpperCase() + normalized.slice(1);
},
currency: {
toLocal: (value) => String(value ?? ""),
},
text: {
truncate: (text, length = 25) => (text.length > length ? `${text.substring(0, length)}...` : text),
},
date: {
timeAgo: () => "just now",
},
},
},
}));
import OrdersTable from "@/components/displays/department/pos/orders/ordersTable.vue";
const ViewportResponsiveWrapperStub = {
template: `
<div class="viewport-wrapper-stub">
<slot name="desktop"></slot>
</div>
`,
};
const EditableTableColumnStub = defineComponent({
name: "EditableTableColumn",
props: {
object: {
type: Object,
required: true,
},
column: {
type: String,
default: null,
},
parseFunction: {
type: Function,
default: null,
},
virtualColumn: {
type: Boolean,
default: false,
},
componentWrapper: {
type: String,
default: null,
},
},
setup(props) {
return () => {
const tagName = props.componentWrapper || "td";
const rawValue = props.virtualColumn ? props.object : props.object?.[props.column];
const content = props.parseFunction ? props.parseFunction(rawValue) : rawValue;
return h(tagName, { class: "editable-table-column-stub" }, String(content ?? ""));
};
},
});
const ColorIndicatorStub = defineComponent({
name: "ColorIndicator",
props: {
label: {
type: Object,
default: () => ({}),
},
},
setup(props) {
return () => h("span", { class: "color-indicator-stub" }, String(props.label?.text ?? ""));
},
});
const ActionSettingsWheelButtonStub = {
template: '<div class="action-settings-wheel-button-stub"><slot name="actions"></slot></div>',
};
const ActionSettingsWheelItemStub = {
template: '<div class="action-settings-wheel-item-stub"></div>',
};
const OrderContentTableStub = {
template: '<div class="order-content-table-stub"></div>',
};
const PosDepartmentStepMobileAttachmentStub = {
template: '<div class="pos-mobile-attachment-stub"></div>',
};
const OrderAttachmentsActionButtonStub = {
template: '<div class="order-attachments-action-button-stub"></div>',
};
const AssignDraftOrderCustomerModalStub = {
template: '<div class="assign-draft-order-customer-modal-stub"></div>',
};
const WhiteBoxCardStub = {
template:
'<div class="white-box-card-stub"><slot name="header"></slot><slot></slot><slot name="footer"></slot></div>',
};
const InvoiceMultipleCollectionsModalStub = {
template: '<div class="invoice-multiple-collections-modal-stub"></div>',
};
const InvoicingBillingPeriodInvoiceProgressBarStub = {
template: '<div class="invoice-progress-bar-stub"></div>',
};
const createOrder = (overrides = {}) => ({
id: 1,
customer_id: 12345,
department_id: 12,
reference: "",
notes: "",
po: "",
reg_1: "AB12345",
reg_2: "",
reg_3: "",
created_at: "2026-04-13 11:00:00",
total_net_amount: 100,
invoice_collection_id: null,
invoice_collection: null,
attachments: [],
economic_invoice_module: null,
stripe_invoice_module: null,
error_message: null,
pending_handheld: false,
completed_at: null,
customer_name: "Demo Customer",
cashier_id: 7,
cashier_name: "Demo Cashier",
...overrides,
});
const mountOrdersTable = (props = {}) => {
return mount(OrdersTable, {
props: {
orders: [],
isCustomerView: true,
invoiceView: false,
allowSelectMultiple: false,
groupInvoiceCollection: false,
showOnlyWithIds: [],
autoExpandAll: false,
excludedOrderIds: [],
...props,
},
global: {
stubs: {
ViewportResponsiveWrapper: ViewportResponsiveWrapperStub,
EditableTableColumn: EditableTableColumnStub,
ColorIndicator: ColorIndicatorStub,
ActionSettingsWheelButton: ActionSettingsWheelButtonStub,
ActionSettingsWheelItem: ActionSettingsWheelItemStub,
OrderContentTable: OrderContentTableStub,
PosDepartmentStepMobileAttachment: PosDepartmentStepMobileAttachmentStub,
OrderAttachmentsActionButton: OrderAttachmentsActionButtonStub,
AssignDraftOrderCustomerModal: AssignDraftOrderCustomerModalStub,
WhiteBoxCard: WhiteBoxCardStub,
InvoiceMultipleCollectionsModal: InvoiceMultipleCollectionsModalStub,
InvoicingBillingPeriodInvoiceProgressBar: InvoicingBillingPeriodInvoiceProgressBarStub,
},
},
});
};
describe("OrdersTable", () => {
beforeEach(() => {
sessionState.canAccessSuperUser = false;
getSingleMock.mockReset();
});
afterEach(() => {
vi.restoreAllMocks();
});
it("renders customer orders with sparse text fields without crashing or preloading invoice collections", async () => {
const consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
const wrapper = mountOrdersTable({
orders: [
createOrder({
id: 10,
reference: undefined,
notes: null,
po: undefined,
invoice_collection_id: undefined,
}),
],
});
await flushPromises();
expect(wrapper.text()).toContain("10");
expect(getSingleMock).not.toHaveBeenCalled();
expect(consoleErrorSpy.mock.calls.map((call) => call.join(" ")).join("\n")).not.toContain(
"Cannot read properties of undefined"
);
});
it("preloads only valid invoice collection ids and keeps rendering when a lookup fails", async () => {
sessionState.canAccessSuperUser = true;
const consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
getSingleMock.mockImplementation(async (invoiceCollectionId) => {
if (invoiceCollectionId === 14) {
return {
id: 14,
orders: [{ id: 1001 }, { id: 1002 }, { id: 3000 }],
};
}
throw new Error("Forbidden");
});
const wrapper = mountOrdersTable({
groupInvoiceCollection: true,
orders: [
createOrder({
id: 20,
invoice_collection_id: 14,
invoice_collection: { id: 14, closed_at: null, booked_invoice_id: null },
}),
createOrder({
id: 21,
invoice_collection_id: "14",
invoice_collection: { id: 14, closed_at: null, booked_invoice_id: null },
}),
createOrder({
id: 22,
invoice_collection_id: undefined,
}),
createOrder({
id: 23,
invoice_collection_id: "",
}),
createOrder({
id: 24,
invoice_collection_id: -1,
}),
createOrder({
id: 25,
invoice_collection_id: 21,
invoice_collection: { id: 21, closed_at: null, booked_invoice_id: null },
}),
],
excludedOrderIds: [3000],
});
await flushPromises();
await flushPromises();
const requestedIds = getSingleMock.mock.calls
.map(([invoiceCollectionId]) => invoiceCollectionId)
.sort((left, right) => left - right);
expect(requestedIds).toEqual([14, 21]);
expect(wrapper.text()).toContain("Faktura samling ID: 14");
expect(wrapper.text()).toContain("Faktura samling ID: 21");
expect(consoleErrorSpy.mock.calls.map((call) => call.join(" ")).join("\n")).not.toContain(
"Unhandled error during execution of component update"
);
});
it("renders the draft assignment action when explicitly enabled", async () => {
const wrapper = mountOrdersTable({
showDraftAssignmentActions: true,
orders: [
createOrder({
id: 56625,
}),
],
});
await flushPromises();
expect(wrapper.find('[data-testid="draft-order-assign-customer-button-56625"]').exists()).toBe(true);
expect(wrapper.text()).toContain("admin.pos.drafts_assignment.button");
});
});