Files
wordpress-theme-aksrt/tests/widget-system/discovery-reference-contract.test.cjs
T

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.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="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);
});