Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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;
Expand Down Expand Up @@ -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');
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,28 @@ 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 {
useTelemetry,
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',
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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 (
<div
Expand Down Expand Up @@ -151,8 +190,8 @@ const CollectionHeaderActions: React.FunctionComponent<
<Button
data-testid="collection-header-generate-mock-data-button"
size={ButtonSize.Small}
disabled={!hasSchemaAnalysisData || exceedsMaxNestingDepth}
onClick={onOpenMockDataModal}
disabled={shouldDisableMockDataButton}
onClick={onMockDataGeneratorCtaButtonClicked}
leftGlyph={<Icon glyph="Sparkle" />}
>
Generate Mock Data
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -67,6 +68,7 @@ const DocumentCountScreen = ({
onDocumentCountChange,
schemaAnalysisState,
}: Props) => {
const track = useTelemetry();
const estimatedDiskSize = useMemo(
() =>
schemaAnalysisState.status === 'complete' &&
Expand Down Expand Up @@ -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,
});
}
};

Expand Down
Loading
Loading