diff --git a/app/client/src/ce/sagas/ApiCallerSaga.test.ts b/app/client/src/ce/sagas/ApiCallerSaga.test.ts new file mode 100644 index 0000000000..1dfece723a --- /dev/null +++ b/app/client/src/ce/sagas/ApiCallerSaga.test.ts @@ -0,0 +1,124 @@ +import { runSaga } from "redux-saga"; +import type { Saga } from "redux-saga"; + +import ActionAPI from "api/ActionAPI"; +import { PostgresFactory } from "test/factories/Actions/Postgres"; +import type { ApiResponse } from "api/ApiResponses"; +import type { Action } from "entities/Action"; +import { JSObjectFactory } from "test/factories/Actions/JSObject"; +// Since this is a ce test, importing from @appsmith might lead to unexpected results +// eslint-disable-next-line @typescript-eslint/no-restricted-imports +import JSActionAPI from "ce/api/JSActionAPI"; +import { + updateActionAPICall, + updateJSCollectionAPICall, +} from "./ApiCallerSagas"; + +jest.mock("ce/api/JSActionAPI"); +jest.mock("api/ActionAPI"); + +const successResponse = (data: T) => { + return { + responseMeta: { + status: 200, + success: true, + }, + data, + errorDisplay: "", + } as ApiResponse; +}; + +describe("updateActionAPICall", () => { + beforeEach(() => { + jest.resetAllMocks(); + }); + + it("should call ActionAPI.updateAction and return the response", async () => { + const dispatchedActions: any[] = []; + const action = PostgresFactory.build(); + + const response: ApiResponse = successResponse(action); + + (ActionAPI.updateAction as jest.Mock).mockResolvedValue(response); + + const result = await runSaga( + { + dispatch: (action) => dispatchedActions.push(action), + }, + updateActionAPICall as Saga, + action, + ).toPromise(); + + expect(ActionAPI.updateAction).toHaveBeenCalledWith(action); + expect(result).toEqual(response); + }); + + it("should throw an error when ActionAPI.updateAction fails", async () => { + const dispatchedActions: any[] = []; + const action = PostgresFactory.build(); + const error = new Error("Some error"); + + (ActionAPI.updateAction as jest.Mock).mockRejectedValue(error); + + try { + await runSaga( + { + dispatch: (action) => dispatchedActions.push(action), + }, + updateActionAPICall as Saga, + action, + ).toPromise(); + } catch (e) { + expect(e).toEqual(error); + } + + expect(ActionAPI.updateAction).toHaveBeenCalledWith(action); + }); +}); + +describe("updateJSCollectionAPICall", () => { + beforeEach(() => { + jest.resetAllMocks(); + }); + + it("should call JSActionAPI.updateJSCollection and return the response", async () => { + const dispatchedActions: any[] = []; + const jsCollection = JSObjectFactory.build(); + const response = successResponse(jsCollection); + + (JSActionAPI.updateJSCollection as jest.Mock).mockResolvedValue(response); + + const result = await runSaga( + { + dispatch: (action) => dispatchedActions.push(action), + }, + updateJSCollectionAPICall as Saga, + jsCollection, + ).toPromise(); + + expect(JSActionAPI.updateJSCollection).toHaveBeenCalledWith(jsCollection); + expect(result).toEqual(response); + }); + + it("should throw an error when JSActionAPI.updateJSCollection fails", async () => { + const dispatchedActions: any[] = []; + const jsCollection = JSObjectFactory.build(); + const error = new Error("Some error"); + + (JSActionAPI.updateJSCollection as jest.Mock).mockRejectedValue(error); + + try { + await runSaga( + { + dispatch: (action) => dispatchedActions.push(action), + }, + updateJSCollectionAPICall as Saga, + jsCollection, + ).toPromise(); + } catch (e) { + expect(e).toEqual(error); + } + + expect(JSActionAPI.updateJSCollection).toHaveBeenCalledWith(jsCollection); + }); +}); diff --git a/app/client/src/ce/sagas/ApiCallerSagas.ts b/app/client/src/ce/sagas/ApiCallerSagas.ts index 4f70d2a463..9790963fe4 100644 --- a/app/client/src/ce/sagas/ApiCallerSagas.ts +++ b/app/client/src/ce/sagas/ApiCallerSagas.ts @@ -1,6 +1,8 @@ +import JSActionAPI from "@appsmith/api/JSActionAPI"; import ActionAPI from "api/ActionAPI"; import type { ApiResponse } from "api/ApiResponses"; import type { Action } from "entities/Action"; +import type { JSCollection } from "entities/JSCollection"; /** * DO NOT ADD any additional code/functionality in this saga. This function is overridden in EE to @@ -18,3 +20,21 @@ export function* updateActionAPICall(action: Action) { throw e; } } + +/** + * DO NOT ADD any additional code/functionality in this saga. This function is overridden in EE to + * use a different API for update jsCollection under the package editor. + * The purpose of this saga is only to call the appropriate API and return the result + * @param action Action + * @returns Action + */ +export function* updateJSCollectionAPICall(jsCollection: JSCollection) { + try { + const response: ApiResponse = + yield JSActionAPI.updateJSCollection(jsCollection); + + return response; + } catch (e) { + throw e; + } +} diff --git a/app/client/src/layoutSystems/anvil/integrations/sagas/pasteSagas/pasteSagas.test.ts b/app/client/src/layoutSystems/anvil/integrations/sagas/pasteSagas/pasteSagas.test.ts index 686690727b..e9316c9636 100644 --- a/app/client/src/layoutSystems/anvil/integrations/sagas/pasteSagas/pasteSagas.test.ts +++ b/app/client/src/layoutSystems/anvil/integrations/sagas/pasteSagas/pasteSagas.test.ts @@ -45,7 +45,7 @@ jest.mock("selectors/layoutSystemSelectors", () => ({ getLayoutSystemType: jest.fn(), })); describe("pasteSagas", () => { - const pageId = "pageId"; + const pageId = "0123456789abcdef00000000"; beforeAll(() => { registerLayoutComponents(); diff --git a/app/client/src/sagas/JSPaneSagas.ts b/app/client/src/sagas/JSPaneSagas.ts index f8fb06a013..60571f757d 100644 --- a/app/client/src/sagas/JSPaneSagas.ts +++ b/app/client/src/sagas/JSPaneSagas.ts @@ -100,6 +100,7 @@ import { getFocusablePropertyPaneField } from "selectors/propertyPaneSelectors"; import { getIsSideBySideEnabled } from "selectors/ideSelectors"; import { setIdeEditorViewMode } from "actions/ideActions"; import { EditorViewMode } from "@appsmith/entities/IDE/constants"; +import { updateJSCollectionAPICall } from "@appsmith/sagas/ApiCallerSagas"; export interface GenerateDefaultJSObjectProps { name: string; @@ -325,8 +326,10 @@ function* updateJSCollection(data: { try { const { deletedActions, jsCollection, newActions } = data; if (jsCollection) { - const response: JSCollectionCreateUpdateResponse = - yield JSActionAPI.updateJSCollection(jsCollection); + const response: JSCollectionCreateUpdateResponse = yield call( + updateJSCollectionAPICall, + jsCollection, + ); const isValidResponse: boolean = yield validateResponse(response); if (isValidResponse) { if (newActions && newActions.length) { @@ -724,7 +727,8 @@ function* handleUpdateJSFunctionPropertySaga( }); collection.actions = updatedActions; const response: ApiResponse = - yield JSActionAPI.updateJSCollection(collection); + yield call(updateJSCollectionAPICall, collection); + const isValidResponse: boolean = yield validateResponse(response); if (isValidResponse) { yield put({