diff --git a/packages/compass-collection/src/components/collection-header-actions/collection-header-actions.spec.tsx b/packages/compass-collection/src/components/collection-header-actions/collection-header-actions.spec.tsx
index 7d3951af8cc..1450a7566d2 100644
--- a/packages/compass-collection/src/components/collection-header-actions/collection-header-actions.spec.tsx
+++ b/packages/compass-collection/src/components/collection-header-actions/collection-header-actions.spec.tsx
@@ -3,6 +3,7 @@ import React, { type ComponentProps } from 'react';
import {
renderWithActiveConnection,
screen,
+ waitFor,
} from '@mongodb-js/testing-library-compass';
import sinon from 'sinon';
import {
@@ -14,6 +15,7 @@ import { CompassExperimentationProvider } from '@mongodb-js/compass-telemetry';
import type { ConnectionInfo } from '@mongodb-js/compass-connections/provider';
import CollectionHeaderActions from '../collection-header-actions';
+import { MAX_COLLECTION_NESTING_DEPTH } from '../mock-data-generator-modal/utils';
describe('CollectionHeaderActions [Component]', function () {
let mockUseAssignment: sinon.SinonStub;
@@ -230,94 +232,132 @@ describe('CollectionHeaderActions [Component]', function () {
);
});
- it('should call onOpenMockDataModal when CTA button is clicked', async function () {
- const onOpenMockDataModal = sinon.stub();
+ context('when in the mock data generator treatment variant', function () {
+ beforeEach(function () {
+ mockUseAssignment.returns({
+ assignment: {
+ assignmentData: {
+ variant: 'mockDataGeneratorVariant',
+ },
+ },
+ });
+ });
- mockUseAssignment.returns({
- assignment: {
- assignmentData: {
- variant: 'mockDataGeneratorVariant',
+ it('should send a track event when the button is viewed', async function () {
+ const result = await renderCollectionHeaderActions(
+ {
+ namespace: 'test.collection',
+ isReadonly: false,
},
- },
+ {},
+ atlasConnectionInfo
+ );
+
+ await waitFor(() => {
+ expect(result.track).to.have.been.calledWith(
+ 'Mock Data Generator CTA Button Viewed',
+ {
+ button_enabled: true,
+ gen_ai_features_enabled: false,
+ send_sample_values_enabled: false,
+ }
+ );
+ });
});
- await renderCollectionHeaderActions(
- {
- namespace: 'test.collection',
- isReadonly: false,
- onOpenMockDataModal,
- },
- {},
- atlasConnectionInfo
- );
+ it('should call onOpenMockDataModal when CTA button is clicked', async function () {
+ const onOpenMockDataModal = sinon.stub();
+ await renderCollectionHeaderActions(
+ {
+ namespace: 'test.collection',
+ isReadonly: false,
+ onOpenMockDataModal,
+ },
+ {},
+ atlasConnectionInfo
+ );
- const button = screen.getByTestId(
- 'collection-header-generate-mock-data-button'
- );
- button.click();
+ const button = screen.getByTestId(
+ 'collection-header-generate-mock-data-button'
+ );
+ button.click();
- expect(onOpenMockDataModal).to.have.been.calledOnce;
- });
+ expect(onOpenMockDataModal).to.have.been.calledOnce;
+ });
- it('should disable button for deeply nested collections', async function () {
- mockUseAssignment.returns({
- assignment: {
- assignmentData: {
- variant: 'mockDataGeneratorVariant', // Treatment variant
+ it('sends a track event when CTA button is clicked', async function () {
+ const onOpenMockDataModal = sinon.stub();
+
+ const result = await renderCollectionHeaderActions(
+ {
+ namespace: 'test.collection',
+ isReadonly: false,
+ onOpenMockDataModal,
},
- },
- });
+ {},
+ atlasConnectionInfo
+ );
- await renderCollectionHeaderActions(
- {
- namespace: 'test.collection',
- isReadonly: false,
- hasSchemaAnalysisData: true,
- analyzedSchemaDepth: 8, // Exceeds MAX_COLLECTION_NESTING_DEPTH (7)
- schemaAnalysisStatus: 'complete',
- onOpenMockDataModal: sinon.stub(),
- },
- {},
- atlasConnectionInfo
- );
+ const button = screen.getByTestId(
+ 'collection-header-generate-mock-data-button'
+ );
+ button.click();
- const button = screen.getByTestId(
- 'collection-header-generate-mock-data-button'
- );
- expect(button).to.exist;
- expect(button).to.have.attribute('aria-disabled', 'true');
- });
+ await waitFor(() => {
+ expect(result.track).to.have.been.calledWith(
+ 'Mock Data Generator Opened',
+ {
+ gen_ai_features_enabled: false,
+ send_sample_values_enabled: false,
+ }
+ );
+ });
+ });
- it('should show an error banner when the schema is in an unsupported state', async function () {
- mockUseAssignment.returns({
- assignment: {
- assignmentData: {
- variant: 'mockDataGeneratorVariant',
+ it('should disable button for deeply nested collections', async function () {
+ await renderCollectionHeaderActions(
+ {
+ namespace: 'test.collection',
+ isReadonly: false,
+ hasSchemaAnalysisData: true,
+ analyzedSchemaDepth: MAX_COLLECTION_NESTING_DEPTH + 1,
+ schemaAnalysisStatus: 'complete',
+ onOpenMockDataModal: sinon.stub(),
},
- },
+ {},
+ atlasConnectionInfo
+ );
+
+ const button = screen.getByTestId(
+ 'collection-header-generate-mock-data-button'
+ );
+ expect(button).to.exist;
+ expect(button).to.have.attribute('aria-disabled', 'true');
});
- await renderCollectionHeaderActions(
- {
- namespace: 'test.collection',
- isReadonly: false,
- hasSchemaAnalysisData: false,
- schemaAnalysisStatus: 'error',
- schemaAnalysisError: {
- errorType: 'unsupportedState',
- errorMessage: 'Unsupported state',
+ it('should show an error banner when the schema is in an unsupported state', async function () {
+ await renderCollectionHeaderActions(
+ {
+ namespace: 'test.collection',
+ isReadonly: false,
+ hasSchemaAnalysisData: false,
+ schemaAnalysisStatus: 'error',
+ schemaAnalysisError: {
+ errorType: 'unsupportedState',
+ errorMessage: 'Unsupported state',
+ },
+ onOpenMockDataModal: sinon.stub(),
},
- onOpenMockDataModal: sinon.stub(),
- },
- {},
- atlasConnectionInfo
- );
+ {},
+ atlasConnectionInfo
+ );
- const button = screen.getByTestId(
- 'collection-header-generate-mock-data-button'
- );
- expect(button).to.exist;
- expect(button).to.have.attribute('aria-disabled', 'true');
+ const button = screen.getByTestId(
+ 'collection-header-generate-mock-data-button'
+ );
+ expect(button).to.exist;
+ expect(button).to.have.attribute('aria-disabled', 'true');
+ });
});
});
});
diff --git a/packages/compass-collection/src/components/collection-header-actions/collection-header-actions.tsx b/packages/compass-collection/src/components/collection-header-actions/collection-header-actions.tsx
index 517233aaaa6..772724035f3 100644
--- a/packages/compass-collection/src/components/collection-header-actions/collection-header-actions.tsx
+++ b/packages/compass-collection/src/components/collection-header-actions/collection-header-actions.tsx
@@ -8,8 +8,12 @@ import {
} from '@mongodb-js/compass-components';
import { useConnectionInfo } from '@mongodb-js/compass-connections/provider';
import { useOpenWorkspace } from '@mongodb-js/compass-workspaces/provider';
-import React from 'react';
-import { usePreferences } from 'compass-preferences-model/provider';
+import React, { useCallback } from 'react';
+import {
+ useIsAIFeatureEnabled,
+ usePreference,
+ usePreferences,
+} from 'compass-preferences-model/provider';
import toNS from 'mongodb-ns';
import { wrapField } from '@mongodb-js/mongodb-constants';
import {
@@ -17,17 +21,15 @@ import {
useAssignment,
ExperimentTestName,
ExperimentTestGroup,
+ useTrackOnChange,
+ type TrackFunction,
} from '@mongodb-js/compass-telemetry/provider';
import {
SCHEMA_ANALYSIS_STATE_ANALYZING,
type SchemaAnalysisStatus,
type SchemaAnalysisError,
} from '../../schema-analysis-types';
-
-/**
- * Maximum allowed nesting depth for collections to show Mock Data Generator
- */
-const MAX_COLLECTION_NESTING_DEPTH = 7;
+import { MAX_COLLECTION_NESTING_DEPTH } from '../mock-data-generator-modal/utils';
const collectionHeaderActionsStyles = css({
display: 'flex',
@@ -92,6 +94,10 @@ const CollectionHeaderActions: React.FunctionComponent<
const { readWrite: preferencesReadWrite, enableShell: showOpenShellButton } =
usePreferences(['readWrite', 'enableShell']);
const track = useTelemetry();
+ const isAIFeatureEnabled = useIsAIFeatureEnabled();
+ const isSampleDocumentPassingEnabled = usePreference(
+ 'enableGenAISampleDocumentPassing'
+ );
// Get experiment assignment for Mock Data Generator
const mockDataGeneratorAssignment = useAssignment(
@@ -122,6 +128,39 @@ const CollectionHeaderActions: React.FunctionComponent<
const isView = isReadonly && sourceName && !editViewName;
const showViewEdit = isView && !preferencesReadWrite;
+ const shouldDisableMockDataButton =
+ !hasSchemaAnalysisData || exceedsMaxNestingDepth;
+
+ const onMockDataGeneratorCtaButtonClicked = useCallback(() => {
+ track('Mock Data Generator Opened', {
+ gen_ai_features_enabled: isAIFeatureEnabled,
+ send_sample_values_enabled: isSampleDocumentPassingEnabled,
+ });
+ onOpenMockDataModal();
+ }, [
+ track,
+ isAIFeatureEnabled,
+ isSampleDocumentPassingEnabled,
+ onOpenMockDataModal,
+ ]);
+
+ useTrackOnChange(
+ (track: TrackFunction) => {
+ if (shouldShowMockDataButton) {
+ track('Mock Data Generator CTA Button Viewed', {
+ button_enabled: !shouldDisableMockDataButton,
+ gen_ai_features_enabled: isAIFeatureEnabled,
+ send_sample_values_enabled: isSampleDocumentPassingEnabled,
+ });
+ }
+ },
+ [
+ shouldShowMockDataButton,
+ shouldDisableMockDataButton,
+ isAIFeatureEnabled,
+ isSampleDocumentPassingEnabled,
+ ]
+ );
return (
}
>
Generate Mock Data
diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/constants.ts b/packages/compass-collection/src/components/mock-data-generator-modal/constants.ts
index caf6bced639..648dd564097 100644
--- a/packages/compass-collection/src/components/mock-data-generator-modal/constants.ts
+++ b/packages/compass-collection/src/components/mock-data-generator-modal/constants.ts
@@ -9,6 +9,20 @@ export const StepButtonLabelMap = {
[MockDataGeneratorStep.GENERATE_DATA]: 'Done',
} as const;
+// Map of the current mock data generator step to the next step or 'finish' if the user is on the last step.
+// For the purposes of telemetry tracking the step progression in the modal.
+export const MOCK_DATA_GENERATOR_STEP_TO_NEXT_STEP_MAP: Record<
+ MockDataGeneratorStep,
+ MockDataGeneratorStep | 'finish'
+> = {
+ [MockDataGeneratorStep.SCHEMA_CONFIRMATION]:
+ MockDataGeneratorStep.SCHEMA_EDITOR,
+ [MockDataGeneratorStep.SCHEMA_EDITOR]: MockDataGeneratorStep.DOCUMENT_COUNT,
+ [MockDataGeneratorStep.DOCUMENT_COUNT]: MockDataGeneratorStep.PREVIEW_DATA,
+ [MockDataGeneratorStep.PREVIEW_DATA]: MockDataGeneratorStep.GENERATE_DATA,
+ [MockDataGeneratorStep.GENERATE_DATA]: 'finish',
+};
+
export const DEFAULT_DOCUMENT_COUNT = 1000;
export const MAX_DOCUMENT_COUNT = 100000;
diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/document-count-screen.tsx b/packages/compass-collection/src/components/mock-data-generator-modal/document-count-screen.tsx
index f17816f34c8..cae442c81a4 100644
--- a/packages/compass-collection/src/components/mock-data-generator-modal/document-count-screen.tsx
+++ b/packages/compass-collection/src/components/mock-data-generator-modal/document-count-screen.tsx
@@ -11,6 +11,7 @@ import { connect } from 'react-redux';
import type { CollectionState } from '../../modules/collection-tab';
import type { SchemaAnalysisState } from '../../schema-analysis-types';
import { DEFAULT_DOCUMENT_COUNT, MAX_DOCUMENT_COUNT } from './constants';
+import { useTelemetry } from '@mongodb-js/compass-telemetry/provider';
const BYTE_PRECISION_THRESHOLD = 1000;
@@ -67,6 +68,7 @@ const DocumentCountScreen = ({
onDocumentCountChange,
schemaAnalysisState,
}: Props) => {
+ const track = useTelemetry();
const estimatedDiskSize = useMemo(
() =>
schemaAnalysisState.status === 'complete' &&
@@ -98,6 +100,9 @@ const DocumentCountScreen = ({
const value = parseInt(event.target.value, 10);
if (!isNaN(value)) {
onDocumentCountChange(value);
+ track('Mock Data Document Count Changed', {
+ document_count: value,
+ });
}
};
diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/faker-schema-editor-screen.tsx b/packages/compass-collection/src/components/mock-data-generator-modal/faker-schema-editor-screen.tsx
index c4b5a6cfd20..d4d402a424f 100644
--- a/packages/compass-collection/src/components/mock-data-generator-modal/faker-schema-editor-screen.tsx
+++ b/packages/compass-collection/src/components/mock-data-generator-modal/faker-schema-editor-screen.tsx
@@ -20,6 +20,7 @@ import type {
MockDataGeneratorState,
} from './types';
import type { MongoDBFieldType } from '../../schema-analysis-types';
+import { useTelemetry } from '@mongodb-js/compass-telemetry/provider';
const containerStyles = css({
display: 'flex',
@@ -62,6 +63,7 @@ const FakerSchemaEditorContent = ({
fakerSchema: FakerSchema;
onSchemaConfirmed: (isConfirmed: boolean) => void;
}) => {
+ const track = useTelemetry();
const [fakerSchemaFormValues, setFakerSchemaFormValues] =
React.useState(fakerSchema);
@@ -93,6 +95,9 @@ const FakerSchemaEditorContent = ({
const originalLlmMapping = originalLlmMappings.current[activeField];
if (currentMapping) {
+ const previousJsonType = currentMapping.mongoType;
+ const previousFakerMethod = currentMapping.fakerMethod;
+
const isSwitchingToOriginalType =
originalLlmMapping && newJsonType === originalLlmMapping.mongoType;
@@ -105,6 +110,16 @@ const FakerSchemaEditorContent = ({
fakerArgs: [],
};
+ const newFakerMethod = newMapping.fakerMethod;
+
+ track('Mock Data JSON Type Changed', {
+ field_name: activeField,
+ previous_json_type: previousJsonType,
+ new_json_type: newJsonType,
+ previous_faker_method: previousFakerMethod,
+ new_faker_method: newFakerMethod,
+ });
+
setFakerSchemaFormValues({
...fakerSchemaFormValues,
[activeField]: newMapping,
@@ -118,6 +133,8 @@ const FakerSchemaEditorContent = ({
const originalLlmMapping = originalLlmMappings.current[activeField];
if (currentMapping) {
+ const previousFakerMethod = currentMapping.fakerMethod;
+
const isSwitchingToLlmSuggestion =
originalLlmMapping &&
currentMapping.mongoType === originalLlmMapping.mongoType &&
@@ -131,6 +148,13 @@ const FakerSchemaEditorContent = ({
fakerArgs: [],
};
+ track('Mock Data Faker Method Changed', {
+ field_name: activeField,
+ json_type: currentMapping.mongoType,
+ previous_faker_method: previousFakerMethod,
+ new_faker_method: newFakerFunction,
+ });
+
setFakerSchemaFormValues({
...fakerSchemaFormValues,
[activeField]: newMapping,
diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.spec.tsx b/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.spec.tsx
index 5c359f5da15..4aa1e833a83 100644
--- a/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.spec.tsx
+++ b/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.spec.tsx
@@ -12,15 +12,16 @@ import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import MockDataGeneratorModal from './mock-data-generator-modal';
-import { MockDataGeneratorStep } from './types';
+import { DataGenerationStep, MockDataGeneratorStep } from './types';
import {
DEFAULT_CONNECTION_STRING_FALLBACK,
+ MOCK_DATA_GENERATOR_STEP_TO_NEXT_STEP_MAP,
StepButtonLabelMap,
} from './constants';
import type { CollectionState } from '../../modules/collection-tab';
import { default as collectionTabReducer } from '../../modules/collection-tab';
import type { ConnectionInfo } from '@mongodb-js/connection-info';
-import type { MockDataSchemaResponse } from '@mongodb-js/compass-generative-ai';
+import { type MockDataSchemaResponse } from '@mongodb-js/compass-generative-ai';
import type { SchemaAnalysisState } from '../../schema-analysis-types';
import * as scriptGenerationUtils from './script-generation-utils';
@@ -149,6 +150,21 @@ describe('MockDataGeneratorModal', () => {
);
});
+ it('fires a track event when the close button is clicked', async () => {
+ const result = await renderModal();
+ userEvent.click(screen.getByLabelText('Close modal'));
+ await waitFor(() => {
+ expect(result.track).to.have.been.calledWith(
+ 'Mock Data Generator Dismissed',
+ {
+ screen: MockDataGeneratorStep.SCHEMA_CONFIRMATION,
+ gen_ai_features_enabled: false,
+ send_sample_values_enabled: false,
+ }
+ );
+ });
+ });
+
it('closes the modal when the cancel button is clicked', async () => {
await renderModal();
@@ -160,6 +176,21 @@ describe('MockDataGeneratorModal', () => {
);
});
+ it('fires a track event when the cancel button is clicked', async () => {
+ const result = await renderModal();
+ userEvent.click(screen.getByText('Cancel'));
+ await waitFor(() => {
+ expect(result.track).to.have.been.calledWith(
+ 'Mock Data Generator Dismissed',
+ {
+ screen: MockDataGeneratorStep.SCHEMA_CONFIRMATION,
+ gen_ai_features_enabled: false,
+ send_sample_values_enabled: false,
+ }
+ );
+ });
+ });
+
function createMockServicesWithSlowAiRequest() {
let abortSignalReceived = false;
let rejectPromise: (reason?: any) => void;
@@ -329,21 +360,25 @@ describe('MockDataGeneratorModal', () => {
fields: [
{
fieldPath: 'name',
+ mongoType: 'String',
fakerMethod: 'person.firstName',
fakerArgs: [],
},
{
fieldPath: 'age',
+ mongoType: 'Int32',
fakerMethod: 'number.int',
fakerArgs: [],
},
{
fieldPath: 'email',
+ mongoType: 'String',
fakerMethod: 'internet',
fakerArgs: [],
},
{
fieldPath: 'username',
+ mongoType: 'String',
fakerMethod: 'noSuchMethod',
fakerArgs: [],
},
@@ -438,6 +473,7 @@ describe('MockDataGeneratorModal', () => {
fields: [
{
fieldPath: 'name',
+ mongoType: 'String',
fakerMethod: 'person.firstName',
fakerArgs: [],
isArray: false,
@@ -445,6 +481,7 @@ describe('MockDataGeneratorModal', () => {
},
{
fieldPath: 'email',
+ mongoType: 'String',
fakerMethod: 'internet.email',
fakerArgs: [],
isArray: false,
@@ -474,6 +511,7 @@ describe('MockDataGeneratorModal', () => {
fields: [
{
fieldPath: 'name',
+ mongoType: 'String',
fakerMethod: 'person.firstName',
fakerArgs: [],
isArray: false,
@@ -481,6 +519,7 @@ describe('MockDataGeneratorModal', () => {
},
{
fieldPath: 'age',
+ mongoType: 'Int32',
fakerMethod: 'number.int',
fakerArgs: [],
isArray: false,
@@ -548,6 +587,7 @@ describe('MockDataGeneratorModal', () => {
fields: [
{
fieldPath: 'name',
+ mongoType: 'String',
fakerMethod: 'person.firstName',
fakerArgs: largeLengthArgs,
isArray: false,
@@ -555,6 +595,7 @@ describe('MockDataGeneratorModal', () => {
},
{
fieldPath: 'age',
+ mongoType: 'Int32',
fakerMethod: 'number.int',
fakerArgs: [
{
@@ -568,6 +609,7 @@ describe('MockDataGeneratorModal', () => {
},
{
fieldPath: 'username',
+ mongoType: 'String',
fakerMethod: 'string.alpha',
// large string
fakerArgs: ['a'.repeat(1001)],
@@ -576,6 +618,7 @@ describe('MockDataGeneratorModal', () => {
},
{
fieldPath: 'avatar',
+ mongoType: 'String',
fakerMethod: 'image.url',
fakerArgs: [
{
@@ -731,6 +774,92 @@ describe('MockDataGeneratorModal', () => {
screen.getByTestId('next-step-button').getAttribute('aria-disabled')
).to.equal('false');
});
+
+ it('fires a track event when the user changes the JSON field type', async () => {
+ const result = await renderModal({
+ mockServices: mockServicesWithMockDataResponse,
+ schemaAnalysis: mockSchemaAnalysis,
+ });
+
+ // advance to the schema editor step
+ userEvent.click(screen.getByText('Confirm'));
+ await waitFor(() => {
+ expect(screen.getByTestId('faker-schema-editor')).to.exist;
+ });
+
+ const jsonTypeSelect = screen.getByLabelText('JSON Type');
+ userEvent.click(jsonTypeSelect);
+ const numberOption = await screen.findByRole('option', {
+ name: 'Number',
+ });
+ userEvent.click(numberOption);
+
+ await waitFor(() => {
+ expect(result.track).to.have.been.calledWith(
+ 'Mock Data JSON Type Changed',
+ {
+ field_name: 'name',
+ previous_json_type: 'String',
+ new_json_type: 'Number',
+ previous_faker_method: 'person.firstName',
+ new_faker_method: 'number.int',
+ }
+ );
+ });
+ });
+
+ it('fires a track event when the user changes the faker method', async () => {
+ const result = await renderModal({
+ mockServices: mockServicesWithMockDataResponse,
+ schemaAnalysis: mockSchemaAnalysis,
+ });
+
+ // advance to the schema editor step
+ userEvent.click(screen.getByText('Confirm'));
+ await waitFor(() => {
+ expect(screen.getByTestId('faker-schema-editor')).to.exist;
+ });
+
+ const fakerMethodSelect = screen.getByLabelText('Faker Function');
+ userEvent.click(fakerMethodSelect);
+ const emailOption = await screen.findByRole('option', {
+ name: 'internet.email',
+ });
+ userEvent.click(emailOption);
+
+ await waitFor(() => {
+ expect(result.track).to.have.been.calledWith(
+ 'Mock Data Faker Method Changed',
+ {
+ field_name: 'name',
+ json_type: 'String',
+ previous_faker_method: 'person.firstName',
+ new_faker_method: 'internet.email',
+ }
+ );
+ });
+ });
+
+ it('fires a track event when the user proceeds to the next step', async () => {
+ const result = await renderModal({
+ mockServices: mockServicesWithMockDataResponse,
+ });
+
+ userEvent.click(screen.getByText('Confirm'));
+
+ await waitFor(() => {
+ expect(result.track).to.have.been.calledWith(
+ 'Mock Data Generator Screen Proceeded',
+ {
+ from_screen: MockDataGeneratorStep.SCHEMA_CONFIRMATION,
+ to_screen:
+ MOCK_DATA_GENERATOR_STEP_TO_NEXT_STEP_MAP[
+ MockDataGeneratorStep.SCHEMA_CONFIRMATION
+ ],
+ }
+ );
+ });
+ });
});
describe('on the document count step', () => {
@@ -810,6 +939,33 @@ describe('MockDataGeneratorModal', () => {
userEvent.type(documentCountInput, '2000');
expect(screen.getByText('200.0 kB')).to.exist;
});
+
+ it('fires a track event when the document count is changed', async () => {
+ const result = await renderModal({
+ currentStep: MockDataGeneratorStep.DOCUMENT_COUNT,
+ schemaAnalysis: {
+ ...defaultSchemaAnalysisState,
+ schemaMetadata: {
+ ...defaultSchemaAnalysisState.schemaMetadata,
+ avgDocumentSize: 100, // 100 bytes
+ },
+ },
+ });
+
+ const documentCountInput = screen.getByLabelText(
+ 'Documents to generate in current collection'
+ );
+ userEvent.clear(documentCountInput);
+ userEvent.type(documentCountInput, '2222');
+ await waitFor(() => {
+ expect(result.track).to.have.been.calledWith(
+ 'Mock Data Document Count Changed',
+ {
+ document_count: 2222,
+ }
+ );
+ });
+ });
});
describe('on the generate data step', () => {
@@ -1075,6 +1231,106 @@ describe('MockDataGeneratorModal', () => {
expect(screen.getByText('insertMany')).to.exist;
});
+ it('fires a track event when the script is generated', async () => {
+ const result = await renderModal({
+ currentStep: MockDataGeneratorStep.GENERATE_DATA,
+ fakerSchemaGeneration: {
+ status: 'completed',
+ originalLlmResponse: {
+ name: {
+ fakerMethod: 'person.firstName',
+ fakerArgs: [],
+ probability: 1.0,
+ mongoType: 'String',
+ },
+ email: {
+ fakerMethod: 'internet.email',
+ fakerArgs: [],
+ probability: 1.0,
+ mongoType: 'String',
+ },
+ },
+ editedFakerSchema: {
+ name: {
+ fakerMethod: 'person.firstName',
+ fakerArgs: [],
+ probability: 1.0,
+ mongoType: 'String',
+ },
+ email: {
+ fakerMethod: 'internet.email',
+ fakerArgs: [],
+ probability: 1.0,
+ mongoType: 'String',
+ },
+ },
+ requestId: 'test-request-id',
+ },
+ });
+
+ await waitFor(() => {
+ expect(result.track).to.have.been.calledWith(
+ 'Mock Data Script Generated',
+ {
+ field_count: 2,
+ output_docs_count: 100,
+ }
+ );
+ });
+ });
+
+ it('fires a track event when the mongosh script is copied', async () => {
+ const result = await renderModal({
+ currentStep: MockDataGeneratorStep.GENERATE_DATA,
+ fakerSchemaGeneration: {
+ status: 'completed',
+ originalLlmResponse: {
+ name: {
+ fakerMethod: 'person.firstName',
+ fakerArgs: [],
+ probability: 1.0,
+ mongoType: 'String',
+ },
+ email: {
+ fakerMethod: 'internet.email',
+ fakerArgs: [],
+ probability: 1.0,
+ mongoType: 'String',
+ },
+ },
+ editedFakerSchema: {
+ name: {
+ fakerMethod: 'person.firstName',
+ fakerArgs: [],
+ probability: 1.0,
+ mongoType: 'String',
+ },
+ email: {
+ fakerMethod: 'internet.email',
+ fakerArgs: [],
+ probability: 1.0,
+ mongoType: 'String',
+ },
+ },
+ requestId: 'test-request-id',
+ },
+ });
+
+ const codeCopyButtons = screen.getAllByTestId('lg-code-copy_button');
+ const mongoshCopyButton = codeCopyButtons[1];
+
+ expect(codeCopyButtons).to.have.length(2);
+ userEvent.click(mongoshCopyButton);
+ await waitFor(() => {
+ expect(result.track).to.have.been.calledWith(
+ 'Mock Data Script Copied',
+ {
+ step: DataGenerationStep.RUN_SCRIPT,
+ }
+ );
+ });
+ });
+
it('shows userConnectionString in the mongosh command when available', async () => {
const atlasConnectionInfo: ConnectionInfo = {
id: 'test-atlas-connection',
@@ -1169,7 +1425,6 @@ describe('MockDataGeneratorModal', () => {
StepButtonLabelMap
) as unknown as MockDataGeneratorStep[];
- // note: these tests can be removed after every modal step is implemented
steps.forEach((currentStep) => {
it(`renders the button with the correct label when the user is in step "${currentStep}"`, async () => {
await renderModal({ currentStep });
@@ -1177,6 +1432,16 @@ describe('MockDataGeneratorModal', () => {
StepButtonLabelMap[currentStep]
);
});
+
+ it('fires a track event when the user is viewing a mock data generator step', async () => {
+ const result = await renderModal({ currentStep });
+ expect(result.track).to.have.been.calledWith(
+ 'Mock Data Generator Screen Viewed',
+ {
+ screen: currentStep,
+ }
+ );
+ });
});
});
});
diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.tsx b/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.tsx
index 74f48452bc9..c8bf3155baa 100644
--- a/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.tsx
+++ b/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.tsx
@@ -1,4 +1,4 @@
-import React, { useMemo } from 'react';
+import React, { useCallback, useEffect, useMemo } from 'react';
import { connect } from 'react-redux';
import {
@@ -17,6 +17,7 @@ import { type MockDataGeneratorState, MockDataGeneratorStep } from './types';
import {
DEFAULT_DOCUMENT_COUNT,
MAX_DOCUMENT_COUNT,
+ MOCK_DATA_GENERATOR_STEP_TO_NEXT_STEP_MAP,
StepButtonLabelMap,
} from './constants';
import type { CollectionState } from '../../modules/collection-tab';
@@ -32,6 +33,11 @@ import FakerSchemaEditorScreen from './faker-schema-editor-screen';
import ScriptScreen from './script-screen';
import DocumentCountScreen from './document-count-screen';
import PreviewScreen from './preview-screen';
+import { useTelemetry } from '@mongodb-js/compass-telemetry/provider';
+import {
+ useIsAIFeatureEnabled,
+ usePreference,
+} from 'compass-preferences-model/provider';
const footerStyles = css`
flex-direction: row;
@@ -75,6 +81,11 @@ const MockDataGeneratorModal = ({
const [documentCount, setDocumentCount] = React.useState(
DEFAULT_DOCUMENT_COUNT
);
+ const track = useTelemetry();
+ const isAIFeatureEnabled = useIsAIFeatureEnabled();
+ const isSampleDocumentPassingEnabled = usePreference(
+ 'enableGenAISampleDocumentPassing'
+ );
const modalBodyContent = useMemo(() => {
switch (currentStep) {
@@ -114,6 +125,12 @@ const MockDataGeneratorModal = ({
setDocumentCount,
]);
+ useEffect(() => {
+ track('Mock Data Generator Screen Viewed', {
+ screen: currentStep,
+ });
+ }, [currentStep, track]);
+
const isNextButtonDisabled =
(currentStep === MockDataGeneratorStep.SCHEMA_EDITOR &&
!isSchemaConfirmed) ||
@@ -122,7 +139,13 @@ const MockDataGeneratorModal = ({
(currentStep === MockDataGeneratorStep.DOCUMENT_COUNT &&
documentCount > MAX_DOCUMENT_COUNT);
- const handleNextClick = () => {
+ const handleNextClick = useCallback(() => {
+ const nextStep = MOCK_DATA_GENERATOR_STEP_TO_NEXT_STEP_MAP[currentStep];
+ track('Mock Data Generator Screen Proceeded', {
+ from_screen: currentStep,
+ to_screen: nextStep,
+ });
+
if (currentStep === MockDataGeneratorStep.GENERATE_DATA) {
onClose();
} else if (currentStep === MockDataGeneratorStep.SCHEMA_CONFIRMATION) {
@@ -130,7 +153,7 @@ const MockDataGeneratorModal = ({
} else {
onNextStep();
}
- };
+ }, [currentStep, onConfirmSchema, onNextStep, onClose, track]);
const shouldShowNamespace =
currentStep !== MockDataGeneratorStep.GENERATE_DATA;
@@ -143,13 +166,28 @@ const MockDataGeneratorModal = ({
onPreviousStep();
};
+ const onModalClose = useCallback(() => {
+ track('Mock Data Generator Dismissed', {
+ screen: currentStep,
+ gen_ai_features_enabled: isAIFeatureEnabled,
+ send_sample_values_enabled: isSampleDocumentPassingEnabled,
+ });
+ onClose();
+ }, [
+ currentStep,
+ track,
+ onClose,
+ isAIFeatureEnabled,
+ isSampleDocumentPassingEnabled,
+ ]);
+
return (
{
if (!open) {
- onClose();
+ onModalClose();
}
}}
data-testid="generate-mock-data-modal"
@@ -171,7 +209,7 @@ const MockDataGeneratorModal = ({
Back
-
+