Review blocker A — form error swap and false success:
- Change HTMX contract so forms target their own container (outerHTML)
instead of appending to #comments-list
- Use OOB swaps to append approved comments to the correct target
- Add success/error message display inside form templates
- Remove hx-on::after-request handlers (no longer needed)
Review blocker B — reply rendering shape:
- Create _reply.html partial with compact reply markup
- Approved replies via HTMX now use compact template + OOB swap
into parent's .replies-container
- Reply form errors render inside reply form container
E2E test fixes:
- Update 4 failing tests to wait for inline HTMX messages instead
of redirect-based URL assertions
- Add aria-label='Comment form errors' to form error display
- Rename test_reply_submission_redirects to
test_reply_submission_shows_moderation_message
Mypy internal error workaround:
- Add mypy override for apps.comments.views (django-stubs triggers
internal error on ORM annotate() chain with mypy 1.11.2)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1. Reply HTMX target: server sends HX-Retarget/HX-Reswap headers to
insert replies inside parent comment's .replies-container div
2. Empty thread swap target: always render #comments-list container
even when no approved comments exist
3. Reaction hydration: add _annotate_reaction_counts() helper that
hydrates reaction_counts and user_reacted on comments in
get_context(), comment_poll(), and single-comment responses
4. HTMX error swap: return 200 instead of 422 for form errors since
HTMX 2 doesn't swap 4xx responses by default
5. Vary header: use patch_vary_headers() instead of direct assignment
to avoid overwriting existing Vary directives
Also fixes _get_session_key() to handle missing session attribute
(e.g. from RequestFactory in performance tests).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Gate e2e-admin superuser behind E2E_MODE env var (security)
- Add status and tag filters to ArticleFilterSet
- Set default_ordering to -published_date on listing viewset
- Add summary to ArticlePage.search_fields for search support
- Add 4 new tests for filters, ordering, and search fields
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add published_date field to ArticlePage with auto-populate from
first_published_at on first publish, plus data migration backfill
- Surface go_live_at/expire_at scheduling fields in editor panels
- Reorganise ArticlePage editor with TabbedInterface (Content,
Metadata, Publishing, SEO tabs)
- Add Articles PageListingViewSet to admin menu with custom columns
(author, category, published date, status) and category/author filters
- Add Articles summary dashboard panel showing drafts, scheduled,
and recently published articles
- Update all front-end queries and RSS feeds to use published_date
- Add 10 unit tests and 4 E2E tests for new admin features
Closes#39
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use an explicit Wagtail Column for pending_in_article in CommentViewSet list_display and add a regression test for /cms/snippets/comments/comment/.
Fixes#37
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use category-state-driven queries for nav and category listing routes, and add regression tests for empty but valid categories.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Disable reverse manager generation on ArticlePage.category and switch category selection to id-based queries so CI mypy can resolve models reliably.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Wrap long lines for Ruff and restore a single 'All' tag-reset link to avoid Playwright strict-mode collisions.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Fix article header tag borders: replace broken border-current/20
(Tailwind can't apply opacity to currentColor) with per-tag border
colour classes via new get_tag_border_css filter
- Add calendar icon before article date in article header
- Add clock icon before read time in article header and home featured
- Match article card footer to wireframe (remove extra min-read span)
- Add rounded-md to code block matching wireframe
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Change SocialMediaLink.url and NavigationMenuItem.link_url from
URLField to CharField(max_length=500) to support internal paths
like /feed/ that fail URLField validation
- Replace destructive reverse_seed (deleted ALL rows) with
RunPython.noop to prevent data loss on rollback
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Update TagMetadata CSS classes to use brand colours with translucent
backgrounds matching the wireframe design:
- cyan: bg-brand-cyan/10 text-brand-cyan
- pink: bg-brand-pink/10 text-brand-pink
- neutral: bg-zinc-800 text-white (dark: bg-zinc-100 text-black)
Previously used muted Tailwind defaults (bg-cyan-100/text-cyan-900)
which appeared as soft pastels instead of the intended neon look.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The SecurityHeadersMiddleware applied a strict style-src policy to all
responses, blocking inline styles that Wagtail admin relies on for
layout. Skip the custom CSP for /cms/ and /django-admin/ paths.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Replace nav inline newsletter form with Subscribe CTA link per wireframe
- Remove newsletter form from footer; add Connect section with social/RSS links
- Fix honeypot inputs using hidden attribute (inline style blocked by CSP)
- Add available_tags to HomePage.get_context for Explore Topics section
- Add data-comment-form attribute to main comment form for reliable locating
- Seed approved comment in E2E content for reply flow testing
- Expand test_comments.py: moderation message, not-immediately-visible,
missing fields, reply form visible, reply submission
- Make COMMENT_RATE_LIMIT_PER_MINUTE configurable; set 100 in dev to prevent
E2E test exhaustion; update rate limit unit test with override_settings
- Update newsletter/home E2E tests to reflect nav form removal
- Update unit test to assert no nav/footer newsletter forms
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
style-src and font-src were 'self' only, blocking fonts.googleapis.com
stylesheet and fonts.gstatic.com font files. Add both origins so
Space Grotesk, Inter and Fira Code load correctly in production.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Wagtail's initial migration creates a localhost:80 site. Wagtail matches
incoming requests by hostname before ever checking is_default_site, so
updating only the is_default_site record left localhost:80 still pointing
at the Welcome page. Fix by updating root_page on every site.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Wagtail initialises the default site with hostname 'localhost'. The previous
get_or_create on '127.0.0.1' left the localhost site intact (still pointing
to the Welcome page), so browsers got the wrong root page.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Mount /opt/playwright-tools/browsers into web container (docker-compose.yml
and CI docker run) — never download browsers, use the ones on this host
- Set PLAYWRIGHT_BROWSERS_PATH in all container envs (compose + CI)
- Drop 'playwright install chromium' steps from pr-e2e and nightly-e2e jobs
- Bump playwright requirement to ~1.57.0 to match the installed browser builds
- Fix seed_e2e_content: de-duplicate default Site entries left by unit test
fixtures so Wagtail always routes to the seeded home page
- Fix test_comments_section_absent_when_disabled: use exact=True on heading
locator to avoid matching 'No Comments Article' h1 as 'Comments' heading
- Fix test_copy_link_button_updates_text: use [data-copy-link] data-attr
locator (stable across text change) and force-override clipboard.writeText
via page.evaluate() rather than relying on init_script polyfill
E2E suite verified locally: 34 passed via docker compose exec
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- test_homepage_title_contains_brand: to_have_title() requires a string or
regex, not a lambda; switch to re.compile('No Hype AI')
- test_granular_preferences_save_dismisses_banner: wrong element clicked to
open <details>; use 'details summary' locator directly
- test_subscribe_invalid_email_shows_error: browser HTML5 email validation
swallows the submit event before the JS handler fires; add 'novalidate' via
evaluate() so the fetch still runs and the server returns 400
- test_copy_link_button_updates_text: clipboard API unavailable in headless
Docker; add polyfill + pre-grant permissions in conftest page fixture so
the JS success path runs and button text becomes 'Copied'
- test_comments_section_absent_when_disabled: guard against Wagtail's
add_child() resetting BooleanField defaults by calling an explicit
.update(comments_enabled=False) + re-setting on the instance before
save_revision().publish(); also tighten test to assert 200 + correct title
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>