diff --git a/assets/js/credits.js b/assets/js/credits.js index 133fdf01..fe5e911a 100644 --- a/assets/js/credits.js +++ b/assets/js/credits.js @@ -56,6 +56,38 @@ }); } + function applyAssetBalances(data) { + data = data || {}; + var wallet = data.wallet_balance !== undefined ? data.wallet_balance : data.wallet; + var points = data.credit_balance !== undefined ? data.credit_balance : data.points_balance; + if (wallet !== undefined && wallet !== null && wallet !== '') { + var walletNumber = Number(wallet); + var walletText = Number.isFinite(walletNumber) ? walletNumber.toFixed(2) : String(wallet); + document.querySelectorAll('[data-aksrt-wallet-value]').forEach(function (element) { + element.textContent = walletText; + }); + } + if (points !== undefined && points !== null && points !== '') { + var pointsNumber = Number(points); + var pointsText = Number.isFinite(pointsNumber) ? pointsNumber.toLocaleString() : String(points); + document.querySelectorAll('[data-aksrt-points-value]').forEach(function (element) { + element.textContent = pointsText; + }); + } + } + + function refreshAssetBalances(snapshot) { + if (snapshot && (snapshot.wallet_balance !== undefined || snapshot.wallet !== undefined)) { + applyAssetBalances(snapshot); + } + return api('/pay/assets').then(function (response) { + if (!response.ok) return null; + var data = response.data || (response.json && response.json.data) || response.json || {}; + applyAssetBalances(data); + return data; + }); + } + // Task-list "去签到" → scroll + click the real asset-card sign-in button. function initSigninGoto() { document.querySelectorAll('[data-aksrt-signin-goto]').forEach(function (el) { @@ -333,6 +365,7 @@ activePoll = null; if (result.success) { notyf('支付成功,正在刷新…', 'success', 5, 'aksrt-balance-charge-payment-toast'); + refreshAssetBalances(); window.setTimeout(function () { window.location.reload(); }, 800); return; } @@ -416,6 +449,7 @@ return; } notyf((data && data.message) || '充值成功,正在刷新…', 'success', 5, 'aksrt-balance-charge-toast'); + refreshAssetBalances(data); window.setTimeout(function () { window.location.reload(); }, 800); }).catch(function () { busy = false; @@ -547,6 +581,7 @@ var data = (r.json && r.json.data) ? r.json.data : r.json; resetOperationKey(form); notyf((data && data.message) || '转账成功,正在刷新…', 'success', 5, 'aksrt-balance-transfer-toast'); + refreshAssetBalances(); window.setTimeout(function () { window.location.reload(); }, 800); }).catch(function () { busy = false; @@ -606,6 +641,7 @@ var data = (r.json && r.json.data) ? r.json.data : r.json; resetOperationKey(form); notyf((data && data.message) || '购买成功,正在刷新…', 'success', 5, 'aksrt-balance-points-toast'); + refreshAssetBalances(data); window.setTimeout(function () { window.location.reload(); }, 800); }).catch(function () { busy = false; @@ -742,8 +778,13 @@ } window.AKSRTWithdrawInit = initWithdraw; + window.AKSRTAssetBalances = { + apply: applyAssetBalances, + refresh: refreshAssetBalances + }; function bootAssets() { + refreshAssetBalances(); initSigninGoto(); initAffiliateRevenue(); initVipShop(); diff --git a/assets/js/routes/user-center.js b/assets/js/routes/user-center.js index 024eeddc..ae1a7fcd 100644 --- a/assets/js/routes/user-center.js +++ b/assets/js/routes/user-center.js @@ -82,11 +82,12 @@ } target.classList.add('is-loading'); var url = restUrl.replace(/\/$/, '') + '/user-center/panel/' + encodeURIComponent(name); - if (name === 'creator' || name === 'revenue' || name === 'posts' || name === 'follow') { + if (name === 'creator' || name === 'revenue' || name === 'posts' || name === 'follow' || name === 'orders') { var params = queryParams || new URLSearchParams(window.location.search); var allowedParams = name === 'creator' ? ['income_page'] : (name === 'revenue' ? ['reward_tab', 'commission_page', 'user_page'] : - (name === 'posts' ? ['post_status', 'posts_page'] : ['follow_list', 'follow_page'])); + (name === 'posts' ? ['post_status', 'posts_page'] : + (name === 'follow' ? ['follow_list', 'follow_page'] : ['order_status', 'order_type', 'order_number', 'order_page']))); allowedParams.forEach(function(key) { if (params.get(key)) url += (url.indexOf('?') === -1 ? '?' : '&') + key + '=' + encodeURIComponent(params.get(key)); }); diff --git a/inc/helpers/aksrt-header-userbox.php b/inc/helpers/aksrt-header-userbox.php index 16e05a7d..ccb6829b 100644 --- a/inc/helpers/aksrt-header-userbox.php +++ b/inc/helpers/aksrt-header-userbox.php @@ -86,9 +86,9 @@ if (!function_exists('aksrt_header_user_wallet_mini')) : $html = ''; $html .= ' ' - . esc_html($currency) . ' ' . esc_html(number_format_i18n($credits)) . ''; + . esc_html($currency) . ' ' . esc_html(number_format_i18n($credits)) . ''; $html .= ' ' - . esc_html__('余额', 'aksrt') . ' ' . esc_html(number_format_i18n($wallet, 2)) . ''; + . esc_html__('余额', 'aksrt') . ' ' . esc_html(number_format_i18n($wallet, 2)) . ''; $html .= ''; return $html; } diff --git a/inc/helpers/user-center-renderers/credits-balance.php b/inc/helpers/user-center-renderers/credits-balance.php index 69b9ec80..258bc267 100644 --- a/inc/helpers/user-center-renderers/credits-balance.php +++ b/inc/helpers/user-center-renderers/credits-balance.php @@ -46,7 +46,7 @@ $currency = (string) ($currency_name ?? __('积分', 'aksrt'));
- +
diff --git a/inc/helpers/user-center-renderers/credits-wallet.php b/inc/helpers/user-center-renderers/credits-wallet.php index 7dffbd34..cdc2b5cd 100644 --- a/inc/helpers/user-center-renderers/credits-wallet.php +++ b/inc/helpers/user-center-renderers/credits-wallet.php @@ -81,7 +81,7 @@ $mark = (string) ($charge_view['currency_mark'] ?? '元');
- +
diff --git a/inc/helpers/user-center-renderers/sidebar-commerce.php b/inc/helpers/user-center-renderers/sidebar-commerce.php index c2647206..4b7dc508 100644 --- a/inc/helpers/user-center-renderers/sidebar-commerce.php +++ b/inc/helpers/user-center-renderers/sidebar-commerce.php @@ -26,7 +26,7 @@ $points = max(0, (int) ($credit_balance ?? 0)); - + @@ -35,7 +35,7 @@ $points = max(0, (int) ($credit_balance ?? 0)); - + diff --git a/src/Core/DomainModel.php b/src/Core/DomainModel.php index d623b3c2..af0b5533 100644 --- a/src/Core/DomainModel.php +++ b/src/Core/DomainModel.php @@ -27,6 +27,7 @@ class DomainModel { public const ORDER_PRODUCT_RESOURCE = 'resource'; public const ORDER_PRODUCT_VIP = 'vip'; public const ORDER_PRODUCT_RECHARGE = 'recharge'; + public const ORDER_PRODUCT_POINTS = 'points'; /** * Human labels for aksrt_order.product_type (front-end order center). @@ -38,6 +39,7 @@ class DomainModel { self::ORDER_PRODUCT_RESOURCE => __('付费内容', 'aksrt'), self::ORDER_PRODUCT_VIP => __('VIP 会员', 'aksrt'), self::ORDER_PRODUCT_RECHARGE => __('余额充值', 'aksrt'), + self::ORDER_PRODUCT_POINTS => __('积分购买', 'aksrt'), ]; } diff --git a/src/Credits/BalanceServiceRest.php b/src/Credits/BalanceServiceRest.php index c0c4da80..93d7953f 100644 --- a/src/Credits/BalanceServiceRest.php +++ b/src/Credits/BalanceServiceRest.php @@ -12,6 +12,12 @@ trait BalanceServiceRest { public function register_routes(): void { + register_rest_route('aksrt/v1', '/pay/assets', [ + 'methods' => \WP_REST_Server::READABLE, + 'callback' => [$this, 'rest_asset_snapshot'], + 'permission_callback' => [$this, 'check_asset_rest'], + ]); + register_rest_route('aksrt/v1', '/pay/balance/charge', [ [ 'methods' => \WP_REST_Server::READABLE, @@ -68,6 +74,17 @@ trait BalanceServiceRest { } + public function rest_asset_snapshot(\WP_REST_Request $request) { + $user_id = get_current_user_id(); + return rest_ensure_response([ + 'success' => true, + 'data' => [ + 'wallet_balance' => \AKSRT\Ledger\LedgerService::get_instance()->balance_decimal($user_id), + 'credit_balance' => (string) \AKSRT\Ledger\LedgerService::get_instance()->balance($user_id, \AKSRT\Ledger\AssetType::POINTS), + ], + ]); + } + public function check_asset_rest(\WP_REST_Request $request) { if (!is_user_logged_in()) { return new \WP_Error('aksrt_need_login', __('请先登录', 'aksrt'), ['status' => 401]); @@ -356,9 +373,36 @@ trait BalanceServiceRest { } return true; }; - $business_mutation = static function () use ($user_id, $amount, $points, $operation_key) { + $order_number = 'PX' . strtoupper(substr(hash('sha256', $operation_key), 0, 30)); + $business_mutation = static function (int $transaction_id) use ($user_id, $amount, $points, $operation_key, $order_number) { + if (!class_exists('\\AKSRT\\Payment\\OrderRepository')) { + return new \WP_Error('aksrt_order_unavailable', __('订单服务不可用', 'aksrt'), ['status' => 500]); + } + $note = wp_json_encode([ + 'points_exchange' => 1, + 'points' => $points, + 'product_title' => sprintf(__('购买 %d 积分', 'aksrt'), $points), + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + $created = \AKSRT\Payment\OrderRepository::get_instance()->create([ + 'order_number' => $order_number, + 'user_id' => $user_id, + 'product_type' => \AKSRT\Core\DomainModel::ORDER_PRODUCT_POINTS, + 'product_id' => 0, + 'amount' => $amount->decimal(), + 'currency' => 'CNY', + 'status' => \AKSRT\Payment\Order::STATUS_COMPLETED, + 'payment_method' => 'balance', + 'payment_transaction_id' => 'ledger_' . $transaction_id, + 'paid_at' => current_time('mysql'), + 'completed_at' => current_time('mysql'), + 'note' => is_string($note) ? $note : '', + 'request_key' => $operation_key, + ]); + if ($created !== $order_number) { + return new \WP_Error('aksrt_order_create_failed', __('积分订单创建失败', 'aksrt'), ['status' => 500]); + } if (class_exists('\\AKSRT\\Payment\\CommissionLedger')) { - return \AKSRT\Payment\CommissionLedger::get_instance()->record_points_exchange($user_id, $amount->decimal(), $points, $operation_key); + return \AKSRT\Payment\CommissionLedger::get_instance()->record_points_exchange($user_id, $amount->decimal(), $points, $operation_key, $order_number); } return true; }; @@ -368,7 +412,7 @@ trait BalanceServiceRest { $points, $operation_key, 'points_exchange', - ['type' => 'points_exchange', 'key' => $operation_key], + ['type' => 'order', 'key' => $order_number], sprintf(__('余额兑换 %d 积分', 'aksrt'), $points), ['rate' => $rate], $guard, @@ -384,6 +428,7 @@ trait BalanceServiceRest { 'wallet_balance' => \AKSRT\Ledger\AssetAmount::from_minor(\AKSRT\Ledger\AssetType::BALANCE, $result->balance($user_id, \AKSRT\Ledger\AssetType::BALANCE))->decimal(), 'credit_balance' => $result->balance($user_id, \AKSRT\Ledger\AssetType::POINTS), 'points' => $points, + 'order_number' => $order_number, 'reload' => true, ]]); } diff --git a/src/Payment/ChargeCard.php b/src/Payment/ChargeCard.php index 592fa579..62b748db 100644 --- a/src/Payment/ChargeCard.php +++ b/src/Payment/ChargeCard.php @@ -714,6 +714,7 @@ class ChargeCard { $amount = $parsed_amount->decimal(); $currency = 'CNY'; } elseif ($type === self::TYPE_CREDITS) { + $product_type = \AKSRT\Core\DomainModel::ORDER_PRODUCT_POINTS; $amount = (int) ($meta['points'] ?? 0); $currency = 'points'; } diff --git a/src/Payment/CommissionLedger.php b/src/Payment/CommissionLedger.php index cdeddef1..53b48af7 100644 --- a/src/Payment/CommissionLedger.php +++ b/src/Payment/CommissionLedger.php @@ -28,9 +28,9 @@ final class CommissionLedger { ? true : new \WP_Error('aksrt_commission_write_failed', __('返佣记录写入失败', 'aksrt'), ['status' => 500]); } - public function record_points_exchange(int $user_id, string $amount, int $points, string $operation_key): bool { + public function record_points_exchange(int $user_id, string $amount, int $points, string $operation_key, string $order_number): bool { $result = $this->record_paid_order([ - 'order_number' => 'PX' . substr(hash('sha256', $operation_key), 0, 30), + 'order_number' => $order_number, 'product_type' => 'credits', 'user_id' => $user_id, 'amount' => $amount, diff --git a/src/Payment/OrderCenterQuery.php b/src/Payment/OrderCenterQuery.php index dd367861..95ce708d 100644 --- a/src/Payment/OrderCenterQuery.php +++ b/src/Payment/OrderCenterQuery.php @@ -89,14 +89,29 @@ final class OrderCenterQuery { public function database_statuses(): array { return match ($this->status()) { self::STATUS_PENDING => [Order::STATUS_CREATED, Order::STATUS_PENDING], - self::STATUS_COMPLETED => [Order::STATUS_PAID], - self::STATUS_CLOSED => [Order::STATUS_CANCELLED, Order::STATUS_EXPIRED], + self::STATUS_COMPLETED => [ + Order::STATUS_PAID, + Order::STATUS_DELIVERED, + Order::STATUS_COMPLETED, + Order::STATUS_PENDING_REVIEW, + ], + self::STATUS_CLOSED => [ + Order::STATUS_CANCELLED, + Order::STATUS_EXPIRED, + Order::STATUS_REFUNDING, + Order::STATUS_REFUNDED, + ], default => [ Order::STATUS_CREATED, Order::STATUS_PENDING, Order::STATUS_PAID, + Order::STATUS_DELIVERED, + Order::STATUS_COMPLETED, + Order::STATUS_PENDING_REVIEW, Order::STATUS_CANCELLED, Order::STATUS_EXPIRED, + Order::STATUS_REFUNDING, + Order::STATUS_REFUNDED, ], }; } diff --git a/src/Payment/OrderRepositoryCore.php b/src/Payment/OrderRepositoryCore.php index 2915ad85..0392b24c 100644 --- a/src/Payment/OrderRepositoryCore.php +++ b/src/Payment/OrderRepositoryCore.php @@ -59,9 +59,10 @@ trait OrderRepositoryCore { 'payment_transaction_id' => sanitize_text_field((string) ($data['payment_transaction_id'] ?? '')), 'created_at' => $data['created_at'] ?? current_time('mysql'), 'paid_at' => $data['paid_at'] ?? null, + 'completed_at' => $data['completed_at'] ?? null, 'note' => $note, ]; - $formats = ['%s', '%d', '%s', '%d', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s']; + $formats = ['%s', '%d', '%s', '%d', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s']; // Pending orders always receive an expiry. Explicit caller values remain // supported for compatibility, while new flows use the shared runtime policy. @@ -204,20 +205,22 @@ trait OrderRepositoryCore { $parts = $this->get_user_order_query_parts($user_id, $args); $pending_statuses = [Order::STATUS_CREATED, Order::STATUS_PENDING]; - $paid_status = Order::STATUS_PAID; + $completed_statuses = [Order::STATUS_PAID, Order::STATUS_DELIVERED, Order::STATUS_COMPLETED, Order::STATUS_PENDING_REVIEW]; + $completed_placeholders = implode(', ', array_fill(0, count($completed_statuses), '%s')); $known_statuses = OrderCenterQuery::from_array([])->database_statuses(); $known_placeholders = implode(', ', array_fill(0, count($known_statuses), '%s')); $sql = "SELECT (SELECT COUNT(*) FROM {$this->table()} {$parts['where']}) AS filtered_total, COUNT(*) AS known_total, SUM(CASE WHEN status IN (%s, %s) THEN 1 ELSE 0 END) AS pending, - SUM(CASE WHEN status = %s THEN 1 ELSE 0 END) AS completed + SUM(CASE WHEN status IN ({$completed_placeholders}) THEN 1 ELSE 0 END) AS completed FROM {$this->table()} WHERE user_id = %d AND status IN ({$known_placeholders})"; $params = array_merge( $parts['params'], $pending_statuses, - [$paid_status, $user_id], + $completed_statuses, + [$user_id], $known_statuses ); $row = $wpdb->get_row($wpdb->prepare($sql, $params), ARRAY_A); diff --git a/src/Payment/UserOrders.php b/src/Payment/UserOrders.php index 5102090e..c4108e9f 100644 --- a/src/Payment/UserOrders.php +++ b/src/Payment/UserOrders.php @@ -286,8 +286,8 @@ class UserOrders { private function status_group(string $status): string { return match ($status) { Order::STATUS_CREATED, Order::STATUS_PENDING => OrderCenterQuery::STATUS_PENDING, - Order::STATUS_PAID => OrderCenterQuery::STATUS_COMPLETED, - Order::STATUS_CANCELLED, Order::STATUS_EXPIRED => OrderCenterQuery::STATUS_CLOSED, + Order::STATUS_PAID, Order::STATUS_DELIVERED, Order::STATUS_COMPLETED, Order::STATUS_PENDING_REVIEW => OrderCenterQuery::STATUS_COMPLETED, + Order::STATUS_CANCELLED, Order::STATUS_EXPIRED, Order::STATUS_REFUNDING, Order::STATUS_REFUNDED => OrderCenterQuery::STATUS_CLOSED, default => OrderCenterQuery::STATUS_CLOSED, }; } @@ -313,6 +313,9 @@ class UserOrders { if ($type === DomainModel::ORDER_PRODUCT_RECHARGE) { return __('余额充值', 'aksrt'); } + if ($type === DomainModel::ORDER_PRODUCT_POINTS) { + return __('积分购买', 'aksrt'); + } if ($product_id > 0) { $cache_key = (string) $product_id; if (!array_key_exists($cache_key, $this->post_title_cache)) { @@ -354,7 +357,7 @@ class UserOrders { if ($type === DomainModel::ORDER_PRODUCT_VIP) { $url = function_exists('aksrt_get_user_center_tab_url') ? aksrt_get_user_center_tab_url('vip') : ''; - } elseif ($type === DomainModel::ORDER_PRODUCT_RECHARGE) { + } elseif (in_array($type, [DomainModel::ORDER_PRODUCT_RECHARGE, DomainModel::ORDER_PRODUCT_POINTS], true)) { $url = function_exists('aksrt_get_user_center_tab_url') ? aksrt_get_user_center_tab_url('credits') : ''; } else { $url = $post_id > 0 ? get_permalink($post_id) : ''; @@ -368,6 +371,7 @@ class UserOrders { return match ($type) { DomainModel::ORDER_PRODUCT_VIP => __('查看会员权益', 'aksrt'), DomainModel::ORDER_PRODUCT_RECHARGE => __('查看余额', 'aksrt'), + DomainModel::ORDER_PRODUCT_POINTS => __('查看积分', 'aksrt'), default => __('打开已购内容', 'aksrt'), }; } @@ -421,7 +425,7 @@ class UserOrders { if (!$order) { return ['success' => false, 'message' => __('当前订单状态不可支付。', 'aksrt')]; } - if ($this->status_group((string) ($order['status'] ?? '')) === OrderCenterQuery::STATUS_PAID) { + if ($this->status_group((string) ($order['status'] ?? '')) === OrderCenterQuery::STATUS_COMPLETED) { $ledger_key = \AKSRT\Ledger\OperationKey::deterministic('order-payment', [$order_number]); $history = \AKSRT\Ledger\LedgerQuery::get_instance()->history(['operation_key' => $ledger_key, 'page' => 1, 'per_page' => 1]); if (!empty($history['items'][0])) { diff --git a/tests/widget-system/asset-ledger-contract.test.cjs b/tests/widget-system/asset-ledger-contract.test.cjs index 4c5e3597..7a8052ff 100644 --- a/tests/widget-system/asset-ledger-contract.test.cjs +++ b/tests/widget-system/asset-ledger-contract.test.cjs @@ -168,3 +168,39 @@ test('admin ledger keeps cash minor values as decimal strings', () => { assert.match(controller, /decimal_from_minor/) assert.doesNotMatch(page, /\/\s*100(?:\D|$)/) }) + +test('asset purchases share the order center and refresh every wallet surface', () => { + const domain = fs.readFileSync(path.join(root, 'src', 'Core', 'DomainModel.php'), 'utf8') + const query = fs.readFileSync(path.join(root, 'src', 'Payment', 'OrderCenterQuery.php'), 'utf8') + const userOrders = fs.readFileSync(path.join(root, 'src', 'Payment', 'UserOrders.php'), 'utf8') + const repository = fs.readFileSync(path.join(root, 'src', 'Payment', 'OrderRepositoryCore.php'), 'utf8') + const balanceRest = fs.readFileSync(path.join(root, 'src', 'Credits', 'BalanceServiceRest.php'), 'utf8') + const chargeCard = fs.readFileSync(path.join(root, 'src', 'Payment', 'ChargeCard.php'), 'utf8') + const creditsJs = fs.readFileSync(path.join(root, 'assets', 'js', 'credits.js'), 'utf8') + const sidebar = fs.readFileSync(path.join(root, 'inc', 'helpers', 'user-center-renderers', 'sidebar-commerce.php'), 'utf8') + const header = fs.readFileSync(path.join(root, 'inc', 'helpers', 'aksrt-header-userbox.php'), 'utf8') + + assert.match(domain, /ORDER_PRODUCT_POINTS\s*=\s*'points'/) + assert.match(domain, /self::ORDER_PRODUCT_POINTS\s*=>\s*__\('积分购买'/) + for (const status of ['STATUS_PAID', 'STATUS_DELIVERED', 'STATUS_COMPLETED', 'STATUS_PENDING_REVIEW']) { + assert.match(query, new RegExp(`Order::${status}`)) + } + assert.match(userOrders, /Order::STATUS_DELIVERED, Order::STATUS_COMPLETED, Order::STATUS_PENDING_REVIEW/) + assert.doesNotMatch(userOrders, /OrderCenterQuery::STATUS_PAID/) + assert.match(repository, /\$completed_statuses = \[Order::STATUS_PAID, Order::STATUS_DELIVERED, Order::STATUS_COMPLETED, Order::STATUS_PENDING_REVIEW\]/) + assert.match(repository, /SUM\(CASE WHEN status IN \(\{\$completed_placeholders\}\) THEN 1 ELSE 0 END\) AS completed/) + + assert.match(balanceRest, /register_rest_route\('aksrt\/v1', '\/pay\/assets'/) + assert.match(balanceRest, /'product_type'\s*=>\s*\\AKSRT\\Core\\DomainModel::ORDER_PRODUCT_POINTS/) + assert.match(balanceRest, /\['type'\s*=>\s*'order',\s*'key'\s*=>\s*\$order_number\]/) + assert.match(balanceRest, /'order_number'\s*=>\s*\$order_number/) + assert.match(chargeCard, /self::TYPE_CREDITS[\s\S]*?ORDER_PRODUCT_POINTS/) + + assert.match(creditsJs, /function refreshAssetBalances\(/) + assert.match(creditsJs, /querySelectorAll\('\[data-aksrt-wallet-value\]'/) + assert.match(creditsJs, /querySelectorAll\('\[data-aksrt-points-value\]'/) + assert.match(sidebar, /data-aksrt-wallet-value/) + assert.match(sidebar, /data-aksrt-points-value/) + assert.match(header, /data-aksrt-wallet-value/) + assert.match(header, /data-aksrt-points-value/) +}) diff --git a/tests/widget-system/post-visual-contract.test.cjs b/tests/widget-system/post-visual-contract.test.cjs index c5444594..59887632 100644 --- a/tests/widget-system/post-visual-contract.test.cjs +++ b/tests/widget-system/post-visual-contract.test.cjs @@ -366,6 +366,8 @@ test('profile routes load versioned leaf styles and keep dialogs hidden initiall assert.match(profileOrders, /data-aksrt-orders-filter-panel hidden/); assert.match(ordersCss, /\.aksrt-orders-page__filter-panel\[hidden\]\s*\{\s*display:\s*none\s*!important;/); assert.match(orderCenterJs, /if \(panel\) panel\.hidden = expanded;/); + assert.match(userCenterJs, /name === 'follow' \|\| name === 'orders'/); + assert.match(userCenterJs, /\['order_status', 'order_type', 'order_number', 'order_page'\]/); }); test('public user home stays param-free and swaps panels via REST fragments', () => {