-
Notifications
You must be signed in to change notification settings - Fork 239
feat(compass-collection): manage faker mapping request state in redux CLOUDP-333850 #7251
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
Merged
kpamaran
merged 46 commits into
main
from
compass-collection-redux-integrates-mock-data-schema
Sep 4, 2025
Merged
Changes from all commits
Commits
Show all changes
46 commits
Select commit
Hold shift + click to select a range
910c1af
feat(generative-ai): add method to get mock data schema CLOUDP-333849
kpamaran 5034086
Merge branch 'main' into atlas-ai-service-get-mock-data-schema
kpamaran 8a2a096
nit
kpamaran b7c5461
add exports
kpamaran cc950ca
Merge branch 'main' into atlas-ai-service-get-mock-data-schema
kpamaran 0f56ee3
address linter err
kpamaran 99c2881
draft redux integration for requesting fakerjs mappings
kpamaran f7d352d
test thunk behavior
kpamaran 77af388
pass collection name, db name, and validation rules
kpamaran 6e1bb35
nit
kpamaran 051f90e
supply err attrs
kpamaran 1e05219
Merge branch 'main' into atlas-ai-service-get-mock-data-schema
kpamaran e5fc781
Revert compass-collection diffs
kpamaran a3d3f98
clean up diffs for review
kpamaran aebff92
Merge branch 'main' into atlas-ai-service-get-mock-data-schema
kpamaran 0de12bc
type state machine of mock data generator request
kpamaran 203ee82
manage request state in redux store
kpamaran 47c1f3b
unit test thunk
kpamaran 549d838
rename request status enum
kpamaran b65be4b
update lock file
kpamaran 95d94b8
remove pnpm-lock.yaml
kpamaran b91a1b6
Merge branch 'atlas-ai-service-get-mock-data-schema' into compass-col…
kpamaran 5d39dee
unit test reducer transitions for fakerSchemaGeneration state
kpamaran ca06674
Merge branch 'main' into compass-collection-redux-integrates-mock-dat…
kpamaran d630729
simplify typing
kpamaran e88a7f7
nit
kpamaran a444035
handle modal closed action on fakerSchemaGeneration state
kpamaran 8f9aaf9
Pass requestId for tracking
kpamaran 44fa4c0
respect user config for using sample docs on gen ai
kpamaran 3e43c3f
fix
kpamaran 4a5dc46
adapt unit test
kpamaran 5d0f7c5
preserve stack trace on err
kpamaran e539d11
rename status val
kpamaran d840ffc
add @mongodb-js/compass-generative-ai as a dep to compass-collection
kpamaran 6d022af
Merge branch 'main' into compass-collection-redux-integrates-mock-dat…
kpamaran 01372f5
remove thunk isolation test
kpamaran 2e1ca5c
address feedback
kpamaran 7206585
Merge branch 'main' into compass-collection-redux-integrates-mock-dat…
kpamaran d2182b0
Merge branch 'main' into compass-collection-redux-integrates-mock-dat…
kpamaran 21ef538
resolve
kpamaran 5716285
control modal steps with redux
kpamaran 7a56c6e
revert nit
kpamaran b17d1f2
Merge branch 'main' into compass-collection-redux-integrates-mock-dat…
kpamaran ef2652f
adapt atlas-ai-service tests
kpamaran c0a306e
add compass-utils as dep to compass-collection
kpamaran 025d2fe
nit
kpamaran File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
12 changes: 12 additions & 0 deletions
12
packages/compass-collection/src/components/mock-data-generator-modal/faker-schema-editor.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| import React from 'react'; | ||
|
|
||
| // TODO: More to come from CLOUDP-333853, CLOUDP-333854 | ||
| const FakerSchemaEditor = () => { | ||
| return ( | ||
| <div data-testid="faker-schema-editor"> | ||
| Schema Editor Content Placeholder | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| export default FakerSchemaEditor; |
249 changes: 203 additions & 46 deletions
249
...ss-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.spec.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,82 +1,239 @@ | ||
| import { expect } from 'chai'; | ||
| import React from 'react'; | ||
| import { render, screen } from '@mongodb-js/testing-library-compass'; | ||
| import Sinon from 'sinon'; | ||
| import { UnconnectedMockDataGeneratorModal as MockDataGeneratorModal } from './mock-data-generator-modal'; | ||
| import { | ||
| screen, | ||
| render, | ||
| waitFor, | ||
| userEvent, | ||
| } from '@mongodb-js/testing-library-compass'; | ||
| import { Provider } from 'react-redux'; | ||
| import { createStore, applyMiddleware } from 'redux'; | ||
| import thunk from 'redux-thunk'; | ||
| import MockDataGeneratorModal from './mock-data-generator-modal'; | ||
| import { MockDataGeneratorStep } from './types'; | ||
| import { StepButtonLabelMap } from './constants'; | ||
| import type { CollectionState } from '../../modules/collection-tab'; | ||
| import { default as collectionTabReducer } from '../../modules/collection-tab'; | ||
|
|
||
| describe('MockDataGeneratorModal', () => { | ||
| const sandbox = Sinon.createSandbox(); | ||
| let onClose: Sinon.SinonSpy; | ||
|
|
||
| beforeEach(() => { | ||
| onClose = sandbox.spy(); | ||
| }); | ||
|
|
||
| afterEach(() => { | ||
| sandbox.restore(); | ||
| }); | ||
|
|
||
| const onNextStep = Sinon.stub(); | ||
| const onPreviousStep = Sinon.stub(); | ||
|
|
||
| function renderModal({ | ||
| isOpen = true, | ||
| currentStep = MockDataGeneratorStep.SCHEMA_CONFIRMATION, | ||
| mockServices = createMockServices(), | ||
| } = {}) { | ||
| const initialState: CollectionState = { | ||
| workspaceTabId: 'test-workspace-tab-id', | ||
| namespace: 'test.collection', | ||
| metadata: null, | ||
| schemaAnalysis: { | ||
| status: 'complete', | ||
| processedSchema: { | ||
| name: { | ||
| type: 'String', | ||
| probability: 1.0, | ||
| sample_values: ['John', 'Jane'], | ||
| }, | ||
| }, | ||
| sampleDocument: { name: 'John' }, | ||
| schemaMetadata: { maxNestingDepth: 1, validationRules: null }, | ||
| }, | ||
| fakerSchemaGeneration: { | ||
| status: 'idle', | ||
| }, | ||
| mockDataGenerator: { | ||
| isModalOpen: isOpen, | ||
| currentStep: currentStep, | ||
| }, | ||
| }; | ||
|
|
||
| const store = createStore( | ||
| collectionTabReducer, | ||
| initialState, | ||
| applyMiddleware(thunk.withExtraArgument(mockServices)) | ||
| ); | ||
|
|
||
| return render( | ||
| <MockDataGeneratorModal | ||
| isOpen={isOpen} | ||
| onClose={onClose} | ||
| currentStep={currentStep} | ||
| onNextStep={onNextStep} | ||
| onPreviousStep={onPreviousStep} | ||
| /> | ||
| <Provider store={store}> | ||
| <MockDataGeneratorModal /> | ||
| </Provider> | ||
| ); | ||
| } | ||
|
|
||
| it('renders the modal when isOpen is true', () => { | ||
| renderModal(); | ||
| function createMockServices() { | ||
| return { | ||
| dataService: {}, | ||
| atlasAiService: { | ||
| getMockDataSchema: () => { | ||
| return Promise.resolve({ | ||
| contents: { | ||
| fields: [], | ||
| }, | ||
| }); | ||
| }, | ||
| }, | ||
| workspaces: {}, | ||
| localAppRegistry: {}, | ||
| experimentationServices: {}, | ||
| connectionInfoRef: { current: {} }, | ||
| logger: { | ||
| log: { | ||
| warn: () => {}, | ||
| error: () => {}, | ||
| }, | ||
| debug: () => {}, | ||
| mongoLogId: () => 'mock-id', | ||
| }, | ||
| preferences: { getPreferences: () => ({}) }, | ||
| fakerSchemaGenerationAbortControllerRef: { current: undefined }, | ||
| }; | ||
| } | ||
|
|
||
| expect(screen.getByTestId('generate-mock-data-modal')).to.exist; | ||
| }); | ||
| describe('generally', () => { | ||
| it('renders the modal when isOpen is true', () => { | ||
| renderModal(); | ||
|
|
||
| it('does not render the modal when isOpen is false', () => { | ||
| renderModal({ isOpen: false }); | ||
| expect(screen.getByTestId('generate-mock-data-modal')).to.exist; | ||
| }); | ||
|
|
||
| expect(screen.queryByTestId('generate-mock-data-modal')).to.not.exist; | ||
| }); | ||
| it('does not render the modal when isOpen is false', () => { | ||
| renderModal({ isOpen: false }); | ||
|
|
||
| it('calls onClose when the modal is closed', () => { | ||
| renderModal(); | ||
| expect(screen.queryByTestId('generate-mock-data-modal')).to.not.exist; | ||
| }); | ||
|
|
||
| screen.getByLabelText('Close modal').click(); | ||
| it('closes the modal when the close button is clicked', async () => { | ||
| renderModal(); | ||
|
|
||
| expect(onClose.calledOnce).to.be.true; | ||
| }); | ||
| expect(screen.getByTestId('generate-mock-data-modal')).to.exist; | ||
| userEvent.click(screen.getByLabelText('Close modal')); | ||
| await waitFor( | ||
| () => | ||
| expect(screen.queryByTestId('generate-mock-data-modal')).to.not.exist | ||
| ); | ||
| }); | ||
|
|
||
| it('calls onClose when the cancel button is clicked', () => { | ||
| renderModal(); | ||
| it('closes the modal when the cancel button is clicked', async () => { | ||
| renderModal(); | ||
|
|
||
| screen.getByText('Cancel').click(); | ||
| expect(screen.getByTestId('generate-mock-data-modal')).to.exist; | ||
| userEvent.click(screen.getByText('Cancel')); | ||
| await waitFor( | ||
| () => | ||
| expect(screen.queryByTestId('generate-mock-data-modal')).to.not.exist | ||
| ); | ||
| }); | ||
|
|
||
| function createMockServicesWithSlowAiRequest() { | ||
| let abortSignalReceived = false; | ||
| let rejectPromise: (reason?: any) => void; | ||
| const rejectedPromise = new Promise((_resolve, reject) => { | ||
| rejectPromise = reject; | ||
| }); | ||
|
|
||
| const baseMockServices = createMockServices(); | ||
|
|
||
| const mockAiService = { | ||
| ...baseMockServices.atlasAiService, | ||
| getMockDataSchema: (request: any) => { | ||
| if (request?.signal) { | ||
| request.signal.addEventListener('abort', () => { | ||
| abortSignalReceived = true; | ||
| rejectPromise(new Error('Request aborted')); | ||
| }); | ||
| } | ||
| return rejectedPromise; | ||
| }, | ||
| getAbortSignalReceived: () => abortSignalReceived, | ||
| }; | ||
|
|
||
| return { | ||
| ...baseMockServices, | ||
| atlasAiService: mockAiService, | ||
| }; | ||
| } | ||
|
|
||
| it('cancels in-flight faker mapping requests when the cancel button is clicked', async () => { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice coverage! |
||
| const mockServices = createMockServicesWithSlowAiRequest(); | ||
| renderModal({ mockServices: mockServices as any }); | ||
|
|
||
| expect(screen.getByTestId('raw-schema-confirmation')).to.exist; | ||
| userEvent.click(screen.getByText('Confirm')); | ||
|
|
||
| await waitFor(() => { | ||
| expect(screen.getByTestId('faker-schema-editor')).to.exist; | ||
| }); | ||
|
|
||
| expect(onClose.calledOnce).to.be.true; | ||
| userEvent.click(screen.getByText('Cancel')); | ||
|
|
||
| expect(mockServices.atlasAiService.getAbortSignalReceived()).to.be.true; | ||
| }); | ||
|
|
||
| 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 }); | ||
|
|
||
| expect(screen.getByTestId('raw-schema-confirmation')).to.exist; | ||
| userEvent.click(screen.getByText('Confirm')); | ||
|
|
||
| await waitFor(() => { | ||
| expect(screen.getByTestId('faker-schema-editor')).to.exist; | ||
| }); | ||
|
|
||
| userEvent.click(screen.getByText('Back')); | ||
|
|
||
| expect(mockServices.atlasAiService.getAbortSignalReceived()).to.be.true; | ||
| }); | ||
| }); | ||
|
|
||
| it('disables the Back button on the first step', () => { | ||
| renderModal(); | ||
| describe('on the schema confirmation step', () => { | ||
| it('disables the Back button', () => { | ||
| renderModal(); | ||
|
|
||
| expect( | ||
| screen | ||
| .getByRole('button', { name: 'Back' }) | ||
| .getAttribute('aria-disabled') | ||
| ).to.equal('true'); | ||
| }); | ||
|
|
||
| it('renders the faker schema editor when the confirm button is clicked', async () => { | ||
| renderModal(); | ||
|
|
||
| expect(screen.getByTestId('raw-schema-confirmation')).to.exist; | ||
| expect(screen.queryByTestId('faker-schema-editor')).to.not.exist; | ||
| userEvent.click(screen.getByText('Confirm')); | ||
| await waitFor(() => { | ||
| expect(screen.queryByTestId('raw-schema-confirmation')).to.not.exist; | ||
| expect(screen.getByTestId('faker-schema-editor')).to.exist; | ||
| }); | ||
| }); | ||
|
|
||
| it('stays on the current step when an error is encountered during faker schema generation', async () => { | ||
| const mockServices = createMockServices(); | ||
| mockServices.atlasAiService.getMockDataSchema = () => | ||
| Promise.reject('faker schema generation failed'); | ||
| renderModal({ mockServices }); | ||
|
|
||
| expect(screen.getByTestId('raw-schema-confirmation')).to.exist; | ||
| expect(screen.queryByTestId('faker-schema-editor')).to.not.exist; | ||
| userEvent.click(screen.getByText('Confirm')); | ||
| await waitFor(() => { | ||
| expect(screen.getByTestId('raw-schema-confirmation')).to.exist; | ||
| expect(screen.queryByTestId('faker-schema-editor')).to.not.exist; | ||
| }); | ||
|
|
||
| // todo: assert a user-friendly error is displayed (CLOUDP-333852) | ||
| }); | ||
|
|
||
| expect( | ||
| screen.getByRole('button', { name: 'Back' }).getAttribute('aria-disabled') | ||
| ).to.equal('true'); | ||
| // todo: assert that closing then re-opening the modal after an LLM err removes the err message | ||
| }); | ||
|
|
||
| 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 }); | ||
|
|
||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just FYI, we call out testing-library-compass in development guide for a reason. A lot of manual setup you do here and in createMockServices we already do via testing helpers exposed from testing-library-compass (specifically https://github.com/mongodb-js/compass/tree/main/configs/testing-library-compass#createplugintesthelpers for testing parts of the plugin)
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I attempted
createPluginTestHelpersbut had some trouble, I'll try again in my upcoming PR and look at the existing usage more closely