diff --git a/package-lock.json b/package-lock.json index c70bed0..a994ab3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7914,6 +7914,15 @@ "@jest/types": "^24.9.0" } }, + "jest-mock-axios": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/jest-mock-axios/-/jest-mock-axios-3.1.1.tgz", + "integrity": "sha512-3GCDj4aMk9+tRbU4wKZCHrewMdmyI/SuNF/2/ezzxRNAct+herwvy44Seeq7iLTAeFBiuEcL7gVfyf4TV+hUwg==", + "dev": true, + "requires": { + "synchronous-promise": "^2.0.10" + } + }, "jest-pnp-resolver": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz", @@ -13099,6 +13108,12 @@ "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" }, + "synchronous-promise": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/synchronous-promise/-/synchronous-promise-2.0.10.tgz", + "integrity": "sha512-6PC+JRGmNjiG3kJ56ZMNWDPL8hjyghF5cMXIFOKg+NiwwEZZIvxTWd0pinWKyD227odg9ygF8xVhhz7gb8Uq7A==", + "dev": true + }, "table": { "version": "5.4.6", "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", diff --git a/package.json b/package.json index fdc8417..37eb694 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "test": "npm run lint && react-scripts test --coverage --watchAll=false", "test:watch": "react-scripts test --coverage", "lint": "eslint --ignore-path .gitignore --ext .js .", - "format": "prettier ./src/**/*.{js,json} --write" + "format": "prettier ./src/**/*.{js,scss,json} --write" }, "dependencies": { "@ethereum-navigator/atlas": "^0.4.0", @@ -23,9 +23,11 @@ "devDependencies": { "@testing-library/jest-dom": "^4.1.0", "@testing-library/react": "^9.1.4", + "eslint": "^6.4.0", "eslint-config-oceanprotocol": "^1.5.0", "eslint-config-prettier": "^6.3.0", "eslint-plugin-prettier": "^3.1.0", + "jest-mock-axios": "^3.1.1", "node-sass": "^4.12.0", "prettier": "^1.18.2" }, diff --git a/src/App.module.scss b/src/App.module.scss index 3317b59..4094587 100644 --- a/src/App.module.scss +++ b/src/App.module.scss @@ -34,5 +34,6 @@ h1, h2 { display: grid; gap: $spacer; grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr)); - margin-top: $spacer * 2; + margin: $spacer * 2 auto; + max-width: 80rem; } diff --git a/src/App.test.js b/src/App.test.js index b6835b5..97a40b5 100644 --- a/src/App.test.js +++ b/src/App.test.js @@ -3,11 +3,7 @@ import { render } from '@testing-library/react' import App from './App' describe('App', () => { - it('should be able to run tests', () => { - expect(1 + 2).toEqual(3) - }) - - it('renders without crashing', async () => { + it('renders without crashing', () => { const { container } = render() expect(container.firstChild).toBeInTheDocument() }) diff --git a/src/Network.js b/src/Network.js index 32c0c71..d172398 100644 --- a/src/Network.js +++ b/src/Network.js @@ -14,42 +14,45 @@ Network.propTypes = { } export default function Network({ network }) { - const [status, setStatus] = useState('...') - const [block, setBlock] = useState('...') - const [latency, setLatency] = useState('...') - const [clientVersion, setClientVersion] = useState('...') + const [status, setStatus] = useState('') + const [block, setBlock] = useState('') + const [latency, setLatency] = useState('') + const [clientVersion, setClientVersion] = useState('') useEffect(() => { - getStatusAndBlock(network, setStatus, setBlock, setLatency) - getClientVersion(network, setClientVersion) + async function getStatusAndBlock() { + const response = await axiosRpcRequest(network.url, 'eth_blockNumber') - const timer = setInterval(() => { - getStatusAndBlock(network, setStatus, setBlock, setLatency) - getClientVersion(network, setClientVersion) - }, 5000) // run every 5 sec. - return () => clearInterval(timer) - }, [network]) + if (!response || response.status !== 200) { + setStatus('Offline') + return + } - async function getStatusAndBlock(network, setStatus, setBlock, setLatency) { - const response = await axiosRpcRequest(network.url, 'eth_blockNumber') + setStatus('Online') + response.duration && setLatency(response.duration) - if (response.status !== 200) { - setStatus('Offline') - return + const blockNumber = + response && response.data && parseInt(response.data.result, 16) + + setBlock(blockNumber) } - setStatus('Online') - setLatency(response.duration) + async function getClientVersion() { + const response = await axiosRpcRequest(network.url, 'web3_clientVersion') + response && response.data && setClientVersion(response.data.result) + } - const blockNumber = parseInt(response.data.result, 16) + getStatusAndBlock() + getClientVersion() - setBlock(blockNumber) - } - - async function getClientVersion(network, setClientVersion) { - const response = await axiosRpcRequest(network.url, 'web3_clientVersion') - setClientVersion(response.data.result) - } + const timer = setInterval(() => { + getStatusAndBlock() + getClientVersion() + }, 5000) // run every 5 sec. + return () => { + clearInterval(timer) + } + }, [network.url]) const isOnline = status === 'Online' @@ -57,8 +60,8 @@ export default function Network({ network }) {

{network.name} - {network.type} {network.networkId} + {network.type}

{network.url} @@ -67,14 +70,18 @@ export default function Network({ network }) { {status} - - {latency} ms - + {latency && ( + + {latency} ms + + )}

-

- At block #{block} -

-

{clientVersion}

+ {block && ( +

+ At block #{block} +

+ )} + {clientVersion &&

{clientVersion}

}
) } diff --git a/src/Network.test.js b/src/Network.test.js index 7c551e2..8ae4161 100644 --- a/src/Network.test.js +++ b/src/Network.test.js @@ -1,7 +1,18 @@ import React from 'react' -import { render } from '@testing-library/react' +import { render, waitForElement } from '@testing-library/react' +import mockAxios from 'axios' import Network from './Network' +const mockResponse = { + status: 200, + duration: 1000, + data: { result: '0x345' } +} + +afterEach(() => { + mockAxios.reset() +}) + describe('Network', () => { const network = { name: 'Pacific', @@ -12,8 +23,12 @@ describe('Network', () => { explorer: 'https://submarine.oceanprotocol.com' } - it('renders without crashing', () => { - const { container } = render() + it('renders without crashing', async () => { + mockAxios.post.mockResolvedValueOnce(mockResponse) + + const { container, getByTitle } = render() expect(container.firstChild).toBeInTheDocument() + await waitForElement(() => getByTitle('Current block number')) + expect(mockAxios.post).toHaveBeenCalledTimes(2) }) }) diff --git a/src/__mocks__/axios.js b/src/__mocks__/axios.js new file mode 100644 index 0000000..ef28bf2 --- /dev/null +++ b/src/__mocks__/axios.js @@ -0,0 +1,2 @@ +import mockAxios from 'jest-mock-axios' +export default mockAxios diff --git a/src/index.js b/src/index.js index 257c623..ac78d82 100644 --- a/src/index.js +++ b/src/index.js @@ -4,6 +4,16 @@ import './styles/global.scss' import App from './App' import * as serviceWorker from './serviceWorker' -ReactDOM.render(, document.getElementById('root')) +function renderToDOM() { + const rootElement = document.getElementById('root') + + if (rootElement !== null) { + ReactDOM.render(, rootElement) + } +} + +export { renderToDOM } + +renderToDOM() serviceWorker.register() diff --git a/src/index.test.js b/src/index.test.js new file mode 100644 index 0000000..c8b4c46 --- /dev/null +++ b/src/index.test.js @@ -0,0 +1,12 @@ +import { act } from '@testing-library/react' +import { renderToDOM } from '.' + +describe('index', () => { + it('should be able to run tests', () => { + expect(1 + 2).toEqual(3) + }) + + it('renders without crashing', () => { + act(() => renderToDOM()) + }) +}) diff --git a/src/rpc.js b/src/rpc.js index de74e60..b21754b 100644 --- a/src/rpc.js +++ b/src/rpc.js @@ -35,7 +35,6 @@ async function axiosRpcRequest(url, method) { return response } catch (error) { - console.error(error.message) return error } } diff --git a/src/styles/_variables.scss b/src/styles/_variables.scss index 2a37273..26a4a2a 100644 --- a/src/styles/_variables.scss +++ b/src/styles/_variables.scss @@ -22,17 +22,17 @@ $body-background: $brand-black; // Fonts $font-family-base: 'Sharp Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', - Helvetica, Arial, sans-serif; + Helvetica, Arial, sans-serif; $font-family-title: 'Sharp Sans Display', -apple-system, BlinkMacSystemFont, - 'Segoe UI', Helvetica, Arial, sans-serif; + 'Segoe UI', Helvetica, Arial, sans-serif; $font-family-monospace: 'Fira Code', 'Fira Mono', Menlo, Monaco, Consolas, - 'Courier New', monospace; + 'Courier New', monospace; $font-size-root: 16px; $font-size-base: 1rem; $font-size-large: 1.2rem; -$font-size-small: .85rem; -$font-size-mini: .65rem; +$font-size-small: 0.85rem; +$font-size-mini: 0.65rem; $font-size-text: $font-size-base; $font-size-label: $font-size-base; $font-size-title: 1.4rem; @@ -49,7 +49,7 @@ $line-height: 1.6; // Sizes $spacer: 2rem; -$page-frame: .75rem; +$page-frame: 0.75rem; $break-point--small: 640px; $break-point--medium: 860px; @@ -57,6 +57,6 @@ $break-point--large: 1140px; $break-point--huge: 1400px; $brand-border-width: 1px; -$border-radius: .2rem; +$border-radius: 0.2rem; $narrowWidth: 35rem; diff --git a/src/styles/global.scss b/src/styles/global.scss index d625847..ec38374 100644 --- a/src/styles/global.scss +++ b/src/styles/global.scss @@ -4,123 +4,123 @@ *, *:before, *:after { - box-sizing: border-box; + box-sizing: border-box; } /* stylelint-disable selector-max-id */ html, body, #root { - height: 100%; + height: 100%; } /* stylelint-enable selector-max-id */ html { - font-size: $font-size-root; + font-size: $font-size-root; } body { - color: $brand-grey-light; - font-size: $font-size-base; - font-family: $font-family-base; - font-weight: $font-weight-base; - line-height: $line-height; - background: $body-background; - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - position: relative; - margin: 0; + color: $brand-grey-light; + font-size: $font-size-base; + font-family: $font-family-base; + font-weight: $font-weight-base; + line-height: $line-height; + background: $body-background; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + position: relative; + margin: 0; - @media screen and (min-width: $break-point--small) { - padding: $page-frame; - } + @media screen and (min-width: $break-point--small) { + padding: $page-frame; + } } a { + text-decoration: none; + color: $brand-pink; + transition: 0.2s ease-out; + + &:hover, + &:focus { + color: darken($brand-pink, 15%); text-decoration: none; - color: $brand-pink; - transition: .2s ease-out; + transform: translate3d(0, -0.1rem, 0); + } - &:hover, - &:focus { - color: darken($brand-pink, 15%); - text-decoration: none; - transform: translate3d(0, -.1rem, 0); - } - - &:active { - color: darken($brand-pink, 15%); - text-decoration: none; - transform: none; - transition: none; - } + &:active { + color: darken($brand-pink, 15%); + text-decoration: none; + transform: none; + transition: none; + } } p { - margin: 0; - margin-bottom: $spacer / $line-height; + margin: 0; + margin-bottom: $spacer / $line-height; } // Lists ///////////////////////////////////// ul { - li { - &:before { - content: ' \25AA'; // Black Small Square: ▪ ▪ - top: -2px; - } + li { + &:before { + content: ' \25AA'; // Black Small Square: ▪ ▪ + top: -2px; } + } } ol { - counter-reset: ol-counter; + counter-reset: ol-counter; - li { - &:before { - content: counter(ol-counter) '.'; - counter-increment: ol-counter; - font-weight: $font-weight-bold; - top: -1px; - } + li { + &:before { + content: counter(ol-counter) '.'; + counter-increment: ol-counter; + font-weight: $font-weight-bold; + top: -1px; } + } - ul li:before { - content: ' \25AA'; - } + ul li:before { + content: ' \25AA'; + } } ul, ol { - margin-top: 0; - margin-bottom: $spacer; - padding-left: $spacer / $line-height; - list-style: none; + margin-top: 0; + margin-bottom: $spacer; + padding-left: $spacer / $line-height; + list-style: none; - li { - position: relative; - display: block; + li { + position: relative; + display: block; - &:before { - position: absolute; - left: -($spacer / $line-height); - color: $brand-grey-light; - user-select: none; - } - - + li { - margin-top: $spacer / 8; - } - - ul, - ol, - p { - margin-bottom: 0; - margin-top: $spacer / 8; - } + &:before { + position: absolute; + left: -($spacer / $line-height); + color: $brand-grey-light; + user-select: none; } + + + li { + margin-top: $spacer / 8; + } + + ul, + ol, + p { + margin-bottom: 0; + margin-top: $spacer / 8; + } + } } // Inline typography @@ -129,27 +129,27 @@ ol { b, strong, .bold { - font-weight: $font-weight-bold; + font-weight: $font-weight-bold; } em, .italic { - font-style: italic; + font-style: italic; } small { - font-size: $font-size-small; - display: inline-block; + font-size: $font-size-small; + display: inline-block; } abbr[title], dfn { - text-transform: none; - font-style: normal; - font-size: inherit; - border-bottom: 1px dashed $brand-grey-light; - cursor: help; - font-feature-settings: inherit; + text-transform: none; + font-style: normal; + font-size: inherit; + border-bottom: 1px dashed $brand-grey-light; + cursor: help; + font-feature-settings: inherit; } h1, @@ -157,30 +157,30 @@ h2, h3, h4, h5 { - font-family: $font-family-title; - color: inherit; - line-height: 1.2; - font-weight: $font-weight-bold; + font-family: $font-family-title; + color: inherit; + line-height: 1.2; + font-weight: $font-weight-bold; } h1 { - font-size: $font-size-h1; + font-size: $font-size-h1; } h2 { - font-size: $font-size-h2; + font-size: $font-size-h2; } h3 { - font-size: $font-size-h3; + font-size: $font-size-h3; } h4 { - font-size: $font-size-h4; + font-size: $font-size-h4; } h5 { - font-size: $font-size-h5; + font-size: $font-size-h5; } // Responsive Media @@ -194,75 +194,75 @@ audio, embed, canvas, picture { - max-width: 100%; - height: auto; - margin: 0 auto; - display: block; + max-width: 100%; + height: auto; + margin: 0 auto; + display: block; } hr { - margin: $spacer 0; - border: 0; - border-bottom: .1rem solid $brand-grey-lighter; + margin: $spacer 0; + border: 0; + border-bottom: 0.1rem solid $brand-grey-lighter; } // Quotes ///////////////////////////////////// q { - font-style: italic; + font-style: italic; } cite { - font-style: normal; - text-transform: uppercase; + font-style: normal; + text-transform: uppercase; } blockquote, blockquote > p { - font-style: italic; - color: lighten($brand-grey, 15%); + font-style: italic; + color: lighten($brand-grey, 15%); } blockquote { - margin: 0 0 $spacer; - padding-left: $spacer / 2; - border-left: .2rem solid $brand-grey-lighter; + margin: 0 0 $spacer; + padding-left: $spacer / 2; + border-left: 0.2rem solid $brand-grey-lighter; - @media screen and (min-width: $break-point--small) { - padding-left: $spacer / $line-height; - } + @media screen and (min-width: $break-point--small) { + padding-left: $spacer / $line-height; + } } // Tables ///////////////////////////////////// table { - width: 100%; - margin-bottom: $spacer * $line-height; - border-collapse: collapse; + width: 100%; + margin-bottom: $spacer * $line-height; + border-collapse: collapse; - th, - td { - border: 0; - margin: 0; - padding: $spacer / 2; - border-bottom: 1px solid $brand-grey-lighter; - text-align: left; - font-size: 90%; + th, + td { + border: 0; + margin: 0; + padding: $spacer / 2; + border-bottom: 1px solid $brand-grey-lighter; + text-align: left; + font-size: 90%; - &[align='center'] { - text-align: center; - } - - &[align='right'] { - text-align: right; - } + &[align='center'] { + text-align: center; } - th { - font-weight: 600; + &[align='right'] { + text-align: right; } + } + + th { + font-weight: 600; + } } // Code @@ -272,63 +272,63 @@ code, kbd, pre, samp { - font-family: $font-family-monospace; - font-size: $font-size-small; - border-radius: $border-radius; - text-shadow: none; - overflow-wrap: break-word; - word-wrap: break-word; - word-break: break-all; + font-family: $font-family-monospace; + font-size: $font-size-small; + border-radius: $border-radius; + text-shadow: none; + overflow-wrap: break-word; + word-wrap: break-word; + word-break: break-all; - h1 &, - h2 &, - h3 &, - h4 &, - h5 & { - font-size: inherit; - } + h1 &, + h2 &, + h3 &, + h4 &, + h5 & { + font-size: inherit; + } } :not(pre) > code { - color: inherit; - display: inline-block; + color: inherit; + display: inline-block; } a > code { - color: $brand-pink; + color: $brand-pink; } pre { + display: block; + margin-bottom: $spacer; + padding: 0; + background: lighten($brand-grey-lighter, 5%); + + // make 'em scrollable + overflow: auto; + -webkit-overflow-scrolling: touch; + + code { + padding: $spacer; + white-space: pre; display: block; - margin-bottom: $spacer; - padding: 0; - background: lighten($brand-grey-lighter, 5%); - - // make 'em scrollable + color: $brand-grey-lighter; + overflow-wrap: normal; + word-wrap: normal; + word-break: normal; overflow: auto; - -webkit-overflow-scrolling: touch; - - code { - padding: $spacer; - white-space: pre; - display: block; - color: $brand-grey-lighter; - overflow-wrap: normal; - word-wrap: normal; - word-break: normal; - overflow: auto; - } + } } // Selection ///////////////////////////////////// ::-moz-selection { - background: $brand-grey-light; - color: #fff; + background: $brand-grey-light; + color: #fff; } ::selection { - background: $brand-grey-light; - color: #fff; + background: $brand-grey-light; + color: #fff; }