Cypress (#734)
* Add some basic integration tests and CI in GitHub * Add push trigger * Add badge for tests to readme * Unit tests * Just use the development infura URL in the tests * Remove unused webpack config * Make integration test run on the same port as yarn start * make test a little bit better * rename describe
This commit is contained in:
parent
a1a9d9f041
commit
28b24036c6
4
.env
Normal file
4
.env
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
REACT_APP_CHAIN_ID="1"
|
||||||
|
REACT_APP_NETWORK_URL="https://mainnet.infura.io/v3/b8800ce81b8c451698081d269b86692b"
|
||||||
|
REACT_APP_PORTIS_ID=""
|
||||||
|
REACT_APP_FORTMATIC_KEY=""
|
34
.github/workflows/tests.yaml
vendored
Normal file
34
.github/workflows/tests.yaml
vendored
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
name: Tests
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- v2
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- v2
|
||||||
|
jobs:
|
||||||
|
integration-tests:
|
||||||
|
name: Integration tests
|
||||||
|
runs-on: ubuntu-16.04
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v1
|
||||||
|
- uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: '12'
|
||||||
|
- run: yarn
|
||||||
|
- run: yarn integration-test
|
||||||
|
|
||||||
|
unit-tests:
|
||||||
|
name: Unit tests
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v1
|
||||||
|
- uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: '12'
|
||||||
|
- run: yarn
|
||||||
|
- run: yarn test
|
||||||
|
|
||||||
|
# todo: add job for typescript linting
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -11,7 +11,6 @@
|
|||||||
|
|
||||||
# misc
|
# misc
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.env
|
|
||||||
.env.local
|
.env.local
|
||||||
.env.development.local
|
.env.development.local
|
||||||
.env.test.local
|
.env.test.local
|
||||||
@ -29,3 +28,6 @@ notes.txt
|
|||||||
.vscode/
|
.vscode/
|
||||||
|
|
||||||
package-lock.json
|
package-lock.json
|
||||||
|
|
||||||
|
cypress/videos
|
||||||
|
cypress/screenshots
|
13
.travis.yml
13
.travis.yml
@ -1,13 +0,0 @@
|
|||||||
branches:
|
|
||||||
except:
|
|
||||||
- master
|
|
||||||
language: node_js
|
|
||||||
node_js:
|
|
||||||
- '10'
|
|
||||||
cache:
|
|
||||||
directories:
|
|
||||||
- node_modules
|
|
||||||
install: yarn
|
|
||||||
script:
|
|
||||||
- yarn check:all
|
|
||||||
- yarn build
|
|
@ -1,7 +1,7 @@
|
|||||||
# Uniswap Frontend
|
# Uniswap Frontend
|
||||||
|
|
||||||
[![Netlify Status](https://api.netlify.com/api/v1/badges/fa110555-b3c7-4eeb-b840-88a835009c62/deploy-status)](https://app.netlify.com/sites/uniswap/deploys)
|
[![Netlify Status](https://api.netlify.com/api/v1/badges/fa110555-b3c7-4eeb-b840-88a835009c62/deploy-status)](https://app.netlify.com/sites/uniswap/deploys)
|
||||||
[![Build Status](https://travis-ci.org/Uniswap/uniswap-frontend.svg)](https://travis-ci.org/Uniswap/uniswap-frontend)
|
[![Tests](https://github.com/Uniswap/uniswap-frontend/workflows/Tests/badge.svg?branch=v2)](https://github.com/Uniswap/uniswap-frontend/actions?query=workflow%3ATests)
|
||||||
[![Styled With Prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://prettier.io/)
|
[![Styled With Prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://prettier.io/)
|
||||||
|
|
||||||
This an an open source interface for Uniswap - a protocol for decentralized exchange of Ethereum tokens.
|
This an an open source interface for Uniswap - a protocol for decentralized exchange of Ethereum tokens.
|
||||||
|
3
cypress.json
Normal file
3
cypress.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"baseUrl": "http://localhost:3000"
|
||||||
|
}
|
18
cypress/integration/no-connect.ts
Normal file
18
cypress/integration/no-connect.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
describe('Homepage', () => {
|
||||||
|
beforeEach(() => cy.visit('/'))
|
||||||
|
it('loads exchange page', () => {
|
||||||
|
cy.get('#exchangePage')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has url /swap', () => {
|
||||||
|
cy.url().should('include', '/swap')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('can enter an amount into input', () => {
|
||||||
|
cy.get('#swapInputField').type('0.001')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('can enter an amount into output', () => {
|
||||||
|
cy.get('#swapOutputField').type('0.001')
|
||||||
|
})
|
||||||
|
})
|
21
cypress/plugins/index.js
Normal file
21
cypress/plugins/index.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/// <reference types="cypress" />
|
||||||
|
// ***********************************************************
|
||||||
|
// This example plugins/index.js can be used to load plugins
|
||||||
|
//
|
||||||
|
// You can change the location of this file or turn off loading
|
||||||
|
// the plugins file with the 'pluginsFile' configuration option.
|
||||||
|
//
|
||||||
|
// You can read more here:
|
||||||
|
// https://on.cypress.io/plugins-guide
|
||||||
|
// ***********************************************************
|
||||||
|
|
||||||
|
// This function is called when a project is opened or re-opened (e.g. due to
|
||||||
|
// the project's config changing)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Cypress.PluginConfig}
|
||||||
|
*/
|
||||||
|
module.exports = (on, config) => {
|
||||||
|
// `on` is used to hook into various events Cypress emits
|
||||||
|
// `config` is the resolved Cypress config
|
||||||
|
}
|
27
cypress/support/commands.ts
Normal file
27
cypress/support/commands.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// ***********************************************
|
||||||
|
// This example commands.js shows you how to
|
||||||
|
// create various custom commands and overwrite
|
||||||
|
// existing commands.
|
||||||
|
//
|
||||||
|
// For more comprehensive examples of custom
|
||||||
|
// commands please read more here:
|
||||||
|
// https://on.cypress.io/custom-commands
|
||||||
|
// ***********************************************
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// -- This is a parent command --
|
||||||
|
// Cypress.Commands.add("login", (email, password) => { ... })
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// -- This is a child command --
|
||||||
|
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// -- This is a dual command --
|
||||||
|
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// -- This will overwrite an existing command --
|
||||||
|
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
|
||||||
|
|
||||||
|
// TODO(moodysalem): commands for connecting a mock ethereum provider.
|
20
cypress/support/index.ts
Normal file
20
cypress/support/index.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// ***********************************************************
|
||||||
|
// This example support/index.ts is processed and
|
||||||
|
// loaded automatically before your test files.
|
||||||
|
//
|
||||||
|
// This is a great place to put global configuration and
|
||||||
|
// behavior that modifies Cypress.
|
||||||
|
//
|
||||||
|
// You can change the location of this file or turn off
|
||||||
|
// automatically serving support files with the
|
||||||
|
// 'supportFile' configuration option.
|
||||||
|
//
|
||||||
|
// You can read more here:
|
||||||
|
// https://on.cypress.io/configuration
|
||||||
|
// ***********************************************************
|
||||||
|
|
||||||
|
// Import commands.ts using ES2015 syntax:
|
||||||
|
import './commands'
|
||||||
|
|
||||||
|
// Alternatively you can use CommonJS syntax:
|
||||||
|
// require('./commands')
|
17
cypress/tsconfig.json
Normal file
17
cypress/tsconfig.json
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"strict": true,
|
||||||
|
"baseUrl": "../node_modules",
|
||||||
|
"target": "es5",
|
||||||
|
"lib": [
|
||||||
|
"es5",
|
||||||
|
"dom"
|
||||||
|
],
|
||||||
|
"types": [
|
||||||
|
"cypress"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"**/*.ts"
|
||||||
|
]
|
||||||
|
}
|
@ -56,7 +56,10 @@
|
|||||||
"@types/react": "^16.9.34",
|
"@types/react": "^16.9.34",
|
||||||
"@types/react-dom": "^16.9.7",
|
"@types/react-dom": "^16.9.7",
|
||||||
"@types/styled-components": "^4.2.0",
|
"@types/styled-components": "^4.2.0",
|
||||||
|
"cypress": "^4.5.0",
|
||||||
"prettier": "^1.17.0",
|
"prettier": "^1.17.0",
|
||||||
|
"serve": "^11.3.0",
|
||||||
|
"start-server-and-test": "^1.11.0",
|
||||||
"typescript": "^3.8.3"
|
"typescript": "^3.8.3"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -71,7 +74,10 @@
|
|||||||
"fix:all": "yarn fix:lint && yarn fix:format",
|
"fix:all": "yarn fix:lint && yarn fix:format",
|
||||||
"check:lint": "yarn lint:base",
|
"check:lint": "yarn lint:base",
|
||||||
"check:format": "yarn format:base --check",
|
"check:format": "yarn format:base --check",
|
||||||
"check:all": "yarn check:lint && yarn check:format"
|
"check:all": "yarn check:lint && yarn check:format",
|
||||||
|
"cy:run": "cypress run",
|
||||||
|
"serve:build": "serve -s build -l 3000",
|
||||||
|
"integration-test": "yarn build && start-server-and-test 'yarn run serve:build' http://localhost:3000 cy:run"
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"extends": "react-app"
|
"extends": "react-app"
|
||||||
|
@ -151,7 +151,8 @@ export default function CurrencyInputPanel({
|
|||||||
hideInput = false,
|
hideInput = false,
|
||||||
showSendWithSwap = false,
|
showSendWithSwap = false,
|
||||||
otherSelectedTokenAddress = null,
|
otherSelectedTokenAddress = null,
|
||||||
advanced = false
|
advanced = false,
|
||||||
|
inputId,
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
@ -195,6 +196,7 @@ export default function CurrencyInputPanel({
|
|||||||
<>
|
<>
|
||||||
<NumericalInput
|
<NumericalInput
|
||||||
value={value}
|
value={value}
|
||||||
|
id={inputId}
|
||||||
onUserInput={val => {
|
onUserInput={val => {
|
||||||
onUserInput(field, val)
|
onUserInput(field, val)
|
||||||
}}
|
}}
|
||||||
|
@ -940,7 +940,7 @@ function ExchangePage({ sendingInput = false, history, params }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Wrapper>
|
<Wrapper id="exchangePage">
|
||||||
<ConfirmationModal
|
<ConfirmationModal
|
||||||
isOpen={showConfirm}
|
isOpen={showConfirm}
|
||||||
title={sendingWithSwap ? 'Confirm swap and send' : sending ? 'Confirm Send' : 'Confirm Swap'}
|
title={sendingWithSwap ? 'Confirm swap and send' : sending ? 'Confirm Send' : 'Confirm Swap'}
|
||||||
@ -985,6 +985,7 @@ function ExchangePage({ sendingInput = false, history, params }) {
|
|||||||
showSendWithSwap={true}
|
showSendWithSwap={true}
|
||||||
advanced={advanced}
|
advanced={advanced}
|
||||||
label={''}
|
label={''}
|
||||||
|
inputId="swapInputField"
|
||||||
otherSelectedTokenAddress={tokens[Field.OUTPUT]?.address}
|
otherSelectedTokenAddress={tokens[Field.OUTPUT]?.address}
|
||||||
/>
|
/>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
@ -1008,6 +1009,7 @@ function ExchangePage({ sendingInput = false, history, params }) {
|
|||||||
}}
|
}}
|
||||||
onTokenSelection={address => onTokenSelection(Field.INPUT, address)}
|
onTokenSelection={address => onTokenSelection(Field.INPUT, address)}
|
||||||
otherSelectedTokenAddress={tokens[Field.OUTPUT]?.address}
|
otherSelectedTokenAddress={tokens[Field.OUTPUT]?.address}
|
||||||
|
inputId="swapInputField"
|
||||||
/>
|
/>
|
||||||
{sendingWithSwap ? (
|
{sendingWithSwap ? (
|
||||||
<ColumnCenter>
|
<ColumnCenter>
|
||||||
@ -1048,6 +1050,7 @@ function ExchangePage({ sendingInput = false, history, params }) {
|
|||||||
pair={pair}
|
pair={pair}
|
||||||
advanced={advanced}
|
advanced={advanced}
|
||||||
otherSelectedTokenAddress={tokens[Field.INPUT]?.address}
|
otherSelectedTokenAddress={tokens[Field.INPUT]?.address}
|
||||||
|
inputId="swapOutputField"
|
||||||
/>
|
/>
|
||||||
{sendingWithSwap && (
|
{sendingWithSwap && (
|
||||||
<RowBetween padding="0 12px">
|
<RowBetween padding="0 12px">
|
||||||
|
@ -722,6 +722,7 @@ function AddLiquidity({ token0, token1, step = false }) {
|
|||||||
error={inputError}
|
error={inputError}
|
||||||
pair={pair}
|
pair={pair}
|
||||||
label="Input"
|
label="Input"
|
||||||
|
inputId="addLiquidityInput"
|
||||||
/>
|
/>
|
||||||
<ColumnCenter>
|
<ColumnCenter>
|
||||||
<Plus size="16" color="#888D9B" />
|
<Plus size="16" color="#888D9B" />
|
||||||
@ -738,6 +739,7 @@ function AddLiquidity({ token0, token1, step = false }) {
|
|||||||
onTokenSelection={address => onTokenSelection(Field.OUTPUT, address)}
|
onTokenSelection={address => onTokenSelection(Field.OUTPUT, address)}
|
||||||
error={outputError}
|
error={outputError}
|
||||||
pair={pair}
|
pair={pair}
|
||||||
|
inputId="addLiquidityOutput"
|
||||||
/>
|
/>
|
||||||
{tokens[Field.OUTPUT] && tokens[Field.INPUT] && (
|
{tokens[Field.OUTPUT] && tokens[Field.INPUT] && (
|
||||||
<LightCard padding="1rem" borderRadius={'20px'}>
|
<LightCard padding="1rem" borderRadius={'20px'}>
|
||||||
|
@ -704,6 +704,7 @@ export default function RemoveLiquidity({ token0, token1 }) {
|
|||||||
token={pair?.liquidityToken}
|
token={pair?.liquidityToken}
|
||||||
isExchange={true}
|
isExchange={true}
|
||||||
pair={pair}
|
pair={pair}
|
||||||
|
inputId="liquidityAmount"
|
||||||
/>
|
/>
|
||||||
<ColumnCenter>
|
<ColumnCenter>
|
||||||
<ArrowDown size="16" color="#888D9B"/>
|
<ArrowDown size="16" color="#888D9B"/>
|
||||||
@ -718,6 +719,7 @@ export default function RemoveLiquidity({ token0, token1 }) {
|
|||||||
error={inputError}
|
error={inputError}
|
||||||
label={'Output'}
|
label={'Output'}
|
||||||
disableTokenSelect
|
disableTokenSelect
|
||||||
|
inputId="removeLiquidityToken0"
|
||||||
/>
|
/>
|
||||||
<ColumnCenter>
|
<ColumnCenter>
|
||||||
<Plus size="16" color="#888D9B"/>
|
<Plus size="16" color="#888D9B"/>
|
||||||
@ -732,6 +734,7 @@ export default function RemoveLiquidity({ token0, token1 }) {
|
|||||||
error={outputError}
|
error={outputError}
|
||||||
label={'Output'}
|
label={'Output'}
|
||||||
disableTokenSelect
|
disableTokenSelect
|
||||||
|
inputId="removeLiquidityToken1"
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
12
src/utils/index.test.ts
Normal file
12
src/utils/index.test.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { getEtherscanLink } from './index'
|
||||||
|
|
||||||
|
describe('utils', () => {
|
||||||
|
describe('#getEtherscanLink', () => {
|
||||||
|
it('correct for tx', () => {
|
||||||
|
expect(getEtherscanLink(1, 'abc', 'transaction')).toEqual('https://etherscan.io/tx/abc')
|
||||||
|
})
|
||||||
|
it('correct for address', () => {
|
||||||
|
expect(getEtherscanLink(1, 'abc', 'address')).toEqual('https://etherscan.io/address/abc')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
@ -34,7 +34,7 @@ const ETHERSCAN_PREFIXES = {
|
|||||||
42: 'kovan.'
|
42: 'kovan.'
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getEtherscanLink(networkId, data, type) {
|
export function getEtherscanLink(networkId: 1 | 3 | 4 | 5 | 42 | any, data: string, type: 'transaction' | 'address') {
|
||||||
const prefix = `https://${ETHERSCAN_PREFIXES[networkId] || ETHERSCAN_PREFIXES[1]}etherscan.io`
|
const prefix = `https://${ETHERSCAN_PREFIXES[networkId] || ETHERSCAN_PREFIXES[1]}etherscan.io`
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
@ -21,7 +21,8 @@
|
|||||||
"allowSyntheticDefaultImports": true
|
"allowSyntheticDefaultImports": true
|
||||||
},
|
},
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"node_modules"
|
"node_modules",
|
||||||
|
"cypress"
|
||||||
],
|
],
|
||||||
"include": [
|
"include": [
|
||||||
"**/*.js",
|
"**/*.js",
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
entry: [
|
|
||||||
path.join(process.cwd(), 'app/app.tsx'), // or whatever the path of your root file is
|
|
||||||
]
|
|
||||||
module: {
|
|
||||||
rules:[{ test: /\.tsx?$/, loader: 'awesome-typescript-loader' }], // other loader configuration goes in the array
|
|
||||||
resolve: {extensions: ['.js', '.jsx', '.react.js', '.ts', '.tsx']}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user