Skip to content

feat(compass-collection): Collection Header Experimentation Integration for Mock Data Generator – CLOUDP-333847 #7181

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
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
@@ -1,6 +1,9 @@
import { expect } from 'chai';
import React, { type ComponentProps } from 'react';
import { render, screen, cleanup } from '@mongodb-js/testing-library-compass';
import {
renderWithActiveConnection,
screen,
} from '@mongodb-js/testing-library-compass';
import sinon from 'sinon';
import {
WorkspacesServiceProvider,
Expand All @@ -9,46 +12,71 @@ import {
import type { PreferencesAccess } from 'compass-preferences-model';
import { createSandboxFromDefaultPreferences } from 'compass-preferences-model';
import { PreferencesProvider } from 'compass-preferences-model/provider';
import { ExperimentTestName } from '@mongodb-js/compass-telemetry/provider';
import { CompassExperimentationProvider } from '@mongodb-js/compass-telemetry';
import type { ConnectionInfo } from '@mongodb-js/compass-connections/provider';

import CollectionHeaderActions from '../collection-header-actions';

describe('CollectionHeaderActions [Component]', function () {
let preferences: PreferencesAccess;
let mockUseAssignment: sinon.SinonStub;

beforeEach(async function () {
preferences = await createSandboxFromDefaultPreferences();
mockUseAssignment = sinon.stub();
mockUseAssignment.returns({
assignment: {
assignmentData: {
variant: 'mockDataGeneratorControl',
},
},
});
});

afterEach(function () {
sinon.restore();
});

const renderCollectionHeaderActions = (
const renderCollectionHeaderActions = async (
props: Partial<ComponentProps<typeof CollectionHeaderActions>> = {},
workspaceService: Partial<WorkspacesService> = {}
workspaceService: Partial<WorkspacesService> = {},
connectionInfo?: ConnectionInfo
) => {
return render(
<WorkspacesServiceProvider value={workspaceService as WorkspacesService}>
<PreferencesProvider value={preferences}>
<CollectionHeaderActions
namespace="test.test"
isReadonly={false}
{...props}
/>
</PreferencesProvider>
</WorkspacesServiceProvider>
const defaultProps = {
namespace: 'test.test',
isReadonly: false,
onOpenMockDataModal: sinon.stub(),
...props,
};

const ui = (
<CompassExperimentationProvider
useAssignment={mockUseAssignment}
assignExperiment={sinon.stub()}
>
<WorkspacesServiceProvider
value={workspaceService as WorkspacesService}
>
<PreferencesProvider value={preferences}>
<CollectionHeaderActions {...defaultProps} />
</PreferencesProvider>
</WorkspacesServiceProvider>
</CompassExperimentationProvider>
);

return await renderWithActiveConnection(ui, connectionInfo);
};

context('when the collection is not readonly', function () {
beforeEach(function () {
renderCollectionHeaderActions({
beforeEach(async function () {
await renderCollectionHeaderActions({
isReadonly: false,
namespace: 'db.coll2',
sourceName: 'db.coll',
});
});

afterEach(cleanup);

it('does not render any buttons', function () {
expect(
screen.queryByTestId('collection-header-actions-edit-button')
Expand All @@ -63,7 +91,7 @@ describe('CollectionHeaderActions [Component]', function () {
it('does not render edit view buttons when in readonly mode', async function () {
await preferences.savePreferences({ readOnly: true });

renderCollectionHeaderActions({
await renderCollectionHeaderActions({
isReadonly: true,
namespace: 'db.coll2',
sourceName: 'db.someSource',
Expand All @@ -78,8 +106,8 @@ describe('CollectionHeaderActions [Component]', function () {
).to.not.exist;
});

it('renders edit view buttons when not in readonly mode', function () {
renderCollectionHeaderActions({
it('renders edit view buttons when not in readonly mode', async function () {
await renderCollectionHeaderActions({
isReadonly: true,
namespace: 'db.coll2',
sourceName: 'db.someSource',
Expand All @@ -94,9 +122,9 @@ describe('CollectionHeaderActions [Component]', function () {

context('when the collection is a view', function () {
let openEditViewWorkspaceStub: sinon.SinonStub;
beforeEach(function () {
beforeEach(async function () {
openEditViewWorkspaceStub = sinon.stub();
renderCollectionHeaderActions(
await renderCollectionHeaderActions(
{
isReadonly: true,
namespace: 'db.coll2',
Expand All @@ -109,8 +137,6 @@ describe('CollectionHeaderActions [Component]', function () {
);
});

afterEach(cleanup);

it('shows a button to edit the view pipeline', function () {
expect(
screen.getByTestId('collection-header-actions-edit-button')
Expand All @@ -135,9 +161,9 @@ describe('CollectionHeaderActions [Component]', function () {

context('when the collection is editing a view', function () {
let openCollectionWorkspaceStub: sinon.SinonStub;
beforeEach(function () {
beforeEach(async function () {
openCollectionWorkspaceStub = sinon.stub();
renderCollectionHeaderActions(
await renderCollectionHeaderActions(
{
isReadonly: false,
namespace: 'db.coll2',
Expand All @@ -149,8 +175,6 @@ describe('CollectionHeaderActions [Component]', function () {
);
});

afterEach(cleanup);

it('shows a button to return to the view', function () {
expect(
screen.getByTestId('collection-header-actions-return-to-view-button')
Expand All @@ -168,4 +192,183 @@ describe('CollectionHeaderActions [Component]', function () {
);
});
});

context('Mock Data Generator Button', function () {
Copy link

@kpamaran kpamaran Aug 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest adding data-testid for the tooltip and asserting its existence directly in maybe new tests like 'should show Tooltip when ...'

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea, how about we add this once we hook up the data so we can test its presence when collection is empty?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good

const atlasConnectionInfo: ConnectionInfo = {
id: 'test-atlas-connection',
connectionOptions: {
connectionString: 'mongodb://localhost:27017',
},
atlasMetadata: {
orgId: 'test-org',
projectId: 'test-project',
clusterName: 'test-cluster',
clusterUniqueId: 'test-cluster-unique-id',
clusterType: 'REPLICASET',
clusterState: 'IDLE',
metricsId: 'test-metrics-id',
metricsType: 'replicaSet',
regionalBaseUrl: null,
instanceSize: 'M10',
supports: {
globalWrites: false,
rollingIndexes: true,
},
},
};

it('should not show Mock Data Generator button when user is in control group', async function () {
mockUseAssignment.returns({
assignment: {
assignmentData: {
variant: 'mockDataGeneratorControl',
},
},
});

await renderCollectionHeaderActions(
{
namespace: 'test.collection',
isReadonly: false,
},
{},
atlasConnectionInfo
);

expect(
screen.queryByTestId('collection-header-generate-mock-data-button')
).to.not.exist;
});

it('should not show Mock Data Generator button when not in Atlas', async function () {
mockUseAssignment.returns({
assignment: {
assignmentData: {
variant: 'treatment',
},
},
});

await renderCollectionHeaderActions({
namespace: 'test.collection',
isReadonly: false,
// Don't pass atlasConnectionInfo, to simulate not being in Atlas
});

expect(
screen.queryByTestId('collection-header-generate-mock-data-button')
).to.not.exist;
});

it('should not show Mock Data Generator button for readonly collections', async function () {
mockUseAssignment.returns({
assignment: {
assignmentData: {
variant: 'treatment',
},
},
});

await renderCollectionHeaderActions(
{
namespace: 'test.collection',
isReadonly: true,
},
{},
atlasConnectionInfo
);

expect(
screen.queryByTestId('collection-header-generate-mock-data-button')
).to.not.exist;
});

it('should not show Mock Data Generator button for views (sourceName present)', async function () {
mockUseAssignment.returns({
assignment: {
assignmentData: {
variant: 'treatment',
},
},
});

await renderCollectionHeaderActions(
{
namespace: 'test.collection',
isReadonly: false,
sourceName: 'source-collection',
},
{},
atlasConnectionInfo
);

expect(
screen.queryByTestId('collection-header-generate-mock-data-button')
).to.not.exist;
});

it('should show Mock Data Generator button when user is in treatment group and in Atlas', async function () {
mockUseAssignment.returns({
assignment: {
assignmentData: {
variant: 'mockDataGeneratorVariant',
},
},
});

await renderCollectionHeaderActions(
{
namespace: 'test.collection',
isReadonly: false,
},
{},
atlasConnectionInfo
);

expect(
screen.getByTestId('collection-header-generate-mock-data-button')
).to.exist;
});

it('should call useAssignment with correct parameters', async function () {
await renderCollectionHeaderActions({
namespace: 'test.collection',
isReadonly: false,
});

expect(mockUseAssignment).to.have.been.calledWith(
ExperimentTestName.mockDataGenerator,
true // trackIsInSample - Experiment viewed analytics event
);
});

it('should call onOpenMockDataModal when CTA button is clicked', async function () {
const onOpenMockDataModal = sinon.stub();

mockUseAssignment.returns({
assignment: {
assignmentData: {
variant: 'mockDataGeneratorVariant',
},
},
});

await renderCollectionHeaderActions(
{
namespace: 'test.collection',
isReadonly: false,
onOpenMockDataModal,
},
{},
atlasConnectionInfo
);

const button = screen.getByTestId(
'collection-header-generate-mock-data-button'
);
button.click();

expect(onOpenMockDataModal).to.have.been.calledOnce;
});
});
Copy link

@kpamaran kpamaran Aug 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would be nice to have a test that the modal opens initially with step MockDataGeneratorStep.AI_DISCLAIMER since that is controlled by the header

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: what step we open up to will depend on the state of project level settings, as well as a user-level "opt in" flag. We do have the test should call onOpenMockDataModal when CTA button is clicked, so I think the step we open to will depend based on functionality we will add in later tickets. Do you think this test is sufficient for now, or would you suggest adding something?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's good. No point in adding a test that'd be irrelevant so soon

});
Loading
Loading