diff --git a/features/editor_seo.feature b/features/editor_seo.feature new file mode 100644 index 0000000..823ae6b --- /dev/null +++ b/features/editor_seo.feature @@ -0,0 +1,532 @@ +Feature: Editor: search and sharing, suggestions and "What the frontend gets" + + As a RenewCMS user who can edit articles + I want to fill in an article's search and sharing data, see suggestions and previews, and see what the frontend receives + So that the API returns good SEO data for every article without blocking me from saving or publishing + + Background: + Given the screen is EditorView (/article/:id/edit) inside DashboardShell, per docs/design/routes.md + And the visual spec of record is docs/design/screens/editor.md (groups "Editor: search and sharing" with 24 states and "Editor: what the frontend gets" with 6 states), docs/design/seo-slices.md, docs/design/components.md (SearchSharingSection, SeoPanel, SoftCounter, SearchPreview, SocialCardPreview, SuggestionList, ApiPreviewDialog, SlugField, MediaField) and docs/design/states.md ("SEO round"). Exact copy is read from those files and is not repeated here unless a scenario needs it + And the board frames of docs/design/source/RenewCMS Screens Back-office.dc.html, AppFrame.dc.html and DirectoryFrame.dc.html are the layout tiebreaker + And this slice is SPA only and reuses the API of features/seo_data_backend.feature and features/seo_output_backend.feature without change + And the SPA calls, with relative URLs through useApi only: GET /v1/article/{id} and PATCH /v1/article/{id} (SEO fields slug, excerpt, seo_title, seo_description, cover_image_id, cover_image_alt, social_title, social_description, social_image_id, canonical_url, noindex), POST /v1/article/{id}/seo/check, GET /v1/article/{id}/seo, GET /v1/site/ and the media routes of the shared picker + And POST /v1/article/{id}/seo/check takes the optional SEO fields (and body) of PATCH as overrides and answers {status, suggestions: [{code, field, params}], slug: {available, suggestion}} with status "ready", "suggestions" or "hidden" + And the SPA never re-implements a suggestion rule. It renders the suggestions, the order and the status the check returns. The copy of each code lives in the SPA and has a French entry + And the suggestion codes are title_long, title_empty, desc_short, desc_long, cover_alt, cover_none, slug_long, no_au, no_h, body_alt and short + And a suggestion's field maps to a link label: seo_title "meta title", seo_description "meta description", cover_image_alt and cover_image_id "cover image", slug "URL slug", author_ids "authors", body "body" + And the soft ranges of the counters are meta title 30 to 60 and description 70 to 160 (docs/design). They colour a counter and never block + And suggestions never disable Save, "Save and publish" or Publish + And the sample article is 12, "How we price a bathroom renovation", online on the site Renew (renew.example, id 1), slug "how-we-price-a-bathroom-renovation", with no SEO field set, and the author "Inès Duarte" + And the sample cover media is "north-wall.jpg" (the site social image) and the sample panel values are those of the board frames "Panel: filled in" and "Panel: outside the soft ranges" + And every visible string goes through t(...) with the English text as the key and a French entry in web/src/i18n/messages.ts + And the screen uses Nuxt UI components and design tokens only, with no raw colors, and no comment is added anywhere + And a scenario tagged @not-in-design uses copy or behaviour that docs/design does not define: it takes the proposal of this feature, has a French entry, and is listed in the .qa.md to be fed back to the design + + Rule: The "Search and sharing" section sits under "Site and authors", collapsed by default + + Scenario: Position, in the sidebar and in the Details sheet + Given article 12 is loaded and nothing is remembered for this browser + When the user opens /article/12/edit on desktop + Then the details sidebar shows a collapsed "Search and sharing" section directly under "Site and authors" + And on mobile (390 px) the section is not visible until the Details sheet is opened, and inside the sheet it sits under "Site and authors" + + Scenario Outline: The one-line summary follows the check + Given the section is collapsed + And POST /v1/article/12/seo/check answers + Then the summary line reads "" in + + Examples: + | check | summary | colour | + | pending | Checking… | the muted colour | + | status "ready", no suggestions | Ready | the muted colour | + | status "suggestions", 1 suggestion | 1 suggestion | the warning colour | + | status "suggestions", 2 suggestions | 2 suggestions | the warning colour | + | status "hidden", 2 suggestions | Hidden from search | the muted colour | + + Scenario: The existing Editor states show the section + Then every editor state that has the details sidebar shows the collapsed section with "Ready" + And the Loading state shows the section header with a skeleton summary, as for "Site and authors" + And the Not found state shows no section + + Scenario: The choice is remembered per browser, with its own key + Given the section is collapsed by default + When the user opens it and later opens another article in the same browser + Then it is open there too + When the user collapses it and reloads + Then it is collapsed + And the choice is kept in localStorage under its own key, independent of the "Site and authors" key, and nothing is sent to the API + And the sidebar and the Details sheet share that key + + Scenario Outline: A storage that throws never breaks the editor + Given localStorage. throws a SecurityError + When the user opens the editor and toggles the section + Then the editor renders, the section opens and closes for this page view, and no error reaches the user + And reading falls back to "collapsed" and writing is silently skipped + + Examples: + | call | + | getItem | + | setItem | + + Scenario: The section opens by itself when asked from elsewhere + When the user activates "Edit search and sharing", "What the frontend gets" or a suggestion link + Then the panel or the dialog opens whatever the stored choice is, and the stored choice is not changed + + Rule: The open section: address, suggestions and actions + + Scenario: Open and ready + Given the section is open and the check answers status "ready" + Then it shows "Address" with "renew.example/how-we-price-a-bathroom-renovation" (the host of the site's base_url, without the scheme, then the slug) + And "All good" with "Nothing to suggest for this article." + And the actions "Edit search and sharing" and "What the frontend gets" + + Scenario: Open with suggestions, at most three + Given the check answers title_long (74 characters), cover_alt, no_h and short, in that order + Then exactly three suggestions are shown, in the order of the response, each with its message and a "Go to " link: "Go to meta title", "Go to cover image", "Go to body" + And a "+1 more in Search and sharing" action follows + When the user activates "+1 more in Search and sharing" + Then the panel opens with the suggestion list in view + And with three suggestions or fewer no "+N more" action is shown + + Scenario Outline: A suggestion link goes to its field + Given the check answers a suggestion whose field is "" + When the user activates its "Go to