Files
wordpress-theme-aksrt/tests/admin/comment-settings-reference-contract.test.cjs
T
kate522 c331b2aeef fix(admin): replicate reference comment settings page 1:1
- Rewrite post_list/comment schema (PHP + JS) with the reference theme's
  field set, order, defaults and dependencies; smilie, comment location
  and quick-reply fields excluded
- Map PostDisplay::get_comment_display_settings() to the new keys without
  legacy fallback: close_comments, comment_paginate_type (ajax_lists),
  user_edit_comment master switch, WP comment_order for sorting
- Wire comment_box_hide visibility gate (signin / vip / vip_2 / auth)
- Drop dead comment settings (order/per_page/depth/avatar/hot threshold/
  empty text/city), the dead post_close_comments field, and city readers
  now fully owned by the IP-geo extension
- Add comment settings reference contract test
2026-09-24 13:43:51 +08:00

225 lines
12 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';
/**
* 「评论设置」后台设置页契约(对齐参考主题 1:1):
* 1) PHP schema(SchemaPostList.php comment section)字段键、顺序、默认值与
* JS schema(assets/admin/src/schema/sections/post-list/comment.js)一致;
* 2) 参考主题字段全集存在:close_comments / comment_box_hide /
* comment_paginate_type / comment_paging_ajax_ias_s /
* comment_paging_ajax_ias_max / comment_code / comment_img /
* comment_upload_img / comment_link / comment_author_tag /
* user_edit_comment / comment_like_s / comment_user_url_s /
* comment_corderby / comment_title / comment_submit_text / comment_text;
* 3) 插入表情(comment_smilie)、评论地理位置(comment_city_s/comment_city_type,
* 由扩展&增强/IP归属地实现)不在本页;
* 4) 快捷回复按评论区硬切换契约保持移除(comment_quick_* 不存在);
* 5) 旧 AKSRT 私有字段(comment_order / comment_per_page / comment_pagination_type /
* comment_nested_depth / comment_avatar_size / comment_close_comments /
* comment_edit_window / comment_hot_threshold / comment_empty_text /
* post_close_comments)已彻底移除,无双路兼容;
* 6) 运行时(PostDisplay::get_comment_display_settings)只读取新键。
*/
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 php = fs.readFileSync(path.join(root, 'src/Admin/Schema/SchemaPostList.php'), 'utf8');
const js = fs.readFileSync(path.join(root, 'assets/admin/src/schema/sections/post-list/comment.js'), 'utf8');
const runtime = fs.readFileSync(path.join(root, 'src/Settings/PostDisplay.php'), 'utf8');
const commentsHelper = fs.readFileSync(path.join(root, 'inc/helpers/aksrt-comments.php'), 'utf8');
const geoHelper = fs.readFileSync(path.join(root, 'inc/helpers/comments/geo.php'), 'utf8');
const actionsHelper = fs.readFileSync(path.join(root, 'inc/helpers/comments/actions.php'), 'utf8');
const REFERENCE_KEYS = [
'close_comments',
'comment_box_hide',
'comment_paginate_type',
'comment_paging_ajax_ias_s',
'comment_paging_ajax_ias_max',
'comment_code',
'comment_img',
'comment_upload_img',
'comment_link',
'comment_author_tag',
'user_edit_comment',
'comment_like_s',
'comment_user_url_s',
'comment_corderby',
'comment_title',
'comment_submit_text',
'comment_text',
];
// 参考主题明确不做 / 本项目不做 / 其他地方实现的字段。
const EXCLUDED_KEYS = [
'comment_smilie',
'comment_city_s',
'comment_city_type',
'comment_quick_s',
'comment_quick_often',
];
// 旧 AKSRT 私有字段(含已并入 WP 讨论设置与死字段 post_close_comments)。
const LEGACY_KEYS = [
'comment_order',
'comment_per_page',
'comment_pagination_type',
'comment_nested_depth',
'comment_avatar_size',
'comment_close_comments',
'comment_edit_window',
'comment_hot_threshold',
'comment_empty_text',
'post_close_comments',
];
/** Extract the PHP 'comment' => [ ... ] section body (balanced brackets). */
function phpCommentSection(src) {
const start = src.indexOf("'comment' => [");
assert.notEqual(start, -1, 'PHP schema has a comment section');
const open = src.indexOf('[', start);
let depth = 0;
for (let i = open; i < src.length; i++) {
const ch = src[i];
if (ch === "'" || ch === '"') {
const quote = ch;
i++;
while (i < src.length) {
if (src[i] === '\\') { i += 2; continue; }
if (src[i] === quote) break;
i++;
}
continue;
}
if (ch === '[') depth++;
else if (ch === ']') {
depth--;
if (depth === 0) return src.slice(open, i + 1);
}
}
throw new Error('unbalanced comment section');
}
const commentPhp = phpCommentSection(php);
test('PHP comment section 字段键与参考主题一致且顺序相同', () => {
// 字段定义均为 ['key' => '...', 'label' => ...] 开头;dependsOn 内的
// ['key' => '...', 'value' => ...] 不是字段定义,须排除。
const found = [];
const re = /'key'\s*=>\s*'([a-z0-9_]+)',\s*'label'/g;
let m;
while ((m = re.exec(commentPhp)) !== null) found.push(m[1]);
assert.deepEqual(found, REFERENCE_KEYS);
});
test('PHP comment section 不含表情/地理位置/快捷回复字段', () => {
for (const key of EXCLUDED_KEYS) {
assert.doesNotMatch(commentPhp, new RegExp(`'${key}'`), `comment section 不应包含 ${key}`);
}
});
test('PHP comment section 默认值对齐参考主题', () => {
assert.match(commentPhp, /'close_comments',\s*'label'\s*=>\s*'关闭文章评论功能',\s*'type'\s*=>\s*'switcher',\s*'default'\s*=>\s*false/);
assert.match(commentPhp, /'comment_box_hide',\s*'label'\s*=>\s*'隐藏评论模块',\s*'type'\s*=>\s*'radio',\s*'default'\s*=>\s*''/);
assert.match(commentPhp, /'comment_paginate_type',\s*'label'\s*=>\s*'评论列表翻页模式',\s*'type'\s*=>\s*'radio',\s*'default'\s*=>\s*'default'/);
assert.match(commentPhp, /'comment_paging_ajax_ias_s',\s*'label'\s*=>\s*'AJAX翻页自动加载',\s*'type'\s*=>\s*'switcher',\s*'default'\s*=>\s*true/);
assert.match(commentPhp, /'comment_paging_ajax_ias_max',\s*'label'\s*=>\s*'自动加载页数',\s*'type'\s*=>\s*'number',\s*'default'\s*=>\s*3/);
assert.match(commentPhp, /'comment_code',\s*'label'\s*=>\s*'允许插入代码',\s*'type'\s*=>\s*'switcher',\s*'default'\s*=>\s*true/);
assert.match(commentPhp, /'comment_img',\s*'label'\s*=>\s*'允许插入图片',\s*'type'\s*=>\s*'switcher',\s*'default'\s*=>\s*true/);
assert.match(commentPhp, /'comment_upload_img',\s*'label'\s*=>\s*'允许上传图片',\s*'type'\s*=>\s*'switcher',\s*'default'\s*=>\s*false/);
assert.match(commentPhp, /'comment_link',\s*'label'\s*=>\s*'允许插入链接',\s*'type'\s*=>\s*'switcher',\s*'default'\s*=>\s*false/);
assert.match(commentPhp, /'comment_author_tag',\s*'label'\s*=>\s*'显示“作者”标签',\s*'type'\s*=>\s*'switcher',\s*'default'\s*=>\s*true/);
assert.match(commentPhp, /'user_edit_comment',\s*'label'\s*=>\s*'允许用户编辑评论',\s*'type'\s*=>\s*'switcher',\s*'default'\s*=>\s*true/);
assert.match(commentPhp, /'comment_like_s',\s*'label'\s*=>\s*'评论点赞功能',\s*'type'\s*=>\s*'switcher',\s*'default'\s*=>\s*true/);
assert.match(commentPhp, /'comment_user_url_s',\s*'label'\s*=>\s*'匿名评论允许输入链接',\s*'type'\s*=>\s*'switcher',\s*'default'\s*=>\s*false/);
assert.match(commentPhp, /'comment_corderby',\s*'label'\s*=>\s*'评论排序功能',\s*'type'\s*=>\s*'switcher',\s*'default'\s*=>\s*true/);
assert.match(commentPhp, /'comment_title',\s*'label'\s*=>\s*'自定义评论标题',\s*'type'\s*=>\s*'textarea',\s*'default'\s*=>\s*'评论'/);
assert.match(commentPhp, /'comment_submit_text',\s*'label'\s*=>\s*'自定义评论提交按钮文案',\s*'type'\s*=>\s*'textarea',\s*'default'\s*=>\s*'提交评论'/);
assert.match(commentPhp, /'comment_text',\s*'label'\s*=>\s*'自定义评论框占位符文案',\s*'type'\s*=>\s*'input',\s*'default'\s*=>\s*'欢迎您留下宝贵的见解!'/);
});
test('PHP comment section 选项与依赖对齐参考主题', () => {
// 隐藏评论模块选项。
for (const option of ['不隐藏', '登录后显示', '所有会员显示', '二级会员显示', '认证用户显示']) {
assert.ok(commentPhp.includes(`'${option}'`), `comment_box_hide 缺少选项 ${option}`);
}
// 翻页模式选项。
for (const option of ['AJAX追加列表翻页', '数字翻页按钮']) {
assert.ok(commentPhp.includes(`'${option}'`), `comment_paginate_type 缺少选项 ${option}`);
}
assert.match(commentPhp, /'comment_box_hide'[\s\S]*?'dependsOn'\s*=>\s*\['key'\s*=>\s*'close_comments',\s*'value'\s*=>\s*false\]/);
assert.match(commentPhp, /'comment_paginate_type'[\s\S]*?'dependsOn'\s*=>\s*\['key'\s*=>\s*'close_comments',\s*'value'\s*=>\s*false\]/);
assert.match(commentPhp, /'comment_paging_ajax_ias_s'[\s\S]*?'dependsOn'\s*=>\s*\['key'\s*=>\s*'comment_paginate_type',\s*'value'\s*=>\s*'ajax_lists'\]/);
assert.match(commentPhp, /'comment_paging_ajax_ias_max'[\s\S]*?'dependsOn'\s*=>\s*\['all'/);
assert.match(commentPhp, /'comment_upload_img'[\s\S]*?'dependsOn'\s*=>\s*\['all'/);
assert.match(commentPhp, /'comment_corderby'[\s\S]*?'dependsOn'\s*=>\s*\['all'/);
// 允许插入链接在参考主题中没有依赖(其后紧跟 comment_author_tag)。
const linkIdx = commentPhp.indexOf("'comment_link'");
const authorTagIdx = commentPhp.indexOf("'comment_author_tag'");
assert.ok(linkIdx !== -1 && authorTagIdx > linkIdx, 'comment_link / comment_author_tag 顺序应符合参考主题');
const linkSlice = commentPhp.slice(linkIdx, authorTagIdx);
assert.ok(!linkSlice.includes('dependsOn'), 'comment_link 不应有 dependsOn');
});
test('JS schema 与 PHP 字段键一致(ui_only 提示行除外)', () => {
// 字段定义为 { key: '...', label: ... };dependsOn 内的 { key, value } 须排除。
const keys = [];
const re = /key:\s*'([a-z0-9_]+)',\s*label:/g;
let m;
while ((m = re.exec(js)) !== null) keys.push(m[1]);
const persisted = keys.filter((k) => REFERENCE_KEYS.includes(k));
assert.deepEqual(persisted, REFERENCE_KEYS);
for (const legacy of LEGACY_KEYS) {
assert.ok(!keys.includes(legacy), `JS schema 残留旧字段: ${legacy}`);
}
for (const excluded of EXCLUDED_KEYS) {
assert.ok(!keys.includes(excluded), `JS schema 不应包含 ${excluded}`);
}
// WP 讨论设置指引行承载参考主题 submessage 与【去设置】链接。
assert.match(js, /ui_only:\s*true/);
assert.match(js, /options-discussion\.php/);
assert.match(js, /分页显示评论/);
});
test('JS schema 默认值与选项对齐参考主题', () => {
assert.match(js, /close_comments:\s*false/);
assert.match(js, /comment_box_hide:\s*''/);
assert.match(js, /comment_paginate_type:\s*'default'/);
assert.match(js, /comment_paging_ajax_ias_s:\s*true/);
assert.match(js, /comment_paging_ajax_ias_max:\s*3/);
assert.match(js, /user_edit_comment:\s*true/);
assert.match(js, /comment_text:\s*'欢迎您留下宝贵的见解!'/);
for (const option of ['AJAX追加列表翻页', '数字翻页按钮', '登录后显示', '所有会员显示', '二级会员显示', '认证用户显示']) {
assert.ok(js.includes(option), `JS schema 缺少选项 ${option}`);
}
assert.match(js, /dependsOn:\s*\{\s*key:\s*'close_comments',\s*value:\s*false\s*\}/);
assert.match(js, /dependsOn:\s*\{\s*key:\s*'comment_paginate_type',\s*value:\s*'ajax_lists'\s*\}/);
});
test('旧字段在 schema 与运行时中均已移除(无双路)', () => {
for (const legacy of LEGACY_KEYS) {
assert.ok(!php.includes(legacy), `PHP schema 残留旧字段: ${legacy}`);
// 运行时按主题设置读取的形态 $settings['<legacy>'] 必须消失
// (comment_order 允许以 WP 讨论选项 get_option('comment_order') 形态存在)。
for (const source of [['PostDisplay', runtime], ['aksrt-comments.php', commentsHelper], ['comments/geo.php', geoHelper], ['comments/actions.php', actionsHelper]]) {
assert.ok(!source[1].includes(`$settings['${legacy}']`), `${source[0]} 残留旧字段读取: ${legacy}`);
}
}
// 运行时只读取新键。
assert.match(runtime, /\$settings\['close_comments'\]/);
assert.match(runtime, /\$settings\['user_edit_comment'\]/);
assert.match(runtime, /\$settings\['comment_box_hide'\]/);
assert.match(runtime, /Settings::get_value\('comment_paginate_type'/);
assert.match(runtime, /return \$mode === 'ajax_lists' \? 'ajax_lists' : 'numbered';/);
// 排序回退到 WP 讨论设置(参考主题行为)。
assert.match(runtime, /get_option\('comment_order', 'desc'\)/);
// 评论区位置显示交由扩展 IP 归属地控制。
assert.match(geoHelper, /aksrt_ip_geo_display_enabled\('comment'\)/);
assert.match(geoHelper, /aksrt_ip_geo_format_key/);
// 编辑权限使用 user_edit 总开关。
assert.match(actionsHelper, /\$display\['user_edit'\]/);
});