Files
kate522 fa53a0f4f9 fix(revenue): align detail card, poster modal and copy feedback with reference
- detail-tab 累计佣金 row now shows 累计 + 已提现 (was 累计/可提现/已提现)
- rebuild poster canvas to match reference layout (768×1168, top banner,
  date overlay, blue title bar, bottom bordered box with QR + link)
- copy-link feedback moves from inline text to a toast (notyf), matching
  the reference; drop the now-unused data-affiliate-feedback element
2026-09-28 21:58:43 +08:00

125 lines
6.7 KiB
JavaScript

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 = (...segments) => fs.readFileSync(path.join(root, ...segments), 'utf8')
test('QR generator is bundled locally; no external QR CDN in front-end sources', () => {
const vendor = path.join(root, 'assets', 'js', 'vendor', 'qrcode.min.js')
assert.ok(fs.existsSync(vendor), 'assets/js/vendor/qrcode.min.js must be committed')
const script = fs.readFileSync(vendor, 'utf8')
assert.ok(script.length > 10000, 'qrcode.min.js must not be a placeholder')
// 前端引用面不允许出现外部二维码 CDN。
const sources = [
read('assets', 'js', 'credits.js'),
read('inc', 'helpers', 'user-center-renderers', 'profile-revenue.php'),
read('src', 'Core', 'ScriptsPart1b.php'),
].join('\n')
assert.doesNotMatch(sources, /cdnjs\.cloudflare\.com[^"']*qrcode|cdn\.jsdelivr\.net[^"']*qrcode|unpkg\.com[^"']*qrcode/)
})
test('revenue detail card is a four-row info list with no standalone QR block', () => {
const template = read('inc', 'helpers', 'user-center-renderers', 'profile-revenue.php')
// 二维码不再作为独立行:整行 markup 已删除。
assert.doesNotMatch(template, /data-affiliate-qr-row/)
assert.doesNotMatch(template, /data-affiliate-qr(?!-)/)
assert.doesNotMatch(template, /data-affiliate-qr-download/)
assert.doesNotMatch(template, /data-affiliate-qr-feedback/)
assert.doesNotMatch(template, /<canvas/)
// 推广海报按钮并入推广链接行([Url][复制链接][推广海报])。
assert.match(template, /uc-revenue-summary-row--link/)
assert.match(template, /data-affiliate-link/)
assert.match(template, /data-affiliate-copy/)
assert.match(template, /data-affiliate-poster/)
// 复制反馈走 Toast(对齐参考主题),不再有内联反馈元素。
assert.doesNotMatch(template, /data-affiliate-feedback/)
// 四行固定标签:推广链接 / 佣金比例 / 累计佣金 / 返佣详解。
for (const label of ['推广链接', '佣金比例', '累计佣金']) {
assert.match(template, new RegExp('<span><\\?php [a-z_]+\\([^)]*' + label), 'detail row label ' + label)
}
// 返佣详解标题走可配置字段,不再硬编码。
assert.match(template, /esc_html\(\$details_title\)/)
const script = read('assets', 'js', 'credits.js')
// 独立二维码绘制函数已删除;window.qrcode 仅供海报使用。
assert.doesNotMatch(script, /function renderAffiliateQr/)
assert.doesNotMatch(script, /renderAffiliateQr\(/)
assert.doesNotMatch(script, /data-affiliate-qr/)
// 海报弹窗:舞台 + 保存按钮 + 复用 openModal/closeModal。
assert.match(script, /data-affiliate-poster-stage/)
assert.match(script, /data-affiliate-poster-download/)
assert.match(script, /openModal\(modal\)/)
assert.match(script, /link\.download = 'referral-poster\.png'/)
// 链接由 PHP 直出:复制按钮立即可用,REST 只负责海报配置与佣金比例。
assert.match(script, /var url = input \? input\.value : '';/)
assert.match(script, /if \(copy\) copy\.disabled = !url;/)
assert.doesNotMatch(script, /input\.value = j\.url/)
// 复制反馈走 Toast(notyf),不再写内联文本。
assert.match(script, /notyf\('推广链接已复制'/)
assert.doesNotMatch(script, /data-affiliate-feedback/)
})
test('promotion link is server-rendered so it needs no round trip', () => {
const template = read('inc', 'helpers', 'user-center-renderers', 'profile-revenue.php')
// input 带服务端 value,不依赖 JS 回填。
assert.match(template, /data-affiliate-link value="<\?php echo esc_attr\(\$affiliate_link_url\); \?>"/)
assert.match(template, /create_link\(\$user_id\)\['url'\]/)
const attribution = read('src', 'Payment', 'ReferralAttribution.php')
// rest_me 不再做 COUNT 聚合,也不触发结算。
assert.doesNotMatch(attribution, /\$this->stats\(\$user_id\)/)
assert.doesNotMatch(attribution, /lazy_settle_due\(\)/)
})
test('poster canvas is built from admin-configured copy and gated on the poster switch', () => {
const runtime = read('src', 'Settings', 'PaymentRuntime.php')
assert.match(runtime, /'poster' => \[/)
assert.match(runtime, /pay_rebate_poster_s/)
assert.match(runtime, /pay_rebate_poster_img/)
assert.match(runtime, /pay_rebate_poster_title/)
assert.match(runtime, /pay_rebate_poster_desc/)
const attribution = read('src', 'Payment', 'ReferralAttribution.php')
assert.match(attribution, /'poster' => \(array\) \(\$affiliate\['poster'\] \?\? \['enabled' => false\]\)/)
assert.match(attribution, /'site_name' => get_bloginfo\('name'\)/)
const script = read('assets', 'js', 'credits.js')
// 海报仅在开关开启时暴露按钮;绘制含顶部图、文案换行、二维码与站点名。
assert.match(script, /initAffiliatePoster\(root, url \|\| j\.url \|\| '', j\.poster \|\| \{\}, j\.site_name \|\| document\.title\)/)
assert.match(script, /poster\.enabled !== true\) return/)
assert.match(script, /function buildPosterCanvas/)
assert.match(script, /function wrapText\(ctx, text, maxWidth\)/)
assert.match(script, /image\.crossOrigin = 'anonymous'/)
})
test('invite-code input verifies live against the public REST endpoint', () => {
const template = read('templates', 'parts', 'auth-content.php')
assert.match(template, /data-invite-verify/)
assert.match(template, /data-invite-feedback/)
const script = read('assets', 'js', 'auth.js')
assert.match(script, /function initInviteVerify\(root\)/)
assert.match(script, /input\[data-invite-verify\]/)
assert.match(script, /initInviteVerify\(root\)/)
assert.match(script, /\/wp-json\/aksrt\/v1\/invite-code\/verify\?code=/)
})
test('qrcode asset is enqueued only for profile + affiliate and credits depends on it', () => {
const scripts = read('src', 'Core', 'ScriptsPart1b.php')
assert.match(scripts, /assets\/js\/vendor\/qrcode\.min\.js/)
assert.match(scripts, /'aksrt-qrcode'/)
// credits.js 的依赖数组必须在 enqueue 调用内合并 QR 库句柄。
assert.match(scripts, /array_merge\(\['loader_js', 'aksrt-vip-coordinator'\], \$affiliate_deps\)/)
})
test('client IP honours the trusted-proxy filter before CDN headers', () => {
const captcha = read('inc', 'modules', 'captcha.php')
assert.match(captcha, /apply_filters\('aksrt_trusted_proxies', \[\]\)/)
assert.match(captcha, /function aksrt_ip_in_list\(string \$ip, array \$list\): bool/)
// CIDR 比较必须先检查同族地址长度,避免跨族误判。
assert.match(captcha, /strlen\(\$ip_binary\) !== strlen\(\$subnet_binary\)/)
// 过滤命中失败时直接回落 REMOTE_ADDR,不读转发头。
assert.match(captcha, /!aksrt_ip_in_list\(\$remote, \$trusted\)/)
})