diff --git a/app/client/cypress/e2e/Regression/ServerSide/Datasources/Snowflake_Basic_Spec.ts b/app/client/cypress/e2e/Regression/ServerSide/Datasources/Snowflake_Basic_Spec.ts new file mode 100644 index 000000000000..81b89a0cdf08 --- /dev/null +++ b/app/client/cypress/e2e/Regression/ServerSide/Datasources/Snowflake_Basic_Spec.ts @@ -0,0 +1,235 @@ +import { + agHelper, + apiPage, + dataSources, + deployMode, + draggableWidgets, + entityExplorer, + entityItems, + locators, + propPane, + table, +} from "../../../../support/Objects/ObjectsCore"; +import datasource from "../../../../../cypress/locators/DatasourcesEditor.json"; +import EditorNavigation, { + EntityType, + PageLeftPane, + PagePaneSegment, +} from "../../../../support/Pages/EditorNavigation"; +import BottomTabs from "../../../../support/Pages/IDE/BottomTabs"; +import { PluginActionForm } from "../../../../support/Pages/PluginActionForm"; + +let pluginActionForm = new PluginActionForm(); + +const myDsName = "SnowflakeDS1"; + +describe( + "Snowflake Basic Tests", + { + tags: [ + "@tag.Datasource", + "@tag.Git", + "@tag.AccessControl", + "@tag.Snowflake", + ], + }, + () => { + it("1. Validate the configuration of database", () => { + dataSources.NavigateToDSCreateNew(); + dataSources.CreatePlugIn("Snowflake"); + agHelper.AssertElementVisibility(dataSources._imgSnowflakeLogo, true, 1); + 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); + agHelper.AssertAttribute( + datasource.datasourceConfigUrl, + "placeholder", + "xy12345.ap-south-1.aws", + ); + + // Fill out the Snowflake configuration form and test/save the datasource + dataSources.FillSnowflakeDSForm(); + dataSources.TestSaveDatasource(); + }); + + it("2. Validate creating & running queries for the datasource", () => { + // Create and run a SELECT query, validating the response views + dataSources.CreateQueryAfterDSSaved( + "SELECT * FROM PUBLIC.DB1 LIMIT 10;", + "SelectQuery", + ); + dataSources.runQueryAndVerifyResponseViews({ + count: 2, + operator: "gte", + responseTypes: ["TABLE", "JSON", "RAW"], + }); + + // Create and run an INSERT query, validating the response + dataSources.CreateQueryForDS( + myDsName, + "INSERT INTO PUBLIC.DB1 (SALARY, ID, NAME, POSITION) VALUES (87000, 3, 'Ada', 'Jr. Developer');", + "InsertQuery", + ); + dataSources.runQueryAndVerifyResponseViews({ + count: 1, + operator: "eq", + responseTypes: ["TABLE", "JSON", "RAW"], + }); + + // Create and run an UPDATE query, validating the response + dataSources.CreateQueryForDS( + myDsName, + "UPDATE PUBLIC.DB1 SET SALARY = 90000 WHERE ID = 3;", + "UpdateQuery", + ); + dataSources.runQueryAndVerifyResponseViews({ + count: 1, + operator: "eq", + responseTypes: ["TABLE", "JSON", "RAW"], + }); + + // Create and run a DELETE query, validating the response + dataSources.CreateQueryForDS( + myDsName, + "DELETE FROM PUBLIC.DB1 WHERE ID = 3;", + "DeleteQuery", + ); + dataSources.runQueryAndVerifyResponseViews({ + count: 1, + operator: "eq", + responseTypes: ["TABLE", "JSON", "RAW"], + }); + }); + + it("3. Validate widget binding with queries & deploying the app", () => { + PageLeftPane.switchSegment(PagePaneSegment.UI); + entityExplorer.DragDropWidgetNVerify(draggableWidgets.TABLE); + propPane.EnterJSContext("Table data", "{{SelectQuery.data}}"); + deployMode.DeployApp(locators._widgetInDeployed(draggableWidgets.TABLE)); + table.WaitUntilTableLoad(0, 0, "v2"); + table.AssertTableHeaderOrder("IDNAMEPOSITIONSALARY"); + deployMode.NavigateBacktoEditor(); + EditorNavigation.SelectEntityByName("SelectQuery", EntityType.Query); + }); + + it("4. Validate deleting the datasource", () => { + // Delete all queries associated with the datasource + agHelper.ActionContextMenuWithInPane({ + action: "Delete", + entityType: entityItems.Query, + }); + PageLeftPane.selectItem("InsertQuery", { ctrlKey: true, force: true }); + agHelper.ActionContextMenuWithInPane({ + action: "Delete", + entityType: entityItems.Query, + }); + PageLeftPane.selectItem("UpdateQuery", { ctrlKey: true, force: true }); + agHelper.ActionContextMenuWithInPane({ + action: "Delete", + entityType: entityItems.Query, + }); + PageLeftPane.selectItem("DeleteQuery", { 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.TABLE)); + table.WaitForTableEmpty("v2"); + deployMode.NavigateBacktoEditor(); + dataSources.DeleteDatasourceFromWithinDS(myDsName, 200); + }); + + it("5. Validate the user can create new queries from the datasource section in response tab", () => { + dataSources.NavigateToDSCreateNew(); + dataSources.CreatePlugIn("Snowflake"); + dataSources.FillSnowflakeDSForm(); + dataSources.TestSaveDatasource(); + + // Create, run, and validate SELECT, INSERT, UPDATE, and DELETE queries using templates + dataSources.createQueryWithDatasourceSchemaTemplate( + "Untitled datasource 1", + "DB1", + "Select", + ); + agHelper.RenameQuery("TemplateSelectQuery"); + apiPage.ToggleOnPageLoadRun(true); + pluginActionForm.toolbar.toggleSettings(); + apiPage.ToggleConfirmBeforeRunning(true); + agHelper.RefreshPage(); + agHelper.GetNClick(locators._confirmationdialogbtn("Yes")); + BottomTabs.response.validateRecordCount({ count: 2, operator: "gte" }); + + dataSources.createQueryWithDatasourceSchemaTemplate( + "Untitled datasource 1", + "DB1", + "Insert", + ); + dataSources.EnterQuery( + "INSERT INTO PUBLIC.DB1 (SALARY, ID, NAME, POSITION) VALUES (87000, 3, 'Ada', 'Jr. Developer');", + ); + agHelper.RenameQuery("TemplateInsertQuery"); + dataSources.runQueryAndVerifyResponseViews({ + count: 1, + operator: "eq", + responseTypes: ["TABLE", "JSON", "RAW"], + }); + + dataSources.createQueryWithDatasourceSchemaTemplate( + "Untitled datasource 1", + "DB1", + "Update", + ); + dataSources.EnterQuery( + "UPDATE PUBLIC.DB1 SET SALARY = 90000 WHERE ID = 3;", + ); + agHelper.RenameQuery("TemplateUpdateQuery"); + dataSources.runQueryAndVerifyResponseViews({ + count: 1, + operator: "eq", + responseTypes: ["TABLE", "JSON", "RAW"], + }); + + dataSources.createQueryWithDatasourceSchemaTemplate( + "Untitled datasource 1", + "DB1", + "Delete", + ); + dataSources.EnterQuery("DELETE FROM PUBLIC.DB1 WHERE ID = 3;"); + agHelper.RenameQuery("TemplateDeleteQuery"); + dataSources.runQueryAndVerifyResponseViews({ + count: 1, + operator: "eq", + responseTypes: ["TABLE", "JSON", "RAW"], + }); + + // Visit the documentation link + agHelper.GetNClick(locators._contextMenuInPane); + deployMode.StubWindowNAssert( + dataSources._queryDoc, + "querying-snowflake-db#querying-snowflake", + "getDatasourceStructure", + ); + }); + + it("6. Validate connection error when misconfiguring datasource", () => { + dataSources.NavigateToDSCreateNew(); + dataSources.CreatePlugIn("Snowflake"); + agHelper.GetNAssertContains(locators._dsName, "Untitled datasource 2"); + agHelper.AssertElementVisibility(dataSources._datasourceCard, true); + dataSources.FillSnowflakeDSForm(undefined, undefined, "wrongpassword"); + dataSources.TestDatasource(false); + }); + }, +); diff --git a/app/client/cypress/support/Objects/DataManager.ts b/app/client/cypress/support/Objects/DataManager.ts index c4a27a021fb2..87c02ae51a72 100644 --- a/app/client/cypress/support/Objects/DataManager.ts +++ b/app/client/cypress/support/Objects/DataManager.ts @@ -108,6 +108,14 @@ export class DataManager { twilio_username: "random-username", twilio_password: "random-password", + + Snowflake_accountName: Cypress.env("SNOWFLAKE_ACCOUNT_NAME"), + Snowflake_warehouseName: "COMPUTE_WH", + Snowflake_databaseName: "TESTDB", + Snowflake_defaultSchema: "PUBLIC", + Snowflake_role: "SYSADMIN", + Snowflake_username: Cypress.env("SNOWFLAKE_USERNAME"), + Snowflake_password: Cypress.env("SNOWFLAKE_PASSWORD"), }, Staging: { @@ -200,6 +208,14 @@ export class DataManager { twilio_username: "random-username", twilio_password: "random-password", + + Snowflake_accountName: Cypress.env("SNOWFLAKE_ACCOUNT_NAME"), + Snowflake_warehouseName: "COMPUTE_WH", + Snowflake_databaseName: "TESTDB", + Snowflake_defaultSchema: "PUBLIC", + Snowflake_role: "SYSADMIN", + Snowflake_username: Cypress.env("SNOWFLAKE_USERNAME"), + Snowflake_password: Cypress.env("SNOWFLAKE_PASSWORD"), }, }; diff --git a/app/client/cypress/support/Pages/DataSources.ts b/app/client/cypress/support/Pages/DataSources.ts index 8064c9eb4055..45f8985feb24 100644 --- a/app/client/cypress/support/Pages/DataSources.ts +++ b/app/client/cypress/support/Pages/DataSources.ts @@ -316,6 +316,10 @@ export class DataSources { private _dsSchemaEntityItem = ".t--entity-item"; private _entityTriggerElement = ".t--template-menu-trigger"; _dsSchemaTableResponse = ".t--table-response"; + _imgSnowflakeLogo = "//img[contains(@src, 'snowflake.svg')]"; + _dsConfigProperties = (index: number) => + "input[name*='datasourceConfiguration.properties[" + index + "]']"; + _dsConfigAuthType = `[data-testid*='datasourceConfiguration.authentication.authenticationType']`; public AssertDSEditViewMode(mode: AppModes) { if (mode == "Edit") this.agHelper.AssertElementAbsence(this._editButton); @@ -568,6 +572,43 @@ export class DataSources { ); } + public FillSnowflakeDSForm( + environment = this.dataManager.defaultEnviorment, + username = "", + password = "", + ) { + const accountName = + this.dataManager.dsValues[environment].Snowflake_accountName; + const warehouseName = + this.dataManager.dsValues[environment].Snowflake_warehouseName; + const databaseName = + this.dataManager.dsValues[environment].Snowflake_databaseName; + const schemaName = + this.dataManager.dsValues[environment].Snowflake_defaultSchema; + const role = this.dataManager.dsValues[environment].Snowflake_role; + this.agHelper.ClearNType(datasource.datasourceConfigUrl, accountName); + this.agHelper.ClearNType(this._dsConfigProperties(0), warehouseName); + this.agHelper.ClearNType(this._dsConfigProperties(1), databaseName); + this.agHelper.ClearNType(this._dsConfigProperties(2), schemaName); + this.agHelper.ClearNType(this._dsConfigProperties(3), role); + this.agHelper.AssertContains("Basic", "exist", this._dsConfigAuthType) + ? null + : this.agHelper.GetNClick(this._dsConfigAuthType) && + this.agHelper.ContainsNClick("Basic"); + this.agHelper.ClearNType( + this._username, + username == "" + ? this.dataManager.dsValues[environment].Snowflake_username + : username, + ); + this.agHelper.ClearNType( + this._password, + password == "" + ? this.dataManager.dsValues[environment].Snowflake_password + : password, + ); + } + public FillMongoDSForm( environment = this.dataManager.defaultEnviorment, shouldAddTrailingSpaces = false, diff --git a/app/client/cypress/tags.js b/app/client/cypress/tags.js index 048e6d6fff08..e37aec5dd5e6 100644 --- a/app/client/cypress/tags.js +++ b/app/client/cypress/tags.js @@ -56,6 +56,7 @@ module.exports = { "@tag.Settings", "@tag.SignIn", "@tag.Slider", + "@tag.Snowflake", "@tag.Statbox", "@tag.Switch", "@tag.Tab",