Skip to content

Commit c774f69

Browse files
authored
feat: new ui usage information tracking (#627)
* chore: collect analytics data on new ui features * feat: add enabled param for analytics and ability to turn it of with NO_ANALYTICS env * test: implement e2e tests on analytics * docs: update docs on usage info tracking * fix: sync package-lock with package.json * fix: update e2e tests and metrika init settings
1 parent 3d04404 commit c774f69

File tree

31 files changed

+449
-102
lines changed

31 files changed

+449
-102
lines changed

docs/en/html-reporter-setup.md

Lines changed: 17 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -311,41 +311,27 @@ customScripts: [
311311

312312
### yandexMetrika
313313

314-
This parameter allows you to add [Yandex.Metrika][yandex-metrika] to the report. The parameter is set as an object with the key `counterNumber`. As the key value, you must specify the Yandex.Metrica counter number (see "[How to create a counter][how-to-create-counter]"). The number should be set as a Number, not a String.
314+
By default, anonymous html-reporter interface usage information is collected for us to analyze usage patterns and improve UX. We collect such info as html-reporter loading speed, how often certain UI features are used (e.g. sorting tests) or clicks on UI elements. NO information about your project or tests is ever tracked.
315315

316-
Also, in the Yandex.Metrika interface, go to the _"Counter"_ tab in the settings section, click _"Copy"_ and paste the counter code into the [customScripts](#customscripts) field.
316+
If you want to opt out, choose any of the options below:
317317

318-
With the help of metrics, you can find out how developers interact with your report and what kind of problems they face.
319-
320-
The report supports the following [goals of metrics][yandex-metrika-goals]:
321-
322-
* **ACCEPT_SCREENSHOT**—there was a click on the _Accept_ button to accept a screenshot;
323-
* **ACCEPT_OPENED_SCREENSHOTS**—there was a click on the _Accept opened_ button to accept screenshots from open tests.
324-
325-
Example of setting up Yandex.Metrika in one of the projects:
326-
327-
```javascript
328-
module.exports = {
329-
plugins: {
330-
'html-reporter/hermione': {
331-
customScripts: [
332-
function(){(function(m,e,t,r,i,k,a){m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)}; m[i].l=1*new Date();k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)}) (window, document, "script", "https://mc.yandex.ru/metrika/tag.js", "ym"); ym(56782912, "init", { clickmap:true, trackLinks:true, accurateTrackBounce:true, webvisor:true })},
333-
334-
// other scripts...
335-
],
336-
yandexMetrika: {
337-
counterNumber: 1234567
318+
- Edit your config:
319+
```javascript
320+
module.exports = {
321+
plugins: {
322+
'html-reporter/testplane': {
323+
yandexMetrika: {
324+
enabled: false
325+
},
326+
// other html-reporter settings...
338327
},
339-
340-
// other plugin settings...
328+
// other Testplane plugins...
341329
},
342-
343-
// other hermione plugins...
344-
},
345-
346-
// other hermione settings...
347-
};
348-
```
330+
// other Testplane settings...
331+
};
332+
```
333+
- Using environment variables: `html_reporter_yandex_metrika_enabled=false` or simply `NO_ANALYTICS=true`
334+
- Using CLI arguments: `--html-reporter-yandex_metrika_enabled=false`
349335

350336
### Passing parameters via the CLI
351337

docs/ru/html-reporter-setup.md

Lines changed: 17 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -312,41 +312,27 @@ customScripts: [
312312

313313
### yandexMetrika
314314

315-
Данный параметр позволяет добавить в отчет [Яндекс.Метрику][yandex-metrika]. Параметр задается в виде объекта с ключом `counterNumber` _(номер счетчика)_. В качестве значения ключа необходимо указать номер счетчика Яндекс.Метрики (см. «[Как создать счетчик][how-to-create-counter]»). Номер должен задаваться как число _(Number)_, а не строка.
315+
По умолчанию выполняется сбор анонимных сведений об использовании интерфейса отчета в целях анализа и улучшения UX. Собираются такие сведения, как скорость загрузки отчета, частота использования некоторых функций (например, сортировка тестов) и клики по элементам управления. Сведения о вашем проекте или содержимом тестов НЕ собираются ни при каких обстоятельствах.
316316

317-
Также в интерфейсе Яндекс.Метрики необходимо перейти в разделе настроек на вкладку _«Счетчик»_, нажать кнопку _«Скопировать»_ и вставить код счетчика в поле [customScripts](#customscripts).
317+
Если вы не хотите делиться аналитикой с нами, вы можете отключить это любым из способов:
318318

319-
С помощью метрики вы сможете узнать как разработчики взаимодействуют с вашим отчетом и с какого рода проблемами они сталкиваются.
320-
321-
Отчет поддерживает следующие [цели для метрики][yandex-metrika-goals]:
322-
323-
* **ACCEPT_SCREENSHOT** — было нажатие на кнопку _Accept_ для принятия скриншота;
324-
* **ACCEPT_OPENED_SCREENSHOTS** — было нажатие на кнопку _Accept opened_ для принятия скриншотов из открытых тестов.
325-
326-
Пример настройки _Яндекс.Метрики_ в одном из проектов:
327-
328-
```javascript
329-
module.exports = {
330-
plugins: {
331-
'html-reporter/hermione': {
332-
customScripts: [
333-
function(){(function(m,e,t,r,i,k,a){m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)}; m[i].l=1*new Date();k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)}) (window, document, "script", "https://mc.yandex.ru/metrika/tag.js", "ym"); ym(56782912, "init", { clickmap:true, trackLinks:true, accurateTrackBounce:true, webvisor:true })},
334-
335-
// другие скрипты...
336-
],
337-
yandexMetrika: {
338-
counterNumber: 1234567
319+
- В конфиге
320+
```javascript
321+
module.exports = {
322+
plugins: {
323+
'html-reporter/testplane': {
324+
yandexMetrika: {
325+
enabled: false
326+
},
327+
// другие настройки html-reporter...
339328
},
340-
341-
// другие настройки плагина...
329+
// другие плагины Testplane...
342330
},
343-
344-
// другие плагины гермионы...
345-
},
346-
347-
// другие настройки гермионы...
348-
};
349-
```
331+
// другие настройки Testplane...
332+
};
333+
```
334+
- С помощью переменных окружения: `html_reporter_yandex_metrika_enabled=false` или просто `NO_ANALYTICS=true`
335+
- С помощью аргументов CLI: `--html-reporter-yandex_metrika_enabled=false`
350336

351337
### Передача параметров через CLI
352338

lib/config/index.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ const assertType = <T>(name: string, validationFn: (value: unknown) => value is
2828
};
2929
const assertString = (name: string): AssertionFn<string> => assertType(name, _.isString, 'string');
3030
const assertBoolean = (name: string): AssertionFn<boolean> => assertType(name, _.isBoolean, 'boolean');
31-
const assertNumber = (name: string): AssertionFn<number> => assertType(name, _.isNumber, 'number');
31+
export const assertNumber = (name: string): AssertionFn<number> => assertType(name, _.isNumber, 'number');
3232
const assertPlainObject = (name: string): AssertionFn<Record<string, unknown>> => assertType(name, isPlainObject, 'plain object');
3333

3434
const assertSaveFormat = (saveFormat: unknown): asserts saveFormat is SaveFormat => {
@@ -206,11 +206,27 @@ const getParser = (): ReturnType<typeof root<ReporterConfig>> => {
206206
validate: assertArrayOf('functions', 'customScripts', _.isFunction)
207207
}),
208208
yandexMetrika: section({
209+
enabled: option({
210+
defaultValue: () => {
211+
return !(process.env.NO_ANALYTICS && JSON.parse(process.env.NO_ANALYTICS));
212+
},
213+
parseEnv: JSON.parse,
214+
parseCli: JSON.parse,
215+
validate: assertBoolean('yandexMetrika.enabled'),
216+
map: (value: boolean) => {
217+
if (process.env.NO_ANALYTICS && JSON.parse(process.env.NO_ANALYTICS)) {
218+
return false;
219+
}
220+
221+
return value;
222+
}
223+
}),
209224
counterNumber: option({
225+
isDeprecated: true,
210226
defaultValue: configDefaults.yandexMetrika.counterNumber,
211227
parseEnv: Number,
212228
parseCli: Number,
213-
validate: (value) => _.isNull(value) || assertNumber('yandexMetrika.counterNumber')(value)
229+
map: () => configDefaults.yandexMetrika.counterNumber
214230
})
215231
}),
216232
pluginsEnabled: option({

lib/constants/defaults.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export const configDefaults: StoreReporterConfig = {
2222
saveErrorDetails: false,
2323
saveFormat: SaveFormat.SQLITE,
2424
yandexMetrika: {
25-
counterNumber: null
25+
counterNumber: 99267510
2626
},
2727
staticImageAccepter: {
2828
enabled: false,

lib/static/components/controls/report-info.jsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,18 @@ import {isEmpty} from 'lodash';
77
import {version} from '../../../../package.json';
88
import useLocalStorage from '@/static/hooks/useLocalStorage';
99
import {LocalStorageKey, UiMode} from '@/constants/local-storage';
10+
import {useAnalytics} from '@/static/new-ui/hooks/useAnalytics';
1011

1112
function ReportInfo(props) {
13+
const analytics = useAnalytics();
1214
const {gui, timestamp} = props;
1315
const lang = isEmpty(navigator.languages) ? navigator.language : navigator.languages[0];
1416
const date = new Date(timestamp).toLocaleString(lang);
1517

1618
const [, setUiMode] = useLocalStorage(LocalStorageKey.UIMode, UiMode.New);
1719

1820
const onNewUiButtonClick = () => {
21+
analytics?.trackFeatureUsage({featureName: 'Switch to new UI'});
1922
setUiMode(UiMode.New);
2023

2124
const targetUrl = new URL(window.location.href);

lib/static/components/gui.jsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {ClientEvents} from '../../gui/constants/client-events';
1515
import FaviconChanger from './favicon-changer';
1616
import ExtensionPoint from './extension-point';
1717
import BottomProgressBar from './bottom-progress-bar';
18+
import {MetrikaScript} from '@/static/new-ui/components/MetrikaScript';
1819

1920
class Gui extends Component {
2021
static propTypes = {
@@ -81,6 +82,7 @@ class Gui extends Component {
8182
<Fragment>
8283
<ExtensionPoint name={ROOT}>
8384
<CustomScripts scripts={customScripts}/>
85+
<MetrikaScript />
8486
{notificationElem}
8587
<FaviconChanger />
8688
<StickyHeader />

lib/static/components/report.jsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {CustomScripts} from '../new-ui/components/CustomScripts';
1414
import FaviconChanger from './favicon-changer';
1515
import ExtensionPoint from './extension-point';
1616
import BottomProgressBar from './bottom-progress-bar';
17+
import {MetrikaScript} from '@/static/new-ui/components/MetrikaScript';
1718

1819
class Report extends Component {
1920
static propTypes = {
@@ -50,6 +51,7 @@ class Report extends Component {
5051
<Fragment>
5152
<ExtensionPoint name={ROOT}>
5253
<CustomScripts scripts={this.props.customScripts}/>
54+
<MetrikaScript />
5355
{notificationElem}
5456
<FaviconChanger />
5557
<StickyHeader />

lib/static/modules/middlewares/metrika.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ export function getMetrikaMiddleware(analytics: YandexMetrika): Middleware<{}, S
3232
analytics.setVisitParams({
3333
[action.type]: Date.now() - startLoadTime,
3434
initView: state.view,
35-
testsCount
35+
testsCount,
36+
isNewUi: Boolean(state?.app?.isNewUi)
3637
});
3738

3839
return result;
@@ -82,6 +83,32 @@ export function getMetrikaMiddleware(analytics: YandexMetrika): Middleware<{}, S
8283
return next(action);
8384
}
8485

86+
case actionNames.GROUP_TESTS_SET_CURRENT_EXPRESSION: {
87+
analytics.trackFeatureUsage({featureName: action.type, groupByKey: action.payload.expressionIds[0]});
88+
89+
return next(action);
90+
}
91+
92+
case actionNames.SORT_TESTS_SET_CURRENT_EXPRESSION: {
93+
analytics.trackFeatureUsage({
94+
featureName: action.type,
95+
sortByKey: action.payload.expressionIds[0],
96+
sortDirection: store.getState().app.sortTestsData.currentDirection
97+
});
98+
99+
return next(action);
100+
}
101+
102+
case actionNames.SORT_TESTS_SET_DIRECTION: {
103+
analytics.trackFeatureUsage({
104+
featureName: action.type,
105+
sortByKey: store.getState().app.sortTestsData.currentExpressionIds[0],
106+
sortDirection: action.payload.direction
107+
});
108+
109+
return next(action);
110+
}
111+
85112
case actionNames.OPEN_MODAL:
86113
case actionNames.CLOSE_MODAL: {
87114
const modalId = get(action, 'payload.id', action.type);

lib/static/modules/store.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@ if (process.env.NODE_ENV !== 'production') {
2121
}
2222

2323
const metrikaConfig = (window.data || {}).config?.yandexMetrika;
24-
const areAnalyticsEnabled = metrikaConfig?.enabled && metrikaConfig?.counterId;
24+
const areAnalyticsEnabled = metrikaConfig?.enabled && metrikaConfig?.counterNumber;
2525
const isYaMetrikaAvailable = window.ym && typeof window.ym === 'function';
2626

2727
if (areAnalyticsEnabled && isYaMetrikaAvailable) {
28-
const metrika = new YandexMetrika();
28+
const metrika = new YandexMetrika(areAnalyticsEnabled && isYaMetrikaAvailable);
2929
const metrikaMiddleware = getMetrikaMiddleware(metrika);
3030

3131
middlewares.push(metrikaMiddleware);

lib/static/modules/yandex-metrika.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import {SortDirection} from '@/static/new-ui/types/store';
2+
13
enum YandexMetrikaMethod {
24
ReachGoal = 'reachGoal',
35
Params = 'params',
@@ -24,10 +26,29 @@ interface ScreenshotAcceptData {
2426
acceptedImagesCount: number;
2527
}
2628

27-
interface FeatureUsageData {
29+
interface BasicFeatureUsageData {
2830
featureName: string;
2931
}
3032

33+
interface GroupByFeatureUsageData extends BasicFeatureUsageData {
34+
groupByKey: string;
35+
}
36+
37+
interface SortByFeatureUsageData extends BasicFeatureUsageData {
38+
sortByKey: string;
39+
sortDirection: SortDirection;
40+
}
41+
42+
interface ChangeTreeViewModeFeatureUsageData extends BasicFeatureUsageData {
43+
treeViewMode: string;
44+
}
45+
46+
type FeatureUsageData =
47+
| BasicFeatureUsageData
48+
| GroupByFeatureUsageData
49+
| SortByFeatureUsageData
50+
| ChangeTreeViewModeFeatureUsageData;
51+
3152
export class YandexMetrika {
3253
protected readonly _isEnabled: boolean;
3354
protected readonly _counterNumber: number;

0 commit comments

Comments
 (0)