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 f20bb70d2f5..4e7a7b2e991 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
@@ -31,6 +31,7 @@ import RawSchemaConfirmationScreen from './raw-schema-confirmation-screen';
import FakerSchemaEditorScreen from './faker-schema-editor-screen';
import ScriptScreen from './script-screen';
import DocumentCountScreen from './document-count-screen';
+import PreviewScreen from './preview-screen';
const footerStyles = css`
flex-direction: row;
@@ -95,7 +96,15 @@ const MockDataGeneratorModal = ({
/>
);
case MockDataGeneratorStep.PREVIEW_DATA:
- return <>>; // TODO: CLOUDP-333857
+ return (
+
+ );
case MockDataGeneratorStep.GENERATE_DATA:
return ;
}
diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/preview-screen.tsx b/packages/compass-collection/src/components/mock-data-generator-modal/preview-screen.tsx
new file mode 100644
index 00000000000..33df025ed52
--- /dev/null
+++ b/packages/compass-collection/src/components/mock-data-generator-modal/preview-screen.tsx
@@ -0,0 +1,67 @@
+import React, { useMemo } from 'react';
+import {
+ css,
+ spacing,
+ Body,
+ DocumentList,
+} from '@mongodb-js/compass-components';
+import HadronDocument from 'hadron-document';
+import type { FakerSchema } from './types';
+import { generateDocument } from './script-generation-utils';
+
+const descriptionStyles = css({
+ marginBottom: spacing[200],
+});
+
+const documentContainerStyles = css({
+ display: 'flex',
+ flexDirection: 'column',
+ gap: spacing[300],
+});
+
+const documentWrapperStyles = css({
+ border: '1px solid #E8EDEB',
+ borderRadius: '6px',
+ padding: spacing[200],
+});
+
+interface PreviewScreenProps {
+ confirmedFakerSchema: FakerSchema;
+}
+
+const NUM_SAMPLE_DOCUMENTS = 3;
+
+function PreviewScreen({ confirmedFakerSchema }: PreviewScreenProps) {
+ const sampleDocuments = useMemo(() => {
+ const documents = [];
+ for (let i = 0; i < NUM_SAMPLE_DOCUMENTS; i++) {
+ const plainDoc = generateDocument(confirmedFakerSchema);
+ const hadronDoc = new HadronDocument(plainDoc);
+ hadronDoc.expand(); // Expand by default for better preview
+ documents.push(hadronDoc);
+ }
+
+ return documents;
+ }, [confirmedFakerSchema]);
+
+ return (
+
+
+ Preview Mock Data
+
+
+ Below is a sample of documents that will be generated based on your
+ script
+
+
+ {sampleDocuments.map((doc, index) => (
+
+
+
+ ))}
+
+
+ );
+}
+
+export default PreviewScreen;
diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/script-generation-utils.spec.ts b/packages/compass-collection/src/components/mock-data-generator-modal/script-generation-utils.spec.ts
index a18cefb4427..ef62a3cba18 100644
--- a/packages/compass-collection/src/components/mock-data-generator-modal/script-generation-utils.spec.ts
+++ b/packages/compass-collection/src/components/mock-data-generator-modal/script-generation-utils.spec.ts
@@ -1,6 +1,6 @@
import { expect } from 'chai';
import { faker } from '@faker-js/faker/locale/en';
-import { generateScript } from './script-generation-utils';
+import { generateScript, generateDocument } from './script-generation-utils';
import type { FakerFieldMapping } from './types';
/**
@@ -1421,4 +1421,151 @@ describe('Script Generation', () => {
}
});
});
+
+ describe('generateDocument', () => {
+ it('should generate document with simple flat fields', () => {
+ const schema = {
+ name: {
+ mongoType: 'String' as const,
+ fakerMethod: 'person.fullName',
+ fakerArgs: [],
+ probability: 1.0,
+ },
+ age: {
+ mongoType: 'Number' as const,
+ fakerMethod: 'number.int',
+ fakerArgs: [{ json: '{"min": 18, "max": 65}' }],
+ probability: 1.0,
+ },
+ };
+
+ const document = generateDocument(schema);
+
+ expect(document).to.be.an('object');
+ expect(document).to.have.property('name');
+ expect(document.name).to.be.a('string').and.not.be.empty;
+ expect(document).to.have.property('age');
+ expect(document.age).to.be.a('number');
+ expect(document.age).to.be.at.least(18).and.at.most(65);
+ });
+
+ it('should generate document with arrays', () => {
+ const schema = {
+ 'tags[]': {
+ mongoType: 'String' as const,
+ fakerMethod: 'lorem.word',
+ fakerArgs: [],
+ probability: 1.0,
+ },
+ };
+
+ const document = generateDocument(schema, { 'tags[]': 2 });
+
+ expect(document).to.be.an('object');
+ expect(document).to.have.property('tags');
+ expect(document.tags).to.be.an('array').with.length(2);
+ for (const tag of document.tags as string[]) {
+ expect(tag).to.be.a('string').and.not.be.empty;
+ }
+ });
+
+ it('should generate document with complex nested arrays and custom lengths', () => {
+ const schema = {
+ 'users[].posts[].tags[]': {
+ mongoType: 'String' as const,
+ fakerMethod: 'lorem.word',
+ fakerArgs: [],
+ probability: 1.0,
+ },
+ 'matrix[][]': {
+ mongoType: 'Number' as const,
+ fakerMethod: 'number.int',
+ fakerArgs: [{ json: '{"min": 1, "max": 10}' }],
+ probability: 1.0,
+ },
+ };
+
+ const arrayLengthMap = {
+ 'users[]': 2,
+ 'users[].posts[]': 3,
+ 'users[].posts[].tags[]': 4,
+ 'matrix[]': 2,
+ 'matrix[][]': 3,
+ };
+
+ const document = generateDocument(schema, arrayLengthMap);
+
+ expect(document).to.be.an('object');
+
+ // Check users array structure
+ expect(document).to.have.property('users');
+ expect(document.users).to.be.an('array').with.length(2);
+
+ // Check nested structure with proper types
+ const users = document.users as Array<{
+ posts: Array<{ tags: string[] }>;
+ }>;
+
+ for (const user of users) {
+ expect(user).to.be.an('object');
+ expect(user).to.have.property('posts');
+ expect(user.posts).to.be.an('array').with.length(3);
+
+ for (const post of user.posts) {
+ expect(post).to.be.an('object');
+ expect(post).to.have.property('tags');
+ expect(post.tags).to.be.an('array').with.length(4);
+
+ for (const tag of post.tags) {
+ expect(tag).to.be.a('string').and.not.be.empty;
+ }
+ }
+ }
+
+ // Check matrix (2D array)
+ expect(document).to.have.property('matrix');
+ expect(document.matrix).to.be.an('array').with.length(2);
+
+ const matrix = document.matrix as number[][];
+ for (const row of matrix) {
+ expect(row).to.be.an('array').with.length(3);
+ for (const cell of row) {
+ expect(cell).to.be.a('number').and.be.at.least(1).and.at.most(10);
+ }
+ }
+ });
+
+ it('should handle probability fields correctly', () => {
+ const schema = {
+ name: {
+ mongoType: 'String' as const,
+ fakerMethod: 'person.fullName',
+ fakerArgs: [],
+ probability: 1.0,
+ },
+ optionalField: {
+ mongoType: 'String' as const,
+ fakerMethod: 'lorem.word',
+ fakerArgs: [],
+ probability: 0.0, // Should never appear
+ },
+ alwaysPresent: {
+ mongoType: 'Number' as const,
+ fakerMethod: 'number.int',
+ fakerArgs: [],
+ probability: 1.0,
+ },
+ };
+
+ const document = generateDocument(schema);
+
+ expect(document).to.be.an('object');
+ expect(document).to.have.property('name');
+ expect(document.name).to.be.a('string').and.not.be.empty;
+ expect(document).to.have.property('alwaysPresent');
+ expect(document.alwaysPresent).to.be.a('number');
+ // optionalField should not be present due to 0.0 probability
+ expect(document).to.not.have.property('optionalField');
+ });
+ });
});
diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/script-generation-utils.ts b/packages/compass-collection/src/components/mock-data-generator-modal/script-generation-utils.ts
index 3138663fd55..7b1f5d74e35 100644
--- a/packages/compass-collection/src/components/mock-data-generator-modal/script-generation-utils.ts
+++ b/packages/compass-collection/src/components/mock-data-generator-modal/script-generation-utils.ts
@@ -1,6 +1,7 @@
import type { MongoDBFieldType } from '@mongodb-js/compass-generative-ai';
import type { FakerFieldMapping } from './types';
import { prettify } from '@mongodb-js/compass-editor';
+import { faker } from '@faker-js/faker/locale/en';
export type FakerArg =
| string
@@ -568,3 +569,170 @@ export function formatFakerArgs(fakerArgs: FakerArg[]): string {
return stringifiedArgs.join(', ');
}
+
+type Document = Record;
+
+/**
+ * Generates documents for the PreviewScreen component.
+ * Executes faker methods to create actual document objects.
+ */
+export function generateDocument(
+ fakerSchema: Record,
+ arrayLengthMap: ArrayLengthMap = {}
+): Document {
+ const structure = buildDocumentStructure(fakerSchema);
+ return constructDocumentValues(structure, arrayLengthMap);
+}
+
+function computeValue(
+ elementType: ArrayStructure | FakerFieldMapping | DocumentStructure,
+ arrayLengthMap: ArrayLengthMap,
+ currentPath: string
+) {
+ try {
+ if ('mongoType' in elementType) {
+ // It's a field mapping
+ const mapping = elementType as FakerFieldMapping;
+
+ // Default to 1.0 for invalid probability values
+ let probability = 1.0;
+ if (
+ typeof mapping.probability === 'number' &&
+ mapping.probability >= 0 &&
+ mapping.probability <= 1
+ ) {
+ probability = mapping.probability;
+ }
+
+ const shouldIncludeField =
+ probability >= 1.0 || Math.random() < probability;
+ if (shouldIncludeField) {
+ return generateFakerValue(mapping);
+ }
+ } else if ('type' in elementType && elementType.type === 'array') {
+ return constructArrayValues(
+ elementType as ArrayStructure,
+ arrayLengthMap,
+ `${currentPath}[]`
+ );
+ } else {
+ return constructDocumentValues(
+ elementType as DocumentStructure,
+ arrayLengthMap,
+ currentPath
+ );
+ }
+ } catch {
+ // Skip invalid faker methods
+ }
+}
+
+/**
+ * Construct actual document values from document structure.
+ * Mirrors renderDocumentCode but executes faker calls instead of generating code.
+ */
+function constructDocumentValues(
+ structure: DocumentStructure,
+ arrayLengthMap: ArrayLengthMap = {},
+ currentPath: string = ''
+) {
+ const result: Document = {};
+ for (const [fieldName, value] of Object.entries(structure)) {
+ const newPath = currentPath ? `${currentPath}.${fieldName}` : fieldName;
+ const val = computeValue(value, arrayLengthMap, newPath);
+ if (val !== undefined) {
+ result[fieldName] = val;
+ }
+ }
+ return result;
+}
+
+/**
+ * Construct array values from array structure.
+ * Mirrors renderArrayCode but executes faker calls instead of generating code.
+ */
+function constructArrayValues(
+ arrayStructure: ArrayStructure,
+ arrayLengthMap: ArrayLengthMap,
+ currentPath: string
+) {
+ const elementType = arrayStructure.elementType;
+
+ // Get array length for this dimension
+ let arrayLength = DEFAULT_ARRAY_LENGTH;
+ if (arrayLengthMap[currentPath] !== undefined) {
+ arrayLength = arrayLengthMap[currentPath];
+ }
+ const result: unknown[] = [];
+ for (let i = 0; i < arrayLength; i++) {
+ result.push(computeValue(elementType, arrayLengthMap, currentPath));
+ }
+ return result;
+}
+
+/**
+ * Prepare faker arguments for execution.
+ * Converts FakerArg[] to actual values that can be passed to faker methods.
+ */
+function prepareFakerArgs(fakerArgs: FakerArg[]): unknown[] {
+ const preparedArgs: unknown[] = [];
+
+ for (const arg of fakerArgs) {
+ if (
+ typeof arg === 'string' ||
+ typeof arg === 'number' ||
+ typeof arg === 'boolean'
+ ) {
+ preparedArgs.push(arg);
+ } else if (typeof arg === 'object' && arg !== null && 'json' in arg) {
+ // Parse JSON objects
+ try {
+ const jsonArg = arg as { json: string };
+ preparedArgs.push(JSON.parse(jsonArg.json));
+ } catch {
+ // Skip invalid JSON
+ continue;
+ }
+ }
+ }
+
+ return preparedArgs;
+}
+
+/**
+ * Execute faker method to generate actual values.
+ * Mirrors generateFakerCall but executes the call instead of generating code.
+ */
+function generateFakerValue(
+ mapping: FakerFieldMapping
+): string | number | boolean | Date | null | undefined {
+ const method =
+ mapping.fakerMethod === 'unrecognized'
+ ? getDefaultFakerMethod(mapping.mongoType)
+ : mapping.fakerMethod;
+
+ try {
+ // Navigate to the faker method
+ const methodParts = method.split('.');
+ let fakerMethod: unknown = faker;
+ for (const part of methodParts) {
+ fakerMethod = (fakerMethod as Record)[part];
+ if (!fakerMethod) {
+ throw new Error(`Faker method not found: ${method}`);
+ }
+ }
+
+ // Prepare arguments
+ const args = prepareFakerArgs(mapping.fakerArgs);
+
+ // Call the faker method
+ const result = (fakerMethod as (...args: unknown[]) => unknown).apply(
+ faker,
+ args
+ );
+
+ return result as string | number | boolean | Date | null | undefined;
+ } catch {
+ return undefined;
+ }
+}