Skip to content

Commit

Permalink
Type declarations for chai matchers (TrueFiEng#67)
Browse files Browse the repository at this point in the history
* Move matchers to its own folder

* Add type declarations for new chai matchers

* Rewrite example tests in typescript

* Fix linter errors
  • Loading branch information
sz-piotr authored and marekkirejczyk committed Jan 25, 2019
1 parent c0683a7 commit 98f8c5f
Show file tree
Hide file tree
Showing 11 changed files with 128 additions and 60 deletions.
10 changes: 5 additions & 5 deletions lib/matchers.ts → lib/matchers/matchers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import overwriteBigNumberFunction from './matchers/overwriteBigNumberFunction';
import overwriteBigNumberFunction from './overwriteBigNumberFunction';
import {bigNumberify} from 'ethers/utils';
import {getBalanceChange, getBalanceChanges} from './utils';
import {getBalanceChange, getBalanceChanges} from '../utils';
import {Contract, Wallet} from 'ethers';

const solidity = (chai: any, utils: any) => {
Expand Down Expand Up @@ -38,10 +38,10 @@ const solidity = (chai: any, utils: any) => {
);
this.then = derivedPromise.then.bind(derivedPromise);
this.catch = derivedPromise.catch.bind(derivedPromise);
return derivedPromise;
return this;
});

Assertion.addMethod('revertedWith', function (revertReason: any) {
Assertion.addMethod('revertedWith', function (revertReason: string) {
/* eslint-disable no-underscore-dangle */
const promise = this._obj;
const derivedPromise = promise.then(
Expand All @@ -66,7 +66,7 @@ const solidity = (chai: any, utils: any) => {
);
this.then = derivedPromise.then.bind(derivedPromise);
this.catch = derivedPromise.catch.bind(derivedPromise);
return derivedPromise;
return this;
});

const filterLogsWithTopics = (logs: any[], topic: any) =>
Expand Down
27 changes: 27 additions & 0 deletions lib/matchers/matchertypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/* tslint:disable */
/// <reference types="chai" />
// I cannot get ethers types to work for some reason

declare namespace Chai {
interface Assertion extends LanguageChains, NumericComparison, TypeComparison {
reverted: AsyncAssertion;
revertedWith(reason: string): AsyncAssertion;
emit(contract: any, eventName: string): EmitAssertion;
properHex(length: number): void;
properPrivateKey: void;
properAddress: void;
changeBalance(wallet: any, balance: any): AsyncAssertion;
changeBalances(wallets: any[], balances: any[]): AsyncAssertion;
}

interface NumberComparer {
(value: any, message?: string): Assertion;
}

interface AsyncAssertion extends Assertion, Promise<void> {
}

interface EmitAssertion extends AsyncAssertion {
withArgs(...args: any[]): AsyncAssertion;
}
}
3 changes: 2 additions & 1 deletion lib/waffle.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import Ganache, { GanacheOpts } from 'ganache-core';
import {ContractFactory, providers, Contract, Wallet} from 'ethers';
import matchers from './matchers';
import matchers from './matchers/matchers';
import defaultAccounts from './config/defaultAccounts';
import defaultDeployOptions from './config/defaultDeployOptions';
import {linkSolidity4, linkSolidity5, LinkableContract} from './link';
import './matchers/matchertypes';

const defaultGanacheOptions = {accounts: defaultAccounts};

Expand Down
24 changes: 15 additions & 9 deletions test/example/example.js → test/example/example.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
import chai from 'chai';
import {createMockProvider, deployContract, getWallets, link} from '../../lib/waffle';
import BasicTokenMock from './build/BasicTokenMock';
import MyLibrary from './build/MyLibrary';
import LibraryConsumer from './build/LibraryConsumer';
import solidity from '../../lib/matchers';
import {
createMockProvider,
deployContract,
getWallets,
link,
solidity
} from '../../lib/waffle';
import BasicTokenMock from './build/BasicTokenMock.json';
import MyLibrary from './build/MyLibrary.json';
import LibraryConsumer from './build/LibraryConsumer.json';
import { Contract } from 'ethers';

chai.use(solidity);
const {expect} = chai;

describe('INTEGRATION: Example', () => {
let provider = createMockProvider();
let [wallet, walletTo] = getWallets(provider);
let token;
const provider = createMockProvider();
const [wallet, walletTo] = getWallets(provider);
let token: Contract;

beforeEach(async () => {
token = await deployContract(wallet, BasicTokenMock, [wallet.address, 1000]);
Expand Down Expand Up @@ -42,7 +48,7 @@ describe('INTEGRATION: Example', () => {
.to.be.reverted;
});

it('should use library to add 7', async() => {
it('should use library to add 7', async () => {
const myLibrary = await deployContract(wallet, MyLibrary, []);
link(LibraryConsumer, 'test/projects/example/MyLibrary.sol:MyLibrary', myLibrary.address);
const libraryConsumer = await deployContract(wallet, LibraryConsumer, []);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import {expect} from 'chai';
import {loadFixture, deployContract} from '../../lib/waffle';
import BasicTokenMock from './build/BasicTokenMock';
import BasicTokenMock from './build/BasicTokenMock.json';
import { Wallet, providers } from 'ethers';

describe('INTEGRATION: Fixtures example', () => {
async function fixture(provider, [wallet, other]) {
async function fixture(provider: providers.Provider, [wallet, other]: Wallet[]) {
const token = await deployContract(wallet, BasicTokenMock, [
wallet.address, 1000
]);
Expand Down
45 changes: 25 additions & 20 deletions test/matchers/balance.js → test/matchers/balance.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,13 @@
import chai, {AssertionError} from 'chai';
import {createMockProvider, getWallets, solidity} from '../../lib/waffle';
import {utils} from 'ethers';
import {utils, Wallet} from 'ethers';

chai.use(solidity);
const {expect} = chai;

describe('INTEGRATION: Balance observers', () => {
let provider;
let sender;
let receiver;

beforeEach(async () => {
provider = createMockProvider();
[sender, receiver] = getWallets(provider);
});
const provider = createMockProvider();
const [sender, receiver] = getWallets(provider);

describe('Change balance, one account', () => {
it('Should pass when expected balance change is passed as string and is equal to an actual', async () => {
Expand Down Expand Up @@ -60,7 +54,10 @@ describe('INTEGRATION: Balance observers', () => {
value: 200
}))
.to.changeBalance(sender, '-500')
).to.be.eventually.rejectedWith(AssertionError, `Expected "${sender.address}" to change balance by -500 wei, but it has changed by -200 wei`);
).to.be.eventually.rejectedWith(
AssertionError,
`Expected "${sender.address}" to change balance by -500 wei, but it has changed by -200 wei`
);
});

it('Should throw in negative case when expected balance change value was equal to an actual', async () => {
Expand All @@ -74,12 +71,8 @@ describe('INTEGRATION: Balance observers', () => {
});

it('Should throw when not a callback is passed to expect', async () => {
expect(() =>
expect(sender.sendTransaction({
to: receiver.address,
gasPrice: 0,
value: 200
})).to.changeBalance(sender, '-200')
await expect(() =>
expect(1).to.changeBalance(sender, '-200')
).to.throw(Error, `Expect subject should be a callback returning the Promise
e.g.: await expect(() => wallet.send({to: '0xb', value: 200})).to.changeBalance('0xa', -200)`);
});
Expand All @@ -95,7 +88,7 @@ describe('INTEGRATION: Balance observers', () => {
.to.changeBalances([sender, receiver], ['-200', 200]);
});

it('Should pass on negative case when one of expected balance changes is not equal to an actual value', async () => {
it('Should pass when negated and numbers don\'t match', async () => {
await expect(() => sender.sendTransaction({
to: receiver.address,
gasPrice: 0,
Expand All @@ -118,15 +111,23 @@ describe('INTEGRATION: Balance observers', () => {
value: 200
}))
.to.changeBalances([sender, receiver], [-200, 201])
).to.be.eventually.rejectedWith(AssertionError, 'Expected 0x17ec8597ff92C3F44523bDc65BF0f1bE632917ff,0x63FC2aD3d021a4D7e64323529a55a9442C444dA0 to change balance by -200,201 wei, but it has changed by -200,200 wei');
).to.be.eventually.rejectedWith(
AssertionError,
'Expected 0x17ec8597ff92C3F44523bDc65BF0f1bE632917ff,0x63FC2aD3d021a4D7e64323529a55a9442C444dA0 ' +
'to change balance by -200,201 wei, but it has changed by -200,200 wei'
);
await expect(
expect(() => sender.sendTransaction({
to: receiver.address,
gasPrice: 0,
value: 200
}))
.to.changeBalances([sender, receiver], [-201, 200])
).to.be.eventually.rejectedWith(AssertionError, 'Expected 0x17ec8597ff92C3F44523bDc65BF0f1bE632917ff,0x63FC2aD3d021a4D7e64323529a55a9442C444dA0 to change balance by -201,200 wei, but it has changed by -200,200 wei');
).to.be.eventually.rejectedWith(
AssertionError,
'Expected 0x17ec8597ff92C3F44523bDc65BF0f1bE632917ff,0x63FC2aD3d021a4D7e64323529a55a9442C444dA0 ' +
'to change balance by -201,200 wei, but it has changed by -200,200 wei'
);
});

it('Should throw in negative case when expected balance changes value were equal to an actual', async () => {
Expand All @@ -136,7 +137,11 @@ describe('INTEGRATION: Balance observers', () => {
gasPrice: 0,
value: 200
})).to.not.changeBalances([sender, receiver], [-200, 200])
).to.be.eventually.rejectedWith(AssertionError, `Expected 0x17ec8597ff92C3F44523bDc65BF0f1bE632917ff,0x63FC2aD3d021a4D7e64323529a55a9442C444dA0 to not change balance by -200,200 wei`);
).to.be.eventually.rejectedWith(
AssertionError,
'Expected 0x17ec8597ff92C3F44523bDc65BF0f1bE632917ff,0x63FC2aD3d021a4D7e64323529a55a9442C444dA0 ' +
'to not change balance by -200,200 wei'
);
});

it('Should throw when not a callback is passed to expect', async () => {
Expand Down
2 changes: 1 addition & 1 deletion test/matchers/bn.js → test/matchers/bn.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import chai, {AssertionError} from 'chai';
import chaiAsPromised from 'chai-as-promised';
import solidity from '../../lib/matchers';
import {solidity} from '../../lib/waffle';
import {utils} from 'ethers';

chai.use(solidity);
Expand Down
39 changes: 28 additions & 11 deletions test/matchers/events.js → test/matchers/events.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import chai, {AssertionError} from 'chai';
import chaiAsPromised from 'chai-as-promised';
import {createMockProvider, deployContract, getWallets} from '../../lib/waffle';
import Events from './build/Events';
import solidity from '../../lib/matchers';
import {
createMockProvider,
deployContract,
getWallets,
solidity
} from '../../lib/waffle';
import Events from './build/Events.json';
import { Contract } from 'ethers';

chai.use(solidity);
chai.use(chaiAsPromised);
Expand All @@ -11,7 +16,7 @@ const {expect} = chai;

describe('INTEGRATION: Events', () => {
let provider;
let events;
let events: Contract;
let wallet;

beforeEach(async () => {
Expand Down Expand Up @@ -41,17 +46,22 @@ describe('INTEGRATION: Events', () => {
});

it('Emit both: success (two expects)', async () => {
await expect(events.emitBoth()).to.emit(events, 'One', '0x0000000000000000000000000000000000000000000000000000000000000001');
await expect(events.emitBoth())
.to.emit(events, 'One')
.withArgs(1, 'One', '0x0000000000000000000000000000000000000000000000000000000000000001');
await expect(events.emitBoth()).to.emit(events, 'Two');
});

it('Emit both: success (one expect with two to)' , async () => {
await expect(events.emitBoth()).to.emit(events, 'One', '0x0000000000000000000000000000000000000000000000000000000000000001')
await expect(events.emitBoth())
.to.emit(events, 'One')
.withArgs(1, 'One', '0x0000000000000000000000000000000000000000000000000000000000000001')
.and.to.emit(events, 'Two');
});

it('Event with proper args', async () => {
await (expect(events.emitOne()).to.emit(events, 'One')).withArgs(1, 'One', '0x00cfbbaf7ddb3a1476767101c12a0162e241fbad2a0162e2410cfbbaf7162123');
await (expect(events.emitOne()).to.emit(events, 'One'))
.withArgs(1, 'One', '0x00cfbbaf7ddb3a1476767101c12a0162e241fbad2a0162e2410cfbbaf7162123');
});

it('Event with not enough args', async () => {
Expand All @@ -68,19 +78,26 @@ describe('INTEGRATION: Events', () => {

it('Event with one different arg (integer)', async () => {
await expect(
expect(events.emitOne()).to.emit(events, 'One').withArgs(2, 'One', '0x00cfbbaf7ddb3a1476767101c12a0162e241fbad2a0162e2410cfbbaf7162123')
expect(events.emitOne()).to.emit(events, 'One')
.withArgs(2, 'One', '0x00cfbbaf7ddb3a1476767101c12a0162e241fbad2a0162e2410cfbbaf7162123')
).to.be.eventually.rejectedWith(AssertionError, 'Expected "2" to be equal 1');
});

it('Event with one different arg (string)', async () => {
await expect(
expect(events.emitOne()).to.emit(events, 'One').withArgs(1, 'Two', '0x00cfbbaf7ddb3a1476767101c12a0162e241fbad2a0162e2410cfbbaf7162123')
expect(events.emitOne()).to.emit(events, 'One')
.withArgs(1, 'Two', '0x00cfbbaf7ddb3a1476767101c12a0162e241fbad2a0162e2410cfbbaf7162123')
).to.be.eventually.rejectedWith(AssertionError, 'expected \'Two\' to equal \'One\'');
});

it('Event with one different arg (string)', async () => {
await expect(
expect(events.emitOne()).to.emit(events, 'One').withArgs(1, 'One', '0x00cfbbaf7ddb3a1476767101c12a0162e241fbad2a0162e2410cfbbaf7162124')
).to.be.eventually.rejectedWith(AssertionError, 'expected \'0x00cfbbaf7ddb3a1476767101c12a0162e241fbad2a0162e2410cfbbaf7162124\' to equal \'0x00cfbbaf7ddb3a1476767101c12a0162e241fbad2a0162e2410cfbbaf7162123\'');
expect(events.emitOne()).to.emit(events, 'One')
.withArgs(1, 'One', '0x00cfbbaf7ddb3a1476767101c12a0162e241fbad2a0162e2410cfbbaf7162124')
).to.be.eventually.rejectedWith(
AssertionError,
'expected \'0x00cfbbaf7ddb3a1476767101c12a0162e241fbad2a0162e2410cfbbaf7162124\' ' +
'to equal \'0x00cfbbaf7ddb3a1476767101c12a0162e241fbad2a0162e2410cfbbaf7162123\''
);
});
});
10 changes: 8 additions & 2 deletions test/matchers/misc.js → test/matchers/misc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,19 @@ describe('UNIT: Miscellaneous', () => {
it('Expect to throw if invalid private', async () => {
expect(
() => expect('0x706618637b8ca922f6290ce1ecd4c31247e9ab75cf0530a0ac95c0332173d7c').to.be.properPrivateKey
).to.throw(AssertionError, 'Expected "0x706618637b8ca922f6290ce1ecd4c31247e9ab75cf0530a0ac95c0332173d7c" to be a proper private key');
).to.throw(
AssertionError,
'Expected "0x706618637b8ca922f6290ce1ecd4c31247e9ab75cf0530a0ac95c0332173d7c" to be a proper private key'
);
});

it('Expect to throw if negation with proper private)', async () => {
expect(
() => expect('0x706618637b8ca922f6290ce1ecd4c31247e9ab75cf0530a0ac95c0332173d7c5').not.to.be.properPrivateKey
).to.throw(AssertionError, 'Expected "0x706618637b8ca922f6290ce1ecd4c31247e9ab75cf0530a0ac95c0332173d7c5" not to be a proper private key');
).to.throw(
AssertionError,
'Expected "0x706618637b8ca922f6290ce1ecd4c31247e9ab75cf0530a0ac95c0332173d7c5" not to be a proper private key'
);
});
});

Expand Down
20 changes: 12 additions & 8 deletions test/matchers/reverted.js → test/matchers/reverted.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
import {createMockProvider, deployContract, getWallets} from '../../lib/waffle';
import Matchers from './build/Matchers';
import solidity from '../../lib/matchers';
import {
createMockProvider,
deployContract,
getWallets,
solidity
} from '../../lib/waffle';
import Matchers from './build/Matchers.json';
import { Contract } from 'ethers';

chai.use(solidity);
chai.use(chaiAsPromised);
Expand All @@ -14,9 +19,9 @@ const alwaysReject = new Promise((resolve, reject) => {
});

describe('INTEGRATION: Matchers: reverted', () => {
let provider = createMockProvider();
let [wallet] = getWallets(provider);
let matchers;
const provider = createMockProvider();
const [wallet] = getWallets(provider);
let matchers: Contract;

beforeEach(async () => {
matchers = await deployContract(wallet, Matchers);
Expand Down Expand Up @@ -67,7 +72,7 @@ describe('INTEGRATION: Matchers: reverted', () => {

describe('INTEGRATION: Matchers: revertedWith', () => {
let provider;
let matchers;
let matchers: Contract;
let wallet;

beforeEach(async () => {
Expand Down Expand Up @@ -142,4 +147,3 @@ describe('INTEGRATION: Matchers: revertedWith', () => {
).to.be.eventually.rejected;
});
});

3 changes: 2 additions & 1 deletion tslint.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"allow-pascal-case"
],
"no-shadowed-variable": false,
"no-empty": [true, "allow-empty-functions"]
"no-empty": [true, "allow-empty-functions"],
"no-unused-expression": false
}
}

0 comments on commit 98f8c5f

Please sign in to comment.