fix: autocomplete fixes & enhancement (#15857)
This commit is contained in:
parent
1660092c79
commit
fd9719aa4e
|
|
@ -2,7 +2,13 @@ import { WIDGET } from "../../../../locators/WidgetLocators";
|
||||||
import { ObjectsRegistry } from "../../../../support/Objects/Registry";
|
import { ObjectsRegistry } from "../../../../support/Objects/Registry";
|
||||||
const explorer = require("../../../../locators/explorerlocators.json");
|
const explorer = require("../../../../locators/explorerlocators.json");
|
||||||
|
|
||||||
const { CommonLocators, EntityExplorer, JSEditor: jsEditor } = ObjectsRegistry;
|
const {
|
||||||
|
AggregateHelper: agHelper,
|
||||||
|
ApiPage,
|
||||||
|
CommonLocators,
|
||||||
|
EntityExplorer,
|
||||||
|
JSEditor: jsEditor,
|
||||||
|
} = ObjectsRegistry;
|
||||||
|
|
||||||
const jsObjectBody = `export default {
|
const jsObjectBody = `export default {
|
||||||
myVar1: [],
|
myVar1: [],
|
||||||
|
|
@ -16,12 +22,11 @@ const jsObjectBody = `export default {
|
||||||
}`;
|
}`;
|
||||||
|
|
||||||
describe("Autocomplete tests", () => {
|
describe("Autocomplete tests", () => {
|
||||||
before(() => {
|
it("1. Verify widgets autocomplete: ButtonGroup & Document viewer widget", () => {
|
||||||
cy.get(explorer.addWidget).click();
|
cy.get(explorer.addWidget).click();
|
||||||
EntityExplorer.DragDropWidgetNVerify(WIDGET.BUTTON_GROUP_WIDGET, 300, 500);
|
EntityExplorer.DragDropWidgetNVerify(WIDGET.BUTTON_GROUP, 200, 200);
|
||||||
});
|
EntityExplorer.DragDropWidgetNVerify(WIDGET.DOCUMENT_VIEWER, 200, 500);
|
||||||
|
|
||||||
it("1. ButtonGroup autocomplete & Eval shouldn't show up", () => {
|
|
||||||
// create js object
|
// create js object
|
||||||
jsEditor.CreateJSObject(jsObjectBody, {
|
jsEditor.CreateJSObject(jsObjectBody, {
|
||||||
paste: true,
|
paste: true,
|
||||||
|
|
@ -30,31 +35,120 @@ describe("Autocomplete tests", () => {
|
||||||
shouldCreateNewJSObj: true,
|
shouldCreateNewJSObj: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const lineNumber = 5;
|
// focus on 5th line
|
||||||
cy.get(`:nth-child(${lineNumber}) > .CodeMirror-line`).click();
|
cy.get(`:nth-child(5) > .CodeMirror-line`).click();
|
||||||
|
|
||||||
|
// 1. Button group widget autocomplete verification
|
||||||
cy.get(CommonLocators._codeMirrorTextArea)
|
cy.get(CommonLocators._codeMirrorTextArea)
|
||||||
.focus()
|
.focus()
|
||||||
.type(`ButtonGroup1.`);
|
.type(`ButtonGroup1.`);
|
||||||
|
|
||||||
cy.get(`.CodeMirror-hints > :nth-child(1)`).contains("groupButtons");
|
agHelper.AssertElementText(CommonLocators._hints, "groupButtons");
|
||||||
|
|
||||||
cy.get(CommonLocators._codeMirrorTextArea)
|
cy.get(CommonLocators._codeMirrorTextArea)
|
||||||
.focus()
|
.focus()
|
||||||
.type(`groupButtons.`);
|
.type(`groupButtons.`);
|
||||||
|
|
||||||
cy.get(`.CodeMirror-hints > :nth-child(1)`).contains("groupButton1");
|
agHelper.AssertElementText(CommonLocators._hints, "groupButton1");
|
||||||
|
|
||||||
cy.get(CommonLocators._codeMirrorTextArea).focus().type(`
|
// 2. Document view widget autocomplete verification
|
||||||
eval`);
|
cy.get(CommonLocators._codeMirrorTextArea)
|
||||||
|
.focus()
|
||||||
|
.type("{backspace}".repeat("ButtonGroup1.groupButtons.".length)) // remove "ButtonGroup1.groupButtons."
|
||||||
|
.wait(20)
|
||||||
|
.type(`DocumentViewer1.`);
|
||||||
|
|
||||||
cy.get(`.CodeMirror-hints > :nth-child(1)`).should(
|
agHelper.AssertElementText(CommonLocators._hints, "docUrl");
|
||||||
"not.have.value",
|
|
||||||
"eval()",
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("2. Local variables autocompletion support", () => {
|
it("2. Verify browser JavaScript APIs in autocomplete ", () => {
|
||||||
|
// create js object
|
||||||
|
jsEditor.CreateJSObject(jsObjectBody, {
|
||||||
|
paste: true,
|
||||||
|
completeReplace: true,
|
||||||
|
toRun: false,
|
||||||
|
shouldCreateNewJSObj: true,
|
||||||
|
prettify: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
// focus on 5th line
|
||||||
|
cy.get(`:nth-child(5) > .CodeMirror-line`).click();
|
||||||
|
|
||||||
|
const JSAPIsToTest = [
|
||||||
|
// console API verification
|
||||||
|
{
|
||||||
|
type: "console",
|
||||||
|
expected: "console",
|
||||||
|
shouldBePresent: true,
|
||||||
|
},
|
||||||
|
// crypto API verification
|
||||||
|
{
|
||||||
|
type: "crypto",
|
||||||
|
expected: "crypto",
|
||||||
|
shouldBePresent: true,
|
||||||
|
},
|
||||||
|
// eval function verification
|
||||||
|
{
|
||||||
|
type: "eval",
|
||||||
|
expected: "eval()",
|
||||||
|
shouldBePresent: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "Blob",
|
||||||
|
expected: "Blob()",
|
||||||
|
shouldBePresent: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "FormData",
|
||||||
|
expected: "FormData()",
|
||||||
|
shouldBePresent: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "FileReader",
|
||||||
|
expected: "FileReader()",
|
||||||
|
shouldBePresent: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
JSAPIsToTest.forEach((test, index) => {
|
||||||
|
const deleteCharCount = (JSAPIsToTest[index - 1]?.type || " ").length;
|
||||||
|
cy.get(CommonLocators._codeMirrorTextArea)
|
||||||
|
.focus()
|
||||||
|
// remove previously typed code
|
||||||
|
.type(deleteCharCount ? "{backspace}".repeat(deleteCharCount) : " ")
|
||||||
|
.wait(20)
|
||||||
|
.type(test.type);
|
||||||
|
|
||||||
|
cy.get(CommonLocators._hints)
|
||||||
|
.eq(0)
|
||||||
|
.should(
|
||||||
|
test.shouldBePresent ? "have.text" : "not.have.text",
|
||||||
|
test.expected,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("3. JSObject this. autocomplete", () => {
|
||||||
|
// create js object
|
||||||
|
jsEditor.CreateJSObject(jsObjectBody, {
|
||||||
|
paste: true,
|
||||||
|
completeReplace: true,
|
||||||
|
toRun: false,
|
||||||
|
shouldCreateNewJSObj: true,
|
||||||
|
});
|
||||||
|
// focus on 5th line
|
||||||
|
cy.get(`:nth-child(5) > .CodeMirror-line`).click();
|
||||||
|
|
||||||
|
cy.get(CommonLocators._codeMirrorTextArea)
|
||||||
|
.focus()
|
||||||
|
.type("this.");
|
||||||
|
|
||||||
|
["myFun2()", "myVar1", "myVar2"].forEach((element, index) => {
|
||||||
|
cy.get(`.CodeMirror-hints > :nth-child(${index + 1})`).contains(element);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("4. Local variables & complex data autocompletion test", () => {
|
||||||
// create js object
|
// create js object
|
||||||
jsEditor.CreateJSObject(jsObjectBody, {
|
jsEditor.CreateJSObject(jsObjectBody, {
|
||||||
paste: true,
|
paste: true,
|
||||||
|
|
@ -65,15 +159,16 @@ describe("Autocomplete tests", () => {
|
||||||
|
|
||||||
const lineNumber = 5;
|
const lineNumber = 5;
|
||||||
|
|
||||||
const array = [
|
const users = [
|
||||||
{ label: "a", value: "b" },
|
{ label: "a", value: "b" },
|
||||||
{ label: "a", value: "b" },
|
{ label: "a", value: "b" },
|
||||||
];
|
];
|
||||||
|
|
||||||
const codeToType = `
|
const codeToType = `
|
||||||
const arr = ${JSON.stringify(array)};
|
const users = ${JSON.stringify(users)};
|
||||||
|
const data = { userCollection: [{ users }, { users }] };
|
||||||
|
|
||||||
arr.map(callBack)
|
users.map(callBack)
|
||||||
`;
|
`;
|
||||||
|
|
||||||
// component re-render cause DOM element of cy.get to lost
|
// component re-render cause DOM element of cy.get to lost
|
||||||
|
|
@ -86,16 +181,36 @@ describe("Autocomplete tests", () => {
|
||||||
.focus()
|
.focus()
|
||||||
.type(`${codeToType}`, { parseSpecialCharSequences: false })
|
.type(`${codeToType}`, { parseSpecialCharSequences: false })
|
||||||
.type(`{upArrow}{upArrow}`)
|
.type(`{upArrow}{upArrow}`)
|
||||||
.type(`const callBack = (item) => item.l`);
|
.type(`const callBack = (user) => user.l`);
|
||||||
|
|
||||||
cy.get(`.CodeMirror-hints > :nth-child(1)`).contains("label");
|
agHelper.AssertElementText(CommonLocators._hints, "label");
|
||||||
|
|
||||||
cy.get(CommonLocators._codeMirrorTextArea)
|
cy.get(CommonLocators._codeMirrorTextArea)
|
||||||
.focus()
|
.focus()
|
||||||
.type(`label`);
|
.type(`abel;`);
|
||||||
|
|
||||||
|
cy.get(CommonLocators._codeMirrorTextArea)
|
||||||
|
.focus()
|
||||||
|
.type(`data.`);
|
||||||
|
|
||||||
|
agHelper.AssertElementText(CommonLocators._hints, "userCollection");
|
||||||
|
|
||||||
|
cy.get(CommonLocators._codeMirrorTextArea)
|
||||||
|
.focus()
|
||||||
|
.type(`userCollection[0].`);
|
||||||
|
|
||||||
|
agHelper.AssertElementText(CommonLocators._hints, "users");
|
||||||
|
|
||||||
|
cy.get(CommonLocators._codeMirrorTextArea)
|
||||||
|
.focus()
|
||||||
|
.type(`users[0].`);
|
||||||
|
|
||||||
|
agHelper.AssertElementText(CommonLocators._hints, "label");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("3. JSObject this. autocomplete", () => {
|
it("5. Api data with array of object autocompletion test", () => {
|
||||||
|
ApiPage.CreateAndFillApi("https://mock-api.appsmith.com/users");
|
||||||
|
ApiPage.RunAPI();
|
||||||
// create js object
|
// create js object
|
||||||
jsEditor.CreateJSObject(jsObjectBody, {
|
jsEditor.CreateJSObject(jsObjectBody, {
|
||||||
paste: true,
|
paste: true,
|
||||||
|
|
@ -104,18 +219,18 @@ describe("Autocomplete tests", () => {
|
||||||
shouldCreateNewJSObj: true,
|
shouldCreateNewJSObj: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const lineNumber = 5;
|
cy.get(`:nth-child(${5}) > .CodeMirror-line`).click();
|
||||||
|
|
||||||
const codeToType = "this.";
|
|
||||||
|
|
||||||
cy.get(`:nth-child(${lineNumber}) > .CodeMirror-line`).click();
|
|
||||||
|
|
||||||
cy.get(CommonLocators._codeMirrorTextArea)
|
cy.get(CommonLocators._codeMirrorTextArea)
|
||||||
.focus()
|
.focus()
|
||||||
.type(`${codeToType}`);
|
.type("Api1.data.u");
|
||||||
|
|
||||||
["myFun2()", "myVar1", "myVar2"].forEach((element, index) => {
|
agHelper.AssertElementText(CommonLocators._hints, "users");
|
||||||
cy.get(`.CodeMirror-hints > :nth-child(${index + 1})`).contains(element);
|
|
||||||
});
|
cy.get(CommonLocators._codeMirrorTextArea)
|
||||||
|
.focus()
|
||||||
|
.type("sers[0].e");
|
||||||
|
|
||||||
|
agHelper.AssertElementText(CommonLocators._hints, "email");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import {
|
||||||
} from "../../../../locators/WidgetLocators";
|
} from "../../../../locators/WidgetLocators";
|
||||||
|
|
||||||
const widgetsToTest = {
|
const widgetsToTest = {
|
||||||
[WIDGET.MULTISELECT_WIDGET]: {
|
[WIDGET.MULTISELECT]: {
|
||||||
testCases: [
|
testCases: [
|
||||||
{
|
{
|
||||||
input:
|
input:
|
||||||
|
|
@ -39,7 +39,7 @@ Object.entries(widgetsToTest).forEach(([widgetSelector, testConfig]) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("2. Bind Button on click and Text widget content", function() {
|
it("2. Bind Button on click and Text widget content", function() {
|
||||||
cy.openPropertyPane(WIDGET.BUTTON_WIDGET);
|
cy.openPropertyPane(WIDGET.BUTTON);
|
||||||
cy.get(PROPERTY_SELECTOR.onClick)
|
cy.get(PROPERTY_SELECTOR.onClick)
|
||||||
.find(".t--js-toggle")
|
.find(".t--js-toggle")
|
||||||
.click();
|
.click();
|
||||||
|
|
@ -60,7 +60,7 @@ Object.entries(widgetsToTest).forEach(([widgetSelector, testConfig]) => {
|
||||||
cy.wrap(item).should("contain.text", "BLUE");
|
cy.wrap(item).should("contain.text", "BLUE");
|
||||||
});
|
});
|
||||||
const inputs = testConfig.testCases;
|
const inputs = testConfig.testCases;
|
||||||
cy.get(getWidgetSelector(WIDGET.BUTTON_WIDGET))
|
cy.get(getWidgetSelector(WIDGET.BUTTON))
|
||||||
.scrollIntoView()
|
.scrollIntoView()
|
||||||
.click({ force: true });
|
.click({ force: true });
|
||||||
cy.wait("@updateLayout");
|
cy.wait("@updateLayout");
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import {
|
||||||
} from "../../../../locators/WidgetLocators";
|
} from "../../../../locators/WidgetLocators";
|
||||||
|
|
||||||
const widgetsToTest = {
|
const widgetsToTest = {
|
||||||
[WIDGET.INPUT_WIDGET_V2]: {
|
[WIDGET.INPUT_V2]: {
|
||||||
testCases: [
|
testCases: [
|
||||||
{ input: "test", expected: "test", clearBeforeType: true },
|
{ input: "test", expected: "test", clearBeforeType: true },
|
||||||
{ input: "12", expected: "test12", clearBeforeType: false },
|
{ input: "12", expected: "test12", clearBeforeType: false },
|
||||||
|
|
@ -29,7 +29,7 @@ const widgetsToTest = {
|
||||||
widgetName: "Input widget",
|
widgetName: "Input widget",
|
||||||
widgetPrefixName: "Input",
|
widgetPrefixName: "Input",
|
||||||
},
|
},
|
||||||
[WIDGET.PHONE_INPUT_WIDGET]: {
|
[WIDGET.PHONE_INPUT]: {
|
||||||
testCases: [
|
testCases: [
|
||||||
{
|
{
|
||||||
input: "9999999999",
|
input: "9999999999",
|
||||||
|
|
@ -50,7 +50,7 @@ const widgetsToTest = {
|
||||||
widgetName: "Phone Input widget",
|
widgetName: "Phone Input widget",
|
||||||
widgetPrefixName: "PhoneInput",
|
widgetPrefixName: "PhoneInput",
|
||||||
},
|
},
|
||||||
[WIDGET.CURRENCY_INPUT_WIDGET]: {
|
[WIDGET.CURRENCY_INPUT]: {
|
||||||
testCases: [
|
testCases: [
|
||||||
{ input: "1233", expected: "1,233", clearBeforeType: true },
|
{ input: "1233", expected: "1,233", clearBeforeType: true },
|
||||||
{
|
{
|
||||||
|
|
@ -102,14 +102,14 @@ Object.entries(widgetsToTest).forEach(([widgetSelector, testConfig], index) => {
|
||||||
cy.dragAndDropToCanvas(widgetSelector, { x: 300, y: 200 });
|
cy.dragAndDropToCanvas(widgetSelector, { x: 300, y: 200 });
|
||||||
cy.get(getWidgetSelector(widgetSelector)).should("exist");
|
cy.get(getWidgetSelector(widgetSelector)).should("exist");
|
||||||
|
|
||||||
cy.dragAndDropToCanvas(WIDGET.BUTTON_WIDGET, { x: 300, y: 400 });
|
cy.dragAndDropToCanvas(WIDGET.BUTTON, { x: 300, y: 400 });
|
||||||
|
|
||||||
cy.dragAndDropToCanvas(WIDGET.TEXT, { x: 300, y: 600 });
|
cy.dragAndDropToCanvas(WIDGET.TEXT, { x: 300, y: 600 });
|
||||||
});
|
});
|
||||||
|
|
||||||
it("2. StoreValue should have complete input value", () => {
|
it("2. StoreValue should have complete input value", () => {
|
||||||
// if default input widget type is changed from text to any other type then uncomment below code.
|
// if default input widget type is changed from text to any other type then uncomment below code.
|
||||||
// if (widgetSelector === WIDGET.INPUT_WIDGET_V2) {
|
// if (widgetSelector === WIDGET.INPUT_V2) {
|
||||||
// cy.openPropertyPane(widgetSelector);
|
// cy.openPropertyPane(widgetSelector);
|
||||||
// cy.selectDropdownValue(".t--property-control-datatype", "Text");
|
// cy.selectDropdownValue(".t--property-control-datatype", "Text");
|
||||||
// cy.get(".t--property-control-required label")
|
// cy.get(".t--property-control-required label")
|
||||||
|
|
@ -119,7 +119,7 @@ Object.entries(widgetsToTest).forEach(([widgetSelector, testConfig], index) => {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// Set onClick action, storing value
|
// Set onClick action, storing value
|
||||||
cy.openPropertyPane(WIDGET.BUTTON_WIDGET);
|
cy.openPropertyPane(WIDGET.BUTTON);
|
||||||
cy.get(PROPERTY_SELECTOR.onClick)
|
cy.get(PROPERTY_SELECTOR.onClick)
|
||||||
.find(".t--js-toggle")
|
.find(".t--js-toggle")
|
||||||
.click();
|
.click();
|
||||||
|
|
@ -147,7 +147,7 @@ Object.entries(widgetsToTest).forEach(([widgetSelector, testConfig], index) => {
|
||||||
cy.get(getWidgetInputSelector(widgetSelector)).type(`${input}`);
|
cy.get(getWidgetInputSelector(widgetSelector)).type(`${input}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
cy.get(getWidgetSelector(WIDGET.BUTTON_WIDGET)).click();
|
cy.get(getWidgetSelector(WIDGET.BUTTON)).click();
|
||||||
|
|
||||||
// Assert if the Text widget contains the whole value, test
|
// Assert if the Text widget contains the whole value, test
|
||||||
cy.get(getWidgetSelector(WIDGET.TEXT)).should("have.text", expected);
|
cy.get(getWidgetSelector(WIDGET.TEXT)).should("have.text", expected);
|
||||||
|
|
@ -156,7 +156,7 @@ Object.entries(widgetsToTest).forEach(([widgetSelector, testConfig], index) => {
|
||||||
|
|
||||||
it("3. Api params getting correct input values", () => {
|
it("3. Api params getting correct input values", () => {
|
||||||
// Set onClick action, storing value
|
// Set onClick action, storing value
|
||||||
cy.openPropertyPane(WIDGET.BUTTON_WIDGET);
|
cy.openPropertyPane(WIDGET.BUTTON);
|
||||||
// cy.get(PROPERTY_SELECTOR.onClick)
|
// cy.get(PROPERTY_SELECTOR.onClick)
|
||||||
// .find(".t--js-toggle")
|
// .find(".t--js-toggle")
|
||||||
// .click();
|
// .click();
|
||||||
|
|
@ -177,7 +177,7 @@ Object.entries(widgetsToTest).forEach(([widgetSelector, testConfig], index) => {
|
||||||
cy.get(getWidgetInputSelector(widgetSelector)).type(`${input}`);
|
cy.get(getWidgetInputSelector(widgetSelector)).type(`${input}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
cy.get(getWidgetSelector(WIDGET.BUTTON_WIDGET)).click();
|
cy.get(getWidgetSelector(WIDGET.BUTTON)).click();
|
||||||
|
|
||||||
// Assert if the Api request contains the expected value
|
// Assert if the Api request contains the expected value
|
||||||
|
|
||||||
|
|
@ -190,7 +190,7 @@ Object.entries(widgetsToTest).forEach(([widgetSelector, testConfig], index) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("4. Delete all the widgets on canvas", () => {
|
it("4. Delete all the widgets on canvas", () => {
|
||||||
cy.get(getWidgetSelector(WIDGET.BUTTON_WIDGET)).click();
|
cy.get(getWidgetSelector(WIDGET.BUTTON)).click();
|
||||||
cy.get("body").type(`{del}`, { force: true });
|
cy.get("body").type(`{del}`, { force: true });
|
||||||
|
|
||||||
cy.get(getWidgetSelector(WIDGET.TEXT)).click();
|
cy.get(getWidgetSelector(WIDGET.TEXT)).click();
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import {
|
||||||
} from "../../../../locators/WidgetLocators";
|
} from "../../../../locators/WidgetLocators";
|
||||||
|
|
||||||
const widgetsToTest = {
|
const widgetsToTest = {
|
||||||
[WIDGET.MULTISELECT_WIDGET]: {
|
[WIDGET.MULTISELECT]: {
|
||||||
widgetName: "MultiSelect",
|
widgetName: "MultiSelect",
|
||||||
widgetPrefixName: "MultiSelect1",
|
widgetPrefixName: "MultiSelect1",
|
||||||
textBindingValue: "{{MultiSelect1.selectedOptionValues}}",
|
textBindingValue: "{{MultiSelect1.selectedOptionValues}}",
|
||||||
|
|
@ -61,7 +61,7 @@ const widgetsToTest = {
|
||||||
selectAndReset();
|
selectAndReset();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[WIDGET.CURRENCY_INPUT_WIDGET]: {
|
[WIDGET.CURRENCY_INPUT]: {
|
||||||
widgetName: "CurrencyInput",
|
widgetName: "CurrencyInput",
|
||||||
widgetPrefixName: "CurrencyInput1",
|
widgetPrefixName: "CurrencyInput1",
|
||||||
textBindingValue: testdata.currencyBindingValue,
|
textBindingValue: testdata.currencyBindingValue,
|
||||||
|
|
@ -435,7 +435,7 @@ Object.entries(widgetsToTest).forEach(([widgetSelector, testConfig]) => {
|
||||||
|
|
||||||
it("2. Bind Button on click and Text widget content", () => {
|
it("2. Bind Button on click and Text widget content", () => {
|
||||||
// Set onClick assertWidgetReset, storing value
|
// Set onClick assertWidgetReset, storing value
|
||||||
cy.openPropertyPane(WIDGET.BUTTON_WIDGET);
|
cy.openPropertyPane(WIDGET.BUTTON);
|
||||||
|
|
||||||
cy.get(PROPERTY_SELECTOR.onClick)
|
cy.get(PROPERTY_SELECTOR.onClick)
|
||||||
.find(".t--js-toggle")
|
.find(".t--js-toggle")
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
export const WIDGET = {
|
export const WIDGET = {
|
||||||
INPUT_WIDGET_V2: "inputwidgetv2",
|
INPUT_V2: "inputwidgetv2",
|
||||||
TEXT: "textwidget",
|
TEXT: "textwidget",
|
||||||
PHONE_INPUT_WIDGET: "phoneinputwidget",
|
PHONE_INPUT: "phoneinputwidget",
|
||||||
CURRENCY_INPUT_WIDGET: "currencyinputwidget",
|
CURRENCY_INPUT: "currencyinputwidget",
|
||||||
BUTTON_WIDGET: "buttonwidget",
|
BUTTON: "buttonwidget",
|
||||||
MULTISELECT_WIDGET: "multiselectwidgetv2",
|
MULTISELECT: "multiselectwidgetv2",
|
||||||
BUTTON_GROUP_WIDGET: "buttongroupwidget",
|
BUTTON_GROUP: "buttongroupwidget",
|
||||||
TREESELECT_WIDGET: "singleselecttreewidget",
|
TREESELECT: "singleselecttreewidget",
|
||||||
TAB: "tabswidget",
|
TAB: "tabswidget",
|
||||||
TABLE: "tablewidgetv2",
|
TABLE: "tablewidgetv2",
|
||||||
SWITCHGROUP: "switchgroupwidget",
|
SWITCHGROUP: "switchgroupwidget",
|
||||||
|
|
@ -23,6 +23,7 @@ export const WIDGET = {
|
||||||
PHONEINPUT: "phoneinputwidget",
|
PHONEINPUT: "phoneinputwidget",
|
||||||
CAMERA: "camerawidget",
|
CAMERA: "camerawidget",
|
||||||
FILEPICKER: "filepickerwidgetv2",
|
FILEPICKER: "filepickerwidgetv2",
|
||||||
|
DOCUMENT_VIEWER: "documentviewerwidget",
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
// property pane element selector are maintained here
|
// property pane element selector are maintained here
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,7 @@ export class AggregateHelper {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public AssertElementText(selector: string, text: string, index: number = 0) {
|
public AssertElementText(selector: string, text: string, index = 0) {
|
||||||
const locator = selector.startsWith("//")
|
const locator = selector.startsWith("//")
|
||||||
? cy.xpath(selector)
|
? cy.xpath(selector)
|
||||||
: cy.get(selector);
|
: cy.get(selector);
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ export class ApiPage {
|
||||||
_saveAsDS = ".t--store-as-datasource";
|
_saveAsDS = ".t--store-as-datasource";
|
||||||
|
|
||||||
CreateApi(
|
CreateApi(
|
||||||
apiName: string = "",
|
apiName = "",
|
||||||
apiVerb: "GET" | "POST" | "PUT" | "DELETE" | "PATCH" = "GET",
|
apiVerb: "GET" | "POST" | "PUT" | "DELETE" | "PATCH" = "GET",
|
||||||
) {
|
) {
|
||||||
cy.get(this.locator._createNew).click({ force: true });
|
cy.get(this.locator._createNew).click({ force: true });
|
||||||
|
|
@ -67,11 +67,11 @@ export class ApiPage {
|
||||||
|
|
||||||
CreateAndFillApi(
|
CreateAndFillApi(
|
||||||
url: string,
|
url: string,
|
||||||
apiname: string = "",
|
apiName = "",
|
||||||
apiVerb: "GET" | "POST" | "PUT" | "DELETE" | "PATCH" = "GET",
|
apiVerb: "GET" | "POST" | "PUT" | "DELETE" | "PATCH" = "GET",
|
||||||
queryTimeout = 30000,
|
queryTimeout = 30000,
|
||||||
) {
|
) {
|
||||||
this.CreateApi(apiname, apiVerb);
|
this.CreateApi(apiName, apiVerb);
|
||||||
this.EnterURL(url);
|
this.EnterURL(url);
|
||||||
this.agHelper.AssertAutoSave();
|
this.agHelper.AssertAutoSave();
|
||||||
//this.agHelper.Sleep(2000);// Added because api name edit takes some time to reflect in api sidebar after the call passes.
|
//this.agHelper.Sleep(2000);// Added because api name edit takes some time to reflect in api sidebar after the call passes.
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ export interface ICreateJSObjectOptions {
|
||||||
toRun: boolean;
|
toRun: boolean;
|
||||||
shouldCreateNewJSObj: boolean;
|
shouldCreateNewJSObj: boolean;
|
||||||
lineNumber?: number;
|
lineNumber?: number;
|
||||||
|
prettify?: boolean;
|
||||||
}
|
}
|
||||||
const DEFAULT_CREATE_JS_OBJECT_OPTIONS = {
|
const DEFAULT_CREATE_JS_OBJECT_OPTIONS = {
|
||||||
paste: true,
|
paste: true,
|
||||||
|
|
@ -124,15 +125,14 @@ export class JSEditor {
|
||||||
completeReplace,
|
completeReplace,
|
||||||
lineNumber = 4,
|
lineNumber = 4,
|
||||||
paste,
|
paste,
|
||||||
|
prettify = true,
|
||||||
shouldCreateNewJSObj,
|
shouldCreateNewJSObj,
|
||||||
toRun,
|
toRun,
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
shouldCreateNewJSObj && this.NavigateToNewJSEditor();
|
shouldCreateNewJSObj && this.NavigateToNewJSEditor();
|
||||||
if (!completeReplace) {
|
if (!completeReplace) {
|
||||||
const downKeys = Array.from(new Array(lineNumber), () => "{downarrow}")
|
const downKeys = "{downarrow}".repeat(lineNumber);
|
||||||
.toString()
|
|
||||||
.replaceAll(",", "");
|
|
||||||
cy.get(this.locator._codeMirrorTextArea)
|
cy.get(this.locator._codeMirrorTextArea)
|
||||||
.first()
|
.first()
|
||||||
.focus()
|
.focus()
|
||||||
|
|
@ -161,8 +161,11 @@ export class JSEditor {
|
||||||
});
|
});
|
||||||
|
|
||||||
this.agHelper.AssertAutoSave();
|
this.agHelper.AssertAutoSave();
|
||||||
this.agHelper.ActionContextMenuWithInPane("Prettify Code");
|
// Ample wait due to open bug # 10284
|
||||||
this.agHelper.AssertAutoSave(); //Ample wait due to open bug # 10284
|
if (prettify) {
|
||||||
|
this.agHelper.ActionContextMenuWithInPane("Prettify Code");
|
||||||
|
this.agHelper.AssertAutoSave();
|
||||||
|
}
|
||||||
|
|
||||||
if (toRun) {
|
if (toRun) {
|
||||||
//clicking 1 times & waits for 2 second for result to be populated!
|
//clicking 1 times & waits for 2 second for result to be populated!
|
||||||
|
|
|
||||||
245
app/client/src/constants/defs/browser.json
Normal file
245
app/client/src/constants/defs/browser.json
Normal file
|
|
@ -0,0 +1,245 @@
|
||||||
|
{
|
||||||
|
"!name": "browser",
|
||||||
|
"console": {
|
||||||
|
"assert": {
|
||||||
|
"!type": "fn(assertion: bool, text: string)",
|
||||||
|
"!url": "https://developer.mozilla.org/en/docs/Web/API/Console.assert",
|
||||||
|
"!doc": "Writes an error message to the console if the assertion is false."
|
||||||
|
},
|
||||||
|
"clear": {
|
||||||
|
"!type": "fn()",
|
||||||
|
"!url": "https://developer.mozilla.org/en-US/docs/Web/API/Console/clear",
|
||||||
|
"!doc": " Clear the console."
|
||||||
|
},
|
||||||
|
"count": {
|
||||||
|
"!type": "fn(label?: string)",
|
||||||
|
"!url": "https://developer.mozilla.org/en/docs/Web/API/Console.count",
|
||||||
|
"!doc": "Logs the number of times that this particular call to count() has been called."
|
||||||
|
},
|
||||||
|
"debug": "console.log",
|
||||||
|
"dir": {
|
||||||
|
"!type": "fn(object: ?)",
|
||||||
|
"!url": "https://developer.mozilla.org/en/docs/Web/API/Console.dir",
|
||||||
|
"!doc": "Displays an interactive list of the properties of the specified JavaScript object."
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"!type": "fn(...msg: ?)",
|
||||||
|
"!url": "https://developer.mozilla.org/en/docs/DOM/console.error",
|
||||||
|
"!doc": "Outputs an error message to the Web Console."
|
||||||
|
},
|
||||||
|
"group": {
|
||||||
|
"!type": "fn(label?: string)",
|
||||||
|
"!url": "https://developer.mozilla.org/en/docs/Web/API/Console.group",
|
||||||
|
"!doc": "Creates a new inline group in the Web Console log."
|
||||||
|
},
|
||||||
|
"groupCollapsed": {
|
||||||
|
"!type": "fn(label?: string)",
|
||||||
|
"!url": "https://developer.mozilla.org/en/docs/Web/API/Console.groupCollapsed",
|
||||||
|
"!doc": "Creates a new inline group in the Web Console log."
|
||||||
|
},
|
||||||
|
"groupEnd": {
|
||||||
|
"!type": "fn()",
|
||||||
|
"!url": "https://developer.mozilla.org/en/docs/Web/API/Console.groupEnd",
|
||||||
|
"!doc": "Exits the current inline group in the Web Console."
|
||||||
|
},
|
||||||
|
"info": {
|
||||||
|
"!type": "fn(...msg: ?)",
|
||||||
|
"!url": "https://developer.mozilla.org/en/docs/DOM/console.info",
|
||||||
|
"!doc": "Outputs an informational message to the Web Console."
|
||||||
|
},
|
||||||
|
"log": {
|
||||||
|
"!type": "fn(...msg: ?)",
|
||||||
|
"!url": "https://developer.mozilla.org/en/docs/DOM/console.log",
|
||||||
|
"!doc": "Outputs a message to the Web Console."
|
||||||
|
},
|
||||||
|
"table": {
|
||||||
|
"!type": "fn(data: []|?, columns?: [])",
|
||||||
|
"!url": "https://developer.mozilla.org/en-US/docs/Web/API/Console/table",
|
||||||
|
"!doc": " Displays tabular data as a table."
|
||||||
|
},
|
||||||
|
"time": {
|
||||||
|
"!type": "fn(label: string)",
|
||||||
|
"!url": "https://developer.mozilla.org/en/docs/Web/API/Console.time",
|
||||||
|
"!doc": "Starts a timer you can use to track how long an operation takes."
|
||||||
|
},
|
||||||
|
"timeEnd": {
|
||||||
|
"!type": "fn(label: string)",
|
||||||
|
"!url": "https://developer.mozilla.org/en/docs/Web/API/Console.timeEnd",
|
||||||
|
"!doc": "Stops a timer that was previously started by calling console.time()."
|
||||||
|
},
|
||||||
|
"trace": {
|
||||||
|
"!type": "fn()",
|
||||||
|
"!url": "https://developer.mozilla.org/en/docs/Web/API/Console.trace",
|
||||||
|
"!doc": "Outputs a stack trace to the Web Console."
|
||||||
|
},
|
||||||
|
"warn": {
|
||||||
|
"!type": "fn(...msg: ?)",
|
||||||
|
"!url": "https://developer.mozilla.org/en/docs/DOM/console.warn",
|
||||||
|
"!doc": "Outputs a warning message to the Web Console."
|
||||||
|
},
|
||||||
|
"!url": "https://developer.mozilla.org/en/docs/Web/API/Console",
|
||||||
|
"!doc": "The console object provides access to the browser's debugging console. The specifics of how it works vary from browser to browser, but there is a de facto set of features that are typically provided."
|
||||||
|
},
|
||||||
|
"crypto": {
|
||||||
|
"getRandomValues": {
|
||||||
|
"!type": "fn([number])",
|
||||||
|
"!url": "https://developer.mozilla.org/en/docs/DOM/window.crypto.getRandomValues",
|
||||||
|
"!doc": "This methods lets you get cryptographically random values."
|
||||||
|
},
|
||||||
|
"!url": "https://developer.mozilla.org/en/docs/DOM/window.crypto.getRandomValues",
|
||||||
|
"!doc": "This methods lets you get cryptographically random values."
|
||||||
|
},
|
||||||
|
"Blob": {
|
||||||
|
"!type": "fn(parts: [?], options?: ?)",
|
||||||
|
"prototype": {
|
||||||
|
"size": {
|
||||||
|
"!type": "number",
|
||||||
|
"!url": "https://developer.mozilla.org/en-US/docs/Web/API/Blob/size",
|
||||||
|
"!doc": "The size, in bytes, of the data contained in the Blob object. Read only."
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"!type": "string",
|
||||||
|
"!url": "https://developer.mozilla.org/en-US/docs/Web/API/Blob/type",
|
||||||
|
"!doc": "An ASCII-encoded string, in all lower case, indicating the MIME type of the data contained in the Blob. If the type is unknown, this string is empty. Read only."
|
||||||
|
},
|
||||||
|
"slice": {
|
||||||
|
"!type": "fn(start: number, end?: number, type?: string) -> +Blob",
|
||||||
|
"!url": "https://developer.mozilla.org/en/docs/DOM/Blob",
|
||||||
|
"!doc": "Returns a new Blob object containing the data in the specified range of bytes of the source Blob."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"!url": "https://developer.mozilla.org/en/docs/DOM/Blob",
|
||||||
|
"!doc": "A Blob object represents a file-like object of immutable, raw data. Blobs represent data that isn't necessarily in a JavaScript-native format. The File interface is based on Blob, inheriting blob functionality and expanding it to support files on the user's system."
|
||||||
|
},
|
||||||
|
"FileReader": {
|
||||||
|
"!type": "fn()",
|
||||||
|
"prototype": {
|
||||||
|
"abort": {
|
||||||
|
"!type": "fn()",
|
||||||
|
"!url": "https://developer.mozilla.org/en/docs/DOM/FileReader",
|
||||||
|
"!doc": "Aborts the read operation. Upon return, the readyState will be DONE."
|
||||||
|
},
|
||||||
|
"readAsArrayBuffer": {
|
||||||
|
"!type": "fn(blob: +Blob)",
|
||||||
|
"!url": "https://developer.mozilla.org/en/docs/DOM/FileReader",
|
||||||
|
"!doc": "Starts reading the contents of the specified Blob, producing an ArrayBuffer."
|
||||||
|
},
|
||||||
|
"readAsBinaryString": {
|
||||||
|
"!type": "fn(blob: +Blob)",
|
||||||
|
"!url": "https://developer.mozilla.org/en/docs/DOM/FileReader",
|
||||||
|
"!doc": "Starts reading the contents of the specified Blob, producing raw binary data."
|
||||||
|
},
|
||||||
|
"readAsDataURL": {
|
||||||
|
"!type": "fn(blob: +Blob)",
|
||||||
|
"!url": "https://developer.mozilla.org/en/docs/DOM/FileReader",
|
||||||
|
"!doc": "Starts reading the contents of the specified Blob, producing a data: url."
|
||||||
|
},
|
||||||
|
"readAsText": {
|
||||||
|
"!type": "fn(blob: +Blob, encoding?: string)",
|
||||||
|
"!url": "https://developer.mozilla.org/en/docs/DOM/FileReader",
|
||||||
|
"!doc": "Starts reading the contents of the specified Blob, producing a string."
|
||||||
|
},
|
||||||
|
"EMPTY": "number",
|
||||||
|
"LOADING": "number",
|
||||||
|
"DONE": "number",
|
||||||
|
"error": {
|
||||||
|
"!type": "?",
|
||||||
|
"!url": "https://developer.mozilla.org/en/docs/DOM/FileReader",
|
||||||
|
"!doc": "The error that occurred while reading the file. Read only."
|
||||||
|
},
|
||||||
|
"readyState": {
|
||||||
|
"!type": "number",
|
||||||
|
"!url": "https://developer.mozilla.org/en/docs/DOM/FileReader",
|
||||||
|
"!doc": "Indicates the state of the FileReader. This will be one of the State constants. Read only."
|
||||||
|
},
|
||||||
|
"result": {
|
||||||
|
"!type": "?",
|
||||||
|
"!url": "https://developer.mozilla.org/en/docs/DOM/FileReader",
|
||||||
|
"!doc": "The file's contents. This property is only valid after the read operation is complete, and the format of the data depends on which of the methods was used to initiate the read operation. Read only."
|
||||||
|
},
|
||||||
|
"onabort": {
|
||||||
|
"!type": "?",
|
||||||
|
"!url": "https://developer.mozilla.org/en/docs/DOM/FileReader",
|
||||||
|
"!doc": "Called when the read operation is aborted."
|
||||||
|
},
|
||||||
|
"onerror": {
|
||||||
|
"!type": "?",
|
||||||
|
"!url": "https://developer.mozilla.org/en/docs/DOM/FileReader",
|
||||||
|
"!doc": "Called when an error occurs."
|
||||||
|
},
|
||||||
|
"onload": {
|
||||||
|
"!type": "?",
|
||||||
|
"!url": "https://developer.mozilla.org/en/docs/DOM/FileReader",
|
||||||
|
"!doc": "Called when the read operation is successfully completed."
|
||||||
|
},
|
||||||
|
"onloadend": {
|
||||||
|
"!type": "?",
|
||||||
|
"!url": "https://developer.mozilla.org/en/docs/DOM/FileReader",
|
||||||
|
"!doc": "Called when the read is completed, whether successful or not. This is called after either onload or onerror."
|
||||||
|
},
|
||||||
|
"onloadstart": {
|
||||||
|
"!type": "?",
|
||||||
|
"!url": "https://developer.mozilla.org/en/docs/DOM/FileReader",
|
||||||
|
"!doc": "Called when reading the data is about to begin."
|
||||||
|
},
|
||||||
|
"onprogress": {
|
||||||
|
"!type": "?",
|
||||||
|
"!url": "https://developer.mozilla.org/en/docs/DOM/FileReader",
|
||||||
|
"!doc": "Called periodically while the data is being read."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"!url": "https://developer.mozilla.org/en/docs/DOM/FileReader",
|
||||||
|
"!doc": "The FileReader object lets web applications asynchronously read the contents of files (or raw data buffers) stored on the user's computer, using File or Blob objects to specify the file or data to read. File objects may be obtained from a FileList object returned as a result of a user selecting files using the <input> element, from a drag and drop operation's DataTransfer object, or from the mozGetAsFile() API on an HTMLCanvasElement."
|
||||||
|
},
|
||||||
|
"FormData": {
|
||||||
|
"!type": "fn()",
|
||||||
|
"!url": "https://developer.mozilla.org/en-US/docs/Web/API/FormData",
|
||||||
|
"prototype": {
|
||||||
|
"append": {
|
||||||
|
"!type": "fn(name: string, value: string|+Blob, filename?: string)",
|
||||||
|
"!url": "https://developer.mozilla.org/en-US/docs/Web/API/FormData/append",
|
||||||
|
"!doc": "Appends a new value onto an existing key inside a FormData object, or adds the key if it does not already exist."
|
||||||
|
},
|
||||||
|
"delete": {
|
||||||
|
"!type": "fn(name: string)",
|
||||||
|
"!url": "https://developer.mozilla.org/en-US/docs/Web/API/FormData/delete",
|
||||||
|
"!doc": "Deletes a key/value pair from a FormData object."
|
||||||
|
},
|
||||||
|
"entries": {
|
||||||
|
"!type": "fn() -> +iter[:t=[number, string|+Blob]]",
|
||||||
|
"!url": "https://developer.mozilla.org/en-US/docs/Web/API/FormData/entries",
|
||||||
|
"!doc": "Returns an iterator allowing to go through all key/value pairs contained in this object."
|
||||||
|
},
|
||||||
|
"get": {
|
||||||
|
"!type": "fn(name: string) -> string|+Blob",
|
||||||
|
"!url": "https://developer.mozilla.org/en-US/docs/Web/API/FormData/get",
|
||||||
|
"!doc": "Returns the first value associated with a given key from within a FormData object."
|
||||||
|
},
|
||||||
|
"getAll": {
|
||||||
|
"!type": "fn(name: string) -> [string|+Blob]",
|
||||||
|
"!url": "https://developer.mozilla.org/en-US/docs/Web/API/FormData/getAll",
|
||||||
|
"!doc": "Returns an array of all the values associated with a given key from within a FormData."
|
||||||
|
},
|
||||||
|
"has": {
|
||||||
|
"!type": "fn(name: string) -> bool",
|
||||||
|
"!url": "https://developer.mozilla.org/en-US/docs/Web/API/FormData/has",
|
||||||
|
"!doc": "Returns a boolean stating whether a FormData object contains a certain key/value pair."
|
||||||
|
},
|
||||||
|
"set": {
|
||||||
|
"!type": "fn(name: string, value: string|+Blob, filename?: string)",
|
||||||
|
"!url": "https://developer.mozilla.org/en-US/docs/Web/API/FormData/set",
|
||||||
|
"!doc": "Sets a new value for an existing key inside a FormData object, or adds the key/value if it does not already exist."
|
||||||
|
},
|
||||||
|
"keys": {
|
||||||
|
"!type": "fn() -> +iter[:t=number]",
|
||||||
|
"!doc": "Returns an iterator allowing to go through all keys of the key/value pairs contained in this object.",
|
||||||
|
"!url": "https://developer.mozilla.org/en-US/docs/Web/API/FormData/keys"
|
||||||
|
},
|
||||||
|
"values": {
|
||||||
|
"!type": "fn() -> +iter[:t=string|blob]",
|
||||||
|
"!doc": "Returns an iterator allowing to go through all values of the key/value pairs contained in this object.",
|
||||||
|
"!url": "https://developer.mozilla.org/en-US/docs/Web/API/FormData/values"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -170,7 +170,13 @@ function JSEditorForm({ jsCollection: currentJSCollection }: Props) {
|
||||||
event: React.MouseEvent<HTMLElement, MouseEvent> | KeyboardEvent,
|
event: React.MouseEvent<HTMLElement, MouseEvent> | KeyboardEvent,
|
||||||
) => {
|
) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
selectedJSActionOption.data && executeJSAction(selectedJSActionOption.data);
|
if (
|
||||||
|
!disableRunFunctionality &&
|
||||||
|
!isExecutingCurrentJSAction &&
|
||||||
|
selectedJSActionOption.data
|
||||||
|
) {
|
||||||
|
executeJSAction(selectedJSActionOption.data);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -179,9 +185,13 @@ function JSEditorForm({ jsCollection: currentJSCollection }: Props) {
|
||||||
} else {
|
} else {
|
||||||
setDisableRunFunctionality(false);
|
setDisableRunFunctionality(false);
|
||||||
}
|
}
|
||||||
setSelectedJSActionOption(getJSActionOption(activeJSAction, jsActions));
|
|
||||||
}, [parseErrors, jsActions, activeJSActionId]);
|
}, [parseErrors, jsActions, activeJSActionId]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// update the selectedJSActionOption when there is addition or removal of jsAction or function
|
||||||
|
setSelectedJSActionOption(getJSActionOption(activeJSAction, jsActions));
|
||||||
|
}, [jsActions, activeJSActionId]);
|
||||||
|
|
||||||
const blockCompletions = useMemo(() => {
|
const blockCompletions = useMemo(() => {
|
||||||
if (selectedJSActionOption.label) {
|
if (selectedJSActionOption.label) {
|
||||||
const funcName = `${selectedJSActionOption.label}()`;
|
const funcName = `${selectedJSActionOption.label}()`;
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,24 @@ export class AndRule implements AutocompleteRule {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set score to -Infinity for internal defs to be hidden from autocompletion like $__dropdownOption__$
|
||||||
|
* Max score - 0
|
||||||
|
* Min score - -Infinity
|
||||||
|
*/
|
||||||
|
class HideInternalDefsRule implements AutocompleteRule {
|
||||||
|
static threshold = -Infinity;
|
||||||
|
|
||||||
|
computeScore(completion: Completion): number {
|
||||||
|
let score = 0;
|
||||||
|
|
||||||
|
if (completion.text.includes("$__") && completion.text.includes("__$")) {
|
||||||
|
score = HideInternalDefsRule.threshold;
|
||||||
|
}
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set score to -Infinity for paths to be blocked from autocompletion
|
* Set score to -Infinity for paths to be blocked from autocompletion
|
||||||
* Max score - 0
|
* Max score - 0
|
||||||
|
|
@ -280,6 +298,7 @@ export class ScoredCompletion {
|
||||||
new JSLibraryRule(),
|
new JSLibraryRule(),
|
||||||
new GlobalJSRule(),
|
new GlobalJSRule(),
|
||||||
new BlockSuggestionsRule(),
|
new BlockSuggestionsRule(),
|
||||||
|
new HideInternalDefsRule(),
|
||||||
];
|
];
|
||||||
completion: Completion;
|
completion: Completion;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
import { generateTypeDef } from "utils/autocomplete/dataTreeTypeDefCreator";
|
import {
|
||||||
|
ExtraDef,
|
||||||
|
generateTypeDef,
|
||||||
|
} from "utils/autocomplete/dataTreeTypeDefCreator";
|
||||||
import {
|
import {
|
||||||
DataTreeAction,
|
DataTreeAction,
|
||||||
DataTreeAppsmith,
|
DataTreeAppsmith,
|
||||||
|
|
@ -6,6 +9,7 @@ import {
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { EVALUATION_PATH } from "utils/DynamicBindingUtils";
|
import { EVALUATION_PATH } from "utils/DynamicBindingUtils";
|
||||||
import { JSCollectionData } from "reducers/entityReducers/jsActionsReducer";
|
import { JSCollectionData } from "reducers/entityReducers/jsActionsReducer";
|
||||||
|
import { Def } from "tern";
|
||||||
import { ButtonGroupWidgetProps } from "widgets/ButtonGroupWidget/widget";
|
import { ButtonGroupWidgetProps } from "widgets/ButtonGroupWidget/widget";
|
||||||
|
|
||||||
const isVisible = {
|
const isVisible = {
|
||||||
|
|
@ -14,9 +18,10 @@ const isVisible = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const entityDefinitions = {
|
export const entityDefinitions = {
|
||||||
APPSMITH: (entity: DataTreeAppsmith) => {
|
APPSMITH: (entity: DataTreeAppsmith, extraDefsToDefine: ExtraDef) => {
|
||||||
const generatedTypeDef = generateTypeDef(
|
const generatedTypeDef = generateTypeDef(
|
||||||
_.omit(entity, "ENTITY_TYPE", EVALUATION_PATH),
|
_.omit(entity, "ENTITY_TYPE", EVALUATION_PATH),
|
||||||
|
extraDefsToDefine,
|
||||||
);
|
);
|
||||||
if (
|
if (
|
||||||
typeof generatedTypeDef === "object" &&
|
typeof generatedTypeDef === "object" &&
|
||||||
|
|
@ -39,11 +44,13 @@ export const entityDefinitions = {
|
||||||
}
|
}
|
||||||
return generatedTypeDef;
|
return generatedTypeDef;
|
||||||
},
|
},
|
||||||
ACTION: (entity: DataTreeAction) => {
|
ACTION: (entity: DataTreeAction, extraDefsToDefine: ExtraDef) => {
|
||||||
const dataDef = generateTypeDef(entity.data);
|
const dataDef = generateTypeDef(entity.data, extraDefsToDefine);
|
||||||
let data: Record<string, any> = {
|
|
||||||
|
let data: Def = {
|
||||||
"!doc": "The response of the action",
|
"!doc": "The response of the action",
|
||||||
};
|
};
|
||||||
|
|
||||||
if (_.isString(dataDef)) {
|
if (_.isString(dataDef)) {
|
||||||
data["!type"] = dataDef;
|
data["!type"] = dataDef;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -59,8 +66,7 @@ export const entityDefinitions = {
|
||||||
"!doc": "The response meta of the action",
|
"!doc": "The response meta of the action",
|
||||||
"!type": "?",
|
"!type": "?",
|
||||||
},
|
},
|
||||||
run:
|
run: "fn(params: ?) -> +Promise[:t=[!0.<i>.:t]]",
|
||||||
"fn(onSuccess: fn() -> void, onError: fn() -> void) -> +Promise[:t=[!0.<i>.:t]]",
|
|
||||||
clear: "fn() -> +Promise[:t=[!0.<i>.:t]]",
|
clear: "fn() -> +Promise[:t=[!0.<i>.:t]]",
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
@ -102,17 +108,20 @@ export const entityDefinitions = {
|
||||||
"!doc": "Selected country code for Currency type input",
|
"!doc": "Selected country code for Currency type input",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
TABLE_WIDGET: (widget: any) => ({
|
TABLE_WIDGET: (widget: any, extraDefsToDefine?: ExtraDef) => ({
|
||||||
"!doc":
|
"!doc":
|
||||||
"The Table is the hero widget of Appsmith. You can display data from an API in a table, trigger an action when a user selects a row and even work with large paginated data sets",
|
"The Table is the hero widget of Appsmith. You can display data from an API in a table, trigger an action when a user selects a row and even work with large paginated data sets",
|
||||||
"!url": "https://docs.appsmith.com/widget-reference/table",
|
"!url": "https://docs.appsmith.com/widget-reference/table",
|
||||||
selectedRow: generateTypeDef(widget.selectedRow),
|
selectedRow: generateTypeDef(widget.selectedRow, extraDefsToDefine),
|
||||||
selectedRows: generateTypeDef(widget.selectedRows),
|
selectedRows: generateTypeDef(widget.selectedRows, extraDefsToDefine),
|
||||||
selectedRowIndices: generateTypeDef(widget.selectedRowIndices),
|
selectedRowIndices: generateTypeDef(widget.selectedRowIndices),
|
||||||
triggeredRow: generateTypeDef(widget.triggeredRow),
|
triggeredRow: generateTypeDef(widget.triggeredRow),
|
||||||
selectedRowIndex: "number",
|
selectedRowIndex: "number",
|
||||||
tableData: generateTypeDef(widget.tableData),
|
tableData: generateTypeDef(widget.tableData, extraDefsToDefine),
|
||||||
filteredTableData: generateTypeDef(widget.filteredTableData),
|
filteredTableData: generateTypeDef(
|
||||||
|
widget.filteredTableData,
|
||||||
|
extraDefsToDefine,
|
||||||
|
),
|
||||||
pageNo: "number",
|
pageNo: "number",
|
||||||
pageSize: "number",
|
pageSize: "number",
|
||||||
isVisible: isVisible,
|
isVisible: isVisible,
|
||||||
|
|
@ -123,17 +132,17 @@ export const entityDefinitions = {
|
||||||
order: ["asc", "desc"],
|
order: ["asc", "desc"],
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
TABLE_WIDGET_V2: (widget: any) => ({
|
TABLE_WIDGET_V2: (widget: any, extraDefsToDefine?: ExtraDef) => ({
|
||||||
"!doc":
|
"!doc":
|
||||||
"The Table is the hero widget of Appsmith. You can display data from an API in a table, trigger an action when a user selects a row and even work with large paginated data sets",
|
"The Table is the hero widget of Appsmith. You can display data from an API in a table, trigger an action when a user selects a row and even work with large paginated data sets",
|
||||||
"!url": "https://docs.appsmith.com/widget-reference/table",
|
"!url": "https://docs.appsmith.com/widget-reference/table",
|
||||||
selectedRow: generateTypeDef(widget.selectedRow),
|
selectedRow: generateTypeDef(widget.selectedRow, extraDefsToDefine),
|
||||||
selectedRows: generateTypeDef(widget.selectedRows),
|
selectedRows: generateTypeDef(widget.selectedRows, extraDefsToDefine),
|
||||||
selectedRowIndices: generateTypeDef(widget.selectedRowIndices),
|
selectedRowIndices: generateTypeDef(widget.selectedRowIndices),
|
||||||
triggeredRow: generateTypeDef(widget.triggeredRow),
|
triggeredRow: generateTypeDef(widget.triggeredRow),
|
||||||
updatedRow: generateTypeDef(widget.updatedRow),
|
updatedRow: generateTypeDef(widget.updatedRow),
|
||||||
selectedRowIndex: "number",
|
selectedRowIndex: "number",
|
||||||
tableData: generateTypeDef(widget.tableData),
|
tableData: generateTypeDef(widget.tableData, extraDefsToDefine),
|
||||||
pageNo: "number",
|
pageNo: "number",
|
||||||
pageSize: "number",
|
pageSize: "number",
|
||||||
isVisible: isVisible,
|
isVisible: isVisible,
|
||||||
|
|
@ -143,7 +152,7 @@ export const entityDefinitions = {
|
||||||
column: "string",
|
column: "string",
|
||||||
order: ["asc", "desc"],
|
order: ["asc", "desc"],
|
||||||
},
|
},
|
||||||
updatedRows: generateTypeDef(widget.updatedRows),
|
updatedRows: generateTypeDef(widget.updatedRows, extraDefsToDefine),
|
||||||
updatedRowIndices: generateTypeDef(widget.updatedRowIndices),
|
updatedRowIndices: generateTypeDef(widget.updatedRowIndices),
|
||||||
triggeredRowIndex: generateTypeDef(widget.triggeredRowIndex),
|
triggeredRowIndex: generateTypeDef(widget.triggeredRowIndex),
|
||||||
}),
|
}),
|
||||||
|
|
@ -341,12 +350,12 @@ export const entityDefinitions = {
|
||||||
yAxisName: "string",
|
yAxisName: "string",
|
||||||
selectedDataPoint: "$__chartDataPoint__$",
|
selectedDataPoint: "$__chartDataPoint__$",
|
||||||
},
|
},
|
||||||
FORM_WIDGET: (widget: any) => ({
|
FORM_WIDGET: (widget: any, extraDefsToDefine?: ExtraDef) => ({
|
||||||
"!doc":
|
"!doc":
|
||||||
"Form is used to capture a set of data inputs from a user. Forms are used specifically because they reset the data inputs when a form is submitted and disable submission for invalid data inputs",
|
"Form is used to capture a set of data inputs from a user. Forms are used specifically because they reset the data inputs when a form is submitted and disable submission for invalid data inputs",
|
||||||
"!url": "https://docs.appsmith.com/widget-reference/form",
|
"!url": "https://docs.appsmith.com/widget-reference/form",
|
||||||
isVisible: isVisible,
|
isVisible: isVisible,
|
||||||
data: generateTypeDef(widget.data),
|
data: generateTypeDef(widget.data, extraDefsToDefine),
|
||||||
hasChanges: "bool",
|
hasChanges: "bool",
|
||||||
}),
|
}),
|
||||||
FORM_BUTTON_WIDGET: {
|
FORM_BUTTON_WIDGET: {
|
||||||
|
|
@ -389,7 +398,7 @@ export const entityDefinitions = {
|
||||||
files: "[$__file__$]",
|
files: "[$__file__$]",
|
||||||
isDisabled: "bool",
|
isDisabled: "bool",
|
||||||
},
|
},
|
||||||
LIST_WIDGET: (widget: any) => ({
|
LIST_WIDGET: (widget: any, extraDefsToDefine?: ExtraDef) => ({
|
||||||
"!doc":
|
"!doc":
|
||||||
"Containers are used to group widgets together to form logical higher order widgets. Containers let you organize your page better and move all the widgets inside them together.",
|
"Containers are used to group widgets together to form logical higher order widgets. Containers let you organize your page better and move all the widgets inside them together.",
|
||||||
"!url": "https://docs.appsmith.com/widget-reference/list",
|
"!url": "https://docs.appsmith.com/widget-reference/list",
|
||||||
|
|
@ -399,9 +408,9 @@ export const entityDefinitions = {
|
||||||
},
|
},
|
||||||
isVisible: isVisible,
|
isVisible: isVisible,
|
||||||
gridGap: "number",
|
gridGap: "number",
|
||||||
selectedItem: generateTypeDef(widget.selectedItem),
|
selectedItem: generateTypeDef(widget.selectedItem, extraDefsToDefine),
|
||||||
items: generateTypeDef(widget.items),
|
items: generateTypeDef(widget.items, extraDefsToDefine),
|
||||||
listData: generateTypeDef(widget.listData),
|
listData: generateTypeDef(widget.listData, extraDefsToDefine),
|
||||||
pageNo: generateTypeDef(widget.pageNo),
|
pageNo: generateTypeDef(widget.pageNo),
|
||||||
pageSize: generateTypeDef(widget.pageSize),
|
pageSize: generateTypeDef(widget.pageSize),
|
||||||
}),
|
}),
|
||||||
|
|
@ -632,6 +641,12 @@ export const entityDefinitions = {
|
||||||
isVisible: isVisible,
|
isVisible: isVisible,
|
||||||
progress: "number",
|
progress: "number",
|
||||||
},
|
},
|
||||||
|
DOCUMENT_VIEWER_WIDGET: {
|
||||||
|
"!doc": "Document viewer widget is used to show documents on a page",
|
||||||
|
"!url": "https://docs.appsmith.com/reference/widgets/document-viewer",
|
||||||
|
isVisible: isVisible,
|
||||||
|
docUrl: "string",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import base64 from "constants/defs/base64-js.json";
|
||||||
import moment from "constants/defs/moment.json";
|
import moment from "constants/defs/moment.json";
|
||||||
import xmlJs from "constants/defs/xmlParser.json";
|
import xmlJs from "constants/defs/xmlParser.json";
|
||||||
import forge from "constants/defs/forge.json";
|
import forge from "constants/defs/forge.json";
|
||||||
|
import browser from "constants/defs/browser.json";
|
||||||
import CodeMirror, { Hint, Pos, cmpPos } from "codemirror";
|
import CodeMirror, { Hint, Pos, cmpPos } from "codemirror";
|
||||||
import {
|
import {
|
||||||
getDynamicStringSegments,
|
getDynamicStringSegments,
|
||||||
|
|
@ -19,10 +20,12 @@ import {
|
||||||
import { FieldEntityInformation } from "components/editorComponents/CodeEditor/EditorConfig";
|
import { FieldEntityInformation } from "components/editorComponents/CodeEditor/EditorConfig";
|
||||||
import { ENTITY_TYPE } from "entities/DataTree/dataTreeFactory";
|
import { ENTITY_TYPE } from "entities/DataTree/dataTreeFactory";
|
||||||
import { AutocompleteSorter } from "./AutocompleteSortRules";
|
import { AutocompleteSorter } from "./AutocompleteSortRules";
|
||||||
|
import { getCompletionsForKeyword } from "./keywordCompletion";
|
||||||
|
|
||||||
const DEFS: Def[] = [
|
const DEFS: Def[] = [
|
||||||
// @ts-expect-error: Types are not available
|
// @ts-expect-error: Types are not available
|
||||||
ecma,
|
ecma,
|
||||||
|
browser,
|
||||||
GLOBAL_FUNCTIONS,
|
GLOBAL_FUNCTIONS,
|
||||||
GLOBAL_DEFS,
|
GLOBAL_DEFS,
|
||||||
lodash,
|
lodash,
|
||||||
|
|
@ -38,7 +41,7 @@ const hintDelay = 1700;
|
||||||
|
|
||||||
export type Completion = Hint & {
|
export type Completion = Hint & {
|
||||||
origin: string;
|
origin: string;
|
||||||
type: AutocompleteDataType;
|
type: AutocompleteDataType | string;
|
||||||
data: {
|
data: {
|
||||||
doc: string;
|
doc: string;
|
||||||
};
|
};
|
||||||
|
|
@ -231,6 +234,15 @@ class TernServer {
|
||||||
) {
|
) {
|
||||||
after = '"]';
|
after = '"]';
|
||||||
}
|
}
|
||||||
|
// Actual char space
|
||||||
|
const trimmedFocusedValueLength = focusedValue.trim().length;
|
||||||
|
// end.ch counts tab space as 1 instead of 2 space chars in string
|
||||||
|
// For eg: lets take string ` ab`. Here, end.ch = 3 & trimmedFocusedValueLength = 2
|
||||||
|
// hence tabSpacesCount = end.ch - trimmedFocusedValueLength
|
||||||
|
const tabSpacesCount = end.ch - trimmedFocusedValueLength;
|
||||||
|
const cursorHorizontalPos =
|
||||||
|
tabSpacesCount * 2 + trimmedFocusedValueLength - 2;
|
||||||
|
|
||||||
for (let i = 0; i < data.completions.length; ++i) {
|
for (let i = 0; i < data.completions.length; ++i) {
|
||||||
const completion = data.completions[i];
|
const completion = data.completions[i];
|
||||||
let className = typeToIcon(completion.type, completion.isKeyword);
|
let className = typeToIcon(completion.type, completion.isKeyword);
|
||||||
|
|
@ -258,6 +270,12 @@ class TernServer {
|
||||||
element.setAttribute("keyword", data.displayText);
|
element.setAttribute("keyword", data.displayText);
|
||||||
element.innerHTML = data.displayText;
|
element.innerHTML = data.displayText;
|
||||||
};
|
};
|
||||||
|
// Add relevant keyword completions
|
||||||
|
const keywordCompletions = getCompletionsForKeyword(
|
||||||
|
codeMirrorCompletion,
|
||||||
|
cursorHorizontalPos,
|
||||||
|
);
|
||||||
|
completions = [...completions, ...keywordCompletions];
|
||||||
}
|
}
|
||||||
completions.push(codeMirrorCompletion);
|
completions.push(codeMirrorCompletion);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,88 @@ describe("dataTreeTypeDefCreator", () => {
|
||||||
expect(objType).toStrictEqual(expected);
|
expect(objType).toStrictEqual(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("creates a correct def for a complex array of object", () => {
|
||||||
|
const data = [
|
||||||
|
{
|
||||||
|
nested: [
|
||||||
|
{
|
||||||
|
nested: [
|
||||||
|
{
|
||||||
|
nested: [
|
||||||
|
{
|
||||||
|
nested: [
|
||||||
|
{
|
||||||
|
nested: [
|
||||||
|
{
|
||||||
|
nested: [
|
||||||
|
{
|
||||||
|
name: "",
|
||||||
|
email: "",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const expected = "[def_6]";
|
||||||
|
|
||||||
|
const extraDef = {};
|
||||||
|
const expectedExtraDef = {
|
||||||
|
def_1: { nested: "[?]" },
|
||||||
|
def_2: { nested: "[def_1]" },
|
||||||
|
def_3: { nested: "[def_2]" },
|
||||||
|
def_4: { nested: "[def_3]" },
|
||||||
|
def_5: { nested: "[def_4]" },
|
||||||
|
def_6: { nested: "[def_5]" },
|
||||||
|
};
|
||||||
|
|
||||||
|
const dataType = generateTypeDef(data, extraDef);
|
||||||
|
|
||||||
|
expect(dataType).toStrictEqual(expected);
|
||||||
|
|
||||||
|
expect(extraDef).toStrictEqual(expectedExtraDef);
|
||||||
|
|
||||||
|
const extraDef2 = {};
|
||||||
|
const expected2 = "[def_10]";
|
||||||
|
const dataType2 = generateTypeDef(
|
||||||
|
data[0].nested[0].nested[0].nested,
|
||||||
|
extraDef2,
|
||||||
|
);
|
||||||
|
|
||||||
|
const expectedExtraDef2 = {
|
||||||
|
def_7: { name: "string", email: "string" },
|
||||||
|
def_8: { nested: "[def_7]" },
|
||||||
|
def_9: { nested: "[def_8]" },
|
||||||
|
def_10: { nested: "[def_9]" },
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(dataType2).toStrictEqual(expected2);
|
||||||
|
expect(extraDef2).toStrictEqual(expectedExtraDef2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("creates a correct def for an array of array of object", () => {
|
||||||
|
const array = [[{ name: "", email: "" }]];
|
||||||
|
const expected = "[[def_11]]";
|
||||||
|
|
||||||
|
const extraDefsToDefine = {};
|
||||||
|
const expectedExtraDef = {
|
||||||
|
def_11: { name: "string", email: "string" },
|
||||||
|
};
|
||||||
|
|
||||||
|
const objType = generateTypeDef(array, extraDefsToDefine);
|
||||||
|
|
||||||
|
expect(objType).toStrictEqual(expected);
|
||||||
|
expect(extraDefsToDefine).toStrictEqual(expectedExtraDef);
|
||||||
|
});
|
||||||
|
|
||||||
it("flatten def", () => {
|
it("flatten def", () => {
|
||||||
const def = {
|
const def = {
|
||||||
entity1: {
|
entity1: {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { DataTree, ENTITY_TYPE } from "entities/DataTree/dataTreeFactory";
|
import { DataTree, ENTITY_TYPE } from "entities/DataTree/dataTreeFactory";
|
||||||
import { get, isFunction } from "lodash";
|
import { uniqueId, get, isFunction, isObject } from "lodash";
|
||||||
import { entityDefinitions } from "utils/autocomplete/EntityDefinitions";
|
import { entityDefinitions } from "utils/autocomplete/EntityDefinitions";
|
||||||
import { getType, Types } from "utils/TypeHelpers";
|
import { getType, Types } from "utils/TypeHelpers";
|
||||||
import { Def } from "tern";
|
import { Def } from "tern";
|
||||||
|
|
@ -11,11 +11,11 @@ import {
|
||||||
isWidget,
|
isWidget,
|
||||||
} from "workers/evaluationUtils";
|
} from "workers/evaluationUtils";
|
||||||
import { DataTreeDefEntityInformation } from "utils/autocomplete/TernServer";
|
import { DataTreeDefEntityInformation } from "utils/autocomplete/TernServer";
|
||||||
|
|
||||||
|
export type ExtraDef = Record<string, Def | string>;
|
||||||
|
|
||||||
import { Variable } from "entities/JSCollection";
|
import { Variable } from "entities/JSCollection";
|
||||||
|
|
||||||
// When there is a complex data type, we store it in extra def and refer to it
|
|
||||||
// in the def
|
|
||||||
let extraDefs: Def = {};
|
|
||||||
// Def names are encoded with information about the entity
|
// Def names are encoded with information about the entity
|
||||||
// This so that we have more info about them
|
// This so that we have more info about them
|
||||||
// when sorting results in autocomplete
|
// when sorting results in autocomplete
|
||||||
|
|
@ -26,6 +26,9 @@ export const dataTreeTypeDefCreator = (
|
||||||
dataTree: DataTree,
|
dataTree: DataTree,
|
||||||
isJSEditorEnabled: boolean,
|
isJSEditorEnabled: boolean,
|
||||||
): { def: Def; entityInfo: Map<string, DataTreeDefEntityInformation> } => {
|
): { def: Def; entityInfo: Map<string, DataTreeDefEntityInformation> } => {
|
||||||
|
// When there is a complex data type, we store it in extra def and refer to it in the def
|
||||||
|
const extraDefsToDefine: Def = {};
|
||||||
|
|
||||||
const def: Def = {
|
const def: Def = {
|
||||||
"!name": "DATA_TREE",
|
"!name": "DATA_TREE",
|
||||||
};
|
};
|
||||||
|
|
@ -37,7 +40,7 @@ export const dataTreeTypeDefCreator = (
|
||||||
if (widgetType in entityDefinitions) {
|
if (widgetType in entityDefinitions) {
|
||||||
const definition = get(entityDefinitions, widgetType);
|
const definition = get(entityDefinitions, widgetType);
|
||||||
if (isFunction(definition)) {
|
if (isFunction(definition)) {
|
||||||
def[entityName] = definition(entity);
|
def[entityName] = definition(entity, extraDefsToDefine);
|
||||||
} else {
|
} else {
|
||||||
def[entityName] = definition;
|
def[entityName] = definition;
|
||||||
}
|
}
|
||||||
|
|
@ -48,21 +51,21 @@ export const dataTreeTypeDefCreator = (
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (isAction(entity)) {
|
} else if (isAction(entity)) {
|
||||||
def[entityName] = entityDefinitions.ACTION(entity);
|
def[entityName] = entityDefinitions.ACTION(entity, extraDefsToDefine);
|
||||||
flattenDef(def, entityName);
|
flattenDef(def, entityName);
|
||||||
entityMap.set(entityName, {
|
entityMap.set(entityName, {
|
||||||
type: ENTITY_TYPE.ACTION,
|
type: ENTITY_TYPE.ACTION,
|
||||||
subType: "ACTION",
|
subType: "ACTION",
|
||||||
});
|
});
|
||||||
} else if (isAppsmithEntity(entity)) {
|
} else if (isAppsmithEntity(entity)) {
|
||||||
def.appsmith = (entityDefinitions.APPSMITH as any)(entity);
|
def.appsmith = entityDefinitions.APPSMITH(entity, extraDefsToDefine);
|
||||||
entityMap.set("appsmith", {
|
entityMap.set("appsmith", {
|
||||||
type: ENTITY_TYPE.APPSMITH,
|
type: ENTITY_TYPE.APPSMITH,
|
||||||
subType: ENTITY_TYPE.APPSMITH,
|
subType: ENTITY_TYPE.APPSMITH,
|
||||||
});
|
});
|
||||||
} else if (isJSAction(entity) && isJSEditorEnabled) {
|
} else if (isJSAction(entity) && isJSEditorEnabled) {
|
||||||
const metaObj = entity.meta;
|
const metaObj = entity.meta;
|
||||||
const jsProperty: Def = {};
|
const jsPropertiesDef: Def = {};
|
||||||
|
|
||||||
for (const key in metaObj) {
|
for (const key in metaObj) {
|
||||||
// const jsFunctionObj = metaObj[key];
|
// const jsFunctionObj = metaObj[key];
|
||||||
|
|
@ -72,42 +75,64 @@ export const dataTreeTypeDefCreator = (
|
||||||
// we will also need to check performance implications here
|
// we will also need to check performance implications here
|
||||||
|
|
||||||
const argsTypeString = getFunctionsArgsType([]);
|
const argsTypeString = getFunctionsArgsType([]);
|
||||||
jsProperty[key] = argsTypeString;
|
jsPropertiesDef[key] = argsTypeString;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < entity.variables.length; i++) {
|
for (let i = 0; i < entity.variables.length; i++) {
|
||||||
const varKey = entity.variables[i];
|
const varKey = entity.variables[i];
|
||||||
const varValue = entity[varKey];
|
const varValue = entity[varKey];
|
||||||
jsProperty[varKey] = generateTypeDef(varValue);
|
jsPropertiesDef[varKey] = generateTypeDef(varValue, extraDefsToDefine);
|
||||||
}
|
}
|
||||||
|
|
||||||
def[entityName] = jsProperty;
|
def[entityName] = jsPropertiesDef;
|
||||||
flattenDef(def, entityName);
|
flattenDef(def, entityName);
|
||||||
entityMap.set(entityName, {
|
entityMap.set(entityName, {
|
||||||
type: ENTITY_TYPE.JSACTION,
|
type: ENTITY_TYPE.JSACTION,
|
||||||
subType: "JSACTION",
|
subType: "JSACTION",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (Object.keys(extraDefs)) {
|
|
||||||
def["!define"] = { ...extraDefs };
|
|
||||||
extraDefs = {};
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (Object.keys(extraDefsToDefine)) {
|
||||||
|
def["!define"] = { ...extraDefsToDefine };
|
||||||
|
}
|
||||||
|
|
||||||
return { def, entityInfo: entityMap };
|
return { def, entityInfo: entityMap };
|
||||||
};
|
};
|
||||||
|
|
||||||
export function generateTypeDef(obj: any): string | Def {
|
export function generateTypeDef(
|
||||||
const type = getType(obj);
|
value: unknown,
|
||||||
switch (type) {
|
extraDefsToDefine?: ExtraDef,
|
||||||
|
depth = 0,
|
||||||
|
): Def | string {
|
||||||
|
switch (getType(value)) {
|
||||||
case Types.ARRAY: {
|
case Types.ARRAY: {
|
||||||
const arrayType = getType(obj[0]);
|
const array = value as [unknown];
|
||||||
return `[${arrayType}]`;
|
if (depth > 5) {
|
||||||
|
return `[?]`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const arrayElementType = generateTypeDef(
|
||||||
|
array[0],
|
||||||
|
extraDefsToDefine,
|
||||||
|
depth + 1,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isObject(arrayElementType)) {
|
||||||
|
if (extraDefsToDefine) {
|
||||||
|
const uniqueDefName = uniqueId("def_");
|
||||||
|
extraDefsToDefine[uniqueDefName] = arrayElementType;
|
||||||
|
return `[${uniqueDefName}]`;
|
||||||
|
}
|
||||||
|
return `[?]`;
|
||||||
|
}
|
||||||
|
return `[${arrayElementType}]`;
|
||||||
}
|
}
|
||||||
case Types.OBJECT: {
|
case Types.OBJECT: {
|
||||||
const objType: Def = {};
|
const objType: Def = {};
|
||||||
Object.keys(obj).forEach((k) => {
|
const object = value as Record<string, unknown>;
|
||||||
objType[k] = generateTypeDef(obj[k]);
|
Object.keys(object).forEach((k) => {
|
||||||
|
objType[k] = generateTypeDef(object[k], extraDefsToDefine, depth);
|
||||||
});
|
});
|
||||||
return objType;
|
return objType;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
144
app/client/src/utils/autocomplete/keywordCompletion.ts
Normal file
144
app/client/src/utils/autocomplete/keywordCompletion.ts
Normal file
|
|
@ -0,0 +1,144 @@
|
||||||
|
import { Completion } from "./TernServer";
|
||||||
|
|
||||||
|
export const getCompletionsForKeyword = (
|
||||||
|
completion: Completion,
|
||||||
|
cursorHorizontalPos: number,
|
||||||
|
) => {
|
||||||
|
const keywordName = completion.text;
|
||||||
|
// indentation needs to be positive number
|
||||||
|
const indentation = cursorHorizontalPos < 0 ? 0 : cursorHorizontalPos;
|
||||||
|
const indentationSpace = " ".repeat(indentation);
|
||||||
|
|
||||||
|
const completions = [];
|
||||||
|
switch (keywordName) {
|
||||||
|
// loops
|
||||||
|
case "for":
|
||||||
|
completions.push({
|
||||||
|
...completion,
|
||||||
|
name: "for-loop",
|
||||||
|
text: `for(let i=0;i < array.length;i++){\n${indentationSpace}\tconst element = array[i];\n${indentationSpace}}`,
|
||||||
|
render: (element: HTMLElement) => {
|
||||||
|
element.setAttribute("keyword", "For Loop");
|
||||||
|
element.innerHTML = completion.text;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
completions.push({
|
||||||
|
...completion,
|
||||||
|
name: "for-in-loop",
|
||||||
|
text: `for(const key in object) {\n${indentationSpace}}`,
|
||||||
|
render: (element: HTMLElement) => {
|
||||||
|
element.setAttribute("keyword", "For-in Loop");
|
||||||
|
element.innerHTML = "forin";
|
||||||
|
},
|
||||||
|
});
|
||||||
|
completions.push({
|
||||||
|
...completion,
|
||||||
|
name: "for-of-loop",
|
||||||
|
text: `for(const iterator of object){\n${indentationSpace}}`,
|
||||||
|
render: (element: HTMLElement) => {
|
||||||
|
element.setAttribute("keyword", "For-of Loop");
|
||||||
|
element.innerHTML = "forof";
|
||||||
|
},
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "while":
|
||||||
|
completions.push({
|
||||||
|
...completion,
|
||||||
|
name: "while-loop",
|
||||||
|
text: `while(condition){\n${indentationSpace}}`,
|
||||||
|
render: (element: HTMLElement) => {
|
||||||
|
element.setAttribute("keyword", "While Statement");
|
||||||
|
element.innerHTML = completion.text;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "do":
|
||||||
|
completions.push({
|
||||||
|
...completion,
|
||||||
|
name: "do-while-statement",
|
||||||
|
text: `do{\n\n${indentationSpace}} while (condition);`,
|
||||||
|
render: (element: HTMLElement) => {
|
||||||
|
element.setAttribute("keyword", "do-While Statement");
|
||||||
|
element.innerHTML = completion.text;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
// conditional statement
|
||||||
|
case "if":
|
||||||
|
completions.push({
|
||||||
|
...completion,
|
||||||
|
name: "if-statement",
|
||||||
|
text: `if(condition){\n\n${indentationSpace}}`,
|
||||||
|
render: (element: HTMLElement) => {
|
||||||
|
element.setAttribute("keyword", "if Statement");
|
||||||
|
element.innerHTML = completion.text;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "switch":
|
||||||
|
completions.push({
|
||||||
|
...completion,
|
||||||
|
name: "switch-statement",
|
||||||
|
text: `switch(key){\n${indentationSpace}\tcase value:\n${indentationSpace}\t\tbreak;\n${indentationSpace}\tdefault:\n${indentationSpace}\t\tbreak;\n${indentationSpace}}`,
|
||||||
|
render: (element: HTMLElement) => {
|
||||||
|
element.setAttribute("keyword", "Switch Statement");
|
||||||
|
element.innerHTML = completion.text;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "function":
|
||||||
|
completions.push({
|
||||||
|
...completion,
|
||||||
|
name: "function-statement",
|
||||||
|
text: `function name(params){\n\n${indentationSpace}}`,
|
||||||
|
render: (element: HTMLElement) => {
|
||||||
|
element.setAttribute("keyword", "Function Statement");
|
||||||
|
element.innerHTML = completion.text;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "try":
|
||||||
|
completions.push({
|
||||||
|
...completion,
|
||||||
|
name: "try-catch",
|
||||||
|
text: `try{\n\n${indentationSpace}}catch(error){\n\n${indentationSpace}}`,
|
||||||
|
render: (element: HTMLElement) => {
|
||||||
|
element.setAttribute("keyword", "Try-catch Statement");
|
||||||
|
element.innerHTML = "try-catch";
|
||||||
|
},
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "throw":
|
||||||
|
completions.push({
|
||||||
|
...completion,
|
||||||
|
name: "throw-exception",
|
||||||
|
text: `throw new Error("");`,
|
||||||
|
render: (element: HTMLElement) => {
|
||||||
|
element.setAttribute("keyword", "Throw Exception");
|
||||||
|
element.innerHTML = completion.text;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case "new":
|
||||||
|
completions.push({
|
||||||
|
...completion,
|
||||||
|
name: "new-statement",
|
||||||
|
text: `const name = new type(arguments);`,
|
||||||
|
render: (element: HTMLElement) => {
|
||||||
|
element.setAttribute("keyword", "new Statement");
|
||||||
|
element.innerHTML = completion.text;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return completions;
|
||||||
|
};
|
||||||
Loading…
Reference in New Issue
Block a user