1cd309d56d
Plan 4 Task 3:
- themes/pilot-minimal 入 workspace(astro ^5.18 纯静态;依赖仅 astro)
- theme.manifest:contracts isThemeManifest 守卫通过——key=pilot-minimal/
七模板条目(home/page/product-list/product-detail/news-list/news-detail/
not-found);brand 刻意不入 requiredSiteConfig(恒可能 {},Task 1 移交契约)
- load-render-data:全主题唯一取数入口(五禁纪律结构保证,JSDoc 钉死)——
SF_BUILD_INPUT 六文件、缺文件/形状破裂明确错误不静默空、同目录缓存单例、
name 空→'Untitled Site'/brand 缺席→{}、编码 slug 原样贯穿(源码令牌扫描
禁 percent-decode)
- 布局/组件:BaseLayout/SeoHead(title/desc/canonical/og)/SiteHeader(导航
路由缺席容忍隐藏)/SiteFooter/RichText(信任链:清洗+重写后直出)/
ResponsiveMedia/Breadcrumbs/ProductCard/NewsCard/Pagination(V1 禁用态)/
NotFound + tokens.css(75rem/系统字体/4-8-16-24-48/中性色+单 accent)
- 根 test 链并入主题(vitest 3 + astro getViteConfig,Container API 组件
渲染测试);typecheck 四段变五段;.astro/ 入 gitignore
354 根测试 + 16 主题测试全绿。
Co-Authored-By: Claude Code <noreply@anthropic.com>
41 lines
1.7 KiB
TypeScript
41 lines
1.7 KiB
TypeScript
// Task 3 用例 1:theme.manifest 满足 contracts 冻结守卫 isThemeManifest,
|
||
// 且 pilot 主题的关键字面量(key/version/renderContractVersion/内容类型/必填配置/七模板)钉死。
|
||
import { describe, expect, it } from 'vitest'
|
||
import { isThemeManifest } from '@sf/contracts'
|
||
import themeManifest from '../theme.manifest.js'
|
||
|
||
describe('theme.manifest(contracts 冻结面)', () => {
|
||
it('isThemeManifest 守卫通过', () => {
|
||
expect(isThemeManifest(themeManifest)).toBe(true)
|
||
})
|
||
|
||
it('关键字段:key/version/renderContractVersion/supportedContentTypes/requiredSiteConfig', () => {
|
||
expect(themeManifest.key).toBe('pilot-minimal')
|
||
expect(themeManifest.version).toBe('0.1.0')
|
||
expect(themeManifest.renderContractVersion).toBe('1')
|
||
expect(themeManifest.supportedContentTypes).toStrictEqual(['news_article', 'product', 'basic_page'])
|
||
expect(themeManifest.requiredSiteConfig).toStrictEqual(['name', 'origin', 'locale'])
|
||
})
|
||
|
||
it('templates 恰七条目:home/page/product-list/product-detail/news-list/news-detail/not-found(key 唯一、appliesTo 非空)', () => {
|
||
expect(themeManifest.templates.map((t) => t.key)).toStrictEqual([
|
||
'home',
|
||
'page',
|
||
'product-list',
|
||
'product-detail',
|
||
'news-list',
|
||
'news-detail',
|
||
'not-found',
|
||
])
|
||
expect(new Set(themeManifest.templates.map((t) => t.key)).size).toBe(7)
|
||
for (const t of themeManifest.templates) {
|
||
expect(typeof t.appliesTo).toBe('string')
|
||
expect(t.appliesTo).not.toBe('')
|
||
}
|
||
})
|
||
|
||
it('守卫面的反例仍然成立(守卫不是恒真)', () => {
|
||
expect(isThemeManifest({ key: 'x', version: 'not-semver', renderContractVersion: '1' })).toBe(false)
|
||
})
|
||
})
|