Align homepage and category discovery, modern widgets, admin controls, quotes, and comments with the reference theme. Remove superseded paths and add rendering and behavior coverage. Update stale asset and finance contracts; all 760 Node tests and 139 widget assertions pass.
115 lines
6.0 KiB
JavaScript
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="zibsvg-hot" viewbox="0 0 16 16" aria-hidden="true"><use href="#zibsvg-hot"><\/use><\/svg>/);
|
|
const sanitizer = read('inc/helpers/aksrt-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.deepEqual(firstQuery.category__in, [7]);
|
|
assert.equal(fixture.aggregation_queries[1].category__in[0], 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="aksrt-panel relative"/);
|
|
assert.match(simple, /title-h-left/);
|
|
assert.match(simple, /共31篇/);
|
|
assert.match(simple, /简洁描述/);
|
|
assert.doesNotMatch(simple, /class="page-cover aksrt-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/aksrt-archive-filters.php');
|
|
const ajax = read('assets/js/core/ajax-list.js');
|
|
const archives = read('inc/helpers/aksrt-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, /\.zib-widget\.posts-mini-lists\s*\{\s*padding:\s*5px;/);
|
|
assert.doesNotMatch(widgetCss, /\.widget-tab-post[^{}]*\{[^}]*!important/s);
|
|
});
|