1
0
mirror of https://github.com/oceanprotocol/commons.git synced 2023-03-15 18:03:00 +01:00
This commit is contained in:
Jernej Pregelj 2019-05-08 10:49:16 +02:00
commit ad633af59d
82 changed files with 2325 additions and 2074 deletions

View File

@ -4,9 +4,14 @@ language: node_js
node_js: node_js:
- '11' - '11'
before_install:
- npm install -g npm
- npm install -g codacy-coverage
script: script:
# - ./scripts/install.sh # runs automatically with npm ci # - ./scripts/install.sh # runs automatically with npm ci
- ./scripts/test.sh - ./scripts/test.sh
- ./scripts/coverage.sh
- ./scripts/build.sh - ./scripts/build.sh
notifications: notifications:
@ -15,8 +20,6 @@ notifications:
cache: cache:
directories: directories:
- node_modules - node_modules
- client/node_modules
- server/node_modules
deploy: deploy:
- provider: script - provider: script

View File

@ -4,6 +4,51 @@ All notable changes to this project will be documented in this file. Dates are d
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
#### [v0.2.7](https://github.com/oceanprotocol/commons/compare/v0.2.6...v0.2.7)
> 6 May 2019
- Add more tests [`#117`](https://github.com/oceanprotocol/commons/pull/117)
- more tests [`9bdfca4`](https://github.com/oceanprotocol/commons/commit/9bdfca4be4bc4568035f5335ea997413c699d22a)
- update changelog [`30d6679`](https://github.com/oceanprotocol/commons/commit/30d6679eb2ae1cb40f8deb053b49d5a6fb71f089)
- consume fix [`20299d9`](https://github.com/oceanprotocol/commons/commit/20299d91164b3dc7b4b1c7183bf34d8ca42ea201)
#### [v0.2.6](https://github.com/oceanprotocol/commons/compare/v0.2.5...v0.2.6)
> 30 April 2019
- add blockies for account display [`#115`](https://github.com/oceanprotocol/commons/pull/115)
- Setup coverage reporting [`#116`](https://github.com/oceanprotocol/commons/pull/116)
- report test coverage to Codacy [`5e8ed02`](https://github.com/oceanprotocol/commons/commit/5e8ed026d556e98471fb9e0fbb5b680955e0dc8a)
- hotfix for search input [`a8969bb`](https://github.com/oceanprotocol/commons/commit/a8969bb70c0f206ea38c969d3b22706d8a9af29d)
- Release 0.2.6 [`269acdf`](https://github.com/oceanprotocol/commons/commit/269acdfc8fc106090232b0fa9716d04c7e195062)
#### [v0.2.5](https://github.com/oceanprotocol/commons/compare/v0.2.4...v0.2.5)
> 29 April 2019
- sanitize search input [`#114`](https://github.com/oceanprotocol/commons/pull/114)
- update telegram link [`#113`](https://github.com/oceanprotocol/commons/pull/113)
- update changelog [`0d3afc8`](https://github.com/oceanprotocol/commons/commit/0d3afc821900fada63013fc8aec434a634c67199)
- Release 0.2.5 [`8290609`](https://github.com/oceanprotocol/commons/commit/82906093e60f4b52302b6f08f19787f65b24fafd)
#### [v0.2.4](https://github.com/oceanprotocol/commons/compare/v0.2.3...v0.2.4)
> 29 April 2019
- fix search titles [`#112`](https://github.com/oceanprotocol/commons/pull/112)
- Release 0.2.4 [`8f832b6`](https://github.com/oceanprotocol/commons/commit/8f832b63220a46a8de0061ffc2498186e2099315)
- update telegram link [`e6806f4`](https://github.com/oceanprotocol/commons/commit/e6806f467d6e2299bc0886eba53d0646dfa4df31)
#### [v0.2.3](https://github.com/oceanprotocol/commons/compare/v0.2.2...v0.2.3)
> 25 April 2019
- fix datepicker styles [`#109`](https://github.com/oceanprotocol/commons/pull/109)
- update changelog [`8a79db0`](https://github.com/oceanprotocol/commons/commit/8a79db04c8792e48224e225b70232441d9653283)
- hotfix for asset title user input [`17082c8`](https://github.com/oceanprotocol/commons/commit/17082c817f11f5124eb5865371dd07e6806a77da)
- lock Travis to Node.js v11 [`7e9013e`](https://github.com/oceanprotocol/commons/commit/7e9013e7cd37d060ce897dd84a55e471372ed843)
#### [v0.2.2](https://github.com/oceanprotocol/commons/compare/v0.2.1...v0.2.2) #### [v0.2.2](https://github.com/oceanprotocol/commons/compare/v0.2.1...v0.2.2)
> 23 April 2019 > 23 April 2019

View File

@ -6,6 +6,8 @@
> https://commons.oceanprotocol.com > https://commons.oceanprotocol.com
[![Build Status](https://travis-ci.com/oceanprotocol/commons.svg?branch=master)](https://travis-ci.com/oceanprotocol/commons) [![Build Status](https://travis-ci.com/oceanprotocol/commons.svg?branch=master)](https://travis-ci.com/oceanprotocol/commons)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/6a19987e62344b1c9c1d5bc9f315c733)](https://www.codacy.com/app/ocean-protocol/commons)
[![Codacy Badge](https://api.codacy.com/project/badge/Coverage/6a19987e62344b1c9c1d5bc9f315c733)](https://www.codacy.com/app/ocean-protocol/commons)
[![code style: prettier](https://img.shields.io/badge/code_style-prettier-7b1173.svg?style=flat-square)](https://github.com/prettier/prettier) [![code style: prettier](https://img.shields.io/badge/code_style-prettier-7b1173.svg?style=flat-square)](https://github.com/prettier/prettier)
[![js oceanprotocol](https://img.shields.io/badge/js-oceanprotocol-7b1173.svg)](https://github.com/oceanprotocol/eslint-config-oceanprotocol) [![js oceanprotocol](https://img.shields.io/badge/js-oceanprotocol-7b1173.svg)](https://github.com/oceanprotocol/eslint-config-oceanprotocol)
[![css bigchaindb](https://img.shields.io/badge/css-bigchaindb-39BA91.svg)](https://github.com/bigchaindb/stylelint-config-bigchaindb) [![css bigchaindb](https://img.shields.io/badge/css-bigchaindb-39BA91.svg)](https://github.com/bigchaindb/stylelint-config-bigchaindb)

View File

@ -0,0 +1,31 @@
const userMock = {
isLogged: false,
isLoading: false,
isWeb3: false,
isNile: false,
account: '',
web3: {},
ocean: {},
balance: { eth: 0, ocn: 0 },
network: '',
requestFromFaucet: jest.fn(),
unlockAccounts: jest.fn(),
message: ''
}
const userMockConnected = {
isLogged: true,
isLoading: false,
isWeb3: true,
isNile: true,
account: '0xxxxxx',
web3: {},
ocean: {},
balance: { eth: 0, ocn: 0 },
network: '',
requestFromFaucet: jest.fn(),
unlockAccounts: jest.fn(),
message: ''
}
export { userMock, userMockConnected }

601
client/package-lock.json generated
View File

@ -1301,6 +1301,345 @@
"uuid": "^3.3.2", "uuid": "^3.3.2",
"web3": "1.0.0-beta.37", "web3": "1.0.0-beta.37",
"whatwg-url": "^7.0.0" "whatwg-url": "^7.0.0"
},
"dependencies": {
"bn.js": {
"version": "4.11.6",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz",
"integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU="
},
"elliptic": {
"version": "6.3.3",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.3.3.tgz",
"integrity": "sha1-VILZZG1UvLif19mU/J4ulWiHbj8=",
"requires": {
"bn.js": "^4.4.0",
"brorand": "^1.0.1",
"hash.js": "^1.0.0",
"inherits": "^2.0.1"
}
},
"ethers": {
"version": "4.0.0-beta.1",
"resolved": "https://registry.npmjs.org/ethers/-/ethers-4.0.0-beta.1.tgz",
"integrity": "sha512-SoYhktEbLxf+fiux5SfCEwdzWENMvgIbMZD90I62s4GZD9nEjgEWy8ZboI3hck193Vs0bDoTohDISx84f2H2tw==",
"requires": {
"@types/node": "^10.3.2",
"aes-js": "3.0.0",
"bn.js": "^4.4.0",
"elliptic": "6.3.3",
"hash.js": "1.1.3",
"js-sha3": "0.5.7",
"scrypt-js": "2.0.3",
"setimmediate": "1.0.4",
"uuid": "2.0.1",
"xmlhttprequest": "1.8.0"
},
"dependencies": {
"setimmediate": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.4.tgz",
"integrity": "sha1-IOgd5iLUoCWIzgyNqJc8vPHTE48="
},
"uuid": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.1.tgz",
"integrity": "sha1-wqMN7bPlNdcsz4LjQ5QaULqFM6w="
}
}
},
"eventemitter3": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.1.1.tgz",
"integrity": "sha1-R3hr2qCHyvext15zq8XH1UAVjNA="
},
"fs-extra": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-2.1.2.tgz",
"integrity": "sha1-BGxwFjzvmq1GsOSn+kZ/si1x3jU=",
"requires": {
"graceful-fs": "^4.1.2",
"jsonfile": "^2.1.0"
}
},
"hash.js": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz",
"integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==",
"requires": {
"inherits": "^2.0.3",
"minimalistic-assert": "^1.0.0"
}
},
"js-sha3": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz",
"integrity": "sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc="
},
"jsonfile": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
"integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=",
"requires": {
"graceful-fs": "^4.1.6"
}
},
"scrypt-js": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-2.0.3.tgz",
"integrity": "sha1-uwBAvgMEPamgEqLOqfyfhSz8h9Q="
},
"swarm-js": {
"version": "0.1.37",
"resolved": "https://registry.npmjs.org/swarm-js/-/swarm-js-0.1.37.tgz",
"integrity": "sha512-G8gi5fcXP/2upwiuOShJ258sIufBVztekgobr3cVgYXObZwJ5AXLqZn52AI+/ffft29pJexF9WNdUxjlkVehoQ==",
"requires": {
"bluebird": "^3.5.0",
"buffer": "^5.0.5",
"decompress": "^4.0.0",
"eth-lib": "^0.1.26",
"fs-extra": "^2.1.2",
"fs-promise": "^2.0.0",
"got": "^7.1.0",
"mime-types": "^2.1.16",
"mkdirp-promise": "^5.0.1",
"mock-fs": "^4.1.0",
"setimmediate": "^1.0.5",
"tar.gz": "^1.0.5",
"xhr-request-promise": "^0.1.2"
}
},
"web3": {
"version": "1.0.0-beta.37",
"resolved": "https://registry.npmjs.org/web3/-/web3-1.0.0-beta.37.tgz",
"integrity": "sha512-8XLgUspdzicC/xHG82TLrcF/Fxzj2XYNJ1KTYnepOI77bj5rvpsxxwHYBWQ6/JOjk0HkZqoBfnXWgcIHCDhZhQ==",
"requires": {
"web3-bzz": "1.0.0-beta.37",
"web3-core": "1.0.0-beta.37",
"web3-eth": "1.0.0-beta.37",
"web3-eth-personal": "1.0.0-beta.37",
"web3-net": "1.0.0-beta.37",
"web3-shh": "1.0.0-beta.37",
"web3-utils": "1.0.0-beta.37"
}
},
"web3-bzz": {
"version": "1.0.0-beta.37",
"resolved": "https://registry.npmjs.org/web3-bzz/-/web3-bzz-1.0.0-beta.37.tgz",
"integrity": "sha512-E+dho49Nsm/QpQvYWOF35YDsQrMvLB19AApENxhlQsu6HpWQt534DQul0t3Y/aAh8rlKD6Kanxt8LhHDG3vejQ==",
"requires": {
"got": "7.1.0",
"swarm-js": "0.1.37",
"underscore": "1.8.3"
}
},
"web3-core": {
"version": "1.0.0-beta.37",
"resolved": "https://registry.npmjs.org/web3-core/-/web3-core-1.0.0-beta.37.tgz",
"integrity": "sha512-cIwEqCj7OJyefQNauI0HOgW4sSaOQ98V99H2/HEIlnCZylsDzfw7gtQUdwnRFiIyIxjbWy3iWsjwDPoXNPZBYg==",
"requires": {
"web3-core-helpers": "1.0.0-beta.37",
"web3-core-method": "1.0.0-beta.37",
"web3-core-requestmanager": "1.0.0-beta.37",
"web3-utils": "1.0.0-beta.37"
}
},
"web3-core-helpers": {
"version": "1.0.0-beta.37",
"resolved": "https://registry.npmjs.org/web3-core-helpers/-/web3-core-helpers-1.0.0-beta.37.tgz",
"integrity": "sha512-efaLOzN28RMnbugnyelgLwPWWaSwElQzcAJ/x3PZu+uPloM/lE5x0YuBKvIh7/PoSMlHqtRWj1B8CpuQOUQ5Ew==",
"requires": {
"underscore": "1.8.3",
"web3-eth-iban": "1.0.0-beta.37",
"web3-utils": "1.0.0-beta.37"
}
},
"web3-core-method": {
"version": "1.0.0-beta.37",
"resolved": "https://registry.npmjs.org/web3-core-method/-/web3-core-method-1.0.0-beta.37.tgz",
"integrity": "sha512-pKWFUeqnVmzx3VrZg+CseSdrl/Yrk2ioid/HzolNXZE6zdoITZL0uRjnsbqXGEzgRRd1Oe/pFndpTlRsnxXloA==",
"requires": {
"underscore": "1.8.3",
"web3-core-helpers": "1.0.0-beta.37",
"web3-core-promievent": "1.0.0-beta.37",
"web3-core-subscriptions": "1.0.0-beta.37",
"web3-utils": "1.0.0-beta.37"
}
},
"web3-core-subscriptions": {
"version": "1.0.0-beta.37",
"resolved": "https://registry.npmjs.org/web3-core-subscriptions/-/web3-core-subscriptions-1.0.0-beta.37.tgz",
"integrity": "sha512-FdXl8so9kwkRRWziuCSpFsAuAdg9KvpXa1fQlT16uoGcYYfxwFO/nkwyBGQzkZt7emShI2IRugcazyPCZDwkOA==",
"requires": {
"eventemitter3": "1.1.1",
"underscore": "1.8.3",
"web3-core-helpers": "1.0.0-beta.37"
}
},
"web3-eth": {
"version": "1.0.0-beta.37",
"resolved": "https://registry.npmjs.org/web3-eth/-/web3-eth-1.0.0-beta.37.tgz",
"integrity": "sha512-Eb3aGtkz3G9q+Z9DKgSQNbn/u8RtcZQQ0R4sW9hy5KK47GoT6vab5c6DiD3QWzI0BzitHzR5Ji+3VHf/hPUGgw==",
"requires": {
"underscore": "1.8.3",
"web3-core": "1.0.0-beta.37",
"web3-core-helpers": "1.0.0-beta.37",
"web3-core-method": "1.0.0-beta.37",
"web3-core-subscriptions": "1.0.0-beta.37",
"web3-eth-abi": "1.0.0-beta.37",
"web3-eth-accounts": "1.0.0-beta.37",
"web3-eth-contract": "1.0.0-beta.37",
"web3-eth-ens": "1.0.0-beta.37",
"web3-eth-iban": "1.0.0-beta.37",
"web3-eth-personal": "1.0.0-beta.37",
"web3-net": "1.0.0-beta.37",
"web3-utils": "1.0.0-beta.37"
}
},
"web3-eth-abi": {
"version": "1.0.0-beta.37",
"resolved": "https://registry.npmjs.org/web3-eth-abi/-/web3-eth-abi-1.0.0-beta.37.tgz",
"integrity": "sha512-g9DKZGM2OqwKp/tX3W/yihcj7mQCtJ6CXyZXEIZfuDyRBED/iSEIFfieDOd+yo16sokLMig6FG7ADhhu+19hdA==",
"requires": {
"ethers": "4.0.0-beta.1",
"underscore": "1.8.3",
"web3-utils": "1.0.0-beta.37"
}
},
"web3-eth-accounts": {
"version": "1.0.0-beta.37",
"resolved": "https://registry.npmjs.org/web3-eth-accounts/-/web3-eth-accounts-1.0.0-beta.37.tgz",
"integrity": "sha512-uvbHL62/zwo4GDmwKdqH9c/EgYd8QVnAfpVw8D3epSISpgbONNY7Hr4MRMSd/CqAP12l2Ls9JVQGLhhC83bW6g==",
"requires": {
"any-promise": "1.3.0",
"crypto-browserify": "3.12.0",
"eth-lib": "0.2.7",
"scrypt.js": "0.2.0",
"underscore": "1.8.3",
"uuid": "2.0.1",
"web3-core": "1.0.0-beta.37",
"web3-core-helpers": "1.0.0-beta.37",
"web3-core-method": "1.0.0-beta.37",
"web3-utils": "1.0.0-beta.37"
},
"dependencies": {
"elliptic": {
"version": "6.4.1",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz",
"integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==",
"requires": {
"bn.js": "^4.4.0",
"brorand": "^1.0.1",
"hash.js": "^1.0.0",
"hmac-drbg": "^1.0.0",
"inherits": "^2.0.1",
"minimalistic-assert": "^1.0.0",
"minimalistic-crypto-utils": "^1.0.0"
}
},
"eth-lib": {
"version": "0.2.7",
"resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.7.tgz",
"integrity": "sha1-L5Pxex4jrsN1nNSj/iDBKGo/wco=",
"requires": {
"bn.js": "^4.11.6",
"elliptic": "^6.4.0",
"xhr-request-promise": "^0.1.2"
}
},
"uuid": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.1.tgz",
"integrity": "sha1-wqMN7bPlNdcsz4LjQ5QaULqFM6w="
}
}
},
"web3-eth-contract": {
"version": "1.0.0-beta.37",
"resolved": "https://registry.npmjs.org/web3-eth-contract/-/web3-eth-contract-1.0.0-beta.37.tgz",
"integrity": "sha512-h1B3A8Z/C7BlnTCHkrWbXZQTViDxfR12lKMeTkT8Sqj5phFmxrBlPE4ORy4lf1Dk5b23mZYE0r/IRACx4ThCrQ==",
"requires": {
"underscore": "1.8.3",
"web3-core": "1.0.0-beta.37",
"web3-core-helpers": "1.0.0-beta.37",
"web3-core-method": "1.0.0-beta.37",
"web3-core-promievent": "1.0.0-beta.37",
"web3-core-subscriptions": "1.0.0-beta.37",
"web3-eth-abi": "1.0.0-beta.37",
"web3-utils": "1.0.0-beta.37"
}
},
"web3-eth-ens": {
"version": "1.0.0-beta.37",
"resolved": "https://registry.npmjs.org/web3-eth-ens/-/web3-eth-ens-1.0.0-beta.37.tgz",
"integrity": "sha512-dR3UkrVzdRrJhfP57xBPx0CMiVnCcYFvh+u2XMkGydrhHgupSUkjqGr89xry/j1T0BkuN9mikpbyhdCVMXqMbg==",
"requires": {
"eth-ens-namehash": "2.0.8",
"underscore": "1.8.3",
"web3-core": "1.0.0-beta.37",
"web3-core-helpers": "1.0.0-beta.37",
"web3-core-promievent": "1.0.0-beta.37",
"web3-eth-abi": "1.0.0-beta.37",
"web3-eth-contract": "1.0.0-beta.37",
"web3-utils": "1.0.0-beta.37"
}
},
"web3-eth-iban": {
"version": "1.0.0-beta.37",
"resolved": "https://registry.npmjs.org/web3-eth-iban/-/web3-eth-iban-1.0.0-beta.37.tgz",
"integrity": "sha512-WQRniGJFxH/XCbd7miO6+jnUG+6bvuzfeufPIiOtCbeIC1ypp1kSqER8YVBDrTyinU1xnf1U5v0KBZ2yiWBJxQ==",
"requires": {
"bn.js": "4.11.6",
"web3-utils": "1.0.0-beta.37"
}
},
"web3-eth-personal": {
"version": "1.0.0-beta.37",
"resolved": "https://registry.npmjs.org/web3-eth-personal/-/web3-eth-personal-1.0.0-beta.37.tgz",
"integrity": "sha512-B4dZpGbD+nGnn48i6nJBqrQ+HB7oDmd+Q3wGRKOsHSK5HRWO/KwYeA7wgwamMAElkut50lIsT9EJl4Apfk3G5Q==",
"requires": {
"web3-core": "1.0.0-beta.37",
"web3-core-helpers": "1.0.0-beta.37",
"web3-core-method": "1.0.0-beta.37",
"web3-net": "1.0.0-beta.37",
"web3-utils": "1.0.0-beta.37"
}
},
"web3-net": {
"version": "1.0.0-beta.37",
"resolved": "https://registry.npmjs.org/web3-net/-/web3-net-1.0.0-beta.37.tgz",
"integrity": "sha512-xG/uBtMdDa1UMXw9KjDUgf3fXA/fDEJUYUS0TDn+U9PMgngA+UVECHNNvQTrVVDxEky38V3sahwIDiopNsQdsw==",
"requires": {
"web3-core": "1.0.0-beta.37",
"web3-core-method": "1.0.0-beta.37",
"web3-utils": "1.0.0-beta.37"
}
},
"web3-shh": {
"version": "1.0.0-beta.37",
"resolved": "https://registry.npmjs.org/web3-shh/-/web3-shh-1.0.0-beta.37.tgz",
"integrity": "sha512-h5STG/xqZNQWtCLYOu7NiMqwqPea8SfkKQUPUFxXKIPVCFVKpHuQEwW1qcPQRJMLhlQIv17xuoUe1A+RzDNbrw==",
"requires": {
"web3-core": "1.0.0-beta.37",
"web3-core-method": "1.0.0-beta.37",
"web3-core-subscriptions": "1.0.0-beta.37",
"web3-net": "1.0.0-beta.37"
}
},
"web3-utils": {
"version": "1.0.0-beta.37",
"resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.0.0-beta.37.tgz",
"integrity": "sha512-kA1fyhO8nKgU21wi30oJQ/ssvu+9srMdjOTKbHYbQe4ATPcr5YNwwrxG3Bcpbu1bEwRUVKHCkqi+wTvcAWBdlQ==",
"requires": {
"bn.js": "4.11.6",
"eth-lib": "0.1.27",
"ethjs-unit": "0.1.6",
"number-to-bn": "1.7.0",
"randomhex": "0.1.5",
"underscore": "1.8.3",
"utf8": "2.1.1"
}
}
} }
}, },
"@oceanprotocol/typographies": { "@oceanprotocol/typographies": {
@ -1308,6 +1647,16 @@
"resolved": "https://registry.npmjs.org/@oceanprotocol/typographies/-/typographies-0.1.0.tgz", "resolved": "https://registry.npmjs.org/@oceanprotocol/typographies/-/typographies-0.1.0.tgz",
"integrity": "sha512-kMsZsqvzpz9KzVbVZzllwhPoIC3zbqsdRrClagZL/C2PHzgLrKGC1kYn3gPt0RMIFg9ZjrwieKaxlgIK9i9zzg==" "integrity": "sha512-kMsZsqvzpz9KzVbVZzllwhPoIC3zbqsdRrClagZL/C2PHzgLrKGC1kYn3gPt0RMIFg9ZjrwieKaxlgIK9i9zzg=="
}, },
"@react-mock/state": {
"version": "0.1.8",
"resolved": "https://registry.npmjs.org/@react-mock/state/-/state-0.1.8.tgz",
"integrity": "sha512-oX16w3FKhfF2+nQE+C4frrfVddzwhF4YEYxF8frXzboEDZsxCRpFuQC0za3T59ZxI4ygBarBqDrhYzipGsD9TQ==",
"dev": true,
"requires": {
"@babel/runtime": "^7.1.2",
"lodash": "^4.17.11"
}
},
"@sheerun/mutationobserver-shim": { "@sheerun/mutationobserver-shim": {
"version": "0.3.2", "version": "0.3.2",
"resolved": "https://registry.npmjs.org/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.2.tgz", "resolved": "https://registry.npmjs.org/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.2.tgz",
@ -4952,15 +5301,6 @@
"version": "3.9.0", "version": "3.9.0",
"resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz",
"integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=" "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek="
},
"get-stream": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz",
"integrity": "sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4=",
"requires": {
"object-assign": "^4.0.1",
"pinkie-promise": "^2.0.0"
}
} }
} }
}, },
@ -5239,9 +5579,9 @@
} }
}, },
"dom-testing-library": { "dom-testing-library": {
"version": "3.19.4", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/dom-testing-library/-/dom-testing-library-3.19.4.tgz", "resolved": "https://registry.npmjs.org/dom-testing-library/-/dom-testing-library-4.0.1.tgz",
"integrity": "sha512-GJOx8CLpnkvM3takILOsld/itUUc9+7Qh6caN1Spj6+9jIgNPY36fsvoH7sEgYokC0lBRdttO7G7fIFYCXlmcA==", "integrity": "sha512-Yr0yWlpI2QdTDEgPEk0TEekwP4VyZlJpl9E7nKP2FCKni44cb1jzjsy9KX6hBDsNA7EVlPpq9DHzO2eoEaqDZg==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/runtime": "^7.4.3", "@babel/runtime": "^7.4.3",
@ -6001,6 +6341,10 @@
"xhr-request-promise": "^0.1.2" "xhr-request-promise": "^0.1.2"
} }
}, },
"ethereum-blockies": {
"version": "github:MyEtherWallet/blockies#d36f87e50149aacafb34f099fe0bea1df76e010c",
"from": "github:MyEtherWallet/blockies"
},
"ethereumjs-util": { "ethereumjs-util": {
"version": "6.1.0", "version": "6.1.0",
"resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.1.0.tgz", "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.1.0.tgz",
@ -6820,6 +7164,25 @@
"fs-extra": "^2.0.0", "fs-extra": "^2.0.0",
"mz": "^2.6.0", "mz": "^2.6.0",
"thenify-all": "^1.6.0" "thenify-all": "^1.6.0"
},
"dependencies": {
"fs-extra": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-2.1.2.tgz",
"integrity": "sha1-BGxwFjzvmq1GsOSn+kZ/si1x3jU=",
"requires": {
"graceful-fs": "^4.1.2",
"jsonfile": "^2.1.0"
}
},
"jsonfile": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
"integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=",
"requires": {
"graceful-fs": "^4.1.6"
}
}
} }
}, },
"fs-write-stream-atomic": { "fs-write-stream-atomic": {
@ -6912,9 +7275,13 @@
"dev": true "dev": true
}, },
"get-stream": { "get-stream": {
"version": "3.0.0", "version": "2.3.1",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz",
"integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" "integrity": "sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4=",
"requires": {
"object-assign": "^4.0.1",
"pinkie-promise": "^2.0.0"
}
}, },
"get-value": { "get-value": {
"version": "2.0.6", "version": "2.0.6",
@ -7070,6 +7437,13 @@
"timed-out": "^4.0.0", "timed-out": "^4.0.0",
"url-parse-lax": "^1.0.0", "url-parse-lax": "^1.0.0",
"url-to-options": "^1.0.1" "url-to-options": "^1.0.1"
},
"dependencies": {
"get-stream": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
"integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
}
} }
}, },
"graceful-fs": { "graceful-fs": {
@ -13389,13 +13763,13 @@
} }
}, },
"react-testing-library": { "react-testing-library": {
"version": "6.1.2", "version": "7.0.0",
"resolved": "https://registry.npmjs.org/react-testing-library/-/react-testing-library-6.1.2.tgz", "resolved": "https://registry.npmjs.org/react-testing-library/-/react-testing-library-7.0.0.tgz",
"integrity": "sha512-z69lhRDGe7u/NOjDCeFRoe1cB5ckJ4656n0tj/Fdcr6OoBUu7q9DBw0ftR7v5i3GRpdSWelnvl+feZFOyXyxwg==", "integrity": "sha512-8SHqwG+uhN9VhAgNVkVa3f7VjTw/L5CIaoAxKmy+EZuDQ6O+VsfcpRAyUw3MDL1h8S/gGrEiazmHBVL/uXsftA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/runtime": "^7.4.2", "@babel/runtime": "^7.4.3",
"dom-testing-library": "^3.19.0" "dom-testing-library": "^4.0.0"
} }
}, },
"react-transition-group": { "react-transition-group": {
@ -16319,6 +16693,13 @@
"requires": { "requires": {
"any-promise": "1.3.0", "any-promise": "1.3.0",
"eventemitter3": "1.1.1" "eventemitter3": "1.1.1"
},
"dependencies": {
"eventemitter3": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.1.1.tgz",
"integrity": "sha1-R3hr2qCHyvext15zq8XH1UAVjNA="
}
} }
}, },
"web3-core-requestmanager": { "web3-core-requestmanager": {
@ -16331,6 +16712,46 @@
"web3-providers-http": "1.0.0-beta.37", "web3-providers-http": "1.0.0-beta.37",
"web3-providers-ipc": "1.0.0-beta.37", "web3-providers-ipc": "1.0.0-beta.37",
"web3-providers-ws": "1.0.0-beta.37" "web3-providers-ws": "1.0.0-beta.37"
},
"dependencies": {
"bn.js": {
"version": "4.11.6",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz",
"integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU="
},
"web3-core-helpers": {
"version": "1.0.0-beta.37",
"resolved": "https://registry.npmjs.org/web3-core-helpers/-/web3-core-helpers-1.0.0-beta.37.tgz",
"integrity": "sha512-efaLOzN28RMnbugnyelgLwPWWaSwElQzcAJ/x3PZu+uPloM/lE5x0YuBKvIh7/PoSMlHqtRWj1B8CpuQOUQ5Ew==",
"requires": {
"underscore": "1.8.3",
"web3-eth-iban": "1.0.0-beta.37",
"web3-utils": "1.0.0-beta.37"
}
},
"web3-eth-iban": {
"version": "1.0.0-beta.37",
"resolved": "https://registry.npmjs.org/web3-eth-iban/-/web3-eth-iban-1.0.0-beta.37.tgz",
"integrity": "sha512-WQRniGJFxH/XCbd7miO6+jnUG+6bvuzfeufPIiOtCbeIC1ypp1kSqER8YVBDrTyinU1xnf1U5v0KBZ2yiWBJxQ==",
"requires": {
"bn.js": "4.11.6",
"web3-utils": "1.0.0-beta.37"
}
},
"web3-utils": {
"version": "1.0.0-beta.37",
"resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.0.0-beta.37.tgz",
"integrity": "sha512-kA1fyhO8nKgU21wi30oJQ/ssvu+9srMdjOTKbHYbQe4ATPcr5YNwwrxG3Bcpbu1bEwRUVKHCkqi+wTvcAWBdlQ==",
"requires": {
"bn.js": "4.11.6",
"eth-lib": "0.1.27",
"ethjs-unit": "0.1.6",
"number-to-bn": "1.7.0",
"randomhex": "0.1.5",
"underscore": "1.8.3",
"utf8": "2.1.1"
}
}
} }
}, },
"web3-core-subscriptions": { "web3-core-subscriptions": {
@ -16482,6 +16903,46 @@
"requires": { "requires": {
"web3-core-helpers": "1.0.0-beta.37", "web3-core-helpers": "1.0.0-beta.37",
"xhr2-cookies": "1.1.0" "xhr2-cookies": "1.1.0"
},
"dependencies": {
"bn.js": {
"version": "4.11.6",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz",
"integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU="
},
"web3-core-helpers": {
"version": "1.0.0-beta.37",
"resolved": "https://registry.npmjs.org/web3-core-helpers/-/web3-core-helpers-1.0.0-beta.37.tgz",
"integrity": "sha512-efaLOzN28RMnbugnyelgLwPWWaSwElQzcAJ/x3PZu+uPloM/lE5x0YuBKvIh7/PoSMlHqtRWj1B8CpuQOUQ5Ew==",
"requires": {
"underscore": "1.8.3",
"web3-eth-iban": "1.0.0-beta.37",
"web3-utils": "1.0.0-beta.37"
}
},
"web3-eth-iban": {
"version": "1.0.0-beta.37",
"resolved": "https://registry.npmjs.org/web3-eth-iban/-/web3-eth-iban-1.0.0-beta.37.tgz",
"integrity": "sha512-WQRniGJFxH/XCbd7miO6+jnUG+6bvuzfeufPIiOtCbeIC1ypp1kSqER8YVBDrTyinU1xnf1U5v0KBZ2yiWBJxQ==",
"requires": {
"bn.js": "4.11.6",
"web3-utils": "1.0.0-beta.37"
}
},
"web3-utils": {
"version": "1.0.0-beta.37",
"resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.0.0-beta.37.tgz",
"integrity": "sha512-kA1fyhO8nKgU21wi30oJQ/ssvu+9srMdjOTKbHYbQe4ATPcr5YNwwrxG3Bcpbu1bEwRUVKHCkqi+wTvcAWBdlQ==",
"requires": {
"bn.js": "4.11.6",
"eth-lib": "0.1.27",
"ethjs-unit": "0.1.6",
"number-to-bn": "1.7.0",
"randomhex": "0.1.5",
"underscore": "1.8.3",
"utf8": "2.1.1"
}
}
} }
}, },
"web3-providers-ipc": { "web3-providers-ipc": {
@ -16492,6 +16953,46 @@
"oboe": "2.1.3", "oboe": "2.1.3",
"underscore": "1.8.3", "underscore": "1.8.3",
"web3-core-helpers": "1.0.0-beta.37" "web3-core-helpers": "1.0.0-beta.37"
},
"dependencies": {
"bn.js": {
"version": "4.11.6",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz",
"integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU="
},
"web3-core-helpers": {
"version": "1.0.0-beta.37",
"resolved": "https://registry.npmjs.org/web3-core-helpers/-/web3-core-helpers-1.0.0-beta.37.tgz",
"integrity": "sha512-efaLOzN28RMnbugnyelgLwPWWaSwElQzcAJ/x3PZu+uPloM/lE5x0YuBKvIh7/PoSMlHqtRWj1B8CpuQOUQ5Ew==",
"requires": {
"underscore": "1.8.3",
"web3-eth-iban": "1.0.0-beta.37",
"web3-utils": "1.0.0-beta.37"
}
},
"web3-eth-iban": {
"version": "1.0.0-beta.37",
"resolved": "https://registry.npmjs.org/web3-eth-iban/-/web3-eth-iban-1.0.0-beta.37.tgz",
"integrity": "sha512-WQRniGJFxH/XCbd7miO6+jnUG+6bvuzfeufPIiOtCbeIC1ypp1kSqER8YVBDrTyinU1xnf1U5v0KBZ2yiWBJxQ==",
"requires": {
"bn.js": "4.11.6",
"web3-utils": "1.0.0-beta.37"
}
},
"web3-utils": {
"version": "1.0.0-beta.37",
"resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.0.0-beta.37.tgz",
"integrity": "sha512-kA1fyhO8nKgU21wi30oJQ/ssvu+9srMdjOTKbHYbQe4ATPcr5YNwwrxG3Bcpbu1bEwRUVKHCkqi+wTvcAWBdlQ==",
"requires": {
"bn.js": "4.11.6",
"eth-lib": "0.1.27",
"ethjs-unit": "0.1.6",
"number-to-bn": "1.7.0",
"randomhex": "0.1.5",
"underscore": "1.8.3",
"utf8": "2.1.1"
}
}
} }
}, },
"web3-providers-ws": { "web3-providers-ws": {
@ -16502,6 +17003,56 @@
"underscore": "1.8.3", "underscore": "1.8.3",
"web3-core-helpers": "1.0.0-beta.37", "web3-core-helpers": "1.0.0-beta.37",
"websocket": "git://github.com/frozeman/WebSocket-Node.git#6c72925e3f8aaaea8dc8450f97627e85263999f2" "websocket": "git://github.com/frozeman/WebSocket-Node.git#6c72925e3f8aaaea8dc8450f97627e85263999f2"
},
"dependencies": {
"bn.js": {
"version": "4.11.6",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz",
"integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU="
},
"web3-core-helpers": {
"version": "1.0.0-beta.37",
"resolved": "https://registry.npmjs.org/web3-core-helpers/-/web3-core-helpers-1.0.0-beta.37.tgz",
"integrity": "sha512-efaLOzN28RMnbugnyelgLwPWWaSwElQzcAJ/x3PZu+uPloM/lE5x0YuBKvIh7/PoSMlHqtRWj1B8CpuQOUQ5Ew==",
"requires": {
"underscore": "1.8.3",
"web3-eth-iban": "1.0.0-beta.37",
"web3-utils": "1.0.0-beta.37"
}
},
"web3-eth-iban": {
"version": "1.0.0-beta.37",
"resolved": "https://registry.npmjs.org/web3-eth-iban/-/web3-eth-iban-1.0.0-beta.37.tgz",
"integrity": "sha512-WQRniGJFxH/XCbd7miO6+jnUG+6bvuzfeufPIiOtCbeIC1ypp1kSqER8YVBDrTyinU1xnf1U5v0KBZ2yiWBJxQ==",
"requires": {
"bn.js": "4.11.6",
"web3-utils": "1.0.0-beta.37"
}
},
"web3-utils": {
"version": "1.0.0-beta.37",
"resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.0.0-beta.37.tgz",
"integrity": "sha512-kA1fyhO8nKgU21wi30oJQ/ssvu+9srMdjOTKbHYbQe4ATPcr5YNwwrxG3Bcpbu1bEwRUVKHCkqi+wTvcAWBdlQ==",
"requires": {
"bn.js": "4.11.6",
"eth-lib": "0.1.27",
"ethjs-unit": "0.1.6",
"number-to-bn": "1.7.0",
"randomhex": "0.1.5",
"underscore": "1.8.3",
"utf8": "2.1.1"
}
},
"websocket": {
"version": "git://github.com/frozeman/WebSocket-Node.git#6c72925e3f8aaaea8dc8450f97627e85263999f2",
"from": "git://github.com/frozeman/WebSocket-Node.git#browserifyCompatible",
"requires": {
"debug": "^2.2.0",
"nan": "^2.3.3",
"typedarray-to-buffer": "^3.1.2",
"yaeti": "^0.0.6"
}
}
} }
}, },
"web3-shh": { "web3-shh": {
@ -16850,16 +17401,6 @@
"source-map": "~0.6.1" "source-map": "~0.6.1"
} }
}, },
"websocket": {
"version": "git://github.com/frozeman/WebSocket-Node.git#6c72925e3f8aaaea8dc8450f97627e85263999f2",
"from": "git://github.com/frozeman/WebSocket-Node.git#browserifyCompatible",
"requires": {
"debug": "^2.2.0",
"nan": "^2.3.3",
"typedarray-to-buffer": "^3.1.2",
"yaeti": "^0.0.6"
}
},
"websocket-driver": { "websocket-driver": {
"version": "0.7.0", "version": "0.7.0",
"resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz", "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz",

View File

@ -6,27 +6,29 @@
"scripts": { "scripts": {
"start": "react-scripts start", "start": "react-scripts start",
"build": "react-scripts --max_old_space_size=4096 build", "build": "react-scripts --max_old_space_size=4096 build",
"test": "react-scripts test --coverage", "test": "react-scripts test --coverage --watchAll=false",
"test:watch": "react-scripts test --coverage --watch", "test:watch": "react-scripts test --coverage",
"eject": "react-scripts eject" "eject": "react-scripts eject",
"coverage": "cat coverage/lcov.info | codacy-coverage --token 8801f827fe1144ffa85cd7da94f2bbf7"
}, },
"dependencies": { "dependencies": {
"@oceanprotocol/art": "^2.2.0", "@oceanprotocol/art": "^2.2.0",
"@oceanprotocol/squid": "^0.5.7", "@oceanprotocol/squid": "^0.5.8",
"@oceanprotocol/typographies": "^0.1.0", "@oceanprotocol/typographies": "^0.1.0",
"classnames": "^2.2.6", "classnames": "^2.2.6",
"ethereum-blockies": "MyEtherWallet/blockies",
"filesize": "^4.1.2", "filesize": "^4.1.2",
"history": "^4.9.0", "history": "^4.9.0",
"is-url": "^1.2.4", "is-url": "^1.2.4",
"moment": "^2.24.0", "moment": "^2.24.0",
"query-string": "^6.4.2", "query-string": "^6.5.0",
"react": "^16.8.6", "react": "^16.8.6",
"react-datepicker": "^2.3.0", "react-datepicker": "^2.5.0",
"react-dom": "^16.8.6", "react-dom": "^16.8.6",
"react-dotdotdot": "^1.2.3", "react-dotdotdot": "^1.3.0",
"react-ga": "^2.5.7", "react-ga": "^2.5.7",
"react-helmet": "^5.2.0", "react-helmet": "^5.2.1",
"react-markdown": "^4.0.6", "react-markdown": "^4.0.8",
"react-moment": "^0.9.2", "react-moment": "^0.9.2",
"react-paginate": "^6.3.0", "react-paginate": "^6.3.0",
"react-popper": "^1.3.3", "react-popper": "^1.3.3",
@ -36,24 +38,25 @@
"web3": "1.0.0-beta.37" "web3": "1.0.0-beta.37"
}, },
"devDependencies": { "devDependencies": {
"@react-mock/state": "^0.1.8",
"@types/classnames": "^2.2.7", "@types/classnames": "^2.2.7",
"@types/filesize": "^4.1.0", "@types/filesize": "^4.1.0",
"@types/is-url": "^1.2.28", "@types/is-url": "^1.2.28",
"@types/jest": "^24.0.11", "@types/jest": "^24.0.12",
"@types/react": "^16.8.13", "@types/react": "^16.8.15",
"@types/react-datepicker": "^2.2.1", "@types/react-datepicker": "^2.3.0",
"@types/react-dom": "^16.8.3", "@types/react-dom": "^16.8.4",
"@types/react-dotdotdot": "^1.2.0", "@types/react-dotdotdot": "^1.2.0",
"@types/react-helmet": "^5.0.8", "@types/react-helmet": "^5.0.8",
"@types/react-paginate": "^6.2.1", "@types/react-paginate": "^6.2.1",
"@types/react-router-dom": "^4.3.1", "@types/react-router-dom": "^4.3.2",
"@types/react-transition-group": "^2.8.0", "@types/react-transition-group": "^2.9.1",
"@types/web3": "^1.0.18", "@types/web3": "^1.0.18",
"jest-dom": "^3.1.3", "jest-dom": "^3.1.4",
"node-sass": "^4.11.0", "node-sass": "^4.12.0",
"react-scripts": "^3.0.0", "react-scripts": "^3.0.0",
"react-testing-library": "^6.1.2", "react-testing-library": "^7.0.0",
"typescript": "^3.4.3" "typescript": "^3.4.5"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@ -64,5 +67,11 @@
"not dead", "not dead",
"not ie <= 11", "not ie <= 11",
"not op_mini all" "not op_mini all"
],
"jest": {
"collectCoverageFrom": [
"src/**/*.{ts,tsx}",
"!src/serviceWorker.ts"
] ]
} }
}

View File

@ -0,0 +1,5 @@
/// <reference types="node" />
declare module 'ethereum-blockies' {
export function toDataUrl(address: string): string
}

View File

@ -1,10 +1,25 @@
import React from 'react' import React from 'react'
import { render } from 'react-testing-library' import { render } from 'react-testing-library'
import App from './App' import App from './App'
import { User } from './context'
import { userMock } from '../__mocks__/user-mock'
describe('App', () => { describe('App', () => {
it('should be able to run tests', () => {
expect(1 + 2).toEqual(3)
})
it('renders without crashing', () => { it('renders without crashing', () => {
const { container } = render(<App />) const { container } = render(<App />)
expect(container.firstChild).toBeInTheDocument() expect(container.firstChild).toBeInTheDocument()
}) })
it('renders loading state', () => {
const { container } = render(
<User.Provider value={{ ...userMock, isLoading: true }}>
<App />
</User.Provider>
)
expect(container.querySelector('.spinner')).toBeInTheDocument()
})
}) })

View File

@ -4,7 +4,6 @@ import Header from './components/organisms/Header'
import Footer from './components/organisms/Footer' import Footer from './components/organisms/Footer'
import Spinner from './components/atoms/Spinner' import Spinner from './components/atoms/Spinner'
import { User } from './context' import { User } from './context'
import UserProvider from './context/UserProvider'
import Routes from './Routes' import Routes from './Routes'
import './styles/global.scss' import './styles/global.scss'
import styles from './App.module.scss' import styles from './App.module.scss'
@ -12,33 +11,27 @@ import styles from './App.module.scss'
export default class App extends Component { export default class App extends Component {
public render() { public render() {
return ( return (
<UserProvider>
<div className={styles.app}> <div className={styles.app}>
<Router> <Router>
<> <>
<Header /> <Header />
<main className={styles.main}> <main className={styles.main}>
<User.Consumer> {this.context.isLoading ? (
{states =>
states.isLoading ? (
<div className={styles.loader}> <div className={styles.loader}>
<Spinner <Spinner message={this.context.message} />
message={states.message}
/>
</div> </div>
) : ( ) : (
<Routes /> <Routes />
) )}
}
</User.Consumer>
</main> </main>
<Footer /> <Footer />
</> </>
</Router> </Router>
</div> </div>
</UserProvider>
) )
} }
} }
App.contextType = User

View File

@ -0,0 +1,15 @@
import React from 'react'
import { BrowserRouter as Router } from 'react-router-dom'
import { render } from 'react-testing-library'
import Routes from './Routes'
describe('Routes', () => {
it('renders without crashing', () => {
const { container } = render(
<Router>
<Routes />
</Router>
)
expect(container.firstChild).toBeInTheDocument()
})
})

View File

@ -0,0 +1,24 @@
@import '../../styles/variables';
.account {
display: flex;
align-items: center;
text-align: left;
> div {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-family: $font-family-monospace;
font-size: $font-size-small;
}
}
.blockies {
width: 1.5rem;
height: 1.5rem;
border-radius: 50%;
display: inline-block;
margin-right: $spacer / 3;
margin-left: 0;
}

View File

@ -0,0 +1,28 @@
import React from 'react'
import { render } from 'react-testing-library'
import { toDataUrl } from 'ethereum-blockies'
import Account from './Account'
describe('Account', () => {
it('renders without crashing', () => {
const { container } = render(<Account account={'0xxxxxxxxxxxxxxx'} />)
expect(container.firstChild).toBeInTheDocument()
})
it('outputs empty state without account', () => {
const { container } = render(<Account account={''} />)
expect(container.firstChild).toHaveTextContent('No account selected')
})
it('outputs blockie img', () => {
const account = '0xxxxxxxxxxxxxxx'
const blockies = toDataUrl(account)
const { container } = render(<Account account={account} />)
expect(container.querySelector('.blockies')).toBeInTheDocument()
expect(container.querySelector('.blockies')).toHaveAttribute(
'src',
blockies
)
})
})

View File

@ -0,0 +1,19 @@
import React from 'react'
import Dotdotdot from 'react-dotdotdot'
import { toDataUrl } from 'ethereum-blockies'
import styles from './Account.module.scss'
const Account = ({ account }: { account: string }) => {
const blockies = account && toDataUrl(account)
return account && blockies ? (
<div className={styles.account}>
<img className={styles.blockies} src={blockies} alt="Blockies" />
<Dotdotdot clamp={1}>{account}</Dotdotdot>
</div>
) : (
<em>No account selected</em>
)
}
export default Account

View File

@ -1,16 +1,13 @@
import React from 'react' import React from 'react'
import { render } from 'react-testing-library' import { render } from 'react-testing-library'
import slugify from 'slugify'
import CategoryImage from './CategoryImage' import CategoryImage from './CategoryImage'
import formPublish from '../../data/form-publish.json' import formPublish from '../../data/form-publish.json'
describe('CategoryImage', () => { describe('CategoryImage', () => {
it('renders fallback image', () => { it('renders fallback image', () => {
const { container, getByTestId } = render( const { container } = render(<CategoryImage category={''} />)
<CategoryImage data-testid="image" category={''} />
)
expect(container.firstChild).toBeInTheDocument() expect(container.firstChild).toBeInTheDocument()
expect(getByTestId('image').style.backgroundImage).toMatch( expect(container.firstChild.style.backgroundImage).toMatch(
/jellyfish-back/ /jellyfish-back/
) )
}) })
@ -21,13 +18,8 @@ describe('CategoryImage', () => {
: [] : []
options.map((category: string) => { options.map((category: string) => {
const { getByTestId } = render( const { container } = render(<CategoryImage category={category} />)
<CategoryImage data-testid="image" category={category} /> expect(container.firstChild).toBeInTheDocument()
)
expect(getByTestId('image')).toBeInTheDocument()
// expect(getByTestId('image').style.backgroundImage).toMatch(
// slugify(category, { lower: true })
// )
}) })
}) })
}) })

View File

@ -148,7 +148,6 @@ export default class CategoryImage extends PureComponent<{ category: string }> {
style={{ style={{
backgroundImage: `url(${image})` backgroundImage: `url(${image})`
}} }}
{...this.props}
/> />
) )
} }

View File

@ -0,0 +1,29 @@
import React from 'react'
import { render } from 'react-testing-library'
import Form from './Form'
describe('Form', () => {
it('renders without crashing', () => {
const { container } = render(<Form>Hello</Form>)
expect(container.firstChild).toBeInTheDocument()
})
it('renders title & description when set', () => {
const { container } = render(
<Form title="Hello Title" description="Hello Description">
Hello
</Form>
)
expect(container.querySelector('.formTitle')).toHaveTextContent(
'Hello Title'
)
expect(container.querySelector('.formDescription')).toHaveTextContent(
'Hello Description'
)
})
it('can switch to minimal', () => {
const { container } = render(<Form minimal>Hello</Form>)
expect(container.firstChild).toHaveClass('formMinimal')
})
})

View File

@ -0,0 +1,90 @@
import React from 'react'
import { render } from 'react-testing-library'
import Input from './Input'
describe('Input', () => {
it('renders default without crashing', () => {
const { container } = render(<Input name="my-input" label="My Input" />)
expect(container.firstChild).toBeInTheDocument()
expect(container.querySelector('.label')).toHaveTextContent('My Input')
expect(container.querySelector('.input')).toHaveAttribute(
'id',
'my-input'
)
})
it('renders as text input by default', () => {
const { container } = render(<Input name="my-input" label="My Input" />)
expect(container.querySelector('.input')).toHaveAttribute(
'type',
'text'
)
})
it('renders search', () => {
const { container } = render(
<Input name="my-input" label="My Input" type="search" />
)
expect(container.querySelector('.input')).toHaveAttribute(
'type',
'search'
)
expect(container.querySelector('label + div')).toHaveClass(
'inputWrapSearch'
)
})
it('renders select', () => {
const { container } = render(
<Input
name="my-input"
label="My Input"
type="select"
options={['hello', 'hello2']}
/>
)
expect(container.querySelector('select')).toBeInTheDocument()
})
it('renders textarea', () => {
const { container } = render(
<Input name="my-input" label="My Input" type="textarea" rows={40} />
)
expect(container.querySelector('textarea')).toBeInTheDocument()
})
it('renders radios', () => {
const { container } = render(
<Input
name="my-input"
label="My Input"
type="radio"
options={['hello', 'hello2']}
/>
)
expect(container.querySelector('input[type=radio]')).toBeInTheDocument()
})
it('renders checkboxes', () => {
const { container } = render(
<Input
name="my-input"
label="My Input"
type="checkbox"
options={['hello', 'hello2']}
/>
)
expect(
container.querySelector('input[type=checkbox]')
).toBeInTheDocument()
})
it('renders date picker', () => {
const { container } = render(
<Input name="my-input" label="My Input" type="date" />
)
expect(
container.querySelector('.react-datepicker-wrapper')
).toBeInTheDocument()
})
})

View File

@ -60,9 +60,8 @@ export default class Input extends PureComponent<InputProps, InputState> {
} }
private handleDateChange = (date: Date) => { private handleDateChange = (date: Date) => {
this.setState({ this.setState({ dateCreated: date })
dateCreated: date
})
const event = { const event = {
currentTarget: { currentTarget: {
name: 'dateCreated', name: 'dateCreated',
@ -80,7 +79,8 @@ export default class Input extends PureComponent<InputProps, InputState> {
name, name,
required, required,
onChange, onChange,
value value,
rows
} = this.props } = this.props
const wrapClass = this.inputWrapClasses() const wrapClass = this.inputWrapClasses()
@ -119,7 +119,7 @@ export default class Input extends PureComponent<InputProps, InputState> {
className={styles.input} className={styles.input}
onFocus={this.toggleFocus} onFocus={this.toggleFocus}
onBlur={this.toggleFocus} onBlur={this.toggleFocus}
{...this.props} rows={rows}
/> />
</div> </div>
) )
@ -174,6 +174,7 @@ export default class Input extends PureComponent<InputProps, InputState> {
<InputGroup> <InputGroup>
<input <input
id={name} id={name}
type={type || 'text'}
className={styles.input} className={styles.input}
onFocus={this.toggleFocus} onFocus={this.toggleFocus}
onBlur={this.toggleFocus} onBlur={this.toggleFocus}
@ -184,6 +185,7 @@ export default class Input extends PureComponent<InputProps, InputState> {
) : ( ) : (
<input <input
id={name} id={name}
type={type || 'text'}
className={styles.input} className={styles.input}
onFocus={this.toggleFocus} onFocus={this.toggleFocus}
onBlur={this.toggleFocus} onBlur={this.toggleFocus}

View File

@ -0,0 +1,10 @@
import React from 'react'
import { render } from 'react-testing-library'
import InputGroup from './InputGroup'
describe('InputGroup', () => {
it('renders without crashing', () => {
const { container } = render(<InputGroup>Hello</InputGroup>)
expect(container.firstChild).toBeInTheDocument()
})
})

View File

@ -0,0 +1,20 @@
import React from 'react'
import { render } from 'react-testing-library'
import Label from './Label'
describe('Label', () => {
it('renders without crashing', () => {
const { container } = render(<Label htmlFor="hello">Hello</Label>)
expect(container.firstChild).toBeInTheDocument()
})
it('renders required state', () => {
const { container } = render(
<Label required htmlFor="hello">
Hello
</Label>
)
expect(container.firstChild).toHaveAttribute('title', 'Required')
expect(container.firstChild).toHaveClass('required')
})
})

View File

@ -0,0 +1,10 @@
import React from 'react'
import { render } from 'react-testing-library'
import Row from './Row'
describe('Row', () => {
it('renders without crashing', () => {
const { container } = render(<Row>Hello</Row>)
expect(container.firstChild).toBeInTheDocument()
})
})

View File

@ -0,0 +1,10 @@
import React from 'react'
import { render } from 'react-testing-library'
import Markdown from './Markdown'
describe('Markdown', () => {
it('renders without crashing', () => {
const { container } = render(<Markdown text={'#hello'} />)
expect(container.firstChild).toBeInTheDocument()
})
})

View File

@ -1,7 +1,7 @@
import React from 'react' import React from 'react'
import ReactMarkdown from 'react-markdown' import ReactMarkdown from 'react-markdown'
const Description = ({ const Markdown = ({
text, text,
className className
}: { }: {
@ -15,4 +15,4 @@ const Description = ({
return <ReactMarkdown source={textCleaned} className={className} /> return <ReactMarkdown source={textCleaned} className={className} />
} }
export default Description export default Markdown

View File

@ -45,14 +45,6 @@ $popoverWidth: 18rem;
} }
} }
.address {
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-family: $font-family-monospace;
}
.balance { .balance {
font-size: $font-size-small; font-size: $font-size-small;
margin-left: $spacer / 2; margin-left: $spacer / 2;

View File

@ -0,0 +1,49 @@
import React from 'react'
import { render } from 'react-testing-library'
import Popover from './Popover'
import { userMock, userMockConnected } from '../../../../__mocks__/user-mock'
import { User } from '../../../context'
describe('Popover', () => {
it('renders without crashing', () => {
const { container } = render(
<User.Provider value={userMock}>
<Popover forwardedRef={() => null} style={{}} />
</User.Provider>
)
expect(container.firstChild).toBeInTheDocument()
})
it('renders connected without crashing', () => {
const { container } = render(
<User.Provider value={userMockConnected}>
<Popover forwardedRef={() => null} style={{}} />
</User.Provider>
)
expect(container.firstChild).toBeInTheDocument()
})
it('renders correct network', () => {
const { container } = render(
<User.Provider value={{ ...userMockConnected, network: 'Nile' }}>
<Popover forwardedRef={() => null} style={{}} />
</User.Provider>
)
expect(container.firstChild).toBeInTheDocument()
expect(container.firstChild).toHaveTextContent('Connected to Nile')
})
it('renders with wrong network', () => {
const { container } = render(
<User.Provider
value={{ ...userMockConnected, isNile: false, network: '1' }}
>
<Popover forwardedRef={() => null} style={{}} />
</User.Provider>
)
expect(container.firstChild).toBeInTheDocument()
expect(container.firstChild).toHaveTextContent(
'Please connect to Custom RPC'
)
})
})

View File

@ -1,5 +1,5 @@
import React, { PureComponent } from 'react' import React, { PureComponent } from 'react'
import Dotdotdot from 'react-dotdotdot' import Account from '../../atoms/Account'
import { User } from '../../../context' import { User } from '../../../context'
import styles from './Popover.module.scss' import styles from './Popover.module.scss'
@ -9,12 +9,24 @@ export default class Popover extends PureComponent<{
}> { }> {
public render() { public render() {
const { account, balance, network, isWeb3, isNile } = this.context const { account, balance, network, isWeb3, isNile } = this.context
return ( return (
<div <div
className={styles.popover} className={styles.popover}
ref={this.props.forwardedRef} ref={this.props.forwardedRef}
style={this.props.style} style={this.props.style}
> >
{!isWeb3 ? (
<div className={styles.popoverInfoline}>
No Web3 detected. Use a browser with MetaMask installed
to publish assets.
</div>
) : (
<>
<div className={styles.popoverInfoline}>
<Account account={account} />
</div>
{account && balance && ( {account && balance && (
<div className={styles.popoverInfoline}> <div className={styles.popoverInfoline}>
<span <span
@ -22,7 +34,9 @@ export default class Popover extends PureComponent<{
title={(balance.eth / 1e18).toFixed(10)} title={(balance.eth / 1e18).toFixed(10)}
> >
<strong> <strong>
{(balance.eth / 1e18).toFixed(3).slice(0, -1)} {(balance.eth / 1e18)
.toFixed(3)
.slice(0, -1)}
</strong>{' '} </strong>{' '}
ETH ETH
</span> </span>
@ -32,24 +46,6 @@ export default class Popover extends PureComponent<{
</div> </div>
)} )}
{!isWeb3 ? (
<div className={styles.popoverInfoline}>
No Web3 detected. Use a browser with MetaMask installed
to publish assets.
</div>
) : (
<>
<div className={styles.popoverInfoline}>
{account ? (
<Dotdotdot clamp={1}>
<span className={styles.address}>
{account}
</span>
</Dotdotdot>
) : (
<em>No account selected</em>
)}
</div>
<div className={styles.popoverInfoline}> <div className={styles.popoverInfoline}>
{network && !isNile {network && !isNile
? 'Please connect to Custom RPC\n https://nile.dev-ocean.com' ? 'Please connect to Custom RPC\n https://nile.dev-ocean.com'

View File

@ -0,0 +1,20 @@
import React from 'react'
import { render, fireEvent } from 'react-testing-library'
import AccountStatus from '.'
describe('AccountStatus', () => {
it('renders without crashing', () => {
const { container } = render(<AccountStatus />)
expect(container.firstChild).toBeInTheDocument()
})
it('togglePopover fires', () => {
const { container } = render(<AccountStatus />)
const indicator = container.querySelector('.statusIndicator')
indicator && fireEvent.mouseOver(indicator)
expect(container.querySelector('.popover')).toBeInTheDocument()
indicator && fireEvent.mouseOut(indicator)
})
})

View File

@ -19,7 +19,7 @@ export default class AccountStatus extends PureComponent<
isPopoverOpen: false isPopoverOpen: false
} }
public togglePopover() { private togglePopover() {
this.setState(prevState => ({ this.setState(prevState => ({
isPopoverOpen: !prevState.isPopoverOpen isPopoverOpen: !prevState.isPopoverOpen
})) }))

View File

@ -2,7 +2,7 @@ import React from 'react'
import { render } from 'react-testing-library' import { render } from 'react-testing-library'
import Pagination from './Pagination' import Pagination from './Pagination'
describe('Button', () => { describe('Pagination', () => {
it('renders without crashing', () => { it('renders without crashing', () => {
const { container } = render( const { container } = render(
<Pagination <Pagination

View File

@ -8,10 +8,7 @@ import styles from './Header.module.scss'
import menu from '../../data/menu.json' import menu from '../../data/menu.json'
import meta from '../../data/meta.json' import meta from '../../data/meta.json'
const MenuItem = ({ item, isWeb3 }: { item: any; isWeb3: boolean }) => { const MenuItem = ({ item }: { item: any }) => (
if (item.web3 && !isWeb3) return null
return (
<NavLink <NavLink
to={item.link} to={item.link}
className={styles.link} className={styles.link}
@ -21,12 +18,9 @@ const MenuItem = ({ item, isWeb3 }: { item: any; isWeb3: boolean }) => {
{item.title} {item.title}
</NavLink> </NavLink>
) )
}
export default class Header extends PureComponent { export default class Header extends PureComponent {
public render() { public render() {
const { isWeb3 } = this.context
return ( return (
<header className={styles.header}> <header className={styles.header}>
<div className={styles.headerContent}> <div className={styles.headerContent}>
@ -37,11 +31,7 @@ export default class Header extends PureComponent {
<nav className={styles.headerMenu}> <nav className={styles.headerMenu}>
{menu.map(item => ( {menu.map(item => (
<MenuItem <MenuItem key={item.title} item={item} />
key={item.title}
item={item}
isWeb3={isWeb3}
/>
))} ))}
<AccountStatus className={styles.accountStatus} /> <AccountStatus className={styles.accountStatus} />
</nav> </nav>

View File

@ -3,27 +3,20 @@
.message { .message {
margin-bottom: $spacer; margin-bottom: $spacer;
color: $brand-grey; color: $brand-grey;
padding-left: 2rem;
position: relative; position: relative;
border-bottom: .1rem solid $brand-grey-lighter; border-bottom: .1rem solid $brand-grey-lighter;
border-top: .1rem solid $brand-grey-lighter; border-top: .1rem solid $brand-grey-lighter;
padding-top: $spacer / 2; padding-top: $spacer / 2;
padding-bottom: $spacer / 2; padding-bottom: $spacer / 2;
text-align: left; text-align: left;
> div {
display: inline-block;
}
} }
.account { .warnings {
display: inline-block; padding-left: $spacer;
margin-left: $spacer / 8;
background: none;
} }
.status { .status {
margin-left: -($spacer); margin-left: -($spacer);
margin-right: $spacer / 3; margin-right: $spacer / 2;
padding: 0; padding: 0;
} }

View File

@ -1,11 +1,62 @@
import React from 'react' import React from 'react'
import { render } from 'react-testing-library' import { render, fireEvent } from 'react-testing-library'
import Web3message from './Web3message' import Web3message from './Web3message'
import { User } from '../../context'
import { userMock, userMockConnected } from '../../../__mocks__/user-mock'
describe('Web3message', () => { describe('Web3message', () => {
it('default renders without crashing', () => { it('renders with noWeb3 message', () => {
const { container } = render(<Web3message />) const { container } = render(
<User.Provider value={{ ...userMock }}>
<Web3message />
</User.Provider>
)
expect(container.firstChild).toHaveTextContent('Not a Web3 Browser')
})
expect(container.firstChild).toBeInTheDocument() it('renders with wrongNetwork message', () => {
const { container } = render(
<User.Provider value={{ ...userMock, isWeb3: true }}>
<Web3message />
</User.Provider>
)
expect(container.firstChild).toHaveTextContent(
'Not connected to Nile network'
)
})
it('renders with noAccount message', () => {
const { container } = render(
<User.Provider value={{ ...userMock, isWeb3: true, isNile: true }}>
<Web3message />
</User.Provider>
)
expect(container.firstChild).toHaveTextContent('No accounts detected')
})
it('renders with hasAccount message', () => {
const { container } = render(
<User.Provider value={userMockConnected}>
<Web3message />
</User.Provider>
)
expect(container.firstChild).toHaveTextContent('0xxxxxx')
})
it('button click fires unlockAccounts', () => {
const { getByText } = render(
<User.Provider
value={{
...userMock,
isWeb3: true,
isNile: true
}}
>
<Web3message />
</User.Provider>
)
fireEvent.click(getByText('Unlock Account'))
expect(userMock.unlockAccounts).toBeCalled()
}) })
}) })

View File

@ -1,5 +1,5 @@
import React, { PureComponent } from 'react' import React, { PureComponent } from 'react'
import Dotdotdot from 'react-dotdotdot' import Account from '../atoms/Account'
import Button from '../atoms/Button' import Button from '../atoms/Button'
import AccountStatus from '../molecules/AccountStatus' import AccountStatus from '../molecules/AccountStatus'
import styles from './Web3message.module.scss' import styles from './Web3message.module.scss'
@ -13,21 +13,18 @@ export default class Web3message extends PureComponent {
unlockAccounts?: () => any unlockAccounts?: () => any
) => ( ) => (
<div className={styles.message}> <div className={styles.message}>
<AccountStatus className={styles.status} />{' '}
{account ? ( {account ? (
<Dotdotdot clamp={1}> <Account account={account} />
{message}
<code className={styles.account}>{account}</code>
</Dotdotdot>
) : ( ) : (
<> <div className={styles.warnings}>
<AccountStatus className={styles.status} />
<span dangerouslySetInnerHTML={{ __html: message }} />{' '} <span dangerouslySetInnerHTML={{ __html: message }} />{' '}
{unlockAccounts && ( {unlockAccounts && (
<Button onClick={() => unlockAccounts()} link> <Button onClick={() => unlockAccounts()} link>
Unlock Account Unlock Account
</Button> </Button>
)} )}
</> </div>
)} )}
</div> </div>
) )

View File

@ -18,12 +18,3 @@
margin-top: $spacer / 2; margin-top: $spacer / 2;
font-size: $font-size-large; font-size: $font-size-large;
} }
.titleReverse {
composes: title;
color: $brand-grey-light;
span {
color: $brand-grey-dark;
}
}

View File

@ -0,0 +1,24 @@
import React from 'react'
import { render } from 'react-testing-library'
import Route from './Route'
describe('Route', () => {
it('renders without crashing', () => {
const { container } = render(<Route title="Hello Title">Hello</Route>)
expect(container.firstChild).toBeInTheDocument()
})
it('renders title & description', () => {
const { container } = render(
<Route title="Hello Title" description="Hello Description">
Hello
</Route>
)
expect(container.querySelector('.title')).toHaveTextContent(
'Hello Title'
)
expect(container.querySelector('.description')).toHaveTextContent(
'Hello Description'
)
})
})

View File

@ -7,14 +7,12 @@ import meta from '../../data/meta.json'
const Route = ({ const Route = ({
title, title,
description, description,
titleReverse,
wide, wide,
children, children,
className className
}: { }: {
title: string title: string
description?: string description?: string
titleReverse?: boolean
children: any children: any
wide?: boolean wide?: boolean
className?: string className?: string
@ -29,13 +27,7 @@ const Route = ({
<Content wide={wide}> <Content wide={wide}>
<article> <article>
<header className={styles.header}> <header className={styles.header}>
<h1 <h1 className={styles.title}>{title}</h1>
className={
titleReverse ? styles.titleReverse : styles.title
}
>
{title}
</h1>
{description && ( {description && (
<p <p
className={styles.description} className={styles.description}

View File

@ -1,6 +1,6 @@
import React, { PureComponent } from 'react' import React, { PureComponent } from 'react'
import Web3 from 'web3' import Web3 from 'web3'
import { Logger } from '@oceanprotocol/squid' import { Logger, Ocean, Account } from '@oceanprotocol/squid'
import { User } from '.' import { User } from '.'
import { provideOcean, requestFromFaucet, FaucetResponse } from '../ocean' import { provideOcean, requestFromFaucet, FaucetResponse } from '../ocean'
import { nodeHost, nodePort, nodeScheme } from '../config' import { nodeHost, nodePort, nodeScheme } from '../config'
@ -54,7 +54,7 @@ interface UserProviderState {
} }
network: string network: string
web3: Web3 web3: Web3
ocean: any ocean: Ocean
requestFromFaucet(account: string): Promise<FaucetResponse> requestFromFaucet(account: string): Promise<FaucetResponse>
unlockAccounts(): Promise<any> unlockAccounts(): Promise<any>
message: string message: string
@ -117,7 +117,7 @@ export default class UserProvider extends PureComponent<{}, UserProviderState> {
} }
} }
private getWeb3 = async () => { private getWeb3 = () => {
// Modern dapp browsers // Modern dapp browsers
if (window.ethereum) { if (window.ethereum) {
window.web3 = new Web3(window.ethereum) window.web3 = new Web3(window.ethereum)
@ -236,7 +236,7 @@ export default class UserProvider extends PureComponent<{}, UserProviderState> {
} }
} }
private fetchBalance = async (account: any) => { private fetchBalance = async (account: Account) => {
const balance = await account.getBalance() const balance = await account.getBalance()
const { eth, ocn } = balance const { eth, ocn } = balance
if (eth !== this.state.balance.eth || ocn !== this.state.balance.ocn) { if (eth !== this.state.balance.eth || ocn !== this.state.balance.ocn) {

View File

@ -25,7 +25,7 @@
}, },
{ {
"title": "Telegram", "title": "Telegram",
"url": "https://t.me/joinchat/GUyxrE0Hi154D0NrlOqLFg" "url": "https://t.me/OceanProtocolCommunity"
} }
] ]
} }

View File

@ -1,6 +1,6 @@
{ {
"noweb3": "Not a Web3 Browser. For publishing and downloading an asset you need to <a href='https://docs.oceanprotocol.com/tutorials/metamask-setup/' target='_blank' rel='noopener noreferrer'>setup MetaMask</a> or use any other Web3-capable plugin or browser.", "noweb3": "Not a Web3 Browser. For publishing and downloading an asset you need to <a href='https://docs.oceanprotocol.com/tutorials/metamask-setup/' target='_blank' rel='noopener noreferrer'>setup MetaMask</a> or use any other Web3-capable plugin or browser.",
"noAccount": "No accounts detected. For publishing and downloading an asset you need to unlock your Web3 account.", "noAccount": "No accounts detected. For publishing and downloading an asset you need to unlock your Web3 account.",
"hasAccount": "Connected with account ", "hasAccount": "",
"wrongNetwork": "Not connected to Nile network.<br />Please connect in MetaMask with Custom RPC <code>https://nile.dev-ocean.com</code>" "wrongNetwork": "Not connected to Nile network.<br />Please connect in MetaMask with Custom RPC <code>https://nile.dev-ocean.com</code>"
} }

View File

@ -8,7 +8,7 @@ const withTracker = <P extends RouteComponentProps>(
options: FieldsObject = {} options: FieldsObject = {}
) => { ) => {
ReactGA.initialize(analyticsId, { ReactGA.initialize(analyticsId, {
testMode: process.env.NODE_ENV === 'development', testMode: process.env.NODE_ENV === 'test',
debug: false debug: false
}) })

22
client/src/index.test.tsx Normal file
View File

@ -0,0 +1,22 @@
import ReactDOM from 'react-dom'
import { renderToDOM } from './index'
describe('test ReactDOM.render', () => {
const originalRender = ReactDOM.render
const originalGetElement = global.document.getElementById
beforeEach(() => {
global.document.getElementById = () => true
ReactDOM.render = jest.fn()
})
afterAll(() => {
global.document.getElementById = originalGetElement
ReactDOM.render = originalRender
})
it('should call ReactDOM.render', () => {
renderToDOM()
expect(ReactDOM.render).toHaveBeenCalled()
})
})

View File

@ -1,9 +1,25 @@
import React from 'react' import React from 'react'
import ReactDOM from 'react-dom' import ReactDOM from 'react-dom'
import UserProvider from './context/UserProvider'
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 root = document.getElementById('root')
if (root !== null) {
ReactDOM.render(
<UserProvider>
<App />
</UserProvider>,
root
)
}
}
export { renderToDOM }
renderToDOM()
// If you want your app to work offline and load faster, you can change // If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls. // unregister() to register() below. Note this comes with some pitfalls.

17
client/src/ocean.test.ts Normal file
View File

@ -0,0 +1,17 @@
import Web3 from 'web3'
import { provideOcean, requestFromFaucet } from './ocean'
describe('ocean', () => {
const web3 = new Web3(Web3.givenProvider)
it('provideOcean can be called', async () => {
const response = await provideOcean(web3)
expect(response.ocean).toBeTruthy()
})
it('requestFromFaucet can be called', async () => {
const response = await requestFromFaucet('0xxxxxx')
response &&
expect(response.errors[0].msg).toBe('Invalid Ethereum address')
})
})

View File

@ -72,6 +72,6 @@ export async function requestFromFaucet(account: string) {
}) })
return response.json() return response.json()
} catch (error) { } catch (error) {
Logger.log('requestFromFaucet', error) Logger.error('requestFromFaucet', error)
} }
} }

View File

@ -0,0 +1,10 @@
import React from 'react'
import { render } from 'react-testing-library'
import About from './About'
describe('About', () => {
it('renders without crashing', () => {
const { container } = render(<About />)
expect(container.firstChild).toBeInTheDocument()
})
})

View File

@ -0,0 +1,68 @@
import React from 'react'
import { render } from 'react-testing-library'
import { DDO, MetaData } from '@oceanprotocol/squid'
import { BrowserRouter as Router } from 'react-router-dom'
import AssetDetails, { datafilesLine } from './AssetDetails'
/* eslint-disable @typescript-eslint/no-explicit-any */
describe('AssetDetails', () => {
it('renders loading without crashing', () => {
const { container } = render(
<AssetDetails
metadata={({ base: { name: '' } } as any) as MetaData}
ddo={({} as any) as DDO}
/>
)
expect(container.firstChild).toBeInTheDocument()
})
it('renders with data', () => {
const { container } = render(
<Router>
<AssetDetails
metadata={
({
base: {
name: 'Hello',
description: 'Description',
categories: ['Category'],
files: [{ index: 0 }]
}
} as any) as MetaData
}
ddo={({} as any) as DDO}
/>
</Router>
)
expect(container.querySelector('.description')).toHaveTextContent(
'Description'
)
expect(container.firstChild).toHaveTextContent('Category')
})
it('datafilesLine renders correctly for one file', () => {
const files = [
{
index: 0,
url: 'https://hello.com'
}
]
const { container } = render(datafilesLine(files))
expect(container.firstChild).toHaveTextContent('1 data file')
})
it('datafilesLine renders correctly for multiple files', () => {
const files = [
{
index: 0,
url: 'https://hello.com'
},
{
index: 1,
url: 'https://hello2.com'
}
]
const { container } = render(datafilesLine(files))
expect(container.firstChild).toHaveTextContent('2 data files')
})
})

View File

@ -1,23 +1,24 @@
import React, { PureComponent } from 'react' import React, { PureComponent } from 'react'
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
import Moment from 'react-moment' import Moment from 'react-moment'
import { DDO, MetaData, File } from '@oceanprotocol/squid'
import Markdown from '../../components/atoms/Markdown' import Markdown from '../../components/atoms/Markdown'
import styles from './AssetDetails.module.scss' import styles from './AssetDetails.module.scss'
import AssetFilesDetails from './AssetFilesDetails' import AssetFilesDetails from './AssetFilesDetails'
interface AssetDetailsProps { interface AssetDetailsProps {
metadata: any metadata: MetaData
ddo: any ddo: DDO
} }
export default class AssetDetails extends PureComponent<AssetDetailsProps> { export function datafilesLine(files: File[]) {
private datafilesLine = (files: any) => {
if (files.length === 1) { if (files.length === 1) {
return <span>{files.length} data file</span> return <span>{files.length} data file</span>
} }
return <span>{files.length} data files</span> return <span>{files.length} data files</span>
} }
export default class AssetDetails extends PureComponent<AssetDetailsProps> {
public render() { public render() {
const { metadata, ddo } = this.props const { metadata, ddo } = this.props
const { base } = metadata const { base } = metadata
@ -51,14 +52,16 @@ export default class AssetDetails extends PureComponent<AssetDetailsProps> {
</Link> </Link>
)} )}
{base.files && this.datafilesLine(base.files)} {base.files && datafilesLine(base.files)}
</div> </div>
</aside> </aside>
{base.description && (
<Markdown <Markdown
text={base.description} text={base.description}
className={styles.description} className={styles.description}
/> />
)}
<ul className={styles.meta}> <ul className={styles.meta}>
<li> <li>

View File

@ -0,0 +1,77 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import React from 'react'
import { render, fireEvent } from 'react-testing-library'
import { DDO } from '@oceanprotocol/squid'
import { StateMock } from '@react-mock/state'
import { User } from '../../context'
import AssetFile from './AssetFile'
const file = {
index: 0,
url: 'https://hello.com',
contentType: 'zip',
contentLength: 100
}
const ddo = ({ id: 'xxx' } as any) as DDO
const contextConnectedMock = {
isLogged: true,
isLoading: false,
isWeb3: true,
isNile: true,
account: '',
web3: {},
ocean: {},
balance: { eth: 0, ocn: 0 },
network: '',
requestFromFaucet: () => {},
unlockAccounts: () => {},
message: ''
}
describe('AssetFile', () => {
it('renders without crashing', () => {
const { container } = render(<AssetFile file={file} ddo={ddo} />)
expect(container.firstChild).toBeInTheDocument()
})
it('button to be disabled when not connected', () => {
const { container } = render(<AssetFile file={file} ddo={ddo} />)
expect(container.querySelector('button')).toHaveAttribute('disabled')
})
it('button to be enabled when connected', async () => {
const { getByText } = render(
<User.Provider value={contextConnectedMock}>
<AssetFile file={file} ddo={ddo} />
</User.Provider>
)
const button = getByText('Get file')
expect(button).not.toHaveAttribute('disabled')
fireEvent.click(button)
})
it('renders loading state', async () => {
const { container } = render(
<StateMock state={{ isLoading: true }}>
<AssetFile file={file} ddo={ddo} />
</StateMock>
)
expect(container.querySelector('.spinner')).toBeInTheDocument()
})
it('renders error', async () => {
const { container } = render(
<StateMock state={{ error: 'Hello Error' }}>
<AssetFile file={file} ddo={ddo} />
</StateMock>
)
expect(container.querySelector('.error')).toBeInTheDocument()
expect(container.querySelector('.error')).toHaveTextContent(
'Hello Error'
)
})
})

View File

@ -1,5 +1,5 @@
import React, { PureComponent } from 'react' import React, { PureComponent } from 'react'
import { Logger } from '@oceanprotocol/squid' import { Logger, DDO, File } from '@oceanprotocol/squid'
import filesize from 'filesize' import filesize from 'filesize'
import Button from '../../components/atoms/Button' import Button from '../../components/atoms/Button'
import Spinner from '../../components/atoms/Spinner' import Spinner from '../../components/atoms/Spinner'
@ -8,8 +8,8 @@ import styles from './AssetFile.module.scss'
import ReactGA from 'react-ga' import ReactGA from 'react-ga'
interface AssetFileProps { interface AssetFileProps {
file: any file: File
ddo: any ddo: DDO
} }
interface AssetFileState { interface AssetFileState {
@ -30,7 +30,7 @@ export default class AssetFile extends PureComponent<
private resetState = () => this.setState({ isLoading: true, error: '' }) private resetState = () => this.setState({ isLoading: true, error: '' })
private purchaseAsset = async (ddo: any, index: number) => { private purchaseAsset = async (ddo: DDO, index: number) => {
this.resetState() this.resetState()
ReactGA.event({ ReactGA.event({
@ -77,10 +77,11 @@ export default class AssetFile extends PureComponent<
const { ddo, file } = this.props const { ddo, file } = this.props
const { isLoading, message, error } = this.state const { isLoading, message, error } = this.state
const { isLogged, isNile } = this.context const { isLogged, isNile } = this.context
const { index } = file
return ( return (
<div className={styles.fileWrap}> <div className={styles.fileWrap}>
<ul key={file.index} className={styles.file}> <ul key={index} className={styles.file}>
<li> <li>
{file.contentType && file.contentType.split('/')[1]} {file.contentType && file.contentType.split('/')[1]}
</li> </li>
@ -97,7 +98,10 @@ export default class AssetFile extends PureComponent<
<Button <Button
primary primary
className={styles.buttonMain} className={styles.buttonMain}
onClick={() => this.purchaseAsset(ddo, file.index)} // TODO: remove the || 0 once hack
// https://github.com/oceanprotocol/squid-js/pull/221
// is released
onClick={() => this.purchaseAsset(ddo, index || 0)}
disabled={!isLogged || !isNile} disabled={!isLogged || !isNile}
> >
Get file Get file

View File

@ -0,0 +1,43 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import React from 'react'
import { render } from 'react-testing-library'
import { DDO } from '@oceanprotocol/squid'
import AssetFilesDetails from './AssetFilesDetails'
import { User } from '../../context'
import { userMockConnected } from '../../../__mocks__/user-mock'
describe('AssetFilesDetails', () => {
it('renders without crashing', () => {
const files = [
{
index: 0,
url: 'https://hello.com'
}
]
const { container } = render(
<AssetFilesDetails files={files} ddo={({} as any) as DDO} />
)
expect(container.firstChild).toBeInTheDocument()
})
it('renders nothing when no files', () => {
const { container } = render(
<AssetFilesDetails files={[]} ddo={({} as any) as DDO} />
)
expect(container.firstChild).toHaveTextContent('No files attached.')
})
it('hides Web3message when all connected', () => {
const { container } = render(
<User.Provider value={userMockConnected}>
<AssetFilesDetails
files={[{ index: 0, url: '' }]}
ddo={({} as any) as DDO}
/>
</User.Provider>
)
expect(container.querySelector('.status')).not.toBeInTheDocument()
})
})

View File

@ -1,31 +1,32 @@
import React, { PureComponent } from 'react' import React, { PureComponent } from 'react'
import { DDO, File } from '@oceanprotocol/squid'
import AssetFile from './AssetFile' import AssetFile from './AssetFile'
import { User } from '../../context' import { User } from '../../context'
import Web3message from '../../components/organisms/Web3message' import Web3message from '../../components/organisms/Web3message'
import styles from './AssetFilesDetails.module.scss' import styles from './AssetFilesDetails.module.scss'
export default class AssetFilesDetails extends PureComponent<{ export default class AssetFilesDetails extends PureComponent<{
files: any[] files: File[]
ddo: any ddo: DDO
}> { }> {
public render() { public render() {
const { files, ddo } = this.props const { files, ddo } = this.props
return files ? ( return files.length ? (
<> <>
<div className={styles.files}> <div className={styles.files}>
{files.map(file => ( {files.map(file => (
<AssetFile key={file.index} ddo={ddo} file={file} /> <AssetFile key={file.index} ddo={ddo} file={file} />
))} ))}
</div> </div>
<User.Consumer> {(!this.context.isNile || !this.context.isLogged) && (
{states => <Web3message />
(!states.isNile || !states.isLogged) && <Web3message /> )}
}
</User.Consumer>
</> </>
) : ( ) : (
<div>No files attached.</div> <div>No files attached.</div>
) )
} }
} }
AssetFilesDetails.contextType = User

View File

@ -0,0 +1,21 @@
import React from 'react'
import { render } from 'react-testing-library'
import Details from './index'
describe('Details', () => {
it('renders loading state by default', () => {
const { container } = render(
<Details
location={{
search: '',
pathname: '/',
state: '',
hash: ''
}}
match={{ params: { did: '' } }}
/>
)
expect(container.firstChild).toBeInTheDocument()
expect(container.querySelector('.loader')).toBeInTheDocument()
})
})

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import { DDO, MetaData, Logger } from '@oceanprotocol/squid'
import Route from '../../components/templates/Route' import Route from '../../components/templates/Route'
import Spinner from '../../components/atoms/Spinner' import Spinner from '../../components/atoms/Spinner'
import { User } from '../../context' import { User } from '../../context'
@ -7,23 +8,37 @@ import stylesApp from '../../App.module.scss'
interface DetailsProps { interface DetailsProps {
location: Location location: Location
match: any match: {
params: {
did: string
}
}
} }
interface DetailsState { interface DetailsState {
ddo: any ddo: DDO
metadata: { base: { name: string } } metadata: MetaData
} }
export default class Details extends Component<DetailsProps, DetailsState> { export default class Details extends Component<DetailsProps, DetailsState> {
public state = { ddo: {}, metadata: { base: { name: '' } } } public state = {
ddo: ({} as any) as DDO,
metadata: ({ base: { name: '' } } as any) as MetaData
}
public async componentDidMount() { public async componentDidMount() {
const ddo = await this.context.ocean.assets.resolve( this.getData()
this.props.match.params.did }
)
private async getData() {
try {
const { ocean } = this.context
const ddo = await ocean.assets.resolve(this.props.match.params.did)
const { metadata } = ddo.findServiceByType('Metadata') const { metadata } = ddo.findServiceByType('Metadata')
this.setState({ ddo, metadata: { base: metadata.base } }) this.setState({ ddo, metadata })
} catch (error) {
Logger.error(error.message)
}
} }
public render() { public render() {

View File

@ -0,0 +1,43 @@
import React from 'react'
import { render, fireEvent } from 'react-testing-library'
import Faucet from './Faucet'
import { User } from '../context'
import { userMockConnected } from '../../__mocks__/user-mock'
const setup = () => {
const utils = render(
<User.Provider value={userMockConnected}>
<Faucet />
</User.Provider>
)
const button = utils.getByText('Request Ether')
const { container } = utils
return {
button,
container,
...utils
}
}
describe('Faucet', () => {
it('renders without crashing', () => {
const { container } = render(<Faucet />)
expect(container.firstChild).toBeInTheDocument()
})
it('shows actions when connected', () => {
const { button } = setup()
expect(button).toBeInTheDocument()
expect(button).not.toHaveAttribute('disabled')
})
it('fires requestFromFaucet', () => {
const { button, getByText } = setup()
fireEvent.click(button)
expect(userMockConnected.requestFromFaucet).toHaveBeenCalledTimes(1)
// check for spinner
expect(getByText('Getting Ether...')).toBeInTheDocument()
})
})

View File

@ -46,7 +46,7 @@ export default class Faucet extends PureComponent<{}, FaucetState> {
trxHash trxHash
}) })
} catch (error) { } catch (error) {
this.setState({ isLoading: false, error }) this.setState({ isLoading: false, error: error.message })
} }
} }

View File

@ -0,0 +1,38 @@
import React from 'react'
import { render } from 'react-testing-library'
import { User } from '../context'
import History from './History'
describe('History', () => {
it('renders without crashing', () => {
const { container } = render(<History />)
expect(container.firstChild).toBeInTheDocument()
})
it('outputs Web3 message when no Web3 detected', () => {
const context = {
isLogged: false,
isLoading: false,
isWeb3: false,
isNile: false,
account: '',
web3: {},
ocean: {},
balance: { eth: 0, ocn: 0 },
network: '',
requestFromFaucet: () => {},
unlockAccounts: () => {},
message: ''
}
const { container } = render(
<User.Provider value={context}>
<History />
</User.Provider>
)
expect(container.querySelector('.message')).toBeInTheDocument()
expect(container.querySelector('.message')).toHaveTextContent(
'Not a Web3 Browser.'
)
})
})

View File

@ -0,0 +1,10 @@
import React from 'react'
import { render } from 'react-testing-library'
import Home from './Home'
describe('Home', () => {
it('renders without crashing', () => {
const { container } = render(<Home history={''} />)
expect(container.firstChild).toBeInTheDocument()
})
})

View File

@ -7,9 +7,10 @@ import AssetsUser from '../components/organisms/AssetsUser'
import styles from './Home.module.scss' import styles from './Home.module.scss'
import meta from '../data/meta.json' import meta from '../data/meta.json'
import { History } from 'history'
interface HomeProps { interface HomeProps {
history: any history: History
} }
interface HomeState { interface HomeState {

View File

@ -0,0 +1,10 @@
import React from 'react'
import { render } from 'react-testing-library'
import NotFound from './NotFound'
describe('NotFound', () => {
it('renders without crashing', () => {
const { container } = render(<NotFound />)
expect(container.firstChild).toBeInTheDocument()
})
})

View File

@ -0,0 +1,39 @@
import React from 'react'
import { render } from 'react-testing-library'
import Item from './Item'
describe('Item', () => {
it('renders without crashing', () => {
const item = {
url: 'https://hello.com/hello.zip',
found: true,
contentType: 'application/zip',
contentLength: 10
}
const { container } = render(
<Item item={item} removeItem={() => null} />
)
expect(container.firstChild).toBeInTheDocument()
})
it('returns unknown strings', () => {
const item = {
url: 'https://hello.com/hello.zip',
found: false,
contentType: '',
contentLength: 10
}
const { container } = render(
<Item item={item} removeItem={() => null} />
)
expect(container.querySelector('.details')).toHaveTextContent(
'unknown type'
)
expect(container.querySelector('.details')).toHaveTextContent(
'unknown size'
)
expect(container.querySelector('.details')).toHaveTextContent(
'not confirmed'
)
})
})

View File

@ -0,0 +1,63 @@
import React from 'react'
import { render, fireEvent } from 'react-testing-library'
import ItemForm from './ItemForm'
const addItem = jest.fn()
const setup = () => {
const utils = render(<ItemForm placeholder={'Hello'} addItem={addItem} />)
const input = utils.getByPlaceholderText('Hello')
const button = utils.getByText('Add File')
const { container } = utils
return {
input,
button,
container,
...utils
}
}
describe('ItemForm', () => {
it('renders without crashing', () => {
const { container } = setup()
expect(container.firstChild).toBeInTheDocument()
})
it('fires addItem', async () => {
const { input, button } = setup()
fireEvent.change(input, {
target: { value: 'https://hello.com' }
})
fireEvent.click(button)
expect(addItem).toHaveBeenCalled()
})
it('does not fire addItem when no url present', () => {
const { input, button, container } = setup()
// empty url
fireEvent.change(input, {
target: { value: '' }
})
fireEvent.click(button)
expect(container.querySelector('.error')).toHaveTextContent(
'Please fill in all required fields.'
)
// invalid url
fireEvent.change(input, {
target: { value: 'blabla' }
})
fireEvent.click(button)
expect(container.querySelector('.error')).toHaveTextContent(
'Please enter a valid URL.'
)
// clear out errors
fireEvent.change(input, {
target: { value: 'blablabla' }
})
expect(container.querySelector('.error')).not.toBeInTheDocument()
})
})

View File

@ -25,7 +25,7 @@ export default class ItemForm extends PureComponent<
noUrl: false noUrl: false
} }
public handleSubmit = (e: Event) => { private handleSubmit = (e: Event) => {
e.preventDefault() e.preventDefault()
const { url } = this.state const { url } = this.state
@ -45,14 +45,14 @@ export default class ItemForm extends PureComponent<
this.props.addItem(url) this.props.addItem(url)
} }
public onChangeUrl = (e: React.FormEvent<HTMLInputElement>) => { private onChangeUrl = (e: React.FormEvent<HTMLInputElement>) => {
this.setState({ url: e.currentTarget.value }) this.setState({ url: e.currentTarget.value })
this.clearErrors() this.clearErrors()
} }
public clearErrors() { private clearErrors() {
if (this.state.hasError) this.setState({ hasError: false }) if (this.state.hasError) this.setState({ hasError: false })
if (this.state.noUrl) this.setState({ noUrl: true }) if (this.state.noUrl) this.setState({ noUrl: false })
} }
public render() { public render() {

View File

@ -0,0 +1,90 @@
import React from 'react'
import { render, fireEvent, waitForElement } from 'react-testing-library'
import Files, { getFileCompression } from '.'
const onChange = jest.fn()
const files = [
{
found: true,
url: 'https://hello.com',
checksum: 'cccccc',
checksumType: 'MD5',
contentLength: 100,
contentType: 'application/zip',
resourceId: 'xxx',
encoding: 'UTF-8',
compression: 'zip'
}
]
const setup = () => {
const utils = render(
<Files
files={files}
placeholder={'Hello'}
name={'Hello'}
onChange={onChange}
/>
)
const { container } = utils
return { container, ...utils }
}
describe('Files', () => {
it('renders without crashing', () => {
const { container } = setup()
expect(container.firstChild).toBeInTheDocument()
expect(container.querySelector('.itemForm')).not.toBeInTheDocument()
})
it('new file form can be opened and closed', async () => {
const { container, getByText } = setup()
// open
fireEvent.click(getByText('+ Add a file'))
await waitForElement(() => getByText('- Cancel'))
expect(container.querySelector('.itemForm')).toBeInTheDocument()
// close
fireEvent.click(getByText('- Cancel'))
await waitForElement(() => getByText('+ Add a file'))
expect(container.querySelector('.grow-exit')).toBeInTheDocument()
})
it('item can be removed', async () => {
const { getByTitle } = setup()
fireEvent.click(getByTitle('Remove item'))
expect(files.length).toBe(0)
})
it('item can be added', async () => {
const { getByText, getByPlaceholderText } = setup()
fireEvent.click(getByText('+ Add a file'))
await waitForElement(() => getByText('- Cancel'))
fireEvent.change(getByPlaceholderText('Hello'), {
target: { value: 'https://hello.com' }
})
fireEvent.click(getByText('Add File'))
})
})
describe('getFileCompression', () => {
it('outputs known compression', async () => {
const compression = await getFileCompression('application/zip')
expect(compression).toBe('zip')
})
it('outputs known x- compression', async () => {
const compression = await getFileCompression('application/x-gtar')
expect(compression).toBe('gtar')
})
it('outputs unknown compression', async () => {
const compression = await getFileCompression('blabla')
expect(compression).toBe('none')
})
})

View File

@ -38,7 +38,7 @@ interface FilesStates {
isFormShown: boolean isFormShown: boolean
} }
const getFileCompression = async (contentType: string) => { export const getFileCompression = async (contentType: string) => {
// TODO: add all the possible archive & compression MIME types // TODO: add all the possible archive & compression MIME types
if ( if (
contentType === 'application/zip' || contentType === 'application/zip' ||
@ -69,14 +69,21 @@ export default class Files extends PureComponent<FilesProps, FilesStates> {
isFormShown: false isFormShown: false
} }
public toggleForm = (e: Event) => { private toggleForm = (e: Event) => {
e.preventDefault() e.preventDefault()
this.setState({ isFormShown: !this.state.isFormShown }) this.setState({ isFormShown: !this.state.isFormShown })
} }
public addItem = async (value: string) => { private addItem = async (value: string) => {
let res: any let res: {
result: {
contentLength: number
contentType: string
found: boolean
}
}
let file: File = { let file: File = {
url: value, url: value,
found: false, found: false,
@ -104,6 +111,7 @@ export default class Files extends PureComponent<FilesProps, FilesStates> {
} catch (error) { } catch (error) {
// error // error
} }
this.props.files.push(file) this.props.files.push(file)
const event = { const event = {
currentTarget: { currentTarget: {
@ -115,7 +123,7 @@ export default class Files extends PureComponent<FilesProps, FilesStates> {
this.setState({ isFormShown: !this.state.isFormShown }) this.setState({ isFormShown: !this.state.isFormShown })
} }
public removeItem = (index: number) => { private removeItem = (index: number) => {
this.props.files.splice(index, 1) this.props.files.splice(index, 1)
const event = { const event = {
currentTarget: { currentTarget: {

View File

@ -0,0 +1,19 @@
import React from 'react'
import { render } from 'react-testing-library'
import Progress from './Progress'
describe('Progress', () => {
it('renders without crashing', () => {
const { container } = render(
<Progress currentStep={1} steps={[{ title: '' }]} />
)
expect(container.firstChild).toBeInTheDocument()
})
it('renders completed state', () => {
const { container } = render(
<Progress currentStep={2} steps={[{ title: '' }]} />
)
expect(container.querySelector('li')).toHaveClass('completed')
})
})

View File

@ -0,0 +1,46 @@
import React from 'react'
import { render } from 'react-testing-library'
import Step from './Step'
const stateMock = {
validationStatus: {
1: { allFieldsValid: true },
2: { allFieldsValid: true },
3: { allFieldsValid: true }
}
}
const propsMock = {
inputChange: () => null,
inputToArrayChange: () => null,
state: stateMock,
title: 'Hello',
description: 'description',
next: () => null,
prev: () => null,
tryAgain: () => null,
toStart: () => null
}
describe('Step', () => {
it('renders without crashing', () => {
const { container } = render(
<Step currentStep={1} index={0} totalSteps={3} {...propsMock} />
)
expect(container.firstChild).toBeInTheDocument()
})
it('renders previous button one page bigger than 1', () => {
const { queryByText } = render(
<Step currentStep={2} index={1} totalSteps={3} {...propsMock} />
)
expect(queryByText('← Previous')).toBeInTheDocument()
})
it('does not render next button when on last step', () => {
const { queryByText } = render(
<Step currentStep={3} index={2} totalSteps={3} {...propsMock} />
)
expect(queryByText('Next →')).toBeNull()
})
})

View File

@ -0,0 +1,76 @@
import React from 'react'
import { render, fireEvent } from 'react-testing-library'
import { BrowserRouter as Router } from 'react-router-dom'
import StepRegisterContent from './StepRegisterContent'
const stateMock = {
publishedDid: '',
publishingError: '',
isPublishing: false,
isPublished: false
}
const propsMock = {
tryAgain: jest.fn(),
toStart: jest.fn(),
content: 'Hello'
}
describe('StepRegisterContent', () => {
it('renders without crashing', () => {
const { container } = render(
<Router>
<StepRegisterContent state={stateMock} {...propsMock} />
</Router>
)
expect(container.firstChild).toBeInTheDocument()
})
it('renders publishing state', () => {
const { container } = render(
<Router>
<StepRegisterContent
state={{ ...stateMock, isPublishing: true }}
{...propsMock}
/>
</Router>
)
expect(container.querySelector('.spinnerMessage')).toHaveTextContent(
'Please sign with your crypto wallet'
)
})
it('renders published state', () => {
const { container, getByText } = render(
<Router>
<StepRegisterContent
state={{ ...stateMock, isPublished: true }}
{...propsMock}
/>
</Router>
)
expect(container.querySelector('.success')).toHaveTextContent(
'Your asset is published!'
)
fireEvent.click(getByText('Publish another asset'))
expect(propsMock.toStart).toHaveBeenCalled()
})
it('renders error state', () => {
const { container, getByText } = render(
<Router>
<StepRegisterContent
state={{ ...stateMock, publishingError: 'Error!' }}
{...propsMock}
/>
</Router>
)
expect(
container.querySelector('.message:last-child')
).toHaveTextContent('Something went wrong')
fireEvent.click(getByText('try again'))
expect(propsMock.tryAgain).toHaveBeenCalled()
})
})

View File

@ -7,7 +7,12 @@ import styles from './StepRegisterContent.module.scss'
interface StepRegisterContentProps { interface StepRegisterContentProps {
tryAgain(): void tryAgain(): void
toStart(): void toStart(): void
state: any state: {
publishedDid: string
isPublishing: boolean
publishingError: string
isPublished: boolean
}
content?: string content?: string
} }

View File

@ -0,0 +1,11 @@
import React from 'react'
import { render } from 'react-testing-library'
import Publish from '.'
describe('Progress', () => {
it('renders without crashing', () => {
const { container, getByText } = render(<Publish />)
expect(container.firstChild).toBeInTheDocument()
expect(getByText('Next →')).toHaveAttribute('disabled')
})
})

View File

@ -1,5 +1,16 @@
@import '../styles/variables'; @import '../styles/variables';
.resultsTitle {
color: $brand-grey-light;
font-size: $font-size-h3;
margin-top: -($spacer / 2);
margin-bottom: $spacer;
span {
color: $brand-grey-dark;
}
}
.results { .results {
display: grid; display: grid;
grid-template-columns: 1fr; grid-template-columns: 1fr;

View File

@ -0,0 +1,51 @@
import React from 'react'
import { render } from 'react-testing-library'
import Search from './Search'
import { User } from '../context'
import { createMemoryHistory } from 'history'
describe('Search', () => {
it('renders without crashing', () => {
const history = createMemoryHistory()
const { container } = render(
<User.Provider
value={{
isLogged: false,
isLoading: false,
isWeb3: false,
isNile: false,
account: '',
web3: {},
ocean: {
aquarius: {
queryMetadata: () => {
return {
results: [],
totalResults: 1,
totalPages: 1
}
}
}
},
balance: { eth: 0, ocn: 0 },
network: '',
requestFromFaucet: () => {},
unlockAccounts: () => {},
message: ''
}}
>
<Search
location={{
search: '?text=Hello&page=1',
pathname: '/search',
state: '',
hash: ''
}}
history={history}
/>
</User.Provider>
)
expect(container.firstChild).toBeInTheDocument()
})
})

View File

@ -1,5 +1,6 @@
import React, { PureComponent } from 'react' import React, { PureComponent } from 'react'
import queryString from 'query-string' import queryString from 'query-string'
import { History, Location } from 'history'
import { Logger } from '@oceanprotocol/squid' import { Logger } from '@oceanprotocol/squid'
import Spinner from '../components/atoms/Spinner' import Spinner from '../components/atoms/Spinner'
import Route from '../components/templates/Route' import Route from '../components/templates/Route'
@ -10,7 +11,7 @@ import styles from './Search.module.scss'
interface SearchProps { interface SearchProps {
location: Location location: Location
history: any history: History
} }
interface SearchState { interface SearchState {
@ -20,6 +21,7 @@ interface SearchState {
totalPages: number totalPages: number
currentPage: number currentPage: number
isLoading: boolean isLoading: boolean
searchTerm: string
} }
export default class Search extends PureComponent<SearchProps, SearchState> { export default class Search extends PureComponent<SearchProps, SearchState> {
@ -29,18 +31,22 @@ export default class Search extends PureComponent<SearchProps, SearchState> {
offset: 25, offset: 25,
totalPages: 1, totalPages: 1,
currentPage: 1, currentPage: 1,
isLoading: true isLoading: true,
searchTerm: ''
} }
private readonly searchTerm = queryString.parse(this.props.location.search)
.text
private readonly searchPage = queryString.parse(this.props.location.search)
.page
public async componentDidMount() { public async componentDidMount() {
const searchTerm = await queryString.parse(this.props.location.search)
.text
const searchPage = queryString.parse(this.props.location.search).page
await this.setState({
searchTerm: JSON.stringify(searchTerm)
})
// switch to respective page if query string is present // switch to respective page if query string is present
if (this.searchPage) { if (searchPage) {
const currentPage = Number(this.searchPage) const currentPage = Number(searchPage)
await this.setState({ currentPage }) await this.setState({ currentPage })
} }
@ -48,11 +54,13 @@ export default class Search extends PureComponent<SearchProps, SearchState> {
} }
private searchAssets = async () => { private searchAssets = async () => {
const { ocean } = this.context
const searchQuery = { const searchQuery = {
offset: this.state.offset, offset: this.state.offset,
page: this.state.currentPage, page: this.state.currentPage,
query: { query: {
text: [this.searchTerm], text: [this.state.searchTerm],
price: [-1, 1] price: [-1, 1]
}, },
sort: { sort: {
@ -60,16 +68,18 @@ export default class Search extends PureComponent<SearchProps, SearchState> {
} }
} }
const search = await this.context.ocean.aquarius.queryMetadata( try {
searchQuery const search = await ocean.aquarius.queryMetadata(searchQuery)
)
this.setState({ this.setState({
results: search.results, results: search.results,
totalResults: search.totalResults, totalResults: search.totalResults,
totalPages: search.totalPages, totalPages: search.totalPages,
isLoading: false isLoading: false
}) })
Logger.log(`Loaded ${this.state.results.length} assets`) } catch (error) {
Logger.error(error)
this.setState({ isLoading: false })
}
} }
private handlePageClick = async (data: { selected: number }) => { private handlePageClick = async (data: { selected: number }) => {
@ -78,7 +88,7 @@ export default class Search extends PureComponent<SearchProps, SearchState> {
this.props.history.push({ this.props.history.push({
pathname: this.props.location.pathname, pathname: this.props.location.pathname,
search: `?text=${this.searchTerm}&page=${toPage}` search: `?text=${this.state.searchTerm}&page=${toPage}`
}) })
await this.setState({ currentPage: toPage, isLoading: true }) await this.setState({ currentPage: toPage, isLoading: true })
@ -102,13 +112,17 @@ export default class Search extends PureComponent<SearchProps, SearchState> {
const { totalResults, totalPages, currentPage } = this.state const { totalResults, totalPages, currentPage } = this.state
return ( return (
<Route <Route title="Search" wide>
title={`${ {totalResults > 0 && (
totalResults > 0 ? totalResults : '' <h2
} Results for <span>${this.searchTerm}</span>`} className={styles.resultsTitle}
titleReverse dangerouslySetInnerHTML={{
wide __html: `${totalResults} results for <span>${
> this.state.searchTerm
}</span>`
}}
/>
)}
{this.renderResults()} {this.renderResults()}
<Pagination <Pagination

View File

@ -0,0 +1,10 @@
import React from 'react'
import { render } from 'react-testing-library'
import Styleguide from './Styleguide'
describe('Styleguide', () => {
it('renders without crashing', () => {
const { container } = render(<Styleguide />)
expect(container.firstChild).toBeInTheDocument()
})
})

1758
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
{ {
"name": "commons", "name": "commons",
"description": "Ocean Protocol marketplace to explore, download, and publish open data sets.", "description": "Ocean Protocol marketplace to explore, download, and publish open data sets.",
"version": "0.2.3", "version": "0.2.7",
"license": "Apache-2.0", "license": "Apache-2.0",
"scripts": { "scripts": {
"install": "./scripts/install.sh", "install": "./scripts/install.sh",
@ -31,16 +31,16 @@
"concurrently": "^4.1.0", "concurrently": "^4.1.0",
"eslint": "^5.16.0", "eslint": "^5.16.0",
"eslint-config-oceanprotocol": "^1.3.0", "eslint-config-oceanprotocol": "^1.3.0",
"eslint-config-prettier": "^4.1.0", "eslint-config-prettier": "^4.2.0",
"eslint-plugin-prettier": "^3.0.1", "eslint-plugin-prettier": "^3.0.1",
"prettier": "^1.16.4", "prettier": "^1.17.0",
"prettier-stylelint": "^0.4.2", "prettier-stylelint": "^0.4.2",
"release-it": "^10.4.3", "release-it": "^11.0.2",
"stylelint": "^10.0.1", "stylelint": "^10.0.1",
"stylelint-config-bigchaindb": "^1.2.1", "stylelint-config-bigchaindb": "^1.2.2",
"stylelint-config-css-modules": "^1.3.0", "stylelint-config-css-modules": "^1.4.0",
"stylelint-config-standard": "^18.2.0", "stylelint-config-standard": "^18.3.0",
"typescript": "^3.4.4" "typescript": "^3.4.5"
}, },
"repository": { "repository": {
"type": "git", "type": "git",

12
scripts/coverage.sh Executable file
View File

@ -0,0 +1,12 @@
#/usr/bin/env/sh
set -e
components="server client"
for component in $components
do
printf "\n\nReporting coverage: $component\n"
cd $component
npm run coverage
cd ..
done

View File

@ -5,7 +5,7 @@ components="server client"
for component in $components for component in $components
do do
echo "\n\nInstalling dependencies: $component\n" printf "\n\nInstalling dependencies: $component\n"
cd $component cd $component
npm install npm install
cd .. cd ..

169
server/package-lock.json generated
View File

@ -440,15 +440,6 @@
"integrity": "sha512-D9MyoQFI7iP5VdpEyPZyjjqIJ8Y8EDNQFIFVLOmeg1rI1xiHOChyUPMPRUVfqFCerxfE+yS3vMyj37F6IdtOoQ==", "integrity": "sha512-D9MyoQFI7iP5VdpEyPZyjjqIJ8Y8EDNQFIFVLOmeg1rI1xiHOChyUPMPRUVfqFCerxfE+yS3vMyj37F6IdtOoQ==",
"dev": true "dev": true
}, },
"@types/dotenv": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/@types/dotenv/-/dotenv-6.1.1.tgz",
"integrity": "sha512-ftQl3DtBvqHl9L16tpqqzA4YzCSXZfi7g8cQceTz5rOlYtk/IZbFjAv3mLOQlNIgOaylCQWQoBdDQHPgEBJPHg==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/express": { "@types/express": {
"version": "4.16.1", "version": "4.16.1",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.16.1.tgz", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.16.1.tgz",
@ -694,11 +685,6 @@
"normalize-path": "^2.1.1" "normalize-path": "^2.1.1"
} }
}, },
"append-field": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
"integrity": "sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY="
},
"arg": { "arg": {
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.0.tgz", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.0.tgz",
@ -1059,16 +1045,8 @@
"buffer-from": { "buffer-from": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
}, "dev": true
"busboy": {
"version": "0.2.14",
"resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz",
"integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=",
"requires": {
"dicer": "0.2.5",
"readable-stream": "1.1.x"
}
}, },
"bytes": { "bytes": {
"version": "3.1.0", "version": "3.1.0",
@ -1257,11 +1235,6 @@
"color-name": "1.1.3" "color-name": "1.1.3"
} }
}, },
"color-js": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/color-js/-/color-js-1.0.5.tgz",
"integrity": "sha512-KgOTVz7fupb3lOXu4lixP6BR2CdMaTtCnGmLfHZWhq8NU3MABd6U9KjWtJVuYChGS/gIaoA8LakKKQfWhQgwYQ=="
},
"color-name": { "color-name": {
"version": "1.1.3", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
@ -1332,46 +1305,6 @@
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
"dev": true "dev": true
}, },
"concat-stream": {
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
"integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
"requires": {
"buffer-from": "^1.0.0",
"inherits": "^2.0.3",
"readable-stream": "^2.2.2",
"typedarray": "^0.0.6"
},
"dependencies": {
"isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
},
"readable-stream": {
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"requires": {
"safe-buffer": "~5.1.0"
}
}
}
},
"configstore": { "configstore": {
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.2.tgz", "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.2.tgz",
@ -1628,15 +1561,6 @@
"integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=", "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=",
"dev": true "dev": true
}, },
"dicer": {
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz",
"integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=",
"requires": {
"readable-stream": "1.1.x",
"streamsearch": "0.1.2"
}
},
"diff": { "diff": {
"version": "3.5.0", "version": "3.5.0",
"resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
@ -1757,13 +1681,15 @@
"esutils": "^2.0.2", "esutils": "^2.0.2",
"optionator": "^0.8.1", "optionator": "^0.8.1",
"source-map": "~0.6.1" "source-map": "~0.6.1"
}
}, },
"dependencies": {
"esprima": { "esprima": {
"version": "3.1.3", "version": "3.1.3",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz",
"integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=",
"dev": true "dev": true
}
}
}, },
"estraverse": { "estraverse": {
"version": "4.2.0", "version": "4.2.0",
@ -3317,11 +3243,6 @@
"integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=",
"dev": true "dev": true
}, },
"isarray": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
},
"isexe": { "isexe": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
@ -4054,14 +3975,6 @@
"yallist": "^2.1.2" "yallist": "^2.1.2"
} }
}, },
"lusca": {
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/lusca/-/lusca-1.6.1.tgz",
"integrity": "sha512-+JzvUMH/rsE/4XfHdDOl70bip0beRcHSviYATQM0vtls59uVtdn1JMu4iD7ZShBpAmFG8EnaA+PrYG9sECMIOQ==",
"requires": {
"tsscmp": "^1.0.5"
}
},
"make-dir": { "make-dir": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
@ -4243,7 +4156,8 @@
"minimist": { "minimist": {
"version": "0.0.8", "version": "0.0.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
"dev": true
}, },
"mixin-deep": { "mixin-deep": {
"version": "1.3.1", "version": "1.3.1",
@ -4270,6 +4184,7 @@
"version": "0.5.1", "version": "0.5.1",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"dev": true,
"requires": { "requires": {
"minimist": "0.0.8" "minimist": "0.0.8"
} }
@ -4301,21 +4216,6 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
}, },
"multer": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/multer/-/multer-1.4.1.tgz",
"integrity": "sha512-zzOLNRxzszwd+61JFuAo0fxdQfvku12aNJgnla0AQ+hHxFmfc/B7jBVuPr5Rmvu46Jze/iJrFpSOsD7afO8SDw==",
"requires": {
"append-field": "^1.0.0",
"busboy": "^0.2.11",
"concat-stream": "^1.5.2",
"mkdirp": "^0.5.1",
"object-assign": "^4.1.1",
"on-finished": "^2.3.0",
"type-is": "^1.6.4",
"xtend": "^4.0.0"
}
},
"nan": { "nan": {
"version": "2.13.2", "version": "2.13.2",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.13.2.tgz", "resolved": "https://registry.npmjs.org/nan/-/nan-2.13.2.tgz",
@ -4481,11 +4381,6 @@
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
"integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ=="
}, },
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
},
"object-copy": { "object-copy": {
"version": "0.1.0", "version": "0.1.0",
"resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
@ -4830,7 +4725,8 @@
"process-nextick-args": { "process-nextick-args": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
"integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
"dev": true
}, },
"prompts": { "prompts": {
"version": "2.0.4", "version": "2.0.4",
@ -4951,17 +4847,6 @@
"read-pkg": "^3.0.0" "read-pkg": "^3.0.0"
} }
}, },
"readable-stream": {
"version": "1.1.14",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
"integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.1",
"isarray": "0.0.1",
"string_decoder": "~0.10.x"
}
},
"readdirp": { "readdirp": {
"version": "2.2.1", "version": "2.2.1",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
@ -5630,11 +5515,6 @@
"integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=",
"dev": true "dev": true
}, },
"streamsearch": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz",
"integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo="
},
"string-length": { "string-length": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/string-length/-/string-length-2.0.0.tgz", "resolved": "https://registry.npmjs.org/string-length/-/string-length-2.0.0.tgz",
@ -5689,11 +5569,6 @@
} }
} }
}, },
"string_decoder": {
"version": "0.10.31",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
},
"strip-ansi": { "strip-ansi": {
"version": "5.2.0", "version": "5.2.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
@ -6024,11 +5899,6 @@
"yn": "^3.0.0" "yn": "^3.0.0"
} }
}, },
"tsscmp": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz",
"integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA=="
},
"tunnel-agent": { "tunnel-agent": {
"version": "0.6.0", "version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
@ -6060,11 +5930,6 @@
"mime-types": "~2.1.24" "mime-types": "~2.1.24"
} }
}, },
"typedarray": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
},
"typescript": { "typescript": {
"version": "3.4.5", "version": "3.4.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.4.5.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.4.5.tgz",
@ -6072,9 +5937,9 @@
"dev": true "dev": true
}, },
"uglify-js": { "uglify-js": {
"version": "3.5.11", "version": "3.5.10",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.5.11.tgz", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.5.10.tgz",
"integrity": "sha512-izPJg8RsSyqxbdnqX36ExpbH3K7tDBsAU/VfNv89VkMFy3z39zFjunQGsSHOlGlyIfGLGprGeosgQno3bo2/Kg==", "integrity": "sha512-/GTF0nosyPLbdJBd+AwYiZ+Hu5z8KXWnO0WCGt1BQ/u9Iamhejykqmz5o1OHJ53+VAk6xVxychonnApDjuqGsw==",
"dev": true, "dev": true,
"optional": true, "optional": true,
"requires": { "requires": {
@ -6276,7 +6141,8 @@
"util-deprecate": { "util-deprecate": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
"dev": true
}, },
"util.promisify": { "util.promisify": {
"version": "1.0.0", "version": "1.0.0",
@ -6493,11 +6359,6 @@
"integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==",
"dev": true "dev": true
}, },
"xtend": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
"integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68="
},
"y18n": { "y18n": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",

View File

@ -9,26 +9,22 @@
"serve": "node dist/server.js", "serve": "node dist/server.js",
"build": "tsc", "build": "tsc",
"test": "jest --coverage", "test": "jest --coverage",
"test:watch": "jest --coverage --watch" "test:watch": "jest --coverage --watch",
"coverage": "cat coverage/lcov.info | codacy-coverage --token 8801f827fe1144ffa85cd7da94f2bbf7"
}, },
"dependencies": { "dependencies": {
"body-parser": "^1.18.3", "body-parser": "^1.18.3",
"color-js": "^1.0.5",
"compression": "^1.7.4", "compression": "^1.7.4",
"debug": "^4.1.1", "debug": "^4.1.1",
"express": "^4.16.4", "express": "^4.16.4",
"express-validator": "^5.3.1", "express-validator": "^5.3.1",
"lusca": "^1.6.1",
"morgan": "^1.9.1", "morgan": "^1.9.1",
"multer": "^1.4.1", "request": "^2.88.0"
"request": "^2.88.0",
"uuid": "^3.3.2"
}, },
"devDependencies": { "devDependencies": {
"@types/body-parser": "^1.17.0", "@types/body-parser": "^1.17.0",
"@types/compression": "0.0.36", "@types/compression": "0.0.36",
"@types/debug": "^4.1.4", "@types/debug": "^4.1.4",
"@types/dotenv": "^6.1.1",
"@types/express": "^4.16.1", "@types/express": "^4.16.1",
"@types/jasmine": "^3.3.12", "@types/jasmine": "^3.3.12",
"@types/jest": "^24.0.11", "@types/jest": "^24.0.11",
@ -41,7 +37,7 @@
"supertest": "^4.0.2", "supertest": "^4.0.2",
"ts-jest": "^24.0.2", "ts-jest": "^24.0.2",
"ts-node": "^8.1.0", "ts-node": "^8.1.0",
"typescript": "^3.4.3" "typescript": "^3.4.5"
}, },
"repository": { "repository": {
"type": "git", "type": "git",

View File

@ -1,4 +1,4 @@
import { Router, Request, Response, NextFunction } from 'express' import { Router, Request, Response } from 'express'
import request from 'request' import request from 'request'
export class UrlCheckRouter { export class UrlCheckRouter {
@ -11,7 +11,7 @@ export class UrlCheckRouter {
this.router = Router() this.router = Router()
} }
public checkUrl(req: Request, res: Response, next: NextFunction) { public checkUrl(req: Request, res: Response) {
if (!req.body.url) { if (!req.body.url) {
return res.send({ status: 'error', message: 'missing url' }) return res.send({ status: 'error', message: 'missing url' })
} }

View File

@ -49,6 +49,7 @@ app.use(morgan('dev'))
app.use(bodyParser.json()) app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: false })) app.use(bodyParser.urlencoded({ extended: false }))
app.use(compression()) app.use(compression())
// routes // routes
app.use('/api/v1/urlcheck', UrlCheckRouter) app.use('/api/v1/urlcheck', UrlCheckRouter)

View File

@ -7,9 +7,21 @@ afterAll(done => {
}) })
describe('POST /api/v1/urlcheck', () => { describe('POST /api/v1/urlcheck', () => {
it('responds with json', function(done) { it('responds with json', async () => {
request(server) const response = await request(server).post('/api/v1/urlcheck')
.post('/api/v1/urlcheck') expect(response.statusCode).toBe(200)
.expect(200, done) })
it('responds with error message when url is missing', async () => {
const response = await request(server).post('/api/v1/urlcheck')
const text = await JSON.parse(response.text)
expect(text.message).toBe('missing url')
})
})
describe('Errors', () => {
it('responds with 404 on unknown path', async () => {
const response = await request(server).post('/whatever')
expect(response.statusCode).toBe(404)
}) })
}) })