Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "openstack-uicore-foundation",
"version": "5.0.51",
"version": "5.0.53-beta.2",
"description": "ui reactjs components for openstack marketing site",
"main": "lib/openstack-uicore-foundation.js",
"scripts": {
Expand Down
7 changes: 6 additions & 1 deletion src/components/mui/MuiBaseCustomTheme.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ export const MuiBaseCustomTheme = {
height: "24px"
}
}
}
},
MuiTooltip: {
Comment thread
santipalenque marked this conversation as resolved.
styleOverrides: {
tooltip: { fontSize: "1em", fontWeight: "400" }
}
},
}
};
277 changes: 260 additions & 17 deletions src/components/mui/SponsorOrderGrid/__tests__/SponsorOrderGrid.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,18 @@ jest.mock("../../../../utils/methods", () => ({
}));

import React from "react";
import { render, screen, fireEvent } from "@testing-library/react";
import { render, screen, fireEvent, waitFor, act } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import "@testing-library/jest-dom";
import T from "i18n-react/dist/i18n-react";
import SponsorOrderGrid from "../index";

const makeItem = (overrides = {}) => ({
line_id: 841,
quantity: 1,
canceled_quantity: 0,
amount: 10000,
canceled_by_id: null,
cancellations: [],
type: { id: 146, name: "Booth", code: "BOOTH" },
meta_fields: [],
...overrides
Expand Down Expand Up @@ -118,53 +121,205 @@ describe("SponsorOrderGrid", () => {
expect(screen.getByText("sponsor_order_grid.action")).toBeInTheDocument();
});

test("renders delete button for active item and calls onCancelForm on click", () => {
test("clicking the action icon opens the change-quantity modal instead of calling callbacks directly", () => {
const onCancelForm = jest.fn();
const onUndoCancelForm = jest.fn();
render(
<SponsorOrderGrid
{...defaultProps}
onCancelForm={onCancelForm}
onUndoCancelForm={onUndoCancelForm}
/>
);
const button = document.querySelector("tbody button");
fireEvent.click(button);
expect(
screen.getByText("sponsor_order_grid.change_quantity_modal.title")
).toBeInTheDocument();
expect(onCancelForm).not.toHaveBeenCalled();
expect(onUndoCancelForm).not.toHaveBeenCalled();
});

test("reset is disabled in the modal when nothing has been cancelled yet", () => {
render(
<SponsorOrderGrid
{...defaultProps}
onCancelForm={jest.fn()}
onUndoCancelForm={jest.fn()}
/>
);
const button = document.querySelector("tbody button");
fireEvent.click(button);
expect(onCancelForm).toHaveBeenCalledTimes(1);
expect(
screen.getByRole("button", {
name: "sponsor_order_grid.change_quantity_modal.reset"
})
).toBeDisabled();
});

test("renders undo button for cancelled item and calls onUndoCancelForm on click", () => {
const onUndoCancelForm = jest.fn();
const order = { forms: [makeForm({ items: [makeItem({ canceled_by_id: 99 })] })], total: 0 };
test("lowering the quantity and applying calls onCancelForm with the delta, item and reason, then closes the dialog", async () => {
const onCancelForm = jest.fn(() => Promise.resolve());
const order = {
forms: [makeForm({ items: [makeItem({ line_id: 841, quantity: 5, canceled_quantity: 0 })] })],
total: 0
};
render(
<SponsorOrderGrid
order={order}
onCancelForm={jest.fn()}
onUndoCancelForm={onUndoCancelForm}
onCancelForm={onCancelForm}
onUndoCancelForm={jest.fn(() => Promise.resolve())}
/>
);
const button = document.querySelector("tbody button");
fireEvent.click(button);
expect(onUndoCancelForm).toHaveBeenCalledTimes(1);

const quantityField = screen.getByLabelText(
"sponsor_order_grid.change_quantity_modal.quantity"
);
fireEvent.change(quantityField, { target: { value: "2" } });
const reasonField = screen.getByLabelText(
"sponsor_order_grid.change_quantity_modal.reason"
);
fireEvent.change(reasonField, { target: { value: "Damaged" } });

fireEvent.click(screen.getByRole("button", { name: "general.apply" }));

await waitFor(() =>
expect(onCancelForm).toHaveBeenCalledWith(
expect.objectContaining({ id: 841 }),
3,
"Damaged"
)
);
await waitFor(() =>
expect(
screen.queryByText("sponsor_order_grid.change_quantity_modal.title")
).not.toBeInTheDocument()
);
});

test("passes the order line id to onCancelForm", () => {
const onCancelForm = jest.fn();
test("dialog stays open and the typed reason is preserved when onCancelForm rejects", async () => {
const onCancelForm = jest.fn(() => Promise.reject(new Error("Cannot cancel 3 units; only 2 remain")));
const order = {
forms: [makeForm({ items: [makeItem({ line_id: 841, quantity: 5, canceled_quantity: 0 })] })],
total: 0
};
render(
<SponsorOrderGrid
{...defaultProps}
order={order}
onCancelForm={onCancelForm}
onUndoCancelForm={jest.fn(() => Promise.resolve())}
/>
);
const button = document.querySelector("tbody button");
fireEvent.click(button);

const quantityField = screen.getByLabelText(
"sponsor_order_grid.change_quantity_modal.quantity"
);
fireEvent.change(quantityField, { target: { value: "2" } });
const reasonField = screen.getByLabelText(
"sponsor_order_grid.change_quantity_modal.reason"
);
fireEvent.change(reasonField, { target: { value: "Damaged" } });

fireEvent.click(screen.getByRole("button", { name: "general.apply" }));

await waitFor(() => expect(onCancelForm).toHaveBeenCalled());
expect(
screen.getByText("sponsor_order_grid.change_quantity_modal.title")
).toBeInTheDocument();
expect(screen.getByLabelText("sponsor_order_grid.change_quantity_modal.reason")).toHaveValue(
"Damaged"
);
});

test("clicking reset then applying calls onUndoCancelForm with the item, then closes the dialog", async () => {
const onUndoCancelForm = jest.fn(() => Promise.resolve());
const order = {
forms: [
makeForm({
items: [makeItem({ line_id: 841, quantity: 5, canceled_quantity: 2 })]
})
],
total: 0
};
render(
<SponsorOrderGrid
order={order}
onCancelForm={jest.fn(() => Promise.resolve())}
onUndoCancelForm={onUndoCancelForm}
/>
);
const button = document.querySelector("tbody button");
fireEvent.click(button);

fireEvent.click(
screen.getByRole("button", {
name: "sponsor_order_grid.change_quantity_modal.reset"
})
);
fireEvent.click(screen.getByRole("button", { name: "general.apply" }));

await waitFor(() =>
expect(onUndoCancelForm).toHaveBeenCalledWith(
expect.objectContaining({ id: 841 })
)
);
await waitFor(() =>
expect(
screen.queryByText("sponsor_order_grid.change_quantity_modal.title")
).not.toBeInTheDocument()
);
});

test("clicking reset disables the reason field, typing a lower quantity re-enables it", () => {
const order = {
forms: [
makeForm({
items: [makeItem({ line_id: 841, quantity: 5, canceled_quantity: 2 })]
})
],
total: 0
};
render(
<SponsorOrderGrid
order={order}
onCancelForm={jest.fn()}
onUndoCancelForm={jest.fn()}
/>
);
const button = document.querySelector("tbody button");
fireEvent.click(button);
expect(onCancelForm).toHaveBeenCalledWith(expect.objectContaining({ id: 841 }));

const reasonField = screen.getByLabelText(
"sponsor_order_grid.change_quantity_modal.reason"
);
expect(reasonField).toBeEnabled();

fireEvent.click(
screen.getByRole("button", {
name: "sponsor_order_grid.change_quantity_modal.reset"
})
);
expect(reasonField).toBeDisabled();

const quantityField = screen.getByLabelText(
"sponsor_order_grid.change_quantity_modal.quantity"
);
fireEvent.change(quantityField, { target: { value: "1" } });
expect(reasonField).toBeEnabled();
});

test("passes the order line id to onUndoCancelForm", () => {
test("a fully cancelled line keeps the quantity field disabled, so it cannot be typed back to a non-zero value that would trigger a silent restore", async () => {
const user = userEvent.setup();
const onUndoCancelForm = jest.fn();
const order = {
forms: [makeForm({ items: [makeItem({ line_id: 841, canceled_by_id: 99 })] })],
forms: [
makeForm({
items: [makeItem({ line_id: 841, quantity: 5, canceled_quantity: 5 })]
})
],
total: 0
};
render(
Expand All @@ -176,7 +331,95 @@ describe("SponsorOrderGrid", () => {
);
const button = document.querySelector("tbody button");
fireEvent.click(button);
expect(onUndoCancelForm).toHaveBeenCalledWith(expect.objectContaining({ id: 841 }));

const quantityField = screen.getByLabelText(
"sponsor_order_grid.change_quantity_modal.quantity"
);
await user.type(quantityField, "4");

const applyButton = screen.getByRole("button", { name: "general.apply" });
await act(async () => {
fireEvent.click(applyButton);
// flush the formik async submit pipeline so a call would have landed by now
await Promise.resolve();
await Promise.resolve();
});

expect(onUndoCancelForm).not.toHaveBeenCalled();
});

test("does not strikethrough or show Cancelled type when only partially cancelled", () => {
const order = {
forms: [makeForm({ items: [makeItem({ quantity: 5, canceled_quantity: 2 })] })],
total: 0
};
render(<SponsorOrderGrid order={order} />);
expect(screen.queryByText("Cancelled")).not.toBeInTheDocument();
expect(screen.getByText(/Booth/).closest("p")).not.toHaveStyle({
textDecoration: "line-through"
});
});

test("strikes through and shows Cancelled type only when fully cancelled", () => {
const order = {
forms: [makeForm({ items: [makeItem({ quantity: 5, canceled_quantity: 5 })] })],
total: 0
};
render(<SponsorOrderGrid order={order} />);
expect(screen.getByText("Cancelled")).toBeInTheDocument();
expect(screen.getByText(/Booth/).closest("p")).toHaveStyle({
textDecoration: "line-through"
});
});

test("renders a list of all cancellations with date, author and reason", () => {
const order = {
forms: [
makeForm({
items: [
makeItem({
quantity: 5,
canceled_quantity: 3,
cancellations: [
{
id: 1,
quantity: 2,
amount: 200,
reason: "Too many",
canceled_by_id: 5,
canceled_by_email: "a@test.com",
canceled_by_full_name: "Alice Admin",
created: 1000
},
{
id: 2,
quantity: 1,
amount: 100,
reason: "",
canceled_by_id: 6,
canceled_by_email: "b@test.com",
canceled_by_full_name: "Bob Admin",
created: 2000
}
]
})
]
})
],
total: 0
};
const translateSpy = jest.spyOn(T, "translate");
render(<SponsorOrderGrid order={order} />);

expect(translateSpy).toHaveBeenCalledWith(
"sponsor_order_grid.cancelled_by",
expect.objectContaining({ x: 2, y: 5, user: "Alice Admin", date: "2026-01-01" })
);
expect(translateSpy).toHaveBeenCalledWith(
"sponsor_order_grid.cancelled_by",
expect.objectContaining({ x: 1, y: 5, user: "Bob Admin", date: "2026-01-01" })
);
expect(screen.getByText(/Too many/)).toBeInTheDocument();
});

test("gives rows from different forms with the same item type distinct ids", () => {
Expand Down
Loading
Loading