进行了一些小优化

This commit is contained in:
2026-08-31 20:58:48 +08:00
parent 2a3e8bf392
commit 2085a702b9
7 changed files with 350 additions and 43 deletions
@@ -262,10 +262,15 @@ test('header multifunction search keeps focus and submit alignment route-indepen
)
})
test('user profile and author widgets render the legacy user-card box', () => {
assert.match(frontendRenderer, /!in_array\(\$definition->slug\(\), \['user_card', 'author_card'\], true\)/)
// 登录卡片与 legacy zib_get_user_card_box 同构:封面、头像框、名字行、统计徽章、简介、按钮。
assert.match(userRenderer, /user-card zib-widget/)
test('user profile and author widgets render the reference user-card contract', () => {
assert.match(frontendRenderer, /in_array\(\$definition->slug\(\), \['user_card', 'author_card'\], true\)/)
assert.match(frontendRenderer, /self::render_user_widget\(\$definition, \$instance, \$widget, \$content\)/)
assert.match(frontendRenderer, /\$classes = \['zib-widget-wrap', 'aksrt-user-widget-wrap'/)
assert.match(frontendRenderer, /echo '<div class="widget-container"><div>';/)
assert.match(frontendRenderer, /\$content_classes = \['widget-content'\]/)
// 登录卡片与 reference zib_get_user_card_box 同构:封面、头像框、名字行、统计徽章、简介、按钮。
assert.match(userRenderer, /<div class="mb20">/)
assert.match(userRenderer, /user-card zib-widget widget/)
assert.match(userRenderer, /user-cover graphic" style="padding-bottom: 50%;"/)
assert.match(userRenderer, /avatar-img avatar-lg/)
assert.match(userRenderer, /user-name flex jc/)
@@ -278,15 +283,28 @@ test('user profile and author widgets render the legacy user-card box', () => {
assert.match(userRenderer, /'show_posts' => false,/)
assert.match(userRenderer, /'show_checkin' => true,/)
assert.match(userRenderer, /aksrt_header_user_checkin_btn\(\$user_id, true, 'img-badge jb-yellow'\)/)
// 文章作者信息小工具显示文章模块。
// 文章作者信息小工具显示文章模块,并走同一 widget 变体。
assert.match(userRenderer, /'show_posts' => !isset\(\$i\['show_posts'\]\) \|\| !empty\(\$i\['show_posts'\]\),/)
assert.match(userRenderer, /'post_type' => 'post',/)
assert.match(userRenderer, /self::card_box\(\(int\) \$user->ID,[\s\S]*?\) \. '<\/div>';/)
assert.match(userRenderer, /aksrt_render_user_card_posts\(/)
// 未登录卡片与 legacy zib_get_user_singin_page_box 同构。
assert.match(userRenderer, /'post_type' => \(string\) \$args\['post_type'\],/)
// 未登录卡片使用主题认证真值和 reference 登录/社交 DOM。
assert.match(userRenderer, /Settings::close_sign\(\)/)
assert.match(userRenderer, /Settings::registration_enabled\(\)/)
assert.match(userRenderer, /signin-loader but jb-blue padding-lg/)
assert.match(userRenderer, /signup-loader ml10 but jb-yellow padding-lg/)
assert.match(userRenderer, /social-separator separator muted-3-color em09 mt20 mb20/)
assert.match(userRenderer, /social_loginbar/)
assert.match(userRenderer, /social-login-item /)
assert.match(userRenderer, /loged_title/)
// 旧的自造卡片样式已清理。
assert.doesNotMatch(widgetCss, /aksrt-user-card/)
assert.doesNotMatch(userRenderer, /user-card__auth-btns|render_social_buttons\(/)
// 后加载的 widget bundle 明确保留单层卡片并让封面贴边。
assert.match(widgetCss, /\.user-card\.zib-widget\s*\{[^}]*padding:\s*0;[^}]*overflow:\s*hidden;/s)
assert.match(legacyMainCss, /\.user-card\.widget \.user-cover \+ \.card-content\s*\{[^}]*margin-top:\s*-55px;/s)
assert.match(widgetCss, /\.aksrt-user-widget-wrap > \.widget-container > div > \.widget-content\s*\{\s*padding:\s*0;/)
assert.doesNotMatch(widgetCss, /user-card__auth-btns/)
assert.doesNotMatch(widgetCss, /\.aksrt-user-card\s*\{/)
assert.doesNotMatch(userRenderer, /aksrt-widget-button is-ghost/)
})
@@ -0,0 +1,46 @@
'use strict'
const assert = require('node:assert/strict')
const path = require('node:path')
const { spawnSync } = require('node:child_process')
const test = require('node:test')
const root = path.resolve(__dirname, '../..')
test('user and author widgets render the reference state contract', () => {
const result = spawnSync(process.env.PHP_BINARY || 'php', [path.join(__dirname, 'user-card-runtime-harness.php')], {
cwd: root,
encoding: 'utf8',
})
assert.equal(result.status, 0, result.stderr || result.stdout)
const data = JSON.parse(result.stdout)
assert.match(data.guest, /^<div class="mb20"><div class="user-card zib-widget widget">/)
assert.match(data.guest, /data-lazy-type="lazy_other"/)
assert.match(data.guest, /alt="默认头像"/)
assert.match(data.guest, /<div class="user-info mt10"><div class="text-center"><p class="muted-color box-body em12">/)
assert.match(data.guest, /signin-loader but jb-blue padding-lg/)
assert.match(data.guest, /signup-loader ml10 but jb-yellow padding-lg/)
assert.match(data.guest, /social-separator separator muted-3-color em09 mt20 mb20/)
assert.match(data.guest, /social_loginbar/)
assert.match(data.guest, /class="social-login-item github"/)
assert.match(data.guest, /class="social-login-item qq" data-provider="clogin_qq"/)
assert.doesNotMatch(data.guest, /aksrt-widget|aksrt-panel-wrap|user-card__auth-btns|oauth-row|oauth-btn/)
assert.match(data.guest_no_registration, /signin-loader but jb-blue padding-lg/)
assert.doesNotMatch(data.guest_no_registration, /signup-loader/)
assert.equal(data.closed, '')
assert.match(data.logged, /^<div class="mb20"><div class="user-card zib-widget widget">/)
assert.match(data.logged, /data-lazy-type="lazy_cover"/)
assert.match(data.logged, /<div class="card-content mt10 relative">/)
assert.match(data.logged, /<div class="user-name flex jc">/)
assert.match(data.logged, /<div class="author-tag mt10 mini-scrollbar">/)
assert.match(data.logged, /<div class="user-desc mt10 muted-2-color em09">/)
assert.doesNotMatch(data.logged, /data-post-type=/)
assert.match(data.author, /^<div class="mb20"><div class="user-card zib-widget widget">/)
assert.match(data.author, /data-lazy-type="lazy_cover"/)
assert.match(data.author, /data-post-type="post" data-post-style="card"/)
assert.doesNotMatch(data.author, /user-btns mt20/)
})
@@ -0,0 +1,173 @@
<?php
declare(strict_types=1);
namespace AKSRT\Settings {
final class Settings {
public static bool $closed = false;
public static bool $registration = true;
public static function close_sign(): bool {
return self::$closed;
}
public static function registration_enabled(): bool {
return self::$registration;
}
}
}
namespace AKSRT\Auth {
final class OAuth {
public static function get_instance(): self {
return new self();
}
public function get_auth_providers_data(): array {
return [
[
'name' => 'github',
'label' => 'GitHub',
'icon' => '<i class="fa fab fa-github"></i>',
'url' => 'https://example.test/oauth/github',
],
[
'name' => 'clogin_qq',
'label' => 'QQ · 聚合',
'icon' => '<i class="fa fab fa-qq"></i>',
'url' => 'https://example.test/oauth/qq',
],
];
}
}
}
namespace {
if (!defined('ABSPATH')) {
define('ABSPATH', __DIR__);
}
final class WP_User {
public int $ID;
public string $display_name;
public function __construct(int $id, string $display_name) {
$this->ID = $id;
$this->display_name = $display_name;
}
}
final class WP_Post {
public int $ID;
public int $post_author;
public function __construct(int $id, int $post_author) {
$this->ID = $id;
$this->post_author = $post_author;
}
}
$GLOBALS['aksrt_user_card_state'] = [
'current_user_id' => 0,
'post' => null,
];
function __($text, $domain = null): string { unset($domain); return (string) $text; }
function esc_html__($text, $domain = null): string { return esc_html(__($text, $domain)); }
function esc_attr__($text, $domain = null): string { return esc_attr(__($text, $domain)); }
function esc_html($value): string { return htmlspecialchars((string) $value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); }
function esc_attr($value): string { return esc_html($value); }
function esc_url($value): string { return esc_attr((string) $value); }
function sanitize_key($value): string { return preg_replace('/[^a-z0-9_\-]/', '', strtolower((string) $value)) ?? ''; }
function sanitize_html_class($value): string { return preg_replace('/[^A-Za-z0-9_-]/', '', (string) $value) ?? ''; }
function wp_parse_args($args, array $defaults = []): array { return array_merge($defaults, is_array($args) ? $args : []); }
function get_option($name, $default = false) { unset($name); return $default; }
function get_current_user_id(): int { return (int) $GLOBALS['aksrt_user_card_state']['current_user_id']; }
function get_userdata($user_id): ?WP_User {
$user_id = (int) $user_id;
return $user_id > 0 ? new WP_User($user_id, 'User ' . $user_id) : null;
}
function get_post(): ?WP_Post { return $GLOBALS['aksrt_user_card_state']['post']; }
function aksrt_get_user_cover_url(int $user_id): string {
return $user_id > 0 ? 'https://example.test/cover-' . $user_id . '.jpg' : 'https://example.test/default-cover.jpg';
}
function aksrt_get_lazy_thumb(): string { return 'https://example.test/lazy.svg'; }
function aksrt_get_lazy_attr(string $type, string $url, string $class, string $placeholder = ''): string {
return ' class="' . esc_attr($class) . '" data-lazy-type="' . esc_attr($type) . '" src="' . esc_url($placeholder ?: $url) . '" data-src="' . esc_url($url) . '"';
}
function aksrt_get_default_avatar_url(): string { return 'https://example.test/default-avatar.png'; }
function aksrt_get_avatar_html($subject, int $size, array $attrs = []): string {
unset($subject);
return '<img class="' . esc_attr((string) ($attrs['class'] ?? 'avatar')) . '" alt="' . esc_attr((string) ($attrs['alt'] ?? '')) . '" width="' . $size . '" height="' . $size . '">';
}
function aksrt_get_avatar_box($user_id, string $class): string {
return '<a href="https://example.test/user/' . (int) $user_id . '"><span class="' . esc_attr($class) . '"><img class="avatar"></span></a>';
}
function aksrt_get_user_name(array $args): string {
return '<name class="' . esc_attr((string) ($args['class'] ?? '')) . '"><a class="display-name">User ' . (int) ($args['id'] ?? 0) . '</a></name>';
}
function aksrt_get_user_badges(int $user_id): string { return '<span class="badg">' . $user_id . '</span>'; }
function aksrt_get_user_description(int $user_id): string { return 'Description ' . $user_id; }
function aksrt_header_user_checkin_btn(int $user_id, bool $large, string $class): string {
unset($user_id, $large);
return '<button class="' . esc_attr($class) . '">签到</button>';
}
function aksrt_render_user_card_posts(array $args): string {
return '<div data-post-type="' . esc_attr((string) ($args['post_type'] ?? '')) . '" data-post-style="' . esc_attr((string) ($args['post_style'] ?? '')) . '"></div>';
}
function aksrt_get_new_post_url(): string { return 'https://example.test/new-post'; }
function aksrt_get_user_home_url($user_id): string { return 'https://example.test/user/' . (int) $user_id; }
function aksrt_get_user_center_url(): string { return 'https://example.test/user-center'; }
function aksrt_renderer_svg_icon(string $name): string { return '<svg data-icon="' . esc_attr($name) . '"></svg>'; }
require dirname(__DIR__, 2) . '/src/WidgetSystem/Renderers/UserRenderer.php';
function aksrt_render_user_widget_state(string $slug, array $instance): string {
return \AKSRT\WidgetSystem\Renderers\UserRenderer::render($slug, $instance);
}
\AKSRT\Settings\Settings::$closed = false;
\AKSRT\Settings\Settings::$registration = true;
$GLOBALS['aksrt_user_card_state']['current_user_id'] = 0;
$guest = aksrt_render_user_widget_state('user_card', [
'show_img_bg' => true,
'loged_title' => 'HI!请登录',
]);
\AKSRT\Settings\Settings::$registration = false;
$guest_no_registration = aksrt_render_user_widget_state('user_card', [
'show_img_bg' => true,
'loged_title' => 'HI!请登录',
]);
\AKSRT\Settings\Settings::$closed = true;
$closed = aksrt_render_user_widget_state('user_card', []);
\AKSRT\Settings\Settings::$closed = false;
\AKSRT\Settings\Settings::$registration = true;
$GLOBALS['aksrt_user_card_state']['current_user_id'] = 7;
$logged = aksrt_render_user_widget_state('user_card', [
'show_img_bg' => true,
'show_button' => false,
]);
$GLOBALS['aksrt_user_card_state']['current_user_id'] = 2;
$GLOBALS['aksrt_user_card_state']['post'] = new WP_Post(33, 9);
$author = aksrt_render_user_widget_state('author_card', [
'show_img_bg' => true,
'show_posts' => true,
'post_style' => 'card',
'limit' => 6,
'orderby' => 'date',
'show_button' => true,
]);
echo json_encode([
'guest' => $guest,
'guest_no_registration' => $guest_no_registration,
'closed' => $closed,
'logged' => $logged,
'author' => $author,
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
}