diff --git a/js/src/util/subscribe-to-events.spec.js b/js/src/util/subscribe-to-events.spec.js new file mode 100644 index 000000000..3629e8d79 --- /dev/null +++ b/js/src/util/subscribe-to-events.spec.js @@ -0,0 +1,115 @@ +// Copyright 2015, 2016 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +import { spy, stub } from 'sinon'; + +import subscribeToEvents from './subscribe-to-events'; +import { + pastLogs, liveLogs, createApi, createContract +} from './subscribe-to-events.test.js'; + +const delay = (t) => new Promise((resolve) => { + setTimeout(resolve, t); +}); + +describe('util/subscribe-to-events', () => { + beforeEach(function () { + this.api = createApi(); + this.contract = createContract(this.api); + }); + + it('installs a filter', async function () { + const { api, contract } = this; + + subscribeToEvents(contract, [ 'Foo', 'Bar' ]); + await delay(0); + + expect(api.eth.newFilter.calledOnce).to.equal(true); + expect(api.eth.newFilter.firstCall.args).to.eql([ { + fromBlock: 0, toBlock: 'latest', + address: contract.address, + topics: [ [ + contract.instance.Foo.signature, + contract.instance.Bar.signature + ] ] + } ]); + }); + + it('queries & parses logs in the beginning', async function () { + const { api, contract } = this; + + subscribeToEvents(contract, [ 'Foo', 'Bar' ]); + + await delay(0); + expect(api.eth.getFilterLogs.callCount).to.equal(1); + expect(api.eth.getFilterLogs.firstCall.args).to.eql([ 123 ]); + + await delay(0); + expect(contract.parseEventLogs.callCount).to.equal(1); + }); + + it('emits logs in the beginning', async function () { + const { contract } = this; + + const onLog = spy(); + const onFoo = spy(); + const onBar = spy(); + subscribeToEvents(contract, [ 'Foo', 'Bar' ]) + .on('log', onLog) + .on('Foo', onFoo) + .on('Bar', onBar); + + await delay(0); + + expect(onLog.callCount).to.equal(2); + expect(onLog.firstCall.args).to.eql([ pastLogs[0] ]); + expect(onLog.secondCall.args).to.eql([ pastLogs[1] ]); + expect(onFoo.callCount).to.equal(1); + expect(onFoo.firstCall.args).to.eql([ pastLogs[0] ]); + expect(onBar.callCount).to.equal(1); + expect(onBar.firstCall.args).to.eql([ pastLogs[1] ]); + }); + + it('uninstalls the filter on sunsubscribe', async function () { + const { api, contract } = this; + + const s = subscribeToEvents(contract, [ 'Foo', 'Bar' ]); + await delay(0); + s.unsubscribe(); + await delay(0); + + expect(api.eth.uninstallFilter.calledOnce).to.equal(true); + expect(api.eth.uninstallFilter.firstCall.args).to.eql([ 123 ]); + }); + + it('checks for new events regularly', async function () { + const { api, contract } = this; + api.eth.getFilterLogs = stub().resolves([]); + + const onLog = spy(); + const onBar = spy(); + const s = subscribeToEvents(contract, [ 'Bar' ], { interval: 5 }) + .on('log', onLog) + .on('Bar', onBar); + await delay(9); + s.unsubscribe(); + + expect(onLog.callCount).to.equal(1); + expect(onLog.firstCall.args).to.eql([ liveLogs[0] ]); + expect(onBar.callCount).to.equal(1); + expect(onBar.firstCall.args).to.eql([ liveLogs[0] ]); + }); +}); diff --git a/js/src/util/subscribe-to-events.test.js b/js/src/util/subscribe-to-events.test.js new file mode 100644 index 000000000..642f8e592 --- /dev/null +++ b/js/src/util/subscribe-to-events.test.js @@ -0,0 +1,53 @@ +// Copyright 2015, 2016 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +import { stub } from 'sinon'; + +export const ADDRESS = '0x1111111111111111111111111111111111111111'; + +export const pastLogs = [ + { event: 'Foo', type: 'mined', address: ADDRESS, params: {} }, + { event: 'Bar', type: 'mined', address: ADDRESS, params: {} } +]; + +export const liveLogs = [ + { event: 'Bar', type: 'mined', address: ADDRESS, params: { foo: 'bar' } } +]; + +export const createApi = () => ({ + eth: { + newFilter: stub().resolves(123), + uninstallFilter: stub() + .rejects(new Error('unknown filter id')) + .withArgs(123).resolves(null), + getFilterLogs: stub() + .rejects(new Error('unknown filter id')) + .withArgs(123).resolves(pastLogs), + getFilterChanges: stub() + .rejects(new Error('unknown filter id')) + .withArgs(123).resolves(liveLogs) + } +}); + +export const createContract = (api) => ({ + api, + address: ADDRESS, + instance: { + Foo: { signature: 'Foo signature' }, + Bar: { signature: 'Bar signature' } + }, + parseEventLogs: stub().returnsArg(0) +});