diff --git a/apps/remix-ide-e2e/src/tests/proxy.test.ts b/apps/remix-ide-e2e/src/tests/proxy.test.ts index 29e611b155..ef587929e4 100644 --- a/apps/remix-ide-e2e/src/tests/proxy.test.ts +++ b/apps/remix-ide-e2e/src/tests/proxy.test.ts @@ -70,12 +70,13 @@ module.exports = { browser .openFile('myTokenV1.sol') .clickLaunchIcon('solidity') - .pause(2000) + .pause(5000) .click('[data-id="compilerContainerCompileBtn"]') .waitForElementPresent('select[id="compiledContracts"] option[value=MyToken]', 60000) .clickLaunchIcon('udapp') .click('select.udapp_contractNames') .click('select.udapp_contractNames option[value=MyToken]') + .pause(5000) .waitForElementPresent('[data-id="contractGUIDeployWithProxyLabel"]') .click('[data-id="contractGUIDeployWithProxyLabel"]') .createContract('') @@ -105,7 +106,8 @@ module.exports = { done() }) }) - }, + },//*[@id="runTabView"]/div/div[2]/div[3]/div[1]/div/div[1]/div[4]/div/div[1]/input + // #runTabView > div > div.udapp_container > div:nth-child(3) > div.udapp_deployDropdown > div > div.udapp_contractProperty > div.pl-4.flex-column.d-flex > div > div:nth-child(1) > input 'Should deploy proxy with initialize parameters #group1': function (browser: NightwatchBrowser) { browser @@ -121,10 +123,12 @@ module.exports = { .click('select.udapp_contractNames option[value=MyInitializedToken]') .waitForElementPresent('[data-id="contractGUIDeployWithProxyLabel"]') .click('[data-id="contractGUIDeployWithProxyLabel"]') - .waitForElementPresent('input[title="tokenName"]') - .waitForElementPresent('input[title="tokenSymbol"]') - .setValue('input[title="tokenName"]', 'Remix') - .setValue('input[title="tokenSymbol"]', "R") + .useXpath() + .waitForElementPresent('//*[@id="runTabView"]/div/div[2]/div[3]/div[1]/div/div[1]/div[4]/div/div[1]/input') + .waitForElementPresent('//*[@id="runTabView"]/div/div[2]/div[3]/div[1]/div/div[1]/div[4]/div/div[2]/input') + .setValue('//*[@id="runTabView"]/div/div[2]/div[3]/div[1]/div/div[1]/div[4]/div/div[1]/input', 'Remix') + .setValue('//*[@id="runTabView"]/div/div[2]/div[3]/div[1]/div/div[1]/div[4]/div/div[2]/input', "R") + .useCss() .createContract('') .waitForElementContainsText('[data-id="udappNotifyModalDialogModalTitle-react"]', 'Deploy Implementation & Proxy (ERC1967)') .waitForElementVisible('[data-id="udappNotify-modal-footer-ok-react"]') diff --git a/apps/remix-ide-e2e/src/tests/workspace.test.ts b/apps/remix-ide-e2e/src/tests/workspace.test.ts index fff723f887..ea6512529c 100644 --- a/apps/remix-ide-e2e/src/tests/workspace.test.ts +++ b/apps/remix-ide-e2e/src/tests/workspace.test.ts @@ -383,106 +383,9 @@ module.exports = { .click('//*[@id="workspacesSelect"]') .useCss() .waitForElementNotPresent(`[data-id="dropdown-item-workspace_name_1"]`) - }, - - // CLONE REPOSITORY E2E START - - 'Should clone a repository #group2': function (browser: NightwatchBrowser) { - browser - .clickLaunchIcon('filePanel') - .useXpath() - .click('//*[@id="workspacesMenuDropdown"]/span/i') - .waitForElementVisible('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]') - .click('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]') - .useCss() - .waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]') - .click('[data-id="fileSystemModalDialogModalBody-react"]') - .waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]') - .setValue('[data-id="modalDialogCustomPromptTextClone"]', 'https://github.com/ethereum/awesome-remix') - .click('[data-id="fileSystem-modal-footer-ok-react"]') - .waitForElementPresent('.fa-spinner') - .pause(5000) - .waitForElementNotPresent('.fa-spinner') - .waitForElementVisible('*[data-id="treeViewLitreeViewItem.git"]') - .waitForElementContainsText('[data-id="workspacesSelect"]', 'awesome-remix') - }, - - 'Should display dgit icon for cloned workspace #group2': function (browser: NightwatchBrowser) { - browser - .switchWorkspace('default_workspace') - .waitForElementNotVisible('[data-id="workspacesSelect"] .fa-code-branch') - .switchWorkspace('awesome-remix') - .waitForElementVisible('[data-id="workspacesSelect"] .fa-code-branch') - }, - - 'Should display non-clashing names for duplicate clone #group2': '' + function (browser: NightwatchBrowser) { - browser - .useXpath() - .click('//*[@id="workspacesMenuDropdown"]/span/i') - .waitForElementVisible('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]') - .click('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]') - .useCss() - .waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]') - .click('[data-id="fileSystemModalDialogModalBody-react"]') - .waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]') - .setValue('[data-id="modalDialogCustomPromptTextClone"]', 'https://github.com/ethereum/awesome-remix') - .click('[data-id="fileSystem-modal-footer-ok-react"]') - .pause(5000) - .waitForElementContainsText('[data-id="workspacesSelect"]', 'awesome-remix1') - .useXpath() - .click('//*[@id="workspacesMenuDropdown"]/span/i') - .waitForElementVisible('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]') - .click('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]') - .useCss() - .waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]') - .click('[data-id="fileSystemModalDialogModalBody-react"]') - .waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]') - .setValue('[data-id="modalDialogCustomPromptTextClone"]', 'https://github.com/ethereum/awesome-remix') - .click('[data-id="fileSystem-modal-footer-ok-react"]') - .pause(5000) - .waitForElementContainsText('[data-id="workspacesSelect"]', 'awesome-remix2') - .useXpath() - .click('//*[@id="workspacesMenuDropdown"]/span/i') - .waitForElementVisible('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]') - .useCss() - .waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]') - .click('[data-id="fileSystemModalDialogModalBody-react"]') - .waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]') - .setValue('[data-id="modalDialogCustomPromptTextClone"]', 'https://github.com/ethereum/awesome-remix') - .click('[data-id="fileSystem-modal-footer-ok-react"]') - .pause(5000) - .waitForElementContainsText('[data-id="workspacesSelect"]', 'awesome-remix3') - .switchWorkspace('awesome-remix') - .switchWorkspace('awesome-remix1') - .switchWorkspace('awesome-remix2') - .switchWorkspace('awesome-remix3') - }, - - 'Should display error message in modal for failed clone #group2': function (browser: NightwatchBrowser) { - browser - .useXpath() - .waitForElementPresent({ - selector: '//i[@data-icon="workspaceDropdownMenuIcon"]', - locateStrategy: 'xpath', - }) - .click('//*[@id="workspacesMenuDropdown"]/span/i') - .waitForElementVisible('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]') - .click('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]') - .useCss() - .waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]') - .click('[data-id="fileSystemModalDialogModalBody-react"]') - .waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]') - .setValue('[data-id="modalDialogCustomPromptTextClone"]', 'https://github.com/ethereum/non-existent-repo') - .click('[data-id="fileSystem-modal-footer-ok-react"]') - .pause(5000) - .waitForElementVisible('[data-id="cloneGitRepositoryModalDialogModalBody-react"]') - .waitForElementContainsText('[data-id="cloneGitRepositoryModalDialogModalBody-react"]', 'An error occurred: Please check that you have the correct URL for the repo. If the repo is private, you need to add your github credentials (with the valid token permissions) in Settings plugin') - .click('[data-id="cloneGitRepository-modal-footer-ok-react"]') .end() }, - // CLONE REPOSITORY E2E END - tearDown: sauce } diff --git a/apps/remix-ide-e2e/src/tests/workspace_git.test.ts b/apps/remix-ide-e2e/src/tests/workspace_git.test.ts new file mode 100644 index 0000000000..c977f8e24d --- /dev/null +++ b/apps/remix-ide-e2e/src/tests/workspace_git.test.ts @@ -0,0 +1,241 @@ +'use strict' +import { NightwatchBrowser } from "nightwatch" +import init from "../helpers/init" +import sauce from "./sauce" + +module.exports = { + '@disabled': true, + before: function (browser: NightwatchBrowser, done: VoidFunction) { + init(browser, done) + }, + + 'Should create and initialize a GIT repository #group1': function (browser: NightwatchBrowser) { + browser + .clickLaunchIcon('filePanel') + .waitForElementNotVisible('[data-id="workspaceGitPanel"]') + .click('*[data-id="workspaceCreate"]') + .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') + .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"]') + .click('select[id="wstemplate"] option[value=blank]') + .click('[data-id="initGitRepositoryLabel"]') + .waitForElementPresent('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') + .execute(function () { (document.querySelector('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') as HTMLElement).click() }) + .pause(100) + .waitForElementVisible('[data-id="workspaceGitPanel"]') + .waitForElementContainsText('[data-id="workspaceGitBranchesDropdown"]', 'main') + }, + + // CLONE REPOSITORY E2E START + + 'Should clone a repository #group2': function (browser: NightwatchBrowser) { + browser + .clickLaunchIcon('filePanel') + .useXpath() + .click('//*[@id="workspacesMenuDropdown"]/span/i') + .waitForElementVisible('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]') + .click('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]') + .useCss() + .waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]') + .click('[data-id="fileSystemModalDialogModalBody-react"]') + .waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]') + .setValue('[data-id="modalDialogCustomPromptTextClone"]', 'https://github.com/ethereum/awesome-remix') + .click('[data-id="fileSystem-modal-footer-ok-react"]') + .waitForElementPresent('.fa-spinner') + .pause(5000) + .waitForElementNotPresent('.fa-spinner') + .waitForElementVisible('*[data-id="treeViewLitreeViewItem.git"]') + .waitForElementContainsText('[data-id="workspacesSelect"]', 'awesome-remix') + }, + + 'Should display dgit icon for cloned workspace #group2': function (browser: NightwatchBrowser) { + browser + .switchWorkspace('default_workspace') + .waitForElementNotVisible('[data-id="workspacesSelect"] .fa-code-branch') + .switchWorkspace('awesome-remix') + .waitForElementVisible('[data-id="workspacesSelect"] .fa-code-branch') + }, + + 'Should display non-clashing names for duplicate clone #group2': '' + function (browser: NightwatchBrowser) { + browser + .useXpath() + .click('//*[@id="workspacesMenuDropdown"]/span/i') + .waitForElementVisible('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]') + .click('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]') + .useCss() + .waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]') + .click('[data-id="fileSystemModalDialogModalBody-react"]') + .waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]') + .setValue('[data-id="modalDialogCustomPromptTextClone"]', 'https://github.com/ethereum/awesome-remix') + .click('[data-id="fileSystem-modal-footer-ok-react"]') + .pause(5000) + .waitForElementContainsText('[data-id="workspacesSelect"]', 'awesome-remix1') + .useXpath() + .click('//*[@id="workspacesMenuDropdown"]/span/i') + .waitForElementVisible('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]') + .click('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]') + .useCss() + .waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]') + .click('[data-id="fileSystemModalDialogModalBody-react"]') + .waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]') + .setValue('[data-id="modalDialogCustomPromptTextClone"]', 'https://github.com/ethereum/awesome-remix') + .click('[data-id="fileSystem-modal-footer-ok-react"]') + .pause(5000) + .waitForElementContainsText('[data-id="workspacesSelect"]', 'awesome-remix2') + .useXpath() + .click('//*[@id="workspacesMenuDropdown"]/span/i') + .waitForElementVisible('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]') + .useCss() + .waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]') + .click('[data-id="fileSystemModalDialogModalBody-react"]') + .waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]') + .setValue('[data-id="modalDialogCustomPromptTextClone"]', 'https://github.com/ethereum/awesome-remix') + .click('[data-id="fileSystem-modal-footer-ok-react"]') + .pause(5000) + .waitForElementContainsText('[data-id="workspacesSelect"]', 'awesome-remix3') + .switchWorkspace('awesome-remix') + .switchWorkspace('awesome-remix1') + .switchWorkspace('awesome-remix2') + .switchWorkspace('awesome-remix3') + }, + + 'Should display error message in modal for failed clone #group2': function (browser: NightwatchBrowser) { + browser + .useXpath() + .waitForElementPresent({ + selector: '//i[@data-icon="workspaceDropdownMenuIcon"]', + locateStrategy: 'xpath', + }) + .click('//*[@id="workspacesMenuDropdown"]/span/i') + .waitForElementVisible('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]') + .click('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]') + .useCss() + .waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]') + .click('[data-id="fileSystemModalDialogModalBody-react"]') + .waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]') + .setValue('[data-id="modalDialogCustomPromptTextClone"]', 'https://github.com/ethereum/non-existent-repo') + .click('[data-id="fileSystem-modal-footer-ok-react"]') + .pause(5000) + .waitForElementVisible('[data-id="cloneGitRepositoryModalDialogModalBody-react"]') + .waitForElementContainsText('[data-id="cloneGitRepositoryModalDialogModalBody-react"]', 'An error occurred: Please check that you have the correct URL for the repo. If the repo is private, you need to add your github credentials (with the valid token permissions) in Settings plugin') + .click('[data-id="cloneGitRepository-modal-footer-ok-react"]') + }, + + // CLONE REPOSITORY E2E END + + // GIT BRANCHES E2E START + 'Should show all cloned repo branches #group3': function (browser: NightwatchBrowser) { + browser + .clickLaunchIcon('filePanel') + .waitForElementNotVisible('[data-id="workspaceGitPanel"]') + .useXpath() + .click('//*[@id="workspacesMenuDropdown"]/span/i') + .waitForElementVisible('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]') + .click('//*[@id="workspacesMenuDropdown"]/div/ul/a[5]') + .useCss() + .waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]') + .click('[data-id="fileSystemModalDialogModalBody-react"]') + .waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]') + .setValue('[data-id="modalDialogCustomPromptTextClone"]', 'https://github.com/ioedeveloper/test-branch-change') + .click('[data-id="fileSystem-modal-footer-ok-react"]') + .waitForElementPresent('.fa-spinner') + .pause(5000) + .waitForElementNotPresent('.fa-spinner') + .waitForElementContainsText('[data-id="workspacesSelect"]', 'test-branch-change') + .waitForElementVisible('[data-id="workspaceGitPanel"]') + .click('[data-id="workspaceGitBranchesDropdown"]') + .waitForElementVisible('[data-id="custom-dropdown-menu"]') + .waitForElementContainsText('[data-id="custom-dropdown-items"]', 'origin/dev') + .waitForElementContainsText('[data-id="custom-dropdown-items"]', 'origin/production') + .waitForElementContainsText('[data-id="custom-dropdown-items"]', 'origin/setup') + .expect.element('[data-id="workspaceGit-main"]').text.to.contain('✓ ') + }, + + 'Should a checkout to a remote branch #group3': function (browser: NightwatchBrowser) { + browser + .waitForElementVisible('[data-id="custom-dropdown-menu"]') + .waitForElementContainsText('[data-id="custom-dropdown-items"]', 'origin/dev') + .waitForElementPresent('[data-id="workspaceGit-origin/dev"]') + .click('[data-id="workspaceGit-origin/dev"]') + .pause(5000) + .waitForElementPresent('[data-id="treeViewDivtreeViewItemdev.ts"]') + .click('[data-id="workspaceGitBranchesDropdown"]') + .expect.element('[data-id="workspaceGit-dev"]').text.to.contain('✓ ') + }, + + 'Should search for a branch (local and remote) #group3': function (browser: NightwatchBrowser) { + browser + .waitForElementVisible('[data-id="custom-dropdown-menu"]') + .waitForElementPresent('[data-id="workspaceGitInput"]') + .sendKeys('[data-id="workspaceGitInput"]', 'setup') + .waitForElementNotPresent('[data-id="workspaceGit-origin/dev"]') + .waitForElementNotPresent('[data-id="workspaceGit-origin/production"]') + .waitForElementNotPresent('[data-id="workspaceGit-dev"]') + .waitForElementNotPresent('[data-id="workspaceGit-main"]') + .waitForElementPresent('[data-id="workspaceGit-origin/setup"]') + }, + + 'Should checkout to a new local branch #group3': function (browser: NightwatchBrowser) { + browser + .waitForElementVisible('[data-id="custom-dropdown-menu"]') + .waitForElementPresent('[data-id="workspaceGitInput"]') + .clearValue('[data-id="workspaceGitInput"]') + .sendKeys('[data-id="workspaceGitInput"]', 'newLocalBranch') + .waitForElementContainsText('[data-id="workspaceGitCreateNewBranch"]', `Create branch: newLocalBranch from 'dev'`) + .click('[data-id="workspaceGitCreateNewBranch"]') + .pause(2000) + .click('[data-id="workspaceGitBranchesDropdown"]') + .waitForElementVisible('[data-id="custom-dropdown-menu"]') + .expect.element('[data-id="workspaceGit-newLocalBranch"]').text.to.contain('✓ ') + }, + + 'Should checkout to an exisiting local branch #group3': function (browser: NightwatchBrowser) { + browser + .waitForElementVisible('[data-id="custom-dropdown-menu"]') + .waitForElementPresent('[data-id="workspaceGitInput"]') + .clearValue('[data-id="workspaceGitInput"]') + .sendKeys('[data-id="workspaceGitInput"]', [browser.Keys.SPACE, browser.Keys.BACK_SPACE]) + .waitForElementPresent('[data-id="workspaceGit-main"]') + .click('[data-id="workspaceGit-main"]') + .pause(2000) + .waitForElementNotPresent('[data-id="treeViewDivtreeViewItemdev.ts"]') + .waitForElementPresent('[data-id="treeViewDivtreeViewItemmain.ts"]') + .click('[data-id="workspaceGitBranchesDropdown"]') + .expect.element('[data-id="workspaceGit-main"]').text.to.contain('✓ ') + }, + + 'Should prevent checkout to a branch if local changes exists #group3': function (browser: NightwatchBrowser) { + browser + .renamePath('README.md', 'README.txt', 'README.txt') + .waitForElementVisible('[data-id="workspaceGitBranchesDropdown"]') + .click('[data-id="workspaceGitBranchesDropdown"]') + .waitForElementVisible('[data-id="workspaceGit-dev"]') + .click('[data-id="workspaceGit-dev"]') + .waitForElementVisible('[data-id="switchBranchModalDialogContainer-react"]') + .waitForElementContainsText('[data-id="switchBranchModalDialogModalBody-react"]', 'Your local changes to the following files would be overwritten by checkout.') + .click('[data-id="switchBranchModalDialogModalFooter-react"]') + .click('[data-id="switchBranch-modal-footer-cancel-react"]') + .pause(2000) + .click('[data-id="workspaceGitBranchesDropdown"]') + .expect.element('[data-id="workspaceGit-main"]').text.to.contain('✓ ') + }, + + 'Should force checkout to a branch with exisiting local changes #group3': function (browser: NightwatchBrowser) { + browser + .waitForElementVisible('[data-id="workspaceGit-dev"]') + .click('[data-id="workspaceGit-dev"]') + .waitForElementVisible('[data-id="switchBranchModalDialogContainer-react"]') + .waitForElementContainsText('[data-id="switchBranchModalDialogModalBody-react"]', 'Your local changes to the following files would be overwritten by checkout.') + .click('[data-id="switchBranchModalDialogModalFooter-react"]') + .click('[data-id="switchBranch-modal-footer-ok-react"]') + .pause(2000) + .click('[data-id="workspaceGitBranchesDropdown"]') + .expect.element('[data-id="workspaceGit-dev"]').text.to.contain('✓ ') + }, + + // GIT BRANCHES E2E END + + tearDown: sauce +} \ No newline at end of file diff --git a/apps/remix-ide/ci/browser_test.sh b/apps/remix-ide/ci/browser_test.sh index 65f5960a9b..00e7ec440d 100755 --- a/apps/remix-ide/ci/browser_test.sh +++ b/apps/remix-ide/ci/browser_test.sh @@ -20,7 +20,7 @@ yarn run build:e2e node apps/remix-ide/ci/splice_tests.js $2 $3 TESTFILES=$(node apps/remix-ide/ci/splice_tests.js $2 $3 | circleci tests split --split-by=timings) for TESTFILE in $TESTFILES; do - npx nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/${TESTFILE}.js --env=$1 || TEST_EXITCODE=1 + npx nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/${TESTFILE}.js --env=$1 || npx nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/${TESTFILE}.js --env=$1 || TEST_EXITCODE=1 done echo "$TEST_EXITCODE" diff --git a/apps/remix-ide/src/app/editor/editor.js b/apps/remix-ide/src/app/editor/editor.js index 23601242f3..4876e0618f 100644 --- a/apps/remix-ide/src/app/editor/editor.js +++ b/apps/remix-ide/src/app/editor/editor.js @@ -50,7 +50,8 @@ class Editor extends Plugin { abi: 'json', rs: 'rust', cairo: 'cairo', - ts: 'typescript' + ts: 'typescript', + move: 'move' } this.activated = false diff --git a/apps/remix-ide/src/app/plugins/notification.tsx b/apps/remix-ide/src/app/plugins/notification.tsx index 975f6943fa..83754e8b38 100644 --- a/apps/remix-ide/src/app/plugins/notification.tsx +++ b/apps/remix-ide/src/app/plugins/notification.tsx @@ -1,8 +1,8 @@ import { Plugin } from '@remixproject/engine' import { LibraryProfile, MethodApi, StatusEvents } from '@remixproject/plugin-utils' import { AppModal } from '@remix-ui/app' -import { AlertModal } from 'libs/remix-ui/app/src/lib/remix-app/interface' -import { dispatchModalInterface } from 'libs/remix-ui/app/src/lib/remix-app/context/context' +import { AlertModal } from '@remix-ui/app' +import { dispatchModalInterface } from '@remix-ui/app' interface INotificationApi { events: StatusEvents, diff --git a/apps/remix-ide/src/app/tabs/compile-and-run.ts b/apps/remix-ide/src/app/tabs/compile-and-run.ts index e90d9f5844..a8ce143125 100644 --- a/apps/remix-ide/src/app/tabs/compile-and-run.ts +++ b/apps/remix-ide/src/app/tabs/compile-and-run.ts @@ -51,11 +51,11 @@ export class CompileAndRun extends Plugin { } async runScript (fileName, clearAllInstances) { - await this.call('terminal', 'log', `running ${fileName} ...`) + await this.call('terminal', 'log', { value: `running ${fileName} ...`, type: 'info' }) try { const exists = await this.call('fileManager', 'exists', fileName) if (!exists) { - await this.call('terminal', 'log', `${fileName} does not exist.`) + await this.call('terminal', 'log', { value: `${fileName} does not exist.`, type: 'info' } ) return } const content = await this.call('fileManager', 'readFile', fileName) diff --git a/apps/remix-ide/src/app/tabs/web3-provider.js b/apps/remix-ide/src/app/tabs/web3-provider.js index 1a0e10a637..615170a9c9 100644 --- a/apps/remix-ide/src/app/tabs/web3-provider.js +++ b/apps/remix-ide/src/app/tabs/web3-provider.js @@ -32,7 +32,7 @@ export class Web3ProviderModule extends Plugin { if (error) { const errorData = error.data ? error.data : error.message ? error.message : error // See: https://github.com/ethers-io/ethers.js/issues/901 - if (!(typeof errorData === 'string' && errorData.includes("unknown method eth_chainId"))) this.call('terminal', 'log', error.data ? error.data : error.message) + if (!(typeof errorData === 'string' && errorData.includes("unknown method eth_chainId"))) this.call('terminal', 'log', { value: error.data ? error.data : error.message, type: 'error' } ) return reject(errorData) } if (payload.method === 'eth_sendTransaction') { diff --git a/apps/remix-ide/src/app/udapp/run-tab.js b/apps/remix-ide/src/app/udapp/run-tab.js index c12e67ae87..f9a49bb7aa 100644 --- a/apps/remix-ide/src/app/udapp/run-tab.js +++ b/apps/remix-ide/src/app/udapp/run-tab.js @@ -78,7 +78,7 @@ export class RunTab extends ViewPlugin { } sendTransaction (tx) { - _paq.push(['trackEvent', 'udapp', 'sendTx']) + _paq.push(['trackEvent', 'udapp', 'sendTx', 'udappTransaction']) return this.blockchain.sendTransaction(tx) } diff --git a/apps/remix-ide/src/blockchain/blockchain.js b/apps/remix-ide/src/blockchain/blockchain.js index 16e42c5cdf..79d63c1684 100644 --- a/apps/remix-ide/src/blockchain/blockchain.js +++ b/apps/remix-ide/src/blockchain/blockchain.js @@ -674,22 +674,24 @@ export class Blockchain extends Plugin { const hhlogs = await this.web3().eth.getHHLogsForTx(txResult.transactionHash) if (hhlogs && hhlogs.length) { - let finalLogs = 'console.log:\n' - for (const log of hhlogs) { - let formattedLog - // Hardhat implements the same formatting options that can be found in Node.js' console.log, - // which in turn uses util.format: https://nodejs.org/dist/latest-v12.x/docs/api/util.html#util_util_format_format_args - // For example: console.log("Name: %s, Age: %d", remix, 6) will log 'Name: remix, Age: 6' - // We check first arg to determine if 'util.format' is needed - if (typeof log[0] === 'string' && (log[0].includes('%s') || log[0].includes('%d'))) { - formattedLog = format(log[0], ...log.slice(1)) - } else { - formattedLog = log.join(' ') - } - finalLogs = finalLogs + ' ' + formattedLog + '\n' - } + let finalLogs =
console.log:
+ { + hhlogs.map((log) => { + let formattedLog + // Hardhat implements the same formatting options that can be found in Node.js' console.log, + // which in turn uses util.format: https://nodejs.org/dist/latest-v12.x/docs/api/util.html#util_util_format_format_args + // For example: console.log("Name: %s, Age: %d", remix, 6) will log 'Name: remix, Age: 6' + // We check first arg to determine if 'util.format' is needed + if (typeof log[0] === 'string' && (log[0].includes('%s') || log[0].includes('%d'))) { + formattedLog = format(log[0], ...log.slice(1)) + } else { + formattedLog = log.join(' ') + } + return
{formattedLog}
+ })} +
_paq.push(['trackEvent', 'udapp', 'hardhat', 'console.log']) - this.call('terminal', 'log', { type: 'info', value: finalLogs }) + this.call('terminal', 'logHtml', finalLogs) } execResult = await this.web3().eth.getExecutionResultFromSimulator(txResult.transactionHash) if (execResult) { diff --git a/apps/remix-ide/src/blockchain/execution-context.js b/apps/remix-ide/src/blockchain/execution-context.js index 0b63c241cd..1b7f808bc4 100644 --- a/apps/remix-ide/src/blockchain/execution-context.js +++ b/apps/remix-ide/src/blockchain/execution-context.js @@ -14,6 +14,8 @@ if (typeof window !== 'undefined' && typeof window.ethereum !== 'undefined') { web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545')) } +const noInjectedProviderMsg = 'No injected provider found. Make sure your provider (e.g. MetaMask) is active and running (when recently activated you may have to reload the page).' + /* trigger contextChanged, web3EndpointChanged */ @@ -38,12 +40,16 @@ export class ExecutionContext { this.executionContext = 'vm' } else { this.executionContext = injectedProvider ? 'injected' : 'vm' - if (this.executionContext === 'injected') this.askPermission() + if (this.executionContext === 'injected') this.askPermission(false) } } - askPermission () { - if (ethereum && typeof ethereum.request === "function") ethereum.request({ method: "eth_requestAccounts" }); + askPermission (throwIfNoInjectedProvider) { + if (typeof ethereum !== "undefined" && typeof ethereum.request === "function") { + ethereum.request({ method: "eth_requestAccounts" }) + } else if (throwIfNoInjectedProvider) { + throw new Error(noInjectedProviderMsg) + } } getProvider () { @@ -80,7 +86,7 @@ export class ExecutionContext { } if (web3.currentProvider.isConnected && !web3.currentProvider.isConnected()) { if (web3.currentProvider.isMetaMask) { - this.askPermission() + this.askPermission(false) } return callback('Provider not connected') } @@ -152,13 +158,18 @@ export class ExecutionContext { if (context === 'injected') { if (injectedProvider === undefined) { - infoCb('No injected provider found. Make sure your provider (e.g. MetaMask) is active and running (when recently activated you may have to reload the page).') + infoCb(noInjectedProviderMsg) return cb() } else { if (injectedProvider && injectedProvider._metamask && injectedProvider._metamask.isUnlocked) { if (!await injectedProvider._metamask.isUnlocked()) infoCb('Please make sure the injected provider is unlocked (e.g Metamask).') } - this.askPermission() + try { + this.askPermission(true) + } catch (e) { + infoCb(e.message) + return cb() + } this.executionContext = context web3.setProvider(injectedProvider) await this._updateChainContext() @@ -176,7 +187,12 @@ export class ExecutionContext { }) } else { // injected - this.askPermission() + try { + this.askPermission(true) + } catch (e) { + infoCb(e.message) + return cb() + } this.executionContext = context web3.setProvider(network.provider) await this._updateChainContext() diff --git a/libs/remix-debug/src/debugger/VmDebugger.ts b/libs/remix-debug/src/debugger/VmDebugger.ts index b639a48375..91d7e54d0a 100644 --- a/libs/remix-debug/src/debugger/VmDebugger.ts +++ b/libs/remix-debug/src/debugger/VmDebugger.ts @@ -114,7 +114,7 @@ export class VmDebuggerLogic { try { const memory = this._traceManager.getMemoryAt(index) if (this.stepManager.currentStepIndex === index) { - this.event.trigger('traceManagerMemoryUpdate', [ui.formatMemory(memory, 16)]) + this.event.trigger('traceManagerMemoryUpdate', [ui.formatMemory(memory, 32)]) } } catch (error) { this.event.trigger('traceManagerMemoryUpdate', [{}]) diff --git a/libs/remix-lib/src/types/ICompilerApi.ts b/libs/remix-lib/src/types/ICompilerApi.ts index 259aa2547a..850c000744 100644 --- a/libs/remix-lib/src/types/ICompilerApi.ts +++ b/libs/remix-lib/src/types/ICompilerApi.ts @@ -49,7 +49,7 @@ export interface ICompilerApi { } export type terminalLog = { - type: 'info' | 'error' | 'warning' + type: 'info' | 'error' | 'warning' | 'log' value: string } diff --git a/libs/remix-ui/app/src/index.ts b/libs/remix-ui/app/src/index.ts index 45082b0fec..2bf8054edb 100644 --- a/libs/remix-ui/app/src/index.ts +++ b/libs/remix-ui/app/src/index.ts @@ -1,5 +1,5 @@ export { default as RemixApp } from './lib/remix-app/remix-app' -export { dispatchModalContext, AppContext } from './lib/remix-app/context/context' +export { dispatchModalContext, dispatchModalInterface, AppContext } from './lib/remix-app/context/context' export { ModalProvider, useDialogDispatchers } from './lib/remix-app/context/provider' export { AppModal } from './lib/remix-app/interface/index' export { AlertModal } from './lib/remix-app/interface/index' diff --git a/libs/remix-ui/app/src/lib/remix-app/actions/modals.ts b/libs/remix-ui/app/src/lib/remix-app/actions/modals.ts index 4a567a83c0..7e78c46cd5 100644 --- a/libs/remix-ui/app/src/lib/remix-app/actions/modals.ts +++ b/libs/remix-ui/app/src/lib/remix-app/actions/modals.ts @@ -22,7 +22,7 @@ export const enum modalActionTypes { type ModalPayload = { [modalActionTypes.setModal]: AppModal [modalActionTypes.handleHideModal]: any - [modalActionTypes.setToast]: string | JSX.Element + [modalActionTypes.setToast]: { message: string | JSX.Element, timestamp: number } [modalActionTypes.handleToaster]: any, [modalActionTypes.processQueue]: any } diff --git a/libs/remix-ui/app/src/lib/remix-app/components/modals/dialogs.tsx b/libs/remix-ui/app/src/lib/remix-app/components/modals/dialogs.tsx index 7aba74f73b..90bd71a05d 100644 --- a/libs/remix-ui/app/src/lib/remix-app/components/modals/dialogs.tsx +++ b/libs/remix-ui/app/src/lib/remix-app/components/modals/dialogs.tsx @@ -10,7 +10,7 @@ const AppDialogs = () => { return ( <> - + ) } export default AppDialogs 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 701375265d..635500a8b3 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 @@ -39,7 +39,7 @@ export const ModalProvider = ({ children = [], reducer = modalReducer, initialSt const toast = (message: string | JSX.Element) => { dispatch({ type: modalActionTypes.setToast, - payload: message + payload: { message, timestamp: Date.now() } }) } 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 cc0521dd51..69db40e679 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 @@ -33,7 +33,7 @@ export interface AlertModal { export interface ModalState { modals: AppModal[], - toasters: (string | JSX.Element)[], + toasters: {message: (string | JSX.Element), timestamp: number }[], focusModal: AppModal, - focusToaster: string | JSX.Element + focusToaster: {message: (string | JSX.Element), timestamp: number } } 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 51641c654c..e2961285d6 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 @@ -62,8 +62,7 @@ export const modalReducer = (state: ModalState = ModalInitialState, action: Moda } case modalActionTypes.setToast: { const toasterList = state.toasters.slice() - const message = action.payload - toasterList.push(message) + toasterList.push(action.payload) if (toasterList.length === 1) { return { ...state, toasters: toasterList, focusToaster: action.payload } } else { 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 2b10dccbea..3120766dac 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,11 +8,11 @@ export const ModalInitialState: ModalState = { hide: true, title: '', message: '', - validationFn: () => { return {valid: true, message: ''} }, + validationFn: () => { return { valid: true, message: '' } }, okLabel: '', okFn: () => { }, cancelLabel: '', cancelFn: () => { } }, - focusToaster: '' + focusToaster: { message: '', timestamp: 0 } } 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 1bad91819b..b74275fe26 100644 --- a/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx +++ b/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx @@ -1,10 +1,12 @@ import React, { useState, useRef, useEffect, useReducer } from 'react' // eslint-disable-line import Editor, { loader, Monaco } from '@monaco-editor/react' +import { AlertModal } from '@remix-ui/app' import { reducerActions, reducerListener, initialState } from './actions/editor' import { solidityTokensProvider, solidityLanguageConfig } from './syntaxes/solidity' import { cairoTokensProvider, cairoLanguageConfig } from './syntaxes/cairo' import { zokratesTokensProvider, zokratesLanguageConfig } from './syntaxes/zokrates' +import { moveTokenProvider, moveLanguageConfig } from './syntaxes/move' import './remix-ui-editor.css' import { loadTypes } from './web-types' @@ -135,6 +137,7 @@ export const EditorUI = (props: EditorUIProps) => { \t\t\t\t\t\t\t\tMedium: https://medium.com/remix-ide\n \t\t\t\t\t\t\t\tTwitter: https://twitter.com/ethereumremix\n ` + const pasteCodeRef = useRef(false) const editorRef = useRef(null) const monacoRef = useRef(null) const currentFileRef = useRef('') @@ -301,6 +304,8 @@ export const EditorUI = (props: EditorUIProps) => { monacoRef.current.editor.setModelLanguage(file.model, 'remix-cairo') } else if (file.language === 'zokrates') { monacoRef.current.editor.setModelLanguage(file.model, 'remix-zokrates') + } else if (file.language === 'move') { + monacoRef.current.editor.setModelLanguage(file.model, 'remix-move') } }, [props.currentFile]) @@ -541,6 +546,33 @@ export const EditorUI = (props: EditorUIProps) => { } }) + editor.onDidPaste((e) => { + if (!pasteCodeRef.current && e && e.range && e.range.startLineNumber >= 0 && e.range.endLineNumber >= 0 && e.range.endLineNumber - e.range.startLineNumber > 10) { + const modalContent: AlertModal = { + id: 'newCodePasted', + title: 'Pasted Code Alert', + message: ( +
+ You have just pasted a code snippet or contract in the editor. +
+ Make sure you fully understand this code before deploying or interacting with it. Don't get scammed! +
+ Running untrusted code can put your wallet at risk . In a worst-case scenario, you could loose all your money. +
+
If you don't fully understand it, please don't run this code.
+
+ If you are not a smart contract developer, ask someone you trust who has the skills to determine if this code is safe to use. +
+
See these recommendations for more information.
+
+
+ ), + } + props.plugin.call('notification', 'alert', modalContent) + pasteCodeRef.current = true + } + }) + // zoomin zoomout editor.addCommand(monacoRef.current.KeyMod.CtrlCmd | (monacoRef.current.KeyCode as any).US_EQUAL, () => { editor.updateOptions({ fontSize: editor.getOption(43).fontSize + 1 }) @@ -618,6 +650,7 @@ export const EditorUI = (props: EditorUIProps) => { monacoRef.current.languages.register({ id: 'remix-solidity' }) monacoRef.current.languages.register({ id: 'remix-cairo' }) monacoRef.current.languages.register({ id: 'remix-zokrates' }) + monacoRef.current.languages.register({ id: 'remix-move' }) // Register a tokens provider for the language monacoRef.current.languages.setMonarchTokensProvider('remix-solidity', solidityTokensProvider as any) @@ -629,6 +662,9 @@ export const EditorUI = (props: EditorUIProps) => { monacoRef.current.languages.setMonarchTokensProvider('remix-zokrates', zokratesTokensProvider as any) monacoRef.current.languages.setLanguageConfiguration('remix-zokrates', zokratesLanguageConfig as any) + monacoRef.current.languages.setMonarchTokensProvider('remix-move', moveTokenProvider as any) + monacoRef.current.languages.setLanguageConfiguration('remix-move', moveLanguageConfig as any) + monacoRef.current.languages.registerDefinitionProvider('remix-solidity', new RemixDefinitionProvider(props, monaco)) monacoRef.current.languages.registerDocumentHighlightProvider('remix-solidity', new RemixHighLightProvider(props, monaco)) monacoRef.current.languages.registerReferenceProvider('remix-solidity', new RemixReferenceProvider(props, monaco)) diff --git a/libs/remix-ui/editor/src/lib/syntaxes/move.ts b/libs/remix-ui/editor/src/lib/syntaxes/move.ts new file mode 100644 index 0000000000..9f184937a0 --- /dev/null +++ b/libs/remix-ui/editor/src/lib/syntaxes/move.ts @@ -0,0 +1,264 @@ +/* eslint-disable no-useless-escape */ +export const moveLanguageConfig = { + comments: { + lineComment: "//", + blockComment: ["/*", "*/"], + }, + brackets: [ + ["{", "}"], + ["[", "]"], + ["(", ")"], + ], + autoClosingPairs: [ + { open: "[", close: "]" }, + { open: "{", close: "}" }, + { open: "(", close: ")" }, + { open: '"', close: '"', notIn: ["string"] }, + ], + surroundingPairs: [ + { open: "{", close: "}" }, + { open: "[", close: "]" }, + { open: "(", close: ")" }, + { open: '"', close: '"' }, + { open: "'", close: "'" }, + ], + folding: { + markers: { + start: new RegExp("^\\s*#pragma\\s+region\\b"), + end: new RegExp("^\\s*#pragma\\s+endregion\\b"), + }, + }, +}; +export const moveTokenProvider = { + // Set defaultToken to invalid to see what you do not tokenize yet + // defaultToken: 'invalid', + keywords: [ + "as", + "break", + "const", + "crate", + "enum", + "extern", + "false", + "fun", + "script", + "in", + "let", + "module", + "move", + "mut", + "pub", + "ref", + "return", + "self", + "Self", + "static", + "struct", + "super", + "trait", + "true", + "type", + "unsafe", + "use", + "where", + "use", + "macro_rules", + ], + + controlFlowKeywords: [ + "continue", + "else", + "for", + "if", + "while", + "loop", + "match", + ], + + typeKeywords: [ + "Self", + "m32", + "m64", + "m128", + "f80", + "f16", + "f128", + "int", + "uint", + "float", + "char", + "bool", + "u8", + "u16", + "u32", + "u64", + "f32", + "f64", + "i8", + "i16", + "i32", + "i64", + "str", + "Option", + "Either", + "c_float", + "c_double", + "c_void", + "FILE", + "fpos_t", + "DIR", + "dirent", + "c_char", + "c_schar", + "c_uchar", + "c_short", + "c_ushort", + "c_int", + "c_uint", + "c_long", + "c_ulong", + "size_t", + "ptrdiff_t", + "clock_t", + "time_t", + "c_longlong", + "c_ulonglong", + "intptr_t", + "uintptr_t", + "off_t", + "dev_t", + "ino_t", + "pid_t", + "mode_t", + "ssize_t", + ], + + operators: [ + "=", + ">", + "<", + "!", + "~", + "?", + ":", + "==", + "<=", + ">=", + "!=", + "&&", + "||", + "++", + "--", + "+", + "-", + "*", + "/", + "&", + "|", + "^", + "%", + "<<", + ">>", + ">>>", + "+=", + "-=", + "*=", + "/=", + "&=", + "|=", + "^=", + "%=", + "<<=", + ">>=", + ">>>=", + ], + + // we include these common regular expressions + symbols: /[=>](?!@symbols)/, "@brackets"], + [/@symbols/, { cases: { "@operators": "operator", "@default": "" } }], + + // @ annotations. + // As an example, we emit a debugging log message on these tokens. + // Note: message are supressed during the first load -- change some lines to see them. + [ + /@\s*[a-zA-Z_\$][\w\$]*/, + { token: "annotation", log: "annotation token: $0" }, + ], + + // numbers + [/\d*\.\d+([eE][\-+]?\d+)?/, "number.float"], + [/0[xX][0-9a-fA-F]+/, "number.hex"], + [/\d+/, "number"], + + // delimiter: after number because of .\d floats + [/[;,.]/, "delimiter"], + + // strings + [/"([^"\\]|\\.)*$/, "string.invalid"], // non-teminated string + [/"/, { token: "string.quote", bracket: "@open", next: "@string" }], + + // characters + [/'[^\\']'/, "string"], + [/(')(@escapes)(')/, ["string", "string.escape", "string"]], + [/'/, "string.invalid"], + ], + + comment: [ + [/[^\/*]+/, "comment"], + [/\/\*/, "comment", "@push"], // nested comment + ["\\*/", "comment", "@pop"], + [/[\/*]/, "comment"], + ], + + string: [ + [/[^\\"]+/, "string"], + [/@escapes/, "string.escape"], + [/\\./, "string.escape.invalid"], + [/"/, { token: "string.quote", bracket: "@close", next: "@pop" }], + ], + + whitespace: [ + [/[ \t\r\n]+/, "white"], + [/\/\*/, "comment", "@comment"], + [/\/\/.*$/, "comment"], + ], + + func_decl: [[/[a-z_$][\w$]*/, "support.function", "@pop"]], + }, +}; diff --git a/libs/remix-ui/home-tab/src/lib/components/homeTabGetStarted.tsx b/libs/remix-ui/home-tab/src/lib/components/homeTabGetStarted.tsx index bea7384767..c0cbd83ad1 100644 --- a/libs/remix-ui/home-tab/src/lib/components/homeTabGetStarted.tsx +++ b/libs/remix-ui/home-tab/src/lib/components/homeTabGetStarted.tsx @@ -60,7 +60,7 @@ function HomeTabGetStarted ({plugin}: HomeTabGetStartedProps) { await plugin.call('filePanel', 'createWorkspace', templateName + "_" + timeStamp, templateName) await plugin.call('filePanel', 'setWorkspace', templateName + "_" + timeStamp) plugin.verticalIcons.select('filePanel') - _paq.push(['trackEvent', 'homeGetStarted', templateName]) + _paq.push(['trackEvent', 'homeTab', 'homeGetStarted', templateName]) } return ( diff --git a/libs/remix-ui/panel/src/lib/plugins/panel.css b/libs/remix-ui/panel/src/lib/plugins/panel.css index b9988e19af..076cb4aafc 100644 --- a/libs/remix-ui/panel/src/lib/plugins/panel.css +++ b/libs/remix-ui/panel/src/lib/plugins/panel.css @@ -53,6 +53,7 @@ iframe { height: 100%; width: 100%; border: 0; + display: block; } .plugins { diff --git a/libs/remix-ui/run-tab/src/lib/actions/deploy.ts b/libs/remix-ui/run-tab/src/lib/actions/deploy.ts index 31ddc10d0f..9c2475ff47 100644 --- a/libs/remix-ui/run-tab/src/lib/actions/deploy.ts +++ b/libs/remix-ui/run-tab/src/lib/actions/deploy.ts @@ -6,6 +6,7 @@ import { DeployMode, MainnetPrompt } from "../types" import { displayNotification, displayPopUp, setDecodedResponse } from "./payload" import { addInstance } from "./actions" import { addressToString, logBuilder } from "@remix-ui/helper" +import Web3 from "web3" declare global { interface Window { @@ -26,11 +27,11 @@ const loadContractFromAddress = (plugin: RunTab, address, confirmCb, cb) => { } catch (e) { return cb('Failed to parse the current file as JSON ABI.') } - _paq.push(['trackEvent', 'udapp', 'AtAddressLoadWithABI']) + _paq.push(['trackEvent', 'udapp', 'useAtAddress' , 'AtAddressLoadWithABI']) cb(null, 'abi', abi) }) } else { - _paq.push(['trackEvent', 'udapp', 'AtAddressLoadWithArtifacts']) + _paq.push(['trackEvent', 'udapp', 'useAtAddress', 'AtAddressLoadWithArtifacts']) cb(null, 'instance') } } @@ -322,4 +323,16 @@ export const updateInstanceBalance = (plugin: RunTab) => { }) } } +} + +export const isValidContractAddress = async (plugin: RunTab, address: string) => { + if (!address) { + return false + } else { + if (Web3.utils.isAddress(address)) { + return await plugin.blockchain.web3().eth.getCode(address) !== '0x' + } else { + return false + } + } } \ No newline at end of file diff --git a/libs/remix-ui/run-tab/src/lib/actions/index.ts b/libs/remix-ui/run-tab/src/lib/actions/index.ts index 88ab644592..9a082d0146 100644 --- a/libs/remix-ui/run-tab/src/lib/actions/index.ts +++ b/libs/remix-ui/run-tab/src/lib/actions/index.ts @@ -6,7 +6,7 @@ import { createNewBlockchainAccount, fillAccountsList, setExecutionContext, sign import { clearInstances, clearPopUp, removeInstance, setAccount, setGasFee, setMatchPassphrasePrompt, setNetworkNameFromProvider, setPassphrasePrompt, setSelectedContract, setSendTransactionValue, setUnit, updateBaseFeePerGas, updateConfirmSettings, updateGasPrice, updateGasPriceStatus, updateMaxFee, updateMaxPriorityFee, updateScenarioPath } from './actions' -import { createInstance, getContext, getFuncABIInputs, getSelectedContract, loadAddress, runTransactions, updateInstanceBalance, syncContractsInternal } from './deploy' +import { createInstance, getContext, getFuncABIInputs, getSelectedContract, loadAddress, runTransactions, updateInstanceBalance, syncContractsInternal, isValidContractAddress } from './deploy' import { CompilerAbstract as CompilerAbstractType } from '@remix-project/remix-solidity-ts' import { ContractData, FuncABI } from "@remix-project/core-plugin" import { DeployMode, MainnetPrompt } from '../types' @@ -61,4 +61,5 @@ export const setScenarioPath = (path: string) => updateScenarioPath(dispatch, pa export const getFuncABIValues = (funcABI: FuncABI) => getFuncABIInputs(plugin, funcABI) export const setNetworkName = (networkName: string) => setNetworkNameFromProvider(dispatch, networkName) export const updateSelectedContract = (contractName) => setSelectedContract(dispatch, contractName) -export const syncContracts = () => syncContractsInternal(plugin) \ No newline at end of file +export const syncContracts = () => syncContractsInternal(plugin) +export const isValidProxyAddress = (address: string) => isValidContractAddress(plugin, address) \ No newline at end of file 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 b02b9fb504..b76748349e 100644 --- a/libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx +++ b/libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx @@ -255,7 +255,7 @@ export function ContractDropdownUI(props: ContractDropdownProps) {
-
{compilerName && compilerName !== '' && }
+
{compilerName && compilerName !== '' && }
{props.remixdActivated ? ( @@ -307,6 +307,7 @@ export function ContractDropdownUI(props: ContractDropdownProps) { evmBC={loadedContractData.bytecodeObject} lookupOnly={false} savedProxyAddress={proxyKey} + isValidProxyAddress={props.isValidProxyAddress} />
({ deploy: false, upgrade: false }) const [useLastProxy, setUseLastProxy] = useState(false) const [proxyAddress, setProxyAddress] = useState('') + const [proxyAddressError, setProxyAddressError] = useState('') const multiFields = useRef>([]) const initializeFields = useRef>([]) const basicInputRef = useRef() @@ -174,7 +176,7 @@ export function ContractGUI (props: ContractGUIProps) { props.clickCallBack(props.initializerOptions.inputs.inputs, proxyInitializeString, ['Deploy with Proxy']) } else if (deployState.upgrade) { - props.clickCallBack(props.funcABI.inputs, proxyAddress, ['Upgrade with Proxy']) + !proxyAddressError && props.clickCallBack(props.funcABI.inputs, proxyAddress, ['Upgrade with Proxy']) } else { props.clickCallBack(props.funcABI.inputs, basicInput) } @@ -223,16 +225,36 @@ export function ContractGUI (props: ContractGUIProps) { const value = e.target.checked const address = props.savedProxyAddress + if (value) { + if (address) { + setProxyAddress(address) + setProxyAddressError('') + } else { + setProxyAddressError('No proxy address available') + setProxyAddress('') + } + } setUseLastProxy(value) - setProxyAddress(address || '') } const handleSetProxyAddress = (e) => { const value = e.target.value - + setProxyAddress(value) } + const validateProxyAddress = async (address: string) => { + if (address === '') { + setProxyAddressError('proxy address cannot be empty') + } else { + if (await props.isValidProxyAddress(address)) { + setProxyAddressError('') + } else { + setProxyAddressError('not a valid contract address') + } + } + } + return (
{props.deployOption && (props.deployOption || []).length > 0 ? ( <> -
+
handleDeployProxySelect(e.target.checked)} checked={deployState.deploy} /> - An ERC1967 proxy contract
will be deployed along
with the selected
implementation contract.} + placement={"bottom-start"} + tooltipClasses="text-wrap" tooltipId="deployWithProxyTooltip" - > + > */} -
+ {/* */}
{props.initializerOptions && @@ -462,12 +485,12 @@ export function ContractGUI (props: ContractGUIProps) { {" "} {inp.name}:{" "} - + placement="top-start" + > */} { initializeFields.current[index] = el; @@ -476,7 +499,7 @@ export function ContractGUI (props: ContractGUIProps) { className="form-control udapp_input" placeholder={inp.type} /> - + {/* */}
); })} @@ -493,12 +516,12 @@ export function ContractGUI (props: ContractGUIProps) { onChange={(e) => handleUpgradeImpSelect(e.target.checked)} checked={deployState.upgrade} /> - + placement="top-start" + > */} - + {/* */}
- {!useLastProxy ? ( + { + !useLastProxy ?
- ) : ( + : ( void, setSelectedContract: (contractName: string) => void - remixdActivated: boolean + remixdActivated: boolean, + isValidProxyAddress?: (address: string) => Promise } export interface RecorderProps { @@ -260,7 +261,8 @@ export interface ContractGUIProps { isDeploy?: boolean, deployOption?: { title: DeployMode, active: boolean }[], initializerOptions?: DeployOption, - savedProxyAddress?: string + savedProxyAddress?: string, + isValidProxyAddress?: (address: string) => Promise } export interface MainnetProps { network: Network, diff --git a/libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx b/libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx index bd10f7893f..7fcccfb4ad 100644 --- a/libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx +++ b/libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx @@ -472,10 +472,8 @@ export const CompilerContainer = (props: CompilerContainerProps) => { compileIcon.current.classList.remove('remixui_spinningIcon') compileIcon.current.classList.remove('remixui_bouncingIcon') if (!state.autoCompile || (state.autoCompile && state.matomoAutocompileOnce)) { - if (state.useFileConfiguration) - _paq.push(['trackEvent', 'compiler', 'compiled_with_config_file']) - - _paq.push(['trackEvent', 'compiler', 'compiled_with_version', _retrieveVersion()]) + _paq.push(['trackEvent', 'compiler', 'compiled', 'with_config_file_' + state.useFileConfiguration]) + _paq.push(['trackEvent', 'compiler', 'compiled', 'with_version_' + _retrieveVersion()]) if (state.autoCompile && state.matomoAutocompileOnce) { setState(prevState => { return { ...prevState, matomoAutocompileOnce: false } @@ -887,7 +885,7 @@ export const CompilerContainer = (props: CompilerContainerProps) => { } }} /> - {!showFilePathInput && } + {!showFilePathInput && }
diff --git a/libs/remix-ui/solidity-compiler/src/lib/logic/compileTabLogic.ts b/libs/remix-ui/solidity-compiler/src/lib/logic/compileTabLogic.ts index 10e08f86ed..74cc1ebffb 100644 --- a/libs/remix-ui/solidity-compiler/src/lib/logic/compileTabLogic.ts +++ b/libs/remix-ui/solidity-compiler/src/lib/logic/compileTabLogic.ts @@ -155,9 +155,9 @@ export class CompileTabLogic { ` const configFilePath = 'remix-compiler.config.js' this.api.writeFile(configFilePath, fileContent) - _paq.push(['trackEvent', 'compiler', 'compileWithHardhat']) + _paq.push(['trackEvent', 'compiler', 'runCompile', 'compileWithHardhat']) this.api.compileWithHardhat(configFilePath).then((result) => { - this.api.logToTerminal({ type: 'info', value: result }) + this.api.logToTerminal({ type: 'log', value: result }) }).catch((error) => { this.api.logToTerminal({ type: 'error', value: error }) }) @@ -181,9 +181,9 @@ export class CompileTabLogic { }` const configFilePath = 'remix-compiler.config.js' this.api.writeFile(configFilePath, fileContent) - _paq.push(['trackEvent', 'compiler', 'compileWithTruffle']) + _paq.push(['trackEvent', 'compiler', 'runCompile', 'compileWithTruffle']) this.api.compileWithTruffle(configFilePath).then((result) => { - this.api.logToTerminal({ type: 'info', value: result }) + this.api.logToTerminal({ type: 'log', value: result }) }).catch((error) => { this.api.logToTerminal({ type: 'error', value: error }) }) diff --git a/libs/remix-ui/solidity-unit-testing/src/lib/solidity-unit-testing.tsx b/libs/remix-ui/solidity-unit-testing/src/lib/solidity-unit-testing.tsx index 1f1533b7a4..8dbf12b281 100644 --- a/libs/remix-ui/solidity-unit-testing/src/lib/solidity-unit-testing.tsx +++ b/libs/remix-ui/solidity-unit-testing/src/lib/solidity-unit-testing.tsx @@ -259,7 +259,7 @@ export const SolidityUnitTesting = (props: Record) => { // eslint-d finalLogs = finalLogs + ' ' + formattedLog + '\n' } _paq.push(['trackEvent', 'solidityUnitTesting', 'hardhat', 'console.log']) - testTab.call('terminal', 'log', { type: 'info', value: finalLogs }) + testTab.call('terminal', 'log', { type: 'log', value: finalLogs }) } const discardHighlight = async () => { @@ -586,7 +586,7 @@ export const SolidityUnitTesting = (props: Record) => { // eslint-d const tests: string[] = selectedTests.current if (!tests || !tests.length) return else setProgressBarHidden(false) - _paq.push(['trackEvent', 'solidityUnitTesting', 'runTests']) + _paq.push(['trackEvent', 'solidityUnitTesting', 'runTests', 'nbTestsRunning' + tests.length]) eachOfSeries(tests, (value: string, key: string, callback: any) => { // eslint-disable-line @typescript-eslint/no-explicit-any if (hasBeenStopped.current) return runTest(value, callback) diff --git a/libs/remix-ui/static-analyser/src/lib/remix-ui-static-analyser.tsx b/libs/remix-ui/static-analyser/src/lib/remix-ui-static-analyser.tsx index 6526980784..4e780b6752 100644 --- a/libs/remix-ui/static-analyser/src/lib/remix-ui-static-analyser.tsx +++ b/libs/remix-ui/static-analyser/src/lib/remix-ui-static-analyser.tsx @@ -215,7 +215,7 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => { const warningErrors = [] // Remix Analysis - _paq.push(['trackEvent', 'solidityStaticAnalyzer', 'analyzeWithRemixAnalyzer']) + _paq.push(['trackEvent', 'solidityStaticAnalyzer', 'analyze', 'remixAnalyzer']) const results = runner.run(lastCompilationResult, categoryIndex) for (const result of results) { let moduleName @@ -280,11 +280,11 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => { try { const compilerState = await props.analysisModule.call('solidity', 'getCompilerState') const { currentVersion, optimize, evmVersion } = compilerState - await props.analysisModule.call('terminal', 'log', { type: 'info', value: '[Slither Analysis]: Running...' }) - _paq.push(['trackEvent', 'solidityStaticAnalyzer', 'analyzeWithSlither']) + await props.analysisModule.call('terminal', 'log', { type: 'log', value: '[Slither Analysis]: Running...' }) + _paq.push(['trackEvent', 'solidityStaticAnalyzer', 'analyze', 'slitherAnalyzer']) const result = await props.analysisModule.call('slither', 'analyse', state.file, { currentVersion, optimize, evmVersion }) if (result.status) { - props.analysisModule.call('terminal', 'log', { type: 'info', value: `[Slither Analysis]: Analysis Completed!! ${result.count} warnings found.` }) + props.analysisModule.call('terminal', 'log', { type: 'log', value: `[Slither Analysis]: Analysis Completed!! ${result.count} warnings found.` }) const report = result.data for (const item of report) { let location: any = {} diff --git a/libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx b/libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx index 07f66efff3..88228e6cb4 100644 --- a/libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx +++ b/libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx @@ -1,8 +1,8 @@ import { fileDecoration, FileDecorationIcons } from '@remix-ui/file-decorators' +import { CustomTooltip } from '@remix-ui/helper' import { Plugin } from '@remixproject/engine' import React, { useState, useRef, useEffect, useReducer } from 'react' // eslint-disable-line -import { OverlayTrigger, Tooltip } from 'react-bootstrap' // eslint-disable-line import { Tab, Tabs, TabList, TabPanel } from 'react-tabs' import './remix-ui-tabs.css' const _paq = window._paq = window._paq || [] @@ -160,16 +160,16 @@ export const TabsUI = (props: TabsUIProps) => { } }} > - - + {(tabsState.currentExt === 'js' || tabsState.currentExt === 'ts') ? "Run script (CTRL + SHIFT + S)" : tabsState.currentExt === 'sol' || tabsState.currentExt === 'yul'? "Compile CTRL + S" : "Select .sol or .yul file to compile or a .ts or .js file and run it"} - - - }> + } + > - + props.onZoomOut()}> props.onZoomIn()}> diff --git a/libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx b/libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx index 11a3cbd57d..aa4c8c90b5 100644 --- a/libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx +++ b/libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx @@ -87,7 +87,13 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => { }, log: (message) => { - scriptRunnerDispatch({ type: 'log', payload: { message: [message] } }) + if (typeof message === 'string') { + message = { + value: message, + type: 'log' + } + } + scriptRunnerDispatch({ type: message.type ? message.type : 'log', payload: { message: [message.value] } }) } }) }, []) diff --git a/libs/remix-ui/toaster/src/lib/toaster.tsx b/libs/remix-ui/toaster/src/lib/toaster.tsx index 571a0c4afa..97255cb4d2 100644 --- a/libs/remix-ui/toaster/src/lib/toaster.tsx +++ b/libs/remix-ui/toaster/src/lib/toaster.tsx @@ -7,7 +7,8 @@ import './toaster.css' export interface ToasterProps { message: string | JSX.Element timeOut?: number, - handleHide?: () => void + handleHide?: () => void, + timestamp?: number } export const Toaster = (props: ToasterProps) => { @@ -49,7 +50,7 @@ export const Toaster = (props: ToasterProps) => { } }) } - }, [props.message]) + }, [props.message, props.timestamp]) useEffect(() => { if (state.hiding) { diff --git a/libs/remix-ui/workspace/src/lib/components/file-explorer-context-menu.tsx b/libs/remix-ui/workspace/src/lib/components/file-explorer-context-menu.tsx index bf864fda69..ec2bb31b03 100644 --- a/libs/remix-ui/workspace/src/lib/components/file-explorer-context-menu.tsx +++ b/libs/remix-ui/workspace/src/lib/components/file-explorer-context-menu.tsx @@ -87,19 +87,19 @@ export const FileExplorerContextMenu = (props: FileExplorerContextMenuProps) => _paq.push(['trackEvent', 'fileExplorer', 'contextMenu', 'delete']) break case 'Push changes to gist': - _paq.push(['trackEvent', 'fileExplorer', 'pushToChangesoGist']) + _paq.push(['trackEvent', 'fileExplorer', 'contextMenu', 'pushToChangesoGist']) pushChangesToGist(path, type) break case 'Publish folder to gist': - _paq.push(['trackEvent', 'fileExplorer', 'publishFolderToGist']) + _paq.push(['trackEvent', 'fileExplorer', 'contextMenu', 'publishFolderToGist']) publishFolderToGist(path, type) break case 'Publish file to gist': - _paq.push(['trackEvent', 'fileExplorer', 'publishFileToGist']) + _paq.push(['trackEvent', 'fileExplorer', 'contextMenu', 'publishFileToGist']) publishFileToGist(path, type) break case 'Run': - _paq.push(['trackEvent', 'fileExplorer', 'runScript']) + _paq.push(['trackEvent', 'fileExplorer', 'contextMenu', 'runScript']) runScript(path) break case 'Copy': diff --git a/libs/remix-ui/workspace/src/lib/components/file-explorer-menu.tsx b/libs/remix-ui/workspace/src/lib/components/file-explorer-menu.tsx index 45b293381b..49fc4393ce 100644 --- a/libs/remix-ui/workspace/src/lib/components/file-explorer-menu.tsx +++ b/libs/remix-ui/workspace/src/lib/components/file-explorer-menu.tsx @@ -1,5 +1,5 @@ +import { CustomTooltip } from '@remix-ui/helper' import React, { useState, useEffect } from 'react' //eslint-disable-line -import { OverlayTrigger, Tooltip } from 'react-bootstrap' import { Placement } from 'react-bootstrap/esm/Overlay' import { FileExplorerMenuProps } from '../types' const _paq = window._paq = window._paq || [] @@ -53,27 +53,23 @@ export const FileExplorerMenu = (props: FileExplorerMenuProps) => { return ( <> - - {props.title} - - } + tooltipId="remixuilabelTooltip" + tooltipClasses="text-nowrap" + tooltipText={props.title} > { props.title } - + { state.menuItems.map(({ action, title, icon, placement }, index) => { if (action === 'uploadFile') { return ( - - {title} - - } + tooltipId="uploadFileTooltip" + tooltipClasses="text-nowrap" + tooltipText={title} > - + ) } else { return ( - - {title} - - } + tooltipId={`${action}-${title}-${icon}-${index}`} + tooltipClasses="text-nowrap" + tooltipText={title} > { key={`${action}-${title}-${index}`} > - + ) } })} diff --git a/libs/remix-ui/workspace/src/lib/components/file-label.tsx b/libs/remix-ui/workspace/src/lib/components/file-label.tsx index 94d53f8a8c..4490273e12 100644 --- a/libs/remix-ui/workspace/src/lib/components/file-label.tsx +++ b/libs/remix-ui/workspace/src/lib/components/file-label.tsx @@ -70,13 +70,12 @@ export const FileLabel = (props: FileLabelProps) => { onKeyDown={handleEditInput} onBlur={handleEditBlur} > - - {file.name} - + + {file.name} +
) } diff --git a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx index 473460382e..dd0716d6d6 100644 --- a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx +++ b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx @@ -1,6 +1,6 @@ import React, { useState, useEffect, useRef, useContext, SyntheticEvent, ChangeEvent, KeyboardEvent } from 'react' // eslint-disable-line -import { Dropdown, OverlayTrigger, Tooltip } from 'react-bootstrap' -import { CustomIconsToggle, CustomMenu, CustomToggle } from '@remix-ui/helper' +import { Dropdown } from 'react-bootstrap' +import { CustomIconsToggle, CustomMenu, CustomToggle, CustomTooltip } from '@remix-ui/helper' import { FileExplorer } from './components/file-explorer' // eslint-disable-line import { FileSystemContext } from './contexts' import './css/remix-ui-workspace.css' @@ -221,8 +221,10 @@ export function Workspace () { try { if (branch.remote) { await global.dispatchCheckoutRemoteBranch(branch.name, branch.remote) + _paq.push(['trackEvent', 'Workspace', 'GIT', 'checkout_remote_branch']) } else { await global.dispatchSwitchToBranch(branch.name) + _paq.push(['trackEvent', 'Workspace', 'GIT', 'switch_to_exisiting_branch']) } } catch (e) { console.error(e) @@ -233,6 +235,7 @@ export function Workspace () { const switchToNewBranch = async () => { try { await global.dispatchCreateNewBranch(branchFilter) + _paq.push(['trackEvent', 'Workspace', 'GIT', 'switch_to_new_branch']) } catch (e) { global.modal('Checkout Git Branch', e.message, 'OK', () => {}) } @@ -333,18 +336,15 @@ export function Workspace () { } const workspaceMenuIcons = [ - - Create - - } + tooltipId="createWorkspaceTooltip" + tooltipClasses="text-nowrap" + tooltipText="Create" >
{ - e.stopPropagation() + onClick={() => { createWorkspace() _paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'workspaceCreate']) hideIconsMenu(!showIconsMenu) @@ -354,8 +354,7 @@ export function Workspace () { hidden={currentWorkspace === LOCALHOST} id='workspaceCreate' data-id='workspaceCreate' - onClick={(e) => { - e.stopPropagation() + onClick={() => { createWorkspace() _paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'workspaceCreate']) hideIconsMenu(!showIconsMenu) @@ -365,19 +364,16 @@ export function Workspace () { Create
-
, - , + - Delete Workspace - - } + tooltipId="createWorkspaceTooltip" + tooltipClasses="text-nowrap" + tooltipText="Delete Workspace" >
{ - e.stopPropagation() + onClick={() => { deleteCurrentWorkspace() _paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'workspaceDelete']) hideIconsMenu(!showIconsMenu) @@ -387,8 +383,7 @@ export function Workspace () { hidden={currentWorkspace === LOCALHOST || currentWorkspace === NO_WORKSPACE} id='workspaceDelete' data-id='workspaceDelete' - onClick={(e) => { - e.stopPropagation() + onClick={() => { deleteCurrentWorkspace() _paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'workspaceDelete']) hideIconsMenu(!showIconsMenu) @@ -398,17 +393,14 @@ export function Workspace () { {'Delete'}
-
, - , + - Rename Workspace - - } + tooltipClasses="text-nowrap" + tooltipId="workspaceRenametooltip" + tooltipText="Rename Workspace" > -
{ - e.stopPropagation() +
{ renameCurrentWorkspace() _paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'workspaceRename']) hideIconsMenu(!showIconsMenu) @@ -419,8 +411,7 @@ export function Workspace () { hidden={currentWorkspace === LOCALHOST || currentWorkspace === NO_WORKSPACE} id='workspaceRename' data-id='workspaceRename' - onClick={(e) => { - e.stopPropagation() + onClick={() => { renameCurrentWorkspace() _paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'workspaceRename']) hideIconsMenu(!showIconsMenu) @@ -429,20 +420,17 @@ export function Workspace () { {'Rename'}
- , + , , - - Clone Git Repository - - } + tooltipId="cloneWorkspaceTooltip" + tooltipClasses="text-nowrap" + tooltipText="Clone Git Repository" >
{ - e.stopPropagation() + onClick={() => { cloneGitRepository() _paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'cloneGitRepository']) hideIconsMenu(!showIconsMenu) @@ -452,8 +440,7 @@ export function Workspace () { hidden={currentWorkspace === LOCALHOST} id='cloneGitRepository' data-id='cloneGitRepository' - onClick={(e) => { - e.stopPropagation() + onClick={() => { cloneGitRepository() _paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'cloneGitRepository']) hideIconsMenu(!showIconsMenu) @@ -463,20 +450,17 @@ export function Workspace () { {'Clone'}
-
, + , , - - Download Workspace - - } + tooltipId="createWorkspaceTooltip" + tooltipClasses="text-nowrap" + tooltipText="Download Workspace" >
{ - e.stopPropagation() + onClick={() => { downloadWorkspaces() _paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'workspacesDownload']) hideIconsMenu(!showIconsMenu) @@ -486,8 +470,7 @@ export function Workspace () { hidden={currentWorkspace === LOCALHOST || currentWorkspace === NO_WORKSPACE} id='workspacesDownload' data-id='workspacesDownload' - onClick={(e) => { - e.stopPropagation() + onClick={() => { downloadWorkspaces() _paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'workspacesDownload']) hideIconsMenu(!showIconsMenu) @@ -497,19 +480,16 @@ export function Workspace () { {'Download'}
-
, - , + - Restore Workspace Backup - - } + tooltipId="createWorkspaceTooltip" + tooltipClasses="text-nowrap" + tooltipText="Restore Workspace Backup" >
{ - e.stopPropagation() + onClick={() => { restoreBackup() _paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'workspacesRestore']) hideIconsMenu(!showIconsMenu) @@ -519,8 +499,7 @@ export function Workspace () { hidden={currentWorkspace === LOCALHOST} id='workspacesRestore' data-id='workspacesRestore' - onClick={(e) => { - e.stopPropagation() + onClick={() => { restoreBackup() _paq.push(['trackEvent', 'fileExplorer', 'workspaceMenu', 'workspacesRestore']) hideIconsMenu(!showIconsMenu) @@ -530,7 +509,7 @@ export function Workspace () { {'Restore'}
-
, + , ] return ( @@ -546,14 +525,12 @@ export function Workspace () { WORKSPACES - - + - Create - - } + tooltipId="createWorkspaceTooltip" + tooltipClasses="text-nowrap" + tooltipText="Create" > - + hideIconsMenu(!showIconsMenu)} show={showIconsMenu}> - + ) : null}
@@ -626,8 +603,7 @@ export function Workspace () { } )) - } - { switchWorkspace(LOCALHOST) }}>{currentWorkspace === LOCALHOST ? ✓ localhost : { LOCALHOST } } + } { ((global.fs.browser.workspaces.length <= 0) || currentWorkspace === NO_WORKSPACE) && { switchWorkspace(NO_WORKSPACE) }}>{ NO_WORKSPACE } } @@ -721,52 +697,57 @@ export function Workspace () {
{ selectedWorkspace && -
+
GIT
-
+
{ global.fs.browser.isRequestingCloning ? : currentBranch || '-none-' } - -
- Switch branches -
{ toggleBranches(false) }}> + +
+
+ Switch branches +
{ toggleBranches(false) }}> +
+
+
+ +
+
+ { + filteredBranches.length > 0 ? filteredBranches.map((branch, index) => { + return ( + { switchToBranch(branch) }} title={branch.remote ? 'Checkout new branch from remote branch' : 'Checkout to local branch'}> +
+ { + (currentBranch === branch.name) && !branch.remote ? + { branch.name } : + { branch.remote ? `${branch.remote}/${branch.name}` : branch.name } + } +
+
+ ) + }) : + +
+ Create branch: { branchFilter } from '{currentBranch}' +
+
+ }
-
-
- -
-
{ - filteredBranches.length > 0 ? filteredBranches.map((branch, index) => { - return ( - { switchToBranch(branch) }} title={branch.remote ? 'Checkout new branch from remote branch' : 'Checkout to local branch'}> - { - (currentBranch === branch.name) && !branch.remote ? - { branch.name } : - { branch.remote ? `${branch.remote}/${branch.name}` : branch.name } - } - - ) - }) : - -
- Create branch: { branchFilter } from '{currentBranch}' -
-
+ (selectedWorkspace.branches || []).length > 4 && }
- { - (selectedWorkspace.branches || []).length > 4 && - }
diff --git a/libs/remixd/src/services/foundryClient.ts b/libs/remixd/src/services/foundryClient.ts index 96a90c5080..aa4e42f4c1 100644 --- a/libs/remixd/src/services/foundryClient.ts +++ b/libs/remixd/src/services/foundryClient.ts @@ -83,7 +83,7 @@ export class FoundryClient extends PluginClient { } if (!this.warnlog) { // @ts-ignore - this.call('terminal', 'log', 'receiving compilation result from Foundry') + this.call('terminal', 'log', { type: 'log', value: 'receiving compilation result from Foundry' }) this.warnlog = true } } @@ -167,6 +167,6 @@ export class FoundryClient extends PluginClient { console.log('syncing from Foundry') this.processArtifact() // @ts-ignore - this.call('terminal', 'log', 'synced with Foundry') + this.call('terminal', 'log', { type: 'log', value: 'synced with Foundry'}) } } diff --git a/libs/remixd/src/services/hardhatClient.ts b/libs/remixd/src/services/hardhatClient.ts index c19479766e..abef18642f 100644 --- a/libs/remixd/src/services/hardhatClient.ts +++ b/libs/remixd/src/services/hardhatClient.ts @@ -105,14 +105,12 @@ export class HardhatClient extends PluginClient { } } if (!this.warnLog) { - // @ts-ignore - this.call('terminal', 'log', 'receiving compilation result from Hardhat') + this.call('terminal', 'log', { value: 'receiving compilation result from Hardhat', type: 'log'} ) this.warnLog = true } if (targetsSynced.length) { console.log(`Processing artifacts for files: ${[...new Set(targetsSynced)].join(', ')}`) - // @ts-ignore - this.call('terminal', 'log', `synced with Hardhat: ${[...new Set(targetsSynced)].join(', ')}`) + this.call('terminal', 'log', { type: 'log', value: `synced with Hardhat: ${[...new Set(targetsSynced)].join(', ')}` }) } } diff --git a/libs/remixd/src/services/truffleClient.ts b/libs/remixd/src/services/truffleClient.ts index 6dfd3b6f66..1fa745042e 100644 --- a/libs/remixd/src/services/truffleClient.ts +++ b/libs/remixd/src/services/truffleClient.ts @@ -81,7 +81,7 @@ export class TruffleClient extends PluginClient { } if (!this.warnLog) { // @ts-ignore - this.call('terminal', 'log', 'receiving compilation result from Truffle') + this.call('terminal', 'log', { type: 'log', value: 'receiving compilation result from Truffle' }) this.warnLog = true } } @@ -141,6 +141,6 @@ export class TruffleClient extends PluginClient { console.log('syncing from Truffle') this.processArtifact() // @ts-ignore - this.call('terminal', 'log', 'synced with Truffle') + this.call('terminal', 'log', { type: 'log', value: 'synced with Truffle' }) } } diff --git a/release-process.md b/release-process.md index d0f8e7f9da..013b600964 100644 --- a/release-process.md +++ b/release-process.md @@ -1,79 +1,102 @@ # Release process -This document includes: - - how to release the remixd - - how to publish remix libs to NPM - - how to update remix.ethereum.org - - how to update remix-alpha.ethereum.org - - how to update remix-beta.ethereum.org - - how to release remix IDE - -## RemixD release - - update new version number in remixd libs/remixd/package.json - - nx build remixd - - cd into ./dist/libs/remixd - - npm publish - - create bump PR to master. - -## Remix libs release -(remove dist and login to npm) - - git fetch origin master - - git checkout origin/master - - git checkout -b bumpLibsVersion - - yarn run publish:libs (this command uses lerna) - - commit - -## Remix IDE release Part 1. First push master to beta. Feature Freeze - - git co -b remix_beta origin/remix_beta - - git reset --hard -master-commit-hash- - - git push -f origin remix_beta - -## Testing phase -## In case of fixing bugs push PR's also to beta to include in Release +This document includes the release instructions for: + - Feature freeze phase + - Publishing `remixd` to NPM + - Publishing remix libraries to NPM + - Updating Remix's live version on remix.ethereum.org + - Updating Remix's alpha version on remix-alpha.ethereum.org + - Updating Remix's beta version on remix-beta.ethereum.org + +## Feature Freeze +Once feature freeze is done, `remix_beta` should be updated latest to the master which will automatically update `remix-beta.ethereum.org` through a CI job. + + - `git checkout remix_beta` + - `git pull origin remix_beta` + - `git reset --hard ` (`master-commit-hash` will be latest commid id from `master` branch) + - `git push -f origin remix_beta` -## Remix IDE release Part 2. Bump the version in beta and release +## Testing +Testing is performed after the Feature Freeze on `remix-beta.ethereum.org`. `build-qa-doc.js` script can be used to generate the list of QA tasks. Instructions to use the file are given in the file itself : https://github.com/ethereum/remix-project/blob/master/build-qa-doc.js#L18 . - - git fetch origin remix_beta - - git checkout origin/remix_beta - - git checkout -b bumpVersion - - update package.json version - - update version in yarn.lock - - merge PR to **origin/remix_beta** - - git fetch origin remix_beta - - git checkout origin/remix_beta - - git tag v(version-number) - - git push --tags - - github-changes -o ethereum -r remix-project -a --only-pulls --use-commit-body --branch remix_beta --only-merges --between-tags previous_version...next_version - - publish a release in github using the changelog +Once ready to run, it can be run using the Node.js: `node build-qa-doc.js` + +## remixd NPM release +Once testing is completed, release will start by publishing `remixd`.if required, `remixd` can be also released individually + + - Bump version for remixd in `./libs/remixd/package.json` + - Run: `nx build remixd` + - Move to build directory: `cd ./dist/libs/remixd` + - Publish to NPM: `npm publish` (Make sure you are logged in to NPM) + - create bump PR to master + +## Remix libraries NPM release + - Make sure you are on latest `master` branch + - `git pull origin master` + - `git checkout -b bumpLibsVersion` + - `yarn run publish:libs ` -## Remix IDE release Part 3. Bump dev branch (master) +This command uses `lerna` and is solely responsible for publishing all the remix libraries. It will ask for a new version of each library. Make sure you are logged in to NPM. - - git fetch origin master - - git checkout origin/master - - git checkout -b bumpDevVersion - - update package.json version: bump the version and add the tag `dev` if not already present. - - update version in yarn.lock - - create a PR and merge it to origin/master +Once this command has been run, the versions for each remix library will be updated to latest in the libs' package.json file. + - Create and merge bump PR to master -## Remix IDE release Part 4. remix.ethereum.org update +## Remix IDE Release +### Part 1. Bump the version and update Beta + +#### Make sure `remix_beta` is up-to-date with `master` branch: + + - `git checkout remix_beta` + - `git pull origin remix_beta` + - `git reset --hard ` + - `git push -f origin remix_beta` + +#### Create bump version PR: + + - Create a new branch from `remix_beta`: `git checkout -b bumpVersion` + - Update package.json version. Usually, you need to remove `-dev` from the version string + - Create a PR with base branch as `remix_beta` + - Merge PR to **origin/remix_beta** + +#### Create git tag from beta branch: -This is not strictly speaking a release. Updating the remix site is done through the Travis build: + - `git checkout remix_beta` + - `git pull origin remix_beta` + - Create tag: `git tag v`, `` should be same as in package.json of `remix_beta` branch + - Push tag: `git push --tags` + - Generate changelog using `build-changelog.js` script as described in the script itself + - Publish a release in GitHub using the generated changelog - - git co -b remix_live origin/remix_live - - git reset --hard -master-commit-hash- (or remix_beta-commit-hash-) - - git push -f origin remix_live +### Part 2. Update the Remix Live - CircleCI will build automaticaly and remix.ethereum.org will be updated +Updating the `remix_live` branch latest to the `remix_beta` runs the CircleCI build which updates liver version of Remix IDE on `remix.ethereum.org` -## Remix IDE release Part 5. Update Zip in release - - after remix_live is updated, drop the zip (from https://github.com/ethereum/remix-live/) to the release. + - `git checkout remix_live` + - `git pull origin remix_live` + - `git reset --hard ` or `` sometimes + - `git push -f origin remix_live` + + CircleCI will build automaticaly and remix.ethereum.org will be updated to the latest. + + ### Part 3. Upload zip file in GitHub release + - Once CI is successful for `remix_live` branch, Go to https://github.com/ethereum/remix-live + - Download the zip file with the name starting with `remix-` + - Upload the zip file in GitHub assets by editing the release for this version + +## Bump master branch + + - `git checkout master` + - `git pull origin master` + - Create a new branch from `master`: `git checkout -b bumpDevVersion` + - Bump the package.json version, add the tag `-dev` if not already present. + - Create and merge PR to `master` + -## Remix-ide beta release +## Remix IDE Beta Release - git fetch origin master - git checkout origin/master - git checkout -b bumpVersion - update package.json version to the new version "vx.x.x-beta.1" - - update version in yarn.lock - merge PR - git fetch origin master - git checkout origin/master @@ -83,6 +106,6 @@ This is not strictly speaking a release. Updating the remix site is done through - publish a beta release in github using the changelog - drop zip file to the beta release (from https://github.com/ethereum/remix-live-alpha) -## remix-alpha.ethereum.org update +## Remix IDE Alpha Release -remix-alpha.ethereum.org is automaticaly updated every time commits are pushed to master +remix-alpha.ethereum.org is automaticaly updated every time a commit is pushed to `master` branch