Add end-to-end tests covering the full feed-to-published-post pipeline #118

Open
opened 2026-08-01 16:35:52 +02:00 by myrmidex · 0 comments
Owner

Summary

The existing suite (Unit + Feature, ~790 tests) passes green while user-facing flows are broken. Every layer is tested in isolation; nothing tests the layers working together, and nothing tests what the user actually sees.

This ticket adds end-to-end coverage so "tests pass" means "the flow works".

Evidence — three failures in a single session, all with a green suite

1. #108 — modals rendered as an opaque grey sheet.
12 Livewire tests passed. They assert rendered HTML, never CSS. A Tailwind v3 utility removed in v4 (bg-opacity-75) emitted no CSS, so the backdrop covered the modal at full opacity. The page was unusable; the suite was green.

2. #115 — Belga feed produced no articles.
The RSS feed had 404'd upstream. ArticleFetcher swallowed the failure and returned an empty collection, the job reported success, and last_fetched_at updated. No test noticed, because no test asserts that a feed actually yields articles.

3. #117 — published posts have no thumbnail.
extractThumbnail() returns an entity-encoded URL (& instead of &). Extraction "succeeds", the value is passed to Lemmy, and the post publishes — just without an image. No test covers the extracted value's shape, only that a value exists.

The common thread: each unit works, the composition does not. All three were found by manual clicking, not by tests.

Current state

  • tests/Unit and tests/Feature only — see phpunit.xml
  • No browser-testing dependency (no Dusk, Playwright, Cypress, or Panther in composer.json / package.json)
  • Livewire component tests assert server-rendered HTML and component state, so they cannot catch CSS, JS, or asset-pipeline failures

Proposed coverage

Tier 1 — pipeline integration tests (no browser)

Cheapest and highest value; catches #115 and #117 style failures. Runs in the existing PHPUnit setup with Http::fake for all external calls.

  • Feed → discovery → articles created, asserting a non-zero count
  • Article → validation → RouteArticle rows created for each active route
  • RouteArticle → publishing → correct payload reaches the Lemmy client, asserting the shape of every field, including custom_thumbnail being a valid URL with no HTML entities
  • A feed whose URL 404s produces zero articles and surfaces that fact (ties into #116)
  • Adding a route after articles were fetched — currently strands them silently (see "Related")

Tier 2 — browser tests

Needed for #108-class failures, where markup renders but the page is unusable.

  • Modal opens, is visible, and is interactive (would have caught #108 directly)
  • Create feed / channel / route through the actual forms
  • Articles page renders rows, filters work
  • Requires choosing a tool — Laravel Dusk is the natural fit for a Laravel/Livewire app and runs in CI headless; Playwright is more capable but a heavier addition. Worth a decision before starting.

Tier 3 — asset/CSS smoke check

Cheap guard against the #108 class of bug without a full browser:

  • Assert that classes used in Blade templates actually exist in the compiled CSS, or at minimum that npm run build output contains selectors for a known set of critical classes (modal backdrop, form inputs)
  • Alternative: a Tailwind lint step in CI flagging utilities that emit nothing

Design questions

  • Where do E2E tests run? Adding them to the default suite slows every run. A separate E2E testsuite in phpunit.xml, run in CI and on demand locally, is probably right.
  • How much external mocking? Tier 1 should use Http::fake throughout (project rule: tests must work offline). But that reintroduces the #115 blind spot — a faked API always returns the shape we expect. Consider a small, separately-run contract test suite that hits real endpoints and is allowed to fail loudly, kept out of the offline suite.
  • Browser tests in CI — Dusk needs Chrome in the container. Weigh against the value.

Acceptance criteria

  • Tooling decision recorded (Dusk vs Playwright vs none for Tier 2)
  • Tier 1 pipeline tests implemented and passing
  • Each of the three failures above has a test that would have caught it
  • E2E tests run in CI
  • Documented in .claude/PLATFORM.md: what each tier covers and when to add to it
  • Tests work offline, per project rules
  • #108, #115, #117 — the three failures that motivated this
  • #116 — detecting feeds that fetch successfully but return nothing; overlapping concern, different mechanism (runtime monitoring vs. test coverage)
## Summary The existing suite (Unit + Feature, ~790 tests) passes green while user-facing flows are broken. Every layer is tested in isolation; nothing tests the layers working together, and nothing tests what the user actually sees. This ticket adds end-to-end coverage so "tests pass" means "the flow works". ## Evidence — three failures in a single session, all with a green suite **1. #108 — modals rendered as an opaque grey sheet.** 12 Livewire tests passed. They assert rendered HTML, never CSS. A Tailwind v3 utility removed in v4 (`bg-opacity-75`) emitted no CSS, so the backdrop covered the modal at full opacity. The page was unusable; the suite was green. **2. #115 — Belga feed produced no articles.** The RSS feed had 404'd upstream. `ArticleFetcher` swallowed the failure and returned an empty collection, the job reported success, and `last_fetched_at` updated. No test noticed, because no test asserts that a feed actually yields articles. **3. #117 — published posts have no thumbnail.** `extractThumbnail()` returns an entity-encoded URL (`&` instead of `&`). Extraction "succeeds", the value is passed to Lemmy, and the post publishes — just without an image. No test covers the extracted value's *shape*, only that a value exists. The common thread: **each unit works, the composition does not.** All three were found by manual clicking, not by tests. ## Current state - `tests/Unit` and `tests/Feature` only — see `phpunit.xml` - No browser-testing dependency (no Dusk, Playwright, Cypress, or Panther in `composer.json` / `package.json`) - Livewire component tests assert server-rendered HTML and component state, so they cannot catch CSS, JS, or asset-pipeline failures ## Proposed coverage ### Tier 1 — pipeline integration tests (no browser) Cheapest and highest value; catches #115 and #117 style failures. Runs in the existing PHPUnit setup with `Http::fake` for all external calls. - Feed → discovery → articles created, asserting a **non-zero** count - Article → validation → `RouteArticle` rows created for each active route - `RouteArticle` → publishing → correct payload reaches the Lemmy client, asserting the **shape** of every field, including `custom_thumbnail` being a valid URL with no HTML entities - A feed whose URL 404s produces zero articles **and** surfaces that fact (ties into #116) - Adding a route after articles were fetched — currently strands them silently (see "Related") ### Tier 2 — browser tests Needed for #108-class failures, where markup renders but the page is unusable. - Modal opens, is visible, and is interactive (would have caught #108 directly) - Create feed / channel / route through the actual forms - Articles page renders rows, filters work - Requires choosing a tool — **Laravel Dusk** is the natural fit for a Laravel/Livewire app and runs in CI headless; Playwright is more capable but a heavier addition. Worth a decision before starting. ### Tier 3 — asset/CSS smoke check Cheap guard against the #108 class of bug without a full browser: - Assert that classes used in Blade templates actually exist in the compiled CSS, or at minimum that `npm run build` output contains selectors for a known set of critical classes (modal backdrop, form inputs) - Alternative: a Tailwind lint step in CI flagging utilities that emit nothing ## Design questions - **Where do E2E tests run?** Adding them to the default suite slows every run. A separate `E2E` testsuite in `phpunit.xml`, run in CI and on demand locally, is probably right. - **How much external mocking?** Tier 1 should use `Http::fake` throughout (project rule: tests must work offline). But that reintroduces the #115 blind spot — a faked API always returns the shape we expect. Consider a small, separately-run contract test suite that hits real endpoints and is allowed to fail loudly, kept out of the offline suite. - **Browser tests in CI** — Dusk needs Chrome in the container. Weigh against the value. ## Acceptance criteria - [ ] Tooling decision recorded (Dusk vs Playwright vs none for Tier 2) - [ ] Tier 1 pipeline tests implemented and passing - [ ] Each of the three failures above has a test that would have caught it - [ ] E2E tests run in CI - [ ] Documented in `.claude/PLATFORM.md`: what each tier covers and when to add to it - [ ] Tests work offline, per project rules ## Related - #108, #115, #117 — the three failures that motivated this - #116 — detecting feeds that fetch successfully but return nothing; overlapping concern, different mechanism (runtime monitoring vs. test coverage)
myrmidex added this to the v1.4.0 milestone 2026-08-01 16:35:52 +02:00
myrmidex added the
testing
label 2026-08-01 16:35:52 +02:00
myrmidex self-assigned this 2026-08-01 16:35:52 +02:00
myrmidex removed this from the v1.4.0 milestone 2026-08-06 22:46:44 +02:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lvl0/fedi-feed-router#118
No description provided.