@hustle-together/api-dev-tools 3.12.3 → 4.5.1
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/.claude/adr-requests/.gitkeep +10 -0
- package/.claude/agents/adr-researcher.md +109 -0
- package/.claude/agents/visual-analyzer.md +183 -0
- package/.claude/api-dev-state.json +7 -463
- package/.claude/documentation-audit.json +114 -0
- package/.claude/registry.json +289 -0
- package/.claude/settings.json +45 -1
- package/.claude/workflow-logs/None.json +49 -0
- package/.claude/workflow-logs/session-20251230-143727.json +106 -0
- package/.skills/adr-deep-research/SKILL.md +351 -0
- package/.skills/api-create/SKILL.md +116 -17
- package/.skills/api-research/SKILL.md +130 -0
- package/.skills/docs-sync/SKILL.md +260 -0
- package/.skills/docs-update/SKILL.md +205 -0
- package/.skills/hustle-brand/SKILL.md +368 -0
- package/.skills/hustle-build/SKILL.md +786 -0
- package/.skills/hustle-build-review/SKILL.md +518 -0
- package/.skills/parallel-spawn/SKILL.md +212 -0
- package/.skills/ralph-continue/SKILL.md +151 -0
- package/.skills/ralph-loop/SKILL.md +341 -0
- package/.skills/ralph-status/SKILL.md +87 -0
- package/.skills/refactor/SKILL.md +59 -0
- package/.skills/shadcn/SKILL.md +522 -0
- package/.skills/test-all/SKILL.md +210 -0
- package/.skills/test-builds/SKILL.md +208 -0
- package/.skills/test-debug/SKILL.md +212 -0
- package/.skills/test-e2e/SKILL.md +168 -0
- package/.skills/test-review/SKILL.md +707 -0
- package/.skills/test-unit/SKILL.md +143 -0
- package/.skills/test-visual/SKILL.md +301 -0
- package/.skills/token-report/SKILL.md +132 -0
- package/CHANGELOG.md +575 -0
- package/README.md +426 -56
- package/bin/cli.js +1538 -88
- package/commands/hustle-api-create.md +22 -0
- package/commands/hustle-build.md +259 -0
- package/commands/hustle-combine.md +81 -2
- package/commands/hustle-ui-create-page.md +84 -2
- package/commands/hustle-ui-create.md +82 -2
- package/hooks/__pycache__/api-workflow-check.cpython-314.pyc +0 -0
- package/hooks/__pycache__/auto-answer.cpython-314.pyc +0 -0
- package/hooks/__pycache__/cache-research.cpython-314.pyc +0 -0
- package/hooks/__pycache__/check-api-routes.cpython-314.pyc +0 -0
- package/hooks/__pycache__/check-playwright-setup.cpython-314.pyc +0 -0
- package/hooks/__pycache__/check-storybook-setup.cpython-314.pyc +0 -0
- package/hooks/__pycache__/check-update.cpython-314.pyc +0 -0
- package/hooks/__pycache__/completion-promise-detector.cpython-314.pyc +0 -0
- package/hooks/__pycache__/context-capacity-warning.cpython-314.pyc +0 -0
- package/hooks/__pycache__/detect-interruption.cpython-314.pyc +0 -0
- package/hooks/__pycache__/docs-update-check.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-a11y-audit.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-brand-guide.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-component-type-confirm.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-deep-research.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-disambiguation.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-documentation.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-dry-run.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-environment.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-external-research.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-freshness.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-interview.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-page-components.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-page-data-schema.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-questions-sourced.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-refactor.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-research.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-schema-from-interview.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-schema.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-scope.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-tdd-red.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-ui-disambiguation.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-ui-interview.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-verify.cpython-314.pyc +0 -0
- package/hooks/__pycache__/generate-adr-options.cpython-314.pyc +0 -0
- package/hooks/__pycache__/generate-manifest-entry.cpython-314.pyc +0 -0
- package/hooks/__pycache__/hook_utils.cpython-314.pyc +0 -0
- package/hooks/__pycache__/notify-input-needed.cpython-314.pyc +0 -0
- package/hooks/__pycache__/notify-phase-complete.cpython-314.pyc +0 -0
- package/hooks/__pycache__/ntfy-on-question.cpython-314.pyc +0 -0
- package/hooks/__pycache__/orchestrator-completion.cpython-314.pyc +0 -0
- package/hooks/__pycache__/orchestrator-handoff.cpython-314.pyc +0 -0
- package/hooks/__pycache__/orchestrator-session-startup.cpython-314.pyc +0 -0
- package/hooks/__pycache__/parallel-orchestrator.cpython-314.pyc +0 -0
- package/hooks/__pycache__/periodic-reground.cpython-314.pyc +0 -0
- package/hooks/__pycache__/project-document-prompt.cpython-314.pyc +0 -0
- package/hooks/__pycache__/remote-question-proxy.cpython-314.pyc +0 -0
- package/hooks/__pycache__/remote-question-server.cpython-314.pyc +0 -0
- package/hooks/__pycache__/run-code-review.cpython-314.pyc +0 -0
- package/hooks/__pycache__/run-visual-qa.cpython-314.pyc +0 -0
- package/hooks/__pycache__/session-logger.cpython-314.pyc +0 -0
- package/hooks/__pycache__/session-startup.cpython-314.pyc +0 -0
- package/hooks/__pycache__/track-scope-coverage.cpython-314.pyc +0 -0
- package/hooks/__pycache__/track-token-usage.cpython-314.pyc +0 -0
- package/hooks/__pycache__/track-tool-use.cpython-314.pyc +0 -0
- package/hooks/__pycache__/update-adr-decision.cpython-314.pyc +0 -0
- package/hooks/__pycache__/update-api-showcase.cpython-314.pyc +0 -0
- package/hooks/__pycache__/update-registry.cpython-314.pyc +0 -0
- package/hooks/__pycache__/update-ui-showcase.cpython-314.pyc +0 -0
- package/hooks/__pycache__/verify-after-green.cpython-314.pyc +0 -0
- package/hooks/__pycache__/verify-implementation.cpython-314.pyc +0 -0
- package/hooks/api-workflow-check.py +34 -0
- package/hooks/auto-answer.py +305 -0
- package/hooks/check-update.py +132 -0
- package/hooks/completion-promise-detector.py +293 -0
- package/hooks/context-capacity-warning.py +171 -0
- package/hooks/docs-update-check.py +120 -0
- package/hooks/enforce-dry-run.py +134 -0
- package/hooks/enforce-external-research.py +25 -0
- package/hooks/enforce-interview.py +20 -0
- package/hooks/generate-adr-options.py +282 -0
- package/hooks/hook_utils.py +609 -0
- package/hooks/lib/__pycache__/__init__.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/greptile.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/ntfy.cpython-314.pyc +0 -0
- package/hooks/ntfy-on-question.py +240 -0
- package/hooks/orchestrator-completion.py +313 -0
- package/hooks/orchestrator-handoff.py +267 -0
- package/hooks/orchestrator-session-startup.py +146 -0
- package/hooks/parallel-orchestrator.py +451 -0
- package/hooks/periodic-reground.py +270 -67
- package/hooks/project-document-prompt.py +302 -0
- package/hooks/remote-question-proxy.py +284 -0
- package/hooks/remote-question-server.py +1224 -0
- package/hooks/run-code-review.py +176 -29
- package/hooks/run-visual-qa.py +338 -0
- package/hooks/session-logger.py +27 -1
- package/hooks/session-startup.py +113 -0
- package/hooks/update-adr-decision.py +236 -0
- package/hooks/update-api-showcase.py +13 -1
- package/hooks/update-testing-checklist.py +195 -0
- package/hooks/update-ui-showcase.py +13 -1
- package/package.json +7 -3
- package/scripts/extract-schema-docs.cjs +322 -0
- package/templates/.skills/hustle-interview/SKILL.md +174 -0
- package/templates/CLAUDE-SECTION.md +89 -64
- package/templates/adr-viewer/_components/ADRViewer.tsx +326 -0
- package/templates/api-dev-state.json +33 -1
- package/templates/api-showcase/_components/APIModal.tsx +100 -8
- package/templates/api-showcase/_components/APIShowcase.tsx +36 -4
- package/templates/api-showcase/_components/APITester.tsx +367 -58
- package/templates/brand-page/page.tsx +645 -0
- package/templates/component/Component.visual.spec.ts +30 -24
- package/templates/docs/page.tsx +230 -0
- package/templates/eslint-plugin-zod-schema/index.js +446 -0
- package/templates/eslint-plugin-zod-schema/package.json +26 -0
- package/templates/github-workflows/security.yml +274 -0
- package/templates/hustle-build-defaults.json +136 -0
- package/templates/hustle-dev-dashboard/page.tsx +365 -0
- package/templates/page/page.e2e.test.ts +30 -26
- package/templates/performance-budgets.json +63 -5
- package/templates/playwright-report/page.tsx +258 -0
- package/templates/registry.json +279 -3
- package/templates/review-dashboard/page.tsx +510 -0
- package/templates/settings.json +155 -7
- package/templates/test-results/page.tsx +237 -0
- package/templates/typedoc.json +19 -0
- package/templates/ui-showcase/_components/UIShowcase.tsx +48 -1
- package/templates/ui-showcase/_components/VisualTestingDashboard.tsx +579 -0
- package/templates/ui-showcase/page.tsx +1 -1
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import Link from "next/link";
|
|
4
|
+
import { useMemo } from "react";
|
|
5
|
+
|
|
6
|
+
// Import registry for stats
|
|
7
|
+
import registryData from "@/../.claude/registry.json";
|
|
8
|
+
|
|
9
|
+
interface Registry {
|
|
10
|
+
version: string;
|
|
11
|
+
apis: Record<string, unknown>;
|
|
12
|
+
components?: Record<string, unknown>;
|
|
13
|
+
pages?: Record<string, unknown>;
|
|
14
|
+
combined?: Record<string, unknown>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Hustle Dev Dashboard
|
|
19
|
+
*
|
|
20
|
+
* Central hub linking to all development tools and showcases.
|
|
21
|
+
* Provides quick access to:
|
|
22
|
+
* - API Showcase & Documentation
|
|
23
|
+
* - UI Showcase & Storybook
|
|
24
|
+
* - Test Results
|
|
25
|
+
* - TypeDoc API Documentation
|
|
26
|
+
*
|
|
27
|
+
* Created with Hustle API Dev Tools (v3.12.11)
|
|
28
|
+
*/
|
|
29
|
+
export default function HustleDevDashboard() {
|
|
30
|
+
const registry: Registry = registryData || {
|
|
31
|
+
version: "1.0.0",
|
|
32
|
+
apis: {},
|
|
33
|
+
components: {},
|
|
34
|
+
pages: {},
|
|
35
|
+
combined: {},
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const stats = useMemo(() => {
|
|
39
|
+
return {
|
|
40
|
+
apis: Object.keys(registry.apis || {}).length,
|
|
41
|
+
combined: Object.keys(registry.combined || {}).length,
|
|
42
|
+
components: Object.keys(registry.components || {}).length,
|
|
43
|
+
pages: Object.keys(registry.pages || {}).length,
|
|
44
|
+
};
|
|
45
|
+
}, [registry]);
|
|
46
|
+
|
|
47
|
+
const totalAPIs = stats.apis + stats.combined;
|
|
48
|
+
const totalUI = stats.components + stats.pages;
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<div className="min-h-screen bg-white dark:bg-gray-950">
|
|
52
|
+
{/* Header */}
|
|
53
|
+
<header className="border-b-4 border-black bg-[#BA0C2F] px-6 py-8 dark:border-gray-600">
|
|
54
|
+
<div className="mx-auto max-w-6xl">
|
|
55
|
+
<h1 className="text-3xl font-black text-white">
|
|
56
|
+
HUSTLE DEV DASHBOARD
|
|
57
|
+
</h1>
|
|
58
|
+
<p className="mt-2 text-white/80">
|
|
59
|
+
Central hub for all development tools and showcases
|
|
60
|
+
</p>
|
|
61
|
+
</div>
|
|
62
|
+
</header>
|
|
63
|
+
|
|
64
|
+
{/* Main Content */}
|
|
65
|
+
<main className="mx-auto max-w-6xl px-6 py-8">
|
|
66
|
+
{/* Stats Overview */}
|
|
67
|
+
<div className="mb-8 grid gap-4 sm:grid-cols-4">
|
|
68
|
+
<StatCard label="APIs" value={stats.apis} />
|
|
69
|
+
<StatCard label="Combined" value={stats.combined} />
|
|
70
|
+
<StatCard label="Components" value={stats.components} />
|
|
71
|
+
<StatCard label="Pages" value={stats.pages} />
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
{/* Links Grid */}
|
|
75
|
+
<div className="grid gap-6 md:grid-cols-2">
|
|
76
|
+
{/* APIs Section */}
|
|
77
|
+
<DashboardSection
|
|
78
|
+
icon="📡"
|
|
79
|
+
title="APIs"
|
|
80
|
+
description={`${totalAPIs} endpoint${totalAPIs !== 1 ? "s" : ""} registered`}
|
|
81
|
+
>
|
|
82
|
+
<DashboardLink
|
|
83
|
+
href="/api-showcase"
|
|
84
|
+
title="API Showcase"
|
|
85
|
+
description="Interactive API testing and documentation"
|
|
86
|
+
primary
|
|
87
|
+
/>
|
|
88
|
+
<DashboardLink
|
|
89
|
+
href="/docs/api"
|
|
90
|
+
title="API Documentation"
|
|
91
|
+
description="TypeDoc-generated API reference"
|
|
92
|
+
/>
|
|
93
|
+
</DashboardSection>
|
|
94
|
+
|
|
95
|
+
{/* UI Section */}
|
|
96
|
+
<DashboardSection
|
|
97
|
+
icon="🧩"
|
|
98
|
+
title="Components & Pages"
|
|
99
|
+
description={`${totalUI} item${totalUI !== 1 ? "s" : ""} registered`}
|
|
100
|
+
>
|
|
101
|
+
<DashboardLink
|
|
102
|
+
href="/ui-showcase"
|
|
103
|
+
title="UI Showcase"
|
|
104
|
+
description="Component gallery with live previews"
|
|
105
|
+
primary
|
|
106
|
+
/>
|
|
107
|
+
<DashboardLink
|
|
108
|
+
href="http://localhost:6006"
|
|
109
|
+
title="Storybook"
|
|
110
|
+
description="Component development environment"
|
|
111
|
+
external
|
|
112
|
+
/>
|
|
113
|
+
</DashboardSection>
|
|
114
|
+
|
|
115
|
+
{/* Testing Section */}
|
|
116
|
+
<DashboardSection
|
|
117
|
+
icon="🧪"
|
|
118
|
+
title="Testing"
|
|
119
|
+
description="Test results and coverage reports"
|
|
120
|
+
>
|
|
121
|
+
<DashboardLink
|
|
122
|
+
href="/test-results"
|
|
123
|
+
title="Test Results"
|
|
124
|
+
description="Vitest unit test results"
|
|
125
|
+
/>
|
|
126
|
+
<DashboardLink
|
|
127
|
+
href="/playwright-report"
|
|
128
|
+
title="Playwright Reports"
|
|
129
|
+
description="E2E test results and screenshots"
|
|
130
|
+
/>
|
|
131
|
+
</DashboardSection>
|
|
132
|
+
|
|
133
|
+
{/* Documentation Section */}
|
|
134
|
+
<DashboardSection
|
|
135
|
+
icon="📚"
|
|
136
|
+
title="Documentation"
|
|
137
|
+
description="Project documentation and guides"
|
|
138
|
+
>
|
|
139
|
+
<DashboardLink
|
|
140
|
+
href="/docs"
|
|
141
|
+
title="TypeDoc Documentation"
|
|
142
|
+
description="Auto-generated code documentation"
|
|
143
|
+
/>
|
|
144
|
+
<DashboardLink
|
|
145
|
+
href="https://github.com/hustle-together/api-dev-tools"
|
|
146
|
+
title="GitHub Repository"
|
|
147
|
+
description="Source code and README"
|
|
148
|
+
external
|
|
149
|
+
/>
|
|
150
|
+
</DashboardSection>
|
|
151
|
+
</div>
|
|
152
|
+
|
|
153
|
+
{/* Workflow Status */}
|
|
154
|
+
<div className="mt-8 border-2 border-black bg-gray-50 p-6 dark:border-gray-600 dark:bg-gray-900">
|
|
155
|
+
<h2 className="mb-4 text-lg font-bold text-black dark:text-white">
|
|
156
|
+
Workflow Status
|
|
157
|
+
</h2>
|
|
158
|
+
<div className="grid gap-3 sm:grid-cols-2">
|
|
159
|
+
<StatusItem
|
|
160
|
+
label="TypeDoc"
|
|
161
|
+
status="ready"
|
|
162
|
+
command="pnpm typedoc"
|
|
163
|
+
description="Auto-generated on build"
|
|
164
|
+
/>
|
|
165
|
+
<StatusItem
|
|
166
|
+
label="Unit Tests"
|
|
167
|
+
status="ready"
|
|
168
|
+
command="pnpm test"
|
|
169
|
+
description="Run during TDD phases"
|
|
170
|
+
/>
|
|
171
|
+
<StatusItem
|
|
172
|
+
label="Storybook"
|
|
173
|
+
status="manual"
|
|
174
|
+
command="pnpm storybook"
|
|
175
|
+
description="Start server manually"
|
|
176
|
+
/>
|
|
177
|
+
<StatusItem
|
|
178
|
+
label="E2E Tests"
|
|
179
|
+
status="ready"
|
|
180
|
+
command="pnpm test:e2e"
|
|
181
|
+
description="Run during verification"
|
|
182
|
+
/>
|
|
183
|
+
</div>
|
|
184
|
+
<p className="mt-4 text-xs text-gray-500 dark:text-gray-400">
|
|
185
|
+
Most commands run automatically during workflows. Manual items require server startup.
|
|
186
|
+
</p>
|
|
187
|
+
</div>
|
|
188
|
+
</main>
|
|
189
|
+
|
|
190
|
+
{/* Footer */}
|
|
191
|
+
<footer className="border-t-2 border-black px-6 py-4 dark:border-gray-600">
|
|
192
|
+
<div className="mx-auto max-w-6xl text-center text-sm text-gray-600 dark:text-gray-400">
|
|
193
|
+
Built with{" "}
|
|
194
|
+
<a
|
|
195
|
+
href="https://github.com/hustle-together/api-dev-tools"
|
|
196
|
+
className="font-bold text-[#BA0C2F] hover:underline"
|
|
197
|
+
>
|
|
198
|
+
Hustle API Dev Tools
|
|
199
|
+
</a>
|
|
200
|
+
</div>
|
|
201
|
+
</footer>
|
|
202
|
+
</div>
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function StatCard({ label, value }: { label: string; value: number }) {
|
|
207
|
+
return (
|
|
208
|
+
<div className="border-2 border-black bg-white p-4 dark:border-gray-600 dark:bg-gray-900">
|
|
209
|
+
<p className="text-3xl font-black text-[#BA0C2F]">{value}</p>
|
|
210
|
+
<p className="text-sm text-gray-600 dark:text-gray-400">{label}</p>
|
|
211
|
+
</div>
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function DashboardSection({
|
|
216
|
+
icon,
|
|
217
|
+
title,
|
|
218
|
+
description,
|
|
219
|
+
children,
|
|
220
|
+
}: {
|
|
221
|
+
icon: string;
|
|
222
|
+
title: string;
|
|
223
|
+
description: string;
|
|
224
|
+
children: React.ReactNode;
|
|
225
|
+
}) {
|
|
226
|
+
return (
|
|
227
|
+
<div className="border-2 border-black dark:border-gray-600">
|
|
228
|
+
<div className="border-b-2 border-black bg-gray-50 px-4 py-3 dark:border-gray-600 dark:bg-gray-800">
|
|
229
|
+
<div className="flex items-center gap-2">
|
|
230
|
+
<span className="text-xl">{icon}</span>
|
|
231
|
+
<div>
|
|
232
|
+
<h2 className="font-bold text-black dark:text-white">{title}</h2>
|
|
233
|
+
<p className="text-xs text-gray-600 dark:text-gray-400">
|
|
234
|
+
{description}
|
|
235
|
+
</p>
|
|
236
|
+
</div>
|
|
237
|
+
</div>
|
|
238
|
+
</div>
|
|
239
|
+
<div className="divide-y divide-gray-200 bg-white dark:divide-gray-700 dark:bg-gray-900">
|
|
240
|
+
{children}
|
|
241
|
+
</div>
|
|
242
|
+
</div>
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function DashboardLink({
|
|
247
|
+
href,
|
|
248
|
+
title,
|
|
249
|
+
description,
|
|
250
|
+
primary,
|
|
251
|
+
external,
|
|
252
|
+
}: {
|
|
253
|
+
href: string;
|
|
254
|
+
title: string;
|
|
255
|
+
description: string;
|
|
256
|
+
primary?: boolean;
|
|
257
|
+
external?: boolean;
|
|
258
|
+
}) {
|
|
259
|
+
const LinkComponent = external ? "a" : Link;
|
|
260
|
+
const externalProps = external
|
|
261
|
+
? { target: "_blank", rel: "noopener noreferrer" }
|
|
262
|
+
: {};
|
|
263
|
+
|
|
264
|
+
return (
|
|
265
|
+
<LinkComponent
|
|
266
|
+
href={href}
|
|
267
|
+
className={`block px-4 py-3 transition-colors hover:bg-gray-50 dark:hover:bg-gray-800 ${
|
|
268
|
+
primary ? "bg-[#BA0C2F]/5" : ""
|
|
269
|
+
}`}
|
|
270
|
+
{...externalProps}
|
|
271
|
+
>
|
|
272
|
+
<div className="flex items-center justify-between">
|
|
273
|
+
<div>
|
|
274
|
+
<p
|
|
275
|
+
className={`font-medium ${
|
|
276
|
+
primary
|
|
277
|
+
? "text-[#BA0C2F]"
|
|
278
|
+
: "text-black dark:text-white"
|
|
279
|
+
}`}
|
|
280
|
+
>
|
|
281
|
+
{title}
|
|
282
|
+
{external && (
|
|
283
|
+
<span className="ml-1 text-xs text-gray-400">↗</span>
|
|
284
|
+
)}
|
|
285
|
+
</p>
|
|
286
|
+
<p className="text-xs text-gray-600 dark:text-gray-400">
|
|
287
|
+
{description}
|
|
288
|
+
</p>
|
|
289
|
+
</div>
|
|
290
|
+
<svg
|
|
291
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
292
|
+
width="16"
|
|
293
|
+
height="16"
|
|
294
|
+
viewBox="0 0 24 24"
|
|
295
|
+
fill="none"
|
|
296
|
+
stroke="currentColor"
|
|
297
|
+
strokeWidth="2"
|
|
298
|
+
strokeLinecap="round"
|
|
299
|
+
strokeLinejoin="round"
|
|
300
|
+
className="text-gray-400"
|
|
301
|
+
>
|
|
302
|
+
<polyline points="9 18 15 12 9 6" />
|
|
303
|
+
</svg>
|
|
304
|
+
</div>
|
|
305
|
+
</LinkComponent>
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function StatusItem({
|
|
310
|
+
label,
|
|
311
|
+
status,
|
|
312
|
+
command,
|
|
313
|
+
description,
|
|
314
|
+
}: {
|
|
315
|
+
label: string;
|
|
316
|
+
status: "ready" | "manual" | "running" | "error";
|
|
317
|
+
command: string;
|
|
318
|
+
description: string;
|
|
319
|
+
}) {
|
|
320
|
+
const statusConfig = {
|
|
321
|
+
ready: {
|
|
322
|
+
bg: "bg-green-100 dark:bg-green-900/30",
|
|
323
|
+
text: "text-green-700 dark:text-green-400",
|
|
324
|
+
label: "Auto",
|
|
325
|
+
icon: "✓",
|
|
326
|
+
},
|
|
327
|
+
manual: {
|
|
328
|
+
bg: "bg-yellow-100 dark:bg-yellow-900/30",
|
|
329
|
+
text: "text-yellow-700 dark:text-yellow-400",
|
|
330
|
+
label: "Manual",
|
|
331
|
+
icon: "⚡",
|
|
332
|
+
},
|
|
333
|
+
running: {
|
|
334
|
+
bg: "bg-blue-100 dark:bg-blue-900/30",
|
|
335
|
+
text: "text-blue-700 dark:text-blue-400",
|
|
336
|
+
label: "Running",
|
|
337
|
+
icon: "◐",
|
|
338
|
+
},
|
|
339
|
+
error: {
|
|
340
|
+
bg: "bg-red-100 dark:bg-red-900/30",
|
|
341
|
+
text: "text-red-700 dark:text-red-400",
|
|
342
|
+
label: "Error",
|
|
343
|
+
icon: "✗",
|
|
344
|
+
},
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
const config = statusConfig[status];
|
|
348
|
+
|
|
349
|
+
return (
|
|
350
|
+
<div className="rounded border border-gray-200 bg-white p-3 dark:border-gray-700 dark:bg-gray-800">
|
|
351
|
+
<div className="flex items-center justify-between">
|
|
352
|
+
<span className="font-medium text-black dark:text-white">{label}</span>
|
|
353
|
+
<span
|
|
354
|
+
className={`rounded px-2 py-0.5 text-xs font-medium ${config.bg} ${config.text}`}
|
|
355
|
+
>
|
|
356
|
+
{config.icon} {config.label}
|
|
357
|
+
</span>
|
|
358
|
+
</div>
|
|
359
|
+
<code className="mt-1 block text-xs text-[#BA0C2F]">{command}</code>
|
|
360
|
+
<p className="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
|
361
|
+
{description}
|
|
362
|
+
</p>
|
|
363
|
+
</div>
|
|
364
|
+
);
|
|
365
|
+
}
|
|
@@ -37,36 +37,40 @@ test.describe("__PAGE_NAME__ Page", () => {
|
|
|
37
37
|
});
|
|
38
38
|
|
|
39
39
|
// ===================================
|
|
40
|
-
// Responsive Tests
|
|
40
|
+
// Responsive Tests (7 Viewports)
|
|
41
41
|
// ===================================
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
43
|
+
// All 7 viewports from performance-budgets.json
|
|
44
|
+
const viewports = [
|
|
45
|
+
{ name: "mobile-portrait", width: 375, height: 667 },
|
|
46
|
+
{ name: "mobile-notch", width: 393, height: 852 },
|
|
47
|
+
{ name: "mobile-landscape", width: 667, height: 375 },
|
|
48
|
+
{ name: "tablet-portrait", width: 768, height: 1024 },
|
|
49
|
+
{ name: "tablet-landscape", width: 1024, height: 768 },
|
|
50
|
+
{ name: "small-desktop", width: 1280, height: 720 },
|
|
51
|
+
{ name: "desktop", width: 1920, height: 1080 },
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
for (const viewport of viewports) {
|
|
55
|
+
test(`should be responsive on ${viewport.name} (${viewport.width}x${viewport.height})`, async ({
|
|
56
|
+
page,
|
|
57
|
+
}) => {
|
|
58
|
+
await page.setViewportSize({
|
|
59
|
+
width: viewport.width,
|
|
60
|
+
height: viewport.height,
|
|
61
|
+
});
|
|
62
|
+
await page.goto("/__PAGE_ROUTE__");
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
await page.goto("/__PAGE_ROUTE__");
|
|
64
|
+
// Verify main content is visible
|
|
65
|
+
await expect(page.getByRole("main")).toBeVisible();
|
|
67
66
|
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
// Verify no horizontal scroll
|
|
68
|
+
const body = await page.locator("body");
|
|
69
|
+
const scrollWidth = await body.evaluate((el) => el.scrollWidth);
|
|
70
|
+
const clientWidth = await body.evaluate((el) => el.clientWidth);
|
|
71
|
+
expect(scrollWidth).toBeLessThanOrEqual(clientWidth + 1);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
70
74
|
|
|
71
75
|
// ===================================
|
|
72
76
|
// Accessibility Tests
|
|
@@ -48,11 +48,69 @@
|
|
|
48
48
|
},
|
|
49
49
|
|
|
50
50
|
"responsive": {
|
|
51
|
-
"description": "Responsive breakpoints to test",
|
|
51
|
+
"description": "Responsive breakpoints to test (7 comprehensive viewports)",
|
|
52
52
|
"breakpoints": [
|
|
53
|
-
{
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
53
|
+
{
|
|
54
|
+
"name": "mobile-portrait",
|
|
55
|
+
"width": 375,
|
|
56
|
+
"height": 667,
|
|
57
|
+
"description": "iPhone SE baseline",
|
|
58
|
+
"safeArea": null
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"name": "mobile-notch",
|
|
62
|
+
"width": 393,
|
|
63
|
+
"height": 852,
|
|
64
|
+
"description": "iPhone 14 Pro with notch and home indicator",
|
|
65
|
+
"safeArea": {
|
|
66
|
+
"top": 47,
|
|
67
|
+
"bottom": 34,
|
|
68
|
+
"left": 0,
|
|
69
|
+
"right": 0
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"name": "mobile-landscape",
|
|
74
|
+
"width": 667,
|
|
75
|
+
"height": 375,
|
|
76
|
+
"description": "iPhone rotated landscape",
|
|
77
|
+
"safeArea": null
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"name": "tablet-portrait",
|
|
81
|
+
"width": 768,
|
|
82
|
+
"height": 1024,
|
|
83
|
+
"description": "iPad Mini baseline",
|
|
84
|
+
"safeArea": null
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"name": "tablet-landscape",
|
|
88
|
+
"width": 1024,
|
|
89
|
+
"height": 768,
|
|
90
|
+
"description": "iPad Mini rotated",
|
|
91
|
+
"safeArea": null
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"name": "small-desktop",
|
|
95
|
+
"width": 1280,
|
|
96
|
+
"height": 720,
|
|
97
|
+
"description": "Laptop screens, 720p displays",
|
|
98
|
+
"safeArea": null
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
"name": "desktop",
|
|
102
|
+
"width": 1920,
|
|
103
|
+
"height": 1080,
|
|
104
|
+
"description": "Standard desktop 1080p",
|
|
105
|
+
"safeArea": null
|
|
106
|
+
}
|
|
107
|
+
],
|
|
108
|
+
"safeAreaCSS": {
|
|
109
|
+
"description": "CSS environment variables for safe area insets",
|
|
110
|
+
"top": "env(safe-area-inset-top, 0px)",
|
|
111
|
+
"bottom": "env(safe-area-inset-bottom, 0px)",
|
|
112
|
+
"left": "env(safe-area-inset-left, 0px)",
|
|
113
|
+
"right": "env(safe-area-inset-right, 0px)"
|
|
114
|
+
}
|
|
57
115
|
}
|
|
58
116
|
}
|