mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 01:47:00 +01:00
Introduce action metrics for mv3 service worker restart (#18346)
* fix dapp interaction e2e test * wip * add sentry post request mock * fix console errors * fix scripting console error and remove e2e test unnecessary check * clean up * remove e2e test * stop skipping test * fixing build mv3 job * fixing unit tests * fixing unit tests * fixing unit tests * update coverages * revert skip mv3 e2e test * remove IN_TEST on the npm script * remove console.log * revert aria label changes * revert aria label changes * revert permission changes * revert permission changes * implement sw restart delay tracking * fix rebase
This commit is contained in:
parent
18a21ca57e
commit
fbd68d4a3f
@ -556,6 +556,10 @@ export function setupController(initState, initLangCode, overrides) {
|
|||||||
if (message.name === WORKER_KEEP_ALIVE_MESSAGE) {
|
if (message.name === WORKER_KEEP_ALIVE_MESSAGE) {
|
||||||
// To test un-comment this line and wait for 1 minute. An error should be shown on MetaMask UI.
|
// To test un-comment this line and wait for 1 minute. An error should be shown on MetaMask UI.
|
||||||
remotePort.postMessage({ name: ACK_KEEP_ALIVE_MESSAGE });
|
remotePort.postMessage({ name: ACK_KEEP_ALIVE_MESSAGE });
|
||||||
|
|
||||||
|
controller.appStateController.setServiceWorkerLastActiveTime(
|
||||||
|
Date.now(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -51,6 +51,7 @@ export default class AppStateController extends EventEmitter {
|
|||||||
'0x5': true,
|
'0x5': true,
|
||||||
'0x539': true,
|
'0x539': true,
|
||||||
},
|
},
|
||||||
|
serviceWorkerLastActiveTime: 0,
|
||||||
});
|
});
|
||||||
this.timer = null;
|
this.timer = null;
|
||||||
|
|
||||||
@ -362,4 +363,10 @@ export default class AppStateController extends EventEmitter {
|
|||||||
getCurrentPopupId() {
|
getCurrentPopupId() {
|
||||||
return this.store.getState().currentPopupId;
|
return this.store.getState().currentPopupId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setServiceWorkerLastActiveTime(serviceWorkerLastActiveTime) {
|
||||||
|
this.store.updateState({
|
||||||
|
serviceWorkerLastActiveTime,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -121,6 +121,7 @@ import { isMain, isFlask } from '../../shared/constants/environment';
|
|||||||
// eslint-disable-next-line import/order
|
// eslint-disable-next-line import/order
|
||||||
import { DesktopController } from '@metamask/desktop/dist/controllers/desktop';
|
import { DesktopController } from '@metamask/desktop/dist/controllers/desktop';
|
||||||
///: END:ONLY_INCLUDE_IN
|
///: END:ONLY_INCLUDE_IN
|
||||||
|
import { ACTION_QUEUE_METRICS_E2E_TEST } from '../../shared/constants/test-flags';
|
||||||
import {
|
import {
|
||||||
onMessageReceived,
|
onMessageReceived,
|
||||||
checkForMultipleVersionsRunning,
|
checkForMultipleVersionsRunning,
|
||||||
@ -695,6 +696,7 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
this.keyringController.memStore.subscribe((state) =>
|
this.keyringController.memStore.subscribe((state) =>
|
||||||
this._onKeyringControllerUpdate(state),
|
this._onKeyringControllerUpdate(state),
|
||||||
);
|
);
|
||||||
|
|
||||||
this.keyringController.on('unlock', () => this._onUnlock());
|
this.keyringController.on('unlock', () => this._onUnlock());
|
||||||
this.keyringController.on('lock', () => this._onLock());
|
this.keyringController.on('lock', () => this._onLock());
|
||||||
|
|
||||||
@ -1190,6 +1192,25 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (isManifestV3 && globalThis.isFirstTimeProfileLoaded === false) {
|
||||||
|
const { serviceWorkerLastActiveTime } =
|
||||||
|
this.appStateController.store.getState();
|
||||||
|
const metametricsPayload = {
|
||||||
|
category: EVENT.SOURCE.SERVICE_WORKERS,
|
||||||
|
event: EVENT_NAMES.SERVICE_WORKER_RESTARTED,
|
||||||
|
properties: {
|
||||||
|
service_worker_restarted_time:
|
||||||
|
Date.now() - serviceWorkerLastActiveTime,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.metaMetricsController.trackEvent(metametricsPayload);
|
||||||
|
} catch (e) {
|
||||||
|
log.warn('Failed to track service worker restart metric:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.metamaskMiddleware = createMetamaskMiddleware({
|
this.metamaskMiddleware = createMetamaskMiddleware({
|
||||||
static: {
|
static: {
|
||||||
eth_syncing: false,
|
eth_syncing: false,
|
||||||
@ -2627,7 +2648,7 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
try {
|
try {
|
||||||
// Automatic login via config password
|
// Automatic login via config password
|
||||||
const password = process.env.CONF?.PASSWORD;
|
const password = process.env.CONF?.PASSWORD;
|
||||||
if (password) {
|
if (password && !process.env.IN_TEST) {
|
||||||
await this.submitPassword(password);
|
await this.submitPassword(password);
|
||||||
}
|
}
|
||||||
// Automatic login via storage encryption key
|
// Automatic login via storage encryption key
|
||||||
@ -2960,6 +2981,13 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
* @returns {} keyState
|
* @returns {} keyState
|
||||||
*/
|
*/
|
||||||
async addNewAccount(accountCount) {
|
async addNewAccount(accountCount) {
|
||||||
|
const isActionMetricsQueueE2ETest =
|
||||||
|
this.appStateController.store.getState()[ACTION_QUEUE_METRICS_E2E_TEST];
|
||||||
|
|
||||||
|
if (process.env.IN_TEST && isActionMetricsQueueE2ETest) {
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 5_000));
|
||||||
|
}
|
||||||
|
|
||||||
const [primaryKeyring] = this.keyringController.getKeyringsByType(
|
const [primaryKeyring] = this.keyringController.getKeyringsByType(
|
||||||
KeyringType.hdKeyTree,
|
KeyringType.hdKeyTree,
|
||||||
);
|
);
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
"build:test": "SEGMENT_HOST='https://api.segment.io' SEGMENT_WRITE_KEY='FAKE' SENTRY_DSN_DEV=https://fake@sentry.io/0000000 PORTFOLIO_URL=http://127.0.0.1:8080 yarn build test",
|
"build:test": "SEGMENT_HOST='https://api.segment.io' SEGMENT_WRITE_KEY='FAKE' SENTRY_DSN_DEV=https://fake@sentry.io/0000000 PORTFOLIO_URL=http://127.0.0.1:8080 yarn build test",
|
||||||
"build:test:flask": "yarn build test --build-type flask",
|
"build:test:flask": "yarn build test --build-type flask",
|
||||||
"build:test:mv3": "ENABLE_MV3=true SEGMENT_HOST='https://api.segment.io' SEGMENT_WRITE_KEY='FAKE' SENTRY_DSN_DEV=https://fake@sentry.io/0000000 PORTFOLIO_URL=http://127.0.0.1:8080 yarn build test",
|
"build:test:mv3": "ENABLE_MV3=true SEGMENT_HOST='https://api.segment.io' SEGMENT_WRITE_KEY='FAKE' SENTRY_DSN_DEV=https://fake@sentry.io/0000000 PORTFOLIO_URL=http://127.0.0.1:8080 yarn build test",
|
||||||
|
"build:test:dev:mv3": "ENABLE_MV3=true SEGMENT_HOST='https://api.segment.io' SEGMENT_WRITE_KEY='FAKE' SENTRY_DSN_DEV=https://fake@sentry.io/0000000 PORTFOLIO_URL=http://127.0.0.1:8080 yarn build:dev testDev --apply-lavamoat=false",
|
||||||
"test": "yarn lint && yarn test:unit && yarn test:unit:jest",
|
"test": "yarn lint && yarn test:unit && yarn test:unit:jest",
|
||||||
"dapp": "node development/static-server.js node_modules/@metamask/test-dapp/dist --port 8080",
|
"dapp": "node development/static-server.js node_modules/@metamask/test-dapp/dist --port 8080",
|
||||||
"dapp-chain": "GANACHE_ARGS='-b 2' concurrently -k -n ganache,dapp -p '[{time}][{name}]' 'yarn ganache:start' 'sleep 5 && yarn dapp'",
|
"dapp-chain": "GANACHE_ARGS='-b 2' concurrently -k -n ganache,dapp -p '[{time}][{name}]' 'yarn ganache:start' 'sleep 5 && yarn dapp'",
|
||||||
@ -32,6 +33,7 @@
|
|||||||
"test:unit:mocha": "node ./test/run-unit-tests.js --mocha",
|
"test:unit:mocha": "node ./test/run-unit-tests.js --mocha",
|
||||||
"test:e2e:chrome": "SELENIUM_BROWSER=chrome node test/e2e/run-all.js",
|
"test:e2e:chrome": "SELENIUM_BROWSER=chrome node test/e2e/run-all.js",
|
||||||
"test:e2e:chrome:snaps": "SELENIUM_BROWSER=chrome node test/e2e/run-all.js --snaps",
|
"test:e2e:chrome:snaps": "SELENIUM_BROWSER=chrome node test/e2e/run-all.js --snaps",
|
||||||
|
"test:e2e:chrome:mv3": "SELENIUM_BROWSER=chrome node test/e2e/run-all.js --mv3",
|
||||||
"test:e2e:firefox": "SELENIUM_BROWSER=firefox node test/e2e/run-all.js",
|
"test:e2e:firefox": "SELENIUM_BROWSER=firefox node test/e2e/run-all.js",
|
||||||
"test:e2e:firefox:snaps": "SELENIUM_BROWSER=firefox node test/e2e/run-all.js --snaps",
|
"test:e2e:firefox:snaps": "SELENIUM_BROWSER=firefox node test/e2e/run-all.js --snaps",
|
||||||
"test:e2e:single": "node test/e2e/run-e2e-test.js",
|
"test:e2e:single": "node test/e2e/run-e2e-test.js",
|
||||||
|
@ -374,6 +374,7 @@ export const EVENT_NAMES = {
|
|||||||
ONBOARDING_WALLET_IMPORT_ATTEMPTED: 'Wallet Import Attempted',
|
ONBOARDING_WALLET_IMPORT_ATTEMPTED: 'Wallet Import Attempted',
|
||||||
ONBOARDING_WALLET_VIDEO_PLAY: 'SRP Intro Video Played',
|
ONBOARDING_WALLET_VIDEO_PLAY: 'SRP Intro Video Played',
|
||||||
ONBOARDING_TWITTER_CLICK: 'External Link Clicked',
|
ONBOARDING_TWITTER_CLICK: 'External Link Clicked',
|
||||||
|
SERVICE_WORKER_RESTARTED: 'Service Worker Restarted',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const EVENT = {
|
export const EVENT = {
|
||||||
@ -447,6 +448,7 @@ export const EVENT = {
|
|||||||
DAPP: 'dapp',
|
DAPP: 'dapp',
|
||||||
USER: 'user',
|
USER: 'user',
|
||||||
},
|
},
|
||||||
|
SERVICE_WORKERS: 'service_workers',
|
||||||
},
|
},
|
||||||
LOCATION: {
|
LOCATION: {
|
||||||
TOKEN_DETAILS: 'token_details',
|
TOKEN_DETAILS: 'token_details',
|
||||||
|
1
shared/constants/test-flags.js
Normal file
1
shared/constants/test-flags.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export const ACTION_QUEUE_METRICS_E2E_TEST = 'action_queue_metrics_e2e_test';
|
@ -4,6 +4,9 @@ const {
|
|||||||
} = require('@metamask/snaps-utils');
|
} = require('@metamask/snaps-utils');
|
||||||
const { merge } = require('lodash');
|
const { merge } = require('lodash');
|
||||||
const { CHAIN_IDS } = require('../../shared/constants/network');
|
const { CHAIN_IDS } = require('../../shared/constants/network');
|
||||||
|
const {
|
||||||
|
ACTION_QUEUE_METRICS_E2E_TEST,
|
||||||
|
} = require('../../shared/constants/test-flags');
|
||||||
const { SMART_CONTRACTS } = require('./seeder/smart-contracts');
|
const { SMART_CONTRACTS } = require('./seeder/smart-contracts');
|
||||||
|
|
||||||
function defaultFixture() {
|
function defaultFixture() {
|
||||||
@ -309,6 +312,7 @@ function onboardingFixture() {
|
|||||||
[CHAIN_IDS.GOERLI]: true,
|
[CHAIN_IDS.GOERLI]: true,
|
||||||
[CHAIN_IDS.LOCALHOST]: true,
|
[CHAIN_IDS.LOCALHOST]: true,
|
||||||
},
|
},
|
||||||
|
[ACTION_QUEUE_METRICS_E2E_TEST]: false,
|
||||||
},
|
},
|
||||||
NetworkController: {
|
NetworkController: {
|
||||||
networkId: '1337',
|
networkId: '1337',
|
||||||
|
@ -12,6 +12,7 @@ describe('MV3 - Dapp interactions', function () {
|
|||||||
balance: convertToHexValue(25000000000000000000),
|
balance: convertToHexValue(25000000000000000000),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
concurrent: { port: 8546, chainId: 1338 },
|
||||||
};
|
};
|
||||||
it('should continue to support dapp interactions after service worker re-start', async function () {
|
it('should continue to support dapp interactions after service worker re-start', async function () {
|
||||||
await withFixtures(
|
await withFixtures(
|
||||||
|
@ -428,10 +428,6 @@ class Driver {
|
|||||||
const artifactDir = `./test-artifacts/${this.browser}/${title}`;
|
const artifactDir = `./test-artifacts/${this.browser}/${title}`;
|
||||||
const filepathBase = `${artifactDir}/test-failure`;
|
const filepathBase = `${artifactDir}/test-failure`;
|
||||||
await fs.mkdir(artifactDir, { recursive: true });
|
await fs.mkdir(artifactDir, { recursive: true });
|
||||||
const isPageError = await this.isElementPresent('.error-page__details');
|
|
||||||
if (isPageError) {
|
|
||||||
await this.clickElement('.error-page__details');
|
|
||||||
}
|
|
||||||
const screenshot = await this.driver.takeScreenshot();
|
const screenshot = await this.driver.takeScreenshot();
|
||||||
await fs.writeFile(`${filepathBase}-screenshot.png`, screenshot, {
|
await fs.writeFile(`${filepathBase}-screenshot.png`, screenshot, {
|
||||||
encoding: 'base64',
|
encoding: 'base64',
|
||||||
@ -482,20 +478,22 @@ class Driver {
|
|||||||
// 4Byte
|
// 4Byte
|
||||||
'Failed to load resource: the server responded with a status of 502 (Bad Gateway)',
|
'Failed to load resource: the server responded with a status of 502 (Bad Gateway)',
|
||||||
];
|
];
|
||||||
|
|
||||||
const { errors } = this;
|
const { errors } = this;
|
||||||
const cdpConnection = await this.driver.createCDPConnection('page');
|
const cdpConnection = await this.driver.createCDPConnection('page');
|
||||||
await this.driver.onLogEvent(cdpConnection, (event) => {
|
await this.driver.onLogEvent(cdpConnection, (event) => {
|
||||||
if (event.type === 'error') {
|
if (event.type === 'error') {
|
||||||
const eventDescription = event.args.filter(
|
const eventDescriptions = event.args.filter(
|
||||||
(err) => err.description !== undefined,
|
(err) => err.description !== undefined,
|
||||||
);
|
);
|
||||||
const [{ description }] = eventDescription;
|
|
||||||
|
const [eventDescription] = eventDescriptions;
|
||||||
const ignore = ignoredErrorMessages.some((message) =>
|
const ignore = ignoredErrorMessages.some((message) =>
|
||||||
description.includes(message),
|
eventDescription?.description.includes(message),
|
||||||
);
|
);
|
||||||
if (!ignore) {
|
if (!ignore) {
|
||||||
errors.push(description);
|
errors.push(eventDescription?.description);
|
||||||
logBrowserError(failOnConsoleError, description);
|
logBrowserError(failOnConsoleError, eventDescription?.description);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -41,6 +41,7 @@ describe('ActionQueue', () => {
|
|||||||
expect(background.backgroundFunction.called).toStrictEqual(false);
|
expect(background.backgroundFunction.called).toStrictEqual(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('submitRequestToBackground', () => {
|
describe('submitRequestToBackground', () => {
|
||||||
it('calls promisified background method if the stream is connected', async () => {
|
it('calls promisified background method if the stream is connected', async () => {
|
||||||
const background = {
|
const background = {
|
||||||
@ -74,6 +75,7 @@ describe('ActionQueue', () => {
|
|||||||
readable: false,
|
readable: false,
|
||||||
},
|
},
|
||||||
backgroundFunction3: sinon.stub().yields(),
|
backgroundFunction3: sinon.stub().yields(),
|
||||||
|
trackMetaMetricsEvent: sinon.stub().yields(),
|
||||||
};
|
};
|
||||||
_setBackgroundConnection(background);
|
_setBackgroundConnection(background);
|
||||||
const requestPromise = submitRequestToBackground('backgroundFunction3');
|
const requestPromise = submitRequestToBackground('backgroundFunction3');
|
||||||
@ -133,6 +135,7 @@ describe('ActionQueue', () => {
|
|||||||
trace.secondStarted = Date.now();
|
trace.secondStarted = Date.now();
|
||||||
setTimeout(() => cb(null, 'second'), 10);
|
setTimeout(() => cb(null, 'second'), 10);
|
||||||
},
|
},
|
||||||
|
trackMetaMetricsEvent: sinon.stub().yields(),
|
||||||
};
|
};
|
||||||
_setBackgroundConnection(background);
|
_setBackgroundConnection(background);
|
||||||
const scheduled = Promise.all([
|
const scheduled = Promise.all([
|
||||||
@ -159,6 +162,7 @@ describe('ActionQueue', () => {
|
|||||||
trace.secondStarted = Date.now();
|
trace.secondStarted = Date.now();
|
||||||
setTimeout(() => cb(null, 'second'), 10);
|
setTimeout(() => cb(null, 'second'), 10);
|
||||||
},
|
},
|
||||||
|
trackMetaMetricsEvent: sinon.stub().yields(),
|
||||||
};
|
};
|
||||||
_setBackgroundConnection(background);
|
_setBackgroundConnection(background);
|
||||||
const scheduled = Promise.all([
|
const scheduled = Promise.all([
|
||||||
@ -189,6 +193,7 @@ describe('ActionQueue', () => {
|
|||||||
trace.secondStarted = Date.now();
|
trace.secondStarted = Date.now();
|
||||||
setTimeout(() => cb(null, 'second'), 10);
|
setTimeout(() => cb(null, 'second'), 10);
|
||||||
},
|
},
|
||||||
|
trackMetaMetricsEvent: sinon.stub().yields(),
|
||||||
};
|
};
|
||||||
_setBackgroundConnection(background);
|
_setBackgroundConnection(background);
|
||||||
const scheduled = Promise.all([
|
const scheduled = Promise.all([
|
||||||
@ -217,6 +222,7 @@ describe('ActionQueue', () => {
|
|||||||
}, 5);
|
}, 5);
|
||||||
},
|
},
|
||||||
second: sinon.stub().yields(),
|
second: sinon.stub().yields(),
|
||||||
|
trackMetaMetricsEvent: sinon.stub().yields(),
|
||||||
};
|
};
|
||||||
_setBackgroundConnection(background);
|
_setBackgroundConnection(background);
|
||||||
const scheduled = Promise.race([
|
const scheduled = Promise.race([
|
||||||
@ -251,6 +257,7 @@ describe('ActionQueue', () => {
|
|||||||
setTimeout(() => cb(null, 'second'), 10);
|
setTimeout(() => cb(null, 'second'), 10);
|
||||||
},
|
},
|
||||||
third: sinon.stub().yields(),
|
third: sinon.stub().yields(),
|
||||||
|
trackMetaMetricsEvent: sinon.stub().yields(),
|
||||||
};
|
};
|
||||||
flowControl.triggerRaceCondition = () => {
|
flowControl.triggerRaceCondition = () => {
|
||||||
flowControl.waitFor = submitRequestToBackground('third');
|
flowControl.waitFor = submitRequestToBackground('third');
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
import pify from 'pify';
|
import pify from 'pify';
|
||||||
|
import { EVENT, EVENT_NAMES } from '../../../shared/constants/metametrics';
|
||||||
import { isManifestV3 } from '../../../shared/modules/mv3.utils';
|
import { isManifestV3 } from '../../../shared/modules/mv3.utils';
|
||||||
|
import { trackMetaMetricsEvent } from '../actions';
|
||||||
|
|
||||||
// // A simplified pify maybe?
|
// // A simplified pify maybe?
|
||||||
// function pify(apiObject) {
|
// function pify(apiObject) {
|
||||||
// return Object.keys(apiObject).reduce((promisifiedAPI, key) => {
|
// return Object.keys(apiObject).reduce((promisifiedAPI, key) => {
|
||||||
@ -187,6 +190,20 @@ async function processActionRetryQueue() {
|
|||||||
}
|
}
|
||||||
processingQueue = true;
|
processingQueue = true;
|
||||||
try {
|
try {
|
||||||
|
if (actionRetryQueue.length > 0) {
|
||||||
|
const metametricsPayload = {
|
||||||
|
category: EVENT.SOURCE.SERVICE_WORKERS,
|
||||||
|
event: EVENT_NAMES.SERVICE_WORKER_RESTARTED,
|
||||||
|
properties: {
|
||||||
|
service_worker_action_queue_methods: actionRetryQueue.map(
|
||||||
|
(action) => action.request.method,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
trackMetaMetricsEvent(metametricsPayload);
|
||||||
|
}
|
||||||
|
|
||||||
while (
|
while (
|
||||||
background?.connectionStream.readable &&
|
background?.connectionStream.readable &&
|
||||||
actionRetryQueue.length > 0
|
actionRetryQueue.length > 0
|
||||||
|
Loading…
Reference in New Issue
Block a user