Skip to content

Commit 1d4f33a

Browse files
authored
Merge pull request #309 from objectstack-ai/copilot/test-object-table-and-components
2 parents a2679a6 + 2309467 commit 1d4f33a

File tree

7 files changed

+930
-1
lines changed

7 files changed

+930
-1
lines changed

examples/msw-object-form/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,11 @@
1414
"test:ui": "vitest --ui"
1515
},
1616
"dependencies": {
17+
"@object-ui/components": "workspace:*",
1718
"@object-ui/fields": "workspace:*",
19+
"@object-ui/plugin-dashboard": "workspace:*",
1820
"@object-ui/plugin-form": "workspace:*",
21+
"@object-ui/plugin-grid": "workspace:*",
1922
"@object-ui/react": "workspace:*",
2023
"@object-ui/types": "workspace:*",
2124
"@objectstack/client": "^0.7.2",
Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
/**
2+
* Dashboard Integration Tests
3+
*
4+
* Tests Dashboard component with MSW server backend
5+
*/
6+
7+
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
8+
import { render, screen, waitFor } from '@testing-library/react';
9+
import '@testing-library/jest-dom';
10+
import { DashboardRenderer } from '@object-ui/plugin-dashboard';
11+
import type { DashboardSchema } from '@object-ui/types';
12+
import { startMockServer, stopMockServer, getDriver } from '../mocks/server';
13+
14+
describe('Dashboard MSW Integration', () => {
15+
beforeAll(async () => {
16+
await startMockServer();
17+
});
18+
19+
afterAll(() => {
20+
stopMockServer();
21+
});
22+
23+
describe('Dashboard Layout', () => {
24+
it('should render dashboard with widgets', async () => {
25+
const schema: DashboardSchema = {
26+
type: 'dashboard',
27+
columns: 3,
28+
gap: 4,
29+
widgets: [
30+
{
31+
id: 'widget-1',
32+
title: 'Total Contacts',
33+
component: {
34+
type: 'metric',
35+
label: 'Total Contacts',
36+
value: '3',
37+
},
38+
layout: { x: 0, y: 0, w: 1, h: 1 },
39+
},
40+
{
41+
id: 'widget-2',
42+
title: 'Active Contacts',
43+
component: {
44+
type: 'metric',
45+
label: 'Active',
46+
value: '2',
47+
},
48+
layout: { x: 1, y: 0, w: 1, h: 1 },
49+
},
50+
],
51+
};
52+
53+
render(<DashboardRenderer schema={schema} />);
54+
55+
// Check that widget titles are rendered
56+
expect(screen.getByText('Total Contacts')).toBeInTheDocument();
57+
expect(screen.getByText('Active Contacts')).toBeInTheDocument();
58+
});
59+
60+
it('should render dashboard with different column configurations', async () => {
61+
const schema: DashboardSchema = {
62+
type: 'dashboard',
63+
columns: 2,
64+
gap: 2,
65+
widgets: [
66+
{
67+
id: 'widget-1',
68+
title: 'Widget 1',
69+
component: {
70+
type: 'metric',
71+
label: 'Metric 1',
72+
value: '100',
73+
},
74+
layout: { x: 0, y: 0, w: 1, h: 1 },
75+
},
76+
{
77+
id: 'widget-2',
78+
title: 'Widget 2',
79+
component: {
80+
type: 'metric',
81+
label: 'Metric 2',
82+
value: '200',
83+
},
84+
layout: { x: 1, y: 0, w: 1, h: 1 },
85+
},
86+
],
87+
};
88+
89+
render(<DashboardRenderer schema={schema} />);
90+
91+
expect(screen.getByText('Widget 1')).toBeInTheDocument();
92+
expect(screen.getByText('Widget 2')).toBeInTheDocument();
93+
});
94+
95+
it('should handle empty widgets array', () => {
96+
const schema: DashboardSchema = {
97+
type: 'dashboard',
98+
columns: 3,
99+
widgets: [],
100+
};
101+
102+
const { container } = render(<DashboardRenderer schema={schema} />);
103+
104+
// Should render empty grid
105+
expect(container.querySelector('.grid')).toBeInTheDocument();
106+
});
107+
});
108+
109+
describe('Dashboard with Server Data', () => {
110+
it('should render dashboard with data-driven metrics', async () => {
111+
const driver = getDriver();
112+
113+
// Get actual data from server
114+
const contacts = await driver!.find('contact', {});
115+
const activeContacts = contacts.filter(c => c.is_active);
116+
117+
const schema: DashboardSchema = {
118+
type: 'dashboard',
119+
columns: 3,
120+
gap: 4,
121+
widgets: [
122+
{
123+
id: 'total-contacts',
124+
title: 'Total Contacts',
125+
component: {
126+
type: 'metric',
127+
label: 'Total',
128+
value: String(contacts.length),
129+
},
130+
layout: { x: 0, y: 0, w: 1, h: 1 },
131+
},
132+
{
133+
id: 'active-contacts',
134+
title: 'Active Contacts',
135+
component: {
136+
type: 'metric',
137+
label: 'Active',
138+
value: String(activeContacts.length),
139+
},
140+
layout: { x: 1, y: 0, w: 1, h: 1 },
141+
},
142+
],
143+
};
144+
145+
render(<DashboardRenderer schema={schema} />);
146+
147+
// Verify metrics from actual server data
148+
await waitFor(() => {
149+
expect(screen.getByText('Total Contacts')).toBeInTheDocument();
150+
expect(screen.getByText(String(contacts.length))).toBeInTheDocument();
151+
});
152+
153+
expect(screen.getByText('Active Contacts')).toBeInTheDocument();
154+
expect(screen.getByText(String(activeContacts.length))).toBeInTheDocument();
155+
});
156+
});
157+
158+
describe('Widget Layout', () => {
159+
it('should render widgets with custom layout', () => {
160+
const schema: DashboardSchema = {
161+
type: 'dashboard',
162+
columns: 12,
163+
gap: 4,
164+
widgets: [
165+
{
166+
id: 'widget-full',
167+
title: 'Full Width Widget',
168+
component: {
169+
type: 'text',
170+
value: 'This widget spans the full width',
171+
},
172+
layout: { x: 0, y: 0, w: 12, h: 1 },
173+
},
174+
{
175+
id: 'widget-half-1',
176+
title: 'Half Width 1',
177+
component: {
178+
type: 'text',
179+
value: 'Half width',
180+
},
181+
layout: { x: 0, y: 1, w: 6, h: 1 },
182+
},
183+
{
184+
id: 'widget-half-2',
185+
title: 'Half Width 2',
186+
component: {
187+
type: 'text',
188+
value: 'Half width',
189+
},
190+
layout: { x: 6, y: 1, w: 6, h: 1 },
191+
},
192+
],
193+
};
194+
195+
render(<DashboardRenderer schema={schema} />);
196+
197+
expect(screen.getByText('Full Width Widget')).toBeInTheDocument();
198+
expect(screen.getByText('Half Width 1')).toBeInTheDocument();
199+
expect(screen.getByText('Half Width 2')).toBeInTheDocument();
200+
});
201+
202+
it('should render widgets without explicit layout', () => {
203+
const schema: DashboardSchema = {
204+
type: 'dashboard',
205+
columns: 3,
206+
widgets: [
207+
{
208+
id: 'widget-1',
209+
title: 'Widget Without Layout',
210+
component: {
211+
type: 'metric',
212+
label: 'Test',
213+
value: '123',
214+
},
215+
},
216+
],
217+
};
218+
219+
render(<DashboardRenderer schema={schema} />);
220+
221+
expect(screen.getByText('Widget Without Layout')).toBeInTheDocument();
222+
});
223+
});
224+
225+
describe('Nested Components', () => {
226+
it('should render dashboard with nested text components', () => {
227+
const schema: DashboardSchema = {
228+
type: 'dashboard',
229+
columns: 2,
230+
widgets: [
231+
{
232+
id: 'text-widget',
233+
title: 'Text Widget',
234+
component: {
235+
type: 'text',
236+
value: 'This is a text component inside a dashboard widget',
237+
},
238+
layout: { x: 0, y: 0, w: 2, h: 1 },
239+
},
240+
],
241+
};
242+
243+
render(<DashboardRenderer schema={schema} />);
244+
245+
expect(screen.getByText('Text Widget')).toBeInTheDocument();
246+
expect(screen.getByText('This is a text component inside a dashboard widget')).toBeInTheDocument();
247+
});
248+
});
249+
});

0 commit comments

Comments
 (0)