Merge branch 'master' into rendercomponent

pull/5370/head
bunsenstraat 3 years ago committed by GitHub
commit 66ae63ecce
  1. 195
      apps/remix-ide-e2e/src/tests/stress.editor.ts
  2. 11
      apps/remix-ide/src/app/files/fileManager.ts
  3. 49
      libs/remix-core-plugin/src/lib/compiler-artefacts.ts
  4. 11
      libs/remix-ui/workspace/src/lib/actions/index.ts
  5. 6
      libs/remix-ui/workspace/src/lib/components/file-explorer-context-menu.tsx
  6. 9
      libs/remix-ui/workspace/src/lib/components/file-explorer.tsx
  7. 1
      libs/remix-ui/workspace/src/lib/contexts/index.ts
  8. 7
      libs/remix-ui/workspace/src/lib/providers/FileSystemProvider.tsx
  9. 2
      libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx
  10. 2
      libs/remix-ui/workspace/src/lib/types/index.ts
  11. 6
      libs/remix-ui/workspace/src/lib/utils/index.ts
  12. 1
      package.json

@ -0,0 +1,195 @@
'use strict'
import { NightwatchBrowser } from 'nightwatch'
import init from '../helpers/init'
module.exports = {
before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done)
},
'Should create 10 files, reload, and check if the files are saved': function (browser: NightwatchBrowser) {
const contents = {}
const checkContent = function (i, done) {
const name = 'test_' + i + '.sol'
browser
.openFile(name)
.pause(500)
.getEditorValue((content) => {
browser.assert.ok(content === contents[i])
done()
})
}
browser.clickLaunchIcon('filePanel').perform((done) => {
let contentEditSet = content.slice()
for (let i = 0; i < 10; i++) {
contentEditSet += contentEditSet
contents[i] = contentEditSet
const name = 'test_' + i + '.sol'
browser.click('[data-id="fileExplorerNewFilecreateNewFile"]')
.waitForElementContainsText('*[data-id$="/blank"]', '', 60000)
.sendKeys('*[data-id$="/blank"] .remixui_items', name)
.sendKeys('*[data-id$="/blank"] .remixui_items', browser.Keys.ENTER)
.waitForElementVisible(`li[data-id="treeViewLitreeViewItem${name}"]`, 60000)
.setEditorValue(contentEditSet)
}
done()
}).pause(10000).refresh()
.perform(done => checkContent(0, done))
.perform(done => checkContent(1, done))
.perform(done => checkContent(2, done))
.perform(done => checkContent(3, done))
.perform(done => checkContent(4, done))
.perform(done => checkContent(5, done))
.perform(done => checkContent(6, done))
.perform(done => checkContent(7, done))
.perform(done => checkContent(8, done))
.perform(done => checkContent(9, done))
.end()
}
}
const content = `
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
/**
* @title Ballot
* @dev Implements voting process along with vote delegation|
*/
contract Ballot {
struct Voter {
uint weight; // weight is accumulated by delegation
bool voted; // if true, that person already voted
address delegate; // person delegated to
uint vote; // index of the voted proposal
}
struct Proposal {
// If you can limit the length to a certain number of bytes,
// always use one of bytes1 to bytes32 because they are much cheaper
bytes32 name; // short name (up to 32 bytes)
uint voteCount; // number of accumulated votes
}
address public chairperson;
mapping(address => Voter) public voters;
Proposal[] public proposals;
function () test {
/**
* @dev Create a new ballot to choose one of 'proposalNames'.
* @param proposalNames names of proposals
*/
constructor(bytes32[] memory proposalNames) {
chairperson = msg.sender;
voters[chairperson].weight = 1;
for (uint i = 0; i < proposalNames.length; i++) {
// 'Proposal({...})' creates a temporary
// Proposal object and 'proposals.push(...)'
// appends it to the end of 'proposals'.
proposals.push(Proposal({
name: proposalNames[i],
voteCount: 0
}));
}
}
/**
* @dev Give 'voter' the right to vote on this ballot. May only be called by 'chairperson'.
* @param voter address of voter
*/
function giveRightToVote(address voter) public {
require(
msg.sender == chairperson,
"Only chairperson can give right to vote."
);
require(
!voters[voter].voted,
"The voter already voted."
);
require(voters[voter].weight == 0);
voters[voter].weight = 1;
}
/**
* @dev Delegate your vote to the voter 'to'.
* @param to address to which vote is delegated
*/
function delegate(address to) public {
Voter storage sender = voters[msg.sender];
require(!sender.voted, "You already voted.");
require(to != msg.sender, "Self-delegation is disallowed.");
while (voters[to].delegate != address(0)) {
to = voters[to].delegate;
// We found a loop in the delegation, not allowed.
require(to != msg.sender, "Found loop in delegation.");
}
sender.voted = true;
sender.delegate = to;
Voter storage delegate_ = voters[to];
if (delegate_.voted) {
// If the delegate already voted,
// directly add to the number of votes
proposals[delegate_.vote].voteCount += sender.weight;
} else {
// If the delegate did not vote yet,
// add to her weight.
delegate_.weight += sender.weight;
}
}
/**
* @dev Give your vote (including votes delegated to you) to proposal 'proposals[proposal].name'.
* @param proposal index of proposal in the proposals array
*/
function vote(uint proposal) public {
Voter storage sender = voters[msg.sender];
require(sender.weight != 0, "Has no right to vote");
require(!sender.voted, "Already voted.");
sender.voted = true;
sender.vote = proposal;
// If 'proposal' is out of the range of the array,
// this will throw automatically and revert all
// changes.
proposals[proposal].voteCount += sender.weight;
}
/**
* @dev Computes the winning proposal taking all previous votes into account.
* @return winningProposal_ index of winning proposal in the proposals array
*/
function winningProposal() public view
returns (uint winningProposal_)
{
uint winningVoteCount = 0;
for (uint p = 0; p < proposals.length; p++) {
if (proposals[p].voteCount > winningVoteCount) {
winningVoteCount = proposals[p].voteCount;
winningProposal_ = p;
}
}
}
/**
* @dev Calls winningProposal() function to get the index of the winner contained in the proposals array and then
* @return winnerName_ the name of the winner
*/
function winnerName() public view
returns (bytes32 winnerName_)
{
winnerName_ = proposals[winningProposal()].name;
}
}
`

@ -19,7 +19,7 @@ const profile = {
icon: 'assets/img/fileManager.webp', icon: 'assets/img/fileManager.webp',
permission: true, permission: true,
version: packageJson.version, version: packageJson.version,
methods: ['closeAllFiles', 'closeFile', 'file', 'exists', 'open', 'writeFile', 'readFile', 'copyFile', 'copyDir', 'rename', 'mkdir', 'readdir', 'remove', 'getCurrentFile', 'getFile', 'getFolder', 'setFile', 'switchFile', 'refresh', 'getProviderOf', 'getProviderByName', 'getPathFromUrl', 'getUrlFromPath', 'saveCurrentFile', 'setBatchFiles'], methods: ['closeAllFiles', 'closeFile', 'file', 'exists', 'open', 'writeFile', 'readFile', 'copyFile', 'copyDir', 'rename', 'mkdir', 'readdir', 'dirList', 'fileList', 'remove', 'getCurrentFile', 'getFile', 'getFolder', 'setFile', 'switchFile', 'refresh', 'getProviderOf', 'getProviderByName', 'getPathFromUrl', 'getUrlFromPath', 'saveCurrentFile', 'setBatchFiles'],
kind: 'file-system' kind: 'file-system'
} }
const errorMsg = { const errorMsg = {
@ -704,7 +704,14 @@ class FileManager extends Plugin {
return collectList(path) return collectList(path)
} }
isRemixDActive() { async fileList (dirPath) {
const paths: any = await this.readdir(dirPath)
for( const path in paths)
if(paths[path].isDirectory) delete paths[path]
return Object.keys(paths)
}
isRemixDActive () {
return this.appManager.isActive('remixd') return this.appManager.isActive('remixd')
} }

@ -4,7 +4,7 @@ import { CompilerAbstract } from '@remix-project/remix-solidity'
const profile = { const profile = {
name: 'compilerArtefacts', name: 'compilerArtefacts',
methods: ['get', 'addResolvedContract', 'getCompilerAbstract', 'getAllContractDatas', 'getLastCompilationResult'], methods: ['get', 'addResolvedContract', 'getCompilerAbstract', 'getAllContractDatas', 'getLastCompilationResult', 'getArtefactsByContractName'],
events: [], events: [],
version: '0.0.1' version: '0.0.1'
} }
@ -72,6 +72,53 @@ export class CompilerArtefacts extends Plugin {
return contractsData return contractsData
} }
async getArtefactsFromFE (path, contractName) {
const dirList = await this.call('fileManager', 'dirList', path)
if(dirList && dirList.length) {
if(dirList.includes(path + '/artifacts')) {
const fileList = await this.call('fileManager', 'fileList', path + '/artifacts')
const artefactsFilePaths = fileList.filter(filePath => {
const filenameArr = filePath.split('/')
const filename = filenameArr[filenameArr.length - 1]
if (filename === `${contractName}.json` || filename === `${contractName}_metadata.json`) return true
})
if (artefactsFilePaths && artefactsFilePaths.length) {
const content = await this.call('fileManager', 'readFile', artefactsFilePaths[1])
const artifacts = JSON.parse(content)
return { abi: artifacts.abi, bytecode: artifacts.data.bytecode.object }
} else {
for (const dirPath of dirList) {
const result = await this.getArtefactsFromFE (dirPath, contractName)
if (result) return result
}
}
} else {
for (const dirPath of dirList) {
const result = await this.getArtefactsFromFE (dirPath, contractName)
if (result) return result
}
}
} else return
}
async getArtefactsByContractName (contractName) {
const contractsDataByFilename = this.getAllContractDatas()
const contractsData = Object.values(contractsDataByFilename)
if (contractsData && contractsData.length) {
const index = contractsData.findIndex((contractsObj) => Object.keys(contractsObj).includes(contractName))
if (index !== -1) return { abi: contractsData[index][contractName].abi, bytecode: contractsData[index][contractName].evm.bytecode.object }
else {
const result = await this.getArtefactsFromFE ('contracts', contractName)
if (result) return result
else throw new Error(`Could not find artifacts for ${contractName}. Compile contract to generate artifacts.`)
}
} else {
const result = await this.getArtefactsFromFE ('contracts', contractName)
if (result) return result
else throw new Error(`Could not find artifacts for ${contractName}. Compile contract to generate artifacts.`)
}
}
getCompilerAbstract (file) { getCompilerAbstract (file) {
return this.compilersArtefactsPerFile[file] return this.compilersArtefactsPerFile[file]
} }

@ -256,6 +256,17 @@ export const runScript = async (path: string) => {
}) })
} }
export const runScriptWithMocha = async (path: string) => {
const provider = plugin.fileManager.currentFileProvider()
provider.get(path, (error, content: string) => {
if (error) {
return dispatch(displayPopUp(error))
}
if (content) content = content + '\n' + 'mocha.run()'
plugin.call('scriptRunner', 'execute', content)
})
}
export const emitContextMenuEvent = async (cmd: customAction) => { export const emitContextMenuEvent = async (cmd: customAction) => {
await plugin.call(cmd.id, cmd.name, cmd) await plugin.call(cmd.id, cmd.name, cmd)
} }

@ -12,7 +12,7 @@ declare global {
const _paq = window._paq = window._paq || [] //eslint-disable-line const _paq = window._paq = window._paq || [] //eslint-disable-line
export const FileExplorerContextMenu = (props: FileExplorerContextMenuProps) => { export const FileExplorerContextMenu = (props: FileExplorerContextMenuProps) => {
const { actions, createNewFile, createNewFolder, deletePath, renamePath, hideContextMenu, pushChangesToGist, publishFileToGist, publishFolderToGist, copy, paste, runScript, emit, pageX, pageY, path, type, focus, ...otherProps } = props const { actions, createNewFile, createNewFolder, deletePath, renamePath, hideContextMenu, pushChangesToGist, publishFileToGist, publishFolderToGist, copy, paste, runScript, runScriptWithMocha, emit, pageX, pageY, path, type, focus, ...otherProps } = props
const contextMenuRef = useRef(null) const contextMenuRef = useRef(null)
useEffect(() => { useEffect(() => {
contextMenuRef.current.focus() contextMenuRef.current.focus()
@ -98,6 +98,10 @@ export const FileExplorerContextMenu = (props: FileExplorerContextMenuProps) =>
_paq.push(['trackEvent', 'fileExplorer', 'runScript']) _paq.push(['trackEvent', 'fileExplorer', 'runScript'])
runScript(path) runScript(path)
break break
case 'Run with Mocha':
_paq.push(['trackEvent', 'fileExplorer', 'runScriptWithMocha'])
runScriptWithMocha(path)
break
case 'Copy': case 'Copy':
copy(path, type) copy(path, type)
break break

@ -221,6 +221,14 @@ export const FileExplorer = (props: FileExplorerProps) => {
} }
} }
const runScriptWithMocha = async (path: string) => {
try {
props.dispatchRunScriptWithMocha(path)
} catch (error) {
props.toast('Run script with Mocha failed')
}
}
const emitContextMenuEvent = (cmd: customAction) => { const emitContextMenuEvent = (cmd: customAction) => {
try { try {
props.dispatchEmitContextMenuEvent(cmd) props.dispatchEmitContextMenuEvent(cmd)
@ -454,6 +462,7 @@ export const FileExplorer = (props: FileExplorerProps) => {
deletePath={deletePath} deletePath={deletePath}
renamePath={editModeOn} renamePath={editModeOn}
runScript={runScript} runScript={runScript}
runScriptWithMocha={runScriptWithMocha}
copy={handleCopyClick} copy={handleCopyClick}
paste={handlePasteClick} paste={handlePasteClick}
emit={emitContextMenuEvent} emit={emitContextMenuEvent}

@ -25,6 +25,7 @@ export const FileSystemContext = createContext<{
dispatchCopyFile: (src: string, dest: string) => Promise<void>, dispatchCopyFile: (src: string, dest: string) => Promise<void>,
dispatchCopyFolder: (src: string, dest: string) => Promise<void>, dispatchCopyFolder: (src: string, dest: string) => Promise<void>,
dispatchRunScript: (path: string) => Promise<void>, dispatchRunScript: (path: string) => Promise<void>,
dispatchRunScriptWithMocha: (path: string) => Promise<void>,
dispatchEmitContextMenuEvent: (cmd: customAction) => Promise<void>, dispatchEmitContextMenuEvent: (cmd: customAction) => Promise<void>,
dispatchHandleClickFile: (path: string, type: 'file' | 'folder' | 'gist') => Promise<void> dispatchHandleClickFile: (path: string, type: 'file' | 'folder' | 'gist') => Promise<void>
dispatchHandleExpandPath: (paths: string[]) => Promise<void> dispatchHandleExpandPath: (paths: string[]) => Promise<void>

@ -5,7 +5,7 @@ import { Toaster } from '@remix-ui/toaster' // eslint-disable-line
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
import { FileSystemContext } from '../contexts' import { FileSystemContext } from '../contexts'
import { browserReducer, browserInitialState } from '../reducers/workspace' import { browserReducer, browserInitialState } from '../reducers/workspace'
import { initWorkspace, fetchDirectory, removeInputField, deleteWorkspace, clearPopUp, publishToGist, createNewFile, setFocusElement, createNewFolder, deletePath, renamePath, copyFile, copyFolder, runScript, emitContextMenuEvent, handleClickFile, handleExpandPath, addInputField, createWorkspace, fetchWorkspaceDirectory, renameWorkspace, switchToWorkspace, uploadFile } from '../actions' import { initWorkspace, fetchDirectory, removeInputField, deleteWorkspace, clearPopUp, publishToGist, createNewFile, setFocusElement, createNewFolder, deletePath, renamePath, copyFile, copyFolder, runScript, runScriptWithMocha, emitContextMenuEvent, handleClickFile, handleExpandPath, addInputField, createWorkspace, fetchWorkspaceDirectory, renameWorkspace, switchToWorkspace, uploadFile } from '../actions'
import { Modal, WorkspaceProps } from '../types' import { Modal, WorkspaceProps } from '../types'
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
import { Workspace } from '../remix-ui-workspace' import { Workspace } from '../remix-ui-workspace'
@ -103,6 +103,10 @@ export const FileSystemProvider = (props: WorkspaceProps) => {
await runScript(path) await runScript(path)
} }
const dispatchRunScriptWithMocha = async (path: string) => {
await runScriptWithMocha(path)
}
const dispatchEmitContextMenuEvent = async (cmd: customAction) => { const dispatchEmitContextMenuEvent = async (cmd: customAction) => {
await emitContextMenuEvent(cmd) await emitContextMenuEvent(cmd)
} }
@ -212,6 +216,7 @@ export const FileSystemProvider = (props: WorkspaceProps) => {
dispatchCopyFile, dispatchCopyFile,
dispatchCopyFolder, dispatchCopyFolder,
dispatchRunScript, dispatchRunScript,
dispatchRunScriptWithMocha,
dispatchEmitContextMenuEvent, dispatchEmitContextMenuEvent,
dispatchHandleClickFile, dispatchHandleClickFile,
dispatchHandleExpandPath dispatchHandleExpandPath

@ -198,6 +198,7 @@ export function Workspace () {
dispatchCopyFolder={global.dispatchCopyFolder} dispatchCopyFolder={global.dispatchCopyFolder}
dispatchPublishToGist={global.dispatchPublishToGist} dispatchPublishToGist={global.dispatchPublishToGist}
dispatchRunScript={global.dispatchRunScript} dispatchRunScript={global.dispatchRunScript}
dispatchRunScriptWithMocha={global.dispatchRunScriptWithMocha}
dispatchEmitContextMenuEvent={global.dispatchEmitContextMenuEvent} dispatchEmitContextMenuEvent={global.dispatchEmitContextMenuEvent}
dispatchHandleClickFile={global.dispatchHandleClickFile} dispatchHandleClickFile={global.dispatchHandleClickFile}
dispatchSetFocusElement={global.dispatchSetFocusElement} dispatchSetFocusElement={global.dispatchSetFocusElement}
@ -233,6 +234,7 @@ export function Workspace () {
dispatchCopyFolder={global.dispatchCopyFolder} dispatchCopyFolder={global.dispatchCopyFolder}
dispatchPublishToGist={global.dispatchPublishToGist} dispatchPublishToGist={global.dispatchPublishToGist}
dispatchRunScript={global.dispatchRunScript} dispatchRunScript={global.dispatchRunScript}
dispatchRunScriptWithMocha={global.dispatchRunScriptWithMocha}
dispatchEmitContextMenuEvent={global.dispatchEmitContextMenuEvent} dispatchEmitContextMenuEvent={global.dispatchEmitContextMenuEvent}
dispatchHandleClickFile={global.dispatchHandleClickFile} dispatchHandleClickFile={global.dispatchHandleClickFile}
dispatchSetFocusElement={global.dispatchSetFocusElement} dispatchSetFocusElement={global.dispatchSetFocusElement}

@ -78,6 +78,7 @@ export interface FileExplorerProps {
dispatchCopyFile: (src: string, dest: string) => Promise<void>, dispatchCopyFile: (src: string, dest: string) => Promise<void>,
dispatchCopyFolder: (src: string, dest: string) => Promise<void>, dispatchCopyFolder: (src: string, dest: string) => Promise<void>,
dispatchRunScript: (path: string) => Promise<void>, dispatchRunScript: (path: string) => Promise<void>,
dispatchRunScriptWithMocha: (path: string) => Promise<void>,
dispatchPublishToGist: (path?: string, type?: string) => Promise<void>, dispatchPublishToGist: (path?: string, type?: string) => Promise<void>,
dispatchEmitContextMenuEvent: (cmd: customAction) => Promise<void>, dispatchEmitContextMenuEvent: (cmd: customAction) => Promise<void>,
dispatchHandleClickFile: (path: string, type: 'file' | 'folder' | 'gist') => Promise<void>, dispatchHandleClickFile: (path: string, type: 'file' | 'folder' | 'gist') => Promise<void>,
@ -108,6 +109,7 @@ export interface FileExplorerContextMenuProps {
publishFolderToGist?: (path?: string, type?: string) => void, publishFolderToGist?: (path?: string, type?: string) => void,
publishFileToGist?: (path?: string, type?: string) => void, publishFileToGist?: (path?: string, type?: string) => void,
runScript?: (path: string) => void, runScript?: (path: string) => void,
runScriptWithMocha?: (path: string) => void,
emit?: (cmd: customAction) => void, emit?: (cmd: customAction) => void,
pageX: number, pageX: number,
pageY: number, pageY: number,

@ -30,6 +30,12 @@ export const contextMenuActions: MenuItems = [{
extension: ['.js'], extension: ['.js'],
multiselect: false, multiselect: false,
label: '' label: ''
}, {
id: 'runWithMocha',
name: 'Run with Mocha',
extension: ['.js'],
multiselect: false,
label: ''
}, { }, {
id: 'pushChangesToGist', id: 'pushChangesToGist',
name: 'Push changes to gist', name: 'Push changes to gist',

@ -96,6 +96,7 @@
"nightwatch_local_verticalIconscontextmenu": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/verticalIconsPanel.test.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.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", "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",
"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_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_stress_editor": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/stress.editor.js --env=chromeDesktop",
"onchange": "onchange apps/remix-ide/build/app.js -- npm-run-all lint", "onchange": "onchange apps/remix-ide/build/app.js -- npm-run-all lint",
"remixd": "nx build remixd && chmod +x dist/libs/remixd/src/bin/remixd.js && dist/libs/remixd/src/bin/remixd.js -s ./apps/remix-ide/contracts --remix-ide http://127.0.0.1:8080", "remixd": "nx build remixd && chmod +x dist/libs/remixd/src/bin/remixd.js && dist/libs/remixd/src/bin/remixd.js -s ./apps/remix-ide/contracts --remix-ide http://127.0.0.1:8080",
"selenium": "selenium-standalone start", "selenium": "selenium-standalone start",

Loading…
Cancel
Save