diff --git a/features/seo_sites_articles_integration.feature b/features/seo_sites_articles_integration.feature new file mode 100644 index 0000000..2fba696 --- /dev/null +++ b/features/seo_sites_articles_integration.feature @@ -0,0 +1,505 @@ +Feature: SEO round, slice 4: Sites defaults, Articles search status, Integration "Search and sharing" and suggestion dismissal + + As the owner of a RenewCMS install, and as anyone who edits articles or builds a frontend on the API + I want to set the site-wide SEO defaults, see which articles need attention, dismiss suggestions I do not want, and read how the SEO routes work + So that every article resolves good SEO data and a developer can find the routes that serve it + + Background: + Given the screens are SitesView (/settings/sites), ArticleView (/article, also reached as /articles), IntegrationView (/integration) and the Editor SEO panel (/article/:id/edit), inside DashboardShell, per docs/design/routes.md + And the visual spec of record is docs/design/screens/sites.md ("Sites: SEO defaults", 5 states), articles.md ("Articles: search status", 3 states), integration.md ("Search and sharing"), editor.md (SuggestionList), docs/design/components.md (SeoStatusChip, SuggestionList), docs/design/seo-slices.md 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/DirectoryFrame.dc.html (Sites form, SuggestionList), AppFrame.dc.html (Articles rows, cards and toolbar) and IntegrationPage.dc.html (group and index) are the layout tiebreaker + And this slice is SPA only and uses the API of features/seo_data_backend.feature and features/seo_output_backend.feature without change. No Go file changes + And the SPA calls, with relative URLs through useApi only: GET /v1/site/, POST /v1/site/ and PATCH /v1/site/{id} (site_name, title_template, default_description), GET /v1/article/ with the optional seo=suggestions parameter, and PATCH /v1/article/{id} with seo_dismissed. The Integration page makes no request + And an article list item carries seo_status ("ready", "suggestions" or "hidden") and seo_suggestions (a count) + And the SPA never re-implements a suggestion rule or an SEO status. It draws the status and the count the list returned + And the API refuses a title_template without the exact, case-sensitive token %title% with 400 invalid_title_template, and refuses site_name above 100, title_template above 200 and default_description above 320 characters + And a new site that omits the three fields gets site_name "", title_template "%title% | %site%" and default_description "" + And the API refuses a seo_dismissed code outside the 11 known codes with 400, replaces the saved list with the list sent, collapses duplicates, and never reports or counts a dismissed code + And the sample site is Renew (id 1, default, renew.example), the sample article title is "How we price a bathroom renovation", and the sample rows are those of the board frame "Articles: search status" + And the roles are those of features/sites_authors_integration.feature: only the owner reaches the Sites form + 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 screens use 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: Sites form: Site name, Title template and Default description (owner only) + + Scenario: Field order in the form, as the board draws it + Given the owner opens "Edit Renew" + Then the fields appear in this order: Name, Key, Base URL, Default social image, Site name, Title template (with its Example), Default description, Default robots policy, Locale, the default switch + And under the fields come the "Canonical URL preview" block and then the "How articles use these defaults" block + And the slide-over keeps its existing width, header, "Cancel" and "Save changes" + + Scenario: Edit: SEO defaults + Given the site Renew has site_name "Renew", title_template "%title% | %site%" and default_description "" + When the owner opens "Edit Renew" + Then "Site name" shows "Renew" with "Shown in titles through the template, and sent as the Open Graph site name." + And "Site name" has no "(optional)" label and an empty value is allowed + And "Title template" shows "%title% | %site%" in the mono face with "Use %title% for the article title and %site% for the site name." + And the "Example" row reads "How we price a bathroom renovation | Renew" + And "Default description" is marked optional, shows the placeholder "One or two sentences about the site." and the counter "0 / 320" + And the helper reads "Used when an article has no description, excerpt or body text." + And the block "How articles use these defaults" lists Title "Article title, then the template.", Description "Used last, after the article's description, excerpt and body.", Image "Used when an article has no cover image." and Robots "Used unless an article is hidden from search engines." + + Scenario: Add: defaults + When the owner opens "Add site" + Then "Site name" is empty with the placeholder "e.g. Renew" + And "Title template" holds "%title% | %site%" + And "Default description" is empty with the placeholder "One or two sentences about the site." + And the "How articles use these defaults" block is shown + + Scenario Outline: The live example follows the typing + Given the form is open and "Site name" holds "" and "Name" holds "" + When the owner types "