feat: built-in type read-only view,s chema list, states, spec files
This commit is contained in:
1 parent
3c67cd2314
commit
f514146fed
24 files changed
+1220
-88
No files matched your search
@@ -0,0 +1,171 @@
|
||||
Feature: Read-only view of a built-in content type
|
||||
As an owner or admin
|
||||
I want to open a built-in type from the Schema list and read its fields
|
||||
So that I can see the rules RenewCMS applies without being able to change them
|
||||
|
||||
Background:
|
||||
Given the SPA calls the API with relative URLs under /v1 and the "jwt" HttpOnly cookie
|
||||
And GET /v1/builtin-type returns [{key, name, fields}] for owners and admins only
|
||||
And each field is {key, label, type, required, unique, default, min, max, options}
|
||||
And API errors are plain text
|
||||
And the route /schema/:key has meta auth "verified" and title "Schema"
|
||||
And the contentTypes store already exposes builtinItems, builtinStatus and load({builtin: true})
|
||||
And the view takes its data from builtinItems, matched on the route key, and never calls GET /v1/builtin-type/{key}
|
||||
And only owners and admins can use Schema; the SPA decides from the session role, the API still enforces it
|
||||
And a session whose role is neither owner nor admin is a test double for the forbidden states
|
||||
And the built-in types are articles ("Articles"), authors, sites, media and users with 5, 5, 8, 6 and 6 fields in the sample data
|
||||
|
||||
Scenario: A built-in row of the Schema list is a link
|
||||
Given the Schema list is Populated
|
||||
When the user looks at the five built-in rows
|
||||
Then each row is a single link to /schema/<its key> whose accessible name is the type name
|
||||
And the row keeps its lock icon, name, "Built-in" badge, key, field count and a chevron-right at the end
|
||||
And on desktop its "Last updated" cell reads "Managed by RenewCMS"
|
||||
And below 640 px the card of a built-in type shows "<key> · <n> fields" with no "Updated" text and a chevron-right
|
||||
And custom rows are not links and show no chevron
|
||||
|
||||
Scenario: Opening a built-in type from the list
|
||||
Given the Schema list is Populated
|
||||
When the user activates the "Articles" row
|
||||
Then the router navigates to /schema/articles
|
||||
And GET /v1/builtin-type is not called a second time
|
||||
|
||||
Scenario: Read-only view of a built-in type (design state 6, desktop)
|
||||
Given an owner or admin is signed in
|
||||
And GET /v1/builtin-type returns the type "articles" named "Articles" with the fields
|
||||
| label | key | type | required | unique | min | max | default |
|
||||
| Title | title | text | true | false | | 200 | |
|
||||
| Slug | slug | text | true | true | | | |
|
||||
| Body | body | richtext | false | false | | 10000 | |
|
||||
| Online | online | boolean | false | false | | | false |
|
||||
| Published on | published_on | date | false | false | | | |
|
||||
When they open /schema/articles
|
||||
Then the breadcrumb is "Schema" (a link to /schema) then "Articles"
|
||||
And the h1 "Articles" is shown with a lock icon and a "Built-in" badge
|
||||
And an info alert titled "Managed by RenewCMS" says "Articles is a built-in type. You can read its fields here, but you can't edit it, delete it or add fields."
|
||||
And a table has the columns "Label", "Key", "Type", "Rules"
|
||||
And the rows are, in this order
|
||||
| Label | Key | Type | Rules |
|
||||
| Title | title | Text | Required · up to 200 characters |
|
||||
| Slug | slug | Text | Required · unique |
|
||||
| Body | body | Rich text | Up to 10,000 characters |
|
||||
| Online | online | Yes or no | Off by default |
|
||||
| Published on | published_on | Date | – |
|
||||
And each row starts with the icon of its type
|
||||
And the note "Entries of built-in types are managed from their own screens, like Articles and Authors." is shown under the table
|
||||
And there is no "New content type", edit, delete, add field, save or discard action
|
||||
And the keys are shown in the mono style
|
||||
And GET /v1/content-type and GET /v1/builtin-type are each called once
|
||||
|
||||
Scenario: Read-only view of a built-in type on mobile
|
||||
Given the same data and a 390 px wide viewport
|
||||
When they open /schema/articles
|
||||
Then the topbar shows an arrow-left and the title "Articles"
|
||||
And the fields are cards in order, each with the icon box of its type, the label, and the key in mono followed by " · " and the rules
|
||||
And the Type column is not shown
|
||||
And the "Built-in" badge, the alert and the note are shown
|
||||
|
||||
Scenario Outline: Field types show their label and icon
|
||||
Given a built-in field of type "<type>"
|
||||
Then the Type cell reads "<label>" with the icon <icon>
|
||||
|
||||
Examples:
|
||||
| type | label | icon |
|
||||
| text | Text | type |
|
||||
| richtext | Rich text | pilcrow |
|
||||
| number | Number | hash |
|
||||
| boolean | Yes or no | toggle-left |
|
||||
| date | Date | calendar |
|
||||
| select | Choice | list |
|
||||
|
||||
Scenario Outline: The Rules cell is derived from the field
|
||||
Given a field of type "<type>" with <settings>
|
||||
Then the Rules cell reads "<rules>"
|
||||
|
||||
Examples:
|
||||
| type | settings | rules |
|
||||
| text | required, max 200 | Required · up to 200 characters |
|
||||
| text | required, unique | Required · unique |
|
||||
| richtext | max 10000 | Up to 10,000 characters |
|
||||
| boolean | default false | Off by default |
|
||||
| boolean | default true | On by default |
|
||||
| date | nothing | – |
|
||||
| text | min 3, max 80 (proposed copy) | 3 to 80 characters |
|
||||
| text | min 3 (proposed copy) | At least 3 characters |
|
||||
| number | min 1, max 5000 (proposed copy) | Between 1 and 5000 |
|
||||
| number | min 1 (proposed copy) | At least 1 |
|
||||
| number | max 5000 (proposed copy) | At most 5000 |
|
||||
| select | 3 options (proposed copy) | 3 choices |
|
||||
| text | default "Draft" (proposed copy) | Default: Draft |
|
||||
|
||||
Scenario: Back to the list
|
||||
Given the read-only view is shown
|
||||
When the user activates the "Schema" breadcrumb, or the arrow-left on mobile
|
||||
Then the router navigates to /schema
|
||||
|
||||
Scenario: Deep link and reload load what the view needs
|
||||
Given the user opens /schema/articles as the first page of the session
|
||||
Then the session is resolved, then GET /v1/builtin-type is called once
|
||||
And the view shows the read-only view when it answers 200
|
||||
|
||||
Scenario: Loading
|
||||
Given GET /v1/builtin-type has not answered
|
||||
When an owner opens /schema/articles
|
||||
Then nothing is shown in the content area during the first 150 ms
|
||||
And after 150 ms skeleton rows are shown
|
||||
And the not-found page and the 403 page are not shown
|
||||
|
||||
Scenario Outline: Unknown key
|
||||
Given GET /v1/builtin-type returns the five built-in types
|
||||
When an owner opens <path>
|
||||
Then the page "Content type not found" is shown with "It may have been deleted, or the address may be mistyped."
|
||||
And the URL stays <path> and nothing redirects
|
||||
And no field table is shown
|
||||
|
||||
Examples:
|
||||
| path | why |
|
||||
| /schema/unknown | a mistyped key |
|
||||
| /schema/events | a custom type; its builder is a later slice |
|
||||
| /schema/new | the builder is a later slice |
|
||||
|
||||
Scenario Outline: Error
|
||||
Given GET /v1/builtin-type <failure>
|
||||
When an owner opens /schema/articles
|
||||
Then an alert shows "Couldn't load content types"
|
||||
And it shows "GET /v1/builtin-type failed. Check your connection and try again."
|
||||
And a "Try again" action is shown
|
||||
And no table is shown
|
||||
|
||||
Examples:
|
||||
| failure |
|
||||
| answers 500 |
|
||||
| fails (network) |
|
||||
|
||||
Scenario: Retry recovers
|
||||
Given the view is in the Error state
|
||||
When the user activates "Try again"
|
||||
Then GET /v1/builtin-type is called again
|
||||
And when it answers 200 the read-only view replaces the alert
|
||||
|
||||
Scenario: Forbidden for a user who is not owner or admin
|
||||
Given a verified user whose role is neither owner nor admin is signed in
|
||||
When they open /schema/articles
|
||||
Then the URL stays /schema/articles and nothing redirects
|
||||
And the page shows "You don't have access to Schema"
|
||||
And it shows "Only owners and admins can change content types. You can still add and edit entries from the Content menu."
|
||||
And a "Go to Home" action navigates to /home
|
||||
And GET /v1/builtin-type is never called
|
||||
|
||||
Scenario: The API answers 403
|
||||
Given the session says owner or admin but GET /v1/builtin-type answers 403
|
||||
When they open /schema/articles
|
||||
Then the same "You don't have access to Schema" page is shown
|
||||
|
||||
Scenario: An unverified or signed-out user keeps the existing redirects
|
||||
Given the user is not verified or not signed in
|
||||
When they open /schema/articles
|
||||
Then they are redirected to /register/pending or /login as for any other "verified" route
|
||||
|
||||
Scenario: The list and the view share one load
|
||||
Given the user opens /schema, then /schema/articles, then goes back
|
||||
Then GET /v1/builtin-type and GET /v1/content-type are each called once in the session
|
||||
@@ -0,0 +1,42 @@
|
||||
# QA: Read-only view of a built-in type (SPA slice 2)
|
||||
|
||||
Runner: Vitest + Vue Test Utils (`task test:web`). No Go test: there is no backend change.
|
||||
Same pattern as features/schema_list_and_content_nav.qa.md: mock `/v1` through the existing api composable, mount with a test router (routes /schema, /schema/:key) and a resolved session whose role varies. The forbidden cases use a session with an unknown role.
|
||||
|
||||
## Automated
|
||||
|
||||
Fixtures: the five built-in types with the board's sample data; "articles" has the 5 fields of the board (Title, Slug, Body, Online, Published on), the others only need a `fields` array of the right length (5, 5, 8, 6, 6). Three custom types as in slice 1.
|
||||
|
||||
SchemaTable / list (extend SchemaView.spec and the SchemaTable spec):
|
||||
1. Built-in rows are single links (`a` with href /schema/<key>), accessible name = type name, chevron present, "Managed by RenewCMS" in the desktop Last updated cell, no "Updated" text on the mobile card (use `components/__tests__/viewport.ts`).
|
||||
2. Custom rows are not links and have no chevron. Replaces the slice-1 test "rows are not links".
|
||||
3. Activating the "Articles" row navigates to /schema/articles; no second GET /v1/builtin-type.
|
||||
|
||||
Read-only view (new SchemaBuiltinView.spec), one test per state:
|
||||
4. Read-only: owner and admin. Assert the breadcrumb, h1 with lock and "Built-in", alert title and text, columns, the five rows (label, key, type label, rules) in order, the note, and the absence of any new/edit/delete/add/save/discard button. Both /v1 calls made once.
|
||||
5. Mobile: arrow-left + title, cards with key and rules, no Type column.
|
||||
6. Type labels and icons (it.each over the 6 types).
|
||||
7. Rules derivation (it.each over the Examples table of the feature, incl. 10,000 separator, the en dash for empty, and capitalisation of the first part). Cases tagged "proposed copy" are separate it.each rows so a design change touches them only.
|
||||
8. Back: the breadcrumb and the mobile arrow push /schema.
|
||||
9. Deep link: a store in its initial state loads the builtin list once on mount.
|
||||
10. Loading: unresolved promise, fake timers. Nothing at 149 ms, skeleton at 150 ms, no not-found or 403 page. A fast response never shows the skeleton.
|
||||
11. Unknown key: it.each over /schema/unknown, /schema/events, /schema/new. Assert the "Content type not found" page copy, URL unchanged, no table.
|
||||
12. Error: 500 and network failure. Assert the alert copy ("GET /v1/builtin-type failed. ..."), "Try again", no table. Retry then returns 200 and the view replaces the alert (second call made).
|
||||
13. Forbidden: unknown role gives the 403 page copy, "Go to Home" navigates to /home, URL stays /schema/articles, builtin-type never called. Plus a case where builtin-type answers 403 for an owner.
|
||||
14. Shared load: navigating /schema -> /schema/articles -> back calls each list endpoint once.
|
||||
|
||||
Router and guard:
|
||||
15. `/schema/:key` is registered with auth "verified" and title "Schema". Unverified and signed-out users get the existing redirects (extend routerGuard.spec). /schema (exact) is unchanged.
|
||||
|
||||
## Visual (task visual:check -- schema)
|
||||
|
||||
Run `task visual:check -- schema`; every capture within 10 % at desktop and mobile, light and dark. Nothing is clock-sensitive.
|
||||
- Replace `skip: 'pending'` of state 6 in `web/visual/states.ts` with a real setup: open /schema/articles. Mock GET /v1/session, GET /v1/content-type (the 3 custom types), GET /v1/builtin-type with the five types. "articles" must carry the board's 5 fields: Title (text, required, max 200), Slug (text, required, unique), Body (richtext, max 10000), Online (boolean, default false), Published on (date, none). Field counts 5, 5, 8, 6, 6. Keys in the mock follow the board's sample text ("articles" etc.), since the view resolves the key from data. Use the sidebar mock of states 1-5 and mock every other /v1 call the screen makes.
|
||||
- States 1 to 5 must still pass after the list-row change (chevron on built-in rows, "Managed by RenewCMS" in Last updated). If the board and the slice-1 rendering differ on custom rows' chevron, report it, do not fix it silently.
|
||||
- Loading, Error, Unknown key and Forbidden-on-detail have no board frame: Vitest only, not in the visual check.
|
||||
|
||||
## Manual
|
||||
|
||||
- Keyboard: Tab order list rows (each row one stop), Enter on a focused built-in row opens it; visible focus ring; the breadcrumb and back arrow are reachable.
|
||||
- Hover on a built-in row, and focus/hover motion.
|
||||
- Real backend: log in as owner and as admin, open each of the five built-in rows, compare the rendered Rules with the actual fields returned by GET /v1/builtin-type (the board's rules are illustrative), and confirm a plain user gets the 403 page on /schema/article.
|
||||
@@ -105,7 +105,7 @@ Feature: Schema list and content navigation in the back-office
|
||||
And a table has the columns "Name", "Key", "Fields", "Last updated"
|
||||
And each custom row shows name, key, the number of fields and the updated date as an absolute date, using the board's sample data
|
||||
And after the custom rows come the five built-in rows, each with a lock icon and a "Built-in" badge
|
||||
And a built-in row shows its field count from the length of fields and an em dash as Last updated
|
||||
And a built-in row shows its field count from the length of fields and "Managed by RenewCMS" as Last updated on desktop (no Updated text on mobile)
|
||||
And the text "Built-in types are managed by RenewCMS. You can read their fields, but not change them." is shown
|
||||
And the summary "3 content types · 5 built-in" is shown
|
||||
And below 640 px the table is a list of cards with the same content
|
||||
@@ -173,9 +173,10 @@ Feature: Schema list and content navigation in the back-office
|
||||
When an owner activates "New content type"
|
||||
Then the router navigates to /schema/new (the builder is a later slice)
|
||||
|
||||
Scenario: Rows are not links in this slice
|
||||
Scenario: Custom rows are not links in this slice
|
||||
Given the list is Populated
|
||||
Then custom and built-in rows do not navigate (the editor and the read-only view are later slices)
|
||||
Then custom rows do not navigate (the editor is a later slice)
|
||||
And built-in rows are links, see schema_builtin_view.feature
|
||||
|
||||
Scenario Outline: The typed client maps responses
|
||||
When the client calls <call>
|
||||
|
||||
@@ -35,7 +35,7 @@ Client and store:
|
||||
|
||||
Run `task visual:check -- schema`, every capture within 10 % at desktop and mobile, light and dark. States to set up in `web/visual/states.ts`, reusing the board's sample data (names, keys, field counts, dates, counts "3 content types · 5 built-in" taken from SchemaFrame.dc.html):
|
||||
- 1 Populated, 2 Loading, 3 Empty, 4 Error, 5 Forbidden (403).
|
||||
- State 6 (built-in read-only view) is out of scope: report it as not set up in this slice, and flag it to the user if the check demands a setup for every state.
|
||||
- State 6 (built-in read-only view) is covered by schema_builtin_view.qa.md.
|
||||
- None of these is clock-sensitive; if the Last updated cell turns out to be relative on the board, mark it `skip: 'clock-sensitive'`.
|
||||
- Mock every `/v1` call the screen makes, including the sidebar's GET /v1/content-type.
|
||||
- The sidebar group and drawer frames belong to the later `content` group.
|
||||
|
||||
Reference in new issue
Block a user