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>
284 lines
15 KiB
JavaScript
284 lines
15 KiB
JavaScript
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
const path = require('node:path');
|
|
const test = require('node:test');
|
|
|
|
const root = path.resolve(__dirname, '..', '..');
|
|
const read = (file) => fs.readFileSync(path.join(root, file), 'utf8');
|
|
|
|
test('block and classic editors share the reference article extension fields', () => {
|
|
const meta = read('src/Core/PostMetaBox.php');
|
|
const fields = [
|
|
'thumbnail_url',
|
|
'cover_image',
|
|
'featured_slide',
|
|
'featured_video',
|
|
'featured_video_title',
|
|
'featured_video_episode',
|
|
'subtitle',
|
|
'views',
|
|
'like',
|
|
'show_layout',
|
|
'no_article-navs',
|
|
'article_maxheight_xz',
|
|
];
|
|
|
|
assert.ok(meta.includes("add_meta_box('post_main', __('文章扩展'"));
|
|
assert.match(meta, /\['post\.php', 'post-new\.php'\]/);
|
|
assert.match(meta, /wp_enqueue_media\(\)/);
|
|
fields.forEach((field) => assert.match(meta, new RegExp(field.replace('-', '\\-'))));
|
|
assert.doesNotMatch(meta, /custom_excerpt|header_image_mode|show_reading_progress|enable_lightbox|enable_pdf|copyright_text|is_featured/);
|
|
});
|
|
|
|
test('front submit follows the reference newposts shell and cards', () => {
|
|
const page = read('templates/pages/page-submit.php');
|
|
const main = read('templates/parts/submit/editor-main.php');
|
|
const terms = read('templates/parts/submit/sidebar-terms.php');
|
|
const pay = read('templates/parts/submit/sidebar-pay.php');
|
|
const submit = read('templates/parts/submit/sidebar-submit.php');
|
|
const submitJs = read('assets/js/front-submit.js');
|
|
const submitCss = read('assets/css/frontend/pages/submit.css');
|
|
const publicCss = read('assets/css/build/public.min.css');
|
|
const mainCss = read('assets/css/main.css');
|
|
const layout = read('inc/helpers/template-tags/site-layout.php');
|
|
const mobileSubmitCss = submitCss.slice(submitCss.indexOf('@media (max-width: 991px)'));
|
|
|
|
assert.match(page, /content-wrap newposts-wrap/);
|
|
assert.match(page, /class="content-layout"/);
|
|
assert.match(page, /<\/div>\s*<\/div>\s*<div class="sidebar show-sidebar">/);
|
|
assert.match(layout, /if \(is_page_template\('templates\/pages\/page-submit\.php'\)\) \{\s*return true;/);
|
|
assert.equal((page.match(/class="sidebar show-sidebar"/g) || []).length, 1);
|
|
assert.match(page, /'jio-submit',[\s\S]*?\['jio-public'\]/);
|
|
assert.match(main, /jio-widget-box full-widget-sm editor-main-box/);
|
|
assert.match(main, /relative newposts-title/);
|
|
assert.match(terms, /newpost-cat-drop drop-select dropdown dependency-box/);
|
|
assert.match(terms, /data-toggle-class="open" data-target="\.newpost-cat-drop"/);
|
|
assert.match(terms, /jio_submit_render_term_checks\(\$ctx\['categories'\], array_map\('intval', \$ctx\['current_cats'\]\), 'category'\)/);
|
|
assert.doesNotMatch(terms, /aria-expanded=/);
|
|
assert.doesNotMatch(submitJs, /bindDropSelect|term-select-box \[data-toggle-class/);
|
|
assert.doesNotMatch(submitCss, /\.term-select-box \.dropdown-menu|\.term-select-box \.drop-select\s*{/);
|
|
assert.match(mainCss, /\.drop-select \.dropdown-menu\s*{[^}]*box-shadow:\s*0 0 0 8000px/);
|
|
assert.match(mainCss, /\.open > \.dropdown-menu\s*{[^}]*visibility:\s*unset/);
|
|
assert.match(pay, /jio-widget-box dependency-box mb10-sm newpost-pay-box/);
|
|
assert.match(pay, /pay-dawnload-edit/);
|
|
assert.match(submit, /separator muted-3-color theme-box/);
|
|
assert.match(submit, /new-posts-submit/);
|
|
assert.equal((submit.match(/fa-fw/g) || []).length, 2);
|
|
assert.doesNotMatch(submit, /fa-(?:dot-circle|check-square) mr6/);
|
|
assert.match(publicCss, /\.but-average\s*>\s*\.but\s*{[^}]*flex:\s*auto/);
|
|
assert.match(submitCss, /#jio-submit-form \.new-posts-submit\s*{[^}]*white-space:\s*nowrap;/);
|
|
assert.match(submitCss, /#jio-submit-form \.but-average > \.new-posts-submit\s*{[^}]*min-width:\s*0;[^}]*flex:\s*1 1 50%;/);
|
|
assert.match(mobileSubmitCss, /body \.newposts-wrap\s*{[^}]*float:\s*none;/);
|
|
assert.match(mobileSubmitCss, /body \.sidebar\.show-sidebar\s*{[^}]*display:\s*unset;[^}]*width:\s*100%;[^}]*margin-left:\s*0;/);
|
|
assert.doesNotMatch(submitCss, /\.newposts-wrap\s*>\s*\.(?:content-layout|sidebar)/);
|
|
assert.doesNotMatch(submitCss, /\.newposts-wrap \.new-posts-submit/);
|
|
// The form wraps both float columns; flow-root is its only layout job.
|
|
assert.match(submitCss, /#jio-submit-form\s*{[^}]*display:\s*flow-root;/);
|
|
assert.doesNotMatch(submitCss, /#jio-submit-form\s*{[^}]*display:\s*(?:grid|flex);/);
|
|
// Submit chrome gating lives in footer.php; no dead filter/body-class duplicates.
|
|
assert.doesNotMatch(read('src/Core/FrontSubmit.php'), /jio_floating_buttons_enabled|jio_footer_tabbar_enabled|jio-hide-float|jio-hide-tabbar|disable_chrome_on_submit/);
|
|
});
|
|
|
|
test('front submit paid resources use the reference modal editor and compact list', () => {
|
|
const page = read('templates/pages/page-submit.php');
|
|
const pay = read('templates/parts/submit/sidebar-pay.php');
|
|
const submit = read('assets/js/front-submit.js');
|
|
const downloads = read('assets/js/front-submit-pay-download.js');
|
|
const model = read('src/Core/DomainModel.php');
|
|
|
|
assert.match(page, /jio-front-submit-pay-download/);
|
|
assert.match(submit, /jioSubmitPayDownloads\.init/);
|
|
assert.match(pay, /data-pay-download-add/);
|
|
assert.match(pay, /data-pay-download-edit/);
|
|
assert.match(pay, /data-pay-download-remove/);
|
|
assert.match(pay, /class="hidden-input"/);
|
|
assert.doesNotMatch(pay, /data-pay-download-upload|data-pay-download-tpl/);
|
|
|
|
['url', 'attachment_id', 'extract_code', 'copy_key', 'copy_val', 'label', 'class'].forEach((field) => {
|
|
assert.match(downloads, new RegExp(`['"]${field}['"]`));
|
|
assert.match(model, new RegExp(`['"]${field}['"]`));
|
|
});
|
|
assert.match(downloads, /pay-dawnload-edit-modal/);
|
|
assert.match(downloads, /data-pay-resource-upload/);
|
|
assert.match(downloads, /data-pay-resource-classes/);
|
|
assert.match(downloads, /提取码\|密码/);
|
|
assert.doesNotMatch(downloads, /shop|forum|bbs_/i);
|
|
});
|
|
|
|
test('the post editor resource row mirrors the reference seven fields with icon support', () => {
|
|
const meta = read('src/Core/ResourceMetaBox.php');
|
|
const model = read('src/Core/DomainModel.php');
|
|
const setup = read('src/Core/Setup.php');
|
|
|
|
['下载地址', '资源备注', '复制的名称', '复制的内容', '自定义按钮图标', '自定义按钮文案', '自定义按钮颜色'].forEach((label) => {
|
|
assert.ok(meta.includes(label), `missing label: ${label}`);
|
|
});
|
|
|
|
// copy_key / copy_val / class must be real inputs, not hidden-only leftovers.
|
|
['url', 'extract_code', 'copy_key', 'copy_val', 'icon', 'label', 'class'].forEach((field) => {
|
|
assert.match(meta, new RegExp(`\\[${field}\\]`));
|
|
assert.match(model, new RegExp(`'${field}'`));
|
|
});
|
|
// copy_key / copy_val must be real visible inputs, not hidden-only leftovers.
|
|
// The color field legitimately keeps a hidden store behind the swatch buttons.
|
|
assert.doesNotMatch(meta, /type="hidden"[^>]*name="[^"]*\[(?:copy_key|copy_val)\]"/);
|
|
|
|
// Accordion + drag reorder affordances.
|
|
assert.match(meta, /data-group-toggle/);
|
|
assert.match(meta, /data-group-drag/);
|
|
assert.match(meta, /jio-icon-picker_assets\(|jio_icon_picker_field\(/);
|
|
// The swatch palette and the sanitizer share one class whitelist.
|
|
assert.match(meta, /DomainModel::DOWNLOAD_BUTTON_CLASSES/);
|
|
assert.match(model, /const DOWNLOAD_BUTTON_CLASSES/);
|
|
|
|
const js = read('assets/js/admin-pay-meta-box.js');
|
|
assert.match(js, /dragstart/);
|
|
assert.match(js, /dragover/);
|
|
assert.match(js, /reindexRows/);
|
|
|
|
// Reindexing rewrites the row index for every field and is scoped to its own
|
|
// group, so dragging a download row can never renumber attributes/episodes.
|
|
const reindex = (group, name, index) =>
|
|
name.replace(new RegExp(`jio_pay\\[${group}\\]\\[\\d+\\]`), `jio_pay[${group}][${index}]`);
|
|
['url', 'label', 'extract_code', 'copy_key', 'copy_val', 'icon', 'class'].forEach((field) => {
|
|
assert.equal(reindex('downloads', `jio_pay[downloads][0][${field}]`, 3), `jio_pay[downloads][3][${field}]`);
|
|
});
|
|
assert.equal(reindex('downloads', 'jio_pay[downloads][5][url]', 1), 'jio_pay[downloads][1][url]');
|
|
assert.equal(reindex('downloads', 'jio_pay[attributes][0][key]', 9), 'jio_pay[attributes][0][key]');
|
|
assert.equal(reindex('downloads', 'jio_pay[video_episode][1][title]', 9), 'jio_pay[video_episode][1][title]');
|
|
|
|
assert.match(setup, /'icon' => \['type' => 'string'\]/);
|
|
|
|
// The icon picker registers itself as depending on the caller's handle, so the
|
|
// pay-meta script must NOT declare the icon-picker handle back: that cycle makes
|
|
// WP_Dependencies recurse until memory is exhausted on every post editor load.
|
|
const picker = read('inc/helpers/jio-icon-picker.php');
|
|
assert.match(picker, /array_merge\(\['jquery', 'jio-svg-sprite'\], \$deps\)/);
|
|
assert.match(meta, /jio_icon_picker_assets\(\['jio-pay-meta-box'\]\)/);
|
|
const payScript = meta.match(/wp_enqueue_script\('jio-pay-meta-box',[^\n]*\)/)[0];
|
|
assert.doesNotMatch(payScript, /jio-icon-picker/);
|
|
});
|
|
|
|
test('front submit paid-content prices and switch use the reference alignment rules', () => {
|
|
const pay = read('templates/parts/submit/sidebar-pay.php');
|
|
const submitCss = read('assets/css/frontend/pages/submit.css');
|
|
|
|
assert.match(pay, /<div class="form-switch flex0"><\/div>/);
|
|
assert.match(submitCss, /\.newpost-pay-price \.line-form-input\s*{[^}]*text-align:\s*right;/);
|
|
assert.doesNotMatch(submitCss, /\.newpost-pay-price \.line-form-input\s*{[^}]*width:\s*130px;/);
|
|
assert.doesNotMatch(submitCss, /\.newpost-pay-box \.form-switch(?:::after)?\s*{/);
|
|
});
|
|
|
|
test('front submit image and visibility actions live in the TinyMCE toolbar', () => {
|
|
const main = read('templates/parts/submit/editor-main.php');
|
|
const page = read('templates/pages/page-submit.php');
|
|
const submit = read('assets/js/front-submit.js');
|
|
const editor = read('assets/js/front-submit-editor.js');
|
|
|
|
assert.match(main, /\$toolbar1\[\]\s*=\s*'jio_image'/);
|
|
assert.match(main, /\$toolbar1\[\]\s*=\s*'jio_hide'/);
|
|
assert.match(main, /mce_external_plugins/);
|
|
assert.match(main, /jio_editor_tools/);
|
|
assert.match(editor, /PluginManager\.add\('jio_editor_tools'/);
|
|
assert.match(editor, /addButton\('jio_image'/);
|
|
assert.match(editor, /addButton\('jio_hide'/);
|
|
assert.match(editor, /type:\s*'menubutton'/);
|
|
assert.match(editor, /data-editor-media-tab="upload"/);
|
|
assert.match(editor, /data-editor-media-tab="external"/);
|
|
assert.match(editor, /class="jio-editor-media__dialog" tabindex="-1"/);
|
|
assert.match(editor, /querySelector\('\.jio-editor-media__dialog'\)/);
|
|
assert.doesNotMatch(editor, /focusTarget\s*=\s*root\.querySelector\('\[data-editor-media-tab\]\.is-active'\)/);
|
|
assert.match(editor, /\(cfg\.uploads \|\| \{\}\)\.image/);
|
|
assert.match(page, /'canUploadImage'\s*=>\s*!empty\(\$ctx\['can_upload_img'\]\)/);
|
|
assert.doesNotMatch(main, /newposts-editor-tools|data-insert-shortcode|data-insert-upload-image/);
|
|
assert.doesNotMatch(submit, /bindShortcodes|data-insert-shortcode|data-insert-upload-image/);
|
|
assert.doesNotMatch(editor, /hidecontent|shop|forum|bbs_/i);
|
|
});
|
|
|
|
test('front submit exposes the complete reference media and formatting toolset', () => {
|
|
const main = read('templates/parts/submit/editor-main.php');
|
|
const page = read('templates/pages/page-submit.php');
|
|
const editor = read('assets/js/front-submit-editor.js');
|
|
const editorCss = read('assets/css/frontend/pages/submit-editor.css');
|
|
const submit = read('src/Core/FrontSubmit.php');
|
|
|
|
['jio_video', 'jio_file', 'jio_quote', 'jio_precode'].forEach((button) => {
|
|
assert.match(main, new RegExp(`['"]${button}['"]`));
|
|
assert.match(editor, new RegExp(`addButton\\(['"]${button}['"]`));
|
|
});
|
|
assert.doesNotMatch(main, /array_merge\(\$toolbar1, \['blockquote'/);
|
|
assert.match(main, /\['jio_quote', 'jio_precode', 'fullscreen', 'wp_adv'\]/);
|
|
assert.match(main, /toolbar2[\s\S]*forecolor/);
|
|
assert.match(main, /colorpicker,textcolor/);
|
|
|
|
assert.match(page, /'canUploadVideo'\s*=>\s*!empty\(\$ctx\['can_upload_video'\]\)/);
|
|
assert.match(page, /'canEmbedVideo'\s*=>\s*!empty\(\$ctx\['can_iframe'\]\)/);
|
|
assert.match(page, /'canUploadFile'\s*=>\s*!empty\(\$ctx\['can_upload_file'\]\)/);
|
|
assert.match(page, /'video'\s*=>\s*!empty\(\$ctx\['can_upload_video'\]\)/);
|
|
assert.match(submit, /\(\?P<kind>image\|video\|resource\)/);
|
|
assert.match(submit, /new_post_upload_video/);
|
|
|
|
assert.match(editor, /data-editor-media-tab="upload"/);
|
|
assert.match(editor, /data-editor-media-tab="external"/);
|
|
assert.match(editor, /data-editor-media-tab="embed"/);
|
|
assert.match(editor, /new-dplayer post-dplayer dplayer/);
|
|
assert.match(editor, /file-download-box/);
|
|
assert.match(editor, /wp-block-embed is-type-video/);
|
|
assert.match(editor, /data-enlighter-language/);
|
|
assert.match(editor, /data-enlighter-theme/);
|
|
assert.match(editor, /enlighter-pre/);
|
|
assert.match(editor, /qe_wzk_c-red/);
|
|
assert.match(editor, /qe_wzk_lan/);
|
|
assert.match(editor, /qe_wzk_lv/);
|
|
assert.match(editorCss, /\.quote_q/);
|
|
assert.match(editorCss, /\.quote_q \[contenteditable="true"\][\s\S]*?box-shadow:\s*none/);
|
|
assert.match(editorCss, /\.file-download-box/);
|
|
assert.match(editorCss, /\.enlighter-pre/);
|
|
assert.doesNotMatch(editor, /shop|forum|bbs_/i);
|
|
});
|
|
|
|
test('article paid-content metabox follows the reference field set without shop enhancements', () => {
|
|
const meta = read('src/Core/ResourceMetaBox.php');
|
|
const fields = [
|
|
'pay_type',
|
|
'pay_limit',
|
|
'pay_modo',
|
|
'order_expired_s',
|
|
'points_price',
|
|
'original_price',
|
|
'promotion_tag',
|
|
'gallery_show',
|
|
'video_episode',
|
|
'downloads',
|
|
'attributes',
|
|
'pay_doc',
|
|
'pay_details',
|
|
'pay_extra_hide',
|
|
];
|
|
|
|
assert.match(meta, /add_meta_box\('jio_pay_meta', __\('付费功能'/);
|
|
fields.forEach((field) => assert.match(meta, new RegExp(field)));
|
|
|
|
// video_scale_height 是参考主题轮播组件 widget 的参数,不属于付费视频字段,
|
|
// 当初误搬到付费 metabox 上且从未被消费,已移除。
|
|
assert.doesNotMatch(meta, /video_scale_height/);
|
|
assert.doesNotMatch(meta, /jio_pay_(?:stock|purchase_limit|card_keys|free_preview|require_login|require_captcha)/);
|
|
assert.doesNotMatch(meta, /电商增强|parse_json_array/);
|
|
});
|
|
|
|
test('ordinary article editor chain does not import shop or forum routes', () => {
|
|
const files = [
|
|
'src/Core/PostMetaBox.php',
|
|
'src/Core/FrontSubmit.php',
|
|
'src/Core/FrontSubmitSave.php',
|
|
'src/Core/FrontSubmitPay.php',
|
|
'src/Core/FrontSubmitFeatured.php',
|
|
'templates/pages/page-submit.php',
|
|
'templates/parts/submit/sidebar-pay.php',
|
|
];
|
|
const source = files.map(read).join('\n');
|
|
|
|
assert.doesNotMatch(source, /shop_product|forum_post|bbs_/i);
|
|
assert.doesNotMatch(source, /jiopay_s|featured_payload|vip_1_(?:price|points)|vip_2_(?:price|points)/);
|
|
});
|