feat(admin): 新增后台文章列表销售数据列与快捷编辑配置项
「所有文章」列表此前只有互动数据列,缺少销售数据表头;快捷/批量编辑 展开后也只有原生三项。本次对照参考主题补齐这两处,不含专题链路。 - 新增 PostListEnhance:统管 sale/stats 两列、付费类型筛选、 orderby=sales_volume 排序,以及四组快捷编辑字段的渲染与保存 - 新增 PostListEditFields:单独承载字段定义,主类职责保持单一 - 新增 admin-post-list.js:按 pay_type/pay_modo 勾选切换字段显隐 - 从 inc/core-hooks.php 迁出原有列实现(删 74 行),不再保留旧闭包 - 清理 admin-main.css 中确认无引用的 .aksrt-tab-pane 与 .aksrt-modal* 死块 - 新增 post-list-quick-edit-contract 测试,更新 stats/icon-picker 契约 快捷编辑锚点列用互动数据列而非参考主题的专题分类法列(本项目无专题)。 真实销量改为页面级批量查询,避免逐行查库。金额运算统一走 Ledger 的 「分」单位,不用裸浮点。保存走 edit_post 钩子,一次覆盖快捷与批量两条路径。
This commit is contained in:
@@ -6,61 +6,89 @@ const path = require('node:path')
|
||||
const root = path.resolve(__dirname, '../..')
|
||||
const read = file => fs.readFileSync(path.join(root, file), 'utf8')
|
||||
|
||||
const hooks = read('inc/core-hooks.php')
|
||||
const list = read('src/Admin/Native/PostListEnhance.php')
|
||||
const domain = read('src/Core/DomainModel.php')
|
||||
const tags = read('inc/helpers/aksrt-frontend-tags.php')
|
||||
const pay = read('inc/helpers/aksrt-pay.php')
|
||||
|
||||
test('admin post list has the interaction stats column', () => {
|
||||
assert.match(hooks, /manage_post_posts_columns/)
|
||||
assert.match(hooks, /互动数据/)
|
||||
assert.match(hooks, /manage_post_posts_custom_column/)
|
||||
assert.match(hooks, /\$column !== 'aksrt_stats'/)
|
||||
assert.match(list, /manage_post_posts_columns/)
|
||||
assert.match(list, /互动数据/)
|
||||
assert.match(list, /manage_post_posts_custom_column/)
|
||||
assert.match(list, /COLUMN_STATS/)
|
||||
})
|
||||
|
||||
test('admin post list has the sales data column with a sortable header', () => {
|
||||
// 表头「销售数据」必须是可点击排序链接,排序键为 sales_volume。
|
||||
assert.match(list, /销售数据/)
|
||||
assert.match(list, /'aksrt_sales'/)
|
||||
assert.match(list, /orderby.*sales_volume/)
|
||||
|
||||
// 排序列必须在表头注册,并把 sales_volume 映射回真实存储键。
|
||||
assert.match(list, /manage_edit-post_sortable_columns/)
|
||||
assert.match(list, /sales_volume/)
|
||||
assert.match(list, /RESOURCE_SALES_VOLUME_KEY/)
|
||||
})
|
||||
|
||||
test('sales column renders pay type, price and both sales counts', () => {
|
||||
// 未开启付费 → 提示;已开启 → 类型链接 + 价格 + 销量。
|
||||
assert.match(list, /未开启付费/)
|
||||
assert.match(list, /pay_type_labels\(\)/)
|
||||
assert.match(list, /真实销量/)
|
||||
assert.match(list, /get_content_sales_map/)
|
||||
// 第一行取自 _aksrt_pay,第二行的销量取自 _aksrt_sales_volume。
|
||||
assert.match(list, /aksrt_get_pay_meta/)
|
||||
assert.match(list, /DomainModel::RESOURCE_SALES_VOLUME_KEY/)
|
||||
// 真实销量链接必须跳到订单页(aksrt_orders)并按商品 ID 过滤。
|
||||
assert.match(list, /'aksrt_orders'/)
|
||||
assert.match(list, /'product_id'/)
|
||||
})
|
||||
|
||||
test('interaction counts are read from the keys the theme actually writes', () => {
|
||||
// 计数一律落在这几个 meta key 上:aksrt_post_views / _aksrt_like_count /
|
||||
// _aksrt_favorite_count / _aksrt_res_download_count。列回调只能用同一套键,
|
||||
// 否则全列恒为 0。旧实现读 'views' / 'like' / 'favorite',仓库里没有任何写入方。
|
||||
// _aksrt_favorite_count / _aksrt_res_download_count。列回调只能用同一套键,
|
||||
// 否则全列恒为 0。旧实现读 'views' / 'like' / 'favorite',仓库里没有任何写入方。
|
||||
assert.match(tags, /'_aksrt_like_count'/)
|
||||
assert.match(tags, /'_aksrt_favorite_count'/)
|
||||
assert.match(tags, /'aksrt_post_views'/)
|
||||
|
||||
assert.match(hooks, /aksrt_get_post_views\(\$post_id\)/)
|
||||
assert.match(hooks, /aksrt_get_post_likes\(\$post_id\)/)
|
||||
assert.match(hooks, /aksrt_get_post_favorites\(\$post_id\)/)
|
||||
assert.match(list, /aksrt_get_post_views\(\$post_id\)/)
|
||||
assert.match(list, /aksrt_get_post_likes\(\$post_id\)/)
|
||||
assert.match(list, /aksrt_get_post_favorites\(\$post_id\)/)
|
||||
|
||||
// 资源下载量只有 _aksrt_res_download_count 一个写入键。
|
||||
assert.match(hooks, /DomainModel::RESOURCE_DOWNLOAD_COUNT_KEY/)
|
||||
assert.match(list, /DomainModel::RESOURCE_DOWNLOAD_COUNT_KEY/)
|
||||
assert.match(domain, /RESOURCE_DOWNLOAD_COUNT_KEY\s*=\s*'_aksrt_res_download_count'/)
|
||||
|
||||
// 不得再直接读这些不存在的键('download_count' 只作为历史遗留键回退读取)。
|
||||
const column = hooks.slice(hooks.indexOf('manage_post_posts_custom_column'), hooks.indexOf('Support sorting by'))
|
||||
const column = list.slice(list.indexOf('private function render_stats_column'), list.indexOf('private function render_sales_column'))
|
||||
assert.doesNotMatch(column, /get_post_meta\(\$post_id, 'views'/)
|
||||
assert.doesNotMatch(column, /get_post_meta\(\$post_id, 'like'/)
|
||||
assert.doesNotMatch(column, /get_post_meta\(\$post_id, 'favorite'/)
|
||||
})
|
||||
|
||||
test('admin stats column shares the aksrt_format_number abbreviation', () => {
|
||||
// 后台列与前台必须同一套缩写口径(≥10000 → 1.2W+,≥1000 → 1.5K)。
|
||||
// 旧实现用了前台从未用过的「万」后缀。
|
||||
assert.match(hooks, /aksrt_format_number\(\$n\)/)
|
||||
const column = hooks.slice(hooks.indexOf('manage_post_posts_custom_column'), hooks.indexOf('Support sorting by'))
|
||||
// 后台列与前台必须同一套缩写口径(≥10000 → 1.2W+,≥1000 → 1.5K)。
|
||||
assert.match(list, /aksrt_format_number\(\$n\)/)
|
||||
const column = list.slice(list.indexOf('private function render_stats_column'), list.indexOf('private function render_sales_column'))
|
||||
assert.doesNotMatch(column, /'万'/)
|
||||
})
|
||||
|
||||
test('sortable meta maps the public orderby keys to the stored keys', () => {
|
||||
assert.match(hooks, /'views'\s*=>\s*\\AKSRT\\Core\\DomainModel::POST_VIEW_COUNT_KEY/)
|
||||
assert.match(hooks, /'like'\s*=>\s*'_aksrt_like_count'/)
|
||||
assert.match(hooks, /'favorite'\s*=>\s*'_aksrt_favorite_count'/)
|
||||
assert.match(hooks, /'download_count'\s*=>\s*\\AKSRT\\Core\\DomainModel::RESOURCE_DOWNLOAD_COUNT_KEY/)
|
||||
// meta_key 必须是真实存储键,而不是用户可见的排序键。
|
||||
assert.match(hooks, /\$query->set\('meta_key', \$allowed\[\$orderby\]\)/)
|
||||
assert.match(list, /'views'\s*=>\s*\\AKSRT\\Core\\DomainModel::POST_VIEW_COUNT_KEY/)
|
||||
assert.match(list, /'like'\s*=>\s*'_aksrt_like_count'/)
|
||||
assert.match(list, /'favorite'\s*=>\s*'_aksrt_favorite_count'/)
|
||||
assert.match(list, /'download_count'\s*=>\s*\\AKSRT\\Core\\DomainModel::RESOURCE_DOWNLOAD_COUNT_KEY/)
|
||||
// meta_key 必须是真实存储键,而不是用户可见的排序键。
|
||||
assert.match(list, /\$query->set\('meta_key', \$this->stats_meta_key\(\$orderby\)\)/)
|
||||
})
|
||||
|
||||
test('every counter meta key the admin column renders has a writer', () => {
|
||||
// 逐键核对:列里展示的每项计数都必须在某个 helper 里真正被写入过。
|
||||
// 逐键核对:列里展示的每项计数都必须在某个 helper 里真正被写入过。
|
||||
// _aksrt_sales_volume 的写入方是付费订单流水(PayOrders),不在这份清单里,
|
||||
// 由下一个用例单独覆盖。
|
||||
const sources = [
|
||||
'inc/core-hooks.php',
|
||||
'src/Admin/Native/PostListEnhance.php',
|
||||
'inc/helpers/aksrt-frontend-tags.php',
|
||||
'src/Core/PostMetaBox.php',
|
||||
'src/Frontend/PageAdminSet.php',
|
||||
@@ -69,31 +97,38 @@ test('every counter meta key the admin column renders has a writer', () => {
|
||||
for (const key of ['aksrt_post_views', '_aksrt_like_count', '_aksrt_favorite_count']) {
|
||||
assert.ok(sources.includes(`'${key}'`), `${key} is written somewhere`)
|
||||
}
|
||||
// 资源帖的浏览有独立键,aksrt_get_post_views() 取两者较大值。
|
||||
// 资源帖的浏览有独立键,aksrt_get_post_views() 取两者较大值。
|
||||
assert.match(tags, /_aksrt_res_view_count/)
|
||||
assert.match(read('inc/helpers/aksrt-resource.php'), /RESOURCE_VIEW_COUNT_KEY|_aksrt_res_view_count/)
|
||||
|
||||
// 资源下载走普通外链,主题内没有任何写入方,所以下载项只在 >0 时出现。
|
||||
// 万一将来接了下载统计,写入方必须落在同一个键上。
|
||||
assert.match(hooks, /\$downloads <= 0/)
|
||||
assert.match(hooks, /\$legacy_downloads/)
|
||||
// 资源下载走普通外链,主题内没有任何写入方,所以下载项只在 >0 时出现。
|
||||
assert.match(list, /\$downloads <= 0/)
|
||||
assert.match(list, /\$legacy_downloads/)
|
||||
})
|
||||
|
||||
test('download count stays read-only until a writer exists', () => {
|
||||
// 目前没有任何 PHP 写入 _aksrt_res_download_count;若哪天新增了写入方,
|
||||
// 这里会失败,提示把该键也纳入后台列的统计口径。
|
||||
const php = ['inc', 'src', 'templates'].flatMap(dir =>
|
||||
fs.readdirSync(path.join(root, dir), { withFileTypes: true })
|
||||
.filter(e => e.isFile() && e.name.endsWith('.php'))
|
||||
.map(e => `${dir}/${e.name}`)
|
||||
).filter(f => !f.includes('core-hooks') && !f.includes('DomainModel')).map(read).join('\n')
|
||||
const writers = php.match(/(?:update|add)_post_meta\([^)]*_aksrt_res_download_count/g) || []
|
||||
assert.equal(writers.length, 0, 'download count still has no writer')
|
||||
test('sales volume is written by the order pipeline', () => {
|
||||
// _aksrt_sales_volume 的写入方在付费助手与订单处理器里,列本身只读。
|
||||
assert.match(pay, /aksrt_bump_content_sales_volume/)
|
||||
assert.match(pay, /RESOURCE_SALES_VOLUME_KEY/)
|
||||
assert.match(read('src/Content/PayOrders.php'), /aksrt_bump_content_sales_volume/)
|
||||
})
|
||||
|
||||
test('admin column icons stay behind the Font Awesome enqueue on edit.php', () => {
|
||||
assert.match(hooks, /admin_enqueue_scripts[\s\S]*?edit\.php[\s\S]*?aksrt_enqueue_fontawesome\(\)/)
|
||||
assert.match(list, /admin_enqueue_scripts[\s\S]*?edit\.php[\s\S]*?aksrt_enqueue_fontawesome\(\)/)
|
||||
for (const klass of ['fa-eye', 'fa-thumbs-up', 'fa-star', 'fa-download']) {
|
||||
assert.match(hooks, new RegExp(klass))
|
||||
assert.match(list, new RegExp(klass))
|
||||
}
|
||||
})
|
||||
|
||||
test('the old closure-based column implementation is gone from core-hooks', () => {
|
||||
// 列与排序逻辑统一收在 src/Admin/Native/PostListEnhance.php,inc/core-hooks.php
|
||||
// 不再重复注册,否则同一列会被渲染两次。
|
||||
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, /aksrt_stats/)
|
||||
})
|
||||
|
||||
test('PostListEnhance is wired up from functions.php', () => {
|
||||
assert.match(read('functions.php'), /PostListEnhance::get_instance\(\)->init\(\)/)
|
||||
})
|
||||
|
||||
@@ -78,16 +78,19 @@ test('icon values sanitize through the SVG-aware helper on every save path', ()
|
||||
test('FA5 loads under a single canonical handle everywhere', () => {
|
||||
const picker = read('inc/helpers/aksrt-icon-picker.php');
|
||||
const admin = read('src/Admin/Admin.php');
|
||||
const coreHooks = read('inc/core-hooks.php');
|
||||
// 文章列表(edit.php)的 FA 入队随列增强迁到了 PostListEnhance。
|
||||
const postList = read('src/Admin/Native/PostListEnhance.php');
|
||||
const part1b = read('src/Core/ScriptsPart1b.php');
|
||||
assert.match(picker, /aksrt_enqueue_fontawesome\(\)/);
|
||||
assert.match(admin, /aksrt_enqueue_fontawesome\(\)/);
|
||||
assert.match(coreHooks, /aksrt_enqueue_fontawesome\(\)/);
|
||||
assert.match(postList, /aksrt_enqueue_fontawesome\(\)/);
|
||||
assert.match(part1b, /aksrt_enqueue_fontawesome\(\['bootstrap'\]\)/);
|
||||
assert.match(part1b, /\['aksrt-font-awesome'\]/, 'public main css depends on the canonical handle');
|
||||
for (const file of [picker, admin, coreHooks, part1b]) {
|
||||
for (const file of [picker, admin, postList, part1b]) {
|
||||
assert.doesNotMatch(file, /wp_enqueue_style\(\s*'aksrt-font-awesome'/, 'no duplicate FA registrations');
|
||||
}
|
||||
// core-hooks.php 不该再自己注册或重复入队 FA。
|
||||
assert.doesNotMatch(read('inc/core-hooks.php'), /aksrt_enqueue_fontawesome|wp_enqueue_style\(\s*'aksrt-font-awesome'/);
|
||||
for (const file of ['inc/helpers/aksrt-widgets-markup.php', 'src/WidgetSystem/Renderers/ContentRenderer.php']) {
|
||||
const source = read(file);
|
||||
assert.doesNotMatch(source, /fa-clock-o|fa-folder-open-o|fa-file-text-o/, `${file} uses FA5 names`);
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
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/)
|
||||
})
|
||||
Reference in New Issue
Block a user