chore(repo): track full tests/ and tools/ suites, drop per-file whitelist

This commit is contained in:
2026-09-21 20:53:12 +08:00
parent 0afead968a
commit 2618530afb
18 changed files with 9949 additions and 22 deletions
@@ -0,0 +1,322 @@
<?php
/**
* Scratch harness: render term_aggregation with a fuller WP stub surface
* to reproduce the "widget does not work" report. Local-only, not tracked.
*/
declare(strict_types=1);
if (!defined('ABSPATH')) {
define('ABSPATH', __DIR__ . DIRECTORY_SEPARATOR);
}
if (!defined('DAY_IN_SECONDS')) {
define('DAY_IN_SECONDS', 86400);
}
error_reporting(E_ALL);
set_error_handler(static function (int $severity, string $message, string $file, int $line): bool {
if (!(error_reporting() & $severity)) {
return false;
}
throw new ErrorException($message, 0, $severity, $file, $line);
});
class WP_Error {
public function __construct(private string $code = '', private string $message = '') {}
public function get_error_code(): string { return $this->code; }
public function get_error_message(): string { return $this->message; }
}
class WP_Post {
public int $ID;
public function __construct(int $id) { $this->ID = $id; }
}
class WP_Term {
public int $term_id;
public string $name = '';
public string $description = '';
public string $taxonomy = 'category';
public int $term_taxonomy_id;
public function __construct(int $term_id, string $name, string $description = '', string $taxonomy = 'category') {
$this->term_id = $term_id;
$this->name = $name;
$this->description = $description;
$this->taxonomy = $taxonomy;
$this->term_taxonomy_id = $term_id;
}
}
class WP_User {
public int $ID;
public string $display_name;
public function __construct(int $id, string $name) { $this->ID = $id; $this->display_name = $name; }
}
class wpdb {
public string $postmeta = 'wp_postmeta';
public string $posts = 'wp_posts';
public string $term_relationships = 'wp_term_relationships';
public function prepare(string $query, ...$args): string {
foreach ($args as $arg) {
$query = preg_replace('/%[sd]/', is_numeric($arg) ? (string) (int) $arg : "'" . (string) $arg . "'", $query, 1);
}
return $query;
}
public function get_var(string $query): ?string {
unset($query);
return '12345';
}
}
$GLOBALS['wpdb'] = new wpdb();
// Fake posts the stub WP_Query will serve, keyed by expected cat.
$GLOBALS['aksrt_harness_posts'] = [
['id' => 101, 'title' => '第一篇文章', 'cat' => 5],
['id' => 102, 'title' => '第二篇文章', 'cat' => 5],
['id' => 103, 'title' => '第三篇文章', 'cat' => 5],
['id' => 104, 'title' => '第四篇文章', 'cat' => 5],
];
$GLOBALS['aksrt_harness_cursor'] = 0;
class WP_Query {
public array $posts = [];
public int $found_posts = 0;
public function __construct(public array $query_args = []) {
$cat_in = (array) ($query_args['category__in'] ?? []);
$matched = array_values(array_filter($GLOBALS['aksrt_harness_posts'], static fn(array $p): bool => in_array($p['cat'], $cat_in, true)));
$this->found_posts = count($matched);
foreach ($matched as $post) {
$this->posts[] = new WP_Post($post['id']);
$GLOBALS['aksrt_harness_lookup'][$post['id']] = $post;
}
}
public function have_posts(): bool { return $GLOBALS['aksrt_harness_cursor'] < count($this->posts); }
public function the_post(): void {
$post = $this->posts[$GLOBALS['aksrt_harness_cursor']];
$GLOBALS['aksrt_harness_cursor']++;
$GLOBALS['aksrt_harness_current_post'] = $GLOBALS['aksrt_harness_lookup'][$post->ID] ?? ['id' => $post->ID, 'title' => '文章' . $post->ID];
}
}
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_raw($value): string {
$value = trim((string) $value);
return preg_match('#^(https?://|/)#', $value) ? $value : '';
}
function esc_url($value): string { return esc_attr(esc_url_raw($value)); }
function absint($value): int { return abs((int) $value); }
function sanitize_key($value): string { return preg_replace('/[^a-z0-9_\-]/', '', strtolower((string) $value)) ?? ''; }
function sanitize_text_field($value): string { return trim(strip_tags((string) $value)); }
function sanitize_textarea_field($value): string { return trim(strip_tags((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 wp_kses($value, array $allowed): string {
unset($allowed);
return strip_tags((string) $value, '<i><span><item><b><strong><em><badge><div>');
}
function wp_kses_post($value): string { return strip_tags((string) $value, '<a><b><br><p><span>'); }
function wp_strip_all_tags($value): string { return trim(strip_tags((string) $value)); }
function wp_trim_words($text, int $count = 55, string $more = '…'): string {
$words = preg_split('/\s+/', trim((string) $text)) ?: [];
if (count($words) <= $count) return implode(' ', $words);
return implode(' ', array_slice($words, 0, $count)) . $more;
}
function wp_is_mobile(): bool { return false; }
function wp_reset_postdata(): void {}
function is_wp_error($value): bool { return $value instanceof WP_Error; }
function is_super_admin(): bool { return false; }
function is_sticky($id = 0): bool { unset($id); return false; }
function current_time(string $type): int { return $type === 'timestamp' ? time() : 0; }
function human_time_diff(int $from, int $to = 0): string {
unset($to);
return (($from % 3600) + 1) . ' 小时';
}
function get_avatar_url($id, array $args = []): string { return 'https://example.test/avatar-' . (int) $id . '.jpg'; }
function get_post_meta($id, $key = '', $single = false) { unset($id, $key, $single); return ''; }
function get_term_meta(int $term_id, string $key, bool $single = false) {
if ($key === 'aksrt_category_cover_id') {
return $term_id === 5 ? 55 : 0;
}
return '';
}
function wp_get_attachment_url(int $id): string { return $id === 55 ? 'https://example.test/cover-cat-5.jpg' : ''; }
function get_posts(array $args): array {
unset($args);
return [102];
}
function get_the_post_thumbnail_url($post = null, string $size = 'post-thumbnail') {
unset($post, $size);
return 'https://example.test/thumb-cat-8.jpg';
}
function get_terms(array $args): array {
unset($args);
return [new WP_Term(5, '技术分享', '这里是分类描述文本'), new WP_Term(8, '生活记录', '')];
}
function get_term(int $term_id, string $taxonomy = '') {
unset($taxonomy);
return match ($term_id) {
5 => new WP_Term(5, '技术分享', '这里是分类描述文本'),
8 => new WP_Term(8, '生活记录', ''),
default => new WP_Error('invalid_term', 'Term not found'),
};
}
function get_term_link(WP_Term $term) {
return 'https://example.test/cat-' . $term->term_id;
}
function get_the_ID(): int { return (int) ($GLOBALS['aksrt_harness_current_post']['id'] ?? 0); }
function get_the_title($id = 0): string {
unset($id);
return (string) ($GLOBALS['aksrt_harness_current_post']['title'] ?? '');
}
function get_permalink($id = 0): string { return 'https://example.test/post-' . (int) ($id ?: get_the_ID()); }
function get_the_excerpt($id = 0): string { unset($id); return '这是摘要'; }
function get_post_field($field, $id = 0, $context = 'display') {
unset($context);
return $field === 'post_author' ? 1 : '';
}
function get_userdata($id): ?WP_User { return $id > 0 ? new WP_User((int) $id, '作者' . (int) $id) : null; }
function get_the_time($format, $id = 0): string { unset($format, $id); return '2026-09-01 10:00:00'; }
function get_the_modified_time($format, $id = 0): string { unset($format, $id); return '2026-09-04 10:00:00'; }
function get_the_category($id = 0): array { unset($id); return [new WP_Term(5, '技术分享')]; }
function get_the_tags($id = 0) { unset($id); return []; }
function get_comments_number($id = 0): int { unset($id); return 3; }
function get_comments_link($id = 0): string { return get_permalink($id) . '#comments'; }
function comments_open($id = 0): bool { unset($id); return true; }
function get_post_type($id = 0): string { unset($id); return 'post'; }
function has_post_format($formats, $id = 0): bool { unset($formats, $id); return false; }
function aksrt_get_pay_meta($post = null): array { unset($post); return ['pay_type' => 'no', 'price' => 0]; }
function aksrt_get_post_views($id = 0): int { return (int) ($id % 90) + 10; }
function aksrt_get_post_likes($id = 0): int { return (int) ($id % 7); }
function aksrt_get_post_favorites($id = 0): int { return (int) ($id % 5); }
function aksrt_resource_card_badge_text($id = 0): string { unset($id); return ''; }
function aksrt_format_number($num): string { return (string) $num; }
function aksrt_is_lazy(string $key, bool $default = false): bool { unset($key); return $default; }
function aksrt_get_lazy_thumb(string $size = ''): string { unset($size); return 'https://example.test/lazy.svg'; }
function aksrt_get_lazy_attr(string $type, string $src, string $class = '', string $lazy_src = ''): string {
$url = $lazy_src !== '' ? $lazy_src : $src;
return ' class="' . esc_attr($class) . '" src="' . esc_url($url) . '" data-src="' . esc_url($src) . '" data-lazy-type="' . esc_attr($type) . '"';
}
function aksrt_renderer_resolve_images($post_id, array $cfg): array {
unset($post_id, $cfg);
return ['primary' => '', 'has_real' => false, 'gallery' => [], 'slides' => [], 'is_slide' => false, 'video_url' => ''];
}
// --- full pipeline stubs (AssetManager / SchemaWidget / registry) ---
function wp_register_style(...$args): void { unset($args); }
function wp_register_script(...$args): void { unset($args); }
function wp_enqueue_style(...$args): void { unset($args); }
function wp_enqueue_script(...$args): void { unset($args); }
function wp_add_inline_script(...$args): void { unset($args); }
function rest_url(string $path = ''): string { return 'https://example.test/wp-json/' . $path; }
function get_template_directory(): string { return dirname(__DIR__, 2); }
function get_template_directory_uri(): string { return 'https://example.test/theme'; }
function wp_json_encode($value): string { return (string) json_encode($value, JSON_UNESCAPED_UNICODE); }
function esc_textarea($value): string { return esc_html($value); }
function checked($actual, $expected = true, bool $display = true): string {
$result = $actual == $expected ? 'checked="checked"' : '';
if ($display) echo $result;
return $result;
}
function selected($actual, $expected = true, bool $display = true): string {
$result = $actual == $expected ? 'selected="selected"' : '';
if ($display) echo $result;
return $result;
}
class WP_Widget {
public string $id_base = '';
public string $id = '';
public int $number = 0;
public function __construct(string $id_base = '') { $this->id_base = $id_base; }
public function get_field_name(string $field): string { return 'widget-' . $this->id_base . '[' . $this->number . '][' . $field . ']'; }
public function get_field_id(string $field): string { return 'widget-' . $this->id_base . '-' . $this->number . '-' . $field; }
public function _set(int $number): void { $this->number = $number; $this->id = $this->id_base . '-' . $number; }
}
require dirname(__DIR__, 2) . '/vendor/autoload.php';
require dirname(__DIR__, 2) . '/inc/helpers/aksrt-graphic-card.php';
require dirname(__DIR__, 2) . '/inc/helpers/aksrt-widgets-markup.php';
require dirname(__DIR__, 2) . '/inc/helpers/posts-renderer/post-data.php';
use AKSRT\WidgetSystem\Renderers\ContentRenderer;
$instance = [
'term_id' => [5, 8],
'pc_row' => 2,
'orderby' => 'modified',
'order' => 'desc',
'count' => 4,
'target_blank' => false,
'obs_animation' => '',
'animation_repeat' => false,
];
try {
$html = ContentRenderer::render('term_aggregation', $instance);
echo "=== RENDER OK ===\n";
echo $html;
echo "\n=== END ===\n";
} catch (Throwable $e) {
echo '=== FATAL: ' . get_class($e) . ': ' . $e->getMessage() . ' @ ' . $e->getFile() . ':' . $e->getLine() . "\n";
echo $e->getTraceAsString() . "\n";
}
// Single-term variant (is_row=false) + animation variant.
try {
$GLOBALS['aksrt_harness_cursor'] = 0;
$single = ContentRenderer::render('term_aggregation', array_merge($instance, ['term_id' => [5], 'obs_animation' => 'fade']));
echo "=== SINGLE OK (len=" . strlen($single) . ") ===\n";
echo $single . "\n";
} catch (Throwable $e) {
echo '=== SINGLE FATAL: ' . get_class($e) . ': ' . $e->getMessage() . ' @ ' . $e->getFile() . ':' . $e->getLine() . "\n";
}
// Full pipeline: registry -> definition -> normalize(raw form input '5,8') -> FrontendRenderer.
try {
$definitions = \AKSRT\WidgetSystem\WidgetRegistry::all();
echo "=== REGISTRY: term_aggregation present = " . (isset($definitions['term_aggregation']) ? 'yes' : 'NO') . " ===\n";
$definition = $definitions['term_aggregation'];
$widget = new \AKSRT\WidgetSystem\SchemaWidget($definition);
$widget->_set(2);
$normalized = $definition->normalize(['term_id' => '5,8']);
echo "=== NORMALIZED term_id = " . json_encode($normalized['term_id']) . " ===\n";
$GLOBALS['aksrt_harness_cursor'] = 0;
ob_start();
\AKSRT\WidgetSystem\FrontendRenderer::render(
$definition,
['before_widget' => '<section class="w">', 'after_widget' => '</section>'],
$normalized,
$widget
);
$full = (string) ob_get_clean();
echo "=== FULL PATH OK (len=" . strlen($full) . ") ===\n";
echo $full . "\n";
// Empty selection must render nothing at all.
ob_start();
\AKSRT\WidgetSystem\FrontendRenderer::render($definition, ['before_widget' => '<section>', 'after_widget' => '</section>'], $definition->normalize([]), $widget);
$empty = (string) ob_get_clean();
echo "=== EMPTY SELECTION OUTPUT len=" . strlen($empty) . " ===\n";
// Admin form HTML for the terms field (what the widgets screen renders).
ob_start();
$widget->render_full_form($definition->normalize(['term_id' => '5,8']));
$form = (string) ob_get_clean();
if (preg_match('/<div class="aksrt-widget-field aksrt-widget-field--terms".*?<\/div>\s*<\/div>\s*<\/div>/s', $form, $m)) {
echo "=== TERMS FIELD HTML ===\n" . $m[0] . "\n";
}
echo "=== FORM contains hidden terms value name = " . (strpos($form, 'name="widget-aksrt_widget_term_aggregation[2][term_id]"') !== false ? 'yes' : 'NO') . " ===\n";
echo "=== FORM hidden value = ";
preg_match('/class="aksrt-widget-terms__value"[^>]*value="([^"]*)"/', $form, $v);
echo ($v[1] ?? '(not found)') . " ===\n";
} catch (Throwable $e) {
echo '=== FULL PATH FATAL: ' . get_class($e) . ': ' . $e->getMessage() . ' @ ' . $e->getFile() . ':' . $e->getLine() . "\n";
echo $e->getTraceAsString() . "\n";
}