From 17b979035a3ec736a6110c873bbb067536a73ebd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 8 Feb 2026 04:21:42 +0000 Subject: [PATCH 1/5] Initial plan From 9233e0621d87ab238d253c6d808501a4ee35ea99 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 8 Feb 2026 04:43:56 +0000 Subject: [PATCH 2/5] fix: resolve form loading, data duplication, and non-grid view height issues - ObjectForm: handle null schema from getObjectSchema by throwing error - ObjectForm: handle missing objectName/dataSource by clearing loading state - browser.ts: remove duplicate data seeding (AppPlugin already seeds during bootstrap) - ObjectView: fix layout to use flex column with proper height chain for non-grid views Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com> --- apps/console/src/mocks/browser.ts | 19 +++---------------- packages/plugin-form/src/ObjectForm.tsx | 7 +++++++ packages/plugin-view/src/ObjectView.tsx | 10 ++++++---- 3 files changed, 16 insertions(+), 20 deletions(-) diff --git a/apps/console/src/mocks/browser.ts b/apps/console/src/mocks/browser.ts index db8f5409..bb23c566 100644 --- a/apps/console/src/mocks/browser.ts +++ b/apps/console/src/mocks/browser.ts @@ -79,22 +79,9 @@ export async function startMockServer() { }); } - // Initialize default data from manifest if available - // Seed initial data from manifest.data[] into the in-memory driver - const manifest = (appConfig as any).manifest; - if (manifest && Array.isArray(manifest.data)) { - const totals: string[] = []; - for (const dataset of manifest.data) { - if (dataset.object && Array.isArray(dataset.records)) { - for (const record of dataset.records) { - await driver!.create(dataset.object, record); - } - totals.push(`${dataset.object}(${dataset.records.length})`); - } - } - if (import.meta.env.DEV) console.log(`[MSW] Seeded: ${totals.join(', ')}`); - } - + // Note: AppPlugin already loads manifest data during bootstrap via the Seeder, + // so we don't need to manually seed it again here. Doing so would create duplicates. + if (import.meta.env.DEV) console.log('[MSW] ObjectStack Runtime ready'); return kernel; } diff --git a/packages/plugin-form/src/ObjectForm.tsx b/packages/plugin-form/src/ObjectForm.tsx index 62a0b346..b55422b7 100644 --- a/packages/plugin-form/src/ObjectForm.tsx +++ b/packages/plugin-form/src/ObjectForm.tsx @@ -227,9 +227,13 @@ const SimpleObjectForm: React.FC = ({ throw new Error('DataSource is required when using ObjectQL schema fetching (inline fields not provided)'); } const schemaData = await dataSource.getObjectSchema(schema.objectName); + if (!schemaData) { + throw new Error(`No schema found for object "${schema.objectName}"`); + } setObjectSchema(schemaData); } catch (err) { setError(err as Error); + setLoading(false); } }; @@ -242,6 +246,9 @@ const SimpleObjectForm: React.FC = ({ }); } else if (schema.objectName && dataSource) { fetchObjectSchema(); + } else if (!hasInlineFields) { + // No objectName or dataSource and no inline fields — cannot proceed + setLoading(false); } }, [schema.objectName, dataSource, hasInlineFields]); diff --git a/packages/plugin-view/src/ObjectView.tsx b/packages/plugin-view/src/ObjectView.tsx index 7e2a32cc..0e7eb11b 100644 --- a/packages/plugin-view/src/ObjectView.tsx +++ b/packages/plugin-view/src/ObjectView.tsx @@ -923,10 +923,10 @@ export const ObjectView: React.FC = ({ : layout; return ( -
+
{/* Title and description */} {(schema.title || schema.description) && ( -
+
{schema.title && (

{schema.title}

)} @@ -937,12 +937,14 @@ export const ObjectView: React.FC = ({ )} {/* Toolbar */} -
+
{renderToolbar()}
{/* Content */} - {renderContent()} +
+ {renderContent()} +
{/* Form (drawer or modal) */} {formLayout === 'drawer' && renderDrawerForm()} From 80138f53c5c628b1aefaaf78e01e8e6f189648e1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 8 Feb 2026 04:46:36 +0000 Subject: [PATCH 3/5] test: add regression tests for form loading, data auto-load, and non-grid views Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com> --- .../src/__tests__/ConsoleIssues.test.tsx | 437 ++++++++++++++++++ 1 file changed, 437 insertions(+) create mode 100644 apps/console/src/__tests__/ConsoleIssues.test.tsx diff --git a/apps/console/src/__tests__/ConsoleIssues.test.tsx b/apps/console/src/__tests__/ConsoleIssues.test.tsx new file mode 100644 index 00000000..6273549c --- /dev/null +++ b/apps/console/src/__tests__/ConsoleIssues.test.tsx @@ -0,0 +1,437 @@ +/** + * Console Issues Regression Tests + * + * Tests for the three reported issues: + * 1. Form loading forever when clicking "New" on list page + * 2. Example initial data not auto-loading + * 3. Non-grid view types (kanban, calendar, etc.) not rendering + */ + +import { describe, it, expect, vi, beforeAll, afterAll } from 'vitest'; +import { render, screen, waitFor, within, fireEvent } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import { ObjectForm } from '@object-ui/plugin-form'; +import { ListView } from '@object-ui/plugin-list'; +import { SchemaRendererProvider } from '@object-ui/react'; +import type { DataSource } from '@object-ui/types'; + +// Import plugins for side-effects (registration) +import '@object-ui/plugin-grid'; +import '@object-ui/plugin-kanban'; +import '@object-ui/plugin-calendar'; +import '@object-ui/plugin-gantt'; +import '@object-ui/plugin-timeline'; +import '@object-ui/plugin-map'; +import '@object-ui/plugin-charts'; + +// ─── Mocks ─────────────────────────────────────────────────────────────────── + +/** + * Standard mock DataSource that returns valid schema and data + */ +function createMockDataSource(overrides?: Partial): DataSource { + return { + async getObjectSchema(_objectName: string) { + return { + name: 'contact', + label: 'Contact', + fields: { + name: { name: 'name', label: 'Name', type: 'text', required: true }, + email: { name: 'email', label: 'Email', type: 'email' }, + status: { + name: 'status', + label: 'Status', + type: 'select', + options: [ + { label: 'New', value: 'new' }, + { label: 'Active', value: 'active' }, + { label: 'Closed', value: 'closed' }, + ], + }, + due_date: { name: 'due_date', label: 'Due Date', type: 'date' }, + image: { name: 'image', label: 'Image', type: 'text' }, + location: { name: 'location', label: 'Location', type: 'text' }, + }, + }; + }, + async find(_objectName: string) { + return { + data: [ + { id: '1', name: 'Alice', email: 'alice@test.com', status: 'active', due_date: '2026-03-01' }, + { id: '2', name: 'Bob', email: 'bob@test.com', status: 'new', due_date: '2026-03-15' }, + { id: '3', name: 'Charlie', email: 'charlie@test.com', status: 'closed', due_date: '2026-02-20' }, + ], + }; + }, + async findOne(_objectName: string, _id: string | number) { + return { id: '1', name: 'Alice', email: 'alice@test.com', status: 'active' }; + }, + async create(_objectName: string, data: any) { + return { ...data, id: 'new-id' }; + }, + async update(_objectName: string, id: string | number, data: any) { + return { ...data, id }; + }, + async delete() { + return true; + }, + ...overrides, + } as DataSource; +} + +// ─── Issue 1: Form loading forever ────────────────────────────────────────── + +describe('Issue 1: Form should not stay in loading state forever', () => { + it('should render create form fields (not loading) when schema is available', async () => { + const dataSource = createMockDataSource(); + + render( + , + ); + + // Form should eventually show fields, not stay in loading + await waitFor( + () => { + expect(screen.getByLabelText(/Name/i)).toBeInTheDocument(); + }, + { timeout: 5000 }, + ); + + expect(screen.getByLabelText(/Email/i)).toBeInTheDocument(); + // Loading indicator should not be present + expect(screen.queryByText(/Loading form/i)).not.toBeInTheDocument(); + }); + + it('should show error (not loading) when getObjectSchema returns null', async () => { + const dataSource = createMockDataSource({ + async getObjectSchema() { + return null; + }, + }); + + render( + , + ); + + // Should show error, NOT stay in loading + await waitFor( + () => { + expect(screen.getByText(/Error loading form/i)).toBeInTheDocument(); + }, + { timeout: 5000 }, + ); + + // Loading spinner text should not be present + expect(screen.queryByText('Loading form...')).not.toBeInTheDocument(); + }); + + it('should show error (not loading) when getObjectSchema throws', async () => { + const dataSource = createMockDataSource({ + async getObjectSchema() { + throw new Error('Network error'); + }, + }); + + render( + , + ); + + // Should show error, NOT stay in loading + await waitFor( + () => { + expect(screen.getByText(/Error loading form/i)).toBeInTheDocument(); + }, + { timeout: 5000 }, + ); + }); + + it('should not stay in loading when dataSource is missing', async () => { + render( + , + ); + + // Should not show loading forever — either shows empty form or no-loading state + await waitFor( + () => { + expect(screen.queryByText(/Loading form/i)).not.toBeInTheDocument(); + }, + { timeout: 5000 }, + ); + }); + + it('should render edit form with fetched data', async () => { + const dataSource = createMockDataSource(); + + render( + , + ); + + // Should show form fields with data + await waitFor( + () => { + const nameInput = screen.getByLabelText(/Name/i) as HTMLInputElement; + expect(nameInput.value).toBe('Alice'); + }, + { timeout: 5000 }, + ); + }); +}); + +// ─── Issue 2: Initial data auto-loading ───────────────────────────────────── + +describe('Issue 2: Initial data should auto-load from dataSource', () => { + it('should auto-fetch and display data in ListView', async () => { + const dataSource = createMockDataSource(); + + render( + + + , + ); + + // Data should be automatically fetched and rendered + await waitFor( + () => { + expect(screen.getByText('Alice')).toBeInTheDocument(); + }, + { timeout: 5000 }, + ); + + expect(screen.getByText('Bob')).toBeInTheDocument(); + expect(screen.getByText('Charlie')).toBeInTheDocument(); + }); + + it('should call find() on mount to fetch initial data', async () => { + const findSpy = vi.fn().mockResolvedValue({ + data: [{ id: '1', name: 'Test', email: 'test@test.com', status: 'new' }], + }); + + const dataSource = createMockDataSource({ find: findSpy }); + + render( + + + , + ); + + // find() should be called automatically + await waitFor(() => { + expect(findSpy).toHaveBeenCalledWith('contact', expect.any(Object)); + }); + }); +}); + +// ─── Issue 3: Non-grid views should render ────────────────────────────────── + +describe('Issue 3: Non-grid view types should load and render', () => { + it('should render kanban view with data', async () => { + const dataSource = createMockDataSource(); + + render( + + + , + ); + + // Should show data items (kanban cards) + await waitFor( + () => { + expect(screen.getByText('Alice')).toBeInTheDocument(); + }, + { timeout: 5000 }, + ); + + expect(screen.getByText('Bob')).toBeInTheDocument(); + }); + + it('should render gallery view with data', async () => { + const dataSource = createMockDataSource(); + + render( + + + , + ); + + // Should show gallery items + await waitFor( + () => { + expect(screen.getByText('Alice')).toBeInTheDocument(); + }, + { timeout: 5000 }, + ); + }); + + it('should render timeline view with data', async () => { + const dataSource = createMockDataSource(); + + render( + + + , + ); + + // Should render timeline view content (not show error) + await waitFor( + () => { + // Timeline view should render — check no "Unknown component" error + expect(screen.queryByText(/Unknown component type/i)).not.toBeInTheDocument(); + }, + { timeout: 5000 }, + ); + }); + + it('should switch from grid to kanban view via viewType prop', async () => { + const dataSource = createMockDataSource(); + + const { rerender } = render( + + + , + ); + + // Initial grid view should load data + await waitFor( + () => { + expect(screen.getByText('Alice')).toBeInTheDocument(); + }, + { timeout: 5000 }, + ); + + // Rerender with kanban viewType + rerender( + + + , + ); + + // Kanban view should also show data + await waitFor( + () => { + expect(screen.getByText('Alice')).toBeInTheDocument(); + }, + { timeout: 5000 }, + ); + }); +}); From f23c7c5f8610bf92e209d90f760e989a93e6abb8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 8 Feb 2026 07:01:56 +0000 Subject: [PATCH 4/5] fix: remove unused imports in ConsoleIssues.test.tsx to fix CI build Remove unused imports (beforeAll, afterAll, within, fireEvent) that caused TS6133 errors during the console app build. Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com> --- apps/console/src/__tests__/ConsoleIssues.test.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/console/src/__tests__/ConsoleIssues.test.tsx b/apps/console/src/__tests__/ConsoleIssues.test.tsx index 6273549c..dce1a7a0 100644 --- a/apps/console/src/__tests__/ConsoleIssues.test.tsx +++ b/apps/console/src/__tests__/ConsoleIssues.test.tsx @@ -7,8 +7,8 @@ * 3. Non-grid view types (kanban, calendar, etc.) not rendering */ -import { describe, it, expect, vi, beforeAll, afterAll } from 'vitest'; -import { render, screen, waitFor, within, fireEvent } from '@testing-library/react'; +import { describe, it, expect, vi } from 'vitest'; +import { render, screen, waitFor } from '@testing-library/react'; import '@testing-library/jest-dom'; import { ObjectForm } from '@object-ui/plugin-form'; import { ListView } from '@object-ui/plugin-list'; From 6af4860c2de95fceacbf9a97a83d11b8c1d8972d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 8 Feb 2026 07:48:58 +0000 Subject: [PATCH 5/5] fix: add missing vitest aliases for @object-ui/types/zod and @object-ui/plugin-report MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add two missing module resolution aliases in vitest.config.mts: - @object-ui/types/zod → packages/types/src/zod/index.zod.ts - @object-ui/plugin-report → packages/plugin-report/src This fixes 3 test file failures: - packages/plugin-grid/src/ListColumnSchema.test.ts (could not resolve @object-ui/types/zod) - apps/console/src/__tests__/BrowserSimulation.test.tsx (could not resolve @object-ui/plugin-report) - apps/console/src/__tests__/ConsoleApp.test.tsx (could not resolve @object-ui/plugin-report) All 117 test files now pass (2472 tests). Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com> --- vitest.config.mts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vitest.config.mts b/vitest.config.mts index 39bbef59..cbfaf4b1 100644 --- a/vitest.config.mts +++ b/vitest.config.mts @@ -40,6 +40,7 @@ export default defineConfig({ resolve: { alias: { '@object-ui/core': path.resolve(__dirname, './packages/core/src'), + '@object-ui/types/zod': path.resolve(__dirname, './packages/types/src/zod/index.zod.ts'), '@object-ui/types': path.resolve(__dirname, './packages/types/src'), '@object-ui/react': path.resolve(__dirname, './packages/react/src'), '@object-ui/protocol': path.resolve(__dirname, './packages/core/src'), @@ -65,6 +66,7 @@ export default defineConfig({ '@object-ui/plugin-markdown': path.resolve(__dirname, './packages/plugin-markdown/src'), '@object-ui/plugin-timeline': path.resolve(__dirname, './packages/plugin-timeline/src'), '@object-ui/plugin-view': path.resolve(__dirname, './packages/plugin-view/src'), + '@object-ui/plugin-report': path.resolve(__dirname, './packages/plugin-report/src'), '@object-ui/runner': path.resolve(__dirname, './packages/runner/src'), '@': path.resolve(__dirname, './packages/components/src'), '@object-ui/ui': path.resolve(__dirname, './packages/ui/src'),