feat: add tests for AddAndMove and AddBuildingBlocks to canvas (#34215)

## Description
Ad unit tests for adding and moving building blocks saga.


Fixes #34146

## Automation

/ok-to-test tags="@tag.Sanity"

### 🔍 Cypress test results
<!-- This is an auto-generated comment: Cypress test results  -->
> [!TIP]
> 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉
> Workflow run:
<https://github.com/appsmithorg/appsmith/actions/runs/9494392536>
> Commit: c6b9b1b679f284458cc6500729408ddb015adb62
> Cypress dashboard url: <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=9494392536&attempt=1"
target="_blank">Click here!</a>

<!-- end of auto-generated comment: Cypress test results  -->






## Communication
Should the DevRel and Marketing teams inform users about this change?
- [ ] Yes
- [x] No


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **Tests**
- Added comprehensive tests for adding and moving building blocks on the
canvas.
  - Updated import paths for consistency in test files.
- Introduced new test suites for sagas handling building block
operations.

- **New Fixtures**
- Added mock data and constants for testing building block
functionalities.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Jacques Ikot 2024-06-13 08:46:45 +01:00 committed by GitHub
parent 78df6b5e52
commit dbffbb81c7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 332 additions and 2 deletions

View File

@ -0,0 +1,116 @@
import { getCurrentWorkspaceId } from "@appsmith/selectors/selectedWorkspaceSelectors";
import type { DragDetails } from "reducers/uiReducers/dragResizeReducer";
import type { CallEffect, PutEffect, SelectEffect } from "redux-saga/effects";
import { call, select } from "redux-saga/effects";
import { addWidgetAndMoveWidgetsSaga } from "sagas/CanvasSagas/DraggingCanvasSagas";
import { getDragDetails, getWidgetByName } from "sagas/selectors";
import { getCurrentApplicationId } from "selectors/editorSelectors";
import { initiateBuildingBlockDropEvent } from "utils/buildingBlockUtils";
import {
addAndMoveBuildingBlockToCanvasSaga,
loadBuildingBlocksIntoApplication,
} from "../BuildingBlockAdditionSagas";
import { actionPayload, skeletonWidget } from "./fixtures";
type GeneratorType = Generator<
CallEffect | SelectEffect | PutEffect,
void,
unknown
>;
describe("addAndMoveBuildingBlockToCanvasSaga", () => {
it("1. should add a skeleton widget and move existing widgets appropriately", () => {
const generator: GeneratorType =
addAndMoveBuildingBlockToCanvasSaga(actionPayload);
// Step 1: select getCurrentApplicationId
let result = generator.next();
expect(result.value).toEqual(select(getCurrentApplicationId));
// Mock return value of getCurrentApplicationId
const applicationId = "app1";
result = generator.next(applicationId);
expect(result.value).toEqual(select(getCurrentWorkspaceId));
// Step 2: select getCurrentWorkspaceId
const workspaceId = "workspace1";
result = generator.next(workspaceId);
expect(result.value).toEqual(select(getDragDetails));
// Step 3: select getDragDetails
const dragDetails: DragDetails = {
newWidget: {
displayName: "TestWidget",
},
};
result = generator.next(dragDetails);
// Generating the skeletonWidgetName
const buildingblockName = dragDetails.newWidget.displayName;
const skeletonWidgetName = `loading_${buildingblockName.toLowerCase().replace(/ /g, "_")}`;
const updatedActionPayload = {
...actionPayload,
payload: {
...actionPayload.payload,
shouldReplay: false,
newWidget: {
...actionPayload.payload.newWidget,
type: "SKELETON_WIDGET",
widgetName: skeletonWidgetName,
},
},
};
// Step 4: call addWidgetAndMoveWidgetsSaga
expect(result.value).toEqual(
call(addWidgetAndMoveWidgetsSaga, updatedActionPayload),
);
// Step 5: call initiateBuildingBlockDropEvent
result = generator.next();
expect(result.value).toEqual(
call(initiateBuildingBlockDropEvent, {
applicationId,
workspaceId,
buildingblockName,
}),
);
// Step 6: select getWidgetByName
result = generator.next();
expect(result.value).toEqual(select(getWidgetByName, skeletonWidgetName));
result = generator.next(skeletonWidget);
// Step 7: call loadBuildingBlocksIntoApplication
expect(result.value).toEqual(
call(
loadBuildingBlocksIntoApplication,
{
...actionPayload.payload.newWidget,
widgetId: actionPayload.payload.canvasId,
},
skeletonWidget.widgetId,
),
);
// Complete the generator
result = generator.next();
expect(result.done).toBe(true);
});
it("2. should handle errors gracefully", () => {
const generator: GeneratorType =
addAndMoveBuildingBlockToCanvasSaga(actionPayload);
generator.next();
// Introduce an error by throwing one manually
const error = new Error("Something went wrong");
try {
generator.throw(error);
} catch (err) {
expect(err).toBe(error);
}
});
});

View File

@ -0,0 +1,111 @@
import type { CallEffect, PutEffect, SelectEffect } from "redux-saga/effects";
import { call, select } from "redux-saga/effects";
import {
addBuildingBlockToCanvasSaga,
loadBuildingBlocksIntoApplication,
} from "../BuildingBlockAdditionSagas";
import type { ReduxAction } from "@appsmith/constants/ReduxActionConstants";
import { getCurrentWorkspaceId } from "@appsmith/selectors/selectedWorkspaceSelectors";
import type { WidgetAddChild } from "actions/pageActions";
import { addChildSaga } from "sagas/WidgetAdditionSagas";
import { getDragDetails, getWidgetByName } from "sagas/selectors";
import { getCurrentApplicationId } from "selectors/editorSelectors";
import { initiateBuildingBlockDropEvent } from "utils/buildingBlockUtils";
import { addEntityAction, skeletonWidget } from "./fixtures";
type GeneratorType = Generator<
CallEffect | SelectEffect | PutEffect,
void,
unknown
>;
describe("addBuildingBlockToCanvasSaga", () => {
it("1. should add a skeleton widget and initiate a building block drop", () => {
const generator: GeneratorType =
addBuildingBlockToCanvasSaga(addEntityAction);
// Step 1: select getCurrentApplicationId
let result = generator.next();
expect(result.value).toEqual(select(getCurrentApplicationId));
// Mock return value of getCurrentApplicationId
const applicationId = "app1";
result = generator.next(applicationId);
expect(result.value).toEqual(select(getCurrentWorkspaceId));
// Step 2: select getCurrentWorkspaceId
const workspaceId = "workspace1";
result = generator.next(workspaceId);
expect(result.value).toEqual(select(getDragDetails));
// Step 3: select getDragDetails
const dragDetails = {
newWidget: {
displayName: "TestWidget",
},
};
result = generator.next(dragDetails);
// Generating the skeletonWidgetName
const buildingblockName = dragDetails.newWidget.displayName;
const skeletonWidgetName = `loading_${buildingblockName.toLowerCase().replace(/ /g, "_")}`;
const addSkeletonWidgetAction: ReduxAction<
WidgetAddChild & { shouldReplay: boolean }
> = {
...addEntityAction,
payload: {
...addEntityAction.payload,
type: "SKELETON_WIDGET",
widgetName: skeletonWidgetName,
shouldReplay: false,
},
};
// Step 4: call initiateBuildingBlockDropEvent
expect(result.value).toEqual(
call(initiateBuildingBlockDropEvent, {
applicationId,
workspaceId,
buildingblockName,
}),
);
// Step 5: call addChildSaga
result = generator.next();
expect(result.value).toEqual(call(addChildSaga, addSkeletonWidgetAction));
// Step 6: select getWidgetByName
result = generator.next();
expect(result.value).toEqual(select(getWidgetByName, skeletonWidgetName));
result = generator.next(skeletonWidget);
// Step 7: call loadBuildingBlocksIntoApplication
expect(result.value).toEqual(
call(
loadBuildingBlocksIntoApplication,
addEntityAction.payload,
skeletonWidget.widgetId,
),
);
// Complete the generator
result = generator.next();
expect(result.done).toBe(true);
});
it("2. should handle errors gracefully", () => {
const generator: GeneratorType =
addBuildingBlockToCanvasSaga(addEntityAction);
generator.next();
// Introduce an error by throwing one manually
const error = new Error("Something went wrong");
try {
generator.throw(error);
} catch (err) {
expect(err).toBe(error);
}
});
});

View File

@ -5,7 +5,7 @@ import type {
SelectEffect,
} from "redux-saga/effects";
import { call, select } from "redux-saga/effects";
import { pasteBuildingBlockWidgetsSaga } from "./BuildingBlockAdditionSagas";
import { pasteBuildingBlockWidgetsSaga } from "../BuildingBlockAdditionSagas";
import { getCopiedWidgets } from "utils/storage";
import type { FlexLayer } from "layoutSystems/autolayout/utils/types";
import { getWidgets } from "sagas/selectors";
@ -21,7 +21,7 @@ import {
copiedWidgets,
leftMostWidget,
topMostWidget,
} from "./pasteWidgetAddition.fixture";
} from "../pasteWidgetAddition.fixture";
import type { NewPastePositionVariables } from "sagas/WidgetOperationUtils";
// Mock data for testing

View File

@ -0,0 +1,103 @@
import type { WidgetAddChild } from "actions/pageActions";
import type { ReduxAction } from "@appsmith/constants/ReduxActionConstants";
import type { WidgetDraggingUpdateParams } from "layoutSystems/common/canvasArenas/ArenaTypes";
import type { FlattenedWidgetProps } from "reducers/entityReducers/canvasWidgetsReducer";
// Mock return value of getWidgetByName
export const skeletonWidget: FlattenedWidgetProps = {
needsErrorInfo: false,
mobileBottomRow: 69,
widgetName: "loading_table_lookup",
displayName: "Skeleton",
topRow: 6,
bottomRow: 69,
parentRowSpace: 10,
type: "SKELETON_WIDGET",
hideCard: true,
mobileRightColumn: 46,
parentColumnSpace: 13.40625,
leftColumn: 15,
dynamicBindingPathList: [],
key: "k0u7iidinm",
isDeprecated: false,
rightColumn: 46,
widgetId: "ndw2y4zajv",
onCanvasUI: {
selectionBGCSSVar: "--on-canvas-ui-widget-selection",
focusBGCSSVar: "--on-canvas-ui-widget-focus",
selectionColorCSSVar: "--on-canvas-ui-widget-focus",
focusColorCSSVar: "--on-canvas-ui-widget-selection",
disableParentSelection: false,
},
isVisible: true,
version: 1,
parentId: "0",
isLoading: false,
renderMode: "CANVAS",
mobileTopRow: 6,
mobileLeftColumn: 15,
};
export const actionPayload: ReduxAction<{
newWidget: WidgetAddChild;
draggedBlocksToUpdate: WidgetDraggingUpdateParams[];
canvasId: string;
}> = {
type: "WIDGETS_ADD_CHILD_AND_MOVE",
payload: {
newWidget: {
type: "BUILDING_BLOCK",
leftColumn: 21,
topRow: 147,
columns: 31,
rows: 63,
parentRowSpace: 10,
parentColumnSpace: 13.40625,
newWidgetId: "9lg3rb7mi2",
widgetId: "0",
tabId: "0",
},
draggedBlocksToUpdate: [
{
left: 388.78125,
top: 1430,
width: 214.5,
height: 40,
columnWidth: 13.40625,
rowHeight: 10,
widgetId: "6b6kauwlxa",
isNotColliding: true,
type: "BUTTON_WIDGET",
updateWidgetParams: {
operation: "MOVE",
widgetId: "6b6kauwlxa",
payload: {
leftColumn: 29,
topRow: 143,
bottomRow: 147,
rightColumn: 45,
parentId: "0",
newParentId: "0",
},
},
},
],
canvasId: "0",
},
};
export const addEntityAction: ReduxAction<WidgetAddChild> = {
type: "WIDGET_ADD_CHILD",
payload: {
widgetId: "0",
type: "BUILDING_BLOCK",
leftColumn: 15,
topRow: 6,
columns: 31,
rows: 63,
parentRowSpace: 10,
parentColumnSpace: 13.40625,
newWidgetId: "ndw2y4zajv",
tabId: "0",
},
};