From 12147864780839df63c2365b4933fef6a4b029de Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 3 Mar 2026 14:02:20 +0000
Subject: [PATCH 1/2] Initial plan
From 41ba4f4f0de6931c6dd41a89f53bfb9b5d511378 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 3 Mar 2026 15:01:15 +0000
Subject: [PATCH 2/2] Remove unnecessary CI tests to reduce test suite overhead
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
---
.../src/__tests__/ConsoleFeatures.test.tsx | 114 -----
.../src/__tests__/ServerDefinitions.test.tsx | 402 ------------------
.../src/__tests__/SkeletonComponents.test.tsx | 61 ---
.../src/__tests__/SpecCompliance.test.tsx | 346 ---------------
.../__tests__/console-accessibility.test.tsx | 350 ---------------
.../console-load-performance.test.tsx | 366 ----------------
.../components/src/__tests__/Registry.test.ts | 21 -
.../plugin-charts/src/registration.test.tsx | 22 -
.../plugin-detail/src/registration.test.tsx | 18 -
.../plugin-list/src/registration.test.tsx | 8 -
.../src/__tests__/registration.test.tsx | 32 --
11 files changed, 1740 deletions(-)
delete mode 100644 apps/console/src/__tests__/ConsoleFeatures.test.tsx
delete mode 100644 apps/console/src/__tests__/ServerDefinitions.test.tsx
delete mode 100644 apps/console/src/__tests__/SkeletonComponents.test.tsx
delete mode 100644 apps/console/src/__tests__/SpecCompliance.test.tsx
delete mode 100644 apps/console/src/__tests__/console-accessibility.test.tsx
delete mode 100644 apps/console/src/__tests__/console-load-performance.test.tsx
delete mode 100644 packages/components/src/__tests__/Registry.test.ts
delete mode 100644 packages/plugin-charts/src/registration.test.tsx
delete mode 100644 packages/plugin-detail/src/registration.test.tsx
delete mode 100644 packages/plugin-list/src/registration.test.tsx
delete mode 100644 packages/plugin-view/src/__tests__/registration.test.tsx
diff --git a/apps/console/src/__tests__/ConsoleFeatures.test.tsx b/apps/console/src/__tests__/ConsoleFeatures.test.tsx
deleted file mode 100644
index a3eed8805..000000000
--- a/apps/console/src/__tests__/ConsoleFeatures.test.tsx
+++ /dev/null
@@ -1,114 +0,0 @@
-import { describe, it, expect, vi } from 'vitest';
-import { render, screen } from '@testing-library/react';
-import '@testing-library/jest-dom';
-import { ObjectView } from '../components/ObjectView';
-import { MemoryRouter, Route, Routes } from 'react-router-dom';
-
-// Mock child plugins to focus on ObjectView logic
-vi.mock('@object-ui/plugin-grid', () => ({
- ObjectGrid: (props: any) =>
Grid View: {props.schema.objectName} (Filter: {JSON.stringify(props.schema.filter)})
-}));
-
-vi.mock('@object-ui/plugin-kanban', () => ({
- ObjectKanban: () => Kanban View
-}));
-
-vi.mock('@object-ui/plugin-calendar', () => ({
- ObjectCalendar: () => Calendar View
-}));
-
-vi.mock('@object-ui/plugin-list', () => ({
- ListView: (props: any) => (
-
- ListView for {props.schema.objectName}
-
-
- )
-}));
-
-// Mock UI Components
-vi.mock('@object-ui/components', async (importOriginal) => {
- const actual = await importOriginal();
- return {
- ...actual,
- Button: ({ children, onClick, className }: any) => (
-
- ),
- Empty: ({ children }: any) => {children}
,
- EmptyTitle: ({ children }: any) => {children}
,
- EmptyDescription: ({ children }: any) => {children}
,
- Sheet: ({ children }: any) => {children}
,
- SheetContent: ({ children }: any) => {children}
,
- SheetHeader: ({ children }: any) => {children}
,
- SheetTitle: ({ children }: any) => {children}
,
- };
-});
-
-// Mock Icons
-vi.mock('lucide-react', async (importOriginal) => {
- const actual = await importOriginal();
- return {
- ...actual,
- Plus: () => +,
- Calendar: () => Cal,
- Kanban: () => Kan,
- Table: () => Tab,
- AlignLeft: () => Gantt,
- Filter: () => FilterIcon,
- X: () => X,
- Code2: () => Code,
- };
-});
-
-describe('ObjectView Console Features', () => {
-
- const mockObjects = [
- {
- name: 'todo_task',
- label: 'Todo Task',
- fields: { name: { type: 'text' }, status: { type: 'select' } },
- list_views: {
- all: { label: 'All Tasks', type: 'grid' }
- }
- }
- ];
-
- const mockDataSource = {
- find: vi.fn().mockResolvedValue([]),
- findOne: vi.fn(),
- create: vi.fn(),
- update: vi.fn(),
- delete: vi.fn()
- };
-
- const renderObjectView = (objectName = 'todo_task') => {
- return render(
-
-
-
- } />
-
-
- );
- };
-
- it('renders ListView', () => {
- renderObjectView();
-
- // "Todo Task" appears in breadcrumb and h1
- const headers = screen.getAllByText('Todo Task');
- expect(headers.length).toBeGreaterThanOrEqual(1);
- // Verify ListView is rendered instead of direct Filter button
- expect(screen.getByTestId('list-view')).toBeInTheDocument();
- expect(screen.getByText('ListView for todo_task')).toBeInTheDocument();
- });
-
- // Removed Filter button test as functionality moved to ListView plugin
-});
diff --git a/apps/console/src/__tests__/ServerDefinitions.test.tsx b/apps/console/src/__tests__/ServerDefinitions.test.tsx
deleted file mode 100644
index f827a1429..000000000
--- a/apps/console/src/__tests__/ServerDefinitions.test.tsx
+++ /dev/null
@@ -1,402 +0,0 @@
-/**
- * Server-Driven Layout Tests
- *
- * Tests for server-driven app layout, page definitions, and component composition
- */
-
-import { describe, it, expect, beforeAll, afterAll } from 'vitest';
-import { render, screen } from '@testing-library/react';
-import '@testing-library/jest-dom';
-import { SchemaRenderer, SchemaRendererProvider } from '@object-ui/react';
-import type { AppSchema } from '@object-ui/types';
-import { startMockServer, stopMockServer } from '../mocks/server';
-
-describe('Server-Driven Definitions', () => {
- beforeAll(async () => {
- await startMockServer();
- });
-
- afterAll(() => {
- stopMockServer();
- });
-
- describe('App Layout Definition', () => {
- it('should render app layout with sidebar', () => {
- const appSchema: AppSchema = {
- type: 'app',
- name: 'test-app',
- title: 'Test Application',
- description: 'Test app for MSW integration',
- layout: 'sidebar',
- menu: [
- {
- type: 'item',
- label: 'Dashboard',
- icon: 'layout-dashboard',
- href: '/dashboard',
- },
- {
- type: 'item',
- label: 'Contacts',
- icon: 'users',
- href: '/contacts',
- },
- ],
- };
-
- // Note: AppSchema would typically be rendered by a higher-level component
- // This is a simplified test for the schema structure
- expect(appSchema.type).toBe('app');
- expect(appSchema.layout).toBe('sidebar');
- expect(appSchema.menu).toHaveLength(2);
- expect(appSchema.menu![0].label).toBe('Dashboard');
- });
-
- it('should render app layout with header navigation', () => {
- const appSchema: AppSchema = {
- type: 'app',
- name: 'header-app',
- title: 'Header App',
- layout: 'header',
- menu: [
- {
- type: 'item',
- label: 'Home',
- href: '/',
- },
- {
- type: 'item',
- label: 'About',
- href: '/about',
- },
- ],
- };
-
- expect(appSchema.layout).toBe('header');
- expect(appSchema.menu).toHaveLength(2);
- });
-
- it('should define app with menu groups', () => {
- const appSchema: AppSchema = {
- type: 'app',
- name: 'grouped-app',
- title: 'Grouped App',
- layout: 'sidebar',
- menu: [
- {
- type: 'group',
- label: 'Main',
- children: [
- { type: 'item', label: 'Dashboard', href: '/dashboard' },
- { type: 'item', label: 'Overview', href: '/overview' },
- ],
- },
- {
- type: 'separator',
- },
- {
- type: 'group',
- label: 'Settings',
- children: [
- { type: 'item', label: 'Profile', href: '/profile' },
- { type: 'item', label: 'Preferences', href: '/preferences' },
- ],
- },
- ],
- };
-
- expect(appSchema.menu).toHaveLength(3);
- expect(appSchema.menu![0].type).toBe('group');
- expect(appSchema.menu![1].type).toBe('separator');
- });
- });
-
- describe('Page Definition', () => {
- it('should define a page with layout components', () => {
- const pageSchema = {
- type: 'div',
- className: 'container mx-auto p-4',
- children: [
- {
- type: 'text',
- value: 'Contact List',
- variant: 'h1',
- },
- {
- type: 'object-grid',
- objectName: 'contact',
- columns: ['name', 'email', 'company'],
- },
- ],
- };
-
- // Test that the schema can be rendered
- render(
-
-
-
- );
-
- expect(screen.getByText('Contact List')).toBeInTheDocument();
- });
-
- it('should define a dashboard page', () => {
- const dashboardPage = {
- type: 'div',
- className: 'p-6',
- children: [
- {
- type: 'text',
- value: 'Dashboard',
- variant: 'h1',
- },
- {
- type: 'dashboard',
- columns: 3,
- widgets: [
- {
- id: 'metric-1',
- title: 'Total Users',
- component: {
- type: 'metric',
- label: 'Users',
- value: '150',
- },
- layout: { x: 0, y: 0, w: 1, h: 1 },
- },
- {
- id: 'metric-2',
- title: 'Active Sessions',
- component: {
- type: 'metric',
- label: 'Sessions',
- value: '42',
- },
- layout: { x: 1, y: 0, w: 1, h: 1 },
- },
- ],
- },
- ],
- };
-
- render();
-
- expect(screen.getByText('Dashboard')).toBeInTheDocument();
- expect(screen.getByText('Total Users')).toBeInTheDocument();
- expect(screen.getByText('Active Sessions')).toBeInTheDocument();
- });
-
- it('should define a form page', () => {
- const formPage = {
- type: 'div',
- className: 'max-w-2xl mx-auto p-6',
- children: [
- {
- type: 'text',
- value: 'Create Contact',
- variant: 'h1',
- },
- {
- type: 'object-form',
- objectName: 'contact',
- mode: 'create',
- fields: ['name', 'email', 'company'],
- },
- ],
- };
-
- expect(formPage.children[0].value).toBe('Create Contact');
- expect(formPage.children[1].type).toBe('object-form');
- expect(formPage.children[1].mode).toBe('create');
- });
- });
-
- describe('Component Composition from Server', () => {
- it('should render nested layout components', () => {
- const nestedLayout = {
- type: 'div',
- className: 'flex flex-col gap-4',
- children: [
- {
- type: 'div',
- className: 'header',
- children: {
- type: 'text',
- value: 'Header Section',
- variant: 'h2',
- },
- },
- {
- type: 'div',
- className: 'content flex gap-4',
- children: [
- {
- type: 'div',
- className: 'sidebar w-64',
- children: {
- type: 'text',
- value: 'Sidebar',
- },
- },
- {
- type: 'div',
- className: 'main flex-1',
- children: {
- type: 'text',
- value: 'Main Content',
- },
- },
- ],
- },
- ],
- };
-
- render();
-
- expect(screen.getByText('Header Section')).toBeInTheDocument();
- expect(screen.getByText('Sidebar')).toBeInTheDocument();
- expect(screen.getByText('Main Content')).toBeInTheDocument();
- });
-
- it('should render complex page composition', () => {
- const complexPage = {
- type: 'div',
- className: 'min-h-screen bg-gray-50',
- children: [
- {
- type: 'div',
- className: 'header bg-white shadow',
- children: {
- type: 'div',
- className: 'container mx-auto p-4',
- children: {
- type: 'text',
- value: 'CRM Dashboard',
- variant: 'h1',
- },
- },
- },
- {
- type: 'div',
- className: 'container mx-auto p-6',
- children: [
- {
- type: 'dashboard',
- columns: 4,
- gap: 4,
- widgets: [
- {
- id: 'contacts-metric',
- title: 'Total Contacts',
- component: { type: 'metric', label: 'Contacts', value: '3' },
- layout: { x: 0, y: 0, w: 1, h: 1 },
- },
- {
- id: 'active-metric',
- title: 'Active',
- component: { type: 'metric', label: 'Active', value: '2' },
- layout: { x: 1, y: 0, w: 1, h: 1 },
- },
- ],
- },
- ],
- },
- ],
- };
-
- render();
-
- expect(screen.getByText('CRM Dashboard')).toBeInTheDocument();
- expect(screen.getAllByText('Total Contacts')).toHaveLength(1);
- expect(screen.getAllByText('Active')).toHaveLength(2);
- });
- });
-
- describe('Dynamic Server-Driven Content', () => {
- it('should support data binding in server definitions', async () => {
- // This test demonstrates how server-driven schemas can include dynamic data
- const dynamicSchema = {
- type: 'div',
- children: [
- {
- type: 'text',
- value: 'Contact Directory',
- variant: 'h1',
- },
- {
- type: 'object-grid',
- objectName: 'contact',
- columns: ['name', 'email'],
- data: {
- provider: 'object',
- object: 'contact',
- },
- },
- ],
- };
-
- expect(dynamicSchema.children[1].objectName).toBe('contact');
- expect(dynamicSchema.children[1].data?.provider).toBe('object');
- });
-
- it('should support conditional rendering based on server data', () => {
- const conditionalSchema = {
- type: 'div',
- children: [
- {
- type: 'text',
- value: 'Dashboard',
- variant: 'h1',
- },
- {
- type: 'div',
- visible: true, // Would be dynamic from server
- children: {
- type: 'metric',
- label: 'Visible Metric',
- value: '100',
- },
- },
- ],
- };
-
- expect(conditionalSchema.children[1].visible).toBe(true);
- });
- });
-
- describe('Layout Schema Validation', () => {
- it('should validate basic layout structure', () => {
- const layout = {
- type: 'div',
- className: 'container',
- children: [
- { type: 'text', value: 'Hello' },
- { type: 'text', value: 'World' },
- ],
- };
-
- expect(layout.type).toBe('div');
- expect(layout.children).toHaveLength(2);
- expect(Array.isArray(layout.children)).toBe(true);
- });
-
- it('should support single child or array of children', () => {
- const singleChild = {
- type: 'div',
- children: { type: 'text', value: 'Single' },
- };
-
- const multipleChildren = {
- type: 'div',
- children: [
- { type: 'text', value: 'First' },
- { type: 'text', value: 'Second' },
- ],
- };
-
- expect(singleChild.children.type).toBe('text');
- expect(Array.isArray(multipleChildren.children)).toBe(true);
- expect(multipleChildren.children).toHaveLength(2);
- });
- });
-});
diff --git a/apps/console/src/__tests__/SkeletonComponents.test.tsx b/apps/console/src/__tests__/SkeletonComponents.test.tsx
deleted file mode 100644
index 37c9c2225..000000000
--- a/apps/console/src/__tests__/SkeletonComponents.test.tsx
+++ /dev/null
@@ -1,61 +0,0 @@
-/**
- * Tests for skeleton loading components
- */
-import { describe, it, expect } from 'vitest';
-import { render } from '@testing-library/react';
-import '@testing-library/jest-dom';
-import { SkeletonGrid } from '../components/skeletons/SkeletonGrid';
-import { SkeletonDashboard } from '../components/skeletons/SkeletonDashboard';
-import { SkeletonDetail } from '../components/skeletons/SkeletonDetail';
-
-// Mock @object-ui/components Skeleton
-vi.mock('@object-ui/components', () => ({
- Skeleton: ({ className, ...props }: any) => (
-
- ),
-}));
-
-describe('SkeletonGrid', () => {
- it('renders with default props', () => {
- const { container } = render();
- const skeletons = container.querySelectorAll('[data-testid="skeleton"]');
- // Header (5) + 8 rows x 5 cols (40) + toolbar (4) + pagination (4) = 53
- expect(skeletons.length).toBeGreaterThan(0);
- });
-
- it('renders correct number of rows', () => {
- const { container } = render();
- // Should have skeletons for 3 rows x 2 columns in the table body
- const rowContainers = container.querySelectorAll('.border-b');
- expect(rowContainers.length).toBeGreaterThanOrEqual(3);
- });
-});
-
-describe('SkeletonDashboard', () => {
- it('renders with default props', () => {
- const { container } = render();
- const skeletons = container.querySelectorAll('[data-testid="skeleton"]');
- expect(skeletons.length).toBeGreaterThan(0);
- });
-
- it('renders correct number of widget cards', () => {
- const { container } = render();
- // 3 widget cards, each with 3 skeletons + stats row (4 cards x 3 each) + header (2)
- const skeletons = container.querySelectorAll('[data-testid="skeleton"]');
- expect(skeletons.length).toBeGreaterThan(0);
- });
-});
-
-describe('SkeletonDetail', () => {
- it('renders with default props', () => {
- const { container } = render();
- const skeletons = container.querySelectorAll('[data-testid="skeleton"]');
- expect(skeletons.length).toBeGreaterThan(0);
- });
-
- it('renders correct number of field rows', () => {
- const { container } = render();
- const skeletons = container.querySelectorAll('[data-testid="skeleton"]');
- expect(skeletons.length).toBeGreaterThan(0);
- });
-});
diff --git a/apps/console/src/__tests__/SpecCompliance.test.tsx b/apps/console/src/__tests__/SpecCompliance.test.tsx
deleted file mode 100644
index feed99dc1..000000000
--- a/apps/console/src/__tests__/SpecCompliance.test.tsx
+++ /dev/null
@@ -1,346 +0,0 @@
-
-import { describe, it, expect } from 'vitest';
-import appConfig from '../../objectstack.config';
-
-/**
- * Spec Compliance Tests
- *
- * These tests verify that the console properly implements the ObjectStack Spec v0.9.0
- * See: apps/console/SPEC_ALIGNMENT.md for full compliance details
- */
-
-describe('ObjectStack Spec v0.9.0 Compliance', () => {
-
- describe('AppSchema Validation', () => {
- it('should have at least one app defined', () => {
- expect(appConfig.apps).toBeDefined();
- expect(Array.isArray(appConfig.apps)).toBe(true);
- expect(appConfig.apps!.length).toBeGreaterThan(0);
- });
-
- it('should have valid app structure', () => {
- const app = appConfig.apps![0];
-
- // Required fields per spec
- expect(app.name).toBeDefined();
- expect(typeof app.name).toBe('string');
- expect(app.label).toBeDefined();
- expect(['string', 'object']).toContain(typeof app.label);
- if (typeof app.label === 'object') {
- expect(app.label).toHaveProperty('key');
- }
-
- // Name convention: lowercase snake_case
- expect(app.name).toMatch(/^[a-z][a-z0-9_]*$/);
- });
-
- it('should support optional app metadata', () => {
- const app = appConfig.apps![0];
-
- // Optional fields that should be defined if present
- if (app.description) {
- expect(['string', 'object']).toContain(typeof app.description);
- if (typeof app.description === 'object') {
- expect(app.description).toHaveProperty('key');
- }
- }
- if (app.version) {
- expect(typeof app.version).toBe('string');
- }
- if (app.icon) {
- expect(typeof app.icon).toBe('string');
- }
- });
-
- it('should support app branding configuration', () => {
- const appsWithBranding = appConfig.apps!.filter((a: any) => a.branding);
-
- if (appsWithBranding.length > 0) {
- const app = appsWithBranding[0];
-
- if (app.branding.primaryColor) {
- // Should be a valid CSS color
- expect(app.branding.primaryColor).toMatch(/^#[0-9A-Fa-f]{6}$/);
- }
-
- if (app.branding.logo) {
- expect(typeof app.branding.logo).toBe('string');
- }
-
- if (app.branding.favicon) {
- expect(typeof app.branding.favicon).toBe('string');
- }
- }
- });
-
- it('should support homePageId for custom landing pages', () => {
- // homePageId is optional but should be a string if defined
- appConfig.apps!.forEach((app: any) => {
- if (app.homePageId) {
- expect(typeof app.homePageId).toBe('string');
- expect(app.homePageId.length).toBeGreaterThan(0);
- }
- });
- });
-
- it('should support permission requirements', () => {
- // requiredPermissions is optional but should be an array if defined
- appConfig.apps!.forEach((app: any) => {
- if (app.requiredPermissions) {
- expect(Array.isArray(app.requiredPermissions)).toBe(true);
- app.requiredPermissions.forEach((perm: any) => {
- expect(typeof perm).toBe('string');
- });
- }
- });
- });
- });
-
- describe('NavigationItem Validation', () => {
- it('should have navigation items defined', () => {
- const app = appConfig.apps![0];
- expect(app.navigation).toBeDefined();
- expect(Array.isArray(app.navigation)).toBe(true);
- });
-
- it('should support object navigation items', () => {
- const objectNavItems = getAllNavItems(appConfig.apps![0].navigation)
- .filter((item: any) => item.type === 'object');
-
- if (objectNavItems.length > 0) {
- const item = objectNavItems[0];
- expect(item.id).toBeDefined();
- expect(item.label).toBeDefined();
- expect(item.objectName).toBeDefined();
- expect(typeof item.objectName).toBe('string');
- }
- });
-
- it('should support group navigation items', () => {
- const groupNavItems = getAllNavItems(appConfig.apps![0].navigation)
- .filter((item: any) => item.type === 'group');
-
- if (groupNavItems.length > 0) {
- const item = groupNavItems[0];
- expect(item.id).toBeDefined();
- expect(item.label).toBeDefined();
- expect(item.children).toBeDefined();
- expect(Array.isArray(item.children)).toBe(true);
- }
- });
-
- it('should have valid navigation item structure', () => {
- const allNavItems = getAllNavItems(appConfig.apps![0].navigation);
-
- allNavItems.forEach((item: any) => {
- // All items must have id, label, and type
- expect(item.id).toBeDefined();
- expect(item.label).toBeDefined();
- expect(item.type).toBeDefined();
-
- // Type must be one of the valid types
- expect(['object', 'dashboard', 'page', 'url', 'group', 'report', 'separator', 'action']).toContain(item.type);
-
- // Type-specific validation
- if (item.type === 'object') {
- expect(item.objectName).toBeDefined();
- }
- if (item.type === 'dashboard') {
- expect(item.dashboardName).toBeDefined();
- }
- if (item.type === 'page') {
- expect(item.pageName).toBeDefined();
- }
- if (item.type === 'url') {
- expect(item.url).toBeDefined();
- }
- if (item.type === 'group') {
- expect(item.children).toBeDefined();
- expect(Array.isArray(item.children)).toBe(true);
- }
- });
- });
-
- it('should support navigation item visibility', () => {
- const allNavItems = getAllNavItems(appConfig.apps![0].navigation);
-
- // visible field is optional but should be string or boolean if present
- allNavItems.forEach((item: any) => {
- if (item.visible !== undefined) {
- const validTypes = ['string', 'boolean'];
- expect(validTypes).toContain(typeof item.visible);
- }
- });
- });
-
- it('should support extended NavigationItem types (separator, action)', () => {
- // These types are valid per the unified NavigationItem model
- const validTypes = ['object', 'dashboard', 'page', 'url', 'group', 'report', 'separator', 'action'];
- const allNavItems = getAllNavItems(appConfig.apps![0].navigation);
-
- allNavItems.forEach((item: any) => {
- expect(validTypes).toContain(item.type);
- });
- });
-
- it('should support optional UX enhancement fields on navigation items', () => {
- const allNavItems = getAllNavItems(appConfig.apps![0].navigation);
-
- allNavItems.forEach((item: any) => {
- // badge is optional
- if (item.badge !== undefined) {
- expect(['string', 'number']).toContain(typeof item.badge);
- }
- // requiredPermissions is optional
- if (item.requiredPermissions) {
- expect(Array.isArray(item.requiredPermissions)).toBe(true);
- }
- // order is optional
- if (item.order !== undefined) {
- expect(typeof item.order).toBe('number');
- }
- });
- });
- });
-
- describe('NavigationArea Validation', () => {
- it('should support areas configuration on apps', () => {
- // areas is optional per unified model
- appConfig.apps!.forEach((app: any) => {
- if (app.areas) {
- expect(Array.isArray(app.areas)).toBe(true);
-
- app.areas.forEach((area: any) => {
- expect(area.id).toBeDefined();
- expect(typeof area.id).toBe('string');
- expect(area.label).toBeDefined();
- expect(typeof area.label).toBe('string');
- expect(area.navigation).toBeDefined();
- expect(Array.isArray(area.navigation)).toBe(true);
- });
- }
- });
- });
-
- it('should have valid navigation items inside areas', () => {
- appConfig.apps!.forEach((app: any) => {
- if (app.areas) {
- app.areas.forEach((area: any) => {
- const allNavItems = getAllNavItems(area.navigation);
- allNavItems.forEach((item: any) => {
- expect(item.id).toBeDefined();
- expect(item.label).toBeDefined();
- expect(item.type).toBeDefined();
- });
- });
- }
- });
- });
- });
-
- describe('ObjectSchema Validation', () => {
- it('should have objects defined', () => {
- expect(appConfig.objects).toBeDefined();
- expect(Array.isArray(appConfig.objects)).toBe(true);
- expect(appConfig.objects!.length).toBeGreaterThan(0);
- });
-
- it('should have valid object structure', () => {
- const object = appConfig.objects![0];
-
- // Required fields
- expect(object.name).toBeDefined();
- expect(typeof object.name).toBe('string');
- expect(object.label).toBeDefined();
- expect(typeof object.label).toBe('string');
- expect(object.fields).toBeDefined();
- });
-
- it('should have valid field definitions', () => {
- const object = appConfig.objects![0];
- const fields = Array.isArray(object.fields)
- ? object.fields
- : Object.values(object.fields);
-
- expect(fields.length).toBeGreaterThan(0);
-
- fields.forEach((field: any) => {
- // Each field should have a type
- expect(field.type).toBeDefined();
- });
- });
-
- it('should reference only defined objects in navigation', () => {
- const objectNames = new Set(appConfig.objects!.map((o: any) => o.name));
- const navItems = getAllNavItems(appConfig.apps![0].navigation);
- const objectNavItems = navItems.filter((item: any) => item.type === 'object');
-
- objectNavItems.forEach((item: any) => {
- expect(objectNames.has(item.objectName)).toBe(true);
- });
- });
- });
-
- describe('Manifest Validation', () => {
- it('should have manifest defined', () => {
- expect(appConfig.manifest).toBeDefined();
- });
-
- it('should have valid data seeds', () => {
- if (appConfig.manifest?.data) {
- expect(Array.isArray(appConfig.manifest.data)).toBe(true);
-
- appConfig.manifest.data.forEach((seed: any) => {
- expect(seed.object).toBeDefined();
- expect(typeof seed.object).toBe('string');
- expect(seed.mode).toBeDefined();
- expect(['upsert', 'insert']).toContain(seed.mode);
- expect(seed.records).toBeDefined();
- expect(Array.isArray(seed.records)).toBe(true);
- });
- }
- });
-
- it('should reference only defined objects in manifest', () => {
- if (appConfig.manifest?.data) {
- const objectNames = new Set(appConfig.objects!.map((o: any) => o.name));
-
- appConfig.manifest.data.forEach((seed: any) => {
- expect(objectNames.has(seed.object)).toBe(true);
- });
- }
- });
- });
-
- describe('Plugin Configuration', () => {
- it('should have plugins defined', () => {
- expect(appConfig.plugins).toBeDefined();
- expect(Array.isArray(appConfig.plugins)).toBe(true);
- });
-
- it('should have datasource configuration', () => {
- expect(appConfig.datasources).toBeDefined();
- expect(appConfig.datasources!.default).toBeDefined();
- expect(appConfig.datasources!.default.driver).toBeDefined();
- });
- });
-});
-
-/**
- * Helper function to recursively get all navigation items including nested ones
- */
-function getAllNavItems(navItems: any[]): any[] {
- if (!navItems) return [];
-
- const result: any[] = [];
-
- for (const item of navItems) {
- result.push(item);
-
- if (item.type === 'group' && item.children) {
- result.push(...getAllNavItems(item.children));
- }
- }
-
- return result;
-}
diff --git a/apps/console/src/__tests__/console-accessibility.test.tsx b/apps/console/src/__tests__/console-accessibility.test.tsx
deleted file mode 100644
index 66d08daf9..000000000
--- a/apps/console/src/__tests__/console-accessibility.test.tsx
+++ /dev/null
@@ -1,350 +0,0 @@
-/**
- * ObjectUI
- * Copyright (c) 2024-present ObjectStack Inc.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-/**
- * Console page-level axe-core accessibility audit.
- *
- * Tests full Console page assemblies (layout, sidebar, header, key views)
- * against WCAG 2.1 AA standards using axe-core.
- * Part of P2.3 Accessibility & Inclusive Design roadmap.
- */
-
-import { describe, it, vi } from 'vitest';
-import { render } from '@testing-library/react';
-import { axe } from 'vitest-axe';
-import React from 'react';
-import { MemoryRouter } from 'react-router-dom';
-
-// Mock heavy dependencies to isolate layout accessibility testing
-vi.mock('@objectstack/client', () => ({
- ObjectStackClient: class {
- connect = vi.fn().mockResolvedValue(true);
- },
-}));
-
-vi.mock('../../objectstack.shared', () => ({
- default: {
- apps: [
- {
- name: 'sales',
- label: 'Sales App',
- active: true,
- icon: 'briefcase',
- navigation: [
- { id: 'nav_opp', label: 'Opportunities', type: 'object', objectName: 'opportunity' },
- { id: 'nav_contact', label: 'Contacts', type: 'object', objectName: 'contact' },
- ],
- },
- ],
- objects: [
- { name: 'opportunity', label: 'Opportunity', fields: {} },
- { name: 'contact', label: 'Contact', fields: {} },
- ],
- },
-}));
-
-vi.mock('../dataSource', () => {
- const MockAdapter = class {
- find = vi.fn().mockResolvedValue([]);
- findOne = vi.fn();
- create = vi.fn();
- update = vi.fn();
- delete = vi.fn();
- connect = vi.fn().mockResolvedValue(true);
- onConnectionStateChange = vi.fn();
- discovery = {};
- };
- return {
- ObjectStackAdapter: MockAdapter,
- ObjectStackDataSource: MockAdapter,
- };
-});
-
-// Mock lucide-react icons
-vi.mock('lucide-react', async (importOriginal) => {
- const actual = await importOriginal();
- const createIcon = (name: string) => (props: any) =>
- React.createElement('span', { 'data-testid': `icon-${name}`, 'aria-hidden': 'true', ...props });
- return {
- ...actual,
- Database: createIcon('database'),
- LayoutDashboard: createIcon('dashboard'),
- Briefcase: createIcon('briefcase'),
- ChevronRight: createIcon('chevron-right'),
- ChevronsUpDown: createIcon('chevrons-up-down'),
- Settings: createIcon('settings'),
- LogOut: createIcon('logout'),
- Plus: createIcon('plus'),
- Menu: createIcon('menu'),
- Search: createIcon('search'),
- Bell: createIcon('bell'),
- User: createIcon('user'),
- Users: createIcon('users'),
- Sun: createIcon('sun'),
- Moon: createIcon('moon'),
- Home: createIcon('home'),
- X: createIcon('x'),
- };
-});
-
-async function expectNoViolations(container: HTMLElement) {
- const results = await axe(container);
- const violations = (results as any).violations || [];
- if (violations.length > 0) {
- const messages = violations.map(
- (v: any) => `[${v.impact}] ${v.id}: ${v.description} (${v.nodes.length} instance(s))`
- );
- throw new Error(
- `Expected no accessibility violations, but found ${violations.length}:\n${messages.join('\n')}`
- );
- }
-}
-
-describe('Console Page-Level Accessibility Audit', () => {
- it('main layout structure has proper landmarks', async () => {
- const { container } = render(
-
-
-
-
-
-
Sales App
-
-
-
-
-
Welcome to the Console
-
-
-
-
- );
- await expectNoViolations(container);
- });
-
- it('sidebar navigation with links has no violations', async () => {
- const { container } = render(
-
-
-
- );
- await expectNoViolations(container);
- });
-
- it('header with search and actions has no violations', async () => {
- const { container } = render(
-
- );
- await expectNoViolations(container);
- });
-
- it('dashboard view with cards and widgets has no violations', async () => {
- const { container } = render(
-
- Dashboard
-
-
-
- Total Revenue
- $45,231.89
-
-
- Active Users
- 2,350
-
-
-
-
-
Recent Activity
-
-
-
- | Name |
- Status |
- Date |
-
-
-
-
- | Task 1 |
- Complete |
- 2024-01-15 |
-
-
-
-
-
- );
- await expectNoViolations(container);
- });
-
- it('object list view with table has no violations', async () => {
- const { container } = render(
-
-
-
Opportunities
-
-
-
-
-
-
-
-
-
- | Name |
- Amount |
- Stage |
-
-
-
-
- | Acme Corp Deal |
- $50,000 |
- Negotiation |
-
-
- | Beta Inc Deal |
- $25,000 |
- Proposal |
-
-
-
-
-
- );
- await expectNoViolations(container);
- });
-
- it('form view with proper labels has no violations', async () => {
- const { container } = render(
-
- New Opportunity
-
-
- );
- await expectNoViolations(container);
- });
-
- it('loading state has appropriate ARIA attributes', async () => {
- const { container } = render(
-
-
-
Initializing Console...
-
- );
- await expectNoViolations(container);
- });
-
- it('error boundary state has proper structure', async () => {
- const { container } = render(
-
-
-
Something went wrong
-
An unexpected error occurred. Please try refreshing the page.
-
-
-
- );
- await expectNoViolations(container);
- });
-
- it('empty state view has no violations', async () => {
- const { container } = render(
-
- Opportunities
-
-
No opportunities found
-
Create your first opportunity to get started.
-
-
-
- );
- await expectNoViolations(container);
- });
-
- it('keyboard shortcuts dialog has no violations', async () => {
- const { container } = render(
-
-
Keyboard Shortcuts
-
- - Ctrl + K
- - Open command palette
- - Ctrl + N
- - Create new record
-
-
-
- );
- await expectNoViolations(container);
- });
-});
diff --git a/apps/console/src/__tests__/console-load-performance.test.tsx b/apps/console/src/__tests__/console-load-performance.test.tsx
deleted file mode 100644
index b571d1f06..000000000
--- a/apps/console/src/__tests__/console-load-performance.test.tsx
+++ /dev/null
@@ -1,366 +0,0 @@
-/**
- * ObjectUI
- * Copyright (c) 2024-present ObjectStack Inc.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-/**
- * Console load-performance smoke tests.
- *
- * Validates code-splitting patterns, render-time budgets, loading-skeleton
- * immediacy, and production-bundle hygiene without requiring a real browser.
- * Since JSDOM cannot measure real network or bundle sizes we verify the
- * structural invariants that guarantee good performance:
- * - lazy() / dynamic import() route splitting
- * - shallow component-tree depth
- * - fast initial render via performance.now()
- * - skeleton placeholders before async data
- * - MSW gated behind a runtime flag
- */
-
-import { describe, it, expect } from 'vitest';
-import { render, screen } from '@testing-library/react';
-import '@testing-library/jest-dom';
-import React, { Suspense } from 'react';
-import * as fs from 'node:fs';
-import * as path from 'node:path';
-
-// ---------------------------------------------------------------------------
-// Performance budget constants (keep in sync with ROADMAP / docs)
-// ---------------------------------------------------------------------------
-
-const PERF = {
- /** Max number of JS chunks a production build may emit */
- MAX_CHUNK_COUNT: 22,
- /** Main entry gzip budget reference (KB) */
- MAIN_ENTRY_GZIP_KB: 50,
- /** Maximum React component nesting depth */
- MAX_TREE_DEPTH: 15,
- /** Initial render must complete within this window (ms) */
- RENDER_TIME_BUDGET_MS: 100,
-} as const;
-
-// ---------------------------------------------------------------------------
-// 1. Bundle chunk count — verify route-level code-splitting exists
-// ---------------------------------------------------------------------------
-
-describe('Bundle chunk verification', () => {
- it('App.tsx declares lazy-loaded route chunks', () => {
- const appSource = fs.readFileSync(
- path.resolve(__dirname, '..', 'App.tsx'),
- 'utf-8',
- );
-
- const lazyImports = appSource.match(/\blazy\s*\(\s*\(\)\s*=>\s*import\(/g) || [];
-
- // We expect at least the route-level splits declared in App.tsx
- expect(lazyImports.length).toBeGreaterThanOrEqual(5);
- expect(lazyImports.length).toBeLessThanOrEqual(PERF.MAX_CHUNK_COUNT);
- });
-
- it('lazy routes cover all three split categories', () => {
- const appSource = fs.readFileSync(
- path.resolve(__dirname, '..', 'App.tsx'),
- 'utf-8',
- );
-
- // Route views
- expect(appSource).toContain("import('./components/RecordDetailView')");
- expect(appSource).toContain("import('./components/DashboardView')");
- // Auth pages
- expect(appSource).toContain("import('./pages/LoginPage')");
- // System admin pages
- expect(appSource).toContain("import('./pages/system/UserManagementPage')");
- });
-});
-
-// ---------------------------------------------------------------------------
-// 2. Main entry size budget reference
-// ---------------------------------------------------------------------------
-
-describe('Entry-size budget constants', () => {
- it('gzip budget reference is below 50 KB', () => {
- expect(PERF.MAIN_ENTRY_GZIP_KB).toBeLessThanOrEqual(50);
- });
-
- it('eagerly loaded imports in App.tsx are limited', () => {
- const appSource = fs.readFileSync(
- path.resolve(__dirname, '..', 'App.tsx'),
- 'utf-8',
- );
-
- // Count non-lazy static imports (lines starting with "import " that are
- // NOT type-only imports and come before the first lazy() declaration)
- const beforeLazy = appSource.split(/\blazy\s*\(/)[0];
- const staticImports =
- beforeLazy.match(/^import\s+(?!type\b)/gm) || [];
-
- // Eagerly loaded modules should stay small; flag if this grows too large
- expect(staticImports.length).toBeLessThanOrEqual(21);
- });
-});
-
-// ---------------------------------------------------------------------------
-// 3. Component tree depth verification
-// ---------------------------------------------------------------------------
-
-describe('Component tree depth', () => {
- function NestedTree({ depth }: { depth: number }): React.ReactElement {
- if (depth <= 0) return leaf;
- return (
-
-
-
- );
- }
-
- it(`renders a tree of depth ${PERF.MAX_TREE_DEPTH} without issue`, () => {
- const { container } = render();
- expect(screen.getByTestId('leaf')).toBeInTheDocument();
-
- // Measure actual DOM nesting depth
- let node: Element | null = container.querySelector('[data-testid="leaf"]');
- let levels = 0;
- while (node && node !== container) {
- node = node.parentElement;
- levels++;
- }
- expect(levels).toBeLessThanOrEqual(PERF.MAX_TREE_DEPTH + 2); // +2 for root wrappers
- });
-
- it('typical console layout nests fewer than MAX_TREE_DEPTH levels', () => {
- const { container } = render(
- ,
- );
-
- // Walk from deepest text node up to the shell
- const deepest = container.querySelector('.grid div')!;
- let el: Element | null = deepest;
- let depth = 0;
- while (el && el !== container) {
- el = el.parentElement;
- depth++;
- }
- expect(depth).toBeLessThan(PERF.MAX_TREE_DEPTH);
- });
-});
-
-// ---------------------------------------------------------------------------
-// 4. Render-time verification (performance.now() timing)
-// ---------------------------------------------------------------------------
-
-describe('Render-time budget', () => {
- it(`core layout renders in under ${PERF.RENDER_TIME_BUDGET_MS}ms`, () => {
- const start = performance.now();
-
- render(
-
-
-
-
-
-
- {Array.from({ length: 9 }, (_, i) => (
-
Card {i}
- ))}
-
-
-
-
,
- );
-
- const elapsed = performance.now() - start;
- expect(elapsed).toBeLessThan(PERF.RENDER_TIME_BUDGET_MS);
- });
-
- it('renders a 100-row data table within budget', () => {
- const rows = Array.from({ length: 100 }, (_, i) => ({
- id: i,
- name: `Row ${i}`,
- status: i % 2 === 0 ? 'active' : 'inactive',
- }));
-
- const start = performance.now();
- render(
-
-
- | ID | Name | Status |
-
-
- {rows.map((r) => (
- | {r.id} | {r.name} | {r.status} |
- ))}
-
-
,
- );
- const elapsed = performance.now() - start;
-
- expect(elapsed).toBeLessThan(PERF.RENDER_TIME_BUDGET_MS);
- });
-});
-
-// ---------------------------------------------------------------------------
-// 5. Loading skeleton renders immediately (before async data)
-// ---------------------------------------------------------------------------
-
-describe('Loading skeleton immediacy', () => {
- it('Suspense fallback renders before lazy component resolves', () => {
- // Simulate a lazy component that never resolves during the test
- const NeverResolves = React.lazy(
- () => new Promise<{ default: React.ComponentType }>(() => {}),
- );
-
- render(
- Loading...}>
-
- ,
- );
-
- // The skeleton must appear synchronously
- expect(screen.getByTestId('skeleton')).toBeInTheDocument();
- expect(screen.getByTestId('skeleton')).toHaveTextContent('Loading...');
- });
-
- it('loading placeholder renders while data is pending', () => {
- function DataView({ data }: { data: string[] | null }) {
- if (!data) {
- return (
-
- );
- }
- return (
-
- {data.map((d, i) => - {d}
)}
-
- );
- }
-
- // Render with null data → skeleton should be visible
- const { rerender } = render();
- expect(screen.getByTestId('loading-state')).toBeInTheDocument();
- expect(screen.queryByTestId('data-list')).not.toBeInTheDocument();
-
- // Provide data → skeleton disappears
- rerender();
- expect(screen.queryByTestId('loading-state')).not.toBeInTheDocument();
- expect(screen.getByTestId('data-list')).toBeInTheDocument();
- });
-});
-
-// ---------------------------------------------------------------------------
-// 6. Lazy-loaded routes use dynamic import()
-// ---------------------------------------------------------------------------
-
-describe('Lazy-loaded routes use dynamic import()', () => {
- it('every lazy() call wraps a dynamic import()', () => {
- const appSource = fs.readFileSync(
- path.resolve(__dirname, '..', 'App.tsx'),
- 'utf-8',
- );
-
- // Extract all lazy() declarations
- const lazyDeclarations = appSource.match(/const \w+ = lazy\(.+?\);/g) || [];
- expect(lazyDeclarations.length).toBeGreaterThan(0);
-
- for (const decl of lazyDeclarations) {
- // Each must contain a dynamic import() call
- expect(decl).toMatch(/import\(/);
- }
- });
-
- it('no route component is eagerly imported AND lazy-loaded', () => {
- const appSource = fs.readFileSync(
- path.resolve(__dirname, '..', 'App.tsx'),
- 'utf-8',
- );
-
- // Collect names of lazy-loaded components
- const lazyNames =
- (appSource.match(/const (\w+) = lazy\(/g) || []).map((m) =>
- m.replace(/^const /, '').replace(/ = lazy\($/, ''),
- );
-
- // Collect eagerly imported identifiers (non-type, non-lazy)
- const eagerImports = appSource.match(/^import \{[^}]+\} from/gm) || [];
- const eagerNames = eagerImports.flatMap((line) => {
- const match = line.match(/\{([^}]+)\}/);
- return match ? match[1].split(',').map((n) => n.trim()) : [];
- });
-
- // No component should appear in both lists
- for (const name of lazyNames) {
- expect(eagerNames).not.toContain(name);
- }
- });
-});
-
-// ---------------------------------------------------------------------------
-// 7. MSW is not bundled in production mode
-// ---------------------------------------------------------------------------
-
-describe('MSW production-bundle hygiene', () => {
- it('MSW import is gated behind a runtime env flag', () => {
- const mainSource = fs.readFileSync(
- path.resolve(__dirname, '..', 'main.tsx'),
- 'utf-8',
- );
-
- // MSW should only be imported dynamically, never at the top level
- const topLevelMswImport = mainSource.match(/^import .+msw/m);
- expect(topLevelMswImport).toBeNull();
-
- // The dynamic import must be guarded by an env check
- expect(mainSource).toMatch(/import\.meta\.env\./);
- expect(mainSource).toMatch(/import\(.+mocks/);
- });
-
- it('no static MSW import exists in main.tsx', () => {
- const mainSource = fs.readFileSync(
- path.resolve(__dirname, '..', 'main.tsx'),
- 'utf-8',
- );
-
- // Ensure no top-level import from 'msw' or './mocks'
- const staticMswLines = mainSource
- .split('\n')
- .filter((line) => /^import\s/.test(line))
- .filter((line) => /msw|mocks/.test(line));
-
- expect(staticMswLines).toHaveLength(0);
- });
-});
diff --git a/packages/components/src/__tests__/Registry.test.ts b/packages/components/src/__tests__/Registry.test.ts
deleted file mode 100644
index a1a12e594..000000000
--- a/packages/components/src/__tests__/Registry.test.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-
-import { describe, it, expect } from 'vitest';
-import { ComponentRegistry } from '@object-ui/core';
-import '../renderers'; // Ensure side-effects are run
-
-describe('Component Registry', () => {
- it('should register standard page types', () => {
- expect(ComponentRegistry.get('page')).toBeDefined();
- expect(ComponentRegistry.get('app')).toBeDefined();
- expect(ComponentRegistry.get('utility')).toBeDefined();
- expect(ComponentRegistry.get('home')).toBeDefined();
- expect(ComponentRegistry.get('record')).toBeDefined();
- });
-
- it('should register standard layout components', () => {
- expect(ComponentRegistry.get('container')).toBeDefined();
- expect(ComponentRegistry.get('grid')).toBeDefined();
- expect(ComponentRegistry.get('stack')).toBeDefined();
- expect(ComponentRegistry.get('card')).toBeDefined();
- });
-});
diff --git a/packages/plugin-charts/src/registration.test.tsx b/packages/plugin-charts/src/registration.test.tsx
deleted file mode 100644
index a63b0bffa..000000000
--- a/packages/plugin-charts/src/registration.test.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import React from 'react';
-import { render, screen } from '@testing-library/react';
-import { describe, it, expect, vi, beforeAll } from 'vitest';
-import { ComponentRegistry } from '@object-ui/core';
-
-describe('Plugin Charts Registration', () => {
- beforeAll(async () => {
- await import('./index');
- }, 30000);
-
- it('registers bar-chart component', () => {
- const config = ComponentRegistry.get('bar-chart');
- expect(config).toBeDefined();
- });
-
- it('registers chart component types', () => {
- expect(ComponentRegistry.get('chart')).toBeDefined(); // Assuming base
- // Verify aliases if they exist
- expect(ComponentRegistry.get('pie-chart')).toBeDefined();
- expect(ComponentRegistry.get('donut-chart')).toBeDefined();
- });
-});
diff --git a/packages/plugin-detail/src/registration.test.tsx b/packages/plugin-detail/src/registration.test.tsx
deleted file mode 100644
index 30ec05b69..000000000
--- a/packages/plugin-detail/src/registration.test.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import { describe, it, expect, beforeAll } from 'vitest';
-import { ComponentRegistry } from '@object-ui/core';
-
-describe('Plugin Detail Registration', () => {
- beforeAll(async () => {
- await import('./index');
- }, 15000); // Increase timeout to 15 seconds for async import
-
- it('registers detail-view component', () => {
- // We must use getConfig to retrieve the metadata (label, category, etc.)
- // .get() only returns the React Component.
- const config = ComponentRegistry.getConfig('detail-view');
-
- expect(config).toBeDefined();
- expect(config?.label).toBe('Detail View');
- expect(config?.category).toBe('Views');
- });
-});
diff --git a/packages/plugin-list/src/registration.test.tsx b/packages/plugin-list/src/registration.test.tsx
deleted file mode 100644
index dcd5567a4..000000000
--- a/packages/plugin-list/src/registration.test.tsx
+++ /dev/null
@@ -1,8 +0,0 @@
-import { describe, it, expect } from 'vitest';
-import { ListView } from './index';
-
-describe('Plugin List Registration', () => {
- it('exports ListView component', () => {
- expect(ListView).toBeDefined();
- });
-});
diff --git a/packages/plugin-view/src/__tests__/registration.test.tsx b/packages/plugin-view/src/__tests__/registration.test.tsx
deleted file mode 100644
index 2620689f2..000000000
--- a/packages/plugin-view/src/__tests__/registration.test.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * ObjectUI
- * Copyright (c) 2024-present ObjectStack Inc.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-import { describe, it, expect } from 'vitest';
-import { ObjectView, ViewSwitcher, FilterUI, SortUI } from '../index';
-
-describe('Plugin View Registration', () => {
- it('exports ObjectView component', () => {
- expect(ObjectView).toBeDefined();
- expect(typeof ObjectView).toBe('function');
- });
-
- it('exports ViewSwitcher component', () => {
- expect(ViewSwitcher).toBeDefined();
- expect(typeof ViewSwitcher).toBe('function');
- });
-
- it('exports FilterUI component', () => {
- expect(FilterUI).toBeDefined();
- expect(typeof FilterUI).toBe('function');
- });
-
- it('exports SortUI component', () => {
- expect(SortUI).toBeDefined();
- expect(typeof SortUI).toBe('function');
- });
-});