diff --git a/src/components/mui/RefundForm/__tests__/refund-form.test.js b/src/components/mui/RefundForm/__tests__/refund-form.test.js index 33cefb4d8..2d8f76c6a 100644 --- a/src/components/mui/RefundForm/__tests__/refund-form.test.js +++ b/src/components/mui/RefundForm/__tests__/refund-form.test.js @@ -18,6 +18,14 @@ describe("RefundForm", () => { ).toBeInTheDocument(); }); + it("shows the refund info note and keeps submit enabled on first render", () => { + render(); + expect(screen.getByText("refund_form.info")).toBeInTheDocument(); + expect( + screen.getByRole("button", { name: /refund_form\.queue_refund/i }) + ).toBeEnabled(); + }); + it("calls onSubmit with reason and amount in cents", async () => { const onSubmit = jest.fn(); render(); diff --git a/src/components/mui/RefundForm/index.jsx b/src/components/mui/RefundForm/index.jsx index 90d53b6fd..51563aebe 100644 --- a/src/components/mui/RefundForm/index.jsx +++ b/src/components/mui/RefundForm/index.jsx @@ -19,9 +19,8 @@ import { Box, Button, Grid2 } from "@mui/material"; import MuiFormikTextField from "openstack-uicore-foundation/lib/components/mui/formik-inputs/textfield"; import MuiFormikPriceField from "openstack-uicore-foundation/lib/components/mui/formik-inputs/price-field"; import InfoNote from "openstack-uicore-foundation/lib/components/mui/info-note"; -import CustomAlert from "openstack-uicore-foundation/lib/components/mui/custom-alert"; -const RefundForm = ({ onSubmit, disabled = false }) => { +const RefundForm = ({ onSubmit }) => { // Formik does not block concurrent submits, so repeated Enter presses would // each queue a refund. The ref guards the gap before isSubmitting re-renders. const submittingRef = useRef(false); @@ -60,7 +59,7 @@ const RefundForm = ({ onSubmit, disabled = false }) => { enableReinitialize: true }); - const isDisabled = disabled || formik.isSubmitting; + const isDisabled = formik.isSubmitting; return ( @@ -105,14 +104,7 @@ const RefundForm = ({ onSubmit, disabled = false }) => { - {disabled ? ( - - ) : ( - - )} + ); diff --git a/src/i18n/en.json b/src/i18n/en.json index 69d6b4d2a..c1e705004 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -4370,8 +4370,7 @@ "reason": "Reason for Refund", "amount": "Amount", "queue_refund": "Queue refund", - "info": "If the original payment was made via Stripe, the refund will be automatically queued and processed.", - "only_online_payments": "Only online payments are eligible for refunds" + "info": "Stripe payments are queued and refunded through Stripe automatically. Offline payments are recorded as refunded immediately; return the funds to the sponsor outside the platform." }, "client_card": { "title": "Client & Address Details", diff --git a/src/pages/sponsors/sponsor-page/tabs/sponsor-purchases-tab/__tests__/sponsor-order-details.test.js b/src/pages/sponsors/sponsor-page/tabs/sponsor-purchases-tab/__tests__/sponsor-order-details.test.js new file mode 100644 index 000000000..4fb5519e4 --- /dev/null +++ b/src/pages/sponsors/sponsor-page/tabs/sponsor-purchases-tab/__tests__/sponsor-order-details.test.js @@ -0,0 +1,112 @@ +import React from "react"; +import { act, screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import "@testing-library/jest-dom"; +import { renderWithRedux } from "../../../../../../utils/test-utils"; +import SponsorOrderDetails from "../sponsor-order-details"; +import { refundSponsorOrder } from "../../../../../../actions/sponsor-purchases-actions"; + +jest.mock("i18n-react/dist/i18n-react", () => ({ + __esModule: true, + default: { translate: (key) => key } +})); + +jest.mock("../../../../../../routes/restrict", () => ({ + __esModule: true, + default: (Component) => Component +})); + +jest.mock("../../../../../../actions/sponsor-purchases-actions", () => ({ + __esModule: true, + getSponsorOrder: jest.fn(() => () => {}), + clearSponsorOrder: jest.fn(() => () => {}), + refundSponsorOrder: jest.fn(() => () => Promise.resolve()), + cancelSponsorForm: jest.fn(() => () => {}), + undoCancelSponsorForm: jest.fn(() => () => {}), + updateClientAddress: jest.fn(() => () => {}), + updateClientInfo: jest.fn(() => () => {}) +})); + +jest.mock("openstack-uicore-foundation/lib/components/mui/cards", () => ({ + __esModule: true, + ListCard: () => null +})); + +jest.mock( + "openstack-uicore-foundation/lib/components/mui/sponsor-order-grid", + () => ({ __esModule: true, default: () => null }) +); + +jest.mock("../../../../../../components/mui/ClientCard", () => ({ + __esModule: true, + default: () => null +})); + +const renderPage = (type) => { + const order = { + id: 135, + number: "ORD-135", + type, + payment_method: type, + status: "Paid", + created: 1700000000, + purchased_by_full_name: "Jane Doe", + purchased_by_email: "jane@example.com", + client: {}, + address: {} + }; + + return renderWithRedux( + , + { + initialState: { + sponsorPagePurchaseListState: { currentOrder: order }, + currentSummitState: { currentSummit: { id: 73, name: "Summit" } }, + currentSponsorState: { + entity: { id: 680, company: { name: "Acme" }, sponsorships: [] } + } + } + } + ); +}; + +describe("SponsorOrderDetails refund form", () => { + beforeEach(() => { + refundSponsorOrder.mockClear(); + }); + + it.each(["Offline", "Online"])( + "renders an enabled refund form for %s orders", + (type) => { + renderPage(type); + + expect( + screen.getByRole("button", { name: /refund_form\.queue_refund/i }) + ).toBeEnabled(); + expect( + screen.queryByText("refund_form.only_online_payments") + ).not.toBeInTheDocument(); + } + ); + + it("submits a refund for an offline order with the amount in cents", async () => { + renderPage("Offline"); + + await act(async () => { + await userEvent.type( + screen.getByLabelText(/refund_form\.reason/i), + "Duplicate invoice" + ); + await userEvent.type(screen.getByLabelText(/refund_form\.amount/i), "10"); + await userEvent.click( + screen.getByRole("button", { name: /refund_form\.queue_refund/i }) + ); + }); + + expect(refundSponsorOrder).toHaveBeenCalledWith( + 135, + 1000, + "Duplicate invoice" + ); + }); +}); diff --git a/src/pages/sponsors/sponsor-page/tabs/sponsor-purchases-tab/sponsor-order-details.js b/src/pages/sponsors/sponsor-page/tabs/sponsor-purchases-tab/sponsor-order-details.js index d5e3b4a74..5e691fc95 100644 --- a/src/pages/sponsors/sponsor-page/tabs/sponsor-purchases-tab/sponsor-order-details.js +++ b/src/pages/sponsors/sponsor-page/tabs/sponsor-purchases-tab/sponsor-order-details.js @@ -26,11 +26,7 @@ import { updateClientAddress, updateClientInfo } from "../../../../../actions/sponsor-purchases-actions"; -import { - ACCESS_ROUTES, - DATE_FORMAT, - PURCHASE_TYPES -} from "../../../../../utils/constants"; +import { ACCESS_ROUTES, DATE_FORMAT } from "../../../../../utils/constants"; import Restrict from "../../../../../routes/restrict"; import { formatDate } from "../../../../../utils/methods"; import RefundForm from "../../../../../components/mui/RefundForm"; @@ -189,10 +185,7 @@ const SponsorOrderDetails = ({ "edit_sponsor.purchase_tab.order_details.issue_refund" )} - +