Add for loops and variable declarations; Coverage improvements

This commit is contained in:
Andre Medeiros 2018-08-20 13:54:44 -04:00
parent 782e7b04e8
commit 5dcd679e12
31 changed files with 3123 additions and 25 deletions

View File

@ -1,3 +1,5 @@
const util = require('util');
const SourceMap = require('./source_map'); const SourceMap = require('./source_map');
class ContractSource { class ContractSource {
@ -68,18 +70,29 @@ class ContractSource {
var bytecodeIdx = 0; var bytecodeIdx = 0;
var pc = 0; var pc = 0;
var instructions = 0; var instructions = 0;
var previousSourceMap = null;
do { do {
let sourceMap;
if(previousSourceMap === null) {
sourceMap = new SourceMap(sourceMaps[instructions]);
} else {
sourceMap = previousSourceMap.createRelativeTo(sourceMaps[instructions]);
}
var instruction = opcodes[bytecodeIdx]; var instruction = opcodes[bytecodeIdx];
var length = this._instructionLength(instruction); var length = this._instructionLength(instruction);
bytecodeMapping[pc] = { bytecodeMapping[pc] = {
instruction: instruction, instruction: instruction,
sourceMap: sourceMaps[instructions], sourceMap: sourceMap,
jump: sourceMap.jump,
seen: false seen: false
}; };
pc += length; pc += length;
instructions++; instructions++;
bytecodeIdx += (length > 1) ? 2 : 1; bytecodeIdx += (length > 1) ? 2 : 1;
previousSourceMap = sourceMap;
} while(bytecodeIdx < opcodes.length); } while(bytecodeIdx < opcodes.length);
} }
} }
@ -89,7 +102,7 @@ class ContractSource {
if(!this.ast || !this.contractBytecode) throw new Error('Error generating coverage: solc output was not assigned'); if(!this.ast || !this.contractBytecode) throw new Error('Error generating coverage: solc output was not assigned');
var coverage = { var coverage = {
code: this.body.split("\n"), code: this.body.trim().split("\n"),
l: {}, l: {},
path: this.file, path: this.file,
s: {}, s: {},
@ -111,10 +124,10 @@ class ContractSource {
var markLocations = []; var markLocations = [];
switch(node.nodeType) { switch(node.nodeType) {
case 'Identifier': case 'Assignment':
case 'Literal': case 'Literal':
case 'VariableDeclaration':
case 'PragmaDirective': case 'PragmaDirective':
case 'VariableDeclaration':
// We don't need to do anything with these. Just carry on. // We don't need to do anything with these. Just carry on.
break; break;
@ -166,8 +179,9 @@ class ContractSource {
node.src = node.expression.src; node.src = node.expression.src;
// falls through // falls through
case 'Assignment':
case 'ExpressionStatement': case 'ExpressionStatement':
case 'Identifier':
case 'UnaryOperation':
coverage.s[node.id] = 0; coverage.s[node.id] = 0;
location = this.sourceMapToLocations(node.src); location = this.sourceMapToLocations(node.src);
@ -219,8 +233,43 @@ class ContractSource {
markLocations = [location]; markLocations = [location];
break; break;
case 'ForStatement':
// For statements will be a bit of a special case. We want to count the body
// iterations but we only want to count the for loop being hit once. Because
// of this, we cover the initialization on the node.
let sourceMap = new SourceMap(node.src);
let bodySourceMap = new SourceMap(node.body.src);
let forLoopDeclaration = sourceMap.subtract(bodySourceMap).toString();
let initializationLocation = this.sourceMapToLocations(node.initializationExpression.src);
location = this.sourceMapToLocations(forLoopDeclaration);
coverage.s[node.id] = 0;
coverage.statementMap[node.id] = location;
if(!sourceMapToNodeType[node.initializationExpression.src]) sourceMapToNodeType[node.initializationExpression.src] = [];
sourceMapToNodeType[node.initializationExpression.src].push({type: 's', id: node.id, body: {loc: location}});
console.dir(node);
children = node.body.statements;
markLocations = [initializationLocation];
break;
case 'VariableDeclarationStatement':
location = this.sourceMapToLocations(node.src);
coverage.s[node.id] = 0;
coverage.statementMap[node.id] = location;
markLocations = [location];
if(!sourceMapToNodeType[node.src]) sourceMapToNodeType[node.src] = [];
sourceMapToNodeType[node.src].push({type: 's', id: node.id, body: {loc: location}, foo: 'bar'});
break;
default: default:
// console.log(`Don't know how to handle node type ${node.nodeType}`); console.log(`Don't know how to handle node type ${node.nodeType}`);
break; break;
} }
@ -243,23 +292,26 @@ class ContractSource {
contractMatches = trace.structLogs.every((step) => { return bytecode[step.pc]; }); contractMatches = trace.structLogs.every((step) => { return bytecode[step.pc]; });
if(!contractMatches) break; if(!contractMatches) break;
console.log(sourceMapToNodeType);
trace.structLogs.forEach((step) => { trace.structLogs.forEach((step) => {
step = bytecode[step.pc]; step = bytecode[step.pc];
if(!step.sourceMap || step.sourceMap == '') return; if(!step.sourceMap || step.sourceMap == '') return;
var nodes = sourceMapToNodeType[step.sourceMap]; let sourceMapString = step.sourceMap.toString(this.id);
var nodes = sourceMapToNodeType[sourceMapString];
if(step.sourceMap.offset == 189) console.log(nodes);
if(!nodes) return; if(!nodes) return;
var recordedLineHit = false;
nodes.forEach((node) => { nodes.forEach((node) => {
if(node.body && node.body.loc && !recordedLineHit) { // Skip duplicate function reports by only reporting when there is a jump.
if(node.type == 'f' && step.jump) return;
if(node.body && node.body.loc) {
for(var line = node.body.loc.start.line; line <= node.body.loc.end.line; line++) { for(var line = node.body.loc.start.line; line <= node.body.loc.end.line; line++) {
coverage.l[line]++; coverage.l[line]++;
} }
recordedLineHit = true;
} }
if(node.type != 'b') coverage[node.type][node.id]++; if(node.type != 'b') coverage[node.type][node.id]++;

View File

@ -10,6 +10,7 @@ web3.extend({
methods: [{name: 'traceTransaction', call: 'debug_traceTransaction', params: 2}] methods: [{name: 'traceTransaction', call: 'debug_traceTransaction', params: 2}]
}); });
class CodeCoverage { class CodeCoverage {
constructor(embark, _options) { constructor(embark, _options) {
this.events = embark.events; this.events = embark.events;
@ -21,6 +22,7 @@ class CodeCoverage {
embark.events.on('block:header', this.runSolc.bind(this)); embark.events.on('block:header', this.runSolc.bind(this));
this.seenTransactions = {}; this.seenTransactions = {};
this.coverageReport = {};
var self = this; var self = this;

View File

@ -1,29 +1,64 @@
class EmptySourceMap {
static createRelativeTo(sourceMapString) {
if(sourceMapString == '') return EmptySourceMap;
return new SourceMap(sourceMapString);
}
static toString() {
return '';
}
}
class SourceMap { class SourceMap {
constructor(sourceMapStringOrOffset, length, id) { constructor(sourceMapStringOrOffset, length, id, jump) {
if(typeof sourceMapStringOrOffset == 'string') { if(typeof sourceMapStringOrOffset == 'string') {
let [offset, length, id, ..._rest] = sourceMapStringOrOffset.split(":"); let [offset, length, id, jump] = sourceMapStringOrOffset.split(":");
this.offset = parseInt(offset, 10); this.offset = parseInt(offset, 10);
this.length = parseInt(length, 10); this.length = parseInt(length, 10);
if(id) this.id = parseInt(id, 10); if(id) this.id = parseInt(id, 10);
this.jump = jump;
} else { } else {
this.offset = sourceMapStringOrOffset; this.offset = sourceMapStringOrOffset;
this.length = length; this.length = length;
this.id = id; this.id = id;
this.jump = jump;
} }
} }
subtract(sourceMap) { createRelativeTo(sourceMapString) {
return new SourceMap(this.offset, sourceMap.offset - this.offset); if(sourceMapString == '' || sourceMapString == undefined) return EmptySourceMap;
let [offset, length, id, jump] = sourceMapString.split(":");
(offset == '') ? offset = this.offset : offset = parseInt(offset, 10);
(id == '' || id == undefined) ? id = this.id : id = parseInt(id, 10);
length = parseInt(length, 10);
return new SourceMap(offset, length, id, jump);
} }
toString() { subtract(sourceMap) {
var parts = [this.offset, this.length]; return new SourceMap(this.offset, sourceMap.offset - this.offset, this.id, this.jump);
if(this.id) parts.push(this.id); }
toString(defaultId) {
let parts = [this.offset, this.length];
if(this.id !== undefined && this.id != '') {
parts.push(this.id)
} else if(defaultId !== undefined) {
parts.push(defaultId);
}
return parts.join(':'); return parts.join(':');
} }
static empty() {
return EmptySourceMap;
}
} }
module.exports = SourceMap; module.exports = SourceMap;

View File

@ -112,7 +112,6 @@ class Test {
compileOnceOnly: true compileOnceOnly: true
}); });
this.events.request('deploy:setGasLimit', 6000000); this.events.request('deploy:setGasLimit', 6000000);
this.engine.startService("codeCoverage");
} }
init(callback) { init(callback) {
@ -139,6 +138,7 @@ class Test {
this.engine.startService("codeRunner"); this.engine.startService("codeRunner");
this.initDeployServices(); this.initDeployServices();
this.engine.startService("codeGenerator"); this.engine.startService("codeGenerator");
this.engine.startService("codeCoverage");
if (this.options.node === 'embark') { if (this.options.node === 'embark') {
return this.engine.ipc.connect((err) => { return this.engine.ipc.connect((err) => {

View File

@ -162,10 +162,10 @@ contract x {
var bytecode = contractSource.contractBytecode['x']; var bytecode = contractSource.contractBytecode['x'];
assert.deepEqual({instruction: 'PUSH1', sourceMap: '26:487:0:-', seen: false}, bytecode[0]); assert.deepEqual({instruction: 'PUSH1', sourceMap: {offset: 26, length: 487, id: 0}, seen: false}, bytecode[0]);
assert.deepEqual({instruction: 'PUSH1', sourceMap: '', seen: false}, bytecode[2]); assert.deepEqual({instruction: 'PUSH1', sourceMap: SourceMap.empty(), seen: false}, bytecode[2]);
assert.deepEqual({instruction: 'MSTORE', sourceMap: '', seen: false}, bytecode[4]); assert.deepEqual({instruction: 'MSTORE', sourceMap: SourceMap.empty(), seen: false}, bytecode[4]);
assert.deepEqual({instruction: 'PUSH1', sourceMap: '', seen: false}, bytecode[5]); assert.deepEqual({instruction: 'PUSH1', sourceMap: SourceMap.empty(), seen: false}, bytecode[5]);
done(); done();
}); });
@ -193,6 +193,7 @@ contract x {
var trace = JSON.parse(loadFixture('geth-debugtrace-output-h-5.json')); var trace = JSON.parse(loadFixture('geth-debugtrace-output-h-5.json'));
var coverage = cs.generateCodeCoverage(trace); var coverage = cs.generateCodeCoverage(trace);
assert.exists(coverage);
done(); done();
}); });
@ -213,7 +214,7 @@ contract x {
// In the fixture, the branch has an ID of 61, and the function has the // In the fixture, the branch has an ID of 61, and the function has the
// ID of 63 // ID of 63
assert.deepEqual([1,1], coverage.b['61']); assert.deepEqual([1,1], coverage.b['61']);
assert.equal(2, coverage.f['63']); assert.equal(4, coverage.f['63']);
done(); done();
}); });
@ -234,4 +235,26 @@ describe('SourceMap', () => {
done(); done();
}); });
}); });
describe('#createRelativeTo', () => {
it('should return an empty source map on an empty string', (done) => {
var sm1 = new SourceMap('192:10:0');
var sm2 = sm1.createRelativeTo('');
assert.equal('', sm2.toString());
done();
});
it('should return the correct source map on a relative string', (done) => {
var sm1 = new SourceMap('192:10:0');
var sm2 = sm1.createRelativeTo(':14');
assert.equal(192, sm2.offset);
assert.equal(14, sm2.length);
assert.equal(0, sm2.id);
done();
});
});
}); });

6
test_apps/coverage_app/.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
.embark/
node_modules/
dist/
config/production/password
config/livenet/password
coverage/

View File

@ -0,0 +1,91 @@
import EmbarkJS from 'Embark/EmbarkJS';
import SimpleStorage from 'Embark/contracts/SimpleStorage';
import React from 'react';
import { Form, FormGroup, FormControl, HelpBlock, Button } from 'react-bootstrap';
class Blockchain extends React.Component {
constructor(props) {
super(props);
this.state = {
valueSet: 10,
valueGet: "",
logs: []
}
}
handleChange(e){
this.setState({valueSet: e.target.value});
}
setValue(e){
e.preventDefault();
var value = parseInt(this.state.valueSet, 10);
// If web3.js 1.0 is being used
if (EmbarkJS.isNewWeb3()) {
SimpleStorage.methods.set(value).send({from: web3.eth.defaultAccount});
this._addToLog("SimpleStorage.methods.set(value).send({from: web3.eth.defaultAccount})");
} else {
SimpleStorage.set(value);
this._addToLog("#blockchain", "SimpleStorage.set(" + value + ")");
}
}
getValue(e){
e.preventDefault();
if (EmbarkJS.isNewWeb3()) {
SimpleStorage.methods.get().call()
.then(_value => this.setState({valueGet: _value}))
this._addToLog("SimpleStorage.methods.get(console.log)");
} else {
SimpleStorage.get()
.then(_value => this.setState({valueGet: _value}));
this._addToLog("SimpleStorage.get()");
}
}
_addToLog(txt){
this.state.logs.push(txt);
this.setState({logs: this.state.logs});
}
render(){
return (<React.Fragment>
<h3> 1. Set the value in the blockchain</h3>
<Form inline>
<FormGroup>
<FormControl
type="text"
defaultValue={this.state.valueSet}
onChange={(e) => this.handleChange(e)} />
<Button bsStyle="primary" onClick={(e) => this.setValue(e)}>Set Value</Button>
<HelpBlock>Once you set the value, the transaction will need to be mined and then the value will be updated on the blockchain.</HelpBlock>
</FormGroup>
</Form>
<h3> 2. Get the current value</h3>
<Form inline>
<FormGroup>
<HelpBlock>current value is <span className="value">{this.state.valueGet}</span></HelpBlock>
<Button bsStyle="primary" onClick={(e) => this.getValue(e)}>Get Value</Button>
<HelpBlock>Click the button to get the current value. The initial value is 100.</HelpBlock>
</FormGroup>
</Form>
<h3> 3. Contract Calls </h3>
<p>Javascript calls being made: </p>
<div className="logs">
{
this.state.logs.map((item, i) => <p key={i}>{item}</p>)
}
</div>
</React.Fragment>
);
}
}
export default Blockchain;

View File

@ -0,0 +1,172 @@
/*global web3*/
import EmbarkJS from 'Embark/EmbarkJS';
import React from 'react';
import { Alert, Form, FormGroup, FormControl, Button } from 'react-bootstrap';
window.EmbarkJS = EmbarkJS;
class ENS extends React.Component {
constructor(props) {
super(props);
this.state = {
valueResolve: 'embark.eth',
responseResolve: null,
isResolveError: false,
valueLookup: '',
responseLookup: null,
isLookupError: false,
valueRegister: '',
addressRegister: '',
responseRegister: null,
isRegisterError: false,
embarkLogs: []
};
}
componentDidMount() {
EmbarkJS.onReady(() => {
if (!web3.eth.defaultAccount) {
this.setState({
globalError: 'There is currently no default account. If Metamask is active, please sign in or deactivate it.'
});
}
this.setState({
addressRegister: web3.eth.defaultAccount,
valueLookup: web3.eth.defaultAccount
})
});
}
handleChange(stateName, e) {
this.setState({ [stateName]: e.target.value });
}
registerSubDomain(e) {
e.preventDefault();
const self = this;
const embarkLogs = this.state.embarkLogs;
embarkLogs.push(`EmbarkJS.Names.registerSubDomain('${this.state.valueRegister}', '${this.state.addressRegister}', console.log)`);
this.setState({
embarkLogs: embarkLogs
});
EmbarkJS.Names.registerSubDomain(this.state.valueRegister, this.state.addressRegister, (err, transaction) => {
const message = err ? err : `Successfully registered "${this.state.valueRegister}" with ${transaction.gasUsed} gas`;
self.setState({
responseRegister: message,
isRegisterError: !!err
});
});
}
resolveName(e) {
e.preventDefault();
const embarkLogs = this.state.embarkLogs;
embarkLogs.push(`EmbarkJS.Names.resolve('${this.state.valueResolve}', console.log)`);
this.setState({
embarkLogs: embarkLogs
});
EmbarkJS.Names.resolve(this.state.valueResolve, (err, result) => {
if (err) {
return this.setState({
responseResolve: err.message || err,
isResolveError: true
});
}
this.setState({
responseResolve: result,
isResolveError: false
});
});
}
lookupAddress(e) {
e.preventDefault();
const embarkLogs = this.state.embarkLogs;
embarkLogs.push(`EmbarkJS.Names.resolve('${this.state.valueLookup}', console.log)`);
this.setState({
embarkLogs: embarkLogs
});
EmbarkJS.Names.lookup(this.state.valueLookup, (err, result) => {
if (err) {
return this.setState({
responseLookup: err.message || err,
isLookupError: true
});
}
this.setState({
responseLookup: result,
isLookupError: false
});
});
}
render() {
return (<React.Fragment>
{this.state.globalError && <Alert bsStyle="danger">{this.state.globalError}</Alert>}
<h3>Resolve a name</h3>
<Form inline>
<FormGroup>
{this.state.responseResolve &&
<Alert className="alert-result" bsStyle={this.state.isResolveError ? 'danger' : 'success'}>
Resolved address: <span className="value">{this.state.responseResolve}</span>
</Alert>}
<FormControl
type="text"
defaultValue={this.state.valueResolve}
onChange={(e) => this.handleChange('valueResolve', e)}/>
<Button bsStyle="primary" onClick={(e) => this.resolveName(e)}>Resolve name</Button>
</FormGroup>
</Form>
<h3>Lookup an address</h3>
<Form inline>
<FormGroup>
{this.state.responseLookup &&
<Alert className="alert-result" bsStyle={this.state.isLookupError ? 'danger' : 'success'}>
Looked up domain: <span className="value">{this.state.responseLookup}</span>
</Alert>}
<FormControl
type="text"
defaultValue={this.state.valueLookup}
onChange={(e) => this.handleChange('valueLookup', e)}/>
<Button bsStyle="primary" onClick={(e) => this.lookupAddress(e)}>Lookup address</Button>
</FormGroup>
</Form>
<h3>Register subdomain for embark</h3>
<Form inline>
<FormGroup>
{this.state.responseRegister &&
<Alert className="alert-result" bsStyle={this.state.isRegisterError ? 'danger' : 'success'}>
<span className="value">{this.state.responseRegister}</span>
</Alert>}
<FormControl
type="text"
defaultValue={this.state.valueRegister}
onChange={(e) => this.handleChange('valueRegister', e)}/>
<FormControl
type="text"
defaultValue={this.state.addressRegister}
onChange={(e) => this.handleChange('addressRegister', e)}/>
<Button bsStyle="primary" onClick={(e) => this.registerSubDomain(e)}>Register subdomain</Button>
</FormGroup>
</Form>
<h3>Embark Calls </h3>
<p>Javascript calls being made: </p>
<div className="logs">
{
this.state.embarkLogs.map((item, i) => <p key={i}>{item}</p>)
}
</div>
</React.Fragment>
);
}
}
export default ENS;

View File

@ -0,0 +1,258 @@
import EmbarkJS from 'Embark/EmbarkJS';
import React from 'react';
import {Alert, Form, FormGroup, FormControl, HelpBlock, Button} from 'react-bootstrap';
class Storage extends React.Component {
constructor(props) {
super(props);
this.state = {
textToSave: 'hello world!',
generatedHash: '',
loadText: '',
storedText: '',
fileToUpload: null,
fileHash: '',
imageToDownload: '',
url: '',
logs: [],
storageError: '',
valueRegister: '',
valueResolver: '',
};
}
handleChange(e, name) {
this.state[name] = e.target.value;
this.setState(this.state);
}
handleFileUpload(e) {
this.setState({fileToUpload: [e.target]});
}
addToLog(txt) {
this.state.logs.push(txt);
this.setState({logs: this.state.logs});
}
setText(e) {
e.preventDefault();
EmbarkJS.Storage.saveText(this.state.textToSave)
.then((hash) => {
this.setState({
generatedHash: hash,
loadText: hash,
storageError: ''
});
this.addToLog("EmbarkJS.Storage.saveText('" + this.state.textToSave + "').then(function(hash) { })");
})
.catch((err) => {
if (err) {
this.setState({storageError: err.message});
console.log("Storage saveText Error => " + err.message);
}
});
}
loadHash(e) {
e.preventDefault();
EmbarkJS.Storage.get(this.state.loadText)
.then((content) => {
this.setState({storedText: content, storageError: ''});
this.addToLog("EmbarkJS.Storage.get('" + this.state.loadText + "').then(function(content) { })");
})
.catch((err) => {
if (err) {
this.setState({storageError: err.message});
console.log("Storage get Error => " + err.message);
}
});
}
uploadFile(e) {
e.preventDefault();
EmbarkJS.Storage.uploadFile(this.state.fileToUpload)
.then((hash) => {
this.setState({
fileHash: hash,
imageToDownload: hash,
storageError: ''
});
this.addToLog("EmbarkJS.Storage.uploadFile(this.state.fileToUpload).then(function(hash) { })");
})
.catch((err) => {
if (err) {
this.setState({storageError: err.message});
console.log("Storage uploadFile Error => " + err.message);
}
});
}
loadFile(e) {
let _url = EmbarkJS.Storage.getUrl(this.state.imageToDownload);
this.setState({url: _url});
this.addToLog("EmbarkJS.Storage.getUrl('" + this.state.imageToDownload + "')");
}
ipnsRegister(e) {
e.preventDefault();
this.setState({ registering: true, responseRegister: false });
this.addToLog("EmbarkJS.Names.register(this.state.ipfsHash).then(function(hash) { })");
EmbarkJS.Names.register(this.state.valueRegister, (err, name) => {
let responseRegister;
let isRegisterError = false;
if (err) {
isRegisterError = true;
responseRegister = "Name Register Error: " + (err.message || err)
} else {
responseRegister = name;
}
this.setState({
registering: false,
responseRegister,
isRegisterError
});
});
}
ipnsResolve(e) {
e.preventDefault();
this.setState({ resolving: true, responseResolver: false });
this.addToLog("EmbarkJS.Names.resolve(this.state.ipnsName, function(err, path) { })");
EmbarkJS.Names.resolve(this.state.valueResolver, (err, path) => {
let responseResolver;
let isResolverError = false;
if (err) {
isResolverError = true;
responseResolver = "Name Resolve Error: " + (err.message || err)
} else {
responseResolver = path;
}
this.setState({
resolving: false,
responseResolver,
isResolverError
});
});
}
render() {
return <React.Fragment>
{
!this.props.enabled ?
<React.Fragment>
<Alert bsStyle="warning">The node you are using does not support IPFS. Please ensure <a
href="https://github.com/ipfs/js-ipfs-api#cors" target="_blank">CORS</a> is setup for the IPFS
node.</Alert>
</React.Fragment> : ''
}
{
this.state.storageError !== '' ?
<Alert bsStyle="danger">{this.state.storageError}</Alert>
: ''
}
<h3>Save text to storage</h3>
<Form inline>
<FormGroup>
<FormControl
type="text"
defaultValue={this.state.textToSave}
onChange={e => this.handleChange(e, 'textToSave')}/>
<Button bsStyle="primary" onClick={(e) => this.setText(e)}>Save Text</Button>
<HelpBlock>generated Hash: <span className="textHash">{this.state.generatedHash}</span></HelpBlock>
</FormGroup>
</Form>
<h3>Load text from storage given an hash</h3>
<Form inline>
<FormGroup>
<FormControl
type="text"
value={this.state.loadText}
onChange={e => this.handleChange(e, 'loadText')}/>
<Button bsStyle="primary" onClick={(e) => this.loadHash(e)}>Load</Button>
<HelpBlock>result: <span className="textHash">{this.state.storedText}</span></HelpBlock>
</FormGroup>
</Form>
<h3>Upload file to storage</h3>
<Form inline>
<FormGroup>
<FormControl
type="file"
onChange={(e) => this.handleFileUpload(e)}/>
<Button bsStyle="primary" onClick={(e) => this.uploadFile(e)}>Upload</Button>
<HelpBlock>generated hash: <span className="fileHash">{this.state.fileHash}</span></HelpBlock>
</FormGroup>
</Form>
<h3>Get file or image from storage</h3>
<Form inline>
<FormGroup>
<FormControl
type="text"
value={this.state.imageToDownload}
onChange={e => this.handleChange(e, 'imageToDownload')}/>
<Button bsStyle="primary" onClick={(e) => this.loadFile(e)}>Download</Button>
<HelpBlock>file available at: <span><a href={this.state.url}
target="_blank">{this.state.url}</a></span></HelpBlock>
<HelpBlock><img src={this.state.url}/></HelpBlock>
</FormGroup>
</Form>
<h3>Register to IPNS</h3>
<Form inline>
<FormGroup>
<FormControl
type="text"
value={this.state.valueRegister}
onChange={e => this.handleChange(e, 'valueRegister')}/>
<Button bsStyle="primary" onClick={(e) => this.ipnsRegister(e)}>
{this.state.registering ? 'Registering...' : 'Register' }
</Button>
<HelpBlock>It will take around 1 minute</HelpBlock>
{this.state.responseRegister &&
<Alert className="alert-result" bsStyle={this.state.isRegisterError ? 'danger' : 'success'}>
<span className="value">{this.state.responseRegister}</span>
</Alert>}
</FormGroup>
</Form>
<h3>Resolve name</h3>
<Form inline>
<FormGroup>
<FormControl
type="text"
value={this.state.valueResolver}
onChange={e => this.handleChange(e, 'valueResolver')}/>
<Button bsStyle="primary" onClick={(e) => this.ipnsResolve(e)}>
{this.state.resolving ? 'Resolving...' : 'Resolve' }
</Button>
<HelpBlock>It will take around 1 minute</HelpBlock>
{this.state.responseResolver &&
<Alert className="alert-result" bsStyle={this.state.isResolverError ? 'danger' : 'success'}>
<span className="value">{this.state.responseResolver}</span>
</Alert>}
</FormGroup>
</Form>
<p>Javascript calls being made: </p>
<div className="logs">
<p>EmbarkJS.Storage.setProvider('ipfs',{'{'}server: 'localhost', port: '5001'{'}'})</p>
{
this.state.logs.map((item, i) => <p key={i}>{item}</p>)
}
</div>
</React.Fragment>;
}
}
export default Storage;

View File

@ -0,0 +1,118 @@
import EmbarkJS from 'Embark/EmbarkJS';
import React from 'react';
import {Alert, Form, FormGroup, FormControl, Button} from 'react-bootstrap';
class Whisper extends React.Component {
constructor (props) {
super(props);
this.state = {
listenTo: '',
channel: '',
message: '',
subscribedChannels: [],
messageList: [],
logs: []
};
}
handleChange (e, name) {
this.state[name] = e.target.value;
this.setState(this.state);
}
sendMessage (e) {
e.preventDefault();
EmbarkJS.Messages.sendMessage({topic: this.state.channel, data: this.state.message});
this.addToLog("EmbarkJS.Messages.sendMessage({topic: '" + this.state.channel + "', data: '" + this.state.message + "'})");
}
listenToChannel (e) {
e.preventDefault();
const subscribedChannels = this.state.subscribedChannels;
subscribedChannels.push(<span>Subscribed to <b>{this.state.listenTo}</b>. Now try sending a message</span>);
this.setState({
subscribedChannels
});
EmbarkJS.Messages.listenTo({topic: [this.state.listenTo]}, (error, message) => {
const messageList = this.state.messageList;
if (error) {
messageList.push(<span className="alert-danger">Error: {error}</span>);
} else {
messageList.push(<span>Channel: <b>{message.topic}</b> | Message: <b>{message.data}</b></span>);
}
this.setState({
messageList
});
});
this.addToLog("EmbarkJS.Messages.listenTo({topic: ['" + this.state.listenTo + "']}).then(function(message) {})");
}
addToLog (txt) {
this.state.logs.push(txt);
this.setState({logs: this.state.logs});
}
render () {
return (
<React.Fragment>
{
!this.props.enabled ?
<React.Fragment>
<Alert bsStyle="warning">The node you are using does not support Whisper</Alert>
<Alert bsStyle="warning">The node uses an unsupported version of Whisper</Alert>
</React.Fragment> : ''
}
<h3>Listen To channel</h3>
<Form inline>
<FormGroup>
<FormControl
type="text"
defaultValue={this.state.listenTo}
placeholder="channel"
onChange={e => this.handleChange(e, 'listenTo')}/>
<Button bsStyle="primary" onClick={(e) => this.listenToChannel(e)}>Start Listening</Button>
<div id="subscribeList">
{this.state.subscribedChannels.map((item, i) => <p key={i}>{item}</p>)}
</div>
<p>messages received:</p>
<div id="messagesList">
{this.state.messageList.map((item, i) => <p key={i}>{item}</p>)}
</div>
</FormGroup>
</Form>
<h3>Send Message</h3>
<Form inline>
<FormGroup>
<FormControl
type="text"
defaultValue={this.state.channel}
placeholder="channel"
onChange={e => this.handleChange(e, 'channel')}/>
<FormControl
type="text"
defaultValue={this.state.message}
placeholder="message"
onChange={e => this.handleChange(e, 'message')}/>
<Button bsStyle="primary" onClick={(e) => this.sendMessage(e)}>Send Message</Button>
</FormGroup>
</Form>
<p>Javascript calls being made: </p>
<div className="logs">
<p>EmbarkJS.Messages.setProvider('whisper')</p>
{
this.state.logs.map((item, i) => <p key={i}>{item}</p>)
}
</div>
</React.Fragment>
);
}
}
export default Whisper;

View File

@ -0,0 +1,57 @@
div {
margin: 15px;
}
.logs {
background-color: black;
font-size: 14px;
color: white;
font-weight: bold;
padding: 10px;
border-radius: 8px;
}
.tab-content {
border-left: 1px solid #ddd;
border-right: 1px solid #ddd;
border-bottom: 1px solid #ddd;
padding: 10px;
margin: 0px;
}
.nav-tabs {
margin-bottom: 0;
}
.status-offline {
vertical-align: middle;
margin-left: 5px;
margin-top: 4px;
width: 12px;
height: 12px;
background: red;
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
border-radius: 10px;
}
.status-online {
vertical-align: middle;
margin-left: 5px;
margin-top: 4px;
width: 12px;
height: 12px;
background: mediumseagreen;
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
border-radius: 10px;
}
input.form-control {
margin-right: 5px;
}
.alert-result {
margin-left: 0;
}

View File

@ -0,0 +1,92 @@
import React from 'react';
import ReactDOM from 'react-dom';
import {Tabs, Tab} from 'react-bootstrap';
import EmbarkJS from 'Embark/EmbarkJS';
import Blockchain from './components/blockchain';
import Whisper from './components/whisper';
import Storage from './components/storage';
import ENS from './components/ens';
import './dapp.css';
class App extends React.Component {
constructor(props) {
super(props);
this.handleSelect = this.handleSelect.bind(this);
this.state = {
activeKey: 1,
whisperEnabled: false,
storageEnabled: false,
ensEnabled: false
};
}
componentDidMount() {
EmbarkJS.onReady(() => {
if (EmbarkJS.isNewWeb3()) {
EmbarkJS.Messages.Providers.whisper.getWhisperVersion((err, _version) => {
if (err) {
return console.log(err);
}
this.setState({whisperEnabled: true});
});
} else {
if (EmbarkJS.Messages.providerName === 'whisper') {
EmbarkJS.Messages.getWhisperVersion((err, _version) => {
if (err) {
return console.log(err);
}
this.setState({whisperEnabled: true});
});
}
}
this.setState({
storageEnabled: EmbarkJS.Storage.isAvailable(),
ensEnabled: EmbarkJS.Names.isAvailable(),
ensNameSystems: EmbarkJS.Names.currentNameSystems
});
});
}
_renderStatus(title, available) {
let className = available ? 'pull-right status-online' : 'pull-right status-offline';
return <React.Fragment>
{title}
<span className={className}></span>
</React.Fragment>;
}
handleSelect(key) {
if (key === 2) {
EmbarkJS.Names.setProvider('ipns', {server: 'localhost', port: '5001'});
} else if (key === 4) {
EmbarkJS.Names.currentNameSystems = this.state.ensNameSystems
}
this.setState({ activeKey: key });
}
render() {
return (<div><h3>Embark - Usage Example</h3>
<Tabs onSelect={this.handleSelect} activeKey={this.state.activeKey} id="uncontrolled-tab-example">
<Tab eventKey={1} title="Blockchain">
<Blockchain/>
</Tab>
<Tab eventKey={2} title={this._renderStatus('Decentralized Storage', this.state.storageEnabled)}>
<Storage enabled={this.state.storageEnabled}/>
</Tab>
<Tab eventKey={3} title={this._renderStatus('P2P communication (Whisper)', this.state.whisperEnabled)}>
<Whisper enabled={this.state.whisperEnabled}/>
</Tab>
<Tab eventKey={4} title={this._renderStatus('Naming (ENS)', this.state.ensEnabled)}>
<ENS enabled={this.state.ensEnabled}/>
</Tab>
</Tabs>
</div>);
}
}
ReactDOM.render(<App></App>, document.getElementById('app'));

View File

@ -0,0 +1,12 @@
<html>
<head>
<title>Embark - SimpleStorage Demo</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
</head>
<body class="container">
<div id="app">
</div>
<script src="js/dapp.js"></script>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,73 @@
module.exports = {
development: {
enabled: true,
networkType: "custom", // Can be: testnet, rinkeby, livenet or custom, in which case, it will use the specified networkId
networkId: "1337", // Network id used when networkType is custom
isDev: true, // Uses and ephemeral proof-of-authority network with a pre-funded developer account, mining enabled
datadir: ".embark/development/datadir", // Data directory for the databases and keystore
mineWhenNeeded: true, // Uses our custom script (if isDev is false) to mine only when needed
nodiscover: true, // Disables the peer discovery mechanism (manual peer addition)
maxpeers: 0, // Maximum number of network peers (network disabled if set to 0) (default: 25)
rpcHost: "localhost", // HTTP-RPC server listening interface (default: "localhost")
rpcPort: 8545, // HTTP-RPC server listening port (default: 8545)
rpcCorsDomain: "auto", // Comma separated list of domains from which to accept cross origin requests (browser enforced)
// When set to "auto", Embark will automatically set the cors to the address of the webserver
proxy: true, // Proxy is used to present meaningful information about transactions
targetGasLimit: 8000000, // Target gas limit sets the artificial target gas floor for the blocks to mine
wsRPC: true, // Enable the WS-RPC server
wsOrigins: "http://localhost:8000,http://localhost:8080,embark", // Origins from which to accept websockets requests
// When set to "auto", Embark will automatically set the cors to the address of the webserver
wsHost: "localhost", // WS-RPC server listening interface (default: "localhost")
wsPort: 8546, // WS-RPC server listening port (default: 8546)
simulatorMnemonic: "example exile argue silk regular smile grass bomb merge arm assist farm", // Mnemonic used by the simulator to generate a wallet
simulatorBlocktime: 0 // Specify blockTime in seconds for automatic mining. Default is 0 and no auto-mining.
},
privatenet: {
enabled: true,
networkType: "custom",
networkId: "1337",
isDev: false,
genesisBlock: "config/privatenet/genesis.json", // Genesis block to initiate on first creation of a development node
datadir: ".embark/privatenet/datadir",
mineWhenNeeded: true,
nodiscover: true,
maxpeers: 0,
rpcHost: "localhost",
rpcPort: 8545,
rpcCorsDomain: "auto",
proxy: true,
account: {
// "address": "", // When specified, uses that address instead of the default one for the network
password: "config/privatenet/password" // Password to unlock the account
},
targetGasLimit: 8000000,
wsRPC: true,
wsOrigins: "auto",
wsHost: "localhost",
wsPort: 8546,
simulatorMnemonic: "example exile argue silk regular smile grass bomb merge arm assist farm",
simulatorBlocktime: 0
},
testnet: {
enabled: true,
networkType: "testnet",
syncMode: "light",
rpcHost: "localhost",
rpcPort: 8545,
rpcCorsDomain: "http://localhost:8000",
account: {
password: "config/testnet/password"
}
},
livenet: {
enabled: true,
networkType: "livenet",
syncMode: "light",
rpcHost: "localhost",
rpcPort: 8545,
rpcCorsDomain: "http://localhost:8000",
account: {
password: "config/livenet/password"
}
}
};

View File

@ -0,0 +1,12 @@
module.exports = {
default: {
enabled: true,
provider: "whisper", // Communication provider. Currently, Embark only supports whisper
available_providers: ["whisper"], // Array of available providers
connection: {
host: "localhost", // Host of the blockchain node
port: 8546, // Port of the blockchain node
type: "ws" // Type of connection (ws or rpc)
}
}
};

View File

@ -0,0 +1,41 @@
module.exports = {
// default applies to all environments
default: {
// Blockchain node to deploy the contracts
deployment: {
host: "localhost", // Host of the blockchain node
port: 8545, // Port of the blockchain node
type: "rpc" // Type of connection (ws or rpc),
// Accounts to use instead of the default account to populate your wallet
/*,accounts: [
{
privateKey: "your_private_key",
balance: "5 ether" // You can set the balance of the account in the dev environment
// Balances are in Wei, but you can specify the unit with its name
},
{
privateKeyFile: "path/to/file" // You can put more than one key, separated by , or ;
},
{
mnemonic: "12 word mnemonic",
addressIndex: "0", // Optionnal. The index to start getting the address
numAddresses: "1", // Optionnal. The number of addresses to get
hdpath: "m/44'/60'/0'/0/" // Optionnal. HD derivation path
}
]*/
},
// order of connections the dapp should connect to
dappConnection: [
"$WEB3", // uses pre existing web3 object if available (e.g in Mist)
"ws://localhost:8546",
"http://localhost:8545"
],
gas: "auto",
contracts: {
SimpleStorage: {
fromIndex: 0,
args: [100]
}
}
}
};

View File

@ -0,0 +1,12 @@
module.exports = {
default: {
available_providers: ["ens", "ipns"],
provider: "ens",
register: {
rootDomain: "embark.eth",
subdomains: {
'status': '0x1a2f3b98e434c02363f3dac3174af93c1d690914'
}
}
}
};

View File

@ -0,0 +1,18 @@
{
"config": {
"homesteadBlock": 0,
"byzantiumBlock": 0,
"daoForkSupport": true
},
"nonce": "0x0000000000000042",
"difficulty": "0x0",
"alloc": {
"0x3333333333333333333333333333333333333333": {"balance": "15000000000000000000"}
},
"mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"coinbase": "0x3333333333333333333333333333333333333333",
"timestamp": "0x00",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"extraData": "0x",
"gasLimit": "0x7a1200"
}

View File

@ -0,0 +1 @@
dev_password

View File

@ -0,0 +1,35 @@
module.exports = {
default: {
enabled: true,
ipfs_bin: "ipfs",
provider: "ipfs",
available_providers: ["ipfs"],
upload: {
host: "localhost",
port: 5001
},
dappConnection: [
{
provider:"ipfs",
host: "localhost",
port: 5001,
getUrl: "http://localhost:8080/ipfs/"
}
]
// Configuration to start Swarm in the same terminal as `embark run`
/*,account: {
address: "YOUR_ACCOUNT_ADDRESS", // Address of account accessing Swarm
password: "PATH/TO/PASSWORD/FILE" // File containing the password of the account
},
swarmPath: "PATH/TO/SWARM/EXECUTABLE" // Path to swarm executable (default: swarm)*/
},
development: {
enabled: true,
provider: "ipfs",
upload: {
host: "localhost",
port: 5001,
getUrl: "http://localhost:8080/ipfs/"
}
}
};

View File

@ -0,0 +1 @@
test_password

View File

@ -0,0 +1,5 @@
module.exports = {
enabled: true,
host: "localhost",
port: 8000
};

View File

@ -0,0 +1,21 @@
pragma solidity ^0.4.23;
contract Loops {
uint public storedData;
constructor(uint initialValue) public {
storedData = initialValue;
}
function set(uint x) public {
for(uint i = storedData; i < x; i++) {
uint newValue = storedData + x;
storedData = newValue;
}
}
function get() public view returns (uint retVal) {
return storedData;
}
}

View File

@ -0,0 +1,17 @@
pragma solidity ^0.4.23;
contract SimpleStorage {
uint public storedData;
constructor(uint initialValue) public {
storedData = initialValue;
}
function set(uint x) public {
storedData = x;
}
function get() public view returns (uint retVal) {
return storedData;
}
}

View File

@ -0,0 +1,17 @@
{
"contracts": ["contracts/**"],
"app": {
"js/dapp.js": ["app/dapp.js"],
"index.html": "app/index.html",
"images/": ["app/images/**"]
},
"buildDir": "dist/",
"config": "config/",
"versions": {
"web3": "1.0.0-beta",
"solc": "0.4.24",
"ipfs-api": "17.2.4"
},
"plugins": {
}
}

278
test_apps/coverage_app/package-lock.json generated Normal file
View File

@ -0,0 +1,278 @@
{
"name": "app_name",
"version": "0.0.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"asap": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
"integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
},
"babel-runtime": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
"integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
"requires": {
"core-js": "^2.4.0",
"regenerator-runtime": "^0.11.0"
},
"dependencies": {
"core-js": {
"version": "2.5.7",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz",
"integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw=="
}
}
},
"classnames": {
"version": "2.2.6",
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz",
"integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q=="
},
"core-js": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
"integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY="
},
"dom-helpers": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.3.1.tgz",
"integrity": "sha512-2Sm+JaYn74OiTM2wHvxJOo3roiq/h25Yi69Fqk269cNUwIXsCvATB6CRSFC9Am/20G2b28hGv/+7NiWydIrPvg=="
},
"encoding": {
"version": "0.1.12",
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
"integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=",
"requires": {
"iconv-lite": "~0.4.13"
}
},
"fbjs": {
"version": "0.8.17",
"resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz",
"integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=",
"requires": {
"core-js": "^1.0.0",
"isomorphic-fetch": "^2.1.1",
"loose-envify": "^1.0.0",
"object-assign": "^4.1.0",
"promise": "^7.1.1",
"setimmediate": "^1.0.5",
"ua-parser-js": "^0.7.18"
}
},
"iconv-lite": {
"version": "0.4.23",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
"integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
},
"invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
"requires": {
"loose-envify": "^1.0.0"
}
},
"is-stream": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
"integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
},
"isomorphic-fetch": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz",
"integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=",
"requires": {
"node-fetch": "^1.0.1",
"whatwg-fetch": ">=0.10.0"
}
},
"js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
},
"keycode": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/keycode/-/keycode-2.2.0.tgz",
"integrity": "sha1-PQr1bce4uOXLqNCpfxByBO7CKwQ="
},
"loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"requires": {
"js-tokens": "^3.0.0 || ^4.0.0"
}
},
"node-fetch": {
"version": "1.7.3",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz",
"integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==",
"requires": {
"encoding": "^0.1.11",
"is-stream": "^1.0.1"
}
},
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
},
"promise": {
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
"integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
"requires": {
"asap": "~2.0.3"
}
},
"prop-types": {
"version": "15.6.2",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz",
"integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==",
"requires": {
"loose-envify": "^1.3.1",
"object-assign": "^4.1.1"
}
},
"prop-types-extra": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.0.tgz",
"integrity": "sha512-QFyuDxvMipmIVKD2TwxLVPzMnO4e5oOf1vr3tJIomL8E7d0lr6phTHd5nkPhFIzTD1idBLLEPeylL9g+rrTzRg==",
"requires": {
"react-is": "^16.3.2",
"warning": "^3.0.0"
}
},
"react": {
"version": "16.4.2",
"resolved": "https://registry.npmjs.org/react/-/react-16.4.2.tgz",
"integrity": "sha512-dMv7YrbxO4y2aqnvA7f/ik9ibeLSHQJTI6TrYAenPSaQ6OXfb+Oti+oJiy8WBxgRzlKatYqtCjphTgDSCEiWFg==",
"requires": {
"fbjs": "^0.8.16",
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.0"
}
},
"react-bootstrap": {
"version": "0.32.1",
"resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-0.32.1.tgz",
"integrity": "sha512-RbfzKUbsukWsToWqGHfCCyMFq9QQI0TznutdyxyJw6dih2NvIne25Mrssg8LZsprqtPpyQi8bN0L0Fx3fUsL8Q==",
"requires": {
"babel-runtime": "^6.11.6",
"classnames": "^2.2.5",
"dom-helpers": "^3.2.0",
"invariant": "^2.2.1",
"keycode": "^2.1.2",
"prop-types": "^15.5.10",
"prop-types-extra": "^1.0.1",
"react-overlays": "^0.8.0",
"react-prop-types": "^0.4.0",
"react-transition-group": "^2.0.0",
"uncontrollable": "^4.1.0",
"warning": "^3.0.0"
}
},
"react-dom": {
"version": "16.4.2",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.4.2.tgz",
"integrity": "sha512-Usl73nQqzvmJN+89r97zmeUpQDKDlh58eX6Hbs/ERdDHzeBzWy+ENk7fsGQ+5KxArV1iOFPT46/VneklK9zoWw==",
"requires": {
"fbjs": "^0.8.16",
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.0"
}
},
"react-is": {
"version": "16.4.2",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.4.2.tgz",
"integrity": "sha512-rI3cGFj/obHbBz156PvErrS5xc6f1eWyTwyV4mo0vF2lGgXgS+mm7EKD5buLJq6jNgIagQescGSVG2YzgXt8Yg=="
},
"react-lifecycles-compat": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
},
"react-overlays": {
"version": "0.8.3",
"resolved": "https://registry.npmjs.org/react-overlays/-/react-overlays-0.8.3.tgz",
"integrity": "sha512-h6GT3jgy90PgctleP39Yu3eK1v9vaJAW73GOA/UbN9dJ7aAN4BTZD6793eI1D5U+ukMk17qiqN/wl3diK1Z5LA==",
"requires": {
"classnames": "^2.2.5",
"dom-helpers": "^3.2.1",
"prop-types": "^15.5.10",
"prop-types-extra": "^1.0.1",
"react-transition-group": "^2.2.0",
"warning": "^3.0.0"
}
},
"react-prop-types": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/react-prop-types/-/react-prop-types-0.4.0.tgz",
"integrity": "sha1-+ZsL+0AGkpya8gUefBQUpcdbk9A=",
"requires": {
"warning": "^3.0.0"
}
},
"react-transition-group": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.4.0.tgz",
"integrity": "sha512-Xv5d55NkJUxUzLCImGSanK8Cl/30sgpOEMGc5m86t8+kZwrPxPCPcFqyx83kkr+5Lz5gs6djuvE5By+gce+VjA==",
"requires": {
"dom-helpers": "^3.3.1",
"loose-envify": "^1.3.1",
"prop-types": "^15.6.2",
"react-lifecycles-compat": "^3.0.4"
}
},
"regenerator-runtime": {
"version": "0.11.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
},
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"setimmediate": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
"integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU="
},
"ua-parser-js": {
"version": "0.7.18",
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.18.tgz",
"integrity": "sha512-LtzwHlVHwFGTptfNSgezHp7WUlwiqb0gA9AALRbKaERfxwJoiX0A73QbTToxteIAuIaFshhgIZfqK8s7clqgnA=="
},
"uncontrollable": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-4.1.0.tgz",
"integrity": "sha1-4DWCkSUuGGUiLZCTmxny9J+Bwak=",
"requires": {
"invariant": "^2.1.0"
}
},
"warning": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz",
"integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=",
"requires": {
"loose-envify": "^1.0.0"
}
},
"whatwg-fetch": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz",
"integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng=="
}
}
}

View File

@ -0,0 +1,19 @@
{
"name": "coverage_app",
"version": "0.0.1",
"description": "",
"main": "Gruntfile.js",
"scripts": {
"test": "../../bin/embark test",
"coverage": "istanbul report --root .embark --format html"
},
"author": "",
"license": "ISC",
"homepage": "",
"dependencies": {
"react": "^16.3.2",
"react-bootstrap": "^0.32.1",
"react-dom": "^16.3.2",
"istanbul": "^0.4.5"
}
}

View File

@ -0,0 +1,28 @@
/*global contract, config, it, assert*/
const Loops = require('Embark/contracts/Loops');
config({
contracts: {
"Loops": {
args: [1]
}
}
});
contract("Loops", function() {
it("should set constructor value", function(done) {
Loops.methods.storedData().call().then((result) => {
assert.strictEqual(parseInt(result, 10), 1);
done();
});
});
it("set storage value", function(done) {
Loops.methods.set(5).send().then(() => {
Loops.methods.get().call().then((result) => {
assert.strictEqual(parseInt(result, 10), 21);
done();
});
});
});
});

View File

@ -0,0 +1,28 @@
/*global contract, config, it, assert*/
const SimpleStorage = require('Embark/contracts/SimpleStorage');
config({
contracts: {
"SimpleStorage": {
args: [100]
}
}
});
contract("SimpleStorage", function() {
it("should set constructor value", function(done) {
SimpleStorage.methods.storedData().call().then((result) => {
assert.strictEqual(parseInt(result, 10), 100);
done();
});
});
it("set storage value", function(done) {
SimpleStorage.methods.set(150).send().then(() => {
SimpleStorage.methods.get().call().then((result) => {
assert.strictEqual(parseInt(result, 10), 150);
done();
});
});
});
});