Full-project rename. `git grep -i aksrt` is now empty apart from the
DBMigrator 3.3.0 changelog note that documents the rename itself.
Code identifiers
- PHP namespace AKSRT\ -> Jio\ (composer PSR-4 + 378 declarations)
- global constants AKSRT_* -> JIO_* (VERSION/DIR/URI, PAY_TYPE_*, CAPTCHA_*)
- procedural functions, hooks, options, meta keys, table names,
cookies, query vars, ajax actions and nonces: aksrt_ -> jio_
- text domain aksrt -> jio; Theme Name -> Jio
- JS globals: aksrtData/aksrtAdminData/... -> jioData/jioAdminData/...,
window.AKSRTCaptcha & friends -> window.JioCaptcha, Aksrt* -> Jio*
- Vue components Aks*.vue -> Jio*.vue (15 files renamed + imports)
- CSS classes/data-attrs/custom properties: aksrt- -> jio-
- REST namespace aksrt/v1 -> jio/v1 (incl. payment gateway callbacks)
Storage layer
- DB tables {prefix}aksrt_* -> {prefix}jio_*; SCHEMA_VERSION 3.2.0 -> 3.3.0
so dbDelta creates the new names on install
- widget id_base aksrt_widget_* -> jio_widget_*
- uninstall.php drop list rebuilt from the actual migrator tables
(dropped 3 stale entries, added the 17 missing ones)
Removed
- hardcoded https://api.aksrt.com/version.json default update endpoint;
get_endpoint() now reads the 「更新端点」 setting and do_check() no-ops
when it is empty
Files renamed: inc/helpers/aksrt-*.php -> jio-*.php (54),
assets/admin/src/components/form/Aks*.vue -> Jio*.vue (15).
Build artifacts regenerated: assets/css/build, assets/js/runtime,
assets/admin/build.
Verified: audit-psr4-paths errors=0, audit-admin-contracts errors=0
(warnings=56, unchanged baseline), 813/813 widget-system tests,
44/44 phpunit, php -l clean across all PHP.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
97 lines
4.5 KiB
JavaScript
97 lines
4.5 KiB
JavaScript
'use strict';
|
|
|
|
/**
|
|
* 「卡密管理」后台页契约:
|
|
* 1) 标题右侧按钮(page-title-action)由页面头部唯一输出,列表渲染不得再输出一次;
|
|
* 2) 表头排序箭头必须锚定在「文字基线」上,而不是依赖绝对定位元素的静态位置:
|
|
* 宿主为零尺寸 inline-block(其基线=下边缘=文字基线),箭头用 bottom + translateY(50%)
|
|
* 定位,中心落在基线以上 .725em/.075em,对称于文字视觉中心。
|
|
* 历史缺陷:靠静态位置 + 固定 -3px/+6px,静态位置随表头字体/字号变化,
|
|
* 量测确认整组箭头相对文字偏下 2px。
|
|
*/
|
|
|
|
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, '..', '..');
|
|
|
|
// PayChargeCards 已拆成 flat sibling traits;按方法名切片需要「类体 + trait 体」仍在同一文本里
|
|
// 保持原有相邻顺序,故按固定顺序拼接壳类与三个 trait(壳类 → List → Assets → Forms)。
|
|
const page = [
|
|
'src/Admin/Native/PayChargeCards.php',
|
|
'src/Admin/Native/PayChargeCardsList.php',
|
|
'src/Admin/Native/PayChargeCardsAssets.php',
|
|
'src/Admin/Native/PayChargeCardsForms.php',
|
|
].map((rel) => fs.readFileSync(path.join(root, rel), 'utf8')).join('\n');
|
|
|
|
const count = (src, pattern) => (src.match(pattern) || []).length;
|
|
|
|
const header = page.slice(
|
|
page.indexOf('public function render_charge_cards_page('),
|
|
page.indexOf('private function render_list(')
|
|
);
|
|
const list = page.slice(
|
|
page.indexOf('private function render_list('),
|
|
page.indexOf('private function render_add_form(')
|
|
);
|
|
|
|
test('标题右侧按钮只在页面头部输出一次', () => {
|
|
assert.ok(header.length > 0 && list.length > 0);
|
|
|
|
// 添加/导出两个 tab 的「返回列表」各一次,列表页的「添加卡密 / 导出卡密」各一次。
|
|
assert.equal(count(header, /class="page-title-action"/g), 4);
|
|
assert.doesNotMatch(list, /class="page-title-action"/);
|
|
assert.equal(count(page, /class="page-title-action"/g), count(header, /class="page-title-action"/g));
|
|
|
|
// 列表页的两个入口按钮各自只有一处定义。
|
|
assert.equal(count(page, /add_query_arg\('tab', 'add'/g), 1);
|
|
assert.equal(count(page, /add_query_arg\('tab', 'export'/g), 1);
|
|
assert.match(header, /esc_html__\('添加卡密', 'jio'\)/);
|
|
assert.match(header, /esc_html__\('导出卡密', 'jio'\)/);
|
|
});
|
|
|
|
test('搜索提示仍由页面头部输出且列表不再自建头部', () => {
|
|
assert.equal(count(header, /update-nag/g), 1);
|
|
assert.match(header, /搜索 “%s” 的内容/);
|
|
assert.doesNotMatch(list, /update-nag/);
|
|
assert.doesNotMatch(list, /\$head_but/);
|
|
|
|
// 搜索词在页面头部解析后传入列表,避免两处各解析一次。
|
|
assert.match(page, /render_list\(\$s\)/);
|
|
assert.match(list, /private function render_list\(string \$s\): void/);
|
|
});
|
|
|
|
test('排序箭头锚定文字基线,不依赖绝对定位的静态位置', () => {
|
|
const host = page.match(/\.orderby-but \{[^}]*\}/);
|
|
assert.ok(host, '.orderby-but 规则缺失');
|
|
// 零尺寸 inline-block:基线即下边缘,从而把箭头锚定到文字基线;尺寸为零则不参与换行/行高。
|
|
assert.match(host[0], /position:\s*relative/);
|
|
assert.match(host[0], /display:\s*inline-block/);
|
|
assert.match(host[0], /width:\s*0/);
|
|
assert.match(host[0], /height:\s*0/);
|
|
assert.doesNotMatch(host[0], /margin-left/);
|
|
|
|
// 箭头必须显式给出 bottom(静态位置方案没有 bottom),并用 translateY(50%) 抵消自身高度。
|
|
const arrow = page.match(/\.orderby-but > a \{[^}]*\}/);
|
|
assert.ok(arrow, '.orderby-but > a 规则缺失');
|
|
assert.match(arrow[0], /position:\s*absolute/);
|
|
assert.match(arrow[0], /bottom:\s*\.725em/);
|
|
assert.match(arrow[0], /transform:\s*translateY\(50%\)/);
|
|
assert.match(arrow[0], /padding-right:\s*1em/);
|
|
|
|
assert.match(page, /\.orderby-but > a \+ a \{ bottom: \.075em; \}/);
|
|
// 旧的静态位置写法(无 bottom + 固定像素位移)不得回归。
|
|
assert.doesNotMatch(page, /translateY\(-3px\)/);
|
|
assert.doesNotMatch(page, /\.orderby-but > a \+ a \{ transform: translateY\(6px\); \}/);
|
|
assert.match(header + list, /<span class="orderby-but">/);
|
|
});
|
|
|
|
test('类型列链接每种类型只生成一次', () => {
|
|
assert.equal(count(page, /\$type_html = '<a/g), 4);
|
|
assert.equal(count(page, /add_query_arg\('type', 'custom'/g), 1);
|
|
assert.match(list, /if \(\$msg\['type'\] !== 'custom'\) \{/);
|
|
assert.doesNotMatch(list, /\$msg\['type'\] === 'custom'\)\s*\{\s*\$type_html/);
|
|
});
|