diff --git a/.circleci/config.yml b/.circleci/config.yml
index 1390a86a..5eddfa50 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -10,13 +10,22 @@ jobs:
<<: *defaults
steps:
- run: sudo apt-get install -y libudev-dev
+ - run:
+ name: Install latest yarn
+ command: |
+ curl -fsSLO --compressed "https://yarnpkg.com/downloads/$YARN_VERSION/yarn-v$YARN_VERSION.tar.gz" ;
+ sudo mkdir -p /opt ;
+ sudo tar -xzf yarn-v$YARN_VERSION.tar.gz -C /opt/ ;
+ sudo ln -sf /opt/yarn-v$YARN_VERSION/bin/yarn /usr/local/bin/yarn ;
+ sudo ln -sf /opt/yarn-v$YARN_VERSION/bin/yarnpkg /usr/local/bin/yarnpkg ;
+ rm yarn-v$YARN_VERSION.tar.gz
- checkout
- restore_cache:
keys:
- - v10-yarn-packages-{{ checksum "yarn.lock" }}
+ - v11-yarn-packages-{{ checksum "yarn.lock" }}
- run: yarn install
- save_cache:
- key: v10-yarn-packages-{{ checksum "yarn.lock" }}
+ key: v11-yarn-packages-{{ checksum "yarn.lock" }}
paths:
- node_modules
- run: yarn lint
diff --git a/package.json b/package.json
index bf0d7b7e..c3365143 100644
--- a/package.json
+++ b/package.json
@@ -3,7 +3,7 @@
"productName": "Ledger Live",
"description": "Ledger Live - Desktop",
"repository": "https://github.com/LedgerHQ/ledger-live-desktop",
- "version": "1.1.11",
+ "version": "1.2.0",
"author": "Ledger",
"license": "MIT",
"scripts": {
@@ -167,5 +167,9 @@
"webpack-cli": "^2.0.14",
"yaml-loader": "^0.5.0"
},
+ "engines": {
+ "node": ">=8.9.0 <=8.12.0",
+ "yarn": "^1.10.1"
+ },
"private": true
}
diff --git a/src/components/ExchangePage/index.js b/src/components/ExchangePage/index.js
index 09f3502b..97b5ca20 100644
--- a/src/components/ExchangePage/index.js
+++ b/src/components/ExchangePage/index.js
@@ -71,6 +71,12 @@ const cards = shuffle([
url: urls.genesis,
logo: ,
},
+ {
+ key: 'kyber',
+ id: 'kyber',
+ url: urls.kyber,
+ logo: ,
+ },
])
class ExchangePage extends PureComponent {
diff --git a/src/config/urls.js b/src/config/urls.js
index 7ee5caeb..60c2fc42 100644
--- a/src/config/urls.js
+++ b/src/config/urls.js
@@ -30,6 +30,7 @@ export const urls = {
luno: 'http://luno.go2cloud.org/aff_c?offer_id=4&aff_id=1001&source=ledger',
shapeshift: 'https://shapeshift.io/#/coins?affiliate=ledger',
genesis: 'https://genesistrading.com/ledger-live/',
+ kyber: 'http://kyber.network/swap?ref=0xE2D8481eeF31CDA994833974FFfEccd576f8D71E',
// Errors
errors: {
diff --git a/src/helpers/anonymizer.js b/src/helpers/anonymizer.js
index 693195a9..3379ce11 100644
--- a/src/helpers/anonymizer.js
+++ b/src/helpers/anonymizer.js
@@ -46,6 +46,9 @@ function filepathRecursiveReplacer(obj: mixed, seen: Array<*>) {
}
}
} else {
+ if (obj instanceof Error) {
+ obj.message = filepathReplace(obj.message)
+ }
for (const k in obj) {
if (typeof obj.hasOwnProperty === 'function' && obj.hasOwnProperty(k)) {
const value = obj[k]
diff --git a/src/main/app.js b/src/main/app.js
index 25a438a3..272b0e40 100644
--- a/src/main/app.js
+++ b/src/main/app.js
@@ -65,21 +65,15 @@ const getDefaultUrl = () =>
__DEV__ ? `http://localhost:${ELECTRON_WEBPACK_WDS_PORT || ''}` : `file://${__dirname}/index.html`
const saveWindowSettings = window => {
- window.on(
- 'resize',
- debounce(() => {
- const [width, height] = window.getSize()
- db.setKey('windowParams', `${window.name}.dimensions`, { width, height })
- }, 100),
- )
-
- window.on(
- 'move',
- debounce(() => {
- const [x, y] = window.getPosition()
- db.setKey('windowParams', `${window.name}.positions`, { x, y })
- }, 100),
- )
+ const windowParamsHandler = () => {
+ const [width, height] = window.getSize()
+ const [x, y] = window.getPosition()
+ db.setKey('windowParams', `${window.name}.dimensions`, { width, height })
+ db.setKey('windowParams', `${window.name}.positions`, { x, y })
+ }
+
+ window.on('resize', debounce(windowParamsHandler, 100))
+ window.on('move', debounce(windowParamsHandler, 100))
}
const defaultWindowOptions = {
@@ -95,8 +89,8 @@ const defaultWindowOptions = {
}
async function createMainWindow() {
- const savedDimensions = await db.getKey('app', 'MainWindow.dimensions', {})
- const savedPositions = await db.getKey('app', 'MainWindow.positions', null)
+ const savedDimensions = await db.getKey('windowParams', 'MainWindow.dimensions', {})
+ const savedPositions = await db.getKey('windowParams', 'MainWindow.positions', null)
const width = savedDimensions.width || DEFAULT_WINDOW_WIDTH
const height = savedDimensions.height || DEFAULT_WINDOW_HEIGHT
@@ -121,7 +115,6 @@ async function createMainWindow() {
const window = new BrowserWindow(windowOptions)
window.name = 'MainWindow'
-
const url = getDefaultUrl()
if (devTools) {
@@ -172,8 +165,6 @@ const installExtensions = async () => {
).catch(console.log) // eslint-disable-line
}
-app.setAsDefaultProtocolClient('ledgerhq')
-
app.on('ready', async () => {
if (__DEV__) {
await installExtensions()
diff --git a/static/i18n/en/app.json b/static/i18n/en/app.json
index 999829f3..a3cebda7 100644
--- a/static/i18n/en/app.json
+++ b/static/i18n/en/app.json
@@ -169,7 +169,8 @@
"paybis": "it is safe and easy to Buy Bitcoin with credit card from PayBis. Service operates inĀ US, Canada, Germany, Russia and Saudi Arabia.",
"luno": "Luno makes it safe and easy to buy, store and learn about cryptocurrencies like Bitcoin and Ethereum",
"shapeshift": "ShapeShift is an online marketplace where users can buy and sell digital assets. It is a fast and secure way for the world to buy and sell digital assets, with no lengthy signup process, no counterparty risk, and no friction.",
- "genesis": "Genesis is an institutional trading firm offering liquidity and borrow for digital currencies, including bitcoin, bitcoin cash, ethereum, ethereum classic, litecoin, and XRP."
+ "genesis": "Genesis is an institutional trading firm offering liquidity and borrow for digital currencies, including bitcoin, bitcoin cash, ethereum, ethereum classic, litecoin, and XRP.",
+ "kyber": "KYBER, his a trading platform for exchange and conversion of ERC-20 tokens"
},
"genuinecheck": {
"modal": {
diff --git a/static/images/logos/exchanges/kyber.svg b/static/images/logos/exchanges/kyber.svg
new file mode 100644
index 00000000..4cc634a0
--- /dev/null
+++ b/static/images/logos/exchanges/kyber.svg
@@ -0,0 +1,13 @@
+
+
\ No newline at end of file
diff --git a/test-e2e/password-lock-check.spec.js b/test-e2e/password-lock-check.spec.js
new file mode 100644
index 00000000..912a4b27
--- /dev/null
+++ b/test-e2e/password-lock-check.spec.js
@@ -0,0 +1,93 @@
+import { Application } from 'spectron'
+import { waitForDisappear, waitForExpectedText } from './helpers'
+
+const os = require('os')
+const path = require('path')
+const fs = require('fs')
+const appVersion = require('../package.json')
+
+let app
+
+const TIMEOUT = 50 * 1000
+
+let appPath
+let configPath
+const platform = os.platform()
+if (platform === 'darwin') {
+ appPath = `./dist/mac/Ledger Live.app/Contents/MacOS/Ledger Live`
+ configPath = `${os.homedir()}/Library/Application Support/Ledger Live/`
+} else if (platform === 'win32') {
+ appPath = `.\\dist\\win-unpacked\\Ledger Live.exe`
+ configPath = '%AppData\\Roaming\\Ledger Live'
+} else {
+ appPath = `./dist/ledger-live-desktop-${appVersion.version}-linux-x86_64.AppImage`
+ configPath = '$HOME/apps/ledger-live-desktop-$ledgerLiveVersion-linux-x86_64.AppImage'
+}
+
+describe('Application launch', () => {
+ beforeEach(async () => {
+ app = new Application({
+ path: appPath,
+ env: {
+ SKIP_ONBOARDING: '1',
+ },
+ })
+ await app.start()
+ }, TIMEOUT)
+
+ afterEach(async () => {
+ if (app && app.isRunning()) {
+ await app.stop()
+ }
+ }, TIMEOUT)
+
+ test(
+ 'Start app, activate password lock, check app.json, deactivate password lock',
+ async () => {
+ const title = await app.client.getTitle()
+ expect(title).toEqual('Ledger Live')
+ await app.client.waitUntilWindowLoaded()
+ await waitForDisappear(app, '#preload')
+ // Verify Account summary text
+ // Count user's accounts
+ const userAccountsList = await app.client.elements('[data-e2e=dashboard_AccountCardWrapper]')
+ const userAccountsCount = Object.keys(userAccountsList.value).length
+ // Check account number
+ const accountSummary = await app.client.getText('[data-e2e=dashboard_accountsSummaryDesc]')
+ const accountSummaryMessage = `Here's the summary of your ${userAccountsCount} accounts`
+ expect(accountSummary).toEqual(accountSummaryMessage)
+
+ // Go to settings
+ await app.client.click('[data-e2e=setting_button]')
+ await waitForExpectedText(app, '[data-e2e=settings_title]', 'Settings')
+
+ // Enable lock password
+ await app.client.click('[data-e2e=passwordLock_button]')
+ await waitForExpectedText(app, '[data-e2e=setPassword_modalTitle]', 'Set a password')
+ await app.client.setValue('[data-e2e=setPassword_NewPassword]', 5)
+ await app.client.setValue('[data-e2e=setPassword_ConfirmPassword]', 5)
+ await app.client.keys('Enter')
+ await waitForExpectedText(app, '[data-e2e=settings_title]', 'Settings')
+ await app.client.pause(2000)
+ // Verify in app.json that accounts data are encrypted
+ const tmpAppJSONPath = path.resolve(configPath, 'app.json')
+ const LockedfileContent = fs.readFileSync(tmpAppJSONPath, 'utf-8')
+ const accountsOperations = '"operations":[{'
+ await expect(LockedfileContent).not.toContain(accountsOperations)
+
+ // Disable password lock
+ await app.client.click('[data-e2e=passwordLock_button]')
+ await waitForExpectedText(app, '[data-e2e=modal_title]', 'Disable password lock')
+ await app.client.setValue('#password', 5)
+ await app.client.pause(500)
+ await app.client.keys('Enter')
+ await waitForExpectedText(app, '[data-e2e=settings_title]', 'Settings')
+ await app.client.pause(3000)
+ const UnlockedfileContent = fs.readFileSync(tmpAppJSONPath, 'utf-8')
+ // Verify in app.json that accounts data are not encrypted
+ await expect(UnlockedfileContent).toContain(accountsOperations)
+ await app.client.pause(1000)
+ },
+ TIMEOUT,
+ )
+})