Skip to content

Commit

Permalink
Added full flow test for click tracking (#15546)
Browse files Browse the repository at this point in the history
refs TryGhost/Product#1967

This tests the full flow of publishing a newsletter, and then checking
that clicked links will increase the click count, generate events for
the member which clicked the link as well as the redirects contain the
correct query params.
  • Loading branch information
allouis authored Oct 10, 2022
1 parent 4ca74c0 commit dc8617a
Showing 1 changed file with 114 additions and 0 deletions.
114 changes: 114 additions & 0 deletions ghost/core/test/e2e-server/click-tracking.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
const assert = require('assert');
const fetch = require('node-fetch').default;
const {agentProvider, mockManager, fixtureManager} = require('../utils/e2e-framework');
const urlUtils = require('../../core/shared/url-utils');

describe('Click Tracking', function () {
let agent;

before(async function () {
agent = await agentProvider.getAdminAPIAgent();
await fixtureManager.init('newsletters', 'members:newsletters');
await agent.loginAsOwner();
});

beforeEach(function () {
mockManager.mockMail();
});

afterEach(function () {
mockManager.restore();
});

it('Full test', async function () {
const {body: {posts: [draft]}} = await agent.post('/posts/', {
body: {
posts: [{
title: 'My Newsletter'
}]
}
});

const newsletterSlug = fixtureManager.get('newsletters', 0).slug;
const {body: {posts: [post]}} = await agent.put(
`/posts/${draft.id}/?newsletter=${newsletterSlug}`,
{
body: {
posts: [{
updated_at: draft.updated_at,
status: 'published'
}]
}
}
);

const {body: {links}} = await agent.get(
`/links/?filter=post_id:${post.id}`
);

/** @type {(url: string) => Promise<import('node-fetch').Response>} */
const fetchWithoutFollowingRedirect = url => fetch(url, {redirect: 'manual'});

const siteUrl = new URL(urlUtils.urlFor('home', true));

let internalRedirectHappened = false;
let externalRedirectHappened = false;
for (const link of links) {
const res = await fetchWithoutFollowingRedirect(link.link.from);
const redirectedToUrl = new URL(res.headers.get('location'));

// startsWith is a little dirty, but we need this because siteUrl
// can have a path when Ghost is hosted on a subdomain.
const isInternal = redirectedToUrl.href.startsWith(siteUrl.href);

if (isInternal) {
internalRedirectHappened = true;

assert(redirectedToUrl.searchParams.get('attribution_id'), 'attribution_id should be present on internal redirects');
assert(redirectedToUrl.searchParams.get('attribution_type'), 'attribution_type should be present on internal redirects');
} else {
externalRedirectHappened = true;

assert(!redirectedToUrl.searchParams.get('attribution_id'), 'attribution_id should not be present on internal redirects');
assert(!redirectedToUrl.searchParams.get('attribution_type'), 'attribution_type should not be present on internal redirects');
}

assert(redirectedToUrl.searchParams.get('ref'), 'ref should be present on all redirects');
}

assert(internalRedirectHappened);
assert(externalRedirectHappened);

const {body: {members}} = await agent.get(
`/members/`
);

const linkToClick = links[0];
const memberToClickLink = members[0];

const urlOfLinkToClick = new URL(linkToClick.link.from);

urlOfLinkToClick.searchParams.set('m', memberToClickLink.uuid);

const previousClickCount = linkToClick.count.clicks;

await fetchWithoutFollowingRedirect(urlOfLinkToClick.href);

const {body: {links: [clickedLink]}} = await agent.get(
`/links/?filter=post_id:${post.id}`
);

const clickCount = clickedLink.count.clicks;

const {body: {events: clickEvents}} = await agent.get(
`/members/events/?filter=data.member_id:${memberToClickLink.id}${encodeURIComponent('+')}type:click_event`
);

const clickEvent = clickEvents.find((/** @type any */ event) => {
return event.data.post.id === post.id && event.data.link.from === urlOfLinkToClick.pathname;
});

assert(clickEvent);
assert(previousClickCount + 1 === clickCount);
});
});

0 comments on commit dc8617a

Please sign in to comment.