Sub tree evaluation (#1841)
Co-authored-by: Trisha Anand <trisha@appsmith.com> Co-authored-by: Piyush Mishra <piyush@codeitout.com> Co-authored-by: Nikhil Nandagopal <nikhil@appsmith.com> Co-authored-by: Akash N <akash@codemonk.in>
This commit is contained in:
parent
bb81d3501a
commit
4387200262
|
|
@ -32,12 +32,13 @@
|
|||
"react": {
|
||||
"pragma": "React",
|
||||
// Tells eslint-plugin-react to automatically detect the version of React to use
|
||||
"version": "detect"
|
||||
"version": "detect"
|
||||
}
|
||||
},
|
||||
"env": {
|
||||
"browser": true,
|
||||
"node": true,
|
||||
"cypress/globals": true
|
||||
"cypress/globals": true,
|
||||
"worker": true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -126,14 +126,12 @@
|
|||
"label": "",
|
||||
"options": [
|
||||
{
|
||||
"id": "1",
|
||||
"label": "Male",
|
||||
"value": "M"
|
||||
"label":"Male",
|
||||
"value":"M"
|
||||
},
|
||||
{
|
||||
"id": "2",
|
||||
"label": "Female",
|
||||
"value": "F"
|
||||
"label":"Female",
|
||||
"value":"F"
|
||||
}
|
||||
],
|
||||
"defaultOptionValue": "1",
|
||||
|
|
|
|||
|
|
@ -87,90 +87,6 @@
|
|||
"userName": "Tobias Funke",
|
||||
"productName": "Beef steak",
|
||||
"orderAmount": 19.99
|
||||
},
|
||||
{
|
||||
"id": 7434532,
|
||||
"email": "byron.fields@reqres.in",
|
||||
"userName": "Byron Fields",
|
||||
"productName": "Chicken Sandwich",
|
||||
"orderAmount": 4.99
|
||||
},
|
||||
{
|
||||
"id": 7434532,
|
||||
"email": "ryan.holmes@reqres.in",
|
||||
"userName": "Ryan Holmes",
|
||||
"productName": "Avocado Panini",
|
||||
"orderAmount": 7.99
|
||||
},
|
||||
{
|
||||
"id": 2381224,
|
||||
"email": "michael.lawson@reqres.in",
|
||||
"userName": "Michael Lawson",
|
||||
"productName": "Chicken Sandwich",
|
||||
"orderAmount": 4.99
|
||||
},
|
||||
{
|
||||
"id": 2736212,
|
||||
"email": "lindsay.ferguson@reqres.in",
|
||||
"userName": "Lindsay Ferguson",
|
||||
"productName": "Tuna Salad",
|
||||
"orderAmount": 9.99
|
||||
},
|
||||
{
|
||||
"id": 6788734,
|
||||
"email": "tobias.funke@reqres.in",
|
||||
"userName": "Tobias Funke",
|
||||
"productName": "Beef steak",
|
||||
"orderAmount": 19.99
|
||||
},
|
||||
{
|
||||
"id": 7434532,
|
||||
"email": "byron.fields@reqres.in",
|
||||
"userName": "Byron Fields",
|
||||
"productName": "Chicken Sandwich",
|
||||
"orderAmount": 4.99
|
||||
},
|
||||
{
|
||||
"id": 7434532,
|
||||
"email": "ryan.holmes@reqres.in",
|
||||
"userName": "Ryan Holmes",
|
||||
"productName": "Avocado Panini",
|
||||
"orderAmount": 7.99
|
||||
},
|
||||
{
|
||||
"id": 2381224,
|
||||
"email": "michael.lawson@reqres.in",
|
||||
"userName": "Michael Lawson",
|
||||
"productName": "Chicken Sandwich",
|
||||
"orderAmount": 4.99
|
||||
},
|
||||
{
|
||||
"id": 2736212,
|
||||
"email": "lindsay.ferguson@reqres.in",
|
||||
"userName": "Lindsay Ferguson",
|
||||
"productName": "Tuna Salad",
|
||||
"orderAmount": 9.99
|
||||
},
|
||||
{
|
||||
"id": 6788734,
|
||||
"email": "tobias.funke@reqres.in",
|
||||
"userName": "Tobias Funke",
|
||||
"productName": "Beef steak",
|
||||
"orderAmount": 19.99
|
||||
},
|
||||
{
|
||||
"id": 7434532,
|
||||
"email": "byron.fields@reqres.in",
|
||||
"userName": "Byron Fields",
|
||||
"productName": "Chicken Sandwich",
|
||||
"orderAmount": 4.99
|
||||
},
|
||||
{
|
||||
"id": 7434532,
|
||||
"email": "ryan.holmes@reqres.in",
|
||||
"userName": "Ryan Holmes",
|
||||
"productName": "Avocado Panini",
|
||||
"orderAmount": 7.99
|
||||
}
|
||||
],
|
||||
"addInputWidgetBinding": "{{Table1.selectedRow.id",
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ describe("Binding the Table and input Widget", function() {
|
|||
.type("2736212", { force: true });
|
||||
cy.get(commonlocators.editPropCrossButton).click();
|
||||
cy.wait("@updateLayout").isSelectRow(0);
|
||||
cy.readTabledataPublish("0", "0").then(tabData => {
|
||||
cy.readTabledataPublish("0", "0").then((tabData) => {
|
||||
const tabValue = tabData;
|
||||
expect(tabValue).to.be.equal("2736212");
|
||||
cy.log("the value is" + tabValue);
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ describe("Create org and a new app / delete and recreate app", function() {
|
|||
|
||||
it("create app within an org and delete and re-create another app with same name", function() {
|
||||
cy.NavigateToHome();
|
||||
cy.generateUUID().then(uid => {
|
||||
cy.generateUUID().then((uid) => {
|
||||
orgid = uid;
|
||||
appid = uid;
|
||||
localStorage.setItem("OrgName", orgid);
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ describe("Create new org and an app within the same", function() {
|
|||
|
||||
it("create multiple apps and validate", function() {
|
||||
cy.NavigateToHome();
|
||||
cy.generateUUID().then(uid => {
|
||||
cy.generateUUID().then((uid) => {
|
||||
orgid = uid;
|
||||
appid = uid;
|
||||
localStorage.setItem("OrgName", orgid);
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@
|
|||
|
||||
const homePage = require("../../../locators/HomePage.json");
|
||||
|
||||
describe("Create new org and share with a user", function () {
|
||||
describe("Create new org and share with a user", function() {
|
||||
let orgid;
|
||||
let appid;
|
||||
|
||||
it("create org and then share with a user from UI", function () {
|
||||
it("create org and then share with a user from UI", function() {
|
||||
cy.NavigateToHome();
|
||||
cy.generateUUID().then(uid => {
|
||||
cy.generateUUID().then((uid) => {
|
||||
orgid = uid;
|
||||
appid = uid;
|
||||
localStorage.setItem("OrgName", orgid);
|
||||
|
|
@ -25,7 +25,7 @@ describe("Create new org and share with a user", function () {
|
|||
cy.LogOut();
|
||||
});
|
||||
|
||||
it("login as invited user and then validate viewer privilage", function () {
|
||||
it("login as invited user and then validate viewer privilage", function() {
|
||||
cy.LogintoApp(Cypress.env("TESTUSERNAME1"), Cypress.env("TESTPASSWORD1"));
|
||||
cy.get(homePage.searchInput).type(appid);
|
||||
cy.wait(2000);
|
||||
|
|
@ -39,7 +39,7 @@ describe("Create new org and share with a user", function () {
|
|||
cy.LogOut();
|
||||
});
|
||||
|
||||
it("login as Org owner and update the invited user role to developer", function () {
|
||||
it("login as Org owner and update the invited user role to developer", function() {
|
||||
cy.LoginFromAPI(Cypress.env("USERNAME"), Cypress.env("PASSWORD"));
|
||||
cy.visit("/applications");
|
||||
cy.wait("@applications").should(
|
||||
|
|
@ -58,7 +58,7 @@ describe("Create new org and share with a user", function () {
|
|||
cy.LogOut();
|
||||
});
|
||||
|
||||
it("login as invited user and then validate developer privilage", function () {
|
||||
it("login as invited user and then validate developer privilage", function() {
|
||||
cy.LogintoApp(Cypress.env("TESTUSERNAME1"), Cypress.env("TESTPASSWORD1"));
|
||||
cy.get(homePage.searchInput).type(appid);
|
||||
cy.wait(2000);
|
||||
|
|
@ -76,7 +76,7 @@ describe("Create new org and share with a user", function () {
|
|||
cy.LogOut();
|
||||
});
|
||||
|
||||
it("login as Org owner and update the invited user role to administrator", function () {
|
||||
it("login as Org owner and update the invited user role to administrator", function() {
|
||||
cy.LoginFromAPI(Cypress.env("USERNAME"), Cypress.env("PASSWORD"));
|
||||
cy.visit("/applications");
|
||||
cy.wait("@applications").should(
|
||||
|
|
@ -95,7 +95,7 @@ describe("Create new org and share with a user", function () {
|
|||
cy.LogOut();
|
||||
});
|
||||
|
||||
it("login as invited user and then validate administrator privilage", function () {
|
||||
it("login as invited user and then validate administrator privilage", function() {
|
||||
cy.LogintoApp(Cypress.env("TESTUSERNAME1"), Cypress.env("TESTPASSWORD1"));
|
||||
cy.get(homePage.searchInput).type(appid);
|
||||
cy.wait(2000);
|
||||
|
|
@ -108,7 +108,7 @@ describe("Create new org and share with a user", function () {
|
|||
cy.LogOut();
|
||||
});
|
||||
|
||||
it("login as Org owner and delete App ", function () {
|
||||
it("login as Org owner and delete App ", function() {
|
||||
cy.LoginFromAPI(Cypress.env("USERNAME"), Cypress.env("PASSWORD"));
|
||||
cy.visit("/applications");
|
||||
cy.wait("@applications").should(
|
||||
|
|
@ -119,7 +119,7 @@ describe("Create new org and share with a user", function () {
|
|||
cy.get(homePage.searchInput).type(appid);
|
||||
cy.wait(2000);
|
||||
cy.navigateToOrgSettings(orgid);
|
||||
cy.get(homePage.emailList).then(function ($list) {
|
||||
cy.get(homePage.emailList).then(function($list) {
|
||||
expect($list).to.have.length(3);
|
||||
expect($list.eq(0)).to.contain(Cypress.env("USERNAME"));
|
||||
expect($list.eq(1)).to.contain(Cypress.env("TESTUSERNAME1"));
|
||||
|
|
|
|||
|
|
@ -1,19 +1,15 @@
|
|||
const homePage = require("../../../locators/HomePage.json");
|
||||
|
||||
|
||||
describe("Duplicate an application must duplicate every API ,Query widget and Datasource", function() {
|
||||
it("Duplicating an application", function()
|
||||
{
|
||||
// Navigate to home Page
|
||||
// Click on any application action icon (Three dots)
|
||||
// Click on "Duplicate" option
|
||||
// Ensure the application gets copied
|
||||
// Click on "Appsmith" to navigate to homepage
|
||||
// Click on action icon
|
||||
// Click on Delete option
|
||||
// Click on "Are You Sure?" option
|
||||
// Ensure the App gets deleted
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
describe("Duplicate an application must duplicate every API ,Query widget and Datasource", function() {
|
||||
it("Duplicating an application", function() {
|
||||
// Navigate to home Page
|
||||
// Click on any application action icon (Three dots)
|
||||
// Click on "Duplicate" option
|
||||
// Ensure the application gets copied
|
||||
// Click on "Appsmith" to navigate to homepage
|
||||
// Click on action icon
|
||||
// Click on Delete option
|
||||
// Click on "Are You Sure?" option
|
||||
// Ensure the App gets deleted
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,15 +1,11 @@
|
|||
const homePage = require("../../../locators/HomePage.json");
|
||||
|
||||
|
||||
describe("Duplicate an application must duplicate every API ,Query widget and Datasource", function() {
|
||||
it("Duplicating an application", function()
|
||||
{
|
||||
// Navigate to home Page
|
||||
// Click on any application action icon (Three dots)
|
||||
// Click on "Duplicate" option
|
||||
// Ensure the application gets copied
|
||||
// Ensure the name is appended with the word "Copy"
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
describe("Duplicate an application must duplicate every API ,Query widget and Datasource", function() {
|
||||
it("Duplicating an application", function() {
|
||||
// Navigate to home Page
|
||||
// Click on any application action icon (Three dots)
|
||||
// Click on "Duplicate" option
|
||||
// Ensure the application gets copied
|
||||
// Ensure the name is appended with the word "Copy"
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,28 +1,22 @@
|
|||
const homePage = require("../../../locators/HomePage.json");
|
||||
|
||||
|
||||
describe("Duplicate an application must duplicate every API ,Query widget and Datasource", function() {
|
||||
it("Duplicating an application", function()
|
||||
{
|
||||
// Navigate to home Page
|
||||
// Click on any application action icon (Three dots)
|
||||
// Click on "Duplicate" option
|
||||
// Ensure the application gets copied
|
||||
// Ensure the name is appended with the word "Copy"
|
||||
}
|
||||
)
|
||||
it("Deleting the duplicated Application ", function()
|
||||
{
|
||||
// Navigate to home Page
|
||||
// Click on any application action icon (Three dots)
|
||||
// Click on "Duplicate" option
|
||||
// Ensure the application gets copied
|
||||
// Click on "Appsmith" to navigate to homepage
|
||||
// Click on action icon
|
||||
// Click on Delete option
|
||||
// Click on "Are You Sure?" option
|
||||
// Ensure the App gets deleted
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
describe("Duplicate an application must duplicate every API ,Query widget and Datasource", function() {
|
||||
it("Duplicating an application", function() {
|
||||
// Navigate to home Page
|
||||
// Click on any application action icon (Three dots)
|
||||
// Click on "Duplicate" option
|
||||
// Ensure the application gets copied
|
||||
// Ensure the name is appended with the word "Copy"
|
||||
});
|
||||
it("Deleting the duplicated Application ", function() {
|
||||
// Navigate to home Page
|
||||
// Click on any application action icon (Three dots)
|
||||
// Click on "Duplicate" option
|
||||
// Ensure the application gets copied
|
||||
// Click on "Appsmith" to navigate to homepage
|
||||
// Click on action icon
|
||||
// Click on Delete option
|
||||
// Click on "Are You Sure?" option
|
||||
// Ensure the App gets deleted
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,15 +1,11 @@
|
|||
const homePage = require("../../../locators/HomePage.json");
|
||||
|
||||
|
||||
describe("Checking for error message on Organisation Name ", function() {
|
||||
it("Ensure of Inactive Submit button ", function()
|
||||
{
|
||||
// Navigate to home Page
|
||||
// Click on Create Organisation
|
||||
// Type "Space" as first character
|
||||
// Ensure "Submit" button does not get Active
|
||||
// Now click on "X" (Close icon) ensure the pop up closes
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
describe("Checking for error message on Organisation Name ", function() {
|
||||
it("Ensure of Inactive Submit button ", function() {
|
||||
// Navigate to home Page
|
||||
// Click on Create Organisation
|
||||
// Type "Space" as first character
|
||||
// Ensure "Submit" button does not get Active
|
||||
// Now click on "X" (Close icon) ensure the pop up closes
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,36 +1,28 @@
|
|||
const homePage = require("../../../locators/HomePage.json");
|
||||
|
||||
|
||||
describe("Checking for error message on Organisation Name ", function() {
|
||||
it("Ensure of Inactive Submit button ", function()
|
||||
{
|
||||
// Navigate to home Page
|
||||
// Click on Create Organisation
|
||||
// Type "Space" as first character
|
||||
// Ensure "Submit" button does not get Active
|
||||
// Now click on "X" (Close icon) ensure the pop up closes
|
||||
}
|
||||
)
|
||||
it("Reuse the name of the deleted application name ", function()
|
||||
{
|
||||
// Navigate to home Page
|
||||
// Create an Application by name "XYZ"
|
||||
// Add some widgets
|
||||
// Navigate back to the application
|
||||
// Delete the Application
|
||||
// Click on "Create New" option under samee organisation
|
||||
// Enter the name "XYZ"
|
||||
// Ensure the application can be created with the same name
|
||||
}
|
||||
)
|
||||
it("Adding Special Character ", function()
|
||||
{
|
||||
// Navigate to home Page
|
||||
// Click on Create Organisation
|
||||
// Add special as first character
|
||||
// Ensure "Submit" get Active
|
||||
// Now click outside and ensure the pop up closes
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
describe("Checking for error message on Organisation Name ", function() {
|
||||
it("Ensure of Inactive Submit button ", function() {
|
||||
// Navigate to home Page
|
||||
// Click on Create Organisation
|
||||
// Type "Space" as first character
|
||||
// Ensure "Submit" button does not get Active
|
||||
// Now click on "X" (Close icon) ensure the pop up closes
|
||||
});
|
||||
it("Reuse the name of the deleted application name ", function() {
|
||||
// Navigate to home Page
|
||||
// Create an Application by name "XYZ"
|
||||
// Add some widgets
|
||||
// Navigate back to the application
|
||||
// Delete the Application
|
||||
// Click on "Create New" option under samee organisation
|
||||
// Enter the name "XYZ"
|
||||
// Ensure the application can be created with the same name
|
||||
});
|
||||
it("Adding Special Character ", function() {
|
||||
// Navigate to home Page
|
||||
// Click on Create Organisation
|
||||
// Add special as first character
|
||||
// Ensure "Submit" get Active
|
||||
// Now click outside and ensure the pop up closes
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,18 +1,14 @@
|
|||
const homePage = require("../../../locators/HomePage.json");
|
||||
|
||||
|
||||
describe("Reuse the name of the deleted application name inside the same organisation", function() {
|
||||
it("Reuse the name of the deleted application name ", function()
|
||||
{
|
||||
// Navigate to home Page
|
||||
// Create an Application by name "XYZ"
|
||||
// Add some widgets
|
||||
// Navigate back to the application
|
||||
// Delete the Application
|
||||
// Click on "Create New" option under samee organisation
|
||||
// Enter the name "XYZ"
|
||||
// Ensure the application can be created with the same name
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
describe("Reuse the name of the deleted application name inside the same organisation", function() {
|
||||
it("Reuse the name of the deleted application name ", function() {
|
||||
// Navigate to home Page
|
||||
// Create an Application by name "XYZ"
|
||||
// Add some widgets
|
||||
// Navigate back to the application
|
||||
// Delete the Application
|
||||
// Click on "Create New" option under samee organisation
|
||||
// Enter the name "XYZ"
|
||||
// Ensure the application can be created with the same name
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,15 +1,11 @@
|
|||
const homePage = require("../../../locators/HomePage.json");
|
||||
|
||||
|
||||
describe("Adding Special Character ", function() {
|
||||
it("Adding Special Character ", function()
|
||||
{
|
||||
// Navigate to home Page
|
||||
// Click on Create Organisation
|
||||
// Add special as first character
|
||||
// Ensure "Submit" get Active
|
||||
// Now click outside and ensure the pop up closes
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
describe("Adding Special Character ", function() {
|
||||
it("Adding Special Character ", function() {
|
||||
// Navigate to home Page
|
||||
// Click on Create Organisation
|
||||
// Add special as first character
|
||||
// Ensure "Submit" get Active
|
||||
// Now click outside and ensure the pop up closes
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ const explorer = require("../locators/explorerlocators.json");
|
|||
|
||||
let pageidcopy = " ";
|
||||
|
||||
Cypress.Commands.add("createOrg", orgName => {
|
||||
Cypress.Commands.add("createOrg", (orgName) => {
|
||||
cy.get(homePage.createOrg)
|
||||
.should("be.visible")
|
||||
.first()
|
||||
|
|
@ -45,7 +45,7 @@ Cypress.Commands.add(
|
|||
},
|
||||
);
|
||||
|
||||
Cypress.Commands.add("navigateToOrgSettings", orgName => {
|
||||
Cypress.Commands.add("navigateToOrgSettings", (orgName) => {
|
||||
cy.get(homePage.orgList.concat(orgName).concat(")"))
|
||||
.scrollIntoView()
|
||||
.should("be.visible");
|
||||
|
|
@ -207,7 +207,7 @@ Cypress.Commands.add("updateUserRoleForOrg", (orgName, email, role) => {
|
|||
);
|
||||
});
|
||||
|
||||
Cypress.Commands.add("launchApp", appName => {
|
||||
Cypress.Commands.add("launchApp", (appName) => {
|
||||
cy.get(homePage.appView)
|
||||
.should("be.visible")
|
||||
.first()
|
||||
|
|
@ -239,7 +239,7 @@ Cypress.Commands.add("CreateAppForOrg", (orgName, appname) => {
|
|||
);
|
||||
});
|
||||
|
||||
Cypress.Commands.add("CreateApp", appname => {
|
||||
Cypress.Commands.add("CreateApp", (appname) => {
|
||||
cy.get(homePage.createNew)
|
||||
.first()
|
||||
.click({ force: true });
|
||||
|
|
@ -259,7 +259,7 @@ Cypress.Commands.add("CreateApp", appname => {
|
|||
);
|
||||
});
|
||||
|
||||
Cypress.Commands.add("DeleteApp", appName => {
|
||||
Cypress.Commands.add("DeleteApp", (appName) => {
|
||||
cy.get(commonlocators.homeIcon).click({ force: true });
|
||||
cy.wait("@applications").should(
|
||||
"have.nested.property",
|
||||
|
|
@ -312,13 +312,13 @@ Cypress.Commands.add("LoginFromAPI", (uname, pword) => {
|
|||
username: uname,
|
||||
password: pword,
|
||||
},
|
||||
}).then(response => {
|
||||
}).then((response) => {
|
||||
expect(response.status).equal(302);
|
||||
cy.log(response.body);
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add("DeleteApp", appName => {
|
||||
Cypress.Commands.add("DeleteApp", (appName) => {
|
||||
cy.get(commonlocators.homeIcon).click({ force: true });
|
||||
cy.get(homePage.searchInput).type(appName);
|
||||
cy.wait(2000);
|
||||
|
|
@ -360,7 +360,7 @@ Cypress.Commands.add("NavigateToHome", () => {
|
|||
);
|
||||
});
|
||||
|
||||
Cypress.Commands.add("NavigateToWidgets", pageName => {
|
||||
Cypress.Commands.add("NavigateToWidgets", (pageName) => {
|
||||
cy.get(pages.pagesIcon).click({ force: true });
|
||||
cy.get(".t--page-sidebar-" + pageName + "")
|
||||
.find(">div")
|
||||
|
|
@ -371,7 +371,7 @@ Cypress.Commands.add("NavigateToWidgets", pageName => {
|
|||
cy.get("#loading").should("not.exist");
|
||||
});
|
||||
|
||||
Cypress.Commands.add("SearchApp", appname => {
|
||||
Cypress.Commands.add("SearchApp", (appname) => {
|
||||
cy.get(homePage.searchInput).type(appname);
|
||||
cy.wait(2000);
|
||||
cy.get(homePage.applicationCard)
|
||||
|
|
@ -398,7 +398,7 @@ Cypress.Commands.add("SearchEntity", (apiname1, apiname2) => {
|
|||
).should("not.be.visible");
|
||||
});
|
||||
|
||||
Cypress.Commands.add("GlobalSearchEntity", apiname1 => {
|
||||
Cypress.Commands.add("GlobalSearchEntity", (apiname1) => {
|
||||
cy.get(commonlocators.entityExplorersearch).should("be.visible");
|
||||
cy.get(commonlocators.entityExplorersearch)
|
||||
.clear()
|
||||
|
|
@ -409,12 +409,12 @@ Cypress.Commands.add("GlobalSearchEntity", apiname1 => {
|
|||
).should("be.visible");
|
||||
});
|
||||
|
||||
Cypress.Commands.add("ResponseStatusCheck", statusCode => {
|
||||
Cypress.Commands.add("ResponseStatusCheck", (statusCode) => {
|
||||
cy.xpath(apiwidget.responseStatus).should("be.visible");
|
||||
cy.xpath(apiwidget.responseStatus).contains(statusCode);
|
||||
});
|
||||
|
||||
Cypress.Commands.add("ResponseCheck", textTocheck => {
|
||||
Cypress.Commands.add("ResponseCheck", (textTocheck) => {
|
||||
//Explicit assert
|
||||
cy.get(apiwidget.responseText).should("be.visible");
|
||||
});
|
||||
|
|
@ -433,7 +433,7 @@ Cypress.Commands.add("NavigateToEntityExplorer", () => {
|
|||
cy.get("#loading").should("not.exist");
|
||||
});
|
||||
|
||||
Cypress.Commands.add("CreateAPI", apiname => {
|
||||
Cypress.Commands.add("CreateAPI", (apiname) => {
|
||||
cy.get(apiwidget.createapi).click({ force: true });
|
||||
cy.wait("@createNewApi");
|
||||
cy.get(apiwidget.resourceUrl).should("be.visible");
|
||||
|
|
@ -449,7 +449,7 @@ Cypress.Commands.add("CreateAPI", apiname => {
|
|||
cy.wait(2000);
|
||||
});
|
||||
|
||||
Cypress.Commands.add("CreateSubsequentAPI", apiname => {
|
||||
Cypress.Commands.add("CreateSubsequentAPI", (apiname) => {
|
||||
cy.get(apiwidget.createApiOnSideBar)
|
||||
.first()
|
||||
.click({ force: true });
|
||||
|
|
@ -462,7 +462,7 @@ Cypress.Commands.add("CreateSubsequentAPI", apiname => {
|
|||
cy.WaitAutoSave();
|
||||
});
|
||||
|
||||
Cypress.Commands.add("EditApiName", apiname => {
|
||||
Cypress.Commands.add("EditApiName", (apiname) => {
|
||||
cy.get(apiwidget.ApiName).click({ force: true });
|
||||
cy.get(apiwidget.apiTxt)
|
||||
.clear()
|
||||
|
|
@ -471,7 +471,7 @@ Cypress.Commands.add("EditApiName", apiname => {
|
|||
cy.WaitAutoSave();
|
||||
});
|
||||
|
||||
Cypress.Commands.add("EditApiNameFromExplorer", apiname => {
|
||||
Cypress.Commands.add("EditApiNameFromExplorer", (apiname) => {
|
||||
cy.xpath(apiwidget.popover)
|
||||
.last()
|
||||
.click({ force: true });
|
||||
|
|
@ -530,7 +530,7 @@ Cypress.Commands.add("validateRequest", (baseurl, path, verb) => {
|
|||
.click({ force: true });
|
||||
});
|
||||
|
||||
Cypress.Commands.add("SelectAction", action => {
|
||||
Cypress.Commands.add("SelectAction", (action) => {
|
||||
cy.get(ApiEditor.ApiVerb)
|
||||
.first()
|
||||
.click({ force: true });
|
||||
|
|
@ -565,7 +565,7 @@ Cypress.Commands.add(
|
|||
},
|
||||
);
|
||||
|
||||
Cypress.Commands.add("SearchEntityandOpen", apiname1 => {
|
||||
Cypress.Commands.add("SearchEntityandOpen", (apiname1) => {
|
||||
cy.get(commonlocators.entityExplorersearch).should("be.visible");
|
||||
cy.get(commonlocators.entityExplorersearch)
|
||||
.clear()
|
||||
|
|
@ -595,7 +595,7 @@ Cypress.Commands.add("enterDatasourceAndPath", (datasource, path) => {
|
|||
.type(path, { parseSpecialCharSequences: false });
|
||||
});
|
||||
|
||||
Cypress.Commands.add("changeZoomLevel", zoomValue => {
|
||||
Cypress.Commands.add("changeZoomLevel", (zoomValue) => {
|
||||
cy.get(commonlocators.changeZoomlevel).click();
|
||||
cy.get("ul.bp3-menu")
|
||||
.children()
|
||||
|
|
@ -609,7 +609,7 @@ Cypress.Commands.add("changeZoomLevel", zoomValue => {
|
|||
cy.get(commonlocators.selectedZoomlevel)
|
||||
.first()
|
||||
.invoke("text")
|
||||
.then(text => {
|
||||
.then((text) => {
|
||||
const someText = text;
|
||||
expect(someText).to.equal(zoomValue);
|
||||
});
|
||||
|
|
@ -663,14 +663,14 @@ Cypress.Commands.add("switchToAPIInputTab", () => {
|
|||
.click({ force: true });
|
||||
});
|
||||
|
||||
Cypress.Commands.add("selectPaginationType", option => {
|
||||
Cypress.Commands.add("selectPaginationType", (option) => {
|
||||
cy.get(apiwidget.paginationOption)
|
||||
.first()
|
||||
.click({ force: true });
|
||||
cy.xpath(option).click({ force: true });
|
||||
});
|
||||
|
||||
Cypress.Commands.add("clickTest", testbutton => {
|
||||
Cypress.Commands.add("clickTest", (testbutton) => {
|
||||
cy.wait(2000);
|
||||
cy.wait("@saveAction");
|
||||
cy.get(testbutton)
|
||||
|
|
@ -726,7 +726,7 @@ Cypress.Commands.add(
|
|||
},
|
||||
);
|
||||
|
||||
Cypress.Commands.add("CreationOfUniqueAPIcheck", apiname => {
|
||||
Cypress.Commands.add("CreationOfUniqueAPIcheck", (apiname) => {
|
||||
cy.get(pages.addEntityAPI).click();
|
||||
cy.get(apiwidget.createapi).click({ force: true });
|
||||
cy.wait("@createNewApi");
|
||||
|
|
@ -738,13 +738,13 @@ Cypress.Commands.add("CreationOfUniqueAPIcheck", apiname => {
|
|||
.type(apiname, { force: true })
|
||||
.should("have.value", apiname)
|
||||
.focus();
|
||||
cy.get(".bp3-popover-content").should($x => {
|
||||
cy.get(".bp3-popover-content").should(($x) => {
|
||||
console.log($x);
|
||||
expect($x).contain(apiname.concat(" is already being used."));
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add("MoveAPIToHome", apiname => {
|
||||
Cypress.Commands.add("MoveAPIToHome", (apiname) => {
|
||||
cy.xpath(apiwidget.popover)
|
||||
.last()
|
||||
.click({ force: true });
|
||||
|
|
@ -757,7 +757,7 @@ Cypress.Commands.add("MoveAPIToHome", apiname => {
|
|||
);
|
||||
});
|
||||
|
||||
Cypress.Commands.add("MoveAPIToPage", pageName => {
|
||||
Cypress.Commands.add("MoveAPIToPage", (pageName) => {
|
||||
cy.xpath(apiwidget.popover)
|
||||
.last()
|
||||
.click({ force: true });
|
||||
|
|
@ -772,7 +772,7 @@ Cypress.Commands.add("MoveAPIToPage", pageName => {
|
|||
);
|
||||
});
|
||||
|
||||
Cypress.Commands.add("copyEntityToPage", pageName => {
|
||||
Cypress.Commands.add("copyEntityToPage", (pageName) => {
|
||||
cy.xpath(apiwidget.popover)
|
||||
.last()
|
||||
.click({ force: true });
|
||||
|
|
@ -825,7 +825,7 @@ Cypress.Commands.add("deleteEntity", () => {
|
|||
cy.get(apiwidget.delete).click({ force: true });
|
||||
});
|
||||
|
||||
Cypress.Commands.add("DeleteAPI", apiname => {
|
||||
Cypress.Commands.add("DeleteAPI", (apiname) => {
|
||||
cy.get(apiwidget.deleteAPI)
|
||||
.first()
|
||||
.click({ force: true });
|
||||
|
|
@ -896,14 +896,14 @@ Cypress.Commands.add("createModal", (modalType, ModalName) => {
|
|||
cy.get(".bp3-overlay-backdrop").click({ force: true });
|
||||
});
|
||||
|
||||
Cypress.Commands.add("CheckWidgetProperties", checkboxCss => {
|
||||
Cypress.Commands.add("CheckWidgetProperties", (checkboxCss) => {
|
||||
cy.get(checkboxCss).check({
|
||||
force: true,
|
||||
});
|
||||
cy.assertPageSave();
|
||||
});
|
||||
|
||||
Cypress.Commands.add("UncheckWidgetProperties", checkboxCss => {
|
||||
Cypress.Commands.add("UncheckWidgetProperties", (checkboxCss) => {
|
||||
cy.get(checkboxCss).uncheck({
|
||||
force: true,
|
||||
});
|
||||
|
|
@ -933,13 +933,13 @@ Cypress.Commands.add("widgetText", (text, inputcss, innercss) => {
|
|||
cy.get(innercss).should("have.text", text);
|
||||
});
|
||||
|
||||
Cypress.Commands.add("EvaluateDataType", dataType => {
|
||||
Cypress.Commands.add("EvaluateDataType", (dataType) => {
|
||||
cy.get(commonlocators.evaluatedType)
|
||||
.should("be.visible")
|
||||
.contains(dataType);
|
||||
});
|
||||
|
||||
Cypress.Commands.add("EvaluateCurrentValue", currentValue => {
|
||||
Cypress.Commands.add("EvaluateCurrentValue", (currentValue) => {
|
||||
cy.wait(2000);
|
||||
cy.get(commonlocators.evaluatedCurrentValue)
|
||||
.should("be.visible")
|
||||
|
|
@ -954,8 +954,8 @@ Cypress.Commands.add("PublishtheApp", () => {
|
|||
cy.assertPageSave();
|
||||
|
||||
// Stubbing window.open to open in the same tab
|
||||
cy.window().then(window => {
|
||||
cy.stub(window, "open").callsFake(url => {
|
||||
cy.window().then((window) => {
|
||||
cy.stub(window, "open").callsFake((url) => {
|
||||
window.location.href = Cypress.config().baseUrl + url.substring(1);
|
||||
window.location.target = "_self";
|
||||
});
|
||||
|
|
@ -976,12 +976,12 @@ Cypress.Commands.add("getCodeMirror", () => {
|
|||
.type("{ctrl}{shift}{downarrow}");
|
||||
});
|
||||
|
||||
Cypress.Commands.add("testCodeMirror", value => {
|
||||
Cypress.Commands.add("testCodeMirror", (value) => {
|
||||
cy.get(".CodeMirror textarea")
|
||||
.first()
|
||||
.focus()
|
||||
.type("{ctrl}{shift}{downarrow}")
|
||||
.then($cm => {
|
||||
.then(($cm) => {
|
||||
if ($cm.val() !== "") {
|
||||
cy.get(".CodeMirror textarea")
|
||||
.first()
|
||||
|
|
@ -1009,7 +1009,7 @@ Cypress.Commands.add("testJsontext", (endp, value) => {
|
|||
.focus({ force: true })
|
||||
.type("{uparrow}", { force: true })
|
||||
.type("{ctrl}{shift}{downarrow}", { force: true });
|
||||
cy.focused().then($cm => {
|
||||
cy.focused().then(($cm) => {
|
||||
if ($cm.contents != "") {
|
||||
cy.log("The field is empty");
|
||||
cy.get(".t--property-control-" + endp + " .CodeMirror textarea")
|
||||
|
|
@ -1028,14 +1028,14 @@ Cypress.Commands.add("testJsontext", (endp, value) => {
|
|||
cy.wait(200);
|
||||
});
|
||||
|
||||
Cypress.Commands.add("selectShowMsg", value => {
|
||||
Cypress.Commands.add("selectShowMsg", (value) => {
|
||||
cy.get(commonlocators.chooseAction)
|
||||
.children()
|
||||
.contains("Show Message")
|
||||
.click();
|
||||
});
|
||||
|
||||
Cypress.Commands.add("addSuccessMessage", value => {
|
||||
Cypress.Commands.add("addSuccessMessage", (value) => {
|
||||
cy.get(commonlocators.chooseMsgType)
|
||||
.last()
|
||||
.click();
|
||||
|
|
@ -1052,12 +1052,12 @@ Cypress.Commands.add("SetDateToToday", () => {
|
|||
cy.assertPageSave();
|
||||
});
|
||||
|
||||
Cypress.Commands.add("enterActionValue", value => {
|
||||
Cypress.Commands.add("enterActionValue", (value) => {
|
||||
cy.get(".CodeMirror textarea")
|
||||
.last()
|
||||
.focus()
|
||||
.type("{ctrl}{shift}{downarrow}")
|
||||
.then($cm => {
|
||||
.then(($cm) => {
|
||||
if ($cm.val() !== "") {
|
||||
cy.get(".CodeMirror textarea")
|
||||
.last()
|
||||
|
|
@ -1079,7 +1079,7 @@ Cypress.Commands.add("enterActionValue", value => {
|
|||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add("enterNavigatePageName", value => {
|
||||
Cypress.Commands.add("enterNavigatePageName", (value) => {
|
||||
cy.get("ul.tree")
|
||||
.children()
|
||||
.first()
|
||||
|
|
@ -1088,7 +1088,7 @@ Cypress.Commands.add("enterNavigatePageName", value => {
|
|||
.first()
|
||||
.focus()
|
||||
.type("{ctrl}{shift}{downarrow}")
|
||||
.then($cm => {
|
||||
.then(($cm) => {
|
||||
if ($cm.val() !== "") {
|
||||
cy.get(".CodeMirror textarea")
|
||||
.first()
|
||||
|
|
@ -1125,7 +1125,7 @@ Cypress.Commands.add("DeleteModal", () => {
|
|||
.click({ force: true });
|
||||
});
|
||||
|
||||
Cypress.Commands.add("Createpage", Pagename => {
|
||||
Cypress.Commands.add("Createpage", (Pagename) => {
|
||||
cy.get(pages.pagesIcon).click({ force: true });
|
||||
cy.get(pages.AddPage)
|
||||
.first()
|
||||
|
|
@ -1148,7 +1148,7 @@ Cypress.Commands.add("Createpage", Pagename => {
|
|||
cy.wait(2000);
|
||||
});
|
||||
|
||||
Cypress.Commands.add("Deletepage", Pagename => {
|
||||
Cypress.Commands.add("Deletepage", (Pagename) => {
|
||||
cy.get(pages.pagesIcon).click({ force: true });
|
||||
cy.get(".t--page-sidebar-" + Pagename + "");
|
||||
cy.get(
|
||||
|
|
@ -1167,11 +1167,11 @@ Cypress.Commands.add("generateUUID", () => {
|
|||
return id.split("-")[0];
|
||||
});
|
||||
|
||||
Cypress.Commands.add("addDsl", dsl => {
|
||||
Cypress.Commands.add("addDsl", (dsl) => {
|
||||
let currentURL;
|
||||
let pageid;
|
||||
let layoutId;
|
||||
cy.url().then(url => {
|
||||
cy.url().then((url) => {
|
||||
currentURL = url;
|
||||
const myRegexp = /pages(.*)/;
|
||||
const match = myRegexp.exec(currentURL);
|
||||
|
|
@ -1180,7 +1180,7 @@ Cypress.Commands.add("addDsl", dsl => {
|
|||
cy.log(pageid + "page id");
|
||||
//Fetch the layout id
|
||||
cy.server();
|
||||
cy.request("GET", "api/v1/pages/" + pageid).then(response => {
|
||||
cy.request("GET", "api/v1/pages/" + pageid).then((response) => {
|
||||
const len = JSON.stringify(response.body);
|
||||
cy.log(len);
|
||||
layoutId = JSON.parse(len).data.layouts[0].id;
|
||||
|
|
@ -1189,7 +1189,7 @@ Cypress.Commands.add("addDsl", dsl => {
|
|||
"PUT",
|
||||
"api/v1/layouts/" + layoutId + "/pages/" + pageid,
|
||||
dsl,
|
||||
).then(response => {
|
||||
).then((response) => {
|
||||
expect(response.status).equal(200);
|
||||
cy.reload();
|
||||
});
|
||||
|
|
@ -1200,7 +1200,7 @@ Cypress.Commands.add("addDsl", dsl => {
|
|||
Cypress.Commands.add("DeleteAppByApi", () => {
|
||||
let currentURL;
|
||||
let appId;
|
||||
cy.url().then(url => {
|
||||
cy.url().then((url) => {
|
||||
currentURL = url;
|
||||
const myRegexp = /applications(.*)/;
|
||||
const match = myRegexp.exec(currentURL);
|
||||
|
|
@ -1212,7 +1212,7 @@ Cypress.Commands.add("DeleteAppByApi", () => {
|
|||
}
|
||||
});
|
||||
});
|
||||
Cypress.Commands.add("togglebar", value => {
|
||||
Cypress.Commands.add("togglebar", (value) => {
|
||||
cy.get(value)
|
||||
.check({ force: true })
|
||||
.should("be.checked");
|
||||
|
|
@ -1229,7 +1229,7 @@ Cypress.Commands.add("optionValue", (value, value2) => {
|
|||
.clear()
|
||||
.type(value2);
|
||||
});
|
||||
Cypress.Commands.add("dropdownDynamic", text => {
|
||||
Cypress.Commands.add("dropdownDynamic", (text) => {
|
||||
cy.wait(2000);
|
||||
cy.get("ul[class='bp3-menu']")
|
||||
.first()
|
||||
|
|
@ -1238,7 +1238,7 @@ Cypress.Commands.add("dropdownDynamic", text => {
|
|||
.should("have.text", text);
|
||||
});
|
||||
|
||||
Cypress.Commands.add("getAlert", alertcss => {
|
||||
Cypress.Commands.add("getAlert", (alertcss) => {
|
||||
cy.get(commonlocators.dropdownSelectButton).click({ force: true });
|
||||
cy.get(widgetsPage.menubar)
|
||||
.contains("Show Alert")
|
||||
|
|
@ -1268,18 +1268,18 @@ Cypress.Commands.add("tabVerify", (index, text) => {
|
|||
.should("be.visible");
|
||||
});
|
||||
|
||||
Cypress.Commands.add("togglebar", value => {
|
||||
Cypress.Commands.add("togglebar", (value) => {
|
||||
cy.get(value)
|
||||
.check({ force: true })
|
||||
.should("be.checked");
|
||||
});
|
||||
Cypress.Commands.add("togglebarDisable", value => {
|
||||
Cypress.Commands.add("togglebarDisable", (value) => {
|
||||
cy.get(value)
|
||||
.uncheck({ force: true })
|
||||
.should("not.checked");
|
||||
});
|
||||
|
||||
Cypress.Commands.add("getAlert", alertcss => {
|
||||
Cypress.Commands.add("getAlert", (alertcss) => {
|
||||
cy.get(commonlocators.dropdownSelectButton).click({ force: true });
|
||||
cy.get(widgetsPage.menubar)
|
||||
.contains("Show Message")
|
||||
|
|
@ -1473,7 +1473,7 @@ Cypress.Commands.add("createPostgresDatasource", () => {
|
|||
cy.testSaveDatasource();
|
||||
});
|
||||
|
||||
Cypress.Commands.add("deleteDatasource", datasourceName => {
|
||||
Cypress.Commands.add("deleteDatasource", (datasourceName) => {
|
||||
cy.NavigateToQueryEditor();
|
||||
|
||||
cy.contains(".t--datasource-name", datasourceName)
|
||||
|
|
@ -1553,7 +1553,7 @@ Cypress.Commands.add("dragAndDropToCanvas", (widgetType, { x, y }) => {
|
|||
.trigger("mouseup", { force: true });
|
||||
});
|
||||
|
||||
Cypress.Commands.add("executeDbQuery", queryName => {
|
||||
Cypress.Commands.add("executeDbQuery", (queryName) => {
|
||||
cy.get(widgetsPage.buttonOnClick)
|
||||
.get(commonlocators.dropdownSelectButton)
|
||||
.click({ force: true })
|
||||
|
|
@ -1567,7 +1567,7 @@ Cypress.Commands.add("executeDbQuery", queryName => {
|
|||
.click({ force: true });
|
||||
});
|
||||
|
||||
Cypress.Commands.add("openPropertyPane", widgetType => {
|
||||
Cypress.Commands.add("openPropertyPane", (widgetType) => {
|
||||
const selector = `.t--draggable-${widgetType}`;
|
||||
cy.get(selector)
|
||||
.first()
|
||||
|
|
@ -1585,13 +1585,13 @@ Cypress.Commands.add("closePropertyPane", () => {
|
|||
Cypress.Commands.add("createAndFillApi", (url, parameters) => {
|
||||
cy.NavigateToApiEditor();
|
||||
cy.testCreateApiButton();
|
||||
cy.get("@createNewApi").then(response => {
|
||||
cy.get("@createNewApi").then((response) => {
|
||||
cy.get(ApiEditor.ApiNameField).should("be.visible");
|
||||
cy.expect(response.response.body.responseMeta.success).to.eq(true);
|
||||
cy.get(ApiEditor.ApiNameField)
|
||||
.click()
|
||||
.invoke("text")
|
||||
.then(text => {
|
||||
.then((text) => {
|
||||
const someText = text;
|
||||
expect(someText).to.equal(response.response.body.data.name);
|
||||
});
|
||||
|
|
@ -1612,7 +1612,7 @@ Cypress.Commands.add("createAndFillApi", (url, parameters) => {
|
|||
cy.get(ApiEditor.ApiRunBtn).should("not.be.disabled");
|
||||
});
|
||||
|
||||
Cypress.Commands.add("isSelectRow", index => {
|
||||
Cypress.Commands.add("isSelectRow", (index) => {
|
||||
cy.get(
|
||||
'.tbody .td[data-rowindex="' + index + '"][data-colindex="' + 0 + '"]',
|
||||
).click({ force: true });
|
||||
|
|
@ -1640,13 +1640,13 @@ Cypress.Commands.add("setDate", (date, dateFormate) => {
|
|||
cy.get(sel).click();
|
||||
});
|
||||
|
||||
Cypress.Commands.add("pageNo", index => {
|
||||
Cypress.Commands.add("pageNo", (index) => {
|
||||
cy.get(".page-item")
|
||||
.first()
|
||||
.click({ force: true });
|
||||
});
|
||||
|
||||
Cypress.Commands.add("pageNoValidate", index => {
|
||||
Cypress.Commands.add("pageNoValidate", (index) => {
|
||||
const data = '.e-numericcontainer a[index="' + index + '"]';
|
||||
const pageVal = cy.get(data);
|
||||
return pageVal;
|
||||
|
|
@ -1661,7 +1661,7 @@ Cypress.Commands.add("validateEnableWidget", (widgetCss, disableCss) => {
|
|||
});
|
||||
|
||||
Cypress.Commands.add("validateHTMLText", (widgetCss, htmlTag, value) => {
|
||||
cy.get(widgetCss + " iframe").then($iframe => {
|
||||
cy.get(widgetCss + " iframe").then(($iframe) => {
|
||||
const $body = $iframe.contents().find("body");
|
||||
cy.wrap($body)
|
||||
.find(htmlTag)
|
||||
|
|
@ -1747,7 +1747,7 @@ Cypress.Commands.add("startServerAndRoutes", () => {
|
|||
cy.route("DELETE", "/api/v1/organizations/*/logo").as("deleteLogo");
|
||||
});
|
||||
|
||||
Cypress.Commands.add("alertValidate", text => {
|
||||
Cypress.Commands.add("alertValidate", (text) => {
|
||||
cy.get(commonlocators.success)
|
||||
.should("be.visible")
|
||||
.and("have.text", text);
|
||||
|
|
@ -1777,7 +1777,7 @@ Cypress.Commands.add("scrollTabledataPublish", (rowNum, colNum) => {
|
|||
return tabVal;
|
||||
});
|
||||
|
||||
Cypress.Commands.add("assertEvaluatedValuePopup", expectedType => {
|
||||
Cypress.Commands.add("assertEvaluatedValuePopup", (expectedType) => {
|
||||
cy.get(dynamicInputLocators.evaluatedValue)
|
||||
.should("be.visible")
|
||||
.children("p")
|
||||
|
|
@ -1787,7 +1787,7 @@ Cypress.Commands.add("assertEvaluatedValuePopup", expectedType => {
|
|||
.should("have.text", expectedType);
|
||||
});
|
||||
|
||||
Cypress.Commands.add("validateToastMessage", value => {
|
||||
Cypress.Commands.add("validateToastMessage", (value) => {
|
||||
cy.get(commonlocators.toastMsg).should("have.text", value);
|
||||
});
|
||||
|
||||
|
|
@ -1802,23 +1802,23 @@ Cypress.Commands.add("NavigateToPaginationTab", () => {
|
|||
.type("{enter}");
|
||||
});
|
||||
|
||||
Cypress.Commands.add("ValidateTableData", value => {
|
||||
Cypress.Commands.add("ValidateTableData", (value) => {
|
||||
// cy.isSelectRow(0);
|
||||
cy.readTabledata("0", "0").then(tabData => {
|
||||
cy.readTabledata("0", "0").then((tabData) => {
|
||||
const tableData = tabData;
|
||||
expect(tableData).to.equal(value.toString());
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add("ValidatePublishTableData", value => {
|
||||
Cypress.Commands.add("ValidatePublishTableData", (value) => {
|
||||
cy.isSelectRow(0);
|
||||
cy.readTabledataPublish("0", "0").then(tabData => {
|
||||
cy.readTabledataPublish("0", "0").then((tabData) => {
|
||||
const tableData = tabData;
|
||||
expect(tableData).to.equal(value);
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add("ValidatePaginateResponseUrlData", runTestCss => {
|
||||
Cypress.Commands.add("ValidatePaginateResponseUrlData", (runTestCss) => {
|
||||
cy.SearchEntityandOpen("Api2");
|
||||
cy.NavigateToPaginationTab();
|
||||
cy.RunAPI();
|
||||
|
|
@ -1832,7 +1832,7 @@ Cypress.Commands.add("ValidatePaginateResponseUrlData", runTestCss => {
|
|||
.contains("name")
|
||||
.siblings("span")
|
||||
.invoke("text")
|
||||
.then(tabData => {
|
||||
.then((tabData) => {
|
||||
const respBody = tabData.match(/"(.*)"/)[0];
|
||||
localStorage.setItem("respBody", respBody);
|
||||
cy.log(respBody);
|
||||
|
|
@ -1840,7 +1840,7 @@ Cypress.Commands.add("ValidatePaginateResponseUrlData", runTestCss => {
|
|||
// cy.openPropertyPane("tablewidget");
|
||||
// cy.testJsontext("tabledata", "{{Api2.data.results}}");
|
||||
cy.isSelectRow(0);
|
||||
cy.readTabledata("0", "1").then(tabData => {
|
||||
cy.readTabledata("0", "1").then((tabData) => {
|
||||
const tableData = tabData;
|
||||
expect(`\"${tableData}\"`).to.equal(respBody);
|
||||
});
|
||||
|
|
@ -1849,13 +1849,13 @@ Cypress.Commands.add("ValidatePaginateResponseUrlData", runTestCss => {
|
|||
|
||||
Cypress.Commands.add("ValidatePaginationInputData", () => {
|
||||
cy.isSelectRow(0);
|
||||
cy.readTabledataPublish("0", "1").then(tabData => {
|
||||
cy.readTabledataPublish("0", "1").then((tabData) => {
|
||||
const tableData = tabData;
|
||||
expect(`\"${tableData}\"`).to.equal(localStorage.getItem("respBody"));
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add("callApi", apiname => {
|
||||
Cypress.Commands.add("callApi", (apiname) => {
|
||||
cy.get(commonlocators.callApi)
|
||||
.first()
|
||||
.click();
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@
|
|||
"copy-to-clipboard": "^3.3.1",
|
||||
"craco-alias": "^2.1.1",
|
||||
"cypress-log-to-output": "^1.1.2",
|
||||
"deep-diff": "^1.0.2",
|
||||
"downloadjs": "^1.4.7",
|
||||
"eslint": "^7.11.0",
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
|
|
@ -166,6 +167,7 @@
|
|||
"@testing-library/jest-dom": "^5.11.4",
|
||||
"@testing-library/react": "^11.0.4",
|
||||
"@types/codemirror": "^0.0.96",
|
||||
"@types/deep-diff": "^1.0.0",
|
||||
"@types/downloadjs": "^1.4.2",
|
||||
"@types/jest": "^24.0.22",
|
||||
"@types/react-beautiful-dnd": "^11.0.4",
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ export interface ActionApiResponse {
|
|||
}
|
||||
|
||||
export interface ActionResponse {
|
||||
body: Record<string, unknown>;
|
||||
body: unknown;
|
||||
headers: Record<string, string[]>;
|
||||
request?: ActionApiResponseReq;
|
||||
statusCode: string;
|
||||
|
|
|
|||
|
|
@ -50,7 +50,6 @@ export const ReduxActionTypes: { [key: string]: string } = {
|
|||
WIDGET_MOVE: "WIDGET_MOVE",
|
||||
WIDGET_RESIZE: "WIDGET_RESIZE",
|
||||
WIDGET_DELETE: "WIDGET_DELETE",
|
||||
WIDGETS_LOADING: "WIDGETS_LOADING",
|
||||
SHOW_PROPERTY_PANE: "SHOW_PROPERTY_PANE",
|
||||
UPDATE_WIDGET_PROPERTY_REQUEST: "UPDATE_WIDGET_PROPERTY_REQUEST",
|
||||
UPDATE_WIDGET_PROPERTY: "UPDATE_WIDGET_PROPERTY",
|
||||
|
|
@ -307,11 +306,13 @@ export const ReduxActionTypes: { [key: string]: string } = {
|
|||
CUT_SELECTED_WIDGET: "CUT_SELECTED_WIDGET",
|
||||
WIDGET_ADD_CHILDREN: "WIDGET_ADD_CHILDREN",
|
||||
SET_EVALUATED_TREE: "SET_EVALUATED_TREE",
|
||||
SET_EVALUATION_DEPENDENCY_MAP: "SET_EVALUATION_DEPENDENCY_MAP",
|
||||
BATCH_UPDATES_SUCCESS: "BATCH_UPDATES_SUCCESS",
|
||||
UPDATE_CANVAS_STRUCTURE: "UPDATE_CANVAS_STRUCTURE",
|
||||
SET_SELECTED_WIDGET_ANCESTORY: "SET_SELECTED_WIDGET_ANCESTORY",
|
||||
START_EVALUATION: "START_EVALUATION",
|
||||
CURRENT_APPLICATION_NAME_UPDATE: "CURRENT_APPLICATION_NAME_UPDATE",
|
||||
SET_WIDGET_LOADING: "SET_WIDGET_LOADING",
|
||||
};
|
||||
|
||||
export type ReduxActionType = typeof ReduxActionTypes[keyof typeof ReduxActionTypes];
|
||||
|
|
|
|||
|
|
@ -126,6 +126,9 @@ export class DataTreeFactory {
|
|||
const derivedPropertyMap = WidgetFactory.getWidgetDerivedPropertiesMap(
|
||||
widget.type,
|
||||
);
|
||||
const defaultProps = WidgetFactory.getWidgetDefaultPropertiesMap(
|
||||
widget.type,
|
||||
);
|
||||
const derivedProps: any = {};
|
||||
const dynamicBindingPathList = getEntityDynamicBindingPathList(widget);
|
||||
dynamicBindingPathList.forEach((dynamicPath) => {
|
||||
|
|
@ -137,6 +140,7 @@ export class DataTreeFactory {
|
|||
}
|
||||
});
|
||||
Object.keys(derivedPropertyMap).forEach((propertyName) => {
|
||||
// TODO regex is too greedy
|
||||
derivedProps[propertyName] = derivedPropertyMap[propertyName].replace(
|
||||
/this./g,
|
||||
`${widget.widgetName}.`,
|
||||
|
|
@ -145,11 +149,18 @@ export class DataTreeFactory {
|
|||
key: propertyName,
|
||||
});
|
||||
});
|
||||
const unInitializedDefaultProps: Record<string, undefined> = {};
|
||||
Object.values(defaultProps).forEach((propertyName) => {
|
||||
if (!(propertyName in widget)) {
|
||||
unInitializedDefaultProps[propertyName] = undefined;
|
||||
}
|
||||
});
|
||||
dataTree[widget.widgetName] = {
|
||||
...widget,
|
||||
...defaultMetaProps,
|
||||
...widgetMetaProps,
|
||||
...derivedProps,
|
||||
...unInitializedDefaultProps,
|
||||
dynamicBindingPathList,
|
||||
ENTITY_TYPE: ENTITY_TYPE.WIDGET,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ export type FlattenedWidgetProps = WidgetProps & {
|
|||
};
|
||||
|
||||
const canvasWidgetsReducer = createImmerReducer(initialState, {
|
||||
// TODO Rename to INIT_LAYOUT
|
||||
[ReduxActionTypes.UPDATE_CANVAS]: (
|
||||
state: CanvasWidgetsReduxState,
|
||||
action: ReduxAction<UpdateCanvasPayload>,
|
||||
|
|
|
|||
|
|
@ -1,21 +1,30 @@
|
|||
import { createReducer } from "utils/AppsmithUtils";
|
||||
import { ReduxAction, ReduxActionTypes } from "constants/ReduxActionConstants";
|
||||
import { DependencyMap } from "../../utils/DynamicBindingUtils";
|
||||
|
||||
export type EvaluationDependencyState = {
|
||||
dependencyMap: Record<string, Array<string>>;
|
||||
dependencyMap: DependencyMap;
|
||||
inverseDependencyMap: DependencyMap;
|
||||
dependencyTree: Array<[string, string]>;
|
||||
};
|
||||
|
||||
const initialState: EvaluationDependencyState = {
|
||||
dependencyMap: {},
|
||||
inverseDependencyMap: {},
|
||||
dependencyTree: [],
|
||||
};
|
||||
|
||||
const evaluationDependencyReducer = createReducer(initialState, {
|
||||
[ReduxActionTypes.SET_EVALUATION_DEPENDENCIES]: (
|
||||
[ReduxActionTypes.SET_EVALUATION_DEPENDENCY_MAP]: (
|
||||
state: EvaluationDependencyState,
|
||||
action: ReduxAction<EvaluationDependencyState>,
|
||||
) => action.payload,
|
||||
action: ReduxAction<{
|
||||
dependencyMap: DependencyMap;
|
||||
inverseDependencyMap: DependencyMap;
|
||||
}>,
|
||||
): EvaluationDependencyState => ({
|
||||
...state,
|
||||
...action.payload,
|
||||
}),
|
||||
});
|
||||
|
||||
export default evaluationDependencyReducer;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ const evaluatedTreeReducer = createImmerReducer(initialState, {
|
|||
state: EvaluatedTreeState,
|
||||
action: ReduxAction<DataTree>,
|
||||
) => action.payload,
|
||||
[ReduxActionTypes.FETCH_PAGE_INIT]: () => initialState,
|
||||
});
|
||||
|
||||
export default evaluatedTreeReducer;
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ import {
|
|||
getAppMode,
|
||||
getCurrentApplication,
|
||||
} from "selectors/applicationSelectors";
|
||||
import { evaluateDynamicTrigger, evaluateSingleValue } from "./evaluationsSaga";
|
||||
import { evaluateDynamicTrigger, evaluateSingleValue } from "./EvaluationsSaga";
|
||||
|
||||
function* navigateActionSaga(
|
||||
action: { pageNameOrUrl: string; params: Record<string, string> },
|
||||
|
|
|
|||
|
|
@ -1,4 +1,11 @@
|
|||
import { actionChannel, call, put, select, take } from "redux-saga/effects";
|
||||
import {
|
||||
actionChannel,
|
||||
call,
|
||||
fork,
|
||||
put,
|
||||
select,
|
||||
take,
|
||||
} from "redux-saga/effects";
|
||||
|
||||
import {
|
||||
EvaluationReduxAction,
|
||||
|
|
@ -19,7 +26,6 @@ import {
|
|||
EvalErrorTypes,
|
||||
} from "../utils/DynamicBindingUtils";
|
||||
import log from "loglevel";
|
||||
import _ from "lodash";
|
||||
import { WidgetType } from "../constants/WidgetConstants";
|
||||
import { WidgetProps } from "../widgets/BaseWidget";
|
||||
import PerformanceTracker, {
|
||||
|
|
@ -70,26 +76,29 @@ function* evaluateTreeSaga(postEvalActions?: ReduxAction<unknown>[]) {
|
|||
PerformanceTracker.startAsyncTracking(
|
||||
PerformanceTransactionName.DATA_TREE_EVALUATION,
|
||||
);
|
||||
const unEvalTree = yield select(getUnevaluatedDataTree);
|
||||
log.debug({ unEvalTree });
|
||||
const unevalTree = yield select(getUnevaluatedDataTree);
|
||||
log.debug({ unevalTree });
|
||||
|
||||
const workerResponse = yield call(
|
||||
worker.request,
|
||||
EVAL_WORKER_ACTIONS.EVAL_TREE,
|
||||
{
|
||||
dataTree: unEvalTree,
|
||||
unevalTree,
|
||||
widgetTypeConfigMap,
|
||||
},
|
||||
);
|
||||
|
||||
const { errors, dataTree, logs } = workerResponse;
|
||||
const parsedDataTree = JSON.parse(dataTree);
|
||||
const { errors, dataTree, dependencies, logs } = workerResponse;
|
||||
log.debug({ dataTree: dataTree });
|
||||
logs.forEach((evalLog: any) => log.debug(evalLog));
|
||||
log.debug({ dataTree: parsedDataTree });
|
||||
evalErrorHandler(errors);
|
||||
yield put({
|
||||
type: ReduxActionTypes.SET_EVALUATED_TREE,
|
||||
payload: parsedDataTree,
|
||||
payload: dataTree,
|
||||
});
|
||||
yield put({
|
||||
type: ReduxActionTypes.SET_EVALUATION_DEPENDENCY_MAP,
|
||||
payload: dependencies,
|
||||
});
|
||||
PerformanceTracker.stopAsyncTracking(
|
||||
PerformanceTransactionName.DATA_TREE_EVALUATION,
|
||||
|
|
@ -173,6 +182,7 @@ export function* validateProperty(
|
|||
props: WidgetProps,
|
||||
) {
|
||||
return yield call(worker.request, EVAL_WORKER_ACTIONS.VALIDATE_PROPERTY, {
|
||||
widgetTypeConfigMap,
|
||||
widgetType,
|
||||
property,
|
||||
value,
|
||||
|
|
@ -223,19 +233,18 @@ const EVALUATE_REDUX_ACTIONS = [
|
|||
];
|
||||
|
||||
function evalQueueBuffer() {
|
||||
let initialised = false;
|
||||
let takable = false;
|
||||
let canTake = false;
|
||||
let postEvalActions: any = [];
|
||||
const take = () => {
|
||||
if (takable) {
|
||||
if (canTake) {
|
||||
const resp = postEvalActions;
|
||||
postEvalActions = [];
|
||||
takable = false;
|
||||
return { postEvalActions: resp, type: "FAKE_ACTION" };
|
||||
canTake = false;
|
||||
return { postEvalActions: resp, type: "BUFFERED_ACTION" };
|
||||
}
|
||||
};
|
||||
const flush = () => {
|
||||
if (takable) {
|
||||
if (canTake) {
|
||||
return [take() as Action];
|
||||
}
|
||||
|
||||
|
|
@ -243,34 +252,7 @@ function evalQueueBuffer() {
|
|||
};
|
||||
|
||||
const put = (action: EvaluationReduxAction<unknown | unknown[]>) => {
|
||||
if (!initialised) {
|
||||
if (
|
||||
![
|
||||
ReduxActionTypes.FETCH_PAGE_SUCCESS,
|
||||
ReduxActionTypes.FETCH_PUBLISHED_PAGE_SUCCESS,
|
||||
].includes(action.type)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
initialised = true;
|
||||
}
|
||||
// When batching success action happens, we need to only evaluate
|
||||
// if the batch had any action we need to evaluate properties for
|
||||
if (
|
||||
action.type === ReduxActionTypes.BATCH_UPDATES_SUCCESS &&
|
||||
Array.isArray(action.payload)
|
||||
) {
|
||||
const batchedActionTypes = action.payload.map(
|
||||
(batchedAction: ReduxAction<unknown>) => batchedAction.type,
|
||||
);
|
||||
if (
|
||||
_.intersection(EVALUATE_REDUX_ACTIONS, batchedActionTypes).length === 0
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
takable = true;
|
||||
canTake = true;
|
||||
// TODO: If the action is the same as before, we can send only one and ignore duplicates.
|
||||
if (action.postEvalActions) {
|
||||
postEvalActions.push(...action.postEvalActions);
|
||||
|
|
@ -281,7 +263,7 @@ function evalQueueBuffer() {
|
|||
take,
|
||||
put,
|
||||
isEmpty: () => {
|
||||
return !takable;
|
||||
return !canTake;
|
||||
},
|
||||
flush,
|
||||
};
|
||||
|
|
@ -292,6 +274,8 @@ function* evaluationChangeListenerSaga() {
|
|||
yield call(worker.shutdown);
|
||||
yield call(worker.start);
|
||||
widgetTypeConfigMap = WidgetFactory.getWidgetTypeConfigMap();
|
||||
const initAction = yield take(FIRST_EVAL_REDUX_ACTIONS);
|
||||
yield fork(evaluateTreeSaga, initAction.postEvalActions);
|
||||
const evtActionChannel = yield actionChannel(
|
||||
EVALUATE_REDUX_ACTIONS,
|
||||
evalQueueBuffer(),
|
||||
|
|
@ -128,7 +128,11 @@ function* listenForSuccessfullBinding() {
|
|||
|
||||
if (widgetProperties.invalidProps) {
|
||||
bindSuccessfull =
|
||||
bindSuccessfull && !("tableData" in widgetProperties.invalidProps);
|
||||
bindSuccessfull &&
|
||||
!(
|
||||
"tableData" in widgetProperties.invalidProps &&
|
||||
widgetProperties.invalidProps.tableData
|
||||
);
|
||||
}
|
||||
|
||||
if (bindSuccessfull) {
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ import {
|
|||
setActionsToExecuteOnPageLoad,
|
||||
} from "actions/actionActions";
|
||||
import { APP_MODE, UrlDataState } from "reducers/entityReducers/appReducer";
|
||||
import { clearEvalCache } from "./evaluationsSaga";
|
||||
import { clearEvalCache } from "./EvaluationsSaga";
|
||||
import { getQueryParams } from "utils/AppsmithUtils";
|
||||
import PerformanceTracker, {
|
||||
PerformanceTransactionName,
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ import {
|
|||
isPathADynamicTrigger,
|
||||
} from "utils/DynamicBindingUtils";
|
||||
import { WidgetProps } from "widgets/BaseWidget";
|
||||
import _, { isString } from "lodash";
|
||||
import _ from "lodash";
|
||||
import WidgetFactory from "utils/WidgetFactory";
|
||||
import {
|
||||
buildWidgetBlueprint,
|
||||
|
|
@ -88,7 +88,7 @@ import { DataTreeWidget } from "entities/DataTree/dataTreeFactory";
|
|||
import {
|
||||
validateProperty,
|
||||
clearEvalPropertyCacheOfWidget,
|
||||
} from "./evaluationsSaga";
|
||||
} from "./EvaluationsSaga";
|
||||
import { WidgetBlueprint } from "reducers/entityReducers/widgetConfigReducer";
|
||||
import { Toaster } from "components/ads/Toast";
|
||||
import { Variant } from "components/ads/common";
|
||||
|
|
|
|||
|
|
@ -19,9 +19,8 @@ import queryPaneSagas from "./QueryPaneSagas";
|
|||
import modalSagas from "./ModalSagas";
|
||||
import batchSagas from "./BatchSagas";
|
||||
import themeSagas from "./ThemeSaga";
|
||||
import evaluationsSaga from "./evaluationsSaga";
|
||||
import evaluationsSaga from "./EvaluationsSaga";
|
||||
import onboardingSaga from "./OnboardingSagas";
|
||||
|
||||
import log from "loglevel";
|
||||
import * as sentry from "@sentry/react";
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,12 @@ export const getUnevaluatedDataTree = createSelector(
|
|||
},
|
||||
);
|
||||
|
||||
export const getEvaluationDependencyMap = (state: AppState) =>
|
||||
state.evaluations.dependencies.dependencyMap;
|
||||
|
||||
export const getEvaluationInverseDependencyMap = (state: AppState) =>
|
||||
state.evaluations.dependencies.inverseDependencyMap;
|
||||
|
||||
/**
|
||||
* returns evaluation tree object
|
||||
*
|
||||
|
|
|
|||
|
|
@ -123,9 +123,9 @@ export const getCanvasWidgetDsl = createSelector(
|
|||
const widgets: Record<string, DataTreeWidget> = {};
|
||||
Object.keys(canvasWidgets).forEach((widgetKey) => {
|
||||
const canvasWidget = canvasWidgets[widgetKey];
|
||||
const evaluatedWidget = evaluatedDataTree[
|
||||
canvasWidget.widgetName
|
||||
] as DataTreeWidget;
|
||||
const evaluatedWidget = _.find(evaluatedDataTree, {
|
||||
widgetId: widgetKey,
|
||||
}) as DataTreeWidget;
|
||||
if (evaluatedWidget) {
|
||||
widgets[widgetKey] = createCanvasWidget(canvasWidget, evaluatedWidget);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ import moment from "moment-timezone";
|
|||
import { WidgetProps } from "../widgets/BaseWidget";
|
||||
import parser from "fast-xml-parser";
|
||||
|
||||
export type DependencyMap = Record<string, Array<string>>;
|
||||
|
||||
export const removeBindingsFromActionObject = (obj: Action) => {
|
||||
const string = JSON.stringify(obj);
|
||||
const withBindings = string.replace(DATA_BIND_REGEX_GLOBAL, "{{ }}");
|
||||
|
|
@ -87,6 +89,7 @@ export enum EvalErrorTypes {
|
|||
EVAL_TREE_ERROR = "EVAL_TREE_ERROR",
|
||||
UNESCAPE_STRING_ERROR = "UNESCAPE_STRING_ERROR",
|
||||
EVAL_ERROR = "EVAL_ERROR",
|
||||
UNKNOWN_ERROR = "UNKNOWN_ERROR",
|
||||
BAD_UNEVAL_TREE_ERROR = "BAD_UNEVAL_TREE_ERROR",
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,213 +1,32 @@
|
|||
// import {
|
||||
// mockExecute,
|
||||
// mockRegisterLibrary,
|
||||
// } from "../../test/__mocks__/RealmExecutorMock";
|
||||
// import {
|
||||
// dependencySortedEvaluateDataTree,
|
||||
// getDynamicValue,
|
||||
// getEntityDependencies,
|
||||
// parseDynamicString,
|
||||
// } from "./DynamicBindingUtils";
|
||||
// import { DataTree, ENTITY_TYPE } from "entities/DataTree/dataTreeFactory";
|
||||
// import { RenderModes, WidgetTypes } from "constants/WidgetConstants";
|
||||
//
|
||||
// beforeAll(() => {
|
||||
// mockRegisterLibrary.mockClear();
|
||||
// mockExecute.mockClear();
|
||||
// });
|
||||
//
|
||||
// it("Gets the value from the data tree", () => {
|
||||
// const dynamicBinding = "{{GetUsers.data}}";
|
||||
// const nameBindingsWithData: DataTree = {
|
||||
// GetUsers: {
|
||||
// data: { text: "correct data" },
|
||||
// config: {
|
||||
// pluginId: "",
|
||||
// id: "id",
|
||||
// name: "text",
|
||||
// actionConfiguration: {},
|
||||
// pageId: "",
|
||||
// jsonPathKeys: [],
|
||||
// datasource: { id: "" },
|
||||
// pluginType: "1",
|
||||
// },
|
||||
// isLoading: false,
|
||||
// ENTITY_TYPE: ENTITY_TYPE.ACTION,
|
||||
// run: jest.fn(),
|
||||
// },
|
||||
// };
|
||||
// const actualValue = { result: { text: "correct data" } };
|
||||
//
|
||||
// const value = getDynamicValue(dynamicBinding, nameBindingsWithData);
|
||||
//
|
||||
// expect(value).toEqual(actualValue);
|
||||
// });
|
||||
//
|
||||
// describe.each([
|
||||
// ["{{A}}", ["{{A}}"]],
|
||||
// ["A {{B}}", ["A ", "{{B}}"]],
|
||||
// [
|
||||
// "Hello {{Customer.Name}}, the status for your order id {{orderId}} is {{status}}",
|
||||
// [
|
||||
// "Hello ",
|
||||
// "{{Customer.Name}}",
|
||||
// ", the status for your order id ",
|
||||
// "{{orderId}}",
|
||||
// " is ",
|
||||
// "{{status}}",
|
||||
// ],
|
||||
// ],
|
||||
// [
|
||||
// "{{data.map(datum => {return {id: datum}})}}",
|
||||
// ["{{data.map(datum => {return {id: datum}})}}"],
|
||||
// ],
|
||||
// ["{{}}{{}}}", ["{{}}", "{{}}", "}"]],
|
||||
// ["{{{}}", ["{{{}}"]],
|
||||
// ["{{ {{", ["{{ {{"]],
|
||||
// ["}} }}", ["}} }}"]],
|
||||
// ["}} {{", ["}} {{"]],
|
||||
// ])("Parse the dynamic string(%s, %j)", (dynamicString, expected) => {
|
||||
// test(`returns ${expected}`, () => {
|
||||
// expect(parseDynamicString(dynamicString as string)).toStrictEqual(expected);
|
||||
// });
|
||||
// });
|
||||
//
|
||||
// const baseWidgetProps = {
|
||||
// parentColumnSpace: 0,
|
||||
// parentRowSpace: 0,
|
||||
// parentId: "0",
|
||||
// type: WidgetTypes.BUTTON_WIDGET,
|
||||
// renderMode: RenderModes.CANVAS,
|
||||
// leftColumn: 0,
|
||||
// rightColumn: 0,
|
||||
// topRow: 0,
|
||||
// bottomRow: 0,
|
||||
// isLoading: false,
|
||||
// };
|
||||
//
|
||||
// it("evaluates the data tree", () => {
|
||||
// const input: DataTree = {
|
||||
// widget1: {
|
||||
// ...baseWidgetProps,
|
||||
// widgetId: "1",
|
||||
// widgetName: "widget1",
|
||||
// displayValue: "{{widget2.computedProperty}}",
|
||||
// ENTITY_TYPE: ENTITY_TYPE.WIDGET,
|
||||
// },
|
||||
// widget2: {
|
||||
// ...baseWidgetProps,
|
||||
// widgetId: "2",
|
||||
// widgetName: "widget2",
|
||||
// computedProperty: "{{ widget2.data[widget2.index] }}",
|
||||
// data: "{{ apiData.data }}",
|
||||
// index: 2,
|
||||
// ENTITY_TYPE: ENTITY_TYPE.WIDGET,
|
||||
// },
|
||||
// apiData: {
|
||||
// config: {
|
||||
// id: "123",
|
||||
// pageId: "1234",
|
||||
// datasource: {},
|
||||
// name: "api",
|
||||
// actionConfiguration: {},
|
||||
// jsonPathKeys: [],
|
||||
// pluginId: "plugin",
|
||||
// },
|
||||
// run: (onSuccess, onError) => ({
|
||||
// type: "RUN_ACTION",
|
||||
// payload: {
|
||||
// actionId: "",
|
||||
// onSuccess: "",
|
||||
// onError: "",
|
||||
// },
|
||||
// }),
|
||||
// isLoading: false,
|
||||
// data: ["wrong value", "still wrong", "correct"],
|
||||
// ENTITY_TYPE: ENTITY_TYPE.ACTION,
|
||||
// },
|
||||
// };
|
||||
//
|
||||
// const dynamicBindings = {
|
||||
// "widget1.displayValue": ["widget2.computedProperty"],
|
||||
// "widget2.computedProperty": ["widget2.data", "widget2.index"],
|
||||
// "widget2.data": ["apiData.data"],
|
||||
// };
|
||||
//
|
||||
// const sortedDeps = [
|
||||
// "apiData.data",
|
||||
// "widget2.data",
|
||||
// "widget2.index",
|
||||
// "widget2.computedProperty",
|
||||
// "widget1.displayValue",
|
||||
// ];
|
||||
//
|
||||
// const output: DataTree = {
|
||||
// widget1: {
|
||||
// ...baseWidgetProps,
|
||||
// widgetId: "1",
|
||||
// widgetName: "widget1",
|
||||
// displayValue: "correct",
|
||||
// ENTITY_TYPE: ENTITY_TYPE.WIDGET,
|
||||
// },
|
||||
// widget2: {
|
||||
// ...baseWidgetProps,
|
||||
// widgetId: "2",
|
||||
// widgetName: "widget2",
|
||||
// computedProperty: "correct",
|
||||
// data: ["wrong value", "still wrong", "correct"],
|
||||
// index: 2,
|
||||
// ENTITY_TYPE: ENTITY_TYPE.WIDGET,
|
||||
// },
|
||||
// apiData: {
|
||||
// config: {
|
||||
// id: "123",
|
||||
// pageId: "1234",
|
||||
// datasource: {},
|
||||
// name: "api",
|
||||
// actionConfiguration: {},
|
||||
// jsonPathKeys: [],
|
||||
// pluginId: "plugin",
|
||||
// },
|
||||
// run: (onSuccess, onError) => ({
|
||||
// type: "RUN_ACTION",
|
||||
// payload: {
|
||||
// actionId: "",
|
||||
// onSuccess: "",
|
||||
// onError: "",
|
||||
// },
|
||||
// }),
|
||||
// isLoading: false,
|
||||
// data: ["wrong value", "still wrong", "correct"],
|
||||
// ENTITY_TYPE: ENTITY_TYPE.ACTION,
|
||||
// },
|
||||
// };
|
||||
//
|
||||
// const result = dependencySortedEvaluateDataTree(
|
||||
// input,
|
||||
// dynamicBindings,
|
||||
// sortedDeps,
|
||||
// );
|
||||
// expect(result).toEqual(output);
|
||||
// });
|
||||
//
|
||||
// it("finds dependencies of a entity", () => {
|
||||
// const depMap: Array<[string, string]> = [
|
||||
// ["Widget5.text", "Widget2.data.visible"],
|
||||
// ["Widget1.options", "Action1.data"],
|
||||
// ["Widget2.text", "Widget1.selectedOption"],
|
||||
// ["Widget3.text", "Widget4.selectedRow.name"],
|
||||
// ["Widget6.label", "Action1.data.label"],
|
||||
// ];
|
||||
// const entity = "Action1";
|
||||
// const result = ["Widget1", "Widget2", "Widget5", "Widget6"];
|
||||
//
|
||||
// const actual = getEntityDependencies(depMap, entity);
|
||||
//
|
||||
// expect(actual).toEqual(result);
|
||||
// });
|
||||
import { getDynamicStringSegments } from "./DynamicBindingUtils";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: No types available
|
||||
it("does nothing. needs implementing", () => {
|
||||
expect(1 + 1).toEqual(2);
|
||||
describe.each([
|
||||
["{{A}}", ["{{A}}"]],
|
||||
["A {{B}}", ["A ", "{{B}}"]],
|
||||
[
|
||||
"Hello {{Customer.Name}}, the status for your order id {{orderId}} is {{status}}",
|
||||
[
|
||||
"Hello ",
|
||||
"{{Customer.Name}}",
|
||||
", the status for your order id ",
|
||||
"{{orderId}}",
|
||||
" is ",
|
||||
"{{status}}",
|
||||
],
|
||||
],
|
||||
[
|
||||
"{{data.map(datum => {return {id: datum}})}}",
|
||||
["{{data.map(datum => {return {id: datum}})}}"],
|
||||
],
|
||||
["{{}}{{}}}", ["{{}}", "{{}}", "}"]],
|
||||
["{{{}}", ["{{{}}"]],
|
||||
["{{ {{", ["{{ {{"]],
|
||||
["}} }}", ["}} }}"]],
|
||||
["}} {{", ["}} {{"]],
|
||||
])("Parse the dynamic string(%s, %j)", (dynamicString, expected) => {
|
||||
test(`returns ${expected}`, () => {
|
||||
expect(getDynamicStringSegments(dynamicString as string)).toStrictEqual(
|
||||
expected,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import * as log from "loglevel";
|
|||
export enum PerformanceTransactionName {
|
||||
DEPLOY_APPLICATION = "DEPLOY_APPLICATION",
|
||||
DATA_TREE_EVALUATION = "DATA_TREE_EVALUATION",
|
||||
DATA_TREE_WORKER_EVALUATION = "DATA_TREE_WORKER_EVALUATION",
|
||||
EVAL_REDUX_UPDATE = "EVAL_REDUX_UPDATE",
|
||||
CONSTRUCT_UNEVAL_TREE = "CONSTRUCT_UNEVAL_TREE",
|
||||
CONSTRUCT_CANVAS_DSL = "CONSTRUCT_CANVAS_DSL",
|
||||
CREATE_DEPENDENCIES = "CREATE_DEPENDENCIES",
|
||||
|
|
@ -38,6 +40,7 @@ export enum PerformanceTransactionName {
|
|||
USER_ME_API = "USER_ME_API",
|
||||
SIGN_UP = "SIGN_UP",
|
||||
LOGIN_CLICK = "LOGIN_CLICK",
|
||||
SET_EVALUATED = "SET_EVALUATED",
|
||||
}
|
||||
|
||||
export enum PerformanceTagNames {
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ abstract class BaseWidget<
|
|||
static getDefaultPropertiesMap(): Record<string, string> {
|
||||
return {};
|
||||
}
|
||||
|
||||
// TODO Find a way to enforce this, (dont let it be set)
|
||||
static getMetaPropertiesMap(): Record<string, any> {
|
||||
return {};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,6 +123,8 @@
|
|||
// );
|
||||
// });
|
||||
// });
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
it("does nothing. needs implementing", () => {
|
||||
expect(1 + 1).toEqual(2);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ class InputWidget extends BaseWidget<InputWidgetProps, WidgetState> {
|
|||
if (this.regex) {
|
||||
/*
|
||||
* break up the regexp pattern into 4 parts: given regex, regex prefix , regex pattern, regex flags
|
||||
* Example /appsmith/i will be split into ["/appsmith/gi", "/", "appsmith", "gi"]
|
||||
* Example /test/i will be split into ["/test/gi", "/", "test", "gi"]
|
||||
*/
|
||||
const regexParts = this.regex.match(/(\\/?)(.+)\\1([a-z]*)/i);
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import React from "react";
|
|||
import BaseWidget, { WidgetProps } from "./BaseWidget";
|
||||
import _ from "lodash";
|
||||
import { EditorContext } from "../components/editorComponents/EditorContextProvider";
|
||||
import { clearEvalPropertyCache } from "sagas/evaluationsSaga";
|
||||
import { clearEvalPropertyCache } from "sagas/EvaluationsSaga";
|
||||
import { ExecuteActionPayload } from "../constants/ActionConstants";
|
||||
|
||||
type DebouncedExecuteActionPayload = Omit<
|
||||
|
|
|
|||
|
|
@ -38,6 +38,12 @@ class TabsWidget extends BaseWidget<
|
|||
};
|
||||
}
|
||||
|
||||
static getMetaPropertiesMap() {
|
||||
return {
|
||||
selectedTabWidgetId: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
static getDefaultPropertiesMap(): Record<string, string> {
|
||||
return {};
|
||||
}
|
||||
|
|
|
|||
767
app/client/src/workers/evaluation.test.ts
Normal file
767
app/client/src/workers/evaluation.test.ts
Normal file
|
|
@ -0,0 +1,767 @@
|
|||
import { DataTreeEvaluator } from "./evaluation.worker";
|
||||
import {
|
||||
DataTreeAction,
|
||||
DataTreeWidget,
|
||||
ENTITY_TYPE,
|
||||
} from "../entities/DataTree/dataTreeFactory";
|
||||
import { WidgetTypeConfigMap } from "../utils/WidgetFactory";
|
||||
import { RenderModes, WidgetTypes } from "../constants/WidgetConstants";
|
||||
import { PluginType } from "../entities/Action";
|
||||
|
||||
const WIDGET_CONFIG_MAP: WidgetTypeConfigMap = {
|
||||
CONTAINER_WIDGET: {
|
||||
validations: {
|
||||
isLoading: "BOOLEAN",
|
||||
isVisible: "BOOLEAN",
|
||||
isDisabled: "BOOLEAN",
|
||||
},
|
||||
defaultProperties: {},
|
||||
derivedProperties: {},
|
||||
triggerProperties: {},
|
||||
metaProperties: {},
|
||||
},
|
||||
TEXT_WIDGET: {
|
||||
validations: {
|
||||
isLoading: "BOOLEAN",
|
||||
isVisible: "BOOLEAN",
|
||||
isDisabled: "BOOLEAN",
|
||||
text: "TEXT",
|
||||
textStyle: "TEXT",
|
||||
shouldScroll: "BOOLEAN",
|
||||
},
|
||||
defaultProperties: {},
|
||||
derivedProperties: {
|
||||
value: "{{ this.text }}",
|
||||
},
|
||||
triggerProperties: {},
|
||||
metaProperties: {},
|
||||
},
|
||||
BUTTON_WIDGET: {
|
||||
validations: {
|
||||
isLoading: "BOOLEAN",
|
||||
isVisible: "BOOLEAN",
|
||||
isDisabled: "BOOLEAN",
|
||||
text: "TEXT",
|
||||
buttonStyle: "TEXT",
|
||||
},
|
||||
defaultProperties: {},
|
||||
derivedProperties: {},
|
||||
triggerProperties: {
|
||||
onClick: true,
|
||||
},
|
||||
metaProperties: {},
|
||||
},
|
||||
INPUT_WIDGET: {
|
||||
validations: {
|
||||
isLoading: "BOOLEAN",
|
||||
isVisible: "BOOLEAN",
|
||||
isDisabled: "BOOLEAN",
|
||||
inputType: "TEXT",
|
||||
defaultText: "TEXT",
|
||||
text: "TEXT",
|
||||
regex: "REGEX",
|
||||
errorMessage: "TEXT",
|
||||
placeholderText: "TEXT",
|
||||
maxChars: "NUMBER",
|
||||
minNum: "NUMBER",
|
||||
maxNum: "NUMBER",
|
||||
label: "TEXT",
|
||||
inputValidators: "ARRAY",
|
||||
focusIndex: "NUMBER",
|
||||
isAutoFocusEnabled: "BOOLEAN",
|
||||
isRequired: "BOOLEAN",
|
||||
isValid: "BOOLEAN",
|
||||
},
|
||||
defaultProperties: {
|
||||
text: "defaultText",
|
||||
},
|
||||
derivedProperties: {
|
||||
isValid:
|
||||
'{{\n function(){\n let parsedRegex = null;\n if (this.regex) {\n /*\n * break up the regexp pattern into 4 parts: given regex, regex prefix , regex pattern, regex flags\n * Example /test/i will be split into ["/test/gi", "/", "test", "gi"]\n */\n const regexParts = this.regex.match(/(\\/?)(.+)\\1([a-z]*)/i);\n if (!regexParts) {\n parsedRegex = new RegExp(this.regex);\n } else {\n /*\n * if we don\'t have a regex flags (gmisuy), convert provided string into regexp directly\n /*\n if (regexParts[3] && !/^(?!.*?(.).*?\\1)[gmisuy]+$/.test(regexParts[3])) {\n parsedRegex = RegExp(this.regex);\n }\n /*\n * if we have a regex flags, use it to form regexp\n */\n parsedRegex = new RegExp(regexParts[2], regexParts[3]);\n }\n }\n if (this.inputType === "EMAIL") {\n const emailRegex = new RegExp(/^\\w+([\\.-]?\\w+)*@\\w+([\\.-]?\\w+)*(\\.\\w{2,3})+$/);\n return emailRegex.test(this.text);\n }\n else if (this.inputType === "NUMBER") {\n return !isNaN(this.text)\n }\n else if (this.isRequired) {\n if(this.text && this.text.length) {\n if (parsedRegex) {\n return parsedRegex.test(this.text)\n } else {\n return true;\n }\n } else {\n return false;\n }\n } if (parsedRegex) {\n return parsedRegex.test(this.text)\n } else {\n return true;\n }\n }()\n }}',
|
||||
value: "{{this.text}}",
|
||||
},
|
||||
triggerProperties: {
|
||||
onTextChanged: true,
|
||||
},
|
||||
metaProperties: {
|
||||
isFocused: false,
|
||||
isDirty: false,
|
||||
},
|
||||
},
|
||||
CHECKBOX_WIDGET: {
|
||||
validations: {
|
||||
isLoading: "BOOLEAN",
|
||||
isVisible: "BOOLEAN",
|
||||
isDisabled: "BOOLEAN",
|
||||
label: "TEXT",
|
||||
defaultCheckedState: "BOOLEAN",
|
||||
},
|
||||
defaultProperties: {
|
||||
isChecked: "defaultCheckedState",
|
||||
},
|
||||
derivedProperties: {
|
||||
value: "{{this.isChecked}}",
|
||||
},
|
||||
triggerProperties: {
|
||||
onCheckChange: true,
|
||||
},
|
||||
metaProperties: {},
|
||||
},
|
||||
DROP_DOWN_WIDGET: {
|
||||
validations: {
|
||||
isLoading: "BOOLEAN",
|
||||
isVisible: "BOOLEAN",
|
||||
isDisabled: "BOOLEAN",
|
||||
placeholderText: "TEXT",
|
||||
label: "TEXT",
|
||||
options: "OPTIONS_DATA",
|
||||
selectionType: "TEXT",
|
||||
isRequired: "BOOLEAN",
|
||||
selectedOptionValues: "ARRAY",
|
||||
defaultOptionValue: "DEFAULT_OPTION_VALUE",
|
||||
},
|
||||
defaultProperties: {
|
||||
selectedOptionValue: "defaultOptionValue",
|
||||
selectedOptionValueArr: "defaultOptionValue",
|
||||
},
|
||||
derivedProperties: {
|
||||
isValid:
|
||||
"{{this.isRequired ? this.selectionType === 'SINGLE_SELECT' ? !!this.selectedOption : !!this.selectedIndexArr && this.selectedIndexArr.length > 0 : true}}",
|
||||
selectedOption:
|
||||
"{{ this.selectionType === 'SINGLE_SELECT' ? _.find(this.options, { value: this.selectedOptionValue }) : undefined}}",
|
||||
selectedOptionArr:
|
||||
'{{this.selectionType === "MULTI_SELECT" ? this.options.filter(opt => _.includes(this.selectedOptionValueArr, opt.value)) : undefined}}',
|
||||
selectedIndex:
|
||||
"{{ _.findIndex(this.options, { value: this.selectedOption.value } ) }}",
|
||||
selectedIndexArr:
|
||||
"{{ this.selectedOptionValueArr.map(o => _.findIndex(this.options, { value: o })) }}",
|
||||
value:
|
||||
"{{ this.selectionType === 'SINGLE_SELECT' ? this.selectedOptionValue : this.selectedOptionValueArr }}",
|
||||
selectedOptionValues: "{{ this.selectedOptionValueArr }}",
|
||||
},
|
||||
triggerProperties: {
|
||||
onOptionChange: true,
|
||||
},
|
||||
metaProperties: {},
|
||||
},
|
||||
RADIO_GROUP_WIDGET: {
|
||||
validations: {
|
||||
isLoading: "BOOLEAN",
|
||||
isVisible: "BOOLEAN",
|
||||
isDisabled: "BOOLEAN",
|
||||
label: "TEXT",
|
||||
options: "OPTIONS_DATA",
|
||||
selectedOptionValue: "TEXT",
|
||||
defaultOptionValue: "TEXT",
|
||||
isRequired: "BOOLEAN",
|
||||
},
|
||||
defaultProperties: {
|
||||
selectedOptionValue: "defaultOptionValue",
|
||||
},
|
||||
derivedProperties: {
|
||||
selectedOption:
|
||||
"{{_.find(this.options, { value: this.selectedOptionValue })}}",
|
||||
isValid: "{{ this.isRequired ? !!this.selectedOptionValue : true }}",
|
||||
value: "{{this.selectedOptionValue}}",
|
||||
},
|
||||
triggerProperties: {
|
||||
onSelectionChange: true,
|
||||
},
|
||||
metaProperties: {},
|
||||
},
|
||||
IMAGE_WIDGET: {
|
||||
validations: {
|
||||
isLoading: "BOOLEAN",
|
||||
isVisible: "BOOLEAN",
|
||||
isDisabled: "BOOLEAN",
|
||||
image: "TEXT",
|
||||
imageShape: "TEXT",
|
||||
defaultImage: "TEXT",
|
||||
maxZoomLevel: "NUMBER",
|
||||
},
|
||||
defaultProperties: {},
|
||||
derivedProperties: {},
|
||||
triggerProperties: {
|
||||
onClick: true,
|
||||
},
|
||||
metaProperties: {},
|
||||
},
|
||||
TABLE_WIDGET: {
|
||||
validations: {
|
||||
isLoading: "BOOLEAN",
|
||||
isVisible: "BOOLEAN",
|
||||
isDisabled: "BOOLEAN",
|
||||
tableData: "TABLE_DATA",
|
||||
nextPageKey: "TEXT",
|
||||
prevPageKey: "TEXT",
|
||||
label: "TEXT",
|
||||
searchText: "TEXT",
|
||||
defaultSearchText: "TEXT",
|
||||
defaultSelectedRow: "DEFAULT_SELECTED_ROW",
|
||||
},
|
||||
defaultProperties: {
|
||||
searchText: "defaultSearchText",
|
||||
selectedRowIndex: "defaultSelectedRow",
|
||||
selectedRowIndices: "defaultSelectedRow",
|
||||
},
|
||||
derivedProperties: {
|
||||
selectedRow: `{{ _.get(this.filteredTableData, this.selectedRowIndex, _.mapValues(this.filteredTableData[0], () => undefined)) }}`,
|
||||
selectedRows: `{{ this.filteredTableData.filter((item, i) => selectedRowIndices.includes(i) }); }}`,
|
||||
},
|
||||
triggerProperties: {
|
||||
onRowSelected: true,
|
||||
onPageChange: true,
|
||||
onSearchTextChanged: true,
|
||||
columnActions: true,
|
||||
},
|
||||
metaProperties: {
|
||||
pageNo: 1,
|
||||
selectedRow: {},
|
||||
selectedRows: [],
|
||||
},
|
||||
},
|
||||
VIDEO_WIDGET: {
|
||||
validations: {
|
||||
isLoading: "BOOLEAN",
|
||||
isVisible: "BOOLEAN",
|
||||
isDisabled: "BOOLEAN",
|
||||
url: "TEXT",
|
||||
},
|
||||
defaultProperties: {},
|
||||
derivedProperties: {},
|
||||
triggerProperties: {
|
||||
onEnd: true,
|
||||
onPlay: true,
|
||||
onPause: true,
|
||||
},
|
||||
metaProperties: {
|
||||
playState: "NOT_STARTED",
|
||||
},
|
||||
},
|
||||
FILE_PICKER_WIDGET: {
|
||||
validations: {
|
||||
isLoading: "BOOLEAN",
|
||||
isVisible: "BOOLEAN",
|
||||
isDisabled: "BOOLEAN",
|
||||
label: "TEXT",
|
||||
maxNumFiles: "NUMBER",
|
||||
allowedFileTypes: "ARRAY",
|
||||
files: "ARRAY",
|
||||
isRequired: "BOOLEAN",
|
||||
},
|
||||
defaultProperties: {},
|
||||
derivedProperties: {
|
||||
isValid: "{{ this.isRequired ? this.files.length > 0 : true }}",
|
||||
value: "{{this.files}}",
|
||||
},
|
||||
triggerProperties: {
|
||||
onFilesSelected: true,
|
||||
},
|
||||
metaProperties: {
|
||||
files: [],
|
||||
uploadedFileData: {},
|
||||
},
|
||||
},
|
||||
DATE_PICKER_WIDGET: {
|
||||
validations: {
|
||||
isLoading: "BOOLEAN",
|
||||
isVisible: "BOOLEAN",
|
||||
isDisabled: "BOOLEAN",
|
||||
defaultDate: "DATE",
|
||||
timezone: "TEXT",
|
||||
enableTimePicker: "BOOLEAN",
|
||||
dateFormat: "TEXT",
|
||||
label: "TEXT",
|
||||
datePickerType: "TEXT",
|
||||
maxDate: "DATE",
|
||||
minDate: "DATE",
|
||||
isRequired: "BOOLEAN",
|
||||
},
|
||||
defaultProperties: {
|
||||
selectedDate: "defaultDate",
|
||||
},
|
||||
derivedProperties: {
|
||||
isValid: "{{ this.isRequired ? !!this.selectedDate : true }}",
|
||||
value: "{{ this.selectedDate }}",
|
||||
},
|
||||
triggerProperties: {
|
||||
onDateSelected: true,
|
||||
},
|
||||
metaProperties: {},
|
||||
},
|
||||
TABS_WIDGET: {
|
||||
validations: {
|
||||
tabs: "TABS_DATA",
|
||||
defaultTab: "SELECTED_TAB",
|
||||
},
|
||||
defaultProperties: {},
|
||||
derivedProperties: {
|
||||
selectedTab:
|
||||
"{{_.find(this.tabs, { widgetId: this.selectedTabWidgetId }).label}}",
|
||||
},
|
||||
triggerProperties: {
|
||||
onTabSelected: true,
|
||||
},
|
||||
metaProperties: {},
|
||||
},
|
||||
MODAL_WIDGET: {
|
||||
validations: {
|
||||
isLoading: "BOOLEAN",
|
||||
isVisible: "BOOLEAN",
|
||||
isDisabled: "BOOLEAN",
|
||||
},
|
||||
defaultProperties: {},
|
||||
derivedProperties: {},
|
||||
triggerProperties: {},
|
||||
metaProperties: {},
|
||||
},
|
||||
RICH_TEXT_EDITOR_WIDGET: {
|
||||
validations: {
|
||||
text: "TEXT",
|
||||
placeholder: "TEXT",
|
||||
defaultValue: "TEXT",
|
||||
isDisabled: "BOOLEAN",
|
||||
isVisible: "BOOLEAN",
|
||||
},
|
||||
defaultProperties: {
|
||||
text: "defaultText",
|
||||
},
|
||||
derivedProperties: {
|
||||
value: "{{this.text}}",
|
||||
},
|
||||
triggerProperties: {
|
||||
onTextChange: true,
|
||||
},
|
||||
metaProperties: {},
|
||||
},
|
||||
CHART_WIDGET: {
|
||||
validations: {
|
||||
xAxisName: "TEXT",
|
||||
yAxisName: "TEXT",
|
||||
chartName: "TEXT",
|
||||
isVisible: "BOOLEAN",
|
||||
chartData: "CHART_DATA",
|
||||
},
|
||||
defaultProperties: {},
|
||||
derivedProperties: {},
|
||||
triggerProperties: {},
|
||||
metaProperties: {},
|
||||
},
|
||||
FORM_WIDGET: {
|
||||
validations: {
|
||||
isLoading: "BOOLEAN",
|
||||
isVisible: "BOOLEAN",
|
||||
isDisabled: "BOOLEAN",
|
||||
},
|
||||
defaultProperties: {},
|
||||
derivedProperties: {},
|
||||
triggerProperties: {},
|
||||
metaProperties: {},
|
||||
},
|
||||
FORM_BUTTON_WIDGET: {
|
||||
validations: {
|
||||
isLoading: "BOOLEAN",
|
||||
isVisible: "BOOLEAN",
|
||||
isDisabled: "BOOLEAN",
|
||||
text: "TEXT",
|
||||
disabledWhenInvalid: "BOOLEAN",
|
||||
buttonStyle: "TEXT",
|
||||
buttonType: "TEXT",
|
||||
},
|
||||
defaultProperties: {},
|
||||
derivedProperties: {},
|
||||
triggerProperties: {
|
||||
onClick: true,
|
||||
},
|
||||
metaProperties: {},
|
||||
},
|
||||
MAP_WIDGET: {
|
||||
validations: {
|
||||
defaultMarkers: "MARKERS",
|
||||
isDisabled: "BOOLEAN",
|
||||
isVisible: "BOOLEAN",
|
||||
enableSearch: "BOOLEAN",
|
||||
enablePickLocation: "BOOLEAN",
|
||||
allowZoom: "BOOLEAN",
|
||||
zoomLevel: "NUMBER",
|
||||
mapCenter: "OBJECT",
|
||||
},
|
||||
defaultProperties: {
|
||||
markers: "defaultMarkers",
|
||||
center: "mapCenter",
|
||||
},
|
||||
derivedProperties: {},
|
||||
triggerProperties: {
|
||||
onMarkerClick: true,
|
||||
onCreateMarker: true,
|
||||
},
|
||||
metaProperties: {},
|
||||
},
|
||||
CANVAS_WIDGET: {
|
||||
validations: {
|
||||
isLoading: "BOOLEAN",
|
||||
isVisible: "BOOLEAN",
|
||||
isDisabled: "BOOLEAN",
|
||||
},
|
||||
defaultProperties: {},
|
||||
derivedProperties: {},
|
||||
triggerProperties: {},
|
||||
metaProperties: {},
|
||||
},
|
||||
ICON_WIDGET: {
|
||||
validations: {
|
||||
isLoading: "BOOLEAN",
|
||||
isVisible: "BOOLEAN",
|
||||
isDisabled: "BOOLEAN",
|
||||
},
|
||||
defaultProperties: {},
|
||||
derivedProperties: {},
|
||||
triggerProperties: {
|
||||
onClick: true,
|
||||
},
|
||||
metaProperties: {},
|
||||
},
|
||||
SKELETON_WIDGET: {
|
||||
validations: {
|
||||
isLoading: "BOOLEAN",
|
||||
isVisible: "BOOLEAN",
|
||||
isDisabled: "BOOLEAN",
|
||||
},
|
||||
defaultProperties: {},
|
||||
derivedProperties: {},
|
||||
triggerProperties: {},
|
||||
metaProperties: {},
|
||||
},
|
||||
};
|
||||
|
||||
const BASE_WIDGET: DataTreeWidget = {
|
||||
widgetId: "randomID",
|
||||
widgetName: "randomName",
|
||||
bottomRow: 0,
|
||||
isLoading: false,
|
||||
leftColumn: 0,
|
||||
parentColumnSpace: 0,
|
||||
parentRowSpace: 0,
|
||||
renderMode: RenderModes.CANVAS,
|
||||
rightColumn: 0,
|
||||
topRow: 0,
|
||||
type: WidgetTypes.SKELETON_WIDGET,
|
||||
parentId: "0",
|
||||
ENTITY_TYPE: ENTITY_TYPE.WIDGET,
|
||||
};
|
||||
|
||||
const BASE_ACTION: DataTreeAction = {
|
||||
actionId: "randomId",
|
||||
name: "randomName",
|
||||
config: {
|
||||
timeoutInMillisecond: 10,
|
||||
},
|
||||
dynamicBindingPathList: [],
|
||||
isLoading: false,
|
||||
pluginType: PluginType.API,
|
||||
run: {},
|
||||
data: {},
|
||||
ENTITY_TYPE: ENTITY_TYPE.ACTION,
|
||||
};
|
||||
|
||||
describe("DataTreeEvaluator", () => {
|
||||
const unEvalTree = {
|
||||
Text1: {
|
||||
...BASE_WIDGET,
|
||||
widgetName: "Text1",
|
||||
text: "Label",
|
||||
type: WidgetTypes.TEXT_WIDGET,
|
||||
},
|
||||
Text2: {
|
||||
...BASE_WIDGET,
|
||||
widgetName: "Text2",
|
||||
text: "{{Text1.text}}",
|
||||
dynamicBindingPathList: [{ key: "text" }],
|
||||
type: WidgetTypes.TEXT_WIDGET,
|
||||
},
|
||||
Text3: {
|
||||
...BASE_WIDGET,
|
||||
widgetName: "Text3",
|
||||
text: "{{Text1.text}}",
|
||||
dynamicBindingPathList: [{ key: "text" }],
|
||||
type: WidgetTypes.TEXT_WIDGET,
|
||||
},
|
||||
Dropdown1: {
|
||||
...BASE_WIDGET,
|
||||
options: [
|
||||
{
|
||||
label: "test",
|
||||
value: "valueTest",
|
||||
},
|
||||
{
|
||||
label: "test2",
|
||||
value: "valueTest2",
|
||||
},
|
||||
],
|
||||
type: WidgetTypes.DROP_DOWN_WIDGET,
|
||||
},
|
||||
Table1: {
|
||||
...BASE_WIDGET,
|
||||
tableData: "{{Api1.data.map(datum => ({ ...datum, raw: Text1.text }) )}}",
|
||||
dynamicBindingPathList: [{ key: "tableData" }],
|
||||
type: WidgetTypes.TABLE_WIDGET,
|
||||
},
|
||||
Text4: {
|
||||
...BASE_WIDGET,
|
||||
text: "{{Table1.selectedRow.test}}",
|
||||
dynamicBindingPathList: [{ key: "text" }],
|
||||
type: WidgetTypes.TEXT_WIDGET,
|
||||
},
|
||||
};
|
||||
const evaluator = new DataTreeEvaluator(WIDGET_CONFIG_MAP);
|
||||
evaluator.createFirstTree(unEvalTree);
|
||||
it("Evaluates a binding in first run", () => {
|
||||
const evaluation = evaluator.evalTree;
|
||||
const dependencyMap = evaluator.dependencyMap;
|
||||
|
||||
expect(evaluation).toHaveProperty("Text2.text", "Label");
|
||||
expect(evaluation).toHaveProperty("Text3.text", "Label");
|
||||
expect(dependencyMap).toStrictEqual({
|
||||
Text1: ["Text1.text"],
|
||||
Text2: ["Text2.text"],
|
||||
Text3: ["Text3.text"],
|
||||
Text4: ["Text4.text"],
|
||||
Table1: [
|
||||
"Table1.tableData",
|
||||
"Table1.searchText",
|
||||
"Table1.selectedRowIndex",
|
||||
"Table1.selectedRowIndices",
|
||||
],
|
||||
Dropdown1: [
|
||||
"Dropdown1.selectedOptionValue",
|
||||
"Dropdown1.selectedOptionValueArr",
|
||||
],
|
||||
"Text2.text": ["Text1.text"],
|
||||
"Text3.text": ["Text1.text"],
|
||||
"Dropdown1.selectedOptionValue": [],
|
||||
"Dropdown1.selectedOptionValueArr": [],
|
||||
"Table1.tableData": ["Text1.text"],
|
||||
"Table1.searchText": [],
|
||||
"Table1.selectedRowIndex": [],
|
||||
"Table1.selectedRowIndices": [],
|
||||
"Text4.text": [],
|
||||
});
|
||||
});
|
||||
|
||||
it("Evaluates a value change in update run", () => {
|
||||
const updatedUnEvalTree = {
|
||||
...unEvalTree,
|
||||
Text1: {
|
||||
...unEvalTree.Text1,
|
||||
text: "Hey there",
|
||||
},
|
||||
};
|
||||
const updatedEvalTree = evaluator.updateDataTree(updatedUnEvalTree);
|
||||
expect(updatedEvalTree).toHaveProperty("Text2.text", "Hey there");
|
||||
expect(updatedEvalTree).toHaveProperty("Text3.text", "Hey there");
|
||||
});
|
||||
|
||||
it("Evaluates a dependency change in update run", () => {
|
||||
const updatedUnEvalTree = {
|
||||
...unEvalTree,
|
||||
Text3: {
|
||||
...unEvalTree.Text3,
|
||||
text: "Label 3",
|
||||
},
|
||||
};
|
||||
const updatedEvalTree = evaluator.updateDataTree(updatedUnEvalTree);
|
||||
const updatedDependencyMap = evaluator.dependencyMap;
|
||||
expect(updatedEvalTree).toHaveProperty("Text2.text", "Label");
|
||||
expect(updatedEvalTree).toHaveProperty("Text3.text", "Label 3");
|
||||
expect(updatedDependencyMap).toStrictEqual({
|
||||
Text1: ["Text1.text"],
|
||||
Text2: ["Text2.text"],
|
||||
Text3: ["Text3.text"],
|
||||
Text4: ["Text4.text"],
|
||||
Table1: [
|
||||
"Table1.tableData",
|
||||
"Table1.searchText",
|
||||
"Table1.selectedRowIndex",
|
||||
"Table1.selectedRowIndices",
|
||||
],
|
||||
Dropdown1: [
|
||||
"Dropdown1.selectedOptionValue",
|
||||
"Dropdown1.selectedOptionValueArr",
|
||||
],
|
||||
"Text2.text": ["Text1.text"],
|
||||
"Dropdown1.selectedOptionValue": [],
|
||||
"Dropdown1.selectedOptionValueArr": [],
|
||||
"Table1.tableData": ["Text1.text"],
|
||||
"Table1.searchText": [],
|
||||
"Table1.selectedRowIndex": [],
|
||||
"Table1.selectedRowIndices": [],
|
||||
"Text4.text": [],
|
||||
});
|
||||
});
|
||||
|
||||
it("Overrides with default value", () => {
|
||||
const updatedUnEvalTree = {
|
||||
...unEvalTree,
|
||||
Input1: {
|
||||
...BASE_WIDGET,
|
||||
text: undefined,
|
||||
defaultText: "Default value",
|
||||
widgetName: "Input1",
|
||||
type: WidgetTypes.INPUT_WIDGET,
|
||||
},
|
||||
};
|
||||
|
||||
const updatedEvalTree = evaluator.updateDataTree(updatedUnEvalTree);
|
||||
expect(updatedEvalTree).toHaveProperty("Input1.text", "Default value");
|
||||
});
|
||||
|
||||
it("Evaluates for value changes in nested diff paths", () => {
|
||||
const updatedUnEvalTree = {
|
||||
...unEvalTree,
|
||||
Dropdown1: {
|
||||
...BASE_WIDGET,
|
||||
options: [
|
||||
{
|
||||
label: "newValue",
|
||||
value: "valueTest",
|
||||
},
|
||||
{
|
||||
label: "test2",
|
||||
value: "valueTest2",
|
||||
},
|
||||
],
|
||||
type: WidgetTypes.DROP_DOWN_WIDGET,
|
||||
},
|
||||
};
|
||||
const updatedEvalTree = evaluator.updateDataTree(updatedUnEvalTree);
|
||||
expect(updatedEvalTree).toHaveProperty(
|
||||
"Dropdown1.options.0.label",
|
||||
"newValue",
|
||||
);
|
||||
});
|
||||
|
||||
it("Adds an entity with a complicated binding", () => {
|
||||
const updatedUnEvalTree = {
|
||||
...unEvalTree,
|
||||
Api1: {
|
||||
...BASE_ACTION,
|
||||
data: [
|
||||
{
|
||||
test: "Hey",
|
||||
},
|
||||
{
|
||||
test: "Ho",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
const updatedEvalTree = evaluator.updateDataTree(updatedUnEvalTree);
|
||||
const updatedDependencyMap = evaluator.dependencyMap;
|
||||
expect(updatedEvalTree).toHaveProperty("Table1.tableData", [
|
||||
{
|
||||
test: "Hey",
|
||||
raw: "Label",
|
||||
},
|
||||
{
|
||||
test: "Ho",
|
||||
raw: "Label",
|
||||
},
|
||||
]);
|
||||
expect(updatedDependencyMap).toStrictEqual({
|
||||
Api1: ["Api1.data"],
|
||||
Input1: ["Input1.text"],
|
||||
Text1: ["Text1.text"],
|
||||
Text2: ["Text2.text"],
|
||||
Text3: ["Text3.text"],
|
||||
Text4: ["Text4.text"],
|
||||
Table1: [
|
||||
"Table1.tableData",
|
||||
"Table1.searchText",
|
||||
"Table1.selectedRowIndex",
|
||||
"Table1.selectedRowIndices",
|
||||
],
|
||||
Dropdown1: [
|
||||
"Dropdown1.selectedOptionValue",
|
||||
"Dropdown1.selectedOptionValueArr",
|
||||
],
|
||||
"Text2.text": ["Text1.text"],
|
||||
"Text3.text": ["Text1.text"],
|
||||
"Dropdown1.selectedOptionValue": [],
|
||||
"Dropdown1.selectedOptionValueArr": [],
|
||||
"Table1.tableData": ["Api1.data", "Text1.text"],
|
||||
"Table1.searchText": [],
|
||||
"Table1.selectedRowIndex": [],
|
||||
"Table1.selectedRowIndices": [],
|
||||
"Text4.text": [],
|
||||
"Input1.text": [],
|
||||
});
|
||||
});
|
||||
|
||||
it("Selects a row", () => {
|
||||
const updatedUnEvalTree = {
|
||||
...unEvalTree,
|
||||
Table1: {
|
||||
...unEvalTree.Table1,
|
||||
selectedRowIndex: 0,
|
||||
selectedRow: {
|
||||
test: "Hey",
|
||||
raw: "Label",
|
||||
},
|
||||
},
|
||||
Api1: {
|
||||
...BASE_ACTION,
|
||||
data: [
|
||||
{
|
||||
test: "Hey",
|
||||
},
|
||||
{
|
||||
test: "Ho",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
const updatedEvalTree = evaluator.updateDataTree(updatedUnEvalTree);
|
||||
const updatedDependencyMap = evaluator.dependencyMap;
|
||||
expect(updatedEvalTree).toHaveProperty("Table1.tableData", [
|
||||
{
|
||||
test: "Hey",
|
||||
raw: "Label",
|
||||
},
|
||||
{
|
||||
test: "Ho",
|
||||
raw: "Label",
|
||||
},
|
||||
]);
|
||||
expect(updatedEvalTree).toHaveProperty("Text4.text", "Hey");
|
||||
expect(updatedDependencyMap).toStrictEqual({
|
||||
Api1: ["Api1.data"],
|
||||
Text1: ["Text1.text"],
|
||||
Text2: ["Text2.text"],
|
||||
Text3: ["Text3.text"],
|
||||
Text4: ["Text4.text"],
|
||||
Table1: [
|
||||
"Table1.tableData",
|
||||
"Table1.selectedRowIndex",
|
||||
"Table1.searchText",
|
||||
"Table1.selectedRowIndices",
|
||||
"Table1.selectedRow",
|
||||
],
|
||||
"Table1.selectedRow": ["Table1.selectedRow.test"],
|
||||
Dropdown1: [
|
||||
"Dropdown1.selectedOptionValue",
|
||||
"Dropdown1.selectedOptionValueArr",
|
||||
],
|
||||
Input1: ["Input1.text"],
|
||||
"Text2.text": ["Text1.text"],
|
||||
"Text3.text": ["Text1.text"],
|
||||
"Dropdown1.selectedOptionValue": [],
|
||||
"Dropdown1.selectedOptionValueArr": [],
|
||||
"Table1.tableData": ["Api1.data", "Text1.text"],
|
||||
"Table1.searchText": [],
|
||||
"Table1.selectedRowIndex": [],
|
||||
"Table1.selectedRowIndices": [],
|
||||
"Text4.text": ["Table1.selectedRow.test"],
|
||||
"Input1.text": [],
|
||||
});
|
||||
});
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
299
app/client/src/workers/evaluationUtils.ts
Normal file
299
app/client/src/workers/evaluationUtils.ts
Normal file
|
|
@ -0,0 +1,299 @@
|
|||
import { DependencyMap, isDynamicValue } from "../utils/DynamicBindingUtils";
|
||||
import { WidgetType } from "../constants/WidgetConstants";
|
||||
import { WidgetProps } from "../widgets/BaseWidget";
|
||||
import { WidgetTypeConfigMap } from "../utils/WidgetFactory";
|
||||
import { VALIDATORS } from "./validations";
|
||||
import { Diff } from "deep-diff";
|
||||
import {
|
||||
DataTree,
|
||||
DataTreeEntity,
|
||||
DataTreeWidget,
|
||||
ENTITY_TYPE,
|
||||
} from "../entities/DataTree/dataTreeFactory";
|
||||
import _ from "lodash";
|
||||
|
||||
export enum DataTreeDiffEvent {
|
||||
NEW = "NEW",
|
||||
DELETE = "DELETE",
|
||||
EDIT = "EDIT",
|
||||
NOOP = "NOOP",
|
||||
}
|
||||
|
||||
type DataTreeDiff = {
|
||||
payload: {
|
||||
propertyPath: string;
|
||||
value?: string;
|
||||
};
|
||||
event: DataTreeDiffEvent;
|
||||
};
|
||||
|
||||
export class CrashingError extends Error {}
|
||||
|
||||
export const convertPathToString = (arrPath: Array<string | number>) => {
|
||||
let string = "";
|
||||
arrPath.forEach((segment) => {
|
||||
if (typeof segment === "string") {
|
||||
if (string.length !== 0) {
|
||||
string = string + ".";
|
||||
}
|
||||
string = string + segment;
|
||||
} else {
|
||||
string = string + "[" + segment + "]";
|
||||
}
|
||||
});
|
||||
return string;
|
||||
};
|
||||
|
||||
export const translateDiffEventToDataTreeDiffEvent = (
|
||||
difference: Diff<any, any>,
|
||||
): DataTreeDiff => {
|
||||
const result: DataTreeDiff = {
|
||||
payload: {
|
||||
propertyPath: "",
|
||||
value: "",
|
||||
},
|
||||
event: DataTreeDiffEvent.NOOP,
|
||||
};
|
||||
if (!difference.path) {
|
||||
return result;
|
||||
}
|
||||
const propertyPath = convertPathToString(difference.path);
|
||||
switch (difference.kind) {
|
||||
case "N": {
|
||||
result.event = DataTreeDiffEvent.NEW;
|
||||
result.payload = {
|
||||
propertyPath,
|
||||
};
|
||||
break;
|
||||
}
|
||||
case "D": {
|
||||
result.event = DataTreeDiffEvent.DELETE;
|
||||
result.payload = { propertyPath };
|
||||
break;
|
||||
}
|
||||
case "E": {
|
||||
const rhsChange =
|
||||
typeof difference.rhs === "string" && isDynamicValue(difference.rhs);
|
||||
|
||||
const lhsChange =
|
||||
typeof difference.lhs === "string" && isDynamicValue(difference.lhs);
|
||||
|
||||
if (rhsChange || lhsChange) {
|
||||
result.event = DataTreeDiffEvent.EDIT;
|
||||
result.payload = {
|
||||
propertyPath,
|
||||
value: difference.rhs,
|
||||
};
|
||||
} else {
|
||||
// Handle static value changes that change structure that can lead to
|
||||
// old bindings being eligible
|
||||
if (
|
||||
difference.lhs === undefined &&
|
||||
typeof difference.rhs === "object"
|
||||
) {
|
||||
result.event = DataTreeDiffEvent.NEW;
|
||||
result.payload = { propertyPath };
|
||||
}
|
||||
if (
|
||||
difference.rhs === undefined &&
|
||||
typeof difference.lhs === "object"
|
||||
) {
|
||||
result.event = DataTreeDiffEvent.DELETE;
|
||||
result.payload = { propertyPath };
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case "A": {
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
export const isPropertyPathOrNestedPath = (
|
||||
path: string,
|
||||
comparePath: string,
|
||||
): boolean => {
|
||||
return path === comparePath || comparePath.startsWith(`${path}.`);
|
||||
};
|
||||
|
||||
/*
|
||||
Table1.selectedRow
|
||||
Table1.selectedRow.email: ["Input1.defaultText"]
|
||||
*/
|
||||
|
||||
export const addDependantsOfNestedPropertyPaths = (
|
||||
parentPaths: Array<string>,
|
||||
inverseMap: DependencyMap,
|
||||
): Array<string> => {
|
||||
const withNestedPaths: Set<string> = new Set();
|
||||
const dependantNodes = Object.keys(inverseMap);
|
||||
parentPaths.forEach((propertyPath) => {
|
||||
withNestedPaths.add(propertyPath);
|
||||
dependantNodes
|
||||
.filter((dependantNodePath) =>
|
||||
isPropertyPathOrNestedPath(propertyPath, dependantNodePath),
|
||||
)
|
||||
.forEach((dependantNodePath) => {
|
||||
inverseMap[dependantNodePath].forEach((path) => {
|
||||
withNestedPaths.add(path);
|
||||
});
|
||||
});
|
||||
});
|
||||
return [...withNestedPaths.values()];
|
||||
};
|
||||
|
||||
export function isWidget(entity: DataTreeEntity): boolean {
|
||||
return (
|
||||
typeof entity === "object" &&
|
||||
"ENTITY_TYPE" in entity &&
|
||||
entity.ENTITY_TYPE === ENTITY_TYPE.WIDGET
|
||||
);
|
||||
}
|
||||
|
||||
export function isAction(entity: DataTreeEntity): boolean {
|
||||
return (
|
||||
typeof entity === "object" &&
|
||||
"ENTITY_TYPE" in entity &&
|
||||
entity.ENTITY_TYPE === ENTITY_TYPE.ACTION
|
||||
);
|
||||
}
|
||||
|
||||
// We need to remove functions from data tree to avoid any unexpected identifier while JSON parsing
|
||||
// Check issue https://github.com/appsmithorg/appsmith/issues/719
|
||||
export const removeFunctions = (value: any) => {
|
||||
if (_.isFunction(value)) {
|
||||
return "Function call";
|
||||
} else if (_.isObject(value) && _.some(value, _.isFunction)) {
|
||||
return JSON.parse(JSON.stringify(value));
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
export const removeFunctionsFromDataTree = (dataTree: DataTree) => {
|
||||
dataTree.actionPaths?.forEach((functionPath) => {
|
||||
_.set(dataTree, functionPath, {});
|
||||
});
|
||||
delete dataTree.actionPaths;
|
||||
return dataTree;
|
||||
};
|
||||
|
||||
export const makeParentsDependOnChildren = (
|
||||
depMap: DependencyMap,
|
||||
): DependencyMap => {
|
||||
//return depMap;
|
||||
// Make all parents depend on child
|
||||
Object.keys(depMap).forEach((key) => {
|
||||
depMap = makeParentsDependOnChild(depMap, key);
|
||||
depMap[key].forEach((path) => {
|
||||
depMap = makeParentsDependOnChild(depMap, path);
|
||||
});
|
||||
});
|
||||
return depMap;
|
||||
};
|
||||
export const makeParentsDependOnChild = (
|
||||
depMap: DependencyMap,
|
||||
child: string,
|
||||
): DependencyMap => {
|
||||
const result: DependencyMap = depMap;
|
||||
let curKey = child;
|
||||
const rgx = /^(.*)\..*$/;
|
||||
let matches: Array<string> | null;
|
||||
// Note: The `=` is intentional
|
||||
// Stops looping when match is null
|
||||
while ((matches = curKey.match(rgx)) !== null) {
|
||||
const parentKey = matches[1];
|
||||
// Todo: switch everything to set.
|
||||
const existing = new Set(result[parentKey] || []);
|
||||
existing.add(curKey);
|
||||
result[parentKey] = Array.from(existing);
|
||||
curKey = parentKey;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
export function validateWidgetProperty(
|
||||
widgetConfigMap: WidgetTypeConfigMap,
|
||||
widgetType: WidgetType,
|
||||
property: string,
|
||||
value: any,
|
||||
props: WidgetProps,
|
||||
dataTree?: DataTree,
|
||||
) {
|
||||
const propertyValidationTypes = widgetConfigMap[widgetType].validations;
|
||||
const validationTypeOrValidator = propertyValidationTypes[property];
|
||||
let validator;
|
||||
|
||||
if (typeof validationTypeOrValidator === "function") {
|
||||
validator = validationTypeOrValidator;
|
||||
} else {
|
||||
validator = VALIDATORS[validationTypeOrValidator];
|
||||
}
|
||||
if (validator) {
|
||||
return validator(value, props, dataTree);
|
||||
} else {
|
||||
return { isValid: true, parsed: value };
|
||||
}
|
||||
}
|
||||
|
||||
export function getValidatedTree(
|
||||
widgetConfigMap: WidgetTypeConfigMap,
|
||||
tree: DataTree,
|
||||
only?: Set<string>,
|
||||
) {
|
||||
return Object.keys(tree).reduce((tree, entityKey: string) => {
|
||||
if (only && only.size) {
|
||||
if (!only.has(entityKey)) {
|
||||
return tree;
|
||||
}
|
||||
}
|
||||
const entity = tree[entityKey] as DataTreeWidget;
|
||||
if (!isWidget(entity)) {
|
||||
return tree;
|
||||
}
|
||||
const parsedEntity = { ...entity };
|
||||
Object.keys(entity).forEach((property: string) => {
|
||||
const validationProperties = widgetConfigMap[entity.type].validations;
|
||||
|
||||
if (property in validationProperties) {
|
||||
const value = _.get(entity, property);
|
||||
// Pass it through parse
|
||||
const {
|
||||
parsed,
|
||||
isValid,
|
||||
message,
|
||||
transformed,
|
||||
} = validateWidgetProperty(
|
||||
widgetConfigMap,
|
||||
entity.type,
|
||||
property,
|
||||
value,
|
||||
entity,
|
||||
tree,
|
||||
);
|
||||
parsedEntity[property] = parsed;
|
||||
const evaluatedValue = isValid
|
||||
? parsed
|
||||
: _.isUndefined(transformed)
|
||||
? value
|
||||
: transformed;
|
||||
const safeEvaluatedValue = removeFunctions(evaluatedValue);
|
||||
_.set(parsedEntity, `evaluatedValues.${property}`, safeEvaluatedValue);
|
||||
if (!isValid) {
|
||||
_.set(parsedEntity, `invalidProps.${property}`, true);
|
||||
_.set(parsedEntity, `validationMessages.${property}`, message);
|
||||
} else {
|
||||
_.set(parsedEntity, `invalidProps.${property}`, false);
|
||||
_.set(parsedEntity, `validationMessages.${property}`, "");
|
||||
}
|
||||
}
|
||||
});
|
||||
return { ...tree, [entityKey]: parsedEntity };
|
||||
}, tree);
|
||||
}
|
||||
646
app/client/src/workers/validations.ts
Normal file
646
app/client/src/workers/validations.ts
Normal file
|
|
@ -0,0 +1,646 @@
|
|||
import {
|
||||
ISO_DATE_FORMAT,
|
||||
VALIDATION_TYPES,
|
||||
ValidationResponse,
|
||||
ValidationType,
|
||||
Validator,
|
||||
} from "../constants/WidgetValidation";
|
||||
import { DataTree } from "../entities/DataTree/dataTreeFactory";
|
||||
import _, {
|
||||
every,
|
||||
isBoolean,
|
||||
isNumber,
|
||||
isObject,
|
||||
isString,
|
||||
isUndefined,
|
||||
toNumber,
|
||||
toString,
|
||||
} from "lodash";
|
||||
import { WidgetProps } from "../widgets/BaseWidget";
|
||||
import { WIDGET_TYPE_VALIDATION_ERROR } from "../constants/messages";
|
||||
import moment from "moment";
|
||||
|
||||
export const VALIDATORS: Record<ValidationType, Validator> = {
|
||||
[VALIDATION_TYPES.TEXT]: (value: any): ValidationResponse => {
|
||||
let parsed = value;
|
||||
if (isUndefined(value) || value === null) {
|
||||
return {
|
||||
isValid: true,
|
||||
parsed: value,
|
||||
message: "",
|
||||
};
|
||||
}
|
||||
if (isObject(value)) {
|
||||
return {
|
||||
isValid: false,
|
||||
parsed: JSON.stringify(value, null, 2),
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: text`,
|
||||
};
|
||||
}
|
||||
let isValid = isString(value);
|
||||
if (!isValid) {
|
||||
try {
|
||||
parsed = toString(value);
|
||||
isValid = true;
|
||||
} catch (e) {
|
||||
console.error(`Error when parsing ${value} to string`);
|
||||
console.error(e);
|
||||
return {
|
||||
isValid: false,
|
||||
parsed: "",
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: text`,
|
||||
};
|
||||
}
|
||||
}
|
||||
return { isValid, parsed };
|
||||
},
|
||||
[VALIDATION_TYPES.REGEX]: (
|
||||
value: any,
|
||||
props: WidgetProps,
|
||||
dataTree?: DataTree,
|
||||
): ValidationResponse => {
|
||||
const { isValid, parsed, message } = VALIDATORS[VALIDATION_TYPES.TEXT](
|
||||
value,
|
||||
props,
|
||||
dataTree,
|
||||
);
|
||||
|
||||
if (isValid) {
|
||||
try {
|
||||
new RegExp(parsed);
|
||||
} catch (e) {
|
||||
return {
|
||||
isValid: false,
|
||||
parsed: parsed,
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: regex`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return { isValid, parsed, message };
|
||||
},
|
||||
[VALIDATION_TYPES.NUMBER]: (value: any): ValidationResponse => {
|
||||
let parsed = value;
|
||||
if (isUndefined(value)) {
|
||||
return {
|
||||
isValid: false,
|
||||
parsed: 0,
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: number`,
|
||||
};
|
||||
}
|
||||
let isValid = isNumber(value);
|
||||
if (!isValid) {
|
||||
try {
|
||||
parsed = toNumber(value);
|
||||
if (isNaN(parsed)) {
|
||||
return {
|
||||
isValid: false,
|
||||
parsed: 0,
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: number`,
|
||||
};
|
||||
}
|
||||
isValid = true;
|
||||
} catch (e) {
|
||||
console.error(`Error when parsing ${value} to number`);
|
||||
console.error(e);
|
||||
return {
|
||||
isValid: false,
|
||||
parsed: 0,
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: number`,
|
||||
};
|
||||
}
|
||||
}
|
||||
return { isValid, parsed };
|
||||
},
|
||||
[VALIDATION_TYPES.BOOLEAN]: (value: any): ValidationResponse => {
|
||||
let parsed = value;
|
||||
if (isUndefined(value)) {
|
||||
return {
|
||||
isValid: false,
|
||||
parsed: false,
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: boolean`,
|
||||
};
|
||||
}
|
||||
const isABoolean = isBoolean(value);
|
||||
const isStringTrueFalse = value === "true" || value === "false";
|
||||
const isValid = isABoolean || isStringTrueFalse;
|
||||
if (isStringTrueFalse) parsed = value !== "false";
|
||||
if (!isValid) {
|
||||
return {
|
||||
isValid: isValid,
|
||||
parsed: parsed,
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: boolean`,
|
||||
};
|
||||
}
|
||||
return { isValid, parsed };
|
||||
},
|
||||
[VALIDATION_TYPES.OBJECT]: (value: any): ValidationResponse => {
|
||||
let parsed = value;
|
||||
if (isUndefined(value)) {
|
||||
return {
|
||||
isValid: false,
|
||||
parsed: {},
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: Object`,
|
||||
};
|
||||
}
|
||||
let isValid = isObject(value);
|
||||
if (!isValid) {
|
||||
try {
|
||||
parsed = JSON.parse(value);
|
||||
isValid = true;
|
||||
} catch (e) {
|
||||
console.error(`Error when parsing ${value} to object`);
|
||||
console.error(e);
|
||||
return {
|
||||
isValid: false,
|
||||
parsed: {},
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: Object`,
|
||||
};
|
||||
}
|
||||
}
|
||||
return { isValid, parsed };
|
||||
},
|
||||
[VALIDATION_TYPES.ARRAY]: (value: any): ValidationResponse => {
|
||||
let parsed = value;
|
||||
try {
|
||||
if (isUndefined(value)) {
|
||||
return {
|
||||
isValid: false,
|
||||
parsed: [],
|
||||
transformed: undefined,
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: Array/List`,
|
||||
};
|
||||
}
|
||||
if (isString(value)) {
|
||||
parsed = JSON.parse(parsed as string);
|
||||
}
|
||||
if (!Array.isArray(parsed)) {
|
||||
return {
|
||||
isValid: false,
|
||||
parsed: [],
|
||||
transformed: parsed,
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: Array/List`,
|
||||
};
|
||||
}
|
||||
return { isValid: true, parsed, transformed: parsed };
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return {
|
||||
isValid: false,
|
||||
parsed: [],
|
||||
transformed: parsed,
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: Array/List`,
|
||||
};
|
||||
}
|
||||
},
|
||||
[VALIDATION_TYPES.TABS_DATA]: (
|
||||
value: any,
|
||||
props: WidgetProps,
|
||||
dataTree?: DataTree,
|
||||
): ValidationResponse => {
|
||||
const { isValid, parsed } = VALIDATORS[VALIDATION_TYPES.ARRAY](
|
||||
value,
|
||||
props,
|
||||
dataTree,
|
||||
);
|
||||
if (!isValid) {
|
||||
return {
|
||||
isValid,
|
||||
parsed,
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: Tabs Data`,
|
||||
};
|
||||
} else if (!every(parsed, (datum) => isObject(datum))) {
|
||||
return {
|
||||
isValid: false,
|
||||
parsed: [],
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: Tabs Data`,
|
||||
};
|
||||
}
|
||||
return { isValid, parsed };
|
||||
},
|
||||
[VALIDATION_TYPES.TABLE_DATA]: (
|
||||
value: any,
|
||||
props: WidgetProps,
|
||||
dataTree?: DataTree,
|
||||
): ValidationResponse => {
|
||||
const { isValid, transformed, parsed } = VALIDATORS.ARRAY(
|
||||
value,
|
||||
props,
|
||||
dataTree,
|
||||
);
|
||||
if (!isValid) {
|
||||
return {
|
||||
isValid,
|
||||
parsed: [],
|
||||
transformed,
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: [{ "Col1" : "val1", "Col2" : "val2" }]`,
|
||||
};
|
||||
}
|
||||
const isValidTableData = every(parsed, (datum) => {
|
||||
return (
|
||||
isObject(datum) &&
|
||||
Object.keys(datum).filter((key) => isString(key) && key.length === 0)
|
||||
.length === 0
|
||||
);
|
||||
});
|
||||
if (!isValidTableData) {
|
||||
return {
|
||||
isValid: false,
|
||||
parsed: [],
|
||||
transformed,
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: [{ "Col1" : "val1", "Col2" : "val2" }]`,
|
||||
};
|
||||
}
|
||||
return { isValid, parsed };
|
||||
},
|
||||
[VALIDATION_TYPES.CHART_DATA]: (
|
||||
value: any,
|
||||
props: WidgetProps,
|
||||
dataTree?: DataTree,
|
||||
): ValidationResponse => {
|
||||
const { isValid, parsed } = VALIDATORS[VALIDATION_TYPES.ARRAY](
|
||||
value,
|
||||
props,
|
||||
dataTree,
|
||||
);
|
||||
if (!isValid) {
|
||||
return {
|
||||
isValid,
|
||||
parsed,
|
||||
transformed: parsed,
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: Chart Data`,
|
||||
};
|
||||
}
|
||||
let validationMessage = "";
|
||||
let index = 0;
|
||||
const isValidChartData = every(
|
||||
parsed,
|
||||
(datum: { name: string; data: any }) => {
|
||||
const validatedResponse: {
|
||||
isValid: boolean;
|
||||
parsed: Array<unknown>;
|
||||
message?: string;
|
||||
} = VALIDATORS[VALIDATION_TYPES.ARRAY](datum.data, props, dataTree);
|
||||
validationMessage = `${index}##${WIDGET_TYPE_VALIDATION_ERROR}: [{ "x": "val", "y": "val" }]`;
|
||||
let isValidChart = validatedResponse.isValid;
|
||||
if (validatedResponse.isValid) {
|
||||
datum.data = validatedResponse.parsed;
|
||||
isValidChart = every(
|
||||
datum.data,
|
||||
(chartPoint: { x: string; y: any }) => {
|
||||
return (
|
||||
isObject(chartPoint) &&
|
||||
isString(chartPoint.x) &&
|
||||
!isUndefined(chartPoint.y)
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
index++;
|
||||
return isValidChart;
|
||||
},
|
||||
);
|
||||
if (!isValidChartData) {
|
||||
return {
|
||||
isValid: false,
|
||||
parsed: [],
|
||||
transformed: parsed,
|
||||
message: validationMessage,
|
||||
};
|
||||
}
|
||||
return { isValid, parsed, transformed: parsed };
|
||||
},
|
||||
[VALIDATION_TYPES.MARKERS]: (
|
||||
value: any,
|
||||
props: WidgetProps,
|
||||
dataTree?: DataTree,
|
||||
): ValidationResponse => {
|
||||
const { isValid, parsed } = VALIDATORS[VALIDATION_TYPES.ARRAY](
|
||||
value,
|
||||
props,
|
||||
dataTree,
|
||||
);
|
||||
if (!isValid) {
|
||||
return {
|
||||
isValid,
|
||||
parsed,
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: Marker Data`,
|
||||
};
|
||||
} else if (!every(parsed, (datum) => isObject(datum))) {
|
||||
return {
|
||||
isValid: false,
|
||||
parsed: [],
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: Marker Data`,
|
||||
};
|
||||
}
|
||||
return { isValid, parsed };
|
||||
},
|
||||
[VALIDATION_TYPES.OPTIONS_DATA]: (
|
||||
value: any,
|
||||
props: WidgetProps,
|
||||
dataTree?: DataTree,
|
||||
): ValidationResponse => {
|
||||
const { isValid, parsed } = VALIDATORS[VALIDATION_TYPES.ARRAY](
|
||||
value,
|
||||
props,
|
||||
dataTree,
|
||||
);
|
||||
if (!isValid) {
|
||||
return {
|
||||
isValid,
|
||||
parsed,
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: Options Data`,
|
||||
};
|
||||
}
|
||||
try {
|
||||
const isValidOption = (option: { label: any; value: any }) =>
|
||||
_.isObject(option) &&
|
||||
_.isString(option.label) &&
|
||||
_.isString(option.value) &&
|
||||
!_.isEmpty(option.label) &&
|
||||
!_.isEmpty(option.value);
|
||||
|
||||
const hasOptions = every(parsed, isValidOption);
|
||||
const validOptions = parsed.filter(isValidOption);
|
||||
const uniqValidOptions = _.uniqBy(validOptions, "value");
|
||||
|
||||
if (!hasOptions || uniqValidOptions.length !== validOptions.length) {
|
||||
return {
|
||||
isValid: false,
|
||||
parsed: uniqValidOptions,
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: Options Data`,
|
||||
};
|
||||
}
|
||||
return { isValid, parsed };
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return {
|
||||
isValid: false,
|
||||
parsed: [],
|
||||
transformed: parsed,
|
||||
};
|
||||
}
|
||||
},
|
||||
[VALIDATION_TYPES.DATE]: (
|
||||
dateString: string,
|
||||
props: WidgetProps,
|
||||
): ValidationResponse => {
|
||||
const today = moment()
|
||||
.hour(0)
|
||||
.minute(0)
|
||||
.second(0)
|
||||
.millisecond(0);
|
||||
const dateFormat = props.dateFormat ? props.dateFormat : ISO_DATE_FORMAT;
|
||||
|
||||
const todayDateString = today.format(dateFormat);
|
||||
if (dateString === undefined) {
|
||||
return {
|
||||
isValid: false,
|
||||
parsed: "",
|
||||
message:
|
||||
`${WIDGET_TYPE_VALIDATION_ERROR}: Date ` + props.dateFormat
|
||||
? props.dateFormat
|
||||
: "",
|
||||
};
|
||||
}
|
||||
const isValid = moment(dateString, dateFormat).isValid();
|
||||
const parsed = isValid ? dateString : todayDateString;
|
||||
return {
|
||||
isValid,
|
||||
parsed,
|
||||
message: isValid ? "" : `${WIDGET_TYPE_VALIDATION_ERROR}: Date`,
|
||||
};
|
||||
},
|
||||
[VALIDATION_TYPES.DEFAULT_DATE]: (
|
||||
dateString: string,
|
||||
props: WidgetProps,
|
||||
): ValidationResponse => {
|
||||
const today = moment()
|
||||
.hour(0)
|
||||
.minute(0)
|
||||
.second(0)
|
||||
.millisecond(0);
|
||||
const dateFormat = props.dateFormat ? props.dateFormat : ISO_DATE_FORMAT;
|
||||
|
||||
const todayDateString = today.format(dateFormat);
|
||||
if (dateString === undefined) {
|
||||
return {
|
||||
isValid: false,
|
||||
parsed: "",
|
||||
message:
|
||||
`${WIDGET_TYPE_VALIDATION_ERROR}: Date ` + props.dateFormat
|
||||
? props.dateFormat
|
||||
: "",
|
||||
};
|
||||
}
|
||||
const parsedCurrentDate = moment(dateString, dateFormat);
|
||||
let isValid = parsedCurrentDate.isValid();
|
||||
const parsedMinDate = moment(props.minDate, dateFormat);
|
||||
const parsedMaxDate = moment(props.maxDate, dateFormat);
|
||||
|
||||
// checking for max/min date range
|
||||
if (isValid) {
|
||||
if (
|
||||
parsedMinDate.isValid() &&
|
||||
parsedCurrentDate.isBefore(parsedMinDate)
|
||||
) {
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
if (
|
||||
isValid &&
|
||||
parsedMaxDate.isValid() &&
|
||||
parsedCurrentDate.isAfter(parsedMaxDate)
|
||||
) {
|
||||
isValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
const parsed = isValid ? dateString : todayDateString;
|
||||
|
||||
return {
|
||||
isValid,
|
||||
parsed,
|
||||
message: isValid ? "" : `${WIDGET_TYPE_VALIDATION_ERROR}: Date R`,
|
||||
};
|
||||
},
|
||||
[VALIDATION_TYPES.ACTION_SELECTOR]: (value: any): ValidationResponse => {
|
||||
if (Array.isArray(value) && value.length) {
|
||||
return {
|
||||
isValid: true,
|
||||
parsed: undefined,
|
||||
transformed: "Function Call",
|
||||
};
|
||||
}
|
||||
/*
|
||||
if (_.isString(value)) {
|
||||
if (value.indexOf("navigateTo") !== -1) {
|
||||
const pageNameOrUrl = modalGetter(value);
|
||||
if (dataTree) {
|
||||
if (isDynamicValue(pageNameOrUrl)) {
|
||||
return {
|
||||
isValid: true,
|
||||
parsed: value,
|
||||
};
|
||||
}
|
||||
const isPage =
|
||||
(dataTree.pageList as PageListPayload).findIndex(
|
||||
page => page.pageName === pageNameOrUrl,
|
||||
) !== -1;
|
||||
const isValidUrl = URL_REGEX.test(pageNameOrUrl);
|
||||
if (!(isValidUrl || isPage)) {
|
||||
return {
|
||||
isValid: false,
|
||||
parsed: value,
|
||||
message: `${NAVIGATE_TO_VALIDATION_ERROR}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
return {
|
||||
isValid: false,
|
||||
parsed: undefined,
|
||||
transformed: "undefined",
|
||||
message: "Not a function call",
|
||||
};
|
||||
},
|
||||
[VALIDATION_TYPES.ARRAY_ACTION_SELECTOR]: (
|
||||
value: any,
|
||||
props: WidgetProps,
|
||||
dataTree?: DataTree,
|
||||
): ValidationResponse => {
|
||||
const { isValid, parsed, message } = VALIDATORS[VALIDATION_TYPES.ARRAY](
|
||||
value,
|
||||
props,
|
||||
dataTree,
|
||||
);
|
||||
let isValidFinal = isValid;
|
||||
let finalParsed = parsed.slice();
|
||||
if (isValid) {
|
||||
finalParsed = parsed.map((value: any) => {
|
||||
const { isValid, message } = VALIDATORS[
|
||||
VALIDATION_TYPES.ACTION_SELECTOR
|
||||
](value.dynamicTrigger, props, dataTree);
|
||||
|
||||
isValidFinal = isValidFinal && isValid;
|
||||
return {
|
||||
...value,
|
||||
message: message,
|
||||
isValid: isValid,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
isValid: isValidFinal,
|
||||
parsed: finalParsed,
|
||||
message: message,
|
||||
};
|
||||
},
|
||||
[VALIDATION_TYPES.SELECTED_TAB]: (
|
||||
value: any,
|
||||
props: WidgetProps,
|
||||
): ValidationResponse => {
|
||||
const tabs =
|
||||
props.tabs && isString(props.tabs)
|
||||
? JSON.parse(props.tabs)
|
||||
: props.tabs && Array.isArray(props.tabs)
|
||||
? props.tabs
|
||||
: [];
|
||||
const tabNames = tabs.map((i: { label: string; id: string }) => i.label);
|
||||
const isValidTabName = tabNames.includes(value);
|
||||
return {
|
||||
isValid: isValidTabName,
|
||||
parsed: value,
|
||||
message: isValidTabName
|
||||
? ""
|
||||
: `${WIDGET_TYPE_VALIDATION_ERROR}: Invalid tab name.`,
|
||||
};
|
||||
},
|
||||
[VALIDATION_TYPES.DEFAULT_OPTION_VALUE]: (
|
||||
value: string | string[],
|
||||
props: WidgetProps,
|
||||
dataTree?: DataTree,
|
||||
) => {
|
||||
let values = value;
|
||||
|
||||
if (props) {
|
||||
if (props.selectionType === "SINGLE_SELECT") {
|
||||
return VALIDATORS[VALIDATION_TYPES.TEXT](value, props, dataTree);
|
||||
} else if (props.selectionType === "MULTI_SELECT") {
|
||||
if (typeof value === "string") {
|
||||
try {
|
||||
values = JSON.parse(value);
|
||||
if (!Array.isArray(values)) {
|
||||
throw new Error();
|
||||
}
|
||||
} catch {
|
||||
values = value.length ? value.split(",") : [];
|
||||
if (values.length > 0) {
|
||||
values = values.map((value) => value.trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(values)) {
|
||||
values = _.uniq(values);
|
||||
}
|
||||
|
||||
return {
|
||||
isValid: true,
|
||||
parsed: values,
|
||||
};
|
||||
},
|
||||
[VALIDATION_TYPES.DEFAULT_SELECTED_ROW]: (
|
||||
value: string | string[],
|
||||
props: WidgetProps,
|
||||
) => {
|
||||
let values = value;
|
||||
|
||||
if (props) {
|
||||
if (props.multiRowSelection) {
|
||||
if (typeof value === "string") {
|
||||
try {
|
||||
values = JSON.parse(value);
|
||||
if (!Array.isArray(values)) {
|
||||
throw new Error();
|
||||
}
|
||||
} catch {
|
||||
values = value.length ? value.split(",") : [];
|
||||
if (values.length > 0) {
|
||||
let numericValues = values.map((value) => {
|
||||
return isNumber(value.trim()) ? -1 : Number(value.trim());
|
||||
});
|
||||
numericValues = _.uniq(numericValues);
|
||||
return {
|
||||
isValid: true,
|
||||
parsed: numericValues,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
const parsed = toNumber(value);
|
||||
return {
|
||||
isValid: true,
|
||||
parsed: parsed,
|
||||
};
|
||||
} catch (e) {
|
||||
return {
|
||||
isValid: true,
|
||||
parsed: -1,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
isValid: true,
|
||||
parsed: values,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
|
@ -3835,6 +3835,11 @@
|
|||
dependencies:
|
||||
"@types/tern" "*"
|
||||
|
||||
"@types/deep-diff@^1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/deep-diff/-/deep-diff-1.0.0.tgz#7eba3202a99b3a207f758f351f7f86387269fc40"
|
||||
integrity sha512-ENsJcujGbCU/oXhDfQ12mSo/mCBWodT2tpARZKmatoSrf8+cGRCPi0KVj3I0FORhYZfLXkewXu7AoIWqiBLkNw==
|
||||
|
||||
"@types/dom4@^2.0.1":
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/dom4/-/dom4-2.0.1.tgz#506d5781b9bcab81bd9a878b198aec7dee2a6033"
|
||||
|
|
@ -7715,6 +7720,11 @@ dedent@^0.7.0:
|
|||
resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c"
|
||||
integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=
|
||||
|
||||
deep-diff@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/deep-diff/-/deep-diff-1.0.2.tgz#afd3d1f749115be965e89c63edc7abb1506b9c26"
|
||||
integrity sha512-aWS3UIVH+NPGCD1kki+DCU9Dua032iSsO43LqQpcs4R3+dVv7tX0qBGjiVHJHjplsoUM2XRO/KB92glqc68awg==
|
||||
|
||||
deep-equal@^1.0.1, deep-equal@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a"
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user