Files
wordpress-theme-aksrt/tests/widget-system/toast-contract.test.cjs
T
kate522 8aa116ec43 fix(theme): align reference layouts and restore passing contracts
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.
2026-10-29 19:16:03 +08:00

464 lines
22 KiB
JavaScript

'use strict';
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const test = require('node:test');
const vm = require('node:vm');
const root = path.resolve(__dirname, '..', '..');
const read = (file) => fs.readFileSync(path.join(root, file), 'utf8');
class FakeElement {
constructor(classes = [], id = '') {
this.classes = new Set(classes);
this.id = id;
this.children = [];
this.parent = null;
this.innerHTML = '';
this.delegated = null;
}
append(child) {
child.parent = this;
this.children.push(child);
}
}
function descendants(element) {
return element.children.flatMap((child) => [child, ...descendants(child)]);
}
function createRuntime() {
const body = new FakeElement(['body']);
const document = { body };
const timers = [];
const window = {
setTimeout(callback, delay) {
timers.push({ callback, delay });
return timers.length;
},
};
class FakeQuery {
constructor(elements) {
this.elements = elements;
this.length = elements.length;
}
append(value) {
let elements = value instanceof FakeQuery ? value.elements : [];
if (typeof value === 'string' && value === '<div class="notyn"></div>') {
elements = [new FakeElement(['notyn'])];
}
this.elements.forEach((parent) => elements.forEach((element) => parent.append(element)));
return this;
}
addClass(classes) {
this.elements.forEach((element) => classes.split(/\s+/).forEach((name) => element.classes.add(name)));
return this;
}
removeClass() {
this.elements.forEach((element) => element.classes.clear());
return this;
}
find(selector) {
return new FakeQuery(this.elements.flatMap(descendants).filter((element) => matches(element, selector)));
}
html(value) {
this.elements.forEach((element) => { element.innerHTML = value; });
return this;
}
remove() {
this.elements.forEach((element) => {
if (!element.parent) return;
element.parent.children = element.parent.children.filter((child) => child !== element);
element.parent = null;
});
return this;
}
on(event, selector, handler) {
this.elements.forEach((element) => { element.delegated = { event, selector, handler }; });
return this;
}
}
function matches(element, selector) {
if (selector.startsWith('.')) return element.classes.has(selector.slice(1));
if (selector.startsWith('#')) return element.id === selector.slice(1);
return false;
}
function parseNotification(html) {
const id = (html.match(/ id="([^"]+)"/) || [])[1] || '';
const content = html.match(/<div class="notyf ([^"]+)">([\s\S]*)<\/div><\/div>$/);
assert.ok(content, 'notification markup should follow the legacy runtime contract');
const wrapper = new FakeElement(['noty1'], id);
const message = new FakeElement(['notyf', content[1]]);
message.innerHTML = content[2];
wrapper.append(message);
return wrapper;
}
function jQuery(value) {
if (value === document.body || value instanceof FakeElement) return new FakeQuery([value]);
if (typeof value === 'string' && value.startsWith('<div class="noty1"')) {
return new FakeQuery([parseNotification(value)]);
}
if (typeof value === 'string') {
return new FakeQuery([body, ...descendants(body)].filter((element) => matches(element, value)));
}
return new FakeQuery([]);
}
vm.runInNewContext(read('assets/js/notyf.js'), { jQuery, window, document });
return { body, timers, window };
}
test('notyf follows legacy-runtime defaults, type normalization, and timing', () => {
const runtime = createRuntime();
runtime.window.notyf('完成');
const container = runtime.body.children[0];
const first = container.children[0];
assert.ok(container.classes.has('notyn'));
assert.ok(first.classes.has('noty1'));
assert.ok(first.children[0].classes.has('success'));
assert.equal(first.children[0].innerHTML, '完成');
assert.equal(runtime.timers[0].delay, 5000);
runtime.window.notyf('失败', 'error', 2);
const second = container.children[1];
assert.ok(second.children[0].classes.has('danger'));
assert.equal(runtime.timers[1].delay, 2000);
});
test('fixed-id notifications stay open until an in-place update', () => {
const runtime = createRuntime();
runtime.window.notyf('处理中', 'load', '', 'request-state');
const container = runtime.body.children[0];
const notification = container.children[0];
assert.equal(runtime.timers.length, 0);
assert.equal(notification.id, 'request-state');
assert.ok(notification.children[0].classes.has('load'));
runtime.window.notyf('<strong>完成</strong>', 'success', 3, 'request-state');
assert.equal(container.children.length, 1);
assert.equal(notification.children[0].innerHTML, '<strong>完成</strong>');
assert.ok(notification.children[0].classes.has('success'));
assert.equal(runtime.timers[0].delay, 3000);
});
test('clicking a notification uses the legacy exit class and removal delay', () => {
const runtime = createRuntime();
runtime.window.notyf('可关闭');
const notification = runtime.body.children[0].children[0];
const delegated = runtime.body.delegated;
assert.deepEqual({ event: delegated.event, selector: delegated.selector }, { event: 'click', selector: '.noty1' });
delegated.handler.call(notification);
assert.ok(notification.classes.has('notyn-out'));
assert.equal(runtime.timers.at(-1).delay, 1000);
runtime.timers.at(-1).callback();
assert.equal(runtime.body.children[0].children.length, 0);
});
test('notyf CSS and WordPress enqueue match the reference contract', () => {
const css = read('assets/css/frontend/notyf.css');
const scripts = read('src/Core/ScriptsPart1c.php');
const builder = read('tools/build-public-assets.mjs');
assert.match(css, /\.notyn\s*\{[\s\S]*?bottom:\s*6em;[\s\S]*?max-width:\s*320px;[\s\S]*?z-index:\s*10000000;/);
assert.match(css, /\.notyf\s*\{[\s\S]*?position:\s*relative;[\s\S]*?font-size:\s*14px;[\s\S]*?line-height:\s*1\.42857143;[\s\S]*?min-width:\s*200px;[\s\S]*?padding:\s*1em 3em 1em 3\.4em;/);
assert.doesNotMatch(css, /\.notyf\s*\{[^}]*white-space:\s*nowrap;/);
assert.match(css, /\.notyf::before\s*\{[\s\S]*?font-family:\s*"Font Awesome 5 Free";[\s\S]*?font-weight:\s*900;[\s\S]*?line-height:\s*1;/);
assert.match(css, /rgba\(15, 147, 249, 0\.7\)[\s\S]*?rgba\(61, 189, 249, 0\.8\)/);
assert.match(css, /rgba\(58, 162, 54, 0\.8\)[\s\S]*?rgba\(89, 247, 131, 0\.8\)/);
assert.match(css, /rgba\(253, 170, 71, 0\.7\)[\s\S]*?rgba\(247, 154, 13, 0\.8\)/);
assert.match(css, /rgba\(253, 69, 28, 0\.7\)[\s\S]*?rgba\(251, 110, 75, 0\.8\)/);
assert.match(css, /content:\s*'\\f058'/);
assert.match(css, /content:\s*'\\f0a1'/);
assert.match(css, /content:\s*'\\f05a'/);
assert.match(css, /content:\s*'\\f057'/);
assert.match(css, /\.notyf\.load::before[\s\S]*?huan-rotate 1s/);
assert.match(builder, /assets\/js\/notyf\.js/);
assert.match(builder, /assets\/css\/frontend\/notyf\.css/);
assert.match(builder, /assets\/js\/runtime\/main\.js/);
assert.match(scripts, /'loader_js',[\s\S]*?'\/assets\/js\/runtime\/loader\.js',[\s\S]*?\['jquery', 'bootstrap'\]/);
});
test('balance center actions use direct fixed-id notyf flows', () => {
const credits = read('assets/js/credits.js');
const signinController = read('assets/js/signin.js');
const scripts = read('src/Core/ScriptsPart1b.php');
const css = read('assets/css/routes/user-center/credits.css');
const shell = read('assets/css/routes/user-center/shell.css');
const header = read('inc/helpers/user-center-renderers/profile-header.php');
const headerUserbox = read('inc/helpers/aksrt-header-userbox.php');
const signinService = read('src/Credits/Signin.php');
const context = read('inc/helpers/aksrt-user-center.php');
const renderers = [
'inc/helpers/user-center-renderers/credits-balance.php',
'inc/helpers/user-center-renderers/credits-wallet.php',
'inc/helpers/user-center-renderers/transfer-modal.php',
'inc/helpers/user-center-renderers/withdraw-modal.php',
].map(read).join('\n');
function section(start, end) {
const from = credits.indexOf('function ' + start + '(');
const to = credits.indexOf('function ' + end + '(', from + 1);
assert.notEqual(from, -1, start + ' should exist');
assert.notEqual(to, -1, end + ' should exist');
return credits.slice(from, to);
}
const flows = [
[signinController, 'aksrt-balance-signin-toast'],
[section('initBalanceCharge', 'initTransfer'), 'aksrt-balance-charge-toast'],
[section('initTransfer', 'initPointsPay'), 'aksrt-balance-transfer-toast'],
[section('initPointsPay', 'initWithdraw'), 'aksrt-balance-points-toast'],
[section('initWithdraw', 'initAssetTabs'), 'aksrt-balance-withdraw-toast'],
];
flows.forEach(([source, id]) => {
assert.match(source, new RegExp("notyf\\([^;]+?'load'[^;]+?'" + id + "'\\);"));
assert.match(source, new RegExp("notyf\\([^;]+?'danger'[^;]+?'" + id + "'\\);"));
assert.match(source, new RegExp("notyf\\([^;]+?'success'[^;]+?'" + id + "'\\);"));
assert.doesNotMatch(source, /setTip|data-(?:signin|aksrt-(?:card|charge|transfer|points-exchange|withdraw))-tip/);
});
assert.match(credits, /notyf\('等待支付结果…', 'load', '', 'aksrt-balance-charge-payment-toast'\)/);
assert.match(signinController, /querySelectorAll\('\[data-signin-btn\]'\)/);
assert.match(signinController, /freshButtons\.forEach[\s\S]*?addEventListener\('click'/);
assert.match(credits, /onTimeout:[\s\S]*?notyf\([^;]+?'danger'[^;]+?'aksrt-balance-charge-payment-toast'\)/);
assert.match(credits, /auto_fun\.aksrt-credits'[\s\S]*?bootAssets\(\);/);
assert.match(credits, /function bootAssets\(\)[\s\S]*?initSigninGoto\(\);[\s\S]*?initBalanceCharge\(\);[\s\S]*?initPointsPay\(\);[\s\S]*?initWithdraw\(\);/);
assert.doesNotMatch(credits, /function initSignin\(|\/signin\/today|POST \/signin/);
assert.match(signinController, /aksrtSigninInitialized/);
assert.match(credits, /aksrtPointsPayInitialized[\s\S]*?aksrtWithdrawInitialized/);
assert.match(scripts, /'aksrt-credits',[\s\S]*?\/assets\/js\/credits\.js',[\s\S]*?\[[^\]]*'loader_js'[^\]]*\]/);
assert.match(scripts, /\$signin_on\s*=\s*is_user_logged_in\(\)[\s\S]*?'aksrt-signin',[\s\S]*?\/assets\/js\/signin\.js',[\s\S]*?'loader_js'/);
assert.ok(header.indexOf('data-signin-btn') < header.indexOf('uc-header__message-btn'));
assert.match(header, /uc-header__message-btn[^>]+aria-label="<\?php esc_attr_e\('消息通知'/);
assert.doesNotMatch(header, /<i class="fa fas fa-bell"[^>]*><\/i>\s*<\?php esc_html_e\('消息通知'/);
assert.doesNotMatch(header, /data-signin-captcha|aksrt_render_captcha\('signin'\)/);
assert.match(headerUserbox, /data-signin-btn[\s\S]*?data-signin-label/);
assert.match(headerUserbox, /add_action\('wp_footer', 'aksrt_render_signin_captcha'\)/);
assert.match(headerUserbox, /data-signin-captcha[\s\S]*?aksrt_render_captcha\('signin'\)/);
assert.match(signinService, /but c-blue ml10 p2-10 radius" data-signin-btn/);
assert.doesNotMatch(headerUserbox + signinService, /data-aksrt-checkin/);
assert.match(context, /'signin_enabled'\s*=>\s*\$signin_enabled/);
assert.match(context, /'signin_signed_today'\s*=>\s*\$signin_signed_today/);
assert.match(shell, /\.uc-header__actions\s*\{[^}]*display:\s*flex;[^}]*gap:\s*6px;/s);
assert.match(shell, /\.uc-header__message-btn\s*\{[^}]*position:\s*relative;/s);
assert.doesNotMatch(renderers, /data-(?:signin|aksrt-(?:card|charge|transfer|points-exchange|withdraw))-tip/);
assert.doesNotMatch(css, /\.uc-(?:card-pass|modal|balance-card)__tip/);
});
test('VIP purchase and points exchange use direct fixed-id notyf flows', () => {
const vip = read('assets/js/pay-vip.js');
const css = read('assets/css/frontend/vip-modal.css');
const scripts = read('src/Core/ScriptsPart1b.php');
assert.match(vip, /notyf\("正在兑换会员…", "load", "", "aksrt-vip-points-toast"\)/);
assert.match(vip, /notyf\([^;]+?"success"[^;]+?"aksrt-vip-points-toast"\)/);
assert.match(vip, /notyf\([^;]+?"danger"[^;]+?"aksrt-vip-points-toast"\)/);
assert.match(vip, /activePurchaseToastId = "aksrt-vip-purchase-toast";[\s\S]*?notyf\("正在创建会员订单…", "load"/);
assert.match(vip, /onPending:[\s\S]*?"info"[\s\S]*?activePurchaseToastId = "aksrt-vip-payment-toast";[\s\S]*?notyf\("等待支付结果…", "load"/);
assert.match(vip, /onSuccess:[\s\S]*?notyf\([^;]+?"success"/);
assert.match(vip, /onFailure:[\s\S]*?notyf\([^;]+?"danger"[^;]+?"aksrt-vip-payment-toast"/);
assert.match(vip, /onTimeout:[\s\S]*?notyf\([^;]+?"danger"[^;]+?"aksrt-vip-payment-toast"/);
assert.match(vip, /已取消会员购买", "warning", 3, activePurchaseToastId/);
assert.match(vip, /会员套餐加载失败,请重试[\s\S]*?"danger"/);
// 积分兑换弹窗改为服务端静态壳(inc/helpers/aksrt-vip.php 输出,pay-vip.js 只负责
// 开合与提交),兑换失败沿用固定 id 的 danger toast。
assert.match(vip, /兑换失败,请重试。[\s\S]*?"danger"/);
assert.doesNotMatch(vip, /setTip|aksrt-vip-modal-tip/);
assert.doesNotMatch(css, /\.aksrt-vip-modal-tip/);
assert.match(scripts, /'aksrt-pay-vip',[\s\S]*?\/assets\/js\/pay-vip\.js',[\s\S]*?\[[^\]]*'loader_js'[^\]]*\]/);
});
test('payment entry points never load the removed legacy pay runtime', () => {
const loader = read('assets/js/runtime/loader.js');
const legacyMain = [
read('assets/js/runtime/main.js'),
read('assets/js/runtime/main.min.js'),
].join('\n');
const vip = read('assets/js/pay-vip.js');
const content = read('assets/js/cms-download.js');
const scripts = [
read('src/Core/ScriptsPart1b.php'),
read('src/Core/ScriptsPart1c.php'),
].join('\n');
assert.doesNotMatch(loader, /['"]pay['"]\s*:/);
assert.doesNotMatch(legacyMain, /tbquire\(\[['"]pay['"]\]\)/);
assert.doesNotMatch(legacyMain, /indexOf\(['"]zippay['"]\)/);
assert.match(vip, /\.pay-vip/);
assert.match(content, /data-aksrt-pay-content/);
assert.match(scripts, /'aksrt-pay-vip'/);
assert.match(scripts, /'aksrt-cms-download'/);
// Every lazy module shares _win.ver, so changing any runtime file must
// invalidate the URL. Watching main.min.js alone misses rebuilt leaves.
assert.match(scripts, /\$runtime_mtimes = array_map\(\s*'filemtime',\s*glob\(get_template_directory\(\) \. '\/assets\/js\/runtime\/\*\.js'\) \?: \[\]/);
assert.match(scripts, /\$runtime_version = \$runtime_mtimes !== \[\] \? \(string\) max\(\$runtime_mtimes\) : \$version/);
assert.match(scripts, /'ver'\s*=> \$runtime_version/);
});
test('profile save uses AJAX rehydrate and a fixed-id notyf flow without query feedback', () => {
const profile = read('assets/js/routes/user-center.js');
const page = read('templates/pages/page-profile.php');
const form = read('inc/helpers/user-center-renderers/profile-form.php');
const layout = read('assets/css/routes/profile/layout.css');
const lists = read('assets/css/routes/profile/lists.css');
const scripts = read('src/Core/ScriptsPart1b.php');
assert.match(profile, /function initProfileForm\(root\)[\s\S]*?aksrtProfileInitialized[\s\S]*?addEventListener\('submit'/);
assert.match(profile, /auto_fun\.aksrt-user-center'[\s\S]*?initProfileForm\(\)/);
assert.match(profile, /notyf\([^;]+?'load'[^;]+?'aksrt-profile-save-toast'\);/);
assert.match(profile, /notyf\([^;]+?'success'[^;]+?'aksrt-profile-save-toast'\);/);
assert.match(profile, /notyf\([^;]+?'danger'[^;]+?'aksrt-profile-save-toast'\);/);
assert.match(profile, /fetch\(ajaxUrl, \{ method: 'POST', credentials: 'same-origin', body: data \}\)/);
assert.match(scripts, /\$user_center_deps[\s\S]*?\[[^\]]*'loader_js'[^\]]*\][\s\S]*?\/assets\/js\/routes\/user-center\.js/);
assert.doesNotMatch(page, /REQUEST_METHOD|profile_error|['"]saved['"]|add_query_arg/);
assert.doesNotMatch([profile, form, layout, lists].join('\n'), /profile-save-msg|profile-form__submit-msg/);
});
test('settings actions use rehydrated fixed-id notyf flows', () => {
const userCenter = read('assets/js/routes/user-center.js');
const auth = read('assets/js/auth.js');
const settings = read('inc/helpers/user-center-renderers/profile-settings.php');
const context = read('inc/helpers/aksrt-user-center.php');
const page = read('templates/pages/page-profile.php');
assert.match(userCenter, /aksrt-settings-oauth-unbind-toast/);
assert.doesNotMatch(userCenter, /aksrt-settings-(?:password|email|phone)-(?:code-)?toast/);
assert.match(userCenter, /notyf\([^;]+?'load'[^;]+?toastId\);/);
assert.match(userCenter, /notyf\([^;]+?'success'[^;]+?toastId\);/);
assert.match(userCenter, /notyf\([^;]+?'danger'[^;]+?toastId\);/);
assert.match(userCenter, /auto_fun\.aksrt-user-center'[\s\S]*?initSettings\(\);[\s\S]*?initSettingsCallbackToast\(\);/);
assert.doesNotMatch(userCenter, /initBindPhone\(/);
assert.match(userCenter, /aksrtSettingsInitialized/);
assert.match(userCenter, /oauth_action[\s\S]*?notyf\(message, type\)[\s\S]*?history\.replaceState/);
assert.doesNotMatch(userCenter, /oauth_unbound/);
assert.match(auth, /\[data-identity-root\], \[data-aksrt-settings-root\]/);
assert.doesNotMatch(auth, /\.settings-section/);
assert.match(userCenter, /\/user\/phone-code/);
assert.match(userCenter, /\/user\/phone-verify/);
const accountSettings = read('inc/helpers/user-center-renderers/settings-account.php');
assert.match(accountSettings, /data-aksrt-sms-scene="verify_old_phone"/);
assert.match(accountSettings, /data-aksrt-sms-scene="bind_new_phone"/);
assert.match(settings, /require \$dir \. '\/settings-account\.php';/);
assert.doesNotMatch(context, /account_modals_html|'account-modals'/);
assert.doesNotMatch(page, /user-center\/account-modals/);
assert.equal(fs.existsSync(path.join(root, 'templates/parts/user-center/account-modals.php')), false);
});
test('front submit actions use direct fixed-id notyf flows', () => {
const submit = read('assets/js/front-submit.js');
const editor = read('assets/js/front-submit-editor.js');
const downloads = read('assets/js/front-submit-pay-download.js');
const source = submit + '\n' + editor + '\n' + downloads;
const page = read('templates/pages/page-submit.php');
[
'aksrt-front-submit-draft-toast',
'aksrt-front-submit-publish-toast',
'aksrt-front-submit-delete-draft-toast',
'aksrt-front-submit-resource-upload-toast',
'aksrt-front-submit-editor-media-toast',
].forEach((id) => assert.match(source, new RegExp("['\"]" + id + "['\"]")));
assert.match(submit, /submit: function[\s\S]*?notyf\([^;]+?'load'[^;]+?toastId[\s\S]*?notyf\([^;]+?'success'[^;]+?toastId[\s\S]*?notyf\([^;]+?'danger'[^;]+?toastId/);
assert.match(submit, /bindDraftDelete: function[\s\S]*?'aksrt-front-submit-delete-draft-toast'[\s\S]*?'load'[\s\S]*?'success'[\s\S]*?'danger'/);
// 封面(单图/多图共用 image 入口、视频封面、视频)选择统一走
// AksrtSubmitMedia 选择器,上传中的 load/success/danger 通知由选择器
// 以固定 id 独立承载;幻灯片动作已并入 image 多选,无独立分支。
['image', 'video_pic', 'video'].forEach((type) => {
assert.match(submit, new RegExp("act === '" + type + "'[\\s\\S]{0,400}?self\\.openMediaPicker\\("));
});
assert.match(editor, /var toastId = 'aksrt-front-submit-editor-media-toast';/);
assert.match(editor, /notify\([^;]+?'load'[^;]+?toastId\);[\s\S]*?notify\([^;]+?'success'[^;]+?toastId\);[\s\S]*?notify\([^;]+?'danger'[^;]+?toastId\);/);
// 付费下载附件选择后仅补一条成功确认,上传过程通知属于选择器。
assert.match(downloads, /AksrtSubmitMedia\.open\([\s\S]*?'success', 5, 'aksrt-front-submit-resource-upload-toast'/);
assert.match(editor, /aksrt-front-submit-editor-media-toast[\s\S]*?'load'[\s\S]*?'success'[\s\S]*?'danger'/);
assert.doesNotMatch(submit, /\b(?:window\.)?alert\s*\(/);
assert.match(page, /'aksrt-front-submit',[\s\S]*?'\/assets\/js\/front-submit\.js',[\s\S]*?\[[^\]]*'loader_js'[^\]]*\]/);
});
test('legacy frontend toast APIs and selectors are fully removed', () => {
const files = [
'assets/js',
'assets/css/frontend',
'assets/css/routes',
'assets/css/build',
'page.php',
'templates/pages/page-profile.php',
];
const legacy = /AksrtToast|aksrtToast|aksrtShowToast|aksrt\.(?:toast|notyf)|showToast|toast-container|toast-item|aksrt-toast/;
function sourceFiles(target) {
const absolute = path.join(root, target);
if (!fs.statSync(absolute).isDirectory()) return [absolute];
return fs.readdirSync(absolute, { withFileTypes: true }).flatMap((entry) => {
if (entry.name === 'admin' || entry.name.endsWith('.min.js')) return [];
const child = path.join(absolute, entry.name);
return entry.isDirectory() ? sourceFiles(path.relative(root, child)) : [child];
});
}
const scanned = files.flatMap(sourceFiles);
const offenders = scanned.filter((file) => legacy.test(fs.readFileSync(file, 'utf8')));
assert.deepEqual(offenders.map((file) => path.relative(root, file)), []);
});
test('public CSS bundles have parser-minified production variants', () => {
const builder = read('tools/build-public-assets.mjs');
assert.match(builder, /require\('esbuild'\)/);
assert.match(builder, /loader: 'css'/);
assert.match(builder, /minify: true/);
for (const basename of ['public', 'homepage', 'single', 'comments']) {
const readablePath = path.join(root, 'assets', 'css', 'build', `${basename}.css`);
const minifiedPath = path.join(root, 'assets', 'css', 'build', `${basename}.min.css`);
assert.equal(fs.existsSync(readablePath), true, `${basename}.css must exist`);
assert.equal(fs.existsSync(minifiedPath), true, `${basename}.min.css must exist`);
assert.ok(
fs.statSync(minifiedPath).size < fs.statSync(readablePath).size * 0.9,
`${basename}.min.css should be at least 10% smaller`,
);
assert.doesNotMatch(fs.readFileSync(minifiedPath, 'utf8'), /\/\* source:/);
}
});
test('WordPress loads minified CSS with a readable-bundle fallback', () => {
const scripts = read('src/Core/ScriptsPart1b.php');
for (const basename of ['public', 'homepage']) {
assert.ok(
scripts.includes(`file_exists(get_template_directory() . '/assets/css/build/${basename}.min.css')`),
);
assert.match(scripts, new RegExp(`\\? '${basename}\\.min\\.css'`));
assert.match(scripts, new RegExp(`: '${basename}\\.css'`));
}
});