@auto-engineer/frontend-generator-react-graphql 1.7.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +40 -0
- package/dist/src/generator/generateComponents.d.ts.map +1 -1
- package/dist/src/generator/generateComponents.js +49 -4
- package/dist/src/generator/generateComponents.js.map +1 -1
- package/dist/src/generator/templates/component.ejs +75 -6
- package/dist/src/generator/templates/component.ejs.specs.js +3 -3
- package/dist/src/generator/templates/component.ejs.specs.js.map +1 -1
- package/dist/src/generator/templates/component.ejs.specs.ts +3 -3
- package/dist/src/generator/templates/page.ejs +108 -44
- package/dist/src/generator/templates/page.ejs.specs.js +169 -35
- package/dist/src/generator/templates/page.ejs.specs.js.map +1 -1
- package/dist/src/generator/templates/page.ejs.specs.ts +175 -35
- package/dist/src/generator/templates/template.ejs +196 -0
- package/dist/src/generator/type-guidance-builder.d.ts +1 -1
- package/dist/src/generator/type-guidance-builder.d.ts.map +1 -1
- package/dist/src/generator/type-guidance-builder.js +6 -6
- package/dist/src/generator/type-guidance-builder.js.map +1 -1
- package/dist/src/types.d.ts +10 -5
- package/dist/src/types.d.ts.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
|
@@ -1,16 +1,30 @@
|
|
|
1
|
-
<%
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
<%
|
|
2
|
+
// Convert template name to PascalCase for import (define at top for use throughout)
|
|
3
|
+
const templateComponentName = template ? template.split(/[-_\s]+/).map(w => w.charAt(0).toUpperCase() + w.slice(1)).join('') : 'div';
|
|
4
|
+
-%>
|
|
4
5
|
<% if (template) { -%>
|
|
5
|
-
import { <%=
|
|
6
|
+
import { <%= templateComponentName %> } from "@/components/templates/<%= template %>";
|
|
6
7
|
<% } -%>
|
|
7
8
|
<% if (typeGuidance && typeGuidance.imports && typeGuidance.imports.length > 0) { -%>
|
|
8
9
|
import { <%= typeGuidance.imports.join(', ') %> } from '@/gql/graphql';
|
|
9
10
|
<% } -%>
|
|
10
11
|
|
|
12
|
+
// PAGE: <%= name %>
|
|
13
|
+
// Route: <%= route %>
|
|
11
14
|
// <%= description %>
|
|
15
|
+
//
|
|
16
|
+
// Pages are specific instances of templates with real representative content.
|
|
17
|
+
// They populate the template's layout structure with actual data and behavior.
|
|
18
|
+
//
|
|
19
|
+
<% if (template) { -%>
|
|
20
|
+
// Template: <%= template %>
|
|
21
|
+
<% if (templateDescription) { -%>
|
|
22
|
+
// Template Purpose: <%= templateDescription %>
|
|
23
|
+
<% } -%>
|
|
24
|
+
<% } -%>
|
|
12
25
|
<% if (specs && specs.length) { -%>
|
|
13
|
-
//
|
|
26
|
+
//
|
|
27
|
+
// Page Specs:
|
|
14
28
|
<% specs.forEach(spec => { -%>
|
|
15
29
|
// - <%- spec %>
|
|
16
30
|
<% }) -%>
|
|
@@ -21,6 +35,11 @@ import { <%= typeGuidance.imports.join(', ') %> } from '@/gql/graphql';
|
|
|
21
35
|
// PAGE COMPOSITION - Spec Coverage Analysis
|
|
22
36
|
// ──────────────────────────────────────────────────────────────────────────────
|
|
23
37
|
//
|
|
38
|
+
// This page uses the "<%= template %>" template which contains:
|
|
39
|
+
<% organisms.forEach(org => { -%>
|
|
40
|
+
// - <%= org %>
|
|
41
|
+
<% }) -%>
|
|
42
|
+
//
|
|
24
43
|
<% if (specs && specs.length > 0) { -%>
|
|
25
44
|
// YOUR SPECS (what this page must accomplish):
|
|
26
45
|
//
|
|
@@ -28,39 +47,59 @@ import { <%= typeGuidance.imports.join(', ') %> } from '@/gql/graphql';
|
|
|
28
47
|
<% const coveredBy = organisms.find(org => organismSpecs[org]?.includes(spec)); -%>
|
|
29
48
|
// [<%= coveredBy ? '✓' : ' ' %>] <%= spec %>
|
|
30
49
|
<% if (coveredBy) { -%>
|
|
31
|
-
// └─ Implemented by <%= coveredBy %>
|
|
50
|
+
// └─ Implemented by <%= coveredBy %> (via template)
|
|
32
51
|
<% } -%>
|
|
33
52
|
<% }); -%>
|
|
34
53
|
//
|
|
35
54
|
<% } -%>
|
|
36
|
-
|
|
55
|
+
<% if (templateSpecs && templateSpecs.length > 0) { -%>
|
|
56
|
+
// TEMPLATE LAYOUT SPECS (handled by template):
|
|
57
|
+
//
|
|
58
|
+
<% templateSpecs.forEach(spec => { -%>
|
|
59
|
+
// • <%= spec %>
|
|
60
|
+
<% }); -%>
|
|
61
|
+
//
|
|
62
|
+
<% } -%>
|
|
63
|
+
// Available Organisms (via template):
|
|
37
64
|
//
|
|
38
65
|
<% organisms.forEach(org => { -%>
|
|
39
66
|
<% const orgSpecs = organismSpecs[org]; -%>
|
|
40
67
|
<% if (orgSpecs && orgSpecs.length > 0) { -%>
|
|
41
68
|
// **<%= org %>** capabilities:
|
|
42
|
-
<% orgSpecs.forEach(spec => { -%>
|
|
69
|
+
<% orgSpecs.slice(0, 5).forEach(spec => { -%>
|
|
43
70
|
// • <%= spec %>
|
|
44
71
|
<% }); -%>
|
|
72
|
+
<% if (orgSpecs.length > 5) { -%>
|
|
73
|
+
// • ... and <%= orgSpecs.length - 5 %> more specs
|
|
74
|
+
<% } -%>
|
|
45
75
|
//
|
|
46
76
|
<% } else { -%>
|
|
47
77
|
// **<%= org %>**
|
|
48
|
-
// (Organism with specific purpose
|
|
78
|
+
// (Organism with specific purpose)
|
|
49
79
|
//
|
|
50
80
|
<% } -%>
|
|
51
81
|
<% }); -%>
|
|
52
|
-
//
|
|
82
|
+
// Page Responsibilities:
|
|
53
83
|
//
|
|
54
|
-
// 1. ✓ Checked specs =
|
|
55
|
-
// 2. [ ] Unchecked specs = your responsibility
|
|
56
|
-
// 3.
|
|
84
|
+
// 1. ✓ Checked specs = implemented by organism in template
|
|
85
|
+
// 2. [ ] Unchecked specs = your responsibility to implement
|
|
86
|
+
// 3. Pages provide real data to populate template structure
|
|
87
|
+
// 4. Handle page-level navigation and routing
|
|
88
|
+
// 5. Manage page-specific state if needed
|
|
57
89
|
//
|
|
58
90
|
<% } -%>
|
|
91
|
+
<% if (navigation && navigation.length > 0) { -%>
|
|
92
|
+
//
|
|
93
|
+
// NAVIGATION:
|
|
94
|
+
<% navigation.forEach(nav => { -%>
|
|
95
|
+
// - On "<%= nav.on %>" → Navigate to <%= nav.to %>
|
|
96
|
+
<% }) -%>
|
|
97
|
+
<% } -%>
|
|
59
98
|
<% if (typeGuidance && (typeGuidance.queryGuidance.length > 0 || typeGuidance.mutationGuidance.length > 0 || typeGuidance.enumGuidance.length > 0)) { -%>
|
|
60
99
|
//
|
|
61
|
-
// CRITICAL - TYPE GUIDANCE -
|
|
100
|
+
// CRITICAL - TYPE GUIDANCE - Data requirements for this page
|
|
62
101
|
//
|
|
63
|
-
// The following operations are used by
|
|
102
|
+
// The following operations are used by organisms in this page's template.
|
|
64
103
|
// You can either:
|
|
65
104
|
// A) Fetch here and pass as props (recommended for shared data/mutations)
|
|
66
105
|
// B) Let each organism fetch its own data (simpler, but may duplicate queries)
|
|
@@ -81,35 +120,60 @@ import { <%= typeGuidance.imports.join(', ') %> } from '@/gql/graphql';
|
|
|
81
120
|
<% }) -%>
|
|
82
121
|
<% } -%>
|
|
83
122
|
//
|
|
84
|
-
//
|
|
85
|
-
//
|
|
86
|
-
//
|
|
87
|
-
//
|
|
88
|
-
//
|
|
89
|
-
// -
|
|
90
|
-
// -
|
|
91
|
-
// -
|
|
92
|
-
//
|
|
93
|
-
//
|
|
94
|
-
//
|
|
95
|
-
//
|
|
96
|
-
//
|
|
97
|
-
//
|
|
98
|
-
//
|
|
99
|
-
//
|
|
100
|
-
//
|
|
101
|
-
//
|
|
102
|
-
//
|
|
103
|
-
//
|
|
104
|
-
//
|
|
105
|
-
//
|
|
106
|
-
//
|
|
107
|
-
//
|
|
108
|
-
//
|
|
109
|
-
//
|
|
110
|
-
//
|
|
123
|
+
// ------------------------------------------------------------------------------
|
|
124
|
+
// PAGE-LEVEL CONSIDERATIONS & VISUAL REQUIREMENTS
|
|
125
|
+
// ------------------------------------------------------------------------------
|
|
126
|
+
//
|
|
127
|
+
// As a PAGE (not a template or component), this component:
|
|
128
|
+
// - Is the entry point for route "<%= route %>"
|
|
129
|
+
// - Should handle route parameters if needed
|
|
130
|
+
// - May coordinate data fetching for child organisms
|
|
131
|
+
// - Should handle page-level loading and error states
|
|
132
|
+
//
|
|
133
|
+
// VISUAL REQUIREMENTS FOR PAGES:
|
|
134
|
+
//
|
|
135
|
+
// LOADING STATES (use beautiful skeletons, not spinners):
|
|
136
|
+
// - Use Skeleton components that match the layout of actual content
|
|
137
|
+
// - Include skeletons for title, grid items, and other key elements
|
|
138
|
+
// - Maintain the same spacing and structure as loaded state
|
|
139
|
+
// - Example pattern:
|
|
140
|
+
// if (loading) return (
|
|
141
|
+
// <<%= template ? templateComponentName : 'div' %>>
|
|
142
|
+
// {/* Skeleton layout matching content structure */}
|
|
143
|
+
// </<%= template ? templateComponentName : 'div' %>>
|
|
144
|
+
// );
|
|
145
|
+
//
|
|
146
|
+
// ERROR STATES (user-friendly, not technical):
|
|
147
|
+
// - Center the error message vertically and horizontally
|
|
148
|
+
// - Include an error icon with appropriate styling
|
|
149
|
+
// - Use clear, non-technical language
|
|
150
|
+
// - Provide a recovery action (refresh, go back, etc.)
|
|
151
|
+
// - Example pattern:
|
|
152
|
+
// if (error) return (
|
|
153
|
+
// <<%= template ? templateComponentName : 'div' %>>
|
|
154
|
+
// {/* Centered error with icon, message, and action button */}
|
|
155
|
+
// </<%= template ? templateComponentName : 'div' %>>
|
|
156
|
+
// );
|
|
157
|
+
//
|
|
158
|
+
// IMPLEMENTATION PATTERN:
|
|
159
|
+
//
|
|
160
|
+
// 1. SIMPLE PAGE (template handles everything):
|
|
161
|
+
// return <<%= template ? templateComponentName : 'div' %> />;
|
|
162
|
+
//
|
|
163
|
+
// 2. PAGE WITH PROPS/CHILDREN (pass additional content):
|
|
164
|
+
// return (
|
|
165
|
+
// <<%= template ? templateComponentName : 'div' %>>
|
|
166
|
+
// {/* Additional page-specific content */}
|
|
167
|
+
// </<%= template ? templateComponentName : 'div' %>>
|
|
168
|
+
// );
|
|
169
|
+
//
|
|
170
|
+
// 3. PAGE WITH DATA FETCHING (with beautiful loading/error states):
|
|
171
|
+
// const { data, loading, error } = useQuery(SomeQuery);
|
|
172
|
+
// if (loading) return <PageSkeleton />; // Custom skeleton component
|
|
173
|
+
// if (error) return <PageError error={error} />; // Custom error component
|
|
174
|
+
// return <<%= template ? templateComponentName : 'div' %> data={data} />;
|
|
111
175
|
//
|
|
112
176
|
|
|
113
177
|
export function <%= name %>() {
|
|
114
|
-
return
|
|
115
|
-
}
|
|
178
|
+
return <<%= templateComponentName %> />;
|
|
179
|
+
}
|
|
@@ -7,33 +7,43 @@ const __filename = fileURLToPath(import.meta.url);
|
|
|
7
7
|
const __dirname = path.dirname(__filename);
|
|
8
8
|
const pageTemplate = fs.readFileSync(path.resolve(__dirname, 'page.ejs'), 'utf-8');
|
|
9
9
|
describe('page.ejs', () => {
|
|
10
|
-
it('should generate a basic page with
|
|
10
|
+
it('should generate a basic page with template and organisms', () => {
|
|
11
11
|
const content = ejs.render(pageTemplate, {
|
|
12
12
|
name: 'Dashboard',
|
|
13
13
|
description: 'Main dashboard page',
|
|
14
14
|
organisms: ['StatsOverview', 'ActivityFeed'],
|
|
15
|
-
template:
|
|
15
|
+
template: 'DashboardTemplate',
|
|
16
|
+
templateDescription: 'Dashboard layout with stats and activity',
|
|
17
|
+
templateSpecs: [],
|
|
18
|
+
route: '/dashboard',
|
|
19
|
+
navigation: [],
|
|
16
20
|
specs: [],
|
|
21
|
+
dataRequirements: [],
|
|
17
22
|
typeGuidance: { imports: [], queryGuidance: [], mutationGuidance: [], enumGuidance: [] },
|
|
18
23
|
organismSpecs: {},
|
|
19
24
|
});
|
|
20
|
-
expect(content).toContain('import {
|
|
21
|
-
expect(content).toContain('import { ActivityFeed } from "@/components/organisms/ActivityFeed";');
|
|
25
|
+
expect(content).toContain('import { DashboardTemplate } from "@/components/templates/DashboardTemplate";');
|
|
22
26
|
expect(content).toContain('// Main dashboard page');
|
|
23
27
|
expect(content).toContain('PAGE COMPOSITION');
|
|
24
28
|
expect(content).toContain('export function Dashboard()');
|
|
29
|
+
expect(content).toContain('return <DashboardTemplate />');
|
|
25
30
|
});
|
|
26
31
|
it('should generate a page with organisms and specs', () => {
|
|
27
32
|
const content = ejs.render(pageTemplate, {
|
|
28
33
|
name: 'AdminPanel',
|
|
29
34
|
description: 'Admin control panel',
|
|
30
35
|
organisms: ['UserManagement', 'SystemSettings'],
|
|
31
|
-
template:
|
|
36
|
+
template: 'AdminTemplate',
|
|
37
|
+
templateDescription: 'Admin layout template',
|
|
38
|
+
templateSpecs: [],
|
|
39
|
+
route: '/admin',
|
|
40
|
+
navigation: [],
|
|
32
41
|
specs: ['manage users', 'configure system'],
|
|
42
|
+
dataRequirements: [],
|
|
33
43
|
typeGuidance: { imports: [], queryGuidance: [], mutationGuidance: [], enumGuidance: [] },
|
|
34
44
|
organismSpecs: {},
|
|
35
45
|
});
|
|
36
|
-
expect(content).toContain('// Specs:');
|
|
46
|
+
expect(content).toContain('// Page Specs:');
|
|
37
47
|
expect(content).toContain('// - manage users');
|
|
38
48
|
expect(content).toContain('// - configure system');
|
|
39
49
|
expect(content).toContain('PAGE COMPOSITION');
|
|
@@ -43,8 +53,13 @@ describe('page.ejs', () => {
|
|
|
43
53
|
name: 'UserProfile',
|
|
44
54
|
description: 'User profile page',
|
|
45
55
|
organisms: ['ProfileHeader', 'ProfileDetails'],
|
|
46
|
-
template:
|
|
56
|
+
template: 'ProfileTemplate',
|
|
57
|
+
templateDescription: 'Profile page layout',
|
|
58
|
+
templateSpecs: [],
|
|
59
|
+
route: '/profile',
|
|
60
|
+
navigation: [],
|
|
47
61
|
specs: ['display user info', 'show activity history'],
|
|
62
|
+
dataRequirements: [],
|
|
48
63
|
typeGuidance: { imports: [], queryGuidance: [], mutationGuidance: [], enumGuidance: [] },
|
|
49
64
|
organismSpecs: {
|
|
50
65
|
ProfileHeader: ['display user info'],
|
|
@@ -53,29 +68,40 @@ describe('page.ejs', () => {
|
|
|
53
68
|
});
|
|
54
69
|
expect(content).toContain('YOUR SPECS (what this page must accomplish):');
|
|
55
70
|
expect(content).toContain('[✓] display user info');
|
|
56
|
-
expect(content).toContain('└─ Implemented by ProfileHeader');
|
|
71
|
+
expect(content).toContain('└─ Implemented by ProfileHeader (via template)');
|
|
57
72
|
expect(content).toContain('[✓] show activity history');
|
|
58
|
-
expect(content).toContain('└─ Implemented by ProfileDetails');
|
|
73
|
+
expect(content).toContain('└─ Implemented by ProfileDetails (via template)');
|
|
59
74
|
});
|
|
60
|
-
it('should generate a page with template import', () => {
|
|
75
|
+
it('should generate a page with template import using PascalCase conversion', () => {
|
|
61
76
|
const content = ejs.render(pageTemplate, {
|
|
62
77
|
name: 'Settings',
|
|
63
78
|
description: 'Settings page',
|
|
64
79
|
organisms: ['GeneralSettings'],
|
|
65
80
|
template: 'single-column-layout',
|
|
81
|
+
templateDescription: 'Single column layout template',
|
|
82
|
+
templateSpecs: [],
|
|
83
|
+
route: '/settings',
|
|
84
|
+
navigation: [],
|
|
66
85
|
specs: [],
|
|
86
|
+
dataRequirements: [],
|
|
67
87
|
typeGuidance: { imports: [], queryGuidance: [], mutationGuidance: [], enumGuidance: [] },
|
|
68
88
|
organismSpecs: {},
|
|
69
89
|
});
|
|
70
90
|
expect(content).toContain('import { SingleColumnLayout } from "@/components/templates/single-column-layout";');
|
|
91
|
+
expect(content).toContain('return <SingleColumnLayout />');
|
|
71
92
|
});
|
|
72
93
|
it('should generate a page with type guidance', () => {
|
|
73
94
|
const content = ejs.render(pageTemplate, {
|
|
74
95
|
name: 'TodoList',
|
|
75
96
|
description: 'Todo list page',
|
|
76
97
|
organisms: ['TodoGrid'],
|
|
77
|
-
template:
|
|
98
|
+
template: 'TodoTemplate',
|
|
99
|
+
templateDescription: 'Todo page layout',
|
|
100
|
+
templateSpecs: [],
|
|
101
|
+
route: '/todos',
|
|
102
|
+
navigation: [],
|
|
78
103
|
specs: ['display todos', 'filter by status'],
|
|
104
|
+
dataRequirements: [],
|
|
79
105
|
typeGuidance: {
|
|
80
106
|
imports: ['Todo', 'TodoStatus'],
|
|
81
107
|
queryGuidance: [
|
|
@@ -97,8 +123,13 @@ describe('page.ejs', () => {
|
|
|
97
123
|
name: 'TaskBoard',
|
|
98
124
|
description: 'Task management board',
|
|
99
125
|
organisms: ['TaskList', 'TaskFilters'],
|
|
100
|
-
template:
|
|
126
|
+
template: 'BoardTemplate',
|
|
127
|
+
templateDescription: 'Board layout template',
|
|
128
|
+
templateSpecs: [],
|
|
129
|
+
route: '/tasks',
|
|
130
|
+
navigation: [],
|
|
101
131
|
specs: ['display tasks', 'filter by status', 'sort by date', 'search tasks'],
|
|
132
|
+
dataRequirements: [],
|
|
102
133
|
typeGuidance: { imports: [], queryGuidance: [], mutationGuidance: [], enumGuidance: [] },
|
|
103
134
|
organismSpecs: {
|
|
104
135
|
TaskList: ['display tasks', 'sort by date'],
|
|
@@ -106,21 +137,25 @@ describe('page.ejs', () => {
|
|
|
106
137
|
},
|
|
107
138
|
});
|
|
108
139
|
expect(content).toContain('[✓] display tasks');
|
|
109
|
-
expect(content).toContain('└─ Implemented by TaskList');
|
|
140
|
+
expect(content).toContain('└─ Implemented by TaskList (via template)');
|
|
110
141
|
expect(content).toContain('[✓] filter by status');
|
|
111
|
-
expect(content).toContain('└─ Implemented by TaskFilters');
|
|
142
|
+
expect(content).toContain('└─ Implemented by TaskFilters (via template)');
|
|
112
143
|
expect(content).toContain('[✓] sort by date');
|
|
113
|
-
expect(content).toContain('└─ Implemented by TaskList');
|
|
144
|
+
expect(content).toContain('└─ Implemented by TaskList (via template)');
|
|
114
145
|
expect(content).toContain('[ ] search tasks');
|
|
115
|
-
expect(content).not.toContain('search tasks\n// └─');
|
|
116
146
|
});
|
|
117
147
|
it('should generate a page with NO spec coverage', () => {
|
|
118
148
|
const content = ejs.render(pageTemplate, {
|
|
119
149
|
name: 'Analytics',
|
|
120
150
|
description: 'Analytics dashboard',
|
|
121
151
|
organisms: ['ChartPanel', 'MetricsPanel'],
|
|
122
|
-
template:
|
|
152
|
+
template: 'AnalyticsTemplate',
|
|
153
|
+
templateDescription: 'Analytics layout template',
|
|
154
|
+
templateSpecs: [],
|
|
155
|
+
route: '/analytics',
|
|
156
|
+
navigation: [],
|
|
123
157
|
specs: ['show revenue chart', 'display user metrics', 'export data'],
|
|
158
|
+
dataRequirements: [],
|
|
124
159
|
typeGuidance: { imports: [], queryGuidance: [], mutationGuidance: [], enumGuidance: [] },
|
|
125
160
|
organismSpecs: {
|
|
126
161
|
ChartPanel: ['render line chart', 'render bar chart'],
|
|
@@ -131,15 +166,19 @@ describe('page.ejs', () => {
|
|
|
131
166
|
expect(content).toContain('[ ] display user metrics');
|
|
132
167
|
expect(content).toContain('[ ] export data');
|
|
133
168
|
expect(content).not.toContain('[✓]');
|
|
134
|
-
expect(content).not.toContain('└─ Implemented by');
|
|
135
169
|
});
|
|
136
170
|
it('should generate a page with mutation guidance', () => {
|
|
137
171
|
const content = ejs.render(pageTemplate, {
|
|
138
172
|
name: 'CreateItem',
|
|
139
173
|
description: 'Create item page',
|
|
140
174
|
organisms: ['ItemForm'],
|
|
141
|
-
template:
|
|
175
|
+
template: 'FormTemplate',
|
|
176
|
+
templateDescription: 'Form page layout',
|
|
177
|
+
templateSpecs: [],
|
|
178
|
+
route: '/items/new',
|
|
179
|
+
navigation: [],
|
|
142
180
|
specs: ['collect item data', 'submit item'],
|
|
181
|
+
dataRequirements: [],
|
|
143
182
|
typeGuidance: {
|
|
144
183
|
imports: ['CreateItemInput'],
|
|
145
184
|
queryGuidance: [],
|
|
@@ -152,15 +191,20 @@ describe('page.ejs', () => {
|
|
|
152
191
|
});
|
|
153
192
|
expect(content).toContain('CRITICAL - TYPE GUIDANCE');
|
|
154
193
|
expect(content).toContain('Mutation - CreateItem:');
|
|
155
|
-
expect(content).toContain('
|
|
194
|
+
expect(content).toContain('Data requirements for this page');
|
|
156
195
|
});
|
|
157
196
|
it('should generate a page with only mutation guidance (no queries)', () => {
|
|
158
197
|
const content = ejs.render(pageTemplate, {
|
|
159
198
|
name: 'DeleteConfirmation',
|
|
160
199
|
description: 'Delete confirmation page',
|
|
161
200
|
organisms: ['ConfirmDialog'],
|
|
162
|
-
template:
|
|
201
|
+
template: 'DialogTemplate',
|
|
202
|
+
templateDescription: 'Dialog layout template',
|
|
203
|
+
templateSpecs: [],
|
|
204
|
+
route: '/delete/:id',
|
|
205
|
+
navigation: [],
|
|
163
206
|
specs: ['confirm deletion'],
|
|
207
|
+
dataRequirements: [],
|
|
164
208
|
typeGuidance: {
|
|
165
209
|
imports: [],
|
|
166
210
|
queryGuidance: [],
|
|
@@ -181,7 +225,12 @@ describe('page.ejs', () => {
|
|
|
181
225
|
description: 'Product catalog page',
|
|
182
226
|
organisms: ['ProductGrid', 'ProductFilters'],
|
|
183
227
|
template: 'two-column-layout',
|
|
228
|
+
templateDescription: 'Two column layout with sidebar',
|
|
229
|
+
templateSpecs: ['responsive sidebar', 'main content area'],
|
|
230
|
+
route: '/products',
|
|
231
|
+
navigation: [],
|
|
184
232
|
specs: ['display products', 'filter products'],
|
|
233
|
+
dataRequirements: [],
|
|
185
234
|
typeGuidance: {
|
|
186
235
|
imports: ['Product'],
|
|
187
236
|
queryGuidance: [
|
|
@@ -199,30 +248,59 @@ describe('page.ejs', () => {
|
|
|
199
248
|
expect(content).toContain('CRITICAL - TYPE GUIDANCE');
|
|
200
249
|
expect(content).toContain('[✓] display products');
|
|
201
250
|
expect(content).toContain('[✓] filter products');
|
|
251
|
+
expect(content).toContain('return <TwoColumnLayout />');
|
|
202
252
|
});
|
|
203
|
-
it('should include
|
|
253
|
+
it('should include template specs when available', () => {
|
|
254
|
+
const content = ejs.render(pageTemplate, {
|
|
255
|
+
name: 'DashboardPage',
|
|
256
|
+
description: 'Main dashboard',
|
|
257
|
+
organisms: ['Header', 'Content'],
|
|
258
|
+
template: 'DashboardLayout',
|
|
259
|
+
templateDescription: 'Dashboard layout with header and content areas',
|
|
260
|
+
templateSpecs: ['fixed header', 'scrollable content area', 'responsive sidebar'],
|
|
261
|
+
route: '/',
|
|
262
|
+
navigation: [],
|
|
263
|
+
specs: [],
|
|
264
|
+
dataRequirements: [],
|
|
265
|
+
typeGuidance: { imports: [], queryGuidance: [], mutationGuidance: [], enumGuidance: [] },
|
|
266
|
+
organismSpecs: {},
|
|
267
|
+
});
|
|
268
|
+
expect(content).toContain('TEMPLATE LAYOUT SPECS (handled by template):');
|
|
269
|
+
expect(content).toContain('• fixed header');
|
|
270
|
+
expect(content).toContain('• scrollable content area');
|
|
271
|
+
expect(content).toContain('• responsive sidebar');
|
|
272
|
+
});
|
|
273
|
+
it('should include page-level considerations', () => {
|
|
204
274
|
const content = ejs.render(pageTemplate, {
|
|
205
275
|
name: 'SimpleList',
|
|
206
276
|
description: 'Simple list page',
|
|
207
277
|
organisms: ['ItemList'],
|
|
208
|
-
template:
|
|
278
|
+
template: 'ListTemplate',
|
|
279
|
+
templateDescription: 'List page layout',
|
|
280
|
+
templateSpecs: [],
|
|
281
|
+
route: '/items',
|
|
282
|
+
navigation: [],
|
|
209
283
|
specs: ['show items'],
|
|
284
|
+
dataRequirements: [],
|
|
210
285
|
typeGuidance: { imports: [], queryGuidance: [], mutationGuidance: [], enumGuidance: [] },
|
|
211
286
|
organismSpecs: {},
|
|
212
287
|
});
|
|
213
|
-
expect(content).toContain('
|
|
214
|
-
expect(content).toContain('
|
|
215
|
-
expect(content).toContain('
|
|
216
|
-
expect(content).toContain('TEXT SIZING: Use responsive classes');
|
|
217
|
-
expect(content).toContain('MENTAL TEST: Would this work in a 320px wide container?');
|
|
288
|
+
expect(content).toContain('PAGE-LEVEL CONSIDERATIONS & VISUAL REQUIREMENTS');
|
|
289
|
+
expect(content).toContain('Is the entry point for route "/items"');
|
|
290
|
+
expect(content).toContain('IMPLEMENTATION PATTERN');
|
|
218
291
|
});
|
|
219
292
|
it('should handle organisms without organismSpecs data', () => {
|
|
220
293
|
const content = ejs.render(pageTemplate, {
|
|
221
294
|
name: 'MixedPage',
|
|
222
295
|
description: 'Page with documented and undocumented organisms',
|
|
223
296
|
organisms: ['DocumentedOrg', 'UndocumentedOrg'],
|
|
224
|
-
template:
|
|
297
|
+
template: 'MixedTemplate',
|
|
298
|
+
templateDescription: 'Mixed content template',
|
|
299
|
+
templateSpecs: [],
|
|
300
|
+
route: '/mixed',
|
|
301
|
+
navigation: [],
|
|
225
302
|
specs: ['feature A', 'feature B'],
|
|
303
|
+
dataRequirements: [],
|
|
226
304
|
typeGuidance: { imports: [], queryGuidance: [], mutationGuidance: [], enumGuidance: [] },
|
|
227
305
|
organismSpecs: {
|
|
228
306
|
DocumentedOrg: ['feature A'],
|
|
@@ -231,7 +309,29 @@ describe('page.ejs', () => {
|
|
|
231
309
|
expect(content).toContain('**DocumentedOrg** capabilities:');
|
|
232
310
|
expect(content).toContain('• feature A');
|
|
233
311
|
expect(content).toContain('**UndocumentedOrg**');
|
|
234
|
-
expect(content).toContain('(Organism with specific purpose
|
|
312
|
+
expect(content).toContain('(Organism with specific purpose)');
|
|
313
|
+
});
|
|
314
|
+
it('should include navigation information', () => {
|
|
315
|
+
const content = ejs.render(pageTemplate, {
|
|
316
|
+
name: 'ProductPage',
|
|
317
|
+
description: 'Product detail page',
|
|
318
|
+
organisms: ['ProductDetails'],
|
|
319
|
+
template: 'DetailTemplate',
|
|
320
|
+
templateDescription: 'Detail page layout',
|
|
321
|
+
templateSpecs: [],
|
|
322
|
+
route: '/product/:id',
|
|
323
|
+
navigation: [
|
|
324
|
+
{ on: 'Click Back', to: 'ProductListPage' },
|
|
325
|
+
{ on: 'Click Buy', to: 'CheckoutPage' },
|
|
326
|
+
],
|
|
327
|
+
specs: [],
|
|
328
|
+
dataRequirements: [],
|
|
329
|
+
typeGuidance: { imports: [], queryGuidance: [], mutationGuidance: [], enumGuidance: [] },
|
|
330
|
+
organismSpecs: {},
|
|
331
|
+
});
|
|
332
|
+
expect(content).toContain('NAVIGATION:');
|
|
333
|
+
expect(content).toContain('On "Click Back" → Navigate to ProductListPage');
|
|
334
|
+
expect(content).toContain('On "Click Buy" → Navigate to CheckoutPage');
|
|
235
335
|
});
|
|
236
336
|
describe('Edge Cases', () => {
|
|
237
337
|
it('should handle empty organisms array', () => {
|
|
@@ -239,26 +339,37 @@ describe('page.ejs', () => {
|
|
|
239
339
|
name: 'EmptyPage',
|
|
240
340
|
description: 'Page without organisms',
|
|
241
341
|
organisms: [],
|
|
242
|
-
template:
|
|
342
|
+
template: 'EmptyTemplate',
|
|
343
|
+
templateDescription: 'Empty page layout',
|
|
344
|
+
templateSpecs: [],
|
|
345
|
+
route: '/empty',
|
|
346
|
+
navigation: [],
|
|
243
347
|
specs: [],
|
|
348
|
+
dataRequirements: [],
|
|
244
349
|
typeGuidance: { imports: [], queryGuidance: [], mutationGuidance: [], enumGuidance: [] },
|
|
245
350
|
organismSpecs: {},
|
|
246
351
|
});
|
|
247
352
|
expect(content).not.toContain('PAGE COMPOSITION');
|
|
248
|
-
expect(content).not.toContain('
|
|
353
|
+
expect(content).not.toContain('Page Responsibilities');
|
|
249
354
|
expect(content).toContain('export function EmptyPage()');
|
|
355
|
+
expect(content).toContain('return <EmptyTemplate />');
|
|
250
356
|
});
|
|
251
357
|
it('should handle undefined specs gracefully', () => {
|
|
252
358
|
const content = ejs.render(pageTemplate, {
|
|
253
359
|
name: 'NoSpecs',
|
|
254
360
|
description: 'Page without specs',
|
|
255
361
|
organisms: ['SomeOrganism'],
|
|
256
|
-
template:
|
|
362
|
+
template: 'BasicTemplate',
|
|
363
|
+
templateDescription: 'Basic layout',
|
|
364
|
+
templateSpecs: [],
|
|
365
|
+
route: '/no-specs',
|
|
366
|
+
navigation: [],
|
|
257
367
|
specs: undefined,
|
|
368
|
+
dataRequirements: [],
|
|
258
369
|
typeGuidance: { imports: [], queryGuidance: [], mutationGuidance: [], enumGuidance: [] },
|
|
259
370
|
organismSpecs: {},
|
|
260
371
|
});
|
|
261
|
-
expect(content).not.toContain('// Specs:');
|
|
372
|
+
expect(content).not.toContain('// Page Specs:');
|
|
262
373
|
expect(content).not.toContain('YOUR SPECS');
|
|
263
374
|
expect(content).toContain('PAGE COMPOSITION');
|
|
264
375
|
});
|
|
@@ -267,8 +378,13 @@ describe('page.ejs', () => {
|
|
|
267
378
|
name: 'EmptySpecs',
|
|
268
379
|
description: 'Organisms with no documented specs',
|
|
269
380
|
organisms: ['Org1', 'Org2'],
|
|
270
|
-
template:
|
|
381
|
+
template: 'BasicTemplate',
|
|
382
|
+
templateDescription: 'Basic layout',
|
|
383
|
+
templateSpecs: [],
|
|
384
|
+
route: '/empty-specs',
|
|
385
|
+
navigation: [],
|
|
271
386
|
specs: ['feature X', 'feature Y'],
|
|
387
|
+
dataRequirements: [],
|
|
272
388
|
typeGuidance: { imports: [], queryGuidance: [], mutationGuidance: [], enumGuidance: [] },
|
|
273
389
|
organismSpecs: {
|
|
274
390
|
Org1: [],
|
|
@@ -279,6 +395,24 @@ describe('page.ejs', () => {
|
|
|
279
395
|
expect(content).toContain('[ ] feature Y');
|
|
280
396
|
expect(content).not.toContain('[✓]');
|
|
281
397
|
});
|
|
398
|
+
it('should handle page without template (fallback to div)', () => {
|
|
399
|
+
const content = ejs.render(pageTemplate, {
|
|
400
|
+
name: 'NoTemplatePage',
|
|
401
|
+
description: 'Page without template',
|
|
402
|
+
organisms: [],
|
|
403
|
+
template: undefined,
|
|
404
|
+
templateDescription: '',
|
|
405
|
+
templateSpecs: [],
|
|
406
|
+
route: '/no-template',
|
|
407
|
+
navigation: [],
|
|
408
|
+
specs: [],
|
|
409
|
+
dataRequirements: [],
|
|
410
|
+
typeGuidance: { imports: [], queryGuidance: [], mutationGuidance: [], enumGuidance: [] },
|
|
411
|
+
organismSpecs: {},
|
|
412
|
+
});
|
|
413
|
+
expect(content).not.toContain('import {');
|
|
414
|
+
expect(content).toContain('return <div />');
|
|
415
|
+
});
|
|
282
416
|
});
|
|
283
417
|
});
|
|
284
418
|
//# sourceMappingURL=page.ejs.specs.js.map
|