v1.3.0: 修复验收链路(iframe 真实渲染+跨帧断言)、基座焦点环、契约第二批 15 个

回归通过率此前不可信:run-tests.ps1 抽取演示页 body 时剥离全部 <script>,
而 79 个 H5 演示页的 DOM 全由内联脚本渲染,且测试页从未引用 frameworks/<组件>.css
—— 断言跑在空 DOM + 无组件样式上,v1.2.0 的 75.1% 属结构性误报。

Fixed — 验收链路
- 测试页改为 iframe 加载真实演示页(_template.html),_runtime.js 跨帧在真实
  渲染结果上断言:帧内 getComputedStyle/styleSheets/outerHTML,用例自动标注 +
  帧内描边,结果 postMessage 上报
- 标注改为容器下钻(.row/.demo-block → 首个含 ≥2 有效子元素的容器,最多 6 层)
- 新增 N/A 语义:静态组件键盘/ARIA 记 skip 而非失败;对比度与 token 检查限定
  在被测组件区,页头文档文字不计
- 通过率分母改为 pass/(pass+fail),tools/run-regression.mjs 与收集器同口径

Added — 设计系统
- 基座 :focus-visible 统一焦点环(colors_and_type.css)。--au-color-focus-ring
  此前只定义未消费,79 个组件样式仅 20 个自带 focus 规则 —— 一次性消除 66 页失败
- tests/_collect.html:零依赖浏览器批量回归收集器(并发 iframe + 汇总)
- tests/_index_template.html:测试总览页模板外置,读数来自 data.json

Added — 契约第二批(展示类 15 个)
- tree/collapse/calendar/carousel/imagepreview/qrcode/countdown/watermark/
  cardlist/treetable/timelinelist/steplist/chartpanel/dashboardcard/employeecard
- 逐份从规格原文(组件2/4/7/9.txt)提炼,含 dims/variants/anatomy/structure/
  usageHints/doNotInvent/unknowns;契约化 21 → 36/79

Changed
- run-tests.ps1 重写:删除失效的快照抽取(占位符替换早已不匹配模板),改生成
  iframe 版测试页;输出 WriteAllText 无 BOM;保持 ASCII-only,中文移入 UTF-8 模板
- 回归基线:960 断言 / 794 通过 / 85.1% / 11 页全通过 / N/A 27
- CHANGELOG/TESTING/改造清单同步;测试总览页与首页通过率联动

验证:浏览器全量 79 页收集器跑通两次,基线一致;详情页契约章节
(含 doNotInvent/unknowns)、chip、首页「通过率 85.1%」、changelog v1.3.0 实测通过
This commit is contained in:
aurora-admin
2026-09-10 21:19:09 +08:00
parent 0289c8acfc
commit 6fec4ba6f8
110 changed files with 4310 additions and 3419 deletions
+33 -136
View File
@@ -1,157 +1,54 @@
# run-tests.ps1 - 批量生成 tests/<slug>.html + tests/index.html
# 读 .design_library/aurora-admin/components/index.json 与 frameworks/*.html 派生每组件测试页
# 零依赖、与现有 build-site.ps1 互不影响
# 注:使用 here-string + ascii-safe 转义避免 PS5.1 在 < 而报错
# run-tests.ps1 - regenerate tests/<slug>.html (one per component) + tests/index.html
#
# Test pages embed an iframe of the real H5 demo (frameworks/<Prefix>.html). Component CSS and the
# demo's inline script actually run inside that frame, and tests/_runtime.js runs every assertion
# against the frame document. Nothing is snapshot-copied here: copying body HTML while stripping
# <script> produced empty test pages, because all 79 demos render their DOM from inline JS.
#
# Output is UTF-8 WITHOUT BOM (PS5.1 `Set-Content -Encoding UTF8` would prepend a BOM).
# This script is intentionally ASCII-only: PS5.1 reads BOM-less files as ANSI, so any non-ASCII
# literal here would be mangled. Chinese labels live in the UTF-8 templates instead.
$ErrorActionPreference = 'Stop'
$lib = ".design_library/aurora-admin"
$idx = Get-Content "$lib/components/index.json" -Raw -Encoding UTF8 | ConvertFrom-Json
$template = Get-Content "tests/_template.html" -Raw -Encoding UTF8
$indexTemplate = Get-Content "tests/_index_template.html" -Raw -Encoding UTF8
$catMap = @{}
$catMap['general'] = @('button','buttongroup','tag','card')
$catMap['navigation']= @('tabs','steps','breadcrumb','sidemenu','topmenu','mixednavigation','quicknav','anchornav','enhancedtabnav','dropdown')
$catMap['input'] = @('input','select','cascader','colorpicker','rangequickpicker','numberrangeinput','switchgroup','dragupload','signaturepad','codeinput','passwordinput','phoneinput','bankcardinput','idcardinput','plateinput','autocomplete','mention','transfer','rate','slider','listpicker','radiocard','checkboxcard','segmented')
$catMap['display'] = @('table','treetable','expandabletable','mergedcelltable','summaryrowtable','fixedcolumntable','cardlist','timelinelist','steplist','chartpanel','dashboardcard','calendar','carousel','imagepreview','qrcode','countdown','tree','collapse','watermark','employeecard')
$catMap['feedback'] = @('modal','confirmmodal','alertmodal','formmodal','fullscreenmodal','messagepro','notificationpro','progressvariants','skeletonpro','emptypro','loadingoverlay','resultvariants','exceptionvariants','popconfirm','bottomsheet')
$catMap['system'] = @('themeswitcher','layoutswitcher','densityswitcher','shortcutpanel','backtotop','pagetransition')
$slugCat = @{}
foreach ($ck in $catMap.Keys) { foreach ($s in $catMap[$ck]) { $slugCat[$s] = $ck } }
$catZh = @{ general='通用'; navigation='导航'; input='数据录入'; display='数据展示'; feedback='反馈'; system='工具与系统' }
$utf8NoBom = New-Object System.Text.UTF8Encoding($false)
function Get-Snapshot([string]$prefix) {
$fw = Get-ChildItem "frameworks" -File -Filter ($prefix + ".html") -ErrorAction SilentlyContinue | Select-Object -First 1
if (-not $fw) { return '<p style="color: var(--au-color-text-placeholder)">\uff08\u65e0 H5 \u6f14\u793a\u9875\uff09</p>' }
$raw = Get-Content $fw.FullName -Raw -Encoding UTF8
# 简化:用纯字符串方法抽取 body 块
$startTag = "<body"
$endTag = "</body>"
$si = $raw.IndexOf($startTag)
if ($si -lt 0) { return '<p style="color: var(--au-color-text-placeholder)">\uff08\u65e0 body \u5185\u5bb9\uff09</p>' }
$bodyStart = $raw.IndexOf(">", $si) + 1
$ei = $raw.IndexOf($endTag, $bodyStart)
if ($ei -lt 0) { $ei = $raw.Length }
$body = $raw.Substring($bodyStart, $ei - $bodyStart)
# 移除 script/style 块(极简)
$body = [regex]::Replace($body, "<script[\s\S]*?</script>", "", "Singleline")
$body = [regex]::Replace($body, "<style[\s\S]*?</style>", "", "Singleline")
$body = [regex]::Replace($body, " on[a-z]+=`"[^`"]*`"", "", "IgnoreCase")
if ($body.Length -gt 1200) { $body = $body.Substring(0, 1200) + "..." }
return $body
function Write-NoBom([string]$path, [string]$content) {
$full = [System.IO.Path]::GetFullPath((Join-Path (Get-Location).Path $path))
[System.IO.File]::WriteAllText($full, $content, $utf8NoBom)
}
# 通用 HTML 模板(用 ASCII-safe placeholder,避免 < 解析问题)
$templatePath = "tests/_template.html"
$template = Get-Content $templatePath -Raw -Encoding UTF8
New-Item -ItemType Directory -Force -Path tests | Out-Null
$count = 0
$missing = @()
foreach ($c in $idx.components) {
$slug = $c.slug
$name = $c.name
$parts = $name -split ' '
$zh = $parts[0]
$en = $parts[-1]
$cat = if ($slugCat.ContainsKey($slug)) { $slugCat[$slug] } else { 'general' }
$batch = [int]$c.specBatch
$prefix = $c.frameworksPrefix
$demo = Get-Snapshot $prefix
$zh = ($c.name -split ' ')[0]
# SLUG_ZH_PLACEHOLDER must be replaced first: it contains SLUG_PLACEHOLDER as a substring.
$html = $template
$html = $html.Replace('SLUG_PLACEHOLDER', $slug)
$html = $html.Replace('SLUG_ZH_PLACEHOLDER', $zh)
$html = $html.Replace('<p style="color: var(--au-color-text-placeholder)">\uff08\u751f\u6210\u5668\u5c06\u586b\u5145\uff09</p>', $demo)
$html = $html.Replace('slug\uff1a<code>SLUG_PLACEHOLDER</code> \u00b7 \u7c7b\u76ee\uff1a<code>general</code> \u00b7 \u89c4\u683c\u6279\u6b21\uff1a\u7b2c <code>1</code> \u6279', "slug\uff1a<code>$slug</code> \u00b7 \u7c7b\u76ee\uff1a<code>$cat</code> \u00b7 \u89c4\u683c\u6279\u6b21\uff1a\u7b2c <code>$batch</code> \u6279")
$html = $html.Replace('PREFIX_PLACEHOLDER', $prefix)
$html = $html.Replace('SLUG_PLACEHOLDER', $slug)
Set-Content -Path "tests/$slug.html" -Value $html -Encoding UTF8
Write-NoBom "tests/$slug.html" $html
if (-not (Test-Path "frameworks/$prefix.html")) { $missing += $prefix }
$count++
}
Write-Output ("generated {0} test pages" -f $count)
# 总览页
$byCat = @{}
foreach ($c in $idx.components) {
$cat = if ($slugCat.ContainsKey($c.slug)) { $slugCat[$c.slug] } else { 'general' }
if (-not $byCat.ContainsKey($cat)) { $byCat[$cat] = @() }
$byCat[$cat] += $c
$indexHtml = $indexTemplate.Replace('__COUNT__', [string]$count)
Write-NoBom "tests/index.html" $indexHtml
Write-Output ("generated {0} test pages + tests/index.html" -f $count)
if ($missing.Count -gt 0) {
Write-Output ("WARNING missing demo pages ({0}): {1}" -f $missing.Count, ($missing -join ', '))
}
$catOrder = @('general','navigation','input','display','feedback','system')
# 这里字符串用 @'...'@ 保留 < > 原样
$idxHead = @'
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>测试总览 — Aurora Admin 组件库</title>
<link rel="stylesheet" href="../.design_library/aurora-admin/colors_and_type.css">
<link rel="stylesheet" href="../site/style.css">
<style>
body { margin: 0; font-family: var(--au-font-family); color: var(--au-color-text-body); background: var(--au-color-page-bg); }
.shell { max-width: 1280px; margin: 0 auto; padding: 24px; }
.shell h1 { margin: 0 0 4px; font-size: 24px; color: var(--au-color-text-title); }
.shell .desc { margin: 0 0 20px; color: var(--au-color-text-secondary); font-size: 14px; }
.summary { display: flex; gap: 12px; margin-bottom: 24px; flex-wrap: wrap; }
.pill { height: 28px; padding: 0 12px; border-radius: var(--au-radius-base); background: var(--au-color-card-bg); border: 1px solid var(--au-color-border); display: inline-flex; align-items: center; font-family: var(--au-font-family-num); font-size: 13px; color: var(--au-color-text-body); }
.pill strong { margin-right: 4px; color: var(--au-color-brand); }
.cat { margin: 24px 0 12px; }
.cat h2 { margin: 0 0 10px; font-size: 16px; color: var(--au-color-text-title); }
.cat h2 .en { font-family: var(--au-font-family-num); font-size: 12px; color: var(--au-color-text-secondary); margin-left: 8px; }
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 12px; }
.card { background: var(--au-color-card-bg); border: 1px solid var(--au-color-border); border-radius: var(--au-radius-medium); padding: 12px; transition: border-color .15s, box-shadow .15s; cursor: pointer; text-decoration: none; display: block; }
.card:hover { border-color: var(--au-color-brand); box-shadow: var(--au-shadow-low); }
.card .zh { font-size: 14px; color: var(--au-color-text-title); }
.card .en { font-family: var(--au-font-family-num); font-size: 11px; color: var(--au-color-text-secondary); }
</style>
</head>
<body>
<div class="shell">
<h1>测试总览 <a href="../site/index.html" style="font-size:13px; margin-left:8px;">← 文档站</a></h1>
<p class="desc">为每个组件生成独立测试页(断言协议 + 通用测试矩阵)。点击卡片进入测试页;通过率会写 localStorage[aa-test-log]。</p>
<div class="summary">
<span class="pill"><strong>__COUNT__</strong> 个组件测试</span>
<span class="pill" id="passed">通过:—</span>
<span class="pill" id="failed">失败:—</span>
</div>
'@
$idxBody = ''
foreach ($cat in $catOrder) {
if (-not $byCat.ContainsKey($cat)) { continue }
$items = $byCat[$cat]
$zhCat = $catZh[$cat]
$idxBody += ' <div class="cat">' + "`n <h2>$zhCat <span class=`"en`">$cat</span></h2>`n <div class=`"grid`">`n"
foreach ($c in $items) {
$slug = $c.slug
$name = $c.name
$parts = $name -split ' '
$zh2 = $parts[0]
$en2 = $parts[-1]
$idxBody += ' <a class="card" data-slug="' + $slug + '" href="' + $slug + '.html"><div class="zh">' + $zh2 + '</div><div class="en">' + $en2 + '</div></a>' + "`n"
}
$idxBody += " </div>`n </div>`n"
}
$idxFoot = @'
</div>
<script src="../site/logger.js"></script>
<script>
(function () {
if (!window.aaLogger) return;
var s = window.aaLogger.summary();
var p = document.getElementById('passed');
var f = document.getElementById('failed');
if (p) p.textContent = '通过:' + s.testPass;
if (f) f.textContent = '失败:' + s.testFail;
})();
</script>
</body>
</html>
'@
$idxFinal = $idxHead -replace '__COUNT__', $count
$idxFinal = $idxFinal + "`n" + $idxBody + "`n" + $idxFoot
Set-Content -Path "tests/index.html" -Value $idxFinal -Encoding UTF8
Write-Output ("generated tests/index.html")
Write-Output ("done.")
Write-Output "done."