Merge branch 'master' of https://github.com/ethereum/remix-project into indexdbpackupdate

pull/5370/head
bunsenstraat 3 years ago
commit 6cf904457b
  1. 18
      .circleci/config.yml
  2. 2
      .gitignore
  3. 30
      apps/remix-ide-e2e/src/commands/acceptAndRemember.ts
  4. 46
      apps/remix-ide-e2e/src/commands/addLocalPlugin.ts
  5. 13
      apps/remix-ide-e2e/src/local-plugin/src/app/Client.ts
  6. 131
      apps/remix-ide-e2e/src/local-plugin/src/app/app.css
  7. 131
      apps/remix-ide-e2e/src/local-plugin/src/app/app.tsx
  8. 9
      apps/remix-ide-e2e/src/local-plugin/src/app/logger.tsx
  9. 17
      apps/remix-ide-e2e/src/local-plugin/src/app/logo.svg
  10. 11
      apps/remix-ide-e2e/src/local-plugin/src/app/star.svg
  11. 5
      apps/remix-ide-e2e/src/local-plugin/src/index.html
  12. 78
      apps/remix-ide-e2e/src/tests/pluginManager.test.ts
  13. 327
      apps/remix-ide-e2e/src/tests/plugin_api.ts
  14. 2
      apps/remix-ide-e2e/src/types/index.d.ts
  15. 2
      apps/remix-ide/ci/browser_tests_plugin_api.sh
  16. 1
      apps/remix-ide/src/assets/css/intro.js/2.7.0/introjs.min.css
  17. 2
      apps/remix-ide/src/assets/css/intro.js/4.1.0/introjs.min.css
  18. 1
      apps/remix-ide/src/remixEngine.js
  19. 858
      package-lock.json
  20. 9
      package.json

@ -221,7 +221,7 @@ jobs:
- store_artifacts:
path: ./reports/screenshots
remix-ide-plugin-manager:
remix-ide-plugin-api:
docker:
# specify the version you desire here
- image: circleci/node:14.17.6-browsers
@ -249,13 +249,11 @@ jobs:
name: Start Selenium
command: ./node_modules/.bin/selenium-standalone start --drivers.chrome.version=2.39 --drivers.chrome.baseURL=https://chromedriver.storage.googleapis.com
background: true
- run: ./apps/remix-ide/ci/browser_tests_plugin_manager.sh
- run: ./apps/remix-ide/ci/browser_tests_plugin_api.sh
- store_test_results:
path: ./reports/tests
- store_artifacts:
path: ./reports/screenshots
deploy-remix-live:
docker:
# specify the version you desire here
@ -375,6 +373,9 @@ workflows:
- remix-libs:
requires:
- lint
- remix-ide-plugin-api:
requires:
- lint
- remix-ide-chrome-1:
requires:
- lint
@ -390,9 +391,6 @@ workflows:
- remix-ide-run-deploy:
requires:
- lint
- remix-ide-plugin-manager:
requires:
- lint
- publish:
requires:
- lint
@ -403,7 +401,7 @@ workflows:
- remix-ide-firefox-1
- remix-ide-firefox-2
- remix-ide-run-deploy
- remix-ide-plugin-manager
- remix-ide-plugin-api
filters:
branches:
only: remix_live
@ -414,7 +412,7 @@ workflows:
- remix-ide-firefox-1
- remix-ide-firefox-2
- remix-ide-run-deploy
- remix-ide-plugin-manager
- remix-ide-plugin-api
filters:
branches:
only: master
@ -425,7 +423,7 @@ workflows:
- remix-ide-firefox-1
- remix-ide-firefox-2
- remix-ide-run-deploy
- remix-ide-plugin-manager
- remix-ide-plugin-api
filters:
branches:
only: remix_beta

2
.gitignore vendored

@ -52,5 +52,5 @@ testem.log
# System Files
.DS_Store
.vscode/launch.json
.vscode/settings.json
.vscode/launch.json

@ -0,0 +1,30 @@
import { NightwatchBrowser } from 'nightwatch'
import EventEmitter from 'events'
class AcceptAndRemember extends EventEmitter {
command (this: NightwatchBrowser, remember:boolean, accept: boolean): NightwatchBrowser {
this.api.perform((done) => {
acceptAndRemember(this.api, remember, accept, () => {
done()
this.emit('complete')
})
})
return this
}
}
function acceptAndRemember (browser: NightwatchBrowser, remember: boolean, accept: boolean, callback: VoidFunction) {
browser.useXpath().waitForElementVisible('//*[@data-id="modalDialogModalBody"]')
if (remember) {
browser.click('//*[@id="remember"]', () => {
if (accept) {
browser.click('//*[@id="modal-footer-ok"]')
} else {
browser.click('//*[@id="modal-footer-cancel"]')
}
browser.perform(function () { callback() })
})
}
}
module.exports = AcceptAndRemember

@ -0,0 +1,46 @@
import { NightwatchBrowser } from 'nightwatch'
import EventEmitter from 'events'
import { ExternalProfile, LocationProfile, Profile } from '@remixproject/plugin-utils'
class AddLocalPlugin extends EventEmitter {
command (this: NightwatchBrowser, profile: Profile & LocationProfile & ExternalProfile): NightwatchBrowser {
this.api.perform((done) => {
addLocalPlugin(this.api, profile, () => {
done()
this.emit('complete')
})
})
return this
}
}
function addLocalPlugin (browser: NightwatchBrowser, profile: Profile & LocationProfile & ExternalProfile, callback: VoidFunction) {
browser.waitForElementVisible('*[data-id="remixIdeSidePanel"]')
.pause(3000).element('css selector', '*[data-id="pluginManagerComponentPluginManager"]', function (result) {
if (result.status === 0) {
browser.click('*[plugin="pluginManager"]')
}
})
browser.waitForElementVisible('*[data-id="pluginManagerComponentPluginManager"]')
.execute(function () {
window.testmode = true
})
.click('*[data-id="pluginManagerComponentPluginSearchButton"]')
.waitForElementVisible('*[data-id="pluginManagerLocalPluginModalDialogModalDialogContainer-react"]')
.click('*[data-id="pluginManagerLocalPluginModalDialogModalDialogModalBody-react"]')
.waitForElementVisible('*[data-id="localPluginName"]')
.clearValue('*[data-id="localPluginName"]').setValue('*[data-id="localPluginName"]', profile.name)
.clearValue('*[data-id="localPluginDisplayName"]').setValue('*[data-id="localPluginDisplayName"]', profile.displayName)
.clearValue('*[data-id="localPluginUrl"]').setValue('*[data-id="localPluginUrl"]', profile.url)
.clearValue('*[data-id="localPluginCanActivate"]').setValue('*[data-id="localPluginCanActivate"]', profile.canActivate ? profile.canActivate.join(',') : '')
.click('*[data-id="localPluginRadioButtoniframe"]')
.click(profile.location === 'sidePanel' ? '*[data-id="localPluginRadioButtonsidePanel"]' : '*[data-id="localPluginRadioButtonmainPanel"]')
.click('*[data-id="pluginManagerLocalPluginModalDialogModalDialogModalFooter-react"]')
.click('*[data-id="pluginManagerLocalPluginModalDialog-modal-footer-ok-react')
.waitForElementVisible('[data-id="verticalIconsKindlocalPlugin"]')
.click('[data-id="verticalIconsKindlocalPlugin"]')
.perform(function () { callback() })
}
module.exports = AddLocalPlugin

@ -0,0 +1,13 @@
import { PluginClient } from '@remixproject/plugin'
import { createClient } from '@remixproject/plugin-webview'
export class RemixPlugin extends PluginClient {
constructor () {
super()
this.methods = ['testCommand']
createClient(this)
}
async testCommand (data: any) {
}
}

@ -1,128 +1,3 @@
.app {
font-family: sans-serif;
min-width: 300px;
max-width: 600px;
margin: 50px auto;
}
.app .gutter-left {
margin-left: 9px;
}
.app .col-span-2 {
grid-column: span 2;
}
.app .flex {
display: flex;
align-items: center;
justify-content: center;
}
.app header {
background-color: #143055;
color: white;
padding: 5px;
border-radius: 3px;
}
.app main {
padding: 0 36px;
}
.app p {
text-align: center;
}
.app h1 {
text-align: center;
margin-left: 18px;
font-size: 24px;
}
.app h2 {
text-align: center;
font-size: 20px;
margin: 40px 0 10px 0;
}
.app .resources {
text-align: center;
list-style: none;
padding: 0;
display: grid;
grid-gap: 9px;
grid-template-columns: 1fr 1fr;
}
.app .resource {
color: #0094ba;
height: 36px;
background-color: rgba(0, 0, 0, 0);
border: 1px solid rgba(0, 0, 0, 0.12);
border-radius: 4px;
padding: 3px 9px;
text-decoration: none;
}
.app .resource:hover {
background-color: rgba(68, 138, 255, 0.04);
}
.app pre {
padding: 9px;
border-radius: 4px;
background-color: black;
color: #eee;
}
.app details {
border-radius: 4px;
color: #333;
background-color: rgba(0, 0, 0, 0);
border: 1px solid rgba(0, 0, 0, 0.12);
padding: 3px 9px;
margin-bottom: 9px;
}
.app summary {
outline: none;
height: 36px;
line-height: 36px;
}
.app .github-star-container {
margin-top: 12px;
line-height: 20px;
}
.app .github-star-container a {
display: flex;
align-items: center;
text-decoration: none;
color: #333;
}
.app .github-star-badge {
color: #24292e;
display: flex;
align-items: center;
font-size: 12px;
padding: 3px 10px;
border: 1px solid rgba(27, 31, 35, 0.2);
border-radius: 3px;
background-image: linear-gradient(-180deg, #fafbfc, #eff3f6 90%);
margin-left: 4px;
font-weight: 600;
}
.app .github-star-badge:hover {
background-image: linear-gradient(-180deg, #f0f3f6, #e6ebf1 90%);
border-color: rgba(27, 31, 35, 0.35);
background-position: -0.5em;
}
.app .github-star-badge .material-icons {
height: 16px;
width: 16px;
margin-right: 4px;
}
.jumbotron {
max-height: 25vh;
}

@ -1,37 +1,126 @@
import React, { useEffect, useState } from 'react'
import { PluginClient } from '@remixproject/plugin'
import { createClient } from '@remixproject/plugin-webview'
import { RemixPlugin } from './Client'
import { Logger } from './logger'
import { filePanelProfile } from '@remixproject/plugin-api/lib/file-system/file-panel/profile'
import { filSystemProfile } from '@remixproject/plugin-api/lib/file-system/file-manager/profile'
import { dGitProfile } from '@remixproject/plugin-api/lib/dgit/profile'
import { editorProfile } from '@remixproject/plugin-api/lib/editor/profile'
import { settingsProfile } from '@remixproject/plugin-api/lib/settings/profile'
import { networkProfile } from '@remixproject/plugin-api/lib/network/profile'
import { terminalProfile } from '@remixproject/plugin-api/lib/terminal/profile'
import { udappProfile } from '@remixproject/plugin-api/lib/udapp'
import { compilerProfile } from '@remixproject/plugin-api/lib/compiler'
import { contentImportProfile } from '@remixproject/plugin-api/lib/content-import'
import { windowProfile } from '@remixproject/plugin-api/lib/window'
import { pluginManagerProfile } from '@remixproject/plugin-api/lib/plugin-manager'
import { Profile } from '@remixproject/plugin-utils'
import './app.css'
import { ReactComponent as Logo } from './logo.svg'
const client = new RemixPlugin()
function App () {
const [payload, setPayload] = useState<string>('')
const [log, setLog] = useState<any>()
const [started, setStarted] = useState<boolean>(false)
const [events, setEvents] = useState<any>()
const [profiles, setProfiles] = useState<Profile[]>([pluginManagerProfile, filePanelProfile, filSystemProfile, dGitProfile, networkProfile, settingsProfile, editorProfile, terminalProfile, compilerProfile, udappProfile, contentImportProfile, windowProfile])
export const App = () => {
const [remixClient, setRemixClient] = useState(null)
const handleChange = ({ target }: any) => {
setPayload(target.value)
}
useEffect(() => {
(async () => {
const client = createClient(new PluginClient())
client.onload(async () => {
const customProfiles = ['menuicons', 'tabs', 'solidityUnitTesting']
client.testCommand = async (data: any) => {
console.log(data)
methodLog(data)
}
let addProfiles = []
for (const name of customProfiles) {
const p = await client.call('manager', 'getProfile', name)
addProfiles = [...addProfiles, p]
}
setProfiles(profiles => [...profiles, ...addProfiles])
await client.onload()
console.log('Local plugin loaded')
setRemixClient(client)
})()
profiles.map((profile: Profile) => {
if (profile.events) {
profile.events.map((event: string) => {
client.on(profile.name as any, event, (...args: any) => {
console.log('event :', event, args)
eventLog({
event: event,
args: args
})
})
})
}
})
})
}, [])
const handleClick = () => {
remixClient.call('manager', 'activatePlugin', 'LearnEth')
const methodLog = (log: any) => {
const addValue = typeof log === 'string' ? log : JSON.stringify(log)
setLog((value) => `${value} ${addValue}`)
}
const eventLog = (log: any) => {
const addValue = typeof log === 'string' ? log : JSON.stringify(log)
setEvents((value) => `${value} ${addValue}`)
}
const clientMethod = async (profile: Profile, method: string) => {
try {
let ob: any = null
try {
ob = JSON.parse(payload)
if (ob && !Array.isArray(ob)) { ob = [ob] }
} catch (e) { }
const args = ob || [payload]
setStarted(true)
setLog('')
setEvents('')
console.log('calling :', profile.name, method, ...args)
await client.call('manager', 'activatePlugin', profile.name)
const result = await client.call(profile.name as any, method, ...args)
console.log('result :', result)
methodLog(result)
} catch (e) {
methodLog(e.message)
}
setStarted(false)
}
return (
<div className="app">
<header className="flex">
<Logo width="75" height="75" />
<h1>Welcome to local-plugin!</h1>
</header>
<main>
<button data-id="btnActivateRemixd" onClick={handleClick}>Activate Learneth</button>
</main>
<div className="App container-fluid">
<h5>PLUGIN API TESTER</h5>
<label id='callStatus'>{started ? <>start</> : <>stop</> }</label><br></br>
<label>method results</label>
<Logger id='methods' log={log}></Logger>
<label>events</label>
<Logger id='events' log={events}></Logger>
<input
className='form-control w-100'
type="text"
id="payload"
placeholder="Enter payload here..."
value={payload}
onChange={handleChange}
/>
{profiles.map((profile: Profile) => {
const methods = profile.methods.map((method: string) => {
return <button data-id={`${profile.name}:${method}`} key={method} className='btn btn-primary btn-sm ml-1 mb-1' onClick={async () => await clientMethod(profile, method)}>{method}</button>
})
const events = profile.events ? profile.events.map((event: string) => {
return <label key={event} className='m-1'>{event}</label>
}) : null
return <div key={profile.name} className='small border-bottom'><label className='text-uppercase'>{profile.name}</label><br></br>{methods}<br></br>{events ? <label>EVENTS:</label> : null}{events}</div>
})}
</div>
)
}

@ -0,0 +1,9 @@
import React from 'react'
interface loggerProps {
log: any,
id: string
}
export const Logger: React.FC<loggerProps> = (props) => {
return (<div id={props.id} className="jumbotron overflow-auto text-break mb-1 p-2">{props.log}</div>)
}

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="262px" height="163px" viewBox="0 0 262 163" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Styles-&amp;-Quick-Wins" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Nx---Quick-Wins" transform="translate(-476.000000, -1284.000000)" fill-rule="nonzero">
<g id="Logos" transform="translate(-11.000000, 782.000000)">
<g id="Nx_Flat_White" transform="translate(487.000000, 502.000000)">
<polygon id="Path" fill="#FFFFFF" points="130.68 104.59 97.49 52.71 97.44 96.3 40.24 0 0 0 0 162.57 39.79 162.57 39.92 66.39 96.53 158.26"></polygon>
<polygon id="Path" fill="#FFFFFF" points="97.5 41.79 137.24 41.79 137.33 41.33 137.33 0 97.54 0 97.49 41.33"></polygon>
<path d="M198.66,86.86 C189.139872,86.6795216 180.538723,92.516445 177.19,101.43 C182.764789,93.0931021 193.379673,89.7432211 202.73,93.37 C207.05,95.13 212.73,97.97 217.23,96.45 C212.950306,90.4438814 206.034895,86.8725952 198.66,86.86 L198.66,86.86 Z" id="Path" fill="#96D8E9"></path>
<path d="M243.75,106.42 C243.75,101.55 241.1,100.42 235.6,98.42 C231.52,97 226.89,95.4 223.52,91 C222.86,90.13 222.25,89.15 221.6,88.11 C220.14382,85.4164099 218.169266,83.037429 215.79,81.11 C212.58,78.75 208.37,77.6 202.91,77.6 C191.954261,77.6076705 182.084192,84.2206169 177.91,94.35 C183.186964,87.0278244 191.956716,83.0605026 200.940147,83.9314609 C209.923578,84.8024193 217.767888,90.3805017 221.54,98.58 C223.424615,101.689762 227.141337,103.174819 230.65,102.22 C236.02,101.07 235.65,106.15 243.76,107.87 L243.75,106.42 Z" id="Path" fill="#48C4E5"></path>
<path d="M261.46,105.38 L261.46,105.27 C261.34,73.03 235.17,45.45 202.91,45.45 C183.207085,45.4363165 164.821777,55.3450614 154,71.81 L153.79,71.45 L137.23,45.45 L97.5,45.4499858 L135.25,104.57 L98.41,162.57 L137,162.57 L153.79,136.78 L170.88,162.57 L209.48,162.57 L174.48,107.49 C173.899005,106.416838 173.583536,105.220114 173.56,104 C173.557346,96.2203871 176.64661,88.7586448 182.147627,83.2576275 C187.648645,77.7566101 195.110387,74.6673462 202.89,74.67 C219.11,74.67 221.82,84.37 225.32,88.93 C232.23,97.93 246.03,93.99 246.03,105.73 L246.03,105.73 C246.071086,108.480945 247.576662,111.001004 249.979593,112.340896 C252.382524,113.680787 255.317747,113.636949 257.679593,112.225896 C260.041438,110.814842 261.471086,108.250945 261.43,105.5 L261.43,105.5 L261.43,105.38 L261.46,105.38 Z" id="Path" fill="#FFFFFF"></path>
<path d="M261.5,113.68 C261.892278,116.421801 261.504116,119.218653 260.38,121.75 C258.18,126.84 254.51,125.14 254.51,125.14 C254.51,125.14 251.35,123.6 253.27,120.65 C255.4,117.36 259.61,117.74 261.5,113.68 Z" id="Path" fill="#FFFFFF"></path>
</g>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.9 KiB

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg
className="material-icons"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
>
<path d="M0 0h24v24H0z" fill="none" />
<path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z" />
</svg>

Before

Width:  |  Height:  |  Size: 347 B

@ -1,10 +1,9 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>LocalPlugin</title>
<base href="/" />
<meta charset="utf-8" />
<title>Remix Plugin API Tester</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/x-icon" href="favicon.ico" />
</head>

@ -12,13 +12,6 @@ const testData = {
pluginUrl: 'https://zokrates.github.io/zokrates-remix-plugin/'
}
const localPluginData = {
pluginName: 'localPlugin',
pluginDisplayName: 'Local Plugin',
pluginCanActivate: 'LearnEth',
pluginUrl: 'http://localhost:2020'
}
module.exports = {
before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done, 'http://127.0.0.1:8080', false)
@ -76,44 +69,6 @@ module.exports = {
.waitForElementVisible('*[data-id="pluginManagerComponentActivateButtonvyper"]', 60000)
},
/*
'Should grant plugin permission (ZOKRATES)': function (browser) {
browser.waitForElementVisible('*[data-id="pluginManagerComponentPluginManager"]')
.click('*[data-id="pluginManagerPermissionsButton"]')
.waitForElementVisible('*[data-id="pluginManagerSettingsPermissionForm"]')
.assert.containsText('*[data-id="pluginManagerSettingsPermissionForm"]', 'No Permission requested yet')
.modalFooterOKClick()
.click('*[data-id="verticalIconsFileExplorerIcons"]')
.openFile('3_Ballot.sol')
.click('*[plugin="ZoKrates"]')
.pause(5000)
.frame(0)
.useXpath().click("//span[text()='Compile']")
.pause(2000)
.frameParent()
.useCss().waitForElementVisible('*[data-id="modalDialogContainer"]')
.assert.containsText('*[data-id="permissionHandlerMessage"]', 'ZOKRATES" WOULD LIKE TO ACCESS "FILE MANAGER" :')
.pause(2000)
.click('*[data-id="permissionHandlerRememberChoice"]')
.pause(2000)
.modalFooterOKClick()
},
'Should revert plugin permission (ZOKRATES)': function (browser) {
browser.waitForElementVisible('*[data-id="verticalIconsSettingsIcons"]')
.click('*[data-id="verticalIconsSettingsIcons"]')
.waitForElementVisible('*[data-id="pluginManagerPermissionsButton"]')
.click('*[data-id="pluginManagerPermissionsButton"]')
.waitForElementVisible('*[data-id="modalDialogContainer"]')
.click('*[data-id="pluginManagerSettingsPermissionForm"]')
.pause(2000)
.click('*[data-id="pluginManagerSettingsClearAllPermission"]')
.pause(2000)
.assert.containsText('*[data-id="pluginManagerSettingsPermissionForm"]', 'No Permission requested yet')
.modalFooterOKClick()
},
*/
'Should connect a local plugin': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="pluginManagerComponentPluginManager"]')
.execute(function () {
@ -130,8 +85,6 @@ module.exports = {
.click('*[data-id="localPluginRadioButtonsidePanel"]')
.click('*[data-id="pluginManagerLocalPluginModalDialogModalDialogModalFooter-react"]')
.click('*[data-id="pluginManagerLocalPluginModalDialog-modal-footer-ok-react')
// .modalFooterOKClick()
// .waitForElementVisible('*[data-id="pluginManagerComponentDeactivateButtonremixIde"]', 60000)
},
'Should display error message for creating already existing plugin': function (browser: NightwatchBrowser) {
@ -154,46 +107,17 @@ module.exports = {
.assert.containsText('*[data-shared="tooltipPopup"]', 'Cannot create Plugin : This name has already been used')
},
'Local plugin should activate LearnEth plugin': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="pluginManagerComponentPluginManager"]')
.click('*[data-id="pluginManagerComponentPluginSearchButton"]')
.waitForElementVisible('*[data-id="pluginManagerLocalPluginModalDialogModalDialogContainer-react"]')
.click('*[data-id="pluginManagerLocalPluginModalDialogModalDialogModalBody-react"]')
.waitForElementVisible('*[data-id="localPluginName"]')
.clearValue('*[data-id="localPluginName"]').setValue('*[data-id="localPluginName"]', localPluginData.pluginName)
.clearValue('*[data-id="localPluginDisplayName"]').setValue('*[data-id="localPluginDisplayName"]', localPluginData.pluginDisplayName)
.clearValue('*[data-id="localPluginCanActivate"]').setValue('*[data-id="localPluginCanActivate"]', localPluginData.pluginCanActivate)
.clearValue('*[data-id="localPluginUrl"]').setValue('*[data-id="localPluginUrl"]', localPluginData.pluginUrl)
.click('*[data-id="localPluginRadioButtoniframe"]')
.click('*[data-id="localPluginRadioButtonsidePanel"]')
.click('*[data-id="pluginManagerLocalPluginModalDialogModalDialogModalFooter-react"]')
.click('*[data-id="pluginManagerLocalPluginModalDialog-modal-footer-ok-react')
.waitForElementVisible('[data-id="verticalIconsKindlocalPlugin"]')
.click('[data-id="verticalIconsKindlocalPlugin"]')
.waitForElementNotPresent('[data-id="verticalIconsKindLearnEth"]')
.pause(2000)
// @ts-ignore
.frame('plugin-localPlugin')
.useXpath().click("//button[text()='Activate Learneth']")
.pause(2000)
.frameParent()
.useCss().waitForElementPresent('[data-id="verticalIconsKindLearnEth"]')
},
'Should load back installed plugins after reload': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="remixIdeSidePanel"]')
.click('*[plugin="pluginManager"]')
.waitForElementVisible('*[data-id="pluginManagerComponentPluginManager"]')
.getInstalledPlugins((plugins) => {
browser.refresh()
.waitForElementVisible('*[data-id="remixIdeSidePanel"]')
.pause(3000)
.perform((done) => {
// const filtered = plugins.filter(plugin => plugin !== 'testremixIde') // remove this when localplugin bug is resolved
plugins.forEach(plugin => {
if ((plugin !== testData.pluginName) && plugin !== localPluginData.pluginName) {
if (plugin !== testData.pluginName) {
browser.waitForElementVisible(`[plugin="${plugin}"`)
}
})

@ -0,0 +1,327 @@
'use strict'
import { ExternalProfile, LocationProfile, Profile } from '@remixproject/plugin-utils'
import { NightwatchBrowser } from 'nightwatch'
import init from '../helpers/init'
declare global {
interface Window { testmode: boolean; }
}
const localPluginData: Profile & LocationProfile & ExternalProfile = {
name: 'localPlugin',
displayName: 'Local Plugin',
canActivate: ['dGitProvider', 'flattener', 'solidityUnitTesting'],
url: 'http://localhost:2020',
location: 'sidePanel'
}
const getBrowserLogs = async function (browser: NightwatchBrowser) {
browser.getLog('browser', (logEntries) => {
if (logEntries && logEntries.length > 0) {
console.log('Browser log:')
console.log(logEntries)
}
})
}
const debugValues = async function (browser: NightwatchBrowser, field: string, expected: any) {
return new Promise((resolve) => {
if (!expected) {
resolve(true)
return
}
browser.waitForElementVisible(`//*[@id="${field}"]`).getText(`//*[@id="${field}"]`, (result) => {
console.log(result)
if (!result.value.toString().includes(expected)) {
console.log('Actual result:')
console.log(result.value.toString())
console.log('Expected result:')
console.log(expected)
getBrowserLogs(browser)
browser.assert.ok(false, 'Returned value from call does not match expected value.')
} else {
browser.assert.ok(true)
}
resolve(true)
})
})
}
const setPayload = async (browser: NightwatchBrowser, payload: any) => {
return new Promise((resolve) => {
if (typeof payload !== 'string') payload = JSON.stringify(payload)
browser.clearValue('//*[@id="payload"]').setValue('//*[@id="payload"]', payload, (result) => {
resolve(result)
})
})
}
const clearPayLoad = async (browser: NightwatchBrowser) => {
return new Promise((resolve) => {
browser.clearValue('//*[@id="payload"]', () => {
resolve(true)
})
})
}
const clickButton = async (browser: NightwatchBrowser, buttonText: string) => {
return new Promise((resolve) => {
browser.useXpath().waitForElementVisible(`//*[@data-id='${buttonText}']`).pause(100)
.click(`//*[@data-id='${buttonText}']`, async () => {
await checkForAcceptAndRemember(browser)
browser.waitForElementContainsText('//*[@id="callStatus"]', 'stop').perform(() => resolve(true))
})
})
}
const checkForAcceptAndRemember = async function (browser: NightwatchBrowser) {
return new Promise((resolve) => {
browser.frameParent(() => {
browser.pause(1000).element('xpath', '//*[@data-id="permissionHandlerRememberUnchecked"]', (visible:any) => {
if (visible.status && visible.status === -1) {
// @ts-ignore
browser.frame(0, () => { resolve(true) })
} else {
browser.waitForElementVisible('//*[@data-id="permissionHandlerRememberUnchecked"]').click('//*[@data-id="permissionHandlerRememberUnchecked"]').waitForElementVisible('//*[@id="modal-footer-ok"]').click('//*[@id="modal-footer-ok"]', () => {
// @ts-ignore
browser.frame(0, () => { resolve(true) })
})
}
})
})
})
}
/**
* performs an action on the test local plugin calling a method on a plugin
*
* @param {NightwatchBrowser} browser
* @param {string} buttonText the button which needs to be clicked formatted as pluginname:methodname, ie 'fileManager:writeFile'
* @param {any} methodResult can be a string expected or an object. it is the result of the method called.
* @param {any} eventResult can be a string expected or an object. it is the event generated by the method called.
* @param {any} payload can be a string expected or an object. it is the payload passed to the call
* @return {Promise}
*/
const clickAndCheckLog = async (browser: NightwatchBrowser, buttonText: string, methodResult: any, eventResult: any, payload: any) => {
if (payload) {
await setPayload(browser, payload)
} else {
await clearPayLoad(browser)
}
if (methodResult && typeof methodResult !== 'string') { methodResult = JSON.stringify(methodResult) }
if (eventResult && typeof eventResult !== 'string') { eventResult = JSON.stringify(eventResult) }
if (buttonText) {
await clickButton(browser, buttonText)
}
await debugValues(browser, 'methods', methodResult)
await debugValues(browser, 'events', eventResult)
}
const assertPluginIsActive = function (browser: NightwatchBrowser, id: string, shouldBeVisible: boolean) {
if (shouldBeVisible) {
browser.waitForElementVisible(`//*[@data-id="verticalIconsKind${id}"]`)
} else {
browser.waitForElementNotPresent(`//*[@data-id="verticalIconsKind${id}"]`)
}
}
module.exports = {
before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done, 'http://127.0.0.1:8080', false)
},
afterEach: function (browser: NightwatchBrowser) {
browser.getLog('browser', (logEntries) => {
console.log(logEntries)
})
},
'Should connect a local plugin': function (browser: NightwatchBrowser) {
browser.addLocalPlugin(localPluginData)
// @ts-ignore
.frame(0).useXpath()
},
// UDAPP
'Should get accounts': async function (browser: NightwatchBrowser) {
await clickAndCheckLog(browser, 'udapp:getAccounts', '0x5B38Da6a701c568545dCfcB03FcB875f56beddC4', null, null)
},
// context menu item
'Should create context menu item': async function (browser: NightwatchBrowser) {
await clickAndCheckLog(browser, 'filePanel:registerContextMenuItem', null, null, {
id: 'localPlugin',
name: 'testCommand',
label: 'testCommand',
type: [],
extension: ['.sol'],
path: [],
pattern: []
})
await browser.useXpath().frameParent(async () => {
browser.useCss().clickLaunchIcon('filePanel')
.waitForElementVisible('[data-id="treeViewLitreeViewItemcontracts"]').element('css selector', '[data-id="treeViewLitreeViewItemcontracts/1_Storage.sol"]', (visible: any) => {
if (visible.status && visible.status === -1) {
browser.click('[data-id="treeViewLitreeViewItemcontracts"]')
}
})
.waitForElementVisible('[data-id="treeViewLitreeViewItemcontracts/1_Storage.sol"]')
.rightClick('[data-id="treeViewLitreeViewItemcontracts/1_Storage.sol"]').useXpath().waitForElementVisible('//*[@id="menuitemtestcommand"]').click('//*[@id="menuitemtestcommand"]', async () => {
// @ts-ignore
browser.click('//*[@data-id="verticalIconsKindlocalPlugin"]').frame(0, async () => {
await clickAndCheckLog(browser, null, { id: 'localPlugin', name: 'testCommand', label: 'testCommand', type: [], extension: ['.sol'], path: ['contracts/1_Storage.sol'], pattern: [] }, null, null)
})
})
})
},
// FILESYSTEM
'Should get current workspace': async function (browser: NightwatchBrowser) {
await clickAndCheckLog(browser, 'filePanel:getCurrentWorkspace', { name: 'default_workspace', isLocalhost: false, absolutePath: '.workspaces/default_workspace' }, null, null)
},
'Should get current files': async function (browser: NightwatchBrowser) {
await clickAndCheckLog(browser, 'fileManager:readdir', { contracts: { isDirectory: true }, scripts: { isDirectory: true }, tests: { isDirectory: true }, 'README.txt': { isDirectory: false } }, null, '/')
},
'Should throw error on current file': async function (browser: NightwatchBrowser) {
await clickAndCheckLog(browser, 'fileManager:getCurrentFile', 'Error from IDE : Error: No such file or directory No file selected', null, null)
},
'Should open readme.txt': async function (browser: NightwatchBrowser) {
await clickAndCheckLog(browser, 'fileManager:open', null, { event: 'currentFileChanged', args: ['README.txt'] }, 'README.txt')
},
'Should have current file': async function (browser: NightwatchBrowser) {
await clickAndCheckLog(browser, 'fileManager:getCurrentFile', 'README.txt', null, null)
},
'Should create dir': async function (browser: NightwatchBrowser) {
await clickAndCheckLog(browser, 'fileManager:mkdir', null, null, 'testdir')
await clickAndCheckLog(browser, 'fileManager:readdir', 'testdir', null, '/')
},
'Should get file': async function (browser: NightwatchBrowser) {
await clickAndCheckLog(browser, 'fileManager:getFile', 'REMIX EXAMPLE PROJECT', null, 'README.txt')
},
'Should close all files': async function (browser: NightwatchBrowser) {
await clickAndCheckLog(browser, 'fileManager:closeAllFiles', null, { event: 'noFileSelected', args: [] }, null)
},
'Should switch to file': async function (browser: NightwatchBrowser) {
await clickAndCheckLog(browser, 'fileManager:switchFile', null, { event: 'currentFileChanged', args: ['contracts/1_Storage.sol'] }, 'contracts/1_Storage.sol')
await clickAndCheckLog(browser, 'fileManager:getCurrentFile', 'contracts/1_Storage.sol', null, null)
await clickAndCheckLog(browser, 'fileManager:switchFile', null, { event: 'currentFileChanged', args: ['README.txt'] }, 'README.txt')
await clickAndCheckLog(browser, 'fileManager:getCurrentFile', 'README.txt', null, null)
},
'Should write to file': async function (browser: NightwatchBrowser) {
await clickAndCheckLog(browser, 'fileManager:writeFile', null, { event: 'fileSaved', args: ['README.txt'] }, ['README.txt', 'test'])
await clickAndCheckLog(browser, 'fileManager:readFile', 'test', null, 'README.txt')
},
'Should set file': async function (browser: NightwatchBrowser) {
await clickAndCheckLog(browser, 'fileManager:setFile', null, { event: 'fileAdded', args: ['new.sol'] }, ['new.sol', 'test'])
await clickAndCheckLog(browser, 'fileManager:readFile', 'test', null, 'new.sol')
},
'Should write to new file': async function (browser: NightwatchBrowser) {
await clickAndCheckLog(browser, 'fileManager:writeFile', null, { event: 'fileAdded', args: ['testing.txt'] }, ['testing.txt', 'test'])
await clickAndCheckLog(browser, 'fileManager:readFile', 'test', null, 'testing.txt')
},
'Should rename file': async function (browser: NightwatchBrowser) {
await clickAndCheckLog(browser, 'fileManager:rename', null, null, ['testing.txt', 'testrename.txt'])
await clickAndCheckLog(browser, 'fileManager:readFile', 'test', null, 'testrename.txt')
},
'Should create empty workspace': async function (browser: NightwatchBrowser) {
await clickAndCheckLog(browser, 'filePanel:createWorkspace', null, null, ['emptyworkspace', true])
await clickAndCheckLog(browser, 'filePanel:getCurrentWorkspace', { name: 'emptyworkspace', isLocalhost: false, absolutePath: '.workspaces/emptyworkspace' }, null, null)
await clickAndCheckLog(browser, 'fileManager:readdir', {}, null, '/')
},
'Should create workspace': async function (browser: NightwatchBrowser) {
await clickAndCheckLog(browser, 'filePanel:createWorkspace', null, null, 'testspace')
await clickAndCheckLog(browser, 'filePanel:getCurrentWorkspace', { name: 'testspace', isLocalhost: false, absolutePath: '.workspaces/testspace' }, null, null)
await clickAndCheckLog(browser, 'fileManager:readdir', { contracts: { isDirectory: true }, scripts: { isDirectory: true }, tests: { isDirectory: true }, 'README.txt': { isDirectory: false } }, null, null)
},
'Should get all workspaces': async function (browser: NightwatchBrowser) {
await clickAndCheckLog(browser, 'filePanel:getWorkspaces', ['default_workspace', 'emptyworkspace', 'testspace'], null, null)
},
'Should have set workspace event': async function (browser: NightwatchBrowser) {
await clickAndCheckLog(browser, 'filePanel:createWorkspace', null, { event: 'setWorkspace', args: [{ name: 'newspace', isLocalhost: false }] }, 'newspace')
},
'Should have event when switching workspace': async function (browser: NightwatchBrowser) {
// @ts-ignore
browser.frameParent().useCss().clickLaunchIcon('filePanel').click('*[data-id="workspacesSelect"] option[value="default_workspace"]').useXpath().click('//*[@data-id="verticalIconsKindlocalPlugin"]').frame(0, async () => {
await clickAndCheckLog(browser, null, null, { event: 'setWorkspace', args: [{ name: 'default_workspace', isLocalhost: false }] }, null)
})
},
'Should rename workspace': async function (browser: NightwatchBrowser) {
await clickAndCheckLog(browser, 'filePanel:renameWorkspace', null, null, ['default_workspace', 'renamed'])
await clickAndCheckLog(browser, 'filePanel:getWorkspaces', ['emptyworkspace', 'testspace', 'newspace', 'renamed'], null, null)
},
'Should delete workspace': async function (browser: NightwatchBrowser) {
await clickAndCheckLog(browser, 'filePanel:deleteWorkspace', null, null, ['testspace'])
await clickAndCheckLog(browser, 'filePanel:getWorkspaces', ['emptyworkspace', 'newspace', 'renamed'], null, null)
},
// DGIT
'Should have changes on new workspace': async function (browser: NightwatchBrowser) {
await clickAndCheckLog(browser, 'filePanel:createWorkspace', null, null, 'dgit')
await clickAndCheckLog(browser, 'dGitProvider:status', [['README.txt', 0, 2, 0], ['contracts/1_Storage.sol', 0, 2, 0], ['contracts/2_Owner.sol', 0, 2, 0], ['contracts/3_Ballot.sol', 0, 2, 0], ['scripts/deploy_ethers.js', 0, 2, 0], ['scripts/deploy_web3.js', 0, 2, 0], ['tests/4_Ballot_test.sol', 0, 2, 0]], null, null)
},
'Should stage contract': async function (browser: NightwatchBrowser) {
await clickAndCheckLog(browser, 'dGitProvider:add', null, null, {
filepath: 'contracts/1_Storage.sol'
})
await clickAndCheckLog(browser, 'dGitProvider:status', [['README.txt', 0, 2, 0], ['contracts/1_Storage.sol', 0, 2, 2], ['contracts/2_Owner.sol', 0, 2, 0], ['contracts/3_Ballot.sol', 0, 2, 0], ['scripts/deploy_ethers.js', 0, 2, 0], ['scripts/deploy_web3.js', 0, 2, 0], ['tests/4_Ballot_test.sol', 0, 2, 0]], null, null)
},
'Should commit changes': async function (browser: NightwatchBrowser) {
await clickAndCheckLog(browser, 'dGitProvider:commit', null, null, { author: { name: 'Remix', email: 'Remix' }, message: 'commit-message' })
await clickAndCheckLog(browser, 'dGitProvider:log', 'commit-message', null, null)
},
'Should have git log': async function (browser: NightwatchBrowser) {
await clickAndCheckLog(browser, 'dGitProvider:log', 'commit-message', null, null)
},
'Should have branches': async function (browser: NightwatchBrowser) {
await clickAndCheckLog(browser, 'dGitProvider:branches', [{ name: 'main' }], null, null)
},
// resolver
'Should resolve url': async function (browser: NightwatchBrowser) {
await clickAndCheckLog(browser, 'contentImport:resolve', '# Remix Project', null, 'https://github.com/ethereum/remix-project/blob/master/README.md')
},
'Should resolve and save url': async function (browser: NightwatchBrowser) {
await clickAndCheckLog(browser, 'contentImport:resolveAndSave', '# Remix Project', { event: 'fileAdded', args: ['.deps/github/ethereum/remix-project/README.md'] }, 'https://github.com/ethereum/remix-project/blob/master/README.md')
},
// UNIT TESTING
'Should activate solidityUnitTesting': async function (browser: NightwatchBrowser) {
await clickAndCheckLog(browser, 'manager:activatePlugin', null, null, 'solidityUnitTesting')
browser.frameParent()
assertPluginIsActive(browser, 'solidityUnitTesting', true)
// @ts-ignore
browser.frame(0)
await clickAndCheckLog(browser, 'manager:isActive', true, null, 'solidityUnitTesting')
},
'Should test from path with solidityUnitTesting': async function (browser: NightwatchBrowser) {
await clickAndCheckLog(browser, 'solidityUnitTesting:testFromPath', '"totalPassing":2,"totalFailing":0', null, 'tests/4_Ballot_test.sol')
},
'Should deactivate solidityUnitTesting': async function (browser: NightwatchBrowser) {
await clickAndCheckLog(browser, 'manager:deactivatePlugin', null, null, 'solidityUnitTesting')
browser.frameParent()
assertPluginIsActive(browser, 'solidityUnitTesting', false)
// @ts-ignore
browser.frame(0)
await clickAndCheckLog(browser, 'manager:isActive', false, null, 'solidityUnitTesting')
},
// COMPILER
'Should compile a file': async function (browser: NightwatchBrowser) {
await clickAndCheckLog(browser, 'solidity:compile', null, null, 'contracts/1_Storage.sol')
browser.pause(5000, async () => {
await clickAndCheckLog(browser, 'solidity:compile', null, 'compilationFinished', null)
})
},
'Should get compilationresults': async function (browser: NightwatchBrowser) {
await clickAndCheckLog(browser, 'solidity:getCompilationResult', 'contracts/1_Storage.sol', null, null)
}
}

@ -57,6 +57,8 @@ declare module 'nightwatch' {
checkAnnotationsNotPresent(type: string): NightwatchBrowser
getLastTransactionHash(callback: (hash: string) => void)
currentWorkspaceIs(name: string): NightwatchBrowser
addLocalPlugin(this: NightwatchBrowser, profile: Profile & LocationProfile & ExternalProfile): NightwatchBrowser
acceptAndRemember (this: NightwatchBrowser, remember: boolean, accept: boolean): NightwatchBrowser
}
export interface NightwatchBrowser {

@ -12,7 +12,7 @@ npx nx serve remix-ide-e2e-src-local-plugin &
sleep 5
npm run build:e2e
npm run nightwatch_local_pluginManager || TEST_EXITCODE=1
npm run nightwatch_local_pluginApi || TEST_EXITCODE=1
echo "$TEST_EXITCODE"
if [ "$TEST_EXITCODE" -eq 1 ]

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -13,6 +13,7 @@ export class RemixEngine extends Engine {
if (name === 'dGitProvider') return { queueTimeout: 60000 * 4 }
if (name === 'slither') return { queueTimeout: 60000 * 4 } // Requires when a solc version is installed
if (name === 'hardhat') return { queueTimeout: 60000 * 4 }
if (name === 'localPlugin') return { queueTimeout: 60000 * 4 }
return { queueTimeout: 10000 }
}

858
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -78,7 +78,7 @@
"nightwatch_local_gist": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/gist.spec.js --env=chrome",
"nightwatch_local_workspace": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/workspace.test.js --env=chrome",
"nightwatch_local_defaultLayout": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/defaultLayout.test.js --env=chrome",
"nightwatch_local_pluginManager": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/pluginManager.js --env=chrome",
"nightwatch_local_pluginManager": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/pluginManager.test.js --env=chrome",
"nightwatch_local_publishContract": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/publishContract.test.js --env=chrome",
"nightwatch_local_generalSettings": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/generalSettings.test.js --env=chrome",
"nightwatch_local_fileExplorer": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/fileExplorer.test.js --env=chrome",
@ -91,6 +91,7 @@
"nightwatch_local_url": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/url.spec.js --env=chrome",
"nightwatch_local_verticalIconscontextmenu": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/verticalIconsPanel.spec.js --env=chrome",
"nightwatch_local_migrate_filesystem": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/migrateFileSystem.test.js --env=chrome",
"nightwatch_local_pluginApi": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/plugin_api.js --env=chrome",
"onchange": "onchange apps/remix-ide/build/app.js -- npm-run-all lint",
"remixd": "nx build remixd && nx serve remixd --folder=./apps/remix-ide/contracts --remixide=http://127.0.0.1:8080",
"selenium": "selenium-standalone start",
@ -219,7 +220,7 @@
"@types/jest": "^27.0.2",
"@types/lodash": "^4.14.172",
"@types/mocha": "^7.0.2",
"@types/nightwatch": "^1.1.6",
"@types/nightwatch": "1.3.4",
"@types/node": "~8.9.4",
"@types/react": "^17.0.24",
"@types/react-beautiful-dnd": "^13.1.2",
@ -279,7 +280,7 @@
"mkdirp": "^0.5.1",
"mocha": "^8.0.1",
"nanohtml": "^1.6.3",
"nightwatch": "^1.5.1",
"nightwatch": "^1.7.11",
"nodemon": "^2.0.4",
"notify-error": "^1.2.0",
"npm-link-local": "^1.1.0",
@ -291,7 +292,7 @@
"remix-tabs": "1.1.3",
"request": "^2.83.0",
"rimraf": "^2.6.1",
"selenium-standalone": "^6.17.0",
"selenium-standalone": "^7.1.0",
"semver": "^6.3.0",
"solc": "0.7.4",
"swarmgw": "^0.3.1",

Loading…
Cancel
Save