Files
wordpress-theme-aksrt/tests/widget-system/single-cover-height-contract.test.cjs
T
kate522 d3eef19b95 fix(single): match the reference video cover height (bare ratio)
Zibll reserves +50px only for covers whose title overlays them
(image/slide via .single-cover/.single-cover-slide); the video cover
keeps its title in the footer below and uses the bare scale height
(.dplayer-scale-height). Drop the +50px from the video box, pin the
reference height matrix in the contract test, and refresh the stale
slide-action assertion in the toast contract.
2026-09-06 11:12:13 +08:00

194 lines
7.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use strict';
// 文章详情页顶部封面高度契约(对齐参考主题):
// 单图/多图(轮播)封面标题叠加封面,高度 = calc(比例% + 50px)(zibll .single-cover /
// .single-cover-slide);视频封面标题在下方 footer、无叠加,高度 = 裸比例%
// (zibll .dplayer-scale-height)。
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const test = require('node:test');
const root = path.resolve(__dirname, '..', '..');
const read = (file) => fs.readFileSync(path.join(root, file), 'utf8');
const singleRender = read('inc/helpers/aksrt-single-render.php');
const sliderHelper = read('inc/helpers/aksrt-slider.php');
test('single cover: image mode keeps the calc(scale% + 50px) height', () => {
assert.match(
singleRender,
/single-cover imgbox-container" style="padding-bottom:calc\(<\?php echo esc_attr\(\(string\) \$img_scale\); \?>% \+ 50px\);"/,
'单图封面必须输出 calc(img_scale% + 50px) 内联高度'
);
});
test('single cover: video mode uses the bare ratio height like the reference dplayer box', () => {
assert.match(
singleRender,
/single-cover--video imgbox-container" style="padding-bottom:<\?php echo esc_attr\(\(string\) \$video_scale\); \?>%;"/,
'视频封面必须输出裸 video_scale% 高度(标题在下方 footer,无叠加补偿)'
);
// +50px 是标题叠加封面的补偿量,仅单图/多图封面适用;视频封面
// 对齐参考主题 .dplayer-scale-height,不得继承该公式。
assert.doesNotMatch(
singleRender,
/single-cover--video imgbox-container" style="padding-bottom:calc\(/,
'视频封面禁止输出 +50px 叠加补偿高度'
);
});
test('single cover: slide mode reserves the same +50px via the slider helper', () => {
assert.match(
singleRender,
/'scale_extra'\s*=>\s*'50px'/,
'多图封面必须向 aksrt_render_slider 传入 scale_extra=50px'
);
assert.match(
sliderHelper,
/'scale_extra'\s*=>\s*''/,
'slider helper 需声明 scale_extra 默认参数'
);
// wrapper 的 padding-bottom 必须由比例 + 附加量组合而成。
assert.match(
sliderHelper,
/\$scale_padding = 'calc\(' \. \$scale_padding \. ' \+ ' \. \$scale_extra \. '\)';/,
'scale 模式必须把 scale_extra 合成 calc 表达式'
);
});
test('single cover: the dead static slide fallback is gone', () => {
assert.doesNotMatch(
singleRender,
/function_exists\('aksrt_render_slider'\)/,
'aksrt_render_slider 由 bootstrap 无条件加载,不得保留 function_exists 双路判断'
);
assert.doesNotMatch(
singleRender,
/single-cover--slide imgbox-container" style="padding-bottom/,
'多图封面只允许经由 slider helper 输出,禁止保留静态 fallback 分支'
);
});
test('slider scale mode: completes the definite height chain for cover cropping', () => {
assert.doesNotMatch(
sliderHelper,
/--scale-ratio/,
'--scale-ratio 无任何 CSS 消费方,禁止输出'
);
// wrapper 决定盒子高度;pc/m-height 100% 让 .swiper-wrapper/.swiper-slide
// 铺满比例盒,使 object-fit: cover 在确定的盒内居中裁切(对齐单图封面)。
assert.match(
sliderHelper,
/\$height_style = '--pc-height:100%;--m-height:100%;';/
);
assert.match(
sliderHelper,
/esc_attr\(\$height_style \. \(\$args\['scale_height'\] \? 'position:absolute;inset:0;width:100%;height:100%;' : ''\)\)/,
'内层 .new-swiper 必须绝对定位铺满 wrapper'
);
});
test('video cover: the player video fills and center-crops like the image cover', () => {
const player = read('assets/js/video-player.js');
assert.match(
player,
/\.aksrt-video video\{display:block;width:100%;height:100%;object-fit:cover;\}/,
'视频必须铺满封面比例盒并居中裁切'
);
assert.doesNotMatch(
player,
/max-height:70vh/,
'封面盒高度由比例盒决定,不得再套 70vh 截断'
);
assert.doesNotMatch(
player,
/video\.style\.cssText/,
'视频不得携带内联样式:内联 object-fit 会以更高优先级覆盖注入样式表的 cover 契约,产生信箱式黑边'
);
});
test('video cover: play never runs against a sourceless player', () => {
const player = read('assets/js/video-player.js');
// toggle 必须先守卫无源状态,且 play() 的 promise 必须被捕获。
assert.match(player, /hasSource\s*=\s*function\s*\(\)\s*\{[^}]*currentSrc[^}]*\}/);
assert.match(player, /toggle\s*=\s*function\s*\(\)\s*\{\s*if\s*\(!this\.hasSource\(\)\)\s*return;/);
assert.match(player, /this\.video\.play\(\)\.catch\(function/);
// hls/flv CDN 脚本加载失败必须收敛,不得产生未处理 rejection。
assert.match(player, /loadScript\(HLS_SRC\)\.then\([\s\S]*?\}\)\.catch\(function/);
assert.match(player, /loadScript\(FLV_SRC\)\.then\([\s\S]*?\}\)\.catch\(function/);
// iframe 剧集归 runtime main 的 .switch-iframe 链路,播放器不得接管其选择器。
assert.doesNotMatch(player, /closest\('\.switch-video, \.switch-iframe'\)/);
assert.doesNotMatch(player, /querySelectorAll\('\.switch-video, \.switch-iframe'\)/);
});
test('resource singles get the cover leaf versioned, never via the aggregate @import', () => {
// 聚合文件的 @import 无版本号:浏览器会拿旧缓存(含已废弃的 .single-cover
// padding 规则)把封面高度链挤成 0,资源型文章封面直接空白。
assert.doesNotMatch(
read('assets/css/frontend/single-post.css'),
/@import[^;]*single-post\/cover\.css/,
'聚合样式不得再 @import cover leaf'
);
const enqueue = read('src/Core/ScriptsPart1b.php');
assert.match(
enqueue,
/if \(!empty\(\$singular_assets\['needs_cover'\]\)\) \{[\s\S]{0,400}?single-post\/cover\.css/,
'资源单页必须按 needs_cover 以 filemtime 版本号入队 cover leaf'
);
assert.match(
enqueue,
/'aksrt-single-post-cover'/,
'cover leaf 句柄与文章分支命名保持一致'
);
});
test('single cover css owns no fallback padding and the scale var is gone', () => {
for (const file of [
'assets/css/frontend/single-post/cover.css',
'assets/css/main.css',
'assets/css/main.min.css',
'assets/css/core/tokens.css',
'assets/css/build/public.css',
'assets/css/build/public.min.css',
]) {
const css = read(file);
assert.doesNotMatch(
css,
/\.single-cover\s*\{[^}]*padding-bottom/,
`${file} 的 .single-cover 不得声明 padding-bottom(高度归内联样式)`
);
assert.doesNotMatch(
css,
/--single-cover-scale/,
`${file} 不得再包含孤儿变量 --single-cover-scale`
);
}
assert.doesNotMatch(
read('src/Frontend/InlineStyleBuilder.php'),
/--single-cover-scale/,
'运行时 CSS 变量不得再输出孤儿 --single-cover-scale'
);
});
test('dead cover css is removed from every stylesheet surface', () => {
for (const file of [
'assets/css/frontend/single-post/cover.css',
'assets/css/main.css',
'assets/css/main.min.css',
'assets/css/build/public.css',
'assets/css/build/public.min.css',
'assets/css/frontend/enhancements/components/base.css',
]) {
const css = read(file);
assert.doesNotMatch(css, /\.single-cover-slide[,{ ]/, `${file} 不得再包含死的 .single-cover-slide 规则`);
assert.doesNotMatch(css, /\.single-cover-slider\b/, `${file} 不得再包含死的 .single-cover-slider 规则`);
}
assert.doesNotMatch(
read('assets/js/core/misc.js'),
/initSlideCovers/,
'misc.js 的 initSlideCovers(无标记生产方)已删除'
);
});