mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-26 12:21:36 +03:00
00230314db
ref https://linear.app/tryghost/issue/ONC-154 - the query params did not carry through on portal sign up links because of the hash creating an ignored fragment (/#/portal/signup?ref=something) Now when we check link attribution, we'll attempt to run the same logic for the referrer source after stripping out `#/portal` from the URL. Otherwise we should continue to treat these fragments as fragments to be ignored by the client. NOTE: We do not have e2e tests that cover member signup on the front end and the data entered in the back end. The tests we have mock only the server side of things. The test added here only covers the data that is generated from the front end request (at this time), *not* the front end request itself, meaning it's fragile.
424 lines
17 KiB
JavaScript
424 lines
17 KiB
JavaScript
const {agentProvider, fixtureManager, configUtils} = require('../../utils/e2e-framework');
|
|
const should = require('should');
|
|
const models = require('../../../core/server/models');
|
|
const urlService = require('../../../core/server/services/url');
|
|
const memberAttributionService = require('../../../core/server/services/member-attribution');
|
|
const urlUtils = require('../../../core/shared/url-utils');
|
|
|
|
describe('Member Attribution Service', function () {
|
|
before(async function () {
|
|
await agentProvider.getAdminAPIAgent();
|
|
await fixtureManager.init('posts');
|
|
});
|
|
|
|
/**
|
|
* Test that getAttribution correctly resolves all model types that are supported
|
|
*/
|
|
describe('getAttribution for models', function () {
|
|
describe('without subdirectory', function () {
|
|
it('resolves urls', async function () {
|
|
const subdomainRelative = '/my-static-page/';
|
|
const url = urlUtils.createUrl(subdomainRelative, false);
|
|
const absoluteUrl = urlUtils.createUrl(subdomainRelative, true);
|
|
|
|
const attribution = await memberAttributionService.service.getAttribution([
|
|
{
|
|
path: url,
|
|
time: Date.now()
|
|
}
|
|
]);
|
|
attribution.should.match(({
|
|
id: null,
|
|
url: subdomainRelative,
|
|
type: 'url'
|
|
}));
|
|
|
|
(await attribution.fetchResource()).should.match(({
|
|
id: null,
|
|
url: absoluteUrl,
|
|
type: 'url',
|
|
title: subdomainRelative
|
|
}));
|
|
});
|
|
|
|
it('resolves posts', async function () {
|
|
const id = fixtureManager.get('posts', 0).id;
|
|
const post = await models.Post.where('id', id).fetch({require: true});
|
|
const url = urlService.getUrlByResourceId(post.id, {absolute: false, withSubdirectory: true});
|
|
|
|
const attribution = await memberAttributionService.service.getAttribution([
|
|
{
|
|
path: url,
|
|
time: Date.now()
|
|
}
|
|
]);
|
|
attribution.should.match(({
|
|
id: post.id,
|
|
url,
|
|
type: 'post'
|
|
}));
|
|
|
|
const absoluteUrl = urlService.getUrlByResourceId(post.id, {absolute: true, withSubdirectory: true});
|
|
|
|
(await attribution.fetchResource()).should.match(({
|
|
id: post.id,
|
|
url: absoluteUrl,
|
|
type: 'post',
|
|
title: post.get('title')
|
|
}));
|
|
});
|
|
|
|
it('resolves removed resources', async function () {
|
|
const id = fixtureManager.get('posts', 0).id;
|
|
const post = await models.Post.where('id', id).fetch({require: true});
|
|
const url = urlService.getUrlByResourceId(post.id, {absolute: false, withSubdirectory: true});
|
|
const urlWithoutSubdirectory = urlService.getUrlByResourceId(post.id, {absolute: false, withSubdirectory: false});
|
|
const absoluteUrl = urlService.getUrlByResourceId(post.id, {absolute: true, withSubdirectory: true});
|
|
|
|
const attribution = await memberAttributionService.service.getAttribution([
|
|
{
|
|
path: url,
|
|
time: Date.now()
|
|
}
|
|
]);
|
|
|
|
// Without subdirectory
|
|
attribution.should.match(({
|
|
id: post.id,
|
|
url: urlWithoutSubdirectory,
|
|
type: 'post'
|
|
}));
|
|
|
|
// Unpublish this post
|
|
await models.Post.edit({status: 'draft'}, {id});
|
|
|
|
(await attribution.fetchResource()).should.match(({
|
|
id: null,
|
|
url: absoluteUrl,
|
|
type: 'url',
|
|
title: urlWithoutSubdirectory
|
|
}));
|
|
|
|
await models.Post.edit({status: 'published'}, {id});
|
|
});
|
|
|
|
it('resolves pages', async function () {
|
|
const id = fixtureManager.get('posts', 5).id;
|
|
const post = await models.Post.where('id', id).fetch({require: true});
|
|
should(post.get('type')).eql('page');
|
|
|
|
const url = urlService.getUrlByResourceId(post.id, {absolute: false, withSubdirectory: true});
|
|
|
|
const attribution = await memberAttributionService.service.getAttribution([
|
|
{
|
|
path: url,
|
|
time: Date.now()
|
|
}
|
|
]);
|
|
attribution.should.match(({
|
|
id: post.id,
|
|
url,
|
|
type: 'page'
|
|
}));
|
|
|
|
const absoluteUrl = urlService.getUrlByResourceId(post.id, {absolute: true, withSubdirectory: true});
|
|
|
|
(await attribution.fetchResource()).should.match(({
|
|
id: post.id,
|
|
url: absoluteUrl,
|
|
type: 'page',
|
|
title: post.get('title')
|
|
}));
|
|
});
|
|
|
|
it('resolves tags', async function () {
|
|
const id = fixtureManager.get('tags', 0).id;
|
|
const tag = await models.Tag.where('id', id).fetch({require: true});
|
|
const url = urlService.getUrlByResourceId(tag.id, {absolute: false, withSubdirectory: true});
|
|
|
|
const attribution = await memberAttributionService.service.getAttribution([
|
|
{
|
|
path: url,
|
|
time: Date.now()
|
|
}
|
|
]);
|
|
attribution.should.match(({
|
|
id: tag.id,
|
|
url,
|
|
type: 'tag'
|
|
}));
|
|
|
|
const absoluteUrl = urlService.getUrlByResourceId(tag.id, {absolute: true, withSubdirectory: true});
|
|
|
|
(await attribution.fetchResource()).should.match(({
|
|
id: tag.id,
|
|
url: absoluteUrl,
|
|
type: 'tag',
|
|
title: tag.get('name')
|
|
}));
|
|
});
|
|
|
|
it('resolves authors', async function () {
|
|
const id = fixtureManager.get('users', 0).id;
|
|
const author = await models.User.where('id', id).fetch({require: true});
|
|
const url = urlService.getUrlByResourceId(author.id, {absolute: false, withSubdirectory: true});
|
|
|
|
const attribution = await memberAttributionService.service.getAttribution([
|
|
{
|
|
path: url,
|
|
time: Date.now()
|
|
}
|
|
]);
|
|
attribution.should.match(({
|
|
id: author.id,
|
|
url,
|
|
type: 'author'
|
|
}));
|
|
|
|
const absoluteUrl = urlService.getUrlByResourceId(author.id, {absolute: true, withSubdirectory: true});
|
|
|
|
(await attribution.fetchResource()).should.match(({
|
|
id: author.id,
|
|
url: absoluteUrl,
|
|
type: 'author',
|
|
title: author.get('name')
|
|
}));
|
|
});
|
|
});
|
|
|
|
describe('with subdirectory', function () {
|
|
beforeEach(function () {
|
|
configUtils.set('url', 'https://siteurl.com/subdirectory/');
|
|
});
|
|
|
|
afterEach(async function () {
|
|
await configUtils.restore();
|
|
});
|
|
|
|
it('resolves urls', async function () {
|
|
const subdomainRelative = '/my-static-page/';
|
|
const url = urlUtils.createUrl(subdomainRelative, false);
|
|
const absoluteUrl = urlUtils.createUrl(subdomainRelative, true);
|
|
|
|
const attribution = await memberAttributionService.service.getAttribution([
|
|
{
|
|
path: url,
|
|
time: Date.now()
|
|
}
|
|
]);
|
|
attribution.should.match(({
|
|
id: null,
|
|
url: subdomainRelative,
|
|
type: 'url'
|
|
}));
|
|
|
|
(await attribution.fetchResource()).should.match(({
|
|
id: null,
|
|
url: absoluteUrl,
|
|
type: 'url',
|
|
title: subdomainRelative
|
|
}));
|
|
});
|
|
|
|
it('resolves posts', async function () {
|
|
const id = fixtureManager.get('posts', 0).id;
|
|
const post = await models.Post.where('id', id).fetch({require: true});
|
|
const url = urlService.getUrlByResourceId(post.id, {absolute: false, withSubdirectory: true});
|
|
const urlWithoutSubdirectory = urlService.getUrlByResourceId(post.id, {absolute: false, withSubdirectory: false});
|
|
|
|
// Check if we are actually testing with subdirectories
|
|
should(url).startWith('/subdirectory/');
|
|
|
|
const attribution = await memberAttributionService.service.getAttribution([
|
|
{
|
|
path: url,
|
|
time: Date.now()
|
|
}
|
|
]);
|
|
|
|
// Without subdirectory
|
|
attribution.should.match(({
|
|
id: post.id,
|
|
url: urlWithoutSubdirectory,
|
|
type: 'post'
|
|
}));
|
|
|
|
const absoluteUrl = urlService.getUrlByResourceId(post.id, {absolute: true, withSubdirectory: true});
|
|
|
|
(await attribution.fetchResource()).should.match(({
|
|
id: post.id,
|
|
url: absoluteUrl,
|
|
type: 'post',
|
|
title: post.get('title')
|
|
}));
|
|
});
|
|
|
|
it('resolves removed resources', async function () {
|
|
const id = fixtureManager.get('posts', 0).id;
|
|
const post = await models.Post.where('id', id).fetch({require: true});
|
|
const url = urlService.getUrlByResourceId(post.id, {absolute: false, withSubdirectory: true});
|
|
const urlWithoutSubdirectory = urlService.getUrlByResourceId(post.id, {absolute: false, withSubdirectory: false});
|
|
const absoluteUrl = urlService.getUrlByResourceId(post.id, {absolute: true, withSubdirectory: true});
|
|
|
|
// Check if we are actually testing with subdirectories
|
|
should(url).startWith('/subdirectory/');
|
|
|
|
const attribution = await memberAttributionService.service.getAttribution([
|
|
{
|
|
path: url,
|
|
time: Date.now()
|
|
}
|
|
]);
|
|
|
|
// Without subdirectory
|
|
attribution.should.match(({
|
|
id: post.id,
|
|
url: urlWithoutSubdirectory,
|
|
type: 'post'
|
|
}));
|
|
|
|
// Unpublish this post
|
|
await models.Post.edit({status: 'draft'}, {id});
|
|
|
|
(await attribution.fetchResource()).should.match(({
|
|
id: null,
|
|
url: absoluteUrl,
|
|
type: 'url',
|
|
title: urlWithoutSubdirectory
|
|
}));
|
|
});
|
|
|
|
it('resolves pages', async function () {
|
|
const id = fixtureManager.get('posts', 5).id;
|
|
const post = await models.Post.where('id', id).fetch({require: true});
|
|
should(post.get('type')).eql('page');
|
|
|
|
const url = urlService.getUrlByResourceId(post.id, {absolute: false, withSubdirectory: true});
|
|
const urlWithoutSubdirectory = urlService.getUrlByResourceId(post.id, {absolute: false, withSubdirectory: false});
|
|
|
|
const attribution = await memberAttributionService.service.getAttribution([
|
|
{
|
|
path: url,
|
|
time: Date.now()
|
|
}
|
|
]);
|
|
attribution.should.match(({
|
|
id: post.id,
|
|
url: urlWithoutSubdirectory,
|
|
type: 'page'
|
|
}));
|
|
|
|
const absoluteUrl = urlService.getUrlByResourceId(post.id, {absolute: true, withSubdirectory: true});
|
|
|
|
(await attribution.fetchResource()).should.match(({
|
|
id: post.id,
|
|
url: absoluteUrl,
|
|
type: 'page',
|
|
title: post.get('title')
|
|
}));
|
|
});
|
|
|
|
it('resolves tags', async function () {
|
|
const id = fixtureManager.get('tags', 0).id;
|
|
const tag = await models.Tag.where('id', id).fetch({require: true});
|
|
const url = urlService.getUrlByResourceId(tag.id, {absolute: false, withSubdirectory: true});
|
|
const urlWithoutSubdirectory = urlService.getUrlByResourceId(tag.id, {absolute: false, withSubdirectory: false});
|
|
|
|
const attribution = await memberAttributionService.service.getAttribution([
|
|
{
|
|
path: url,
|
|
time: Date.now()
|
|
}
|
|
]);
|
|
attribution.should.match(({
|
|
id: tag.id,
|
|
url: urlWithoutSubdirectory,
|
|
type: 'tag'
|
|
}));
|
|
|
|
const absoluteUrl = urlService.getUrlByResourceId(tag.id, {absolute: true, withSubdirectory: true});
|
|
|
|
(await attribution.fetchResource()).should.match(({
|
|
id: tag.id,
|
|
url: absoluteUrl,
|
|
type: 'tag',
|
|
title: tag.get('name')
|
|
}));
|
|
});
|
|
|
|
it('resolves authors', async function () {
|
|
const id = fixtureManager.get('users', 0).id;
|
|
const author = await models.User.where('id', id).fetch({require: true});
|
|
const url = urlService.getUrlByResourceId(author.id, {absolute: false, withSubdirectory: true});
|
|
const urlWithoutSubdirectory = urlService.getUrlByResourceId(author.id, {absolute: false, withSubdirectory: false});
|
|
|
|
const attribution = await memberAttributionService.service.getAttribution([
|
|
{
|
|
path: url,
|
|
time: Date.now()
|
|
}
|
|
]);
|
|
attribution.should.match(({
|
|
id: author.id,
|
|
url: urlWithoutSubdirectory,
|
|
type: 'author'
|
|
}));
|
|
|
|
const absoluteUrl = urlService.getUrlByResourceId(author.id, {absolute: true, withSubdirectory: true});
|
|
|
|
(await attribution.fetchResource()).should.match(({
|
|
id: author.id,
|
|
url: absoluteUrl,
|
|
type: 'author',
|
|
title: author.get('name')
|
|
}));
|
|
});
|
|
});
|
|
});
|
|
|
|
/**
|
|
* Test that getAttribution correctly resolves all model types that are supported
|
|
*/
|
|
describe('getAttribution for referrer', function () {
|
|
it('resolves urls', async function () {
|
|
const attribution = await memberAttributionService.service.getAttribution([
|
|
{
|
|
id: null,
|
|
path: '/',
|
|
time: Date.now(),
|
|
referrerSource: 'ghost-explore',
|
|
referrerMedium: null,
|
|
referrerUrl: null
|
|
}
|
|
]);
|
|
attribution.should.match(({
|
|
id: null,
|
|
url: '/',
|
|
type: 'url',
|
|
referrerSource: 'Ghost Explore',
|
|
referrerMedium: 'Ghost Network',
|
|
referrerUrl: null
|
|
}));
|
|
});
|
|
|
|
it('resolves Portal signup URLs', async function () {
|
|
// NOTE: We cannot test the actual hash URL here; the attribution below is what is receieved when navigating to /#/portal/signup?ref=ghost
|
|
// TODO: We don't appear to have tests for parsing URLs for params.
|
|
const attribution = await memberAttributionService.service.getAttribution([
|
|
{
|
|
path: '/',
|
|
time: Date.now(),
|
|
referrerSource: 'casper'
|
|
}
|
|
]);
|
|
attribution.should.match(({
|
|
id: null,
|
|
url: '/',
|
|
type: 'url',
|
|
referrerSource: 'casper',
|
|
referrerMedium: null,
|
|
referrerUrl: null
|
|
}));
|
|
});
|
|
});
|
|
}); |