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: Genesis, }, + { + key: 'kyber', + id: 'kyber', + url: urls.kyber, + logo: KYBER, + }, ]) 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 @@ + + + + Kyber network + Created with Sketch. + + + + + \ 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, + ) +})