Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Metametrics #6171

Merged
merged 50 commits into from
Mar 5, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
058f658
Add metametrics provider and util.
danjm Jan 29, 2019
41e51b3
Add backend api and state for participating in metametrics.
danjm Jan 29, 2019
0849337
Add frontend action for participating in metametrics.
danjm Jan 29, 2019
87c06c1
Add metametrics opt-in screen.
danjm Jan 29, 2019
d39011f
Add metametrics events to first time flow.
danjm Jan 29, 2019
29ff3b6
Add metametrics events for route changes
danjm Jan 29, 2019
6cf113f
Add metametrics events for send and confirm screens
danjm Jan 29, 2019
c29348d
Add metametrics events to dropdowns, transactions, log in and out, se…
danjm Jan 29, 2019
35e3175
Ensures each log in is measured as a new visit by metametrics.
danjm Jan 29, 2019
bee4e15
Ensure metametrics is called with an empty string for dimensions para…
danjm Jan 29, 2019
38ad6e1
Adds opt in metametrics modal after unlock for existing users
danjm Jan 30, 2019
9ce1182
Adds settings page toggle for opting in and out of MetaMetrics
danjm Jan 30, 2019
e4ced75
Switch metametrics dimensions to page level scope
danjm Feb 6, 2019
3284348
Lint, test and translation fixes for metametrics.
danjm Feb 6, 2019
2132777
Update design for metametrics opt-in screen
danjm Feb 6, 2019
dc97baf
Complete responsive styling of metametrics-opt-in modal
danjm Feb 19, 2019
a1145ea
Use new chart image on metrics opt in screens
danjm Feb 19, 2019
88e0cc7
Incorporate the metametrics opt-in screen into the new onboarding flow
danjm Feb 27, 2019
4c8eb5c
Update e2e tests to accomodate metametrics changes
danjm Feb 27, 2019
65caafc
Mock out metametrics network requests in integration tests
danjm Feb 27, 2019
ae58572
Fix tx-list integration test to support metametrics provider.
danjm Feb 28, 2019
ec2934d
Send number of tokens and accounts data with every metametrics event.
danjm Feb 28, 2019
556bc99
Update metametrics event descriptor schema and add new events.
danjm Feb 28, 2019
d5d54d7
Fix import tos bug and send gas button bug due to metametrics changes.
danjm Feb 28, 2019
5fe45cf
Various small fixes on the metametrics branch.
danjm Mar 1, 2019
88055b5
Add origin custom variable type to metametrics.util
danjm Mar 1, 2019
2061a61
Fix names of onboarding complete actions (metametrics).
danjm Mar 1, 2019
268ae09
Fix names of Metrics Options actions (metametrics).
danjm Mar 1, 2019
debd058
Clean up code related to metametrics.
danjm Mar 4, 2019
d33f434
Fix bad merge conflict resolution and improve promise handling in sen…
danjm Mar 4, 2019
c4ee46d
Don't send a second metrics event if user has gone back during first …
danjm Mar 4, 2019
f20eb10
Collect metametrics on going back from onboarding create/import.
danjm Mar 4, 2019
55b09f2
Add missing custom variable constants for metametrics
danjm Mar 4, 2019
4a74b0a
Fix metametrics provider
danjm Mar 4, 2019
9a6f943
Make height of opt-in modal responsive.
danjm Mar 4, 2019
8eadc38
Adjust text content for opt-in modal.
danjm Mar 4, 2019
8ea5215
Update metametrics event names and clean up code in opt-in-modal
danjm Mar 4, 2019
8b7b253
Put phishing warning step next to last in onboarding flow
danjm Mar 4, 2019
ae8d9c0
Link terms of service on create and import screens of first time flow
danjm Mar 4, 2019
c5641b1
Add subtext to options on the onboarding select action screen.
danjm Mar 4, 2019
d629c2e
Fix styling of bullet points on end of onboarding screen.
danjm Mar 4, 2019
12f19bb
Combine phishing warning and congratulations screens.
danjm Mar 4, 2019
c762b8b
Fix placement of users if unlocking after an incomplete onboarding im…
danjm Mar 4, 2019
48508c6
Fix capitalization in opt-in screen
danjm Mar 4, 2019
d904233
Fix last onboarding screen translations
danjm Mar 4, 2019
a3afffb
Add link to 'Learn More' on the last screen of onboarding
danjm Mar 4, 2019
1e5d8fa
Code clean up: metametrics branch
danjm Mar 4, 2019
1f3bdc9
Update e2e tests for phishing warning step removal
danjm Mar 5, 2019
2dbc223
e2e tests passing on metametrics branch
danjm Mar 5, 2019
53356fd
Different tracking urls for metametrics on development and prod
danjm Mar 5, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 21 additions & 3 deletions app/_locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -470,15 +470,21 @@
"message": "Tips on storing it safely"
},
"endOfFlowMessage3": {
"message": "Save a backup in multiple places"
"message": "Save a backup in multiple places."
},
"endOfFlowMessage4": {
"message": "Never tell anyone"
"message": "Never share the phrase with anyone."
},
"endOfFlowMessage5": {
"message": "If you need to back your seed phrase again, you can find it in Settings -> Security."
"message": "Be careful of phishing! MetaMask will never spontaneously ask for your seed phrase."
},
"endOfFlowMessage6": {
"message": "If you need to back your up seed phrase again, you can find it in Settings -> Security."
},
"endOfFlowMessage7": {
"message": "If you ever have questions or see something fishy, email [email protected]."
},
"endOfFlowMessage8": {
"message": "MetaMask cannot recover your seedphrase. Learn more."
},
"ensNameNotFound": {
Expand Down Expand Up @@ -689,6 +695,9 @@
"importWallet": {
"message": "Import Wallet"
},
"importYourExisting": {
"message": "Import your existing wallet using a 12 word seed phrase"
},
"imported": {
"message": "Imported",
"description": "status showing that an account has been fully loaded into the keyring"
Expand Down Expand Up @@ -1000,6 +1009,12 @@
"originalTotal": {
"message": "Original Total"
},
"participateInMetaMetrics": {
"message": "Participate in MetaMetrics"
},
"participateInMetaMetricsDesciption": {
"message": "Participate in MetaMetrics to help us make MetaMask better"
},
"password": {
"message": "Password"
},
Expand Down Expand Up @@ -1435,6 +1450,9 @@
"testFaucet": {
"message": "Test Faucet"
},
"thisWillCreate": {
"message": "This will create a new wallet and seed phrase"
},
"tips": {
"message": "Tips"
},
Expand Down
8 changes: 8 additions & 0 deletions app/images/metrics-chart.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
44 changes: 43 additions & 1 deletion app/scripts/controllers/preferences.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const ObservableStore = require('obs-store')
const normalizeAddress = require('eth-sig-util').normalize
const { isValidAddress } = require('ethereumjs-util')
const { isValidAddress, sha3, bufferToHex } = require('ethereumjs-util')
const extend = require('xtend')


Expand Down Expand Up @@ -42,6 +42,8 @@ class PreferencesController {
// perform sensitive operations.
featureFlags: {},
knownMethodData: {},
participateInMetaMetrics: null,
firstTimeFlowType: null,
currentLocale: opts.initLangCode,
identities: {},
lostIdentities: {},
Expand All @@ -52,6 +54,8 @@ class PreferencesController {
},
completedOnboarding: false,
completedUiMigration: true,
metaMetricsId: null,
metaMetricsSendCount: 0,
}, opts.initState)

this.diagnostics = opts.diagnostics
Expand Down Expand Up @@ -92,6 +96,44 @@ class PreferencesController {
this.store.updateState({ useBlockie: val })
}

/**
* Setter for the `participateInMetaMetrics` property
*
* @param {boolean} bool Whether or not the user wants to participate in MetaMetrics
* @returns {string|null} the string of the new metametrics id, or null if not set
*
*/
setParticipateInMetaMetrics (bool) {
this.store.updateState({ participateInMetaMetrics: bool })
let metaMetricsId = null
if (bool && !this.store.getState().metaMetricsId) {
metaMetricsId = bufferToHex(sha3(String(Date.now()) + String(Math.round(Math.random() * Number.MAX_SAFE_INTEGER))))
this.store.updateState({ metaMetricsId })
} else if (bool === false) {
this.store.updateState({ metaMetricsId })
}
return metaMetricsId
}

setMetaMetricsSendCount (val) {
this.store.updateState({ metaMetricsSendCount: val })
}

getMetaMetricsSendCount () {
return this.store.getState().metaMetricsSendCount
}

/**
* Setter for the `firstTimeFlowType` property
*
* @param {String} type Indicates the type of first time flow - create or import - the user wishes to follow
*
*/
setFirstTimeFlowType (type) {
this.store.updateState({ firstTimeFlowType: type })
}


getSuggestedTokens () {
return this.store.getState().suggestedTokens
}
Expand Down
41 changes: 41 additions & 0 deletions app/scripts/metamask-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,9 @@ module.exports = class MetamaskController extends EventEmitter {
getState: (cb) => cb(null, this.getState()),
setCurrentCurrency: this.setCurrentCurrency.bind(this),
setUseBlockie: this.setUseBlockie.bind(this),
setParticipateInMetaMetrics: this.setParticipateInMetaMetrics.bind(this),
setMetaMetricsSendCount: this.setMetaMetricsSendCount.bind(this),
setFirstTimeFlowType: this.setFirstTimeFlowType.bind(this),
setCurrentLocale: this.setCurrentLocale.bind(this),
markAccountsFound: this.markAccountsFound.bind(this),
markPasswordForgotten: this.markPasswordForgotten.bind(this),
Expand Down Expand Up @@ -1624,6 +1627,44 @@ module.exports = class MetamaskController extends EventEmitter {
}
}

/**
* Sets whether or not the user will have usage data tracked with MetaMetrics
* @param {boolean} bool - True for users that wish to opt-in, false for users that wish to remain out.
* @param {Function} cb - A callback function called when complete.
*/
setParticipateInMetaMetrics (bool, cb) {
try {
const metaMetricsId = this.preferencesController.setParticipateInMetaMetrics(bool)
cb(null, metaMetricsId)
} catch (err) {
cb(err)
}
}

setMetaMetricsSendCount (val, cb) {
try {
this.preferencesController.setMetaMetricsSendCount(val)
cb(null)
} catch (err) {
cb(err)
}
}

/**
* Sets the type of first time flow the user wishes to follow: create or import
* @param {String} type - Indicates the type of first time flow the user wishes to follow
* @param {Function} cb - A callback function called when complete.
*/
setFirstTimeFlowType (type, cb) {
try {
this.preferencesController.setFirstTimeFlowType(type)
cb(null)
} catch (err) {
cb(err)
}
}


/**
* A method for setting a user's current locale, affecting the language rendered.
* @param {string} key - Locale identifier.
Expand Down
3 changes: 2 additions & 1 deletion development/states/tx-list-items.json
Original file line number Diff line number Diff line change
Expand Up @@ -1403,5 +1403,6 @@
],
"priceAndTimeEstimatesLastRetrieved": 1541527901281,
"errors": {}
}
},
"confirmTransaction": {}
}
1 change: 1 addition & 0 deletions test/e2e/beta/fetch-mocks.js

Large diffs are not rendered by default.

15 changes: 8 additions & 7 deletions test/e2e/beta/from-import-beta-ui.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ describe('Using MetaMask with an existing account', function () {
'Promise.resolve({ json: () => Promise.resolve(JSON.parse(\'' + fetchMockResponses.ethGasBasic + '\')) }); } else if ' +
'(args[0] === "https://ethgasstation.info/json/predictTable.json") { return ' +
'Promise.resolve({ json: () => Promise.resolve(JSON.parse(\'' + fetchMockResponses.ethGasPredictTable + '\')) }); } else if ' +
'(args[0].match(/chromeextensionmm/)) { return ' +
'Promise.resolve({ json: () => Promise.resolve(JSON.parse(\'' + fetchMockResponses.metametrics + '\')) }); } else if ' +
'(args[0] === "https://dev.blockscale.net/api/gasexpress.json") { return ' +
'Promise.resolve({ json: () => Promise.resolve(JSON.parse(\'' + fetchMockResponses.gasExpress + '\')) }); } ' +
'return window.origFetch(...args); }'
Expand Down Expand Up @@ -110,6 +112,12 @@ describe('Using MetaMask with an existing account', function () {
await delay(largeDelayMs)
})

it('clicks the "No thanks" option on the metametrics opt-in screen', async () => {
const optOutButton = await findElement(driver, By.css('.btn-default'))
optOutButton.click()
await delay(largeDelayMs)
})

it('imports a seed phrase', async () => {
const [seedTextArea] = await findElements(driver, By.css('textarea.first-time-flow__textarea'))
await seedTextArea.sendKeys(testSeedPhrase)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

screen shot 2019-02-28 at 10 50 17 am

The TOS needs to be clicked after the seed phrase and password is entered.

Expand All @@ -128,13 +136,6 @@ describe('Using MetaMask with an existing account', function () {
await delay(regularDelayMs)
})

it('clicks through the security warning screen', async () => {
await findElement(driver, By.xpath(`//div[contains(text(), 'Protect Your Keys!')]`))
const nextScreen = await findElement(driver, By.css('button.first-time-flow__button'))
await nextScreen.click()
await delay(regularDelayMs)
})

it('clicks through the success screen', async () => {
await findElement(driver, By.xpath(`//div[contains(text(), 'Congratulations')]`))
const doneButton = await findElement(driver, By.css('button.first-time-flow__button'))
Expand Down
31 changes: 24 additions & 7 deletions test/e2e/beta/metamask-beta-responsive-ui.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const {
loadExtension,
verboseReportOnFailure,
} = require('./helpers')
const fetchMockResponses = require('./fetch-mocks.js')

describe('MetaMask', function () {
let extensionId
Expand Down Expand Up @@ -61,6 +62,23 @@ describe('MetaMask', function () {
await driver.get(extensionUrl)
})

beforeEach(async function () {
await driver.executeScript(
'window.origFetch = window.fetch.bind(window);' +
'window.fetch = ' +
'(...args) => { ' +
'if (args[0] === "https://ethgasstation.info/json/ethgasAPI.json") { return ' +
'Promise.resolve({ json: () => Promise.resolve(JSON.parse(\'' + fetchMockResponses.ethGasBasic + '\')) }); } else if ' +
'(args[0] === "https://ethgasstation.info/json/predictTable.json") { return ' +
'Promise.resolve({ json: () => Promise.resolve(JSON.parse(\'' + fetchMockResponses.ethGasPredictTable + '\')) }); } else if ' +
'(args[0].match(/chromeextensionmm/)) { return ' +
'Promise.resolve({ json: () => Promise.resolve(JSON.parse(\'' + fetchMockResponses.metametrics + '\')) }); } else if ' +
'(args[0] === "https://dev.blockscale.net/api/gasexpress.json") { return ' +
'Promise.resolve({ json: () => Promise.resolve(JSON.parse(\'' + fetchMockResponses.gasExpress + '\')) }); } ' +
'return window.origFetch(...args); }'
)
})

afterEach(async function () {
if (process.env.SELENIUM_BROWSER === 'chrome') {
const errors = await checkBrowserForConsoleErrors(driver)
Expand Down Expand Up @@ -93,6 +111,12 @@ describe('MetaMask', function () {
await delay(largeDelayMs)
})

it('clicks the "I agree" option on the metametrics opt-in screen', async () => {
const optOutButton = await findElement(driver, By.css('.btn-confirm'))
optOutButton.click()
await delay(largeDelayMs)
})

it('accepts a secure password', async () => {
const passwordBox = await findElement(driver, By.css('.first-time-flow__form #create-password'))
const passwordBoxConfirm = await findElement(driver, By.css('.first-time-flow__form #confirm-password'))
Expand All @@ -108,13 +132,6 @@ describe('MetaMask', function () {
await delay(regularDelayMs)
})

it('clicks through the security warning screen', async () => {
await findElement(driver, By.xpath(`//div[contains(text(), 'Protect Your Keys!')]`))
const nextScreen = await findElement(driver, By.css('button.first-time-flow__button'))
await nextScreen.click()
await delay(regularDelayMs)
})

let seedPhrase

it('reveals the seed phrase', async () => {
Expand Down
23 changes: 14 additions & 9 deletions test/e2e/beta/metamask-beta-ui.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ describe('MetaMask', function () {
'Promise.resolve({ json: () => Promise.resolve(JSON.parse(\'' + fetchMockResponses.ethGasBasic + '\')) }); } else if ' +
'(args[0] === "https://ethgasstation.info/json/predictTable.json") { return ' +
'Promise.resolve({ json: () => Promise.resolve(JSON.parse(\'' + fetchMockResponses.ethGasPredictTable + '\')) }); } else if ' +
'(args[0].match(/chromeextensionmm/)) { return ' +
'Promise.resolve({ json: () => Promise.resolve(JSON.parse(\'' + fetchMockResponses.metametrics + '\')) }); } else if ' +
'(args[0] === "https://dev.blockscale.net/api/gasexpress.json") { return ' +
'Promise.resolve({ json: () => Promise.resolve(JSON.parse(\'' + fetchMockResponses.gasExpress + '\')) }); } ' +
'return window.origFetch(...args); }'
Expand Down Expand Up @@ -114,6 +116,12 @@ describe('MetaMask', function () {
await delay(largeDelayMs)
})

it('clicks the "No thanks" option on the metametrics opt-in screen', async () => {
const optOutButton = await findElement(driver, By.css('.btn-default'))
optOutButton.click()
await delay(largeDelayMs)
})

it('accepts a secure password', async () => {
const passwordBox = await findElement(driver, By.css('.first-time-flow__form #create-password'))
const passwordBoxConfirm = await findElement(driver, By.css('.first-time-flow__form #confirm-password'))
Expand All @@ -129,13 +137,6 @@ describe('MetaMask', function () {
await delay(regularDelayMs)
})

it('clicks through the security warning screen', async () => {
await findElement(driver, By.xpath(`//div[contains(text(), 'Protect Your Keys!')]`))
const nextScreen = await findElement(driver, By.css('button.first-time-flow__button'))
await nextScreen.click()
await delay(regularDelayMs)
})

let seedPhrase

it('reveals the seed phrase', async () => {
Expand Down Expand Up @@ -688,12 +689,16 @@ describe('MetaMask', function () {
})

it('rejects a transaction', async () => {
await delay(tinyDelayMs / 2)
const rejectButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Reject')]`), 10000)
await delay(tinyDelayMs / 2)
await rejectButton.click()
await delay(regularDelayMs)

const navigationElement = await findElement(driver, By.css('.confirm-page-container-navigation'))
await delay(tinyDelayMs / 2)
const navigationText = await navigationElement.getText()
await delay(tinyDelayMs / 2)
assert.equal(navigationText.includes('3'), true, 'transaction rejected')
})

Expand Down Expand Up @@ -1124,7 +1129,7 @@ describe('MetaMask', function () {
const txValues = await findElements(driver, By.css('.transaction-list-item__amount--primary'))
await driver.wait(until.elementTextMatches(txValues[0], /-7\s*TST/))
const txStatuses = await findElements(driver, By.css('.transaction-list-item__action'))
await driver.wait(until.elementTextMatches(txStatuses[0], /Sent\sToken/))
await driver.wait(until.elementTextMatches(txStatuses[0], /Sent\sToken/), 10000)

const walletBalance = await findElement(driver, By.css('.wallet-balance'))
await walletBalance.click()
Expand All @@ -1137,7 +1142,7 @@ describe('MetaMask', function () {
// or possibly until we use latest version of firefox in the tests
if (process.env.SELENIUM_BROWSER !== 'firefox') {
const tokenBalanceAmount = await findElements(driver, By.css('.transaction-view-balance__primary-balance'))
await driver.wait(until.elementTextMatches(tokenBalanceAmount[0], /43\s*TST/))
await driver.wait(until.elementTextMatches(tokenBalanceAmount[0], /43\s*TST/), 10000)
}
})
})
Expand Down
8 changes: 8 additions & 0 deletions test/integration/lib/confirm-sig-requests.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const {
timeout,
queryAsync,
} = require('../../lib/util')
const fetchMockResponses = require('../../e2e/beta/fetch-mocks.js')

QUnit.module('confirm sig requests')

Expand All @@ -19,6 +20,13 @@ async function runConfirmSigRequestsTest (assert, done) {
selectState.val('confirm sig requests')
reactTriggerChange(selectState[0])

global.fetch = (...args) => {
if (args[0].match(/chromeextensionmm/)) {
return Promise.resolve({ json: () => Promise.resolve(JSON.parse(fetchMockResponses.metametrics)) })
}
return window.fetch(...args)
}

const pendingRequestItem = $.find('.transaction-list-item .transaction-list-item__grid')

if (pendingRequestItem[2]) {
Expand Down
Loading