diff --git a/backend/src/schema/resolvers/comments.spec.js b/backend/src/schema/resolvers/comments.spec.js index 0b6d5f727..87518cc02 100644 --- a/backend/src/schema/resolvers/comments.spec.js +++ b/backend/src/schema/resolvers/comments.spec.js @@ -1,48 +1,38 @@ import { GraphQLClient } from 'graphql-request' import Factory from '../../seed/factories' import { host, login, gql } from '../../jest/helpers' -import { neode } from '../../bootstrap/neo4j' +import { createTestClient } from 'apollo-server-testing' +import createServer from '../../server' +import { neode as getNeode, getDriver } from '../../bootstrap/neo4j' + +const driver = getDriver() +const neode = getNeode() +const factory = Factory() let client -let createCommentVariables -let createCommentVariablesSansPostId -let createCommentVariablesWithNonExistentPost -let userParams let headers -const factory = Factory() -const instance = neode() const categoryIds = ['cat9'] -const createPostMutation = gql` - mutation($id: ID, $title: String!, $content: String!, $categoryIds: [ID]!) { - CreatePost(id: $id, title: $title, content: $content, categoryIds: $categoryIds) { - id - } - } -` -const createCommentMutation = gql` - mutation($id: ID, $postId: ID!, $content: String!) { - CreateComment(id: $id, postId: $postId, content: $content) { - id - content - } - } -` -const createPostVariables = { - id: 'p1', - title: 'post to comment on', - content: 'please comment on me', - categoryIds, -} +let variables +let mutate +let authenticatedUser + +beforeAll(() => { + const { server } = createServer({ + context: () => { + return { + driver, + user: authenticatedUser, + } + }, + }) + const client = createTestClient(server) + mutate = client.mutate +}) beforeEach(async () => { - userParams = { - name: 'TestUser', - email: 'test@example.org', - password: '1234', - } - await factory.create('User', userParams) - await instance.create('Category', { + variables = {} + await neode.create('Category', { id: 'cat9', name: 'Democracy & Politics', icon: 'university', @@ -53,144 +43,97 @@ afterEach(async () => { await factory.cleanDatabase() }) +const createCommentMutation = gql` + mutation($id: ID, $postId: ID!, $content: String!) { + CreateComment(id: $id, postId: $postId, content: $content) { + id + content + author { + name + } + } + } +` describe('CreateComment', () => { describe('unauthenticated', () => { it('throws authorization error', async () => { - createCommentVariables = { + variables = { + ...variables, postId: 'p1', content: "I'm not authorised to comment", } - client = new GraphQLClient(host) - await expect(client.request(createCommentMutation, createCommentVariables)).rejects.toThrow( - 'Not Authorised', - ) + const result = await mutate({ mutation: createCommentMutation, variables }) + expect(result.errors[0]).toHaveProperty('message', 'Not Authorised!') }) }) describe('authenticated', () => { beforeEach(async () => { - headers = await login(userParams) - client = new GraphQLClient(host, { - headers, - }) - createCommentVariables = { - postId: 'p1', - content: "I'm authorised to comment", - } - await client.request(createPostMutation, createPostVariables) + const user = await neode.create('User', { name: 'Author' }) + authenticatedUser = await user.toJson() }) - it('creates a comment', async () => { - const expected = { - CreateComment: { + describe('given a post', () => { + beforeEach(async () => { + await factory.create('Post', { categoryIds, id: 'p1' }) + variables = { + ...variables, + postId: 'p1', content: "I'm authorised to comment", - }, - } - - await expect( - client.request(createCommentMutation, createCommentVariables), - ).resolves.toMatchObject(expected) - }) - - it('assigns the authenticated user as author', async () => { - await client.request(createCommentMutation, createCommentVariables) - - const { User } = await client.request(gql` - { - User(name: "TestUser") { - comments { - content - } - } } - `) + }) - expect(User).toEqual([ - { - comments: [ - { - content: "I'm authorised to comment", - }, - ], - }, - ]) - }) + it('creates a comment', async () => { + await expect(mutate({ mutation: createCommentMutation, variables })).resolves.toMatchObject( + { + data: { CreateComment: { content: "I'm authorised to comment" } }, + }, + ) + }) - it('throw an error if an empty string is sent from the editor as content', async () => { - createCommentVariables = { - postId: 'p1', - content: '
', - } + it('assigns the authenticated user as author', async () => { + await expect(mutate({ mutation: createCommentMutation, variables })).resolves.toMatchObject( + { + data: { CreateComment: { author: { name: 'Author' } } }, + }, + ) + }) - await expect(client.request(createCommentMutation, createCommentVariables)).rejects.toThrow( - 'Comment must be at least 1 character long!', - ) - }) + describe('comment content is empty', () => { + beforeEach(() => { + variables = { ...variables, content: '' } + }) - it('throws an error if a comment sent from the editor does not contain a single character', async () => { - createCommentVariables = { - postId: 'p1', - content: '', - } + it('throw UserInput error', async () => { + const { data, errors } = await mutate({ mutation: createCommentMutation, variables }) + expect(data).toEqual({ CreateComment: null }) + expect(errors[0]).toHaveProperty('message', 'Comment must be at least 1 character long!') + }) + }) - await expect(client.request(createCommentMutation, createCommentVariables)).rejects.toThrow( - 'Comment must be at least 1 character long!', - ) - }) + describe('comment content contains only whitespaces', () => { + beforeEach(() => { + variables = { ...variables, content: '
' } + }) - it('throws an error if postId is sent as an empty string', async () => { - createCommentVariables = { - postId: 'p1', - content: '', - } + it('throw UserInput error', async () => { + const { data, errors } = await mutate({ mutation: createCommentMutation, variables }) + expect(data).toEqual({ CreateComment: null }) + expect(errors[0]).toHaveProperty('message', 'Comment must be at least 1 character long!') + }) + }) - await expect(client.request(createCommentMutation, createCommentVariables)).rejects.toThrow( - 'Comment must be at least 1 character long!', - ) - }) + describe('invalid post id', () => { + beforeEach(() => { + variables = { ...variables, postId: 'does-not-exist' } + }) - it('throws an error if content is sent as an string of empty characters', async () => { - createCommentVariables = { - postId: 'p1', - content: ' ', - } - - await expect(client.request(createCommentMutation, createCommentVariables)).rejects.toThrow( - 'Comment must be at least 1 character long!', - ) - }) - - it('throws an error if postId is sent as an empty string', async () => { - createCommentVariablesSansPostId = { - postId: '', - content: 'this comment should not be created', - } - - await expect( - client.request(createCommentMutation, createCommentVariablesSansPostId), - ).rejects.toThrow('Comment cannot be created without a post!') - }) - - it('throws an error if postId is sent as an string of empty characters', async () => { - createCommentVariablesSansPostId = { - postId: ' ', - content: 'this comment should not be created', - } - - await expect( - client.request(createCommentMutation, createCommentVariablesSansPostId), - ).rejects.toThrow('Comment cannot be created without a post!') - }) - - it('throws an error if the post does not exist in the database', async () => { - createCommentVariablesWithNonExistentPost = { - postId: 'p2', - content: "comment should not be created cause the post doesn't exist", - } - - await expect( - client.request(createCommentMutation, createCommentVariablesWithNonExistentPost), - ).rejects.toThrow('Comment cannot be created without a post!') + it('throw UserInput error', async () => { + const { data, errors } = await mutate({ mutation: createCommentMutation, variables }) + expect(data).toEqual({ CreateComment: null }) + expect(errors[0]).toHaveProperty('message', 'Comment cannot be created without a post!') + }) + }) }) }) }) diff --git a/backend/src/seed/factories/posts.js b/backend/src/seed/factories/posts.js index f2f1432dc..82128359b 100644 --- a/backend/src/seed/factories/posts.js +++ b/backend/src/seed/factories/posts.js @@ -1,51 +1,43 @@ import faker from 'faker' +import slugify from 'slug' import uuid from 'uuid/v4' -export default function(params) { - const { - id = uuid(), - slug = '', - title = faker.lorem.sentence(), - content = [ - faker.lorem.sentence(), - faker.lorem.sentence(), - faker.lorem.sentence(), - faker.lorem.sentence(), - faker.lorem.sentence(), - ].join('. '), - image = faker.image.unsplash.imageUrl(), - visibility = 'public', - deleted = false, - categoryIds, - } = params - +export default function create() { return { - mutation: ` - mutation( - $id: ID! - $slug: String - $title: String! - $content: String! - $image: String - $visibility: Visibility - $deleted: Boolean - $categoryIds: [ID] - ) { - CreatePost( - id: $id - slug: $slug - title: $title - content: $content - image: $image - visibility: $visibility - deleted: $deleted - categoryIds: $categoryIds - ) { - title - content - } + factory: async ({ args, neodeInstance }) => { + const defaults = { + id: uuid(), + slug: '', + title: faker.lorem.sentence(), + content: [ + faker.lorem.sentence(), + faker.lorem.sentence(), + faker.lorem.sentence(), + faker.lorem.sentence(), + faker.lorem.sentence(), + ].join('. '), + image: faker.image.unsplash.imageUrl(), + visibility: 'public', + deleted: false, + categoryIds: [], } - `, - variables: { id, slug, title, content, image, visibility, deleted, categoryIds }, + defaults.slug = slugify(defaults.title, { lower: true }) + args = { + ...defaults, + ...args, + } + const { categoryIds } = args + if (!categoryIds.length) throw new Error('CategoryIds are empty!') + const categories = await Promise.all( + categoryIds.map(c => { + return neodeInstance.find('Category', c) + }), + ) + const author = args.author || (await neodeInstance.create('User', args)) + const post = await neodeInstance.create('Post', args) + await post.relateTo(author, 'author') + await Promise.all(categories.map(c => c.relateTo(post, 'post'))) + return post + }, } }