feat: edit an existing custom type
This commit is contained in:
1 parent
083cc9e23e
commit
8e344b2500
57 files changed
+5648
-425
No files matched your search
@@ -0,0 +1,103 @@
|
||||
# QA: Schema builder, edit (SPA slice 4; delete is slice 5)
|
||||
|
||||
Runner: Vitest + Vue Test Utils (`task test:web`). No Go test: PUT /v1/content-type/{key} and the entry list are already covered by features/custom_content_types.qa.md and nothing changes in the backend.
|
||||
Same pattern as features/schema_builder_create.qa.md: mock `/v1` through the api composable (extend `views/__tests__/schemaTestKit.ts` with PUT and the entry count handlers, and a full "Events" type with the board's 7 saved fields), mount with the real router and a resolved session whose role varies, `components/__tests__/viewport.ts` for 1440 and 390, fake timers where a delay exists. Never freeze the system date. "Saved at HH:MM" is asserted with /^Saved at \d{2}:\d{2}$/ only.
|
||||
|
||||
## Automated
|
||||
|
||||
Fixtures: Events (id 1, 7 saved fields as drawn on the board: Title text required unique max 80, Starts on date required, Ends on date, Online boolean default false, Capacity number min 1 max 5000, Category select Talk/Workshop/Meetup, Description richtext) with icon "calendar"; Recipes (4 saved fields, 0 entries); the entry-count body {items:[],total:37,page:1,size:1}; plain-text bodies schema_incompatible, content_type_has_entries, content_type_not_found, invalid_field.
|
||||
|
||||
Pure logic (lib/__tests__, it.each over the feature's Examples):
|
||||
1. Form from a ContentType: API field to BuilderField (number default to string, boolean default to defaultOn, options, ISO dates), saved flag, locked keyMode, and the round trip: loaded form to payload equals the loaded fields (no spurious diff).
|
||||
2. Change detection per saved field: Changed on and off by restoring the value; reorder-only is not a field change; New for added fields.
|
||||
3. Diff for the review: adds, changes, removes in that order, the summary line (singular, zero parts omitted), the row texts of the "Review rows" table, the flags ("Entries may not fit", "Removes data", none), and the flagged-only list used by the refusal. Name-only or reorder-only means no review.
|
||||
4. Payload for PUT: {name, icon, fields}, no key, icon equal to the loaded one (null stays null), undo-row fields excluded, saved key and type unchanged.
|
||||
5. Field key derivation against saved keys and keys in undo rows (`description_2`).
|
||||
6. Undo bookkeeping: positions, several removals, limit of 50.
|
||||
7. Info-bar copy for 0, 1, 37 and 1200 entries.
|
||||
|
||||
Store (stores/__tests__/contentTypes.spec.ts):
|
||||
8. replace(type) swaps by key and keeps order by name when the name changed, does nothing when "idle", never touches built-in items, no request. reset() clears both slices (signed-out case).
|
||||
|
||||
Composable and API (composables/__tests__/useContentTypes.spec.ts):
|
||||
9. updateContentType PUTs JSON to /v1/content-type/{key} and returns the type. countEntries reads `total` from the size=5 request. ApiError carries status and plain-text body for 400, 404, 409, 422, 403, 500. NetworkError passes through.
|
||||
|
||||
Resolver and routing:
|
||||
10. Key resolver (built-in, custom, none, loading) for events, articles, unknown, new. /schema/new is still the create builder, GET /v1/builtin-type result is awaited for /schema/:key. Custom rows of SchemaTable and the mobile cards are links to /schema/<key>.
|
||||
11. Roles: owner, admin, neither (403 page), unverified and signed-out redirects (extend routerGuard.spec) on /schema/events.
|
||||
12. Session reset: sign-out resets the store and discards any edit state; a second visit starts from the store, not from an old draft.
|
||||
|
||||
Builder view in edit mode (new views/__tests__/SchemaBuilderEdit*.spec.ts), one test per scenario:
|
||||
13. Loading: calls list (assert once each), skeleton until type, list and count are known, no GET /v1/content-type/events.
|
||||
14. Count or list failure: error copy, Try again re-sends, builder shown after success. Count 404 gives "Content type not found".
|
||||
15. State 2 desktop and mobile: breadcrumb, Name editable, locked Key without pencil/Auto/Reset, chip "Saved", info bar, "7 of 50", no badges, first row selected, Save and Discard disabled, collapsed API bar. Mobile topbar, stack order, bottom bar.
|
||||
16. Info bar for 0, 1, 37, 1200.
|
||||
17. Locked key tooltip on hover and on focus, closes on blur and Esc, input not editable.
|
||||
18. Saved fields locked: key lock and help, "Can't change after saving", tiles not selectable, other controls editable. New fields unlocked.
|
||||
19. Changed badge table (5 rows) and revert; reorder-only is no Changed badge but dirty.
|
||||
20. State 4 end to end: badges, undo row text and action, header count, chip.
|
||||
21. Remove and Undo: position restored, no residue, chip back to "Saved". Several removals, reserved key, Undo disabled at 50, empty list error.
|
||||
22. Name-only edit keeps the key; empty name error and disabled Save.
|
||||
23. Field errors (state 5) and single field key error on a new field; a saved field never shows a key error.
|
||||
24. Choice list on a saved Choice field (state 8) and keyboard reorder (state 9) with the live-region texts.
|
||||
25. API preview in edit mode (state 10): locked key in the routes, live sample, note text.
|
||||
26. PUT without entries: one request, no dialog, exact body (icon preserved, no key), Saving state (state 11) with dimmed/disabled controls and no second request on double activation.
|
||||
27. Saved (state 12): toast copy incl. singular, stays on the page, badges and undo rows cleared, newly saved fields now locked, chip /^Saved at \d{2}:\d{2}$/, store.replace, list/sidebar/count updated with no extra GET, no leave prompt, edit after save and Discard back to last saved.
|
||||
28. Discard: back to the loaded form, nothing sent.
|
||||
29. Leave guard (5 rows) with Keep editing, dismiss and Discard changes.
|
||||
30. Review (group 3, state 1): opens only for adds/changes/removes with total > 0; no PUT yet; summary, rows (the table), order, footer note, Back to editing, Esc, mobile sheet.
|
||||
31. Applying (state 2): one PUT, loader, non-dismissable (Esc, overlay, close icon), second activation ignored.
|
||||
32. Applied: dialog closes, same results as item 27.
|
||||
33. Refused: 409 schema_incompatible: refusal dialog copy, only flagged changes listed with "Possible cause", no entry named, store untouched, edits kept. Without flagged change: section omitted. Back to editing: banner "Nothing was changed", "Review changes" reopens the review, row markers. Discard changes resets. Markers clear on success, Discard or revert.
|
||||
34. Other PUT answers: 422 banner, 400 name message, 404 not-found page and store drop, 403 page, 401 redirect.
|
||||
35. Save failed (state 13): 500 and network, review closes, banner and chip, edits kept, Try again resends the same body through the review again.
|
||||
36. Offline (state 16) and offline with the review open.
|
||||
37. Mobile field sheet in edit mode.
|
||||
40. Accessibility: focus return from dialogs, Applying focus on its title, Undo reachable by keyboard.
|
||||
|
||||
Existing tests to update:
|
||||
41. features/schema_builtin_view.qa.md and SchemaTypeView.spec: "Content type not found" for a key that is neither built-in nor custom; events is now the editor. features/schema_list_and_content_nav.qa.md and SchemaView.spec: custom rows are links.
|
||||
42. features/schema_builder_create.qa.md item 29/31: nothing changes for create; assert that the create builder still never sends "icon" (regression guard next to the edit icon test).
|
||||
43. French: every new string has a French entry (the i18n completeness spec must pass).
|
||||
|
||||
## Visual (task visual:check -- schema)
|
||||
|
||||
Run `task visual:check -- schema`. Every capture within 10 %, desktop and mobile, light and dark. Mock GET /v1/auth/me (owner "Marta Alves"), GET /v1/builtin-type, and GET /v1/content-type with the 3 custom types. Replace the placeholder Events fields of `schemaCustomTypes` in `web/visual/states.ts` with the board's full 7 fields (EV), keep 7 as the list's field count, and add Recipes with the board's 4 fields (RC). Mock GET /v1/content-type/events/entry as total 37 and /recipes/entry as total 0. Register the group "3 Review and delete" in `extraSources` of `web/visual/check.ts`.
|
||||
|
||||
Setups to replace `skip: 'pending'` in `web/visual/states.ts` (screen `schema`, steps are Playwright actions on the SPA):
|
||||
|
||||
| Board state | Setup (path /schema/events unless noted) | Board sample data |
|
||||
|---|---|---|
|
||||
| Editing an existing type | no steps | Events, 7 saved fields, Title selected, info bar "37 entries use this content type…", chip "Saved", locked key |
|
||||
| Locked key: tooltip | select the row "Starts on", hover (focus on mobile) the locked key | tooltip text, Starts on selected |
|
||||
| Unsaved changes | steps: Capacity Highest value 2000, Add field "Speaker", Add field "Room" with Choice, Required, choices Main hall and Studio, default Main hall, remove Description, select Capacity | rows New and Changed, undo row "Description will be removed", chip "Unsaved changes" |
|
||||
| Field errors | steps: empty the label of Ends on, Capacity Lowest value 6000, Category choices Talk, Workshop, blank, Talk, select Capacity | chip "3 problems to fix" and the four messages |
|
||||
| Choice list editor | steps: Category choices Talk, Workshop, Meetup, blank, Talk, select Category | "5 of 50", chip "1 problem to fix" |
|
||||
| Keyboard reorder | steps: focus the grip "Move Ends on", press Space, press Up | live region "Moving Ends on. Position 2 of 7. …", Ends on picked up |
|
||||
| API preview open | steps: open "API preview" (sheet on mobile) | key events, board's sample JSON |
|
||||
| Saved | `skip: 'clock-sensitive'` | the chip says "Saved at 11:02" |
|
||||
| Save failed | PUT mocked 500; same edits as Unsaved changes; steps: Save, Apply changes | banner "Couldn't save the content type", chip "Save failed" |
|
||||
| Schema incompatible | PUT mocked 409 schema_incompatible; same edits; steps: Save, Apply changes, Back to editing | banner "Nothing was changed" with "Review changes", "Possible cause" on Capacity |
|
||||
| Offline | same edits; steps: go offline | offline banner, chip "Waiting for connection" |
|
||||
| Saving | see open question 6 | |
|
||||
| Review before saving | same edits; steps: Save | dialog copy and the 4 rows of group 3 state 1 |
|
||||
| Applying | PUT mocked to hang; steps: Save, Apply changes | "Applying…" |
|
||||
| Refused (schema_incompatible) | PUT mocked 409; steps: Save, Apply changes | refusal dialog with Change Capacity |
|
||||
|
||||
Not checked: "Review with counts and diff" (scope Later, not built; leave it out of `extraSources`). "Field key error" has no board frame: keep it as a documented `skip: 'pending'` with that reason and cover it in Vitest only. Do not hide it.
|
||||
Known visual differences to report, never hide: the Key help copy (Q1) on /schema/new.
|
||||
|
||||
## Manual
|
||||
|
||||
- Real backend, as owner and admin:
|
||||
- Create Events with all six field types and some entries.
|
||||
- Open it from the list. Edit the name and a label. Confirm the review does not open for a name-only edit and does for a field edit.
|
||||
- Lower Capacity's Highest value below an existing entry's value. Confirm the real 409 `schema_incompatible`, the refusal dialog and that nothing changed (reload).
|
||||
- Add a required field with no default (409). Add one with a default (200 and existing entries get it).
|
||||
- Turn Unique on over duplicate values (409). Remove a Choice option that entries use (409). Remove a field and check entries no longer return it.
|
||||
- Create a type through curl with an `icon`, edit it in the SPA, and check the icon is still returned by GET.
|
||||
- Check that `GET /v1/content-type/{key}/entry?page=1&size=5` is accepted as the count request.
|
||||
- Keyboard: Tab order is Name, locked Key (tooltip on focus), Save, Discard, the field list (one stop, arrows move between rows), the inspector, Undo, API preview. Dialogs trap focus and return it to the trigger. Screen reader: the lock states are announced, the live region reads each reorder step, errors are read with their input.
|
||||
- Hover and motion on the lock tooltip, the undo row and the dialogs. Sheet open and close focus return on a real phone.
|
||||
- Real offline: edit, go offline with the review open, reconnect, press Apply.
|
||||
- Reload or close the tab with unsaved edits (not handled by the leave guard): note the browser behaviour.
|
||||
Reference in new issue
Block a user