import * as async from 'async' import Web3 from 'web3'; import * as assert from 'assert' import { Provider } from '@remix-project/remix-simulator' import { compileFileOrFiles } from '../src/compiler' import { deployAll } from '../src/deployer' import { runTest, compilationInterface } from '../src/index' import { ResultsInterface, TestCbInterface, ResultCbInterface } from '../src/index' // deepEqualExcluding allows us to exclude specific keys whose values vary. // In this specific test, we'll use this helper to exclude `time` keys. // Assertions for the existance of these will be made at the correct places. function deepEqualExcluding(a: any, b: any, excludedKeys: string[]) { function removeKeysFromObject(obj: any, excludedKeys: string[]) { if (obj !== Object(obj)) { return obj } if(Object.prototype.toString.call(obj) !== '[object Array]') { obj = Object.assign({}, obj) for (const key of excludedKeys) { delete obj[key] } return obj } let newObj = [] for (const idx in obj) { newObj[idx] = removeKeysFromObject(obj[idx], excludedKeys); } return newObj } let aStripped: any = removeKeysFromObject(a, excludedKeys); let bStripped: any = removeKeysFromObject(b, excludedKeys); assert.deepEqual(aStripped, bStripped) } let accounts: string[] let provider: any = new Provider() async function compileAndDeploy(filename: string, callback: Function) { let web3: Web3 = new Web3() let sourceASTs: any = {} await provider.init() web3.setProvider(provider) let compilationData: object async.waterfall([ function getAccountList(next: Function): void { web3.eth.getAccounts((_err: Error | null | undefined, _accounts: string[]) => { accounts = _accounts web3.eth.defaultAccount = accounts[0] next(_err) }) }, function compile(next: Function): void { compileFileOrFiles(filename, false, { accounts }, next) }, function deployAllContracts(compilationResult: compilationInterface, asts, next: Function): void { for(const filename in asts) { if(filename.endsWith('_test.sol')) sourceASTs[filename] = asts[filename].ast } try { compilationData = compilationResult deployAll(compilationResult, web3, false, next) } catch (e) { throw e } } ], function (_err: Error | null | undefined, contracts: any): void { callback(null, compilationData, contracts, sourceASTs, accounts) }) } describe('testRunner', () => { let tests: any[] = [], results: ResultsInterface; const testCallback: TestCbInterface = (err, test) => { if (err) { throw err } if (test.type === 'testPass' || test.type === 'testFailure') { assert.ok(test.time, 'test time not reported') assert.ok(!Number.isInteger(test.time || 0), 'test time should not be an integer') } tests.push(test) } const resultsCallback: Function = (done) => { return (err, _results) => { if (err) { throw err } results = _results done() } } describe('#runTest', () => { describe('assert library OK method tests', () => { const filename: string = __dirname + '/examples_0/assert_ok_test.sol' beforeAll((done) => { compileAndDeploy(filename, (_err: Error | null | undefined, compilationData: object, contracts: any, asts: any, accounts: string[]) => { runTest('AssertOkTest', contracts.AssertOkTest, compilationData[filename]['AssertOkTest'], asts[filename], { accounts }, testCallback, resultsCallback(done)) }) }) afterAll(() => { tests = [] }) it('should have 1 passing test', () => { assert.equal(results.passingNum, 1) }) it('should have 1 failing test', () => { assert.equal(results.failureNum, 1) }) it('should return', () => { deepEqualExcluding(tests, [ { type: 'accountList', value: accounts }, { type: 'contract', value: 'AssertOkTest', filename: __dirname + '/examples_0/assert_ok_test.sol' }, { type: 'testPass', value: 'Ok pass test', context: 'AssertOkTest' }, { type: 'testFailure', value: 'Ok fail test', errMsg: 'okFailTest fails', context: 'AssertOkTest', expected: 'true', returned: 'false'}, ], ['time']) }) }) describe('assert library EQUAL method tests', () => { const filename: string = __dirname + '/examples_0/assert_equal_test.sol' beforeAll((done) => { compileAndDeploy(filename, (_err: Error | null | undefined, compilationData: object, contracts: any, asts: any, accounts: string[]) => { runTest('AssertEqualTest', contracts.AssertEqualTest, compilationData[filename]['AssertEqualTest'], asts[filename], { accounts }, testCallback, resultsCallback(done)) }) }) afterAll(() => { tests = [] }) it('should have 1 passing test', () => { assert.equal(results.passingNum, 1) }) it('should have 1 failing test', () => { assert.equal(results.failureNum, 1) }) it('should return', () => { deepEqualExcluding(tests, [ { type: 'accountList', value: accounts }, { type: 'contract', value: 'AssertEqualTest', filename: __dirname + '/examples_0/assert_equal_test.sol' }, { type: 'testPass', value: 'Equal uint pass test', context: 'AssertEqualTest' }, { type: 'testFailure', value: 'Equal uint fail test', errMsg: 'equalUintFailTest fails', context: 'AssertEqualTest', expected: '2', returned: '1'} ], ['time']) }) }) describe('assert library NOTEQUAL method tests', () => { const filename: string = __dirname + '/examples_0/assert_notEqual_test.sol' beforeAll((done) => { compileAndDeploy(filename, (_err: Error | null | undefined, compilationData: object, contracts: any, asts: any, accounts: string[]) => { runTest('AssertNotEqualTest', contracts.AssertNotEqualTest, compilationData[filename]['AssertNotEqualTest'], asts[filename], { accounts }, testCallback, resultsCallback(done)) }) }) afterAll(() => { tests = [] }) it('should have 1 passing test', () => { assert.equal(results.passingNum, 1) }) it('should have 1 failing test', () => { assert.equal(results.failureNum, 1) }) it('should return', () => { deepEqualExcluding(tests, [ { type: 'accountList', value: accounts }, { type: 'contract', value: 'AssertNotEqualTest', filename: __dirname + '/examples_0/assert_notEqual_test.sol' }, { type: 'testPass', value: 'Not equal uint pass test', context: 'AssertNotEqualTest' }, { type: 'testFailure', value: 'Not equal uint fail test', errMsg: 'notEqualUintFailTest fails', context: 'AssertNotEqualTest', expected: '1', returned: '1'} ], ['time']) }) }) describe('assert library GREATERTHAN method tests', () => { const filename: string = __dirname + '/examples_0/assert_greaterThan_test.sol' beforeAll((done) => { compileAndDeploy(filename, (_err: Error | null | undefined, compilationData: object, contracts: any, asts: any, accounts: string[]) => { runTest('AssertGreaterThanTest', contracts.AssertGreaterThanTest, compilationData[filename]['AssertGreaterThanTest'], asts[filename], { accounts }, testCallback, resultsCallback(done)) }) }) afterAll(() => { tests = [] }) it('should have 1 passing test', () => { assert.equal(results.passingNum, 1) }) it('should have 1 failing test', () => { assert.equal(results.failureNum, 1) }) it('should return', () => { deepEqualExcluding(tests, [ { type: 'accountList', value: accounts }, { type: 'contract', value: 'AssertGreaterThanTest', filename: __dirname + '/examples_0/assert_greaterThan_test.sol' }, { type: 'testPass', value: 'Greater than uint pass test', context: 'AssertGreaterThanTest' }, { type: 'testFailure', value: 'Greater than uint fail test', errMsg: 'greaterThanUintFailTest fails', context: 'AssertGreaterThanTest', expected: '4', returned: '1'} ], ['time']) }) }) describe('assert library LESSERTHAN method tests', () => { const filename: string = __dirname + '/examples_0/assert_lesserThan_test.sol' beforeAll((done) => { compileAndDeploy(filename, (_err: Error | null | undefined, compilationData: object, contracts: any, asts: any, accounts: string[]) => { runTest('AssertLesserThanTest', contracts.AssertLesserThanTest, compilationData[filename]['AssertLesserThanTest'], asts[filename], { accounts }, testCallback, resultsCallback(done)) }) }) afterAll(() => { tests = [] }) it('should have 1 passing test', () => { assert.equal(results.passingNum, 1) }) it('should have 1 failing test', () => { assert.equal(results.failureNum, 1) }) it('should return', () => { deepEqualExcluding(tests, [ { type: 'accountList', value: accounts }, { type: 'contract', value: 'AssertLesserThanTest', filename: __dirname + '/examples_0/assert_lesserThan_test.sol' }, { type: 'testPass', value: 'Lesser than uint pass test', context: 'AssertLesserThanTest' }, { type: 'testFailure', value: 'Lesser than uint fail test', errMsg: 'lesserThanUintFailTest fails', context: 'AssertLesserThanTest', expected: '2', returned: '4'} ], ['time']) }) }) describe('test with beforeAll', () => { const filename: string = __dirname + '/examples_1/simple_storage_test.sol' beforeAll((done) => { compileAndDeploy(filename, (_err: Error | null | undefined, compilationData: object, contracts: any, asts: any, accounts: string[]) => { runTest('MyTest', contracts.MyTest, compilationData[filename]['MyTest'], asts[filename], { accounts }, testCallback, resultsCallback(done)) }) }) afterAll(() => { tests = [] }) it('should have 3 passing test', () => { assert.equal(results.passingNum, 3) }) it('should have 1 failing test', () => { assert.equal(results.failureNum, 1) }) it('should return 6 messages', () => { deepEqualExcluding(tests, [ { type: 'accountList', value: accounts }, { type: 'contract', value: 'MyTest', filename: __dirname + '/examples_1/simple_storage_test.sol' }, { type: 'testPass', value: 'Initial value should be100', context: 'MyTest' }, { type: 'testPass', value: 'Initial value should not be200', context: 'MyTest' }, { type: 'testFailure', value: 'Should trigger one fail', errMsg: 'uint test 1 fails', context: 'MyTest', expected: '2', returned: '1'}, { type: 'testPass', value: 'Should trigger one pass', context: 'MyTest' } ], ['time']) }) }) describe('test with beforeEach', () => { const filename: string = __dirname + '/examples_2/simple_storage_test.sol' beforeAll(done => { compileAndDeploy(filename, function (_err: Error | null | undefined, compilationData: object, contracts: any, asts: any, accounts: string[]) { runTest('MyTest', contracts.MyTest, compilationData[filename]['MyTest'], asts[filename], { accounts }, testCallback, resultsCallback(done)) }) }) afterAll(() => { tests = [] }) it('should have 2 passing tests', () => { assert.equal(results.passingNum, 2) }) it('should 0 failing tests', () => { assert.equal(results.failureNum, 0) }) it('should return 4 messages', () => { deepEqualExcluding(tests, [ { type: 'accountList', value: accounts }, { type: 'contract', value: 'MyTest', filename: __dirname + '/examples_2/simple_storage_test.sol' }, { type: 'testPass', value: 'Initial value should be100', context: 'MyTest' }, { type: 'testPass', value: 'Value is set200', context: 'MyTest' } ], ['time']) }) }) // Test string equality describe('test string equality', () => { const filename: string = __dirname + '/examples_3/simple_string_test.sol' beforeAll(done => { compileAndDeploy(filename, function (_err: Error | null | undefined, compilationData: object, contracts: any, asts: any, accounts: string[]) { runTest('StringTest', contracts.StringTest, compilationData[filename]['StringTest'], asts[filename], { accounts }, testCallback, resultsCallback(done)) }) }) afterAll(() => { tests = [] }) it('should 2 passing tests', () => { assert.equal(results.passingNum, 2) }) it('should return 4 messages', () => { deepEqualExcluding(tests, [ { type: 'accountList', value: accounts }, { type: 'contract', value: 'StringTest', filename: __dirname + '/examples_3/simple_string_test.sol' }, { type: 'testPass', value: 'Initial value should be hello world', context: 'StringTest' }, { type: 'testPass', value: 'Value should not be hello wordl', context: 'StringTest' } ], ['time']) }) }) // Test multiple directory import in test contract describe('test multiple directory import in test contract', () => { const filename: string = __dirname + '/examples_5/test/simple_storage_test.sol' beforeAll(done => { compileAndDeploy(filename, function (_err: Error | null | undefined, compilationData: object, contracts: any, asts: any, accounts: string[]) { runTest('StorageResolveTest', contracts.StorageResolveTest, compilationData[filename]['StorageResolveTest'], asts[filename], { accounts }, testCallback, resultsCallback(done)) }) }) afterAll(() => { tests = [] }) it('should 3 passing tests', () => { assert.equal(results.passingNum, 3) }) it('should return 4 messages', () => { deepEqualExcluding(tests, [ { type: 'accountList', value: accounts }, { type: 'contract', value: 'StorageResolveTest', filename: __dirname + '/examples_5/test/simple_storage_test.sol' }, { type: 'testPass', value: 'Initial value should be100', context: 'StorageResolveTest' }, { type: 'testPass', value: 'Check if even', context: 'StorageResolveTest' }, { type: 'testPass', value: 'Check if odd', context: 'StorageResolveTest' } ], ['time']) }) }) //Test signed/unsigned integer weight describe('test number weight', () => { const filename: string = __dirname + '/number/number_test.sol' beforeAll(done => { compileAndDeploy(filename, function (_err: Error | null | undefined, compilationData: object, contracts: any, asts: any, accounts: string[]) { runTest('IntegerTest', contracts.IntegerTest, compilationData[filename]['IntegerTest'], asts[filename], { accounts }, testCallback, resultsCallback(done)) }) }) afterAll(() => { tests = [] }) it('should have 6 passing tests', () => { assert.equal(results.passingNum, 6) }) it('should have 2 failing tests', () => { assert.equal(results.failureNum, 2) }) }) // Test Transaction with custom sender & value describe('various sender', () => { const filename: string = __dirname + '/various_sender/sender_and_value_test.sol' beforeAll(done => { compileAndDeploy(filename, function (_err: Error | null | undefined, compilationData: object, contracts: any, asts: any, accounts: string[]) { runTest('SenderAndValueTest', contracts.SenderAndValueTest, compilationData[filename]['SenderAndValueTest'], asts[filename], { accounts }, testCallback, resultsCallback(done)) }) }) afterAll(() => { tests = [] }) it('should have 17 passing tests', () => { assert.equal(results.passingNum, 17) }) it('should have 0 failing tests', () => { assert.equal(results.failureNum, 0) }) }) // Test `runTest` method without sending contract object (should throw error) describe('runTest method without contract json interface', () => { const filename: string = __dirname + '/various_sender/sender_and_value_test.sol' const errorCallback: Function = (done) => { return (err, _results) => { if (err && err.message.includes('Contract interface not available')) { results = _results done() } else throw err } } beforeAll(done => { compileAndDeploy(filename, function (_err: Error | null | undefined, compilationData: object, contracts: any, asts: any, accounts: string[]) { runTest('SenderAndValueTest', undefined, compilationData[filename]['SenderAndValueTest'], asts[filename], { accounts }, testCallback, errorCallback(done)) }) }) it('should have 0 passing tests', () => { assert.equal(results.passingNum, 0) }) it('should have 0 failing tests', () => { assert.equal(results.failureNum, 0) }) }) }) })