Files
RenewCMS/features/editor_image_upload.qa.md
T

4.4 KiB

QA: The editor's Upload tab uploads to the library

Automated (Vitest + Vue Test Utils, task test:web)

Mock XMLHttpRequest (a fake with open, setRequestHeader, send, abort, firing upload.progress, load, error) for POST /v1/media/. Mock useApi's request for GET /v1/media/ (list and ?hash=), reusing the mock from api_client_global_error_handling.qa.md. Stub hashFile where a hash is needed, and add one test with crypto.subtle deleted. Mount EditorPage inside DashboardShell at /article/12/edit with the editor_core.qa.md fixture article, GET already resolved 200. Assert URLs by parsing the query, not by string equality.

  1. Toolbar button, empty library - GET /v1/media/?page=1&size=25 resolves {items: [], total: 0, page: 1, size: 25, counts: {all: 0, unused: 0, duplicates: 0}}; click "Image"; assert the dialog "Add an image", both tabs, the Upload tab selected, the drop zone copy, footer "Cancel" only, no XHR.
  2. Drop opens Upload with the file - dispatch drop with before.png (image/png, 3.9 MB); assert the Upload tab, a ?hash=...&page=1&size=100 request, no ?page=1&size=25 list request, then the crop step after an empty lookup, and no XHR.
  3. Dragging over shows "Drop to upload".
  4. Rejected, Examples-driven - plan.pdf, disguised.png, north-wall.tif; assert no ?hash= request and no XHR, plus the exact title, text, confirm and cancel.
  5. "Choose another file" returns to idle, with the tabs back.
  6. Duplicate card - the hash lookup resolves one item (id 1, "north-wall.jpg", created_at 12 Sep 2026, uploaded_by "Marta Alves"); assert the card copy and buttons, no XHR, and no crop step.
  7. Several matches name the oldest, and no uploader drops the "by" part.
  8. "Use existing" - assert the Library tab is selected, image 1 selected, the alt field empty, "Insert image" enabled, and no XHR.
  9. "Upload anyway" - assert the crop step opens (raster) with no second ?hash= request; for an SVG assert the XHR is created at once.
  10. Check skipped - Examples: hashFile resolves null, lookup 500, lookup rejects; assert the crop step opens with no card.
  11. Uploading and progress - confirm the crop step; assert the XHR is POST /v1/media/ with field file, cancel "Cancel upload", and loaded: 2.4 * 1024 * 1024, total: 3.9 * 1024 * 1024 gives "north-wall.jpg 62%" and "Uploading 2.4 of 3.9 MB".
  12. Closing during an upload aborts - abort called, dialog closed, body and dirty state unchanged.
  13. 201 inserts - resolve the XHR with the item (id 13, url /static/uploadedImages/13.jpg); assert insertImage was called with (url, "", 13), SaveStatus "Unsaved changes", Save enabled, the toast (color success, icon i-lucide-circle-check, "Image added" / "Save to keep it in the article."), and after 600 ms of fake timers the dialog closes.
  14. Server answers, Examples-driven - 415, 413, 400 give the rejected copy; 500, 429 and a network error give "Upload failed"; assert no insertImage call and the body unchanged.
  15. "Try again" resends the same file with no new ?hash= request.
  16. Sessions - Examples from the feature; assert the same redirect useApi performs, with no local copy.

Visual

The Upload tab states are drawn in the media-picker group; see editor_media_picker.qa.md. The editor-group state 'Upload: choose' awaits the user's editor board update: the user refreshes the editor board themselves, and until then task visual:check -- editor is expected to fail on that state. The states "Upload: in progress" and "Upload: rejected" and the crop and resize states are unchanged. web/visual/states.ts line 2309 mocks POST /v1/article/12/image and must mock POST /v1/media/ with the item shape, and every editor upload state needs a GET /v1/media/?limit=24 mock.

Manual verification

  • Real round trip against task dev with a live SQLite file: a real upload through the SPA (201, 413, 415, 400, 401), then the image shows in /media.
  • Real OS drag and drop of an image onto the editor in a real browser.
  • Real crypto.subtle in a secure context (localhost or HTTPS), including the duplicate card for a file already in the library, and the skipped check on plain HTTP.
  • Keyboard and focus: the drop zone is reachable by Tab and opens the file chooser with Enter or Space. Focus moves into the dialog and is trapped. Esc during "Upload: in progress" aborts and closes. Tabs are reachable and arrow keys move between them.