feat(@embark/test-runner): make modules configurable per test

Make storage, ens and communication configurable per test
Tested ENS and it works like a charm
This commit is contained in:
Jonathan Rainville 2019-06-07 14:28:11 -04:00
parent 0f7237abac
commit 48aaceef7c
7 changed files with 145 additions and 61 deletions

View File

@ -2,7 +2,9 @@ module.exports = {
"default": { "default": {
"enabled": true, "enabled": true,
"available_providers": ["ens"], "available_providers": ["ens"],
"provider": "ens", "provider": "ens"
},
development: {
"register": { "register": {
"rootDomain": "embark.eth", "rootDomain": "embark.eth",
"subdomains": { "subdomains": {
@ -11,8 +13,5 @@ module.exports = {
"MyToken2": "$MyToken2" "MyToken2": "$MyToken2"
} }
} }
},
test: {
enabled: true
} }
}; };

View File

@ -4,6 +4,12 @@ const SimpleStorage = require('Embark/contracts/SimpleStorage');
const EmbarkJS = require('Embark/EmbarkJS'); const EmbarkJS = require('Embark/EmbarkJS');
config({ config({
namesystem: {
enabled: true,
register: {
"rootDomain": "test.eth"
}
},
contracts: { contracts: {
deploy: { deploy: {
"SimpleStorage": { "SimpleStorage": {
@ -14,12 +20,12 @@ config({
}); });
describe("EmbarkJS functions", function() { describe("EmbarkJS functions", function() {
it('should have access to ENS functions', async function() { it('should have access to ENS functions and registered test.eth', async function() {
const rootAddress = await EmbarkJS.Names.resolve('embark.eth'); const rootAddress = await EmbarkJS.Names.resolve('test.eth');
assert.strictEqual(rootAddress, web3.eth.defaultAccount); assert.strictEqual(rootAddress, web3.eth.defaultAccount);
const rootName = await EmbarkJS.Names.lookup(rootAddress); const rootName = await EmbarkJS.Names.lookup(rootAddress);
assert.strictEqual(rootName, 'embark.eth'); assert.strictEqual(rootName, 'test.eth');
}); });
it('should have access to Storage functions', async function() { it('should have access to Storage functions', async function() {

View File

@ -5,6 +5,15 @@ const MyToken2 = require('Embark/contracts/MyToken2');
const EmbarkJS = require('Embark/EmbarkJS'); const EmbarkJS = require('Embark/EmbarkJS');
config({ config({
namesystem: {
"register": {
"rootDomain": "embark.eth",
"subdomains": {
"mytoken": "$MyToken",
"MyToken2": "$MyToken2"
}
}
},
contracts: { contracts: {
deploy: { deploy: {
"Token": { "Token": {

View File

@ -64,27 +64,38 @@ class ENS {
this.logger = embark.logger; this.logger = embark.logger;
this.events = embark.events; this.events = embark.events;
this.fs = embark.fs; this.fs = embark.fs;
this.namesConfig = embark.config.namesystemConfig; this.config = embark.config;
this.enabled = false; this.enabled = false;
this.registration = this.namesConfig.register || {};
this.embark = embark; this.embark = embark;
this.ensConfig = ensConfig; this.ensConfig = ensConfig;
this.configured = false; this.configured = false;
this.consoleCmdsRegistered = false;
this.eventsRegistered = false;
this.events.setCommandHandler("ens:resolve", this.ensResolve.bind(this)); this.events.setCommandHandler("ens:resolve", this.ensResolve.bind(this));
this.events.setCommandHandler("ens:isENSName", this.isENSName.bind(this)); this.events.setCommandHandler("ens:isENSName", this.isENSName.bind(this));
if (this.namesConfig === {} || this.embark.events.setCommandHandler("module:namesystem:reset", (cb) => {
this.namesConfig.enabled !== true || this.reset();
this.namesConfig.available_providers.indexOf('ens') < 0) { this.init(cb);
return; });
this.init();
}
init(cb = () => {}) {
if (this.config.namesystemConfig === {} ||
this.config.namesystemConfig.enabled !== true ||
!this.config.namesystemConfig.available_providers ||
this.config.namesystemConfig.available_providers.indexOf('ens') < 0) {
return cb();
} }
this.enabled = true; this.enabled = true;
this.doSetENSProvider = this.namesConfig.provider === 'ens'; this.doSetENSProvider = this.config.namesystemConfig.provider === 'ens';
this.addENSToEmbarkJS();
this.registerEvents(); this.registerEvents();
this.registerConsoleCommands(); this.registerConsoleCommands();
this.addENSToEmbarkJS(cb);
} }
reset() { reset() {
@ -92,6 +103,10 @@ class ENS {
} }
registerConsoleCommands() { registerConsoleCommands() {
if (this.consoleCmdsRegistered) {
return;
}
this.consoleCmdsRegistered = true;
this.embark.registerConsoleCommand({ this.embark.registerConsoleCommand({
usage: 'resolve [name]', usage: 'resolve [name]',
description: __('Resolves an ENS name'), description: __('Resolves an ENS name'),
@ -134,6 +149,10 @@ class ENS {
} }
registerEvents() { registerEvents() {
if (this.eventsRegistered) {
return;
}
this.eventsRegistered = true;
this.embark.registerActionForEvent("deploy:beforeAll", this.configureContractsAndRegister.bind(this)); this.embark.registerActionForEvent("deploy:beforeAll", this.configureContractsAndRegister.bind(this));
this.events.on('blockchain:reseted', this.reset.bind(this)); this.events.on('blockchain:reseted', this.reset.bind(this));
this.events.setCommandHandler("storage:ens:associate", this.associateStorageToEns.bind(this)); this.events.setCommandHandler("storage:ens:associate", this.associateStorageToEns.bind(this));
@ -143,7 +162,7 @@ class ENS {
getEnsConfig(cb) { getEnsConfig(cb) {
cb({ cb({
env: this.env, env: this.env,
registration: this.registration, registration: this.config.namesystemConfig.register,
registryAbi: this.ensConfig.ENSRegistry.abiDefinition, registryAbi: this.ensConfig.ENSRegistry.abiDefinition,
registryAddress: this.ensConfig.ENSRegistry.deployedAddress, registryAddress: this.ensConfig.ENSRegistry.deployedAddress,
registrarAbi: this.ensConfig.FIFSRegistrar.abiDefinition, registrarAbi: this.ensConfig.FIFSRegistrar.abiDefinition,
@ -162,7 +181,7 @@ class ENS {
self.events.request('blockchain:networkId', (networkId) => { self.events.request('blockchain:networkId', (networkId) => {
const isKnownNetwork = Boolean(ENS_CONTRACTS_CONFIG[networkId]); const isKnownNetwork = Boolean(ENS_CONTRACTS_CONFIG[networkId]);
const shouldRegisterSubdomain = self.registration && self.registration.subdomains && Object.keys(self.registration.subdomains).length; const shouldRegisterSubdomain = self.config.namesystemConfig.register && self.config.namesystemConfig.register.subdomains && Object.keys(self.config.namesystemConfig.register.subdomains).length;
if (isKnownNetwork || !shouldRegisterSubdomain) { if (isKnownNetwork || !shouldRegisterSubdomain) {
return cb(); return cb();
} }
@ -244,8 +263,8 @@ class ENS {
registerConfigDomains(config, cb) { registerConfigDomains(config, cb) {
this.events.request("blockchain:defaultAccount:get", (defaultAccount) => { this.events.request("blockchain:defaultAccount:get", (defaultAccount) => {
async.each(Object.keys(this.registration.subdomains), (subDomainName, eachCb) => { async.each(Object.keys(this.config.namesystemConfig.register.subdomains), (subDomainName, eachCb) => {
const address = this.registration.subdomains[subDomainName]; const address = this.config.namesystemConfig.register.subdomains[subDomainName];
const directivesRegExp = new RegExp(/\$(\w+\[?\d?\]?)/g); const directivesRegExp = new RegExp(/\$(\w+\[?\d?\]?)/g);
@ -277,13 +296,13 @@ class ENS {
} }
safeRegisterSubDomain(subDomainName, address, defaultAccount, callback) { safeRegisterSubDomain(subDomainName, address, defaultAccount, callback) {
this.ensResolve(`${subDomainName}.${this.registration.rootDomain}`, (error, currentAddress) => { this.ensResolve(`${subDomainName}.${this.config.namesystemConfig.register.rootDomain}`, (error, currentAddress) => {
if (currentAddress && currentAddress.toLowerCase() === address.toLowerCase()) { if (currentAddress && currentAddress.toLowerCase() === address.toLowerCase()) {
return callback(); return callback();
} }
if (error && error !== NOT_REGISTERED_ERROR) { if (error && error !== NOT_REGISTERED_ERROR) {
this.logger.error(__('Error resolving %s', `${subDomainName}.${this.registration.rootDomain}`)); this.logger.error(__('Error resolving %s', `${subDomainName}.${this.config.namesystemConfig.register.rootDomain}`));
return callback(error); return callback(error);
} }
@ -295,7 +314,7 @@ class ENS {
registerSubDomain(defaultAccount, subDomainName, reverseNode, address, secureSend, cb) { registerSubDomain(defaultAccount, subDomainName, reverseNode, address, secureSend, cb) {
this.events.request("blockchain:get", (web3) => { this.events.request("blockchain:get", (web3) => {
ENSFunctions.registerSubDomain(web3, this.ensContract, this.registrarContract, this.resolverContract, defaultAccount, ENSFunctions.registerSubDomain(web3, this.ensContract, this.registrarContract, this.resolverContract, defaultAccount,
subDomainName, this.registration.rootDomain, reverseNode, address, this.logger, secureSend, cb, namehash); subDomainName, this.config.namesystemConfig.register.rootDomain, reverseNode, address, this.logger, secureSend, cb, namehash);
}); });
} }
@ -359,25 +378,27 @@ class ENS {
if (error) { if (error) {
return res.send({error: error.message || error}); return res.send({error: error.message || error});
} }
res.send({name: `${req.body.subdomain}.${self.registration.rootDomain}`, address: req.body.address}); res.send({name: `${req.body.subdomain}.${self.config.namesystemConfig.register.rootDomain}`, address: req.body.address});
}); });
}); });
} }
); );
} }
addENSToEmbarkJS() { addENSToEmbarkJS(cb) {
this.events.request('version:downloadIfNeeded', 'eth-ens-namehash', (err, location) => { this.events.request('version:downloadIfNeeded', 'eth-ens-namehash', (err, location) => {
if (err) { if (err) {
this.logger.error(__('Error downloading NameHash')); this.logger.error(__('Error downloading NameHash'));
return this.logger.error(err.message || err); this.logger.error(err.message || err);
return cb();
} }
this.events.request('code-generator:ready', () => { this.events.request('code-generator:ready', () => {
this.events.request('code-generator:symlink:generate', location, 'eth-ens-namehash', (err, symlinkDest) => { this.events.request('code-generator:symlink:generate', location, 'eth-ens-namehash', (err, symlinkDest) => {
if (err) { if (err) {
this.logger.error(__('Error creating a symlink to eth-ens-namehash')); this.logger.error(__('Error creating a symlink to eth-ens-namehash'));
return this.logger.error(err.message || err); this.logger.error(err.message || err);
return cb();
} }
this.events.emit('runcode:register', 'namehash', require('eth-ens-namehash'), () => { this.events.emit('runcode:register', 'namehash', require('eth-ens-namehash'), () => {
let code = `\nconst namehash = global.namehash || require('${symlinkDest}');`; let code = `\nconst namehash = global.namehash || require('${symlinkDest}');`;
@ -386,6 +407,7 @@ class ENS {
code += "\nEmbarkJS.Names.registerProvider('ens', __embarkENS);"; code += "\nEmbarkJS.Names.registerProvider('ens', __embarkENS);";
this.embark.addCodeToEmbarkJS(code); this.embark.addCodeToEmbarkJS(code);
cb();
}); });
}); });
}); });
@ -409,6 +431,7 @@ class ENS {
if (self.configured) { if (self.configured) {
return cb(); return cb();
} }
const registration = this.config.namesystemConfig.register;
async.waterfall([ async.waterfall([
function getNetworkId(next) { function getNetworkId(next) {
@ -433,19 +456,19 @@ class ENS {
}); });
}, },
function checkRootNode(next) { function checkRootNode(next) {
if (!self.registration || !self.registration.rootDomain) { if (!registration || !registration.rootDomain) {
return next(NO_REGISTRATION); return next(NO_REGISTRATION);
} }
if (!self.isENSName(self.registration.rootDomain)) { if (!self.isENSName(registration.rootDomain)) {
return next(__('Invalid domain name: {{name}}\nValid extensions are: {{extenstions}}', return next(__('Invalid domain name: {{name}}\nValid extensions are: {{extenstions}}',
{name: self.registration.rootDomain, extenstions: ENS_WHITELIST.join(', ')})); {name: registration.rootDomain, extenstions: ENS_WHITELIST.join(', ')}));
} }
next(); next();
}, },
function registrar(next) { function registrar(next) {
const registryAddress = self.ensConfig.ENSRegistry.deployedAddress; const registryAddress = self.ensConfig.ENSRegistry.deployedAddress;
const rootNode = namehash.hash(self.registration.rootDomain); const rootNode = namehash.hash(registration.rootDomain);
const contract = self.ensConfig.FIFSRegistrar; const contract = self.ensConfig.FIFSRegistrar;
contract.args = [registryAddress, rootNode]; contract.args = [registryAddress, rootNode];
@ -497,7 +520,7 @@ class ENS {
self.resolverContract = result[2]; self.resolverContract = result[2];
const web3 = result[3]; const web3 = result[3];
const rootNode = namehash.hash(self.registration.rootDomain); const rootNode = namehash.hash(registration.rootDomain);
var reverseNode = namehash.hash(web3.eth.defaultAccount.toLowerCase().substr(2) + reverseAddrSuffix); var reverseNode = namehash.hash(web3.eth.defaultAccount.toLowerCase().substr(2) + reverseAddrSuffix);
const owner = await self.ensContract.methods.owner(rootNode).call(); const owner = await self.ensContract.methods.owner(rootNode).call();
@ -529,7 +552,7 @@ class ENS {
}, false); }, false);
}).then(() => { }).then(() => {
// Set name of the reverse node to the root domain // Set name of the reverse node to the root domain
return secureSend(web3, self.resolverContract.methods.setName(reverseNode, self.registration.rootDomain), { return secureSend(web3, self.resolverContract.methods.setName(reverseNode, registration.rootDomain), {
from: web3.eth.defaultAccount, from: web3.eth.defaultAccount,
gas: ENS_GAS_PRICE gas: ENS_GAS_PRICE
}, false); }, false);

View File

@ -1,5 +1,6 @@
import { __ } from 'embark-i18n'; import { __ } from 'embark-i18n';
import { deconstructUrl, prepareContractsConfig, buildUrl } from 'embark-utils'; import { deconstructUrl, prepareContractsConfig, buildUrl } from 'embark-utils';
import deepEqual from 'deep-equal';
const async = require('async'); const async = require('async');
const web3Utils = require('web3-utils'); const web3Utils = require('web3-utils');
@ -24,6 +25,11 @@ class Test {
this.accounts = []; this.accounts = [];
this.embarkjs = {}; this.embarkjs = {};
this.dappPath = options.dappPath; this.dappPath = options.dappPath;
this.moduleConfigs = {
namesystem: {},
storage: {},
communication: {}
};
this.events.setCommandHandler("blockchain:provider:contract:accounts:get", cb => { this.events.setCommandHandler("blockchain:provider:contract:accounts:get", cb => {
this.events.request("blockchain:getAccounts", cb); this.events.request("blockchain:getAccounts", cb);
@ -164,6 +170,26 @@ class Test {
}); });
} }
checkModuleConfigs(options, callback) {
const self = this;
const restartModules = [];
Object.keys(this.moduleConfigs).forEach(moduleName => {
options[moduleName] = options[moduleName] || {};
if (!deepEqual(options[moduleName], this.moduleConfigs[moduleName])) {
restartModules.push(function (paraCb) {
self.events.request(`config:${moduleName}Config:set`, options[moduleName], true, () => {
self.events.request(`module:${moduleName}:reset`, paraCb);
});
});
}
});
async.parallel(restartModules, (err, _result) => {
callback(err);
});
}
config(options, callback) { config(options, callback) {
const self = this; const self = this;
self.needConfig = false; self.needConfig = false;
@ -184,6 +210,9 @@ class Test {
function checkDeploymentOpts(next) { function checkDeploymentOpts(next) {
self.checkDeploymentOptions(options, next); self.checkDeploymentOptions(options, next);
}, },
function checkModuleConfigs(next) {
self.checkModuleConfigs(options, next);
},
function prepareContracts(next) { function prepareContracts(next) {
if (!self.firstDeployment || !self.options.coverage) { if (!self.firstDeployment || !self.options.coverage) {
return next(); return next();

View File

@ -32,11 +32,12 @@ const PACKAGE = require('../../../package.json');
const embark5ChangesUrl = 'https://...'; const embark5ChangesUrl = 'https://...';
var Config = function(options) { var Config = function(options) {
const self = this;
this.env = options.env || 'default'; this.env = options.env || 'default';
this.blockchainConfig = {}; this.blockchainConfig = {};
this.contractsConfig = {}; this.contractsConfig = {};
this.pipelineConfig = {}; this.pipelineConfig = {};
this.namesystemConfig = {};
this.communicationConfig = {};
this.webServerConfig = options.webServerConfig; this.webServerConfig = options.webServerConfig;
this.chainTracker = {}; this.chainTracker = {};
this.assetFiles = {}; this.assetFiles = {};
@ -53,51 +54,65 @@ var Config = function(options) {
this.shownNoAccountConfigMsg = false; // flag to ensure "no account config" message is only displayed once to the user this.shownNoAccountConfigMsg = false; // flag to ensure "no account config" message is only displayed once to the user
this.corsParts = []; this.corsParts = [];
this.providerUrl = null; this.providerUrl = null;
this.registerEvents();
};
Config.prototype.setConfig = function(configName, newConfig, recursive, cb) {
if (typeof recursive === 'function') {
cb = recursive;
recursive = false;
}
if (recursive) {
this[configName] = recursiveMerge(this[configName], newConfig);
} else {
this[configName] = newConfig;
}
cb();
};
Config.prototype.registerEvents = function() {
this.events.setCommandHandler("config:cors:add", (url) => { this.events.setCommandHandler("config:cors:add", (url) => {
this.corsParts.push(url); this.corsParts.push(url);
this._updateBlockchainCors(); this._updateBlockchainCors();
}); });
self.events.setCommandHandler("config:contractsConfig", (cb) => { this.events.setCommandHandler("config:contractsConfig", (cb) => {
cb(self.contractsConfig); cb(this.contractsConfig);
}); });
self.events.setCommandHandler("config:contractsConfig:set", (config, cb) => { this.events.setCommandHandler("config:contractsConfig:set", this.setConfig.bind(this, 'contractsConfig'));
self.contractsConfig = config; this.events.setCommandHandler("config:blockchainConfig:set", this.setConfig.bind(this, 'blockchainConfig'));
cb(); this.events.setCommandHandler("config:storageConfig:set", this.setConfig.bind(this, 'storageConfig'));
}); this.events.setCommandHandler("config:namesystemConfig:set", this.setConfig.bind(this, 'namesystemConfig'));
this.events.setCommandHandler("config:communicationConfig:set", this.setConfig.bind(this, 'communicationConfig'));
self.events.setCommandHandler("config:blockchainConfig:set", (config, cb) => { this.events.setCommandHandler("config:contractsFiles", (cb) => {
self.blockchainConfig = config; cb(this.contractsFiles);
cb();
});
self.events.setCommandHandler("config:contractsFiles", (cb) => {
cb(self.contractsFiles);
}); });
// TODO: refactor this so reading the file can be done with a normal resolver or something that takes advantage of the plugin api // TODO: refactor this so reading the file can be done with a normal resolver or something that takes advantage of the plugin api
self.events.setCommandHandler("config:contractsFiles:add", (filename, resolver) => { this.events.setCommandHandler("config:contractsFiles:add", (filename, resolver) => {
resolver = resolver || function(callback) { resolver = resolver || function(callback) {
callback(fs.readFileSync(filename).toString()); callback(fs.readFileSync(filename).toString());
}; };
self.contractsFiles.push(new File({path: filename, originalPath: filename, type: Types.custom, resolver})); this.contractsFiles.push(new File({path: filename, originalPath: filename, type: Types.custom, resolver}));
}); });
self.events.setCommandHandler("config:contractsFiles:reset", (cb) => { this.events.setCommandHandler("config:contractsFiles:reset", (cb) => {
self.contractsFiles.forEach((file) => { this.contractsFiles.forEach((file) => {
if(file.path.includes(".embark")) { if(file.path.includes(".embark")) {
fs.removeSync(file.path); fs.removeSync(file.path);
} }
self.contractsFiles = self.contractsFiles.filter((contractFile) => contractFile.path !== file.path); this.contractsFiles = this.contractsFiles.filter((contractFile) => contractFile.path !== file.path);
}); });
cb(); cb();
}); });
self.events.on('file-remove', (fileType, removedPath) => { this.events.on('file-remove', (fileType, removedPath) => {
if(fileType !== 'contract') return; if(fileType !== 'contract') return;
const normalizedPath = path.normalize(removedPath); const normalizedPath = path.normalize(removedPath);
self.contractsFiles = self.contractsFiles.filter(file => path.normalize(file.path) !== normalizedPath); this.contractsFiles = this.contractsFiles.filter(file => path.normalize(file.path) !== normalizedPath);
}); });
}; };

View File

@ -3,7 +3,6 @@ import { __ } from 'embark-i18n';
class Storage { class Storage {
constructor(embark, options){ constructor(embark, options){
this.embark = embark; this.embark = embark;
this.storageConfig = embark.config.storageConfig;
this.plugins = options.plugins; this.plugins = options.plugins;
this.ready = false; this.ready = false;
@ -14,22 +13,24 @@ class Storage {
this.embark.events.once("module:storage:ready", cb); this.embark.events.once("module:storage:ready", cb);
}); });
if (!this.storageConfig.enabled) { this.embark.events.setCommandHandler("module:storage:reset", (cb) => {
this.ready = false;
this.addSetProviders(cb);
});
if (!embark.config.storageConfig.enabled) {
this.ready = true; this.ready = true;
return; return;
} }
this.handleUploadCommand(); this.handleUploadCommand();
this.addSetProviders(() => { this.addSetProviders(() => {});
this.ready = true;
this.embark.events.emit("module:storage:ready");
});
} }
handleUploadCommand() { handleUploadCommand() {
const self = this; const self = this;
this.embark.events.setCommandHandler('storage:upload', (cb) => { this.embark.events.setCommandHandler('storage:upload', (cb) => {
let platform = self.storageConfig.upload.provider; let platform = this.embark.config.storageConfig.upload.provider;
let uploadCmds = self.plugins.getPluginsProperty('uploadCmds', 'uploadCmds'); let uploadCmds = self.plugins.getPluginsProperty('uploadCmds', 'uploadCmds');
for (let uploadCmd of uploadCmds) { for (let uploadCmd of uploadCmds) {
@ -43,7 +44,7 @@ class Storage {
} }
addSetProviders(cb) { addSetProviders(cb) {
let code = `\nEmbarkJS.Storage.setProviders(${JSON.stringify(this.storageConfig.dappConnection || [])}, {web3});`; let code = `\nEmbarkJS.Storage.setProviders(${JSON.stringify(this.embark.config.storageConfig.dappConnection || [])}, {web3});`;
let shouldInit = (storageConfig) => { let shouldInit = (storageConfig) => {
return storageConfig.enabled; return storageConfig.enabled;
@ -53,6 +54,8 @@ class Storage {
this.embark.events.request("runcode:storage:providerRegistered", () => { this.embark.events.request("runcode:storage:providerRegistered", () => {
this.embark.addConsoleProviderInit('storage', code, shouldInit); this.embark.addConsoleProviderInit('storage', code, shouldInit);
this.embark.events.request("runcode:storage:providerSet", () => { this.embark.events.request("runcode:storage:providerSet", () => {
this.ready = true;
this.embark.events.emit("module:storage:ready");
cb(); cb();
}); });
}); });