diff --git a/apps/remix-ide-e2e/src/commands/currentSelectedFileIs.ts b/apps/remix-ide-e2e/src/commands/currentSelectedFileIs.ts
new file mode 100644
index 0000000000..81164cb97a
--- /dev/null
+++ b/apps/remix-ide-e2e/src/commands/currentSelectedFileIs.ts
@@ -0,0 +1,15 @@
+import { NightwatchBrowser } from 'nightwatch'
+import EventEmitter from 'events'
+
+class CurrentSelectedFileIs extends EventEmitter {
+ command (this: NightwatchBrowser, value: string): NightwatchBrowser {
+ this.api
+ .waitForElementContainsText('*[data-id="tabs-component"] *[data-id="tab-active"]', value)
+ .perform(() => {
+ this.emit('complete')
+ })
+ return this
+ }
+}
+
+module.exports = CurrentSelectedFileIs
diff --git a/apps/remix-ide-e2e/src/tests/editor.test.ts b/apps/remix-ide-e2e/src/tests/editor.test.ts
index 7b714a432b..59c3211e79 100644
--- a/apps/remix-ide-e2e/src/tests/editor.test.ts
+++ b/apps/remix-ide-e2e/src/tests/editor.test.ts
@@ -147,6 +147,7 @@ module.exports = {
.waitForElementContainsText('.contextview .type', 'uint256')
.waitForElementContainsText('.contextview .name', 'number')
.click('.contextview [data-action="previous"]') // declaration
+ .pause(1000)
.execute(() => {
return (document.getElementById('editorView') as any).getCursorPosition()
}, [], (result) => {
@@ -154,6 +155,7 @@ module.exports = {
browser.assert.equal(result.value, '180')
})
.click('.contextview [data-action="next"]') // back to the initial state
+ .pause(1000)
.execute(() => {
return (document.getElementById('editorView') as any).getCursorPosition()
}, [], (result) => {
@@ -161,6 +163,7 @@ module.exports = {
browser.assert.equal(result.value, '323')
})
.click('.contextview [data-action="next"]') // next reference
+ .pause(1000)
.execute(() => {
return (document.getElementById('editorView') as any).getCursorPosition()
}, [], (result) => {
@@ -168,12 +171,74 @@ module.exports = {
browser.assert.equal(result.value, '489')
})
.click('.contextview [data-action="gotoref"]') // back to the declaration
+ .pause(1000)
.execute(() => {
return (document.getElementById('editorView') as any).getCursorPosition()
}, [], (result) => {
console.log('result', result)
browser.assert.equal(result.value, '180')
})
+ },
+
+ 'Should display the context view, loop over "Owner" by switching file #group2': function (browser: NightwatchBrowser) {
+ browser
+ .clickLaunchIcon('solidity')
+ .click('[for="autoCompile"]') // disable auto compile
+ .openFile('contracts')
+ .openFile('contracts/3_Ballot.sol')
+ .waitForElementVisible('#editorView')
+ .setEditorValue(BallotWithARefToOwner)
+ .clickLaunchIcon('solidity')
+ .click('*[data-id="compilerContainerCompileBtn"]') // compile
+ .pause(2000)
+ .execute(() => {
+ (document.getElementById('editorView') as any).gotoLine(14, 6)
+ }, [], () => {})
+ .waitForElementVisible('.contextview')
+ .waitForElementContainsText('.contextview .type', 'ContractDefinition')
+ .waitForElementContainsText('.contextview .name', 'Owner')
+ .click('.contextview [data-action="next"]')
+ .pause(1000)
+ .execute(() => {
+ return (document.getElementById('editorView') as any).getCursorPosition()
+ }, [], (result) => {
+ console.log('result', result)
+ browser.assert.equal(result.value, '1061')
+ })
+ .click('.contextview [data-action="next"]')
+ .pause(1000)
+ .execute(() => {
+ return (document.getElementById('editorView') as any).getCursorPosition()
+ }, [], (result) => {
+ console.log('result', result)
+ browser.assert.equal(result.value, '122')
+ })
+ .currentSelectedFileIs('2_Owner.sol') // make sure the current file has been properly changed
+ .click('.contextview [data-action="next"]')
+ .pause(1000)
+ .execute(() => {
+ return (document.getElementById('editorView') as any).getCursorPosition()
+ }, [], (result) => {
+ console.log('result', result)
+ browser.assert.equal(result.value, '211')
+ })
+ .click('.contextview [data-action="next"]')
+ .currentSelectedFileIs('3_Ballot.sol')
+ .pause(1000)
+ .execute(() => {
+ return (document.getElementById('editorView') as any).getCursorPosition()
+ }, [], (result) => {
+ console.log('result', result)
+ browser.assert.equal(result.value, '1061')
+ })
+ .click('.contextview [data-action="gotoref"]') // go to the declaration
+ .pause(1000)
+ .execute(() => {
+ return (document.getElementById('editorView') as any).getCursorPosition()
+ }, [], (result) => {
+ console.log('result', result)
+ browser.assert.equal(result.value, '122')
+ })
.end()
}
}
@@ -281,3 +346,149 @@ contract Storage {
return number;
}
}`
+
+const BallotWithARefToOwner = `
+
+
+// SPDX-License-Identifier: GPL-3.0
+
+pragma solidity >=0.7.0 <0.9.0;
+
+import "./2_Owner.sol";
+
+/**
+ * @title Ballot
+ * @dev Implements voting process along with vote delegation
+ */
+contract Ballot {
+ Owner c;
+ 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;
+
+ /**
+ * @dev Create a new ballot to choose one of 'proposalNames'.
+ * @param proposalNames names of proposals
+ */
+ constructor(bytes32[] memory proposalNames) {
+ c = new Owner();
+ 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;
+ }
+}
+`
diff --git a/apps/remix-ide-e2e/src/tests/plugin_api.ts b/apps/remix-ide-e2e/src/tests/plugin_api.ts
index d3df343415..fc09a9c8f1 100644
--- a/apps/remix-ide-e2e/src/tests/plugin_api.ts
+++ b/apps/remix-ide-e2e/src/tests/plugin_api.ts
@@ -149,6 +149,18 @@ module.exports = {
await clickAndCheckLog(browser, 'udapp:getAccounts', '0x5B38Da6a701c568545dCfcB03FcB875f56beddC4', null, null)
},
+ 'Should select another provider #group1': async function (browser: NightwatchBrowser) {
+ await clickAndCheckLog(browser, 'udapp:setEnvironmentMode', null, null, { context: 'vm', fork: 'berlin' })
+ await browser
+ .frameParent()
+ .useCss()
+ .clickLaunchIcon('udapp')
+ .waitForElementContainsText('#selectExEnvOptions option:checked', 'JavaScript VM (Berlin)')
+ .clickLaunchIcon('localPlugin')
+ .useXpath()
+ // @ts-ignore
+ .frame(0)
+ },
// context menu item
'Should create context menu item #group1': async function (browser: NightwatchBrowser) {
diff --git a/apps/remix-ide-e2e/src/tests/url.spec.ts b/apps/remix-ide-e2e/src/tests/url.spec.ts
index 9d7e8e323e..13227238f7 100644
--- a/apps/remix-ide-e2e/src/tests/url.spec.ts
+++ b/apps/remix-ide-e2e/src/tests/url.spec.ts
@@ -77,12 +77,13 @@ module.exports = {
'Should load using URL compiler params': function (browser: NightwatchBrowser) {
browser
.pause(5000)
- .url('http://127.0.0.1:8080/#optimize=true&runs=300&autoCompile=true&evmVersion=istanbul&version=soljson-v0.7.4+commit.3f05b770.js')
+ .url('http://127.0.0.1:8080/#optimize=true&runs=300&autoCompile=true&evmVersion=istanbul&version=soljson-v0.7.4+commit.3f05b770.js&language=Yul')
.refresh()
.pause(5000)
.clickLaunchIcon('solidity')
.assert.containsText('#versionSelector option[data-id="selected"]', '0.7.4+commit.3f05b770')
.assert.containsText('#evmVersionSelector option[data-id="selected"]', 'istanbul')
+ .assert.containsText('#compilierLanguageSelector option[data-id="selected"]', 'Yul')
.verify.elementPresent('#optimize:checked')
.verify.elementPresent('#autoCompile:checked')
.verify.attributeEquals('#runs', 'value', '300')
diff --git a/apps/remix-ide-e2e/src/types/index.d.ts b/apps/remix-ide-e2e/src/types/index.d.ts
index f9a7c35d63..9abe5c2174 100644
--- a/apps/remix-ide-e2e/src/types/index.d.ts
+++ b/apps/remix-ide-e2e/src/types/index.d.ts
@@ -61,6 +61,7 @@ declare module 'nightwatch' {
acceptAndRemember (this: NightwatchBrowser, remember: boolean, accept: boolean): NightwatchBrowser
clearConsole (this: NightwatchBrowser): NightwatchBrowser
clearTransactions (this: NightwatchBrowser): NightwatchBrowser
+ currentSelectedFileIs (name: string): NightwatchBrowser
}
export interface NightwatchBrowser {
diff --git a/apps/remix-ide/src/app/editor/editor.js b/apps/remix-ide/src/app/editor/editor.js
index 013b1b9853..1bc0a00b82 100644
--- a/apps/remix-ide/src/app/editor/editor.js
+++ b/apps/remix-ide/src/app/editor/editor.js
@@ -438,7 +438,7 @@ class Editor extends Plugin {
if (!filePath) return
filePath = await this.call('fileManager', 'getPathFromUrl', filePath)
filePath = filePath.file
- if (!this.sessions[filePath]) throw new Error('file not found' + filePath)
+ if (!this.sessions[filePath]) return
const path = filePath || this.currentFile
const { from } = this.currentRequest
diff --git a/apps/remix-ide/src/app/files/fileManager.ts b/apps/remix-ide/src/app/files/fileManager.ts
index 1c4a3e61bf..a3828d68b3 100644
--- a/apps/remix-ide/src/app/files/fileManager.ts
+++ b/apps/remix-ide/src/app/files/fileManager.ts
@@ -470,7 +470,7 @@ class FileManager extends Plugin {
}
currentFile () {
- return this._deps.config.get('currentFile')
+ return this.editor.current()
}
async closeAllFiles () {
diff --git a/apps/remix-ide/src/app/plugins/remixd-handle.tsx b/apps/remix-ide/src/app/plugins/remixd-handle.tsx
index 68367d1df3..9ff3d8339f 100644
--- a/apps/remix-ide/src/app/plugins/remixd-handle.tsx
+++ b/apps/remix-ide/src/app/plugins/remixd-handle.tsx
@@ -142,11 +142,11 @@ function remixdDialog () {
If you are just looking for the remixd command, here it is:
-
${commandText}
+
{commandText}
- When connected, a session will be started between ${window.location.origin} and your local file system at ws://127.0.0.1:65520.
+ When connected, a session will be started between {window.location.origin} and your local file system at ws://127.0.0.1:65520.
The shared folder will be in the "File Explorers" workspace named "localhost".
Read more about other Remixd ports usage
@@ -155,7 +155,7 @@ function remixdDialog () {
- Before using, make sure remixd version is latest i.e. ${remixdVersion}
+ Before using, make sure remixd version is latest i.e. v{remixdVersion} Read here how to update it