This commit is contained in:
fuenmao
2026-10-09 18:06:21 +08:00
parent 563836f789
commit 20467ca479
18 changed files with 547 additions and 102 deletions
+45 -1
View File
@@ -5,7 +5,7 @@ import fs from 'node:fs'
import path from 'node:path'
import vm from 'node:vm'
import { fileURLToPath } from 'node:url'
import { parse, compileScript } from 'vue/compiler-sfc'
import { parse, compileScript, compileStyle } from 'vue/compiler-sfc'
import { createSSRApp, h, isRef } from 'vue'
import { renderToString } from 'vue/server-renderer'
import { createMemoryHistory, createRouter } from 'vue-router'
@@ -183,3 +183,47 @@ test('auth and both questionnaires render safely; loading records never show an
assert.ok(!html.includes('type="number"')); assert.ok(!html.includes('<textarea'))
}
})
test('reduced-motion stops all loading animations rather than making infinite loops faster', () => {
const readStylesheet = relative => {
const filename = path.join(root, relative)
const result = compileStyle({ source: fs.readFileSync(filename, 'utf8'), filename, id: 'test-motion' })
assert.deepEqual(result.errors, [], relative)
return result.rawResult.root
}
const stylesheet = readStylesheet('styles/page-polish.css')
const reducedMotion = stylesheet.nodes.find(node => node.type === 'atrule'
&& node.name === 'media' && node.params === '(prefers-reduced-motion: reduce)')
assert.ok(reducedMotion, 'keep the reduced-motion preference supported')
const rule = reducedMotion.nodes.find(node => node.type === 'rule'
&& ['*', '*::before', '*::after'].every(selector => node.selectors.includes(selector)))
assert.ok(rule, 'cover page indicators, device indicators and Vant loading animations')
const declarations = Object.fromEntries(rule.nodes.filter(node => node.type === 'decl')
.map(node => [node.prop, node]))
assert.equal(declarations.animation?.value, 'none')
assert.equal(declarations.animation?.important, true)
assert.equal(declarations['transition-duration']?.value, '0s')
assert.equal(declarations['transition-duration']?.important, true)
reducedMotion.walkDecls('animation-duration', declaration => {
assert.notEqual(declaration.value, '.01ms', 'never accelerate an infinite loading animation')
})
const loadingRule = stylesheet.nodes.find(node => node.type === 'rule' && node.selector === '.page-loading-mark')
assert.ok(loadingRule.nodes.some(node => node.prop === 'animation' && node.value === 'page-spin .9s linear infinite'),
'normal motion keeps the existing loading speed')
const deviceStyles = readStylesheet('styles/device-control.css')
const deviceLoadingRule = deviceStyles.nodes.find(node => node.type === 'rule' && node.selector === '.control-page .operation-spinner')
assert.ok(deviceLoadingRule.nodes.some(node => node.prop === 'animation' && node.value === 'operation-spin 0.75s linear infinite'))
})
test('loading indicators keep visible status text even when their animation is stopped', async () => {
for (const [name, routePath, message] of [
['DashboardView', '/dashboard', '正在读取健康档案与舒睡设备…'],
['ProfileView', '/profile', '正在读取您的健康档案…'],
]) {
const html = await render(name, { loading: true }, routePath)
assert.ok(html.includes('page-loading-mark'), name)
assert.ok(html.includes(message), name)
assert.ok(html.includes('role="status"'), name)
}
})