Files
wordpress-theme-aksrt/tests/widget-system/comment-image-upload-contract.test.cjs
T
kate522andClaude Opus 5 0007e78bfe fix(comments): render image upload progress like the reference
While uploading, the confirm button turned into a thin 3px bar below the
label instead of the reference's striped overlay that fills the button.

Two causes:
- the markup used <span class="progress">; Bootstrap sizes the bar with a
  block-level .progress, so the swap produced a flattened line
- shell.css carried a bespoke override pinning .progress to
  `position: static; height: 3px`, discarding the absolute full-size
  overlay that main.css already provides

Changes:
- emit the reference skeleton: <div class="progress progress-striped active">
  > <div class="progress-bar progress-bar-success">, so the striped bar
  spans the button and the gradient shows through the translucent layer
- give the upload button `position: relative` so the overlay is contained by
  the button rather than the dropdown panel; delete the static-line override
- restore the reference's "正在处理请稍等..." toast and switch the label to
  "处理中 ..." at 100%, both keyed to the shared `miniupload_ajax` toast id
  so the completion message replaces the loading one
- notify() now forwards the toast id instead of dropping it

Tests: comment-image-upload contract gains two cases pinning the div-based
striped overlay and the deletion of the 3px override; suite 835/836 with the
pre-existing frontend-lifecycle failure.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-11-03 17:12:52 +08:00

109 lines
5.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use strict';
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');
const formParts = read('inc/helpers/comments/form-parts.php');
const tools = read('assets/js/comment-tools.js');
const controller = read('src/Admin/UserController.php');
const display = read('src/Settings/PostDisplay.php');
const phpSchema = read('src/Admin/Schema/SchemaPostList.php');
const adminSchema = read('assets/admin/src/schema/sections/post-list/comment.js');
const runtime = read('src/Core/ScriptsPart1c.php');
const commentsCss = read('assets/css/build/comments.css');
const runtimeBundle = read('assets/js/runtime/main.min.js');
test('comment image settings use one two-switch contract', () => {
for (const source of [phpSchema, adminSchema]) {
assert.match(source, /comment_img/);
assert.match(source, /comment_upload_img/);
}
assert.match(display, /!empty\(\$settings\['comment_upload_img'\]\)/);
assert.match(display, /is_user_logged_in\(\)/);
assert.match(display, /jio_user_can\('upload_comment_image'/);
assert.doesNotMatch(runtime, /commentUploadEnabled/);
assert.doesNotMatch(runtime, /'comment_upload_img'\s*=>/);
});
test('comment image form matches the single-file reference interaction', () => {
assert.match(formParts, /comment-image-tabs/);
assert.match(formParts, /input-image-upload-tab/);
assert.match(formParts, /input-image-address-tab/);
assert.match(formParts, /class="form-upload comment-image-upload"/);
assert.match(formParts, /class="preview text-center mb6"/);
assert.match(formParts, /class="hide comment-image-file"/);
assert.match(formParts, /accept="image\/gif,image\/jpeg,image\/jpg,image\/png"/);
assert.match(formParts, /name="image"/);
assert.match(formParts, /data-comment-upload-submit/);
assert.equal((formParts.match(/name="image"/g) || []).length, 1);
});
test('comment uploader validates, previews, tracks progress, and inserts only a valid URL', () => {
assert.match(tools, /FileReader/);
assert.match(tools, /commentUploadMaxSize/);
assert.match(tools, /validateImage/);
assert.match(tools, /XMLHttpRequest/);
assert.match(tools, /xhr\.upload\.onprogress/);
assert.match(tools, /formData\.append\('image'/);
assert.match(tools, /response\.success === true/);
assert.match(tools, /response\.data\.url/);
assert.match(tools, /'\[img=' \+ url \+ '\]\\n'/);
assert.doesNotMatch(tools, /formData\.append\(['"]file['"]/);
assert.doesNotMatch(tools, /source_url/);
assert.doesNotMatch(tools, /attachment_id/);
assert.doesNotMatch(tools, /input-image-upload(?!-tab)/);
});
test('comment upload REST endpoint has explicit nonce, switch, capability, and image-only guards', () => {
assert.match(controller, /check_comment_image_upload_permission/);
assert.match(controller, /wp_verify_nonce\(\$nonce, 'wp_rest'\)/);
assert.match(controller, /\$settings\['comment_img'\]/);
assert.match(controller, /\$settings\['comment_upload_img'\]/);
assert.match(controller, /jio_user_can\('upload_comment_image'/);
assert.match(controller, /empty\(\$_FILES\['image'\]\)/);
assert.match(controller, /\$file_key = 'image'/);
assert.match(controller, /wp_delete_attachment\(\(int\) \$attachment_id, true\)/);
assert.match(controller, /'url' => \$url/);
assert.doesNotMatch(controller, /\$_FILES\['file'\]/);
assert.doesNotMatch(controller, /'attachment_id'\s*=>/);
});
test('built comment assets carry the upload contract and no removed submit handler', () => {
assert.match(commentsCss, /comment-image-upload/);
assert.match(commentsCss, /progress-bar/);
assert.doesNotMatch(runtimeBundle, /handleResponse/);
});
test('上传中骨架与参考主题一致:Bootstrap 条纹进度层铺满按钮', () => {
// 参考结构:<span class="miniupload-ing"> + <div class="progress progress-striped active">
// 内层 <div class="progress-bar progress-bar-success">,用 div 而非 span。
assert.match(
tools,
/'<span class="miniupload-ing">'[\s\S]{0,400}'<div class="progress progress-striped active"><div class="progress-bar progress-bar-success" role="progressbar" style="width:0"><\/div><\/div>'/,
'上传中骨架必须复用参考主题的 miniupload-ing + Bootstrap 条纹进度层'
);
assert.doesNotMatch(tools, /<span class="progress progress-striped/, '进度层必须是 div,span 会破坏 Bootstrap 的块级布局');
assert.match(tools, /window\.notyf\('正在处理请稍等\.\.\.', 'load', '', 'miniupload_ajax'\)/, '上传开始应弹出参考主题的「正在处理」提示');
assert.match(tools, /loadingText\.innerHTML = '处理中 \.\.\.'/, '到达 100% 后文案切换为「处理中 ...」');
});
test('上传按钮提供定位上下文,并去掉自造的静态细条覆盖', () => {
const shell = read('assets/css/frontend/comments/shell.css');
assert.match(
shell,
/#comments \.comment-image-upload \.input-expand-upload \{[\s\S]*?position: relative;/,
'.progress 是绝对铺满层,按钮必须是定位祖先'
);
assert.doesNotMatch(
shell,
/#comments \.comment-image-upload \.progress \{[\s\S]{0,200}position: static/,
'不得再把 .progress 改成静态细条(偏离参考主题)'
);
assert.doesNotMatch(shell, /height: 3px;[\s\S]{0,80}#comments \.comment-image-upload \.progress/, '自造的 3px 细条已删除');
});