89 lines
5.2 KiB
Markdown
89 lines
5.2 KiB
Markdown
# QA: Removing an inline image from the editor
|
|
|
|
## Automated (Vitest + Vue Test Utils, `task test:web`)
|
|
|
|
Mount EditorPage inside DashboardShell at `/article/12/edit` with the
|
|
editor_core.qa.md fixture article (id 12, "How we price a bathroom
|
|
renovation", is_online false), GET already resolved 200, and a body
|
|
containing `<img src="/static/uploadedImages/before.png" data-image-id="10"
|
|
alt="">`. Mock `useApi`'s `request` for `DELETE /v1/article/12/image/10`
|
|
(fetch-based, not XHR — reuse whatever mock
|
|
`api_client_global_error_handling.qa.md` already sets up for `request`).
|
|
|
|
1. **Selecting the image reveals the delete button** — click the image
|
|
node; assert a delete button (trash icon, error color) renders anchored
|
|
to it; click elsewhere in the editor; assert the button is gone.
|
|
2. **Activating the delete button opens the confirm dialog** — select the
|
|
image, activate its delete button; assert a dialog titled "Remove this
|
|
image?", description "This image will be removed from the article. This
|
|
can't be undone.", cancel "Cancel", confirm "Remove image"; assert no
|
|
request was sent yet; assert the image node is still present (Tiptap's
|
|
native deletion did not run).
|
|
3. **Backspace on the selected image opens the same confirm dialog instead
|
|
of deleting the node** — select the image node (NodeSelection), fire a
|
|
`keydown` for `Backspace`; assert the same dialog as step 2 opens;
|
|
assert no request was sent yet; assert the image node is still present
|
|
in the editor's document (i.e. the keydown was intercepted before
|
|
Tiptap's default node-deletion ran, not just undone afterwards).
|
|
4. **Delete on the selected image opens the same confirm dialog instead of
|
|
deleting the node** — same as step 3 but firing `keydown` for `Delete`.
|
|
5. **Cancelling changes nothing** — from the open dialog (opened via the
|
|
button), click "Cancel"; assert the dialog closes, no request was sent,
|
|
the image node is still in the editor's content, and SaveStatus/dirty
|
|
state is unchanged.
|
|
6. **Confirming removes the image, marks dirty, and toasts (button
|
|
trigger)** — from the open dialog (Saved state), click "Remove image";
|
|
assert a DELETE request to `/v1/article/12/image/10` was made; resolve
|
|
it 204; assert the dialog closes, the image node is gone from the
|
|
editor's content, SaveStatus reads "Unsaved changes" and Save is
|
|
enabled, and a toast appears with color success, icon
|
|
`i-lucide-circle-check`, title "Image removed", description "Save to
|
|
keep this change."
|
|
7. **Confirming removes the image the same way when opened via
|
|
Backspace/Delete** — repeat step 6's assertions, but open the dialog via
|
|
the Backspace keydown from step 3 instead of the button; assert the
|
|
outcome (request, node removal, dirty state, toast) is identical.
|
|
8. **A failed removal keeps the image and shows the inline error,
|
|
Examples-driven** — for a 404 response, a 500 response, and a rejected/
|
|
network-failure request: from the open dialog (button trigger), click
|
|
"Remove image"; resolve/reject accordingly; assert the dialog stays open
|
|
showing the inline error "Couldn't remove the image, so it's still
|
|
there. Try again in a moment."; assert the image node is still present
|
|
and the draft's dirty state is unchanged.
|
|
|
|
## Visual
|
|
|
|
No `docs/design/` board or state exists for this trigger (see the feature's
|
|
Background). Per the user's explicit direction this slice ships the SPA
|
|
behaviour anyway, but `task visual:check -- editor` has nothing to compare
|
|
it against, so no visual capture is added for it — this is an intentional
|
|
gap, `skip: no-design-coverage` (distinct from `skip: clock-sensitive`), not
|
|
a QA oversight. If a board for this state is added to `docs/design/` later,
|
|
it needs its own follow-up slice to add the visual check.
|
|
|
|
- [ ] Keyboard/focus: once a delete button is shown, it is reachable by Tab
|
|
and activates on Enter/Space; pressing Backspace/Delete with the
|
|
image node selected but focus elsewhere in the page does not trigger
|
|
removal (only an actual Tiptap NodeSelection on the image should);
|
|
ConfirmDialog's existing focus trap and cancel-button-default-focus
|
|
behaviour (already covered by the article-delete flow) applies
|
|
unchanged regardless of which trigger opened it.
|
|
|
|
## Manual verification
|
|
|
|
- **Real DELETE round trip against a running server** — Vitest only mocks
|
|
`useApi`'s request; confirm a real removal (204/404/500) against
|
|
`task dev` with a live SQLite file and the real
|
|
`DELETE /v1/article/{id}/image/{imageId}` handler, since
|
|
`features/remove_article_image.feature`'s httptest suite covers the
|
|
handler in isolation but never through this SPA flow.
|
|
- **Real keyboard behaviour** — confirm in a real browser that selecting an
|
|
image (click) and pressing Backspace/Delete opens the confirm dialog
|
|
rather than deleting the node, including with an IME or on a platform
|
|
where Backspace/Delete semantics differ, since jsdom's `keydown`
|
|
simulation doesn't exercise Tiptap's real ProseMirror keymap precedence.
|
|
- **Design review of the delete-button/ConfirmDialog approach** — since
|
|
there is no board for this state, once a board is added, re-verify this
|
|
implementation still matches it (or update the implementation to match);
|
|
until then this is a best-effort, design-team-unreviewed UI.
|