-
Notifications
You must be signed in to change notification settings - Fork 4
fix: adjust payload to customized managed sponsor forms #958
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
7ac284b
ff59678
e7daa56
d212b89
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,125 @@ | ||
| import sponsorCustomizedFormItemsListReducer from "../sponsor-customized-form-items-list-reducer"; | ||
| import { | ||
| RECEIVE_SPONSOR_CUSTOMIZED_FORM_ITEM, | ||
| SPONSOR_FORM_MANAGED_ITEM_UPDATED | ||
| } from "../../../actions/sponsor-forms-actions"; | ||
|
|
||
| const DEFAULT_STATE = { | ||
| items: [], | ||
| showArchived: false, | ||
| term: "", | ||
| order: "name", | ||
| orderDir: 1, | ||
| currentPage: 1, | ||
| lastPage: 1, | ||
| perPage: 10, | ||
| totalCount: 0, | ||
| currentItem: { | ||
| code: "", | ||
| name: "", | ||
| description: "", | ||
| early_bird_rate: 0, | ||
| standard_rate: 0, | ||
| onsite_rate: 0, | ||
| quantity_limit_per_show: 0, | ||
| quantity_limit_per_sponsor: 0, | ||
| default_quantity: 0, | ||
| images: [], | ||
| meta_fields: [] | ||
| } | ||
| }; | ||
|
|
||
| const buildItem = (overrides = {}) => ({ | ||
| id: 1, | ||
| code: "ITEM1", | ||
| name: "Item One", | ||
| description: "desc", | ||
| early_bird_rate: 100, | ||
| standard_rate: 200, | ||
| onsite_rate: 300, | ||
| default_quantity: 5, | ||
| is_archived: false, | ||
| images: [], | ||
| meta_fields: [], | ||
| ...overrides | ||
| }); | ||
|
|
||
| describe("sponsorCustomizedFormItemsListReducer", () => { | ||
| describe("RECEIVE_SPONSOR_CUSTOMIZED_FORM_ITEM", () => { | ||
| it("maps file_url to file_path on each image — the edit-form image fix", () => { | ||
| const result = sponsorCustomizedFormItemsListReducer(DEFAULT_STATE, { | ||
| type: RECEIVE_SPONSOR_CUSTOMIZED_FORM_ITEM, | ||
| payload: { | ||
| response: buildItem({ | ||
| images: [ | ||
| { id: 10, file_url: "https://cdn/a.png" }, | ||
| { id: 11, file_url: "https://cdn/b.png" } | ||
| ] | ||
| }) | ||
| } | ||
| }); | ||
|
|
||
| expect(result.currentItem.images).toEqual([ | ||
| { | ||
| id: 10, | ||
| file_url: "https://cdn/a.png", | ||
| file_path: "https://cdn/a.png" | ||
| }, | ||
| { | ||
| id: 11, | ||
| file_url: "https://cdn/b.png", | ||
| file_path: "https://cdn/b.png" | ||
| } | ||
| ]); | ||
| }); | ||
|
|
||
| it("handles absent images without throwing", () => { | ||
| const result = sponsorCustomizedFormItemsListReducer(DEFAULT_STATE, { | ||
| type: RECEIVE_SPONSOR_CUSTOMIZED_FORM_ITEM, | ||
| payload: { response: buildItem({ images: undefined }) } | ||
| }); | ||
|
|
||
| expect(result.currentItem.images).toEqual([]); | ||
| }); | ||
|
|
||
| it("handles absent meta_fields without throwing — guards the .length access", () => { | ||
| const result = sponsorCustomizedFormItemsListReducer(DEFAULT_STATE, { | ||
| type: RECEIVE_SPONSOR_CUSTOMIZED_FORM_ITEM, | ||
| payload: { response: buildItem({ meta_fields: undefined }) } | ||
| }); | ||
|
|
||
| expect(result.currentItem.meta_fields).toEqual([]); | ||
| }); | ||
| }); | ||
|
|
||
| describe("SPONSOR_FORM_MANAGED_ITEM_UPDATED", () => { | ||
| it("replaces the matching list item and preserves its images as-is", () => { | ||
| const images = [{ id: 20, file_url: "https://cdn/img.png" }]; | ||
| const state = { | ||
| ...DEFAULT_STATE, | ||
| items: [ | ||
| buildItem({ id: 1, name: "Before", images }), | ||
| buildItem({ id: 2, name: "Other" }) | ||
| ] | ||
| }; | ||
|
|
||
| const result = sponsorCustomizedFormItemsListReducer(state, { | ||
| type: SPONSOR_FORM_MANAGED_ITEM_UPDATED, | ||
| payload: { | ||
| response: buildItem({ | ||
| id: 1, | ||
| name: "After", | ||
| early_bird_rate: 500, | ||
| standard_rate: 600, | ||
| onsite_rate: 700, | ||
| images | ||
| }) | ||
| } | ||
| }); | ||
|
|
||
| expect(result.items[0].name).toBe("After"); | ||
| expect(result.items[0].images).toBe(images); | ||
| expect(result.items[1].name).toBe("Other"); | ||
| }); | ||
| }); | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,105 @@ | ||
| import sponsorCustomizedFormReducer from "../sponsor-customized-form-reducer"; | ||
| import { | ||
| RECEIVE_SPONSOR_CUSTOMIZED_FORM, | ||
| RESET_SPONSOR_CUSTOMIZED_FORM | ||
| } from "../../../actions/sponsor-forms-actions"; | ||
|
|
||
| const DEFAULT_ENTITY = { | ||
| id: 0, | ||
| code: "", | ||
| name: "", | ||
| allowed_add_ons: [], | ||
| opens_at: "", | ||
| expires_at: "", | ||
| instructions: "", | ||
| meta_fields: [], | ||
| items: [] | ||
| }; | ||
|
|
||
| const DEFAULT_STATE = { entity: DEFAULT_ENTITY }; | ||
|
|
||
| describe("sponsorCustomizedFormReducer", () => { | ||
| describe("RECEIVE_SPONSOR_CUSTOMIZED_FORM", () => { | ||
| it("maps file_url to file_path for images on each item", () => { | ||
| const result = sponsorCustomizedFormReducer(DEFAULT_STATE, { | ||
| type: RECEIVE_SPONSOR_CUSTOMIZED_FORM, | ||
| payload: { | ||
| response: { | ||
| id: 1, | ||
| code: "FORM1", | ||
| items: [ | ||
| { | ||
| id: 10, | ||
| name: "Item A", | ||
| images: [ | ||
| { id: 100, file_url: "https://example.com/img1.png" }, | ||
| { id: 101, file_url: "https://example.com/img2.png" } | ||
| ] | ||
| } | ||
| ] | ||
| } | ||
| } | ||
| }); | ||
|
|
||
| expect(result.entity.items[0].images).toEqual([ | ||
| { | ||
| id: 100, | ||
| file_url: "https://example.com/img1.png", | ||
| file_path: "https://example.com/img1.png" | ||
| }, | ||
| { | ||
| id: 101, | ||
| file_url: "https://example.com/img2.png", | ||
| file_path: "https://example.com/img2.png" | ||
| } | ||
| ]); | ||
| }); | ||
|
|
||
| it("handles items with no images", () => { | ||
| const result = sponsorCustomizedFormReducer(DEFAULT_STATE, { | ||
| type: RECEIVE_SPONSOR_CUSTOMIZED_FORM, | ||
| payload: { | ||
| response: { | ||
| id: 1, | ||
| code: "FORM1", | ||
| items: [{ id: 10, name: "Item A", images: [] }] | ||
| } | ||
| } | ||
| }); | ||
|
|
||
| expect(result.entity.items[0].images).toEqual([]); | ||
| }); | ||
|
|
||
| it("handles empty items array", () => { | ||
| const result = sponsorCustomizedFormReducer(DEFAULT_STATE, { | ||
| type: RECEIVE_SPONSOR_CUSTOMIZED_FORM, | ||
| payload: { | ||
| response: { id: 1, code: "FORM1", items: [] } | ||
| } | ||
| }); | ||
|
|
||
| expect(result.entity.items).toEqual([]); | ||
| }); | ||
|
|
||
| it("handles missing items field", () => { | ||
| const result = sponsorCustomizedFormReducer(DEFAULT_STATE, { | ||
| type: RECEIVE_SPONSOR_CUSTOMIZED_FORM, | ||
| payload: { | ||
| response: { id: 1, code: "FORM1" } | ||
| } | ||
| }); | ||
|
|
||
| expect(result.entity.items).toEqual([]); | ||
| }); | ||
| }); | ||
|
|
||
| describe("RESET_SPONSOR_CUSTOMIZED_FORM", () => { | ||
| it("resets to default state", () => { | ||
| const dirty = { entity: { id: 99, items: [{ id: 1 }] } }; | ||
| const result = sponsorCustomizedFormReducer(dirty, { | ||
| type: RESET_SPONSOR_CUSTOMIZED_FORM | ||
| }); | ||
| expect(result).toEqual(DEFAULT_STATE); | ||
| }); | ||
| }); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -110,7 +110,11 @@ const sponsorCustomizedFormItemsListReducer = ( | |
|
|
||
| const currentItem = { | ||
| ...item, | ||
| meta_fields: item.meta_fields.length > 0 ? item.meta_fields : [] | ||
| images: (item.images || []).map((img) => ({ | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This block (the The |
||
| ...img, | ||
| file_path: img.file_url | ||
| })), | ||
| meta_fields: (item.meta_fields ?? []).length > 0 ? item.meta_fields : [] | ||
| }; | ||
| return { ...state, currentItem }; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No test file was added for this reducer, while the form reducer received full coverage in this PR. The three cases most worth adding:
|
||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -26,7 +26,8 @@ const DEFAULT_ENTITY = { | |
| opens_at: "", | ||
| expires_at: "", | ||
| instructions: "", | ||
| meta_fields: [] | ||
| meta_fields: [], | ||
| items: [] | ||
| }; | ||
|
|
||
| const DEFAULT_STATE = { | ||
|
|
@@ -43,10 +44,22 @@ const sponsorCustomizedFormReducer = (state = DEFAULT_STATE, action) => { | |
| return DEFAULT_STATE; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @tomrndom |
||
| } | ||
| case RECEIVE_SPONSOR_CUSTOMIZED_FORM: { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Two different action creators dispatch
This is safe today, but worth documenting — a future developer adding item-level display to the customized-form popup may not notice that items are empty in that path. Consider a comment here or splitting the action types to make the contract explicit. |
||
| return { | ||
| ...state, | ||
| entity: payload.response | ||
| // this actions is dispatched by getSponsorManagedForm and getSponsorCustomizedForm | ||
| // getSponsorManagedForm expands items.images, getSponsorCustomizedForm not, | ||
| // so items is absent here for the customized path. | ||
| // Add expand=items,items.images to that action if item display is ever needed in | ||
| // the customized-form popup. | ||
| const entity = { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @tomrndom |
||
| ...payload.response, | ||
| items: (payload.response.items || []).map((it) => ({ | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @tomrndom |
||
| ...it, | ||
| images: (it.images || []).map((img) => ({ | ||
| ...img, | ||
| file_path: img.file_url | ||
| })) | ||
| })) | ||
| }; | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| return { ...state, entity }; | ||
| } | ||
| default: | ||
| return state; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a test for items where
imagesis missing (not just empty).You already validate
images: [], but the reducer also handlesimagesbeing absent. A direct test for that case will lock the intended fallback contract.Suggested test case
it("handles items with no images", () => { @@ expect(result.entity.items[0].images).toEqual([]); }); + it("handles items with missing images field", () => { + const result = sponsorCustomizedFormReducer(DEFAULT_STATE, { + type: RECEIVE_SPONSOR_CUSTOMIZED_FORM, + payload: { + response: { + id: 1, + code: "FORM1", + items: [{ id: 10, name: "Item A" }] + } + } + }); + + expect(result.entity.items[0].images).toEqual([]); + });📝 Committable suggestion
🤖 Prompt for AI Agents