Skip to content

Commit

Permalink
⛺️ Add tests with refresh field for useCall hook (#832)
Browse files Browse the repository at this point in the history
  • Loading branch information
yivlad committed Jun 20, 2022
1 parent 55a28a2 commit 2909416
Show file tree
Hide file tree
Showing 11 changed files with 163 additions and 17 deletions.
5 changes: 5 additions & 0 deletions .changeset/chilly-wasps-stare.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@usedapp/core': patch
---

Fix refresh field in QueryOptions
7 changes: 4 additions & 3 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
"nanoid": "3.1.22"
},
"peerDependencies": {
"react": "*",
"ethers": "*"
"ethers": "*",
"react": "*"
},
"devDependencies": {
"@ethereum-waffle/provider": "4.0.0-alpha.0",
Expand Down Expand Up @@ -53,7 +53,8 @@
"react": "^17.0.1",
"solc": "^0.8.12",
"typechain": "^7.0.0",
"typescript": "^4.6.2"
"typescript": "^4.6.2",
"wait-for-expect": "^3.0.2"
},
"scripts": {
"build": "yarn run build:esm && yarn run build:cjs",
Expand Down
19 changes: 19 additions & 0 deletions packages/core/src/constants/abi/BlockNumber.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"contractName": "BlockNumber",
"abi": [
{
"inputs": [],
"name": "getBlockNumber",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
}
],
"bytecode": "0x608060405234801561001057600080fd5b5060b58061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c806342cbb15c14602d575b600080fd5b60336047565b604051603e9190605c565b60405180910390f35b600043905090565b6056816075565b82525050565b6000602082019050606f6000830184604f565b92915050565b600081905091905056fea26469706673582212204740e85f94e6d1887dda3a56af161b21a85b8ddb19dab72de0279a74db1a727064736f6c63430008070033"
}
3 changes: 3 additions & 0 deletions packages/core/src/constants/abi/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import MultiCall from './MultiCall.json'
import MultiCall2 from './MultiCall2.json'
import ERC20 from './ERC20.json'
import ERC20Mock from './ERC20Mock.json'
import BlockNumberContract from './BlockNumber.json'

const Interface = utils.Interface

Expand All @@ -21,3 +22,5 @@ export { ERC20, ERC20Interface }
const ERC20MockInterface = new Interface(ERC20Mock.abi)

export { ERC20Mock, ERC20MockInterface }

export { BlockNumberContract }
101 changes: 101 additions & 0 deletions packages/core/src/hooks/useCall.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,13 @@ import {
MOCK_TOKEN_INITIAL_BALANCE,
SECOND_TEST_CHAIN_ID,
SECOND_MOCK_TOKEN_INITIAL_BALANCE,
getResultPropery,
} from '../testing'
import { ChainId } from '../constants/chainId'
import { BigNumber } from 'ethers'
import { deployContract } from 'ethereum-waffle'
import { BlockNumberContract } from '../constants'
import waitForExpect from 'wait-for-expect'

describe('useCall', () => {
const mockProvider = new MockProvider()
Expand All @@ -19,10 +23,14 @@ describe('useCall', () => {
const [secondDeployer] = secondMockProvider.getWallets()
let token: Contract
let secondToken: Contract
let blockNumberContract: Contract
let secondBlockNumberContract: Contract

beforeEach(async () => {
token = await deployMockToken(deployer)
secondToken = await deployMockToken(secondDeployer, SECOND_MOCK_TOKEN_INITIAL_BALANCE)
blockNumberContract = await deployContract(deployer, BlockNumberContract)
secondBlockNumberContract = await deployContract(deployer, BlockNumberContract)
})

it('initial test balance to be correct', async () => {
Expand Down Expand Up @@ -74,4 +82,97 @@ describe('useCall', () => {
expect(result.error).to.be.undefined
expect(result.current?.value[0]).to.eq(endValue)
}

it('Properly handles two calls', async () => {
const { result, waitForCurrent, mineBlock } = await renderWeb3Hook(
() => {
const balance = useCall({
contract: token,
method: 'balanceOf',
args: [deployer.address],
})
const block = useCall({
contract: blockNumberContract,
method: 'getBlockNumber',
args: [],
})

return { balance, block }
},
{
mockProvider,
}
)

const blockNumber = await mockProvider.getBlockNumber()

await waitForCurrent(({ balance, block }) => !!(balance && block))
expect(result.error).to.be.undefined
expect(getResultPropery(result, 'balance')).to.eq(MOCK_TOKEN_INITIAL_BALANCE)
expect(getResultPropery(result, 'block')).to.eq(blockNumber)

await mineBlock()

await waitForExpect(() => {
expect(getResultPropery(result, 'balance')).to.eq(MOCK_TOKEN_INITIAL_BALANCE)
expect(getResultPropery(result, 'block')).to.eq(blockNumber + 1)
})
})

it('Properly handles refresh per block', async () => {
const { result, waitForCurrent, mineBlock } = await renderWeb3Hook(
() => {
const block1 = useCall({
contract: blockNumberContract,
method: 'getBlockNumber',
args: [],
})
const block2 = useCall(
{
// TODO: add similar test but with the same contract (blockNumberContract). It would currently fail
contract: secondBlockNumberContract,
method: 'getBlockNumber',
args: [],
},
{
refresh: 2,
}
)

return { block1, block2 }
},
{
mockProvider,
}
)

const blockNumber = await mockProvider.getBlockNumber()

await waitForCurrent(({ block1, block2 }) => !!(block1 && block2))
expect(result.error).to.be.undefined
expect(getResultPropery(result, 'block1')).to.eq(blockNumber)
expect(getResultPropery(result, 'block2')).to.eq(blockNumber)

await mineBlock()

await waitForCurrent(({ block1 }) => block1 !== undefined && block1.value[0].toNumber() === blockNumber + 1)
expect(getResultPropery(result, 'block1')).to.eq(blockNumber + 1)
expect(getResultPropery(result, 'block2')).to.eq(blockNumber)

await mineBlock()

await waitForExpect(() => {
expect(getResultPropery(result, 'block1')).to.eq(blockNumber + 2)
expect(getResultPropery(result, 'block2')).to.eq(blockNumber + 2)
})

for (let i = 0; i < 3; i++) {
await mineBlock()
}

await waitForExpect(() => {
expect(getResultPropery(result, 'block1')).to.eq(blockNumber + 5)
expect(getResultPropery(result, 'block2')).to.eq(blockNumber + 5)
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export function callsReducer(state: RawCall[] = [], action: Action) {
}
const blockNumber = action.blockNumber
if (call.refreshPerBlocks && call.lastUpdatedBlockNumber) {
return call.lastUpdatedBlockNumber + call.refreshPerBlocks === blockNumber
return call.lastUpdatedBlockNumber + call.refreshPerBlocks <= blockNumber
? {
...call,
lastUpdatedBlockNumber: blockNumber,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ describe('chainStateReducer', () => {
expect(result).to.deep.equal(state)
})

it('overwrites with updates from newer blocks', () => {
it('merges with updates from newer blocks', () => {
const state: State = {
[Mainnet.chainId]: {
blockNumber: 1234,
Expand Down Expand Up @@ -68,6 +68,12 @@ describe('chainStateReducer', () => {
[Mainnet.chainId]: {
blockNumber: 1235,
state: {
[ADDRESS_A]: {
'0xdead': {
value: '0xbeef',
success: true,
},
},
[ADDRESS_B]: {
'0xabcd': {
value: '0x5678',
Expand Down
21 changes: 9 additions & 12 deletions packages/core/src/providers/chainState/common/chainStateReducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,15 @@ export function chainStateReducer(state: State = {}, action: ChainStateAction) {
if (!current || action.blockNumber >= current) {
if (action.type === 'FETCH_SUCCESS') {
let newState = action.state
if (action.blockNumber === current) {
// merge with existing state to prevent requests coming out of order
// from overwriting the data
const oldState = state[action.chainId]?.state ?? {}
for (const [address, entries] of Object.entries(oldState)) {
newState = {
...newState,
[address]: {
...entries,
...newState[address],
},
}
// merge with existing state
const oldState = state[action.chainId]?.state ?? {}
for (const [address, entries] of Object.entries(oldState)) {
newState = {
...newState,
[address]: {
...entries,
...newState[address],
},
}
}
return {
Expand Down
11 changes: 11 additions & 0 deletions packages/core/src/testing/utils/getResultProperty.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { RenderResult } from '@testing-library/react-hooks'
import { Contract } from 'ethers'
import { CallResult } from '../../helpers'

export type HookResult = {
[key: string]: CallResult<Contract, string>
}

export const getResultPropery = <T extends HookResult>(result: RenderResult<T>, property: keyof T) => {
return result.current?.[property]?.value[0]
}
1 change: 1 addition & 0 deletions packages/core/src/testing/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export * from './waitUntil'
export * from './waitUtils'
export * from './deployMockToken'
export * from './setupTestingConfig'
export * from './getResultProperty'
2 changes: 2 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 comments on commit 2909416

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.