Files
wordpress-theme-aksrt/tests/widget-system/discovery-reference-contract.test.cjs
kate522andClaude Opus 5 427968a721 chore(branding): remove leftover upstream theme identifiers
The theme was renamed Zibll/子比 → AKSRT → Jio, but upstream branding
survived the sweeps in CSS classes, the SVG sprite prefix, a persisted
widget field ID, JS events, admin Vue class names, test infrastructure
and docblocks.

Renames:
- zib-widget / zib-widget-wrap / zib-slider → jio-widget-box /
  jio-widget-box-wrap / jio-slider-box (jio-widget is taken by the modern
  widget shell, jio-box-* by the card primitives)
- zib-partners-scroll + zps-* → jio-partners-scroll + jps-*
- zib-combo-info-card + zci-* → jio-combo-info-card + jci-*
- zibsvg- → jiosvg-, including the exported picker helpers and the
  fa-zibsvg- family-prefix guard
- zibpay_type → pay_type; the dual-read fallback in PostQuery collapses
  to a single key
- [zibajax="submit"] dropped (no PHP emitter); zib_ajax.success →
  jio_ajax.success
- aks-* admin Vue classes → jio-*
- zib_palette() → palette()

User-facing vendor traces are gone: the floating "more" button default no
longer ships a zibll.com link or 子比主题 marketing copy, the notice
widget ships no preset promo entries, and the four 官网教程 links are
removed from the post-list admin schema.

Test infrastructure renamed to neutral names (docker-compose.reference.yml,
Caddyfile, article-baseline.json, modern-widgets-contract.test.cjs) with
their DB/volume/env/slug identifiers following.

Kept intentionally, documented in tests/fixtures/README.md: the golden
fixture reference fingerprint (a SHA-256 record of an external directory,
read by validate-widget-golden / generate-widget-golden-fixture /
audit-widget-system), the doesNotMatch guards that prove the old tokens are
gone, and the reference theme's own meta key in the visual fixtures.

No backward-compat or data migration: saved widget configs holding old
icon keys or the old field ID silently lose them, per the project's
rewrite-over-compatibility rule.

Verified: 132/132 node contract tests, php tests/widget-system/run.php
(146 assertions), audit-admin-contracts, audit-psr4-paths and
validate-widget-golden all green; tracked bundles regenerated and in sync.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-11-02 00:06:54 +08:00

115 lines
6.0 KiB
JavaScript

const test = require('node:test');
const assert = require('node:assert/strict');
const { execFileSync } = require('node:child_process');
const fs = require('node:fs');
const path = require('node:path');
const root = path.resolve(__dirname, '..', '..');
const read = (file) => fs.readFileSync(path.join(root, file), 'utf8');
function renderFixtures() {
const output = execFileSync('php', [path.join(__dirname, 'discovery-reference-harness.php')], { encoding: 'utf8' });
return JSON.parse(output);
}
test('term aggregation renders the real SVG use element, total count, and configured first page', () => {
const fixture = renderFixtures();
const card = fixture.aggregation;
assert.match(card, /<svg class="jiosvg-hot" viewbox="0 0 16 16" aria-hidden="true"><use href="#jiosvg-hot"><\/use><\/svg>/);
const sanitizer = read('inc/helpers/jio-graphic-card.php');
assert.match(sanitizer, /'svg'\s*=>\s*\['class'\s*=>\s*true,\s*'viewbox'\s*=>\s*true,\s*'aria-hidden'\s*=>\s*true\]/);
assert.match(sanitizer, /'use'\s*=>\s*\['href'\s*=>\s*true,\s*'xlink:href'\s*=>\s*true\]/);
assert.match(card, /7篇文章/);
assert.match(card, /文章 101/);
assert.match(card, /文章 102/);
assert.match(card, /class="fa far fa-file-alt fa-fw"/);
assert.match(card, /class="fa far fa-clock mr3"/);
assert.doesNotMatch(card, /文章 103|空分类/);
const baseCss = read('assets/css/frontend/base.css');
assert.match(baseCss, /\.fa\.far\s*\{[^}]*font-weight:\s*400;[^}]*\}/s);
const firstQuery = fixture.aggregation_queries[0];
assert.equal(firstQuery.posts_per_page, 2);
assert.equal(firstQuery.no_found_rows, false);
assert.equal(firstQuery.cat, '7');
assert.equal(fixture.aggregation_queries[1].cat, '8');
});
test('category filters keep dropdown and inline terms in separate rendered groups on the ajax-option pipeline', () => {
const fixture = renderFixtures();
const html = fixture.archive_filter_html;
const group = fixture.filter_group;
assert.deepEqual(group.chips.map((chip) => chip.label), ['技术分类']);
assert.deepEqual(group.dropdown.map((chip) => chip.label), ['下拉分类']);
assert.match(html, /class="ajax-option ajax-replace" win-ajax-replace="filter"/);
assert.match(html, /class="ajax-next" href="https:\/\/example\.test\/category\/7"/);
const dropdown = html.match(/<ul class="dropdown-menu">([\s\S]*?)<\/ul>/)[1];
const inline = html.match(/<ul class="list-inline[^>]*option-items">([\s\S]*?)<\/ul>/)[1];
assert.match(dropdown, /下拉分类/);
assert.doesNotMatch(dropdown, /技术分类/);
assert.match(inline, /技术分类/);
assert.doesNotMatch(inline, /下拉分类/);
});
test('category header matches first-page, later-page, and simple-title render contracts', () => {
const fixture = renderFixtures();
const first = fixture.term_header_first;
assert.match(first, /win-ajax-replace="page-cover"/);
assert.match(first, /src="https:\/\/example\.test\/theme\/assets\/img\/user_t\.jpg"/);
assert.match(first, /共31篇/);
assert.match(first, /class="main-search-btn"[^>]*search-trem="7"/);
const second = fixture.term_header_second;
assert.doesNotMatch(second, /win-ajax-replace="page-cover"|共31篇/);
assert.match(second, /第2页/);
assert.match(second, /search-trem="7"/);
const simple = fixture.term_header_simple;
assert.match(simple, /class="jio-panel relative"/);
assert.match(simple, /title-h-left/);
assert.match(simple, /共31篇/);
assert.match(simple, /简洁描述/);
assert.doesNotMatch(simple, /class="page-cover jio-panel|<img/);
});
test('homepage sorting retains distinct dropdown and inline option containers', () => {
const html = renderFixtures().home_sort;
assert.match(html, /<ul class="dropdown-menu">[\s\S]*更新[\s\S]*随机[\s\S]*<\/ul>/);
assert.match(html, /<ul class="list-inline[^>]*option-items"><a[^>]*orderby=date[\s\S]*发布[\s\S]*<a[^>]*orderby=views[\s\S]*浏览[\s\S]*<\/ul>/);
assert.equal((html.match(/class="ajax-next/g) || []).length, 9);
});
test('number pagination renders a page trigger and tab panes distinguish loaded from lazy content', () => {
const fixture = renderFixtures();
assert.match(fixture.posts, /class="widget-ajaxpager"[^>]*data-loaded="1"/);
assert.match(fixture.posts, /class="next-page page-numbers"[^>]*data-widget-page="2"/);
assert.match(fixture.tabs, /data-segment="0" data-loaded="1"/);
const lazyPane = fixture.tabs.match(/<div class="widget-ajaxpager"[^>]*data-segment="1"[^>]*>[\s\S]*?<\/div>/)[0];
assert.doesNotMatch(lazyPane, /data-loaded="1"/);
assert.match(fixture.tabs, /class="next-page page-numbers"[^>]*data-widget-page="2"/);
const runtime = read('assets/widgets/frontend.js');
assert.match(runtime, /container\.dataset\.loaded = '1'/);
assert.match(runtime, /if \(panel\.dataset\.loaded !== '1'\) loadItems\(panel, 1, false\)/);
assert.match(runtime, /var pageLink = event\.target\.closest\('\.page-numbers\[data-widget-page\]'\)/);
assert.match(runtime, /loadItems\(container, Number\(pageLink\.dataset\.widgetPage \|\| 1\), false\)/);
});
test('archive filters use the shared ajax-next handler without a category REST/archive script path', () => {
const filters = read('inc/helpers/jio-archive-filters.php');
const ajax = read('assets/js/core/ajax-list.js');
const archives = read('inc/helpers/jio-archive.php');
const globalController = read('src/Admin/GlobalController.php');
assert.match(filters, /class="ajax-option ajax-replace" win-ajax-replace="filter"/);
assert.match(filters, /class="ajax-next/);
assert.match(ajax, /link\.closest\("\.ajax-option, \.post_ajax_trigger"\)/);
assert.doesNotMatch(archives, /register_rest_route|rest_archive_posts|\/archive\/posts/);
assert.doesNotMatch(globalController, /register_rest_route\([^\n]*['"][^'"]*archive\/posts/);
assert.equal(fs.existsSync(path.join(root, 'assets', 'js', 'core', 'archive.js')), false);
const widgetCss = read('assets/widgets/frontend.css');
assert.match(widgetCss, /\.jio-widget-box\.posts-mini-lists\s*\{\s*padding:\s*5px;/);
assert.doesNotMatch(widgetCss, /\.widget-tab-post[^{}]*\{[^}]*!important/s);
});