diff --git a/package.json b/package.json index afacc50006..aac26fff09 100644 --- a/package.json +++ b/package.json @@ -185,6 +185,7 @@ "nightwatch_local_fileExplorer": "nightwatch ./test-browser/tests/fileExplorer.test.js --config nightwatch.js --env chrome ", "nightwatch_local_debugger": "nightwatch ./test-browser/tests/debugger.test.js --config nightwatch.js --env chrome ", "nightwatch_local_editor": "nightwatch ./test-browser/tests/editor.test.js --config nightwatch.js --env chrome ", + "nightwatch_local_compiler": "nightwatch ./test-browser/tests/compiler_api.test.js --config nightwatch.js --env chrome ", "nightwatch_local_runAndDeploy": "nightwatch ./test-browser/tests/runAndDeploy.js --config nightwatch.js --env chrome-runAndDeploy ", "onchange": "onchange build/app.js -- npm-run-all lint", "prepublish": "mkdirp build; npm-run-all -ls downloadsolc_root build", diff --git a/src/app/compiler/compiler-helpers.js b/src/app/compiler/compiler-helpers.js index 820e5c298f..c4ca22c001 100644 --- a/src/app/compiler/compiler-helpers.js +++ b/src/app/compiler/compiler-helpers.js @@ -9,8 +9,10 @@ export const compile = async (compilationTargets, settings) => { const compiler = new Compiler(() => {}) compiler.set('evmVersion', settings.evmVersion) compiler.set('optimize', settings.optimize) + compiler.set('language', settings.language) compiler.loadVersion(canUseWorker(settings.version), urlFromVersion(settings.version)) compiler.event.register('compilationFinished', (success, compilationData, source) => { + console.log(success, compilationData) if (!success) return reject(compilationData) resolve(new CompilerAbstract(settings.version, compilationData, source)) }) diff --git a/src/app/compiler/compiler-sourceVerifier-fetchAndCompile.js b/src/app/compiler/compiler-sourceVerifier-fetchAndCompile.js index 5df4e66390..d608a8d061 100644 --- a/src/app/compiler/compiler-sourceVerifier-fetchAndCompile.js +++ b/src/app/compiler/compiler-sourceVerifier-fetchAndCompile.js @@ -105,7 +105,7 @@ export default class FetchAndCompile extends Plugin { // compile const settings = { version: data.metadata.compiler.version, - languageName: data.metadata.language, + language: data.metadata.language, evmVersion: data.metadata.settings.evmVersion, optimize: data.metadata.settings.optimizer.enabled } diff --git a/src/app/tabs/compile-tab.js b/src/app/tabs/compile-tab.js index 52de47dd7e..e027e1e5ce 100644 --- a/src/app/tabs/compile-tab.js +++ b/src/app/tabs/compile-tab.js @@ -20,6 +20,7 @@ const CompilerContainer = require('./compileTab/compilerContainer.js') import { ViewPlugin } from '@remixproject/engine' import * as packageJson from '../../../package.json' import publishToStorage from '../../publishToStorage' +import { compile } from '../compiler/compiler-helpers' const profile = { name: 'solidity', @@ -31,7 +32,7 @@ const profile = { location: 'sidePanel', documentation: 'https://remix-ide.readthedocs.io/en/latest/solidity_editor.html', version: packageJson.version, - methods: ['getCompilationResult', 'compile'] + methods: ['getCompilationResult', 'compile', 'compileWithParameters', 'setCompilerConfig'] } @@ -60,7 +61,8 @@ class CompileTab extends ViewPlugin { this.data = { contractsDetails: {}, - eventHandlers: {} + eventHandlers: {}, + loading: false } } @@ -88,11 +90,13 @@ class CompileTab extends ViewPlugin { this.editor.event.register('contentChanged', this.data.eventHandlers.onContentChanged) this.data.eventHandlers.onLoadingCompiler = () => { + this.data.loading = true this.emit('statusChanged', {key: 'loading', title: 'loading compiler...', type: 'info'}) } this.compiler.event.register('loadingCompiler', this.data.eventHandlers.onLoadingCompiler) this.data.eventHandlers.onCompilerLoaded = () => { + this.data.loading = false this.emit('statusChanged', {key: 'none'}) } this.compiler.event.register('compilerLoaded', this.data.eventHandlers.onCompilerLoaded) @@ -198,11 +202,28 @@ class CompileTab extends ViewPlugin { return this.compileTabLogic.compiler.state.lastCompilationResult } - // This function is used by remix-plugin + /** + * compile using @arg fileName. + * The module UI will be updated accordingly to the new compilation result. + * This function is used by remix-plugin compiler API. + * @param {string} fileName to compile + */ compile (fileName) { + addTooltip(yo`
${this.currentRequest.from} is requiring to compile ${fileName}
`) return this.compileTabLogic.compileFile(fileName) } + /** + * compile using @arg compilationTargets and @arg settings + * The module UI will *not* be updated, the compilation result is returned + * This function is used by remix-plugin compiler API. + * @param {object} map of source files. + * @param {object} settings {evmVersion, optimize, compilerUrl, version, language} + */ + async compileWithParameters (compilationTargets, settings) { + return await compile(compilationTargets, settings) + } + // This function is used for passing the compiler remix-tests getCurrentVersion () { return this.compilerContainer.data.selectedVersion @@ -217,6 +238,31 @@ class CompileTab extends ViewPlugin { } } + /** + * set the compiler configuration + * This function is used by remix-plugin compiler API. + * @param {object} settings {evmVersion, optimize, compilerUrl, version, language} + */ + setCompilerConfig (settings) { + return new Promise((resolve, reject) => { + addTooltip(yo`
${this.currentRequest.from} is updating the Solidity compiler configuration.
${JSON.stringify(settings, null, '\t')}
`) + this.compilerContainer.setLanguage(settings.language) + this.compilerContainer.setEvmVersion(settings.evmVersion) + this.compilerContainer.setOptimize(settings.optimize) + this.compilerContainer.setVersion(settings.version) + // @todo(#2875) should use loading compiler return value to check whether the compiler is loaded instead of "setInterval" + let timeout = 0 + const id = setInterval(() => { + timeout++ + console.log(this.data.loading) + if (!this.data.loading || timeout > 10) { + resolve() + clearInterval(id) + } + }, 200) + }) + } + /********* * SUB-COMPONENTS */ @@ -261,7 +307,7 @@ class CompileTab extends ViewPlugin { Publish on Ipfs - diff --git a/src/app/tabs/compileTab/compilerContainer.js b/src/app/tabs/compileTab/compilerContainer.js index b91f933bf3..9c3fbfb50a 100644 --- a/src/app/tabs/compileTab/compilerContainer.js +++ b/src/app/tabs/compileTab/compilerContainer.js @@ -318,17 +318,21 @@ class CompilerContainer { this.compileIfAutoCompileOn() } + /* + The following functions are handlers for internal events. + */ + onchangeOptimize () { this.compileTabLogic.setOptimize(!!this._view.optimize.checked) this.compileIfAutoCompileOn() } - onchangeLanguage (event) { - this.compileTabLogic.setLanguage(event.target.value) + onchangeLanguage () { + this.compileTabLogic.setLanguage(this._view.languageSelector.value) this.compileIfAutoCompileOn() } - onchangeEvmVersion (_) { + onchangeEvmVersion () { let s = this._view.evmVersionSelector let v = s.value if (v === 'default') { @@ -338,12 +342,37 @@ class CompilerContainer { this.compileIfAutoCompileOn() } - onchangeLoadVersion (event) { + onchangeLoadVersion () { this.data.selectedVersion = this._view.versionSelector.value this._updateVersionSelector() this._updateLanguageSelector() } + /* + The following functions map with the above event handlers. + They are an external API for modifying the compiler configuration. + */ + + setOptimize (enabled) { + this._view.optimize.checked = enabled + this.onchangeOptimize() + } + + setLanguage (lang) { + this._view.languageSelector.value = lang + this.onchangeLanguage() + } + + setEvmVersion (version) { + this._view.evmVersionSelector.value = version || 'default' + this.onchangeEvmVersion() + } + + setVersion (version) { + this._view.versionSelector.value = `soljson-v${version}.js` + this.onchangeLoadVersion() + } + _shouldBeAdded (version) { return !version.includes('nightly') || (version.includes('nightly') && this._view.includeNightlies.checked) diff --git a/test-browser/commands/verifyContracts.js b/test-browser/commands/verifyContracts.js index 14f006504e..3275cfb65a 100644 --- a/test-browser/commands/verifyContracts.js +++ b/test-browser/commands/verifyContracts.js @@ -1,7 +1,7 @@ const EventEmitter = require('events') class VerifyContracts extends EventEmitter { - command (compiledContractNames, opts = { wait: 1000 }) { + command (compiledContractNames, opts = { wait: 1000, version: null }) { this.api.perform((done) => { verifyContracts(this.api, compiledContractNames, opts, () => { done() @@ -17,6 +17,17 @@ function getCompiledContracts (browser, opts, callback) { .clickLaunchIcon('solidity') .pause(opts.wait) .waitForElementPresent('*[data-id="compiledContracts"] option') + .perform((done) => { + if (opts.version) { + browser + .click('*[data-id="compilation-details"]') + .waitForElementPresent('*[data-id="treeViewLicompiler"]') + .click('*[data-id="treeViewLicompiler"]') + .waitForElementPresent('*[data-id="treeViewTogglecompiler/version"]') + .assert.containsText('*[data-id="treeViewLicompiler/version"]', `version: ${opts.version}`) + .perform(done) + } else done() + }) .execute(function () { var contracts = document.querySelectorAll('*[data-id="compiledContracts"] option') if (!contracts) { diff --git a/test-browser/tests/compiler_api.test.js b/test-browser/tests/compiler_api.test.js new file mode 100644 index 0000000000..2c267b8177 --- /dev/null +++ b/test-browser/tests/compiler_api.test.js @@ -0,0 +1,101 @@ +'use strict' +var examples = require('../../src/app/editor/example-contracts') +var init = require('../helpers/init') +var sauce = require('./sauce') + +var sources = [ + {'browser/Untitled.sol': {content: examples.ballot.content}} +] + +module.exports = { + before: function (browser, done) { + init(browser, done) + }, + '@sources': function () { + return sources + }, + + 'Should compile using "compileWithParamaters" API': function (browser) { + browser + .addFile('test_jsCompile.js', { content: jsCompile }) + .executeScript('remix.exeCurrent()') + .pause(5000) + .journalChildIncludes('"languageversion": "0.6.8+commit.0bbfe453"') + }, + + 'Should update the compiler configuration with "setCompilerConfig" API': function (browser) { + browser + .addFile('test_updateConfiguration.js', { content: updateConfiguration }) + .executeScript('remix.exeCurrent()') + .pause(5000) + .addFile('test_updateConfiguration.sol', { content: simpleContract }) + .verifyContracts(['StorageTestUpdateConfiguration'], {wait: 5000, version: '0.6.8+commit.0bbfe453'}) + .end() + }, + + tearDown: sauce +} + +const simpleContract = `pragma solidity >=0.4.22 <0.7.0; + +/** +* @title Storage +* @dev Store & retreive value in a variable +*/ +contract StorageTestUpdateConfiguration { + + uint256 number; + + /** + * @dev Store value in variable + * @param num value to store + */ + function store(uint256 num) public { + number = num; + } + + /** + * @dev Return value + * @return value of 'number' + */ + function retreive() public view returns (uint256){ + return number; + } +} + + ` + +const jsCompile = `(async () => { + + try { + const contract = { + "storage.sol": {content : \`${simpleContract}\` } + } + console.log('compile') + const params = { + optimize: false, + evmVersion: null, + language: 'Solidity', + version: '0.6.8+commit.0bbfe453', + compilerUrl: 'https://solc-bin.ethereum.org/bin/soljson-v0.6.8+commit.0bbfe453.js' + } + const result = await remix.call('solidity', 'compileWithParameters', contract, params) + console.log('result ', result) + } catch (e) { + console.log(e.message) + } +})()` + +const updateConfiguration = `(async () => { + try { + const params = { + optimize: false, + evmVersion: null, + language: 'Solidity', + version: '0.6.8+commit.0bbfe453' + } + await remix.call('solidity', 'setCompilerConfig', params) + } catch (e) { + console.log(e.message) + } +})()`