diff --git a/package-lock.json b/package-lock.json
index 1a10f197185..dc29161bea3 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -6732,6 +6732,28 @@
"@leafygreen-ui/leafygreen-provider": "^4.0.2"
}
},
+ "node_modules/@leafygreen-ui/copyable": {
+ "version": "10.0.14",
+ "resolved": "https://registry.npmjs.org/@leafygreen-ui/copyable/-/copyable-10.0.14.tgz",
+ "integrity": "sha512-O4dstObiN04Zjrd4Z10ratWZAi7pnb6gpML/HQnkAxR+0OwzKOvrR6XOQ2/3IzlLfIiY1TUHIbjavpHy/ppqVw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@leafygreen-ui/button": "^23.1.6",
+ "@leafygreen-ui/emotion": "^4.1.1",
+ "@leafygreen-ui/hooks": "^8.4.1",
+ "@leafygreen-ui/icon": "^13.4.0",
+ "@leafygreen-ui/lib": "^14.2.0",
+ "@leafygreen-ui/palette": "^4.1.4",
+ "@leafygreen-ui/tokens": "^2.12.2",
+ "@leafygreen-ui/tooltip": "^13.0.13",
+ "@leafygreen-ui/typography": "^20.1.9",
+ "clipboard": "^2.0.6",
+ "polished": "^4.2.2"
+ },
+ "peerDependencies": {
+ "@leafygreen-ui/leafygreen-provider": "^4.0.7"
+ }
+ },
"node_modules/@leafygreen-ui/descendants": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@leafygreen-ui/descendants/-/descendants-2.1.5.tgz",
@@ -47813,6 +47835,7 @@
"@leafygreen-ui/code": "^16.0.2",
"@leafygreen-ui/combobox": "^11.0.2",
"@leafygreen-ui/confirmation-modal": "^6.0.2",
+ "@leafygreen-ui/copyable": "^10.0.14",
"@leafygreen-ui/descendants": "^2.1.0",
"@leafygreen-ui/emotion": "^4.0.9",
"@leafygreen-ui/guide-cue": "^7.0.2",
@@ -58479,6 +58502,24 @@
"@leafygreen-ui/typography": "^20.0.2"
}
},
+ "@leafygreen-ui/copyable": {
+ "version": "10.0.14",
+ "resolved": "https://registry.npmjs.org/@leafygreen-ui/copyable/-/copyable-10.0.14.tgz",
+ "integrity": "sha512-O4dstObiN04Zjrd4Z10ratWZAi7pnb6gpML/HQnkAxR+0OwzKOvrR6XOQ2/3IzlLfIiY1TUHIbjavpHy/ppqVw==",
+ "requires": {
+ "@leafygreen-ui/button": "^22.0.2",
+ "@leafygreen-ui/emotion": "^4.0.9",
+ "@leafygreen-ui/hooks": "^8.3.4",
+ "@leafygreen-ui/icon": "^13.1.2",
+ "@leafygreen-ui/lib": "^15.2.0",
+ "@leafygreen-ui/palette": "^4.1.3",
+ "@leafygreen-ui/tokens": "^2.11.3",
+ "@leafygreen-ui/tooltip": "^13.0.13",
+ "@leafygreen-ui/typography": "^20.0.2",
+ "clipboard": "^2.0.6",
+ "polished": "^4.2.2"
+ }
+ },
"@leafygreen-ui/descendants": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@leafygreen-ui/descendants/-/descendants-2.1.5.tgz",
@@ -61267,6 +61308,7 @@
"@leafygreen-ui/code": "^16.0.2",
"@leafygreen-ui/combobox": "^11.0.2",
"@leafygreen-ui/confirmation-modal": "^6.0.2",
+ "@leafygreen-ui/copyable": "^10.0.14",
"@leafygreen-ui/descendants": "^2.1.0",
"@leafygreen-ui/emotion": "^4.0.9",
"@leafygreen-ui/guide-cue": "^7.0.2",
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 9d0dd164906..e45f5816cf5 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
@@ -2,7 +2,7 @@ import { expect } from 'chai';
import React from 'react';
import {
screen,
- render,
+ renderWithActiveConnection,
waitFor,
userEvent,
} from '@mongodb-js/testing-library-compass';
@@ -14,12 +14,19 @@ import { MockDataGeneratorStep } from './types';
import { 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';
describe('MockDataGeneratorModal', () => {
- function renderModal({
+ async function renderModal({
isOpen = true,
currentStep = MockDataGeneratorStep.SCHEMA_CONFIRMATION,
mockServices = createMockServices(),
+ connectionInfo,
+ }: {
+ isOpen?: boolean;
+ currentStep?: MockDataGeneratorStep;
+ mockServices?: any;
+ connectionInfo?: ConnectionInfo;
} = {}) {
const initialState: CollectionState = {
workspaceTabId: 'test-workspace-tab-id',
@@ -52,10 +59,11 @@ describe('MockDataGeneratorModal', () => {
applyMiddleware(thunk.withExtraArgument(mockServices))
);
- return render(
+ return await renderWithActiveConnection(
-
+ ,
+ connectionInfo
);
}
@@ -89,20 +97,20 @@ describe('MockDataGeneratorModal', () => {
}
describe('generally', () => {
- it('renders the modal when isOpen is true', () => {
- renderModal();
+ it('renders the modal when isOpen is true', async () => {
+ await renderModal();
expect(screen.getByTestId('generate-mock-data-modal')).to.exist;
});
- it('does not render the modal when isOpen is false', () => {
- renderModal({ isOpen: false });
+ it('does not render the modal when isOpen is false', async () => {
+ await renderModal({ isOpen: false });
expect(screen.queryByTestId('generate-mock-data-modal')).to.not.exist;
});
it('closes the modal when the close button is clicked', async () => {
- renderModal();
+ await renderModal();
expect(screen.getByTestId('generate-mock-data-modal')).to.exist;
userEvent.click(screen.getByLabelText('Close modal'));
@@ -113,7 +121,7 @@ describe('MockDataGeneratorModal', () => {
});
it('closes the modal when the cancel button is clicked', async () => {
- renderModal();
+ await renderModal();
expect(screen.getByTestId('generate-mock-data-modal')).to.exist;
userEvent.click(screen.getByText('Cancel'));
@@ -154,7 +162,7 @@ describe('MockDataGeneratorModal', () => {
it('cancels in-flight faker mapping requests when the cancel button is clicked', async () => {
const mockServices = createMockServicesWithSlowAiRequest();
- renderModal({ mockServices: mockServices as any });
+ await renderModal({ mockServices: mockServices as any });
expect(screen.getByTestId('raw-schema-confirmation')).to.exist;
userEvent.click(screen.getByText('Confirm'));
@@ -170,7 +178,7 @@ describe('MockDataGeneratorModal', () => {
it('cancels in-flight faker mapping requests when the back button is clicked after schema confirmation', async () => {
const mockServices = createMockServicesWithSlowAiRequest();
- renderModal({ mockServices: mockServices as any });
+ await renderModal({ mockServices: mockServices as any });
expect(screen.getByTestId('raw-schema-confirmation')).to.exist;
userEvent.click(screen.getByText('Confirm'));
@@ -186,8 +194,8 @@ describe('MockDataGeneratorModal', () => {
});
describe('on the schema confirmation step', () => {
- it('disables the Back button', () => {
- renderModal();
+ it('disables the Back button', async () => {
+ await renderModal();
expect(
screen
@@ -197,7 +205,7 @@ describe('MockDataGeneratorModal', () => {
});
it('renders the faker schema editor when the confirm button is clicked', async () => {
- renderModal();
+ await renderModal();
expect(screen.getByTestId('raw-schema-confirmation')).to.exist;
expect(screen.queryByTestId('faker-schema-editor')).to.not.exist;
@@ -212,7 +220,7 @@ describe('MockDataGeneratorModal', () => {
const mockServices = createMockServices();
mockServices.atlasAiService.getMockDataSchema = () =>
Promise.reject('faker schema generation failed');
- renderModal({ mockServices });
+ await renderModal({ mockServices });
expect(screen.getByTestId('raw-schema-confirmation')).to.exist;
expect(screen.queryByTestId('faker-schema-editor')).to.not.exist;
@@ -228,6 +236,92 @@ describe('MockDataGeneratorModal', () => {
// todo: assert that closing then re-opening the modal after an LLM err removes the err message
});
+ describe('on the generate data step', () => {
+ it('enables the Back button', async () => {
+ await renderModal({ currentStep: MockDataGeneratorStep.GENERATE_DATA });
+
+ expect(
+ screen
+ .getByRole('button', { name: 'Back' })
+ .getAttribute('aria-disabled')
+ ).to.not.equal('true');
+ });
+
+ it('renders the main sections: Prerequisites, steps, and Resources', async () => {
+ await renderModal({ currentStep: MockDataGeneratorStep.GENERATE_DATA });
+
+ expect(screen.getByText('Prerequisites')).to.exist;
+ expect(screen.getByText('1. Create a .js file with the following script'))
+ .to.exist;
+ expect(screen.getByText('2. Run the script with')).to.exist;
+ expect(screen.getByText('Resources')).to.exist;
+ });
+
+ it('closes the modal when the Done button is clicked', async () => {
+ await renderModal({ currentStep: MockDataGeneratorStep.GENERATE_DATA });
+
+ expect(screen.getByTestId('generate-mock-data-modal')).to.exist;
+ userEvent.click(screen.getByText('Done'));
+ await waitFor(
+ () =>
+ expect(screen.queryByTestId('generate-mock-data-modal')).to.not.exist
+ );
+ });
+
+ it('renders the Database Users link with correct URL when projectId is available', async () => {
+ const atlasConnectionInfo: ConnectionInfo = {
+ id: 'test-atlas-connection',
+ connectionOptions: { connectionString: 'mongodb://localhost:27017' },
+ atlasMetadata: {
+ orgId: 'test-org',
+ projectId: 'test-project-123',
+ clusterName: 'test-cluster',
+ clusterUniqueId: 'test-cluster-unique-id',
+ clusterType: 'REPLICASET' as const,
+ clusterState: 'IDLE' as const,
+ metricsId: 'test-metrics-id',
+ metricsType: 'replicaSet' as const,
+ regionalBaseUrl: null,
+ instanceSize: 'M10',
+ supports: {
+ globalWrites: false,
+ rollingIndexes: true,
+ },
+ },
+ };
+
+ await renderModal({
+ currentStep: MockDataGeneratorStep.GENERATE_DATA,
+ connectionInfo: atlasConnectionInfo,
+ });
+
+ const databaseUsersLink = screen.getByRole('link', {
+ name: 'Access your Database Users',
+ });
+ expect(databaseUsersLink.getAttribute('href')).to.equal(
+ '/v2/test-project-123#/security/database/users'
+ );
+ });
+
+ it('does not render the Database Users link when projectId is not available', async () => {
+ const nonAtlasConnectionInfo: ConnectionInfo = {
+ id: 'test-local-connection',
+ connectionOptions: { connectionString: 'mongodb://localhost:27017' },
+ // No atlasMetadata means no projectId
+ };
+
+ await renderModal({
+ currentStep: MockDataGeneratorStep.GENERATE_DATA,
+ connectionInfo: nonAtlasConnectionInfo,
+ });
+
+ expect(screen.queryByRole('link', { name: 'Access your Database Users' }))
+ .to.not.exist;
+ });
+
+ // todo: assert that the generated script is displayed in the code block (CLOUDP-333860)
+ });
+
describe('when rendering the modal in a specific step', () => {
const steps = Object.keys(
StepButtonLabelMap
@@ -235,8 +329,8 @@ describe('MockDataGeneratorModal', () => {
// 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}"`, () => {
- renderModal({ currentStep });
+ it(`renders the button with the correct label when the user is in step "${currentStep}"`, async () => {
+ await renderModal({ currentStep });
expect(screen.getByTestId('next-step-button')).to.have.text(
StepButtonLabelMap[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 d09861a6b42..1f85dd8e998 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 from 'react';
+import React, { useMemo } from 'react';
import { connect } from 'react-redux';
import {
@@ -23,6 +23,7 @@ import {
} from '../../modules/collection-tab';
import { default as SchemaConfirmationScreen } from './raw-schema-confirmation';
import FakerSchemaEditor from './faker-schema-editor';
+import ScriptScreen from './script-screen';
const footerStyles = css`
flex-direction: row;
@@ -52,6 +53,21 @@ const MockDataGeneratorModal = ({
onConfirmSchema,
onPreviousStep,
}: Props) => {
+ const modalBodyContent = useMemo(() => {
+ switch (currentStep) {
+ case MockDataGeneratorStep.SCHEMA_CONFIRMATION:
+ return ;
+ case MockDataGeneratorStep.SCHEMA_EDITOR:
+ return ;
+ case MockDataGeneratorStep.DOCUMENT_COUNT:
+ return <>>; // TODO: CLOUDP-333856
+ case MockDataGeneratorStep.PREVIEW_DATA:
+ return <>>; // TODO: CLOUDP-333857
+ case MockDataGeneratorStep.GENERATE_DATA:
+ return ;
+ }
+ }, [currentStep]);
+
const handleNextClick = () => {
if (currentStep === MockDataGeneratorStep.GENERATE_DATA) {
onClose();
@@ -62,18 +78,9 @@ const MockDataGeneratorModal = ({
}
};
- let stepContent: React.ReactNode;
-
- if (currentStep === MockDataGeneratorStep.SCHEMA_CONFIRMATION) {
- stepContent = ;
- }
-
- if (currentStep === MockDataGeneratorStep.SCHEMA_EDITOR) {
- stepContent = ;
- }
-
return (
{
if (!open) {
@@ -84,8 +91,9 @@ const MockDataGeneratorModal = ({
>
- {stepContent}
-
+
+ {modalBodyContent}
+