159 lines
8.9 KiB
JavaScript
159 lines
8.9 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 root = path.resolve(__dirname, '..', '..');
|
|
const read = (file) => fs.readFileSync(path.join(root, file), 'utf8');
|
|
|
|
const frontSubmit = read('src/Core/FrontSubmit.php');
|
|
const mediaTrait = read('src/Core/FrontSubmitMedia.php');
|
|
const page = read('templates/pages/page-submit.php');
|
|
const editor = read('assets/js/front-submit-editor.js');
|
|
const submit = read('assets/js/front-submit.js');
|
|
const payDownload = read('assets/js/front-submit-pay-download.js');
|
|
const domainModel = read('src/Core/DomainModel.php');
|
|
const featured = read('src/Core/FrontSubmitFeatured.php');
|
|
const frontSubmitPay = read('src/Core/FrontSubmitPay.php');
|
|
|
|
const runtimeFiles = [
|
|
'assets/js/runtime/main.js',
|
|
'assets/js/runtime/main.min.js',
|
|
];
|
|
|
|
test('front submit owns the typed media REST routes and media trait wiring', () => {
|
|
assert.match(frontSubmit, /use FrontSubmitMedia\s*;/);
|
|
assert.match(frontSubmit, /add_action\('rest_api_init', \[\$this, 'register_rest_routes'\]\)/);
|
|
assert.match(frontSubmit, /add_action\('aksrt_new_add_posts', \[\$this, 'sync_post_attachments'\]\)/);
|
|
assert.match(frontSubmit, /add_action\('aksrt_new_edit_posts', \[\$this, 'sync_post_attachments'\]\)/);
|
|
|
|
assert.match(frontSubmit, /register_rest_route\('aksrt\/v1', '\/front-submit\/media\/\(\?P<type>image\|video\|file\)'/);
|
|
assert.match(frontSubmit, /'methods'\s*=>\s*\\WP_REST_Server::READABLE/);
|
|
assert.match(frontSubmit, /'callback'\s*=>\s*\[\$this, 'rest_list_attachments'\]/);
|
|
assert.match(frontSubmit, /'post_id'\s*=>\s*\[/);
|
|
assert.match(frontSubmit, /'permission_callback'\s*=>\s*\[\$this, 'can_list_attachments'\]/);
|
|
|
|
assert.match(frontSubmit, /register_rest_route\('aksrt\/v1', '\/front-submit\/upload\/\(\?P<kind>image\|video\|resource\)'/);
|
|
assert.match(frontSubmit, /'methods'\s*=>\s*'POST'/);
|
|
assert.match(frontSubmit, /'callback'\s*=>\s*\[\$this, 'rest_upload_attachment'\]/);
|
|
assert.match(frontSubmit, /'permission_callback'\s*=>\s*\[\$this, 'can_upload_attachment'\]/);
|
|
});
|
|
|
|
test('front submit media listing freezes ownership, visibility, ordering, paging, search, and MIME semantics', () => {
|
|
assert.match(mediaTrait, /\$user_id\s*=\s*get_current_user_id\(\)/);
|
|
assert.match(mediaTrait, /'post_type'\s*=>\s*'attachment'/);
|
|
assert.match(mediaTrait, /'post_status'\s*=>\s*\['inherit', 'private'\]/);
|
|
assert.match(mediaTrait, /'author'\s*=>\s*\$user_id/);
|
|
assert.match(mediaTrait, /'posts_per_page'\s*=>\s*48/);
|
|
assert.match(mediaTrait, /'paged'\s*=>\s*\$page/);
|
|
assert.match(mediaTrait, /'orderby'\s*=>\s*'ID'/);
|
|
assert.match(mediaTrait, /'order'\s*=>\s*'DESC'/);
|
|
assert.match(mediaTrait, /\$request->get_param\('search'\)/);
|
|
assert.match(mediaTrait, /\$request->get_param\('exclude'\)/);
|
|
assert.match(mediaTrait, /\$args\['post__not_in'\]\s*=\s*\$exclude/);
|
|
assert.match(mediaTrait, /\$args\['s'\]\s*=\s*\$search/);
|
|
assert.match(mediaTrait, /add_filter\('wp_allow_query_attachment_by_filename', '__return_true'\)/);
|
|
assert.match(mediaTrait, /remove_filter\('wp_allow_query_attachment_by_filename', '__return_true'\)/);
|
|
assert.match(mediaTrait, /\$args\['post_mime_type'\]\s*=\s*'image'/);
|
|
assert.match(mediaTrait, /\$args\['post_mime_type'\]\s*=\s*'video'/);
|
|
assert.doesNotMatch(mediaTrait, /\$args\['post_mime_type'\]\s*=\s*'file'/);
|
|
assert.match(mediaTrait, /return \$mime !== '';/);
|
|
assert.match(mediaTrait, /!in_array\(\$type, \['image', 'video', 'file'\], true\)/);
|
|
assert.match(frontSubmit, /\['image', 'video', 'file'\]/);
|
|
});
|
|
|
|
test('front submit media responses and uploads use the attachment contract', () => {
|
|
for (const field of ['attachment_id', 'id', 'url', 'filename', 'type', 'subtype', 'thumbnail_url', 'filesizeInBytes']) {
|
|
assert.match(mediaTrait, new RegExp(`['"]${field}['"]`), `missing normalized field ${field}`);
|
|
}
|
|
assert.match(mediaTrait, /wp_prepare_attachment_for_js/);
|
|
assert.match(mediaTrait, /'attachment_id'\s*=>\s*\(int\) \$attachment->ID/);
|
|
assert.match(mediaTrait, /'id'\s*=>\s*\(int\) \$attachment->ID/);
|
|
assert.match(mediaTrait, /'thumbnail_url'\s*=>/);
|
|
assert.match(mediaTrait, /'filesizeInBytes'\s*=>/);
|
|
|
|
assert.match(frontSubmit, /\$files\['file'\]/);
|
|
assert.match(frontSubmit, /media_handle_upload\('aksrt_front_submit_file'/);
|
|
assert.match(frontSubmit, /update_post_meta\(\$attachment_id, '_aksrt_front_submit_media', \$kind\)/);
|
|
assert.match(frontSubmit, /\$attachment\['success'\]\s*=\s*true/);
|
|
assert.match(editor, /body\.append\('file', file\)/);
|
|
assert.match(editor, /method:\s*'POST'/);
|
|
assert.match(editor, /payload\.success/);
|
|
});
|
|
|
|
test('saved submission media sync enforces ownership and cleans stale parent/meta relations', () => {
|
|
assert.match(mediaTrait, /if \(\$user_id <= 0 \|\| \(int\) \$post->post_author !== \$user_id\)/);
|
|
assert.match(mediaTrait, /'post_parent'\s*=>\s*0/);
|
|
assert.match(mediaTrait, /\(int\) \$attachment->post_author !== \$user_id/);
|
|
assert.match(mediaTrait, /\['inherit', 'private'\]/);
|
|
assert.match(mediaTrait, /\(int\) \$attachment->post_parent !== 0/);
|
|
assert.match(mediaTrait, /'meta_key'\s*=>\s*'_aksrt_front_submit_media'/);
|
|
assert.match(mediaTrait, /delete_post_meta\(\$attachment_id, '_aksrt_front_submit_media'\)/);
|
|
assert.match(mediaTrait, /update_post_meta\(\$attachment_id, '_aksrt_front_submit_media', \$kind\)/);
|
|
assert.match(mediaTrait, /featured_video_attachment_id/);
|
|
assert.match(mediaTrait, /data-aksrt-file-id/);
|
|
assert.match(mediaTrait, /\['post_parent'\s*=>\s*\$post_id\]/);
|
|
});
|
|
|
|
test('submit page configures media URLs, REST nonce, and the three media copy groups without mini-upload', () => {
|
|
assert.match(page, /'restNonce'\s*=>\s*wp_create_nonce\('wp_rest'\)/);
|
|
assert.match(page, /'mediaUrl'\s*=>\s*esc_url_raw\(rest_url\('aksrt\/v1\/front-submit\/media'\)\)/);
|
|
assert.match(page, /'image'\s*=>\s*\['url'\s*=>\s*esc_url_raw\(rest_url\('aksrt\/v1\/front-submit\/media\/image'\)\)\]/);
|
|
assert.match(page, /'video'\s*=>\s*\['url'\s*=>\s*esc_url_raw\(rest_url\('aksrt\/v1\/front-submit\/media\/video'\)\)\]/);
|
|
assert.match(page, /'file'\s*=>\s*\['url'\s*=>\s*esc_url_raw\(rest_url\('aksrt\/v1\/front-submit\/media\/file'\)\)\]/);
|
|
for (const copy of ['mineImage', 'mineVideo', 'mineFile', 'uploadImage', 'uploadVideo', 'uploadFile']) {
|
|
assert.match(page, new RegExp(`['"]${copy}['"]`), `missing copy ${copy}`);
|
|
}
|
|
assert.doesNotMatch(page, /aksrt-mini-upload/);
|
|
assert.equal(fs.existsSync(path.join(root, 'assets/js/mini-upload.js')), false, 'mini-upload.js must stay removed');
|
|
});
|
|
|
|
test('shared submit media picker exposes mine search paging selection and nonce-backed upload', () => {
|
|
assert.match(editor, /window\.AksrtSubmitMedia\s*=\s*\{\s*open:/);
|
|
assert.match(editor, /data-editor-media-tab="mine"/);
|
|
assert.match(editor, /data-editor-media-search/);
|
|
assert.match(editor, /state\.search\.length >= 2/);
|
|
assert.match(editor, /state\.search\.length === 1/);
|
|
assert.match(editor, /state\.searchTimer = window\.setTimeout/);
|
|
assert.match(editor, /state\.searchTimer[\s\S]*?600\)/);
|
|
assert.match(editor, /params\.set\('paged', String\(page\)\)/);
|
|
assert.match(editor, /data-editor-media-more/);
|
|
assert.match(editor, /params\.set\('exclude', state\.exclude\.join\(','\)\)/);
|
|
assert.match(editor, /multiple\s*=\s*options\.multiple/);
|
|
assert.match(editor, /if \(!multiple\) maxItems = 1/);
|
|
assert.match(editor, /state\.selected\.length >= maxItems/);
|
|
assert.match(editor, /headers:\s*\{\s*'X-WP-Nonce': cfg\.restNonce \|\| ''\s*\}/);
|
|
assert.match(editor, /data-aksrt-file-id/);
|
|
assert.doesNotMatch(editor, /data-download-file/);
|
|
assert.match(submit, /data-aksrt-submit-ready/);
|
|
});
|
|
|
|
test('paid downloads and featured covers use the shared picker and attachment IDs', () => {
|
|
assert.match(payDownload, /window\.AksrtSubmitMedia\.open\(/);
|
|
assert.match(payDownload, /type:\s*'file'/);
|
|
assert.match(payDownload, /item\.attachment_id \|\| item\.id/);
|
|
assert.match(payDownload, /data-pay-resource-field="attachment_id"/);
|
|
assert.match(domainModel, /'attachment_id'\s*=>\s*is_scalar\(\$row\['attachment_id'\]/);
|
|
assert.match(domainModel, /max\(0, \(int\) \$row\['attachment_id'\]\)/);
|
|
|
|
assert.match(submit, /openMediaPicker:\s*function \(options\)/);
|
|
assert.match(submit, /window\.AksrtSubmitMedia\.open\(options\)/);
|
|
assert.match(submit, /type:\s*'image'/);
|
|
assert.match(submit, /type:\s*'video'/);
|
|
assert.match(submit, /item\.attachment_id \|\| item\.id/);
|
|
assert.match(domainModel, /'featured_video_attachment_id'\s*=>\s*0/);
|
|
assert.match(payDownload, /attachment_id/);
|
|
assert.match(frontSubmitPay, /\$meta\['downloads'\]\s*=\s*\$downloads/);
|
|
assert.match(featured, /featured_video_attachment_id/);
|
|
});
|
|
|
|
test('runtime bundles contain no legacy download-file action handler', () => {
|
|
for (const file of runtimeFiles) {
|
|
const source = read(file);
|
|
assert.doesNotMatch(source, /action=download_file/);
|
|
assert.doesNotMatch(source, /download-nonce/);
|
|
}
|
|
});
|