diff --git a/features/schema_builder_create.feature b/features/schema_builder_create.feature new file mode 100644 index 0000000..a226601 --- /dev/null +++ b/features/schema_builder_create.feature @@ -0,0 +1,516 @@ +Feature: Create a custom content type in the Schema builder + As an owner or admin + I want to define a new content type (name, key and ordered fields) on /schema/new + So that users can write entries of that type + + Background: + Given the SPA calls the API with relative URLs under /v1 and the "jwt" HttpOnly cookie + And API errors are plain text + And POST /v1/content-type takes {key, name, fields[{key, label, type, required, unique, default, min, max, options}]} + And the builder never sends an "icon" key + And POST answers 201 with the content type, 400 invalid_content_type_key, 409 content_type_key_taken, 422 invalid_field, 403 or 500 + And the route /schema/new is declared before /schema/:key, has meta auth "verified" and title "Schema", and renders the builder + And a route meta flag, not a path pattern, tells the shell that /schema/new needs the custom list only + And only owners and admins can use Schema; the SPA decides from the session role, the API still enforces it + And the six field types are text "Text", richtext "Rich text", number "Number", boolean "Yes or no", date "Date" and select "Choice" + And the limits are a type name of up to 100 characters, a type key of 2 to 40, 1 to 50 fields, a field key of up to 40 characters and 1 to 50 choices + And the layout is: Name and Key with the status chip, Discard and Save on top; the FieldList on the left; the FieldInspector on the right; the ApiPreview at the bottom + And below 48rem the FieldList is the page, the FieldInspector is a 92% bottom sheet closed with "Done", the ApiPreview is a bottom sheet, and Discard and Save sit in a bottom bar + And every string goes through t() and has a French entry in i18n/messages.ts + And a copy tagged "(proposed copy)" is not in the design and is the coder's single point of change + + Scenario: Reaching the builder + Given the Schema list is Populated or Empty + When the user activates "New content type" (header button, empty-state button or the mobile floating button) + Then the router navigates to /schema/new + And the builder is shown, not the "Content type not found" page + And /schema/events and /schema/unknown still show "Content type not found" for a key that is not built-in + + Scenario: Roles and sessions + Given opens /schema/new + Then + + Examples: + | who | result | + | a verified owner or admin | the builder | + | a verified user who is neither | "You don't have access to Schema" with "Go to Home", URL unchanged | + | an unverified user | the redirect to /register/pending | + | a signed-out visitor | the redirect to /login | + + Scenario: The builder needs no built-in list + Given an owner opens /schema/new as the first page of the session + Then GET /v1/content-type is called once for the sidebar + And GET /v1/builtin-type is never called + And the builder is shown as soon as the session is ready, with no skeleton + + Scenario: New type, desktop (design state 1) + Given an owner opens /schema/new on a 1440 px viewport + Then the breadcrumb is "Schema" (a link to /schema) then "New content type" + And "Name" is an empty text input with the placeholder "For example, Events", marked required, with maxlength 100 + And "Key" is a read-only mono input with the placeholder "events" and the help "Used in URLs and the API. Lowercase letters, digits and hyphens. It can't change after you save." + And the list header reads "Fields" and "1 of 50" + And one row is shown with the label "Title" and the key "title" in mono, selected, with the badge "New" + And "Add field" is shown under the list + And the inspector title is "Field: Title" with the badge "New" + And "Label" holds "Title" + And "Key" holds "title" with the Auto badge and the help "The name used in the API. Starts with a letter. Lowercase letters, digits and underscores." + And the type tiles show Text selected + And "Required" is off with "An entry can't be saved with this field empty." + And "Unique values" is off + And "Default value" is empty with "Used when an entry is created and this field is left out." + And "Shortest" and "Longest" are empty with "Counted in characters. Entries show a counter." + And "Save" and "Discard" are disabled + And no status chip is shown + And no "entries use this content type" bar is shown + And the API preview bar is collapsed + + Scenario: New type, mobile (design state 1) + Given the same page on a 390 px viewport + Then the topbar shows an arrow-left and the title "New content type" + And Name and Key are stacked above the list, then the rows, then the "API preview" row with a chevron + And no inspector is shown until a row is activated + And the bottom bar holds Discard and Save + + Scenario Outline: The key follows the name until it is edited + Given an empty new type + When the user types the name "" + Then the Key holds "" with the Auto badge, read-only + + Examples: + | name | key | + | Events | events | + | Press mentions | press-mentions | + | Café & Bar | cafe-bar | + | Job openings! | job-openings | + | 2026 Awards | 2026-awards | + | !!! | (empty, mode empty) | + | a name of 60 letters | the first 40 characters, without a trailing hyphen | + + Scenario: Editing and resetting the key + Given the name "Events" and the Auto key "events" + When the user activates the pencil + Then the Key becomes editable, the Auto badge goes and "Reset to match the name" is shown + When the user types the key "agenda" then edits the name to "Events and talks" + Then the Key stays "agenda" + When the user activates "Reset to match the name" + Then the Key is "events-and-talks" with the Auto badge + + Scenario Outline: Type-level rules (design states 6 and 14) + Given a new type with a valid field + When the user sets to and activates Save + Then is shown under with an alert icon + And POST /v1/content-type is not called + + Examples: + | input | value | message | + | Name | (empty or spaces) | Give the content type a name. | + | Key | e | Use 2 to 40 lowercase letters, digits or hyphens. | + | Key | Events | Use 2 to 40 lowercase letters, digits or hyphens. | + | Key | my_type | Use 2 to 40 lowercase letters, digits or hyphens. | + | Key | -events | Use 2 to 40 lowercase letters, digits or hyphens. | + | Key | events--2 | Use 2 to 40 lowercase letters, digits or hyphens. | + | Key | new | The key "new" is reserved. Choose a different key. (proposed copy) | + + Scenario Outline: Accepted type keys + Given a new type with a valid field and the name "X" + When the user types the key "" and activates Save + Then POST /v1/content-type is called with that key + + Examples: + | key | + | ab | + | press-mentions | + | 2026-awards | + | 40 lowercase letters | + + Scenario: Type-level errors together (design state 6) + Given a new type with no name, the key "e" and no field + When the user activates Save + Then "Give the content type a name.", "Use 2 to 40 lowercase letters, digits or hyphens." and "A content type needs at least one field." are shown + And the empty list shows "No fields yet" with the message in the error colour + And the chip reads "3 problems to fix" + And Save is disabled until all three are fixed, and Discard stays enabled + And the inspector shows "Field settings" and "Add a field, then choose its type and rules here." + + Scenario: When errors are shown + Given a new type whose name is empty + Then no error is shown while nothing was edited + When the user edits an input, an error for that input shows as they type + And when the user activates Save, every error of the form shows at once + And errors are tied to their input with aria-describedby and aria-invalid + And a row with an error shows an alert icon, not only the red border + + Scenario Outline: The status chip + Given + Then the chip reads "" + + Examples: + | situation | chip | + | a pristine new type | (no chip) | + | an edit with no error | Unsaved changes | + | three inputs in error | 3 problems to fix | + | one input in error | 1 problem to fix | + | the POST in flight | Saving… | + | the POST failed with 500 | Save failed | + | navigator.onLine is false | Waiting for connection | + + Scenario Outline: Counting problems + Given + Then the chip counts + + Examples: + | errors | count | + | no name, a one-letter key, no field | 3 | + | a field with no label, a field with min above max, a choice list with a blank and a duplicate | 3 | + | one choice list with a blank row and a duplicate row | 1 | + | one field with a bad key | 1 | + + Scenario: Field list rows + Given a type with the fields Title, Starts on, Online + Then each row shows a grip button labelled "Move