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 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(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/) // 排序靠表头 label 里的链接 + pre_get_posts 落地,不注册 sortable_columns: // 注册后 WordPress 会套自己的排序指示器与 ,与 label 里的 嵌套, // 箭头会被解析到文字前面。 assert.doesNotMatch(list, /manage_edit-post_sortable_columns/) assert.match(list, /pre_get_posts/) 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。列回调只能用同一套键,否则全列恒为 0。 // 互动数据不做下载量统计,因此不再读取任何 download 键。 assert.match(tags, /'_aksrt_like_count'/) assert.match(tags, /'_aksrt_favorite_count'/) assert.match(tags, /'aksrt_post_views'/) 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\)/) // 不得再直接读这些不存在的键。 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'/) // 下载量统计已移除:不得再读下载键,也不得再渲染下载项。 assert.doesNotMatch(column, /RESOURCE_DOWNLOAD_COUNT_KEY/) assert.doesNotMatch(column, /'download_count'/) assert.doesNotMatch(column, /fa-download/) assert.doesNotMatch(column, /下载/) }) test('admin stats column shares the aksrt_format_number abbreviation', () => { // 后台列与前台必须同一套缩写口径(≥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(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'/) // meta_key 必须是真实存储键,而不是用户可见的排序键。 assert.match(list, /\$query->set\('meta_key', \$this->stats_meta_key\(\$orderby\)\)/) // 互动数据不统计下载量,排序映射里也不得再出现该维度。 assert.doesNotMatch(list, /'download_count'/) }) test('every counter meta key the admin column renders has a writer', () => { // 逐键核对:列里展示的每项计数都必须在某个 helper 里真正被写入过。 // _aksrt_sales_volume 的写入方是付费订单流水(PayOrders),不在这份清单里, // 由下一个用例单独覆盖。 const sources = [ 'src/Admin/Native/PostListEnhance.php', 'inc/helpers/aksrt-frontend-tags.php', 'src/Core/PostMetaBox.php', 'src/Frontend/PageAdminSet.php', ].map(read).join('\n') 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() 取两者较大值。 assert.match(tags, /_aksrt_res_view_count/) assert.match(read('inc/helpers/aksrt-resource.php'), /RESOURCE_VIEW_COUNT_KEY|_aksrt_res_view_count/) // 互动数据不统计下载量:列里不得再出现下载键的读取或回退逻辑。 assert.doesNotMatch(list, /RESOURCE_DOWNLOAD_COUNT_KEY/) assert.doesNotMatch(list, /\$downloads/) }) 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(list, /admin_enqueue_scripts[\s\S]*?edit\.php[\s\S]*?aksrt_enqueue_fontawesome\(\)/) for (const klass of ['fa-eye', 'fa-thumbs-up', 'fa-star']) { 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\(\)/) })