Merge pull request #5358 from ethereum/metamask2

Metamask2
pull/5355/head
bunsenstraat 3 weeks ago committed by GitHub
commit 23c8ae02dd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 34
      .circleci/config.yml
  2. 2
      apps/remix-ide-e2e/nightwatch-chrome.ts
  3. 35
      apps/remix-ide-e2e/src/commands/hideMetaMaskPopup.ts
  4. 36
      apps/remix-ide-e2e/src/commands/setupMetamask.ts
  5. 2
      apps/remix-ide-e2e/src/tests/matomo.test.ts
  6. 267
      apps/remix-ide-e2e/src/tests/metamask.test.ts
  7. 2
      apps/remix-ide/ci/browser_test.sh
  8. 33
      apps/remix-ide/ci/metamask.sh
  9. 2
      apps/remix-ide/src/app/udapp/run-tab.tsx
  10. 2
      apps/remixdesktop/test/nighwatch.app.ts
  11. 2
      libs/remix-ui/workspace/src/lib/actions/index.tsx

@ -640,7 +640,10 @@ jobs:
type: string
jobsize:
type: string
parallelism: 10
parallelism:
type: integer
default: 1
parallelism: << parameters.parallelism >>
steps:
- checkout
- attach_workspace:
@ -781,6 +784,7 @@ workflows:
script: ["flaky.sh"]
job: ["nogroup"]
jobsize: ["1"]
parallelism: [1]
build_all:
unless: << pipeline.parameters.run_flaky_tests >>
jobs:
@ -862,16 +866,33 @@ workflows:
requires:
- build
matrix:
alias: browser-tests
parameters:
browser: ["chrome", "firefox"]
script: ["browser_test.sh"]
job: ["0","1","2","3","4","5","6","7","8","9"]
jobsize: ["10"]
parallelism: [15]
- remix-ide-browser:
requires:
- build
matrix:
alias: metamask
parameters:
browser: ["chrome"]
script: ["metamask.sh"]
job: ["0"]
jobsize: ["10"]
parallelism: [1]
filters:
branches:
only: [/.*metamask.*/, 'master', 'remix_live', 'remix_beta']
- tests-passed:
requires:
- lint
- remix-libs
- remix-ide-browser
- browser-tests
- metamask
- plugins
- predeploy:
@ -884,7 +905,8 @@ workflows:
requires:
- lint
- remix-libs
- remix-ide-browser
- browser-tests
- metamask
- plugins
- predeploy
filters:
@ -896,7 +918,8 @@ workflows:
requires:
- lint
- remix-libs
- remix-ide-browser
- browser-tests
- metamask
- plugins
- predeploy
filters:
@ -908,7 +931,8 @@ workflows:
requires:
- lint
- remix-libs
- remix-ide-browser
- browser-tests
- metamask
- plugins
- predeploy
filters:

@ -21,7 +21,7 @@ module.exports = {
'default': {
globals: {
waitForConditionTimeout: 10000,
asyncHookTimeout: 10000000
asyncHookTimeout: 120000
},
screenshots: {
enabled: true,

@ -4,25 +4,22 @@ import EventEmitter from 'events'
class HideMetaMaskPopup extends EventEmitter {
command(this: NightwatchBrowser) {
browser
.pause(5000)
.isPresent({
selector: 'button[data-testid="popover-close"]',
locateStrategy: 'css selector',
suppressNotFoundErrors: true,
timeout: 2000
}, (okVisible) => {
console.log('okVisible', okVisible)
if (!okVisible.value) {
console.log('popover not found')
} else {
console.log('popover found... closing')
browser.click('button[data-testid="popover-close"]')
}
})
.waitForElementNotPresent({
selector: 'button[data-testid="popover-close"]',
locateStrategy: 'css selector',
timeout: 2000
.perform((done) => {
browser.execute(function () {
function addStyle(styleString) {
const style = document.createElement('style')
style.textContent = styleString
document.head.append(style)
}
addStyle(`
#popover-content {
display:none !important
}
.popover-container {
display:none !important;
}
`)
}, [], done())
})
.perform((done) => {
done()

@ -50,26 +50,24 @@ function setupMetaMask(browser: NightwatchBrowser, passphrase: string, password:
.click('button[data-testid="pin-extension-next"]')
.waitForElementVisible('button[data-testid="pin-extension-done"]')
.click('button[data-testid="pin-extension-done"]')
.pause(5000)
.isVisible({
selector: 'button[data-testid="popover-close"]',
locateStrategy: 'css selector',
suppressNotFoundErrors: true,
timeout: 3000
}, (okVisible) => {
console.log('okVisible', okVisible)
if (!okVisible.value) {
console.log('popover not found')
} else {
console.log('popover found... closing')
browser.click('button[data-testid="popover-close"]')
}
})
.waitForElementNotPresent({
selector: 'button[data-testid="popover-close"]',
locateStrategy: 'css selector',
timeout: 3000
.perform((done) => {
browser.execute(function () {
function addStyle(styleString) {
const style = document.createElement('style')
style.textContent = styleString
document.head.append(style)
}
addStyle(`
#popover-content {
display:none !important
}
.popover-container {
display:none !important;
}
`)
}, [], done())
})
.saveScreenshot('./reports/screenshots/metamask.png')
.click('[data-testid="network-display"]')
.click('.mm-modal-content label.toggle-button--off') // show test networks

@ -433,7 +433,7 @@ module.exports = {
.click('[data-id="matomoModal-modal-footer-cancel-react"]') // cancel
.waitForElementNotVisible('*[data-id="matomoModalModalDialogModalBody-react"]')
},
'verify Matomo events are tracked on app start #group4 #lfaky': function (browser: NightwatchBrowser) {
'verify Matomo events are tracked on app start #group4': function (browser: NightwatchBrowser) {
browser
.execute(function () {
return (window as any)._paq

@ -1,6 +1,7 @@
'use strict'
import { NightwatchBrowser } from 'nightwatch'
import init from '../helpers/init'
import examples from '../examples/example-contracts'
const passphrase = process.env.account_passphrase
const password = process.env.account_password
@ -25,6 +26,13 @@ const checkAlerts = function (browser: NightwatchBrowser) {
})
}
const localsCheck = {
to: {
value: '0x4B0897B0513FDC7C541B6D9D7E929C4E5364D2DB',
type: 'address'
}
}
const tests = {
'@disabled': true,
before: function (browser: NightwatchBrowser, done: VoidFunction) {
@ -36,7 +44,6 @@ const tests = {
},
'Should connect to Sepolia Test Network using MetaMask #group1': function (browser: NightwatchBrowser) {
if (!checkBrowserIsChrome(browser)) return
browser.waitForElementPresent('*[data-id="remixIdeSidePanel"]')
.setupMetamask(passphrase, password)
.useCss().switchBrowserTab(0)
@ -56,14 +63,11 @@ const tests = {
.pause(2000)
.waitForElementVisible('*[data-testid="page-container-footer-next"]', 60000)
.click('*[data-testid="page-container-footer-next"]')
// .waitForElementVisible('*[data-testid="popover-close"]')
// .click('*[data-testid="popover-close"]')
})
.switchBrowserTab(0) // back to remix
},
'Should add a contract file #group1': function (browser: NightwatchBrowser) {
if (!checkBrowserIsChrome(browser)) return
browser.waitForElementVisible('*[data-id="remixIdeSidePanel"]')
.clickLaunchIcon('filePanel')
.addFile('Greet.sol', sources[0]['Greet.sol'])
@ -72,7 +76,6 @@ const tests = {
},
'Should deploy contract on Sepolia Test Network using MetaMask #group1': function (browser: NightwatchBrowser) {
if (!checkBrowserIsChrome(browser)) return
browser.clearConsole().waitForElementPresent('*[data-id="runTabSelectAccount"] option', 45000)
.clickLaunchIcon('filePanel')
.openFile('Greet.sol')
@ -96,6 +99,58 @@ const tests = {
})
})
},
'Should run low level interaction (fallback function) on Sepolia Test Network using MetaMask #group1': function (browser: NightwatchBrowser) {
browser.clearConsole().waitForElementPresent('*[data-id="remixIdeSidePanel"]')
.clickInstance(0)
.clearConsole()
.waitForElementPresent('*[data-id="pluginManagerSettingsDeployAndRunLLTxSendTransaction"]')
.click('*[data-id="pluginManagerSettingsDeployAndRunLLTxSendTransaction"]')
.perform((done) => {
browser.switchBrowserWindow(extension_url, 'MetaMask', (browser) => {
browser
.maximizeWindow()
.hideMetaMaskPopup()
.pause(3000)
.scrollAndClick('[data-testid="page-container-footer-next"]')
.pause(2000)
.switchBrowserTab(0) // back to remix
.waitForElementVisible({
locateStrategy: 'xpath',
selector: "//span[@class='text-log' and contains(., 'transact to HelloWorld.(fallback) pending')]"
})
.waitForElementContainsText('*[data-id="terminalJournal"]', 'view on etherscan', 60000)
.waitForElementContainsText('*[data-id="terminalJournal"]', 'from: 0x76a...2708f', 60000)
.perform(() => done())
})
})
},
'Should run transaction (greet function) on Sepolia Test Network using MetaMask #group1': function (browser: NightwatchBrowser) {
browser.clearConsole().waitForElementPresent('*[data-id="remixIdeSidePanel"]')
.clearConsole()
.waitForElementPresent('*[data-title="string _message"]')
.setValue('*[data-title="string _message"]', 'test')
.waitForElementVisible('*[data-id="greet - transact (not payable)"]')
.click('*[data-id="greet - transact (not payable)"]')
.perform((done) => {
browser.switchBrowserWindow(extension_url, 'MetaMask', (browser) => {
browser
.maximizeWindow()
.hideMetaMaskPopup()
.pause(3000)
.scrollAndClick('[data-testid="page-container-footer-next"]')
.pause(2000)
.switchBrowserTab(0) // back to remix
.waitForElementVisible({
locateStrategy: 'xpath',
selector: "//span[@class='text-log' and contains(., 'transact to HelloWorld.greet pending')]"
})
.waitForElementContainsText('*[data-id="terminalJournal"]', 'view on etherscan', 60000)
.waitForElementContainsText('*[data-id="terminalJournal"]', 'from: 0x76a...2708f', 60000)
.perform(() => done())
})
})
},
'Should deploy faulty contract on Sepolia Test Network using MetaMask and show error in terminal #group1': function (browser: NightwatchBrowser) {
browser
.clearConsole()
@ -105,17 +160,14 @@ const tests = {
.waitForElementPresent('*[data-id="Deploy - transact (not payable)"]')
.click('*[data-id="Deploy - transact (not payable)"]')
.pause(5000)
.saveScreenshot('./reports/screenshots/metamask_7.png')
.waitForElementVisible('*[data-id="udappNotifyModalDialogModalBody-react"]', 60000)
.click('[data-id="udappNotify-modal-footer-cancel-react"]')
.saveScreenshot('./reports/screenshots/metamask_8.png')
.waitForElementVisible({
locateStrategy: 'xpath',
selector: "//span[@class='text-log' and contains(., 'errored')]"
})
},
'Should deploy contract on Sepolia Test Network using MetaMask again #group1': function (browser: NightwatchBrowser) {
if (!checkBrowserIsChrome(browser)) return
browser.clearConsole().waitForElementPresent('*[data-id="runTabSelectAccount"] option', 45000)
.clickLaunchIcon('filePanel')
.openFile('Greet.sol')
@ -138,21 +190,214 @@ const tests = {
.perform(() => done())
})
})
},
// main network tests
'Should connect to Ethereum Main Network using MetaMask #group1': function (browser: NightwatchBrowser) {
browser.waitForElementPresent('*[data-id="remixIdeSidePanel"]')
.switchBrowserTab(1)
.click('[data-testid="network-display"]')
.click('div[data-testid="Ethereum Mainnet"]') // switch to mainnet
.useCss().switchBrowserTab(0)
.refreshPage()
.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000)
.click('*[data-id="landingPageStartSolidity"]')
.clickLaunchIcon('udapp')
.switchEnvironment('injected-MetaMask')
.waitForElementPresent('*[data-id="settingsNetworkEnv"]')
.assert.containsText('*[data-id="settingsNetworkEnv"]', 'Main (1) network')
},
'Should deploy contract on Ethereum Main Network using MetaMask #group1': function (browser: NightwatchBrowser) {
browser.waitForElementPresent('*[data-id="runTabSelectAccount"] option')
.clickLaunchIcon('filePanel')
.addFile('Greet.sol', sources[0]['Greet.sol'])
.clickLaunchIcon('udapp')
.waitForElementPresent('*[data-id="Deploy - transact (not payable)"]')
.click('*[data-id="Deploy - transact (not payable)"]')
.waitForElementVisible('*[data-id="udappNotifyModalDialogModalBody-react"]', 65000)
.modalFooterOKClick('udappNotify')
.pause(10000)
.assert.containsText('*[data-id="udappNotifyModalDialogModalBody-react"]', 'You are about to create a transaction on Main Network. Confirm the details to send the info to your provider.')
.modalFooterCancelClick('udappNotify')
},
// debug transaction
'Should deploy Ballot to Sepolia using metamask #group1 #flaky': function (browser: NightwatchBrowser) {
browser.waitForElementPresent('*[data-id="remixIdeSidePanel"]')
.switchBrowserTab(1)
.click('[data-testid="network-display"]')
.click('div[data-testid="Sepolia"]') // switch to sepolia
.useCss().switchBrowserTab(0)
.addFile('BallotTest.sol', examples.ballot)
.clickLaunchIcon('udapp')
.clearConsole()
.clearTransactions()
.clickLaunchIcon('udapp')
.waitForElementVisible('input[placeholder="bytes32[] proposalNames"]')
.pause(2000)
.setValue('input[placeholder="bytes32[] proposalNames"]', '["0x48656c6c6f20576f726c64210000000000000000000000000000000000000000"]')
.pause(1000)
.click('*[data-id="Deploy - transact (not payable)"]') // deploy ballot
.pause(1000)
.waitForElementVisible({
locateStrategy: 'xpath',
selector: "//span[@class='text-log' and contains(., 'creation of')]"
})
.waitForElementVisible({
locateStrategy: 'xpath',
selector: "//span[@class='text-log' and contains(., 'pending')]"
})
.perform((done) => {
browser.switchBrowserWindow(extension_url, 'MetaMask', (browser) => {
browser
.maximizeWindow()
.hideMetaMaskPopup()
.pause(3000)
.waitForElementPresent('[data-testid="page-container-footer-next"]')
.scrollAndClick('[data-testid="page-container-footer-next"]')
.pause(2000)
.switchBrowserTab(0) // back to remix
.waitForElementContainsText('*[data-id="terminalJournal"]', 'view on etherscan', 60000)
.waitForElementContainsText('*[data-id="terminalJournal"]', 'from: 0x76a...2708f', 60000)
.perform(() => done())
})
})
},
'do transaction #group1': function (browser: NightwatchBrowser) {
browser.waitForElementPresent('*[data-id="universalDappUiContractActionWrapper"]', 60000)
.clearConsole()
.clickInstance(0)
.clickFunction('delegate - transact (not payable)', { types: 'address to', values: '"0x4b0897b0513fdc7c541b6d9d7e929c4e5364d2db"' })
.pause(5000)
.perform((done) => { // call delegate
browser.switchBrowserWindow(extension_url, 'MetaMask', (browser) => {
browser
.maximizeWindow()
.hideMetaMaskPopup()
.pause(5000)
.waitForElementPresent('[data-testid="page-container-footer-next"]')
.scrollAndClick('[data-testid="page-container-footer-next"]')
.pause(2000)
.switchBrowserTab(0) // back to remix
.waitForElementContainsText('*[data-id="terminalJournal"]', 'view on etherscan', 60000)
.waitForElementContainsText('*[data-id="terminalJournal"]', 'from: 0x76a...2708f', 60000)
.perform(() => done())
})
})
.testFunction('last',
{
status: '0x1 Transaction mined and execution succeed',
'decoded input': { 'address to': '0x4B0897b0513fdC7C541B6d9D7E929C4e5364D2dB' }
})
},
'Should debug Sepolia transaction with source highlighting MetaMask #group1': function (browser: NightwatchBrowser) {
let txhash
browser.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000)
.clickLaunchIcon('pluginManager') // load debugger and source verification
.clickLaunchIcon('udapp')
.perform((done) => {
browser.getLastTransactionHash((hash) => {
txhash = hash
done()
})
})
.pause(5000)
.perform((done) => {
browser
.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000)
.clickLaunchIcon('debugger')
.waitForElementVisible('*[data-id="debuggerTransactionInput"]')
.setValue('*[data-id="debuggerTransactionInput"]', txhash) // debug tx
.pause(2000)
.click('*[data-id="debuggerTransactionStartButton"]')
.waitForElementVisible('*[data-id="treeViewDivto"]', 30000)
.checkVariableDebug('soliditylocals', localsCheck)
.perform(() => done())
})
},
'Call web3.eth.getAccounts() using Injected Provider (Metamask) #group1': function (browser: NightwatchBrowser) {
if (!checkBrowserIsChrome(browser)) return
browser
.executeScriptInTerminal('web3.eth.getAccounts()')
.journalLastChildIncludes('["0x76a3ABb5a12dcd603B52Ed22195dED17ee82708f"]')
},
// EIP 712 tests
'Test EIP 712 Signature with Injected Provider (Metamask) #group1': function (browser: NightwatchBrowser) {
browser
.clickLaunchIcon('udapp')
.waitForElementPresent('i[id="remixRunSignMsg"]')
.click('i[id="remixRunSignMsg"]')
.waitForElementVisible('*[data-id="signMessageTextarea"]', 120000)
.click('*[data-id="sign-eip-712"]')
.waitForElementVisible('*[data-id="udappNotify-modal-footer-ok-react"]')
.modalFooterOKClick('udappNotify')
.pause(1000)
.getEditorValue((content) => {
browser.assert.ok(content.indexOf('"primaryType": "AuthRequest",') !== -1, 'EIP 712 data file must be opened')
})
.setEditorValue(JSON.stringify(EIP712_Example, null, '\t'))
.pause(5000)
.clickLaunchIcon('filePanel')
.rightClick('li[data-id="treeViewLitreeViewItemEIP-712-data.json"]')
.click('*[data-id="contextMenuItemsignTypedData"]')
.pause(1000)
.perform((done) => { // call delegate
browser.switchBrowserWindow(extension_url, 'MetaMask', (browser) => {
browser
.maximizeWindow()
.hideMetaMaskPopup()
.pause(1000)
.waitForElementPresent('[data-testid="page-container-footer-next"]')
.scrollAndClick('button[data-testid="page-container-footer-next"]') // confirm
.switchBrowserTab(0) // back to remix
.perform(() => done())
})
})
.pause(1000)
.journalChildIncludes('0xec72bbabeb47a3a766af449674a45a91a6e94e35ebf0ae3c644b66def7bd387f1c0b34d970c9f4a1e9398535e5860b35e82b2a8931b7c9046b7766a53e66db3d1b')
}
}
const branch = process.env.CIRCLE_BRANCH;
const isMasterBranch = (branch === 'master' || branch === 'remix_beta');
const branch = process.env.CIRCLE_BRANCH
const runTestsConditions = branch && (branch === 'master' || branch === 'remix_live' || branch.includes('remix_beta') || branch.includes('metamask'))
if (!checkBrowserIsChrome(browser)) {
module.exports = {}
} else {
module.exports = {
...(branch ? (isMasterBranch ? tests : {}) : tests),
...(branch ? (runTestsConditions ? tests : {}) : tests)
};
}
const EIP712_Example = {
domain: {
chainId: 11155111,
name: "Example App",
verifyingContract: "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC",
version: "1",
},
message: {
prompt: "Welcome! In order to authenticate to this website, sign this request and your public address will be sent to the server in a verifiable way.",
createdAt: 1718570375196,
},
primaryType: 'AuthRequest',
types: {
EIP712Domain: [
{ name: 'name', type: 'string' },
{ name: 'version', type: 'string' },
{ name: 'chainId', type: 'uint256' },
{ name: 'verifyingContract', type: 'address' },
],
AuthRequest: [
{ name: 'prompt', type: 'string' },
{ name: 'createdAt', type: 'uint256' },
],
},
}
const sources = [
{

@ -15,7 +15,7 @@ sleep 5
# grep -IRiL "@disabled" "dist/apps/remix-ide-e2e/src/tests" | grep "\.spec\|\.test" | xargs -I {} basename {} .test.js | grep -E "\b[${2}]"
# TESTFILES=$(grep -IRiL "@disabled" "dist/apps/remix-ide-e2e/src/tests" | grep "\.spec\|\.test" | xargs -I {} basename {} .test.js | grep -E "\b[$2]" | circleci tests split --split-by=timings )
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)
TESTFILES=$(node apps/remix-ide/ci/splice_tests.js $2 $3 | grep -v 'metamask' | circleci tests split --split-by=timings)
for TESTFILE in $TESTFILES; do
npx nightwatch --config dist/apps/remix-ide-e2e/nightwatch-${1}.js dist/apps/remix-ide-e2e/src/tests/${TESTFILE}.js --env=$1 || npx nightwatch --config dist/apps/remix-ide-e2e/nightwatch-${1}.js dist/apps/remix-ide-e2e/src/tests/${TESTFILE}.js --env=$1 || TEST_EXITCODE=1
done

@ -0,0 +1,33 @@
#!/usr/bin/env bash
set -e
TESTFILES=$(grep -IRiL "\'@disabled\': \?true" "dist/apps/remix-ide-e2e/src/tests" | grep "metamask" | sort )
# count test files
fileCount=$(grep -IRiL "\'@disabled\': \?true" "dist/apps/remix-ide-e2e/src/tests" | grep "metamask" | wc -l )
# if fileCount is 0
if [ $fileCount -eq 0 ]
then
echo "No metamask tests found"
exit 0
fi
BUILD_ID=${CIRCLE_BUILD_NUM:-${TRAVIS_JOB_NUMBER}}
echo "$BUILD_ID"
TEST_EXITCODE=0
npx ganache &
npx http-server -p 9090 --cors='*' ./node_modules &
yarn run serve:production &
sleep 5
for TESTFILE in $TESTFILES; do
npx nightwatch --config dist/apps/remix-ide-e2e/nightwatch-${1}.js $TESTFILE --env=$1 || TEST_EXITCODE=1
done
echo "$TEST_EXITCODE"
if [ "$TEST_EXITCODE" -eq 1 ]
then
exit 1
fi

@ -304,7 +304,7 @@ class Provider {
if (response.error) {
reject(response.error.message)
} else {
resolve(response)
resolve(response.result? response.result : response)
}
}).catch((err) => {
if (typeof err === 'string') {

@ -42,7 +42,7 @@ module.exports = {
selenium_host: 'localhost',
globals: {
waitForConditionTimeout: 10000,
asyncHookTimeout: 100000
asyncHookTimeout: 30000
},
screenshots: {
enabled: true,

@ -530,7 +530,7 @@ export const signTypedData = async (path: string) => {
plugin.call('terminal', 'log', { type: 'log', value: `${path} signature using ${settings.selectedAccount} : ${result}` })
} catch (e) {
console.error(e)
plugin.call('terminal', 'log', { type: 'error', value: `error while signing ${path}: ${e.message}` })
plugin.call('terminal', 'log', { type: 'error', value: `error while signing ${path}: ${e.message || e}` })
dispatch(displayPopUp(e.message))
}
}

Loading…
Cancel
Save