Skip to content
42 changes: 42 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { expect } from 'chai';
import React from 'react';
import {
screen,
render,
renderWithActiveConnection,
waitFor,
userEvent,
} from '@mongodb-js/testing-library-compass';
Expand All @@ -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',
Expand Down Expand Up @@ -52,10 +59,11 @@ describe('MockDataGeneratorModal', () => {
applyMiddleware(thunk.withExtraArgument(mockServices))
);

return render(
return await renderWithActiveConnection(
<Provider store={store}>
<MockDataGeneratorModal />
</Provider>
</Provider>,
connectionInfo
);
}

Expand Down Expand Up @@ -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'));
Expand All @@ -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'));
Expand Down Expand Up @@ -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'));
Expand All @@ -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'));
Expand All @@ -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
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -228,15 +236,101 @@ 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
});

describe('when rendering the modal in a specific step', () => {
const steps = Object.keys(
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}"`, () => {
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]
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,15 @@ 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 STEP_TO_STEP_CONTENT: Record<MockDataGeneratorStep, React.JSX.Element> = {
[MockDataGeneratorStep.SCHEMA_CONFIRMATION]: <SchemaConfirmationScreen />,
[MockDataGeneratorStep.SCHEMA_EDITOR]: <FakerSchemaEditor />,
[MockDataGeneratorStep.DOCUMENT_COUNT]: <></>, // TODO: Implement as part of CLOUDP-333856
[MockDataGeneratorStep.PREVIEW_DATA]: <></>, // TODO: Implement as part of CLOUDP-333857
[MockDataGeneratorStep.GENERATE_DATA]: <ScriptScreen />,
};

const footerStyles = css`
flex-direction: row;
Expand Down Expand Up @@ -62,18 +71,9 @@ const MockDataGeneratorModal = ({
}
};

let stepContent: React.ReactNode;

if (currentStep === MockDataGeneratorStep.SCHEMA_CONFIRMATION) {
stepContent = <SchemaConfirmationScreen />;
}

if (currentStep === MockDataGeneratorStep.SCHEMA_EDITOR) {
stepContent = <FakerSchemaEditor />;
}

return (
<Modal
size="large"
open={isOpen}
setOpen={(open) => {
if (!open) {
Expand All @@ -84,8 +84,9 @@ const MockDataGeneratorModal = ({
>
<ModalHeader title="Generate Mock Data" />
<ModalBody>
{stepContent}
<div data-testid={`generate-mock-data-step-${currentStep}`} />
<div data-testid={`generate-mock-data-step-${currentStep}`}>
{STEP_TO_STEP_CONTENT[currentStep]}
Copy link
Member

Choose a reason for hiding this comment

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

A bit of a nit, instead of instantiating these components on load, can we instead write this with a switch? Either in a function or a variable. That way we create them when we're rendering.

Here's an example in the code base:

If we want to keep the Record approach, using functions would also work:

const STEP_TO_STEP_CONTENT: Record<MockDataGeneratorStep, React.JSX.Element> = {
  [MockDataGeneratorStep.SCHEMA_CONFIRMATION]: () => <SchemaConfirmationScreen />,
  [MockDataGeneratorStep.SCHEMA_EDITOR]: () => <FakerSchemaEditor />,
  [MockDataGeneratorStep.DOCUMENT_COUNT]: () => <></>, // TODO: Implement as part of CLOUDP-333856
  [MockDataGeneratorStep.PREVIEW_DATA]: () => <></>, // TODO: Implement as part of CLOUDP-333857
  [MockDataGeneratorStep.GENERATE_DATA]: () => <ScriptScreen />,
};

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Got it. Went with the useMemo approach used in the linked code.

</div>
</ModalBody>
<ModalFooter className={footerStyles}>
<Button
Expand Down
Loading
Loading