@mandujs/core 0.53.2 → 0.54.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/README.md +654 -654
- package/package.json +2 -2
- package/src/a11y/__tests__/run-audit.test.ts +333 -333
- package/src/a11y/fix-hints.ts +76 -76
- package/src/a11y/index.ts +18 -18
- package/src/a11y/run-audit.ts +394 -394
- package/src/a11y/types.ts +125 -125
- package/src/auth/__tests__/login.test.ts +1 -1
- package/src/auth/__tests__/password.test.ts +122 -122
- package/src/auth/__tests__/tokens.test.ts +274 -274
- package/src/auth/__tests__/verification.test.ts +274 -274
- package/src/auth/index.ts +76 -76
- package/src/auth/login.ts +225 -225
- package/src/auth/password.ts +120 -120
- package/src/auth/reset.ts +243 -243
- package/src/auth/tokens.ts +612 -612
- package/src/auth/verification.ts +253 -253
- package/src/brain/__tests__/redactor.test.ts +94 -94
- package/src/brain/adapters/__tests__/_helpers.ts +83 -83
- package/src/brain/adapters/__tests__/anthropic-oauth.test.ts +196 -196
- package/src/brain/adapters/__tests__/chatgpt-auth.test.ts +193 -193
- package/src/brain/adapters/__tests__/openai-oauth.test.ts +209 -209
- package/src/brain/adapters/__tests__/resolver.test.ts +143 -143
- package/src/brain/adapters/anthropic-oauth.ts +1 -1
- package/src/brain/adapters/chatgpt-auth.ts +300 -300
- package/src/brain/adapters/index.ts +319 -319
- package/src/brain/adapters/oauth-flow.ts +439 -439
- package/src/brain/consent.ts +240 -240
- package/src/brain/credentials.ts +396 -396
- package/src/bundler/__tests__/build-runner.ts +113 -113
- package/src/bundler/__tests__/dev-reliability.test.ts +619 -619
- package/src/bundler/__tests__/extended-watch.test.ts +711 -711
- package/src/bundler/__tests__/fast-refresh.test.ts +10 -10
- package/src/bundler/__tests__/hdr.test.ts +353 -353
- package/src/bundler/__tests__/hmr-client.test.ts +532 -532
- package/src/bundler/__tests__/manifest-schema.test.ts +266 -266
- package/src/bundler/__tests__/prod-smoke.test.ts +138 -138
- package/src/bundler/__tests__/reverse-import-graph.test.ts +519 -519
- package/src/bundler/__tests__/slot-dispatch.test.ts +573 -573
- package/src/bundler/__tests__/url-cap-and-slot-regex.test.ts +286 -286
- package/src/bundler/__tests__/vendor-cache.test.ts +455 -455
- package/src/bundler/budget.ts +404 -404
- package/src/bundler/build.test.ts +179 -179
- package/src/bundler/build.ts +55 -55
- package/src/bundler/dev.ts +42 -42
- package/src/bundler/manifest-schema.ts +301 -301
- package/src/bundler/plugins/__tests__/block-generated-imports.test.ts +263 -263
- package/src/bundler/plugins/__tests__/react-compiler-config.test.ts +83 -83
- package/src/bundler/plugins/__tests__/react-compiler-lint.test.ts +110 -110
- package/src/bundler/plugins/block-generated-imports.ts +155 -155
- package/src/bundler/plugins/index.ts +83 -83
- package/src/bundler/plugins/react-compiler-config.ts +108 -108
- package/src/bundler/plugins/react-compiler-lint.ts +253 -253
- package/src/bundler/plugins/react-compiler.ts +162 -162
- package/src/bundler/reverse-import-graph.ts +339 -339
- package/src/bundler/safe-build.test.ts +201 -201
- package/src/bundler/safe-build.ts +103 -103
- package/src/bundler/scenario-matrix.ts +229 -229
- package/src/bundler/types.ts +10 -10
- package/src/bundler/vendor-cache-types.ts +130 -130
- package/src/bundler/vendor-cache.ts +526 -526
- package/src/change/snapshot.ts +18 -18
- package/src/client/Form.tsx +105 -105
- package/src/client/Link.tsx +9 -9
- package/src/client/__tests__/use-sse.test.ts +153 -153
- package/src/client/globals.ts +1 -1
- package/src/client/hooks.ts +362 -362
- package/src/client/hydrate.ts +340 -340
- package/src/client/prefetch-helper.ts +55 -55
- package/src/client/router.ts +11 -11
- package/src/client/runtime.ts +47 -47
- package/src/client/serialize.ts +404 -404
- package/src/client/use-fetch.ts +6 -6
- package/src/client/use-head.ts +197 -197
- package/src/client/use-sse.ts +378 -378
- package/src/client/window-state.ts +101 -101
- package/src/components/Image.tsx +162 -162
- package/src/config/validate.ts +3 -3
- package/src/config/watcher.ts +311 -311
- package/src/constants.ts +40 -40
- package/src/content/collection.ts +8 -8
- package/src/content/content-layer.ts +7 -7
- package/src/content/data-store.ts +245 -245
- package/src/content/frontmatter.ts +189 -189
- package/src/content/loader-context.ts +171 -171
- package/src/content/loaders/api.ts +216 -216
- package/src/content/loaders/file.ts +172 -172
- package/src/content/loaders/glob.ts +253 -253
- package/src/content/loaders/index.ts +34 -34
- package/src/content/loaders/types.ts +137 -137
- package/src/content/meta-store.ts +209 -209
- package/src/content/prebuild.test.ts +571 -571
- package/src/content/prebuild.ts +636 -636
- package/src/content/schema.ts +20 -20
- package/src/content/sidebar.ts +630 -630
- package/src/content/slug.ts +110 -110
- package/src/content/types.ts +282 -282
- package/src/content/watcher.ts +135 -135
- package/src/contract/client-safe.test.ts +42 -42
- package/src/contract/client-safe.ts +114 -114
- package/src/contract/define.ts +11 -11
- package/src/contract/index.ts +1 -1
- package/src/contract/normalize.test.ts +276 -276
- package/src/contract/normalize.ts +410 -410
- package/src/contract/registry.test.ts +206 -206
- package/src/contract/route-helpers.ts +1 -1
- package/src/contract/rpc.ts +443 -443
- package/src/contract/schema.ts +48 -48
- package/src/contract/types.ts +58 -58
- package/src/db/__tests__/db.test.ts +482 -482
- package/src/db/index.ts +138 -138
- package/src/db/migrations/history-table.ts +345 -345
- package/src/db/migrations/index.ts +3 -3
- package/src/db/migrations/lock.ts +324 -324
- package/src/db/migrations/runner.ts +650 -650
- package/src/deploy/cache.ts +140 -140
- package/src/deploy/compile/vercel.ts +344 -344
- package/src/deploy/index.ts +87 -87
- package/src/deploy/inference/brain.ts +268 -268
- package/src/deploy/inference/context.ts +182 -182
- package/src/deploy/inference/filling-extract.ts +245 -245
- package/src/deploy/inference/heuristic.ts +182 -182
- package/src/deploy/intent.ts +173 -173
- package/src/deploy/plan.ts +178 -178
- package/src/design/__tests__/agents-link.test.ts +109 -109
- package/src/design/__tests__/extract-patch-diff.test.ts +265 -265
- package/src/design/__tests__/lint.test.ts +110 -110
- package/src/design/__tests__/parser.test.ts +195 -195
- package/src/design/__tests__/tailwind-theme.test.ts +229 -229
- package/src/design/agents-link.ts +165 -165
- package/src/design/diff.ts +138 -138
- package/src/design/extract.ts +285 -285
- package/src/design/index.ts +102 -102
- package/src/design/lint.ts +209 -209
- package/src/design/parser.ts +555 -555
- package/src/design/patch.ts +241 -241
- package/src/design/scaffold.ts +147 -147
- package/src/design/tailwind-theme.ts +441 -441
- package/src/design/types.ts +210 -210
- package/src/desktop/__tests__/webview-fallback.test.ts +254 -254
- package/src/desktop/__tests__/window.test.ts +248 -248
- package/src/desktop/__tests__/worker.test.ts +266 -266
- package/src/desktop/index.ts +43 -43
- package/src/desktop/types.ts +158 -158
- package/src/desktop/worker.ts +180 -180
- package/src/dev-error-overlay/__tests__/overlay-injector.test.ts +241 -241
- package/src/dev-error-overlay/index.ts +30 -30
- package/src/dev-error-overlay/overlay-injector.ts +243 -243
- package/src/dev-error-overlay/overlay-styles.ts +52 -52
- package/src/dev-error-overlay/types.ts +66 -66
- package/src/devtools/ai/context-builder.ts +375 -375
- package/src/devtools/ai/index.ts +25 -25
- package/src/devtools/ai/mcp-connector.ts +25 -25
- package/src/devtools/client/catchers/error-catcher.ts +344 -344
- package/src/devtools/client/catchers/index.ts +18 -18
- package/src/devtools/client/components/index.ts +39 -39
- package/src/devtools/client/components/mandu-character.tsx +331 -331
- package/src/devtools/client/components/overlay.tsx +368 -368
- package/src/devtools/client/components/panel/errors-panel.tsx +259 -259
- package/src/devtools/client/components/panel/guard-panel.tsx +30 -30
- package/src/devtools/client/components/panel/islands-panel.tsx +320 -320
- package/src/devtools/client/components/panel/network-panel.tsx +291 -291
- package/src/devtools/client/components/panel/panel-container.tsx +500 -500
- package/src/devtools/client/components/panel/preview-panel.tsx +46 -46
- package/src/devtools/client/filters/context-filters.ts +282 -282
- package/src/devtools/client/filters/index.ts +16 -16
- package/src/devtools/client/index.ts +63 -63
- package/src/devtools/client/persistence.ts +335 -335
- package/src/devtools/hook/create-hook.ts +207 -207
- package/src/devtools/hook/index.ts +13 -13
- package/src/devtools/index.ts +439 -439
- package/src/devtools/init.ts +265 -265
- package/src/devtools/protocol.ts +237 -237
- package/src/devtools/server/index.ts +17 -17
- package/src/devtools/server/source-context.ts +450 -450
- package/src/devtools/types.ts +35 -35
- package/src/devtools/worker/index.ts +25 -25
- package/src/devtools/worker/redaction-worker.ts +233 -233
- package/src/devtools/worker/worker-manager.ts +410 -410
- package/src/diagnose/__tests__/checks.test.ts +451 -380
- package/src/diagnose/checks.ts +832 -720
- package/src/diagnose/index.ts +17 -16
- package/src/diagnose/run.ts +93 -91
- package/src/diagnose/types.ts +53 -53
- package/src/email/__tests__/email.test.ts +355 -355
- package/src/email/index.ts +282 -282
- package/src/email/smtp.ts +64 -64
- package/src/error/domains.ts +265 -265
- package/src/error/result.ts +60 -60
- package/src/error/types.ts +6 -6
- package/src/errors/extractor.ts +409 -409
- package/src/errors/index.ts +19 -19
- package/src/filling/__tests__/session-sqlite.test.ts +454 -454
- package/src/filling/auth.ts +308 -308
- package/src/filling/cookie-codec.ts +299 -299
- package/src/filling/deps.ts +265 -265
- package/src/filling/session-sqlite.ts +617 -617
- package/src/filling/sse.ts +5 -5
- package/src/filling/ws.ts +78 -78
- package/src/generator/index.ts +3 -3
- package/src/guard/__tests__/design-inline-class.test.ts +219 -219
- package/src/guard/__tests__/tsgolint-bridge.test.ts +347 -347
- package/src/guard/analyzer.ts +360 -360
- package/src/guard/auto-correct.ts +1 -1
- package/src/guard/contract-guard.ts +9 -9
- package/src/guard/define-rule.ts +243 -243
- package/src/guard/design-inline-class.ts +393 -393
- package/src/guard/file-type.test.ts +24 -24
- package/src/guard/graph.ts +1 -1
- package/src/guard/healing.ts +36 -36
- package/src/guard/presets/atomic.ts +70 -70
- package/src/guard/presets/clean.ts +77 -77
- package/src/guard/presets/fsd.ts +79 -79
- package/src/guard/presets/hexagonal.ts +68 -68
- package/src/guard/reporter.ts +442 -442
- package/src/guard/rule-presets.ts +379 -379
- package/src/guard/semantic-slots.ts +1 -1
- package/src/guard/suggestions.ts +358 -358
- package/src/guard/tsgolint-bridge.ts +512 -512
- package/src/guard/types.ts +348 -348
- package/src/guard/watcher.ts +405 -405
- package/src/i18n/define.ts +126 -126
- package/src/i18n/index.ts +52 -52
- package/src/i18n/message-registry.ts +173 -173
- package/src/i18n/types.ts +112 -112
- package/src/id/index.ts +105 -105
- package/src/index.ts +2 -2
- package/src/kitchen/api/agent-devtools-api.ts +779 -544
- package/src/kitchen/api/errors-grouping.ts +126 -0
- package/src/kitchen/kitchen-handler.ts +192 -63
- package/src/kitchen/kitchen-ui.ts +842 -464
- package/src/logging/index.ts +22 -22
- package/src/logging/transports.ts +365 -365
- package/src/middleware/bridge.ts +147 -147
- package/src/middleware/compose.ts +134 -134
- package/src/middleware/compress.ts +62 -62
- package/src/middleware/cors.ts +47 -47
- package/src/middleware/csrf.ts +328 -328
- package/src/middleware/define.ts +132 -132
- package/src/middleware/jwt.ts +134 -134
- package/src/middleware/logger.ts +58 -58
- package/src/middleware/oauth/__tests__/oauth.test.ts +1 -1
- package/src/middleware/oauth/index.ts +505 -505
- package/src/middleware/oauth/providers.ts +115 -115
- package/src/middleware/rate-limit/index.ts +522 -522
- package/src/middleware/rate-limit/sqlite-store.ts +382 -382
- package/src/middleware/scheduler-cron.ts +96 -96
- package/src/middleware/secure/__tests__/secure.test.ts +360 -360
- package/src/middleware/secure/csp.ts +193 -193
- package/src/middleware/session.ts +174 -174
- package/src/middleware/timeout.ts +55 -55
- package/src/observability/logger-adapter.ts +36 -36
- package/src/observability/sqlite-store.ts +254 -254
- package/src/openapi/generator.ts +1 -1
- package/src/openapi/openapi.test.ts +43 -43
- package/src/perf/__tests__/user-marks.test.ts +354 -354
- package/src/perf/index.ts +133 -133
- package/src/plugins/__tests__/lifecycle-integration.test.ts +272 -272
- package/src/plugins/__tests__/runner.test.ts +409 -409
- package/src/plugins/define.ts +124 -124
- package/src/plugins/examples/dep-check-plugin.ts +80 -80
- package/src/plugins/examples/prerender-cache-plugin.ts +111 -111
- package/src/plugins/examples/sitemap-plugin.ts +65 -65
- package/src/plugins/runner.ts +361 -361
- package/src/plugins/types.ts +368 -368
- package/src/report/index.ts +1 -1
- package/src/resource/__tests__/generator.test.ts +32 -32
- package/src/resource/ddl/__tests__/diff.test.ts +639 -639
- package/src/resource/ddl/__tests__/emit.test.ts +823 -823
- package/src/resource/ddl/__tests__/snapshot.test.ts +499 -499
- package/src/resource/ddl/emit.ts +559 -559
- package/src/resource/ddl/persistence-types.ts +218 -218
- package/src/resource/ddl/type-map.ts +223 -223
- package/src/resource/ddl/types.ts +232 -232
- package/src/resource/generator-repo.ts +630 -630
- package/src/resource/schema.ts +1 -1
- package/src/router/fs-patterns.test.ts +96 -96
- package/src/router/fs-routes.ts +6 -6
- package/src/routes/index.ts +74 -74
- package/src/routes/metadata-routes.ts +427 -427
- package/src/routes/types.ts +341 -341
- package/src/runtime/__tests__/error-boundary-redaction.test.ts +141 -141
- package/src/runtime/__tests__/hdr-client.test.ts +223 -223
- package/src/runtime/__tests__/http-errors.test.ts +117 -117
- package/src/runtime/__tests__/not-found.test.ts +152 -152
- package/src/runtime/adapter.ts +47 -47
- package/src/runtime/boundary.tsx +252 -252
- package/src/runtime/cache.ts +494 -494
- package/src/runtime/compose.ts +222 -222
- package/src/runtime/fast-refresh-runtime.ts +322 -322
- package/src/runtime/handler.ts +65 -65
- package/src/runtime/handlers.ts +300 -300
- package/src/runtime/http-errors.ts +113 -113
- package/src/runtime/image-handler.ts +1 -1
- package/src/runtime/lifecycle.ts +381 -381
- package/src/runtime/logger.test.ts +345 -345
- package/src/runtime/middleware.ts +264 -264
- package/src/runtime/not-found.ts +93 -93
- package/src/runtime/openapi-endpoint.ts +236 -236
- package/src/runtime/ppr.ts +74 -74
- package/src/runtime/registry.ts +171 -171
- package/src/runtime/router.ts +105 -105
- package/src/runtime/server.ts +67 -67
- package/src/runtime/shims.ts +48 -48
- package/src/runtime/ssr.ts +24 -31
- package/src/runtime/streaming-ssr.ts +32 -37
- package/src/runtime/trace.ts +144 -144
- package/src/scheduler/validate.ts +169 -169
- package/src/seo/index.ts +219 -219
- package/src/seo/integration/ssr.ts +306 -306
- package/src/seo/render/basic.ts +435 -435
- package/src/seo/render/index.ts +143 -143
- package/src/seo/render/jsonld.ts +539 -539
- package/src/seo/render/opengraph.ts +197 -197
- package/src/seo/render/robots.ts +116 -116
- package/src/seo/render/sitemap.ts +137 -137
- package/src/seo/render/twitter.ts +127 -127
- package/src/seo/resolve/opengraph.ts +143 -143
- package/src/seo/resolve/robots.ts +73 -73
- package/src/seo/resolve/title.ts +94 -94
- package/src/seo/resolve/twitter.ts +73 -73
- package/src/seo/resolve/url.ts +104 -104
- package/src/seo/routes/index.ts +290 -290
- package/src/seo/types.ts +588 -588
- package/src/slot/validator.ts +39 -39
- package/src/storage/s3/__tests__/s3.test.ts +479 -479
- package/src/storage/s3/index.ts +412 -412
- package/src/testing/__tests__/assertions.test.ts +632 -632
- package/src/testing/__tests__/reporter.test.ts +454 -454
- package/src/testing/assertions.ts +986 -986
- package/src/testing/db.ts +157 -157
- package/src/testing/mocks.ts +203 -203
- package/src/testing/session.ts +190 -190
- package/src/types/branded.ts +56 -56
- package/src/types/index.ts +1 -1
- package/src/utils/safe-io.ts +188 -188
- package/src/utils/string-safe.ts +298 -298
- package/src/watcher/watcher.ts +18 -18
|
@@ -1,110 +1,110 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Issue #245 M5 — DESIGN.md lint tests.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { describe, it, expect } from "bun:test";
|
|
6
|
-
import { lintDesignSpec } from "../lint";
|
|
7
|
-
import { parseDesignMd } from "../parser";
|
|
8
|
-
|
|
9
|
-
describe("lintDesignSpec — color-palette", () => {
|
|
10
|
-
it("warns when parser couldn't extract a value (rule: color-missing-value)", () => {
|
|
11
|
-
// Parser drops non-hex/rgb values during extraction, so the
|
|
12
|
-
// resulting token has no `.value` — the linter flags that.
|
|
13
|
-
const r = lintDesignSpec(parseDesignMd(`# T
|
|
14
|
-
## Color Palette
|
|
15
|
-
- Primary — #FF8C42 — brand
|
|
16
|
-
- Bad — notahex — wrong
|
|
17
|
-
`));
|
|
18
|
-
expect(
|
|
19
|
-
r.issues.find((i) => i.rule === "color-missing-value" && i.name === "Bad"),
|
|
20
|
-
).toBeDefined();
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it("warns on missing color value", () => {
|
|
24
|
-
const r = lintDesignSpec(parseDesignMd(`# T
|
|
25
|
-
## Color Palette
|
|
26
|
-
- Primary — see docs
|
|
27
|
-
`));
|
|
28
|
-
expect(r.issues.find((i) => i.rule === "color-missing-value")?.severity).toBe("warning");
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it("warns on slug collision", () => {
|
|
32
|
-
const r = lintDesignSpec(parseDesignMd(`# T
|
|
33
|
-
## Color Palette
|
|
34
|
-
- Primary — #FF0000
|
|
35
|
-
- primary — #00FF00
|
|
36
|
-
`));
|
|
37
|
-
expect(r.issues.find((i) => i.rule === "color-slug-collision")).toBeDefined();
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it("notes duplicate values across distinct names (info)", () => {
|
|
41
|
-
const r = lintDesignSpec(parseDesignMd(`# T
|
|
42
|
-
## Color Palette
|
|
43
|
-
- Brand — #FF8C42 — main
|
|
44
|
-
- Accent — #FF8C42 — secondary
|
|
45
|
-
`));
|
|
46
|
-
const dup = r.issues.find((i) => i.rule === "color-duplicate-value");
|
|
47
|
-
expect(dup?.severity).toBe("info");
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
describe("lintDesignSpec — typography / layout / shadows / components", () => {
|
|
52
|
-
it("warns on a typography token with neither family nor size", () => {
|
|
53
|
-
const r = lintDesignSpec(parseDesignMd(`# T
|
|
54
|
-
## Typography
|
|
55
|
-
- body: weight: 400
|
|
56
|
-
`));
|
|
57
|
-
expect(r.issues.find((i) => i.rule === "typography-empty-token")).toBeDefined();
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it("warns on layout slug collision", () => {
|
|
61
|
-
const r = lintDesignSpec(parseDesignMd(`# T
|
|
62
|
-
## Layout
|
|
63
|
-
- sm: 0.5rem
|
|
64
|
-
- SM: 0.5rem
|
|
65
|
-
`));
|
|
66
|
-
expect(r.issues.find((i) => i.rule === "spacing-slug-collision")).toBeDefined();
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it("warns on shadow slug collision", () => {
|
|
70
|
-
const r = lintDesignSpec(parseDesignMd(`# T
|
|
71
|
-
## Depth & Elevation
|
|
72
|
-
- card — 0 1px 2px rgba(0,0,0,0.1)
|
|
73
|
-
- Card — 0 2px 4px rgba(0,0,0,0.2)
|
|
74
|
-
`));
|
|
75
|
-
expect(r.issues.find((i) => i.rule === "shadow-slug-collision")).toBeDefined();
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it("warns on duplicate component H3 names", () => {
|
|
79
|
-
const r = lintDesignSpec(parseDesignMd(`# T
|
|
80
|
-
## Components
|
|
81
|
-
|
|
82
|
-
### Button
|
|
83
|
-
|
|
84
|
-
### button
|
|
85
|
-
`));
|
|
86
|
-
expect(r.issues.find((i) => i.rule === "component-duplicate")).toBeDefined();
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
describe("lintDesignSpec — clean DESIGN.md", () => {
|
|
91
|
-
it("returns ok with empty issues for a well-formed spec", () => {
|
|
92
|
-
const r = lintDesignSpec(parseDesignMd(`# T
|
|
93
|
-
## Color Palette
|
|
94
|
-
- Primary — #FF8C42 — brand
|
|
95
|
-
- Accent — #1A1F36 — text
|
|
96
|
-
|
|
97
|
-
## Typography
|
|
98
|
-
- body: font-family: Inter, sans-serif; size: 16px
|
|
99
|
-
|
|
100
|
-
## Layout
|
|
101
|
-
- sm: 0.5rem
|
|
102
|
-
- md: 1rem
|
|
103
|
-
|
|
104
|
-
## Depth & Elevation
|
|
105
|
-
- card — 0 1px 3px rgba(0,0,0,0.1)
|
|
106
|
-
`));
|
|
107
|
-
expect(r.issues).toEqual([]);
|
|
108
|
-
expect(r.ok).toBe(true);
|
|
109
|
-
});
|
|
110
|
-
});
|
|
1
|
+
/**
|
|
2
|
+
* Issue #245 M5 — DESIGN.md lint tests.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect } from "bun:test";
|
|
6
|
+
import { lintDesignSpec } from "../lint";
|
|
7
|
+
import { parseDesignMd } from "../parser";
|
|
8
|
+
|
|
9
|
+
describe("lintDesignSpec — color-palette", () => {
|
|
10
|
+
it("warns when parser couldn't extract a value (rule: color-missing-value)", () => {
|
|
11
|
+
// Parser drops non-hex/rgb values during extraction, so the
|
|
12
|
+
// resulting token has no `.value` — the linter flags that.
|
|
13
|
+
const r = lintDesignSpec(parseDesignMd(`# T
|
|
14
|
+
## Color Palette
|
|
15
|
+
- Primary — #FF8C42 — brand
|
|
16
|
+
- Bad — notahex — wrong
|
|
17
|
+
`));
|
|
18
|
+
expect(
|
|
19
|
+
r.issues.find((i) => i.rule === "color-missing-value" && i.name === "Bad"),
|
|
20
|
+
).toBeDefined();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("warns on missing color value", () => {
|
|
24
|
+
const r = lintDesignSpec(parseDesignMd(`# T
|
|
25
|
+
## Color Palette
|
|
26
|
+
- Primary — see docs
|
|
27
|
+
`));
|
|
28
|
+
expect(r.issues.find((i) => i.rule === "color-missing-value")?.severity).toBe("warning");
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("warns on slug collision", () => {
|
|
32
|
+
const r = lintDesignSpec(parseDesignMd(`# T
|
|
33
|
+
## Color Palette
|
|
34
|
+
- Primary — #FF0000
|
|
35
|
+
- primary — #00FF00
|
|
36
|
+
`));
|
|
37
|
+
expect(r.issues.find((i) => i.rule === "color-slug-collision")).toBeDefined();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("notes duplicate values across distinct names (info)", () => {
|
|
41
|
+
const r = lintDesignSpec(parseDesignMd(`# T
|
|
42
|
+
## Color Palette
|
|
43
|
+
- Brand — #FF8C42 — main
|
|
44
|
+
- Accent — #FF8C42 — secondary
|
|
45
|
+
`));
|
|
46
|
+
const dup = r.issues.find((i) => i.rule === "color-duplicate-value");
|
|
47
|
+
expect(dup?.severity).toBe("info");
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe("lintDesignSpec — typography / layout / shadows / components", () => {
|
|
52
|
+
it("warns on a typography token with neither family nor size", () => {
|
|
53
|
+
const r = lintDesignSpec(parseDesignMd(`# T
|
|
54
|
+
## Typography
|
|
55
|
+
- body: weight: 400
|
|
56
|
+
`));
|
|
57
|
+
expect(r.issues.find((i) => i.rule === "typography-empty-token")).toBeDefined();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("warns on layout slug collision", () => {
|
|
61
|
+
const r = lintDesignSpec(parseDesignMd(`# T
|
|
62
|
+
## Layout
|
|
63
|
+
- sm: 0.5rem
|
|
64
|
+
- SM: 0.5rem
|
|
65
|
+
`));
|
|
66
|
+
expect(r.issues.find((i) => i.rule === "spacing-slug-collision")).toBeDefined();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("warns on shadow slug collision", () => {
|
|
70
|
+
const r = lintDesignSpec(parseDesignMd(`# T
|
|
71
|
+
## Depth & Elevation
|
|
72
|
+
- card — 0 1px 2px rgba(0,0,0,0.1)
|
|
73
|
+
- Card — 0 2px 4px rgba(0,0,0,0.2)
|
|
74
|
+
`));
|
|
75
|
+
expect(r.issues.find((i) => i.rule === "shadow-slug-collision")).toBeDefined();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("warns on duplicate component H3 names", () => {
|
|
79
|
+
const r = lintDesignSpec(parseDesignMd(`# T
|
|
80
|
+
## Components
|
|
81
|
+
|
|
82
|
+
### Button
|
|
83
|
+
|
|
84
|
+
### button
|
|
85
|
+
`));
|
|
86
|
+
expect(r.issues.find((i) => i.rule === "component-duplicate")).toBeDefined();
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe("lintDesignSpec — clean DESIGN.md", () => {
|
|
91
|
+
it("returns ok with empty issues for a well-formed spec", () => {
|
|
92
|
+
const r = lintDesignSpec(parseDesignMd(`# T
|
|
93
|
+
## Color Palette
|
|
94
|
+
- Primary — #FF8C42 — brand
|
|
95
|
+
- Accent — #1A1F36 — text
|
|
96
|
+
|
|
97
|
+
## Typography
|
|
98
|
+
- body: font-family: Inter, sans-serif; size: 16px
|
|
99
|
+
|
|
100
|
+
## Layout
|
|
101
|
+
- sm: 0.5rem
|
|
102
|
+
- md: 1rem
|
|
103
|
+
|
|
104
|
+
## Depth & Elevation
|
|
105
|
+
- card — 0 1px 3px rgba(0,0,0,0.1)
|
|
106
|
+
`));
|
|
107
|
+
expect(r.issues).toEqual([]);
|
|
108
|
+
expect(r.ok).toBe(true);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
@@ -1,195 +1,195 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* DESIGN.md parser tests.
|
|
3
|
-
*
|
|
4
|
-
* Cover the structural contract: each of the 9 sections is recognised
|
|
5
|
-
* across reasonable formatting variants, and a malformed row never
|
|
6
|
-
* throws — it just gets skipped.
|
|
7
|
-
*/
|
|
8
|
-
import { describe, it, expect } from "bun:test";
|
|
9
|
-
import {
|
|
10
|
-
parseDesignMd,
|
|
11
|
-
validateDesignSpec,
|
|
12
|
-
EMPTY_DESIGN_MD,
|
|
13
|
-
DESIGN_SECTION_IDS,
|
|
14
|
-
} from "../index";
|
|
15
|
-
|
|
16
|
-
describe("parseDesignMd", () => {
|
|
17
|
-
it("parses an empty source without throwing", () => {
|
|
18
|
-
const spec = parseDesignMd("");
|
|
19
|
-
expect(spec.title).toBeUndefined();
|
|
20
|
-
for (const id of DESIGN_SECTION_IDS) {
|
|
21
|
-
expect(spec.sections[id].present).toBe(false);
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it("captures the H1 title", () => {
|
|
26
|
-
const spec = parseDesignMd("# Acme DESIGN.md\n\n## Color Palette\n\n- primary — #000\n");
|
|
27
|
-
expect(spec.title).toBe("Acme DESIGN.md");
|
|
28
|
-
expect(spec.sections["color-palette"].present).toBe(true);
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it("recognises all 9 sections via fuzzy heading matching", () => {
|
|
32
|
-
const md = `# X
|
|
33
|
-
|
|
34
|
-
## Visual Theme & Philosophy
|
|
35
|
-
Minimal and dense.
|
|
36
|
-
|
|
37
|
-
## Color Palette
|
|
38
|
-
- primary — #FF8C42 — brand action
|
|
39
|
-
|
|
40
|
-
## Typography
|
|
41
|
-
- display — Inter, 48px, weight 700 — hero
|
|
42
|
-
|
|
43
|
-
## Components
|
|
44
|
-
### Button
|
|
45
|
-
variant: primary | secondary | ghost
|
|
46
|
-
|
|
47
|
-
## Layout
|
|
48
|
-
- md — 16px
|
|
49
|
-
|
|
50
|
-
## Depth & Elevation
|
|
51
|
-
- card: 0 1px 2px rgba(0,0,0,.06)
|
|
52
|
-
|
|
53
|
-
## Do's & Don'ts
|
|
54
|
-
### Do
|
|
55
|
-
- Use tokens
|
|
56
|
-
|
|
57
|
-
### Don't
|
|
58
|
-
- Inline btn-hard
|
|
59
|
-
|
|
60
|
-
## Responsive
|
|
61
|
-
- mobile — 640px
|
|
62
|
-
|
|
63
|
-
## Agent Prompts
|
|
64
|
-
### hero
|
|
65
|
-
Generate using display token.
|
|
66
|
-
`;
|
|
67
|
-
const spec = parseDesignMd(md);
|
|
68
|
-
expect(spec.sections.theme.present).toBe(true);
|
|
69
|
-
expect(spec.sections.theme.summary).toBe("Minimal and dense.");
|
|
70
|
-
expect(spec.sections["color-palette"].tokens).toEqual([
|
|
71
|
-
{ name: "primary", value: "#FF8C42", role: "brand action" },
|
|
72
|
-
]);
|
|
73
|
-
expect(spec.sections.typography.tokens[0]?.name).toBe("display");
|
|
74
|
-
expect(spec.sections.typography.tokens[0]?.size).toBe("48px");
|
|
75
|
-
expect(spec.sections.components.tokens[0]?.name).toBe("Button");
|
|
76
|
-
expect(spec.sections.components.tokens[0]?.variants.variant).toEqual([
|
|
77
|
-
"primary",
|
|
78
|
-
"secondary",
|
|
79
|
-
"ghost",
|
|
80
|
-
]);
|
|
81
|
-
expect(spec.sections.layout.tokens[0]).toEqual({ name: "md", value: "16px" });
|
|
82
|
-
expect(spec.sections.shadows.tokens[0]?.name).toBe("card");
|
|
83
|
-
expect(spec.sections["dos-donts"].rules.find((r) => r.kind === "do")?.text).toBe(
|
|
84
|
-
"Use tokens",
|
|
85
|
-
);
|
|
86
|
-
expect(spec.sections["dos-donts"].rules.find((r) => r.kind === "dont")?.text).toBe(
|
|
87
|
-
"Inline btn-hard",
|
|
88
|
-
);
|
|
89
|
-
expect(spec.sections.responsive.breakpoints[0]).toEqual({
|
|
90
|
-
name: "mobile",
|
|
91
|
-
value: "640px",
|
|
92
|
-
notes: undefined,
|
|
93
|
-
});
|
|
94
|
-
expect(spec.sections["agent-prompts"].prompts[0]?.title).toBe("hero");
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it("treats unknown H2 sections as extras (round-trip)", () => {
|
|
98
|
-
const md = `## Color Palette\n- primary — #000\n\n## Internationalization\nKO + EN only.\n`;
|
|
99
|
-
const spec = parseDesignMd(md);
|
|
100
|
-
expect(spec.sections["color-palette"].present).toBe(true);
|
|
101
|
-
expect(spec.extraSections).toHaveLength(1);
|
|
102
|
-
expect(spec.extraSections[0]?.heading).toBe("Internationalization");
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
it("absorbs malformed rows without throwing", () => {
|
|
106
|
-
const md = `## Color Palette
|
|
107
|
-
- primary — #FF8C42 — brand
|
|
108
|
-
- garbage row with no shape at all
|
|
109
|
-
- — — — —
|
|
110
|
-
- accent — not-a-color — ok
|
|
111
|
-
`;
|
|
112
|
-
expect(() => parseDesignMd(md)).not.toThrow();
|
|
113
|
-
const spec = parseDesignMd(md);
|
|
114
|
-
const tokens = spec.sections["color-palette"].tokens;
|
|
115
|
-
// primary (with hex) survives. accent (no hex) is captured with name+role only.
|
|
116
|
-
expect(tokens.find((t) => t.name === "primary")?.value).toBe("#FF8C42");
|
|
117
|
-
expect(tokens.length).toBeGreaterThanOrEqual(1);
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
it("supports markdown table rows for color palette", () => {
|
|
121
|
-
const md = `## Color Palette
|
|
122
|
-
| Name | Hex | Role |
|
|
123
|
-
|------|-----|------|
|
|
124
|
-
| primary | #000000 | brand |
|
|
125
|
-
| surface | #ffffff | background |
|
|
126
|
-
`;
|
|
127
|
-
const spec = parseDesignMd(md);
|
|
128
|
-
const names = spec.sections["color-palette"].tokens.map((t) => t.name);
|
|
129
|
-
expect(names).toContain("primary");
|
|
130
|
-
expect(names).toContain("surface");
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
it("parses the empty skeleton without finding tokens", () => {
|
|
134
|
-
const spec = parseDesignMd(EMPTY_DESIGN_MD);
|
|
135
|
-
expect(spec.title).toBe("DESIGN.md");
|
|
136
|
-
// All sections are present (headings exist) but have no structured tokens.
|
|
137
|
-
for (const id of DESIGN_SECTION_IDS) {
|
|
138
|
-
expect(spec.sections[id].present).toBe(true);
|
|
139
|
-
}
|
|
140
|
-
expect(spec.sections["color-palette"].tokens).toEqual([]);
|
|
141
|
-
});
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
describe("validateDesignSpec", () => {
|
|
145
|
-
it("flags empty source with all 9 missing", () => {
|
|
146
|
-
const result = validateDesignSpec(parseDesignMd(""));
|
|
147
|
-
expect(result.ok).toBe(false);
|
|
148
|
-
expect(result.issues.filter((i) => i.kind === "missing")).toHaveLength(9);
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
it("flags the empty skeleton as `empty` for every section", () => {
|
|
152
|
-
const result = validateDesignSpec(parseDesignMd(EMPTY_DESIGN_MD));
|
|
153
|
-
// All sections present, all empty — so no `missing`, all `empty`.
|
|
154
|
-
expect(result.issues.every((i) => i.kind === "empty")).toBe(true);
|
|
155
|
-
expect(result.issues).toHaveLength(9);
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
it("returns ok=true when every section has structured content", () => {
|
|
159
|
-
const md = `# X
|
|
160
|
-
|
|
161
|
-
## Visual Theme & Philosophy
|
|
162
|
-
Minimal.
|
|
163
|
-
|
|
164
|
-
## Color Palette
|
|
165
|
-
- primary — #000
|
|
166
|
-
|
|
167
|
-
## Typography
|
|
168
|
-
- body — Inter, 16px
|
|
169
|
-
|
|
170
|
-
## Components
|
|
171
|
-
### Button
|
|
172
|
-
variant: primary | secondary
|
|
173
|
-
|
|
174
|
-
## Layout
|
|
175
|
-
- md — 16px
|
|
176
|
-
|
|
177
|
-
## Depth & Elevation
|
|
178
|
-
- card: 0 1px 2px rgba(0,0,0,.06)
|
|
179
|
-
|
|
180
|
-
## Do's & Don'ts
|
|
181
|
-
### Do
|
|
182
|
-
- Use tokens
|
|
183
|
-
|
|
184
|
-
## Responsive
|
|
185
|
-
- mobile — 640px
|
|
186
|
-
|
|
187
|
-
## Agent Prompts
|
|
188
|
-
### default
|
|
189
|
-
Use tokens.
|
|
190
|
-
`;
|
|
191
|
-
const result = validateDesignSpec(parseDesignMd(md));
|
|
192
|
-
expect(result.ok).toBe(true);
|
|
193
|
-
expect(result.issues).toHaveLength(0);
|
|
194
|
-
});
|
|
195
|
-
});
|
|
1
|
+
/**
|
|
2
|
+
* DESIGN.md parser tests.
|
|
3
|
+
*
|
|
4
|
+
* Cover the structural contract: each of the 9 sections is recognised
|
|
5
|
+
* across reasonable formatting variants, and a malformed row never
|
|
6
|
+
* throws — it just gets skipped.
|
|
7
|
+
*/
|
|
8
|
+
import { describe, it, expect } from "bun:test";
|
|
9
|
+
import {
|
|
10
|
+
parseDesignMd,
|
|
11
|
+
validateDesignSpec,
|
|
12
|
+
EMPTY_DESIGN_MD,
|
|
13
|
+
DESIGN_SECTION_IDS,
|
|
14
|
+
} from "../index";
|
|
15
|
+
|
|
16
|
+
describe("parseDesignMd", () => {
|
|
17
|
+
it("parses an empty source without throwing", () => {
|
|
18
|
+
const spec = parseDesignMd("");
|
|
19
|
+
expect(spec.title).toBeUndefined();
|
|
20
|
+
for (const id of DESIGN_SECTION_IDS) {
|
|
21
|
+
expect(spec.sections[id].present).toBe(false);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("captures the H1 title", () => {
|
|
26
|
+
const spec = parseDesignMd("# Acme DESIGN.md\n\n## Color Palette\n\n- primary — #000\n");
|
|
27
|
+
expect(spec.title).toBe("Acme DESIGN.md");
|
|
28
|
+
expect(spec.sections["color-palette"].present).toBe(true);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("recognises all 9 sections via fuzzy heading matching", () => {
|
|
32
|
+
const md = `# X
|
|
33
|
+
|
|
34
|
+
## Visual Theme & Philosophy
|
|
35
|
+
Minimal and dense.
|
|
36
|
+
|
|
37
|
+
## Color Palette
|
|
38
|
+
- primary — #FF8C42 — brand action
|
|
39
|
+
|
|
40
|
+
## Typography
|
|
41
|
+
- display — Inter, 48px, weight 700 — hero
|
|
42
|
+
|
|
43
|
+
## Components
|
|
44
|
+
### Button
|
|
45
|
+
variant: primary | secondary | ghost
|
|
46
|
+
|
|
47
|
+
## Layout
|
|
48
|
+
- md — 16px
|
|
49
|
+
|
|
50
|
+
## Depth & Elevation
|
|
51
|
+
- card: 0 1px 2px rgba(0,0,0,.06)
|
|
52
|
+
|
|
53
|
+
## Do's & Don'ts
|
|
54
|
+
### Do
|
|
55
|
+
- Use tokens
|
|
56
|
+
|
|
57
|
+
### Don't
|
|
58
|
+
- Inline btn-hard
|
|
59
|
+
|
|
60
|
+
## Responsive
|
|
61
|
+
- mobile — 640px
|
|
62
|
+
|
|
63
|
+
## Agent Prompts
|
|
64
|
+
### hero
|
|
65
|
+
Generate using display token.
|
|
66
|
+
`;
|
|
67
|
+
const spec = parseDesignMd(md);
|
|
68
|
+
expect(spec.sections.theme.present).toBe(true);
|
|
69
|
+
expect(spec.sections.theme.summary).toBe("Minimal and dense.");
|
|
70
|
+
expect(spec.sections["color-palette"].tokens).toEqual([
|
|
71
|
+
{ name: "primary", value: "#FF8C42", role: "brand action" },
|
|
72
|
+
]);
|
|
73
|
+
expect(spec.sections.typography.tokens[0]?.name).toBe("display");
|
|
74
|
+
expect(spec.sections.typography.tokens[0]?.size).toBe("48px");
|
|
75
|
+
expect(spec.sections.components.tokens[0]?.name).toBe("Button");
|
|
76
|
+
expect(spec.sections.components.tokens[0]?.variants.variant).toEqual([
|
|
77
|
+
"primary",
|
|
78
|
+
"secondary",
|
|
79
|
+
"ghost",
|
|
80
|
+
]);
|
|
81
|
+
expect(spec.sections.layout.tokens[0]).toEqual({ name: "md", value: "16px" });
|
|
82
|
+
expect(spec.sections.shadows.tokens[0]?.name).toBe("card");
|
|
83
|
+
expect(spec.sections["dos-donts"].rules.find((r) => r.kind === "do")?.text).toBe(
|
|
84
|
+
"Use tokens",
|
|
85
|
+
);
|
|
86
|
+
expect(spec.sections["dos-donts"].rules.find((r) => r.kind === "dont")?.text).toBe(
|
|
87
|
+
"Inline btn-hard",
|
|
88
|
+
);
|
|
89
|
+
expect(spec.sections.responsive.breakpoints[0]).toEqual({
|
|
90
|
+
name: "mobile",
|
|
91
|
+
value: "640px",
|
|
92
|
+
notes: undefined,
|
|
93
|
+
});
|
|
94
|
+
expect(spec.sections["agent-prompts"].prompts[0]?.title).toBe("hero");
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("treats unknown H2 sections as extras (round-trip)", () => {
|
|
98
|
+
const md = `## Color Palette\n- primary — #000\n\n## Internationalization\nKO + EN only.\n`;
|
|
99
|
+
const spec = parseDesignMd(md);
|
|
100
|
+
expect(spec.sections["color-palette"].present).toBe(true);
|
|
101
|
+
expect(spec.extraSections).toHaveLength(1);
|
|
102
|
+
expect(spec.extraSections[0]?.heading).toBe("Internationalization");
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it("absorbs malformed rows without throwing", () => {
|
|
106
|
+
const md = `## Color Palette
|
|
107
|
+
- primary — #FF8C42 — brand
|
|
108
|
+
- garbage row with no shape at all
|
|
109
|
+
- — — — —
|
|
110
|
+
- accent — not-a-color — ok
|
|
111
|
+
`;
|
|
112
|
+
expect(() => parseDesignMd(md)).not.toThrow();
|
|
113
|
+
const spec = parseDesignMd(md);
|
|
114
|
+
const tokens = spec.sections["color-palette"].tokens;
|
|
115
|
+
// primary (with hex) survives. accent (no hex) is captured with name+role only.
|
|
116
|
+
expect(tokens.find((t) => t.name === "primary")?.value).toBe("#FF8C42");
|
|
117
|
+
expect(tokens.length).toBeGreaterThanOrEqual(1);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it("supports markdown table rows for color palette", () => {
|
|
121
|
+
const md = `## Color Palette
|
|
122
|
+
| Name | Hex | Role |
|
|
123
|
+
|------|-----|------|
|
|
124
|
+
| primary | #000000 | brand |
|
|
125
|
+
| surface | #ffffff | background |
|
|
126
|
+
`;
|
|
127
|
+
const spec = parseDesignMd(md);
|
|
128
|
+
const names = spec.sections["color-palette"].tokens.map((t) => t.name);
|
|
129
|
+
expect(names).toContain("primary");
|
|
130
|
+
expect(names).toContain("surface");
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("parses the empty skeleton without finding tokens", () => {
|
|
134
|
+
const spec = parseDesignMd(EMPTY_DESIGN_MD);
|
|
135
|
+
expect(spec.title).toBe("DESIGN.md");
|
|
136
|
+
// All sections are present (headings exist) but have no structured tokens.
|
|
137
|
+
for (const id of DESIGN_SECTION_IDS) {
|
|
138
|
+
expect(spec.sections[id].present).toBe(true);
|
|
139
|
+
}
|
|
140
|
+
expect(spec.sections["color-palette"].tokens).toEqual([]);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
describe("validateDesignSpec", () => {
|
|
145
|
+
it("flags empty source with all 9 missing", () => {
|
|
146
|
+
const result = validateDesignSpec(parseDesignMd(""));
|
|
147
|
+
expect(result.ok).toBe(false);
|
|
148
|
+
expect(result.issues.filter((i) => i.kind === "missing")).toHaveLength(9);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it("flags the empty skeleton as `empty` for every section", () => {
|
|
152
|
+
const result = validateDesignSpec(parseDesignMd(EMPTY_DESIGN_MD));
|
|
153
|
+
// All sections present, all empty — so no `missing`, all `empty`.
|
|
154
|
+
expect(result.issues.every((i) => i.kind === "empty")).toBe(true);
|
|
155
|
+
expect(result.issues).toHaveLength(9);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it("returns ok=true when every section has structured content", () => {
|
|
159
|
+
const md = `# X
|
|
160
|
+
|
|
161
|
+
## Visual Theme & Philosophy
|
|
162
|
+
Minimal.
|
|
163
|
+
|
|
164
|
+
## Color Palette
|
|
165
|
+
- primary — #000
|
|
166
|
+
|
|
167
|
+
## Typography
|
|
168
|
+
- body — Inter, 16px
|
|
169
|
+
|
|
170
|
+
## Components
|
|
171
|
+
### Button
|
|
172
|
+
variant: primary | secondary
|
|
173
|
+
|
|
174
|
+
## Layout
|
|
175
|
+
- md — 16px
|
|
176
|
+
|
|
177
|
+
## Depth & Elevation
|
|
178
|
+
- card: 0 1px 2px rgba(0,0,0,.06)
|
|
179
|
+
|
|
180
|
+
## Do's & Don'ts
|
|
181
|
+
### Do
|
|
182
|
+
- Use tokens
|
|
183
|
+
|
|
184
|
+
## Responsive
|
|
185
|
+
- mobile — 640px
|
|
186
|
+
|
|
187
|
+
## Agent Prompts
|
|
188
|
+
### default
|
|
189
|
+
Use tokens.
|
|
190
|
+
`;
|
|
191
|
+
const result = validateDesignSpec(parseDesignMd(md));
|
|
192
|
+
expect(result.ok).toBe(true);
|
|
193
|
+
expect(result.issues).toHaveLength(0);
|
|
194
|
+
});
|
|
195
|
+
});
|