Ghost/ghost/core/test/e2e-api/admin/collections.test.js
Naz 53f9f954c1 Added tag filter support to collections
refs https://github.com/TryGhost/Arch/issues/41

- When an new collection is created the relational "tags" filter is now picked up properly and appropriate posts matching the tag filter are assigned and stored in the collection. Example collection filter that is now supported: `tags:['bacon']`
- Additionally cleaned up returned collection post DTOs, so we return as little data as possible and add only the fields that are needed
2023-07-18 20:18:54 +08:00

415 lines
13 KiB
JavaScript

const assert = require('assert/strict');
const {
agentProvider,
fixtureManager,
mockManager,
matchers
} = require('../../utils/e2e-framework');
const {
anyContentVersion,
anyEtag,
anyErrorId,
anyLocationFor,
anyObjectId,
anyISODateTime,
anyISODateTimeWithTZ,
anyNumber,
anyLocalURL,
anyString
} = matchers;
const matchCollection = {
id: anyObjectId,
created_at: anyISODateTime,
updated_at: anyISODateTime
};
const matchCollectionPost = {
id: anyObjectId,
url: anyLocalURL,
created_at: anyISODateTimeWithTZ,
updated_at: anyISODateTimeWithTZ,
published_at: anyISODateTimeWithTZ
};
/**
*
* @param {number} postCount
*/
const buildMatcher = (postCount, opts = {}) => {
let obj = {
id: anyObjectId
};
if (opts.withSortOrder) {
obj.sort_order = anyNumber;
}
return {
...matchCollection,
posts: Array(postCount).fill(obj)
};
};
describe('Collections API', function () {
let agent;
before(async function () {
mockManager.mockLabsEnabled('collections');
agent = await agentProvider.getAdminAPIAgent();
await fixtureManager.init('users', 'posts');
await agent.loginAsOwner();
});
afterEach(function () {
mockManager.restore();
});
it('Can add a Collection', async function () {
const collection = {
title: 'Test Collection',
description: 'Test Collection Description'
};
await agent
.post('/collections/')
.body({
collections: [collection]
})
.expectStatus(201)
.matchHeaderSnapshot({
'content-version': anyContentVersion,
etag: anyEtag,
location: anyLocationFor('collections')
})
.matchBodySnapshot({
collections: [matchCollection]
});
});
// @NOTE: Below are flaky tests. They have inconsistent state due to collection events race conditions.
// Should be uncommented and fixed with: https://github.com/TryGhost/Arch/issues/16
// describe('Browse', function () {
// it('Can browse Collections', async function () {
// await agent
// .get('/collections/')
// .expectStatus(200)
// .matchHeaderSnapshot({
// 'content-version': anyContentVersion,
// etag: anyEtag
// })
// .matchBodySnapshot({
// collections: [
// buildMatcher(13, {withSortOrder: true}),
// buildMatcher(2, {withSortOrder: true}),
// buildMatcher(0)
// ]
// });
// });
// });
// describe('Browse Posts', function () {
// it('Can browse Collections Posts', async function () {
// const collections = await agent.get('/collections/');
// const latestCollection = collections.body.collections.find(c => c.slug === 'latest');
// await agent
// .get(`/collections/${latestCollection.id}/posts/`)
// .expectStatus(200)
// .matchHeaderSnapshot({
// 'content-version': anyContentVersion,
// etag: anyEtag
// })
// .matchBodySnapshot({
// collection_posts: Array(13).fill(matchCollectionPost)
// });
// });
// it('Can browse Collections Posts using paging parameters', async function () {
// const collections = await agent.get('/collections/');
// const indexCollection = collections.body.collections.find(c => c.slug === 'latest');
// await agent
// .get(`/collections/${indexCollection.id}/posts/?limit=2&page=2`)
// .expectStatus(200)
// .matchHeaderSnapshot({
// 'content-version': anyContentVersion,
// etag: anyEtag
// })
// .matchBodySnapshot({
// collection_posts: Array(2).fill(matchCollectionPost)
// });
// });
// it('Can browse Collections Posts using collection slug', async function () {
// await agent
// .get(`/collections/latest/posts/`)
// .expectStatus(200)
// .matchHeaderSnapshot({
// 'content-version': anyContentVersion,
// etag: anyEtag
// })
// .matchBodySnapshot({
// collection_posts: Array(13).fill(matchCollectionPost)
// });
// });
// });
it('Can read a Collection', async function () {
const collection = {
title: 'Test Collection to Read'
};
const addResponse = await agent
.post('/collections/')
.body({
collections: [collection]
})
.expectStatus(201)
.matchHeaderSnapshot({
'content-version': anyContentVersion,
etag: anyEtag,
location: anyLocationFor('collections')
})
.matchBodySnapshot({
collections: [matchCollection]
});
const collectionId = addResponse.body.collections[0].id;
const readResponse = await agent
.get(`/collections/${collectionId}/`)
.expectStatus(200)
.matchHeaderSnapshot({
'content-version': anyContentVersion,
etag: anyEtag
})
.matchBodySnapshot({
collections: [matchCollection]
});
assert.equal(readResponse.body.collections[0].title, 'Test Collection to Read');
await agent
.delete(`/collections/${collectionId}/`)
.expectStatus(204);
});
describe('Edit', function () {
let collectionToEdit;
before(async function () {
const collection = {
title: 'Test Collection to Edit'
};
const addResponse = await agent
.post('/collections/')
.body({
collections: [collection]
})
.expectStatus(201);
collectionToEdit = addResponse.body.collections[0];
});
it('Can edit a Collection', async function () {
const editResponse = await agent
.put(`/collections/${collectionToEdit.id}/`)
.body({
collections: [{
title: 'Test Collection Edited'
}]
})
.expectStatus(200)
.matchHeaderSnapshot({
'content-version': anyContentVersion,
etag: anyEtag
})
.matchBodySnapshot({
collections: [matchCollection]
});
assert.equal(editResponse.body.collections[0].title, 'Test Collection Edited');
});
it('Fails to edit unexistent Collection', async function () {
const unexistentID = '5951f5fca366002ebd5dbef7';
await agent
.put(`/collections/${unexistentID}/`)
.body({
collections: [{
id: unexistentID,
title: 'Editing unexistent Collection'
}]
})
.expectStatus(404)
.matchBodySnapshot({
errors: [{
id: anyErrorId
}]
})
.matchHeaderSnapshot({
'content-version': anyContentVersion,
etag: anyEtag
});
});
});
it('Can delete a Collection', async function () {
const collection = {
title: 'Test Collection to Delete'
};
const addResponse = await agent
.post('/collections/')
.body({
collections: [collection]
})
.expectStatus(201)
.matchHeaderSnapshot({
'content-version': anyContentVersion,
etag: anyEtag,
location: anyLocationFor('collections')
})
.matchBodySnapshot({
collections: [matchCollection]
});
const collectionId = addResponse.body.collections[0].id;
await agent
.delete(`/collections/${collectionId}/`)
.expectStatus(204)
.matchHeaderSnapshot({
'content-version': anyContentVersion,
etag: anyEtag
})
.matchBodySnapshot();
await agent
.get(`/collections/${collectionId}/`)
.expectStatus(404)
.matchHeaderSnapshot({
'content-version': anyContentVersion,
etag: anyEtag
})
.matchBodySnapshot({
errors: [{
id: anyErrorId
}]
});
});
it('Cannot delete a built in collection', async function () {
const builtInCollection = await agent
.get('/collections/?filter=slug:featured')
.expectStatus(200);
assert.ok(builtInCollection.body.collections);
assert.equal(builtInCollection.body.collections.length, 1);
await agent
.delete(`/collections/${builtInCollection.body.collections[0].id}/`)
.expectStatus(405)
.matchHeaderSnapshot({
'content-version': anyContentVersion,
etag: anyEtag
})
.matchBodySnapshot({
errors: [{
id: anyErrorId,
context: anyString
}]
});
});
describe('Automatic Collection Filtering', function () {
it('Creates an automatic Collection with a featured filter', async function () {
const collection = {
title: 'Test Featured Collection',
description: 'Test Collection Description',
type: 'automatic',
filter: 'featured:true'
};
await agent
.post('/collections/')
.body({
collections: [collection]
})
.expectStatus(201)
.matchHeaderSnapshot({
'content-version': anyContentVersion,
etag: anyEtag,
location: anyLocationFor('collections')
})
.matchBodySnapshot({
collections: [buildMatcher(2)]
});
});
it('Creates an automatic Collection with a published_at filter', async function () {
const collection = {
title: 'Test Collection with published_at filter',
description: 'Test Collection Description with published_at filter',
type: 'automatic',
// should return all available posts
filter: 'published_at:>=2022-05-25'
};
await agent
.post('/collections/')
.body({
collections: [collection]
})
.expectStatus(201)
.matchHeaderSnapshot({
'content-version': anyContentVersion,
etag: anyEtag,
location: anyLocationFor('collections')
})
.matchBodySnapshot({
collections: [buildMatcher(9)]
});
});
it('Creates an automatic Collection with a tag filter', async function () {
const collection = {
title: 'Test Collection with tag filter',
slug: 'bacon',
description: 'BACON!',
type: 'automatic',
filter: 'tags:[\'bacon\']'
};
await agent
.post('/collections/')
.body({
collections: [collection]
})
.expectStatus(201)
.matchHeaderSnapshot({
'content-version': anyContentVersion,
etag: anyEtag,
location: anyLocationFor('collections')
})
.matchBodySnapshot({
collections: [buildMatcher(2)]
});
await agent
.get('/collections/bacon/posts/')
.expectStatus(200)
.matchHeaderSnapshot({
'content-version': anyContentVersion,
etag: anyEtag
})
.matchBodySnapshot({
collection_posts: [
matchCollectionPost,
matchCollectionPost
]
});
});
});
});