Fixes product detail requests so null optional query params are omitted while final_price=false is preserved.
334 lines
8.5 KiB
JavaScript
334 lines
8.5 KiB
JavaScript
// @vitest-environment jsdom
|
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
vi.mock("sweetalert2", () => ({
|
|
default: {
|
|
fire: vi.fn(),
|
|
showValidationMessage: vi.fn(),
|
|
},
|
|
}));
|
|
|
|
vi.mock("@/i18n", () => ({
|
|
default: {
|
|
global: {
|
|
t: (key) => key,
|
|
},
|
|
},
|
|
}));
|
|
|
|
vi.mock("@/components/session/authenticatedRequest.vue", () => ({
|
|
authenticatedRequest: vi.fn(),
|
|
unauthenticatedRequest: vi.fn(),
|
|
}));
|
|
|
|
vi.mock("@/components/session/token/SessionUser/Objects/systemUserIds.js", () => ({
|
|
getSystemUserIds: () => [],
|
|
}));
|
|
|
|
vi.mock("@/components/session/token/SessionUser.vue", () => ({
|
|
SessionUser: {
|
|
objects: {
|
|
global: {
|
|
language: {
|
|
no_data: "No data",
|
|
},
|
|
},
|
|
products: {
|
|
get: {
|
|
all: vi.fn(),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}));
|
|
|
|
import Swal from "sweetalert2";
|
|
import { authenticatedRequest } from "@/components/session/authenticatedRequest.vue";
|
|
import { ObjectsGlobal } from "@/components/session/token/SessionUser/Objects/ObjectsGlobal.vue";
|
|
import { Products } from "@/components/session/token/SessionUser/Objects/Products.vue";
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
ObjectsGlobal.clearCache();
|
|
document.body.innerHTML = "";
|
|
});
|
|
|
|
describe("ObjectsGlobal query parameters", () => {
|
|
it("omits nullish object GET parameters while preserving false and zero", async () => {
|
|
authenticatedRequest.mockResolvedValueOnce({
|
|
data: {
|
|
data: {
|
|
id: 40,
|
|
},
|
|
},
|
|
});
|
|
|
|
await expect(
|
|
ObjectsGlobal.get.object("/products", {
|
|
id: 40,
|
|
department_id: null,
|
|
customer_id: undefined,
|
|
category_id: null,
|
|
final_price: false,
|
|
page: 0,
|
|
})
|
|
).resolves.toEqual({
|
|
id: 40,
|
|
});
|
|
|
|
expect(authenticatedRequest).toHaveBeenCalledWith("/products?id=40&final_price=false&page=0", "GET");
|
|
});
|
|
|
|
it("omits nullish object list parameters while preserving false", async () => {
|
|
authenticatedRequest.mockResolvedValueOnce({
|
|
data: {
|
|
data: [],
|
|
},
|
|
});
|
|
|
|
await expect(
|
|
ObjectsGlobal.get.objects("/products", {
|
|
department_id: null,
|
|
customer_id: undefined,
|
|
final_price: false,
|
|
})
|
|
).resolves.toEqual([]);
|
|
|
|
expect(authenticatedRequest).toHaveBeenCalledWith("/products", "GET", {
|
|
final_price: false,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("Products query parameters", () => {
|
|
it("omits nullish default pricing parameters for single product GETs", async () => {
|
|
authenticatedRequest.mockResolvedValueOnce({
|
|
data: {
|
|
data: {
|
|
id: 40,
|
|
},
|
|
},
|
|
});
|
|
|
|
await expect(Products.get.single(40)).resolves.toEqual({
|
|
id: 40,
|
|
});
|
|
|
|
expect(authenticatedRequest).toHaveBeenCalledWith("/products?id=40&final_price=false", "GET");
|
|
});
|
|
|
|
it("preserves explicit product pricing parameters", async () => {
|
|
authenticatedRequest.mockResolvedValueOnce({
|
|
data: {
|
|
data: {
|
|
id: 40,
|
|
},
|
|
},
|
|
});
|
|
|
|
await expect(
|
|
Products.get.single(40, {
|
|
department_id: 2,
|
|
customer_id: 12345,
|
|
category_id: 8,
|
|
final_price: true,
|
|
})
|
|
).resolves.toEqual({
|
|
id: 40,
|
|
});
|
|
|
|
expect(authenticatedRequest).toHaveBeenCalledWith(
|
|
"/products?id=40&department_id=2&customer_id=12345&category_id=8&final_price=true",
|
|
"GET"
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("ObjectsGlobal select editor escaping", () => {
|
|
it("escapes option ids and names before rendering SweetAlert HTML", async () => {
|
|
const object = {
|
|
columns: {
|
|
relay_in_id: {
|
|
label: "Relay",
|
|
type: "select",
|
|
options: vi.fn().mockResolvedValue([
|
|
{
|
|
id: 'relay-1" autofocus onfocus="alert(1)',
|
|
name: '</option></select><iframe src="javascript:parent.localStorage.token"></iframe>',
|
|
},
|
|
]),
|
|
},
|
|
},
|
|
};
|
|
|
|
const html = await ObjectsGlobal.generateEditObjectFieldForm(object, "relay_in_id", null);
|
|
|
|
expect(html).not.toContain("</option></select><iframe");
|
|
expect(html).not.toContain("<iframe");
|
|
expect(html).not.toContain('value="relay-1" autofocus');
|
|
expect(html).toContain("relay-1" autofocus onfocus="alert(1)");
|
|
expect(html).toContain(
|
|
"</option></select><iframe src="javascript:parent.localStorage.token"></iframe>"
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("ObjectsGlobal create form locked values", () => {
|
|
it("renders boolean create fields with an explicit false default selected", async () => {
|
|
const html = await ObjectsGlobal.generateCreateObjectForm(
|
|
{
|
|
wash_subscription: {
|
|
label: "Wash subscription",
|
|
type: "boolean",
|
|
creation: {
|
|
required: true,
|
|
default: false,
|
|
},
|
|
},
|
|
},
|
|
{}
|
|
);
|
|
|
|
document.body.innerHTML = html;
|
|
|
|
expect(document.querySelector("#wash_subscription")?.value).toBe("false");
|
|
});
|
|
|
|
it("submits required locked values without reading them back from the DOM", async () => {
|
|
const add = vi.fn().mockResolvedValue({});
|
|
const object = {
|
|
meta: {
|
|
title: "Vehicles",
|
|
labels: {
|
|
single: "Vehicle",
|
|
},
|
|
},
|
|
columns: {
|
|
type: {
|
|
label: "Type",
|
|
type: "number",
|
|
creation: {
|
|
required: true,
|
|
},
|
|
},
|
|
reg: {
|
|
label: "Registration",
|
|
type: "string",
|
|
creation: {
|
|
required: true,
|
|
},
|
|
},
|
|
customer_id: {
|
|
label: "Customer",
|
|
type: "number",
|
|
creation: {
|
|
required: true,
|
|
},
|
|
},
|
|
},
|
|
add,
|
|
};
|
|
|
|
Swal.fire.mockImplementationOnce(async (options) => {
|
|
document.body.innerHTML = `
|
|
<input id="type" value="5">
|
|
<input id="reg" value="AB12345">
|
|
`;
|
|
|
|
await options.preConfirm();
|
|
return {};
|
|
});
|
|
|
|
await ObjectsGlobal.showCreateObjectForm(object, null, { customer_id: 12345679 });
|
|
|
|
expect(add).toHaveBeenCalledWith("5", "AB12345", 12345679);
|
|
expect(Swal.showValidationMessage).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("waits for the create callback before resolving preConfirm", async () => {
|
|
const add = vi.fn().mockResolvedValue({});
|
|
const onAfterSubmit = vi.fn().mockResolvedValue("reloaded");
|
|
const object = {
|
|
meta: {
|
|
title: "Vehicles",
|
|
labels: {
|
|
single: "Vehicle",
|
|
},
|
|
},
|
|
columns: {
|
|
reg: {
|
|
label: "Registration",
|
|
type: "string",
|
|
creation: {
|
|
required: true,
|
|
},
|
|
},
|
|
},
|
|
add,
|
|
};
|
|
|
|
Swal.fire.mockImplementationOnce(async (options) => {
|
|
document.body.innerHTML = '<input id="reg" value="AB12345">';
|
|
return options.preConfirm();
|
|
});
|
|
|
|
await expect(ObjectsGlobal.showCreateObjectForm(object, onAfterSubmit)).resolves.toBe("reloaded");
|
|
expect(onAfterSubmit).toHaveBeenCalledOnce();
|
|
});
|
|
});
|
|
|
|
describe("ObjectsGlobal edit and delete callbacks", () => {
|
|
it("waits for the edit callback before resolving preConfirm", async () => {
|
|
const setReg = vi.fn().mockResolvedValue({});
|
|
const onAfterSubmit = vi.fn().mockResolvedValue("reloaded");
|
|
const object = {
|
|
meta: {
|
|
title: "Vehicles",
|
|
labels: {
|
|
single: "Vehicle",
|
|
},
|
|
},
|
|
columns: {
|
|
reg: {
|
|
label: "Registration",
|
|
type: "string",
|
|
},
|
|
},
|
|
set: {
|
|
reg: setReg,
|
|
},
|
|
};
|
|
|
|
Swal.fire.mockImplementationOnce(async (options) => {
|
|
document.body.innerHTML = '<input id="reg" value="CD67890">';
|
|
return options.preConfirm();
|
|
});
|
|
|
|
await expect(ObjectsGlobal.showEditObjectFieldForm(object, 10, "reg", "AB12345", onAfterSubmit)).resolves.toBe(
|
|
"reloaded"
|
|
);
|
|
expect(setReg).toHaveBeenCalledWith(10, "CD67890");
|
|
expect(onAfterSubmit).toHaveBeenCalledOnce();
|
|
});
|
|
|
|
it("waits for the delete callback before resolving preConfirm", async () => {
|
|
const deleteObject = vi.fn().mockResolvedValue({});
|
|
const onAfterSubmit = vi.fn().mockResolvedValue("reloaded");
|
|
const object = {
|
|
meta: {
|
|
title: "Vehicles",
|
|
labels: {
|
|
single: "Vehicle",
|
|
},
|
|
},
|
|
delete: deleteObject,
|
|
};
|
|
|
|
Swal.fire.mockImplementationOnce(async (options) => options.preConfirm());
|
|
|
|
await expect(ObjectsGlobal.showDeleteObjectForm(object, 10, onAfterSubmit)).resolves.toBe("reloaded");
|
|
expect(deleteObject).toHaveBeenCalledWith(10);
|
|
expect(onAfterSubmit).toHaveBeenCalledOnce();
|
|
});
|
|
});
|