diff --git a/app/client/cypress/e2e/Regression/ClientSide/BugTests/Binding_Bug28287_Spec.ts b/app/client/cypress/e2e/Regression/ClientSide/BugTests/Binding_Bug28287_Spec.ts index 5a38feeaf45..3f92ef47c03 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/BugTests/Binding_Bug28287_Spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/BugTests/Binding_Bug28287_Spec.ts @@ -45,9 +45,9 @@ describe( EditorNavigation.SelectEntityByName(queryName, EntityType.Query); BottomPane.response.switchToResponseTab(); - + BottomPane.response.openResponseTypeMenu(); agHelper.AssertElementVisibility( - BottomPane.response.getResponseTypeSelector("TABLE"), + BottomPane.response.locators.responseTypeMenuItem("TABLE"), ); }); }); diff --git a/app/client/cypress/e2e/Regression/ServerSide/Datasources/Redis_Basic_Spec.ts b/app/client/cypress/e2e/Regression/ServerSide/Datasources/Redis_Basic_Spec.ts index 01e4106a445..7e833e24bcb 100644 --- a/app/client/cypress/e2e/Regression/ServerSide/Datasources/Redis_Basic_Spec.ts +++ b/app/client/cypress/e2e/Regression/ServerSide/Datasources/Redis_Basic_Spec.ts @@ -30,32 +30,32 @@ describe( //Add HSET dataSources.CreateQueryAfterDSSaved(); dataSources.EnterQuery(hSetReceipe); - dataSources.RunQueryNVerifyResponseViews(1); //verify all views are returned! + dataSources.runQueryAndVerifyResponseViews(); //verify all views are returned! dataSources.AssertQueryTableResponse(0, "4"); //Success response for 4 keys inserted via above HSET! //Read only one key from above HSET dataSources.EnterQuery(hGetKeys); - dataSources.RunQueryNVerifyResponseViews(1); //verify all views are returned! + dataSources.runQueryAndVerifyResponseViews(); //verify all views are returned! dataSources.AssertQueryTableResponse(0, "Vegetable Stir Fry"); //Read more than one key from above HSET dataSources.EnterQuery(hMGet); - dataSources.RunQueryNVerifyResponseViews(2); + dataSources.runQueryAndVerifyResponseViews({ count: 2 }); //verify all views are returned! dataSources.AssertQueryTableResponse(0, "easy"); dataSources.AssertQueryTableResponse(1, "Vegetable Stir Fry"); //Update key value in HSET dataSources.EnterQuery(hUpdate); - dataSources.RunQueryNVerifyResponseViews(1); //verify all views are returned! + dataSources.runQueryAndVerifyResponseViews(); //verify all views are returned! //validate updated key dataSources.EnterQuery(getUpdatedKey); - dataSources.RunQueryNVerifyResponseViews(1); + dataSources.runQueryAndVerifyResponseViews(); dataSources.AssertQueryTableResponse(0, "medium"); //Get All keys from HSET dataSources.EnterQuery(getAll); - dataSources.RunQueryNVerifyResponseViews(8); //4 keys, 4 values + dataSources.runQueryAndVerifyResponseViews({ count: 8 }); //4 keys, 4 values dataSources.ReadQueryTableResponse(0).then(($cellData: any) => { expect($cellData).to.be.oneOf([ "name", @@ -70,11 +70,11 @@ describe( //Ading one more key/value to HSET dataSources.EnterQuery(addNewKeyValue); - dataSources.RunQueryNVerifyResponseViews(1); + dataSources.runQueryAndVerifyResponseViews(); //Verify new key/value also added to HSET dataSources.EnterQuery(getAll); - dataSources.RunQueryNVerifyResponseViews(10); //5 keys, 5 values + dataSources.runQueryAndVerifyResponseViews({ count: 10 }); //5 keys, 5 values dataSources.ReadQueryTableResponse(0).then(($cellData: any) => { expect($cellData).to.be.oneOf([ "name", @@ -87,11 +87,11 @@ describe( //Deleting the Hash key dataSources.EnterQuery(deletehKey); - dataSources.RunQueryNVerifyResponseViews(1); + dataSources.runQueryAndVerifyResponseViews(); //Verify Deletion is success dataSources.EnterQuery(hGetKeys); - dataSources.RunQueryNVerifyResponseViews(); //5 keys, 5 values + dataSources.runQueryAndVerifyResponseViews(); dataSources.AssertQueryTableResponse(0, "null"); // Delete the query & datasource diff --git a/app/client/cypress/e2e/Regression/ServerSide/GenerateCRUD/MySQL1_Spec.ts b/app/client/cypress/e2e/Regression/ServerSide/GenerateCRUD/MySQL1_Spec.ts index e19bc35a846..e2a95ac3e39 100644 --- a/app/client/cypress/e2e/Regression/ServerSide/GenerateCRUD/MySQL1_Spec.ts +++ b/app/client/cypress/e2e/Regression/ServerSide/GenerateCRUD/MySQL1_Spec.ts @@ -166,7 +166,7 @@ describe( agHelper.FocusElement(locators._codeMirrorTextArea); //agHelper.VerifyEvaluatedValue(tableCreateQuery); //failing sometimes! - dataSources.RunQueryNVerifyResponseViews(); + dataSources.runQueryAndVerifyResponseViews(); dataSources.AssertTableInVirtuosoList(dsName, "productlines"); agHelper.ActionContextMenuWithInPane({ @@ -282,7 +282,7 @@ describe( agHelper.FocusElement(locators._codeMirrorTextArea); //agHelper.VerifyEvaluatedValue(tableCreateQuery); - dataSources.RunQueryNVerifyResponseViews(); + dataSources.runQueryAndVerifyResponseViews(); dataSources.AssertTableInVirtuosoList(dsName, "Stores", false); }); diff --git a/app/client/cypress/e2e/Regression/ServerSide/GenerateCRUD/MySQL2_Spec.ts b/app/client/cypress/e2e/Regression/ServerSide/GenerateCRUD/MySQL2_Spec.ts index 7cc2f30a82c..8cdcdb87118 100644 --- a/app/client/cypress/e2e/Regression/ServerSide/GenerateCRUD/MySQL2_Spec.ts +++ b/app/client/cypress/e2e/Regression/ServerSide/GenerateCRUD/MySQL2_Spec.ts @@ -68,7 +68,7 @@ describe( agHelper.FocusElement(locators._codeMirrorTextArea); //agHelper.VerifyEvaluatedValue(tableCreateQuery); - dataSources.RunQueryNVerifyResponseViews(); + dataSources.runQueryAndVerifyResponseViews(); dataSources.AssertTableInVirtuosoList(dsName, "Stores"); agHelper.ActionContextMenuWithInPane({ @@ -79,7 +79,7 @@ describe( it("2. Validate Select record from Postgress datasource & verify query response", () => { dataSources.CreateQueryForDS(dsName, "SELECT * FROM Stores LIMIT 10"); - dataSources.RunQueryNVerifyResponseViews(10); + dataSources.runQueryAndVerifyResponseViews({ count: 10 }); dataSources.AssertQueryTableResponse(5, "2112"); dataSources.AssertQueryTableResponse(6, "Mike's Liquors"); // Commenting this deletion of query to make the generate crud work on the new page instead of the current page @@ -375,7 +375,7 @@ describe( agHelper.FocusElement(locators._codeMirrorTextArea); //agHelper.VerifyEvaluatedValue(tableCreateQuery); - dataSources.RunQueryNVerifyResponseViews(); + dataSources.runQueryAndVerifyResponseViews(); dataSources.AssertTableInVirtuosoList(dsName, "Stores", false); agHelper.ActionContextMenuWithInPane({ diff --git a/app/client/cypress/e2e/Regression/ServerSide/GenerateCRUD/Postgres2_Spec.ts b/app/client/cypress/e2e/Regression/ServerSide/GenerateCRUD/Postgres2_Spec.ts index 09a77eb14b2..1124d1235f0 100644 --- a/app/client/cypress/e2e/Regression/ServerSide/GenerateCRUD/Postgres2_Spec.ts +++ b/app/client/cypress/e2e/Regression/ServerSide/GenerateCRUD/Postgres2_Spec.ts @@ -61,7 +61,7 @@ describe( agHelper.FocusElement(locators._codeMirrorTextArea); //agHelper.VerifyEvaluatedValue(tableCreateQuery); //failing sometimes! - dataSources.RunQueryNVerifyResponseViews(); + dataSources.runQueryAndVerifyResponseViews(); }); it("2. Validate Select record from Postgress datasource & verify query response", () => { @@ -70,7 +70,7 @@ describe( "public.vessels", "Select", ); - dataSources.RunQueryNVerifyResponseViews(10); + dataSources.runQueryAndVerifyResponseViews({ count: 10 }); dataSources.AssertQueryTableResponse(0, "371681"); dataSources.AssertQueryTableResponse(6, "Passenger"); agHelper.ActionContextMenuWithInPane({ @@ -617,7 +617,7 @@ describe( dataSources.CreateQueryForDS(dsName, deleteTblQuery, "DropVessels"); agHelper.FocusElement(locators._codeMirrorTextArea); - dataSources.RunQueryNVerifyResponseViews(); + dataSources.runQueryAndVerifyResponseViews(); dataSources.AssertTableInVirtuosoList(dsName, "public.vessels", false); }); diff --git a/app/client/cypress/e2e/Regression/ServerSide/Postgres_DataTypes/UUID_Spec.ts b/app/client/cypress/e2e/Regression/ServerSide/Postgres_DataTypes/UUID_Spec.ts index d63fde861a2..94425b929e3 100644 --- a/app/client/cypress/e2e/Regression/ServerSide/Postgres_DataTypes/UUID_Spec.ts +++ b/app/client/cypress/e2e/Regression/ServerSide/Postgres_DataTypes/UUID_Spec.ts @@ -259,7 +259,7 @@ describe( //Validating use of extention query = `CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; CREATE EXTENSION IF NOT EXISTS "pgcrypto"`; dataSources.CreateQueryForDS(dsName, query, "verifyUUIDFunctions"); - dataSources.RunQueryNVerifyResponseViews(1); + dataSources.runQueryAndVerifyResponseViews(); dataSources.AssertQueryResponseHeaders(["affectedRows"]); dataSources.ReadQueryTableResponse(0).then(($cellData) => { expect($cellData).to.eq("0"); @@ -297,7 +297,7 @@ describe( //Validating Addition of new column taking default value form package method query = `ALTER TABLE uuidtype ADD COLUMN newUUID uuid DEFAULT uuid_generate_v4();`; dataSources.EnterQuery(query); - dataSources.RunQueryNVerifyResponseViews(1); + dataSources.runQueryAndVerifyResponseViews(); dataSources.AssertQueryResponseHeaders(["affectedRows"]); dataSources.ReadQueryTableResponse(0).then(($cellData) => { expect($cellData).to.eq("0"); @@ -314,7 +314,7 @@ describe( //Validating altering the new column default value to generate id from pgcrypto package query = `ALTER TABLE uuidtype ALTER COLUMN newUUID SET DEFAULT gen_random_uuid();`; dataSources.EnterQuery(query); - dataSources.RunQueryNVerifyResponseViews(1); + dataSources.runQueryAndVerifyResponseViews(); dataSources.AssertQueryResponseHeaders(["affectedRows"]); dataSources.ReadQueryTableResponse(0).then(($cellData) => { expect($cellData).to.eq("0"); diff --git a/app/client/cypress/e2e/Regression/ServerSide/QueryPane/Mongo1_spec.ts b/app/client/cypress/e2e/Regression/ServerSide/QueryPane/Mongo1_spec.ts index 08fe4506112..6583c23c847 100644 --- a/app/client/cypress/e2e/Regression/ServerSide/QueryPane/Mongo1_spec.ts +++ b/app/client/cypress/e2e/Regression/ServerSide/QueryPane/Mongo1_spec.ts @@ -16,7 +16,6 @@ import EditorNavigation, { EntityType, } from "../../../../support/Pages/EditorNavigation"; import PageList from "../../../../support/Pages/PageList"; -import BottomPane from "../../../../support/Pages/IDE/BottomPane"; let dsName: any; @@ -333,7 +332,11 @@ describe( "Find", ); dataSources.ValidateNSelectDropdown("Command", "Find document(s)"); - dataSources.RunQueryNVerifyResponseViews(1, false); + dataSources.runQueryAndVerifyResponseViews({ + count: 1, + operator: "gte", + responseTypes: ["JSON", "RAW"], + }); agHelper.ActionContextMenuWithInPane({ action: "Delete", entityType: entityItems.Query, @@ -352,7 +355,11 @@ describe( directInput: false, inputFieldName: "Query", }); - dataSources.RunQueryNVerifyResponseViews(1, false); + dataSources.runQueryAndVerifyResponseViews({ + count: 1, + operator: "gte", + responseTypes: ["JSON", "RAW"], + }); agHelper.ActionContextMenuWithInPane({ action: "Delete", entityType: entityItems.Query, @@ -430,12 +437,13 @@ describe( parseInt(JSON.stringify(resObj.response.body.data.body.n)), ).to.eq(3); }); - agHelper.AssertElementVisibility( - BottomPane.response.getResponseTypeSelector("JSON"), - ); - agHelper.AssertElementVisibility( - BottomPane.response.getResponseTypeSelector("RAW"), - ); + + dataSources.runQueryAndVerifyResponseViews({ + count: 1, + operator: "gte", + responseTypes: ["JSON", "RAW"], + }); + agHelper.ActionContextMenuWithInPane({ action: "Delete", entityType: entityItems.Query, @@ -467,12 +475,13 @@ describe( parseInt(JSON.stringify(resObj.response.body.data.body.nModified)), ).to.eq(0); }); - agHelper.AssertElementVisibility( - BottomPane.response.getResponseTypeSelector("JSON"), - ); - agHelper.AssertElementVisibility( - BottomPane.response.getResponseTypeSelector("RAW"), - ); + + dataSources.runQueryAndVerifyResponseViews({ + count: 1, + operator: "gte", + responseTypes: ["JSON", "RAW"], + }); + agHelper.ActionContextMenuWithInPane({ action: "Delete", entityType: entityItems.Query, @@ -514,12 +523,13 @@ describe( parseInt(JSON.stringify(resObj.response.body.data.body.nModified)), ).to.eq(2); }); - agHelper.AssertElementVisibility( - BottomPane.response.getResponseTypeSelector("JSON"), - ); - agHelper.AssertElementVisibility( - BottomPane.response.getResponseTypeSelector("RAW"), - ); + + dataSources.runQueryAndVerifyResponseViews({ + count: 1, + operator: "gte", + responseTypes: ["JSON", "RAW"], + }); + agHelper.ActionContextMenuWithInPane({ action: "Delete", entityType: entityItems.Query, @@ -556,12 +566,13 @@ describe( parseInt(JSON.stringify(resObj.response.body.data.body.nModified)), ).to.eq(1); }); - agHelper.AssertElementVisibility( - BottomPane.response.getResponseTypeSelector("JSON"), - ); - agHelper.AssertElementVisibility( - BottomPane.response.getResponseTypeSelector("RAW"), - ); + + dataSources.runQueryAndVerifyResponseViews({ + count: 1, + operator: "gte", + responseTypes: ["JSON", "RAW"], + }); + agHelper.ActionContextMenuWithInPane({ action: "Delete", entityType: entityItems.Query, @@ -587,12 +598,13 @@ describe( parseInt(JSON.stringify(resObj.response.body.data.body.n)), ).to.eq(0); }); - agHelper.AssertElementVisibility( - BottomPane.response.getResponseTypeSelector("JSON"), - ); - agHelper.AssertElementVisibility( - BottomPane.response.getResponseTypeSelector("RAW"), - ); + + dataSources.runQueryAndVerifyResponseViews({ + count: 1, + operator: "gte", + responseTypes: ["JSON", "RAW"], + }); + agHelper.ActionContextMenuWithInPane({ action: "Delete", entityType: entityItems.Query, @@ -620,12 +632,13 @@ describe( parseInt(JSON.stringify(resObj.response.body.data.body.n)), ).to.eq(1); }); - agHelper.AssertElementVisibility( - BottomPane.response.getResponseTypeSelector("JSON"), - ); - agHelper.AssertElementVisibility( - BottomPane.response.getResponseTypeSelector("RAW"), - ); + + dataSources.runQueryAndVerifyResponseViews({ + count: 1, + operator: "gte", + responseTypes: ["JSON", "RAW"], + }); + agHelper.ActionContextMenuWithInPane({ action: "Delete", entityType: entityItems.Query, @@ -657,12 +670,13 @@ describe( 2, ); }); - agHelper.AssertElementVisibility( - BottomPane.response.getResponseTypeSelector("JSON"), - ); - agHelper.AssertElementVisibility( - BottomPane.response.getResponseTypeSelector("RAW"), - ); + + dataSources.runQueryAndVerifyResponseViews({ + count: 1, + operator: "gte", + responseTypes: ["JSON", "RAW"], + }); + agHelper.ActionContextMenuWithInPane({ action: "Delete", entityType: entityItems.Query, @@ -682,12 +696,13 @@ describe( 7, ); }); - agHelper.AssertElementVisibility( - BottomPane.response.getResponseTypeSelector("JSON"), - ); - agHelper.AssertElementVisibility( - BottomPane.response.getResponseTypeSelector("RAW"), - ); + + dataSources.runQueryAndVerifyResponseViews({ + count: 1, + operator: "gte", + responseTypes: ["JSON", "RAW"], + }); + agHelper.ActionContextMenuWithInPane({ action: "Delete", entityType: entityItems.Query, @@ -719,12 +734,13 @@ describe( JSON.parse(JSON.stringify(resObj.response.body.data.body.values[1])), ).to.eql("51e062189c6ae665454e301d"); }); - agHelper.AssertElementVisibility( - BottomPane.response.getResponseTypeSelector("JSON"), - ); - agHelper.AssertElementVisibility( - BottomPane.response.getResponseTypeSelector("RAW"), - ); + + dataSources.runQueryAndVerifyResponseViews({ + count: 1, + operator: "gte", + responseTypes: ["JSON", "RAW"], + }); + agHelper.ActionContextMenuWithInPane({ action: "Delete", entityType: entityItems.Query, @@ -738,7 +754,12 @@ describe( "Aggregate", ); dataSources.ValidateNSelectDropdown("Command", "Aggregate"); - dataSources.RunQueryNVerifyResponseViews(7, false); + + dataSources.runQueryAndVerifyResponseViews({ + count: 7, + responseTypes: ["JSON", "RAW"], + }); + agHelper.ActionContextMenuWithInPane({ action: "Delete", entityType: entityItems.Query, @@ -899,7 +920,12 @@ describe( "Find", ); dataSources.ValidateNSelectDropdown("Command", "Find document(s)"); - dataSources.RunQueryNVerifyResponseViews(4, false); + + dataSources.runQueryAndVerifyResponseViews({ + count: 4, + responseTypes: ["JSON", "RAW"], + }); + agHelper.ActionContextMenuWithInPane({ action: "Delete", entityType: entityItems.Query, diff --git a/app/client/cypress/e2e/Regression/ServerSide/QueryPane/Querypane_Mongo_Spec.js b/app/client/cypress/e2e/Regression/ServerSide/QueryPane/Querypane_Mongo_Spec.js index f1f7adcb057..dcbd88e431d 100644 --- a/app/client/cypress/e2e/Regression/ServerSide/QueryPane/Querypane_Mongo_Spec.js +++ b/app/client/cypress/e2e/Regression/ServerSide/QueryPane/Querypane_Mongo_Spec.js @@ -71,7 +71,7 @@ describe( dataSources.EnterQuery(`{"find": "listingAndReviews","limit": 10}`); agHelper.FocusElement(locators._codeMirrorTextArea); dataSources.RunQuery(); - BottomPane.response.validateRecordCount(10); + BottomPane.response.validateRecordCount({ count: 10, operator: "lte" }); cy.deleteQueryUsingContext(); }); @@ -93,7 +93,7 @@ describe( fieldValue: "listingAndReviews", }); dataSources.RunQuery(); - BottomPane.response.validateRecordCount(10); + BottomPane.response.validateRecordCount({ count: 10, operator: "lte" }); agHelper.EnterValue("{beds : {$lte: 2}}", { propFieldName: "", @@ -101,7 +101,7 @@ describe( inputFieldName: "Query", }); dataSources.RunQuery(); - BottomPane.response.validateRecordCount(10); + BottomPane.response.validateRecordCount({ count: 10, operator: "lte" }); agHelper.EnterValue("{number_of_reviews: -1}", { propFieldName: "", @@ -109,7 +109,7 @@ describe( inputFieldName: "Sort", }); //sort descending dataSources.RunQuery(); - BottomPane.response.validateRecordCount(10); + BottomPane.response.validateRecordCount({ count: 10, operator: "lte" }); agHelper.EnterValue("{house_rules: 1, description:1}", { propFieldName: "", @@ -131,7 +131,7 @@ describe( "Response is not as expected for Find commmand with multiple conditions", ); }); - BottomPane.response.validateRecordCount(5); + BottomPane.response.validateRecordCount({ count: 5, operator: "lte" }); agHelper.EnterValue("2", { propFieldName: "", @@ -145,7 +145,7 @@ describe( "Response is not as expected for Find commmand with multiple conditions", ); }); - BottomPane.response.validateRecordCount(5); + BottomPane.response.validateRecordCount({ count: 5, operator: "lte" }); cy.deleteQueryUsingContext(); }); @@ -433,7 +433,7 @@ describe( ); dataSources.RunQuery(); - BottomPane.response.validateRecordCount(3); + BottomPane.response.validateRecordCount({ count: 10, operator: "lte" }); dataSources.AssertTableInVirtuosoList(datasourceName, "NonAsciiTest"); diff --git a/app/client/cypress/e2e/Regression/ServerSide/QueryPane/S3_1_spec.js b/app/client/cypress/e2e/Regression/ServerSide/QueryPane/S3_1_spec.js index 2bd496ead58..4cd40d69e78 100644 --- a/app/client/cypress/e2e/Regression/ServerSide/QueryPane/S3_1_spec.js +++ b/app/client/cypress/e2e/Regression/ServerSide/QueryPane/S3_1_spec.js @@ -77,7 +77,11 @@ describe( "List files", ); - dataSources.RunQueryNVerifyResponseViews(100); + dataSources.runQueryAndVerifyResponseViews({ + count: 100, + operator: "gte", + }); + agHelper.ActionContextMenuWithInPane({ action: "Delete", entityType: entityItems.Query, @@ -191,8 +195,7 @@ describe( cy.typeValueNValidate(fileName, formControls.s3ListPrefix); dataSources.RunQuery({ toValidateResponse: false }); - BottomPane.response.switchResponseType("TABLE"); - BottomPane.response.switchResponseType("JSON"); + BottomPane.response.selectResponseResponseTypeFromMenu("JSON"); cy.wait("@postExecute").then(({ response }) => { expect(response.body.data.isExecutionSuccess).to.eq(true); diff --git a/app/client/cypress/e2e/Sanity/Datasources/Arango_Basic_Spec.ts b/app/client/cypress/e2e/Sanity/Datasources/Arango_Basic_Spec.ts index cbf69cdf90b..5fc5d0bc4c0 100644 --- a/app/client/cypress/e2e/Sanity/Datasources/Arango_Basic_Spec.ts +++ b/app/client/cypress/e2e/Sanity/Datasources/Arango_Basic_Spec.ts @@ -219,7 +219,7 @@ if (CURRENT_REPO == REPO.CE) { } INTO ${collectionName}`; dataSources.EnterQuery(query); - dataSources.RunQueryNVerifyResponseViews(); + dataSources.runQueryAndVerifyResponseViews(); dataSources.AssertQueryResponseHeaders([ "writesExecuted", "writesIgnored", @@ -232,7 +232,7 @@ if (CURRENT_REPO == REPO.CE) { FILTER place.type == "Natural" RETURN { country: doc.country, name: place.name }`; dataSources.EnterQuery(query); - dataSources.RunQueryNVerifyResponseViews(5); //Verify all records are filtered + dataSources.runQueryAndVerifyResponseViews({ count: 5 }); //Verify all records are filtered dataSources.AssertQueryTableResponse(0, "Japan"); dataSources.AssertQueryTableResponse(1, "Mount Fuji"); dataSources.AssertQueryTableResponse(6, "Brazil"); //Widget binding is verified here @@ -265,7 +265,7 @@ if (CURRENT_REPO == REPO.CE) { } IN ${collectionName}`; dataSources.EnterQuery(query); - dataSources.RunQueryNVerifyResponseViews(); + dataSources.runQueryAndVerifyResponseViews(); dataSources.createQueryWithDatasourceSchemaTemplate( dsName, @@ -276,7 +276,7 @@ if (CURRENT_REPO == REPO.CE) { FILTER document._key == "1" RETURN document`; dataSources.EnterQuery(query); - dataSources.RunQueryNVerifyResponseViews(1); + dataSources.runQueryAndVerifyResponseViews(); dataSources.AssertQueryTableResponse(3, "Australia"); //Delete record from collection @@ -287,7 +287,7 @@ if (CURRENT_REPO == REPO.CE) { ); query = `REMOVE "1" in ${collectionName}`; dataSources.EnterQuery(query); - dataSources.RunQueryNVerifyResponseViews(1); //Removing Australia + dataSources.runQueryAndVerifyResponseViews(); //Removing Australia //Verify no records return for the deleted key query = `FOR document IN ${collectionName} diff --git a/app/client/cypress/e2e/Sanity/Datasources/MockDBs_Spec.ts b/app/client/cypress/e2e/Sanity/Datasources/MockDBs_Spec.ts index b1cd5d459fc..11df7b8477e 100644 --- a/app/client/cypress/e2e/Sanity/Datasources/MockDBs_Spec.ts +++ b/app/client/cypress/e2e/Sanity/Datasources/MockDBs_Spec.ts @@ -42,7 +42,11 @@ describe( 'SELECT * FROM public."users" LIMIT 10;', ); - dataSources.RunQueryNVerifyResponseViews(); //minimum 1 rows are expected + dataSources.runQueryAndVerifyResponseViews({ + count: 1, + operator: "gte", + }); //minimum 1 rows are expected + AppSidebar.navigate(AppSidebarButton.Data); dataSources .getDatasourceListItemDescription(mockDBName) @@ -57,7 +61,11 @@ describe( expect(interception.request.body.source).to.equal("SELF"); }); - dataSources.RunQueryNVerifyResponseViews(); //minimum 1 rows are expected + dataSources.runQueryAndVerifyResponseViews({ + count: 1, + operator: "gte", + }); //minimum 1 rows are expected + AppSidebar.navigate(AppSidebarButton.Data); dataSources .getDatasourceListItemDescription(mockDBName) @@ -81,6 +89,12 @@ describe( assertHelper.AssertNetworkStatus("@trigger"); dataSources.ValidateNSelectDropdown("Command", "Find document(s)"); + + dataSources.runQueryAndVerifyResponseViews({ + count: 1, + operator: "gte", + responseTypes: ["JSON", "RAW"], + }); }); }); }, diff --git a/app/client/cypress/support/Pages/DataSources.ts b/app/client/cypress/support/Pages/DataSources.ts index 059bf5413b8..4b9329d561e 100644 --- a/app/client/cypress/support/Pages/DataSources.ts +++ b/app/client/cypress/support/Pages/DataSources.ts @@ -1133,6 +1133,7 @@ export class DataSources { this.assertHelper.AssertNetworkStatus("@saveAction", 200); } + /** @deprecated */ public RunQueryNVerifyResponseViews( expectedRecordsCount = 1, tableCheck = true, @@ -1149,7 +1150,35 @@ export class DataSources { BottomPane.response.getResponseTypeSelector("RAW"), ); } - BottomPane.response.validateRecordCount(expectedRecordsCount); + } + + public runQueryAndVerifyResponseViews({ + count = 1, + operator = "eq", + responseTypes = ["TABLE", "JSON", "RAW"], + }: { + count?: number; + operator?: Parameters< + typeof BottomPane.response.validateRecordCount + >[0]["operator"]; + responseTypes?: ("TABLE" | "JSON" | "RAW")[]; + } = {}) { + this.RunQuery(); + + BottomPane.response.openResponseTypeMenu(); + + responseTypes.forEach((responseType) => { + this.agHelper.AssertElementVisibility( + BottomPane.response.locators.responseTypeMenuItem(responseType), + ); + }); + + BottomPane.response.closeResponseTypeMenu(); + + BottomPane.response.validateRecordCount({ + count, + operator, + }); } public CreateDataSource( diff --git a/app/client/cypress/support/Pages/IDE/BottomPane/Response.ts b/app/client/cypress/support/Pages/IDE/BottomPane/Response.ts index ca1db0fa7ca..485559535a7 100644 --- a/app/client/cypress/support/Pages/IDE/BottomPane/Response.ts +++ b/app/client/cypress/support/Pages/IDE/BottomPane/Response.ts @@ -1,21 +1,85 @@ +type ComparisonOperator = "eq" | "gt" | "gte" | "lt" | "lte"; + +interface ValidationParams { + count: number; + operator?: ComparisonOperator; +} + class Response { - private ResponseTab = "//button[@data-testid='t--tab-RESPONSE_TAB']"; + public locators = { + responseTab: "[data-testid='t--tab-RESPONSE_TAB']", + responseDataContainer: "[data-testid='t--query-response-data-container']", + responseTypeMenuTrigger: "[data-testid='t--query-response-type-trigger']", + responseRecordCount: "[data-testid='t--query-response-record-count']", + + /** @deprecated */ + responseType(type: string): string { + return `//div[@data-testid='t--response-tab-segmented-control']//span[text()='${type}']`; + }, + + responseTypeMenuItem(type: string) { + return `[data-testid="t--query-response-type-menu-item"][data-value="${type}"]`; + }, + }; + + /** @deprecated: method will be deleted when segmented control in response pane is replaced */ + public getResponseTypeSelector = this.locators.responseType; + + /** @deprecated: method will be deleted when segmented control in response pane is replaced */ + public switchResponseType(type: string): void { + this.switchToResponseTab(); + cy.xpath(this.locators.responseType(type)).click({ force: true }); + } public switchToResponseTab(): void { - cy.xpath(this.ResponseTab).click({ force: true }); + cy.get(this.locators.responseTab).click({ force: true }); } - public getResponseTypeSelector(type: string): string { - return `//div[@data-testid='t--response-tab-segmented-control']//span[text()='${type}']`; + public openResponseTypeMenu() { + cy.get(this.locators.responseDataContainer).realHover(); + cy.get(this.locators.responseTypeMenuTrigger).click({ force: true }); } - public switchResponseType(type: string): void { + public selectResponseResponseTypeFromMenu(type: string): void { this.switchToResponseTab(); - cy.xpath(this.getResponseTypeSelector(type)).click({ force: true }); + this.openResponseTypeMenu(); + cy.get(this.locators.responseTypeMenuItem(type)).realClick(); } - // TODO: Implement this method when response UI is ready - public validateRecordCount(count: number): void {} + public closeResponseTypeMenu() { + cy.get(this.locators.responseTypeMenuTrigger).realClick(); + } + + public validateRecordCount({ + count, + operator = "eq", + }: ValidationParams): void { + cy.get(this.locators.responseRecordCount) + .invoke("text") + .then((text) => { + const extractedCount = parseInt(text.match(/\d+/)?.[0] || "0", 10); + + switch (operator) { + case "eq": + expect(extractedCount).to.equal(count); + break; + case "gt": + expect(extractedCount).to.be.greaterThan(count); + break; + case "gte": + expect(extractedCount).to.be.at.least(count); + break; + case "lt": + expect(extractedCount).to.be.lessThan(count); + break; + case "lte": + expect(extractedCount).to.be.at.most(count); + break; + default: + throw new Error(`Invalid comparison operator: ${operator}`); + } + }); + } } export { Response }; diff --git a/app/client/packages/design-system/ads/src/Icon/Icon.provider.tsx b/app/client/packages/design-system/ads/src/Icon/Icon.provider.tsx index b4154a27d2d..5a5ff05eb1e 100644 --- a/app/client/packages/design-system/ads/src/Icon/Icon.provider.tsx +++ b/app/client/packages/design-system/ads/src/Icon/Icon.provider.tsx @@ -1072,6 +1072,18 @@ const InputCursorMoveIcon = importSvg( async () => import("../__assets__/icons/ads/input-cursor-move.svg"), ); +const ContentTypeTable = importSvg( + async () => import("../__assets__/icons/ads/content-type-table.svg"), +); + +const ContentTypeJson = importSvg( + async () => import("../__assets__/icons/ads/content-type-json.svg"), +); + +const ContentTypeRaw = importSvg( + async () => import("../__assets__/icons/ads/content-type-raw.svg"), +); + import PlayIconPNG from "../__assets__/icons/control/play-icon.png"; function PlayIconPNGWrapper() { @@ -1085,134 +1097,168 @@ function PlayIconPNGWrapper() { } const ICON_LOOKUP = { - "delete-control": DeleteIcon, - "move-control": MoveIcon, - "edit-control": EditIcon, - "view-control": ViewIcon, - "more-vertical-control": MoreVerticalIcon, - "more-horizontal-control": OverflowMenuIcon, - "js-toggle": JsToggleIcon, - "increase-control": IncreaseIcon, - "decrease-control": DecreaseIcon, - "draggable-control": DraggableIcon, - "drag-handle": DragHandleIcon, - "close-control": CloseIcon, - "close-circle-control": CloseCircleIcon, - "add-circle-control": AddCircleIcon, - "pick-my-location-selected-control": PickMyLocationSelectedIcon, - "settings-control": SettingsIcon, - "help-control": HelpIcon, - "play-video": PlayIconPNGWrapper, - "remove-control": RemoveIcon, - "drag-control": DragIcon, - "collapse-control": CollapseIcon, - "sort-control": SortIcon, - "edit-white": EditWhiteIcon, - "launch-control": LaunchIcon, - "back-control": BackIcon, - "show-column": EyeIcon, - "hide-column": EyeOffIcon, - "delete-column": DeleteColumnIcon, - "bold-font": BoldFontIcon, - underline: UnderlineIcon, - "italics-font": ItalicsFontIcon, - "center-align": CenterAlignIcon, - "left-align": LeftAlignIcon, - "right-align": RightAlignIcon, - "vertical-right": VerticalAlignRight, - "vertical-left": VerticalAlignLeft, - "vertical-top": VerticalAlignTop, - "vertical-bottom": VerticalAlignBottom, - "vertical-center": VerticalAlignCenter, - "copy-control": CopyIcon, - "copy2-control": Copy2Icon, - "cut-control": CutIcon, - "group-control": GroupIcon, - bullets: BulletsIcon, - "divider-cap-right": DividerCapRightIcon, - "divider-cap-left": DividerCapLeftIcon, - "divider-cap-all": DividerCapAllIcon, - "bind-data-control": TrendingFlat, - "icon-align-left": AlignLeftIcon, - "icon-align-right": AlignRightIcon, - "border-radius-sharp": BorderRadiusSharpIcon, - "border-radius-rounded": BorderRadiusRoundedIcon, - "border-radius-circle": BorderRadiusCircleIcon, - "box-shadow-none": BoxShadowNoneIcon, - "box-shadow-variant1": BoxShadowVariant1Icon, - "box-shadow-variant2": BoxShadowVariant2Icon, - "box-shadow-variant3": BoxShadowVariant3Icon, - "box-shadow-variant4": BoxShadowVariant4Icon, - "box-shadow-variant5": BoxShadowVariant5Icon, - "increase-control-v2": IncreaseV2Icon, - question: QuestionIcon, - "column-unfreeze": SubtractIcon, - "heading-one": HeadingOneIcon, - "heading-two": HeadingTwoIcon, - "heading-three": HeadingThreeIcon, - "gift-line": GiftLineIcon, - paragraph: ParagraphIcon, - "paragraph-two": ParagraphTwoIcon, + "account-box-line": AccountBoxLineIcon, "add-box-line": AddBoxLineIcon, + "add-circle-control": AddCircleIcon, + "add-line": AddLineIcon, "add-more": AddMoreIcon, "add-more-fill": AddMoreFillIcon, - "alert-line": AlertLineIcon, "alert-fill": AlertFillIcon, - "info-fill": InfoFillIcon, + "alert-line": AlertLineIcon, + "align-center": AlignCenter, + "align-left": AlignLeft, + "align-right": AlignRight, + "apps-line": AppsLineIcon, "arrow-down-s-fill": ArrowDownSFillIcon, + "arrow-down-s-line": ArrowDownSLineIcon, "arrow-forward": ArrowForwardIcon, "arrow-go-back": ArrowGoBackLineIcon, "arrow-left": ArrowLeft, + "arrow-left-line": ArrowLeftLineIcon, + "arrow-left-s-line": ArrowLeftSLineIcon, + "arrow-right-line": ArrowRightLineIcon, "arrow-right-s-fill": ArrowRightSFillIcon, + "arrow-right-s-line": ArrowRightSLineIcon, "arrow-right-up-line": ArrowRightUpLineIcon, "arrow-up-line": ArrowUpLineIcon, + "arrow-up-s-line": ArrowUpSLineIcon, + "back-control": BackIcon, + "bill-line": BillLineIcon, + "bind-data-control": TrendingFlat, + "binding-new": BindingIcon, + "bold-font": BoldFontIcon, "book-line": BookLineIcon, + "border-radius-circle": BorderRadiusCircleIcon, + "border-radius-rounded": BorderRadiusRoundedIcon, + "border-radius-sharp": BorderRadiusSharpIcon, + "box-3-line": Box3LineIcon, + "box-shadow-none": BoxShadowNoneIcon, + "box-shadow-variant1": BoxShadowVariant1Icon, + "box-shadow-variant2": BoxShadowVariant2Icon, + "box-shadow-variant3": BoxShadowVariant3Icon, + "box-shadow-variant4": BoxShadowVariant4Icon, + "box-shadow-variant5": BoxShadowVariant5Icon, + "braces-line": BracesLineIcon, "bug-line": BugLineIcon, "cap-dot": CapDotIcon, "cap-solid": CapSolidIcon, "card-context-menu": CardContextMenu, + "center-align": CenterAlignIcon, "chat-help": ChatIcon, + "chat-upload-line": ChatUploadLineIcon, "check-line": CheckLineIcon, "chevron-left": ChevronLeft, "chevron-right": ChevronRight, "close-circle": CloseCircleIcon, + "close-circle-control": CloseCircleIcon, + "close-circle-line": CloseCircleLineIcon, + "close-control": CloseIcon, + "close-line": CloseIcon, "close-modal": CloseLineIcon, "close-x": CloseLineIcon, "cloud-off-line": CloudOfflineIcon, + "collapse-control": CollapseIcon, + "column-freeze": ColumnFreeze, + "column-unfreeze": SubtractIcon, "comment-context-menu": CommentContextMenu, "compasses-line": CompassesLine, + "content-type-table": ContentTypeTable, + "content-type-json": ContentTypeJson, + "content-type-raw": ContentTypeRaw, "context-menu": ContextMenuIcon, + "contract-left-line": ContractLeft, + "contract-right-line": ContractRight, + "copy-control": CopyIcon, + "copy2-control": Copy2Icon, + "cut-control": CutIcon, + "dashboard-line": DashboardLineIcon, "database-2-line": Database2Line, + "datasource-v3": DatasourceV3Icon, + "datasources-2": Datasources2, + "decrease-control": DecreaseIcon, + "delete-bin-line": DeleteBinLineIcon, "delete-blank": DeleteBin7, + "delete-column": DeleteColumnIcon, + "delete-control": DeleteIcon, "delete-row": DeleteRowIcon, - "double-arrow-right": DoubleArrowRightIcon, + "divider-cap-all": DividerCapAllIcon, + "divider-cap-left": DividerCapLeftIcon, + "divider-cap-right": DividerCapRightIcon, "double-arrow-left": DoubleArrowLeftIcon, + "double-arrow-right": DoubleArrowRightIcon, "down-arrow": DownArrowIcon, "down-arrow-2": ArrowDownLineIcon, "download-line": DownloadLineIcon, + "drag-control": DragIcon, + "drag-handle": DragHandleIcon, + "draggable-control": DraggableIcon, + "edit-2-line": Edit2LineIcon, "edit-box-line": EditBoxLineIcon, + "edit-control": EditIcon, "edit-line": EditLineIcon, - "edit-2-line": Edit2LineIcon, "edit-underline": EditUnderlineIcon, + "edit-white": EditWhiteIcon, + "editor-v3": EditorV3Icon, + "enter-line": CornerDownLeftLineIcon, "expand-less": ExpandLess, "expand-more": ExpandMore, + "external-link-line": ExternalLinkIcon, "eye-off": EyeOff, "eye-on": EyeOn, + "file-add-line": FileAddLineIcon, + "file-copy-2-line": FileCopy2Line, "file-line": FileLine, "file-list-2-line": FileList2LineIcon, "file-list-line": FileListLineIcon, + "file-paper-2-line": FilePaper2LineIcon, "file-transfer": FileTransfer, - "fork-2": Fork2Icon, + "folder-download-line": FolderDownloadLineIcon, + "folder-line": FolderLineIcon, + "folder-reduce-line": FolderReduceLineIcon, "forbid-line": ForbidLineIcon, + "fork-2": Fork2Icon, + "gift-line": GiftLineIcon, "git-branch": GitBranchLineIcon, "git-commit": GitCommit, "git-pull-request": GitPullRequest, "git-repository": GitRepository, + "github-fill": GithubFillIcon, "global-line": GlobalLineIcon, + "google-colored": GoogleColoredIcon, + "google-fill": GoogleFillIcon, "group-2-line": Group2LineIcon, + "group-control": GroupIcon, "group-line": GroupLineIcon, + "h-line": HLineIcon, + "heading-one": HeadingOneIcon, + "heading-three": HeadingThreeIcon, + "heading-two": HeadingTwoIcon, + "help-control": HelpIcon, + "hide-column": EyeOffIcon, + "home-3-line": Home3LineIcon, + "icon-align-left": AlignLeftIcon, + "icon-align-right": AlignRightIcon, + "increase-control": IncreaseIcon, + "increase-control-v2": IncreaseV2Icon, + "info-fill": InfoFillIcon, + "input-cursor-move": InputCursorMoveIcon, "invite-user": InviteUserIcon, + "italics-font": ItalicsFontIcon, + "js-file": JSFile, + "js-function": JSFunction, + "js-icon-v2": JSIconV2, + "js-square-v3": JsSquareV3Icon, + "js-toggle": JsToggleIcon, + "js-toggle-v2": JsToggleV2, + "js-toggle-v2-bold": JsToggleV2Bold, + "js-yellow": JSYellowIcon, "key-2-line": Key2LineIcon, + "launch-control": LaunchIcon, + "layout-2-line": Layout2LineIcon, + "layout-5-line": Layout5LineIcon, + "layout-column-line": LayoutColumnLineIcon, + "layout-left-2-line": LayoutLeft2LineIcon, + "left-align": LeftAlignIcon, "left-arrow-2": LeftArrowIcon2, "lightbulb-flash-line": LightbulbFlashLine, "line-dashed": LineDashedIcon, @@ -1220,45 +1266,88 @@ const ICON_LOOKUP = { "link-2": Link2, "link-unlink": LinkUnlinkIcon, "links-line": LinksLineIcon, - "external-link-line": ExternalLinkIcon, + "loader-line": LoaderLineIcon, "lock-2-line": Lock2LineIcon, + "lock-fill": LockFillIcon, "lock-password-line": LockPasswordLineIcon, "lock-unlock-line": LockUnlockLineIcon, - "lock-fill": LockFillIcon, "magic-line": MagicLineIcon, "mail-check-line": MailCheckLineIcon, "mail-line": MailLineIcon, + "map-2-line": Map2LineIcon, "map-pin-2-line": MapPin2LineIcon, + "map-pin-5-line": MapPin5LineIcon, + "map-pin-user-line": MapPinUserLineIcon, + "maximize-v3": MaximizeV3Icon, + "menu-fold": MenuFoldLineIcon, + "menu-unfold": MenuUnfoldLineIcon, + "message-2-line": Message2LineIcon, + "minimize-v3": MinimizeV3Icon, + "money-dollar-circle-line": MoneyDollarCircleLineIcon, "more-2-fill": More2FillIcon, + "more-horizontal-control": OverflowMenuIcon, + "more-vertical-control": MoreVerticalIcon, + "move-control": MoveIcon, "news-paper": NewsPaperLine, + "no-action": ForbidTwoLineIcon, "no-response": NoResponseIcon, "oval-check": OvalCheck, "oval-check-fill": OvalCheckFill, + "packages-v3": PackagesV3Icon, + "page-line": PagesLineIcon, + "paragraph-two": ParagraphTwoIcon, "pencil-fill-icon": PencilFillIcon, + "pencil-line": PencilLineIcon, + "pick-my-location-selected-control": PickMyLocationSelectedIcon, "pin-3": Pin3, "play-circle-line": PlayCircleLineIcon, + "play-line": PlayLineIcon, + "play-video": PlayIconPNGWrapper, + "queries-line": QueriesLineIcon, + "queries-v3": QueriesV3Icon, + "query-main": QueryMain, "question-fill": QuestionFillIcon, "question-line": QuestionLineIcon, "question-mark": QuestionMarkIcon, "reaction-2": Reaction2, "read-pin": ReadPin, + "remove-control": RemoveIcon, + "restart-line": RestartLineIcon, + "right-align": RightAlignIcon, "right-arrow": RightArrowIcon, "right-arrow-2": RightArrowIcon2, "search-eye-line": SearchEyeLineIcon, + "search-line": SearchLineIcon, "send-button": SendButton, + "server-line": ServerLineIcon, "settings-2-line": Settings2LineIcon, + "settings-control": SettingsIcon, "settings-line": SettingsLineIcon, + "settings-v3": SettingsV3Icon, "share-2": ShareIcon2, "share-box": ShareBoxFillIcon, "share-box-line": ShareBoxLineIcon, "share-line": ShareLineIcon, + "show-column": EyeIcon, + "show-modal": ShowModalIcon, + "sip-line": SipLineIcon, + "skip-left-line": SkipLeftLineIcon, + "skip-right-line": SkipRightLineIcon, "sort-asc": SortAscIcon, + "sort-control": SortIcon, "sort-desc": SortDescIcon, "star-fill": StarFillIcon, "star-line": StarLineIcon, "subtract-line": SubtractLine, "swap-horizontal": ArrowLeftRightIcon, + "text-bold": BoldIcon, + "text-italic": ItalicIcon, + "text-underline": UnderLineIcon, + "thumb-down-line": ThumbDownLineIcon, + "thumb-up-line": ThumbUpLineIcon, "timer-2-line": Timer2LineIcon, + "timer-flash-line": TimerFlashLineIcon, + "timer-line": TimerLineIcon, "trash-outline": TrashOutline, "trending-flat": TrendingFlat, "unread-pin": UnreadPin, @@ -1274,104 +1363,27 @@ const ICON_LOOKUP = { "user-settings-line": UserSettingsLineIcon, "user-shared-line": UserSharedLineIcon, "user-unfollow-line": UserUnfollowLineIcon, - "view-all": RightArrowIcon, - "view-less": LeftArrowIcon, - "warning-line": WarningLineIcon, - "warning-triangle": WarningTriangleIcon, - "money-dollar-circle-line": MoneyDollarCircleLineIcon, - "js-toggle-v2": JsToggleV2, - "js-toggle-v2-bold": JsToggleV2Bold, - "query-main": QueryMain, - "js-icon-v2": JSIconV2, - "js-file": JSFile, - "js-function": JSFunction, - "datasources-2": Datasources2, - "arrow-left-line": ArrowLeftLineIcon, - "arrow-right-line": ArrowRightLineIcon, - "close-line": CloseIcon, - "close-circle-line": CloseCircleLineIcon, - "arrow-left-s-line": ArrowLeftSLineIcon, - "arrow-right-s-line": ArrowRightSLineIcon, - "arrow-up-s-line": ArrowUpSLineIcon, - "arrow-down-s-line": ArrowDownSLineIcon, - "account-box-line": AccountBoxLineIcon, - "add-line": AddLineIcon, - "search-line": SearchLineIcon, - "loader-line": LoaderLineIcon, - "delete-bin-line": DeleteBinLineIcon, - "align-center": AlignCenter, - "align-left": AlignLeft, - "align-right": AlignRight, - "column-freeze": ColumnFreeze, "vertical-align-bottom": VerticalBottom, "vertical-align-middle": VerticalMiddle, "vertical-align-top": VerticalTop, - "skip-right-line": SkipRightLineIcon, - "skip-left-line": SkipLeftLineIcon, - "contract-left-line": ContractLeft, - "contract-right-line": ContractRight, + "vertical-bottom": VerticalAlignBottom, + "vertical-center": VerticalAlignCenter, + "vertical-left": VerticalAlignLeft, + "vertical-right": VerticalAlignRight, + "vertical-top": VerticalAlignTop, + "view-all": RightArrowIcon, + "view-control": ViewIcon, + "view-less": LeftArrowIcon, "w-line": WLineIcon, - "h-line": HLineIcon, - "file-add-line": FileAddLineIcon, - "layout-2-line": Layout2LineIcon, - "page-line": PagesLineIcon, - "pencil-line": PencilLineIcon, - "server-line": ServerLineIcon, - "layout-column-line": LayoutColumnLineIcon, - "layout-left-2-line": LayoutLeft2LineIcon, - "text-bold": BoldIcon, - "text-italic": ItalicIcon, - "text-underline": UnderLineIcon, - "sip-line": SipLineIcon, - "no-action": ForbidTwoLineIcon, - "message-2-line": Message2LineIcon, - "folder-download-line": FolderDownloadLineIcon, - "restart-line": RestartLineIcon, - "folder-reduce-line": FolderReduceLineIcon, - "timer-flash-line": TimerFlashLineIcon, - "timer-line": TimerLineIcon, - "map-2-line": Map2LineIcon, - "map-pin-user-line": MapPinUserLineIcon, - "map-pin-5-line": MapPin5LineIcon, - "chat-upload-line": ChatUploadLineIcon, - "home-3-line": Home3LineIcon, - "show-modal": ShowModalIcon, - "folder-line": FolderLineIcon, - "google-colored": GoogleColoredIcon, - "google-fill": GoogleFillIcon, - "github-fill": GithubFillIcon, - "enter-line": CornerDownLeftLineIcon, - "play-line": PlayLineIcon, - "thumb-up-line": ThumbUpLineIcon, - "thumb-down-line": ThumbDownLineIcon, - "menu-fold": MenuFoldLineIcon, - "menu-unfold": MenuUnfoldLineIcon, - "layout-5-line": Layout5LineIcon, - "js-yellow": JSYellowIcon, - "binding-new": BindingIcon, - "bill-line": BillLineIcon, - "file-paper-2-line": FilePaper2LineIcon, - "file-copy-2-line": FileCopy2Line, - "box-3-line": Box3LineIcon, - "apps-line": AppsLineIcon, - "queries-line": QueriesLineIcon, - "braces-line": BracesLineIcon, - "dashboard-line": DashboardLineIcon, - "js-square-v3": JsSquareV3Icon, - "queries-v3": QueriesV3Icon, + "warning-line": WarningLineIcon, + "warning-triangle": WarningTriangleIcon, "widgets-v3": WidgetsV3Icon, - "datasource-v3": DatasourceV3Icon, - "editor-v3": EditorV3Icon, - "settings-v3": SettingsV3Icon, - "packages-v3": PackagesV3Icon, - "minimize-v3": MinimizeV3Icon, - "maximize-v3": MaximizeV3Icon, "workflows-mono": WorkflowsMonochromeIcon, - "input-cursor-move": InputCursorMoveIcon, billing: BillingIcon, binding: Binding, book: BookIcon, bug: BugIcon, + bullets: BulletsIcon, cancel: CancelIcon, chat: Chat, close: CloseIcon, @@ -1402,9 +1414,9 @@ const ICON_LOOKUP = { general: GeneralIcon, grid: GridLineIcon, guide: GuideIcon, - history: HistoryLineIcon, hamburger: HamburgerIcon, help: HelpIcon, + history: HistoryLineIcon, info: InfoIcon, js: JsIcon, key: KeyIcon, @@ -1418,14 +1430,17 @@ const ICON_LOOKUP = { member: UserHeartLineIcon, minimize: MinimizeIcon, minus: RemoveIcon, - module: ModuleIcon, mobile: MobileIcon, + module: ModuleIcon, open: OpenIcon, + package: PackageIcon, pantone: PantoneLineIcon, + paragraph: ParagraphIcon, pin: Pin, play: PlayIcon, plus: CreateNewIcon, query: QueryIcon, + question: QuestionIcon, reaction: Reaction, refresh: RefreshLineIcon, rocket: RocketIcon, @@ -1433,8 +1448,9 @@ const ICON_LOOKUP = { search: SearchIcon, setting: SettingIcon, share: ShareForwardIcon, - shine: ShineIcon, shield: Shield, + shine: ShineIcon, + slash: SlashIcon, snippet: Snippet, success: SuccessIcon, support: SupportIcon, @@ -1442,6 +1458,7 @@ const ICON_LOOKUP = { tablet: TabletIcon, tabletLandscape: TabletLandscapeIcon, trash: Trash, + underline: UnderlineIcon, unpin: Unpin, upArrow: UpArrow, upgrade: DvdLineIcon, @@ -1452,8 +1469,6 @@ const ICON_LOOKUP = { widget: WidgetIcon, workflows: WorkflowsIcon, workspace: WorkspaceIcon, - package: PackageIcon, - slash: SlashIcon, }; export const IconCollection = Object.keys(ICON_LOOKUP); diff --git a/app/client/packages/design-system/ads/src/__assets__/icons/ads/content-type-json.svg b/app/client/packages/design-system/ads/src/__assets__/icons/ads/content-type-json.svg new file mode 100644 index 00000000000..0d49b1320d3 --- /dev/null +++ b/app/client/packages/design-system/ads/src/__assets__/icons/ads/content-type-json.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/app/client/packages/design-system/ads/src/__assets__/icons/ads/content-type-raw.svg b/app/client/packages/design-system/ads/src/__assets__/icons/ads/content-type-raw.svg new file mode 100644 index 00000000000..a7c77f9dacd --- /dev/null +++ b/app/client/packages/design-system/ads/src/__assets__/icons/ads/content-type-raw.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/app/client/packages/design-system/ads/src/__assets__/icons/ads/content-type-table.svg b/app/client/packages/design-system/ads/src/__assets__/icons/ads/content-type-table.svg new file mode 100644 index 00000000000..601db25e8a6 --- /dev/null +++ b/app/client/packages/design-system/ads/src/__assets__/icons/ads/content-type-table.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/app/client/src/IDE/Components/BottomView.tsx b/app/client/src/IDE/Components/BottomView.tsx index 1c8b8f87019..5fb01de1d8a 100644 --- a/app/client/src/IDE/Components/BottomView.tsx +++ b/app/client/src/IDE/Components/BottomView.tsx @@ -69,7 +69,7 @@ interface Props { const ViewHideButton = styled(Button)` &.view-hide-button { position: absolute; - top: 3px; + top: 2px; right: 0; padding: 9px 11px; } diff --git a/app/client/src/PluginActionEditor/components/PluginActionResponse/components/BindDataButton.tsx b/app/client/src/PluginActionEditor/components/PluginActionResponse/components/BindDataButton.tsx index 76cf88936ff..dee036bd53e 100644 --- a/app/client/src/PluginActionEditor/components/PluginActionResponse/components/BindDataButton.tsx +++ b/app/client/src/PluginActionEditor/components/PluginActionResponse/components/BindDataButton.tsx @@ -380,7 +380,7 @@ function BindDataButton(props: BindDataButtonProps) { size="sm" startIcon="binding-new" > - Bind Data + Display on UI props.theme.spaces[9]}px; - } +const RunGroup = styled.div` + display: flex; + flex-direction: column; + align-items: center; + gap: 16px; `; interface NoResponseProps { @@ -49,8 +42,8 @@ export const NoResponse = ({ }: NoResponseProps) => ( no-response-yet -
- {EMPTY_RESPONSE_FIRST_HALF()} + + {EMPTY_RESPONSE_RUN()} - {EMPTY_RESPONSE_LAST_HALF()} -
+
); diff --git a/app/client/src/PluginActionEditor/components/PluginActionResponse/components/QueryResponseTab.tsx b/app/client/src/PluginActionEditor/components/PluginActionResponse/components/QueryResponseTab.tsx deleted file mode 100644 index e5880bd5032..00000000000 --- a/app/client/src/PluginActionEditor/components/PluginActionResponse/components/QueryResponseTab.tsx +++ /dev/null @@ -1,356 +0,0 @@ -import React, { useCallback, useState } from "react"; -import { useDispatch, useSelector } from "react-redux"; -import ReactJson from "react-json-view"; -import { - apiReactJsonProps, - ResponseTabErrorContainer, - ResponseTabErrorContent, - ResponseTabErrorDefaultMessage, -} from "./ApiResponse"; -import { ResponseFormatTabs } from "./ResponseFormatTabs"; -import { NoResponse } from "./NoResponse"; -import LogAdditionalInfo from "components/editorComponents/Debugger/ErrorLogs/components/LogAdditionalInfo"; -import LogHelper from "components/editorComponents/Debugger/ErrorLogs/components/LogHelper"; -import LOG_TYPE from "entities/AppsmithConsole/logtype"; -import { JsonWrapper } from "components/editorComponents/Debugger/ErrorLogs/components/LogCollapseData"; -import { - Callout, - Flex, - SegmentedControl, - type CalloutLinkProps, -} from "@appsmith/ads"; -import styled from "styled-components"; -import { DEBUGGER_TAB_KEYS } from "components/editorComponents/Debugger/constants"; -import AnalyticsUtil from "ee/utils/AnalyticsUtil"; -import { setActionResponseDisplayFormat } from "actions/pluginActionActions"; -import { getUpdateTimestamp } from "components/editorComponents/Debugger/ErrorLogs/ErrorLogItem"; -import type { SourceEntity } from "entities/AppsmithConsole"; -import type { Action } from "entities/Action"; -import { getActionData } from "ee/selectors/entitiesSelector"; -import { actionResponseDisplayDataFormats } from "pages/Editor/utils"; -import { getErrorAsString } from "sagas/ActionExecution/errorUtils"; -import { isString } from "lodash"; -import ActionExecutionInProgressView from "components/editorComponents/ActionExecutionInProgressView"; -import { EditorTheme } from "components/editorComponents/CodeEditor/EditorConfig"; -import BindDataButton from "./BindDataButton"; -import { - getPluginActionDebuggerState, - openPluginActionSettings, - setPluginActionEditorSelectedTab, -} from "../../../store"; -import { - createMessage, - PREPARED_STATEMENT_WARNING, -} from "ee/constants/messages"; -import { EDITOR_TABS } from "constants/QueryEditorConstants"; -import { useFeatureFlag } from "utils/hooks/useFeatureFlag"; -import { FEATURE_FLAG } from "ee/entities/FeatureFlag"; - -const HelpSection = styled.div``; - -export const ResponseDataContainer = styled.div` - padding: 0 var(--ads-v2-spaces-7); - padding-top: var(--ads-v2-spaces-4); - display: flex; - flex-direction: column; - gap: var(--ads-v2-spaces-4); - overflow-y: clip; - overflow-x: scroll; -`; - -const ResponseContentWrapper = styled.div<{ isError: boolean }>` - overflow-y: clip; - display: grid; - height: ${(props) => (props.isError ? "" : "100%")}; - - ${HelpSection} { - margin-bottom: 10px; - } - - position: relative; -`; - -interface Props { - actionSource: SourceEntity; - isRunDisabled?: boolean; - isRunning: boolean; - onRunClick: () => void; - currentActionConfig: Action; - runErrorMessage?: string; - actionName: string; -} - -const QueryResponseTab = (props: Props) => { - const { - actionName, - actionSource, - currentActionConfig, - isRunDisabled = false, - isRunning, - onRunClick, - runErrorMessage, - } = props; - const dispatch = useDispatch(); - - const isActionRedesignEnabled = useFeatureFlag( - FEATURE_FLAG.release_actions_redesign_enabled, - ); - - const actionResponse = useSelector((state) => - getActionData(state, currentActionConfig.id), - ); - const { responseTabHeight } = useSelector(getPluginActionDebuggerState); - - const { responseDataTypes, responseDisplayFormat } = - actionResponseDisplayDataFormats(actionResponse); - - let output: Record[] | string = ""; - - const responseBodyTabs = - responseDataTypes && - responseDataTypes.map((dataType, index) => { - return { - index: index, - key: dataType.key, - title: dataType.title, - panelComponent: ( - - ), - }; - }); - - const segmentedControlOptions = - responseBodyTabs && - responseBodyTabs.map((item) => { - return { value: item.key, label: item.title }; - }); - - const [selectedControl, setSelectedControl] = useState( - segmentedControlOptions[0]?.value, - ); - - const responseState = - actionResponse && getUpdateTimestamp(actionResponse.request); - - const selectedTabIndex = - responseDataTypes && - responseDataTypes.findIndex( - (dataType) => dataType.title === responseDisplayFormat?.title, - ); - - const onResponseTabSelect = (tabKey: string) => { - if (tabKey === DEBUGGER_TAB_KEYS.ERROR_TAB) { - AnalyticsUtil.logEvent("OPEN_DEBUGGER", { - source: "QUERY_PANE", - }); - } - - dispatch( - setActionResponseDisplayFormat({ - id: currentActionConfig?.id || "", - field: "responseDisplayFormat", - value: tabKey, - }), - ); - }; - - const responseTabOnRunClick = () => { - onRunClick(); - - AnalyticsUtil.logEvent("RESPONSE_TAB_RUN_ACTION_CLICK", { - source: "QUERY_PANE", - }); - }; - - let error = runErrorMessage; - let hintMessages: Array = []; - let showPreparedStatementWarning = false; - - // Query is executed even once during the session, show the response data. - if (actionResponse) { - if (!actionResponse.isExecutionSuccess) { - // Pass the error to be shown in the error tab - error = actionResponse.readableError - ? getErrorAsString(actionResponse.readableError) - : getErrorAsString(actionResponse.body); - } else if (isString(actionResponse.body)) { - //reset error. - error = ""; - try { - // Try to parse response as JSON array to be displayed in the Response tab - output = JSON.parse(actionResponse.body); - } catch (e) { - // In case the string is not a JSON, wrap it in a response object - output = [ - { - response: actionResponse.body, - }, - ]; - } - } else { - //reset error. - error = ""; - // TODO: Fix this the next time the file is edited - // eslint-disable-next-line @typescript-eslint/no-explicit-any - output = actionResponse.body as any; - } - - if (actionResponse.messages && actionResponse.messages.length) { - //reset error. - error = ""; - hintMessages = actionResponse.messages; - } - - const { actionConfiguration } = currentActionConfig; - const hasPluginSpecifiedTemplates = - actionConfiguration?.pluginSpecifiedTemplates?.[0]?.value === true; - // oracle have different key for prepared statements - const hasPreparedStatement = - actionConfiguration?.formData?.preparedStatement?.data === true; - - if (error && (hasPluginSpecifiedTemplates || hasPreparedStatement)) { - showPreparedStatementWarning = true; - } - } - - const navigateToSettings = useCallback(() => { - if (isActionRedesignEnabled) { - dispatch(openPluginActionSettings(true)); - } else { - dispatch(setPluginActionEditorSelectedTab(EDITOR_TABS.SETTINGS)); - } - }, [dispatch, isActionRedesignEnabled]); - - const preparedStatementCalloutLinks: CalloutLinkProps[] = [ - { - onClick: navigateToSettings, - children: createMessage(PREPARED_STATEMENT_WARNING.LINK), - }, - ]; - - if (isRunning) { - return ( - - ); - } - - return ( - - {showPreparedStatementWarning && ( - - {createMessage(PREPARED_STATEMENT_WARNING.MESSAGE)} - - )} - {error && ( - - - - Your query failed to execute - {actionResponse && - (actionResponse.pluginErrorDetails || actionResponse.body) && - ":"} - - {actionResponse && - (actionResponse.pluginErrorDetails ? ( - <> -
- {actionResponse.pluginErrorDetails.downstreamErrorMessage || - actionResponse.pluginErrorDetails.appsmithErrorMessage} -
- {actionResponse.pluginErrorDetails.downstreamErrorCode && ( - - )} - - ) : ( - actionResponse.body && ( -
{actionResponse.body}
- ) - ))} - -
- {actionResponse && actionResponse.request && ( - e.stopPropagation()} - > - - - )} -
- )} - {hintMessages && hintMessages.length > 0 && ( - - {hintMessages.map((msg, index) => ( - - {msg} - - ))} - - )} - {currentActionConfig && - output && - responseBodyTabs && - responseBodyTabs.length > 0 && - selectedTabIndex !== -1 && ( - - - { - setSelectedControl(value); - onResponseTabSelect(value); - }} - options={segmentedControlOptions} - value={selectedControl} - /> - - - - - )} - {!output && !error && ( - - )} -
- ); -}; - -export default QueryResponseTab; diff --git a/app/client/src/PluginActionEditor/components/PluginActionResponse/components/QueryResponseTab/QueryResponseTab.tsx b/app/client/src/PluginActionEditor/components/PluginActionResponse/components/QueryResponseTab/QueryResponseTab.tsx new file mode 100644 index 00000000000..2f2c5942786 --- /dev/null +++ b/app/client/src/PluginActionEditor/components/PluginActionResponse/components/QueryResponseTab/QueryResponseTab.tsx @@ -0,0 +1,480 @@ +import React, { useCallback, useMemo, useState } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import ReactJson from "react-json-view"; +import { + apiReactJsonProps, + ResponseTabErrorContainer, + ResponseTabErrorContent, + ResponseTabErrorDefaultMessage, +} from "../ApiResponse"; +import { ResponseFormatTabs } from "../ResponseFormatTabs"; +import { NoResponse } from "../NoResponse"; +import LogAdditionalInfo from "components/editorComponents/Debugger/ErrorLogs/components/LogAdditionalInfo"; +import LogHelper from "components/editorComponents/Debugger/ErrorLogs/components/LogHelper"; +import LOG_TYPE from "entities/AppsmithConsole/logtype"; +import { JsonWrapper } from "components/editorComponents/Debugger/ErrorLogs/components/LogCollapseData"; +import { + Callout, + Menu, + MenuContent, + MenuGroup, + MenuGroupName, + MenuItem, + MenuTrigger, + Text, + Tooltip, + type CalloutLinkProps, +} from "@appsmith/ads"; + +import { DEBUGGER_TAB_KEYS } from "components/editorComponents/Debugger/constants"; +import AnalyticsUtil from "ee/utils/AnalyticsUtil"; +import { setActionResponseDisplayFormat } from "actions/pluginActionActions"; +import { getUpdateTimestamp } from "components/editorComponents/Debugger/ErrorLogs/ErrorLogItem"; +import type { SourceEntity } from "entities/AppsmithConsole"; +import type { Action } from "entities/Action"; +import { getActionData } from "ee/selectors/entitiesSelector"; +import { actionResponseDisplayDataFormats } from "pages/Editor/utils"; +import { getErrorAsString } from "sagas/ActionExecution/errorUtils"; +import { isString } from "lodash"; +import ActionExecutionInProgressView from "components/editorComponents/ActionExecutionInProgressView"; +import { EditorTheme } from "components/editorComponents/CodeEditor/EditorConfig"; +import BindDataButton from "../BindDataButton"; +import { + getPluginActionDebuggerState, + openPluginActionSettings, + setPluginActionEditorSelectedTab, +} from "../../../../store"; +import { + createMessage, + PREPARED_STATEMENT_WARNING, +} from "ee/constants/messages"; +import { EDITOR_TABS } from "constants/QueryEditorConstants"; +import { useFeatureFlag } from "utils/hooks/useFeatureFlag"; +import { FEATURE_FLAG } from "ee/entities/FeatureFlag"; + +import * as Styled from "./styles"; +import { useBoolean, useEventCallback } from "usehooks-ts"; +import { RESPONSE_TABLE_HEIGHT_OFFSET } from "./constants"; +import { scrollbarWidth } from "utils/helpers"; + +interface Props { + actionSource: SourceEntity; + isRunDisabled?: boolean; + isRunning: boolean; + onRunClick: () => void; + currentActionConfig: Action; + runErrorMessage?: string; + actionName: string; +} + +export const QueryResponseTab = (props: Props) => { + const { + actionName, + actionSource, + currentActionConfig, + isRunDisabled = false, + isRunning, + onRunClick, + runErrorMessage, + } = props; + + const dispatch = useDispatch(); + const { toggle: toggleContentTypeMenuOpen, value: isContentTypeMenuOpen } = + useBoolean(false); + + const { + setFalse: setIsNotHovered, + setTrue: setIsHovered, + value: isDataContainerHovered, + } = useBoolean(false); + + const isContentTypeSelectorVisible = + isDataContainerHovered || isContentTypeMenuOpen; + + const isActionRedesignEnabled = useFeatureFlag( + FEATURE_FLAG.release_actions_redesign_enabled, + ); + + const actionResponse = useSelector((state) => + getActionData(state, currentActionConfig.id), + ); + const { responseTabHeight } = useSelector(getPluginActionDebuggerState); + + const { responseDataTypes, responseDisplayFormat } = + actionResponseDisplayDataFormats(actionResponse); + + const scrollbarOffset = scrollbarWidth(); + + let output: Record[] | string = ""; + let errorMessage = runErrorMessage; + let hintMessages: Array = []; + let showPreparedStatementWarning = false; + + // Query is executed even once during the session, show the response data. + if (actionResponse) { + if (!actionResponse.isExecutionSuccess) { + // Pass the error to be shown in the error tab + errorMessage = actionResponse.readableError + ? getErrorAsString(actionResponse.readableError) + : getErrorAsString(actionResponse.body); + } else if (isString(actionResponse.body)) { + //reset error. + errorMessage = ""; + try { + // Try to parse response as JSON array to be displayed in the Response tab + output = JSON.parse(actionResponse.body); + } catch (e) { + // In case the string is not a JSON, wrap it in a response object + output = [ + { + response: actionResponse.body, + }, + ]; + } + } else { + //reset error. + errorMessage = ""; + // TODO: Fix this the next time the file is edited + // eslint-disable-next-line @typescript-eslint/no-explicit-any + output = actionResponse.body as any; + } + + if (actionResponse.messages && actionResponse.messages.length) { + //reset error. + errorMessage = ""; + hintMessages = actionResponse.messages; + } + + const { actionConfiguration } = currentActionConfig; + const hasPluginSpecifiedTemplates = + actionConfiguration?.pluginSpecifiedTemplates?.[0]?.value === true; + // oracle have different key for prepared statements + const hasPreparedStatement = + actionConfiguration?.formData?.preparedStatement?.data === true; + + if (errorMessage && (hasPluginSpecifiedTemplates || hasPreparedStatement)) { + showPreparedStatementWarning = true; + } + } + + const recordCount = output?.length ?? 1; + + const responseBodyTabs = + responseDataTypes && + responseDataTypes.map((dataType, index) => { + return { + index: index, + key: dataType.key, + title: dataType.title, + panelComponent: ( + + ), + }; + }); + + const contentTypeOptions = + responseBodyTabs && + responseBodyTabs.map((item) => { + return { value: item.key, label: item.title }; + }); + + const [firstContentTypeOption] = contentTypeOptions; + const [selectedContentType, setSelectedContentType] = useState( + firstContentTypeOption?.value, + ); + + const currentContentType = + selectedContentType || firstContentTypeOption?.value; + + const responseState = + actionResponse && getUpdateTimestamp(actionResponse.request); + + const selectedTabIndex = + responseDataTypes && + responseDataTypes.findIndex( + (dataType) => dataType.title === responseDisplayFormat?.title, + ); + + const onResponseTabSelect = (tabKey: string) => { + if (tabKey === DEBUGGER_TAB_KEYS.ERROR_TAB) { + AnalyticsUtil.logEvent("OPEN_DEBUGGER", { + source: "QUERY_PANE", + }); + } + + dispatch( + setActionResponseDisplayFormat({ + id: currentActionConfig?.id || "", + field: "responseDisplayFormat", + value: tabKey, + }), + ); + }; + + const handleRunClick = useEventCallback(() => { + onRunClick(); + + AnalyticsUtil.logEvent("RESPONSE_TAB_RUN_ACTION_CLICK", { + source: "QUERY_PANE", + }); + }); + + const navigateToSettings = useCallback(() => { + if (isActionRedesignEnabled) { + dispatch(openPluginActionSettings(true)); + } else { + dispatch(setPluginActionEditorSelectedTab(EDITOR_TABS.SETTINGS)); + } + }, [dispatch, isActionRedesignEnabled]); + + const preparedStatementCalloutLinks: CalloutLinkProps[] = useMemo( + () => [ + { + onClick: navigateToSettings, + children: createMessage(PREPARED_STATEMENT_WARNING.LINK), + }, + ], + [navigateToSettings], + ); + + const queryTooltipContent = useMemo(() => { + if (actionResponse) { + const messages = [ + [ + "duration", + "Time to run", + `${(Number(actionResponse.duration) / 1000).toFixed(1)}s`, + ], + ]; + + if (actionResponse.size) { + messages.push([ + "size", + "Response size", + `${(Number(actionResponse.size) / 1000).toFixed(1)}kb`, + ]); + } + + return ( + <> + {messages.map(([key, title, message]) => ( +
{`${title}: ${message}`}
+ ))} + + ); + } + + return null; + }, [actionResponse]); + + const handleContentTypeChange = useEventCallback((e?: Event) => { + if (e?.target && e.target instanceof HTMLElement) { + const { value } = e.target.dataset; + + if (typeof value === "string") { + setSelectedContentType(value); + onResponseTabSelect(value); + } + } + }); + + const handleJsonWrapperClick = useEventCallback( + (e: React.MouseEvent) => e.stopPropagation(), + ); + + if (isRunning) { + return ( + + + + ); + } + + if (!output && !errorMessage) { + return ( + + ); + } + + return ( + + {showPreparedStatementWarning && ( + + {createMessage(PREPARED_STATEMENT_WARNING.MESSAGE)} + + )} + {errorMessage && ( +
+ + + + {`${actionName}.run():`} + + + Error + + + + + + + Your query failed to execute + {actionResponse && + (actionResponse.pluginErrorDetails || actionResponse.body) && + ":"} + + {actionResponse && + (actionResponse.pluginErrorDetails ? ( + <> +
+ {actionResponse.pluginErrorDetails + .downstreamErrorMessage || + actionResponse.pluginErrorDetails.appsmithErrorMessage} +
+ {actionResponse.pluginErrorDetails.downstreamErrorCode && ( + + )} + + ) : ( + actionResponse.body && ( +
+ {actionResponse.body} +
+ ) + ))} + +
+ {actionResponse && actionResponse.request && ( + + + + )} +
+
+ )} + {hintMessages && hintMessages.length > 0 && ( + + {hintMessages.map((msg, index) => ( + + {msg} + + ))} + + )} + + {currentActionConfig && + output && + responseBodyTabs && + responseBodyTabs.length > 0 && + selectedTabIndex !== -1 && ( + + + + + + {`${actionName}.run():`} + + {`${recordCount} record${recordCount > 1 ? "s" : ""}`} + + + + + + + + + + + + {currentContentType} + + + + + View as + + + {contentTypeOptions.map(({ label, value }) => ( + + {label} + + ))} + + + + + )} +
+ ); +}; diff --git a/app/client/src/PluginActionEditor/components/PluginActionResponse/components/QueryResponseTab/constants.ts b/app/client/src/PluginActionEditor/components/PluginActionResponse/components/QueryResponseTab/constants.ts new file mode 100644 index 00000000000..c8093c4039a --- /dev/null +++ b/app/client/src/PluginActionEditor/components/PluginActionResponse/components/QueryResponseTab/constants.ts @@ -0,0 +1,2 @@ +/** Offset needed to make table height match response tab height */ +export const RESPONSE_TABLE_HEIGHT_OFFSET = 14; diff --git a/app/client/src/PluginActionEditor/components/PluginActionResponse/components/QueryResponseTab/index.ts b/app/client/src/PluginActionEditor/components/PluginActionResponse/components/QueryResponseTab/index.ts new file mode 100644 index 00000000000..9b719118de8 --- /dev/null +++ b/app/client/src/PluginActionEditor/components/PluginActionResponse/components/QueryResponseTab/index.ts @@ -0,0 +1 @@ +export { QueryResponseTab as default } from "./QueryResponseTab"; diff --git a/app/client/src/PluginActionEditor/components/PluginActionResponse/components/QueryResponseTab/styles.ts b/app/client/src/PluginActionEditor/components/PluginActionResponse/components/QueryResponseTab/styles.ts new file mode 100644 index 00000000000..a94fde58c5e --- /dev/null +++ b/app/client/src/PluginActionEditor/components/PluginActionResponse/components/QueryResponseTab/styles.ts @@ -0,0 +1,73 @@ +import styled from "styled-components"; +import { Button, Text } from "@appsmith/ads"; +import { TAB_BAR_HEIGHT } from "../constants"; + +export const HelpSection = styled.div``; + +export const Root = styled.div` + display: flex; + flex-direction: column; +`; + +export const DataContainer = styled.div<{ $height: number }>` + height: calc(${({ $height }) => $height}px - 1px); + display: grid; + grid-template-rows: ${TAB_BAR_HEIGHT}px 1fr; + grid-template-columns: 100%; + position: relative; + overflow: clip; +`; + +export const Response = styled.div` + overflow: auto; + width: 100%; + height: 100%; +`; + +export const StatusBar = styled.div` + position: sticky; + top: 0px; + display: flex; + justify-content: space-between; + height: ${TAB_BAR_HEIGHT}px; + padding: 8px; + border-bottom: 1px solid var(--ads-v2-color-border); + z-index: var(--ads-v2-z-index-1); +`; + +export const StatusBarInfo = styled.div` + display: flex; + align-items: center; + gap: var(--ads-v2-spaces-2); +`; + +export const Fab = styled(Button)<{ $isVisible: boolean }>` + && { + position: absolute; + right: 20px; + bottom: calc(${TAB_BAR_HEIGHT}px + 20px); + box-shadow: 0px 1px 20px 0px rgba(76, 86, 100, 0.11); + z-index: var(--ads-v2-z-index-3); + opacity: ${({ $isVisible }) => ($isVisible ? 1 : 0)}; + transition: opacity 0.25s; + } +`; + +export const LoadingContainer = styled.div` + height: calc(100% - ${TAB_BAR_HEIGHT}px); +`; + +interface StatusBarTextProps { + $isBold?: boolean; + $isError?: boolean; + $hasTooltip?: boolean; +} + +export const StatusBarText = styled(Text)` + font-size: 13px; + ${({ $hasTooltip }) => + $hasTooltip && + `text-decoration: underline var(--ads-v2-color-border) dashed;`} + ${({ $isBold }) => $isBold && `font-weight: 700;`} + ${({ $isError }) => $isError && `color: var(--ads-v2-color-fg-on-error);`} +`; diff --git a/app/client/src/PluginActionEditor/components/PluginActionResponse/components/Table.tsx b/app/client/src/PluginActionEditor/components/PluginActionResponse/components/Table.tsx index d8d622476a8..d833e5b43e0 100644 --- a/app/client/src/PluginActionEditor/components/PluginActionResponse/components/Table.tsx +++ b/app/client/src/PluginActionEditor/components/PluginActionResponse/components/Table.tsx @@ -39,12 +39,11 @@ export const TableWrapper = styled.div<{ minColumnWidth?: number }>` width: 100%; height: auto; background: var(--ads-v2-color-bg); - border: 1px solid var(--ads-v2-color-border); box-sizing: border-box; display: flex; justify-content: space-between; flex-direction: column; - overflow: scroll; + overflow: auto; .tableWrap { height: 100%; display: block; @@ -55,7 +54,6 @@ export const TableWrapper = styled.div<{ minColumnWidth?: number }>` border-spacing: 0; color: var(--ads-v2-color-fg); position: relative; - background: var(--ads-v2-color-gray-50); display: table; width: 100%; height: auto; @@ -71,13 +69,7 @@ export const TableWrapper = styled.div<{ minColumnWidth?: number }>` } .tr { overflow: hidden; - border-right: 1px solid var(--ads-v2-color-border); - :nth-child(even) { - background: var(--ads-v2-color-gray-50); - } - :nth-child(odd) { - background: var(--ads-v2-color-bg); - } + border-bottom: 1px solid var(--ads-v2-color-black-75); &.selected-row { background: var(--ads-v2-color-bg-subtle); &:hover { @@ -92,7 +84,6 @@ export const TableWrapper = styled.div<{ minColumnWidth?: number }>` .td { margin: 0; padding: 9px 10px; - border-right: 1px solid var(--ads-v2-color-border); position: relative; font-size: ${TABLE_SIZES.ROW_FONT_SIZE}px; line-height: ${TABLE_SIZES.ROW_FONT_SIZE}px; @@ -255,7 +246,7 @@ function Table(props: TableProps) { const defaultColumn = React.useMemo( () => ({ - width: 170, + minWidth: 170, }), [], ); @@ -343,7 +334,7 @@ function Table(props: TableProps) {
diff --git a/app/client/src/PluginActionEditor/components/PluginActionResponse/components/constants.ts b/app/client/src/PluginActionEditor/components/PluginActionResponse/components/constants.ts new file mode 100644 index 00000000000..be218f4476f --- /dev/null +++ b/app/client/src/PluginActionEditor/components/PluginActionResponse/components/constants.ts @@ -0,0 +1,2 @@ +/** Height for tab bar in px. Required for container height calculations. */ +export const TAB_BAR_HEIGHT = 40; diff --git a/app/client/src/assets/images/no-response.svg b/app/client/src/assets/images/no-response.svg index 6fea7ab0957..89f819dc189 100644 --- a/app/client/src/assets/images/no-response.svg +++ b/app/client/src/assets/images/no-response.svg @@ -1,27 +1,56 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/client/src/ce/constants/messages.ts b/app/client/src/ce/constants/messages.ts index f525c40f0d3..ddf41bdfbdc 100644 --- a/app/client/src/ce/constants/messages.ts +++ b/app/client/src/ce/constants/messages.ts @@ -569,6 +569,7 @@ export const ACTION_CONFIGURATION_UPDATED = () => "Configuration updated"; export const WIDGET_PROPERTIES_UPDATED = () => "Widget properties were updated"; export const EMPTY_RESPONSE_FIRST_HALF = () => "🙌 Click on"; export const EMPTY_RESPONSE_LAST_HALF = () => "to get a response"; +export const EMPTY_RESPONSE_RUN = () => "Click ‘Run’ to get a response"; export const EMPTY_JS_RESPONSE_LAST_HALF = () => "to view response of selected function"; export const INVALID_EMAIL = () => "Please enter a valid email"; diff --git a/app/client/src/components/editorComponents/ActionExecutionInProgressView.tsx b/app/client/src/components/editorComponents/ActionExecutionInProgressView.tsx index 627d1f1de39..1627c0c1f31 100644 --- a/app/client/src/components/editorComponents/ActionExecutionInProgressView.tsx +++ b/app/client/src/components/editorComponents/ActionExecutionInProgressView.tsx @@ -21,13 +21,12 @@ const LoadingOverlayContainer = styled.div` flex-direction: column; justify-content: center; align-items: center; - gap: var(--ads-v2-spaces-3); + gap: var(--ads-v2-spaces-5); background-color: transparent; position: relative; z-index: 20; width: 100%; height: 100%; - margin-top: 5px; `; const LoadingProgressWrapper = styled.div` @@ -56,7 +55,7 @@ const ActionExecutionInProgressView = ({ - + {createMessage(ACTION_EXECUTION_MESSAGE, actionType)} @@ -64,7 +63,7 @@ const ActionExecutionInProgressView = ({ className={`t--cancel-action-button`} kind="secondary" onClick={handleCancelActionExecution} - size="md" + size="sm" > {createMessage(ACTION_EXECUTION_CANCEL)} diff --git a/app/client/src/components/editorComponents/EntityBottomTabs.tsx b/app/client/src/components/editorComponents/EntityBottomTabs.tsx index e649ba41d1a..01f8eeb5da6 100644 --- a/app/client/src/components/editorComponents/EntityBottomTabs.tsx +++ b/app/client/src/components/editorComponents/EntityBottomTabs.tsx @@ -24,8 +24,10 @@ const TabPanelWrapper = styled(TabPanel)` `; const TabsListWrapper = styled(TabsList)` - padding: calc(var(--ads-v2-spaces-1) + 2px) var(--ads-v2-spaces-7) - var(--ads-v2-spaces-1); + && { + padding: var(--ads-v2-spaces-2); + padding-bottom: var(--ads-v2-spaces-1); + } `; export interface BottomTab { diff --git a/app/client/src/components/editorComponents/JSResponseView.test.tsx b/app/client/src/components/editorComponents/JSResponseView.test.tsx index 5d3e177cc17..b873c110660 100644 --- a/app/client/src/components/editorComponents/JSResponseView.test.tsx +++ b/app/client/src/components/editorComponents/JSResponseView.test.tsx @@ -12,7 +12,7 @@ import { EditorViewMode } from "ee/entities/IDE/constants"; import type { JSCollectionData } from "ee/reducers/entityReducers/jsActionsReducer"; import { PluginType } from "entities/Action"; import "@testing-library/jest-dom/extend-expect"; -import { EMPTY_RESPONSE_LAST_HALF } from "ee/constants/messages"; +import { EMPTY_RESPONSE_RUN } from "ee/constants/messages"; import { DEBUGGER_TAB_KEYS } from "./Debugger/constants"; jest.mock("ee/utils/actionExecutionUtils"); @@ -116,7 +116,7 @@ describe("JSResponseView", () => { , ); - expect(getByText(EMPTY_RESPONSE_LAST_HALF())).toBeInTheDocument(); + expect(getByText(EMPTY_RESPONSE_RUN())).toBeInTheDocument(); }); it("should render correctly when isBrowserExecutionAllowed returns false", () => { @@ -144,7 +144,7 @@ describe("JSResponseView", () => { , ); // nothing should be rendered here since the implementation for component is in EE code - expect(queryByText(document.body, EMPTY_RESPONSE_LAST_HALF())).toBeNull(); + expect(queryByText(document.body, EMPTY_RESPONSE_RUN())).toBeNull(); }); it("the container should have class select-text to enable the selection of text for user", () => { diff --git a/app/client/src/pages/Editor/QueryEditor/QueryDebuggerTabs.test.tsx b/app/client/src/pages/Editor/QueryEditor/QueryDebuggerTabs.test.tsx index 047147748af..603fa158132 100644 --- a/app/client/src/pages/Editor/QueryEditor/QueryDebuggerTabs.test.tsx +++ b/app/client/src/pages/Editor/QueryEditor/QueryDebuggerTabs.test.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { render, screen } from "@testing-library/react"; +import { render } from "@testing-library/react"; import configureStore from "redux-mock-store"; import { Provider } from "react-redux"; import { ThemeProvider } from "styled-components"; @@ -10,36 +10,9 @@ import { EditorViewMode } from "ee/entities/IDE/constants"; import "@testing-library/jest-dom/extend-expect"; import QueryDebuggerTabs from "./QueryDebuggerTabs"; import { ENTITY_TYPE } from "ee/entities/AppsmithConsole/utils"; -import type { ActionResponse } from "api/ActionAPI"; const mockStore = configureStore([]); -const mockSuccessResponse: ActionResponse = { - body: ["Record 1", "Record 2"], - statusCode: "200", - dataTypes: [], - duration: "3000", - size: "200", - isExecutionSuccess: true, - headers: { - "Content-Type": ["application/json"], - "Cache-Control": ["no-cache"], - }, -}; - -const mockFailedResponse: ActionResponse = { - body: [{ response: "Failed" }], - statusCode: "200", - dataTypes: [], - duration: "3000", - size: "200", - isExecutionSuccess: false, - headers: { - "Content-Type": ["application/json"], - "Cache-Control": ["no-cache"], - }, -}; - const storeState = { ...unitTestBaseMockStore, evaluations: { @@ -113,59 +86,4 @@ describe("ApiResponseView", () => { ?.classList.contains("select-text"), ).toBe(true); }); - it("should show record count as result if the query response returns records", () => { - render( - - - - {}} - /> - - - , - ); - - const expectedResultText = "Result: 2 Records"; - const resultTextElement = screen.getByTestId("result-text"); - - expect(resultTextElement).toBeInTheDocument(); - expect(resultTextElement?.textContent).toContain(expectedResultText); - }); - - it("should show error as result if the query response returns the error", () => { - render( - - - - {}} - /> - - - , - ); - - const expectedResultText = "Result: Error"; - const resultTextElement = screen.getByTestId("result-text"); - - expect(resultTextElement).toBeInTheDocument(); - expect(resultTextElement?.textContent).toContain(expectedResultText); - }); }); diff --git a/app/client/src/pages/Editor/QueryEditor/QueryDebuggerTabs.tsx b/app/client/src/pages/Editor/QueryEditor/QueryDebuggerTabs.tsx index 9dd4c29ead0..de65f52af95 100644 --- a/app/client/src/pages/Editor/QueryEditor/QueryDebuggerTabs.tsx +++ b/app/client/src/pages/Editor/QueryEditor/QueryDebuggerTabs.tsx @@ -2,9 +2,7 @@ import type { BottomTab } from "components/editorComponents/EntityBottomTabs"; import EntityBottomTabs from "components/editorComponents/EntityBottomTabs"; import React, { useCallback, useEffect, useState } from "react"; import { useDispatch, useSelector } from "react-redux"; -import styled from "styled-components"; import { getErrorCount } from "selectors/debuggerSelectors"; -import { Text, TextType } from "@appsmith/ads-old"; import { DEBUGGER_TAB_KEYS } from "components/editorComponents/Debugger/constants"; import { DEBUGGER_ERRORS, @@ -16,7 +14,6 @@ import DebuggerLogs from "components/editorComponents/Debugger/DebuggerLogs"; import ErrorLogs from "components/editorComponents/Debugger/Errors"; import Schema from "PluginActionEditor/components/PluginActionResponse/components/Schema"; import type { ActionResponse } from "api/ActionAPI"; -import { isString } from "lodash"; import type { SourceEntity } from "entities/AppsmithConsole"; import type { Action } from "entities/Action"; import QueryResponseTab from "PluginActionEditor/components/PluginActionResponse/components/QueryResponseTab"; @@ -37,17 +34,6 @@ import { getIDEViewMode } from "selectors/ideSelectors"; import { EditorViewMode } from "ee/entities/IDE/constants"; import { IDEBottomView, ViewHideBehaviour } from "IDE"; -const ResultsCount = styled.div` - position: absolute; - right: ${(props) => props.theme.spaces[17] + 1}px; - top: 9px; - color: var(--ads-v2-color-fg); -`; - -const ErrorText = styled(Text)` - color: var(--ads-v2-colors-action-error-label-default-fg); -`; - interface QueryDebuggerTabsProps { actionSource: SourceEntity; currentActionConfig?: Action; @@ -71,9 +57,6 @@ function QueryDebuggerTabs({ runErrorMessage, showSchema, }: QueryDebuggerTabsProps) { - // TODO: Fix this the next time the file is edited - // eslint-disable-next-line @typescript-eslint/no-explicit-any - let output: Record[] | null = null; const dispatch = useDispatch(); const { open, responseTabHeight, selectedTab } = useSelector( @@ -172,27 +155,6 @@ function QueryDebuggerTabs({ } }, [currentActionConfig?.id]); - // Query is executed even once during the session, show the response data. - if (actionResponse) { - if (isString(actionResponse.body)) { - try { - // Try to parse response as JSON array to be displayed in the Response tab - output = JSON.parse(actionResponse.body); - } catch (e) { - // In case the string is not a JSON, wrap it in a response object - output = [ - { - response: actionResponse.body, - }, - ]; - } - } else { - // TODO: Fix this the next time the file is edited - // eslint-disable-next-line @typescript-eslint/no-explicit-any - output = actionResponse.body as any; - } - } - const setQueryResponsePaneHeight = useCallback( (height: number) => { dispatch( @@ -276,21 +238,6 @@ function QueryDebuggerTabs({ onHideClick={onToggle} setHeight={setQueryResponsePaneHeight} > - {output && !!output.length && ( - - - Result: - {actionResponse?.isExecutionSuccess ? ( - {` ${output.length} Record${ - output.length > 1 ? "s" : "" - }`} - ) : ( - {" Error"} - )} - - - )} -