Skip to content

Commit 8d41a4b

Browse files
Copilothotlong
andcommitted
test: add accessorKey inference tests and update ROADMAP.md
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
1 parent e3c1079 commit 8d41a4b

2 files changed

Lines changed: 137 additions & 2 deletions

File tree

ROADMAP.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -988,6 +988,9 @@ The `FlowDesigner` is a canvas-based flow editor that bridges the gap between th
988988
- [x] ISO datetime fallback: ObjectGrid `inferColumnType()` now detects ISO 8601 datetime strings (`YYYY-MM-DDTHH:MM`) in data values as a catch-all for fields whose names don't match date/datetime patterns.
989989
- [x] Date/datetime human-friendly display: `DateCellRenderer` (relative format) and `DateTimeCellRenderer` (split date/time) already registered in field registry for all grid/table views.
990990
- [x] Currency/status/boolean renderers: Already implemented with proper formatting (currency symbol, Badge colors, checkbox display).
991+
- [x] **accessorKey-format type inference**: `generateColumns()` in ObjectGrid now applies `inferColumnType()` + `getCellRenderer()` to `accessorKey`-format columns that don't already have a `cell` renderer. Previously, `accessorKey` columns bypassed the entire inference pipeline, showing raw dates, plain text status/priority, and raw numbers for progress.
992+
- [x] **humanizeLabel() utility**: New `humanizeLabel()` export in `@object-ui/fields` converts snake_case/kebab-case values to Title Case (e.g., `in_progress``In Progress`). Used as fallback in `SelectCellRenderer` when no explicit `option.label` exists.
993+
- [x] **PercentCellRenderer progress-field normalization**: `PercentCellRenderer` now uses field name to disambiguate 0-1 fraction vs 0-100 whole number — fields matching `/progress|completion/` treat values as already in 0-100 range (e.g., `75``75%`), while other fields like `probability` still treat `0.75``75%`.
991994

992995
**Header & Breadcrumb i18n:**
993996
- [x] AppHeader breadcrumb labels (`Dashboards`, `Pages`, `Reports`, `System`) now use `t()` translation via `useObjectTranslation`.
@@ -997,8 +1000,8 @@ The `FlowDesigner` is a canvas-based flow editor that bridges the gap between th
9971000

9981001
**Tests:**
9991002
- [x] 46 NavigationRenderer tests passing (pin/favorites/search/reorder)
1000-
- [x] 75 field cell renderer tests passing (date/datetime/select/boolean/percent)
1001-
- [x] 263 ObjectGrid tests passing (inference, rendering, accessibility)
1003+
- [x] 86 field cell renderer tests passing (date/datetime/select/boolean/percent/humanizeLabel/progress)
1004+
- [x] 293 ObjectGrid tests passing (inference, rendering, accessibility, accessorKey-format inference)
10021005
- [x] 28 DataTable tests passing
10031006
- [x] 78 Layout tests passing (NavigationRenderer + AppSchemaRenderer)
10041007
- [x] 11 AppSidebar tests passing
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
/**
2+
* AccessorKey Inference Tests
3+
*
4+
* Tests that accessorKey-format columns receive type inference
5+
* via inferColumnType() + getCellRenderer(), matching the behavior
6+
* of ListColumn (field) format columns.
7+
*/
8+
import { describe, it, expect } from 'vitest';
9+
import { render, screen, waitFor } from '@testing-library/react';
10+
import '@testing-library/jest-dom';
11+
import React from 'react';
12+
import { ObjectGrid } from '../ObjectGrid';
13+
import { registerAllFields } from '@object-ui/fields';
14+
import { ActionProvider } from '@object-ui/react';
15+
16+
registerAllFields();
17+
18+
// --- Mock Data with various types ---
19+
const mockData = [
20+
{
21+
_id: '1',
22+
name: 'Project Alpha',
23+
status: 'in_progress',
24+
priority: 'high',
25+
progress: 75,
26+
start_date: '2024-02-01T00:00:00.000Z',
27+
},
28+
{
29+
_id: '2',
30+
name: 'Project Beta',
31+
status: 'completed',
32+
priority: 'low',
33+
progress: 100,
34+
start_date: '2024-03-15T00:00:00.000Z',
35+
},
36+
];
37+
38+
// Helper: Render ObjectGrid with accessorKey-format columns
39+
function renderAccessorGrid(columns: any[], data?: any[]) {
40+
const schema: any = {
41+
type: 'object-grid' as const,
42+
objectName: 'test_object',
43+
columns,
44+
data: { provider: 'value', items: data || mockData },
45+
};
46+
47+
return render(
48+
<ActionProvider>
49+
<ObjectGrid schema={schema} />
50+
</ActionProvider>
51+
);
52+
}
53+
54+
// =========================================================================
55+
// 1. accessorKey columns get type inference
56+
// =========================================================================
57+
describe('accessorKey-format: type inference', () => {
58+
it('should infer select type for status field and render badges', async () => {
59+
renderAccessorGrid([
60+
{ header: 'Name', accessorKey: 'name' },
61+
{ header: 'Status', accessorKey: 'status' },
62+
]);
63+
64+
await waitFor(() => {
65+
expect(screen.getByText('Name')).toBeInTheDocument();
66+
});
67+
68+
// Status should be inferred as select and render humanized badges
69+
expect(screen.getByText('In Progress')).toBeInTheDocument();
70+
expect(screen.getByText('Completed')).toBeInTheDocument();
71+
});
72+
73+
it('should infer date type for date fields', async () => {
74+
renderAccessorGrid([
75+
{ header: 'Name', accessorKey: 'name' },
76+
{ header: 'Start Date', accessorKey: 'start_date' },
77+
]);
78+
79+
await waitFor(() => {
80+
expect(screen.getByText('Name')).toBeInTheDocument();
81+
});
82+
83+
// Date fields should NOT show raw ISO strings
84+
expect(screen.queryByText('2024-02-01T00:00:00.000Z')).not.toBeInTheDocument();
85+
});
86+
87+
it('should infer percent type for progress field and render progress bar', async () => {
88+
renderAccessorGrid([
89+
{ header: 'Name', accessorKey: 'name' },
90+
{ header: 'Progress', accessorKey: 'progress' },
91+
]);
92+
93+
await waitFor(() => {
94+
expect(screen.getByText('Name')).toBeInTheDocument();
95+
});
96+
97+
// Progress should render as percentage with progress bar
98+
expect(screen.getByText('75%')).toBeInTheDocument();
99+
const bars = screen.getAllByRole('progressbar');
100+
expect(bars.length).toBeGreaterThan(0);
101+
});
102+
103+
it('should NOT override columns that already have a cell renderer', async () => {
104+
const customRenderer = (value: any) => <span data-testid="custom">{value}-custom</span>;
105+
renderAccessorGrid([
106+
{ header: 'Name', accessorKey: 'name' },
107+
{ header: 'Status', accessorKey: 'status', cell: customRenderer },
108+
]);
109+
110+
await waitFor(() => {
111+
expect(screen.getByText('Name')).toBeInTheDocument();
112+
});
113+
114+
// Custom renderer should be preserved
115+
expect(screen.getByText('in_progress-custom')).toBeInTheDocument();
116+
});
117+
118+
it('should pass through columns with explicit type', async () => {
119+
renderAccessorGrid([
120+
{ header: 'Name', accessorKey: 'name' },
121+
{ header: 'Priority', accessorKey: 'priority', type: 'select' },
122+
]);
123+
124+
await waitFor(() => {
125+
expect(screen.getByText('Name')).toBeInTheDocument();
126+
});
127+
128+
// Priority with explicit select type should render as humanized badge
129+
expect(screen.getByText('High')).toBeInTheDocument();
130+
expect(screen.getByText('Low')).toBeInTheDocument();
131+
});
132+
});

0 commit comments

Comments
 (0)