diff --git a/apps/remix-ide-e2e/src/commands/currentWorkspaceIs.ts b/apps/remix-ide-e2e/src/commands/currentWorkspaceIs.ts index a090735f45..26da9cf9c8 100644 --- a/apps/remix-ide-e2e/src/commands/currentWorkspaceIs.ts +++ b/apps/remix-ide-e2e/src/commands/currentWorkspaceIs.ts @@ -3,15 +3,15 @@ import EventEmitter from 'events' class CurrentWorkspaceIs extends EventEmitter { command (this: NightwatchBrowser, name: string): NightwatchBrowser { - this.api - .execute(function () { - const el = document.querySelector('select[data-id="workspacesSelect"]') as HTMLSelectElement - return el.value - }, [], (result) => { - console.log(result) - this.api.assert.equal(result.value, name) - this.emit('complete') - }) + const browser = this.api + + browser.getText('[data-id="workspacesSelect"]', function (result) { + browser.assert.equal(result.value, name) + }) + .perform((done) => { + done() + this.emit('complete') + }) return this } } diff --git a/apps/remix-ide-e2e/src/commands/switchWorkspace.ts b/apps/remix-ide-e2e/src/commands/switchWorkspace.ts new file mode 100644 index 0000000000..6219ddd218 --- /dev/null +++ b/apps/remix-ide-e2e/src/commands/switchWorkspace.ts @@ -0,0 +1,18 @@ +import { NightwatchBrowser } from 'nightwatch' +import EventEmitter from 'events' + +class switchWorkspace extends EventEmitter { + command (this: NightwatchBrowser, workspaceName: string): NightwatchBrowser { + this.api.waitForElementVisible('[data-id="workspacesSelect"]') + .click('[data-id="workspacesSelect"]') + .waitForElementVisible(`[data-id="dropdown-item-${workspaceName}"]`) + .click(`[data-id="dropdown-item-${workspaceName}"]`) + .perform((done) => { + done() + this.emit('complete') + }) + return this + } +} + +module.exports = switchWorkspace diff --git a/apps/remix-ide-e2e/src/tests/ballot.test.ts b/apps/remix-ide-e2e/src/tests/ballot.test.ts index 5244241e89..9d2e9c5b07 100644 --- a/apps/remix-ide-e2e/src/tests/ballot.test.ts +++ b/apps/remix-ide-e2e/src/tests/ballot.test.ts @@ -83,10 +83,10 @@ module.exports = { browser .openFile('Untitled.sol') .clickLaunchIcon('udapp') - .click('*[data-id="settingsWeb3Mode"]') - .waitForElementPresent('[data-id="envNotification-modal-footer-ok-react"]') + .click('*[data-id="settingsSelectEnvOptions"] *[data-id="External Http Provider"]') + .waitForElementPresent('[data-id="basic-http-provider-modal-footer-ok-react"]') .execute(function () { - const modal = document.querySelector('[data-id="envNotification-modal-footer-ok-react"]') as any + const modal = document.querySelector('[data-id="basic-http-provider-modal-footer-ok-react"]') as any modal.click() }) @@ -96,7 +96,7 @@ module.exports = { return env.value }, [], function (result) { - browser.assert.ok(result.value === 'web3', 'Web3 Provider not selected') + browser.assert.ok(result.value === 'External Http Provider', 'Web3 Provider not selected') }) .clickLaunchIcon('solidity') .clickLaunchIcon('udapp') diff --git a/apps/remix-ide-e2e/src/tests/ballot_0_4_11.test.ts b/apps/remix-ide-e2e/src/tests/ballot_0_4_11.test.ts index 28b03219fc..95696e3882 100644 --- a/apps/remix-ide-e2e/src/tests/ballot_0_4_11.test.ts +++ b/apps/remix-ide-e2e/src/tests/ballot_0_4_11.test.ts @@ -78,10 +78,10 @@ module.exports = { browser .openFile('Untitled.sol') .clickLaunchIcon('udapp') - .click('*[data-id="settingsWeb3Mode"]') - .waitForElementPresent('[data-id="envNotification-modal-footer-ok-react"]') + .click('*[data-id="settingsSelectEnvOptions"] *[data-id="External Http Provider"]') + .waitForElementPresent('[data-id="basic-http-provider-modal-footer-ok-react"]') .execute(function () { - const modal = document.querySelector('[data-id="envNotification-modal-footer-ok-react"]') as any + const modal = document.querySelector('[data-id="basic-http-provider-modal-footer-ok-react"]') as any modal.click() }) diff --git a/apps/remix-ide-e2e/src/tests/debugger.test.ts b/apps/remix-ide-e2e/src/tests/debugger.test.ts index d46c1020ce..9b8da7e7df 100644 --- a/apps/remix-ide-e2e/src/tests/debugger.test.ts +++ b/apps/remix-ide-e2e/src/tests/debugger.test.ts @@ -214,10 +214,10 @@ module.exports = { .setSolidityCompilerVersion('soljson-v0.8.7+commit.e28d00a7.js') .addFile('useDebugNodes.sol', sources[5]['useDebugNodes.sol']) // compile contract .clickLaunchIcon('udapp') - .click('*[data-id="settingsWeb3Mode"]') // select web3 provider with debug nodes URL - .clearValue('*[data-id="modalDialogCustomPromptText"]') - .setValue('*[data-id="modalDialogCustomPromptText"]', 'https://remix-rinkeby.ethdevops.io') - .modalFooterOKClick() + .click('*[data-id="settingsSelectEnvOptions"] *[data-id="External Http Provider"]') // select web3 provider with debug nodes URL + .clearValue('*[data-id="modalDialogCustomPromp"]') + .setValue('*[data-id="modalDialogCustomPromp"]', 'https://remix-rinkeby.ethdevops.io') + .modalFooterOKClick('basic-http-provider') .waitForElementPresent('*[title="Deploy - transact (not payable)"]', 65000) // wait for the compilation to succeed .clickLaunchIcon('debugger') .clearValue('*[data-id="debuggerTransactionInput"]') diff --git a/apps/remix-ide-e2e/src/tests/generalSettings.test.ts b/apps/remix-ide-e2e/src/tests/generalSettings.test.ts index ca54678a7b..a4932b05fd 100644 --- a/apps/remix-ide-e2e/src/tests/generalSettings.test.ts +++ b/apps/remix-ide-e2e/src/tests/generalSettings.test.ts @@ -41,7 +41,7 @@ module.exports = { .setValue('*[data-id="settingsTabGistAccessToken"]', '**********') .click('*[data-id="settingsTabSaveGistToken"]') .waitForElementVisible('*[data-shared="tooltipPopup"]', 5000) - .assert.containsText('*[data-shared="tooltipPopup"]', 'Access token has been saved') + .assert.containsText('*[data-shared="tooltipPopup"]', 'GitHub credentials updated') .pause(3000) }, @@ -59,7 +59,7 @@ module.exports = { .pause(1000) .click('*[data-id="settingsTabRemoveGistToken"]') .waitForElementVisible('*[data-shared="tooltipPopup"]', 5000) - .assert.containsText('*[data-shared="tooltipPopup"]', 'Access token removed') + .assert.containsText('*[data-shared="tooltipPopup"]', 'GitHub credentials removed') .assert.containsText('*[data-id="settingsTabGistAccessToken"]', '') }, diff --git a/apps/remix-ide-e2e/src/tests/migrateFileSystem.test.ts b/apps/remix-ide-e2e/src/tests/migrateFileSystem.test.ts index 3d04a3edfc..0914f6c2f5 100644 --- a/apps/remix-ide-e2e/src/tests/migrateFileSystem.test.ts +++ b/apps/remix-ide-e2e/src/tests/migrateFileSystem.test.ts @@ -81,7 +81,7 @@ module.exports = { // these are test data entries 'Should have a workspace_test #group1 #group3 #group5 #group7': function (browser: NightwatchBrowser) { browser.waitForElementVisible('*[data-id="remixIdeSidePanel"]', 5000) - .click('*[data-id="workspacesSelect"] option[value="workspace_test"]') + .switchWorkspace('workspace_test') .waitForElementVisible('*[data-id="treeViewLitreeViewItemtest_contracts"]') }, 'Should have a sol file with test data #group1 #group3 #group5 #group7': function (browser: NightwatchBrowser) { @@ -103,7 +103,7 @@ module.exports = { }, 'Should have a empty workspace #group1 #group3 #group5 #group7': function (browser: NightwatchBrowser) { browser.waitForElementVisible('*[data-id="remixIdeSidePanel"]', 5000) - .click('*[data-id="workspacesSelect"] option[value="emptyspace"]') + .switchWorkspace('emptyspace') }, // end of test data entries 'Should load with all storage blocked #group4': function (browser: NightwatchBrowser) { diff --git a/apps/remix-ide-e2e/src/tests/plugin_api.ts b/apps/remix-ide-e2e/src/tests/plugin_api.ts index faabf03247..f05a2fe130 100644 --- a/apps/remix-ide-e2e/src/tests/plugin_api.ts +++ b/apps/remix-ide-e2e/src/tests/plugin_api.ts @@ -188,7 +188,7 @@ module.exports = { .frameParent() .useCss() .clickLaunchIcon('udapp') - .waitForElementContainsText('#selectExEnvOptions option:checked', 'JavaScript VM (Berlin)') + .waitForElementContainsText('#selectExEnvOptions option:checked', 'Remix VM (Berlin)') .clickLaunchIcon('localPlugin') .useXpath() // @ts-ignore @@ -298,25 +298,25 @@ module.exports = { }, null, null) }, 'Should get all workspaces #group2': async function (browser: NightwatchBrowser) { - await clickAndCheckLog(browser, 'filePanel:getWorkspaces', ['default_workspace', 'emptyworkspace', 'testspace'], null, null) + await clickAndCheckLog(browser, 'filePanel:getWorkspaces', [{name:"default_workspace",isGitRepo:false}, {name:"emptyworkspace",isGitRepo:false}, {name:"testspace",isGitRepo:false}], null, null) }, 'Should have set workspace event #group2': async function (browser: NightwatchBrowser) { await clickAndCheckLog(browser, 'filePanel:createWorkspace', null, { event: 'setWorkspace', args: [{ name: 'newspace', isLocalhost: false }] }, 'newspace') }, 'Should have event when switching workspace #group2': async function (browser: NightwatchBrowser) { // @ts-ignore - browser.frameParent().useCss().clickLaunchIcon('filePanel').click('*[data-id="workspacesSelect"] option[value="default_workspace"]').useXpath().click('//*[@data-id="verticalIconsKindlocalPlugin"]').frame(0, async () => { + browser.frameParent().useCss().clickLaunchIcon('filePanel').switchWorkspace('default_workspace').useXpath().click('//*[@data-id="verticalIconsKindlocalPlugin"]').frame(0, async () => { await clickAndCheckLog(browser, null, null, { event: 'setWorkspace', args: [{ name: 'default_workspace', isLocalhost: false }] }, null) }) }, 'Should rename workspace #group2': async function (browser: NightwatchBrowser) { await clickAndCheckLog(browser, 'filePanel:renameWorkspace', null, null, ['default_workspace', 'renamed']) - await clickAndCheckLog(browser, 'filePanel:getWorkspaces', ['emptyworkspace', 'testspace', 'newspace', 'renamed'], null, null) + await clickAndCheckLog(browser, 'filePanel:getWorkspaces', [{name:"emptyworkspace",isGitRepo:false},{name:"testspace",isGitRepo:false},{name:"newspace",isGitRepo:false},{name:"renamed",isGitRepo:false}], null, null) }, 'Should delete workspace #group2': async function (browser: NightwatchBrowser) { await clickAndCheckLog(browser, 'filePanel:deleteWorkspace', null, null, ['testspace']) - await clickAndCheckLog(browser, 'filePanel:getWorkspaces', ['emptyworkspace', 'newspace', 'renamed'], null, null) + await clickAndCheckLog(browser, 'filePanel:getWorkspaces', [{name:"emptyworkspace",isGitRepo:false},{name:"newspace",isGitRepo:false},{name:"renamed",isGitRepo:false}], null, null) }, // DGIT 'Should have changes on new workspace #group3': async function (browser: NightwatchBrowser) { diff --git a/apps/remix-ide-e2e/src/tests/providers.test.ts b/apps/remix-ide-e2e/src/tests/providers.test.ts index b2c883fd53..fe6dcde8bf 100644 --- a/apps/remix-ide-e2e/src/tests/providers.test.ts +++ b/apps/remix-ide-e2e/src/tests/providers.test.ts @@ -44,9 +44,6 @@ module.exports = { .waitForElementContainsText('*[data-id="foundry-providerModalDialogModalBody-react"]', 'Error while connecting to the provider') .modalFooterOKClick('foundry-provider') .waitForElementNotVisible('*[data-id="foundry-providerModalDialogModalBody-react"]') - .waitForElementVisible('*[data-id="PermissionHandler-modal-footer-ok-react"]') - .click('*[data-id="PermissionHandler-modal-footer-ok-react"]') - .waitForElementNotVisible('*[data-id="PermissionHandler-modal-footer-ok-react"]') .pause(1000) }, diff --git a/apps/remix-ide-e2e/src/tests/runAndDeploy.test.ts b/apps/remix-ide-e2e/src/tests/runAndDeploy.test.ts index 7b3315e614..dc00530bdd 100644 --- a/apps/remix-ide-e2e/src/tests/runAndDeploy.test.ts +++ b/apps/remix-ide-e2e/src/tests/runAndDeploy.test.ts @@ -198,7 +198,7 @@ module.exports = { .assert.containsText('*[data-id="solidityLocals"]', 'to: 0x6C3CCC7FBA111707D5A1AAF2758E9D4F4AC5E7B1') }, - 'Call web3.eth.getAccounts() using Injected web3 (Metamask)': '' + function (browser: NightwatchBrowser) { + 'Call web3.eth.getAccounts() using Injected Provider (Metamask)': '' + function (browser: NightwatchBrowser) { browser .executeScript('web3.eth.getAccounts()') .pause(2000) diff --git a/apps/remix-ide-e2e/src/tests/solidityUnittests.test.ts b/apps/remix-ide-e2e/src/tests/solidityUnittests.test.ts index 30a2913891..7e61167107 100644 --- a/apps/remix-ide-e2e/src/tests/solidityUnittests.test.ts +++ b/apps/remix-ide-e2e/src/tests/solidityUnittests.test.ts @@ -181,7 +181,7 @@ module.exports = { .execute(function () { document.querySelector('*[data-id="modalDialogCustomPromptTextCreate"]')['value'] = 'workspace_new' }) .waitForElementVisible('*[data-id="fileSystem-modal-footer-ok-react"]') .execute(function () { (document.querySelector('[data-id="fileSystem-modal-footer-ok-react"]') as HTMLElement).click() }) - .waitForElementPresent('*[data-id="workspacesSelect"] option[value="workspace_new"]') + .currentWorkspaceIs('workspace_new') .waitForElementVisible('li[data-id="treeViewLitreeViewItem.deps/remix-tests/remix_tests.sol"]') .waitForElementVisible('li[data-id="treeViewLitreeViewItem.deps/remix-tests/remix_accounts.sol"]') .openFile('.deps/remix-tests/remix_tests.sol') diff --git a/apps/remix-ide-e2e/src/tests/terminal.test.ts b/apps/remix-ide-e2e/src/tests/terminal.test.ts index 40c59875da..59af766031 100644 --- a/apps/remix-ide-e2e/src/tests/terminal.test.ts +++ b/apps/remix-ide-e2e/src/tests/terminal.test.ts @@ -40,7 +40,7 @@ module.exports = { .waitForElementContainsText('*[data-id="terminalJournal"]', 'contract Ballot {', 60000) }, - 'Call web3.eth.getAccounts() using JavaScript VM #group2': function (browser: NightwatchBrowser) { + 'Call web3.eth.getAccounts() using Remix VM #group2': function (browser: NightwatchBrowser) { browser .executeScript('web3.eth.getAccounts()') .waitForElementContainsText('*[data-id="terminalJournal"]', '["0x5B38Da6a701c568545dCfcB03FcB875f56beddC4","0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2","0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db","0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB","0x617F2E2fD72FD9D5503197092aC168c91465E7f2","0x17F6AD8Ef982297579C203069C1DbfFE4348c372","0x5c6B0f7Bf3E7ce046039Bd8FABdfD3f9F5021678","0x03C6FcED478cBbC9a4FAB34eF9f40767739D1Ff7","0x1aE0EA34a72D944a8C7603FfB3eC30a6669E454C","0x0A098Eda01Ce92ff4A4CCb7A4fFFb5A43EBC70DC","0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c","0x14723A09ACff6D2A60DcdF7aA4AFf308FDDC160C","0x4B0897b0513fdC7C541B6d9D7E929C4e5364D2dB","0x583031D1113aD414F02576BD6afaBfb302140225","0xdD870fA1b7C4700F2BD7f44238821C26f7392148"]') @@ -50,8 +50,8 @@ module.exports = { browser .click('*[data-id="terminalClearConsole"]') // clear the terminal .clickLaunchIcon('udapp') - .click('*[data-id="settingsWeb3Mode"]') - .modalFooterOKClick('envNotification') + .click('*[data-id="settingsSelectEnvOptions"] *[data-id="External Http Provider"]') + .modalFooterOKClick('basic-http-provider') .executeScript('web3.eth.getAccounts()') .waitForElementContainsText('*[data-id="terminalJournal"]', '["', 60000) // we check if an array is present, don't need to check for the content .waitForElementContainsText('*[data-id="terminalJournal"]', '"]', 60000) diff --git a/apps/remix-ide-e2e/src/tests/url.test.ts b/apps/remix-ide-e2e/src/tests/url.test.ts index a201b4ea36..00885338b4 100644 --- a/apps/remix-ide-e2e/src/tests/url.test.ts +++ b/apps/remix-ide-e2e/src/tests/url.test.ts @@ -29,6 +29,7 @@ module.exports = { .click('[for="autoCompile"]') // we set it too false again .click('[for="autoCompile"]') // back to True in the local storage .assert.containsText('*[data-id="compilerContainerCompileBtn"]', 'contract-76747f6e19.sol') + .clickLaunchIcon('filePanel') .currentWorkspaceIs('code-sample') .getEditorValue((content) => { browser.assert.ok(content && content.indexOf( @@ -57,6 +58,7 @@ module.exports = { .url('http://127.0.0.1:8080/#optimize=true&runs=300&evmVersion=istanbul&version=soljson-v0.7.4+commit.3f05b770.js&url=https://github.com/ethereum/remix-project/blob/master/apps/remix-ide/contracts/app/solidity/mode.sol&code=cHJhZ21hIHNvbGlkaXR5ID49MC42LjAgPDAuNy4wOwoKaW1wb3J0ICJodHRwczovL2dpdGh1Yi5jb20vT3BlblplcHBlbGluL29wZW56ZXBwZWxpbi1jb250cmFjdHMvYmxvYi9tYXN0ZXIvY29udHJhY3RzL2FjY2Vzcy9Pd25hYmxlLnNvbCI7Cgpjb250cmFjdCBHZXRQYWlkIGlzIE93bmFibGUgewogIGZ1bmN0aW9uIHdpdGhkcmF3KCkgZXh0ZXJuYWwgb25seU93bmVyIHsKICB9Cn0') .refresh() // we do one reload for making sure we already have the default workspace .pause(5000) + .clickLaunchIcon('filePanel') .currentWorkspaceIs('code-sample') .getEditorValue((content) => { browser.assert.ok(content && content.indexOf( @@ -113,7 +115,7 @@ module.exports = { .url('http://127.0.0.1:8080/#optimize=false&runs=200&evmVersion=null&version=soljson-v0.6.12+commit.27d51765.js&url=https://raw.githubusercontent.com/EthVM/evm-source-verification/main/contracts/1/0x011e5846975c6463a8c6337eecf3cbf64e328884/input.json') .refresh() .pause(5000) - .waitForElementPresent('*[data-id="workspacesSelect"] option[value="code-sample"]') + .switchWorkspace('code-sample') .openFile('@openzeppelin') .openFile('@openzeppelin/contracts') .openFile('@openzeppelin/contracts/access') diff --git a/apps/remix-ide-e2e/src/tests/workspace.test.ts b/apps/remix-ide-e2e/src/tests/workspace.test.ts index 7e7cfbd55e..aa0d2bbaf1 100644 --- a/apps/remix-ide-e2e/src/tests/workspace.test.ts +++ b/apps/remix-ide-e2e/src/tests/workspace.test.ts @@ -38,7 +38,7 @@ module.exports = { .clickLaunchIcon('filePanel') .click('*[data-id="workspaceCreate"]') .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') - .waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > span') + .waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > button') // eslint-disable-next-line dot-notation .execute(function () { document.querySelector('*[data-id="modalDialogCustomPromptTextCreate"]')['value'] = 'workspace_remix_default' }) .waitForElementPresent('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') @@ -94,7 +94,7 @@ module.exports = { browser .click('*[data-id="workspaceCreate"]') .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') - .waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > span') + .waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > button') // eslint-disable-next-line dot-notation .execute(function () { document.querySelector('*[data-id="modalDialogCustomPromptTextCreate"]')['value'] = 'workspace_blank' }) .click('select[id="wstemplate"]') @@ -115,7 +115,7 @@ module.exports = { browser .click('*[data-id="workspaceCreate"]') .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') - .waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > span') + .waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > button') // eslint-disable-next-line dot-notation .execute(function () { document.querySelector('*[data-id="modalDialogCustomPromptTextCreate"]')['value'] = 'workspace_erc20' }) .click('select[id="wstemplate"]') @@ -163,7 +163,7 @@ module.exports = { browser .click('*[data-id="workspaceCreate"]') .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') - .waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > span') + .waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > button') // eslint-disable-next-line dot-notation .execute(function () { document.querySelector('*[data-id="modalDialogCustomPromptTextCreate"]')['value'] = 'workspace_erc721' }) .click('select[id="wstemplate"]') @@ -213,7 +213,7 @@ module.exports = { browser .click('*[data-id="workspaceCreate"]') // create workspace_name .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') - .waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > span') + .waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > button') .click('*[data-id="modalDialogCustomPromptTextCreate"]') .clearValue('*[data-id="modalDialogCustomPromptTextCreate"]') .setValue('*[data-id="modalDialogCustomPromptTextCreate"]', 'workspace_name') @@ -225,7 +225,7 @@ module.exports = { .waitForElementVisible('*[data-id="treeViewLitreeViewItemtest.sol"]') .click('*[data-id="workspaceCreate"]') // create workspace_name_1 .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') - .waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > span') + .waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > button') .click('*[data-id="modalDialogCustomPromptTextCreate"]') .clearValue('*[data-id="modalDialogCustomPromptTextCreate"]') .setValue('*[data-id="modalDialogCustomPromptTextCreate"]', 'workspace_name_1') @@ -235,7 +235,7 @@ module.exports = { .pause(2000) .waitForElementNotPresent('*[data-id="treeViewLitreeViewItemtest.sol"]') .pause(2000) - .click('*[data-id="workspacesSelect"] option[value="workspace_name"]') + .switchWorkspace('workspace_name') .waitForElementVisible('*[data-id="treeViewLitreeViewItemtests"]') }, @@ -249,23 +249,23 @@ module.exports = { .setValue('*[data-id="modalDialogCustomPromptTextRename"]', 'workspace_name_renamed') .waitForElementPresent('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') .click('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') - .waitForElementPresent('*[data-id="workspacesSelect"] option[value="workspace_name_1"]') - .click('*[data-id="workspacesSelect"] option[value="workspace_name_1"]') + .switchWorkspace('workspace_name_1') .pause(2000) .waitForElementNotPresent('*[data-id="treeViewLitreeViewItemtest.sol"]') - .waitForElementPresent('*[data-id="workspacesSelect"] option[value="workspace_name_renamed"]') - .click('*[data-id="workspacesSelect"] option[value="workspace_name_renamed"]') + .switchWorkspace('workspace_name_renamed') .pause(2000) .waitForElementVisible('*[data-id="treeViewLitreeViewItemtest.sol"]') }, 'Should delete a workspace #group1': function (browser: NightwatchBrowser) { browser - .click('*[data-id="workspacesSelect"] option[value="workspace_name_1"]') + .switchWorkspace('workspace_name_1') .click('*[data-id="workspaceDelete"]') // delete workspace_name_1 .waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') .click('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') - .waitForElementNotPresent('*[data-id="workspacesSelect"] option[value="workspace_name_1"]') + .waitForElementVisible('[data-id="workspacesSelect"]') + .click('[data-id="workspacesSelect"]') + .waitForElementNotPresent(`[data-id="dropdown-item-workspace_name_1"]`) .end() }, diff --git a/apps/remix-ide-e2e/src/types/index.d.ts b/apps/remix-ide-e2e/src/types/index.d.ts index f17e6b17de..eccd50bad5 100644 --- a/apps/remix-ide-e2e/src/types/index.d.ts +++ b/apps/remix-ide-e2e/src/types/index.d.ts @@ -62,7 +62,8 @@ declare module 'nightwatch' { clearConsole (this: NightwatchBrowser): NightwatchBrowser clearTransactions (this: NightwatchBrowser): NightwatchBrowser getBrowserLogs (this: NightwatchBrowser): NightwatchBrowser - currentSelectedFileIs (name: string): NightwatchBrowser + currentSelectedFileIs (name: string): NightwatchBrowser, + switchWorkspace: (workspaceName: string) => NightwatchBrowser } export interface NightwatchBrowser { diff --git a/apps/remix-ide/src/app.js b/apps/remix-ide/src/app.js index df6fe7bddc..6037f1bfbb 100644 --- a/apps/remix-ide/src/app.js +++ b/apps/remix-ide/src/app.js @@ -29,6 +29,7 @@ import { Blockchain } from './blockchain/blockchain.js' import { HardhatProvider } from './app/tabs/hardhat-provider' import { GanacheProvider } from './app/tabs/ganache-provider' import { FoundryProvider } from './app/tabs/foundry-provider' +import { ExternalHttpProvider } from './app/tabs/external-http-provider' const isElectron = require('is-electron') @@ -183,6 +184,7 @@ class AppComponent { const hardhatProvider = new HardhatProvider(blockchain) const ganacheProvider = new GanacheProvider(blockchain) const foundryProvider = new FoundryProvider(blockchain) + const externalHttpProvider = new ExternalHttpProvider(blockchain) // ----------------- convert offset to line/column service ----------- const offsetToLineColumnConverter = new OffsetToLineColumnConverter() Registry.getInstance().put({ @@ -241,6 +243,7 @@ class AppComponent { hardhatProvider, ganacheProvider, foundryProvider, + externalHttpProvider, this.walkthroughService, search ]) diff --git a/apps/remix-ide/src/app/files/dgitProvider.js b/apps/remix-ide/src/app/files/dgitProvider.js index 249640a0f3..8b5ce925c5 100644 --- a/apps/remix-ide/src/app/files/dgitProvider.js +++ b/apps/remix-ide/src/app/files/dgitProvider.js @@ -233,12 +233,11 @@ class DGitProvider extends Plugin { return this.calculateLocalStorage() } - async clone (input) { + async clone (input, workspaceName, workspaceExists = false) { const permission = await this.askUserPermission('clone', 'Import multiple files into your workspaces.') if (!permission) return false if (this.calculateLocalStorage() > 10000) throw new Error('The local storage of the browser is full.') - await this.call('filePanel', 'createWorkspace', `workspace_${Date.now()}`, true) - + if (!workspaceExists) await this.call('filePanel', 'createWorkspace', workspaceName || `workspace_${Date.now()}`, true) const cmd = { url: input.url, singleBranch: input.singleBranch, @@ -249,9 +248,11 @@ class DGitProvider extends Plugin { } const result = await git.clone(cmd) - setTimeout(async () => { - await this.call('fileManager', 'refresh') - }, 1000) + if (!workspaceExists) { + setTimeout(async () => { + await this.call('fileManager', 'refresh') + }, 1000) + } return result } diff --git a/apps/remix-ide/src/app/files/fileManager.ts b/apps/remix-ide/src/app/files/fileManager.ts index e7ae4763df..44b604b8d4 100644 --- a/apps/remix-ide/src/app/files/fileManager.ts +++ b/apps/remix-ide/src/app/files/fileManager.ts @@ -19,7 +19,7 @@ const profile = { icon: 'assets/img/fileManager.webp', permission: true, version: packageJson.version, - methods: ['closeAllFiles', 'closeFile', 'file', 'exists', 'open', 'writeFile', 'readFile', 'copyFile', 'copyDir', 'rename', 'mkdir', 'readdir', 'dirList', 'fileList', 'remove', 'getCurrentFile', 'getFile', 'getFolder', 'setFile', 'switchFile', 'refresh', 'getProviderOf', 'getProviderByName', 'getPathFromUrl', 'getUrlFromPath', 'saveCurrentFile', 'setBatchFiles'], + methods: ['closeAllFiles', 'closeFile', 'file', 'exists', 'open', 'writeFile', 'readFile', 'copyFile', 'copyDir', 'rename', 'mkdir', 'readdir', 'dirList', 'fileList', 'remove', 'getCurrentFile', 'getFile', 'getFolder', 'setFile', 'switchFile', 'refresh', 'getProviderOf', 'getProviderByName', 'getPathFromUrl', 'getUrlFromPath', 'saveCurrentFile', 'setBatchFiles', 'isGitRepo'], kind: 'file-system' } const errorMsg = { @@ -810,6 +810,13 @@ class FileManager extends Plugin { return provider.workspace } } + + async isGitRepo (directory: string): Promise { + const path = directory + '/.git' + const exists = await this.exists(path) + + return exists + } } module.exports = FileManager diff --git a/apps/remix-ide/src/app/plugins/storage.ts b/apps/remix-ide/src/app/plugins/storage.ts index 32d5c90387..cd7ec1e162 100644 --- a/apps/remix-ide/src/app/plugins/storage.ts +++ b/apps/remix-ide/src/app/plugins/storage.ts @@ -15,14 +15,14 @@ export class StoragePlugin extends Plugin { async getStorage() { let storage = null if ('storage' in navigator && 'estimate' in navigator.storage && (window as any).remixFileSystem.name !== 'localstorage') { - storage = navigator.storage.estimate() + storage = await navigator.storage.estimate() } else { storage ={ usage: parseFloat(this.calculateLocalStorage()) * 1000, quota: 5000000, } } - const _paq = window._paq = window._paq || [] + const _paq = (window as any)._paq = (window as any)._paq || [] _paq.push(['trackEvent', 'Storage', 'used', this.formatString(storage)]); return storage } @@ -32,8 +32,8 @@ export class StoragePlugin extends Plugin { } calculateLocalStorage() { - var _lsTotal = 0 - var _xLen; var _x + let _lsTotal = 0 + let _xLen; let _x for (_x in localStorage) { // eslint-disable-next-line no-prototype-builtins if (!localStorage.hasOwnProperty(_x)) { diff --git a/apps/remix-ide/src/app/tabs/abstract-provider.tsx b/apps/remix-ide/src/app/tabs/abstract-provider.tsx index b17f181166..ff924a618a 100644 --- a/apps/remix-ide/src/app/tabs/abstract-provider.tsx +++ b/apps/remix-ide/src/app/tabs/abstract-provider.tsx @@ -58,6 +58,20 @@ export abstract class AbstractProvider extends Plugin { modalType: ModalTypes.prompt, okLabel: 'OK', cancelLabel: 'Cancel', + validationFn: (value) => { + if (!value) return { valid: false, message: "value is empty" } + if (value.startsWith('https://') || value.startsWith('http://')) { + return { + valid: true, + message: '' + } + } else { + return { + valid: false, + message: 'the provided value should contain the protocol ( e.g starts with http:// or https:// )' + } + } + }, okFn: (value: string) => { setTimeout(() => resolve(value), 0) }, diff --git a/apps/remix-ide/src/app/tabs/external-http-provider.tsx b/apps/remix-ide/src/app/tabs/external-http-provider.tsx new file mode 100644 index 0000000000..5a22b48ba4 --- /dev/null +++ b/apps/remix-ide/src/app/tabs/external-http-provider.tsx @@ -0,0 +1,41 @@ +import * as packageJson from '../../../../../package.json' +import React from 'react' // eslint-disable-line +import { AbstractProvider } from './abstract-provider' + +const profile = { + name: 'basic-http-provider', + displayName: 'External Http Provider', + kind: 'provider', + description: '', + methods: ['sendAsync'], + version: packageJson.version +} + +export class ExternalHttpProvider extends AbstractProvider { + constructor (blockchain) { + super(profile, blockchain, 'http://127.0.0.1:8545') + } + + body (): JSX.Element { + const thePath = '' + return ( + <> +
+ Note: To use Geth & https://remix.ethereum.org, configure it to allow requests from Remix:(see Geth Docs on rpc server) +
geth --http --http.corsdomain https://remix.ethereum.org
+
+ To run Remix & a local Geth test node, use this command: (see Geth Docs on Dev mode) +
geth --http --http.corsdomain="{window.origin}" --http.api web3,eth,debug,personal,net --vmdebug --datadir {thePath} --dev console
+
+
+ WARNING: It is not safe to use the --http.corsdomain flag with a wildcard: --http.corsdomain * +
+
For more info: Remix Docs on Web3 Provider +
+
+ External HTTP Provider Endpoint +
+ + ) + } +} \ No newline at end of file diff --git a/apps/remix-ide/src/app/tabs/runTab/model/recorder.js b/apps/remix-ide/src/app/tabs/runTab/model/recorder.js index 550cf5fa26..caa758b180 100644 --- a/apps/remix-ide/src/app/tabs/runTab/model/recorder.js +++ b/apps/remix-ide/src/app/tabs/runTab/model/recorder.js @@ -33,7 +33,13 @@ class Recorder extends Plugin { // convert to and from to tokens if (this.data._listen) { - var record = { value, parameters: payLoad.funArgs } + var record = { + value, + inputs: txHelper.serializeInputs(payLoad.funAbi), + parameters: payLoad.funArgs, + name: payLoad.funAbi.name, + type: payLoad.funAbi.type + } if (!to) { var abi = payLoad.contractABI var keccak = ethutil.bufferToHex(ethutil.keccakFromString(JSON.stringify(abi))) @@ -55,10 +61,7 @@ class Recorder extends Plugin { var creationTimestamp = this.data._createdContracts[to] record.to = `created{${creationTimestamp}}` record.abi = this.data._contractABIReferences[creationTimestamp] - } - record.name = payLoad.funAbi.name - record.inputs = txHelper.serializeInputs(payLoad.funAbi) - record.type = payLoad.funAbi.type + } for (var p in record.parameters) { var thisarg = record.parameters[p] var thistimestamp = this.data._createdContracts[thisarg] diff --git a/apps/remix-ide/src/app/tabs/theme-module.js b/apps/remix-ide/src/app/tabs/theme-module.js index 2fd9e92e1a..ceb7192317 100644 --- a/apps/remix-ide/src/app/tabs/theme-module.js +++ b/apps/remix-ide/src/app/tabs/theme-module.js @@ -37,7 +37,7 @@ export class ThemeModule extends Plugin { themes.map((theme) => { this.themes[theme.name.toLocaleLowerCase()] = { ...theme, - url: window.location.origin + window.location.pathname + theme.url + url: window.location.origin + ( window.location.pathname.startsWith('/address/') || window.location.pathname.endsWith('.sol') ? '/' : window.location.pathname ) + theme.url } }) this._paq = _paq diff --git a/apps/remix-ide/src/app/udapp/run-tab.js b/apps/remix-ide/src/app/udapp/run-tab.js index 495b714ba2..f465b88bcc 100644 --- a/apps/remix-ide/src/app/udapp/run-tab.js +++ b/apps/remix-ide/src/app/udapp/run-tab.js @@ -156,6 +156,20 @@ export class RunTab extends ViewPlugin { } } }) + + await this.call('blockchain', 'addProvider', { + name: 'External Http Provider', + provider: { + async sendAsync (payload, callback) { + try { + const result = await udapp.call('basic-http-provider', 'sendAsync', payload) + callback(null, result) + } catch (e) { + callback(e) + } + } + } + }) } writeFile (fileName, content) { diff --git a/apps/remix-ide/src/blockchain/blockchain.js b/apps/remix-ide/src/blockchain/blockchain.js index 5f6d3ad6c6..3d6e885cec 100644 --- a/apps/remix-ide/src/blockchain/blockchain.js +++ b/apps/remix-ide/src/blockchain/blockchain.js @@ -464,7 +464,7 @@ export class Blockchain extends Plugin { } /** - * This function send a tx only to javascript VM or testnet, will return an error for the mainnet + * This function send a tx only to Remix VM or testnet, will return an error for the mainnet * SHOULD BE TAKEN CAREFULLY! * * @param {Object} tx - transaction. @@ -530,6 +530,7 @@ export class Blockchain extends Plugin { if (this.transactionContextAPI.getAddress) { return this.transactionContextAPI.getAddress(function (err, address) { if (err) return reject(err) + if (!address) return reject('"from" is not defined. Please make sure an account is selected. If you are using a public node, it is likely that no account will be provided. In that case, add the public node to your injected provider (type Metamask) and use injected provider in Remix.') return resolve(address) }) } @@ -548,9 +549,18 @@ export class Blockchain extends Plugin { const runTransaction = async () => { // eslint-disable-next-line no-async-promise-executor return new Promise(async (resolve, reject) => { - const fromAddress = await getAccount() - const value = await queryValue() - const gasLimit = await getGasLimit() + let fromAddress + let value + let gasLimit + try { + fromAddress = await getAccount() + value = await queryValue() + gasLimit = await getGasLimit() + } catch (e) { + reject(e) + return + } + const tx = { to: args.to, data: args.data.dataHex, useCall: args.useCall, from: fromAddress, value: value, gasLimit: gasLimit, timestamp: args.data.timestamp } const payLoad = { funAbi: args.data.funAbi, funArgs: args.data.funArgs, contractBytecode: args.data.contractBytecode, contractName: args.data.contractName, contractABI: args.data.contractABI, linkReferences: args.data.linkReferences } diff --git a/apps/remix-ide/src/blockchain/execution-context.js b/apps/remix-ide/src/blockchain/execution-context.js index e570b25056..95b4abe45d 100644 --- a/apps/remix-ide/src/blockchain/execution-context.js +++ b/apps/remix-ide/src/blockchain/execution-context.js @@ -164,9 +164,6 @@ export class ExecutionContext { } } - if (context === 'web3') { - confirmCb(cb) - } if (this.customNetWorks[context]) { var network = this.customNetWorks[context] this.setProviderFromEndpoint(network.provider, { context: network.name }, (error) => { diff --git a/apps/remix-ide/src/remixAppManager.js b/apps/remix-ide/src/remixAppManager.js index 89aa405757..e1dcd80f07 100644 --- a/apps/remix-ide/src/remixAppManager.js +++ b/apps/remix-ide/src/remixAppManager.js @@ -19,7 +19,7 @@ const sensitiveCalls = { } export function isNative(name) { - const nativePlugins = ['vyper', 'workshops', 'debugger', 'remixd', 'menuicons', 'solidity', 'solidity-logic', 'solidityStaticAnalysis', 'solidityUnitTesting', 'layout', 'notification', 'hardhat-provider', 'ganache-provider'] + const nativePlugins = ['vyper', 'workshops', 'debugger', 'remixd', 'menuicons', 'solidity', 'solidity-logic', 'solidityStaticAnalysis', 'solidityUnitTesting', 'layout', 'notification', 'hardhat-provider', 'ganache-provider', 'foundry-provider', 'basic-http-provider'] return nativePlugins.includes(name) || requiredModules.includes(name) } diff --git a/libs/remix-core-plugin/src/index.ts b/libs/remix-core-plugin/src/index.ts index 420fe2e6d7..5018bca7e2 100644 --- a/libs/remix-core-plugin/src/index.ts +++ b/libs/remix-core-plugin/src/index.ts @@ -8,3 +8,4 @@ export { GistHandler } from './lib/gist-handler' export * from './types/contract' export { LinkLibraries, DeployLibraries } from './lib/link-libraries' export { OpenZeppelinProxy } from './lib/openzeppelin-proxy' +export { fetchContractFromEtherscan } from './lib/helpers/fetch-etherscan' diff --git a/libs/remix-core-plugin/src/lib/helpers/fetch-etherscan.ts b/libs/remix-core-plugin/src/lib/helpers/fetch-etherscan.ts index 32264c32fa..16f6b284b8 100644 --- a/libs/remix-core-plugin/src/lib/helpers/fetch-etherscan.ts +++ b/libs/remix-core-plugin/src/lib/helpers/fetch-etherscan.ts @@ -1,8 +1,11 @@ -export const fetchContractFromEtherscan = async (plugin, network, contractAddress, targetPath) => { +export const fetchContractFromEtherscan = async (plugin, network, contractAddress, targetPath, key?) => { let data const compilationTargets = {} + let etherscanKey - const etherscanKey = await plugin.call('config', 'getAppParameter', 'etherscan-access-token') + if (!key) etherscanKey = await plugin.call('config', 'getAppParameter', 'etherscan-access-token') + else etherscanKey = key + if (etherscanKey) { const endpoint = network.id == 1 ? 'api.etherscan.io' : 'api-' + network.name + '.etherscan.io' data = await fetch('https://' + endpoint + '/api?module=contract&action=getsourcecode&address=' + contractAddress + '&apikey=' + etherscanKey) @@ -10,7 +13,7 @@ export const fetchContractFromEtherscan = async (plugin, network, contractAddres // etherscan api doc https://docs.etherscan.io/api-endpoints/contracts if (data.message === 'OK' && data.status === "1") { if (data.result.length) { - if (data.result[0].SourceCode === '') throw new Error('contract not verified in Etherscan') + if (data.result[0].SourceCode === '') throw new Error(`contract not verified on Etherscan ${network.name} network`) if (data.result[0].SourceCode.startsWith('{')) { data.result[0].SourceCode = JSON.parse(data.result[0].SourceCode.replace(/(?:\r\n|\r|\n)/g, '').replace(/^{{/,'{').replace(/}}$/,'}')) } diff --git a/libs/remix-lib/src/execution/txRunnerWeb3.ts b/libs/remix-lib/src/execution/txRunnerWeb3.ts index 4483a41fe0..2300973b3a 100644 --- a/libs/remix-lib/src/execution/txRunnerWeb3.ts +++ b/libs/remix-lib/src/execution/txRunnerWeb3.ts @@ -85,7 +85,7 @@ export class TxRunnerWeb3 { runInNode (from, to, data, value, gasLimit, useCall, timestamp, confirmCb, gasEstimationForceSend, promptCb, callback) { const tx = { from: from, to: to, data: data, value: value } - + if (!from) return callback('the value of "from" is not defined. Please make sure an account is selected.') if (useCall) { tx['gas'] = gasLimit if (this._api && this._api.isVM()) tx['timestamp'] = timestamp diff --git a/libs/remix-ui/app/src/lib/remix-app/components/modals/modal-wrapper.tsx b/libs/remix-ui/app/src/lib/remix-app/components/modals/modal-wrapper.tsx index 12644f5fa7..17c6f604d6 100644 --- a/libs/remix-ui/app/src/lib/remix-app/components/modals/modal-wrapper.tsx +++ b/libs/remix-ui/app/src/lib/remix-app/components/modals/modal-wrapper.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useRef, useState } from 'react' -import { ModalDialog, ModalDialogProps } from '@remix-ui/modal-dialog' +import { ModalDialog, ModalDialogProps, ValidationResult } from '@remix-ui/modal-dialog' import { ModalTypes } from '../../types' interface ModalWrapperProps extends ModalDialogProps { @@ -29,12 +29,23 @@ const ModalWrapper = (props: ModalWrapperProps) => { (props.cancelFn) ? props.cancelFn() : props.resolve(false) } - const createModalMessage = (defaultValue: string) => { + const onInputChanged = (event) => { + if (props.validationFn) { + const validation = props.validationFn(event.target.value) + setState(prevState => { + return { ...prevState, message: createModalMessage(props.defaultValue, validation), validation } + }) + } + } + + const createModalMessage = (defaultValue: string, validation: ValidationResult) => { return ( <> {props.message} - - ) + + {!validation.valid && {validation.message}} + + ) } useEffect(() => { @@ -47,13 +58,13 @@ const ModalWrapper = (props: ModalWrapperProps) => { ...props, okFn: onFinishPrompt, cancelFn: onCancelFn, - message: createModalMessage(props.defaultValue) + message: createModalMessage(props.defaultValue, { valid: true }) }) break default: setState({ ...props, - okFn: (onOkFn), + okFn: onOkFn, cancelFn: onCancelFn }) break @@ -67,8 +78,16 @@ const ModalWrapper = (props: ModalWrapperProps) => { } }, [props]) + // reset the message and input if any, so when the modal is shown again it doesn't show the previous value. + const handleHide = () => { + setState(prevState => { + return { ...prevState, message: '' } + }) + props.handleHide() + } + return ( - + ) } export default ModalWrapper diff --git a/libs/remix-ui/app/src/lib/remix-app/context/provider.tsx b/libs/remix-ui/app/src/lib/remix-app/context/provider.tsx index 2aca27c1be..701375265d 100644 --- a/libs/remix-ui/app/src/lib/remix-app/context/provider.tsx +++ b/libs/remix-ui/app/src/lib/remix-app/context/provider.tsx @@ -16,11 +16,11 @@ export const ModalProvider = ({ children = [], reducer = modalReducer, initialSt } const modal = (modalData: AppModal) => { - const { id, title, message, okLabel, okFn, cancelLabel, cancelFn, modalType, defaultValue, hideFn, data } = modalData + const { id, title, message, validationFn, okLabel, okFn, cancelLabel, cancelFn, modalType, defaultValue, hideFn, data } = modalData return new Promise((resolve, reject) => { dispatch({ type: modalActionTypes.setModal, - payload: { id, title, message, okLabel, okFn, cancelLabel, cancelFn, modalType: modalType || ModalTypes.default, defaultValue: defaultValue, hideFn, resolve, next: onNextFn, data } + payload: { id, title, message, okLabel, validationFn, okFn, cancelLabel, cancelFn, modalType: modalType || ModalTypes.default, defaultValue: defaultValue, hideFn, resolve, next: onNextFn, data } }) }) } diff --git a/libs/remix-ui/app/src/lib/remix-app/interface/index.ts b/libs/remix-ui/app/src/lib/remix-app/interface/index.ts index f31536678e..cc0521dd51 100644 --- a/libs/remix-ui/app/src/lib/remix-app/interface/index.ts +++ b/libs/remix-ui/app/src/lib/remix-app/interface/index.ts @@ -1,10 +1,16 @@ import { ModalTypes } from '../types' +export type ValidationResult = { + valid: boolean, + message?: string +} + export interface AppModal { id: string timestamp?: number hide?: boolean title: string + validationFn?: (value: string) => ValidationResult // eslint-disable-next-line no-undef message: string | JSX.Element okLabel: string diff --git a/libs/remix-ui/app/src/lib/remix-app/reducer/modals.ts b/libs/remix-ui/app/src/lib/remix-app/reducer/modals.ts index a50a3dbd66..51641c654c 100644 --- a/libs/remix-ui/app/src/lib/remix-app/reducer/modals.ts +++ b/libs/remix-ui/app/src/lib/remix-app/reducer/modals.ts @@ -11,6 +11,7 @@ export const modalReducer = (state: ModalState = ModalInitialState, action: Moda id: action.payload.id || timestamp.toString(), hide: false, title: action.payload.title, + validationFn: action.payload.validationFn, message: action.payload.message, okLabel: action.payload.okLabel, okFn: action.payload.okFn, diff --git a/libs/remix-ui/app/src/lib/remix-app/state/modals.ts b/libs/remix-ui/app/src/lib/remix-app/state/modals.ts index 8332d60120..2b10dccbea 100644 --- a/libs/remix-ui/app/src/lib/remix-app/state/modals.ts +++ b/libs/remix-ui/app/src/lib/remix-app/state/modals.ts @@ -8,6 +8,7 @@ export const ModalInitialState: ModalState = { hide: true, title: '', message: '', + validationFn: () => { return {valid: true, message: ''} }, okLabel: '', okFn: () => { }, cancelLabel: '', diff --git a/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx b/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx index 7787daeddf..46a5e76d9b 100644 --- a/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx +++ b/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx @@ -416,6 +416,20 @@ export const EditorUI = (props: EditorUIProps) => { editor.addCommand(monacoRef.current.KeyMod.CtrlCmd | monacoRef.current.KeyCode.US_MINUS, () => { editor.updateOptions({ fontSize: editor.getOption(43).fontSize - 1 }) }) + + const editorService = editor._codeEditorService; + const openEditorBase = editorService.openCodeEditor.bind(editorService); + editorService.openCodeEditor = async (input, source) => { + const result = await openEditorBase(input, source) + if (input && input.resource && input.resource.path) { + try { + await props.plugin.call('fileManager', 'open', input.resource.path) + } catch (e) { + console.log(e) + } + } + return result + } } function handleEditorWillMount (monaco) { diff --git a/libs/remix-ui/helper/src/lib/components/web3Dialog.tsx b/libs/remix-ui/helper/src/lib/components/web3Dialog.tsx index 30055f322a..3412148086 100644 --- a/libs/remix-ui/helper/src/lib/components/web3Dialog.tsx +++ b/libs/remix-ui/helper/src/lib/components/web3Dialog.tsx @@ -24,10 +24,10 @@ export function Web3ProviderDialog (props: web3ProviderDialogProps) {
WARNING: It is not safe to use the --http.corsdomain flag with a wildcard: --http.corsdomain *
-
For more info: Remix Docs on Web3 Provider +
For more info: Remix Docs on Remix Provider

- Web3 Provider Endpoint + External HTTP Provider Endpoint (
@@ -54,10 +53,6 @@ export const sourceVerificationNotAvailableToastMsg = () => (
) -export const web3Dialog = (externalEndpoint: string, setWeb3Endpoint: (value: string) => void) => ( - -) - export const envChangeNotification = (env: { context: string, fork: string }, from: string) => (
diff --git a/libs/remix-ui/helper/src/lib/remix-ui-helper.ts b/libs/remix-ui/helper/src/lib/remix-ui-helper.ts index 71abad338d..d9fd9b03b9 100644 --- a/libs/remix-ui/helper/src/lib/remix-ui-helper.ts +++ b/libs/remix-ui/helper/src/lib/remix-ui-helper.ts @@ -47,6 +47,22 @@ export const createNonClashingNameAsync = async (name: string, fileManager, pref return name + counter + prefix + '.' + ext } +export const createNonClashingTitle = async (name: string, fileManager) => { + if (!name) name = 'Undefined' + let _counter + let exist = true + + do { + const isDuplicate = await fileManager.exists(name + (_counter || '')) + + if (isDuplicate) _counter = (_counter || 0) + 1 + else exist = false + } while (exist) + const counter = _counter || '' + + return name + counter +} + export const joinPath = (...paths) => { paths = paths.filter((value) => value !== '').map((path) => path.replace(/^\/|\/$/g, '')) // remove first and last slash) if (paths.length === 1) return paths[0] diff --git a/libs/remix-ui/modal-dialog/src/lib/remix-ui-modal-dialog.tsx b/libs/remix-ui/modal-dialog/src/lib/remix-ui-modal-dialog.tsx index 38df2fd8b8..936956c62a 100644 --- a/libs/remix-ui/modal-dialog/src/lib/remix-ui-modal-dialog.tsx +++ b/libs/remix-ui/modal-dialog/src/lib/remix-ui-modal-dialog.tsx @@ -96,18 +96,20 @@ export const ModalDialog = (props: ModalDialogProps) => {
{/* todo add autofocus ^^ */} - { props.okLabel && { + if (props.validation && !props.validation.valid) return if (props.okFn) props.okFn() handleHide() }} > {props.okLabel ? props.okLabel : 'OK'} - + } - { props.cancelLabel && { }} > {props.cancelLabel ? props.cancelLabel : 'Cancel'} - + }
diff --git a/libs/remix-ui/modal-dialog/src/lib/types/index.ts b/libs/remix-ui/modal-dialog/src/lib/types/index.ts index a8793971d7..f1ec8c88d9 100644 --- a/libs/remix-ui/modal-dialog/src/lib/types/index.ts +++ b/libs/remix-ui/modal-dialog/src/lib/types/index.ts @@ -1,8 +1,15 @@ +export type ValidationResult = { + valid: boolean, + message?: string +} + /* eslint-disable no-undef */ export interface ModalDialogProps { id: string timestamp?: number, title?: string, + validation?: ValidationResult + validationFn?: (value: string) => ValidationResult message?: string | JSX.Element, okLabel?: string, okFn?: (value?:any) => void, diff --git a/libs/remix-ui/run-tab/src/lib/actions/account.ts b/libs/remix-ui/run-tab/src/lib/actions/account.ts index 9fba9a0723..12b05bec2b 100644 --- a/libs/remix-ui/run-tab/src/lib/actions/account.ts +++ b/libs/remix-ui/run-tab/src/lib/actions/account.ts @@ -1,4 +1,4 @@ -import { shortenAddress, web3Dialog } from "@remix-ui/helper" +import { shortenAddress } from "@remix-ui/helper" import { RunTab } from "../types/run-tab" import { clearInstances, setAccount, setExecEnv } from "./actions" import { displayNotification, displayPopUp, fetchAccountsListFailed, fetchAccountsListRequest, fetchAccountsListSuccess, setExternalEndpoint, setMatchPassphrase, setPassphrase } from "./payload" @@ -74,28 +74,7 @@ const _getProviderDropdownValue = (plugin: RunTab): string => { } export const setExecutionContext = (plugin: RunTab, dispatch: React.Dispatch, executionContext: { context: string, fork: string }) => { - const displayContent = web3Dialog(plugin.REACT_API.externalEndpoint, (endpoint: string) => { - dispatch(setExternalEndpoint(endpoint)) - }) - - plugin.blockchain.changeExecutionContext(executionContext, () => { - plugin.call('notification', 'modal', { - id: 'envNotification', - title: 'External node request', - message: displayContent, - okLabel: 'OK', - cancelLabel: 'Cancel', - okFn: () => { - plugin.blockchain.setProviderFromEndpoint(plugin.REACT_API.externalEndpoint, executionContext, (alertMsg) => { - if (alertMsg) plugin.call('notification', 'toast', alertMsg) - setFinalContext(plugin, dispatch) - }) - }, - cancelFn: () => { - setFinalContext(plugin, dispatch) - } - }) - }, (alertMsg) => { + plugin.blockchain.changeExecutionContext(executionContext, null, (alertMsg) => { plugin.call('notification', 'toast', alertMsg) }, () => { setFinalContext(plugin, dispatch) }) } diff --git a/libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx b/libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx index 159cbb0a8e..6d4617732b 100644 --- a/libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx +++ b/libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx @@ -59,10 +59,7 @@ export function ContractDropdownUI (props: ContractDropdownProps) { content: currentFile }) enableAtAddress(true) - } else if (/.(.sol)$/.exec(currentFile) || - /.(.vy)$/.exec(currentFile) || // vyper - /.(.lex)$/.exec(currentFile) || // lexon - /.(.contract)$/.exec(currentFile)) { + } else if (isContractFile(currentFile)) { setAbiLabel({ display: 'none', content: '' @@ -117,6 +114,13 @@ export function ContractDropdownUI (props: ContractDropdownProps) { } } + const isContractFile = (file) => { + return /.(.sol)$/.exec(file) || + /.(.vy)$/.exec(file) || // vyper + /.(.lex)$/.exec(file) || // lexon + /.(.contract)$/.exec(file) + } + const enableAtAddress = (enable: boolean) => { if (enable) { setAtAddressOptions({ @@ -218,7 +222,7 @@ export function ContractDropdownUI (props: ContractDropdownProps) {
- { (contractList[currentFile] || []).map((contract, index) => { return }) } @@ -262,7 +266,7 @@ export function ContractDropdownUI (props: ContractDropdownProps) {
: '' } -
+
diff --git a/libs/remix-ui/run-tab/src/lib/components/environment.tsx b/libs/remix-ui/run-tab/src/lib/components/environment.tsx index 61c5668eec..8fbd17cfa9 100644 --- a/libs/remix-ui/run-tab/src/lib/components/environment.tsx +++ b/libs/remix-ui/run-tab/src/lib/components/environment.tsx @@ -9,7 +9,7 @@ export function EnvironmentUI (props: EnvironmentProps) { const fork = provider.fork // can be undefined if connected to an external source (web3 provider / injected) let context = provider.value - context = context.startsWith('vm') ? 'vm' : context // context has to be 'vm', 'web3' or 'injected' + context = context.startsWith('vm') ? 'vm' : context props.setExecutionContext({ context, fork }) } diff --git a/libs/remix-ui/run-tab/src/lib/components/instanceContainerUI.tsx b/libs/remix-ui/run-tab/src/lib/components/instanceContainerUI.tsx index 09aa84b3d3..b6a9a5f57d 100644 --- a/libs/remix-ui/run-tab/src/lib/components/instanceContainerUI.tsx +++ b/libs/remix-ui/run-tab/src/lib/components/instanceContainerUI.tsx @@ -15,7 +15,7 @@ export function InstanceContainerUI (props: InstanceContainerProps) { return (
-