Files
wordpress-theme-aksrt/tests/widget-system/ticket-spa-config-contract.test.cjs
T
kate522andClaude Opus 5 35bb96e82f refactor: rename the theme brand and every identifier from AKSRT to Jio
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>
2026-11-01 20:05:04 +08:00

111 lines
5.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';
/**
* SPA「工单中心」接线契约:
* 1) /users/ticket 是 customPage,组件 UsersTicket 已注册并能被 router 解析;
* 2) 页面内嵌 SchemaPage 承载 users/ticket 分区,schema 表单零改动复用;
* 3) 三块字典 CRUD 走 TicketRestAdmin(jio/v1/admin)的封装模块;
* 4) 双向深链:SPA → 原生工单列表,原生 → SPA 工单中心;
* 5) 配置 Tab 的残留(tab=config / UsersMenusTicketsConfig)不得回流。
*/
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 read = (file) => fs.readFileSync(path.join(root, file), 'utf8');
test('the ticket page is a custom page backed by UsersTicket', () => {
const loaders = read('assets/admin/src/router/component-loaders/users.js');
assert.ok(
loaders.includes("UsersTicket: () => import('@/pages/users/UsersTicket.vue')"),
'UsersTicket loader missing'
);
const constants = read('assets/admin/src/constants/sections/users.js');
// 入口必须是 customPage 且显式指定组件,否则 router 会回落到 SchemaPage。
assert.match(constants, /customPage\(\{[\s\S]*?component: 'UsersTicket'/);
assert.ok(constants.includes("key: 'users-ticket'"), 'menu page key missing');
assert.ok(constants.includes("route: '/users/ticket'"), 'menu route missing');
assert.ok(constants.includes("section: 'ticket'"), 'menu section missing');
assert.ok(constants.includes("canonicalDomain: 'users'"), 'menu canonical domain missing');
});
test('UsersTicket embeds the schema page and the three CRUD panels', () => {
const page = read('assets/admin/src/pages/users/UsersTicket.vue');
// 基础配置复用 SchemaPage,域/分区显式传入,避免依赖路由 meta。
assert.match(page, /<SchemaPage\s+domain="users"\s+section="ticket"/);
for (const panel of ['TicketTypePanel', 'TicketStatusPanel', 'TicketPrereplyPanel']) {
assert.ok(page.includes(panel), `panel ${panel} not mounted`);
assert.ok(
fs.existsSync(path.join(root, `assets/admin/src/pages/users/partials/ticket/${panel}.vue`)),
`panel ${panel} file missing`
);
}
// 反向深链:回到原生工单列表。
assert.match(page, /users\.php\?page=jio_tickets/);
assert.match(page, /window\.jioAdminData\?\.adminUrl/);
});
test('ticket admin REST is wrapped in its own api module and registered', () => {
const module = read('assets/admin/src/api/modules/tickets.js');
// TicketRestAdmin 挂在 jio/v1/admin,客户端 baseUrl 只到 jio/v1,
// 因此必须走 /jio/ 全路径形式。
assert.match(module, /'\/jio\/v1\/admin\/tickets'/);
for (const method of [
'listTicketStatuses', 'createTicketStatus', 'updateTicketStatus', 'deleteTicketStatus',
'listTicketTypes', 'createTicketType', 'updateTicketType', 'deleteTicketType',
'listTicketPrereplies', 'createTicketPrereply', 'updateTicketPrereply',
'deleteTicketPrereply', 'reorderTicketPrereplies',
]) {
assert.ok(module.includes(`${method}(`), `api method ${method} missing`);
}
const index = read('assets/admin/src/api/modules/index.js');
assert.ok(index.includes("import { ticketsApiMethods } from './tickets.js'"), 'module not imported');
assert.ok(index.includes('...ticketsApiMethods,'), 'module not spread into apiMethods');
});
test('panels enforce the backend guards instead of trusting the client', () => {
const status = read('assets/admin/src/pages/users/partials/ticket/TicketStatusPanel.vue');
// 默认状态的编辑/删除入口在前端禁用,但后端 TicketConfigRest 仍有 409 兜底。
assert.match(status, /disabled: row\.is_default/);
// 颜色必须落在 TicketStatusService::sanitize_color() 接受的范围内。
assert.match(status, /import \{ toHex \} from '@\/utils\/color\.js'/);
assert.match(status, /<JioColorPicker/);
assert.doesNotMatch(status, /n-color-picker|HEX_RE/);
assert.match(status, /#6b7280/);
const color = read('assets/admin/src/utils/color.js');
assert.match(color, /export const HEX_RE = \/\^#\?\(\[0-9a-f\]\{3\}\|\[0-9a-f\]\{6\}\)\$\/i/);
const prereply = read('assets/admin/src/pages/users/partials/ticket/TicketPrereplyPanel.vue');
// 排序接口要求全量 ID。
assert.match(prereply, /rows\.value\.map\(\(row\) => row\.id\)/);
assert.match(prereply, /reorderTicketPrereplies\(ids\)/);
});
test('no config tab residue survives in the SPA or the native page', () => {
const router = read('assets/admin/src/router/index.js');
const constants = read('assets/admin/src/constants/sections/users.js');
const schema = read('assets/admin/src/schema/sections/users/ticket.js');
for (const [file, source] of [['router', router], ['constants', constants], ['ticket schema', schema]]) {
assert.ok(!source.includes('tab=config'), `${file} still references tab=config`);
}
// 原生页不再有配置 Tab 的渲染 trait。
assert.ok(
!fs.existsSync(path.join(root, 'src/Admin/Native/UsersMenusTicketsConfig.php')),
'native config trait must be deleted'
);
});