'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\/\(\?Pimage\|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\/\(\?Pimage\|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/); } });