chore: Cypress - add pane interactions (#32091)

## Description

Added cypress test for add pane interactions.

Fixes #31868

## Automation

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

### 🔍 Cypress test results
<!-- This is an auto-generated comment: Cypress test results  -->
> [!CAUTION]
> Workflow run:
<https://github.com/appsmithorg/appsmith/actions/runs/8504922496>
> Commit: `0c74a26f4c959c84bf308876e4c0740e0c45ca90`
> Cypress dashboard: <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=8504922496&attempt=1&selectiontype=test&testsstatus=failed&specsstatus=fail"
target="_blank"> Click here!</a>
> The following are new failures, please fix them before merging the PR:
<ol>
>
<li>cypress/e2e/Regression/ClientSide/AppNavigation/AppNavigationWithMultiplePages_spec.ts
</ol>
> To know the list of identified flaky tests - <a
href="https://internal.appsmith.com/app/cypress-dashboard/identified-flaky-tests-65890b3c81d7400d08fa9ee3?branch=master"
target="_blank">Refer here</a>

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











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

- **New Features**
- Introduced test cases for adding panes in the IDE, covering various
tab interactions.
- Added the ability to switch between full screen and split screen modes
in the IDE.

- **Tests**
	- Added a new Cypress custom command for easier test maintenance.

- **Refactor**
- Enhanced testability by adding `data-testid` attributes across various
IDE components for improved element selection in tests.
- Improved code clarity through reorganization and reformatting of
component properties.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Hetu Nandu <hetunandu@gmail.com>
This commit is contained in:
albinAppsmith 2024-04-02 10:06:54 +05:30 committed by GitHub
parent a38f7fbb5f
commit 834c0f8280
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 304 additions and 15 deletions

View File

@ -0,0 +1,106 @@
import EditorNavigation, {
EditorViewMode,
PageLeftPane,
PagePaneSegment,
} from "../../../../support/Pages/EditorNavigation";
import { ObjectsRegistry } from "../../../../support/Objects/Registry";
import FileTabs from "../../../../support/Pages/IDE/FileTabs";
const agHelper = ObjectsRegistry.AggregateHelper;
const commonLocators = ObjectsRegistry.CommonLocators;
describe("IDE add pane interactions", { tags: ["@tag.IDE"] }, () => {
it("1. UI tab add interactions", () => {
// check add pane is open
PageLeftPane.assertInAddView();
// close add pane to show blank state
PageLeftPane.closeAddView();
// click on add button and check add state
PageLeftPane.switchToAddNew();
// check add pane
PageLeftPane.assertInAddView();
// drag and drop a widget and list view should be opened
cy.dragAndDropToCanvas("textwidget", { x: 300, y: 200 });
// check listing ui
PageLeftPane.selectedItem().contains("Text1");
// click add button
PageLeftPane.switchToAddNew();
// check add pane is open
PageLeftPane.assertInAddView();
// close add pane
PageLeftPane.closeAddView();
// click on canvas and check add pane visible or not
agHelper.GetNClick(commonLocators._canvas).click();
// check add pane
PageLeftPane.assertInAddView();
});
it("2. JS tab add interactions", () => {
/** Fullscreen */
// switch to JS tab from UI
PageLeftPane.switchSegment(PagePaneSegment.JS);
// check and click on blank state add button
PageLeftPane.switchToAddNew();
// check listing UI
PageLeftPane.assertInListView();
// click on add btn in the listing UI
PageLeftPane.switchToAddNew();
// check item got added or not
PageLeftPane.assertInListView();
PageLeftPane.assertItemCount(2);
/** Splitscreen */
// switch to splitscreen
EditorNavigation.SwitchScreenMode(EditorViewMode.SplitScreen);
// click on add
FileTabs.switchToAddNew();
// check tabs count to verify js added or not
FileTabs.assertTabCount(3);
// switch back to full screen
EditorNavigation.SwitchScreenMode(EditorViewMode.FullScreen);
// delete all js objects and check add screen
cy.get(".editor-tab").each(($ele) => {
cy.selectByTestId("more-action-trigger").click();
cy.get(".t--apiFormDeleteBtn").click();
cy.get(".t--apiFormDeleteBtn").click();
});
PageLeftPane.assertInAddView();
});
it("3. Queries tab add interactions", () => {
/** Fullscreen */
// switch to Query tab from JS
PageLeftPane.switchSegment(PagePaneSegment.Queries);
// check and click on blank state add button
PageLeftPane.switchToAddNew();
// check add pane
PageLeftPane.assertInAddView();
// close add pane
PageLeftPane.closeAddView();
// open add pane to add item
PageLeftPane.switchToAddNew();
// add item
cy.get(".t--new-blank-api").children("div").first().click();
// check item added or not
PageLeftPane.assertPresence("Api1");
/** Splitscreen */
// switch to splitscreen
EditorNavigation.SwitchScreenMode(EditorViewMode.SplitScreen);
// click on add
FileTabs.switchToAddNew();
// check add pane
PageLeftPane.assertInAddView();
// add item
cy.get(".t--new-blank-api").children("div").first().click();
// check tabs count to verify js added or not
FileTabs.assertTabCount(2);
// switch back to full screen
EditorNavigation.SwitchScreenMode(EditorViewMode.FullScreen);
// delete all queries and check add screen
cy.get(".editor-tab").each(($ele) => {
cy.selectByTestId("more-action-trigger").click();
cy.get(".t--apiFormDeleteBtn").click();
cy.get(".t--apiFormDeleteBtn").click();
});
PageLeftPane.assertInAddView();
});
});

View File

@ -19,6 +19,11 @@ export enum PagePaneSegment {
JS = "JS",
}
export enum EditorViewMode {
FullScreen = "FullScreen",
SplitScreen = "SplitScreen",
}
const pagePaneListItemSelector = (name: string) =>
"//div[contains(@class, 't--entity-name')][text()='" + name + "']";
@ -38,6 +43,12 @@ export enum EntityType {
Page = "Page",
}
class EditorNavigation {
public locators = {
MaximizeBtn: "[data-testid='t--ide-maximize']",
MinimizeBtn: "[data-testid='t--ide-minimize']",
announcementCloseButton: "[data-testid='t--ide-close-announcement']",
};
NavigateToDatasource(name: string) {
AppSidebar.navigate(AppSidebarButton.Data);
cy.get(datasource.datasourceCard)
@ -119,6 +130,23 @@ class EditorNavigation {
AppSidebar.navigate(AppSidebarButton.Editor);
PageLeftPane.switchSegment(PagePaneSegment.UI);
}
SwitchScreenMode(mode: EditorViewMode) {
if (mode === EditorViewMode.FullScreen) {
_.AggregateHelper.GetNClick(this.locators.MaximizeBtn);
} else {
_.AggregateHelper.GetNClick(this.locators.MinimizeBtn);
cy.get("body").then(($body) => {
if ($body.find(this.locators.announcementCloseButton).length > 0) {
_.AggregateHelper.GetNClick(
this.locators.announcementCloseButton,
0,
true,
);
}
});
}
}
}
export default new EditorNavigation();

View File

@ -0,0 +1,28 @@
import { ObjectsRegistry } from "../../Objects/Registry";
class AddView {
public locators = {
closePaneButton: "[data-testid='t--add-pane-close-icon']",
createOption: "[data-testid='t--create-option']",
};
constructor() {
//
}
public assertInAddView() {
ObjectsRegistry.AggregateHelper.AssertElementVisibility(
this.locators.closePaneButton,
);
}
public closeAddView() {
ObjectsRegistry.AggregateHelper.GetNClick(this.locators.closePaneButton);
}
public getCreateOptions(): Cypress.Chainable {
return cy.get(this.locators.createOption);
}
}
export default AddView;

View File

@ -0,0 +1,37 @@
import { ObjectsRegistry } from "../../Objects/Registry";
class FileTabs {
locators = {
container: "[data-testid='t--editor-tabs']",
tabName: (name: string) => `[data-testid='t--ide-tab-${name}']`,
tabs: ".editor-tab",
addItem: "[data-testid='t--ide-split-screen-add-button']",
};
assertVisibility() {
ObjectsRegistry.AggregateHelper.AssertElementVisibility(
this.locators.container,
);
}
assertTabCount(count: number) {
ObjectsRegistry.AggregateHelper.GetElement(this.locators.tabs).should(
"have.length",
count,
);
}
switchToAddNew() {
// for js it will directly add a new file
cy.get("body").then(($body) => {
if ($body.find(this.locators.addItem).length > 0) {
ObjectsRegistry.AggregateHelper.GetNClick(
this.locators.addItem,
0,
true,
);
}
});
}
}
export default new FileTabs();

View File

@ -1,4 +1,6 @@
import { ObjectsRegistry } from "../../Objects/Registry";
import AddView from "./AddView";
import ListView from "./ListView";
export class LeftPane {
segments?: string[];
@ -10,11 +12,13 @@ export class LeftPane {
"//div[text()='" +
name +
"']/ancestor::div/span[contains(@class, 't--entity-collapse-toggle')]",
addItem: "button.t--add-item",
activeItemSelector: "",
selector: "",
};
private addView: AddView;
private listView: ListView;
constructor(
listItemSelector: (name: string) => string,
selector: string,
@ -25,6 +29,8 @@ export class LeftPane {
this.segments = segments;
this.locators.selector = selector;
this.locators.activeItemSelector = activeItemSelector;
this.addView = new AddView();
this.listView = new ListView();
}
public assertAbsence(name: string) {
@ -96,15 +102,26 @@ export class LeftPane {
}
public switchToAddNew() {
// for js it will directly add a new file
cy.get("body").then(($body) => {
if ($body.find(this.locators.addItem).length > 0) {
ObjectsRegistry.AggregateHelper.GetNClick(
this.locators.addItem,
0,
true,
);
}
});
this.listView.switchToAddNew();
}
public assertInAddView() {
this.addView.assertInAddView();
}
public closeAddView() {
this.addView.closeAddView();
}
public getCreateOptions() {
return this.addView.getCreateOptions();
}
public assertInListView() {
this.listView.assertListVisibility();
}
public assertItemCount(count: number) {
this.listView.assertItemCount(count);
}
}

View File

@ -0,0 +1,47 @@
import { ObjectsRegistry } from "../../Objects/Registry";
class ListView {
public locators = {
list: "[data-testid='t--ide-list']",
listItem: "[data-testid='t--ide-list-item']",
addItem: "button.t--add-item",
};
public assertListVisibility() {
ObjectsRegistry.AggregateHelper.AssertElementVisibility(this.locators.list);
}
public assertItemVisibility(name: string) {
ObjectsRegistry.AggregateHelper.GetNAssertElementText(
this.locators.listItem,
name,
);
}
public getItem(name: string) {
return ObjectsRegistry.AggregateHelper.GetElement(
this.locators.listItem,
).should("have.text", name);
}
public assertItemCount(count: number) {
return ObjectsRegistry.AggregateHelper.GetElement(
this.locators.listItem,
).should("have.length", count);
}
switchToAddNew() {
// for js it will directly add a new file
cy.get("body").then(($body) => {
if ($body.find(this.locators.addItem).length > 0) {
ObjectsRegistry.AggregateHelper.GetNClick(
this.locators.addItem,
0,
true,
);
}
});
}
}
export default ListView;

View File

@ -2077,3 +2077,16 @@ Cypress.Commands.add("stubPricingPage", () => {
}).as("pricingPage");
});
});
/**
* @param testID
* @returns
*
* This function act as a data-testid selector. In
* any case it is decided to rename the data-testid,
* it's thing single function that needs to be updated.
*
*/
Cypress.Commands.add("selectByTestId", (testId) => {
return cy.get(`[data-testid="${testId}"]`);
});

View File

@ -175,5 +175,6 @@ declare namespace Cypress {
skipSignposting();
stubPricingPage();
validateEvaluatedValue(value: string);
selectByTestId(value: string): Chainable<JQuery<HTMLElement>>;
}
}

View File

@ -14,7 +14,7 @@ export interface JSListItemProps {
export const JSListItem = (props: JSListItemProps) => {
const { isActive, item, parentEntityId, parentEntityType } = props;
return (
<Flex flexDirection={"column"}>
<Flex data-testid="t--ide-list-item" flexDirection={"column"}>
<ExplorerJSCollectionEntity
id={item.key}
isActive={isActive}

View File

@ -76,6 +76,7 @@ const ListJSObjects = () => {
parentEntityType={ActionParentEntityType.PAGE}
>
<Flex
data-testid="t--ide-list"
flex="1"
flexDirection="column"
gap="spaces-4"

View File

@ -81,7 +81,13 @@ const ListWidgets = () => {
</Flex>
) : null}
{widgetsExist ? (
<Flex flex="1" flexDirection={"column"} overflowY="auto" px="spaces-3">
<Flex
data-testid="t--ide-list"
flex="1"
flexDirection={"column"}
overflowY="auto"
px="spaces-3"
>
{widgets?.children?.map((child) => (
<WidgetEntity
childWidgets={child.children}

View File

@ -27,7 +27,12 @@ const Announcement = () => {
const modalFooter = () => (
<>
<Button kind="primary" onClick={tryClickHandler} size="md">
<Button
data-testid="t--ide-close-announcement"
kind="primary"
onClick={tryClickHandler}
size="md"
>
Try it out
</Button>
<Button kind="tertiary" onClick={learnClickHandler} size="md">

View File

@ -27,7 +27,7 @@ const SegmentAddHeader = (props: Props) => {
</Text>
<Button
aria-label="Close pane"
data-testid="t--widget-add-pane-close-icon"
data-testid="t--add-pane-close-icon"
isIconButton
kind={"tertiary"}
onClick={props.onCloseClick}