diff --git a/src/actions/promocode-actions.js b/src/actions/promocode-actions.js index efd0f23a0..6cc3463c0 100644 --- a/src/actions/promocode-actions.js +++ b/src/actions/promocode-actions.js @@ -147,6 +147,12 @@ const normalizeEntity = (entity) => { normalizedEntity.tags = entity.tags.map((e) => e.tag); } + if (Array.isArray(entity.allowed_ticket_types)) { + normalizedEntity.allowed_ticket_types = entity.allowed_ticket_types.map( + (tt) => (typeof tt === "object" && tt !== null ? tt.id : tt) + ); + } + delete normalizedEntity.owner; delete normalizedEntity.owner_id; delete normalizedEntity.speaker; diff --git a/src/components/forms/promocode-form/__tests__/promocode-form.integration.test.js b/src/components/forms/promocode-form/__tests__/promocode-form.integration.test.js index 6e88578eb..98c331f70 100644 --- a/src/components/forms/promocode-form/__tests__/promocode-form.integration.test.js +++ b/src/components/forms/promocode-form/__tests__/promocode-form.integration.test.js @@ -600,3 +600,37 @@ describe("regression — non-DomainAuthorized classes are unaffected by the layo } ); }); + +describe("regression — handleSubmit must not dehydrate allowed_ticket_types", () => { + // Reproduces the "Ticket Types reverts to undefined" bug (Jam + // b1566c62-c802-4f43-b093-9c5c2f5a5fde, ClickUp 86b9v01bt). Pre-fix, + // handleSubmit mutated state.entity.allowed_ticket_types from [{id, name}] + // to [id]; on a 412, the form re-rendered with raw IDs, and the + // openstack-uicore TicketTypesInput's getOptionLabel = `${e.name}` produced + // the literal string "undefined" for each chip. The action layer + // (normalizeEntity in promocode-actions.js) is responsible for the + // hydrated → API-shape conversion; the form must hand off untouched + // option objects. + it("passes onSubmit an entity whose allowed_ticket_types retain {id,name} objects", () => { + const onSubmit = jest.fn(); + const ticketType = { id: 197, name: "Early Access Members" }; + renderForm( + baseEntity({ + class_name: "DOMAIN_AUTHORIZED_PROMO_CODE", + allowed_email_domains: ["@valid.com"], + allowed_ticket_types: [ticketType] + }), + { onSubmit } + ); + + fireEvent.click(screen.getByRole("button", { name: /save/i })); + + expect(onSubmit).toHaveBeenCalledTimes(1); + const submittedEntity = onSubmit.mock.calls[0][0]; + expect(submittedEntity.allowed_ticket_types).toEqual([ticketType]); + expect(submittedEntity.allowed_ticket_types[0]).toMatchObject({ + id: 197, + name: "Early Access Members" + }); + }); +}); diff --git a/src/components/forms/promocode-form/index.js b/src/components/forms/promocode-form/index.js index 5cfb908ea..ba292222a 100644 --- a/src/components/forms/promocode-form/index.js +++ b/src/components/forms/promocode-form/index.js @@ -141,12 +141,6 @@ class PromocodeForm extends React.Component { const { entity } = this.state; const typeScope = this.fragmentParser.getParam("type"); - if (entity.allowed_ticket_types.length > 0) { - entity.allowed_ticket_types = entity.allowed_ticket_types.map( - (tt) => tt.id - ); - } - if (this.validate()) { onSubmit(entity, typeScope === "sponsor"); }