Add MSW runtime environment and test suite for ObjectForm#308
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
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>
There was a problem hiding this comment.
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 viasetupServer+ 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
| 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; | ||
| } |
There was a problem hiding this comment.
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.
| 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 []; | ||
| } |
There was a problem hiding this comment.
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.
| 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 []; | ||
| } |
There was a problem hiding this comment.
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.
| server = setupServer(...handlers); | ||
| server.listen({ onUnhandledRequest: 'bypass' }); | ||
|
|
There was a problem hiding this comment.
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.
| // Wait a bit to ensure MSW is fully ready | ||
| await new Promise(resolve => setTimeout(resolve, 500)); |
There was a problem hiding this comment.
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).
| // 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. |
| rollupOptions: { | ||
| onwarn(warning, warn) { | ||
| if ( | ||
| warning.code === 'UNRESOLVED_IMPORT' && | ||
| warning.message.includes('@objectstack/driver-memory') | ||
| ) { | ||
| return; | ||
| } | ||
| warn(warning); | ||
| } |
There was a problem hiding this comment.
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.
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
src/mocks/browser.ts): MSWPlugin + ObjectKernel + InMemoryDriver for developmentsrc/mocks/server.ts): Manual HTTP handlers + ObjectKernel for Node.js testingExample Application (
examples/msw-object-form/)objectstack.config.tsTest Suite
Usage
Security
Fixed happy-dom VM Context Escape vulnerability (CVE: RCE < 20.0.0) by upgrading 15.11.7 → 20.3.9.
Original prompt
💡 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.