Browse Source

Merge branch 'develop' into master

master
Thibaut 6 years ago
committed by GitHub
parent
commit
769a757871
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      .circleci/config.yml
  2. 1
      .eslintrc
  3. 8
      .gitignore
  4. 12
      README.md
  5. 4
      build/linux/arch/PKGBUILD
  6. 8
      package.json
  7. 4
      src/api/Ethereum.js
  8. 4
      src/api/Fees.js
  9. 6
      src/api/network.js
  10. 5
      src/bridge/EthereumJSBridge.js
  11. 3
      src/bridge/LibcoreBridge.js
  12. 4
      src/bridge/RippleJSBridge.js
  13. 3
      src/commands/getCurrentFirmware.js
  14. 6
      src/commands/getDeviceInfo.js
  15. 2
      src/commands/getIsGenuine.js
  16. 4
      src/commands/getLatestFirmwareForDevice.js
  17. 6
      src/commands/installApp.js
  18. 2
      src/commands/installMcu.js
  19. 2
      src/commands/installOsuFirmware.js
  20. 4
      src/commands/libcoreGetFees.js
  21. 4
      src/commands/libcoreHardReset.js
  22. 4
      src/commands/listAppVersions.js
  23. 5
      src/commands/listApps.js
  24. 5
      src/commands/listCategories.js
  25. 3
      src/commands/shouldFlashMcu.js
  26. 10
      src/commands/uninstallApp.js
  27. 16
      src/components/DashboardPage/EmptyState.js
  28. 3
      src/components/EnsureDeviceApp.js
  29. 2
      src/components/GenuineCheck.js
  30. 4
      src/components/IsUnlocked.js
  31. 6
      src/components/ManagerPage/AppSearchBar.js
  32. 22
      src/components/ManagerPage/AppsList.js
  33. 2
      src/components/ManagerPage/Dashboard.js
  34. 5
      src/components/ManagerPage/FirmwareUpdate.js
  35. 2
      src/components/ManagerPage/index.js
  36. 8
      src/components/Onboarding/OnboardingFooter.js
  37. 2
      src/components/Onboarding/steps/Analytics.js
  38. 4
      src/components/Onboarding/steps/Finish.js
  39. 2
      src/components/SettingsPage/DevModeButton.js
  40. 4
      src/components/SettingsPage/DisablePasswordModal.js
  41. 4
      src/components/SettingsPage/PasswordForm.js
  42. 4
      src/components/SettingsPage/PasswordModal.js
  43. 2
      src/components/SettingsPage/index.js
  44. 2
      src/components/TopBar/index.js
  45. 5
      src/components/modals/AccountSettingRenderBody.js
  46. 4
      src/components/modals/Disclaimer.js
  47. 3
      src/components/modals/Send/fields/RecipientField.js
  48. 4
      src/components/modals/UpdateFirmware/index.js
  49. 36
      src/config/errors.js
  50. 26
      src/helpers/apps/installApp.js
  51. 15
      src/helpers/apps/listAppVersions.js
  52. 3
      src/helpers/apps/listApps.js
  53. 5
      src/helpers/apps/listCategories.js
  54. 18
      src/helpers/apps/uninstallApp.js
  55. 3
      src/helpers/debugAppInfosForCurrency/btc.js
  56. 5
      src/helpers/devices/getCurrentFirmware.js
  57. 12
      src/helpers/devices/getDeviceInfo.js
  58. 6
      src/helpers/devices/getDeviceVersion.js
  59. 12
      src/helpers/devices/getIsGenuine.js
  60. 36
      src/helpers/devices/getLatestFirmwareForDevice.js
  61. 2
      src/helpers/devices/shouldFlashMcu.js
  62. 3
      src/helpers/firmware/getFirmwareInfo.js
  63. 7
      src/helpers/firmware/getNextMCU.js
  64. 15
      src/helpers/firmware/installFinalFirmware.js
  65. 11
      src/helpers/firmware/installMcu.js
  66. 12
      src/helpers/firmware/installOsuFirmware.js
  67. 4
      src/helpers/getAddressForCurrency/btc.js
  68. 5
      src/helpers/libcore.js
  69. 14
      src/helpers/socket.js
  70. 132
      src/helpers/types.js
  71. 64
      test-e2e/nav_to_settings.spec.js
  72. 249
      yarn.lock

4
.circleci/config.yml

@ -13,10 +13,10 @@ jobs:
- checkout
- restore_cache:
keys:
- v7-yarn-packages-{{ checksum "yarn.lock" }}
- v8-yarn-packages-{{ checksum "yarn.lock" }}
- run: yarn install
- save_cache:
key: v7-yarn-packages-{{ checksum "yarn.lock" }}
key: v8-yarn-packages-{{ checksum "yarn.lock" }}
paths:
- node_modules
- run: yarn lint

1
.eslintrc

@ -21,6 +21,7 @@
"jest": false,
"describe": false,
"beforeEach": false,
"afterEach": false,
"test": false,
"it": false,
"expect": false,

8
.gitignore

@ -10,11 +10,3 @@
/build/linux/arch/src
/build/linux/arch/*.tar.gz
/build/linux/arch/*.tar.xz
# TODO this should be in devs global gitignore
# it makes no sense to have it here
*.log
.DS_Store
.vscode
thumbs.db
jsconfig.json

12
README.md

@ -159,3 +159,15 @@ yarn reset-files
├── webpack : build configuration
└── yarn.lock
```
## Troubleshooting
#### The 'gyp==0.1' distribution was not found and is required by the application
You will need to install the python gyp module
```
pip install git+https://chromium.googlesource.com/external/gyp
```
see [stackoverflow/40025591](https://stackoverflow.com/questions/40025591/the-gyp-0-1-distribution-was-not-found)

4
build/linux/arch/PKGBUILD

@ -2,7 +2,7 @@
# shellcheck disable=SC2154,SC2034,SC2164
pkgname=ledger-live
pkgver=1.0.7
pkgver=1.1.0
pkgrel=1
pkgdesc="Open source companion app for your Ledger devices"
arch=('x86_64')
@ -15,7 +15,7 @@ changelog=
source=("https://github.com/LedgerHQ/ledger-live-desktop/archive/v${pkgver}.tar.gz"
"ledger-live.desktop")
md5sums=('d60d772a03c0a1c59df07f93b0268a4c'
md5sums=('5dc6bd1e8d6dedcfe760d1c75565aad8'
'52705147909a0a988907a23a71199092')
# TODO sign with ledger pgp
validpgpkeys=()

8
package.json

@ -13,10 +13,11 @@
"dist": "bash ./scripts/dist.sh",
"dist:dir": "bash ./scripts/dist-dir.sh",
"compile": "bash ./scripts/compile.sh",
"lint": "eslint src webpack .storybook",
"lint": "eslint src webpack .storybook test-e2e",
"flow": "flow",
"test": "jest",
"prettier": "prettier --write \"{src,webpack,.storybook}/**/*.{js,json}\"",
"test": "jest src",
"test-e2e": "jest test-e2e",
"prettier": "prettier --write \"{src,webpack,.storybook,test-e2e}/**/*.{js,json}\"",
"ci": "yarn lint && yarn flow && yarn prettier && yarn test",
"storybook": "NODE_ENV=development STORYBOOK_ENV=1 start-storybook -s ./static -p 4444",
"publish-storybook": "bash ./scripts/legacy/publish-storybook.sh",
@ -158,6 +159,7 @@
"prettier": "^1.13.5",
"react-hot-loader": "^4.3.2",
"react-test-renderer": "^16.4.1",
"spectron": "^3.8.0",
"webpack": "^4.6.0",
"webpack-bundle-analyzer": "^2.11.1",
"webpack-cli": "^2.0.14",

4
src/api/Ethereum.js

@ -1,12 +1,10 @@
// @flow
import type { CryptoCurrency } from '@ledgerhq/live-common/lib/types'
import { BigNumber } from 'bignumber.js'
import { createCustomErrorClass } from 'helpers/errors'
import { LedgerAPINotAvailable } from 'config/errors'
import network from './network'
import { blockchainBaseURL } from './Ledger'
export const LedgerAPINotAvailable = createCustomErrorClass('LedgerAPINotAvailable')
export type Block = { height: number } // TODO more fields actually
export type Tx = {
hash: string,

4
src/api/Fees.js

@ -2,12 +2,10 @@
import invariant from 'invariant'
import LRU from 'lru-cache'
import type { Currency } from '@ledgerhq/live-common/lib/types'
import { createCustomErrorClass } from 'helpers/errors'
import { FeeEstimationFailed } from 'config/errors'
import { blockchainBaseURL } from './Ledger'
import network from './network'
const FeeEstimationFailed = createCustomErrorClass('FeeEstimationFailed')
export type Fees = {
[_: string]: number,
}

6
src/api/network.js

@ -3,13 +3,9 @@ import axios from 'axios'
import { GET_CALLS_RETRY, GET_CALLS_TIMEOUT } from 'config/constants'
import { retry } from 'helpers/promise'
import logger from 'logger'
import { createCustomErrorClass } from 'helpers/errors'
import { LedgerAPIErrorWithMessage, LedgerAPIError, NetworkDown } from 'config/errors'
import anonymizer from 'helpers/anonymizer'
export const LedgerAPIErrorWithMessage = createCustomErrorClass('LedgerAPIErrorWithMessage')
export const LedgerAPIError = createCustomErrorClass('LedgerAPIError')
export const NetworkDown = createCustomErrorClass('NetworkDown')
const userFriendlyError = <A>(p: Promise<A>, { url, method, startTime }): Promise<A> =>
p.catch(error => {
let errorToThrow

5
src/bridge/EthereumJSBridge.js

@ -16,12 +16,9 @@ import { getDerivations } from 'helpers/derivations'
import getAddressCommand from 'commands/getAddress'
import signTransactionCommand from 'commands/signTransaction'
import { getAccountPlaceholderName, getNewAccountPlaceholderName } from 'helpers/accountName'
import { createCustomErrorClass } from 'helpers/errors'
import { ETHAddressNonEIP } from 'config/errors'
import { NotEnoughBalance, ETHAddressNonEIP } from 'config/errors'
import type { EditProps, WalletBridge } from './types'
const NotEnoughBalance = createCustomErrorClass('NotEnoughBalance')
type Transaction = {
recipient: string,
amount: BigNumber,

3
src/bridge/LibcoreBridge.js

@ -11,11 +11,10 @@ import libcoreSyncAccount from 'commands/libcoreSyncAccount'
import libcoreSignAndBroadcast from 'commands/libcoreSignAndBroadcast'
import libcoreGetFees from 'commands/libcoreGetFees'
import libcoreValidAddress from 'commands/libcoreValidAddress'
import { createCustomErrorClass } from 'helpers/errors'
import { NotEnoughBalance } from 'config/errors'
import type { WalletBridge, EditProps } from './types'
const NOT_ENOUGH_FUNDS = 52
const NotEnoughBalance = createCustomErrorClass('NotEnoughBalance')
const notImplemented = new Error('LibcoreBridge: not implemented')

4
src/bridge/RippleJSBridge.js

@ -20,11 +20,9 @@ import {
import FeesRippleKind from 'components/FeesField/RippleKind'
import AdvancedOptionsRippleKind from 'components/AdvancedOptions/RippleKind'
import { getAccountPlaceholderName, getNewAccountPlaceholderName } from 'helpers/accountName'
import { createCustomErrorClass } from 'helpers/errors'
import { NotEnoughBalance } from 'config/errors'
import type { WalletBridge, EditProps } from './types'
const NotEnoughBalance = createCustomErrorClass('NotEnoughBalance')
type Transaction = {
amount: BigNumber,
recipient: string,

3
src/commands/getCurrentFirmware.js

@ -4,6 +4,7 @@ import { createCommand, Command } from 'helpers/ipc'
import { fromPromise } from 'rxjs/observable/fromPromise'
import getCurrentFirmware from 'helpers/devices/getCurrentFirmware'
import type { FinalFirmware } from 'helpers/types'
type Input = {
deviceId: string | number,
@ -11,7 +12,7 @@ type Input = {
provider: number,
}
type Result = *
type Result = FinalFirmware
const cmd: Command<Input, Result> = createCommand('getCurrentFirmware', data =>
fromPromise(getCurrentFirmware(data)),

6
src/commands/getDeviceInfo.js

@ -5,13 +5,15 @@ import { fromPromise } from 'rxjs/observable/fromPromise'
import { withDevice } from 'helpers/deviceAccess'
import getDeviceInfo from 'helpers/devices/getDeviceInfo'
import type { DeviceInfo } from 'helpers/devices/getDeviceInfo'
import type { DeviceInfo } from 'helpers/types'
type Input = {
devicePath: string,
}
const cmd: Command<Input, DeviceInfo> = createCommand('getDeviceInfo', ({ devicePath }) =>
type Result = DeviceInfo
const cmd: Command<Input, Result> = createCommand('getDeviceInfo', ({ devicePath }) =>
fromPromise(withDevice(devicePath)(transport => getDeviceInfo(transport))),
)

2
src/commands/getIsGenuine.js

@ -2,7 +2,7 @@
import { createCommand, Command } from 'helpers/ipc'
import { fromPromise } from 'rxjs/observable/fromPromise'
import type { DeviceInfo } from 'helpers/devices/getDeviceInfo'
import type { DeviceInfo } from 'helpers/types'
import getIsGenuine from 'helpers/devices/getIsGenuine'
import { withDevice } from 'helpers/deviceAccess'

4
src/commands/getLatestFirmwareForDevice.js

@ -2,11 +2,11 @@
import { createCommand, Command } from 'helpers/ipc'
import { fromPromise } from 'rxjs/observable/fromPromise'
import type { DeviceInfo } from 'helpers/devices/getDeviceInfo'
import type { DeviceInfo, OsuFirmware } from 'helpers/types'
import getLatestFirmwareForDevice from '../helpers/devices/getLatestFirmwareForDevice'
type Result = *
type Result = ?(OsuFirmware & { shouldFlashMcu: boolean })
const cmd: Command<DeviceInfo, Result> = createCommand('getLatestFirmwareForDevice', data =>
fromPromise(getLatestFirmwareForDevice(data)),

6
src/commands/installApp.js

@ -6,15 +6,15 @@ import { fromPromise } from 'rxjs/observable/fromPromise'
import { withDevice } from 'helpers/deviceAccess'
import installApp from 'helpers/apps/installApp'
import type { LedgerScriptParams } from 'helpers/types'
import type { ApplicationVersion } from 'helpers/types'
type Input = {
app: LedgerScriptParams,
app: ApplicationVersion,
devicePath: string,
targetId: string | number,
}
type Result = *
type Result = void
const cmd: Command<Input, Result> = createCommand(
'installApp',

2
src/commands/installMcu.js

@ -10,7 +10,7 @@ type Input = {
devicePath: string,
}
type Result = *
type Result = void
const cmd: Command<Input, Result> = createCommand('installMcu', ({ devicePath }) =>
fromPromise(withDevice(devicePath)(transport => installMcu(transport))),

2
src/commands/installOsuFirmware.js

@ -14,7 +14,7 @@ type Input = {
firmware: Firmware,
}
type Result = *
type Result = { success: boolean }
const cmd: Command<Input, Result> = createCommand(
'installOsuFirmware',

4
src/commands/libcoreGetFees.js

@ -6,9 +6,7 @@ import withLibcore from 'helpers/withLibcore'
import { createCommand, Command } from 'helpers/ipc'
import * as accountIdHelper from 'helpers/accountId'
import { isValidAddress, libcoreAmountToBigNumber, bigNumberToLibcoreAmount } from 'helpers/libcore'
import { createCustomErrorClass } from 'helpers/errors'
const InvalidAddress = createCustomErrorClass('InvalidAddress')
import { InvalidAddress } from 'config/errors'
type BitcoinLikeTransaction = {
// TODO we rename this Transaction concept into transactionInput

4
src/commands/libcoreHardReset.js

@ -3,9 +3,7 @@
import { createCommand } from 'helpers/ipc'
import { fromPromise } from 'rxjs/observable/fromPromise'
import withLibcore from 'helpers/withLibcore'
import { createCustomErrorClass } from 'helpers/errors'
const HardResetFail = createCustomErrorClass('HardResetFail')
import { HardResetFail } from 'config/errors'
const cmd = createCommand('libcoreHardReset', () =>
fromPromise(

4
src/commands/listAppVersions.js

@ -2,11 +2,11 @@
import { createCommand, Command } from 'helpers/ipc'
import { fromPromise } from 'rxjs/observable/fromPromise'
import type { DeviceInfo } from 'helpers/devices/getDeviceInfo'
import type { DeviceInfo, ApplicationVersion } from 'helpers/types'
import listAppVersions from 'helpers/apps/listAppVersions'
type Result = *
type Result = Array<ApplicationVersion>
const cmd: Command<DeviceInfo, Result> = createCommand('listAppVersions', deviceInfo =>
fromPromise(listAppVersions(deviceInfo)),

5
src/commands/listApps.js

@ -4,10 +4,11 @@ import { createCommand, Command } from 'helpers/ipc'
import { fromPromise } from 'rxjs/observable/fromPromise'
import listApps from 'helpers/apps/listApps'
import type { Application } from 'helpers/types'
type Input = {}
type Input = void
type Result = *
type Result = Array<Application>
const cmd: Command<Input, Result> = createCommand('listApps', () => fromPromise(listApps()))

5
src/commands/listCategories.js

@ -4,10 +4,11 @@ import { createCommand, Command } from 'helpers/ipc'
import { fromPromise } from 'rxjs/observable/fromPromise'
import listCategories from 'helpers/apps/listCategories'
import type { Category } from 'helpers/types'
type Input = {}
type Input = void
type Result = *
type Result = Array<Category>
const cmd: Command<Input, Result> = createCommand('listCategories', () =>
fromPromise(listCategories()),

3
src/commands/shouldFlashMcu.js

@ -2,9 +2,10 @@
import { createCommand, Command } from 'helpers/ipc'
import { fromPromise } from 'rxjs/observable/fromPromise'
import type { DeviceInfo } from 'helpers/devices/getDeviceInfo'
import shouldFlashMcu from 'helpers/devices/shouldFlashMcu'
import type { DeviceInfo } from 'helpers/types'
type Result = boolean
const cmd: Command<DeviceInfo, Result> = createCommand('shouldFlashMcu', data =>

10
src/commands/uninstallApp.js

@ -6,20 +6,20 @@ import { withDevice } from 'helpers/deviceAccess'
import uninstallApp from 'helpers/apps/uninstallApp'
import type { LedgerScriptParams } from 'helpers/types'
import type { ApplicationVersion } from 'helpers/types'
type Input = {
app: LedgerScriptParams,
app: ApplicationVersion,
devicePath: string,
targetId: string | number,
}
type Result = *
type Result = void
const cmd: Command<Input, Result> = createCommand(
'uninstallApp',
({ devicePath, targetId, ...rest }) =>
fromPromise(withDevice(devicePath)(transport => uninstallApp(transport, targetId, rest))),
({ devicePath, targetId, ...app }) =>
fromPromise(withDevice(devicePath)(transport => uninstallApp(transport, targetId, app))),
)
export default cmd

16
src/components/DashboardPage/EmptyState.js

@ -45,15 +45,25 @@ class EmptyState extends PureComponent<Props, *> {
height="157"
/>
<Box mt={5} alignItems="center">
<Title>{t('app:emptyState.dashboard.title')}</Title>
<Title data-e2e="dashboard_empty_title">{t('app:emptyState.dashboard.title')}</Title>
<Description mt={3} style={{ maxWidth: 600 }}>
{t('app:emptyState.dashboard.desc')}
</Description>
<Box mt={5} horizontal style={{ width: 300 }} flow={3} justify="center">
<Button primary style={{ minWidth: 120 }} onClick={this.handleInstallApp}>
<Button
primary
style={{ minWidth: 120 }}
onClick={this.handleInstallApp}
data-e2e="dashboard_empty_OpenManager"
>
{t('app:emptyState.dashboard.buttons.installApp')}
</Button>
<Button outline style={{ minWidth: 120 }} onClick={() => openModal(MODAL_ADD_ACCOUNTS)}>
<Button
outline
style={{ minWidth: 120 }}
onClick={() => openModal(MODAL_ADD_ACCOUNTS)}
data-e2e="dashboard_empty_AddAccounts"
>
{t('app:emptyState.dashboard.buttons.addAccount')}
</Button>
</Box>

3
src/components/EnsureDeviceApp.js

@ -12,7 +12,6 @@ import getAddress from 'commands/getAddress'
import { createCancelablePolling } from 'helpers/promise'
import { standardDerivation } from 'helpers/derivations'
import { isSegwitPath } from 'helpers/bip32'
import { BtcUnmatchedApp } from 'helpers/getAddressForCurrency/btc'
import DeviceInteraction from 'components/DeviceInteraction'
import Text from 'components/base/Text'
@ -21,7 +20,7 @@ import IconUsb from 'icons/Usb'
import type { Device } from 'types/common'
import { WrongDeviceForAccount, CantOpenDevice } from 'config/errors'
import { WrongDeviceForAccount, CantOpenDevice, BtcUnmatchedApp } from 'config/errors'
import { getCurrentDevice } from 'reducers/devices'
const usbIcon = <IconUsb size={16} />

2
src/components/GenuineCheck.js

@ -9,7 +9,7 @@ import { delay, createCancelablePolling } from 'helpers/promise'
import logger from 'logger'
import type { T, Device } from 'types/common'
import type { DeviceInfo } from 'helpers/devices/getDeviceInfo'
import type { DeviceInfo } from 'helpers/types'
import { GENUINE_TIMEOUT, DEVICE_INFOS_TIMEOUT, GENUINE_CACHE_DELAY } from 'config/constants'

4
src/components/IsUnlocked.js

@ -17,7 +17,7 @@ import hardReset from 'helpers/hardReset'
import { fetchAccounts } from 'actions/accounts'
import { isLocked, unlock } from 'reducers/application'
import { createCustomErrorClass } from 'helpers/errors'
import { PasswordIncorrectError } from 'config/errors'
import Box from 'components/base/Box'
import InputPassword from 'components/base/InputPassword'
@ -26,8 +26,6 @@ import IconArrowRight from 'icons/ArrowRight'
import Button from './base/Button/index'
import ConfirmModal from './base/Modal/ConfirmModal'
const PasswordIncorrectError = createCustomErrorClass('PasswordIncorrect')
type InputValue = {
password: string,
}

6
src/components/ManagerPage/AppSearchBar.js

@ -3,7 +3,7 @@
import React, { PureComponent, Fragment } from 'react'
import styled from 'styled-components'
import type { LedgerScriptParams } from 'helpers/types'
import type { ApplicationVersion } from 'helpers/types'
import Box from 'components/base/Box'
import Space from 'components/base/Space'
@ -23,8 +23,8 @@ const CrossContainer = styled(Box).attrs({
`
type Props = {
list: Array<LedgerScriptParams>,
children: (list: Array<LedgerScriptParams>) => React$Node,
list: Array<ApplicationVersion>,
children: (list: Array<ApplicationVersion>) => React$Node,
}
type State = {

22
src/components/ManagerPage/AppsList.js

@ -7,8 +7,7 @@ import { translate } from 'react-i18next'
import { connect } from 'react-redux'
import { compose } from 'redux'
import type { Device, T } from 'types/common'
import type { LedgerScriptParams } from 'helpers/types'
import type { DeviceInfo } from 'helpers/devices/getDeviceInfo'
import type { Application, ApplicationVersion, DeviceInfo } from 'helpers/types'
import { developerModeSelector } from 'reducers/settings'
import listApps from 'commands/listApps'
@ -66,7 +65,7 @@ type Props = {
type State = {
status: Status,
error: ?Error,
filteredAppVersionsList: LedgerScriptParams[],
filteredAppVersionsList: Array<ApplicationVersion>,
appsLoaded: boolean,
app: string,
mode: Mode,
@ -102,9 +101,14 @@ class AppsList extends PureComponent<Props, State> {
filterAppVersions = (applicationsList, compatibleAppVersionsList) => {
if (!this.props.isDevMode) {
return compatibleAppVersionsList.filter(
version => applicationsList.find(e => e.id === version.app).category !== 2,
)
return compatibleAppVersionsList.filter(version => {
const app = applicationsList.find(e => e.id === version.app)
if (app) {
return app.category !== 2
}
return false
})
}
return compatibleAppVersionsList
}
@ -112,7 +116,7 @@ class AppsList extends PureComponent<Props, State> {
async fetchAppList() {
try {
const { deviceInfo } = this.props
const applicationsList = await listApps.send({}).toPromise()
const applicationsList: Array<Application> = await listApps.send().toPromise()
const compatibleAppVersionsList = await listAppVersions.send(deviceInfo).toPromise()
const filteredAppVersionsList = this.filterAppVersions(
applicationsList,
@ -131,7 +135,7 @@ class AppsList extends PureComponent<Props, State> {
}
}
handleInstallApp = (app: LedgerScriptParams) => async () => {
handleInstallApp = (app: ApplicationVersion) => async () => {
this.setState({ status: 'busy', app: app.name, mode: 'installing' })
try {
const {
@ -146,7 +150,7 @@ class AppsList extends PureComponent<Props, State> {
}
}
handleUninstallApp = (app: LedgerScriptParams) => async () => {
handleUninstallApp = (app: ApplicationVersion) => async () => {
this.setState({ status: 'busy', app: app.name, mode: 'uninstalling' })
try {
const {

2
src/components/ManagerPage/Dashboard.js

@ -4,7 +4,7 @@ import { translate } from 'react-i18next'
import styled from 'styled-components'
import type { T, Device } from 'types/common'
import type { DeviceInfo } from 'helpers/devices/getDeviceInfo'
import type { DeviceInfo } from 'helpers/types'
import Box from 'components/base/Box'
import Text from 'components/base/Text'

5
src/components/ManagerPage/FirmwareUpdate.js

@ -9,7 +9,7 @@ import invariant from 'invariant'
import type { Device, T } from 'types/common'
import type { LedgerScriptParams } from 'helpers/types'
import type { DeviceInfo, OsuFirmware } from 'helpers/types'
import type { StepId } from 'components/modals/UpdateFirmware'
import getLatestFirmwareForDevice from 'commands/getLatestFirmwareForDevice'
@ -19,7 +19,6 @@ import installFinalFirmware from 'commands/installFinalFirmware'
import installMcu from 'commands/installMcu'
import DisclaimerModal from 'components/modals/UpdateFirmware/Disclaimer'
import UpdateModal from 'components/modals/UpdateFirmware'
import type { DeviceInfo } from 'helpers/devices/getDeviceInfo'
import Tooltip from 'components/base/Tooltip'
import Box, { Card } from 'components/base/Box'
@ -43,7 +42,7 @@ type Props = {
}
type State = {
latestFirmware: ?LedgerScriptParams & ?{ shouldFlashMcu: boolean },
latestFirmware: ?OsuFirmware & ?{ shouldFlashMcu: boolean },
modal: ModalStatus,
stepId: ?StepId,
shouldFlash: boolean,

2
src/components/ManagerPage/index.js

@ -6,7 +6,7 @@ import { openURL } from 'helpers/linking'
import { urls } from 'config/urls'
import type { Device } from 'types/common'
import type { DeviceInfo } from 'helpers/devices/getDeviceInfo'
import type { DeviceInfo } from 'helpers/types'
import Dashboard from './Dashboard'

8
src/components/Onboarding/OnboardingFooter.js

@ -26,7 +26,13 @@ const OnboardingFooter = ({
<Button outlineGrey onClick={() => prevStep()}>
{t('app:common.back')}
</Button>
<Button disabled={isContinueDisabled} primary onClick={() => nextStep()} ml="auto">
<Button
data-e2e="continue_button"
disabled={isContinueDisabled}
primary
onClick={() => nextStep()}
ml="auto"
>
{t('app:common.continue')}
</Button>
</OnboardingFooterWrapper>

2
src/components/Onboarding/steps/Analytics.js

@ -70,7 +70,7 @@ class Analytics extends PureComponent<StepProps, State> {
deviceType={onboarding.isLedgerNano ? 'Nano S' : 'Blue'}
/>
<StepContainerInner>
<Title>{t('onboarding:analytics.title')}</Title>
<Title data-e2e="onboarding_title">{t('onboarding:analytics.title')}</Title>
<Description>{t('onboarding:analytics.desc')}</Description>
<Box mt={5}>
<Container>

4
src/components/Onboarding/steps/Finish.js

@ -103,11 +103,11 @@ export default class Finish extends Component<StepProps, *> {
</Box>
<Box pt={5} align="center">
<Title>{t('onboarding:finish.title')}</Title>
<Title data-e2e="finish_title">{t('onboarding:finish.title')}</Title>
<Description>{t('onboarding:finish.desc')}</Description>
</Box>
<Box p={5}>
<Button primary onClick={() => finish()}>
<Button primary onClick={() => finish()} data-e2e="continue_button">
{t('onboarding:finish.openAppButton')}
</Button>
</Box>

2
src/components/SettingsPage/DevModeButton.js

@ -27,7 +27,7 @@ class DevModeButton extends PureComponent<Props> {
return (
<Fragment>
<Track onUpdate event={developerMode ? 'DevModeEnabled' : 'DevModeDisabled'} />
<Switch isChecked={developerMode} onChange={setDeveloperMode} />
<Switch isChecked={developerMode} onChange={setDeveloperMode} data-e2e="devMode_button" />
</Fragment>
)
}

4
src/components/SettingsPage/DisablePasswordModal.js

@ -1,7 +1,7 @@
// @flow
import React, { PureComponent } from 'react'
import { createCustomErrorClass } from 'helpers/errors'
import { PasswordIncorrectError } from 'config/errors'
import db from 'helpers/db'
import Box from 'components/base/Box'
@ -12,8 +12,6 @@ import { Modal, ModalContent, ModalBody, ModalTitle, ModalFooter } from 'compone
import type { T } from 'types/common'
const PasswordIncorrectError = createCustomErrorClass('PasswordIncorrect')
type Props = {
t: T,
onClose: Function,

4
src/components/SettingsPage/PasswordForm.js

@ -6,12 +6,10 @@ import Box from 'components/base/Box'
import InputPassword from 'components/base/InputPassword'
import Label from 'components/base/Label'
import { createCustomErrorClass } from 'helpers/errors'
import { PasswordsDontMatchError } from 'config/errors'
import type { T } from 'types/common'
const PasswordsDontMatchError = createCustomErrorClass('PasswordsDontMatch')
type Props = {
t: T,
hasPassword: boolean,

4
src/components/SettingsPage/PasswordModal.js

@ -5,15 +5,13 @@ import React, { PureComponent } from 'react'
import type { T } from 'types/common'
import db from 'helpers/db'
import { createCustomErrorClass } from 'helpers/errors'
import { PasswordIncorrectError } from 'config/errors'
import Box from 'components/base/Box'
import Button from 'components/base/Button'
import { Modal, ModalContent, ModalBody, ModalTitle, ModalFooter } from 'components/base/Modal'
import PasswordForm from './PasswordForm'
const PasswordIncorrectError = createCustomErrorClass('PasswordIncorrect')
type Props = {
t: T,
onClose: () => void,

2
src/components/SettingsPage/index.js

@ -106,7 +106,7 @@ class SettingsPage extends PureComponent<Props, State> {
return (
<Box pb={4} selectable>
<Box ff="Museo Sans|Regular" color="dark" fontSize={7} mb={5}>
<Box ff="Museo Sans|Regular" color="dark" fontSize={7} mb={5} data-e2e="settings_title">
{t('app:settings.title')}
</Box>
<Pills mb={4} items={items} activeKey={tab.key} onChange={this.handleChangeTab} />

2
src/components/TopBar/index.js

@ -109,7 +109,7 @@ class TopBar extends PureComponent<Props> {
</Box>
</Fragment>
)}
<Tooltip render={() => t('app:settings.title')}>
<Tooltip render={() => t('app:settings.title')} data-e2e="setting_button">
<ItemContainer isInteractive onClick={this.navigateToSettings}>
<IconSettings size={16} />
</ItemContainer>

5
src/components/modals/AccountSettingRenderBody.js

@ -17,7 +17,7 @@ import { setDataModal } from 'reducers/modals'
import { getBridgeForCurrency } from 'bridge'
import { createCustomErrorClass } from 'helpers/errors'
import { AccountNameRequiredError, EnpointConfigError } from 'config/errors'
import TrackPage from 'analytics/TrackPage'
import Spoiler from 'components/base/Spoiler'
@ -36,9 +36,6 @@ import {
ConfirmModal,
} from 'components/base/Modal'
const AccountNameRequiredError = createCustomErrorClass('AccountNameRequired')
const EnpointConfigError = createCustomErrorClass('EnpointConfig')
type State = {
accountName: ?string,
accountUnit: ?Unit,

4
src/components/modals/Disclaimer.js

@ -25,7 +25,7 @@ class DisclaimerModal extends PureComponent<Props> {
name={MODAL_DISCLAIMER}
render={({ onClose }) => (
<ModalBody onClose={onClose}>
<ModalTitle>{t('app:disclaimerModal.title')}</ModalTitle>
<ModalTitle data-e2e="disclaimer_title">{t('app:disclaimerModal.title')}</ModalTitle>
<ModalContent flow={4} ff="Open Sans|Regular" fontSize={4} color="smoke">
<Box align="center" mt={4} pb={4}>
<HandShield size={55} />
@ -34,7 +34,7 @@ class DisclaimerModal extends PureComponent<Props> {
<p>{t('app:disclaimerModal.desc_2')}</p>
</ModalContent>
<ModalFooter horizontal justifyContent="flex-end">
<Button onClick={onClose} primary>
<Button data-e2e="continue_button" onClick={onClose} primary>
{t('app:disclaimerModal.cta')}
</Button>
</ModalFooter>

3
src/components/modals/Send/fields/RecipientField.js

@ -11,6 +11,7 @@ import RecipientAddress from 'components/RecipientAddress'
import { track } from 'analytics/segment'
import { createCustomErrorClass } from 'helpers/errors'
import { CantScanQRCode } from 'config/errors'
import { InvalidAddress } from 'config/errors'
type Props<Transaction> = {
t: T,
@ -21,8 +22,6 @@ type Props<Transaction> = {
autoFocus?: boolean,
}
const InvalidAddress = createCustomErrorClass('InvalidAddress')
class RecipientField<Transaction> extends Component<
Props<Transaction>,
{ isValid: boolean, warning: ?Error, QRCodeRefusedReason: ?Error },

4
src/components/modals/UpdateFirmware/index.js

@ -10,7 +10,7 @@ import SyncSkipUnderPriority from 'components/SyncSkipUnderPriority'
import type { StepProps as DefaultStepProps, Step } from 'components/base/Stepper'
import type { ModalStatus } from 'components/ManagerPage/FirmwareUpdate'
import type { LedgerScriptParams } from 'helpers/types'
import type { OsuFirmware } from 'helpers/types'
import { FreezeDeviceChangeEvents } from '../../ManagerPage/HookDeviceChange'
import StepFullFirmwareInstall from './steps/01-step-install-full-firmware'
@ -56,7 +56,7 @@ const createSteps = ({ t, shouldFlashMcu }: { t: T, shouldFlashMcu: boolean }):
return steps
}
export type Firmware = LedgerScriptParams & { shouldFlashMcu: boolean }
export type Firmware = OsuFirmware & { shouldFlashMcu: boolean }
export type StepProps = DefaultStepProps & {
firmware: Firmware,

36
src/config/errors.js

@ -4,15 +4,41 @@
import { createCustomErrorClass } from 'helpers/errors'
export const DisconnectedDevice = createCustomErrorClass('DisconnectedDevice')
export const UserRefusedOnDevice = createCustomErrorClass('UserRefusedOnDevice') // TODO rename because it's just for transaction refusal
export const AccountNameRequiredError = createCustomErrorClass('AccountNameRequired')
export const BtcUnmatchedApp = createCustomErrorClass('BtcUnmatchedApp')
export const CantOpenDevice = createCustomErrorClass('CantOpenDevice')
export const DeviceAppVerifyNotSupported = createCustomErrorClass('DeviceAppVerifyNotSupported')
export const UserRefusedAddress = createCustomErrorClass('UserRefusedAddress')
export const WrongDeviceForAccount = createCustomErrorClass('WrongDeviceForAccount')
export const DeviceNotGenuineError = createCustomErrorClass('DeviceNotGenuine')
export const DeviceGenuineSocketEarlyClose = createCustomErrorClass('DeviceGenuineSocketEarlyClose')
export const DeviceNotGenuineError = createCustomErrorClass('DeviceNotGenuine')
export const DeviceSocketFail = createCustomErrorClass('DeviceSocketFail')
export const DeviceSocketNoBulkStatus = createCustomErrorClass('DeviceSocketNoBulkStatus')
export const DeviceSocketNoHandler = createCustomErrorClass('DeviceSocketNoHandler')
export const DisconnectedDevice = createCustomErrorClass('DisconnectedDevice')
export const EnpointConfigError = createCustomErrorClass('EnpointConfig')
export const FeeEstimationFailed = createCustomErrorClass('FeeEstimationFailed')
export const HardResetFail = createCustomErrorClass('HardResetFail')
export const InvalidAddress = createCustomErrorClass('InvalidAddress')
export const LatestMCUInstalledError = createCustomErrorClass('LatestMCUInstalledError')
export const LedgerAPIError = createCustomErrorClass('LedgerAPIError')
export const LedgerAPIErrorWithMessage = createCustomErrorClass('LedgerAPIErrorWithMessage')
export const LedgerAPINotAvailable = createCustomErrorClass('LedgerAPINotAvailable')
export const ManagerAppAlreadyInstalledError = createCustomErrorClass('ManagerAppAlreadyInstalled')
export const ManagerAppRelyOnBTCError = createCustomErrorClass('ManagerAppRelyOnBTC')
export const ManagerDeviceLockedError = createCustomErrorClass('ManagerDeviceLocked')
export const ManagerNotEnoughSpaceError = createCustomErrorClass('ManagerNotEnoughSpace')
export const ManagerUninstallBTCDep = createCustomErrorClass('ManagerUninstallBTCDep')
export const NetworkDown = createCustomErrorClass('NetworkDown')
export const NoAddressesFound = createCustomErrorClass('NoAddressesFound')
export const NotEnoughBalance = createCustomErrorClass('NotEnoughBalance')
export const PasswordsDontMatchError = createCustomErrorClass('PasswordsDontMatch')
export const PasswordIncorrectError = createCustomErrorClass('PasswordIncorrect')
export const TimeoutTagged = createCustomErrorClass('TimeoutTagged')
export const UserRefusedAddress = createCustomErrorClass('UserRefusedAddress')
export const UserRefusedFirmwareUpdate = createCustomErrorClass('UserRefusedFirmwareUpdate')
export const UserRefusedOnDevice = createCustomErrorClass('UserRefusedOnDevice') // TODO rename because it's just for transaction refusal
export const WebsocketConnectionError = createCustomErrorClass('WebsocketConnectionError')
export const WebsocketConnectionFailed = createCustomErrorClass('WebsocketConnectionFailed')
export const WrongDeviceForAccount = createCustomErrorClass('WrongDeviceForAccount')
export const ETHAddressNonEIP = createCustomErrorClass('ETHAddressNonEIP')
export const CantScanQRCode = createCustomErrorClass('CantScanQRCode')

26
src/helpers/apps/installApp.js

@ -1,18 +1,17 @@
// @flow
import qs from 'qs'
import type Transport from '@ledgerhq/hw-transport'
import { BASE_SOCKET_URL } from 'config/constants'
import { createDeviceSocket } from 'helpers/socket'
import type { LedgerScriptParams } from 'helpers/types'
import type { ApplicationVersion } from 'helpers/types'
import { WS_INSTALL } from 'helpers/urls'
import { createCustomErrorClass } from 'helpers/errors'
const ManagerNotEnoughSpaceError = createCustomErrorClass('ManagerNotEnoughSpace')
const ManagerDeviceLockedError = createCustomErrorClass('ManagerDeviceLocked')
const ManagerAppAlreadyInstalledError = createCustomErrorClass('ManagerAppAlreadyInstalled')
const ManagerAppRelyOnBTCError = createCustomErrorClass('ManagerAppRelyOnBTC')
import {
ManagerNotEnoughSpaceError,
ManagerDeviceLockedError,
ManagerAppAlreadyInstalledError,
ManagerAppRelyOnBTCError,
} from 'config/errors'
function remapError(promise) {
return promise.catch((e: Error) => {
@ -37,8 +36,8 @@ function remapError(promise) {
export default async function installApp(
transport: Transport<*>,
targetId: string | number,
{ app }: { app: LedgerScriptParams },
): Promise<*> {
{ app }: { app: ApplicationVersion },
): Promise<void> {
const params = {
targetId,
perso: app.perso,
@ -47,6 +46,7 @@ export default async function installApp(
firmwareKey: app.firmware_key,
hash: app.hash,
}
const url = `${BASE_SOCKET_URL}/install?${qs.stringify(params)}`
return remapError(createDeviceSocket(transport, url).toPromise())
const url = WS_INSTALL(params)
await remapError(createDeviceSocket(transport, url).toPromise())
}

15
src/helpers/apps/listAppVersions.js

@ -1,14 +1,19 @@
// @flow
import network from 'api/network'
import type { DeviceInfo } from 'helpers/devices/getDeviceInfo'
import type { DeviceInfo, DeviceVersion, FinalFirmware, ApplicationVersion } from 'helpers/types'
import { APPLICATIONS_BY_DEVICE } from 'helpers/urls'
import getDeviceVersion from 'helpers/devices/getDeviceVersion'
import getCurrentFirmware from 'helpers/devices/getCurrentFirmware'
export default async (deviceInfo: DeviceInfo) => {
const deviceData = await getDeviceVersion(deviceInfo.targetId, deviceInfo.providerId)
const firmwareData = await getCurrentFirmware({
type NetworkResponse = { data: { application_versions: Array<ApplicationVersion> } }
export default async (deviceInfo: DeviceInfo): Promise<Array<ApplicationVersion>> => {
const deviceData: DeviceVersion = await getDeviceVersion(
deviceInfo.targetId,
deviceInfo.providerId,
)
const firmwareData: FinalFirmware = await getCurrentFirmware({
deviceId: deviceData.id,
fullVersion: deviceInfo.fullVersion,
provider: deviceInfo.providerId,
@ -20,6 +25,6 @@ export default async (deviceInfo: DeviceInfo) => {
}
const {
data: { application_versions },
} = await network({ method: 'POST', url: APPLICATIONS_BY_DEVICE, data: params })
}: NetworkResponse = await network({ method: 'POST', url: APPLICATIONS_BY_DEVICE, data: params })
return application_versions.length > 0 ? application_versions : []
}

3
src/helpers/apps/listApps.js

@ -2,8 +2,9 @@
import network from 'api/network'
import { GET_APPLICATIONS } from 'helpers/urls'
import type { Application } from 'helpers/types'
export default async () => {
export default async (): Promise<Array<Application>> => {
const { data } = await network({ method: 'GET', url: GET_APPLICATIONS })
return data.length > 0 ? data : []
}

5
src/helpers/apps/listCategories.js

@ -2,8 +2,9 @@
import network from 'api/network'
import { GET_CATEGORIES } from 'helpers/urls'
import type { Category } from 'helpers/types'
export default async () => {
const { data } = await network({ method: 'GET', url: GET_CATEGORIES })
export default async (): Promise<Array<Category>> => {
const { data }: { data: Array<Category> } = await network({ method: 'GET', url: GET_CATEGORIES })
return data.length > 0 ? data : []
}

18
src/helpers/apps/uninstallApp.js

@ -1,15 +1,11 @@
// @flow
import qs from 'qs'
import type Transport from '@ledgerhq/hw-transport'
import { BASE_SOCKET_URL } from 'config/constants'
import { createDeviceSocket } from 'helpers/socket'
import type { LedgerScriptParams } from 'helpers/types'
import { createCustomErrorClass } from '../errors'
const ManagerDeviceLockedError = createCustomErrorClass('ManagerDeviceLocked')
const ManagerUninstallBTCDep = createCustomErrorClass('ManagerUninstallBTCDep')
import type { ApplicationVersion } from 'helpers/types'
import { ManagerDeviceLockedError, ManagerUninstallBTCDep } from 'config/errors'
import { WS_INSTALL } from 'helpers/urls'
function remapError(promise) {
return promise.catch((e: Error) => {
@ -30,8 +26,8 @@ function remapError(promise) {
export default async function uninstallApp(
transport: Transport<*>,
targetId: string | number,
{ app }: { app: LedgerScriptParams },
): Promise<*> {
{ app }: { app: ApplicationVersion },
): Promise<void> {
const params = {
targetId,
perso: app.perso,
@ -40,6 +36,6 @@ export default async function uninstallApp(
firmwareKey: app.delete_key,
hash: app.hash,
}
const url = `${BASE_SOCKET_URL}/install?${qs.stringify(params)}`
return remapError(createDeviceSocket(transport, url).toPromise())
const url = WS_INSTALL(params)
await remapError(createDeviceSocket(transport, url).toPromise())
}

3
src/helpers/debugAppInfosForCurrency/btc.js

@ -1,9 +1,6 @@
// @flow
import type Transport from '@ledgerhq/hw-transport'
import { createCustomErrorClass } from '../errors'
export const BtcUnmatchedApp = createCustomErrorClass('BtcUnmatchedApp')
export default async (transport: Transport<*>) => {
const r = await transport.send(0xe0, 0xc4, 0, 0)

5
src/helpers/devices/getCurrentFirmware.js

@ -2,6 +2,7 @@
import network from 'api/network'
import { GET_CURRENT_FIRMWARE } from 'helpers/urls'
import type { FinalFirmware } from 'helpers/types'
type Input = {
fullVersion: string,
@ -9,8 +10,8 @@ type Input = {
provider: number,
}
export default async (input: Input): Promise<*> => {
const { data } = await network({
export default async (input: Input): Promise<FinalFirmware> => {
const { data }: { data: FinalFirmware } = await network({
method: 'POST',
url: GET_CURRENT_FIRMWARE,
data: {

12
src/helpers/devices/getDeviceInfo.js

@ -5,17 +5,7 @@ import type Transport from '@ledgerhq/hw-transport'
import getFirmwareInfo from 'helpers/firmware/getFirmwareInfo'
import { FORCE_PROVIDER } from 'config/constants'
export type DeviceInfo = {
targetId: string | number,
seVersion: string,
isBootloader: boolean,
flags: string,
mcuVersion: string,
isOSU: boolean,
providerName: string,
providerId: number,
fullVersion: string,
}
import type { DeviceInfo } from 'helpers/types'
const PROVIDERS = {
'': 1,

6
src/helpers/devices/getDeviceVersion.js

@ -2,8 +2,10 @@
import { GET_DEVICE_VERSION } from 'helpers/urls'
import network from 'api/network'
export default async (targetId: string | number, provider: number): Promise<*> => {
const { data } = await network({
import type { DeviceVersion } from 'helpers/types'
export default async (targetId: string | number, provider: number): Promise<DeviceVersion> => {
const { data }: { data: DeviceVersion } = await network({
method: 'POST',
url: GET_DEVICE_VERSION,
data: {

12
src/helpers/devices/getIsGenuine.js

@ -2,23 +2,29 @@
import type Transport from '@ledgerhq/hw-transport'
import { SKIP_GENUINE } from 'config/constants'
import { WS_GENUINE } from 'helpers/urls'
import type { DeviceInfo } from 'helpers/devices/getDeviceInfo'
import type { DeviceInfo, FinalFirmware, DeviceVersion } from 'helpers/types'
import { createDeviceSocket } from 'helpers/socket'
import getCurrentFirmware from './getCurrentFirmware'
import getDeviceVersion from './getDeviceVersion'
export default async (transport: Transport<*>, deviceInfo: DeviceInfo): Promise<string> => {
const deviceVersion = await getDeviceVersion(deviceInfo.targetId, deviceInfo.providerId)
const firmware = await getCurrentFirmware({
const deviceVersion: DeviceVersion = await getDeviceVersion(
deviceInfo.targetId,
deviceInfo.providerId,
)
const firmware: FinalFirmware = await getCurrentFirmware({
deviceId: deviceVersion.id,
fullVersion: deviceInfo.fullVersion,
provider: deviceInfo.providerId,
})
const params = {
targetId: deviceInfo.targetId,
perso: firmware.perso,
}
const url = WS_GENUINE(params)
return SKIP_GENUINE
? new Promise(resolve => setTimeout(() => resolve('0000'), 1000))

36
src/helpers/devices/getLatestFirmwareForDevice.js

@ -1,7 +1,13 @@
// @flow
import network from 'api/network'
import { GET_LATEST_FIRMWARE } from 'helpers/urls'
import type { DeviceInfo } from 'helpers/devices/getDeviceInfo'
import type {
DeviceInfo,
DeviceVersion,
FinalFirmware,
OsuFirmware,
McuVersion,
} from 'helpers/types'
import getFinalFirmwareById from 'helpers/firmware/getFinalFirmwareById'
import getMcus from 'helpers/firmware/getMcus'
@ -9,19 +15,31 @@ import getMcus from 'helpers/firmware/getMcus'
import getCurrentFirmware from './getCurrentFirmware'
import getDeviceVersion from './getDeviceVersion'
export default async (deviceInfo: DeviceInfo) => {
type NetworkResponse = {
data: {
result: string,
se_firmware_osu_version: OsuFirmware,
},
}
type Result = ?(OsuFirmware & { shouldFlashMcu: boolean })
export default async (deviceInfo: DeviceInfo): Promise<Result> => {
// Get device infos from targetId
const deviceVersion = await getDeviceVersion(deviceInfo.targetId, deviceInfo.providerId)
const deviceVersion: DeviceVersion = await getDeviceVersion(
deviceInfo.targetId,
deviceInfo.providerId,
)
// Get firmware infos with firmware name and device version
const seFirmwareVersion = await getCurrentFirmware({
const seFirmwareVersion: FinalFirmware = await getCurrentFirmware({
fullVersion: deviceInfo.fullVersion,
deviceId: deviceVersion.id,
provider: deviceInfo.providerId,
})
// Fetch next possible firmware
const { data } = await network({
const { data }: NetworkResponse = await network({
method: 'POST',
url: GET_LATEST_FIRMWARE,
data: {
@ -37,11 +55,13 @@ export default async (deviceInfo: DeviceInfo) => {
const { se_firmware_osu_version } = data
const { next_se_firmware_final_version } = se_firmware_osu_version
const seFirmwareFinalVersion = await getFinalFirmwareById(next_se_firmware_final_version)
const seFirmwareFinalVersion: FinalFirmware = await getFinalFirmwareById(
next_se_firmware_final_version,
)
const mcus = await getMcus()
const mcus: Array<McuVersion> = await getMcus()
const currentMcuVersionId = mcus
const currentMcuVersionId: Array<number> = mcus
.filter(mcu => mcu.name === deviceInfo.mcuVersion)
.map(mcu => mcu.id)

2
src/helpers/devices/shouldFlashMcu.js

@ -1,7 +1,7 @@
// @flow
import network from 'api/network'
import { GET_LATEST_FIRMWARE } from 'helpers/urls'
import type { DeviceInfo } from 'helpers/devices/getDeviceInfo'
import type { DeviceInfo } from 'helpers/types'
import getFinalFirmwareById from 'helpers/firmware/getFinalFirmwareById'
import getMcus from 'helpers/firmware/getMcus'

3
src/helpers/firmware/getFirmwareInfo.js

@ -1,6 +1,7 @@
// @flow
import type Transport from '@ledgerhq/hw-transport'
import type { FirmwareInfo } from 'helpers/types'
const APDUS = {
GET_FIRMWARE: [0xe0, 0x01, 0x00, 0x00],
@ -12,7 +13,7 @@ const APDUS = {
/**
* Retrieve targetId and firmware version from device
*/
export default async function getFirmwareInfo(transport: Transport<*>) {
export default async function getFirmwareInfo(transport: Transport<*>): Promise<FirmwareInfo> {
const res = await transport.send(...APDUS.GET_FIRMWARE)
const byteArray = [...res]
const data = byteArray.slice(0, byteArray.length - 2)

7
src/helpers/firmware/getNextMCU.js

@ -2,12 +2,13 @@
import network from 'api/network'
import { GET_NEXT_MCU } from 'helpers/urls'
import { createCustomErrorClass } from 'helpers/errors'
import type { OsuFirmware } from 'helpers/types'
import { LatestMCUInstalledError } from 'config/errors'
const LatestMCUInstalledError = createCustomErrorClass('LatestMCUInstalledError')
type NetworkResponse = { data: OsuFirmware | 'default' }
export default async (bootloaderVersion: string): Promise<*> => {
const { data } = await network({
const { data }: NetworkResponse = await network({
method: 'POST',
url: GET_NEXT_MCU,
data: {

15
src/helpers/firmware/installFinalFirmware.js

@ -1,16 +1,15 @@
// @flow
import type Transport from '@ledgerhq/hw-transport'
import type { DeviceInfo } from 'helpers/devices/getDeviceInfo'
import type { DeviceInfo, DeviceVersion, OsuFirmware, FinalFirmware } from 'helpers/types'
import { WS_INSTALL } from 'helpers/urls'
import { createDeviceSocket } from 'helpers/socket'
import { createCustomErrorClass } from 'helpers/errors'
import getDeviceVersion from 'helpers/devices/getDeviceVersion'
import getOsuFirmware from 'helpers/devices/getOsuFirmware'
import getDeviceInfo from 'helpers/devices/getDeviceInfo'
import getFinalFirmwareById from './getFinalFirmwareById'
import { ManagerDeviceLockedError } from 'config/errors'
const ManagerDeviceLockedError = createCustomErrorClass('ManagerDeviceLocked')
import getFinalFirmwareById from './getFinalFirmwareById'
function remapSocketError(promise) {
return promise.catch((e: Error) => {
@ -23,19 +22,19 @@ function remapSocketError(promise) {
})
}
type Result = Promise<{ success: boolean, error?: string }>
type Result = Promise<{ success: boolean }>
export default async (transport: Transport<*>): Result => {
try {
const deviceInfo: DeviceInfo = await getDeviceInfo(transport)
const device = await getDeviceVersion(deviceInfo.targetId, deviceInfo.providerId)
const firmware = await getOsuFirmware({
const device: DeviceVersion = await getDeviceVersion(deviceInfo.targetId, deviceInfo.providerId)
const firmware: OsuFirmware = await getOsuFirmware({
deviceId: device.id,
version: deviceInfo.fullVersion,
provider: deviceInfo.providerId,
})
const { next_se_firmware_final_version } = firmware
const nextFirmware = await getFinalFirmwareById(next_se_firmware_final_version)
const nextFirmware: FinalFirmware = await getFinalFirmwareById(next_se_firmware_final_version)
const params = {
targetId: deviceInfo.targetId,

11
src/helpers/firmware/installMcu.js

@ -5,9 +5,9 @@ import { WS_MCU } from 'helpers/urls'
import { createDeviceSocket } from 'helpers/socket'
import getNextMCU from 'helpers/firmware/getNextMCU'
import getDeviceInfo from 'helpers/devices/getDeviceInfo'
import { createCustomErrorClass } from 'helpers/errors'
import { ManagerDeviceLockedError } from 'config/errors'
const ManagerDeviceLockedError = createCustomErrorClass('ManagerDeviceLocked')
import type { DeviceInfo } from 'helpers/types'
function remapSocketError(promise) {
return promise.catch((e: Error) => {
@ -20,16 +20,15 @@ function remapSocketError(promise) {
})
}
type Result = Promise<*>
type Result = Promise<void>
export default async (transport: Transport<*>): Result => {
const deviceInfo = await getDeviceInfo(transport)
const { seVersion: version, targetId } = deviceInfo
const { seVersion: version, targetId }: DeviceInfo = await getDeviceInfo(transport)
const nextVersion = await getNextMCU(version)
const params = {
targetId,
version: nextVersion.name,
}
const url = WS_MCU(params)
return remapSocketError(createDeviceSocket(transport, url).toPromise())
await remapSocketError(createDeviceSocket(transport, url).toPromise())
}

12
src/helpers/firmware/installOsuFirmware.js

@ -6,11 +6,11 @@ import { createDeviceSocket } from 'helpers/socket'
import type { Firmware } from 'components/modals/UpdateFirmware'
import { createCustomErrorClass } from '../errors'
const ManagerNotEnoughSpaceError = createCustomErrorClass('ManagerNotEnoughSpace')
const ManagerDeviceLockedError = createCustomErrorClass('ManagerDeviceLocked')
const UserRefusedFirmwareUpdate = createCustomErrorClass('UserRefusedFirmwareUpdate')
import {
ManagerNotEnoughSpaceError,
ManagerDeviceLockedError,
UserRefusedFirmwareUpdate,
} from 'config/errors'
function remapError(promise) {
return promise.catch((e: Error) => {
@ -27,7 +27,7 @@ function remapError(promise) {
})
}
type Result = Promise<{ success: boolean, error?: any }>
type Result = Promise<{ success: boolean }>
export default async (
transport: Transport<*>,

4
src/helpers/getAddressForCurrency/btc.js

@ -3,10 +3,8 @@
import type { CryptoCurrency } from '@ledgerhq/live-common/lib/types'
import Btc from '@ledgerhq/hw-app-btc'
import type Transport from '@ledgerhq/hw-transport'
import { BtcUnmatchedApp } from 'config/errors'
import getBitcoinLikeInfo from '../devices/getBitcoinLikeInfo'
import { createCustomErrorClass } from '../errors'
export const BtcUnmatchedApp = createCustomErrorClass('BtcUnmatchedApp')
export default async (
transport: Transport<*>,

5
src/helpers/libcore.js

@ -14,12 +14,11 @@ import type { NJSAccount, NJSOperation } from '@ledgerhq/ledger-core/src/ledgerc
import { isSegwitPath, isUnsplitPath } from 'helpers/bip32'
import * as accountIdHelper from 'helpers/accountId'
import { createCustomErrorClass, deserializeError } from './errors'
import { NoAddressesFound } from 'config/errors'
import { deserializeError } from './errors'
import { getAccountPlaceholderName, getNewAccountPlaceholderName } from './accountName'
import { timeoutTagged } from './promise'
const NoAddressesFound = createCustomErrorClass('NoAddressesFound')
// TODO: put that info inside currency itself
const SPLITTED_CURRENCIES = {
bitcoin_cash: {

14
src/helpers/socket.js

@ -5,13 +5,13 @@ import logger from 'logger'
import Websocket from 'ws'
import type Transport from '@ledgerhq/hw-transport'
import { Observable } from 'rxjs'
import { createCustomErrorClass } from './errors'
const WebsocketConnectionError = createCustomErrorClass('WebsocketConnectionError')
const WebsocketConnectionFailed = createCustomErrorClass('WebsocketConnectionFailed')
const DeviceSocketFail = createCustomErrorClass('DeviceSocketFail')
const DeviceSocketNoBulkStatus = createCustomErrorClass('DeviceSocketNoBulkStatus')
const DeviceSocketNoHandler = createCustomErrorClass('DeviceSocketNoHandler')
import {
WebsocketConnectionError,
WebsocketConnectionFailed,
DeviceSocketFail,
DeviceSocketNoBulkStatus,
DeviceSocketNoHandler,
} from 'config/errors'
/**
* use Ledger WebSocket API to exchange data with the device

132
src/helpers/types.js

@ -1,14 +1,134 @@
// @flow
type Id = number
export type LedgerScriptParams = {
firmware?: string,
firmware_key?: string,
firmware: string,
firmwareKey: string,
delete?: string,
delete_key?: string,
deleteKey?: string,
targetId?: string | number,
hash: string,
perso: string,
}
export type DeviceInfo = {
targetId: string | number,
seVersion: string,
isBootloader: boolean,
flags: string,
mcuVersion: string,
isOSU: boolean,
providerName: string,
providerId: number,
fullVersion: string,
}
export type DeviceVersion = {
id: Id,
name: string,
display_name: string,
target_id: string,
description: string,
device: Id,
providers: Array<Id>,
mcu_versions: Array<Id>,
se_firmware_final_versions: Array<Id>,
osu_versions: Array<Id>,
application_versions: Array<Id>,
date_creation: string,
date_last_modified: string,
}
export type McuVersion = {
id: Id,
mcu: Id,
name: string,
description: ?string,
providers: Array<Id>,
from_bootloader_version: string,
device_versions: Array<Id>,
se_firmware_final_versions: Array<Id>,
date_creation: string,
date_last_modified: string,
}
export type FirmwareInfo = {
targetId: Id,
seVersion: string,
flags: string,
mcuVersion: string,
}
type BaseFirmware = {
id: Id,
name: string,
description: ?string,
display_name: ?string,
notes: ?string,
perso: string,
firmware: string,
firmware_key: string,
hash: string,
date_creation: string,
date_last_modified: string,
device_versions: Array<Id>,
providers: Array<Id>,
}
export type OsuFirmware = BaseFirmware & {
next_se_firmware_final_version: Id,
previous_se_firmware_final_version: Array<Id>,
}
export type FinalFirmware = BaseFirmware & {
version: string,
se_firmware: Id,
osu_versions: Array<OsuFirmware>,
mcu_versions: Array<Id>,
application_versions: Array<Id>,
}
export type ApplicationVersion = {
id: Id,
name: string,
version: string,
app: Id,
description: ?string,
display_name: string,
icon: string,
app?: number,
hash?: string,
perso?: string,
picture: Id,
notes: ?string,
perso: string,
hash: string,
firmware: string,
firmware_key: string,
delete: string,
delete_key: string,
device_versions: Array<Id>,
se_firmware_final_versions: Array<Id>,
providers: Array<Id>,
date_creation: string,
date_last_modified: string,
}
export type Application = {
id: Id,
name: string,
description: ?string,
application_versions: Array<ApplicationVersion>,
providers: Array<Id>,
category: Id,
publisher: ?Id,
date_creation: string,
date_last_modified: string,
}
export type Category = {
id: Id,
name: string,
description: ?string,
providers: Array<Id>,
applications: Array<Id>,
date_creation: string,
date_last_modified: string,
}

64
test-e2e/nav_to_settings.spec.js

@ -0,0 +1,64 @@
const Application = require('spectron').Application
let app
const TIMEOUT = 50 * 1000
describe('Application launch', () => {
beforeEach(async () => {
app = new Application({
path: './dist/ledger-live-desktop-1.1.0-linux-x86_64.AppImage',
env: {
SKIP_ONBOARDING: '1',
},
})
await app.start()
}, TIMEOUT)
afterEach(async () => {
if (app && app.isRunning()) {
await app.stop()
}
}, TIMEOUT)
test(
'Start app and set developper mode ',
async () => {
const title = await app.client.getTitle()
expect(title).toEqual('Ledger Live')
await app.client.waitUntilWindowLoaded()
await app.client.pause(2000)
// Post Onboarding
const title_onboarding = await app.client.getText('[data-e2e=onboarding_title]')
expect(title_onboarding).toEqual('Analytics and bug reports')
await app.client.click('[data-e2e=continue_button]')
await app.client.pause(1000)
const title_finish = await app.client.getText('[data-e2e=finish_title]')
expect(title_finish).toEqual('Your device is ready!')
await app.client.click('[data-e2e=continue_button]')
await app.client.pause(1000)
const title_disclaimer = await app.client.getText('[data-e2e=disclaimer_title]')
expect(title_disclaimer).toEqual('Trade safely')
await app.client.click('[data-e2e=continue_button]')
await app.client.pause(1000)
// Dashboard EmptyState
const title_dashboard_empty = await app.client.getText('[data-e2e=dashboard_empty_title]')
expect(title_dashboard_empty).toEqual('Add accounts to your portfolio')
// Open Settings
await app.client.click('[data-e2e=setting_button]')
await app.client.pause(1000)
const title_settings = await app.client.getText('[data-e2e=settings_title]')
expect(title_settings).toEqual('Settings')
// DevMode ON
await app.client.click('[data-e2e=devMode_button]')
await app.client.pause(500)
},
TIMEOUT,
)
})

249
yarn.lock

@ -2213,6 +2213,30 @@ aproba@^1.0.3, aproba@^1.1.1, aproba@^1.1.2, aproba@~1.2.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc"
archiver-utils@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/archiver-utils/-/archiver-utils-1.3.0.tgz#e50b4c09c70bf3d680e32ff1b7994e9f9d895174"
dependencies:
glob "^7.0.0"
graceful-fs "^4.1.0"
lazystream "^1.0.0"
lodash "^4.8.0"
normalize-path "^2.0.0"
readable-stream "^2.0.0"
archiver@~2.1.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/archiver/-/archiver-2.1.1.tgz#ff662b4a78201494a3ee544d3a33fe7496509ebc"
dependencies:
archiver-utils "^1.3.0"
async "^2.0.0"
buffer-crc32 "^0.2.1"
glob "^7.0.0"
lodash "^4.8.0"
readable-stream "^2.0.0"
tar-stream "^1.5.0"
zip-stream "^1.2.0"
archy@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40"
@ -2414,7 +2438,7 @@ async@^1.4.0, async@^1.5.0, async@^1.5.2:
version "1.5.2"
resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
async@^2.1.2, async@^2.1.4, async@^2.6.0, async@^2.6.1:
async@^2.0.0, async@^2.1.2, async@^2.1.4, async@^2.6.0, async@^2.6.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610"
dependencies:
@ -3889,6 +3913,10 @@ buffer-alloc@^1.1.0:
buffer-alloc-unsafe "^1.1.0"
buffer-fill "^1.0.0"
buffer-crc32@^0.2.1:
version "0.2.13"
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
buffer-fill@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c"
@ -3928,6 +3956,13 @@ buffer@^5.0.3:
base64-js "^1.0.2"
ieee754 "^1.1.4"
buffer@^5.1.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.2.0.tgz#53cf98241100099e9eeae20ee6d51d21b16e541e"
dependencies:
base64-js "^1.0.2"
ieee754 "^1.1.4"
buffers@~0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/buffers/-/buffers-0.1.1.tgz#b24579c3bed4d6d396aeee6d9a8ae7f5482ab7bb"
@ -4612,6 +4647,15 @@ component-emitter@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6"
compress-commons@^1.2.0:
version "1.2.2"
resolved "https://registry.yarnpkg.com/compress-commons/-/compress-commons-1.2.2.tgz#524a9f10903f3a813389b0225d27c48bb751890f"
dependencies:
buffer-crc32 "^0.2.1"
crc32-stream "^2.0.0"
normalize-path "^2.0.0"
readable-stream "^2.0.0"
compressible@~2.0.13:
version "2.0.14"
resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.14.tgz#326c5f507fbb055f54116782b969a81b67a29da7"
@ -4774,6 +4818,19 @@ cosmiconfig@^4.0.0:
parse-json "^4.0.0"
require-from-string "^2.0.1"
crc32-stream@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/crc32-stream/-/crc32-stream-2.0.0.tgz#e3cdd3b4df3168dd74e3de3fbbcb7b297fe908f4"
dependencies:
crc "^3.4.4"
readable-stream "^2.0.0"
crc@^3.4.4:
version "3.8.0"
resolved "https://registry.yarnpkg.com/crc/-/crc-3.8.0.tgz#ad60269c2c856f8c299e2c4cc0de4556914056c6"
dependencies:
buffer "^5.1.0"
create-ecdh@^4.0.0:
version "4.0.3"
resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.3.tgz#c9111b6f33045c4697f144787f9254cdc77c45ff"
@ -4935,6 +4992,12 @@ css-loader@^0.28.11:
postcss-value-parser "^3.3.0"
source-list-map "^2.0.0"
css-parse@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/css-parse/-/css-parse-2.0.0.tgz#a468ee667c16d81ccf05c58c38d2a97c780dbfd4"
dependencies:
css "^2.0.0"
css-select-base-adapter@~0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.0.tgz#0102b3d14630df86c3eb9fa9f5456270106cf990"
@ -4991,10 +5054,23 @@ css-url-regex@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/css-url-regex/-/css-url-regex-1.1.0.tgz#83834230cc9f74c457de59eebd1543feeb83b7ec"
css-value@~0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/css-value/-/css-value-0.0.1.tgz#5efd6c2eea5ea1fd6b6ac57ec0427b18452424ea"
css-what@2.1:
version "2.1.0"
resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.0.tgz#9467d032c38cfaefb9f2d79501253062f87fa1bd"
css@^2.0.0:
version "2.2.3"
resolved "https://registry.yarnpkg.com/css/-/css-2.2.3.tgz#f861f4ba61e79bedc962aa548e5780fd95cbc6be"
dependencies:
inherits "^2.0.1"
source-map "^0.1.38"
source-map-resolve "^0.5.1"
urix "^0.1.0"
cssesc@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-0.1.0.tgz#c814903e45623371a0477b40109aaafbeeaddbb4"
@ -5383,6 +5459,10 @@ deep-is@~0.1.3:
version "0.1.3"
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
deepmerge@~2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.0.1.tgz#25c1c24f110fb914f80001b925264dd77f3f4312"
default-require-extensions@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-2.0.0.tgz#f5f8fbb18a7d6d50b21f641f649ebb522cfe24f7"
@ -5508,6 +5588,10 @@ detect-port-alt@1.1.6:
address "^1.0.1"
debug "^2.6.0"
dev-null@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/dev-null/-/dev-null-0.1.1.tgz#5a205ce3c2b2ef77b6238d6ba179eb74c6a0e818"
dezalgo@^1.0.0, dezalgo@~1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.3.tgz#7f742de066fc748bc8db820569dddce49bf0d456"
@ -5754,6 +5838,10 @@ ejs@^2.5.7, ejs@^2.5.9, ejs@^2.6.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.6.1.tgz#498ec0d495655abc6f23cd61868d926464071aa0"
ejs@~2.5.6:
version "2.5.9"
resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.9.tgz#7ba254582a560d267437109a68354112475b0ce5"
electron-builder-lib@20.14.7, electron-builder-lib@~20.14.6:
version "20.14.7"
resolved "https://registry.yarnpkg.com/electron-builder-lib/-/electron-builder-lib-20.14.7.tgz#db91977dd13b0a288e1da5629183807a9847de21"
@ -5803,6 +5891,13 @@ electron-builder@20.14.7:
update-notifier "^2.5.0"
yargs "^11.0.0"
electron-chromedriver@~1.8.0:
version "1.8.0"
resolved "https://registry.yarnpkg.com/electron-chromedriver/-/electron-chromedriver-1.8.0.tgz#901714133cf6f6093d365e1f44a52d99624d8241"
dependencies:
electron-download "^4.1.0"
extract-zip "^1.6.5"
electron-context-menu@^0.10.0:
version "0.10.0"
resolved "https://registry.yarnpkg.com/electron-context-menu/-/electron-context-menu-0.10.0.tgz#97fce2b805e03ac2b1dae11eb6a68b064b78d633"
@ -5855,6 +5950,20 @@ electron-download@^3.0.1:
semver "^5.3.0"
sumchecker "^1.2.0"
electron-download@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/electron-download/-/electron-download-4.1.0.tgz#bf932c746f2f87ffcc09d1dd472f2ff6b9187845"
dependencies:
debug "^2.2.0"
env-paths "^1.0.0"
fs-extra "^2.0.0"
minimist "^1.2.0"
nugget "^2.0.0"
path-exists "^3.0.0"
rc "^1.1.2"
semver "^5.3.0"
sumchecker "^2.0.1"
electron-is-dev@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/electron-is-dev/-/electron-is-dev-0.3.0.tgz#14e6fda5c68e9e4ecbeff9ccf037cbd7c05c5afe"
@ -6632,7 +6741,7 @@ extglob@^2.0.4:
snapdragon "^0.8.1"
to-regex "^3.0.1"
extract-zip@^1.0.3:
extract-zip@^1.0.3, extract-zip@^1.6.5:
version "1.6.7"
resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.6.7.tgz#a840b4b8af6403264c8db57f4f1a74333ef81fe9"
dependencies:
@ -6994,6 +7103,13 @@ fs-extra@^0.30.0:
path-is-absolute "^1.0.0"
rimraf "^2.2.8"
fs-extra@^2.0.0:
version "2.1.2"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-2.1.2.tgz#046c70163cef9aad46b0e4a7fa467fb22d71de35"
dependencies:
graceful-fs "^4.1.2"
jsonfile "^2.1.0"
fs-extra@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-3.0.1.tgz#3794f378c58b342ea7dbbb23095109c4b3b62291"
@ -7102,6 +7218,12 @@ gauge@~2.7.3:
strip-ansi "^3.0.1"
wide-align "^1.1.0"
gaze@~1.1.2:
version "1.1.3"
resolved "https://registry.yarnpkg.com/gaze/-/gaze-1.1.3.tgz#c441733e13b927ac8c0ff0b4c3b033f28812924a"
dependencies:
globule "^1.0.0"
genfun@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/genfun/-/genfun-4.0.1.tgz#ed10041f2e4a7f1b0a38466d17a5c3e27df1dfc1"
@ -7222,7 +7344,7 @@ glob-to-regexp@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab"
glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@~7.1.2:
glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@~7.1.1, glob@~7.1.2:
version "7.1.2"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
dependencies:
@ -7305,6 +7427,14 @@ globby@^8.0.0, globby@^8.0.1:
pify "^3.0.0"
slash "^1.0.0"
globule@^1.0.0:
version "1.2.1"
resolved "https://registry.yarnpkg.com/globule/-/globule-1.2.1.tgz#5dffb1b191f22d20797a9369b49eab4e9839696d"
dependencies:
glob "~7.1.1"
lodash "~4.17.10"
minimatch "~3.0.2"
got@^6.7.1:
version "6.7.1"
resolved "https://registry.yarnpkg.com/got/-/got-6.7.1.tgz#240cd05785a9a18e561dc1b44b41c763ef1e8db0"
@ -7362,7 +7492,7 @@ got@^8.3.1:
url-parse-lax "^3.0.0"
url-to-options "^1.0.1"
graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@~4.1.11:
graceful-fs@^4.1.0, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@~4.1.11:
version "4.1.11"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
@ -7949,7 +8079,7 @@ inline-style-prefixer@^3.0.6:
bowser "^1.7.3"
css-in-js-utils "^2.0.0"
inquirer@3.3.0, inquirer@^3.0.6:
inquirer@3.3.0, inquirer@^3.0.6, inquirer@~3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9"
dependencies:
@ -9075,6 +9205,12 @@ lazy-val@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/lazy-val/-/lazy-val-1.0.3.tgz#bb97b200ef00801d94c317e29dc6ed39e31c5edc"
lazystream@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.0.tgz#f6995fe0f820392f61396be89462407bb77168e4"
dependencies:
readable-stream "^2.0.5"
lcid@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835"
@ -9354,7 +9490,7 @@ lodash@^3.10.1:
version "3.10.1"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
lodash@^4.0.1, lodash@^4.12.0, lodash@^4.13.1, lodash@^4.15.0, lodash@^4.17.0, lodash@^4.17.10, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.5.1:
lodash@^4.0.1, lodash@^4.12.0, lodash@^4.13.1, lodash@^4.15.0, lodash@^4.17.0, lodash@^4.17.10, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.5.1, lodash@^4.8.0, lodash@~4.17.10:
version "4.17.10"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7"
@ -9767,7 +9903,7 @@ minimatch@3.0.3:
dependencies:
brace-expansion "^1.0.0"
minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4:
minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4, minimatch@~3.0.2:
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
dependencies:
@ -10136,7 +10272,7 @@ normalize-path@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-1.0.0.tgz#32d0e472f91ff345701c15a8311018d3b0a90379"
normalize-path@^2.0.1, normalize-path@^2.1.1:
normalize-path@^2.0.0, normalize-path@^2.0.1, normalize-path@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9"
dependencies:
@ -10184,6 +10320,10 @@ npm-install-checks@~3.0.0:
dependencies:
semver "^2.3.0 || 3.x || 4 || 5"
npm-install-package@~2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/npm-install-package/-/npm-install-package-2.1.0.tgz#d7efe3cfcd7ab00614b896ea53119dc9ab259125"
npm-lifecycle@^2.0.1, npm-lifecycle@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/npm-lifecycle/-/npm-lifecycle-2.0.3.tgz#696bedf1143371163e9cc16fe872357e25d8d90e"
@ -10556,7 +10696,7 @@ opn@^5.1.0:
dependencies:
is-wsl "^1.1.0"
optimist@^0.6.1:
optimist@^0.6.1, optimist@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686"
dependencies:
@ -11565,7 +11705,7 @@ pushdata-bitcoin@^1.0.1:
dependencies:
bitcoin-ops "^1.3.0"
q@^1.1.2:
q@^1.1.2, q@~1.5.0:
version "1.5.1"
resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
@ -12140,7 +12280,7 @@ read@1, read@~1.0.1, read@~1.0.7:
dependencies:
mute-stream "~0.0.4"
"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.2.9, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6:
"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.2.9, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6:
version "2.3.6"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf"
dependencies:
@ -12516,7 +12656,7 @@ request-promise-native@^1.0.5:
tunnel-agent "^0.6.0"
uuid "^3.0.0"
request@^2.45.0, request@^2.74.0, request@^2.83.0, request@^2.85.0:
request@^2.45.0, request@^2.74.0, request@^2.81.0, request@^2.83.0, request@^2.85.0:
version "2.87.0"
resolved "https://registry.yarnpkg.com/request/-/request-2.87.0.tgz#32f00235cd08d482b4d0d68db93a829c0ed5756e"
dependencies:
@ -12647,6 +12787,10 @@ retry@^0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b"
rgb2hex@~0.1.4:
version "0.1.9"
resolved "https://registry.yarnpkg.com/rgb2hex/-/rgb2hex-0.1.9.tgz#5d3e0e14b0177b568e6f0d5b43e34fbfdb670346"
right-align@^0.1.1:
version "0.1.3"
resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef"
@ -13233,7 +13377,7 @@ source-list-map@~0.1.7:
version "0.1.8"
resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-0.1.8.tgz#c550b2ab5427f6b3f21f5afead88c4f5587b2106"
source-map-resolve@^0.5.0:
source-map-resolve@^0.5.0, source-map-resolve@^0.5.1:
version "0.5.2"
resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259"
dependencies:
@ -13268,6 +13412,12 @@ source-map@0.7.3, source-map@^0.7.2:
version "0.7.3"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
source-map@^0.1.38:
version "0.1.43"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346"
dependencies:
amdefine ">=0.0.4"
source-map@^0.4.4, source-map@~0.4.1:
version "0.4.4"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b"
@ -13335,6 +13485,16 @@ spdy@^3.4.1:
select-hose "^2.0.0"
spdy-transport "^2.0.18"
spectron@^3.8.0:
version "3.8.0"
resolved "https://registry.yarnpkg.com/spectron/-/spectron-3.8.0.tgz#122c3562fd7e92b7cdf6f94094aa495b150dfa51"
dependencies:
dev-null "^0.1.1"
electron-chromedriver "~1.8.0"
request "^2.81.0"
split "^1.0.0"
webdriverio "^4.8.0"
speedometer@~0.1.2:
version "0.1.4"
resolved "https://registry.yarnpkg.com/speedometer/-/speedometer-0.1.4.tgz#9876dbd2a169d3115402d48e6ea6329c8816a50d"
@ -13345,6 +13505,12 @@ split-string@^3.0.1, split-string@^3.0.2:
dependencies:
extend-shallow "^3.0.0"
split@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9"
dependencies:
through "2"
sprintf-js@~1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
@ -13641,7 +13807,7 @@ sumchecker@^1.2.0:
debug "^2.2.0"
es6-promise "^4.0.5"
sumchecker@^2.0.2:
sumchecker@^2.0.1, sumchecker@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/sumchecker/-/sumchecker-2.0.2.tgz#0f42c10e5d05da5d42eea3e56c3399a37d6c5b3e"
dependencies:
@ -13673,6 +13839,12 @@ supports-color@^5.1.0, supports-color@^5.3.0, supports-color@^5.4.0:
dependencies:
has-flag "^3.0.0"
supports-color@~5.0.0:
version "5.0.1"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.0.1.tgz#1c5331f22250c84202805b2f17adf16699f3a39a"
dependencies:
has-flag "^2.0.0"
svg-tag-names@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/svg-tag-names/-/svg-tag-names-1.1.1.tgz#9641b29ef71025ee094c7043f7cdde7d99fbd50a"
@ -13765,7 +13937,7 @@ tar-fs@^1.13.0:
pump "^1.0.0"
tar-stream "^1.1.2"
tar-stream@^1.1.2:
tar-stream@^1.1.2, tar-stream@^1.5.0:
version "1.6.1"
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.1.tgz#f84ef1696269d6223ca48f6e1eeede3f7e81f395"
dependencies:
@ -13863,7 +14035,7 @@ through2@~0.2.3:
readable-stream "~1.1.9"
xtend "~2.1.1"
"through@>=2.2.7 <3", through@^2.3.6, through@^2.3.8, through@~2.3.6:
through@2, "through@>=2.2.7 <3", through@^2.3.6, through@^2.3.8, through@~2.3.6:
version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
@ -14347,7 +14519,7 @@ url-to-options@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9"
url@^0.11.0:
url@^0.11.0, url@~0.11.0:
version "0.11.0"
resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1"
dependencies:
@ -14584,6 +14756,36 @@ wcwidth@^1.0.0:
dependencies:
defaults "^1.0.3"
wdio-dot-reporter@~0.0.8:
version "0.0.10"
resolved "https://registry.yarnpkg.com/wdio-dot-reporter/-/wdio-dot-reporter-0.0.10.tgz#facfb7c9c5984149951f59cbc3cd0752101cf0e0"
webdriverio@^4.8.0:
version "4.13.1"
resolved "https://registry.yarnpkg.com/webdriverio/-/webdriverio-4.13.1.tgz#624ef4ca569f3c9a5e8e9b11302b4431eda1fb8a"
dependencies:
archiver "~2.1.0"
babel-runtime "^6.26.0"
css-parse "^2.0.0"
css-value "~0.0.1"
deepmerge "~2.0.1"
ejs "~2.5.6"
gaze "~1.1.2"
glob "~7.1.1"
inquirer "~3.3.0"
json-stringify-safe "~5.0.1"
mkdirp "~0.5.1"
npm-install-package "~2.1.0"
optimist "~0.6.1"
q "~1.5.0"
request "^2.83.0"
rgb2hex "~0.1.4"
safe-buffer "~5.1.1"
supports-color "~5.0.0"
url "~0.11.0"
wdio-dot-reporter "~0.0.8"
wgxpath "~1.0.0"
webidl-conversions@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
@ -14810,6 +15012,10 @@ websocket-extensions@>=0.1.1:
version "0.1.3"
resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29"
wgxpath@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/wgxpath/-/wgxpath-1.0.0.tgz#eef8a4b9d558cc495ad3a9a2b751597ecd9af690"
whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.3.tgz#57c235bc8657e914d24e1a397d3c82daee0a6ba3"
@ -15280,6 +15486,15 @@ yeoman-generator@^2.0.5:
through2 "^2.0.0"
yeoman-environment "^2.0.5"
zip-stream@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-1.2.0.tgz#a8bc45f4c1b49699c6b90198baacaacdbcd4ba04"
dependencies:
archiver-utils "^1.3.0"
compress-commons "^1.2.0"
lodash "^4.8.0"
readable-stream "^2.0.0"
zxcvbn@^4.4.2:
version "4.4.2"
resolved "https://registry.yarnpkg.com/zxcvbn/-/zxcvbn-4.4.2.tgz#28ec17cf09743edcab056ddd8b1b06262cc73c30"

Loading…
Cancel
Save