Skip to content

Commit

Permalink
Merge ac53ac3 into fc6aedb
Browse files Browse the repository at this point in the history
  • Loading branch information
LordotU committed Jan 27, 2020
2 parents fc6aedb + ac53ac3 commit af11a54
Show file tree
Hide file tree
Showing 23 changed files with 11,370 additions and 2,887 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

node_modules/
build/
tests/coverage/
__tests__/coverage/

yarn-error.log
.coveralls.yml
5 changes: 5 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
language: node_js

node_js:
- 10
- 12
17 changes: 9 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
# React Metamask Checker

[![License](https://img.shields.io/badge/License-MIT-000000.svg)](https://opensource.org/licenses/MIT)
[![Build Status](https://travis-ci.org/LordotU/react-metamask-checker.svg?branch=master)](https://travis-ci.org/LordotU/react-metamask-checker)
[![Coverage Status](https://coveralls.io/repos/github/LordotU/react-metamask-checker/badge.svg)](https://coveralls.io/github/LordotU/react-metamask-checker)

## [Live demo](https://react-metamask-checker-demo-with-parcel.lordotu.now.sh)

## Description

React component which uses [Render Props](https://reactjs.org/docs/render-props.html) approach for checking Web3 instance object injected by [Metamask](https://metamask.io/) extension.
Expand Down Expand Up @@ -37,32 +40,30 @@ import Content from './components/Content'

class App extends Component {

async initialize (provider, account) {
console.log(provider, account)
async initialize (provider, account, network) {
console.log(provider, account, network)
}

render () {

const props = {
/* Ethereum account (address) which should be selected in Metamask */
// account : null,
/* Ethereum network_id (numeric) which should be selected in Metamask */
// network : null,

/* Checking timeout (ms) in case of checking error */
// checkTimeout : 300,
/* Ethereum account (address) which should be selected in Metamask */
// account : null,

/* Function which executes on checking error */
// onCheckError : async (error) => null,

/* Function which executes on checking success */
onCheckSuccess : async (provider, account) => await this.initialize(provider, account),
onCheckSuccess : async (provider, account, network) => await this.initialize(provider, account, network),

renderDefault : () => <Loader />,

renderErrored : error => <Err message={error.message || 'Unexpected error'} />,

renderChecked : (provider, account) => <Content />
renderChecked : (provider, account, network) => <Content />
}

return <MetamaskChecker {...props} />
Expand Down
85 changes: 85 additions & 0 deletions __tests__/MetamaskChecker.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import Loader from './__mocks__/components/Loader'
import Err from './__mocks__/components/Err'
import App from './__mocks__/components/App'

import MetamaskInpageProvider from './__mocks__/MetamaskInpageProvider'

import MetamaskChecker from '../src/MetamaskChecker'

const flushPromises = () => new Promise(resolve => setImmediate(resolve))

describe('MetamaskChecker', () => {
let wrapper

const renderProps = {
renderDefault : () =>
<Loader />,
renderErrored : error =>
<Err error={ error.message || 'Unexpected error' } />,
renderChecked : (provider, account, network) =>
<App account={account} network={network} />
}

const NETWORK = 0x01
const ACCOUNT = '0x1111111111111111111111111111111111111111'

beforeEach(async () => {
window.ethereum = new MetamaskInpageProvider({
network : NETWORK,
accounts : [ACCOUNT]
})

wrapper = shallow( <MetamaskChecker { ...renderProps } /> )
})
afterEach(() => {
wrapper.unmount()
})

it('renders Loader when nothing happens', async () => {
expect(wrapper.html()).toEqual('<div id="loader">Loader</div>')
})

it('renders Err when something wrong with MetamaskInpageProvider', async () => {
window.ethereum = null
await flushPromises()
window.dispatchEvent(new Event('load'))
await flushPromises()

expect(wrapper.html()).toEqual(`<div id="err">Can&#x27;t find ethereum provider object!</div>`)
})

it('renders Err when something wrong with given network', async () => {
await flushPromises()
wrapper.setProps({ network : 0 })
window.dispatchEvent(new Event('load'))
await flushPromises()

expect(wrapper.html()).toEqual(`<div id="err">Metamask&#x27;s selected network is not the same as given (0)!</div>`)
})

it('renders Err when something wrong with given account', async () => {
await flushPromises()
wrapper.setProps({ account : '0x0000000000000000000000000000000000000000' })
window.dispatchEvent(new Event('load'))
await flushPromises()

expect(wrapper.html()).toEqual(`<div id="err">Metamask&#x27;s selected account is not the same as given (0x0000000000000000000000000000000000000000)!</div>`)
})

it('renders App when all ok with MetamaskInpageProvider', async () => {
await flushPromises()
window.dispatchEvent(new Event('load'))
await flushPromises()

expect(wrapper.html()).toEqual(`<div id="app">Selected account ${ACCOUNT} in selected network ${NETWORK}</div>`)
})

it('renders App when all ok with MetamaskInpageProvider, given network and account', async () => {
await flushPromises()
wrapper.setProps({ network : NETWORK, account : ACCOUNT })
window.dispatchEvent(new Event('load'))
await flushPromises()

expect(wrapper.html()).toEqual(`<div id="app">Selected account ${ACCOUNT} in selected network ${NETWORK}</div>`)
})
})
36 changes: 36 additions & 0 deletions __tests__/__mocks__/MetamaskInpageProvider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
export default class MetamaskInpageProvider {
constructor ({
isMetaMask = true,
network = null,
accounts = [null],
} = {}) {
this.isMetaMask = isMetaMask

this._data = {
network,
accounts,
}
}

async send (method = '') {
switch (method) {
case 'eth_accounts':
case 'eth_requestAccounts':
return { result: this._data.accounts }

case 'eth_chainId':
return { result: this._data.network }

default:
return { result: null }
}
}

on () {
return this
}

off () {
return this
}
}
3 changes: 3 additions & 0 deletions __tests__/__mocks__/components/App.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default ({ account, network }) => (
<div id="app">Selected account {account} in selected network {network}</div>
)
File renamed without changes.
File renamed without changes.
62 changes: 62 additions & 0 deletions __tests__/checkMetamask.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import MetamaskInpageProvider from './__mocks__/MetamaskInpageProvider'


const metamaskInpageProviderOpts = {
network : '0x01',
accounts : ['0x1'],
}

describe('checkMetamask', () => {
beforeEach(() => {
jest.resetModules()
})

it('imports without errors', async () => {
expect(() => require('../src/checkMetamask').default).not.toThrow()
})

it('throws an error when inpageProvider is undefined', async () => {
const checkMetamask = require('../src/checkMetamask').default

const ERROR_MSG = `Can't find ethereum provider object!`

await expect(checkMetamask()).rejects.toThrowError(ERROR_MSG)
await expect(checkMetamask(null)).rejects.toThrowError(ERROR_MSG)
await expect(checkMetamask(false)).rejects.toThrowError(ERROR_MSG)
await expect(checkMetamask({ isMetaMask: false })).rejects.toThrowError(ERROR_MSG)
})

it('throws an error when wrong network has been passed', async () => {
const checkMetamask = require('../src/checkMetamask').default
const inpageProvider = new MetamaskInpageProvider(metamaskInpageProviderOpts)

await expect(checkMetamask(inpageProvider, 42)).rejects.toThrow(`Metamask's selected network is not the same as given (42)!`)
})

it('throws an error when wrong account has been passed', async () => {
const checkMetamask = require('../src/checkMetamask').default
const inpageProvider = new MetamaskInpageProvider(metamaskInpageProviderOpts)

await expect(checkMetamask(inpageProvider, null, '0x0')).rejects.toThrow(`Metamask's selected account is not the same as given (0x0)!`)
})

it('executes without errors when network and account not passed', async () => {
const checkMetamask = require('../src/checkMetamask').default
const inpageProvider = new MetamaskInpageProvider(metamaskInpageProviderOpts)

await expect(checkMetamask(inpageProvider)).resolves.toStrictEqual({
'selectedNetwork': 1,
'selectedAccount': '0x1',
})
})

it('executes without errors when right network and account has been passed', async () => {
const checkMetamask = require('../src/checkMetamask').default
const inpageProvider = new MetamaskInpageProvider(metamaskInpageProviderOpts)

await expect(checkMetamask(inpageProvider, 1, '0x1')).resolves.toStrictEqual({
'selectedNetwork': 1,
'selectedAccount': '0x1',
})
})
})
12 changes: 12 additions & 0 deletions __tests__/setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from 'react'
import { configure, shallow, render, mount } from 'enzyme'
import Adapter from 'enzyme-adapter-react-16'
import toJson from 'enzyme-to-json'

configure({ adapter: new Adapter() })

global.React = React
global.shallow = shallow
global.render = render
global.mount = mount
global.toJson = toJson
Loading

0 comments on commit af11a54

Please sign in to comment.