Files
wordpress-theme-aksrt/tests/widget-system/user-badges-modal.test.cjs
T

98 lines
5.3 KiB
JavaScript

const assert = require('node:assert/strict');
const { spawnSync } = require('node:child_process');
const fs = require('node:fs');
const path = require('node:path');
const test = require('node:test');
const root = path.resolve(__dirname, '..', '..');
const harness = path.join(__dirname, 'user-badges-modal-harness.php');
function render() {
const result = spawnSync('php', [harness], { encoding: 'utf8', cwd: root });
assert.equal(result.status, 0, `PHP modal harness failed:\n${result.stderr || result.stdout}`);
return JSON.parse(result.stdout);
}
function assertBefore(html, first, second, message, from = 0) {
const firstIndex = html.indexOf(first, from);
const secondIndex = html.indexOf(second, from);
assert.notEqual(firstIndex, -1, `missing ${first}`);
assert.notEqual(secondIndex, -1, `missing ${second}`);
assert.ok(firstIndex < secondIndex, message);
}
test('overview matches the reference header, safely renders labels and places owned badges first', () => {
const cases = render();
const html = cases.self.html;
assert.match(html, /modal-colorful-header colorful-bg c-blue/);
assert.match(html, /class="avatar-img"[^>]*>[\s\S]*?width="38"/);
assert.doesNotMatch(html, /<a[^>]*>\s*<img[^>]*avatar-img/);
assert.match(html, /medal-list-box[^>]*flex[^>]*at[^>]*hh|medal-list-box[^>]*hh[^>]*flex/);
const listStart = html.indexOf('mini-scrollbar scroll-y max-vh5');
assertBefore(html, '自动已得', '手动已得', 'owned medals keep the reference category order', listStart);
assertBefore(html, '手动已得', '手动未得', 'owned medals render before unearned medals', listStart);
assert.match(html, /类别 &lt;svg onload=alert\(1\)&gt;/);
assert.match(html, /手动未得 &lt;b&gt;危险&lt;\/b&gt;/);
assert.match(cases.grant_admin.html, /说明 &lt;script&gt;bad\(\)&lt;\/script&gt;/);
assert.doesNotMatch(html, /<script>bad\(\)<\/script>|<svg onload=alert\(1\)>|<b>危险<\/b>/);
assert.match(html, /data-aksrt-modal-height="201"|data-height="201"/);
assert.match(html, /data-aksrt-modal-new="true"|new="new"/);
assert.match(html, /data-aksrt-modal-mobile-bottom="true"|mobile-bottom="true"/);
});
test('own and other-user overviews apply the ownership visibility rule for ordinary users and administrators', () => {
const cases = render();
for (const key of ['other', 'admin_other']) {
const html = cases[key].html;
assert.match(html, /自动已得/);
assert.match(html, /手动已得/);
assert.doesNotMatch(html, /手动未得|自动未得/);
}
assert.match(cases.other.html, /Other &lt;img src=x onerror=alert\(1\)&gt;/);
assert.doesNotMatch(cases.other.html, /<img src=x onerror=alert\(1\)>/);
assert.match(cases.admin_self.html, /授予徽章/);
assert.match(cases.admin_self.html, /data-aksrt-modal-height="240"|data-height="240"/);
assert.match(cases.admin_self.html, /data-aksrt-modal-mobile-bottom="true"|mobile-bottom="true"/);
});
test('single-badge details show ownership state and only the permitted action', () => {
const cases = render();
assert.match(cases.single_owned_self.html, /2026-02-03 04:05:06 获得/);
assert.match(cases.single_owned_self.html, /data-aksrt-badge-action="wear"/);
assert.match(cases.single_unowned_self.html, /暂未获得/);
assert.doesNotMatch(cases.single_unowned_self.html, /data-aksrt-badge-action=/);
assert.match(cases.single_wearing_self.html, /当前佩戴此徽章/);
assert.doesNotMatch(cases.single_wearing_self.html, /data-aksrt-badge-action="wear"/);
assert.match(cases.single_manual_admin.html, /收回此徽章/);
assert.match(cases.single_manual_admin.html, /data-aksrt-badge-action="revoke"/);
assert.doesNotMatch(cases.single_manual_admin.html, /2026-03-04 05:06:07 获得|暂未获得/);
assert.doesNotMatch(cases.single_auto_admin.html, /data-aksrt-badge-action="revoke"|收回此徽章/);
assert.match(cases.single_auto_unowned_admin.html, /参数传入错误或没有此徽章/);
assert.doesNotMatch(cases.single_auto_unowned_admin.html, /data-aksrt-badge-action=/);
assert.match(cases.single_owned_self.html, /data-aksrt-modal-close/);
});
test('grant modal preserves configured manual-badge order, actions, mobile sizing, and illustrated empty state', () => {
const cases = render();
const html = cases.grant_admin.html;
assert.match(html, /手动未得/);
assert.match(html, /手动已得/);
assert.doesNotMatch(html, /自动已得|自动未得/);
assertBefore(html, '手动未得', '手动已得', 'grant catalog follows the configured order');
assert.match(html, /data-aksrt-badge-action="grant"/);
assert.match(html, /data-aksrt-badge-action="revoke"/);
const css = fs.readFileSync(path.join(root, 'assets/css/main.css'), 'utf8');
assert.match(css, /\.medal-list\s*\{[^}]*width:\s*calc\(20%\s*-\s*14px\)[^}]*margin:\s*10px\s+7px/s);
assert.match(css, /@media\s*\(max-width:\s*767px\)[\s\S]*?\.medal-list\s*\{[^}]*width:\s*calc\(25%\s*-\s*14px\)/);
assert.match(css, /\.avatar-img\s*\{[^}]*--this-size:\s*38px/s);
const badgeCss = fs.readFileSync(path.join(root, 'assets/css/frontend/medal.css'), 'utf8');
assert.doesNotMatch(badgeCss, /\.medal-list-box\s*\{|\.medal-card__name|\.medal-card \.medal-icon/);
const empty = cases.illustrated_empty;
assert.match(empty.html, /aksrt-test-null/);
assert.ok(empty.null_calls.some((call) => call[1] === 30 && call[2] === 'null-2.svg'));
});