Files
RenewCMS/features/content_entry_editor.qa.md
T

70 lines
8.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# QA: Entry editor of a custom content type (SPA slice 7)
Runner: Vitest + Vue Test Utils (`task test:web`). No Go test: the five entry endpoints and /v1/user/lookup already exist and the backend does not change. Kit: reuse `views/__tests__/schemaTestKit.ts` and add GET/POST/PUT/DELETE handlers for `/v1/content-type/{key}/entry[/{id}]` with the plain-text bodies `entry_not_found`, `content_type_not_found`, `invalid_entry: <key>: <rule>`, and the Events type with its 7 fields and entry 42 of SchemaFrame.dc.html. Pin the time zone. Never freeze the system date: "Saved at" is derived from the mocked `updated_at`, so the expected text is built with the same local formatter from that value.
## Automated
1. Registry (lib/fieldTypes spec, it.each over the six types): empty value, default pre-fill, control component, help line, counter rule, validate (required, type, min, max, options), message text, payload value (null for empty, false for a required boolean). The builder and the list cells still pass on the same registry.
2. Entry API (composable spec): getEntry, createEntry, saveEntry, deleteEntry; the body is {data}; error parsing of "invalid_entry: key: rule" with status 422 and 409 into {key, rule, status}; 404 bodies; 400, 401, 403 and NetworkError pass through; lookupUsers is sent once with distinct ids.
3. Route resolution (it.each): "new", "42", "abc", "0" (resolves to id 0, sent as is: the API answers 404 entry_not_found), "4294967296", "-1".
4. State 1 New entry: title, subtitle, chip "New entry", asterisks, Default tag on Online, helps, placeholders, footer "Not saved yet" and "–", Save enabled, Delete absent; mobile layout.
5. Defaults (it.each from the feature table): value shown, tag shown, tag leaves on change and returns on restore; no tag in edit.
6. State 2 Editing: breadcrumb, title, "Entry 42", chip "Saved", Save disabled, footer names and dates after the lookup; "–" with the date kept when the lookup fails or lacks an id; mobile trash-2 action.
7. Page title (it.each): text, null → "Entry 42", richtext first line, number; unchanged while typing.
8. Controls and help lines (it.each): one control per type, placeholders, Off/On text, empty select "None" item on optional select only.
9. Layout rule (pairing of date, number, boolean; full width for text, richtext, select; single column below md).
10. State 3 Unsaved: counter "32 / 80", "91 / 80" in the error colour without blocking, no counter without max, chip, Save enabled, revert returns to clean.
11. State 4 Preview: rendering of bold and list, raw HTML shown as text, unsafe links inert, empty preview, tab switch keeps value and caret, toolbar inserts (it.each).
12. Payloads: create and edit bodies exactly as in the feature, explicit nulls, no unknown key, boolean table.
13. State 5 Saving: chip, button, dimming, disabled controls, no second request on click or Enter.
14. State 6 Saved: edit and create; toast; chip "Saved at HH:MM" from the answer; footer update; create replaces the URL to /content/events/43 with no extra GET; no leave prompt; edit after save is dirty again.
15. State 7 Save failed (500, network, 400, 422 with no field): alert copy, chip, values kept, Try again resends the same body.
16. State 8 Required: no request, all messages, chip "Not saved", `document.activeElement` is the first erroring control in schema order for each type (input, textarea, select trigger, date segment), aria-invalid and aria-describedby, per-type required messages (it.each).
17. State 9 Rules: one message per field, message table (it.each), server answers that pass the client map to their field and take focus.
18. Errors clear per field; chip returns to "Unsaved changes"; no blur validation.
19. State 10 Unique (409): alert and field message, per-type messages, alert clears when the field is edited.
20. State 11 Not found: GET 404, PUT 404, DELETE 404 and bad ids with no request; "Back to Events" goes to /content/events with no prompt.
21. Content type 404, 403 and 401; load failure variants with "Try again" resending only the failed request; loading skeleton with aria-busy.
22. State 12 Schema changed: unknown key, unknown_field, required on an unknown key, 409 on an unknown key; read-only form; Save disabled; Cancel enabled; chip "Not saved"; "Reload form" refetches the type (and the entry) and rebuilds the form; reload failure.
23. State 13 Delete: dialog copy, focus on Cancel, Cancel/Esc/close, no request; Deleting lock; 204 replaces the URL and shows the toast; 404, 403, 401, 500 and network outcomes; delete with unsaved edits.
24. Leave guard (it.each from the feature table), including defaults-only new form untouched, and Cancel history rule (previous entry is the list vs not).
25. Stale answers dropped; navigating between routes carries nothing.
26. Reaching the editor from the list: "New entry" and row links resolve to the editor (update the slice-6 specs that expected the catch-all 404). The slice-6 Background wording about `content_type_id` and `sort` is checked against the Go handler and corrected with the list code and tests if it differs.
27. Accessibility: labels, aria-required, live-region chip, tablist keyboard, toolbar names, dialog focus.
28. French: every new string has an entry (i18n completeness spec passes).
## Visual (task visual:check -- entry)
Register the group "6 Entry editor" in `extraSources` if needed. Every capture within 10 %, desktop and mobile, light and dark. Mock the sidebar's GET /v1/content-type and /v1/user/lookup. No state is clock-sensitive: "Saved at" comes from the response timestamp. If the runner cannot pin the zone, build `updated_at` as the builder's Saved setup does.
| Board state | Setup in web/visual/states.ts | Board sample data |
|---|---|---|
| 1 New entry | /content/events/new, Events type | "New entry", "Not saved yet", Online Off + "Default" |
| 2 Editing | /content/events/42, lookup 3 and 5 | "Lime plaster workshop", "Entry 42", "Inès Duarte · 12 Oct 2026, 09:14", "Marta Alves · 15 Oct 2026, 11:02" |
| 3 Unsaved changes | step: type ", two days" in Title | "Lime plaster workshop, two days", "32 / 80", focus ring on Title |
| 4 Markdown preview | step: click "Preview" on Description | "Two days of hands-on work with lime putty and old stone." and the two bullets |
| 5 Saving | PUT `hang: true` after an edit | "Saving…" |
| 6 Saved | PUT 200 with updated_at 2026-10-19 11:02 local, updated_by 5 | "Saved at 11:02", "Entry saved", "Marta Alves · 19 Oct 2026, 11:02" |
| 7 Save failed | PUT 500 after an edit | "Couldn't save the entry" |
| 8 Required fields empty | /content/events/new, click Save | "Fill in Title.", "Pick a date for Starts on." |
| 9 Rule errors | /content/events/42 with the board's values typed, PUT 422 for any server-side one | "91 / 80", "Capacity must be at most 5000.", "Choose one of: Talk, Workshop or Meetup." |
| 10 Duplicate (409) | /content/events/new, title "Heat pumps: what the numbers say", POST 409 | "Another entry already uses this Title. Enter a different one." |
| 11 Entry not found | /content/events/999, entry 404 entry_not_found | "Entry not found" |
| 12 Schema changed | /content/events/new, POST 422 invalid_entry: room: required | "The fields of Events changed" |
| 13 Delete entry: confirm | /content/events/42, step: click "Delete" | "Delete this entry?" |
Board caveats to report, never hide:
- State 9 draws `31/11/2026` as text in Ends on; a segmented date control cannot hold it, so that field will differ.
- State 11's breadcrumb shows the entry title, unknown on a 404.
- State 12's chip is drawn "Saved" and its Cancel is drawn disabled; the spec uses "Not saved" and an enabled Cancel (user decision).
- Mobile in state 9 depends on the chosen layout rule.
## Manual
- Real backend with two users: create an entry; Back from it returns to the list, not the empty form; edit; delete; the list shows the change.
- Two tabs: change the type's fields in one (add a required field), save the entry in the other: state 12, "Reload form". Delete the entry in one tab and save in the other: state 11.
- Duplicate a unique title for a real 409.
- Keyboard: tab order across fields, toolbar, tabs, footer buttons; Enter in a single-line field saves; focus lands on the first error; dialogs trap focus.
- Markdown with raw HTML and `javascript:` links stays inert in Preview.
- Reload on /content/events/42 keeps the page; the browser back from a saved new entry goes to the page before the new form.