Skip to content

Commit 59e3b43

Browse files
authored
feat(compass-collection): Fill in the script screen of the Generate Mock Data modal flow CLOUDP-333859 (#7296)
* Add leafygreen copyable to compass components * Create MVP of script screen * Fix whitespace and apostrphe issue * better name * Update comment with ticket * Add tests * Add CLOUDP tickets * Add todo to test * Add connection to current project database users * Better placeholders * Make all h2 * Update per review
1 parent 0d862f2 commit 59e3b43

File tree

6 files changed

+337
-31
lines changed

6 files changed

+337
-31
lines changed

package-lock.json

Lines changed: 42 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.spec.tsx

Lines changed: 112 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { expect } from 'chai';
22
import React from 'react';
33
import {
44
screen,
5-
render,
5+
renderWithActiveConnection,
66
waitFor,
77
userEvent,
88
} from '@mongodb-js/testing-library-compass';
@@ -14,12 +14,19 @@ import { MockDataGeneratorStep } from './types';
1414
import { StepButtonLabelMap } from './constants';
1515
import type { CollectionState } from '../../modules/collection-tab';
1616
import { default as collectionTabReducer } from '../../modules/collection-tab';
17+
import type { ConnectionInfo } from '@mongodb-js/connection-info';
1718

1819
describe('MockDataGeneratorModal', () => {
19-
function renderModal({
20+
async function renderModal({
2021
isOpen = true,
2122
currentStep = MockDataGeneratorStep.SCHEMA_CONFIRMATION,
2223
mockServices = createMockServices(),
24+
connectionInfo,
25+
}: {
26+
isOpen?: boolean;
27+
currentStep?: MockDataGeneratorStep;
28+
mockServices?: any;
29+
connectionInfo?: ConnectionInfo;
2330
} = {}) {
2431
const initialState: CollectionState = {
2532
workspaceTabId: 'test-workspace-tab-id',
@@ -52,10 +59,11 @@ describe('MockDataGeneratorModal', () => {
5259
applyMiddleware(thunk.withExtraArgument(mockServices))
5360
);
5461

55-
return render(
62+
return await renderWithActiveConnection(
5663
<Provider store={store}>
5764
<MockDataGeneratorModal />
58-
</Provider>
65+
</Provider>,
66+
connectionInfo
5967
);
6068
}
6169

@@ -89,20 +97,20 @@ describe('MockDataGeneratorModal', () => {
8997
}
9098

9199
describe('generally', () => {
92-
it('renders the modal when isOpen is true', () => {
93-
renderModal();
100+
it('renders the modal when isOpen is true', async () => {
101+
await renderModal();
94102

95103
expect(screen.getByTestId('generate-mock-data-modal')).to.exist;
96104
});
97105

98-
it('does not render the modal when isOpen is false', () => {
99-
renderModal({ isOpen: false });
106+
it('does not render the modal when isOpen is false', async () => {
107+
await renderModal({ isOpen: false });
100108

101109
expect(screen.queryByTestId('generate-mock-data-modal')).to.not.exist;
102110
});
103111

104112
it('closes the modal when the close button is clicked', async () => {
105-
renderModal();
113+
await renderModal();
106114

107115
expect(screen.getByTestId('generate-mock-data-modal')).to.exist;
108116
userEvent.click(screen.getByLabelText('Close modal'));
@@ -113,7 +121,7 @@ describe('MockDataGeneratorModal', () => {
113121
});
114122

115123
it('closes the modal when the cancel button is clicked', async () => {
116-
renderModal();
124+
await renderModal();
117125

118126
expect(screen.getByTestId('generate-mock-data-modal')).to.exist;
119127
userEvent.click(screen.getByText('Cancel'));
@@ -154,7 +162,7 @@ describe('MockDataGeneratorModal', () => {
154162

155163
it('cancels in-flight faker mapping requests when the cancel button is clicked', async () => {
156164
const mockServices = createMockServicesWithSlowAiRequest();
157-
renderModal({ mockServices: mockServices as any });
165+
await renderModal({ mockServices: mockServices as any });
158166

159167
expect(screen.getByTestId('raw-schema-confirmation')).to.exist;
160168
userEvent.click(screen.getByText('Confirm'));
@@ -170,7 +178,7 @@ describe('MockDataGeneratorModal', () => {
170178

171179
it('cancels in-flight faker mapping requests when the back button is clicked after schema confirmation', async () => {
172180
const mockServices = createMockServicesWithSlowAiRequest();
173-
renderModal({ mockServices: mockServices as any });
181+
await renderModal({ mockServices: mockServices as any });
174182

175183
expect(screen.getByTestId('raw-schema-confirmation')).to.exist;
176184
userEvent.click(screen.getByText('Confirm'));
@@ -186,8 +194,8 @@ describe('MockDataGeneratorModal', () => {
186194
});
187195

188196
describe('on the schema confirmation step', () => {
189-
it('disables the Back button', () => {
190-
renderModal();
197+
it('disables the Back button', async () => {
198+
await renderModal();
191199

192200
expect(
193201
screen
@@ -197,7 +205,7 @@ describe('MockDataGeneratorModal', () => {
197205
});
198206

199207
it('renders the faker schema editor when the confirm button is clicked', async () => {
200-
renderModal();
208+
await renderModal();
201209

202210
expect(screen.getByTestId('raw-schema-confirmation')).to.exist;
203211
expect(screen.queryByTestId('faker-schema-editor')).to.not.exist;
@@ -212,7 +220,7 @@ describe('MockDataGeneratorModal', () => {
212220
const mockServices = createMockServices();
213221
mockServices.atlasAiService.getMockDataSchema = () =>
214222
Promise.reject('faker schema generation failed');
215-
renderModal({ mockServices });
223+
await renderModal({ mockServices });
216224

217225
expect(screen.getByTestId('raw-schema-confirmation')).to.exist;
218226
expect(screen.queryByTestId('faker-schema-editor')).to.not.exist;
@@ -228,15 +236,101 @@ describe('MockDataGeneratorModal', () => {
228236
// todo: assert that closing then re-opening the modal after an LLM err removes the err message
229237
});
230238

239+
describe('on the generate data step', () => {
240+
it('enables the Back button', async () => {
241+
await renderModal({ currentStep: MockDataGeneratorStep.GENERATE_DATA });
242+
243+
expect(
244+
screen
245+
.getByRole('button', { name: 'Back' })
246+
.getAttribute('aria-disabled')
247+
).to.not.equal('true');
248+
});
249+
250+
it('renders the main sections: Prerequisites, steps, and Resources', async () => {
251+
await renderModal({ currentStep: MockDataGeneratorStep.GENERATE_DATA });
252+
253+
expect(screen.getByText('Prerequisites')).to.exist;
254+
expect(screen.getByText('1. Create a .js file with the following script'))
255+
.to.exist;
256+
expect(screen.getByText('2. Run the script with')).to.exist;
257+
expect(screen.getByText('Resources')).to.exist;
258+
});
259+
260+
it('closes the modal when the Done button is clicked', async () => {
261+
await renderModal({ currentStep: MockDataGeneratorStep.GENERATE_DATA });
262+
263+
expect(screen.getByTestId('generate-mock-data-modal')).to.exist;
264+
userEvent.click(screen.getByText('Done'));
265+
await waitFor(
266+
() =>
267+
expect(screen.queryByTestId('generate-mock-data-modal')).to.not.exist
268+
);
269+
});
270+
271+
it('renders the Database Users link with correct URL when projectId is available', async () => {
272+
const atlasConnectionInfo: ConnectionInfo = {
273+
id: 'test-atlas-connection',
274+
connectionOptions: { connectionString: 'mongodb://localhost:27017' },
275+
atlasMetadata: {
276+
orgId: 'test-org',
277+
projectId: 'test-project-123',
278+
clusterName: 'test-cluster',
279+
clusterUniqueId: 'test-cluster-unique-id',
280+
clusterType: 'REPLICASET' as const,
281+
clusterState: 'IDLE' as const,
282+
metricsId: 'test-metrics-id',
283+
metricsType: 'replicaSet' as const,
284+
regionalBaseUrl: null,
285+
instanceSize: 'M10',
286+
supports: {
287+
globalWrites: false,
288+
rollingIndexes: true,
289+
},
290+
},
291+
};
292+
293+
await renderModal({
294+
currentStep: MockDataGeneratorStep.GENERATE_DATA,
295+
connectionInfo: atlasConnectionInfo,
296+
});
297+
298+
const databaseUsersLink = screen.getByRole('link', {
299+
name: 'Access your Database Users',
300+
});
301+
expect(databaseUsersLink.getAttribute('href')).to.equal(
302+
'/v2/test-project-123#/security/database/users'
303+
);
304+
});
305+
306+
it('does not render the Database Users link when projectId is not available', async () => {
307+
const nonAtlasConnectionInfo: ConnectionInfo = {
308+
id: 'test-local-connection',
309+
connectionOptions: { connectionString: 'mongodb://localhost:27017' },
310+
// No atlasMetadata means no projectId
311+
};
312+
313+
await renderModal({
314+
currentStep: MockDataGeneratorStep.GENERATE_DATA,
315+
connectionInfo: nonAtlasConnectionInfo,
316+
});
317+
318+
expect(screen.queryByRole('link', { name: 'Access your Database Users' }))
319+
.to.not.exist;
320+
});
321+
322+
// todo: assert that the generated script is displayed in the code block (CLOUDP-333860)
323+
});
324+
231325
describe('when rendering the modal in a specific step', () => {
232326
const steps = Object.keys(
233327
StepButtonLabelMap
234328
) as unknown as MockDataGeneratorStep[];
235329

236330
// note: these tests can be removed after every modal step is implemented
237331
steps.forEach((currentStep) => {
238-
it(`renders the button with the correct label when the user is in step "${currentStep}"`, () => {
239-
renderModal({ currentStep });
332+
it(`renders the button with the correct label when the user is in step "${currentStep}"`, async () => {
333+
await renderModal({ currentStep });
240334
expect(screen.getByTestId('next-step-button')).to.have.text(
241335
StepButtonLabelMap[currentStep]
242336
);

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

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from 'react';
1+
import React, { useMemo } from 'react';
22
import { connect } from 'react-redux';
33

44
import {
@@ -23,6 +23,7 @@ import {
2323
} from '../../modules/collection-tab';
2424
import { default as SchemaConfirmationScreen } from './raw-schema-confirmation';
2525
import FakerSchemaEditor from './faker-schema-editor';
26+
import ScriptScreen from './script-screen';
2627

2728
const footerStyles = css`
2829
flex-direction: row;
@@ -52,6 +53,21 @@ const MockDataGeneratorModal = ({
5253
onConfirmSchema,
5354
onPreviousStep,
5455
}: Props) => {
56+
const modalBodyContent = useMemo(() => {
57+
switch (currentStep) {
58+
case MockDataGeneratorStep.SCHEMA_CONFIRMATION:
59+
return <SchemaConfirmationScreen />;
60+
case MockDataGeneratorStep.SCHEMA_EDITOR:
61+
return <FakerSchemaEditor />;
62+
case MockDataGeneratorStep.DOCUMENT_COUNT:
63+
return <></>; // TODO: CLOUDP-333856
64+
case MockDataGeneratorStep.PREVIEW_DATA:
65+
return <></>; // TODO: CLOUDP-333857
66+
case MockDataGeneratorStep.GENERATE_DATA:
67+
return <ScriptScreen />;
68+
}
69+
}, [currentStep]);
70+
5571
const handleNextClick = () => {
5672
if (currentStep === MockDataGeneratorStep.GENERATE_DATA) {
5773
onClose();
@@ -62,18 +78,9 @@ const MockDataGeneratorModal = ({
6278
}
6379
};
6480

65-
let stepContent: React.ReactNode;
66-
67-
if (currentStep === MockDataGeneratorStep.SCHEMA_CONFIRMATION) {
68-
stepContent = <SchemaConfirmationScreen />;
69-
}
70-
71-
if (currentStep === MockDataGeneratorStep.SCHEMA_EDITOR) {
72-
stepContent = <FakerSchemaEditor />;
73-
}
74-
7581
return (
7682
<Modal
83+
size="large"
7784
open={isOpen}
7885
setOpen={(open) => {
7986
if (!open) {
@@ -84,8 +91,9 @@ const MockDataGeneratorModal = ({
8491
>
8592
<ModalHeader title="Generate Mock Data" />
8693
<ModalBody>
87-
{stepContent}
88-
<div data-testid={`generate-mock-data-step-${currentStep}`} />
94+
<div data-testid={`generate-mock-data-step-${currentStep}`}>
95+
{modalBodyContent}
96+
</div>
8997
</ModalBody>
9098
<ModalFooter className={footerStyles}>
9199
<Button

0 commit comments

Comments
 (0)