Skip to content

Add MSW runtime environment and test suite for ObjectForm#308

Merged
hotlong merged 5 commits intomainfrom
copilot/build-objectstack-msw-env
Jan 31, 2026
Merged

Add MSW runtime environment and test suite for ObjectForm#308
hotlong merged 5 commits intomainfrom
copilot/build-objectstack-msw-env

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Jan 31, 2026

Implements complete MSW integration for testing ObjectForm components without backend dependency, following the reference architecture from objectstack-ai/spec msw-react-crud example.

Implementation

MSW Dual-Mode Setup

  • Browser mode (src/mocks/browser.ts): MSWPlugin + ObjectKernel + InMemoryDriver for development
  • Server mode (src/mocks/server.ts): Manual HTTP handlers + ObjectKernel for Node.js testing
  • Supports both setupWorker (browser) and setupServer (tests) patterns

Example Application (examples/msw-object-form/)

  • Contact CRUD UI with ObjectForm component
  • ObjectStackDataSource adapter bridging ObjectForm ↔ ObjectStack Client
  • Schema-driven validation, field types, and defaults from objectstack.config.ts

Test Suite

  • MSW server integration tests: Full coverage of kernel initialization and driver CRUD (2/2 passing)
  • ObjectForm unit tests: Mock DataSource approach for form rendering and validation (2/7 passing)
  • HTTP-level integration test framework (scaffolded)

Usage

// Browser: MSW worker intercepts API calls
import { startMockServer } from './mocks/browser';
await startMockServer(); // Boots ObjectKernel in-browser

// Tests: MSW server with direct driver access
import { startMockServer, getDriver } from './mocks/server';
await startMockServer();
const driver = getDriver();
const contacts = await driver.find('contact', {});

Security

Fixed happy-dom VM Context Escape vulnerability (CVE: RCE < 20.0.0) by upgrading 15.11.7 → 20.3.9.

Original prompt

参考这个例子,构建真实的objectstack msw运行环境,并编写测试用例测试对象表单。https://github.com/objectstack-ai/spec/tree/main/examples/msw-react-crud


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

@vercel
Copy link
Copy Markdown

vercel Bot commented Jan 31, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
objectui Ready Ready Preview, Comment Jan 31, 2026 3:25pm
objectui-components Ready Ready Preview, Comment Jan 31, 2026 3:25pm
objectui-crm-app Error Error Jan 31, 2026 3:25pm

Request Review

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
…ulnerability

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Copilot AI changed the title [WIP] Set up ObjectStack MSW runtime environment and write test cases Add MSW runtime environment and test suite for ObjectForm Jan 31, 2026
Copilot AI requested a review from hotlong January 31, 2026 15:23
@hotlong hotlong marked this pull request as ready for review January 31, 2026 18:32
Copilot AI review requested due to automatic review settings January 31, 2026 18:32
@hotlong hotlong merged commit a2679a6 into main Jan 31, 2026
7 of 8 checks passed
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a new examples/msw-object-form example that runs ObjectStack on MSW (browser worker + Node test server) to enable ObjectForm development/testing without a backend, and updates dependencies/lockfile (incl. happy-dom security upgrade).

Changes:

  • Introduces a dual-mode MSW setup (browser worker via MSWPlugin, Node test server via setupServer + manual handlers).
  • Adds an ObjectForm-based Contact CRUD example app, plus a DataSource adapter to bridge ObjectForm ↔ ObjectStack client.
  • Adds Vitest + Testing Library test scaffolding for MSW runtime and ObjectForm behavior.

Reviewed changes

Copilot reviewed 22 out of 23 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
pnpm-lock.yaml Adds new example importer deps; upgrades happy-dom and related resolved packages.
examples/msw-object-form/package.json Defines the new example app’s deps, scripts, and tooling.
examples/msw-object-form/vite.config.ts Vite + Vitest configuration for the example (MSW/objectstack deps handling).
examples/msw-object-form/vitest.setup.ts Vitest setup (jest-dom).
examples/msw-object-form/tsconfig.json TS config for example app source + tests.
examples/msw-object-form/tsconfig.node.json TS config for node-side config files (vite/vitest/objectstack config).
examples/msw-object-form/tailwind.config.js Tailwind configuration for example UI styling.
examples/msw-object-form/postcss.config.js PostCSS config for Tailwind integration.
examples/msw-object-form/index.html Vite HTML entry for the example app.
examples/msw-object-form/README.md Documentation for the new example and its testing approach.
examples/msw-object-form/.gitignore Example-local ignores (build output, MSW worker file, etc.).
examples/msw-object-form/objectstack.config.ts ObjectStack schema/manifest for the Contact object and seed data.
examples/msw-object-form/src/main.tsx App bootstrap that starts MSW (browser) before rendering.
examples/msw-object-form/src/App.tsx Example Contact CRUD UI using ObjectForm + a list view.
examples/msw-object-form/src/App.css Basic styling for the example app shell.
examples/msw-object-form/src/types.ts Contact type definition used by the example UI.
examples/msw-object-form/src/dataSource.ts DataSource adapter implementation for ObjectForm ↔ ObjectStack client.
examples/msw-object-form/src/components/ContactList.tsx UI list component for existing contacts with edit/delete actions.
examples/msw-object-form/src/mocks/browser.ts Browser MSW runtime setup (ObjectKernel + InMemoryDriver + MSWPlugin).
examples/msw-object-form/src/mocks/server.ts Node MSW server setup for tests (manual HTTP handlers backed by ObjectKernel).
examples/msw-object-form/src/tests/MSWServer.test.tsx Minimal tests verifying Node MSW runtime + in-memory driver behavior.
examples/msw-object-form/src/tests/ObjectForm.test.tsx ObjectForm integration tests using the Node MSW runtime and ObjectStack client.
examples/msw-object-form/src/tests/ObjectFormUnit.test.tsx ObjectForm unit tests using a mock DataSource.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

Comment on lines +10 to +41
export class ObjectStackDataSource implements DataSource {
constructor(private client: ObjectStackClient) {}

async getObjectSchema(objectName: string): Promise<any> {
// Fetch object metadata from ObjectStack
const metadata = await this.client.metadata.getObject(objectName);
return metadata;
}

async findOne(objectName: string, id: string): Promise<any> {
const result = await this.client.data.findById(objectName, id);
return result.value;
}

async create(objectName: string, data: any): Promise<any> {
const result = await this.client.data.create(objectName, data);
return result.value;
}

async update(objectName: string, id: string, data: any): Promise<any> {
const result = await this.client.data.update(objectName, id, data);
return result.value;
}

async delete(objectName: string, id: string): Promise<void> {
await this.client.data.delete(objectName, id);
}

async find(objectName: string, options?: any): Promise<any[]> {
const result = await this.client.data.find(objectName, options);
return result.value;
}
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

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

ObjectStackDataSource does not correctly implement DataSource from @object-ui/types: find should return a QueryResult (e.g. { data, total }) not a raw array, and delete should return Promise<boolean> rather than Promise<void>. As written, this should fail TypeScript compilation under strict and can break consumers that rely on the interface contract.

Copilot uses AI. Check for mistakes.
Comment on lines +15 to +60
class MockDataSource implements DataSource {
private mockObject = {
name: 'contact',
fields: {
name: { name: 'name', label: 'Full Name', type: 'text', required: true },
email: { name: 'email', label: 'Email', type: 'email', required: true },
phone: { name: 'phone', label: 'Phone Number', type: 'phone' },
company: { name: 'company', label: 'Company', type: 'text' },
priority: { name: 'priority', label: 'Priority', type: 'number', min: 1, max: 10, defaultValue: 5 },
is_active: { name: 'is_active', label: 'Active', type: 'boolean', defaultValue: true },
notes: { name: 'notes', label: 'Notes', type: 'textarea' },
}
};

async getObjectSchema(objectName: string): Promise<any> {
return this.mockObject;
}

async findOne(objectName: string, id: string): Promise<any> {
return {
id,
name: 'John Doe',
email: 'john@example.com',
phone: '+1234567890',
company: 'Acme Corp',
priority: 8,
is_active: true,
notes: 'Test contact'
};
}

async create(objectName: string, data: any): Promise<any> {
return { ...data, id: 'new-id' };
}

async update(objectName: string, id: string, data: any): Promise<any> {
return { ...data, id };
}

async delete(objectName: string, id: string): Promise<void> {
// Mock delete
}

async find(objectName: string, options?: any): Promise<any[]> {
return [];
}
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

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

MockDataSource does not match the DataSource interface: find should return Promise<QueryResult> (not any[]) and delete should return Promise<boolean> (not void). This will cause type errors and can make the ObjectForm tests misleading because the mock doesn’t reflect the real adapter contract.

Copilot uses AI. Check for mistakes.
Comment on lines +29 to +60
async getObjectSchema(objectName: string): Promise<any> {
return this.mockObject;
}

async findOne(objectName: string, id: string): Promise<any> {
return {
id,
name: 'John Doe',
email: 'john@example.com',
phone: '+1234567890',
company: 'Acme Corp',
priority: 8,
is_active: true,
notes: 'Test contact'
};
}

async create(objectName: string, data: any): Promise<any> {
return { ...data, id: 'new-id' };
}

async update(objectName: string, id: string, data: any): Promise<any> {
return { ...data, id };
}

async delete(objectName: string, id: string): Promise<void> {
// Mock delete
}

async find(objectName: string, options?: any): Promise<any[]> {
return [];
}
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

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

With noUnusedParameters: true in this example’s tsconfig.json, the unused method parameters in MockDataSource (e.g., objectName, options) will trigger TypeScript errors. Consider prefixing intentionally-unused parameters with _ so they’re ignored by the repo’s eslint/ts rules.

Copilot uses AI. Check for mistakes.
Comment on lines +59 to +61
server = setupServer(...handlers);
server.listen({ onUnhandledRequest: 'bypass' });

Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

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

server.listen({ onUnhandledRequest: 'bypass' }) can mask missing/incorrect MSW handlers, letting tests pass while real requests aren’t mocked. For a test-only MSW server, consider using 'error' (or at least 'warn') so unexpected network calls fail loudly and keep the suite reliable.

Copilot uses AI. Check for mistakes.
Comment on lines +34 to +35
// Wait a bit to ensure MSW is fully ready
await new Promise(resolve => setTimeout(resolve, 500));
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

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

The fixed setTimeout(..., 500) delay adds unnecessary latency and can still be flaky (slow machines may need longer). Since src/main.tsx awaits startMockServer() before rendering, prefer removing the delay and relying on connect()/startMockServer() to indicate readiness (or add an explicit readiness signal).

Suggested change
// Wait a bit to ensure MSW is fully ready
await new Promise(resolve => setTimeout(resolve, 500));
// MSW mock server is started before React renders (see main.tsx),
// so we can connect directly without an artificial delay.

Copilot uses AI. Check for mistakes.
Comment on lines +28 to +37
rollupOptions: {
onwarn(warning, warn) {
if (
warning.code === 'UNRESOLVED_IMPORT' &&
warning.message.includes('@objectstack/driver-memory')
) {
return;
}
warn(warning);
}
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

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

The Rollup onwarn suppression for UNRESOLVED_IMPORT of @objectstack/driver-memory is currently undocumented here. Adding a brief comment (like in examples/msw-todo/vite.config.ts) explaining why it’s safe to ignore makes it easier to maintain and reduces the risk of hiding a real bundling issue.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants