From 01b218a07d4a3da94ed277a1eaec42f808934527 Mon Sep 17 00:00:00 2001 From: Roman Kuznetsov Date: Wed, 6 Sep 2023 15:41:14 +0300 Subject: [PATCH 1/2] chore: add performance metrics --- lib/constants/performance-marks.ts | 8 ++++ lib/static/modules/actions.js | 11 ++++++ lib/static/modules/middlewares/metrika.js | 38 ++++++++++++++++++- test/unit/lib/static/modules/actions.js | 3 ++ .../lib/static/modules/middlewares/metrika.js | 3 ++ 5 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 lib/constants/performance-marks.ts diff --git a/lib/constants/performance-marks.ts b/lib/constants/performance-marks.ts new file mode 100644 index 000000000..33300b06c --- /dev/null +++ b/lib/constants/performance-marks.ts @@ -0,0 +1,8 @@ +export default { + JS_EXEC: 'js exec', + DBS_LOADED: 'dbs loaded', + DBS_MERGED: 'dbs merged', + PLUGINS_LOADED: 'plugins loaded', + DB_EXTRACTED_ROWS: 'db extracted rows', + FULLY_LOADED: 'fully loaded' +} as const; diff --git a/lib/static/modules/actions.js b/lib/static/modules/actions.js index 817afeaf4..34b8b86f5 100644 --- a/lib/static/modules/actions.js +++ b/lib/static/modules/actions.js @@ -10,6 +10,7 @@ import {getHttpErrorMessage} from './utils'; import {fetchDataFromDatabases, mergeDatabases, connectToDatabase, getMainDatabaseUrl, getSuitesTableRows} from '../../db-utils/client'; import {setFilteredBrowsers} from './query-params'; import plugins from './plugins'; +import performanceMarks from '../../constants/performance-marks'; export const createNotification = (id, status, message, props = {}) => { const notificationProps = { @@ -58,6 +59,7 @@ export const initGuiReport = () => { export const initStaticReport = () => { return async dispatch => { + performance?.mark?.(performanceMarks.JS_EXEC); const dataFromStaticFile = window.data || {}; let fetchDbDetails = []; let db = null; @@ -66,17 +68,23 @@ export const initStaticReport = () => { const mainDatabaseUrls = new URL('databaseUrls.json', window.location.href); const fetchDbResponses = await fetchDataFromDatabases([mainDatabaseUrls.href]); + performance?.mark?.(performanceMarks.DBS_LOADED); + fetchDbDetails = fetchDbResponses.map(({url, status, data}) => ({url, status, success: !!data})); const dataForDbs = fetchDbResponses.map(({data}) => data).filter(data => data); db = await mergeDatabases(dataForDbs); + + performance?.mark?.(performanceMarks.DBS_MERGED); } catch (e) { dispatch(createNotificationError('initStaticReport', e)); } await plugins.loadAll(dataFromStaticFile.config); + performance?.mark?.(performanceMarks.PLUGINS_LOADED); + if (!db || isEmpty(fetchDbDetails)) { return dispatch({ type: actionNames.INIT_STATIC_REPORT, @@ -86,6 +94,9 @@ export const initStaticReport = () => { const testsTreeBuilder = StaticTestsTreeBuilder.create(); const suitesRows = getSuitesTableRows(db); + + performance?.mark?.(performanceMarks.DB_EXTRACTED_ROWS); + const {tree, stats, skips, browsers} = testsTreeBuilder.build(suitesRows); dispatch({ diff --git a/lib/static/modules/middlewares/metrika.js b/lib/static/modules/middlewares/metrika.js index ae5e51685..75d6c3fa0 100644 --- a/lib/static/modules/middlewares/metrika.js +++ b/lib/static/modules/middlewares/metrika.js @@ -1,9 +1,11 @@ -import {get} from 'lodash'; +import {get, isEmpty} from 'lodash'; import actionNames from '../action-names'; import {measurePerformance} from '../web-vitals'; +import performanceMarks from '../../../constants/performance-marks'; let metrika; +let reportFullyLoaded = false; export default metrikaClass => store => next => action => { switch (action.type) { @@ -58,6 +60,29 @@ export default metrikaClass => store => next => action => { return next(action); } + case actionNames.BROWSERS_SELECTED: { + execOnMetrikaEnabled(() => { + sendCounterId(action.type); + }); + + const result = next(action); + const state = store.getState(); + + if (!reportFullyLoaded && state.gui === false) { + reportFullyLoaded = true; + + performance?.mark?.(performanceMarks.FULLY_LOADED); + + const marks = extractPerformanceMarks(); + + if (metrika && !isEmpty(marks)) { + metrika.sendVisitParams(marks); + } + } + + return result; + } + case actionNames.RUN_ALL_TESTS: case actionNames.RUN_FAILED_TESTS: case actionNames.RETRY_SUITE: @@ -75,7 +100,6 @@ export default metrikaClass => store => next => action => { case actionNames.VIEW_SWITCH_DIFF: case actionNames.VIEW_SWIPE_DIFF: case actionNames.VIEW_ONION_SKIN_DIFF: - case actionNames.BROWSERS_SELECTED: case actionNames.VIEW_UPDATE_FILTER_BY_NAME: case actionNames.VIEW_SET_STRICT_MATCH_FILTER: case actionNames.RUN_CUSTOM_GUI_ACTION: @@ -119,3 +143,13 @@ function execOnMetrikaEnabled(cb) { function sendCounterId(counterId) { metrika.sendVisitParams({counterId}); } + +function extractPerformanceMarks() { + const marks = performance?.getEntriesByType?.('mark') || []; + + return marks.reduce((acc, {name, startTime}) => { + acc[name] = Math.round(startTime); + + return acc; + }, {}); +} diff --git a/test/unit/lib/static/modules/actions.js b/test/unit/lib/static/modules/actions.js index b9a6526d0..e9d96ef00 100644 --- a/test/unit/lib/static/modules/actions.js +++ b/test/unit/lib/static/modules/actions.js @@ -7,6 +7,9 @@ import {StaticTestsTreeBuilder} from 'lib/tests-tree-builder/static'; import {LOCAL_DATABASE_NAME} from 'lib/constants/database'; import {DiffModes} from 'lib/constants/diff-modes'; +// eslint-disable-next-line +globalThis.performance = globalThis.performance; // node v14 stub + describe('lib/static/modules/actions', () => { const sandbox = sinon.sandbox.create(); let dispatch, actions, notify, getSuitesTableRows, getMainDatabaseUrl, connectToDatabaseStub, pluginsStub; diff --git a/test/unit/lib/static/modules/middlewares/metrika.js b/test/unit/lib/static/modules/middlewares/metrika.js index 2bd901e6f..c0a0942b1 100644 --- a/test/unit/lib/static/modules/middlewares/metrika.js +++ b/test/unit/lib/static/modules/middlewares/metrika.js @@ -2,6 +2,9 @@ import YandexMetrika from 'lib/static/modules/yandex-metrika'; import actionNames from 'lib/static/modules/action-names'; import webVitals from 'lib/static/modules/web-vitals'; +// eslint-disable-next-line +globalThis.performance = globalThis.performance; // node v14 stub + describe('lib/static/modules/middlewares/metrika', () => { const sandbox = sinon.sandbox.create(); let next, metrikaMiddleware; From 70f93a9bf2f2f5bcfb21c95c9346c3cb6ff4f9d8 Mon Sep 17 00:00:00 2001 From: Roman Kuznetsov Date: Thu, 7 Sep 2023 13:00:49 +0300 Subject: [PATCH 2/2] chore: enable metrics in gui mode --- lib/static/modules/actions.js | 5 +++++ lib/static/modules/middlewares/metrika.js | 3 +-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/static/modules/actions.js b/lib/static/modules/actions.js index 34b8b86f5..99e562a40 100644 --- a/lib/static/modules/actions.js +++ b/lib/static/modules/actions.js @@ -32,14 +32,19 @@ export const dismissNotification = dismissNotify; export const initGuiReport = () => { return async (dispatch) => { + performance?.mark?.(performanceMarks.JS_EXEC); try { const appState = await axios.get('/init'); const mainDatabaseUrl = getMainDatabaseUrl(); const db = await connectToDatabase(mainDatabaseUrl.href); + performance?.mark?.(performanceMarks.DBS_LOADED); + await plugins.loadAll(appState.data.config); + performance?.mark?.(performanceMarks.PLUGINS_LOADED); + dispatch({ type: actionNames.INIT_GUI_REPORT, payload: {...appState.data, db} diff --git a/lib/static/modules/middlewares/metrika.js b/lib/static/modules/middlewares/metrika.js index 75d6c3fa0..2da1a7017 100644 --- a/lib/static/modules/middlewares/metrika.js +++ b/lib/static/modules/middlewares/metrika.js @@ -66,9 +66,8 @@ export default metrikaClass => store => next => action => { }); const result = next(action); - const state = store.getState(); - if (!reportFullyLoaded && state.gui === false) { + if (!reportFullyLoaded) { reportFullyLoaded = true; performance?.mark?.(performanceMarks.FULLY_LOADED);