Skip to content

Added gift-link analytics journey E2E test#29005

Open
jonatansberg wants to merge 4 commits into
mainfrom
ber-3746-gift-link-analytics-e2e
Open

Added gift-link analytics journey E2E test#29005
jonatansberg wants to merge 4 commits into
mainfrom
ber-3746-gift-link-analytics-e2e

Conversation

@jonatansberg

Copy link
Copy Markdown
Member

ref https://linear.app/ghost/issue/BER-3746/integrate-gift-link-usage-tracking-with-analytics

Draft — end-to-end coverage for the gift-link analytics journey. Opened as a draft because it can't pass until the analytics proxy carries gift_link (see Blocked on, below).

What it covers

The one seam no single-layer test proves: that a real gift read flows producer → client beacon → TrafficAnalytics proxy → Tinybird and surfaces back in admin. It:

  1. Creates a published paid post and mints a gift link (PUT /posts/:id/gift_links).
  2. Visits /<slug>/?gift=<token> in an isolated context, firing the real ghost-stats beacon.
  3. Asserts the visit shows up in admin on both surfaces:
    • the per-link visitor count (share-modal gift-link-views badge), and
    • the web-traffic Gift link = used filter (unique visitors scoped to the gift read).

Runs in the analytics Playwright project, where PublicPage.goto waits for the page-hit request and the proxy runs TINYBIRD_WAIT=true — so ingestion is synchronous and no polling is needed.

Blocked on

Skipped behind GIFT_LINK_PROXY_READY. The pinned analytics proxy rebuilds the page-hit payload field-by-field and drops gift_link, so the beacon can't carry it yet. To enable, in one change:

  1. Land TryGhost/TrafficAnalytics#742 (the gift_link passthrough) and publish an image.
  2. Bump the analytics-lane proxy image to that version.
  3. Flip GIFT_LINK_PROXY_READY on (or remove the guard) and undraft.

Also depends on the gift-link analytics stack being merged: #28854 (datafiles / api_gift_link_visits_v2 + gift_link column) and the usage-count wiring fix (#29004).

Notes

  • Adds PostAnalyticsOverviewPage (gift-link card + share modal) page object.
  • pnpm lint and pnpm test:types pass.
@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

This PR updates the post analytics overview page object with gift-link locators and helper methods, adds a new e2e gift-link analytics journey that creates a paid post, opens the share modal, copies and visits a gift link, and checks analytics results, updates the Gift link card visitors element test id, exports the new page object from the barrel, and bumps the analytics service Docker image digest.

Changes

Area Changes
E2E page objects Added PostAnalyticsOverviewPage locators and methods for the gift-link card, share modal, page refresh, and gift-link clipboard flow; exported it from the analytics page-object barrel
E2E tests Added a gift-link analytics journey suite with helper flow and assertions for visitors count and web-traffic filtering
App UI Added data-testid="gift-link-card-visitors" to the Gift link card visitors value
Infrastructure Updated the analytics service image digest in compose.dev.analytics.yaml

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Test
  participant PostAnalyticsOverviewPage
  participant IsolatedPage

  Test->>PostAnalyticsOverviewPage: openShareModal()
  Test->>PostAnalyticsOverviewPage: copyGiftLinkUrl()
  Test->>IsolatedPage: visit gift-read URL
  IsolatedPage-->>Test: analytics beacon sent
  Test->>PostAnalyticsOverviewPage: refreshData()
  Test->>PostAnalyticsOverviewPage: assert visitors value and filters
Loading

Possibly related PRs

  • TryGhost/Ghost#28853: Adds the gift-link tracking dimension that this PR’s analytics assertions exercise.
  • TryGhost/Ghost#28897: Introduces gift-link UI and usage behavior closely tied to the new page object and e2e journey.

Suggested reviewers: cmraible, kevinansfield

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately highlights the new gift-link analytics E2E test.
Description check ✅ Passed The description directly matches the changeset and explains the new gift-link analytics journey coverage.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ber-3746-gift-link-analytics-e2e

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@nx-cloud

nx-cloud Bot commented Jun 30, 2026

Copy link
Copy Markdown

🤖 Nx Cloud AI Fix

Ensure the fix-ci command is configured to always run in your CI pipeline to get automatic fixes in future runs. For more information, please see https://nx.dev/ci/features/self-healing-ci


View your CI Pipeline Execution ↗ for commit 5ccc9e3

Command Status Duration Result
nx run-many --target=build --projects=tag:publi... ✅ Succeeded <1s View ↗
nx run-many -t test:unit -p @tryghost/posts,@tr... ✅ Succeeded 2m 29s View ↗
nx run @tryghost/admin:build ✅ Succeeded 1m 49s View ↗
nx run-many -t lint -p @tryghost/posts,@tryghos... ✅ Succeeded 21s View ↗
nx run ghost:build:assets ✅ Succeeded 2s View ↗
nx run ghost:build:tsc ✅ Succeeded 6s View ↗

💡 Verify your cache is correct by running tasks in a sandbox. Read docs ↗


☁️ Nx Cloud last updated this comment at 2026-07-01 14:59:42 UTC

ref https://linear.app/ghost/issue/BER-3746/integrate-gift-link-usage-tracking-with-analytics

- end-to-end coverage for the gift-link analytics journey: create a paid post, mint
  a gift link, visit its public URL to fire the real ghost-stats beacon, then assert
  the visit surfaces in admin — both the per-link visitor count (share modal badge)
  and the web-traffic "Gift link = used" filter
- runs in the analytics Playwright project (real beacon -> proxy -> Tinybird, with
  the lane's TINYBIRD_WAIT=true making ingestion synchronous, so no polling)
- skipped behind GIFT_LINK_PROXY_READY: the pinned analytics proxy rebuilds the
  page-hit payload and drops gift_link, so the beacon can't carry it until
  TryGhost/TrafficAnalytics#742 ships and the lane's proxy image is bumped; flip the
  env on (and bump the image) in that same change to enable
- adds PostAnalyticsOverviewPage for the gift-link card + share modal
ref https://linear.app/tryghost/issue/BER-3746

- tightened the proxy-readiness note to the essentials (the pipeline, the #742
  dependency, and how to enable) and dropped the self-evident badge comment
- e2e guidance prefers fewer comments and clearer names
ref https://linear.app/tryghost/issue/BER-3746

- TrafficAnalytics#742 (gift passthrough) merged and shipped in
  ghost/traffic-analytics 1.0.261, so the pinned dev/e2e proxy now carries
  gift_link end-to-end instead of dropping it
- with the real image in place the GIFT_LINK_PROXY_READY escape hatch has no
  reason to exist; removing it lets the journey test run by default in CI
  rather than skipping until a manual local build was wired up
@jonatansberg jonatansberg force-pushed the ber-3746-gift-link-analytics-e2e branch from af9b928 to 523448e Compare July 1, 2026 14:21
@jonatansberg jonatansberg marked this pull request as ready for review July 1, 2026 14:21
@jonatansberg jonatansberg requested a review from 9larsons as a code owner July 1, 2026 14:21

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 523448eeb0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

await webTraffic.selectFilterField('used');

await expect(webTraffic.getActiveFilter('Gift link')).toBeVisible();
await expect(webTraffic.totalUniqueVisitorsTab).toContainText('1');

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Isolate the gift-link count test from prior visits

When this file runs as a whole, the E2E fixture reuses one Ghost/Tinybird environment per file unless the file opts into per-test isolation, so the first test’s gift-link page hit is still present when this second test applies the site-wide Gift link = used filter. After the preceding test has already recorded one gift-link visitor, this assertion can see two unique gift-link visitors instead of one; either isolate the file/test or scope the assertion to data created within this test.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@e2e/tests/admin/analytics/gift-link-journey.test.ts`:
- Around line 15-17: The gift-link setup in the test currently assumes
`page.request.put` succeeded and that `gift_links` contains at least one item
before reading `giftLinks[0].token`. Update the `gift-link-journey.test.ts` flow
to first assert the PUT response is successful, then verify the parsed JSON
contains a non-empty `gift_links` array before indexing, so failures in this
test surface as clear assertions instead of an undefined access error.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5213d8c0-dac4-48d8-9f99-cd4eed628420

📥 Commits

Reviewing files that changed from the base of the PR and between ffa040a and 523448e.

📒 Files selected for processing (4)
  • compose.dev.analytics.yaml
  • e2e/helpers/pages/admin/analytics/post-analytics/index.ts
  • e2e/helpers/pages/admin/analytics/post-analytics/post-analytics-overview-page.ts
  • e2e/tests/admin/analytics/gift-link-journey.test.ts
Comment on lines +15 to +17
const response = await page.request.put(`/ghost/api/admin/posts/${post.id}/gift_links`);
const {gift_links: giftLinks} = await response.json();
const token = giftLinks[0].token;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Guard the gift-link API response before indexing.

response.json() result isn't checked for success or a non-empty gift_links array before accessing giftLinks[0].token. If the PUT fails or returns no links, the failure surfaces as a confusing undefined error deep in the helper rather than a clear assertion failure.

🛡️ Proposed fix
     const response = await page.request.put(`/ghost/api/admin/posts/${post.id}/gift_links`);
+    expect(response.ok()).toBeTruthy();
     const {gift_links: giftLinks} = await response.json();
+    expect(giftLinks.length).toBeGreaterThan(0);
     const token = giftLinks[0].token;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const response = await page.request.put(`/ghost/api/admin/posts/${post.id}/gift_links`);
const {gift_links: giftLinks} = await response.json();
const token = giftLinks[0].token;
const response = await page.request.put(`/ghost/api/admin/posts/${post.id}/gift_links`);
expect(response.ok()).toBeTruthy();
const {gift_links: giftLinks} = await response.json();
expect(giftLinks.length).toBeGreaterThan(0);
const token = giftLinks[0].token;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@e2e/tests/admin/analytics/gift-link-journey.test.ts` around lines 15 - 17,
The gift-link setup in the test currently assumes `page.request.put` succeeded
and that `gift_links` contains at least one item before reading
`giftLinks[0].token`. Update the `gift-link-journey.test.ts` flow to first
assert the PUT response is successful, then verify the parsed JSON contains a
non-empty `gift_links` array before indexing, so failures in this test surface
as clear assertions instead of an undefined access error.
ref https://linear.app/tryghost/issue/BER-3746

- the test minted the link over the admin API and only checked a badge, so it
  never exercised the path a user actually takes
- it now creates the link through the share modal's copy button (reading the
  URL back off the clipboard), reads the gifted post in a real browser, and
  asserts the visit surfaces on the post's gift-link analytics card as well as
  the web-traffic filter
- adds a data-testid for the card's visitor count and a clipboard-backed copy
  action on the overview page object; reloads (not hash re-nav) to refetch, the
  same guard the other analytics visit tests use

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5ccc9e3c8c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


await withIsolatedPage(browser, {baseURL}, async ({page: publicPage}) => {
const {pathname, search} = new URL(giftUrl);
await new PublicPage(publicPage).goto(`${pathname}${search}`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Run the journey test in the analytics project

In the context I checked, e2e/playwright.config.mjs puts only files matching tests/analytics/**/*.test.ts in the analytics project and the main project ignores only that same pattern, so this new tests/admin/analytics/... file runs under main. In that project PublicPage.goto does not enable synthetic analytics or wait for the page-hit beacon, so this gift-link visit is never reliably ingested before the later 1-visitor assertions; move the file under the analytics project's matched path or update the project globs.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
e2e/helpers/pages/admin/analytics/post-analytics/post-analytics-overview-page.ts (1)

8-9: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Declare assertion-facing locator as public readonly.

giftLinkCardVisitors is asserted against directly in the test (toHaveText('1')), but it's declared as bare readonly rather than public readonly. PublicPage already follows the explicit public readonly (assertion-facing) vs private readonly (internal-only) split for its locators — this new field breaks that convention.

✏️ Suggested fix
-    readonly giftLinkCardVisitors: Locator;
-    readonly copyGiftLinkButton: Locator;
+    public readonly giftLinkCardVisitors: Locator;
+    private readonly copyGiftLinkButton: Locator;

As per path instructions, "In Page Objects, expose locators as public readonly when used with assertions."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@e2e/helpers/pages/admin/analytics/post-analytics/post-analytics-overview-page.ts`
around lines 8 - 9, Update the locator declaration in PostAnalyticsOverviewPage
so the assertion-facing field giftLinkCardVisitors follows the PublicPage
convention and is marked public readonly instead of bare readonly. Keep
copyGiftLinkButton aligned with its intended visibility by leaving it private
only if it is internal, and ensure any locator used directly in tests remains
publicly exposed for assertions.

Source: Path instructions

e2e/tests/admin/analytics/gift-link-journey.test.ts (1)

35-61: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Consider consolidating the two scenarios into one test.

Both tests call the expensive createGiftLinkThenVisit helper (post creation, UI-driven mint through the share modal, and a full isolated-context visit with ingestion wait) independently, doubling that cost for what is one journey verified from two angles (gift-link card visitors, web-traffic filter). Based on learnings, grouping closely related scenarios sharing the same setup/outcome into a single test() is preferred in e2e/tests/admin/ given how expensive E2E setup is here.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@e2e/tests/admin/analytics/gift-link-journey.test.ts` around lines 35 - 61,
Consolidate the two Gift link analytics journey cases in
gift-link-journey.test.ts into a single test that reuses one
createGiftLinkThenVisit setup. Move the assertions for
overview.giftLinkCardVisitors and AnalyticsWebTrafficPage into the same test
flow, keeping the existing helper and page objects but avoiding a second
expensive journey setup.

Source: Learnings

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In
`@e2e/helpers/pages/admin/analytics/post-analytics/post-analytics-overview-page.ts`:
- Around line 8-9: Update the locator declaration in PostAnalyticsOverviewPage
so the assertion-facing field giftLinkCardVisitors follows the PublicPage
convention and is marked public readonly instead of bare readonly. Keep
copyGiftLinkButton aligned with its intended visibility by leaving it private
only if it is internal, and ensure any locator used directly in tests remains
publicly exposed for assertions.

In `@e2e/tests/admin/analytics/gift-link-journey.test.ts`:
- Around line 35-61: Consolidate the two Gift link analytics journey cases in
gift-link-journey.test.ts into a single test that reuses one
createGiftLinkThenVisit setup. Move the assertions for
overview.giftLinkCardVisitors and AnalyticsWebTrafficPage into the same test
flow, keeping the existing helper and page objects but avoiding a second
expensive journey setup.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: c2ddc5f4-5400-4167-81b8-4677408552a8

📥 Commits

Reviewing files that changed from the base of the PR and between 523448e and 5ccc9e3.

📒 Files selected for processing (3)
  • apps/posts/src/views/PostAnalytics/Overview/overview.tsx
  • e2e/helpers/pages/admin/analytics/post-analytics/post-analytics-overview-page.ts
  • e2e/tests/admin/analytics/gift-link-journey.test.ts
✅ Files skipped from review due to trivial changes (1)
  • apps/posts/src/views/PostAnalytics/Overview/overview.tsx
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant