From cdd3d13fa7fffda15ac29aa63720cbcfcd0c16ac Mon Sep 17 00:00:00 2001 From: Nicolas Olmos Date: Fri, 16 Jan 2026 15:23:43 -0300 Subject: [PATCH 1/4] fix: pagination on sponsor forms when rows per page change --- .../__tests__/sponsor-forms-actions.test.js | 160 ++++++++++++------ src/actions/sponsor-forms-actions.js | 8 +- 2 files changed, 115 insertions(+), 53 deletions(-) diff --git a/src/actions/__tests__/sponsor-forms-actions.test.js b/src/actions/__tests__/sponsor-forms-actions.test.js index 646f2483e..357b85506 100644 --- a/src/actions/__tests__/sponsor-forms-actions.test.js +++ b/src/actions/__tests__/sponsor-forms-actions.test.js @@ -1,66 +1,124 @@ -import * as OpenStackUiCoreActions from "openstack-uicore-foundation/lib/utils/actions"; -import { deleteSponsorFormItem } from "../sponsor-forms-actions"; -import * as UtilsMethods from "../../utils/methods"; -import * as BaseActions from "../base-actions"; +/** + * @jest-environment jsdom + */ +import { expect, jest, describe, it } from "@jest/globals"; +import configureStore from "redux-mock-store"; +import thunk from "redux-thunk"; +import flushPromises from "flush-promises"; +import { getRequest } from "openstack-uicore-foundation/lib/utils/actions"; +import { getSponsorForms } from "../sponsor-forms-actions"; +import * as methods from "../../utils/methods"; -jest.mock("openstack-uicore-foundation/lib/utils/actions", () => { - const originalModule = jest.requireActual( - "openstack-uicore-foundation/lib/utils/actions" - ); - - return { +jest.mock("openstack-uicore-foundation/lib/utils/actions", () => ({ __esModule: true, - ...originalModule, - deleteRequest: jest.fn(() => () => () => Promise.resolve()) - }; -}); + ...jest.requireActual("openstack-uicore-foundation/lib/utils/actions"), + postRequest: jest.fn(), + getRequest: jest.fn() + })); + +describe("Sponsor Forms Actions", () => { + describe("GetSponsorForms", () => { + const middlewares = [thunk]; + const mockStore = configureStore(middlewares); + + beforeEach(() => { + jest.spyOn(methods, "getAccessTokenSafely").mockReturnValue("TOKEN"); + + getRequest.mockImplementation( + ( + requestActionCreator, + receiveActionCreator, + endpoint, // eslint-disable-line no-unused-vars + payload, // eslint-disable-line no-unused-vars + errorHandler = null, // eslint-disable-line no-unused-vars + requestActionPayload = {} + ) => + ( + params = {} // eslint-disable-line no-unused-vars + ) => + (dispatch) => { + if ( + requestActionCreator && + typeof requestActionCreator === "function" + ) + dispatch(requestActionCreator(requestActionPayload)); + + return new Promise((resolve) => { + if (typeof receiveActionCreator === "function") { + dispatch(receiveActionCreator({ response: {} })); + resolve({ response: {} }); + } + dispatch(receiveActionCreator); + resolve({ response: {} }); + }); + } + ); + }); -describe("SponsorFormActions", () => { - describe("DeleteSponsorFormItem", () => { afterEach(() => { // restore the spy created with spyOn jest.restoreAllMocks(); }); + describe("On perPage change", () => { + it("should request first page if perPage is greater than the total items count", async () => { + const store = mockStore({ + currentSummitState: { + currentSummit: {} + }, + sponsorFormsListState: { + totalCount: 13 + } + }); - it("execute", async () => { - const mockedDispatch = jest.fn(); - const mockedGetState = jest.fn(() => ({ - currentSummitState: { - currentSummit: "SSS" - }, - sponsorFormItemsListState: { - currentPage: 1, - perPage: 10, - order: "asc", - orderDir: 1, - hideArchived: false - } - })); + store.dispatch(getSponsorForms("", 2, 50, "id", 1, false, [])); + await flushPromises(); - const params = { - formId: "AAA", - itemId: "III" - }; + expect(getRequest).toHaveBeenCalled(); + expect(getRequest).toHaveBeenCalledWith( + expect.anything(), + expect.anything(), + expect.anything(), + expect.anything(), + { + hideArchived: false, + order: "id", + orderDir: 1, + page: 1, + perPage: 50, + term: "" + } + ); + }); - const spyOnGetAccessTokenSafely = jest - .spyOn(UtilsMethods, "getAccessTokenSafely") - .mockImplementation(() => "access _token"); - const spyOnSnackbarSuccessHandler = jest.spyOn( - BaseActions, - "snackbarSuccessHandler" - ); + it("should request user selected page if perPage is lower than the total items count", async () => { + const store = mockStore({ + currentSummitState: { + currentSummit: {} + }, + sponsorFormsListState: { + totalCount: 50 + } + }); - await deleteSponsorFormItem(params.formId, params.itemId)( - mockedDispatch, - mockedGetState - ); + store.dispatch(getSponsorForms("", 2, 20, "id", 1, false, [])); + await flushPromises(); - // gets acces token safely - expect(spyOnGetAccessTokenSafely).toHaveBeenCalled(); - // calls delete request - expect(OpenStackUiCoreActions.deleteRequest).toHaveBeenCalled(); - // shows snackbar - expect(spyOnSnackbarSuccessHandler).toHaveBeenCalled(); + expect(getRequest).toHaveBeenCalled(); + expect(getRequest).toHaveBeenCalledWith( + expect.anything(), + expect.anything(), + expect.anything(), + expect.anything(), + { + hideArchived: false, + order: "id", + orderDir: 1, + page: 2, + perPage: 20, + term: "" + } + ); + }); }); }); }); diff --git a/src/actions/sponsor-forms-actions.js b/src/actions/sponsor-forms-actions.js index 085b09bfd..5437f0102 100644 --- a/src/actions/sponsor-forms-actions.js +++ b/src/actions/sponsor-forms-actions.js @@ -106,7 +106,7 @@ export const SPONSOR_FORM_ITEM_UNARCHIVED = "SPONSOR_FORM_ITEM_UNARCHIVED"; export const getSponsorForms = ( term = "", - page = DEFAULT_CURRENT_PAGE, + currentPage = DEFAULT_CURRENT_PAGE, perPage = DEFAULT_PER_PAGE, order = "id", orderDir = DEFAULT_ORDER_DIR, @@ -114,8 +114,9 @@ export const getSponsorForms = sponsorshipTypesId = [] ) => async (dispatch, getState) => { - const { currentSummitState } = getState(); + const { currentSummitState, sponsorFormsListState } = getState(); const { currentSummit } = currentSummitState; + const { totalCount } = sponsorFormsListState; const accessToken = await getAccessTokenSafely(); const filter = []; @@ -126,6 +127,9 @@ export const getSponsorForms = filter.push(`name=@${escapedTerm},code=@${escapedTerm}`); } + // Resets page to avoid backend error. + const page = perPage > totalCount ? 1 : currentPage; + const params = { page, fields: "id,code,name,level,expire_date,is_archived", From 5cdbc044c5694e26691ef7ab87b0eb55767b6d11 Mon Sep 17 00:00:00 2001 From: Nicolas Olmos Date: Tue, 3 Feb 2026 11:43:17 -0300 Subject: [PATCH 2/4] chore: remove fix applied on get sponsors --- src/actions/sponsor-forms-actions.js | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/actions/sponsor-forms-actions.js b/src/actions/sponsor-forms-actions.js index 5437f0102..480619f4a 100644 --- a/src/actions/sponsor-forms-actions.js +++ b/src/actions/sponsor-forms-actions.js @@ -114,9 +114,8 @@ export const getSponsorForms = sponsorshipTypesId = [] ) => async (dispatch, getState) => { - const { currentSummitState, sponsorFormsListState } = getState(); + const { currentSummitState } = getState(); const { currentSummit } = currentSummitState; - const { totalCount } = sponsorFormsListState; const accessToken = await getAccessTokenSafely(); const filter = []; @@ -127,11 +126,8 @@ export const getSponsorForms = filter.push(`name=@${escapedTerm},code=@${escapedTerm}`); } - // Resets page to avoid backend error. - const page = perPage > totalCount ? 1 : currentPage; - const params = { - page, + page: currentPage, fields: "id,code,name,level,expire_date,is_archived", relations: "items", per_page: perPage, @@ -161,7 +157,7 @@ export const getSponsorForms = createAction(RECEIVE_SPONSOR_FORMS), `${window.PURCHASES_API_URL}/api/v1/summits/${currentSummit.id}/show-forms`, authErrorHandler, - { order, orderDir, page, perPage, term, hideArchived } + { order, orderDir, currentPage, perPage, term, hideArchived } )(params)(dispatch).then(() => { dispatch(stopLoading()); }); From a01a5adca075b7e161e8b4a591bef8cdad79a65b Mon Sep 17 00:00:00 2001 From: Nicolas Olmos Date: Tue, 3 Feb 2026 11:44:03 -0300 Subject: [PATCH 3/4] fix: use default current page to fix pagination issues --- src/pages/sponsors/sponsor-forms-list-page/index.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/pages/sponsors/sponsor-forms-list-page/index.js b/src/pages/sponsors/sponsor-forms-list-page/index.js index 56c011083..451e3a4b1 100644 --- a/src/pages/sponsors/sponsor-forms-list-page/index.js +++ b/src/pages/sponsors/sponsor-forms-list-page/index.js @@ -65,7 +65,7 @@ const SponsorFormsListPage = ({ const handlePerPageChange = (newPerPage) => { getSponsorForms( term, - currentPage, + DEFAULT_CURRENT_PAGE, newPerPage, order, orderDir, @@ -73,13 +73,20 @@ const SponsorFormsListPage = ({ ); }; const handleSort = (key, dir) => { - getSponsorForms(term, currentPage, perPage, key, dir, hideArchived); + getSponsorForms( + term, + DEFAULT_CURRENT_PAGE, + perPage, + key, + dir, + hideArchived + ); }; const handleSearch = (searchTerm) => { getSponsorForms( searchTerm, - currentPage, + DEFAULT_CURRENT_PAGE, perPage, order, orderDir, From 2e14998dd2e42b78ba7f3fb4da8a46f002cb0350 Mon Sep 17 00:00:00 2001 From: Nicolas Olmos Date: Tue, 3 Feb 2026 12:07:04 -0300 Subject: [PATCH 4/4] chore: replace and fix unit tests --- .../__tests__/sponsor-forms-actions.test.js | 44 +++---------------- 1 file changed, 7 insertions(+), 37 deletions(-) diff --git a/src/actions/__tests__/sponsor-forms-actions.test.js b/src/actions/__tests__/sponsor-forms-actions.test.js index 357b85506..dbf2689e9 100644 --- a/src/actions/__tests__/sponsor-forms-actions.test.js +++ b/src/actions/__tests__/sponsor-forms-actions.test.js @@ -10,11 +10,11 @@ import { getSponsorForms } from "../sponsor-forms-actions"; import * as methods from "../../utils/methods"; jest.mock("openstack-uicore-foundation/lib/utils/actions", () => ({ - __esModule: true, - ...jest.requireActual("openstack-uicore-foundation/lib/utils/actions"), - postRequest: jest.fn(), - getRequest: jest.fn() - })); + __esModule: true, + ...jest.requireActual("openstack-uicore-foundation/lib/utils/actions"), + postRequest: jest.fn(), + getRequest: jest.fn() +})); describe("Sponsor Forms Actions", () => { describe("GetSponsorForms", () => { @@ -60,7 +60,7 @@ describe("Sponsor Forms Actions", () => { jest.restoreAllMocks(); }); describe("On perPage change", () => { - it("should request first page if perPage is greater than the total items count", async () => { + it("should request page specified", async () => { const store = mockStore({ currentSummitState: { currentSummit: {} @@ -83,42 +83,12 @@ describe("Sponsor Forms Actions", () => { hideArchived: false, order: "id", orderDir: 1, - page: 1, + currentPage: 2, perPage: 50, term: "" } ); }); - - it("should request user selected page if perPage is lower than the total items count", async () => { - const store = mockStore({ - currentSummitState: { - currentSummit: {} - }, - sponsorFormsListState: { - totalCount: 50 - } - }); - - store.dispatch(getSponsorForms("", 2, 20, "id", 1, false, [])); - await flushPromises(); - - expect(getRequest).toHaveBeenCalled(); - expect(getRequest).toHaveBeenCalledWith( - expect.anything(), - expect.anything(), - expect.anything(), - expect.anything(), - { - hideArchived: false, - order: "id", - orderDir: 1, - page: 2, - perPage: 20, - term: "" - } - ); - }); }); }); });