Skip to content

newsletter: hide footer band on token pages; assert subscribe payload attribution - #270

Merged
devin-ai-integration[bot] merged 2 commits into
masterfrom
devin/newsletter-footer-attribution
Oct 29, 2026
Merged

devin-ai-integration[bot] merged 2 commits into
masterfrom
devin/newsletter-footer-attribution

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Part of #269 (newsletter polish for DEMA 2026). Two independent changes:

1. Hide the footer signup band on newsletter token pages

NewsletterFooterCta now returns null on /newsletter/confirm and /newsletter/unsubscribe in addition to /newsletter. A signup prompt under "You're subscribed!" is redundant, and under "You've been unsubscribed." it contradicts the action just taken. All three pages already carry links to /newsletter in their outcome states, so no signup entry point is lost. The hide is still gated by isRoutePublished(locale, path) — unpublished localized routes (e.g. /nl/newsletter/confirm, no Dutch newsletter pages exist yet) keep the band as the visitor's only signup entry point. Footer nav, contact info, and links are untouched; ordinary pages are untouched.

2. Assert the actual subscribe payload carries DEMA attribution

Issue #269 noted the existing tests only prove attribution params stay in the URL, not that the submitted request contains them. New tests/e2e/newsletter-attribution.spec.ts intercepts the browser's own POST /api/newsletter/subscribe (fully mocked — no real subscribers, no real email) and asserts on the submitted JSON:

  • campaign=dema-2026, source=qr, placement=group-flyer / individual-flyer with matching audience
  • subscription.audiences contains both memberships when both boxes are checked
  • Attribution is preserved when the visitor changes the preselected audience before submitting
  • Footer handoff submits source=website, placement=footer, campaign/audience=null
  • The email appears only in the POST body — never in the request URL or the page URL

Tests fail if attribution is silently dropped from the payload (exact-match assertions on attribution), which is the regression #269 asked to catch.

Verification

  • npx vitest run on touched suites: 13/13 pass (footer band hidden on /newsletter/confirm + /newsletter/unsubscribe, visible on /nl/newsletter* variants)
  • npx eslint + tsc --noEmit: clean
  • npm run check (docs/env/hygiene): pass
  • npm run build:test: production build succeeds
  • npx playwright test newsletter-attribution newsletter-footer: 36/36 pass across desktop-chromium, mobile-chromium, mobile-webkit

No production endpoints touched; all e2e traffic is intercepted and mocked.

Link to Devin session: https://app.devin.ai/sessions/64a46d6928c84834a009ce1898be907f
Open in Devin Desktop: https://app.devin.ai/desktop/session/64a46d6928c84834a009ce1898be907f?variant=devin
Requested by: @spizeck

Summary by Sourcery

Prevent redundant newsletter signup prompts on token outcome pages and verify that attribution reaches the subscription request payload.

Bug Fixes:

  • Hide the newsletter footer signup band on published newsletter signup, confirmation, and unsubscribe pages while preserving it on unpublished localized routes.
  • Provide a direct link to resubscribe from the successful unsubscribe state.

Tests:

  • Add end-to-end coverage asserting newsletter subscription payloads preserve campaign attribution, audience selections, footer handoff metadata, and email privacy.

… attribution

- Hide the footer signup band on /newsletter/confirm and
  /newsletter/unsubscribe (published locales only): a signup prompt
  under 'You're subscribed!' is redundant, and under an opt-out
  confirmation contradictory. Both pages already link to /newsletter.
- Extend the footer integration tests for the new routes and for
  unpublished localized variants (nl keeps the band).
- New e2e spec intercepting the browser's own subscribe POST to assert
  the submitted payload carries campaign/source/placement/audience:
  group- and individual-flyer DEMA links, both-audience selection,
  attribution preserved across audience changes, footer
  source=website/placement=footer, and no email in request or page
  URLs. Closes the test gap in #269. Endpoints are fully mocked.

Co-Authored-By: Chad <chadnuttall1@gmail.com>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".

  • Disable automatic comment, CI, and merge conflict monitoring

@sourcery-ai

sourcery-ai Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

Reviewer's Guide

The PR hides redundant or contradictory footer signup prompts on published newsletter and token pages while retaining them for unpublished localized routes, and adds mocked Playwright coverage that verifies DEMA attribution and audience data reach the actual subscribe POST payload without exposing email addresses in URLs.

Sequence diagram for newsletter attribution submission

sequenceDiagram
    actor Visitor
    participant Page as NewsletterPage
    participant Browser
    participant API as SubscribeAPI
    Visitor->>Page: Select audience and submit email
    Page->>Browser: POST /api/newsletter/subscribe
    Browser->>API: JSON email subscription.audiences attribution
    API-->>Browser: Mocked subscribe response
    Browser-->>Page: Submission result
Loading

Flow diagram for localized newsletter footer visibility

flowchart TD
    A[Current pathname] --> B[parsePathname]
    B --> C{Newsletter route?}
    C -->|No| D[Render footer signup band]
    C -->|Yes| E{Route published for locale?}
    E -->|No| D
    E -->|Yes| F[Hide footer signup band]
Loading

File-Level Changes

Change Details Files
Restrict the footer signup band to published newsletter routes, hiding it on the main signup page and both token outcome pages while preserving localized fallback behavior.
  • Expanded hidden-route matching to confirm and unsubscribe paths.
  • Continued gating visibility with localized route publication status.
  • Added unit and end-to-end coverage for success, error, query-string, and unpublished localized token routes.
components/newsletter-footer-cta.tsx
tests/integration/newsletter-footer-cta.test.tsx
tests/e2e/newsletter-footer.spec.ts
Add browser-level assertions that newsletter subscription requests retain campaign attribution and keep email data out of URLs.
  • Intercepted and mocked subscribe POST requests, asserting exact attribution and subscription payloads.
  • Covered flyer audiences, audience changes, multi-audience subscriptions, and footer handoff attribution.
  • Verified submitted email appears in the POST body rather than request or page URLs.
tests/e2e/newsletter-attribution.spec.ts

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@vercel

vercel Bot commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
seasaba-web Ready Ready Preview Oct 29, 2026 10:56pm UTC

Request Review

@coderabbitai

coderabbitai Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: a2fd7f6e-f127-4d92-b377-96872d6eb447

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 3 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="tests/e2e/newsletter-attribution.spec.ts" line_range="31" />
<code_context>
+function mockSubscribe(page: import("@playwright/test").Page) {
+  const seen: SubscribePayload[] = [];
+  const requestUrls: string[] = [];
+  void page.route("**/api/newsletter/subscribe", async (route) => {
+    requestUrls.push(route.request().url());
+    seen.push(route.request().postDataJSON() as SubscribePayload);
</code_context>
<issue_to_address>
**Real newsletter signups escape mocks**

When route registration has not completed before form submission, `mockSubscribe` discards the `page.route` promise, so the POST reaches the app instead of the mock and `seen` stays empty; tests can create real subscriptions or send emails.

Make `mockSubscribe` await route registration and wait for it to complete before allowing form submission.
</issue_to_address>

### Comment 2
<location path="tests/integration/newsletter-footer-cta.test.tsx" line_range="75" />
<code_context>
+    // No Dutch newsletter pages are published yet — the band remains the
+    // only signup entry point on those routes.
+    mockPathname = "/nl/newsletter/confirm";
+    render(<NewsletterFooterCta />);
+    expect(
+      screen.getByRole("textbox", { name: /newsletter email/i })
</code_context>
<issue_to_address>
**Integration test finds duplicate forms**

When the localized-token test renders the component a second time without cleanup, `screen.getByRole` matches both forms after the second render and throws a multiple-elements error, so the integration test fails.

Clean up the first render before rendering again, or use `rerender` to update the existing render.

Also at `tests/integration/newsletter-footer-cta.test.tsx:78`.
</issue_to_address>

### Comment 3
<location path="components/newsletter-footer-cta.tsx" line_range="30" />
<code_context>
+  const HIDDEN_ON = ["/newsletter", "/newsletter/confirm", "/newsletter/unsubscribe"];
   const { locale, path } = parsePathname(pathname ?? "/");
-  if (path === "/newsletter" && isRoutePublished(locale, "/newsletter")) {
+  if (HIDDEN_ON.includes(path) && isRoutePublished(locale, path)) {
     return null;
   }
</code_context>
<issue_to_address>
**Resubscribe link disappears after opt-out**

When a visitor wants to subscribe again immediately after successfully unsubscribing, `NewsletterFooterCta` returns `null` while `NewsletterTokenHandler` shows only the success message and detail, and the footer navigation has no newsletter link, so the visitor has no visible route back to signup.

Keep the signup CTA visible on the successful unsubscribe page or provide another visible link to signup.
</issue_to_address>

Sourcery assessment

Approval pending. 3 findings to address first.

Blocking findings: tests/e2e/newsletter-attribution.spec.ts:31, tests/integration/newsletter-footer-cta.test.tsx:75, components/newsletter-footer-cta.tsx:30


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread components/newsletter-footer-cta.tsx Resolved
- Await page.route() registration in mockSubscribe before navigation so
  no subscribe request can escape the mock (Sourcery finding 1).
- Split the duplicated localized-token render into it.each cases so each
  route gets an isolated DOM (finding 2).
- Give the unsubscribe success state a text link back to /newsletter for
  visitors who want to resubscribe, without restoring the signup band
  (finding 3). Asserted in e2e.

Co-Authored-By: Chad <chadnuttall1@gmail.com>

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sourcery assessment

Approved.

@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

End-to-end verified on a production test build (npm run build:test + next start) in Chrome — all assertions passed on the final commit.

Band hidden on token/signup pages, footer nav intact:

  • /newsletter/confirm?token=nlc_… — outcome card shown, no band, all 4 footer columns render
  • /newsletter/unsubscribe?token=nlu_… — same; success state also exposes a "Subscribe again on the newsletter page" link
  • /newsletter — signup form renders, no band

/newsletter/confirm — band hidden, footer nav intact

Controls: band still renders where expected
  • /about — band present with working Subscribe button

/about — band present

  • /nl/newsletter (unpublished localized route → 404) — band kept as the only signup entry point

/nl/newsletter — 404 keeps band

Footer handoff + DEMA attribution still work
  • Footer signup on /about → /newsletter?placement=footer&source=website, email prefilled via sessionStorage (never in URL)

prefilled handoff

  • DEMA link ?campaign=dema-2026&source=qr&placement=group-flyer&audience=group → Group Organizer preselected, params preserved

DEMA group preselect

@devin-ai-integration
devin-ai-integration Bot merged commit f8dccee into master Oct 29, 2026
11 checks passed
@devin-ai-integration
devin-ai-integration Bot deleted the devin/newsletter-footer-attribution branch October 29, 2026 23:06

This branch was successfully deployed

1 active deployment
Preview — 0cb7fa0c Deployed Oct 29, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant