Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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 @@ -8,10 +8,10 @@ import { StepButtonLabelMap } from './constants';

describe('MockDataGeneratorModal', () => {
const sandbox = Sinon.createSandbox();
let setIsOpen: Sinon.SinonSpy;
let onOpenChange: Sinon.SinonSpy;

beforeEach(() => {
setIsOpen = sandbox.spy();
onOpenChange = sandbox.spy();
});

afterEach(() => {
Expand All @@ -23,15 +23,15 @@ describe('MockDataGeneratorModal', () => {
currentStep = MockDataGeneratorStep.AI_DISCLAIMER,
} = {}) {
function MockDataGeneratorModalWrapper() {
const [currentStepStateMock, setCurrentStepStateMock] =
const [currentStepStateMock, onCurrentStepChangeStateMock] =
React.useState<MockDataGeneratorStep>(currentStep);
return (
<MockDataGeneratorModal
isOpen={isOpen}
setIsOpen={setIsOpen}
onOpenChange={onOpenChange}
currentStep={currentStepStateMock}
setCurrentStep={(step) => {
setCurrentStepStateMock(step);
onCurrentStepChange={(step) => {
onCurrentStepChangeStateMock(step);
}}
/>
);
Expand All @@ -51,20 +51,20 @@ describe('MockDataGeneratorModal', () => {
expect(screen.queryByTestId('generate-mock-data-modal')).to.not.exist;
});

it('calls setIsOpen(false) when the modal is closed', () => {
it('calls onOpenChange(false) when the modal is closed', () => {
renderModal();

screen.getByLabelText('Close modal').click();

expect(setIsOpen.calledOnceWith(false)).to.be.true;
expect(onOpenChange.calledOnceWith(false)).to.be.true;
});

it('calls setIsOpen(false) when the cancel button is clicked', () => {
it('calls onOpenChange(false) when the cancel button is clicked', () => {
renderModal();

screen.getByText('Cancel').click();

expect(setIsOpen.calledOnceWith(false)).to.be.true;
expect(onOpenChange.calledOnceWith(false)).to.be.true;
});

it('disables the Back button on the first step', () => {
Expand All @@ -76,9 +76,9 @@ describe('MockDataGeneratorModal', () => {
});

describe('when rendering the modal in a specific step', () => {
const steps = Object.values(MockDataGeneratorStep).filter(
(step) => typeof step === 'number'
) as MockDataGeneratorStep[];
const steps = Object.keys(
StepButtonLabelMap
) as unknown as MockDataGeneratorStep[];

steps.forEach((currentStep) => {
it(`renders the button with the correct label when the user is in step "${currentStep}"`, () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import React from 'react';

import { css, ModalBody, ModalHeader } from '@mongodb-js/compass-components';
import {
css,
ModalBody,
ModalHeader,
spacing,
} from '@mongodb-js/compass-components';

import {
Button,
Expand All @@ -18,51 +23,51 @@ const footerStyles = css`

const rightButtonsStyles = css`
display: flex;
gap: 8px;
gap: ${spacing[200]}px;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Q: So this corresponds to the 8px from before?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

flex-direction: row;
`;

interface Props {
isOpen: boolean;
setIsOpen: (isOpen: boolean) => void;
onOpenChange: (isOpen: boolean) => void;
currentStep: MockDataGeneratorStep;
setCurrentStep: (step: MockDataGeneratorStep) => void;
onCurrentStepChange: (step: MockDataGeneratorStep) => void;
}

const MockDataGeneratorModal = ({
isOpen,
setIsOpen,
onOpenChange,
currentStep,
setCurrentStep,
onCurrentStepChange,
}: Props) => {
const resetState = () => {
setCurrentStep(MockDataGeneratorStep.AI_DISCLAIMER);
onCurrentStepChange(MockDataGeneratorStep.AI_DISCLAIMER);
};

const onNext = () => {
if (currentStep < MockDataGeneratorStep.GENERATE_DATA) {
setCurrentStep(currentStep + 1);
onCurrentStepChange(currentStep + 1);
Copy link
Collaborator

Choose a reason for hiding this comment

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

This code is brittle as it allows to pass an arbitrary number value here, the code below is completely valid from TypeScript perspective:

      onCurrentStepChange(currentStep + 42);

If you are opting into using enums (we generally avoid them in the codebase, using string unions instead, even string enum would be a better choice) to introduce rigid typings for allowed steps, I would suggest that the step transitions are clearly defined through switch case statements to disallow such behavior (you can extract this logic into its own method that can be shared later with other parts of the code or easily moved around). I would also strongly recommend to switch to either string enums or string unions to make the transitions clearer and avoid the pitfals of using numbered enums

} else {
setIsOpen(false);
onOpenChange(false);
resetState();
}
};

const onBack = () => {
if (currentStep > MockDataGeneratorStep.AI_DISCLAIMER) {
setCurrentStep(currentStep - 1);
onCurrentStepChange(currentStep - 1);
}
};

const onCancel = () => {
setIsOpen(false);
onOpenChange(false);
resetState();
};

return (
<Modal
open={isOpen}
setOpen={(open) => setIsOpen(open)}
setOpen={(open) => onOpenChange(open)}
data-testid="generate-mock-data-modal"
>
<ModalHeader title="Generate Mock Data" />
Expand Down
Loading