Skip to content

Commit bf64826

Browse files
authored
chore(compass-collection): Add track events for mock data generator modal CLOUDP-333861 (#7482)
* Add analytics track events for mock data generator
1 parent 2051b4b commit bf64826

File tree

11 files changed

+740
-94
lines changed

11 files changed

+740
-94
lines changed

packages/compass-collection/src/components/collection-header-actions/collection-header-actions.spec.tsx

Lines changed: 112 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import React, { type ComponentProps } from 'react';
33
import {
44
renderWithActiveConnection,
55
screen,
6+
waitFor,
67
} from '@mongodb-js/testing-library-compass';
78
import sinon from 'sinon';
89
import {
@@ -14,6 +15,7 @@ import { CompassExperimentationProvider } from '@mongodb-js/compass-telemetry';
1415
import type { ConnectionInfo } from '@mongodb-js/compass-connections/provider';
1516

1617
import CollectionHeaderActions from '../collection-header-actions';
18+
import { MAX_COLLECTION_NESTING_DEPTH } from '../mock-data-generator-modal/utils';
1719

1820
describe('CollectionHeaderActions [Component]', function () {
1921
let mockUseAssignment: sinon.SinonStub;
@@ -230,94 +232,132 @@ describe('CollectionHeaderActions [Component]', function () {
230232
);
231233
});
232234

233-
it('should call onOpenMockDataModal when CTA button is clicked', async function () {
234-
const onOpenMockDataModal = sinon.stub();
235+
context('when in the mock data generator treatment variant', function () {
236+
beforeEach(function () {
237+
mockUseAssignment.returns({
238+
assignment: {
239+
assignmentData: {
240+
variant: 'mockDataGeneratorVariant',
241+
},
242+
},
243+
});
244+
});
235245

236-
mockUseAssignment.returns({
237-
assignment: {
238-
assignmentData: {
239-
variant: 'mockDataGeneratorVariant',
246+
it('should send a track event when the button is viewed', async function () {
247+
const result = await renderCollectionHeaderActions(
248+
{
249+
namespace: 'test.collection',
250+
isReadonly: false,
240251
},
241-
},
252+
{},
253+
atlasConnectionInfo
254+
);
255+
256+
await waitFor(() => {
257+
expect(result.track).to.have.been.calledWith(
258+
'Mock Data Generator CTA Button Viewed',
259+
{
260+
button_enabled: true,
261+
gen_ai_features_enabled: false,
262+
send_sample_values_enabled: false,
263+
}
264+
);
265+
});
242266
});
243267

244-
await renderCollectionHeaderActions(
245-
{
246-
namespace: 'test.collection',
247-
isReadonly: false,
248-
onOpenMockDataModal,
249-
},
250-
{},
251-
atlasConnectionInfo
252-
);
268+
it('should call onOpenMockDataModal when CTA button is clicked', async function () {
269+
const onOpenMockDataModal = sinon.stub();
270+
await renderCollectionHeaderActions(
271+
{
272+
namespace: 'test.collection',
273+
isReadonly: false,
274+
onOpenMockDataModal,
275+
},
276+
{},
277+
atlasConnectionInfo
278+
);
253279

254-
const button = screen.getByTestId(
255-
'collection-header-generate-mock-data-button'
256-
);
257-
button.click();
280+
const button = screen.getByTestId(
281+
'collection-header-generate-mock-data-button'
282+
);
283+
button.click();
258284

259-
expect(onOpenMockDataModal).to.have.been.calledOnce;
260-
});
285+
expect(onOpenMockDataModal).to.have.been.calledOnce;
286+
});
261287

262-
it('should disable button for deeply nested collections', async function () {
263-
mockUseAssignment.returns({
264-
assignment: {
265-
assignmentData: {
266-
variant: 'mockDataGeneratorVariant', // Treatment variant
288+
it('sends a track event when CTA button is clicked', async function () {
289+
const onOpenMockDataModal = sinon.stub();
290+
291+
const result = await renderCollectionHeaderActions(
292+
{
293+
namespace: 'test.collection',
294+
isReadonly: false,
295+
onOpenMockDataModal,
267296
},
268-
},
269-
});
297+
{},
298+
atlasConnectionInfo
299+
);
270300

271-
await renderCollectionHeaderActions(
272-
{
273-
namespace: 'test.collection',
274-
isReadonly: false,
275-
hasSchemaAnalysisData: true,
276-
analyzedSchemaDepth: 8, // Exceeds MAX_COLLECTION_NESTING_DEPTH (7)
277-
schemaAnalysisStatus: 'complete',
278-
onOpenMockDataModal: sinon.stub(),
279-
},
280-
{},
281-
atlasConnectionInfo
282-
);
301+
const button = screen.getByTestId(
302+
'collection-header-generate-mock-data-button'
303+
);
304+
button.click();
283305

284-
const button = screen.getByTestId(
285-
'collection-header-generate-mock-data-button'
286-
);
287-
expect(button).to.exist;
288-
expect(button).to.have.attribute('aria-disabled', 'true');
289-
});
306+
await waitFor(() => {
307+
expect(result.track).to.have.been.calledWith(
308+
'Mock Data Generator Opened',
309+
{
310+
gen_ai_features_enabled: false,
311+
send_sample_values_enabled: false,
312+
}
313+
);
314+
});
315+
});
290316

291-
it('should show an error banner when the schema is in an unsupported state', async function () {
292-
mockUseAssignment.returns({
293-
assignment: {
294-
assignmentData: {
295-
variant: 'mockDataGeneratorVariant',
317+
it('should disable button for deeply nested collections', async function () {
318+
await renderCollectionHeaderActions(
319+
{
320+
namespace: 'test.collection',
321+
isReadonly: false,
322+
hasSchemaAnalysisData: true,
323+
analyzedSchemaDepth: MAX_COLLECTION_NESTING_DEPTH + 1,
324+
schemaAnalysisStatus: 'complete',
325+
onOpenMockDataModal: sinon.stub(),
296326
},
297-
},
327+
{},
328+
atlasConnectionInfo
329+
);
330+
331+
const button = screen.getByTestId(
332+
'collection-header-generate-mock-data-button'
333+
);
334+
expect(button).to.exist;
335+
expect(button).to.have.attribute('aria-disabled', 'true');
298336
});
299337

300-
await renderCollectionHeaderActions(
301-
{
302-
namespace: 'test.collection',
303-
isReadonly: false,
304-
hasSchemaAnalysisData: false,
305-
schemaAnalysisStatus: 'error',
306-
schemaAnalysisError: {
307-
errorType: 'unsupportedState',
308-
errorMessage: 'Unsupported state',
338+
it('should show an error banner when the schema is in an unsupported state', async function () {
339+
await renderCollectionHeaderActions(
340+
{
341+
namespace: 'test.collection',
342+
isReadonly: false,
343+
hasSchemaAnalysisData: false,
344+
schemaAnalysisStatus: 'error',
345+
schemaAnalysisError: {
346+
errorType: 'unsupportedState',
347+
errorMessage: 'Unsupported state',
348+
},
349+
onOpenMockDataModal: sinon.stub(),
309350
},
310-
onOpenMockDataModal: sinon.stub(),
311-
},
312-
{},
313-
atlasConnectionInfo
314-
);
351+
{},
352+
atlasConnectionInfo
353+
);
315354

316-
const button = screen.getByTestId(
317-
'collection-header-generate-mock-data-button'
318-
);
319-
expect(button).to.exist;
320-
expect(button).to.have.attribute('aria-disabled', 'true');
355+
const button = screen.getByTestId(
356+
'collection-header-generate-mock-data-button'
357+
);
358+
expect(button).to.exist;
359+
expect(button).to.have.attribute('aria-disabled', 'true');
360+
});
321361
});
322362
});
323363
});

packages/compass-collection/src/components/collection-header-actions/collection-header-actions.tsx

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,26 +8,28 @@ import {
88
} from '@mongodb-js/compass-components';
99
import { useConnectionInfo } from '@mongodb-js/compass-connections/provider';
1010
import { useOpenWorkspace } from '@mongodb-js/compass-workspaces/provider';
11-
import React from 'react';
12-
import { usePreferences } from 'compass-preferences-model/provider';
11+
import React, { useCallback } from 'react';
12+
import {
13+
useIsAIFeatureEnabled,
14+
usePreference,
15+
usePreferences,
16+
} from 'compass-preferences-model/provider';
1317
import toNS from 'mongodb-ns';
1418
import { wrapField } from '@mongodb-js/mongodb-constants';
1519
import {
1620
useTelemetry,
1721
useAssignment,
1822
ExperimentTestName,
1923
ExperimentTestGroup,
24+
useTrackOnChange,
25+
type TrackFunction,
2026
} from '@mongodb-js/compass-telemetry/provider';
2127
import {
2228
SCHEMA_ANALYSIS_STATE_ANALYZING,
2329
type SchemaAnalysisStatus,
2430
type SchemaAnalysisError,
2531
} from '../../schema-analysis-types';
26-
27-
/**
28-
* Maximum allowed nesting depth for collections to show Mock Data Generator
29-
*/
30-
const MAX_COLLECTION_NESTING_DEPTH = 7;
32+
import { MAX_COLLECTION_NESTING_DEPTH } from '../mock-data-generator-modal/utils';
3133

3234
const collectionHeaderActionsStyles = css({
3335
display: 'flex',
@@ -92,6 +94,10 @@ const CollectionHeaderActions: React.FunctionComponent<
9294
const { readWrite: preferencesReadWrite, enableShell: showOpenShellButton } =
9395
usePreferences(['readWrite', 'enableShell']);
9496
const track = useTelemetry();
97+
const isAIFeatureEnabled = useIsAIFeatureEnabled();
98+
const isSampleDocumentPassingEnabled = usePreference(
99+
'enableGenAISampleDocumentPassing'
100+
);
95101

96102
// Get experiment assignment for Mock Data Generator
97103
const mockDataGeneratorAssignment = useAssignment(
@@ -122,6 +128,39 @@ const CollectionHeaderActions: React.FunctionComponent<
122128
const isView = isReadonly && sourceName && !editViewName;
123129

124130
const showViewEdit = isView && !preferencesReadWrite;
131+
const shouldDisableMockDataButton =
132+
!hasSchemaAnalysisData || exceedsMaxNestingDepth;
133+
134+
const onMockDataGeneratorCtaButtonClicked = useCallback(() => {
135+
track('Mock Data Generator Opened', {
136+
gen_ai_features_enabled: isAIFeatureEnabled,
137+
send_sample_values_enabled: isSampleDocumentPassingEnabled,
138+
});
139+
onOpenMockDataModal();
140+
}, [
141+
track,
142+
isAIFeatureEnabled,
143+
isSampleDocumentPassingEnabled,
144+
onOpenMockDataModal,
145+
]);
146+
147+
useTrackOnChange(
148+
(track: TrackFunction) => {
149+
if (shouldShowMockDataButton) {
150+
track('Mock Data Generator CTA Button Viewed', {
151+
button_enabled: !shouldDisableMockDataButton,
152+
gen_ai_features_enabled: isAIFeatureEnabled,
153+
send_sample_values_enabled: isSampleDocumentPassingEnabled,
154+
});
155+
}
156+
},
157+
[
158+
shouldShowMockDataButton,
159+
shouldDisableMockDataButton,
160+
isAIFeatureEnabled,
161+
isSampleDocumentPassingEnabled,
162+
]
163+
);
125164

126165
return (
127166
<div
@@ -151,8 +190,8 @@ const CollectionHeaderActions: React.FunctionComponent<
151190
<Button
152191
data-testid="collection-header-generate-mock-data-button"
153192
size={ButtonSize.Small}
154-
disabled={!hasSchemaAnalysisData || exceedsMaxNestingDepth}
155-
onClick={onOpenMockDataModal}
193+
disabled={shouldDisableMockDataButton}
194+
onClick={onMockDataGeneratorCtaButtonClicked}
156195
leftGlyph={<Icon glyph="Sparkle" />}
157196
>
158197
Generate Mock Data

packages/compass-collection/src/components/mock-data-generator-modal/constants.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,20 @@ export const StepButtonLabelMap = {
99
[MockDataGeneratorStep.GENERATE_DATA]: 'Done',
1010
} as const;
1111

12+
// Map of the current mock data generator step to the next step or 'finish' if the user is on the last step.
13+
// For the purposes of telemetry tracking the step progression in the modal.
14+
export const MOCK_DATA_GENERATOR_STEP_TO_NEXT_STEP_MAP: Record<
15+
MockDataGeneratorStep,
16+
MockDataGeneratorStep | 'finish'
17+
> = {
18+
[MockDataGeneratorStep.SCHEMA_CONFIRMATION]:
19+
MockDataGeneratorStep.SCHEMA_EDITOR,
20+
[MockDataGeneratorStep.SCHEMA_EDITOR]: MockDataGeneratorStep.DOCUMENT_COUNT,
21+
[MockDataGeneratorStep.DOCUMENT_COUNT]: MockDataGeneratorStep.PREVIEW_DATA,
22+
[MockDataGeneratorStep.PREVIEW_DATA]: MockDataGeneratorStep.GENERATE_DATA,
23+
[MockDataGeneratorStep.GENERATE_DATA]: 'finish',
24+
};
25+
1226
export const DEFAULT_DOCUMENT_COUNT = 1000;
1327
export const MAX_DOCUMENT_COUNT = 100000;
1428

packages/compass-collection/src/components/mock-data-generator-modal/document-count-screen.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { connect } from 'react-redux';
1111
import type { CollectionState } from '../../modules/collection-tab';
1212
import type { SchemaAnalysisState } from '../../schema-analysis-types';
1313
import { DEFAULT_DOCUMENT_COUNT, MAX_DOCUMENT_COUNT } from './constants';
14+
import { useTelemetry } from '@mongodb-js/compass-telemetry/provider';
1415

1516
const BYTE_PRECISION_THRESHOLD = 1000;
1617

@@ -67,6 +68,7 @@ const DocumentCountScreen = ({
6768
onDocumentCountChange,
6869
schemaAnalysisState,
6970
}: Props) => {
71+
const track = useTelemetry();
7072
const estimatedDiskSize = useMemo(
7173
() =>
7274
schemaAnalysisState.status === 'complete' &&
@@ -98,6 +100,9 @@ const DocumentCountScreen = ({
98100
const value = parseInt(event.target.value, 10);
99101
if (!isNaN(value)) {
100102
onDocumentCountChange(value);
103+
track('Mock Data Document Count Changed', {
104+
document_count: value,
105+
});
101106
}
102107
};
103108

0 commit comments

Comments
 (0)