126 lines
7.6 KiB
Gherkin
126 lines
7.6 KiB
Gherkin
Feature: Creating a new article from NewArticleModal
|
|
|
|
As a verified RenewCMS owner
|
|
I want NewArticleModal to take only a title, create the article as a draft
|
|
over POST /v1/article/, and take me straight to the editor to write it
|
|
So that starting a new article is a one-field, low-friction step and I land where I can
|
|
actually write it
|
|
|
|
Background:
|
|
Given NewArticleModal is a UModal opened from Home's "New article" action (mobile header
|
|
button, desktop header button, and the empty state's "New article" action), per
|
|
docs/design/routes.md and docs/design/screens/new-article.md; the Articles-page trigger and
|
|
the /article/new deep link are out of scope for this slice, deferred until ArticlesPage exists
|
|
And every time it opens, it starts in the Default state: title "New article", text "It's
|
|
saved as a draft. Titles can't be changed later, so check it now.", an empty, focused Title
|
|
field with placeholder "e.g. Choosing tiles for a small bathroom", a CharacterCounter
|
|
reading "0 / 200" and help "3 to 200 characters.", a "Cancel" button and a "Create draft"
|
|
button; there is no body or First paragraph field
|
|
And the Title field is sent as the request's title and is validated required, 3 to 200
|
|
characters, counted the same way as the CharacterCounter ([...str].length, which is rune
|
|
count, matching article_title_rules.feature's server-side rune counting)
|
|
And the request carries no body key; the server treats an absent body as "" per
|
|
features/article_body_unlimited.feature
|
|
And the Title field validates on blur and again on submit; submitting while it is invalid
|
|
sends no request: a title under 3 characters shows "Use at least 3 characters." in place of
|
|
its help text, with the counter in its error style
|
|
And activating "Create draft" while the title is valid sends POST /v1/article/ with JSON
|
|
{"title": "<title>"}; while the request is in flight the Title field and both buttons are
|
|
disabled, the confirm button reads "Creating…" with a loading indicator, and the modal
|
|
cannot be dismissed by Cancel, Esc or a backdrop click
|
|
And a 201 response closes the modal, emits created(article) with the response body, and (for
|
|
this slice) HomeView.vue's created handler navigates to /article/{id}/edit and raises a
|
|
toast: color success, icon i-lucide-circle-check, title "Draft created", description
|
|
"\"{title}\" is saved as a draft.", with id "article-created-{id}" per
|
|
docs/design/components.md's toast-id convention
|
|
And a 400 response is never shown verbatim; it re-enables the form with the title kept,
|
|
shows "Check this title and try again." in place of the Title field's help text, and shows
|
|
an alert "Something in the form isn't valid. Check the highlighted fields."
|
|
And a 500 response or a request that never reaches the server re-enables the form with the
|
|
title kept and shows an alert "Couldn't create the article. Your text is still here. Try
|
|
again in a moment.", with no field-level error
|
|
And "Cancel" always closes the modal immediately and discards the title, with no
|
|
confirmation prompt, whenever the form is not disabled by an in-flight request
|
|
And a 401 response is handled entirely by useApi's existing global handling
|
|
(features/api_client_global_error_handling.feature): the session is cleared and the app
|
|
navigates to /login?redirect=<path>&reason=expired; this slice adds no draft-preservation
|
|
behavior for the modal's field, unlike the editor's session-expiry handling
|
|
|
|
Scenario: Opening the modal shows the Default state
|
|
Given the user is on /home
|
|
When the user activates "New article"
|
|
Then NewArticleModal opens with title "New article" and the text "It's saved as a draft.
|
|
Titles can't be changed later, so check it now."
|
|
And the Title field is empty, focused, and its counter reads "0 / 200"
|
|
And there is no First paragraph field
|
|
And "Create draft" is present but the form has not been submitted
|
|
|
|
Scenario: Validation — a title under 3 characters on blur
|
|
Given the modal is open
|
|
When the user types "Ti" into the Title field and moves focus away
|
|
Then the Title field shows the error "Use at least 3 characters." in place of its help text
|
|
And the counter reads "2 / 200" in its error style
|
|
And no request is sent
|
|
|
|
Scenario: Validation — submitting an invalid title sends nothing
|
|
Given the modal is open with Title "Ti"
|
|
When the user activates "Create draft"
|
|
Then the Title field shows "Use at least 3 characters."
|
|
And POST /v1/article/ is never sent
|
|
|
|
Scenario: Submitting disables the form and cannot be dismissed
|
|
Given the modal is open with Title "Choosing tiles for a small bathroom"
|
|
When the user activates "Create draft"
|
|
Then POST /v1/article/ is sent with JSON {"title": "Choosing tiles for a small bathroom"}
|
|
And the Title field becomes disabled, "Cancel" becomes disabled, and the confirm button reads
|
|
"Creating…" with a loading indicator
|
|
And pressing Esc or clicking the backdrop does not close the modal
|
|
|
|
Scenario: A successful create closes the modal and hands off to the editor with a toast
|
|
Given the user is on /home with the modal open, Title "Choosing tiles for a small bathroom",
|
|
and "Create draft" has been activated
|
|
When POST /v1/article/ resolves 201 with the created article, id 42, is_online false
|
|
Then the modal closes
|
|
And the app navigates to /article/42/edit
|
|
And a toast appears: color success, icon i-lucide-circle-check, title "Draft created",
|
|
description "\"Choosing tiles for a small bathroom\" is saved as a draft.", id
|
|
"article-created-42"
|
|
|
|
Scenario: Title rejected — a 400 that got past this form's own check
|
|
Given the modal is open with Title "Choosing tiles for a small bathroom" (already valid by
|
|
this form's own rule), and "Create draft" has been activated
|
|
When POST /v1/article/ resolves 400
|
|
Then the modal re-enables, keeping the title
|
|
And the Title field shows "Check this title and try again." in place of its help text
|
|
And an alert reads "Something in the form isn't valid. Check the highlighted fields."
|
|
|
|
Scenario Outline: Error — a 500 or a network failure keeps the text and offers to retry
|
|
Given the modal is open with Title "Choosing tiles for a small bathroom", and "Create draft"
|
|
has been activated
|
|
When POST /v1/article/ <failure>
|
|
Then the modal re-enables, keeping the title
|
|
And an alert reads "Couldn't create the article. Your text is still here. Try again in a
|
|
moment."
|
|
And the Title field shows no field-level error
|
|
|
|
Examples:
|
|
| failure |
|
|
| resolves with a 500 response |
|
|
| never reaches the server |
|
|
|
|
Scenario: Retrying after an Error succeeds
|
|
Given the modal is in the Error state after a 500, with Title "Choosing tiles for a small
|
|
bathroom" still in the field
|
|
When the user activates "Create draft" again
|
|
Then POST /v1/article/ is sent again with the same JSON body
|
|
When it resolves 201 with the created article, id 42
|
|
Then the modal closes, the app navigates to /article/42/edit, and a toast appears: color
|
|
success, icon i-lucide-circle-check, title "Draft created", description "\"Choosing tiles
|
|
for a small bathroom\" is saved as a draft.", id "article-created-42"
|
|
|
|
Scenario: Cancel discards the title with no confirmation
|
|
Given the modal is open with Title "Choosing tiles for a small bathroom"
|
|
When the user activates "Cancel"
|
|
Then the modal closes immediately, with no confirmation prompt
|
|
And no request is sent
|