Skip to content
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

test: Add cypress tests for Hubspot Datasource functionalities #39419

Merged
merged 2 commits into from
Feb 25, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
import {
agHelper,
appSettings,
dataSources,
deployMode,
draggableWidgets,
entityExplorer,
entityItems,
locators,
propPane,
assertHelper,
} from "../../../../support/Objects/ObjectsCore";
import EditorNavigation, {
EntityType,
PageLeftPane,
PagePaneSegment,
} from "../../../../support/Pages/EditorNavigation";
const myDsName = "HubspotDS";

describe(
"Hubspot Basic Tests",
{
tags: ["@tag.Datasource", "@tag.Git", "@tag.AccessControl", "@tag.Hubspot"],
},
() => {
it("1. Validate the configuration of Hubspot datasource", () => {
dataSources.NavigateToDSCreateNew();
dataSources.CreatePlugIn("HubSpot");
agHelper.AssertElementVisibility(dataSources._imgHubspotLogo, true, 0); // Ensure the Hubspot logo is visible
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Use data- attributes for selectors.*

According to the guidelines, we should:

  • Use data-* attributes for selectors
  • Avoid using plain strings in assertions

Replace the current selectors with data-* attributes:

-agHelper.AssertElementVisibility(dataSources._imgHubspotLogo, true, 0);
+agHelper.AssertElementVisibility('[data-testid="hubspot-logo"]', true, 0);

-cy.get(locators._widgetInDeployed(draggableWidgets.TEXT)).should(
-  "contain.text",
-  "appsmith1",
-);
+cy.get('[data-testid="text-widget"]').should(($el) => {
+  expect($el.text().trim()).to.equal("appsmith1");
+});

-cy.get(locators._widgetInDeployed(draggableWidgets.TEXT)).should(
-  "have.text",
-  "",
-);
+cy.get('[data-testid="text-widget"]').should(($el) => {
+  expect($el.text().trim()).to.equal("");
+});

Also applies to: 111-114, 138-141

agHelper.GetNAssertContains(locators._dsName, "Untitled datasource 1");

// Attempt to rename the datasource with invalid and valid names
agHelper.GetNClick(locators._dsName);
agHelper.ClearTextField(locators._dsNameTxt, true);
agHelper.AssertTooltip("Please enter a valid name");
agHelper.PressEnter();
agHelper.ValidateToastMessage("Invalid name");
agHelper.GetNClick(locators._dsName);
agHelper.TypeText(locators._dsNameTxt, myDsName);
agHelper.PressEnter();
agHelper.AssertElementVisibility(dataSources._datasourceCard, true);
// Fill out the Hubspot configuration form and save the datasource
dataSources.FillHubspotDSForm();
dataSources.SaveDatasource();
});

it("2. Validate creating & running queries for the datasource", () => {
// Create and run a SELECT query, validating the response views
dataSources.CreateQueryForDS("HubspotDS");
dataSources.ValidateNSelectDropdown(
"Commands",
"Please select an option",
"HubDB - get details of a published table",
);
agHelper.EnterValue("appsmith1", {
propFieldName: "",
directInput: false,
inputFieldName: "Table ID or name",
});

const fireApi = (retries = 5, responseTimeout = 100000) => {
if (retries === 0) {
throw new Error("Max retries reached, API did not return success.");
}

dataSources.RunQuery({
toValidateResponse: false,
});
cy.wait(assertHelper.GetAliasName("@postExecute"), {
timeout: responseTimeout,
})
Comment on lines +69 to +71
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove cy.wait and agHelper.Sleep calls.

According to the coding guidelines:

  • Avoid using cy.wait in code
  • Avoid using agHelper.sleep()

Replace the wait and sleep calls with proper Cypress commands that wait for specific conditions:

-cy.wait(assertHelper.GetAliasName("@postExecute"), {
-  timeout: responseTimeout,
-});
+cy.intercept('POST', '**/postExecute').as('postExecute');
+cy.get(assertHelper.GetAliasName("@postExecute")).should('exist');

-assertHelper.Sleep();
+cy.get(assertHelper.GetAliasName("@postExecute")).should('not.exist');

Also applies to: 84-84

.then((interceptions) => {
return cy
.get(assertHelper.GetAliasName("@postExecute"), {
timeout: responseTimeout,
})
.its("response");
})
.then((response) => {
const { isExecutionSuccess } = response.body.data;

if (!isExecutionSuccess) {
cy.log(`Retrying... Attempts left: ${retries - 1}`);
assertHelper.Sleep();
fireApi(retries - 1);
} else {
expect(isExecutionSuccess).to.eq(true);
}
});
};

fireApi(5);
Comment on lines +61 to +92
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Refactor retry mechanism to use Cypress built-in retries.

The custom retry mechanism can be replaced with Cypress's built-in retry-ability and assertions.

-const fireApi = (retries = 5, responseTimeout = 100000) => {
-  if (retries === 0) {
-    throw new Error("Max retries reached, API did not return success.");
-  }
-
-  dataSources.RunQuery({
-    toValidateResponse: false,
-  });
-  cy.wait(assertHelper.GetAliasName("@postExecute"))
-    .then((interceptions) => {
-      return cy.get(assertHelper.GetAliasName("@postExecute"))
-        .its("response");
-    })
-    .then((response) => {
-      const { isExecutionSuccess } = response.body.data;
-
-      if (!isExecutionSuccess) {
-        cy.log(`Retrying... Attempts left: ${retries - 1}`);
-        assertHelper.Sleep();
-        fireApi(retries - 1);
-      } else {
-        expect(isExecutionSuccess).to.eq(true);
-      }
-    });
-};
-
-fireApi(5);
+dataSources.RunQuery({
+  toValidateResponse: false,
+});
+
+cy.intercept('POST', '**/postExecute').as('postExecute');
+cy.get('@postExecute').should('exist').then((interception) => {
+  expect(interception.response.body.data.isExecutionSuccess).to.eq(true);
+});

Committable suggestion skipped: line range outside the PR's diff.

// PageLeftPane.switchSegment(PagePaneSegment.UI); // Switching the tab to ensure connection reset from Hubspot platform gets refreshed
// PageLeftPane.switchSegment(PagePaneSegment.Queries);
cy.get("@postExecute").then((resObj: any) => {
const json = resObj.response.body.data.body;
const name = json.name;
cy.log("Name is :" + name);
expect(name).to.equal("appsmith1"); //Verify if record contains the table
});
});

it("3. Validate widget binding with queries & deploying the app", () => {
PageLeftPane.switchSegment(PagePaneSegment.UI);
entityExplorer.DragDropWidgetNVerify(draggableWidgets.TEXT);
propPane.EnterJSContext("Text", "{{Api1.data}}");
deployMode.DeployApp(locators._widgetInDeployed(draggableWidgets.TEXT));
agHelper.AssertElementVisibility(appSettings.locators._header);
//agHelper.RefreshPage(); // Refreshing the page due to frequent connection reset from Hubspot
// Assert that the text widget contains the expected data
cy.get(locators._widgetInDeployed(draggableWidgets.TEXT)).should(
"contain.text",
"appsmith1",
);
// agHelper
// .GetElement(locators._widgetInDeployed(draggableWidgets.TEXT))
// .then(($elements) => {
// const values = $elements
// .map((_, el) => Cypress.$(el).text().trim())
// .get();
// expect(values).to.include("appsmith1");
// });
deployMode.NavigateBacktoEditor();
EditorNavigation.SelectEntityByName("Api1", EntityType.Query);
});

it("4. Validate deleting the datasource", () => {
// Delete all queries associated with the datasource
PageLeftPane.selectItem("Api1", { ctrlKey: true, force: true });
agHelper.ActionContextMenuWithInPane({
action: "Delete",
entityType: entityItems.Query,
});

// Delete the datasource and verify its removal
dataSources.DeleteDatasourceFromWithinDS(myDsName, 409);
deployMode.DeployApp(locators._widgetInDeployed(draggableWidgets.TEXT));
cy.get(locators._widgetInDeployed(draggableWidgets.TEXT)).should(
"have.text",
"",
);
deployMode.NavigateBacktoEditor();
dataSources.DeleteDatasourceFromWithinDS(myDsName, 200);
agHelper.ValidateToastMessage(
"HubspotDS datasource deleted successfully",
);
});
it("5. Validate connection error when misconfiguring datasource", () => {
dataSources.NavigateToDSCreateNew();
dataSources.CreatePlugIn("HubSpot");
agHelper.GetNAssertContains(locators._dsName, "Untitled datasource 1");
agHelper.AssertElementVisibility(dataSources._datasourceCard, true);
dataSources.FillHubspotDSForm(undefined, "wrongpassword");
dataSources.SaveDatasource(false);
dataSources.CreateQueryForDS("Untitled datasource 1");
agHelper.RefreshPage(); // Refreshing the page due to frequent connection reset from Hubspot
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove page refresh call.

The code uses agHelper.RefreshPage() which should be avoided. Instead, handle the connection reset gracefully using proper test setup and teardown.

-agHelper.RefreshPage(); // Refreshing the page due to frequent connection reset from Hubspot
+// Handle connection reset by implementing proper retry mechanism or connection handling
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
agHelper.RefreshPage(); // Refreshing the page due to frequent connection reset from Hubspot
// Handle connection reset by implementing proper retry mechanism or connection handling

dataSources.ValidateNSelectDropdown(
"Commands",
"Please select an option",
"HubDB - get details of a published table",
);
agHelper.EnterValue("appsmith1", {
propFieldName: "",
directInput: false,
inputFieldName: "Table ID or name",
});
dataSources.RunQuery({
expectedStatus: false,
toValidateResponse: true,
});
});
},
);
1 change: 1 addition & 0 deletions app/client/cypress/limited-tests.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# To run only limited tests - give the spec names in below format:
#cypress/e2e/Regression/ClientSide/VisualTests/JSEditorIndent_spec.js
cypress/e2e/Regression/ServerSide/Datasources/Hubspot_spec.ts
# For running all specs - uncomment below:
#cypress/e2e/**/**/*
cypress/e2e/Regression/ClientSide/Anvil/Widgets/*
Expand Down
4 changes: 4 additions & 0 deletions app/client/cypress/support/Objects/DataManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ export class DataManager {
Snowflake_role: "SYSADMIN",
Snowflake_username: Cypress.env("SNOWFLAKE_USERNAME"),
Snowflake_password: Cypress.env("SNOWFLAKE_PASSWORD"),

hubspotBearerToken: Cypress.env("HUBSPOT_TOKEN"),
},

Staging: {
Expand Down Expand Up @@ -216,6 +218,8 @@ export class DataManager {
Snowflake_role: "SYSADMIN",
Snowflake_username: Cypress.env("SNOWFLAKE_USERNAME"),
Snowflake_password: Cypress.env("SNOWFLAKE_PASSWORD"),

hubspotBearerToken: Cypress.env("HUBSPOT_TOKEN"),
},
};

Expand Down
21 changes: 20 additions & 1 deletion app/client/cypress/support/Pages/DataSources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@ export class DataSources {
private _entityTriggerElement = ".t--template-menu-trigger";
_dsSchemaTableResponse = ".t--table-response";
_imgSnowflakeLogo = "//img[contains(@src, 'snowflake.svg')]";
_imgHubspotLogo = "//img[contains(@src, 'hubspot.png')]";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Replace XPath with data- attribute selector.*

Using XPath selectors is against the coding guidelines. Use data-* attributes for more reliable and maintainable selectors.

-  _imgHubspotLogo = "//img[contains(@src, 'hubspot.png')]";
+  _imgHubspotLogo = "[data-testid='hubspot-logo-img']";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
_imgHubspotLogo = "//img[contains(@src, 'hubspot.png')]";
_imgHubspotLogo = "[data-testid='hubspot-logo-img']";

_dsConfigProperties = (index: number) =>
"input[name*='datasourceConfiguration.properties[" + index + "]']";
_dsConfigAuthType = `[data-testid*='datasourceConfiguration.authentication.authenticationType']`;
Expand Down Expand Up @@ -608,7 +609,25 @@ export class DataSources {
: password,
);
}

public FillHubspotDSForm(
environment = this.dataManager.defaultEnviorment,
password = "",
) {
this.ValidateNSelectDropdown(
"Authentication type",
"Please select an option",
"Bearer token",
);
this.agHelper.TypeText(
this.locator._inputFieldByName("Bearer token") +
"//" +
this.locator._inputField,
!password
? this.dataManager.dsValues[environment].hubspotBearerToken
: password,
);
this.agHelper.Sleep();
}
Comment on lines +612 to +630
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Remove agHelper.Sleep() and use proper wait conditions.

Using Sleep() is against the coding guidelines. Use proper wait conditions or assertions instead.

  public FillHubspotDSForm(
    environment = this.dataManager.defaultEnviorment,
    password = "",
  ) {
    this.ValidateNSelectDropdown(
      "Authentication type",
      "Please select an option",
      "Bearer token",
    );
    this.agHelper.TypeText(
      this.locator._inputFieldByName("Bearer token") +
        "//" +
        this.locator._inputField,
      !password
        ? this.dataManager.dsValues[environment].hubspotBearerToken
        : password,
    );
-   this.agHelper.Sleep();
+   this.agHelper.WaitUntilAllToastsDisappear();
  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public FillHubspotDSForm(
environment = this.dataManager.defaultEnviorment,
password = "",
) {
this.ValidateNSelectDropdown(
"Authentication type",
"Please select an option",
"Bearer token",
);
this.agHelper.TypeText(
this.locator._inputFieldByName("Bearer token") +
"//" +
this.locator._inputField,
!password
? this.dataManager.dsValues[environment].hubspotBearerToken
: password,
);
this.agHelper.Sleep();
}
public FillHubspotDSForm(
environment = this.dataManager.defaultEnviorment,
password = "",
) {
this.ValidateNSelectDropdown(
"Authentication type",
"Please select an option",
"Bearer token",
);
this.agHelper.TypeText(
this.locator._inputFieldByName("Bearer token") +
"//" +
this.locator._inputField,
!password
? this.dataManager.dsValues[environment].hubspotBearerToken
: password,
);
this.agHelper.WaitUntilAllToastsDisappear();
}

public FillMongoDSForm(
environment = this.dataManager.defaultEnviorment,
shouldAddTrailingSpaces = false,
Expand Down
1 change: 1 addition & 0 deletions app/client/cypress/tags.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ module.exports = {
"@tag.GSheet",
"@tag.GenerateCRUD",
"@tag.Git",
"@tag.Hubspot",
"@tag.IDE",
"@tag.IconButton",
"@tag.Iframe",
Expand Down
Loading