mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-19 00:11:49 +03:00
7a40ab52fb
refs https://linear.app/tryghost/issue/ENG-750 - when adding a recommendation, we fetch the recommended site's metadata - before this change, if the metadata fetch failed for some reason, we'd show an error and block the recommendation from being added - after this change, we use fallback values if the metadata fails to fetch, instead of blocking the recommendation from being added. We use the site domain as the title and leave the rest empty (no favicon, no description) - this change also means we are not checking whether a site exists or not for the publisher anymore. It’s then up to the publisher to make sure they don’t enter broken URLs
748 lines
30 KiB
JavaScript
748 lines
30 KiB
JavaScript
const {agentProvider, fixtureManager, mockManager, matchers} = require('../../utils/e2e-framework');
|
|
const {anyObjectId, anyErrorId, anyISODateTime, anyContentVersion, anyLocationFor, anyEtag} = matchers;
|
|
const assert = require('assert/strict');
|
|
const recommendationsService = require('../../../core/server/services/recommendations');
|
|
const {Recommendation, ClickEvent, SubscribeEvent} = require('@tryghost/recommendations');
|
|
const nock = require('nock');
|
|
|
|
async function addDummyRecommendation(i = 0) {
|
|
const recommendation = Recommendation.create({
|
|
title: `Recommendation ${i}`,
|
|
description: `Description ${i}`,
|
|
url: new URL(`https://recommendation${i}.com`),
|
|
favicon: new URL(`https://recommendation${i}.com/favicon.ico`),
|
|
featuredImage: new URL(`https://recommendation${i}.com/featured.jpg`),
|
|
excerpt: 'Test excerpt',
|
|
oneClickSubscribe: true,
|
|
createdAt: new Date(i * 5000) // Reliable ordering
|
|
});
|
|
|
|
await recommendationsService.repository.save(recommendation);
|
|
return recommendation.id;
|
|
}
|
|
|
|
async function addDummyRecommendations(amount = 15) {
|
|
// Add 15 recommendations using the repository
|
|
for (let i = 0; i < amount; i++) {
|
|
await addDummyRecommendation(i);
|
|
}
|
|
}
|
|
|
|
async function addClicksAndSubscribers({memberId}) {
|
|
const recommendations = await recommendationsService.repository.getAll({order: [{field: 'createdAt', direction: 'desc'}]});
|
|
|
|
// Create 2 clicks for 1st
|
|
for (let i = 0; i < 2; i++) {
|
|
const clickEvent = ClickEvent.create({
|
|
recommendationId: recommendations[0].id
|
|
});
|
|
|
|
await recommendationsService.clickEventRepository.save(clickEvent);
|
|
}
|
|
|
|
// Create 3 clicks for 2nd
|
|
for (let i = 0; i < 3; i++) {
|
|
const clickEvent = ClickEvent.create({
|
|
recommendationId: recommendations[1].id
|
|
});
|
|
|
|
await recommendationsService.clickEventRepository.save(clickEvent);
|
|
}
|
|
|
|
// Create 3 subscribers for 1st
|
|
for (let i = 0; i < 3; i++) {
|
|
const subscribeEvent = SubscribeEvent.create({
|
|
recommendationId: recommendations[0].id,
|
|
memberId
|
|
});
|
|
|
|
await recommendationsService.subscribeEventRepository.save(subscribeEvent);
|
|
}
|
|
|
|
// Create 2 subscribers for 3rd
|
|
for (let i = 0; i < 2; i++) {
|
|
const subscribeEvent = SubscribeEvent.create({
|
|
recommendationId: recommendations[2].id,
|
|
memberId
|
|
});
|
|
|
|
await recommendationsService.subscribeEventRepository.save(subscribeEvent);
|
|
}
|
|
}
|
|
|
|
describe('Recommendations Admin API', function () {
|
|
let agent, memberId;
|
|
|
|
before(async function () {
|
|
agent = await agentProvider.getAdminAPIAgent();
|
|
await fixtureManager.init('posts', 'members');
|
|
await agent.loginAsOwner();
|
|
|
|
memberId = fixtureManager.get('members', 0).id;
|
|
});
|
|
|
|
afterEach(async function () {
|
|
for (const recommendation of (await recommendationsService.repository.getAll())) {
|
|
recommendation.delete();
|
|
await recommendationsService.repository.save(recommendation);
|
|
}
|
|
mockManager.restore();
|
|
});
|
|
|
|
describe('browse', function () {
|
|
it('Can browse', async function () {
|
|
await addDummyRecommendation();
|
|
|
|
await agent.get('recommendations/')
|
|
.expectStatus(200)
|
|
.matchHeaderSnapshot({
|
|
'content-version': anyContentVersion,
|
|
etag: anyEtag
|
|
})
|
|
.matchBodySnapshot({
|
|
recommendations: [
|
|
{
|
|
id: anyObjectId,
|
|
created_at: anyISODateTime,
|
|
updated_at: anyISODateTime
|
|
}
|
|
]
|
|
});
|
|
});
|
|
|
|
it('Can request pages', async function () {
|
|
// Add 15 recommendations using the repository
|
|
await addDummyRecommendations(15);
|
|
|
|
const {body: page1} = await agent.get('recommendations/?page=1&limit=10')
|
|
.expectStatus(200)
|
|
.matchHeaderSnapshot({
|
|
'content-version': anyContentVersion,
|
|
etag: anyEtag
|
|
})
|
|
.matchBodySnapshot({
|
|
recommendations: new Array(10).fill({
|
|
id: anyObjectId,
|
|
created_at: anyISODateTime,
|
|
updated_at: anyISODateTime
|
|
})
|
|
});
|
|
|
|
assert.equal(page1.meta.pagination.page, 1);
|
|
assert.equal(page1.meta.pagination.limit, 10);
|
|
assert.equal(page1.meta.pagination.pages, 2);
|
|
assert.equal(page1.meta.pagination.next, 2);
|
|
assert.equal(page1.meta.pagination.prev, null);
|
|
assert.equal(page1.meta.pagination.total, 15);
|
|
|
|
const {body: page2} = await agent.get('recommendations/?page=2&limit=10')
|
|
.expectStatus(200)
|
|
.matchHeaderSnapshot({
|
|
'content-version': anyContentVersion,
|
|
etag: anyEtag
|
|
})
|
|
.matchBodySnapshot({
|
|
recommendations: new Array(5).fill({
|
|
id: anyObjectId,
|
|
created_at: anyISODateTime,
|
|
updated_at: anyISODateTime
|
|
})
|
|
});
|
|
|
|
assert.equal(page2.meta.pagination.page, 2);
|
|
assert.equal(page2.meta.pagination.limit, 10);
|
|
assert.equal(page2.meta.pagination.pages, 2);
|
|
assert.equal(page2.meta.pagination.next, null);
|
|
assert.equal(page2.meta.pagination.prev, 1);
|
|
assert.equal(page2.meta.pagination.total, 15);
|
|
});
|
|
|
|
it('Uses default limit of 5', async function () {
|
|
await addDummyRecommendations(6);
|
|
const {body: page1} = await agent.get('recommendations/')
|
|
.expectStatus(200)
|
|
.matchHeaderSnapshot({
|
|
'content-version': anyContentVersion,
|
|
etag: anyEtag
|
|
});
|
|
|
|
assert.equal(page1.meta.pagination.limit, 5);
|
|
});
|
|
|
|
it('Can include click and subscribe counts', async function () {
|
|
await addDummyRecommendations(5);
|
|
await addClicksAndSubscribers({memberId});
|
|
|
|
const {body: page1} = await agent.get('recommendations/?include=count.clicks,count.subscribers')
|
|
.expectStatus(200)
|
|
.matchHeaderSnapshot({
|
|
'content-version': anyContentVersion,
|
|
etag: anyEtag
|
|
})
|
|
.matchBodySnapshot({
|
|
recommendations: new Array(5).fill({
|
|
id: anyObjectId,
|
|
created_at: anyISODateTime,
|
|
updated_at: anyISODateTime
|
|
})
|
|
});
|
|
|
|
assert.equal(page1.recommendations[0].count.clicks, 2);
|
|
assert.equal(page1.recommendations[1].count.clicks, 3);
|
|
|
|
assert.equal(page1.recommendations[0].count.subscribers, 3);
|
|
assert.equal(page1.recommendations[1].count.subscribers, 0);
|
|
assert.equal(page1.recommendations[2].count.subscribers, 2);
|
|
});
|
|
|
|
it('Can include only clicks', async function () {
|
|
await addDummyRecommendations(5);
|
|
await addClicksAndSubscribers({memberId});
|
|
|
|
const {body: page1} = await agent.get('recommendations/?include=count.clicks')
|
|
.expectStatus(200)
|
|
.matchHeaderSnapshot({
|
|
'content-version': anyContentVersion,
|
|
etag: anyEtag
|
|
})
|
|
.matchBodySnapshot({
|
|
recommendations: new Array(5).fill({
|
|
id: anyObjectId,
|
|
created_at: anyISODateTime,
|
|
updated_at: anyISODateTime
|
|
})
|
|
});
|
|
|
|
assert.equal(page1.recommendations[0].count.clicks, 2);
|
|
assert.equal(page1.recommendations[1].count.clicks, 3);
|
|
|
|
assert.equal(page1.recommendations[0].count.subscribers, undefined);
|
|
assert.equal(page1.recommendations[1].count.subscribers, undefined);
|
|
assert.equal(page1.recommendations[2].count.subscribers, undefined);
|
|
});
|
|
|
|
it('Can include only subscribers', async function () {
|
|
await addDummyRecommendations(5);
|
|
await addClicksAndSubscribers({memberId});
|
|
|
|
const {body: page1} = await agent.get('recommendations/?include=count.subscribers')
|
|
.expectStatus(200)
|
|
.matchHeaderSnapshot({
|
|
'content-version': anyContentVersion,
|
|
etag: anyEtag
|
|
})
|
|
.matchBodySnapshot({
|
|
recommendations: new Array(5).fill({
|
|
id: anyObjectId,
|
|
created_at: anyISODateTime,
|
|
updated_at: anyISODateTime
|
|
})
|
|
});
|
|
|
|
assert.equal(page1.recommendations[0].count.clicks, undefined);
|
|
assert.equal(page1.recommendations[1].count.clicks, undefined);
|
|
|
|
assert.equal(page1.recommendations[0].count.subscribers, 3);
|
|
assert.equal(page1.recommendations[1].count.subscribers, 0);
|
|
assert.equal(page1.recommendations[2].count.subscribers, 2);
|
|
});
|
|
|
|
it('Can include click and subscribe counts and order by clicks+subscribe count', async function () {
|
|
await addDummyRecommendations(5);
|
|
await addClicksAndSubscribers({memberId});
|
|
|
|
const {body: page1} = await agent.get('recommendations/?include=count.clicks,count.subscribers&order=' + encodeURIComponent('count.clicks desc, count.subscribers asc'))
|
|
.expectStatus(200)
|
|
.matchHeaderSnapshot({
|
|
'content-version': anyContentVersion,
|
|
etag: anyEtag
|
|
})
|
|
.matchBodySnapshot({
|
|
recommendations: new Array(5).fill({
|
|
id: anyObjectId,
|
|
created_at: anyISODateTime,
|
|
updated_at: anyISODateTime
|
|
})
|
|
});
|
|
|
|
assert.equal(page1.recommendations[0].count.clicks, 3);
|
|
assert.equal(page1.recommendations[1].count.clicks, 2);
|
|
|
|
assert.equal(page1.recommendations[0].count.subscribers, 0);
|
|
assert.equal(page1.recommendations[1].count.subscribers, 3);
|
|
assert.equal(page1.recommendations[2].count.subscribers, 0);
|
|
});
|
|
|
|
it('Can order by click and subscribe counts and they will be included by default', async function () {
|
|
await addDummyRecommendations(5);
|
|
await addClicksAndSubscribers({memberId});
|
|
|
|
const {body: page1} = await agent.get('recommendations/?order=' + encodeURIComponent('count.clicks desc, count.subscribers asc'))
|
|
.expectStatus(200)
|
|
.matchHeaderSnapshot({
|
|
'content-version': anyContentVersion,
|
|
etag: anyEtag
|
|
})
|
|
.matchBodySnapshot({
|
|
recommendations: new Array(5).fill({
|
|
id: anyObjectId,
|
|
created_at: anyISODateTime,
|
|
updated_at: anyISODateTime
|
|
})
|
|
});
|
|
|
|
assert.equal(page1.recommendations[0].count.clicks, 3);
|
|
assert.equal(page1.recommendations[1].count.clicks, 2);
|
|
|
|
assert.equal(page1.recommendations[0].count.subscribers, 0);
|
|
assert.equal(page1.recommendations[1].count.subscribers, 3);
|
|
assert.equal(page1.recommendations[2].count.subscribers, 0);
|
|
});
|
|
|
|
it('Can fetch recommendations with relations when there are no recommendations', async function () {
|
|
const recommendations = await recommendationsService.repository.getCount();
|
|
assert.equal(recommendations, 0, 'This test expects there to be no recommendations');
|
|
|
|
const {body: page1} = await agent.get('recommendations/?include=count.clicks,count.subscribers')
|
|
.expectStatus(200)
|
|
.matchHeaderSnapshot({
|
|
'content-version': anyContentVersion,
|
|
etag: anyEtag
|
|
})
|
|
.matchBodySnapshot({});
|
|
|
|
assert.equal(page1.recommendations.length, 0);
|
|
});
|
|
|
|
it('can fetch recommendations filtered by an exact title', async function () {
|
|
await addDummyRecommendations(5);
|
|
|
|
const {body} = await agent.get(`recommendations/?filter=title:'Recommendation 1'`)
|
|
.expectStatus(200);
|
|
|
|
assert.equal(body.recommendations.length, 1);
|
|
assert.equal(body.recommendations[0].title, 'Recommendation 1');
|
|
});
|
|
|
|
it('can fetch recommendations filtered by a partial URL', async function () {
|
|
await addDummyRecommendations(5);
|
|
|
|
const {body} = await agent.get(`recommendations/?filter=url:~'recommendation1.com'`)
|
|
.expectStatus(200);
|
|
|
|
assert.equal(body.recommendations.length, 1);
|
|
assert.equal(body.recommendations[0].url, 'https://recommendation1.com/');
|
|
});
|
|
});
|
|
|
|
describe('read', function () {
|
|
it('can get a recommendation by ID', async function () {
|
|
const id = await addDummyRecommendation(1);
|
|
const {body} = await agent.get(`recommendations/${id}/`)
|
|
.expectStatus(200)
|
|
.matchHeaderSnapshot({
|
|
'content-version': anyContentVersion,
|
|
etag: anyEtag
|
|
})
|
|
.matchBodySnapshot({
|
|
recommendations: [
|
|
{
|
|
id: anyObjectId,
|
|
created_at: anyISODateTime,
|
|
updated_at: anyISODateTime
|
|
}
|
|
]
|
|
});
|
|
|
|
// Check data
|
|
assert.equal(body.recommendations[0].id, id);
|
|
assert.equal(body.recommendations[0].title, 'Recommendation 1');
|
|
assert.equal(body.recommendations[0].url, 'https://recommendation1.com/');
|
|
assert.equal(body.recommendations[0].description, 'Description 1');
|
|
assert.equal(body.recommendations[0].excerpt, 'Test excerpt');
|
|
assert.equal(body.recommendations[0].featured_image, 'https://recommendation1.com/featured.jpg');
|
|
assert.equal(body.recommendations[0].favicon, 'https://recommendation1.com/favicon.ico');
|
|
assert.equal(body.recommendations[0].one_click_subscribe, true);
|
|
});
|
|
|
|
it('returns an empty array when the recommendation is not found', async function () {
|
|
const id = 'i-dont-exist';
|
|
const {body} = await agent.get(`recommendations/${id}/`)
|
|
.expectStatus(422)
|
|
.matchHeaderSnapshot({
|
|
'content-version': anyContentVersion,
|
|
etag: anyEtag
|
|
})
|
|
.matchBodySnapshot({
|
|
errors: [
|
|
{
|
|
id: anyErrorId
|
|
}
|
|
]
|
|
});
|
|
|
|
assert.equal(body.errors[0].type, 'ValidationError');
|
|
assert.equal(body.errors[0].message, 'Validation error, cannot read recommendation.');
|
|
});
|
|
});
|
|
|
|
describe('edit', function () {
|
|
it('Can edit recommendation', async function () {
|
|
const id = await addDummyRecommendation();
|
|
const {body} = await agent.put(`recommendations/${id}/`)
|
|
.body({
|
|
recommendations: [{
|
|
title: 'Cat Pictures',
|
|
url: 'https://dogpictures.com',
|
|
description: 'Because cats are cute',
|
|
excerpt: 'Cats are cute',
|
|
featured_image: 'https://catpictures.com/cat.jpg',
|
|
favicon: 'https://catpictures.com/favicon.ico',
|
|
one_click_subscribe: false
|
|
}]
|
|
})
|
|
.expectStatus(200)
|
|
.matchHeaderSnapshot({
|
|
'content-version': anyContentVersion,
|
|
etag: anyEtag
|
|
})
|
|
.matchBodySnapshot({
|
|
recommendations: [
|
|
{
|
|
id: anyObjectId,
|
|
created_at: anyISODateTime,
|
|
updated_at: anyISODateTime
|
|
}
|
|
]
|
|
});
|
|
|
|
// Check everything is set correctly
|
|
assert.equal(body.recommendations[0].id, id);
|
|
assert.equal(body.recommendations[0].title, 'Cat Pictures');
|
|
assert.equal(body.recommendations[0].url, 'https://dogpictures.com/');
|
|
assert.equal(body.recommendations[0].description, 'Because cats are cute');
|
|
assert.equal(body.recommendations[0].excerpt, 'Cats are cute');
|
|
assert.equal(body.recommendations[0].featured_image, 'https://catpictures.com/cat.jpg');
|
|
assert.equal(body.recommendations[0].favicon, 'https://catpictures.com/favicon.ico');
|
|
assert.equal(body.recommendations[0].one_click_subscribe, false);
|
|
});
|
|
|
|
it('Can edit recommendation and set nullable fields to null', async function () {
|
|
const id = await addDummyRecommendation();
|
|
const {body} = await agent.put(`recommendations/${id}/`)
|
|
.body({
|
|
recommendations: [{
|
|
description: null,
|
|
excerpt: null,
|
|
featured_image: null,
|
|
favicon: null
|
|
}]
|
|
})
|
|
.expectStatus(200)
|
|
.matchHeaderSnapshot({
|
|
'content-version': anyContentVersion,
|
|
etag: anyEtag
|
|
})
|
|
.matchBodySnapshot({
|
|
recommendations: [
|
|
{
|
|
id: anyObjectId,
|
|
created_at: anyISODateTime,
|
|
updated_at: anyISODateTime
|
|
}
|
|
]
|
|
});
|
|
|
|
// Check everything is set correctly
|
|
assert.equal(body.recommendations[0].id, id);
|
|
assert.equal(body.recommendations[0].description, null);
|
|
assert.equal(body.recommendations[0].excerpt, null);
|
|
assert.equal(body.recommendations[0].featured_image, null);
|
|
assert.equal(body.recommendations[0].favicon, null);
|
|
});
|
|
|
|
it('Can edit some fields of a recommendation without changing others', async function () {
|
|
const id = await addDummyRecommendation();
|
|
const {body} = await agent.put(`recommendations/${id}/`)
|
|
.body({
|
|
recommendations: [{
|
|
title: 'Changed'
|
|
}]
|
|
})
|
|
.expectStatus(200)
|
|
.matchHeaderSnapshot({
|
|
'content-version': anyContentVersion,
|
|
etag: anyEtag
|
|
})
|
|
.matchBodySnapshot({
|
|
recommendations: [
|
|
{
|
|
id: anyObjectId,
|
|
created_at: anyISODateTime,
|
|
updated_at: anyISODateTime
|
|
}
|
|
]
|
|
});
|
|
|
|
// Check everything is set correctly
|
|
assert.equal(body.recommendations[0].id, id);
|
|
assert.equal(body.recommendations[0].title, 'Changed');
|
|
assert.equal(body.recommendations[0].url, 'https://recommendation0.com/');
|
|
assert.equal(body.recommendations[0].description, 'Description 0');
|
|
assert.equal(body.recommendations[0].excerpt, 'Test excerpt');
|
|
assert.equal(body.recommendations[0].featured_image, 'https://recommendation0.com/featured.jpg');
|
|
assert.equal(body.recommendations[0].favicon, 'https://recommendation0.com/favicon.ico');
|
|
assert.equal(body.recommendations[0].one_click_subscribe, true);
|
|
});
|
|
|
|
it('Cannot use invalid protocols when editing', async function () {
|
|
const id = await addDummyRecommendation();
|
|
|
|
await agent.put(`recommendations/${id}/`)
|
|
.body({
|
|
recommendations: [{
|
|
title: 'Cat Pictures',
|
|
url: 'https://dogpictures.com',
|
|
description: 'Because cats are cute',
|
|
excerpt: 'Cats are cute',
|
|
featured_image: 'ftp://dogpictures.com/dog.jpg',
|
|
favicon: 'ftp://dogpictures.com/favicon.ico',
|
|
one_click_subscribe: false
|
|
}]
|
|
})
|
|
.expectStatus(422)
|
|
.matchHeaderSnapshot({
|
|
'content-version': anyContentVersion,
|
|
etag: anyEtag
|
|
})
|
|
.matchBodySnapshot({
|
|
errors: [
|
|
{
|
|
id: anyErrorId
|
|
}
|
|
]
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('add', function () {
|
|
it('Can add a minimal recommendation', async function () {
|
|
const {body} = await agent.post('recommendations/')
|
|
.body({
|
|
recommendations: [{
|
|
title: 'Dog Pictures',
|
|
url: 'https://dogpictures.com'
|
|
}]
|
|
})
|
|
.expectStatus(201)
|
|
.matchHeaderSnapshot({
|
|
'content-version': anyContentVersion,
|
|
etag: anyEtag,
|
|
location: anyLocationFor('recommendations')
|
|
})
|
|
.matchBodySnapshot({
|
|
recommendations: [
|
|
{
|
|
id: anyObjectId,
|
|
created_at: anyISODateTime
|
|
}
|
|
]
|
|
});
|
|
|
|
// Check everything is set correctly
|
|
assert.equal(body.recommendations[0].title, 'Dog Pictures');
|
|
assert.equal(body.recommendations[0].url, 'https://dogpictures.com/');
|
|
assert.equal(body.recommendations[0].description, null);
|
|
assert.equal(body.recommendations[0].excerpt, null);
|
|
assert.equal(body.recommendations[0].featured_image, null);
|
|
assert.equal(body.recommendations[0].favicon, null);
|
|
assert.equal(body.recommendations[0].one_click_subscribe, false);
|
|
});
|
|
|
|
it('Can add a full recommendation', async function () {
|
|
const {body} = await agent.post('recommendations/')
|
|
.body({
|
|
recommendations: [{
|
|
title: 'Dog Pictures',
|
|
url: 'https://dogpictures.com',
|
|
description: 'Because dogs are cute',
|
|
excerpt: 'Dogs are cute',
|
|
featured_image: 'https://dogpictures.com/dog.jpg',
|
|
favicon: 'https://dogpictures.com/favicon.ico',
|
|
one_click_subscribe: true
|
|
}]
|
|
})
|
|
.expectStatus(201)
|
|
.matchHeaderSnapshot({
|
|
'content-version': anyContentVersion,
|
|
etag: anyEtag,
|
|
location: anyLocationFor('recommendations')
|
|
})
|
|
.matchBodySnapshot({
|
|
recommendations: [
|
|
{
|
|
id: anyObjectId,
|
|
created_at: anyISODateTime
|
|
}
|
|
]
|
|
});
|
|
|
|
// Check everything is set correctly
|
|
assert.equal(body.recommendations[0].title, 'Dog Pictures');
|
|
assert.equal(body.recommendations[0].url, 'https://dogpictures.com/');
|
|
assert.equal(body.recommendations[0].description, 'Because dogs are cute');
|
|
assert.equal(body.recommendations[0].excerpt, 'Dogs are cute');
|
|
assert.equal(body.recommendations[0].featured_image, 'https://dogpictures.com/dog.jpg');
|
|
assert.equal(body.recommendations[0].favicon, 'https://dogpictures.com/favicon.ico');
|
|
assert.equal(body.recommendations[0].one_click_subscribe, true);
|
|
});
|
|
|
|
it('Can add a recommendation with the same hostname but different paths', async function () {
|
|
// Add a recommendation with URL https://recommendation3.com
|
|
await addDummyRecommendation(3);
|
|
|
|
await agent.post('recommendations/')
|
|
.body({
|
|
recommendations: [{
|
|
title: 'Recommendation 3 with a different path',
|
|
url: 'https://recommendation3.com/path-1'
|
|
}]
|
|
})
|
|
.expectStatus(201)
|
|
.matchHeaderSnapshot({
|
|
'content-version': anyContentVersion,
|
|
etag: anyEtag,
|
|
location: anyLocationFor('recommendations')
|
|
})
|
|
.matchBodySnapshot({
|
|
recommendations: [
|
|
{
|
|
id: anyObjectId,
|
|
created_at: anyISODateTime
|
|
}
|
|
]
|
|
});
|
|
});
|
|
|
|
it('Cannot add the same recommendation URL twice (exact URL match)', async function () {
|
|
// Add a recommendation with URL https://recommendation3.com
|
|
await addDummyRecommendation(3);
|
|
|
|
await agent.post('recommendations/')
|
|
.body({
|
|
recommendations: [{
|
|
title: 'Recommendation 3 with the exact same URL',
|
|
url: 'https://recommendation3.com'
|
|
}]
|
|
})
|
|
.expectStatus(422)
|
|
.matchBodySnapshot({
|
|
errors: [
|
|
{
|
|
id: anyErrorId
|
|
}
|
|
]
|
|
});
|
|
});
|
|
|
|
it('Cannot add the same recommendation twice (partial URL match)', async function () {
|
|
// Add a recommendation with URL https://recommendation3.com
|
|
await addDummyRecommendation(3);
|
|
|
|
await agent.post('recommendations/')
|
|
.body({
|
|
recommendations: [{
|
|
title: 'Recommendation 3 with the same hostname and pathname, but with different protocol, www, query params and hash fragement',
|
|
url: 'http://www.recommendation3.com/?query=1#hash'
|
|
}]
|
|
})
|
|
.expectStatus(422)
|
|
.matchBodySnapshot({
|
|
errors: [
|
|
{
|
|
id: anyErrorId
|
|
}
|
|
]
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('check', function () {
|
|
it('Can check a recommendation url', async function () {
|
|
nock('https://dogpictures.com')
|
|
.get('/members/api/site')
|
|
.reply(200, {
|
|
site: {
|
|
title: 'Dog Pictures',
|
|
description: 'Because dogs are cute',
|
|
cover_image: 'https://dogpictures.com/dog.jpg',
|
|
icon: 'https://dogpictures.com/favicon.ico',
|
|
allow_external_signup: true
|
|
}
|
|
});
|
|
|
|
const {body} = await agent.post('recommendations/check/')
|
|
.body({
|
|
recommendations: [{
|
|
url: 'https://dogpictures.com'
|
|
}]
|
|
})
|
|
.expectStatus(200)
|
|
.matchHeaderSnapshot({
|
|
'content-version': anyContentVersion,
|
|
etag: anyEtag
|
|
})
|
|
.matchBodySnapshot({});
|
|
|
|
// Check everything is set correctly
|
|
assert.equal(body.recommendations[0].title, 'Dog Pictures');
|
|
assert.equal(body.recommendations[0].url, 'https://dogpictures.com/');
|
|
assert.equal(body.recommendations[0].description, null);
|
|
assert.equal(body.recommendations[0].excerpt, 'Because dogs are cute');
|
|
assert.equal(body.recommendations[0].featured_image, 'https://dogpictures.com/dog.jpg');
|
|
assert.equal(body.recommendations[0].favicon, 'https://dogpictures.com/favicon.ico');
|
|
assert.equal(body.recommendations[0].one_click_subscribe, true);
|
|
});
|
|
|
|
it('Returns nullified values if site fails to fetch', async function () {
|
|
nock('https://dogpictures.com')
|
|
.get('/')
|
|
.reply(404);
|
|
|
|
const {body} = await agent.post('recommendations/check/')
|
|
.body({
|
|
recommendations: [{
|
|
url: 'https://dogpictures.com'
|
|
}]
|
|
})
|
|
.expectStatus(200)
|
|
.matchHeaderSnapshot({
|
|
'content-version': anyContentVersion,
|
|
etag: anyEtag
|
|
})
|
|
.matchBodySnapshot({});
|
|
|
|
assert.equal(body.recommendations[0].title, null);
|
|
assert.equal(body.recommendations[0].url, 'https://dogpictures.com/');
|
|
assert.equal(body.recommendations[0].description, null);
|
|
assert.equal(body.recommendations[0].excerpt, null);
|
|
assert.equal(body.recommendations[0].featured_image, null);
|
|
assert.equal(body.recommendations[0].favicon, null);
|
|
assert.equal(body.recommendations[0].one_click_subscribe, false);
|
|
});
|
|
});
|
|
|
|
describe('delete', function () {
|
|
it('Can delete recommendation', async function () {
|
|
const id = await addDummyRecommendation();
|
|
await agent.delete(`recommendations/${id}/`)
|
|
.expectStatus(204)
|
|
.matchHeaderSnapshot({
|
|
'content-version': anyContentVersion,
|
|
etag: anyEtag
|
|
})
|
|
.matchBodySnapshot({});
|
|
});
|
|
});
|
|
});
|