From abba2e3487bcc8affffbdf59635c08a1679d4b27 Mon Sep 17 00:00:00 2001 From: filip mertens Date: Sun, 17 Jul 2022 15:57:43 +0200 Subject: [PATCH] more tests --- .../src/examples/editor-test-contracts.ts | 102 ++++-- .../src/tests/editorAutoComplete.test.ts | 311 ++++++++++++++---- .../src/lib/providers/completionProvider.ts | 23 +- 3 files changed, 358 insertions(+), 78 deletions(-) diff --git a/apps/remix-ide-e2e/src/examples/editor-test-contracts.ts b/apps/remix-ide-e2e/src/examples/editor-test-contracts.ts index cd45fc5e6d..86992350e4 100644 --- a/apps/remix-ide-e2e/src/examples/editor-test-contracts.ts +++ b/apps/remix-ide-e2e/src/examples/editor-test-contracts.ts @@ -1,12 +1,13 @@ // eslint-disable-next-line @typescript-eslint/no-unused-vars const testContract = { - name: 'test.sol', - content: ` + name: 'contracts/test.sol', + content: ` // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; import "contracts/base.sol"; - + import "contracts/import1.sol"; + contract test is base { string public publicstring; string private privatestring; @@ -20,6 +21,7 @@ const testContract = { TestBookDefinition public mybook; enum MyEnum{ SMALL, MEDIUM, LARGE } event MyEvent(uint abc); + importcontract importedcontract; modifier costs(uint price) { if (msg.value >= price) { @@ -46,12 +48,12 @@ const testContract = { } }`} - - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const baseContract = { - name: 'base.sol', - content: ` + + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const baseContract = { + name: 'contracts/base.sol', + content: ` // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; @@ -67,11 +69,11 @@ const testContract = { } Book public book; }`} - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const baseOfBaseContract = { - name: 'baseofbase.sol', - content: ` + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const baseOfBaseContract = { + name: 'contracts/baseofbase.sol', + content: ` // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; @@ -102,8 +104,70 @@ const testContract = { } }`} - export default { - testContract, - baseContract, - baseOfBaseContract - } \ No newline at end of file +const import1Contract = { + name: 'contracts/import1.sol', + content: ` + // SPDX-License-Identifier: MIT + pragma solidity >=0.7.0 <0.9.0; + + import "contracts/importbase.sol"; + import "contracts/secondimport.sol"; + +contract importcontract is importbase { + struct ImportedBook { + string title; + string author; + uint book_id; + } + ImportedBook public importedbook; + + string private importprivatestring; + string internal internalimportstring; + string public importpublicstring; + + function privateimport() private { + + } + + function internalimport() internal { + + } + + function publicimport() public { + + } + + function externalimport() external { + } +}`} + +const importbase = { + name: 'contracts/importbase.sol', + content: ` + // SPDX-License-Identifier: MIT +pragma solidity >=0.7.0 <0.9.0; + +contract importbase { + string public importbasestring; +} +`} + +const secondimport = { + name: 'contracts/secondimport.sol', + content: ` + // SPDX-License-Identifier: MIT +pragma solidity >=0.7.0 <0.9.0; + +contract secondimport { + string public secondimportstring; +} +`} + +export default { + testContract, + baseContract, + baseOfBaseContract, + import1Contract, + importbase, + secondimport +} \ No newline at end of file diff --git a/apps/remix-ide-e2e/src/tests/editorAutoComplete.test.ts b/apps/remix-ide-e2e/src/tests/editorAutoComplete.test.ts index 1a6ed6b492..85e5d55411 100644 --- a/apps/remix-ide-e2e/src/tests/editorAutoComplete.test.ts +++ b/apps/remix-ide-e2e/src/tests/editorAutoComplete.test.ts @@ -13,10 +13,13 @@ module.exports = { init(browser, done, 'http://127.0.0.1:8080', false) }, 'Should add test and base files #group2': function (browser: NightwatchBrowser) { - browser.addFile('contracts/test.sol', examples.testContract) - .addFile('contracts/base.sol', examples.baseContract) - .addFile('contracts/baseofbase.sol', examples.baseOfBaseContract) - .openFile('contracts/test.sol').pause(3000) + browser.addFile(examples.testContract.name, examples.testContract) + .addFile(examples.baseContract.name, examples.baseContract) + .addFile(examples.import1Contract.name, examples.import1Contract) + .addFile(examples.baseOfBaseContract.name, examples.baseOfBaseContract) + .addFile(examples.secondimport.name, examples.secondimport) + .addFile(examples.importbase.name, examples.importbase) + .openFile(examples.testContract.name) }, 'Should put cursor in the () of the function #group2': function (browser: NightwatchBrowser) { browser.scrollToLine(18) @@ -27,49 +30,49 @@ module.exports = { }, 'Should complete variable declaration types in a function definition #group2': function (browser: NightwatchBrowser) { browser - .perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys('uint25') - }) - .waitForElementPresent(autoCompleteLineElement('uint256')) - .click(autoCompleteLineElement('uint256')) - .perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys(' abc, testb') - }) - .waitForElementPresent(autoCompleteLineElement('"TestBookDefinition"')) - .click(autoCompleteLineElement('"TestBookDefinition"')) - .perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys(' memo') - }) - .waitForElementPresent(autoCompleteLineElement('memory')) - .click(autoCompleteLineElement('memory')) - .perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys(' t, BaseB') - }) - .waitForElementPresent(autoCompleteLineElement('"BaseBook"')) - .click(autoCompleteLineElement('"BaseBook"')) - .perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys(' stor') - }) - .waitForElementPresent(autoCompleteLineElement('storage')) - .click(autoCompleteLineElement('storage')) - .perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys(' b') - }) + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys('uint25') + }) + .waitForElementPresent(autoCompleteLineElement('uint256')) + .click(autoCompleteLineElement('uint256')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys(' abc, testb') + }) + .waitForElementPresent(autoCompleteLineElement('"TestBookDefinition"')) + .click(autoCompleteLineElement('"TestBookDefinition"')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys(' memo') + }) + .waitForElementPresent(autoCompleteLineElement('memory')) + .click(autoCompleteLineElement('memory')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys(' btextbook, BaseB') + }) + .waitForElementPresent(autoCompleteLineElement('"BaseBook"')) + .click(autoCompleteLineElement('"BaseBook"')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys(' stor') + }) + .waitForElementPresent(autoCompleteLineElement('storage')) + .click(autoCompleteLineElement('storage')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys(' localbbook') + }).pause(3000) }, 'Should put cursor at the end of function #group2': function (browser: NightwatchBrowser) { - + const path = "//*[@class='view-line' and contains(.,'myprivatefunction') and contains(.,'private')]//span//span[contains(.,'{')]" browser .useXpath() @@ -82,7 +85,139 @@ module.exports = { sendKeys(this.Keys.ARROW_RIGHT) }) }, + 'Should autcomplete address types': function (browser: NightwatchBrowser) { + browser + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys('addre') + }) + .waitForElementPresent(autoCompleteLineElement('address')) + .click(autoCompleteLineElement('address')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys(' someaddress;') + .sendKeys(this.Keys.ENTER) + }).pause(2000) + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys('someaddress.') + }) + .waitForElementVisible(autoCompleteLineElement('balance')) + .waitForElementVisible(autoCompleteLineElement('send')) + .waitForElementVisible(autoCompleteLineElement('transfer')) + .waitForElementVisible(autoCompleteLineElement('code')) + .click(autoCompleteLineElement('balance')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions + .sendKeys(this.Keys.ENTER) + }) + }, + 'Should autcomplete array types': function (browser: NightwatchBrowser) { + browser + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys('uin') + }) + .waitForElementPresent(autoCompleteLineElement('uint')) + .click(autoCompleteLineElement('uint')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys('[] mem') + }) + .waitForElementVisible(autoCompleteLineElement('memory')) + .click(autoCompleteLineElement('memory')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys(' somearray;') + } + ).pause(2000) + .perform(function () { + const actions = this.actions({ async: true }); + return actions + .sendKeys(this.Keys.ENTER) + .sendKeys('somearray.') + }) + .waitForElementVisible(autoCompleteLineElement('push')) + .waitForElementVisible(autoCompleteLineElement('pop')) + .waitForElementVisible(autoCompleteLineElement('length')) + .click(autoCompleteLineElement('length')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions + .sendKeys(this.Keys.ENTER) + }) + }, + 'Should see and autocomplete second import because it was imported by the first import #group2': function (browser: NightwatchBrowser) { + browser + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys('secondi') + }) + .waitForElementPresent(autoCompleteLineElement('secondimport')) + .click(autoCompleteLineElement('secondimport')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys(' sec;') + .sendKeys(this.Keys.ENTER) + }).pause(3000) + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys('sec.') + }) + .waitForElementVisible(autoCompleteLineElement('secondimportstring')) + .click(autoCompleteLineElement('secondimportstring')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys(';') + .sendKeys(this.Keys.ENTER) + }) + + }, + 'Should see and autocomplete imported local class #group2': function (browser: NightwatchBrowser) { + browser + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys('import') + }) + .waitForElementPresent(autoCompleteLineElement('importedcontract')) + .click(autoCompleteLineElement('importedcontract')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys('.') + }) + .waitForElementVisible(autoCompleteLineElement('externalimport')) + .waitForElementVisible(autoCompleteLineElement('importbasestring')) + .waitForElementVisible(autoCompleteLineElement('importedbook')) + .waitForElementVisible(autoCompleteLineElement('importpublicstring')) + .waitForElementVisible(autoCompleteLineElement('publicimport')) + // no private + .waitForElementNotPresent(autoCompleteLineElement('importprivatestring')) + .waitForElementNotPresent(autoCompleteLineElement('privateimport')) + // no internal + .waitForElementNotPresent(autoCompleteLineElement('importinternalstring')) + .waitForElementNotPresent(autoCompleteLineElement('internalimport')) + .click(autoCompleteLineElement('importbasestring')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys(';') + .sendKeys(this.Keys.ENTER) + }) + }, 'Should autocomplete derived and local event when not using this. #group2': function (browser: NightwatchBrowser) { browser.perform(function () { const actions = this.actions({ async: true }); @@ -106,15 +241,15 @@ module.exports = { return actions. sendKeys('emit MyEv') }) - .waitForElementVisible(autoCompleteLineElement('MyEvent')) - .click(autoCompleteLineElement('MyEvent')) - .perform(function () { - const actions = this.actions({ async: true }); - return actions - .sendKeys('3232') - .sendKeys(this.Keys.TAB) - .sendKeys(this.Keys.ENTER) - }) + .waitForElementVisible(autoCompleteLineElement('MyEvent')) + .click(autoCompleteLineElement('MyEvent')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions + .sendKeys('3232') + .sendKeys(this.Keys.TAB) + .sendKeys(this.Keys.ENTER) + }) }, 'Should type and get msg options #group2': function (browser: NightwatchBrowser) { @@ -134,7 +269,17 @@ module.exports = { .perform(function () { const actions = this.actions({ async: true }); return actions. - sendKeys(';'). + sendKeys('.') + }) + .waitForElementVisible(autoCompleteLineElement('balance')) + .waitForElementVisible(autoCompleteLineElement('code')) + .waitForElementVisible(autoCompleteLineElement('codehash')) + .waitForElementVisible(autoCompleteLineElement('send')) + .waitForElementVisible(autoCompleteLineElement('transfer')) + .click(autoCompleteLineElement('balance')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions. sendKeys(this.Keys.ENTER) }) }, @@ -194,6 +339,62 @@ module.exports = { .sendKeys(this.Keys.ENTER) }) }, + 'Should block scoped localbbook #group2': function (browser: NightwatchBrowser) { + browser.pause(4000). + perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys(this.Keys.ENTER). + sendKeys('localb') + }) + .waitForElementVisible(autoCompleteLineElement('localbbook')) + .click(autoCompleteLineElement('localbbook')) + }, + 'Should autcomplete derived struct from block localbbook #group2': function (browser: NightwatchBrowser) { + browser.perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys('.') + }) + .waitForElementVisible(autoCompleteLineElement('author')) + .waitForElementVisible(autoCompleteLineElement('book_id')) + .waitForElementVisible(autoCompleteLineElement('title')) + .click(autoCompleteLineElement('title')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys(';') + .sendKeys(this.Keys.ENTER) + }) + }, + 'Should block scoped btextbook #group2': function (browser: NightwatchBrowser) { + browser. + perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys(this.Keys.ENTER). + sendKeys('btext') + }) + .waitForElementVisible(autoCompleteLineElement('btextbook')) + .click(autoCompleteLineElement('btextbook')) + }, + 'Should autcomplete derived struct from block btextbook #group2': function (browser: NightwatchBrowser) { + browser.perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys('.') + }) + .waitForElementVisible(autoCompleteLineElement('author')) + .waitForElementVisible(autoCompleteLineElement('book_id')) + .waitForElementVisible(autoCompleteLineElement('title')) + .click(autoCompleteLineElement('title')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys(';') + .sendKeys(this.Keys.ENTER) + }) + }, 'Should find private and internal local functions #group2': function (browser: NightwatchBrowser) { browser.perform(function () { const actions = this.actions({ async: true }); diff --git a/libs/remix-ui/editor/src/lib/providers/completionProvider.ts b/libs/remix-ui/editor/src/lib/providers/completionProvider.ts index 217f4202c3..33c0962b0f 100644 --- a/libs/remix-ui/editor/src/lib/providers/completionProvider.ts +++ b/libs/remix-ui/editor/src/lib/providers/completionProvider.ts @@ -263,11 +263,22 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider } } } - //const nodesOfScope = await this.props.plugin.call('codeParser', 'getNodesWithScope', node.id) - // nodes = [...nodes, ...nodesOfScope] + // blocks can have statements + /* + if (node.statements){ + console.log('statements', node.statements) + for (const statement of node.statements) { + if(statement.expression){ + const declaration = await this.props.plugin.call('codeParser', 'declarationOf', statement.expression) + declaration.outSideBlock = true + nodes = [...nodes, declaration] + } + } + } + */ } } - console.log('NODES AT BLOCK SCOPE', nodes) + // we are only interested in nodes that are in the same block as the cursor nodes = nodes.filter(node => { if (node.src) { @@ -276,9 +287,12 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider return true } } + if(node.outSideBlock){ return true } return false }) + console.log('NODES AT BLOCK SCOPE', nodes) + return nodes; } @@ -373,7 +387,7 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider for (const nodeOfScope of contractCompletions) { - // console.log('nodeOfScope', nodeOfScope.name, nodeOfScope) + console.log('nodeOfScope', nodeOfScope.name, nodeOfScope.memberName, nodeOfScope) if (nodeOfScope.name === nameOfLastTypedExpression) { console.log('FOUND NODE', nodeOfScope) if (nodeOfScope.typeName && nodeOfScope.typeName.nodeType === 'UserDefinedTypeName') { @@ -396,6 +410,7 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider suggestions = [...suggestions, ...getContextualAutoCompleteBTypeName('address', range, this.monaco)] } } + }