From c8a57a5054ae2d6867b7be8c7090a94981b8e060 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 14 Jan 2026 19:16:18 +0000 Subject: [PATCH 1/3] Initial plan From 05a2803e4f30571b41a6980795d738260c8ea1b8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 14 Jan 2026 19:57:27 +0000 Subject: [PATCH 2/3] Add comprehensive test suites for all plugins Co-authored-by: huangyiirene <7665279+huangyiirene@users.noreply.github.com> --- packages/plugin-charts/package.json | 4 +- packages/plugin-charts/src/index.test.ts | 128 +++++++++++++++++++++ packages/plugin-editor/package.json | 4 +- packages/plugin-editor/src/index.test.ts | 90 +++++++++++++++ packages/plugin-kanban/package.json | 4 +- packages/plugin-kanban/src/index.test.ts | 104 +++++++++++++++++ packages/plugin-markdown/package.json | 4 +- packages/plugin-markdown/src/index.test.ts | 55 +++++++++ 8 files changed, 389 insertions(+), 4 deletions(-) create mode 100644 packages/plugin-charts/src/index.test.ts create mode 100644 packages/plugin-editor/src/index.test.ts create mode 100644 packages/plugin-kanban/src/index.test.ts create mode 100644 packages/plugin-markdown/src/index.test.ts diff --git a/packages/plugin-charts/package.json b/packages/plugin-charts/package.json index f472e0747..955cfc59c 100644 --- a/packages/plugin-charts/package.json +++ b/packages/plugin-charts/package.json @@ -14,7 +14,9 @@ } }, "scripts": { - "build": "vite build" + "build": "vite build", + "test": "vitest run", + "test:watch": "vitest" }, "dependencies": { "recharts": "^3.6.0", diff --git a/packages/plugin-charts/src/index.test.ts b/packages/plugin-charts/src/index.test.ts new file mode 100644 index 000000000..154927a01 --- /dev/null +++ b/packages/plugin-charts/src/index.test.ts @@ -0,0 +1,128 @@ +import { describe, it, expect, beforeAll } from 'vitest'; +import { ComponentRegistry } from '@object-ui/core'; + +describe('Plugin Charts', () => { + // Import all renderers to register them + beforeAll(async () => { + await import('./index'); + }); + + describe('chart-bar component', () => { + it('should be registered in ComponentRegistry', () => { + const chartBarRenderer = ComponentRegistry.get('chart-bar'); + expect(chartBarRenderer).toBeDefined(); + }); + + it('should have proper metadata', () => { + const config = ComponentRegistry.getConfig('chart-bar'); + expect(config).toBeDefined(); + expect(config?.label).toBe('Bar Chart'); + expect(config?.category).toBe('plugin'); + expect(config?.inputs).toBeDefined(); + expect(config?.defaultProps).toBeDefined(); + }); + + it('should have expected inputs', () => { + const config = ComponentRegistry.getConfig('chart-bar'); + const inputNames = config?.inputs?.map((input: any) => input.name) || []; + + expect(inputNames).toContain('data'); + expect(inputNames).toContain('dataKey'); + expect(inputNames).toContain('xAxisKey'); + expect(inputNames).toContain('height'); + expect(inputNames).toContain('color'); + }); + + it('should have data as required input', () => { + const config = ComponentRegistry.getConfig('chart-bar'); + const dataInput = config?.inputs?.find((input: any) => input.name === 'data'); + + expect(dataInput).toBeDefined(); + expect(dataInput?.required).toBe(true); + expect(dataInput?.type).toBe('array'); + }); + + it('should have sensible default props', () => { + const config = ComponentRegistry.getConfig('chart-bar'); + const defaults = config?.defaultProps; + + expect(defaults).toBeDefined(); + expect(defaults?.dataKey).toBe('value'); + expect(defaults?.xAxisKey).toBe('name'); + expect(defaults?.height).toBe(400); + expect(defaults?.color).toBe('#8884d8'); + expect(defaults?.data).toBeDefined(); + expect(Array.isArray(defaults?.data)).toBe(true); + expect(defaults?.data.length).toBeGreaterThan(0); + }); + }); + + describe('chart (advanced) component', () => { + it('should be registered in ComponentRegistry', () => { + const chartRenderer = ComponentRegistry.get('chart'); + expect(chartRenderer).toBeDefined(); + }); + + it('should have proper metadata', () => { + const config = ComponentRegistry.getConfig('chart'); + expect(config).toBeDefined(); + expect(config?.label).toBe('Chart'); + expect(config?.category).toBe('plugin'); + expect(config?.inputs).toBeDefined(); + expect(config?.defaultProps).toBeDefined(); + }); + + it('should have expected inputs', () => { + const config = ComponentRegistry.getConfig('chart'); + const inputNames = config?.inputs?.map((input: any) => input.name) || []; + + expect(inputNames).toContain('chartType'); + expect(inputNames).toContain('data'); + expect(inputNames).toContain('config'); + expect(inputNames).toContain('xAxisKey'); + expect(inputNames).toContain('series'); + expect(inputNames).toContain('className'); + }); + + it('should have chartType as enum input', () => { + const config = ComponentRegistry.getConfig('chart'); + const chartTypeInput = config?.inputs?.find((input: any) => input.name === 'chartType'); + + expect(chartTypeInput).toBeDefined(); + expect(chartTypeInput?.type).toBe('enum'); + expect(chartTypeInput?.enum).toBeDefined(); + expect(Array.isArray(chartTypeInput?.enum)).toBe(true); + + const enumValues = chartTypeInput?.enum?.map((e: any) => e.value) || []; + expect(enumValues).toContain('bar'); + expect(enumValues).toContain('line'); + expect(enumValues).toContain('area'); + }); + + it('should have data and series as required inputs', () => { + const config = ComponentRegistry.getConfig('chart'); + const dataInput = config?.inputs?.find((input: any) => input.name === 'data'); + const seriesInput = config?.inputs?.find((input: any) => input.name === 'series'); + + expect(dataInput?.required).toBe(true); + expect(seriesInput?.required).toBe(true); + }); + + it('should have sensible default props', () => { + const config = ComponentRegistry.getConfig('chart'); + const defaults = config?.defaultProps; + + expect(defaults).toBeDefined(); + expect(defaults?.chartType).toBe('bar'); + expect(defaults?.xAxisKey).toBe('name'); + expect(defaults?.data).toBeDefined(); + expect(Array.isArray(defaults?.data)).toBe(true); + expect(defaults?.data.length).toBeGreaterThan(0); + expect(defaults?.config).toBeDefined(); + expect(typeof defaults?.config).toBe('object'); + expect(defaults?.series).toBeDefined(); + expect(Array.isArray(defaults?.series)).toBe(true); + expect(defaults?.series.length).toBeGreaterThan(0); + }); + }); +}); diff --git a/packages/plugin-editor/package.json b/packages/plugin-editor/package.json index 787c65da1..c0529f394 100644 --- a/packages/plugin-editor/package.json +++ b/packages/plugin-editor/package.json @@ -14,7 +14,9 @@ } }, "scripts": { - "build": "vite build" + "build": "vite build", + "test": "vitest run", + "test:watch": "vitest" }, "dependencies": { "@monaco-editor/react": "^4.6.0", diff --git a/packages/plugin-editor/src/index.test.ts b/packages/plugin-editor/src/index.test.ts new file mode 100644 index 000000000..3d1e07ec6 --- /dev/null +++ b/packages/plugin-editor/src/index.test.ts @@ -0,0 +1,90 @@ +import { describe, it, expect, beforeAll } from 'vitest'; +import { ComponentRegistry } from '@object-ui/core'; + +describe('Plugin Editor', () => { + // Import all renderers to register them + beforeAll(async () => { + await import('./index'); + }); + + describe('code-editor component', () => { + it('should be registered in ComponentRegistry', () => { + const editorRenderer = ComponentRegistry.get('code-editor'); + expect(editorRenderer).toBeDefined(); + }); + + it('should have proper metadata', () => { + const config = ComponentRegistry.getConfig('code-editor'); + expect(config).toBeDefined(); + expect(config?.label).toBe('Code Editor'); + expect(config?.category).toBe('plugin'); + expect(config?.inputs).toBeDefined(); + expect(config?.defaultProps).toBeDefined(); + }); + + it('should have expected inputs', () => { + const config = ComponentRegistry.getConfig('code-editor'); + const inputNames = config?.inputs?.map((input: any) => input.name) || []; + + expect(inputNames).toContain('value'); + expect(inputNames).toContain('language'); + expect(inputNames).toContain('theme'); + expect(inputNames).toContain('height'); + expect(inputNames).toContain('readOnly'); + }); + + it('should have language as enum input', () => { + const config = ComponentRegistry.getConfig('code-editor'); + const languageInput = config?.inputs?.find((input: any) => input.name === 'language'); + + expect(languageInput).toBeDefined(); + expect(languageInput?.type).toBe('enum'); + expect(languageInput?.enum).toBeDefined(); + expect(Array.isArray(languageInput?.enum)).toBe(true); + + const enumValues = languageInput?.enum || []; + expect(enumValues).toContain('javascript'); + expect(enumValues).toContain('typescript'); + expect(enumValues).toContain('python'); + expect(enumValues).toContain('json'); + expect(enumValues).toContain('html'); + expect(enumValues).toContain('css'); + }); + + it('should have theme as enum input', () => { + const config = ComponentRegistry.getConfig('code-editor'); + const themeInput = config?.inputs?.find((input: any) => input.name === 'theme'); + + expect(themeInput).toBeDefined(); + expect(themeInput?.type).toBe('enum'); + expect(themeInput?.enum).toBeDefined(); + expect(Array.isArray(themeInput?.enum)).toBe(true); + + const enumValues = themeInput?.enum || []; + expect(enumValues).toContain('vs-dark'); + expect(enumValues).toContain('light'); + }); + + it('should have sensible default props', () => { + const config = ComponentRegistry.getConfig('code-editor'); + const defaults = config?.defaultProps; + + expect(defaults).toBeDefined(); + expect(defaults?.value).toBeDefined(); + expect(typeof defaults?.value).toBe('string'); + expect(defaults?.language).toBe('javascript'); + expect(defaults?.theme).toBe('vs-dark'); + expect(defaults?.height).toBe('400px'); + expect(defaults?.readOnly).toBe(false); + }); + + it('should have readOnly as boolean input', () => { + const config = ComponentRegistry.getConfig('code-editor'); + const readOnlyInput = config?.inputs?.find((input: any) => input.name === 'readOnly'); + + expect(readOnlyInput).toBeDefined(); + expect(readOnlyInput?.type).toBe('boolean'); + expect(readOnlyInput?.defaultValue).toBe(false); + }); + }); +}); diff --git a/packages/plugin-kanban/package.json b/packages/plugin-kanban/package.json index 2723f2a41..bd95bb0d3 100644 --- a/packages/plugin-kanban/package.json +++ b/packages/plugin-kanban/package.json @@ -14,7 +14,9 @@ } }, "scripts": { - "build": "vite build" + "build": "vite build", + "test": "vitest run", + "test:watch": "vitest" }, "dependencies": { "@dnd-kit/core": "^6.3.1", diff --git a/packages/plugin-kanban/src/index.test.ts b/packages/plugin-kanban/src/index.test.ts new file mode 100644 index 000000000..57cf5f228 --- /dev/null +++ b/packages/plugin-kanban/src/index.test.ts @@ -0,0 +1,104 @@ +import { describe, it, expect, beforeAll } from 'vitest'; +import { ComponentRegistry } from '@object-ui/core'; + +describe('Plugin Kanban', () => { + // Import all renderers to register them + beforeAll(async () => { + await import('./index'); + }); + + describe('kanban component', () => { + it('should be registered in ComponentRegistry', () => { + const kanbanRenderer = ComponentRegistry.get('kanban'); + expect(kanbanRenderer).toBeDefined(); + }); + + it('should have proper metadata', () => { + const config = ComponentRegistry.getConfig('kanban'); + expect(config).toBeDefined(); + expect(config?.label).toBe('Kanban Board'); + expect(config?.icon).toBe('LayoutDashboard'); + expect(config?.category).toBe('plugin'); + expect(config?.inputs).toBeDefined(); + expect(config?.defaultProps).toBeDefined(); + }); + + it('should have expected inputs', () => { + const config = ComponentRegistry.getConfig('kanban'); + const inputNames = config?.inputs?.map((input: any) => input.name) || []; + + expect(inputNames).toContain('columns'); + expect(inputNames).toContain('onCardMove'); + expect(inputNames).toContain('className'); + }); + + it('should have columns as required input', () => { + const config = ComponentRegistry.getConfig('kanban'); + const columnsInput = config?.inputs?.find((input: any) => input.name === 'columns'); + + expect(columnsInput).toBeDefined(); + expect(columnsInput?.required).toBe(true); + expect(columnsInput?.type).toBe('array'); + expect(columnsInput?.description).toBeDefined(); + }); + + it('should have onCardMove as code input', () => { + const config = ComponentRegistry.getConfig('kanban'); + const onCardMoveInput = config?.inputs?.find((input: any) => input.name === 'onCardMove'); + + expect(onCardMoveInput).toBeDefined(); + expect(onCardMoveInput?.type).toBe('code'); + expect(onCardMoveInput?.advanced).toBe(true); + expect(onCardMoveInput?.description).toBeDefined(); + }); + + it('should have sensible default props', () => { + const config = ComponentRegistry.getConfig('kanban'); + const defaults = config?.defaultProps; + + expect(defaults).toBeDefined(); + expect(defaults?.columns).toBeDefined(); + expect(Array.isArray(defaults?.columns)).toBe(true); + expect(defaults?.columns.length).toBeGreaterThan(0); + expect(defaults?.className).toBe('w-full'); + }); + + it('should have default columns with proper structure', () => { + const config = ComponentRegistry.getConfig('kanban'); + const defaults = config?.defaultProps; + const columns = defaults?.columns || []; + + // Verify at least 3 columns exist (todo, in-progress, done) + expect(columns.length).toBeGreaterThanOrEqual(3); + + // Verify each column has required properties + columns.forEach((column: any) => { + expect(column.id).toBeDefined(); + expect(column.title).toBeDefined(); + expect(column.cards).toBeDefined(); + expect(Array.isArray(column.cards)).toBe(true); + }); + + // Verify at least one column has cards + const hasCards = columns.some((column: any) => column.cards.length > 0); + expect(hasCards).toBe(true); + }); + + it('should have cards with proper structure', () => { + const config = ComponentRegistry.getConfig('kanban'); + const defaults = config?.defaultProps; + const columns = defaults?.columns || []; + + // Find a column with cards + const columnWithCards = columns.find((column: any) => column.cards.length > 0); + expect(columnWithCards).toBeDefined(); + + const card = columnWithCards.cards[0]; + expect(card.id).toBeDefined(); + expect(card.title).toBeDefined(); + expect(card.description).toBeDefined(); + expect(card.badges).toBeDefined(); + expect(Array.isArray(card.badges)).toBe(true); + }); + }); +}); diff --git a/packages/plugin-markdown/package.json b/packages/plugin-markdown/package.json index cc40005ca..4faa6debf 100644 --- a/packages/plugin-markdown/package.json +++ b/packages/plugin-markdown/package.json @@ -14,7 +14,9 @@ } }, "scripts": { - "build": "vite build" + "build": "vite build", + "test": "vitest run", + "test:watch": "vitest" }, "dependencies": { "react-markdown": "^10.1.0", diff --git a/packages/plugin-markdown/src/index.test.ts b/packages/plugin-markdown/src/index.test.ts new file mode 100644 index 000000000..a457be4c4 --- /dev/null +++ b/packages/plugin-markdown/src/index.test.ts @@ -0,0 +1,55 @@ +import { describe, it, expect, beforeAll } from 'vitest'; +import { ComponentRegistry } from '@object-ui/core'; + +describe('Plugin Markdown', () => { + // Import all renderers to register them + beforeAll(async () => { + await import('./index'); + }); + + describe('markdown component', () => { + it('should be registered in ComponentRegistry', () => { + const markdownRenderer = ComponentRegistry.get('markdown'); + expect(markdownRenderer).toBeDefined(); + }); + + it('should have proper metadata', () => { + const config = ComponentRegistry.getConfig('markdown'); + expect(config).toBeDefined(); + expect(config?.label).toBe('Markdown'); + expect(config?.category).toBe('plugin'); + expect(config?.inputs).toBeDefined(); + expect(config?.defaultProps).toBeDefined(); + }); + + it('should have expected inputs', () => { + const config = ComponentRegistry.getConfig('markdown'); + const inputNames = config?.inputs?.map((input: any) => input.name) || []; + + expect(inputNames).toContain('content'); + expect(inputNames).toContain('className'); + }); + + it('should have content as required input', () => { + const config = ComponentRegistry.getConfig('markdown'); + const contentInput = config?.inputs?.find((input: any) => input.name === 'content'); + + expect(contentInput).toBeDefined(); + expect(contentInput?.required).toBe(true); + expect(contentInput?.type).toBe('string'); + expect(contentInput?.inputType).toBe('textarea'); + }); + + it('should have sensible default props', () => { + const config = ComponentRegistry.getConfig('markdown'); + const defaults = config?.defaultProps; + + expect(defaults).toBeDefined(); + expect(defaults?.content).toBeDefined(); + expect(typeof defaults?.content).toBe('string'); + expect(defaults?.content.length).toBeGreaterThan(0); + // Verify it contains markdown syntax + expect(defaults?.content).toContain('#'); + }); + }); +}); From 10ec2d341a6eecaa610ff985daa1134f5a9b3e72 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 15 Jan 2026 00:09:57 +0000 Subject: [PATCH 3/3] Fix pnpm test command to use root vitest - Changed root test script from 'pnpm -r test' to 'vitest run' - Added pretest script to build required packages before running tests - Fixes issue where individual packages couldn't find vitest locally - All 86 tests now pass successfully Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com> --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 897acd971..668cff49f 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,8 @@ "prototype": "pnpm --filter prototype dev", "playground": "pnpm --filter @apps/playground dev", "build": "pnpm -r build", - "test": "pnpm -r test", + "pretest": "pnpm --filter @object-ui/types build && pnpm --filter @object-ui/core build && pnpm --filter @object-ui/react build && pnpm --filter @object-ui/components build", + "test": "vitest run", "docs:dev": "pnpm --filter object-ui-docs dev", "docs:build": "pnpm --filter object-ui-docs build", "docs:preview": "pnpm --filter object-ui-docs preview",