feat: editorial article list
This commit is contained in:
1 parent
a0b3c58c52
commit
a671b81f80
103 files changed
+6493
-1805
No files matched your search
@@ -0,0 +1,42 @@
|
||||
# QA: Editorial article list by story and language
|
||||
|
||||
Order of work is part of the procedure: the repository query and its real-database tests come first (items 1 to 4), then the use case and the HTTP contract (5 to 8), then the SPA (9 onward). Backend items are Go tests in `test/`, black-box.
|
||||
|
||||
## Automated, repository (SQLite file, task test)
|
||||
|
||||
1. Seed the "Journal" dataset (8 stories, 17 versions, bylines, updated_at as in the feature) and the "hidden-version" variant through the real `ArticleRepository` on a temp SQLite file with `database.Migrate`. Drive the new grouped query through `article.Repository` with the new filter fields (group, lang, tab, author) and assert, table-driven over the feature's Examples: story ids per page, `total`, representative per story, coverage order, `missing_langs`, `story_updated_at`, `story_authors`, counts.
|
||||
2. Access filter inside the query: for a caller with scope `own`, `all`-online and `online only`, assert the hidden-version dataset yields the same ids, totals and counts as a database where the hidden articles do not exist. Also check page boundaries (a hidden version must not shift a page: 5 per page of readable stories).
|
||||
3. Filter semantics: the "single readable version satisfies all version-level filters" cases (status+author, status+q, author+seo), the "story_authors is the union" case, the "missing" cases (no readable version, site not enabling the language, disabled language, unassigned articles) and accent-insensitive q in grouped, language and not-in modes.
|
||||
4. Sort whitelist and stability: each sort of the feature, ties by story_id descending, no overlap or gap between pages 1 to 3 on a 12-story set.
|
||||
|
||||
## Automated, real databases (task test:mysql, task test:postgres)
|
||||
|
||||
Repeat items 1 to 4 against MySQL 8.4, MariaDB 11 and PostgreSQL. Compare ids, totals and counts with SQLite, not only pass/fail. Also assert no query returns a different id set under a different collation (title sort aside). Record NOT RUN with the reason if the daemon is down, or use the podman shim (see memory). Inspect the executed SQL once (GORM debug) to confirm no dialect-only construct, no per-row query for coverage (coverage and authors are loaded in a bounded number of queries per page), and that request text is never concatenated.
|
||||
|
||||
## Automated, use case (task test)
|
||||
|
||||
5. `ListArticlesUseCase` over fakes: grouped command, role grants for owner, reader and restricted writer, fake `site` and `localization` repositories. Assert representative choice, `missing_langs` only when `create_translation` is held, the counts rules, and that a hidden version never reaches the result. Assert sentinel errors with `errors.Is`. Existing use case tests stay green.
|
||||
|
||||
## Automated, HTTP (task test)
|
||||
|
||||
6. `article.NewHandler(...)` over the real use cases and fakes, a fresh `chi.NewRouter()` mirroring `NewArticleRouter` in `api/router.go` (GET / under `jwtauth.Verifier` only), `validator.InitValidator()` first, `httptest`, jwt cookies signed with a test `jwtauth.New("HS256", ...)` secret (owner, reader, restricted writer, unverified user, expired, none).
|
||||
7. "Public lists are unchanged": bare request and a public request keep the flat keys exactly (compare the key set and values with a golden produced before the slice); `lang=fr` and `author` narrow the flat list; `group=story` for the three public callers and every invalid query in the Examples answer 400 with `apperr.BodyErrorMessage` and the fake repository records no call.
|
||||
8. Grouped response shape: decode and assert `items[]` keys (flat item keys plus `story_updated_at`, `story_authors`, `coverage`, `missing_langs`, `capabilities`), `total`, `page`, `size`, `counts` keys (all, online, draft, suggestions, missing), `items` `[]` not null, snake_case, errors plain text. Repository failure and count failure answer 500 "Failed to retrieve articles". Docs: `docs/` gains the grouped contract (parameters, shape, counts, errors) so the SPA mocks `/v1` from it.
|
||||
|
||||
## Automated, SPA (Vitest + Vue Test Utils, task test:web)
|
||||
|
||||
9. Mock `GET /v1/article/` per state with the Journal responses of the feature (and Press, Atelier and the reader). One test per design state of group L7 except `loc.l7.review`: `loc.l7.all`, `page2`, `fr`, `frMissing`, `emptyMissing`, `noResults`, `frLoading`, `listFail`, `limited`, `author`, `langFilter`, `rowMenu`, `filters`, `tabsScroll`, `long`. Assert: the request query (`group=story`, lang, tab, author, page, size; defaults omitted), the URL after a successful response, the unit label text, the tab labels and counts, "–" counts while loading, the retained filters on failure, the chip order and collapse (+N, accessible names), dashed chips only from `missing_langs`, and the identical row menu items at mobile and desktop widths.
|
||||
10. URL state: invalid `lang`, `tab=review`, `author=abc`, `page=abc` fall back and are replaced; every filter change drops `page`; the tab and filter chips match the URL; "Clear all" empties all filters.
|
||||
11. Regression of the amended screens: `ArticlesPage` still passes the sort, pagination, row action, delete and toast cases of `articles_list` and `articles_pagination_screen`, now on grouped rows.
|
||||
|
||||
## Visual (task visual:check -- localization-list or the L7 group)
|
||||
|
||||
Compare each state with its board frame at desktop (1440), 1024 where the board draws it, and mobile (390), light and dark. The gate is hard: every capture within 10 %. Mock `/v1` from `web/visual/states.ts` with the board's own data (`docs/design/source/locale-fixtures.js`: Journal 8 stories, 17 versions, page size 5; Press; Atelier; the limited scope). Titles, bylines (Marta Alves, Camille Roche, Luis Ferrer) and the language names come from the fixture. All states use relative text ("2 h ago", "Yesterday", "3 days ago"): list them as `skip: clock-sensitive` only if the SPA renders them from the current time; otherwise freeze nothing and use the board's absolute dates. `loc.l7.review` is out of scope (release 2). `loc.l7.tabsScroll` is captured at 1440 and 390 only.
|
||||
|
||||
## Manual
|
||||
|
||||
- Keyboard and focus: tab order through search, filters, tabs (arrow keys), rows, row menu (Enter or Down opens, Esc returns to the button), chips, pager; focus ring on the dashed chip buttons; the filter sheet traps focus and returns it to its trigger.
|
||||
- Screen reader: the unit label announces politely on every filter change; chips announce "French: draft" and "3 more languages: ...".
|
||||
- Hover and motion: tab fade and chevrons, skeleton pulse, sheet open and close, reduced motion.
|
||||
- Real databases through a running server: open the list against a real SQLite file with several languages, a restricted account, and the same data on MySQL and PostgreSQL; compare counts with the Languages page.
|
||||
- Real SMTP: not applicable.
|
||||
Reference in new issue
Block a user