Files
wordpress-theme-aksrt/tests/widget-system/report-submit-runtime.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

183 lines
7.6 KiB
JavaScript

'use strict';
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const { spawnSync } = require('node:child_process');
const test = require('node:test');
const root = path.resolve(__dirname, '..', '..');
const harness = path.join(__dirname, 'report-submit-harness.php');
let cachedResults;
function results() {
if (cachedResults) return cachedResults;
const run = spawnSync(process.env.PHP_BINARY || 'php', [harness], {
cwd: root,
encoding: 'utf8',
});
assert.ifError(run.error);
assert.equal(run.status, 0, `PHP report harness failed:\n${run.stderr}\n${run.stdout}`);
cachedResults = JSON.parse(run.stdout);
return cachedResults;
}
test('stores readable preset category separately from details and evidence', () => {
const result = results().preset_with_evidence;
const reason = '补充详情 https://example.test/uploads/proof.png';
assert.equal(result.response.success, true);
assert.equal(result.rows.length, 1);
assert.equal(result.rows[0].category, '垃圾信息');
assert.equal(result.rows[0].reason, reason);
assert.doesNotMatch(result.rows[0].reason, /^垃圾信息/);
assert.deepEqual(result.claims, [[reason, 42]]);
const hook = result.actions[0][1];
assert.equal(hook.category, '垃圾信息');
assert.equal(hook.reason, reason);
});
test('accepts preset-only reports and includes their category in admin notifications', () => {
const result = results().preset_label_only;
assert.equal(result.response.success, true);
assert.equal(result.rows[0].category, '人身攻击');
assert.equal(result.rows[0].reason, '');
assert.equal(result.messages.length, 1);
assert.match(result.messages[0].content, /人身攻击/);
});
test('accepts the reserved other category only when details are present', () => {
const result = results().other_with_details;
assert.equal(result.response.success, true);
assert.equal(result.rows[0].category, '其他原因');
assert.equal(result.rows[0].reason, '请核查相关资料');
});
test('preserves long configured category labels without truncating them', () => {
const result = results().long_preset_label;
const label = '非常长的预置举报分类名称超过数据库原先四十字符限制并且应完整保留在工单及后台列表当中';
assert.ok(label.length > 40);
assert.equal(result.response.success, true);
assert.equal(result.rows[0].category, label);
});
test('uses sanitized category and reason consistently in storage and the submit hook', () => {
const result = results().sanitized_snapshot;
assert.equal(result.response.success, true);
assert.equal(result.rows[0].category, '垃圾信息');
assert.equal(result.rows[0].reason, '补充详情');
assert.deepEqual(result.actions[0][1], {
reporter_id: 42,
target_type: 'post',
target_id: 12,
reason: '补充详情',
category: '垃圾信息',
});
});
test('rejects blank, unknown, and hash-like categories before inserting', () => {
for (const name of ['blank_category', 'unknown_category', 'hash_category']) {
const result = results()[name];
assert.equal(result.response.success, false, name);
assert.equal(result.insert_calls.length, 0, name);
assert.equal(result.rows.length, 0, name);
}
});
test('requires details for other, while authentication and target validation still apply', () => {
const missingOtherDetails = results().missing_other_details;
assert.equal(missingOtherDetails.response.success, false);
assert.equal(missingOtherDetails.insert_calls.length, 0);
const unauthenticated = results().unauthenticated;
assert.equal(unauthenticated.response.success, false);
assert.equal(unauthenticated.status, 401);
assert.equal(unauthenticated.insert_calls.length, 0);
const invalidTarget = results().invalid_target;
assert.equal(invalidTarget.response.success, false);
assert.equal(invalidTarget.insert_calls.length, 0);
});
test('prevents duplicate pending reports without claiming evidence again', () => {
const result = results().duplicate;
assert.equal(result.response.success, false);
assert.equal(result.status, 409);
assert.equal(result.rows.length, 1);
assert.equal(result.insert_calls.length, 0);
assert.deepEqual(result.claims, []);
});
test('does not notify, fire hooks, or claim evidence after an insert failure with stale insert_id', () => {
const result = results().insert_failure;
assert.equal(result.response.success, false);
assert.equal(result.insert_calls.length, 1);
assert.equal(result.rows.length, 0);
assert.deepEqual(result.messages, []);
assert.deepEqual(result.actions, []);
assert.deepEqual(result.claims, []);
});
test('rejects a blank category at the storage service boundary', () => {
const result = results().service_blank_category;
assert.equal(result.id, 0);
assert.equal(result.rows.length, 0);
assert.equal(result.insert_calls.length, 0);
});
test('preserves readable labels through the admin category filter, list, and detail', () => {
const result = results().admin_category;
assert.equal(result.submit_success, true);
assert.equal(result.list_items.length, 1);
assert.equal(result.list_items[0].category, '涉嫌侵权');
assert.ok(result.list_options.some((option) => option.label === '涉嫌侵权' && option.value === '涉嫌侵权'));
assert.equal(result.detail_report.category, '涉嫌侵权');
});
test('stores report categories as text and uses schema version 3.3.0', () => {
const migrator = fs.readFileSync(path.join(root, 'src/Core/DBMigrator.php'), 'utf8');
const tableDefinitions = fs.readFileSync(path.join(root, 'src/Core/DBMigratorPart2.php'), 'utf8');
const reportTable = tableDefinitions.match(/\$sql_report\s*=\s*"([\s\S]*?)";/);
assert.match(migrator, /const SCHEMA_VERSION\s*=\s*'3\.3\.0';/);
assert.ok(reportTable, 'report table definition should exist');
assert.match(reportTable[1], /\bcategory\s+TEXT\s+NOT NULL\b/);
});
test('preserves submitted category snapshots after preset settings change or disappear', () => {
const result = results().category_snapshot;
const expected = ['提交时的分类', '待删除的分类'];
assert.equal(result.submit_success, true);
assert.deepEqual([...result.changed_list_categories].sort(), [...expected].sort());
assert.deepEqual([...result.changed_list_options].sort(), [...expected].sort());
assert.deepEqual([...result.removed_list_categories].sort(), [...expected].sort());
assert.deepEqual(result.details, expected);
});
test('preserves literal zero text and exposes preset names without slugs', () => {
const data = results();
const result = data.falsey_text;
assert.equal(result.response.success, true);
assert.equal(result.rows[0].category, '0');
assert.equal(result.rows[0].reason, '0');
assert.deepEqual(data.runtime_reasons, [{ name: '垃圾信息' }, { name: '人身攻击' }]);
});
test('passes category and details to restriction actions without rewriting report content', () => {
const data = results();
const preset = data.process_preset_only;
assert.equal(preset.success, true);
assert.equal(preset.restriction.type, 'warn');
assert.equal(preset.restriction.reason, '人身攻击');
assert.equal(preset.report_category, '人身攻击');
assert.equal(preset.report_reason, '');
const other = data.process_other_with_details;
assert.equal(other.success, true);
assert.equal(other.restriction.type, 'warn');
assert.equal(other.restriction.reason, '其他原因\n\n自定义说明不能丢');
assert.equal(other.report_category, '其他原因');
assert.equal(other.report_reason, '自定义说明不能丢');
assert.doesNotMatch(other.report_reason, /^其他原因/);
});