fix(frontend): keep asset and homepage filters in DOM

This commit is contained in:
2026-09-03 19:21:24 +08:00
parent a0adddce59
commit 7308f055c7
23 changed files with 362 additions and 305 deletions
@@ -78,16 +78,32 @@ test('user asset submissions send idempotency keys', () => {
})
test('profile asset records show order references and only the changed amount', () => {
for (const file of ['profile-credits.php', 'credits-transactions.php']) {
for (const file of ['credits-transactions.php', 'credits-wallet-ledger.php']) {
const source = fs.readFileSync(path.join(root, 'inc', 'helpers', 'user-center-renderers', file), 'utf8')
assert.match(source, /\$record_note\s*=\s*\(string\) \$row\['reason'\] === 'points_exchange'[\s\S]*?\(string\) \$row\['ref_key'\]/)
assert.doesNotMatch(source, /uc-transactions__balance|交易 #%d|\$row\['fee_minor'\]|\$row\['ref_id'\]/)
}
})
test('asset record tabs and pagination stay in the DOM without URL state', () => {
const profile = fs.readFileSync(path.join(root, 'inc', 'helpers', 'user-center-renderers', 'profile-credits.php'), 'utf8')
const points = fs.readFileSync(path.join(root, 'inc', 'helpers', 'user-center-renderers', 'credits-transactions.php'), 'utf8')
const wallet = fs.readFileSync(path.join(root, 'inc', 'helpers', 'user-center-renderers', 'credits-wallet-ledger.php'), 'utf8')
const credits = fs.readFileSync(path.join(root, 'assets', 'js', 'credits.js'), 'utf8')
const center = fs.readFileSync(path.join(root, 'src', 'User', 'Center.php'), 'utf8')
assert.match(profile, /data-aksrt-asset-tab="balance"/)
assert.match(profile, /data-aksrt-asset-tab="points"/)
assert.match(points, /aksrt_render_asset_pagination\([^;]+, 'points'\)/)
assert.match(wallet, /aksrt_render_asset_pagination\([^;]+, 'balance'\)/)
assert.doesNotMatch(profile + points + wallet, /\$_GET\[['"](?:wallet_page|credits_page)['"]\]/)
assert.match(credits, /data-aksrt-asset-page/)
assert.match(credits, /user-center\/panel\/credits\?asset=/)
assert.match(center, /'asset'\s*=>\s*\[/)
assert.match(center, /\$context\['asset_request'\]/)
})
test('core asset paths never use floating-point money helpers', () => {
const files = [
'src/Ledger/AssetAmount.php',
'src/Ledger/LedgerCommand.php',
'src/Ledger/LedgerService.php',
'src/Credits/BalanceService.php',
@@ -39,13 +39,13 @@ test('native and front-end surfaces expose immutable business numbers', () => {
assert.match(read('src/Admin/Native/PayWithdrawals.php'), /提现单号/)
assert.match(read('src/Payment/UserOrders.php'), /\$view\['flow_number'\]/)
assert.match(read('assets/js/order-center.js'), /\["流水号", order\.flow_number \|\| "—"\]/)
assert.match(read('inc/helpers/user-center-renderers/credits-wallet.php'), /withdrawal_number/)
assert.match(read('inc/helpers/user-center-renderers/credits-wallet-ledger.php'), /withdrawal_number/)
})
test('balance history renders withdrawal numbers from the committed flow', () => {
const query = read('src/Ledger/LedgerQuery.php')
const profile = read('inc/helpers/user-center-renderers/profile-credits.php')
const wallet = read('inc/helpers/user-center-renderers/credits-wallet.php')
const profile = read('inc/helpers/user-center-renderers/credits-wallet-ledger.php')
const wallet = read('inc/helpers/user-center-renderers/credits-wallet.php') + read('inc/helpers/user-center-renderers/credits-wallet-ledger.php')
assert.match(query, /LEFT JOIN \{\$flow_table\} f ON f\.ledger_transaction_id = t\.id/)
assert.match(query, /'withdrawal_number'\s*=>\s*\(string\)/)
for (const source of [profile, wallet]) {
@@ -99,6 +99,29 @@ test('routed modules use one namespaced auto_fun owner and no registry', () => {
}
});
test('user center navigation initializes once across auto_fun rescans', () => {
const source = read('assets/js/routes/user-center.js');
const initAt = source.indexOf('function initDashboardNavigation()');
const guardAt = source.indexOf('aksrtUserCenterNavigationInitialized', initAt);
const initialActivateAt = source.indexOf('activate(initialTab, false)', initAt);
assert.ok(initAt >= 0);
assert.ok(guardAt > initAt && guardAt < initialActivateAt);
assert.match(source.slice(initAt, initialActivateAt), /if \(document\.documentElement\.dataset\.aksrtUserCenterNavigationInitialized === 'true'\) return;/);
assert.match(source.slice(initAt, initialActivateAt), /document\.documentElement\.dataset\.aksrtUserCenterNavigationInitialized = 'true';/);
assert.doesNotMatch(source, /name === 'vip' && !forceLoad\) forceLoad = true/);
});
test('homepage sort refreshes one DOM panel without URL state', () => {
const source = read('assets/js/homepage-fixed.js')
const homepage = read('inc/helpers/aksrt-homepage.php')
const controller = read('src/Admin/GlobalController.php')
assert.match(source, /event\.preventDefault\(\)/)
assert.match(source, /requestHomepagePanel\(activePanel, \{/)
assert.doesNotMatch(source, /home_sort|replaceState|location\.search|data-sort-ajax|homepageSortAjax/)
assert.doesNotMatch(homepage, /home_sort|data-sort-ajax|add_query_arg\(['"]home_sort['"]|homepage_sort_ajax_enabled/)
assert.doesNotMatch(controller, /'page_url'\s*=>|'source'\s*=>/)
})
test('HTML fragment injection announces post_ajax before auto_fun', () => {
for (const file of ['assets/js/core/pagination.js', 'assets/js/author.js', 'assets/js/credits.js', 'assets/js/routes/user-center.js']) {
const source = read(file);
@@ -157,6 +157,18 @@ test('lazySizes has one auto_fun integration owner and loader boots main once',
assert.match(runtime, /var cases = jQuery\('\.vanilla-tilt:not\(\.is-initialized\)'\);[\s\S]*?if \(!cases\.length\)[\s\S]*?tbquire\(\['tilt'\]/);
assert.doesNotMatch(pagination, /IntersectionObserver|new Image\(/);
assert.doesNotMatch(widgets, /IntersectionObserver/);
const dynamicSources = [
read('assets/js/order-center.js'),
read('assets/js/credits.js'),
read('assets/js/page-navs.js'),
read('assets/js/pay-vip.js'),
loader,
].join('\n');
const legacyDynamicImageApi = 'aksrt' + 'GetLazyImageAttr';
assert.equal(dynamicSources.includes(legacyDynamicImageApi), false);
assert.match(read('assets/js/order-center.js'), /<img src="' \+ escapeHtml\(image\)/);
assert.match(read('assets/js/page-navs.js'), /<img src="' \+ this\.escHtml\(item\.thumbnail\)/);
assert.match(read('assets/js/pay-vip.js'), /document\.createElement\("img"\)/);
});
test('post lists have no second lazy switch and keep legacy fallback thumbnails', () => {