mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-30 16:18:07 +01:00
b7033196d2
The `waitUntilCalled` utility now has a timeout. It will now throw an error if the stub is not called enough times, rather than blocking forever. The return type had to be changed to a function, so that we could throw when the timeout is triggered. I tried returning an error that rejected first, but if you don't handle the error synchronously Node.js will consider it to be an unhandled Promise rejected (even if it _is_ handled later on). I worked around this by resolving in the timeout case as well, so that there is never a "deferred" Promise exception in the timeout case. The returned function re-throws the error if it's given. That way there is never any unhandled Promise rejection.
547 lines
16 KiB
JavaScript
547 lines
16 KiB
JavaScript
import { strict as assert } from 'assert'
|
|
import sinon from 'sinon'
|
|
import MetaMetricsController from '../../../../app/scripts/controllers/metametrics'
|
|
import { ENVIRONMENT_TYPE_BACKGROUND } from '../../../../app/scripts/lib/enums'
|
|
import { createSegmentMock } from '../../../../app/scripts/lib/segment'
|
|
import {
|
|
METAMETRICS_ANONYMOUS_ID,
|
|
METAMETRICS_BACKGROUND_PAGE_OBJECT,
|
|
} from '../../../../shared/constants/metametrics'
|
|
import waitUntilCalled from '../../../lib/wait-until-called'
|
|
|
|
const segment = createSegmentMock(2, 10000)
|
|
const segmentLegacy = createSegmentMock(2, 10000)
|
|
|
|
const VERSION = '0.0.1-test'
|
|
const NETWORK = 'Mainnet'
|
|
const FAKE_CHAIN_ID = '0x1338'
|
|
const LOCALE = 'en_US'
|
|
const TEST_META_METRICS_ID = '0xabc'
|
|
|
|
const DEFAULT_TEST_CONTEXT = {
|
|
app: { name: 'MetaMask Extension', version: VERSION },
|
|
page: METAMETRICS_BACKGROUND_PAGE_OBJECT,
|
|
referrer: undefined,
|
|
userAgent: window.navigator.userAgent,
|
|
}
|
|
|
|
const DEFAULT_SHARED_PROPERTIES = {
|
|
chain_id: FAKE_CHAIN_ID,
|
|
locale: LOCALE.replace('_', '-'),
|
|
network: NETWORK,
|
|
environment_type: 'background',
|
|
}
|
|
|
|
const DEFAULT_EVENT_PROPERTIES = {
|
|
category: 'Unit Test',
|
|
revenue: undefined,
|
|
value: undefined,
|
|
currency: undefined,
|
|
...DEFAULT_SHARED_PROPERTIES,
|
|
}
|
|
|
|
const DEFAULT_PAGE_PROPERTIES = {
|
|
...DEFAULT_SHARED_PROPERTIES,
|
|
}
|
|
|
|
function getMockNetworkController(
|
|
chainId = FAKE_CHAIN_ID,
|
|
provider = { type: NETWORK },
|
|
) {
|
|
let networkStore = { chainId, provider }
|
|
const on = sinon.stub().withArgs('networkDidChange')
|
|
const updateState = (newState) => {
|
|
networkStore = { ...networkStore, ...newState }
|
|
on.getCall(0).args[1]()
|
|
}
|
|
return {
|
|
store: {
|
|
getState: () => networkStore,
|
|
updateState,
|
|
},
|
|
getCurrentChainId: () => networkStore.chainId,
|
|
getNetworkIdentifier: () => networkStore.provider.type,
|
|
on,
|
|
}
|
|
}
|
|
|
|
function getMockPreferencesStore({ currentLocale = LOCALE } = {}) {
|
|
let preferencesStore = {
|
|
currentLocale,
|
|
}
|
|
const subscribe = sinon.stub()
|
|
const updateState = (newState) => {
|
|
preferencesStore = { ...preferencesStore, ...newState }
|
|
subscribe.getCall(0).args[0](preferencesStore)
|
|
}
|
|
return {
|
|
getState: sinon.stub().returns(preferencesStore),
|
|
updateState,
|
|
subscribe,
|
|
}
|
|
}
|
|
|
|
function getMetaMetricsController({
|
|
participateInMetaMetrics = true,
|
|
metaMetricsId = TEST_META_METRICS_ID,
|
|
metaMetricsSendCount = 0,
|
|
preferencesStore = getMockPreferencesStore(),
|
|
networkController = getMockNetworkController(),
|
|
} = {}) {
|
|
return new MetaMetricsController({
|
|
segment,
|
|
segmentLegacy,
|
|
getNetworkIdentifier: networkController.getNetworkIdentifier.bind(
|
|
networkController,
|
|
),
|
|
getCurrentChainId: networkController.getCurrentChainId.bind(
|
|
networkController,
|
|
),
|
|
onNetworkDidChange: networkController.on.bind(
|
|
networkController,
|
|
'networkDidChange',
|
|
),
|
|
preferencesStore,
|
|
version: '0.0.1',
|
|
environment: 'test',
|
|
initState: {
|
|
participateInMetaMetrics,
|
|
metaMetricsId,
|
|
metaMetricsSendCount,
|
|
},
|
|
})
|
|
}
|
|
describe('MetaMetricsController', function () {
|
|
describe('constructor', function () {
|
|
it('should properly initialize', function () {
|
|
const metaMetricsController = getMetaMetricsController()
|
|
assert.strictEqual(metaMetricsController.version, VERSION)
|
|
assert.strictEqual(metaMetricsController.network, NETWORK)
|
|
assert.strictEqual(metaMetricsController.chainId, FAKE_CHAIN_ID)
|
|
assert.strictEqual(
|
|
metaMetricsController.state.participateInMetaMetrics,
|
|
true,
|
|
)
|
|
assert.strictEqual(
|
|
metaMetricsController.state.metaMetricsId,
|
|
TEST_META_METRICS_ID,
|
|
)
|
|
assert.strictEqual(metaMetricsController.locale, LOCALE.replace('_', '-'))
|
|
})
|
|
|
|
it('should update when network changes', function () {
|
|
const networkController = getMockNetworkController()
|
|
const metaMetricsController = getMetaMetricsController({
|
|
networkController,
|
|
})
|
|
assert.strictEqual(metaMetricsController.network, NETWORK)
|
|
networkController.store.updateState({
|
|
provider: {
|
|
type: 'NEW_NETWORK',
|
|
},
|
|
chainId: '0xaab',
|
|
})
|
|
assert.strictEqual(metaMetricsController.network, 'NEW_NETWORK')
|
|
assert.strictEqual(metaMetricsController.chainId, '0xaab')
|
|
})
|
|
|
|
it('should update when preferences changes', function () {
|
|
const preferencesStore = getMockPreferencesStore()
|
|
const metaMetricsController = getMetaMetricsController({
|
|
preferencesStore,
|
|
})
|
|
assert.strictEqual(metaMetricsController.network, NETWORK)
|
|
preferencesStore.updateState({
|
|
currentLocale: 'en_UK',
|
|
})
|
|
assert.strictEqual(metaMetricsController.locale, 'en-UK')
|
|
})
|
|
})
|
|
|
|
describe('generateMetaMetricsId', function () {
|
|
it('should generate an 0x prefixed hex string', function () {
|
|
const metaMetricsController = getMetaMetricsController()
|
|
assert.equal(
|
|
metaMetricsController.generateMetaMetricsId().startsWith('0x'),
|
|
true,
|
|
)
|
|
})
|
|
})
|
|
|
|
describe('setParticipateInMetaMetrics', function () {
|
|
it('should update the value of participateInMetaMetrics', function () {
|
|
const metaMetricsController = getMetaMetricsController({
|
|
participateInMetaMetrics: null,
|
|
metaMetricsId: null,
|
|
})
|
|
assert.equal(metaMetricsController.state.participateInMetaMetrics, null)
|
|
metaMetricsController.setParticipateInMetaMetrics(true)
|
|
assert.equal(metaMetricsController.state.participateInMetaMetrics, true)
|
|
metaMetricsController.setParticipateInMetaMetrics(false)
|
|
assert.equal(metaMetricsController.state.participateInMetaMetrics, false)
|
|
})
|
|
it('should generate and update the metaMetricsId when set to true', function () {
|
|
const metaMetricsController = getMetaMetricsController({
|
|
participateInMetaMetrics: null,
|
|
metaMetricsId: null,
|
|
})
|
|
assert.equal(metaMetricsController.state.metaMetricsId, null)
|
|
metaMetricsController.setParticipateInMetaMetrics(true)
|
|
assert.equal(typeof metaMetricsController.state.metaMetricsId, 'string')
|
|
})
|
|
it('should nullify the metaMetricsId when set to false', function () {
|
|
const metaMetricsController = getMetaMetricsController()
|
|
metaMetricsController.setParticipateInMetaMetrics(false)
|
|
assert.equal(metaMetricsController.state.metaMetricsId, null)
|
|
})
|
|
})
|
|
|
|
describe('setMetaMetricsSendCount', function () {
|
|
it('should update the send count in state', function () {
|
|
const metaMetricsController = getMetaMetricsController()
|
|
metaMetricsController.setMetaMetricsSendCount(1)
|
|
assert.equal(metaMetricsController.state.metaMetricsSendCount, 1)
|
|
})
|
|
})
|
|
|
|
describe('trackEvent', function () {
|
|
it('should not track an event if user is not participating in metametrics', function () {
|
|
const mock = sinon.mock(segment)
|
|
const metaMetricsController = getMetaMetricsController({
|
|
participateInMetaMetrics: false,
|
|
})
|
|
mock.expects('track').never()
|
|
metaMetricsController.trackEvent({
|
|
event: 'Fake Event',
|
|
category: 'Unit Test',
|
|
properties: {
|
|
test: 1,
|
|
},
|
|
})
|
|
mock.verify()
|
|
})
|
|
|
|
it('should track an event if user has not opted in, but isOptIn is true', function () {
|
|
const mock = sinon.mock(segment)
|
|
const metaMetricsController = getMetaMetricsController({
|
|
participateInMetaMetrics: false,
|
|
})
|
|
mock
|
|
.expects('track')
|
|
.once()
|
|
.withArgs({
|
|
event: 'Fake Event',
|
|
anonymousId: METAMETRICS_ANONYMOUS_ID,
|
|
context: DEFAULT_TEST_CONTEXT,
|
|
properties: {
|
|
test: 1,
|
|
...DEFAULT_EVENT_PROPERTIES,
|
|
},
|
|
})
|
|
metaMetricsController.trackEvent(
|
|
{
|
|
event: 'Fake Event',
|
|
category: 'Unit Test',
|
|
properties: {
|
|
test: 1,
|
|
},
|
|
},
|
|
{ isOptIn: true },
|
|
)
|
|
mock.verify()
|
|
})
|
|
|
|
it('should track an event during optin and allow for metaMetricsId override', function () {
|
|
const mock = sinon.mock(segment)
|
|
const metaMetricsController = getMetaMetricsController({
|
|
participateInMetaMetrics: false,
|
|
})
|
|
mock
|
|
.expects('track')
|
|
.once()
|
|
.withArgs({
|
|
event: 'Fake Event',
|
|
userId: 'TESTID',
|
|
context: DEFAULT_TEST_CONTEXT,
|
|
properties: {
|
|
test: 1,
|
|
...DEFAULT_EVENT_PROPERTIES,
|
|
},
|
|
})
|
|
metaMetricsController.trackEvent(
|
|
{
|
|
event: 'Fake Event',
|
|
category: 'Unit Test',
|
|
properties: {
|
|
test: 1,
|
|
},
|
|
},
|
|
{ isOptIn: true, metaMetricsId: 'TESTID' },
|
|
)
|
|
mock.verify()
|
|
})
|
|
|
|
it('should track a legacy event', function () {
|
|
const mock = sinon.mock(segmentLegacy)
|
|
const metaMetricsController = getMetaMetricsController()
|
|
mock
|
|
.expects('track')
|
|
.once()
|
|
.withArgs({
|
|
event: 'Fake Event',
|
|
userId: TEST_META_METRICS_ID,
|
|
context: DEFAULT_TEST_CONTEXT,
|
|
properties: {
|
|
test: 1,
|
|
...DEFAULT_EVENT_PROPERTIES,
|
|
},
|
|
})
|
|
metaMetricsController.trackEvent(
|
|
{
|
|
event: 'Fake Event',
|
|
category: 'Unit Test',
|
|
properties: {
|
|
test: 1,
|
|
},
|
|
},
|
|
{ matomoEvent: true },
|
|
)
|
|
mock.verify()
|
|
})
|
|
|
|
it('should track a non legacy event', function () {
|
|
const mock = sinon.mock(segment)
|
|
const metaMetricsController = getMetaMetricsController()
|
|
mock
|
|
.expects('track')
|
|
.once()
|
|
.withArgs({
|
|
event: 'Fake Event',
|
|
userId: TEST_META_METRICS_ID,
|
|
context: DEFAULT_TEST_CONTEXT,
|
|
properties: {
|
|
test: 1,
|
|
...DEFAULT_EVENT_PROPERTIES,
|
|
},
|
|
})
|
|
metaMetricsController.trackEvent({
|
|
event: 'Fake Event',
|
|
category: 'Unit Test',
|
|
properties: {
|
|
test: 1,
|
|
},
|
|
})
|
|
mock.verify()
|
|
})
|
|
|
|
it('should use anonymousId when metametrics send count is not trackable in send flow', function () {
|
|
const mock = sinon.mock(segment)
|
|
const metaMetricsController = getMetaMetricsController({
|
|
metaMetricsSendCount: 1,
|
|
})
|
|
mock
|
|
.expects('track')
|
|
.once()
|
|
.withArgs({
|
|
event: 'Send Fake Event',
|
|
anonymousId: METAMETRICS_ANONYMOUS_ID,
|
|
context: DEFAULT_TEST_CONTEXT,
|
|
properties: {
|
|
test: 1,
|
|
...DEFAULT_EVENT_PROPERTIES,
|
|
},
|
|
})
|
|
metaMetricsController.trackEvent({
|
|
event: 'Send Fake Event',
|
|
category: 'Unit Test',
|
|
properties: {
|
|
test: 1,
|
|
},
|
|
})
|
|
mock.verify()
|
|
})
|
|
|
|
it('should use user metametrics id when metametrics send count is trackable in send flow', function () {
|
|
const mock = sinon.mock(segment)
|
|
const metaMetricsController = getMetaMetricsController()
|
|
mock
|
|
.expects('track')
|
|
.once()
|
|
.withArgs({
|
|
event: 'Send Fake Event',
|
|
userId: TEST_META_METRICS_ID,
|
|
context: DEFAULT_TEST_CONTEXT,
|
|
properties: {
|
|
test: 1,
|
|
...DEFAULT_EVENT_PROPERTIES,
|
|
},
|
|
})
|
|
metaMetricsController.trackEvent(
|
|
{
|
|
event: 'Send Fake Event',
|
|
category: 'Unit Test',
|
|
properties: {
|
|
test: 1,
|
|
},
|
|
},
|
|
{ metaMetricsSendCount: 0 },
|
|
)
|
|
mock.verify()
|
|
})
|
|
|
|
it('should immediately flush queue if flushImmediately set to true', async function () {
|
|
const metaMetricsController = getMetaMetricsController()
|
|
const flushStub = sinon.stub(segment, 'flush')
|
|
const flushCalled = waitUntilCalled(flushStub, segment)
|
|
metaMetricsController.trackEvent(
|
|
{
|
|
event: 'Fake Event',
|
|
category: 'Unit Test',
|
|
},
|
|
{ flushImmediately: true },
|
|
)
|
|
assert.doesNotReject(flushCalled())
|
|
})
|
|
|
|
it('should throw if event or category not provided', function () {
|
|
const metaMetricsController = getMetaMetricsController()
|
|
assert.rejects(
|
|
() => metaMetricsController.trackEvent({ event: 'test' }),
|
|
/Must specify event and category\./u,
|
|
'must specify category',
|
|
)
|
|
|
|
assert.rejects(
|
|
() => metaMetricsController.trackEvent({ category: 'test' }),
|
|
/Must specify event and category\./u,
|
|
'must specify event',
|
|
)
|
|
})
|
|
|
|
it('should throw if provided sensitiveProperties, when excludeMetaMetricsId is true', function () {
|
|
const metaMetricsController = getMetaMetricsController()
|
|
assert.rejects(
|
|
() =>
|
|
metaMetricsController.trackEvent(
|
|
{
|
|
event: 'Fake Event',
|
|
category: 'Unit Test',
|
|
sensitiveProperties: { foo: 'bar' },
|
|
},
|
|
{ excludeMetaMetricsId: true },
|
|
),
|
|
/sensitiveProperties was specified in an event payload that also set the excludeMetaMetricsId flag/u,
|
|
)
|
|
})
|
|
|
|
it('should track sensitiveProperties in a separate, anonymous event', function () {
|
|
const metaMetricsController = getMetaMetricsController()
|
|
const spy = sinon.spy(segment, 'track')
|
|
metaMetricsController.trackEvent({
|
|
event: 'Fake Event',
|
|
category: 'Unit Test',
|
|
sensitiveProperties: { foo: 'bar' },
|
|
})
|
|
assert.ok(spy.calledTwice)
|
|
assert.ok(
|
|
spy.calledWith({
|
|
event: 'Fake Event',
|
|
anonymousId: METAMETRICS_ANONYMOUS_ID,
|
|
context: DEFAULT_TEST_CONTEXT,
|
|
properties: {
|
|
foo: 'bar',
|
|
...DEFAULT_EVENT_PROPERTIES,
|
|
},
|
|
}),
|
|
)
|
|
assert.ok(
|
|
spy.calledWith({
|
|
event: 'Fake Event',
|
|
userId: TEST_META_METRICS_ID,
|
|
context: DEFAULT_TEST_CONTEXT,
|
|
properties: DEFAULT_EVENT_PROPERTIES,
|
|
}),
|
|
)
|
|
})
|
|
})
|
|
|
|
describe('trackPage', function () {
|
|
it('should track a page view', function () {
|
|
const mock = sinon.mock(segment)
|
|
const metaMetricsController = getMetaMetricsController()
|
|
mock
|
|
.expects('page')
|
|
.once()
|
|
.withArgs({
|
|
name: 'home',
|
|
userId: TEST_META_METRICS_ID,
|
|
context: DEFAULT_TEST_CONTEXT,
|
|
properties: {
|
|
params: null,
|
|
...DEFAULT_PAGE_PROPERTIES,
|
|
},
|
|
})
|
|
metaMetricsController.trackPage({
|
|
name: 'home',
|
|
params: null,
|
|
environmentType: ENVIRONMENT_TYPE_BACKGROUND,
|
|
page: METAMETRICS_BACKGROUND_PAGE_OBJECT,
|
|
})
|
|
mock.verify()
|
|
})
|
|
|
|
it('should not track a page view if user is not participating in metametrics', function () {
|
|
const mock = sinon.mock(segment)
|
|
const metaMetricsController = getMetaMetricsController({
|
|
participateInMetaMetrics: false,
|
|
})
|
|
mock.expects('page').never()
|
|
metaMetricsController.trackPage({
|
|
name: 'home',
|
|
params: null,
|
|
environmentType: ENVIRONMENT_TYPE_BACKGROUND,
|
|
page: METAMETRICS_BACKGROUND_PAGE_OBJECT,
|
|
})
|
|
mock.verify()
|
|
})
|
|
|
|
it('should track a page view if isOptInPath is true and user not yet opted in', function () {
|
|
const mock = sinon.mock(segment)
|
|
const metaMetricsController = getMetaMetricsController({
|
|
preferencesStore: getMockPreferencesStore({
|
|
participateInMetaMetrics: null,
|
|
}),
|
|
})
|
|
mock
|
|
.expects('page')
|
|
.once()
|
|
.withArgs({
|
|
name: 'home',
|
|
userId: TEST_META_METRICS_ID,
|
|
context: DEFAULT_TEST_CONTEXT,
|
|
properties: {
|
|
params: null,
|
|
...DEFAULT_PAGE_PROPERTIES,
|
|
},
|
|
})
|
|
metaMetricsController.trackPage(
|
|
{
|
|
name: 'home',
|
|
params: null,
|
|
environmentType: ENVIRONMENT_TYPE_BACKGROUND,
|
|
page: METAMETRICS_BACKGROUND_PAGE_OBJECT,
|
|
},
|
|
{ isOptInPath: true },
|
|
)
|
|
mock.verify()
|
|
})
|
|
})
|
|
|
|
afterEach(function () {
|
|
// flush the queues manually after each test
|
|
segment.flush()
|
|
segmentLegacy.flush()
|
|
sinon.restore()
|
|
})
|
|
})
|