Skip to content

Commit 29f0b9a

Browse files
committed
feat: add testing strategy documentation and initial spec compliance tests for Kitchen Sink
1 parent 34b3649 commit 29f0b9a

File tree

2 files changed

+140
-0
lines changed

2 files changed

+140
-0
lines changed

TESTING_STRATEGY.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# ObjectStack Testing Strategy
2+
3+
This document outlines the testing strategy designed to ensure the reliability and spec-compliance of the ObjectStack UI ecosystem. The goal is to enable **AI-Driven Development** where the test suite acts as a strict guardrail and feedback loop.
4+
5+
## 1. Philosophy: Validating the "Protocol"
6+
7+
ObjectUI is a **renderer**. Its primary job is to faithfully translate a JSON Protocol (`@objectstack/spec`) into a UI. Therefore, our tests must be structured around verifying this translation Contract.
8+
9+
### The "AI Feedback Loop" Principle
10+
Tests should be descriptive enough that an AI Agent reading a failure message can understand:
11+
1. Which part of the Spec failed.
12+
2. What the JSON input was.
13+
3. What the expected DOM output was.
14+
15+
## 2. Test Layers
16+
17+
### A. Spec Compliance Tests (The Contract)
18+
* **Location**: `apps/console/src/__tests__/SpecCompliance.test.tsx` (and similar in packages)
19+
* **Purpose**: To verify that **every** field type and layout definition in the Spec renders correctly in the reference app (Console).
20+
* **Methodology**:
21+
* Iterate over the "Kitchen Sink" schema (which contains one of everything).
22+
* Render the component.
23+
* Assert semantic HTML attributes (e.g., `input[type="password"]` for `type: "password"`).
24+
* **Crucial**: These tests ensure that adding a new feature to the spec doesn't break existing renderers.
25+
26+
### B. Component Unit Tests (The Atoms)
27+
* **Location**: `packages/components/src/__tests__`
28+
* **Purpose**: Test individual UI primitives (e.g., `Text`, `Badge`, `Card`) in isolation.
29+
* **Methodology**:
30+
* Test `className` merging (Tailwind).
31+
* Test property mapping (JSON props -> React props).
32+
* Test event handling.
33+
34+
### C. Logic/kernel Tests (The Brain)
35+
* **Location**: `packages/core`, `packages/react`
36+
* **Purpose**: Test the non-visual logic.
37+
* **Methodology**:
38+
* Expression evaluation (`visible: "${data.age > 18}"`).
39+
* Data context scoping.
40+
* Action dispatching.
41+
42+
## 3. The "Kitchen Sink" as the Gold Standard
43+
The `examples/kitchen-sink` package is not just a demo; it is the **Conformance Suite**.
44+
* **Rule**: If a feature exists in ObjectStack, it MUST be represented in the Kitchen Sink.
45+
* **CI**: The Console tests import the Kitchen Sink schema and run assertions against it.
46+
47+
## 4. Implementation Guide (For Developers & AI)
48+
49+
When implementing a new feature:
50+
51+
1. **Update the Spec**: Add the type to `@object-ui/types` or `@objectstack/spec`.
52+
2. **Update the Kitchen Sink**: Add an example usage in `examples/kitchen-sink`.
53+
3. **Run Compliance Tests**: `pnpm test` in `apps/console`. It should fail (missing renderer).
54+
4. **Implement Renderer**: Add the code in `@object-ui/components`.
55+
5. **Pass Tests**: Compliance tests pass when the new element is rendered correctly.
56+
57+
## 5. Directory Structure for Tests
58+
59+
```
60+
apps/console/src/__tests__/
61+
├── SpecCompliance.test.tsx # <-- The "Grand Unified Test" using Kitchen Sink
62+
├── AppStructure.test.tsx # Tests routing and shell
63+
└── ...
64+
65+
packages/components/src/__tests__/
66+
├── atoms/ # Button, Badge, etc.
67+
├── fields/ # Input, Select, etc.
68+
└── ...
69+
```
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
2+
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
3+
import { render, screen, waitFor } from '@testing-library/react';
4+
import '@testing-library/jest-dom';
5+
import { KitchenSinkObject } from '@object-ui/example-kitchen-sink/objects/kitchen_sink.object';
6+
import { ObjectForm } from '@object-ui/plugin-form';
7+
import { SchemaRendererProvider } from '@object-ui/react';
8+
import { SchemaRegistry } from '@objectstack/objectql/registry'; // Direct import for test setup
9+
10+
// Mocks
11+
const mockDataSource = {
12+
find: async () => [],
13+
findOne: async () => ({
14+
name: 'Test Name',
15+
amount: 100,
16+
active: true
17+
}),
18+
insert: async () => ({}),
19+
update: async () => ({}),
20+
delete: async () => ({}),
21+
};
22+
23+
describe('Spec Compliance: Kitchen Sink', () => {
24+
25+
beforeAll(() => {
26+
// Register the kitchen sink object so the form can find it
27+
// The ObjectForm component typically calls the protocol to get metadata.
28+
// We need to ensure the "protocol" used by the Component matches what we register here.
29+
// However, ObjectForm typically makes HTTP calls or uses a client.
30+
// For unit tests, we usually mock the data source hook.
31+
32+
// This is a simplified check of the Schema content itself for now.
33+
});
34+
35+
describe('Field Type Rendering', () => {
36+
// We will iterate over the fields defined in the kitchen sink schema
37+
// and verify they produce a corresponding DOM element.
38+
39+
const fields = KitchenSinkObject.fields || {};
40+
41+
it('should have fields defined', () => {
42+
expect(Object.keys(fields).length).toBeGreaterThan(0);
43+
});
44+
45+
// Loop through common types and verify they render generically
46+
// Note: In a real environment, we'd render the whole form once in beforeAll
47+
// but for unit testing isolation we might render per block or check fragments.
48+
49+
it('should render correct inputs for standard types', async () => {
50+
render(
51+
<TestWrapper>
52+
<ObjectForm
53+
schema={{
54+
type: 'form',
55+
object: 'kitchen_sink'
56+
}}
57+
// We need to inject the object definition manually since we aren't running the full kernel here
58+
// In a real app the kernel/registry handles this.
59+
// For this test, we might rely on the plugin-form's ability to take definition directly
60+
// OR mock the registry lookup.
61+
// Assuming ObjectForm primarily fetches metadata from registry.
62+
/>
63+
</TestWrapper>
64+
);
65+
66+
// Note: Since ObjectForm fetches metadata asynchronously, we might need to mock the registry response
67+
// or pass the definition in a prop if supported.
68+
// If ObjectForm ONLY supports fetching by name, we need to mock the DataSource/Registry.
69+
});
70+
});
71+
});

0 commit comments

Comments
 (0)