This commit is contained in:
Matthias Kretschmann 2019-09-16 19:01:33 +02:00
parent ebed6ee666
commit d7a5526b84
Signed by: m
GPG Key ID: 606EEEF3C479A91F
12 changed files with 282 additions and 223 deletions

15
package-lock.json generated
View File

@ -7914,6 +7914,15 @@
"@jest/types": "^24.9.0" "@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": { "jest-pnp-resolver": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz", "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", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
"integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" "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": { "table": {
"version": "5.4.6", "version": "5.4.6",
"resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz",

View File

@ -9,7 +9,7 @@
"test": "npm run lint && react-scripts test --coverage --watchAll=false", "test": "npm run lint && react-scripts test --coverage --watchAll=false",
"test:watch": "react-scripts test --coverage", "test:watch": "react-scripts test --coverage",
"lint": "eslint --ignore-path .gitignore --ext .js .", "lint": "eslint --ignore-path .gitignore --ext .js .",
"format": "prettier ./src/**/*.{js,json} --write" "format": "prettier ./src/**/*.{js,scss,json} --write"
}, },
"dependencies": { "dependencies": {
"@ethereum-navigator/atlas": "^0.4.0", "@ethereum-navigator/atlas": "^0.4.0",
@ -23,9 +23,11 @@
"devDependencies": { "devDependencies": {
"@testing-library/jest-dom": "^4.1.0", "@testing-library/jest-dom": "^4.1.0",
"@testing-library/react": "^9.1.4", "@testing-library/react": "^9.1.4",
"eslint": "^6.4.0",
"eslint-config-oceanprotocol": "^1.5.0", "eslint-config-oceanprotocol": "^1.5.0",
"eslint-config-prettier": "^6.3.0", "eslint-config-prettier": "^6.3.0",
"eslint-plugin-prettier": "^3.1.0", "eslint-plugin-prettier": "^3.1.0",
"jest-mock-axios": "^3.1.1",
"node-sass": "^4.12.0", "node-sass": "^4.12.0",
"prettier": "^1.18.2" "prettier": "^1.18.2"
}, },

View File

@ -34,5 +34,6 @@ h1, h2 {
display: grid; display: grid;
gap: $spacer; gap: $spacer;
grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr)); grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
margin-top: $spacer * 2; margin: $spacer * 2 auto;
max-width: 80rem;
} }

View File

@ -3,11 +3,7 @@ import { render } from '@testing-library/react'
import App from './App' import App from './App'
describe('App', () => { describe('App', () => {
it('should be able to run tests', () => { it('renders without crashing', () => {
expect(1 + 2).toEqual(3)
})
it('renders without crashing', async () => {
const { container } = render(<App />) const { container } = render(<App />)
expect(container.firstChild).toBeInTheDocument() expect(container.firstChild).toBeInTheDocument()
}) })

View File

@ -14,42 +14,45 @@ Network.propTypes = {
} }
export default function Network({ network }) { export default function Network({ network }) {
const [status, setStatus] = useState('...') const [status, setStatus] = useState('')
const [block, setBlock] = useState('...') const [block, setBlock] = useState('')
const [latency, setLatency] = useState('...') const [latency, setLatency] = useState('')
const [clientVersion, setClientVersion] = useState('...') const [clientVersion, setClientVersion] = useState('')
useEffect(() => { useEffect(() => {
getStatusAndBlock(network, setStatus, setBlock, setLatency) async function getStatusAndBlock() {
getClientVersion(network, setClientVersion) const response = await axiosRpcRequest(network.url, 'eth_blockNumber')
const timer = setInterval(() => { if (!response || response.status !== 200) {
getStatusAndBlock(network, setStatus, setBlock, setLatency) setStatus('Offline')
getClientVersion(network, setClientVersion) return
}, 5000) // run every 5 sec. }
return () => clearInterval(timer)
}, [network])
async function getStatusAndBlock(network, setStatus, setBlock, setLatency) { setStatus('Online')
const response = await axiosRpcRequest(network.url, 'eth_blockNumber') response.duration && setLatency(response.duration)
if (response.status !== 200) { const blockNumber =
setStatus('Offline') response && response.data && parseInt(response.data.result, 16)
return
setBlock(blockNumber)
} }
setStatus('Online') async function getClientVersion() {
setLatency(response.duration) 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) const timer = setInterval(() => {
} getStatusAndBlock()
getClientVersion()
async function getClientVersion(network, setClientVersion) { }, 5000) // run every 5 sec.
const response = await axiosRpcRequest(network.url, 'web3_clientVersion') return () => {
setClientVersion(response.data.result) clearInterval(timer)
} }
}, [network.url])
const isOnline = status === 'Online' const isOnline = status === 'Online'
@ -57,8 +60,8 @@ export default function Network({ network }) {
<div className={styles.network}> <div className={styles.network}>
<h2 className={styles.title}> <h2 className={styles.title}>
{network.name} {network.name}
<span>{network.type}</span>
<code>{network.networkId}</code> <code>{network.networkId}</code>
<span>{network.type}</span>
</h2> </h2>
<p> <p>
<code>{network.url}</code> <code>{network.url}</code>
@ -67,14 +70,18 @@ export default function Network({ network }) {
<span className={isOnline ? styles.success : styles.error}> <span className={isOnline ? styles.success : styles.error}>
{status} {status}
</span> </span>
<span className={styles.latency} title="Latency"> {latency && (
{latency} ms <span className={styles.latency} title="Latency">
</span> {latency} ms
</span>
)}
</p> </p>
<p className={styles.block}> {block && (
At block #<a href={`${network.explorer}/blocks/${block}`}>{block}</a> <p className={styles.block} title="Current block number">
</p> At block #<a href={`${network.explorer}/blocks/${block}`}>{block}</a>
<p className={styles.clientVersion}>{clientVersion}</p> </p>
)}
{clientVersion && <p className={styles.clientVersion}>{clientVersion}</p>}
</div> </div>
) )
} }

View File

@ -1,7 +1,18 @@
import React from 'react' 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' import Network from './Network'
const mockResponse = {
status: 200,
duration: 1000,
data: { result: '0x345' }
}
afterEach(() => {
mockAxios.reset()
})
describe('Network', () => { describe('Network', () => {
const network = { const network = {
name: 'Pacific', name: 'Pacific',
@ -12,8 +23,12 @@ describe('Network', () => {
explorer: 'https://submarine.oceanprotocol.com' explorer: 'https://submarine.oceanprotocol.com'
} }
it('renders without crashing', () => { it('renders without crashing', async () => {
const { container } = render(<Network network={network} />) mockAxios.post.mockResolvedValueOnce(mockResponse)
const { container, getByTitle } = render(<Network network={network} />)
expect(container.firstChild).toBeInTheDocument() expect(container.firstChild).toBeInTheDocument()
await waitForElement(() => getByTitle('Current block number'))
expect(mockAxios.post).toHaveBeenCalledTimes(2)
}) })
}) })

2
src/__mocks__/axios.js Normal file
View File

@ -0,0 +1,2 @@
import mockAxios from 'jest-mock-axios'
export default mockAxios

View File

@ -4,6 +4,16 @@ import './styles/global.scss'
import App from './App' import App from './App'
import * as serviceWorker from './serviceWorker' import * as serviceWorker from './serviceWorker'
ReactDOM.render(<App />, document.getElementById('root')) function renderToDOM() {
const rootElement = document.getElementById('root')
if (rootElement !== null) {
ReactDOM.render(<App />, rootElement)
}
}
export { renderToDOM }
renderToDOM()
serviceWorker.register() serviceWorker.register()

12
src/index.test.js Normal file
View File

@ -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())
})
})

View File

@ -35,7 +35,6 @@ async function axiosRpcRequest(url, method) {
return response return response
} catch (error) { } catch (error) {
console.error(error.message)
return error return error
} }
} }

View File

@ -22,17 +22,17 @@ $body-background: $brand-black;
// Fonts // Fonts
$font-family-base: 'Sharp Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', $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, $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, $font-family-monospace: 'Fira Code', 'Fira Mono', Menlo, Monaco, Consolas,
'Courier New', monospace; 'Courier New', monospace;
$font-size-root: 16px; $font-size-root: 16px;
$font-size-base: 1rem; $font-size-base: 1rem;
$font-size-large: 1.2rem; $font-size-large: 1.2rem;
$font-size-small: .85rem; $font-size-small: 0.85rem;
$font-size-mini: .65rem; $font-size-mini: 0.65rem;
$font-size-text: $font-size-base; $font-size-text: $font-size-base;
$font-size-label: $font-size-base; $font-size-label: $font-size-base;
$font-size-title: 1.4rem; $font-size-title: 1.4rem;
@ -49,7 +49,7 @@ $line-height: 1.6;
// Sizes // Sizes
$spacer: 2rem; $spacer: 2rem;
$page-frame: .75rem; $page-frame: 0.75rem;
$break-point--small: 640px; $break-point--small: 640px;
$break-point--medium: 860px; $break-point--medium: 860px;
@ -57,6 +57,6 @@ $break-point--large: 1140px;
$break-point--huge: 1400px; $break-point--huge: 1400px;
$brand-border-width: 1px; $brand-border-width: 1px;
$border-radius: .2rem; $border-radius: 0.2rem;
$narrowWidth: 35rem; $narrowWidth: 35rem;

View File

@ -4,123 +4,123 @@
*, *,
*:before, *:before,
*:after { *:after {
box-sizing: border-box; box-sizing: border-box;
} }
/* stylelint-disable selector-max-id */ /* stylelint-disable selector-max-id */
html, html,
body, body,
#root { #root {
height: 100%; height: 100%;
} }
/* stylelint-enable selector-max-id */ /* stylelint-enable selector-max-id */
html { html {
font-size: $font-size-root; font-size: $font-size-root;
} }
body { body {
color: $brand-grey-light; color: $brand-grey-light;
font-size: $font-size-base; font-size: $font-size-base;
font-family: $font-family-base; font-family: $font-family-base;
font-weight: $font-weight-base; font-weight: $font-weight-base;
line-height: $line-height; line-height: $line-height;
background: $body-background; background: $body-background;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
position: relative; position: relative;
margin: 0; margin: 0;
@media screen and (min-width: $break-point--small) { @media screen and (min-width: $break-point--small) {
padding: $page-frame; padding: $page-frame;
} }
} }
a { a {
text-decoration: none;
color: $brand-pink;
transition: 0.2s ease-out;
&:hover,
&:focus {
color: darken($brand-pink, 15%);
text-decoration: none; text-decoration: none;
color: $brand-pink; transform: translate3d(0, -0.1rem, 0);
transition: .2s ease-out; }
&:hover, &:active {
&:focus { color: darken($brand-pink, 15%);
color: darken($brand-pink, 15%); text-decoration: none;
text-decoration: none; transform: none;
transform: translate3d(0, -.1rem, 0); transition: none;
} }
&:active {
color: darken($brand-pink, 15%);
text-decoration: none;
transform: none;
transition: none;
}
} }
p { p {
margin: 0; margin: 0;
margin-bottom: $spacer / $line-height; margin-bottom: $spacer / $line-height;
} }
// Lists // Lists
///////////////////////////////////// /////////////////////////////////////
ul { ul {
li { li {
&:before { &:before {
content: ' \25AA'; // Black Small Square: &#9642; content: ' \25AA'; // Black Small Square: &#9642;
top: -2px; top: -2px;
}
} }
}
} }
ol { ol {
counter-reset: ol-counter; counter-reset: ol-counter;
li { li {
&:before { &:before {
content: counter(ol-counter) '.'; content: counter(ol-counter) '.';
counter-increment: ol-counter; counter-increment: ol-counter;
font-weight: $font-weight-bold; font-weight: $font-weight-bold;
top: -1px; top: -1px;
}
} }
}
ul li:before { ul li:before {
content: ' \25AA'; content: ' \25AA';
} }
} }
ul, ul,
ol { ol {
margin-top: 0; margin-top: 0;
margin-bottom: $spacer; margin-bottom: $spacer;
padding-left: $spacer / $line-height; padding-left: $spacer / $line-height;
list-style: none; list-style: none;
li { li {
position: relative; position: relative;
display: block; display: block;
&:before { &:before {
position: absolute; position: absolute;
left: -($spacer / $line-height); left: -($spacer / $line-height);
color: $brand-grey-light; color: $brand-grey-light;
user-select: none; user-select: none;
}
+ li {
margin-top: $spacer / 8;
}
ul,
ol,
p {
margin-bottom: 0;
margin-top: $spacer / 8;
}
} }
+ li {
margin-top: $spacer / 8;
}
ul,
ol,
p {
margin-bottom: 0;
margin-top: $spacer / 8;
}
}
} }
// Inline typography // Inline typography
@ -129,27 +129,27 @@ ol {
b, b,
strong, strong,
.bold { .bold {
font-weight: $font-weight-bold; font-weight: $font-weight-bold;
} }
em, em,
.italic { .italic {
font-style: italic; font-style: italic;
} }
small { small {
font-size: $font-size-small; font-size: $font-size-small;
display: inline-block; display: inline-block;
} }
abbr[title], abbr[title],
dfn { dfn {
text-transform: none; text-transform: none;
font-style: normal; font-style: normal;
font-size: inherit; font-size: inherit;
border-bottom: 1px dashed $brand-grey-light; border-bottom: 1px dashed $brand-grey-light;
cursor: help; cursor: help;
font-feature-settings: inherit; font-feature-settings: inherit;
} }
h1, h1,
@ -157,30 +157,30 @@ h2,
h3, h3,
h4, h4,
h5 { h5 {
font-family: $font-family-title; font-family: $font-family-title;
color: inherit; color: inherit;
line-height: 1.2; line-height: 1.2;
font-weight: $font-weight-bold; font-weight: $font-weight-bold;
} }
h1 { h1 {
font-size: $font-size-h1; font-size: $font-size-h1;
} }
h2 { h2 {
font-size: $font-size-h2; font-size: $font-size-h2;
} }
h3 { h3 {
font-size: $font-size-h3; font-size: $font-size-h3;
} }
h4 { h4 {
font-size: $font-size-h4; font-size: $font-size-h4;
} }
h5 { h5 {
font-size: $font-size-h5; font-size: $font-size-h5;
} }
// Responsive Media // Responsive Media
@ -194,75 +194,75 @@ audio,
embed, embed,
canvas, canvas,
picture { picture {
max-width: 100%; max-width: 100%;
height: auto; height: auto;
margin: 0 auto; margin: 0 auto;
display: block; display: block;
} }
hr { hr {
margin: $spacer 0; margin: $spacer 0;
border: 0; border: 0;
border-bottom: .1rem solid $brand-grey-lighter; border-bottom: 0.1rem solid $brand-grey-lighter;
} }
// Quotes // Quotes
///////////////////////////////////// /////////////////////////////////////
q { q {
font-style: italic; font-style: italic;
} }
cite { cite {
font-style: normal; font-style: normal;
text-transform: uppercase; text-transform: uppercase;
} }
blockquote, blockquote,
blockquote > p { blockquote > p {
font-style: italic; font-style: italic;
color: lighten($brand-grey, 15%); color: lighten($brand-grey, 15%);
} }
blockquote { blockquote {
margin: 0 0 $spacer; margin: 0 0 $spacer;
padding-left: $spacer / 2; padding-left: $spacer / 2;
border-left: .2rem solid $brand-grey-lighter; border-left: 0.2rem solid $brand-grey-lighter;
@media screen and (min-width: $break-point--small) { @media screen and (min-width: $break-point--small) {
padding-left: $spacer / $line-height; padding-left: $spacer / $line-height;
} }
} }
// Tables // Tables
///////////////////////////////////// /////////////////////////////////////
table { table {
width: 100%; width: 100%;
margin-bottom: $spacer * $line-height; margin-bottom: $spacer * $line-height;
border-collapse: collapse; border-collapse: collapse;
th, th,
td { td {
border: 0; border: 0;
margin: 0; margin: 0;
padding: $spacer / 2; padding: $spacer / 2;
border-bottom: 1px solid $brand-grey-lighter; border-bottom: 1px solid $brand-grey-lighter;
text-align: left; text-align: left;
font-size: 90%; font-size: 90%;
&[align='center'] { &[align='center'] {
text-align: center; text-align: center;
}
&[align='right'] {
text-align: right;
}
} }
th { &[align='right'] {
font-weight: 600; text-align: right;
} }
}
th {
font-weight: 600;
}
} }
// Code // Code
@ -272,63 +272,63 @@ code,
kbd, kbd,
pre, pre,
samp { samp {
font-family: $font-family-monospace; font-family: $font-family-monospace;
font-size: $font-size-small; font-size: $font-size-small;
border-radius: $border-radius; border-radius: $border-radius;
text-shadow: none; text-shadow: none;
overflow-wrap: break-word; overflow-wrap: break-word;
word-wrap: break-word; word-wrap: break-word;
word-break: break-all; word-break: break-all;
h1 &, h1 &,
h2 &, h2 &,
h3 &, h3 &,
h4 &, h4 &,
h5 & { h5 & {
font-size: inherit; font-size: inherit;
} }
} }
:not(pre) > code { :not(pre) > code {
color: inherit; color: inherit;
display: inline-block; display: inline-block;
} }
a > code { a > code {
color: $brand-pink; color: $brand-pink;
} }
pre { 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; display: block;
margin-bottom: $spacer; color: $brand-grey-lighter;
padding: 0; overflow-wrap: normal;
background: lighten($brand-grey-lighter, 5%); word-wrap: normal;
word-break: normal;
// make 'em scrollable
overflow: auto; 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 // Selection
///////////////////////////////////// /////////////////////////////////////
::-moz-selection { ::-moz-selection {
background: $brand-grey-light; background: $brand-grey-light;
color: #fff; color: #fff;
} }
::selection { ::selection {
background: $brand-grey-light; background: $brand-grey-light;
color: #fff; color: #fff;
} }