Ghost/ghost/member-attribution/test/attribution.test.js
Rishabh c765c3230e Updated attribution service to handle referrer information
refs TryGhost/Team#1907

- calculates final attribution source and medium using captured referrer information in history
- adds new referrer-translator that goes through available history and based to determine most valid referrer info
- includes referrer url, source and medium in the attribution data for storage
2022-09-19 12:29:22 +05:30

257 lines
8.9 KiB
JavaScript

// Switch these lines once there are useful utils
// const testUtils = require('./utils');
require('./utils');
const UrlHistory = require('../lib/history');
const AttributionBuilder = require('../lib/attribution');
describe('AttributionBuilder', function () {
let attributionBuilder;
let urlTranslator;
let now;
before(function () {
now = Date.now();
urlTranslator = {
getResourceDetails(item) {
if (!item.path) {
if (item.id === 'invalid') {
return null;
}
return {
id: item.id,
type: item.type,
url: `/${item.type}/${item.id}`
};
}
const path = this.stripSubdirectoryFromPath(item.path);
if (path === '/my-post') {
return {
id: 123,
type: 'post',
url: path
};
}
if (path === '/my-page') {
return {
id: 845,
type: 'page',
url: path
};
}
return {
id: null,
type: 'url',
url: path
};
},
getResourceById(id, type) {
if (id === 'invalid') {
return null;
}
return {
id,
get(prop) {
if (prop === 'title' && type === 'author') {
// Simulate an author doesn't have a title
return undefined;
}
if (id === 'no-props') {
// Simulate a model without properties for branch coverage
return undefined;
}
return prop;
}
};
},
getUrlTitle(url) {
return url;
},
getUrlByResourceId() {
return 'https://absolute/dir/path';
},
relativeToAbsolute(path) {
return 'https://absolute/dir' + path;
},
stripSubdirectoryFromPath(path) {
if (path.startsWith('/dir/')) {
return path.substring('/dir/'.length - 1);
}
return path;
}
};
attributionBuilder = new AttributionBuilder({
urlTranslator,
referrerTranslator: {
getReferrerDetails(history) {
if (history) {
return {
refSource: 'Ghost Explore',
refMedium: 'Ghost Network',
refUrl: 'https://ghost.org/explore'
};
}
return null;
}
}
});
});
it('Returns empty if empty history', async function () {
const history = UrlHistory.create([]);
should(await attributionBuilder.getAttribution(history)).match({id: null, type: null, url: null, refSource: null, refMedium: null, refUrl: null});
});
it('Returns last url', async function () {
const history = UrlHistory.create([{path: '/dir/not-last', time: now + 123}, {path: '/dir/test/', time: now + 123}]);
should(await attributionBuilder.getAttribution(history)).match({type: 'url', id: null, url: '/test/'});
});
it('Returns last post', async function () {
const history = UrlHistory.create([
{path: '/dir/my-post', time: now + 123},
{path: '/dir/test', time: now + 124},
{path: '/dir/unknown-page', time: now + 125}
]);
should(await attributionBuilder.getAttribution(history)).match({type: 'post', id: 123, url: '/my-post'});
});
it('Returns last post even when it found pages', async function () {
const history = UrlHistory.create([
{path: '/dir/my-post', time: now + 123},
{path: '/dir/my-page', time: now + 124},
{path: '/dir/unknown-page', time: now + 125}
]);
should(await attributionBuilder.getAttribution(history)).match({type: 'post', id: 123, url: '/my-post'});
});
it('Returns last page if no posts', async function () {
const history = UrlHistory.create([
{path: '/dir/other', time: now + 123},
{path: '/dir/my-page', time: now + 124},
{path: '/dir/unknown-page', time: now + 125}
]);
should(await attributionBuilder.getAttribution(history)).match({type: 'page', id: 845, url: '/my-page'});
});
it('Returns last post via id', async function () {
const history = UrlHistory.create([
{path: '/dir/other', time: now + 123},
{id: '123', type: 'post', time: now + 124},
{path: '/dir/unknown-page', time: now + 125}
]);
should(await attributionBuilder.getAttribution(history)).match({type: 'post', id: '123', url: '/post/123'});
});
it('Returns referrer attribution', async function () {
const history = UrlHistory.create([
{path: '/dir/other', time: now + 123},
{id: '123', type: 'post', time: now + 124},
{path: '/dir/unknown-page', time: now + 125}
]);
should(await attributionBuilder.getAttribution(history)).match({
refSource: 'Ghost Explore',
refMedium: 'Ghost Network',
refUrl: 'https://ghost.org/explore'
});
});
it('Returns null referrer attribution', async function () {
attributionBuilder = new AttributionBuilder({
urlTranslator,
referrerTranslator: {
getReferrerDetails() {
return null;
}
}
});
const history = UrlHistory.create([
{path: '/dir/other', time: now + 123},
{id: '123', type: 'post', time: now + 124},
{path: '/dir/unknown-page', time: now + 125}
]);
should(await attributionBuilder.getAttribution(history)).match({
refSource: null,
refMedium: null,
refUrl: null
});
});
it('Returns all null if only invalid ids', async function () {
const history = UrlHistory.create([
{id: 'invalid', type: 'post', time: now + 124},
{id: 'invalid', type: 'post', time: now + 124}
]);
should(await attributionBuilder.getAttribution(history)).match({
type: null,
id: null,
url: null
});
});
it('Returns all null for invalid histories', async function () {
const history = UrlHistory.create('invalid');
should(await attributionBuilder.getAttribution(history)).match({
type: null,
id: null,
url: null
});
});
it('Returns all null for empty histories', async function () {
const history = UrlHistory.create([]);
should(await attributionBuilder.getAttribution(history)).match({
type: null,
id: null,
url: null
});
});
it('Returns post resource', async function () {
should(await attributionBuilder.build({type: 'post', id: '123', url: '/post'}).fetchResource()).match({
type: 'post',
id: '123',
url: 'https://absolute/dir/path',
title: 'title'
});
});
it('Returns author resource', async function () {
should(await attributionBuilder.build({type: 'author', id: '123', url: '/author'}).fetchResource()).match({
type: 'author',
id: '123',
url: 'https://absolute/dir/path',
title: 'name'
});
});
it('Returns default url title for resource if no title or name', async function () {
should(await attributionBuilder.build({type: 'post', id: 'no-props', url: '/post'}).fetchResource()).match({
type: 'post',
id: 'no-props',
url: 'https://absolute/dir/path',
title: '/post'
});
});
it('Returns url resource', async function () {
should(await attributionBuilder.build({type: 'url', id: null, url: '/url'}).fetchResource()).match({
type: 'url',
id: null,
url: 'https://absolute/dir/url',
title: '/url'
});
});
it('Returns url resource if not found', async function () {
should(await attributionBuilder.build({type: 'post', id: 'invalid', url: '/post'}).fetchResource()).match({
type: 'url',
id: null,
url: 'https://absolute/dir/post',
title: '/post'
});
});
});