const test = require('node:test') const assert = require('node:assert/strict') const fs = require('node:fs') const path = require('node:path') const root = path.resolve(__dirname, '../..') const read = file => fs.readFileSync(path.join(root, file), 'utf8') const list = read('src/Admin/Native/PostListEnhance.php') const fields = read('src/Admin/Native/PostListEditFields.php') const domain = read('src/Core/DomainModel.php') const pay = read('inc/helpers/aksrt-pay.php') const metabox = read('src/Core/PostMetaBox.php') const resourceBox = read('src/Core/ResourceMetaBox.php') test('quick and bulk edit boxes hook the post list with no topics column', () => { // 参考主题把配置项挂在「专题」分类法列上;本项目没有专题,统一挂在互动数据列。 assert.match(list, /quick_edit_custom_box/) assert.match(list, /bulk_edit_custom_box/) assert.match(list, /COLUMN_STATS/) // 代码里不得再出现专题分类法列(类文档块的说明除外,只看 namespace 之后的部分)。 const code = list.slice(list.indexOf('namespace AKSRT')) assert.doesNotMatch(code, /taxonomy-topics/) assert.doesNotMatch(code, /topics/) }) test('every quick-edit field group is rendered', () => { // 四组字段的入口函数与分组标识都要存在,缺一组该列就少一块配置。 assert.match(list, /render_edit_boxes/) assert.match(list, /PostListEditFields::extended\(\)/) assert.match(list, /PostListEditFields::pay\(\$this->currency_symbol\(\)\)/) assert.match(list, /filter_fields_html\(\)/) assert.match(list, /'pay'/) assert.match(list, /'filter'/) // 字段定义本体放在独立的 PostListEditFields,两个静态方法必须真的存在。 assert.match(fields, /function extended\(\): array/) assert.match(fields, /function pay\(string \$currency\): array/) }) test('quick-edit inputs are namespaced under aksrt_bulk_edit', () => { // 字段名统一 aksrt_bulk_edit[{id}][{field}],避免与 WP 原生数据冲突。 assert.match(list, /aksrt_bulk_edit\[' \. \$id \. '\]\[' \. \$field\['id'\] \. '\]/) // number 类型带运算子字段。 assert.match(list, /\[operation\]/) assert.match(list, /\[val\]/) }) test('number fields support the full operation set', () => { for (const op of ['ignore', 'set', 'plus', 'subtract', 'multiply', 'division']) { assert.match(list, new RegExp(`value="${op}"`), `${op} operation exists`) } }) test('all control types used by the fields exist in the renderer', () => { for (const type of ['number', 'checkbox', 'switcher', 'radio', 'select']) { assert.match(list, new RegExp(`case '${type}':`), `${type} renderer exists`) } }) test('interaction quick-edit fields write to the keys the theme reads', () => { // 阅读量/点赞数是唯一可批量设置的互动项,键必须与前端 helper 一致。 assert.match(fields, /'views'/) assert.match(fields, /'like'/) assert.match(metabox, /DomainModel::POST_VIEW_COUNT_KEY, max\(0, \(int\) \(\$raw\['views'\]/) assert.match(metabox, /'_aksrt_like_count', max\(0, \(int\) \(\$raw\['like'\]/) // 保存侧按这两个键分流到统计 meta。 assert.match(list, /stats_meta_key/) }) test('article config quick-edit fields match the extension meta box keys', () => { // 显示布局 + 两个开关必须与「文章扩展」metabox 的键名一致。 assert.match(metabox, /show_layout/) assert.match(metabox, /no_article-navs/) assert.match(metabox, /article_maxheight_xz/) // 布局取值白名单:metabox 的 sanitize 是裸数组,快捷编辑提供同四值的单选。 assert.match(metabox, /\['false', 'no_sidebar', 'sidebar_left', 'sidebar_right'\]/) assert.match(fields, /'false'\s+=> __\('跟随主题', 'aksrt'\)/) assert.match(fields, /'no_sidebar'\s+=> __\('无侧边栏', 'aksrt'\)/) assert.match(fields, /'sidebar_left'\s+=> __\('侧边栏靠左', 'aksrt'\)/) assert.match(fields, /'sidebar_right'\s+=> __\('侧边栏靠右', 'aksrt'\)/) // 保存侧再用同一份白名单收敛,避免快捷编辑写入未知布局。 assert.match(list, /\['false', 'no_sidebar', 'sidebar_left', 'sidebar_right'\]/) // 落到 aksrt_post_meta 这个共享 bag 上,与 metabox 同一键(self::META_KEY)。 assert.match(metabox, /post_meta/) assert.match(list, /PostMetaBox::META_KEY/) }) test('pay quick-edit fields map onto the real pay meta schema', () => { // 逐字段核对:快捷编辑写入的键必须存在于默认付费 meta / metabox 中。 const payKeys = [ 'pay_type', 'pay_modo', 'pay_limit', 'points_price', 'vip1_points', 'vip2_points', 'price', 'original_price', 'vip1_price', 'vip2_price', 'pay_count', 'coupon_s', 'order_expired_s', 'order_expired_time', 'order_expired_unit', 'show_sales', ] for (const key of payKeys) { assert.match(domain, new RegExp(`'${key}'\\s*=>`), `pay meta has ${key}`) assert.match(fields, new RegExp(`'${key}'`), `quick edit offers ${key}`) } }) test('pay quick-edit save sanitizes through the shared sanitizer', () => { // 任何批量写入都必须过 sanitize_pay_meta,再回写派生键,与 metabox 保存一致。 assert.match(list, /DomainModel::sanitize_pay_meta/) assert.match(list, /DomainModel::PAY_META_KEY, \$pay_meta/) assert.match(list, /DomainModel::PAY_TYPE_KEY/) assert.match(list, /DomainModel::PAY_PRICE_KEY/) assert.match(list, /'_aksrt_pay_modo'/) assert.match(list, /'_aksrt_pay_limit'/) assert.match(list, /RESOURCE_MARKER_KEY/) }) test('pay prices go through the ledger, not raw floats', () => { // 金额字段运算在「分」上进行,避免浮点误差。 assert.match(list, /balance_minor|price_to_minor/) assert.match(list, /decimal_from_minor/) assert.match(list, /\$pay_meta\[\$field_id\] = \\AKSRT\\Ledger\\AssetAmount::decimal_from_minor/) }) test('quick-edit save ignores invalid operations and clamps negatives', () => { assert.match(list, /apply_operation/) assert.match(list, /case 'set'/) assert.match(list, /case 'plus'/) assert.match(list, /case 'subtract'/) assert.match(list, /case 'multiply'/) assert.match(list, /case 'division'/) // 除法要防零。 assert.match(list, /\$current != 0 && \$value != 0/) // 负值归零。 assert.match(list, /max\(0, \(int\) \$new\)/) }) test('custom filter quick-edit fields reuse the settings dimensions', () => { // 高级筛选项从同一份设置里动态生成,键名前缀与 metabox 保存一致。 assert.match(list, /get_post_filter_settings/) assert.match(list, /custom_filter/) assert.match(list, /'_aksrt_cf_' \. \$field_id/) assert.match(metabox, /'_aksrt_cf_' \. \$key/) }) test('quick-edit save is guarded by screen, autosave, revision and capability', () => { assert.match(list, /DOING_AUTOSAVE/) assert.match(list, /wp_is_post_revision/) assert.match(list, /current_user_can\('edit_post', \$post_id\)/) assert.match(list, /edit-post/) // 只处理 post 类型,不动页面、CPT 与其他列表。 assert.match(list, /\$post->post_type !== 'post'/) }) test('no legacy zib-style field names or topic links survive', () => { assert.doesNotMatch(list, /zib_bulk_edit/) assert.doesNotMatch(list, /posts_zibpay/) assert.doesNotMatch(list, /zibpay_type/) assert.doesNotMatch(list, /pay_cuont/) // 本项目不使用「自动售卡」类目。 assert.doesNotMatch(list, /售卡/) }) test('the old inline column code was fully migrated', () => { // inc/core-hooks.php 不再有任何文章列表列/排序逻辑,全部由新类负责。 // (L295 的 pre_get_posts 属于前台查询,不是后台排序列增强;这里只锁列表增强的部分。) const hooks = read('inc/core-hooks.php') assert.doesNotMatch(hooks, /manage_post_posts_columns/) assert.doesNotMatch(hooks, /manage_post_posts_custom_column/) assert.doesNotMatch(hooks, /manage_edit-post_sortable_columns/) assert.doesNotMatch(hooks, /aksrt_enqueue_fontawesome/) assert.doesNotMatch(hooks, /aksrt_stats/) }) test('the dead tab-pane and modal css blocks are removed', () => { // 这些选择器无任何引用,已确认删除;文件仍保留 quick-edit 需要的轮子类。 const css = read('assets/css/admin-main.css') assert.doesNotMatch(css, /aksrt-tab-pane/) assert.doesNotMatch(css, /\.aksrt-modal\b/) assert.doesNotMatch(css, /\.aksrt-modal-backdrop\b/) // 快捷编辑卡片与依赖样式仍在。 assert.match(css, /\.aksrt-bulk-edit-box/) assert.match(css, /\.aksrt-bulk-edit-desc/) assert.match(css, /\.flex\s*\{/) }) test('the quick-edit dependency script exists and toggles by data-dependency', () => { assert.ok(fs.existsSync(path.join(root, 'assets/js/admin-post-list.js'))) const js = read('assets/js/admin-post-list.js') assert.match(js, /data-dependency/) assert.match(js, /aksrt_bulk_edit\[pay\]/) assert.match(js, /inline-edit-row/) })