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.
- Toolbar button, empty library -
GET /v1/media/?limit=24resolves{items: [], next_cursor: null, 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. - Drop opens Upload with the file - dispatch
dropwithbefore.png(image/png, 3.9 MB); assert the Upload tab, a?hash=request, no?limit=24list request, then the crop step after an empty lookup, and no XHR. - Dragging over shows "Drop to upload".
- 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. - "Choose another file" returns to idle, with the tabs back.
- Duplicate card - the hash lookup resolves one item (id 1, "north-wall.jpg",
created_at12 Sep 2026,uploaded_by"Marta Alves"); assert the card copy and buttons, no XHR, and no crop step. - Several matches name the oldest, and no uploader drops the "by" part.
- "Use existing" - assert the Library tab is selected, image 1 selected, the alt field empty, "Insert image" enabled, and no XHR.
- "Upload anyway" - assert the crop step opens (raster) with no second
?hash=request; for an SVG assert the XHR is created at once. - Check skipped - Examples:
hashFileresolves null, lookup 500, lookup rejects; assert the crop step opens with no card. - Uploading and progress - confirm the crop step; assert the XHR is
POST /v1/media/with fieldfile, cancel "Cancel upload", andloaded: 2.4 * 1024 * 1024, total: 3.9 * 1024 * 1024gives "north-wall.jpg 62%" and "Uploading 2.4 of 3.9 MB". - Closing during an upload aborts -
abortcalled, dialog closed, body and dirty state unchanged. - 201 inserts - resolve the XHR with the item (id 13, url
/static/uploadedImages/13.jpg); assertinsertImagewas called with (url, "", 13), SaveStatus "Unsaved changes", Save enabled, the toast (color success, iconi-lucide-circle-check, "Image added" / "Save to keep it in the article."), and after 600 ms of fake timers the dialog closes. - Server answers, Examples-driven - 415, 413, 400 give the rejected copy; 500, 429 and a network
errorgive "Upload failed"; assert noinsertImagecall and the body unchanged. - "Try again" resends the same file with no new
?hash=request. - Sessions - Examples from the feature; assert the same redirect
useApiperforms, 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 devwith 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.subtlein 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.