Files
wordpress-theme-aksrt/tests/admin/submit-settings-reference-contract.test.cjs
T
kate522 264f2c51b2 fix(admin): replicate reference front-submit settings page 1:1
- Rewrite post_list/submit schema (PHP + JS) with the reference theme's
  field set, order, defaults and dependencies; topics fields excluded
- Map PostDisplay::get_submit_settings() and the FrontSubmit runtime to
  the new keys without legacy fallback
- Guest submission follows the capability matrix; category required on
  submit; pay limits read from the cash/points fieldset
- Drop dead front_submit_* settings, template branches and JS config
- Add submit settings reference contract test
2026-09-24 12:29:19 +08:00

184 lines
8.0 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 submit section)字段键、顺序、默认值与
* JS schema(assets/admin/src/schema/sections/post-list/submit.js)一致;
* 2) 参考主题字段全集存在:post_article_s / post_article_btn_txte / post_article_cat /
* post_article_custom_filter_s / post_article_tag / post_article_user /
* post_article_title_strlen_limit / post_article_pay_free_s /
* post_article_pay_price_limit / post_article_pay_vip_price_s /
* post_article_pay_vip_1_discount / post_article_pay_vip_2_discount /
* post_t_placeholder / post_c_placeholder;
* 3) 专题(topics)相关字段不存在;
* 4) 旧 AKSRT 私有字段(front_submit_*、post_article_title_strlen_limit_min/max、
* post_article_pay_price_min/max)已彻底移除,无双路兼容;
* 5) 运行时(PostDisplay::get_submit_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/submit.js'), 'utf8');
const runtime = fs.readFileSync(path.join(root, 'src/Settings/PostDisplay.php'), 'utf8');
const REFERENCE_KEYS = [
'post_article_s',
'post_article_btn_txte',
'post_article_cat',
'post_article_custom_filter_s',
'post_article_tag',
'post_article_user',
'post_article_title_strlen_limit',
'post_article_pay_free_s',
'post_article_pay_price_limit',
'post_article_pay_vip_price_s',
'post_article_pay_vip_1_discount',
'post_article_pay_vip_2_discount',
'post_t_placeholder',
'post_c_placeholder',
];
// PHP 源码中字段定义的完整出现顺序(含 post_article_pay_price_limit 的
// fieldset 嵌套子字段 price / points)。
const PHP_FIELD_ORDER = [
...REFERENCE_KEYS.slice(0, REFERENCE_KEYS.indexOf('post_article_pay_vip_price_s')),
'price',
'points',
...REFERENCE_KEYS.slice(REFERENCE_KEYS.indexOf('post_article_pay_vip_price_s')),
];
const LEGACY_KEYS = [
'front_submit_enabled',
'front_submit_roles',
'front_submit_title',
'front_submit_content',
'front_submit_excerpt',
'front_submit_cover',
'front_submit_category',
'front_submit_guest_enabled',
'front_submit_tags',
'front_submit_custom_fields',
'front_submit_review_mode',
'front_submit_cover_required',
'front_submit_max_images',
'front_submit_editor_mode',
'front_submit_draft_save',
'front_submit_leave_warning',
'post_article_title_strlen_limit_min',
'post_article_title_strlen_limit_max',
'post_article_pay_price_min',
'post_article_pay_price_max',
];
/** Extract the PHP 'submit' => [ ... ] section body (balanced brackets). */
function phpSubmitSection(src) {
const start = src.indexOf("'submit' => [");
assert.notEqual(start, -1, 'PHP schema has a submit 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 submit section');
}
const submitPhp = phpSubmitSection(php);
test('PHP submit 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(submitPhp)) !== null) found.push(m[1]);
assert.deepEqual(found, PHP_FIELD_ORDER);
});
test('PHP submit section 不含专题字段', () => {
assert.doesNotMatch(submitPhp, /post_article_topics/);
assert.doesNotMatch(submitPhp, /'topics'/);
});
test('PHP submit section 默认值对齐参考主题', () => {
assert.match(submitPhp, /'post_article_s',\s*'label'\s*=>\s*'启用前台投稿',\s*'type'\s*=>\s*'switcher',\s*'default'\s*=>\s*true/);
assert.match(submitPhp, /'post_article_btn_txte',\s*'label'\s*=>\s*'按钮文字',\s*'type'\s*=>\s*'input',\s*'default'\s*=>\s*'发布文章'/);
assert.match(submitPhp, /'post_article_user',\s*'label'\s*=>\s*'免登陆投稿发布用户',\s*'type'\s*=>\s*'user_select',\s*'default'\s*=>\s*1/);
assert.match(submitPhp, /'post_article_title_strlen_limit',\s*'label'\s*=>\s*'标题字数限制',\s*'type'\s*=>\s*'between_number',\s*'default'\s*=>\s*\['min'\s*=>\s*5,\s*'max'\s*=>\s*30\]/);
assert.match(submitPhp, /'post_article_pay_vip_1_discount',\s*'label'\s*=>\s*'一级会员折扣',\s*'type'\s*=>\s*'number',\s*'default'\s*=>\s*100/);
assert.match(submitPhp, /'post_article_pay_vip_2_discount',\s*'label'\s*=>\s*'二级会员折扣',\s*'type'\s*=>\s*'number',\s*'default'\s*=>\s*100/);
assert.match(submitPhp, /'post_t_placeholder',\s*'label'\s*=>\s*'投稿标题占位符',\s*'type'\s*=>\s*'input',\s*'default'\s*=>\s*'请输入标题'/);
assert.match(submitPhp, /'post_c_placeholder',\s*'label'\s*=>\s*'投稿内容占位符',\s*'type'\s*=>\s*'input',\s*'default'\s*=>\s*'请输入内容'/);
assert.match(submitPhp, /'price',\s*'label'\s*=>\s*'现金金额限制'/);
assert.match(submitPhp, /'points',\s*'label'\s*=>\s*'积分金额限制'/);
});
test('会员折扣仅在关闭手动会员价时显示(对齐参考主题 dependency)', () => {
assert.match(
submitPhp,
/'post_article_pay_vip_1_discount'[\s\S]*?'dependsOn'\s*=>\s*\['all'\s*=>\s*\[\['key'\s*=>\s*'post_article_s',\s*'value'\s*=>\s*true\],\s*\['key'\s*=>\s*'post_article_pay_vip_price_s',\s*'value'\s*=>\s*false\]\]\]/
);
assert.match(
submitPhp,
/'post_article_pay_vip_2_discount'[\s\S]*?'dependsOn'\s*=>\s*\['all'\s*=>\s*\[\['key'\s*=>\s*'post_article_s',\s*'value'\s*=>\s*true\],\s*\['key'\s*=>\s*'post_article_pay_vip_price_s',\s*'value'\s*=>\s*false\]\]\]/
);
});
test('JS schema 与 PHP 字段键一致(ui_only content 行除外)', () => {
// 字段定义为 { 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}`);
}
// 两个 ui_only 提示行承载参考主题 desc 中的【去设置】链接。
assert.match(js, /permissions\/capabilities/);
assert.match(js, /post-list\/filter/);
assert.match(js, /ui_only:\s*true/);
});
test('JS schema 默认值对齐参考主题', () => {
assert.match(js, /post_article_user:\s*1\b/);
assert.match(js, /post_article_pay_vip_1_discount:\s*100\b/);
assert.match(js, /post_article_pay_vip_2_discount:\s*100\b/);
assert.match(js, /post_t_placeholder:\s*'请输入标题'/);
assert.match(js, /post_c_placeholder:\s*'请输入内容'/);
assert.match(js, /price:\s*\{\s*min:\s*0,\s*max:\s*99999\s*\}/);
});
test('旧字段在 schema 与运行时中均已移除(无双路)', () => {
for (const legacy of LEGACY_KEYS) {
assert.ok(!php.includes(legacy), `PHP schema 残留旧字段: ${legacy}`);
assert.ok(!runtime.includes(legacy), `PostDisplay 残留旧字段: ${legacy}`);
}
// 运行时读取新键。
assert.match(runtime, /\$settings\['post_article_s'\]/);
assert.match(runtime, /\$settings\['post_article_title_strlen_limit'\]/);
assert.match(runtime, /\$settings\['post_article_pay_price_limit'\]/);
assert.match(runtime, /\$settings\['post_article_tag'\]/);
assert.match(runtime, /\$settings\['post_article_user'\]/);
});