Ulf Gebhardt 45d2283138
refactor(backend): externalize all remaining queries in spec files (#8902)
* refactor: externalize all remaining queries in spec files

* User with different queries due to permissions

* fix notification:groups

* fix hashtagsmiddleware

* fix blockedUsers

* fix softDeleteMiddleware

* fix shouts.spec

* fix userInteractions spec

* fix mutedUsers spec

* seocialMedia spec

* fix notificationMiddleware.spec

* fix user.spce & fix undefined activeCategories

* fix notifications.spec

* fix userInteractions.spec

* fix blockedUsers & mutedUsers spec

* remove unused comment

* fix locations spec

* fix orderByMiddleware & spec

* fix lint

* fix shout spec
2025-09-18 17:43:15 +02:00

2303 lines
66 KiB
TypeScript

/* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import Factory, { cleanDatabase } from '@db/factories'
import Image from '@db/models/Image'
import { AddPostEmotions } from '@graphql/queries/AddPostEmotions'
import { CreateGroup } from '@graphql/queries/CreateGroup'
import { CreatePost } from '@graphql/queries/CreatePost'
import { DeletePost } from '@graphql/queries/DeletePost'
import { pinPost } from '@graphql/queries/pinPost'
import { Post } from '@graphql/queries/Post'
import { PostsEmotionsByCurrentUser } from '@graphql/queries/PostsEmotionsByCurrentUser'
import { PostsEmotionsCountByEmotion } from '@graphql/queries/PostsEmotionsCountByEmotion'
import { pushPost } from '@graphql/queries/pushPost'
import { RemovePostEmotions } from '@graphql/queries/RemovePostEmotions'
import { unpinPost } from '@graphql/queries/unpinPost'
import { unpushPost } from '@graphql/queries/unpushPost'
import { UpdatePost } from '@graphql/queries/UpdatePost'
import type { ApolloTestSetup } from '@root/test/helpers'
import { createApolloTestSetup } from '@root/test/helpers'
import type { Context } from '@src/context'
let user
let authenticatedUser: Context['user']
const context = () => ({ authenticatedUser, config })
let mutate: ApolloTestSetup['mutate']
let query: ApolloTestSetup['query']
let database: ApolloTestSetup['database']
let server: ApolloTestSetup['server']
const defaultConfig = {
CATEGORIES_ACTIVE: true,
// MAPBOX_TOKEN: CONFIG.MAPBOX_TOKEN,
}
let config: Partial<Context['config']>
beforeAll(async () => {
await cleanDatabase()
const apolloSetup = createApolloTestSetup({ context })
mutate = apolloSetup.mutate
query = apolloSetup.query
database = apolloSetup.database
server = apolloSetup.server
})
afterAll(() => {
void server.stop()
void database.driver.close()
database.neode.close()
})
const categoryIds = ['cat9', 'cat4', 'cat15']
let variables
beforeEach(async () => {
config = { ...defaultConfig }
variables = {}
user = await Factory.build(
'user',
{
id: 'current-user',
name: 'TestUser',
},
{
email: 'test@example.org',
password: '1234',
},
)
await Promise.all([
database.neode.create('Category', {
id: 'cat9',
name: 'Democracy & Politics',
icon: 'university',
}),
database.neode.create('Category', {
id: 'cat4',
name: 'Environment & Nature',
icon: 'tree',
}),
database.neode.create('Category', {
id: 'cat15',
name: 'Consumption & Sustainability',
icon: 'shopping-cart',
}),
database.neode.create('Category', {
id: 'cat27',
name: 'Animal Protection',
icon: 'paw',
}),
])
authenticatedUser = null
})
afterEach(async () => {
await cleanDatabase()
})
describe('Post', () => {
describe('can be filtered', () => {
let followedUser, happyPost, cryPost
beforeEach(async () => {
;[followedUser] = await Promise.all([
Factory.build(
'user',
{
id: 'followed-by-me',
name: 'Followed User',
},
{
email: 'followed@example.org',
password: '1234',
},
),
])
;[happyPost, cryPost] = await Promise.all([
Factory.build('post', { id: 'happy-post' }, { categoryIds: ['cat4'] }),
Factory.build('post', { id: 'cry-post' }, { categoryIds: ['cat15'] }),
Factory.build(
'post',
{
id: 'post-by-followed-user',
},
{
categoryIds: ['cat9'],
author: followedUser,
},
),
])
})
describe('no filter', () => {
it('returns all posts', async () => {
variables = { filter: {} }
await expect(query({ query: Post, variables })).resolves.toMatchObject({
data: {
Post: expect.arrayContaining([
expect.objectContaining({ id: 'happy-post' }),
expect.objectContaining({ id: 'cry-post' }),
expect.objectContaining({ id: 'post-by-followed-user' }),
]),
},
})
})
})
/* it('by categories', async () => {
const postQueryFilteredByCategories = gql`
query Post($filter: _PostFilter) {
Post(filter: $filter) {
id
categories {
id
}
}
}
`
const expected = {
data: {
Post: [
{
id: 'post-by-followed-user',
categories: [{ id: 'cat9' }],
},
],
},
}
variables = { ...variables, filter: { categories_some: { id_in: ['cat9'] } } }
await expect(
query({ query: postQueryFilteredByCategories, variables }),
).resolves.toMatchObject(expected)
}) */
describe('by emotions', () => {
it('filters by single emotion', async () => {
const expected = {
data: {
Post: [
{
id: 'happy-post',
emotions: [{ emotion: 'happy' }],
},
],
},
}
await user.relateTo(happyPost, 'emoted', { emotion: 'happy' })
variables = { ...variables, filter: { emotions_some: { emotion_in: ['happy'] } } }
await expect(query({ query: Post, variables })).resolves.toMatchObject(expected)
})
it('filters by multiple emotions', async () => {
await user.relateTo(happyPost, 'emoted', { emotion: 'happy' })
await user.relateTo(cryPost, 'emoted', { emotion: 'cry' })
variables = { ...variables, filter: { emotions_some: { emotion_in: ['happy', 'cry'] } } }
await expect(query({ query: Post, variables })).resolves.toMatchObject({
data: {
Post: expect.arrayContaining([
expect.objectContaining({
id: 'happy-post',
emotions: [expect.objectContaining({ emotion: 'happy' })],
}),
expect.objectContaining({
id: 'cry-post',
emotions: [expect.objectContaining({ emotion: 'cry' })],
}),
]),
},
errors: undefined,
})
})
})
it('by followed-by', async () => {
await user.relateTo(followedUser, 'following')
variables = { filter: { author: { followedBy_some: { id: 'current-user' } } } }
await expect(query({ query: Post, variables })).resolves.toMatchObject({
data: {
Post: [
{
id: 'post-by-followed-user',
author: { name: 'Followed User' },
},
],
},
errors: undefined,
})
})
})
})
describe('CreatePost', () => {
beforeEach(() => {
variables = {
...variables,
id: 'p3589',
title: 'I am a title',
content: 'Some content',
categoryIds,
}
})
describe('unauthenticated', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({ mutation: CreatePost, variables })
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
describe('authenticated', () => {
beforeEach(async () => {
authenticatedUser = await user.toJson()
})
it('creates a post', async () => {
const expected = {
data: { CreatePost: { title: 'I am a title', content: 'Some content' } },
errors: undefined,
}
await expect(mutate({ mutation: CreatePost, variables })).resolves.toMatchObject(expected)
})
it('assigns the authenticated user as author', async () => {
const expected = {
data: {
CreatePost: {
title: 'I am a title',
author: {
name: 'TestUser',
},
},
},
errors: undefined,
}
await expect(mutate({ mutation: CreatePost, variables })).resolves.toMatchObject(expected)
})
it('`disabled` and `deleted` default to `false`', async () => {
const expected = { data: { CreatePost: { disabled: false, deleted: false } } }
await expect(mutate({ mutation: CreatePost, variables })).resolves.toMatchObject(expected)
})
it('has label "Article" as default', async () => {
await expect(mutate({ mutation: CreatePost, variables })).resolves.toMatchObject({
data: { CreatePost: { postType: ['Article'] } },
})
})
describe('with invalid post type', () => {
it('throws an error', async () => {
await expect(
mutate({
mutation: CreatePost,
variables: { ...variables, postType: 'not-valid' },
}),
).resolves.toMatchObject({
errors: [
{
message:
'Variable "$postType" got invalid value "not-valid"; Expected type PostType.',
},
],
})
})
})
describe('with post type "Event"', () => {
describe('without event start date', () => {
it('throws an error', async () => {
await expect(
mutate({
mutation: CreatePost,
variables: {
...variables,
postType: 'Event',
},
}),
).resolves.toMatchObject({
errors: [
{
message: "Cannot read properties of undefined (reading 'eventStart')",
},
],
})
})
})
describe('with invalid event start date', () => {
it('throws an error', async () => {
await expect(
mutate({
mutation: CreatePost,
variables: {
...variables,
postType: 'Event',
eventInput: {
eventStart: 'no date',
},
},
}),
).resolves.toMatchObject({
errors: [
{
message: 'Event start date must be a valid date!',
},
],
})
})
})
describe('with event start in no ISO format', () => {
it('throws an error', async () => {
const now = new Date()
const eventStart = new Date(now.getFullYear(), now.getMonth() - 1).toISOString()
await expect(
mutate({
mutation: CreatePost,
variables: {
...variables,
postType: 'Event',
eventInput: {
eventStart: eventStart.split('T')[0],
},
},
}),
).resolves.toMatchObject({
errors: [
{
message: 'Event start date must be in ISO format!',
},
],
})
})
})
describe('with event start date in the past', () => {
it('throws an error', async () => {
const now = new Date()
await expect(
mutate({
mutation: CreatePost,
variables: {
...variables,
postType: 'Event',
eventInput: {
eventStart: new Date(now.getFullYear(), now.getMonth() - 1).toISOString(),
},
},
}),
).resolves.toMatchObject({
errors: [
{
message: 'Event start date must be in the future!',
},
],
})
})
})
describe('with valid start date and invalid end date', () => {
it('throws an error', async () => {
const now = new Date()
await expect(
mutate({
mutation: CreatePost,
variables: {
...variables,
postType: 'Event',
eventInput: {
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
eventEnd: 'not-valid',
},
},
}),
).resolves.toMatchObject({
errors: [
{
message: 'Event end date must be a valid date!',
},
],
})
})
})
describe('with valid start date and not ISO formated end date', () => {
it('throws an error', async () => {
const now = new Date()
const eventEnd = new Date(now.getFullYear(), now.getMonth() + 2).toISOString()
await expect(
mutate({
mutation: CreatePost,
variables: {
...variables,
postType: 'Event',
eventInput: {
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
eventEnd: eventEnd.split('T')[0],
},
},
}),
).resolves.toMatchObject({
errors: [
{
message: 'Event end date must be in ISO format!',
},
],
})
})
})
describe('with valid start date and end date before start date', () => {
it('throws an error', async () => {
const now = new Date()
await expect(
mutate({
mutation: CreatePost,
variables: {
...variables,
postType: 'Event',
eventInput: {
eventStart: new Date(now.getFullYear(), now.getMonth() + 2).toISOString(),
eventEnd: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
},
},
}),
).resolves.toMatchObject({
errors: [
{
message: 'Event end date must be a after event start date!',
},
],
})
})
})
describe('with valid start date and valid end date', () => {
it('creates the event', async () => {
const now = new Date()
await expect(
mutate({
mutation: CreatePost,
variables: {
...variables,
postType: 'Event',
eventInput: {
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
eventEnd: new Date(now.getFullYear(), now.getMonth() + 2).toISOString(),
},
},
}),
).resolves.toMatchObject({
data: {
CreatePost: {
postType: ['Event'],
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
eventEnd: new Date(now.getFullYear(), now.getMonth() + 2).toISOString(),
eventIsOnline: false,
},
},
errors: undefined,
})
})
})
describe('with valid start date and event is online', () => {
it('creates the event', async () => {
const now = new Date()
await expect(
mutate({
mutation: CreatePost,
variables: {
...variables,
postType: 'Event',
eventInput: {
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
eventIsOnline: true,
},
},
}),
).resolves.toMatchObject({
data: {
CreatePost: {
postType: ['Event'],
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
eventIsOnline: true,
},
},
errors: undefined,
})
})
})
describe('event location name is given but event venue is missing', () => {
it('throws an error', async () => {
const now = new Date()
await expect(
mutate({
mutation: CreatePost,
variables: {
...variables,
postType: 'Event',
eventInput: {
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
eventLocationName: 'Berlin',
},
},
}),
).resolves.toMatchObject({
errors: [
{
message: 'Event venue must be present if event location is given!',
},
],
})
})
})
describe('valid event input without location', () => {
it('has label "Event" set', async () => {
const now = new Date()
await expect(
mutate({
mutation: CreatePost,
variables: {
...variables,
postType: 'Event',
eventInput: {
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
},
},
}),
).resolves.toMatchObject({
data: {
CreatePost: {
postType: ['Event'],
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
eventIsOnline: false,
},
},
errors: undefined,
})
})
})
describe('valid event input with location name', () => {
it('has label "Event" set', async () => {
const now = new Date()
await expect(
mutate({
mutation: CreatePost,
variables: {
...variables,
postType: 'Event',
eventInput: {
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
eventLocationName: 'Leipzig',
eventVenue: 'Connewitzer Kreuz',
},
},
}),
).resolves.toMatchObject({
data: {
CreatePost: {
postType: ['Event'],
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
eventLocationName: 'Leipzig',
eventVenue: 'Connewitzer Kreuz',
eventLocation: {
lng: 12.375101,
lat: 51.34083,
},
},
},
errors: undefined,
})
})
})
})
})
})
describe('UpdatePost', () => {
let author, newlyCreatedPost
beforeEach(async () => {
author = await Factory.build('user', { slug: 'the-author' })
authenticatedUser = await author.toJson()
const { data } = await mutate({
mutation: CreatePost,
variables: {
title: 'Old title',
content: 'Old content',
categoryIds,
},
})
newlyCreatedPost = (data as any).CreatePost // eslint-disable-line @typescript-eslint/no-explicit-any
variables = {
id: newlyCreatedPost.id,
title: 'New title',
content: 'New content',
}
})
describe('unauthenticated', () => {
it('throws authorization error', async () => {
authenticatedUser = null
await expect(mutate({ mutation: UpdatePost, variables })).resolves.toMatchObject({
errors: [{ message: 'Not Authorized!' }],
data: { UpdatePost: null },
})
})
})
describe('authenticated but not the author', () => {
beforeEach(async () => {
authenticatedUser = await user.toJson()
})
it('throws authorization error', async () => {
const { errors } = await mutate({ mutation: UpdatePost, variables })
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
describe('authenticated as author', () => {
beforeEach(async () => {
authenticatedUser = await author.toJson()
})
it('updates a post', async () => {
const expected = {
data: { UpdatePost: { id: newlyCreatedPost.id, content: 'New content' } },
errors: undefined,
}
await expect(mutate({ mutation: UpdatePost, variables })).resolves.toMatchObject(expected)
})
it('updates a post, but maintains non-updated attributes', async () => {
const expected = {
data: {
UpdatePost: {
id: newlyCreatedPost.id,
content: 'New content',
createdAt: expect.any(String),
},
},
errors: undefined,
}
await expect(mutate({ mutation: UpdatePost, variables })).resolves.toMatchObject(expected)
})
it('updates the updatedAt attribute', async () => {
const {
data: { UpdatePost: UpdatePostData },
} = (await mutate({ mutation: UpdatePost, variables })) as any // eslint-disable-line @typescript-eslint/no-explicit-any
expect(UpdatePostData.updatedAt).toBeTruthy()
expect(Date.parse(UpdatePostData.updatedAt)).toEqual(expect.any(Number))
expect(newlyCreatedPost.updatedAt).not.toEqual(UpdatePostData.updatedAt)
})
describe('no new category ids provided for update', () => {
it('resolves and keeps current categories', async () => {
const expected = {
data: {
UpdatePost: {
id: newlyCreatedPost.id,
categories: expect.arrayContaining([{ id: 'cat9' }, { id: 'cat4' }, { id: 'cat15' }]),
},
},
errors: undefined,
}
await expect(mutate({ mutation: UpdatePost, variables })).resolves.toMatchObject(expected)
})
})
describe('given category ids', () => {
beforeEach(() => {
variables = { ...variables, categoryIds: ['cat27'] }
})
it('updates categories of a post', async () => {
const expected = {
data: {
UpdatePost: {
id: newlyCreatedPost.id,
categories: expect.arrayContaining([{ id: 'cat27' }]),
},
},
errors: undefined,
}
await expect(mutate({ mutation: UpdatePost, variables })).resolves.toMatchObject(expected)
})
})
describe('change post type to event', () => {
describe('with missing event start date', () => {
it('throws an error', async () => {
await expect(
mutate({
mutation: UpdatePost,
variables: { ...variables, postType: 'Event' },
}),
).resolves.toMatchObject({
errors: [
{
message: "Cannot read properties of undefined (reading 'eventStart')",
},
],
})
})
})
describe('with invalid event start date', () => {
it('throws an error', async () => {
await expect(
mutate({
mutation: UpdatePost,
variables: {
...variables,
postType: 'Event',
eventInput: {
eventStart: 'no-date',
},
},
}),
).resolves.toMatchObject({
errors: [
{
message: 'Event start date must be a valid date!',
},
],
})
})
})
describe('with event start date in the past', () => {
it('throws an error', async () => {
const now = new Date()
await expect(
mutate({
mutation: UpdatePost,
variables: {
...variables,
postType: 'Event',
eventInput: {
eventStart: new Date(now.getFullYear(), now.getMonth() - 1).toISOString(),
},
},
}),
).resolves.toMatchObject({
errors: [
{
message: 'Event start date must be in the future!',
},
],
})
})
})
describe('event location name is given but event venue is missing', () => {
it('throws an error', async () => {
const now = new Date()
await expect(
mutate({
mutation: UpdatePost,
variables: {
...variables,
postType: 'Event',
eventInput: {
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
eventLocationName: 'Berlin',
},
},
}),
).resolves.toMatchObject({
errors: [
{
message: 'Event venue must be present if event location is given!',
},
],
})
})
})
describe('valid event input without location name', () => {
it('has label "Event" set', async () => {
const now = new Date()
await expect(
mutate({
mutation: UpdatePost,
variables: {
...variables,
postType: 'Event',
eventInput: {
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
},
},
}),
).resolves.toMatchObject({
data: {
UpdatePost: {
postType: ['Event'],
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
},
},
errors: undefined,
})
})
})
describe('valid event input with location name', () => {
it('has label "Event" set', async () => {
const now = new Date()
await expect(
mutate({
mutation: UpdatePost,
variables: {
...variables,
postType: 'Event',
eventInput: {
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
eventLocationName: 'Leipzig',
eventVenue: 'Connewitzer Kreuz',
},
},
}),
).resolves.toMatchObject({
data: {
UpdatePost: {
postType: ['Event'],
eventStart: new Date(now.getFullYear(), now.getMonth() + 1).toISOString(),
eventLocationName: 'Leipzig',
eventVenue: 'Connewitzer Kreuz',
eventLocation: {
lng: 12.375101,
lat: 51.34083,
},
},
},
errors: undefined,
})
})
})
})
// eslint-disable-next-line jest/no-disabled-tests
describe.skip('params.image', () => {
describe('is object', () => {
beforeEach(() => {
variables = { ...variables, image: { sensitive: true } }
})
it('updates the image', async () => {
await expect(
database.neode.first<typeof Image>('Image', { sensitive: true }, undefined),
).resolves.toBeFalsy()
await mutate({ mutation: UpdatePost, variables })
await expect(
database.neode.first<typeof Image>('Image', { sensitive: true }, undefined),
).resolves.toBeTruthy()
})
})
describe('is null', () => {
beforeEach(() => {
variables = { ...variables, image: null }
})
it('deletes the image', async () => {
await expect(database.neode.all('Image')).resolves.toHaveLength(6)
await mutate({ mutation: UpdatePost, variables })
await expect(database.neode.all('Image')).resolves.toHaveLength(5)
})
})
describe('is undefined', () => {
beforeEach(() => {
delete variables.image
})
it('keeps the image unchanged', async () => {
await expect(
database.neode.first<typeof Image>('Image', { sensitive: true }, undefined),
).resolves.toBeFalsy()
await mutate({ mutation: UpdatePost, variables })
await expect(
database.neode.first<typeof Image>('Image', { sensitive: true }, undefined),
).resolves.toBeFalsy()
})
})
})
})
})
describe('push posts', () => {
let author
beforeEach(async () => {
author = await Factory.build('user', { slug: 'the-author' })
await Factory.build(
'post',
{
id: 'pFirst',
},
{
author,
categoryIds,
},
)
await Factory.build(
'post',
{
id: 'pSecond',
},
{
author,
categoryIds,
},
)
await Factory.build(
'post',
{
id: 'pThird',
},
{
author,
categoryIds,
},
)
})
describe('unauthenticated', () => {
it('throws authorization error', async () => {
authenticatedUser = null
await expect(
mutate({ mutation: pushPost, variables: { id: 'pSecond' } }),
).resolves.toMatchObject({
errors: [{ message: 'Not Authorized!' }],
data: null,
})
})
})
describe('ordinary users', () => {
it('throws authorization error', async () => {
await expect(
mutate({ mutation: pushPost, variables: { id: 'pSecond' } }),
).resolves.toMatchObject({
errors: [{ message: 'Not Authorized!' }],
data: null,
})
})
})
describe('moderators', () => {
let moderator
beforeEach(async () => {
moderator = await user.update({ role: 'moderator', updatedAt: new Date().toISOString() })
authenticatedUser = await moderator.toJson()
})
it('throws authorization error', async () => {
await expect(
mutate({ mutation: pushPost, variables: { id: 'pSecond' } }),
).resolves.toMatchObject({
errors: [{ message: 'Not Authorized!' }],
data: null,
})
})
})
describe('admins', () => {
let admin
beforeEach(async () => {
admin = await Factory.build('user', {
id: 'admin',
role: 'admin',
})
authenticatedUser = await admin.toJson()
})
it('pushes the post to the front of the feed', async () => {
await expect(
query({ query: Post, variables: { orderBy: ['sortDate_desc'] } }),
).resolves.toMatchObject({
errors: undefined,
data: {
Post: [
{
id: 'pThird',
},
{
id: 'pSecond',
},
{
id: 'pFirst',
},
],
},
})
await expect(
mutate({ mutation: pushPost, variables: { id: 'pSecond' } }),
).resolves.toMatchObject({
errors: undefined,
data: {
pushPost: {
id: 'pSecond',
},
},
})
await expect(
query({ query: Post, variables: { orderBy: ['sortDate_desc'] } }),
).resolves.toMatchObject({
errors: undefined,
data: {
Post: [
{
id: 'pSecond',
},
{
id: 'pThird',
},
{
id: 'pFirst',
},
],
},
})
})
})
})
describe('unpush posts', () => {
let author
let admin
beforeEach(async () => {
author = await Factory.build('user', { slug: 'the-author' })
await Factory.build(
'post',
{
id: 'pFirst',
},
{
author,
categoryIds,
},
)
await Factory.build(
'post',
{
id: 'pSecond',
},
{
author,
categoryIds,
},
)
await Factory.build(
'post',
{
id: 'pThird',
},
{
author,
categoryIds,
},
)
admin = await Factory.build('user', {
id: 'admin',
role: 'admin',
})
authenticatedUser = await admin.toJson()
await mutate({ mutation: pushPost, variables: { id: 'pSecond' } })
authenticatedUser = null
})
describe('unauthenticated', () => {
it('throws authorization error', async () => {
authenticatedUser = null
await expect(
mutate({ mutation: unpushPost, variables: { id: 'pSecond' } }),
).resolves.toMatchObject({
errors: [{ message: 'Not Authorized!' }],
data: null,
})
})
})
describe('ordinary users', () => {
it('throws authorization error', async () => {
authenticatedUser = await user.toJson()
await expect(
mutate({ mutation: unpushPost, variables: { id: 'pSecond' } }),
).resolves.toMatchObject({
errors: [{ message: 'Not Authorized!' }],
data: null,
})
})
})
describe('moderators', () => {
let moderator
beforeEach(async () => {
moderator = await user.update({ role: 'moderator', updatedAt: new Date().toISOString() })
authenticatedUser = await moderator.toJson()
})
it('throws authorization error', async () => {
await expect(
mutate({ mutation: unpushPost, variables: { id: 'pSecond' } }),
).resolves.toMatchObject({
errors: [{ message: 'Not Authorized!' }],
data: null,
})
})
})
describe('admins', () => {
it('cancels the push of the post and puts it in the original order', async () => {
authenticatedUser = await admin.toJson()
await expect(
query({ query: Post, variables: { orderBy: ['sortDate_desc'] } }),
).resolves.toMatchObject({
errors: undefined,
data: {
Post: [
{
id: 'pSecond',
},
{
id: 'pThird',
},
{
id: 'pFirst',
},
],
},
})
await expect(
mutate({ mutation: unpushPost, variables: { id: 'pSecond' } }),
).resolves.toMatchObject({
errors: undefined,
data: {
unpushPost: {
id: 'pSecond',
},
},
})
await expect(
query({ query: Post, variables: { orderBy: ['sortDate_desc'] } }),
).resolves.toMatchObject({
errors: undefined,
data: {
Post: [
{
id: 'pThird',
},
{
id: 'pSecond',
},
{
id: 'pFirst',
},
],
},
})
})
})
})
describe('pin posts', () => {
let author
beforeEach(async () => {
author = await Factory.build('user', { slug: 'the-author' })
await Factory.build(
'post',
{
id: 'p9876',
title: 'Old title',
content: 'Old content',
},
{
author,
categoryIds,
},
)
variables = {
id: 'p9876',
}
})
describe('unauthenticated', () => {
it('throws authorization error', async () => {
authenticatedUser = null
await expect(mutate({ mutation: pinPost, variables })).resolves.toMatchObject({
errors: [{ message: 'Not Authorized!' }],
data: { pinPost: null },
})
})
})
describe('ordinary users', () => {
it('throws authorization error', async () => {
await expect(mutate({ mutation: pinPost, variables })).resolves.toMatchObject({
errors: [{ message: 'Not Authorized!' }],
data: { pinPost: null },
})
})
})
describe('moderators', () => {
let moderator
beforeEach(async () => {
moderator = await user.update({ role: 'moderator', updatedAt: new Date().toISOString() })
authenticatedUser = await moderator.toJson()
})
it('throws authorization error', async () => {
await expect(mutate({ mutation: pinPost, variables })).resolves.toMatchObject({
errors: [{ message: 'Not Authorized!' }],
data: { pinPost: null },
})
})
})
describe('admins', () => {
let admin
beforeEach(async () => {
admin = await user.update({
role: 'admin',
name: 'Admin',
updatedAt: new Date().toISOString(),
})
authenticatedUser = await admin.toJson()
})
describe('MAX_PINNED_POSTS is 0', () => {
beforeEach(async () => {
config = { ...defaultConfig, MAX_PINNED_POSTS: 0 }
await Factory.build(
'post',
{
id: 'created-and-pinned-by-same-admin',
},
{
author: admin,
},
)
variables = { ...variables, id: 'created-and-pinned-by-same-admin' }
})
it('throws with error that pinning posts is not allowed', async () => {
await expect(mutate({ mutation: pinPost, variables })).resolves.toMatchObject({
data: { pinPost: null },
errors: [{ message: 'Pinned posts are not allowed!' }],
})
})
})
describe('MAX_PINNED_POSTS is 1', () => {
beforeEach(() => {
config = { ...defaultConfig, MAX_PINNED_POSTS: 1 }
})
describe('are allowed to pin posts', () => {
beforeEach(async () => {
await Factory.build(
'post',
{
id: 'created-and-pinned-by-same-admin',
},
{
author: admin,
},
)
variables = { ...variables, id: 'created-and-pinned-by-same-admin' }
})
it('responds with the updated Post', async () => {
const expected = {
data: {
pinPost: {
id: 'created-and-pinned-by-same-admin',
author: {
name: 'Admin',
},
pinnedBy: {
id: 'current-user',
name: 'Admin',
role: 'admin',
},
},
},
errors: undefined,
}
await expect(mutate({ mutation: pinPost, variables })).resolves.toMatchObject(expected)
})
it('sets createdAt date for PINNED', async () => {
const expected = {
data: {
pinPost: {
id: 'created-and-pinned-by-same-admin',
pinnedAt: expect.any(String),
},
},
errors: undefined,
}
await expect(mutate({ mutation: pinPost, variables })).resolves.toMatchObject(expected)
})
it('sets redundant `pinned` property for performant ordering', async () => {
variables = { ...variables, id: 'created-and-pinned-by-same-admin' }
const expected = {
data: { pinPost: { pinned: true } },
errors: undefined,
}
await expect(mutate({ mutation: pinPost, variables })).resolves.toMatchObject(expected)
})
})
describe('post created by another admin', () => {
let otherAdmin
beforeEach(async () => {
otherAdmin = await Factory.build('user', {
role: 'admin',
name: 'otherAdmin',
})
authenticatedUser = await otherAdmin.toJson()
await Factory.build(
'post',
{
id: 'created-by-one-admin-pinned-by-different-one',
},
{
author: otherAdmin,
},
)
})
it('responds with the updated Post', async () => {
authenticatedUser = await admin.toJson()
variables = { ...variables, id: 'created-by-one-admin-pinned-by-different-one' }
const expected = {
data: {
pinPost: {
id: 'created-by-one-admin-pinned-by-different-one',
author: {
name: 'otherAdmin',
},
pinnedBy: {
id: 'current-user',
name: 'Admin',
role: 'admin',
},
},
},
errors: undefined,
}
await expect(mutate({ mutation: pinPost, variables })).resolves.toMatchObject(expected)
})
})
describe('post created by another user', () => {
it('responds with the updated Post', async () => {
const expected = {
data: {
pinPost: {
id: 'p9876',
author: {
slug: 'the-author',
},
pinnedBy: {
id: 'current-user',
name: 'Admin',
role: 'admin',
},
},
},
errors: undefined,
}
await expect(mutate({ mutation: pinPost, variables })).resolves.toMatchObject(expected)
})
})
describe('pinned post already exists', () => {
let pinnedPost
beforeEach(async () => {
await Factory.build(
'post',
{
id: 'only-pinned-post',
},
{
author: admin,
},
)
await mutate({ mutation: pinPost, variables })
})
it('removes previous `pinned` attribute', async () => {
const cypher = 'MATCH (post:Post) WHERE post.pinned IS NOT NULL RETURN post'
pinnedPost = await database.neode.cypher(cypher, {})
expect(pinnedPost.records).toHaveLength(1)
variables = { ...variables, id: 'only-pinned-post' }
await mutate({ mutation: pinPost, variables })
pinnedPost = await database.neode.cypher(cypher, {})
expect(pinnedPost.records).toHaveLength(1)
})
it('removes previous PINNED relationship', async () => {
variables = { ...variables, id: 'only-pinned-post' }
await mutate({ mutation: pinPost, variables })
pinnedPost = await database.neode.cypher(
`MATCH (:User)-[pinned:PINNED]->(post:Post) RETURN post, pinned`,
{},
)
expect(pinnedPost.records).toHaveLength(1)
})
})
describe('post in public group', () => {
beforeEach(async () => {
await mutate({
mutation: CreateGroup,
variables: {
name: 'Public Group',
id: 'public-group',
about: 'This is a public group',
groupType: 'public',
actionRadius: 'regional',
description:
'This is a public group to test if the posts of this group can be pinned.',
categoryIds,
},
})
await mutate({
mutation: CreatePost,
variables: {
id: 'public-group-post',
title: 'Public group post',
content: 'This is a post in a public group',
groupId: 'public-group',
categoryIds,
},
})
variables = { ...variables, id: 'public-group-post' }
})
it('can be pinned', async () => {
await expect(mutate({ mutation: pinPost, variables })).resolves.toMatchObject({
data: {
pinPost: {
id: 'public-group-post',
author: {
slug: 'testuser',
},
pinnedBy: {
id: 'current-user',
name: 'Admin',
role: 'admin',
},
},
},
errors: undefined,
})
})
})
describe('post in closed group', () => {
beforeEach(async () => {
await mutate({
mutation: CreateGroup,
variables: {
name: 'Closed Group',
id: 'closed-group',
about: 'This is a closed group',
groupType: 'closed',
actionRadius: 'regional',
description:
'This is a closed group to test if the posts of this group can be pinned.',
categoryIds,
},
})
await mutate({
mutation: CreatePost,
variables: {
id: 'closed-group-post',
title: 'Closed group post',
content: 'This is a post in a closed group',
groupId: 'closed-group',
categoryIds,
},
})
variables = { ...variables, id: 'closed-group-post' }
})
it('can not be pinned', async () => {
await expect(mutate({ mutation: pinPost, variables })).resolves.toMatchObject({
data: {
pinPost: null,
},
errors: undefined,
})
})
})
describe('post in hidden group', () => {
beforeEach(async () => {
await mutate({
mutation: CreateGroup,
variables: {
name: 'Hidden Group',
id: 'hidden-group',
about: 'This is a hidden group',
groupType: 'hidden',
actionRadius: 'regional',
description:
'This is a hidden group to test if the posts of this group can be pinned.',
categoryIds,
},
})
await mutate({
mutation: CreatePost,
variables: {
id: 'hidden-group-post',
title: 'Hidden group post',
content: 'This is a post in a hidden group',
groupId: 'hidden-group',
categoryIds,
},
})
variables = { ...variables, id: 'hidden-group-post' }
})
it('can not be pinned', async () => {
await expect(mutate({ mutation: pinPost, variables })).resolves.toMatchObject({
data: {
pinPost: null,
},
errors: undefined,
})
})
})
describe('PostOrdering', () => {
beforeEach(async () => {
await Factory.build('post', {
id: 'im-a-pinned-post',
createdAt: '2019-11-22T17:26:29.070Z',
pinned: true,
})
await Factory.build('post', {
id: 'i-was-created-before-pinned-post',
// fairly old, so this should be 3rd
createdAt: '2019-10-22T17:26:29.070Z',
})
})
describe('order by `pinned_asc` and `createdAt_desc`', () => {
beforeEach(() => {
// this is the ordering in the frontend
variables = { orderBy: ['pinned_asc', 'createdAt_desc'] }
})
it('pinned post appear first even when created before other posts', async () => {
await expect(query({ query: Post, variables })).resolves.toMatchObject({
data: {
Post: [
{
id: 'im-a-pinned-post',
pinned: true,
createdAt: '2019-11-22T17:26:29.070Z',
pinnedAt: expect.any(String),
},
{
id: 'p9876',
pinned: null,
createdAt: expect.any(String),
pinnedAt: null,
},
{
id: 'i-was-created-before-pinned-post',
pinned: null,
createdAt: '2019-10-22T17:26:29.070Z',
pinnedAt: null,
},
],
},
errors: undefined,
})
})
})
})
})
describe('MAX_PINNED_POSTS = 3', () => {
const postsPinnedCountsQuery = `query { PostsPinnedCounts { maxPinnedPosts, currentlyPinnedPosts } }`
beforeEach(async () => {
config = { ...defaultConfig, MAX_PINNED_POSTS: 3 }
await Factory.build(
'post',
{
id: 'first-post',
createdAt: '2019-10-22T17:26:29.070Z',
},
{
author: admin,
},
)
await Factory.build(
'post',
{
id: 'second-post',
createdAt: '2018-10-22T17:26:29.070Z',
},
{
author: admin,
},
)
await Factory.build(
'post',
{
id: 'third-post',
createdAt: '2017-10-22T17:26:29.070Z',
},
{
author: admin,
},
)
await Factory.build(
'post',
{
id: 'another-post',
},
{
author: admin,
},
)
})
describe('first post', () => {
let result
beforeEach(async () => {
variables = { ...variables, id: 'first-post' }
result = await mutate({ mutation: pinPost, variables })
})
it('pins the first post', () => {
expect(result).toMatchObject({
data: {
pinPost: {
id: 'first-post',
pinned: true,
pinnedAt: expect.any(String),
pinnedBy: {
id: 'current-user',
},
},
},
})
})
it('returns the correct counts', async () => {
await expect(
query({
query: postsPinnedCountsQuery,
}),
).resolves.toMatchObject({
data: {
PostsPinnedCounts: {
maxPinnedPosts: 3,
currentlyPinnedPosts: 1,
},
},
})
})
describe('second post', () => {
beforeEach(async () => {
variables = { ...variables, id: 'second-post' }
result = await mutate({ mutation: pinPost, variables })
})
it('pins the second post', () => {
expect(result).toMatchObject({
data: {
pinPost: {
id: 'second-post',
pinned: true,
pinnedAt: expect.any(String),
pinnedBy: {
id: 'current-user',
},
},
},
})
})
it('returns the correct counts', async () => {
await expect(
query({
query: postsPinnedCountsQuery,
}),
).resolves.toMatchObject({
data: {
PostsPinnedCounts: {
maxPinnedPosts: 3,
currentlyPinnedPosts: 2,
},
},
})
})
describe('third post', () => {
beforeEach(async () => {
variables = { ...variables, id: 'third-post' }
result = await mutate({ mutation: pinPost, variables })
})
it('pins the second post', () => {
expect(result).toMatchObject({
data: {
pinPost: {
id: 'third-post',
pinned: true,
pinnedAt: expect.any(String),
pinnedBy: {
id: 'current-user',
},
},
},
})
})
it('returns the correct counts', async () => {
await expect(
query({
query: postsPinnedCountsQuery,
}),
).resolves.toMatchObject({
data: {
PostsPinnedCounts: {
maxPinnedPosts: 3,
currentlyPinnedPosts: 3,
},
},
})
})
describe('another post', () => {
beforeEach(async () => {
variables = { ...variables, id: 'another-post' }
result = await mutate({ mutation: pinPost, variables })
})
it('throws with max pinned posts is reached', () => {
expect(result).toMatchObject({
data: { pinPost: null },
errors: [{ message: 'Max number of pinned posts is reached!' }],
})
})
})
describe('post ordering', () => {
beforeEach(() => {
// this is the ordering in the frontend
variables = { orderBy: ['pinned_asc', 'createdAt_desc'] }
})
it('places the pinned posts first, though they are much older', async () => {
await expect(query({ query: Post, variables })).resolves.toMatchObject({
data: {
Post: [
{
id: 'first-post',
pinned: true,
pinnedAt: expect.any(String),
createdAt: '2019-10-22T17:26:29.070Z',
},
{
id: 'second-post',
pinned: true,
pinnedAt: expect.any(String),
createdAt: '2018-10-22T17:26:29.070Z',
},
{
id: 'third-post',
pinned: true,
pinnedAt: expect.any(String),
createdAt: '2017-10-22T17:26:29.070Z',
},
{
id: 'another-post',
pinned: null,
pinnedAt: null,
createdAt: expect.any(String),
},
{
id: 'p9876',
pinned: null,
pinnedAt: null,
createdAt: expect.any(String),
},
],
},
errors: undefined,
})
})
})
})
})
})
})
})
})
describe('unpin posts', () => {
let pinnedPost
beforeEach(async () => {
pinnedPost = await Factory.build('post', { id: 'post-to-be-unpinned' })
variables = {
id: 'post-to-be-unpinned',
}
})
describe('unauthenticated', () => {
it('throws authorization error', async () => {
authenticatedUser = null
await expect(mutate({ mutation: unpinPost, variables })).resolves.toMatchObject({
errors: [{ message: 'Not Authorized!' }],
data: { unpinPost: null },
})
})
})
describe('users cannot unpin posts', () => {
it('throws authorization error', async () => {
await expect(mutate({ mutation: unpinPost, variables })).resolves.toMatchObject({
errors: [{ message: 'Not Authorized!' }],
data: { unpinPost: null },
})
})
})
describe('moderators cannot unpin posts', () => {
let moderator
beforeEach(async () => {
moderator = await user.update({ role: 'moderator', updatedAt: new Date().toISOString() })
authenticatedUser = await moderator.toJson()
})
it('throws authorization error', async () => {
await expect(mutate({ mutation: unpinPost, variables })).resolves.toMatchObject({
errors: [{ message: 'Not Authorized!' }],
data: { unpinPost: null },
})
})
})
describe('admin can unpin posts', () => {
let admin
beforeEach(async () => {
admin = await user.update({
role: 'admin',
name: 'Admin',
updatedAt: new Date().toISOString(),
})
authenticatedUser = await admin.toJson()
await admin.relateTo(pinnedPost, 'pinned', { createdAt: new Date().toISOString() })
})
it('responds with the unpinned Post', async () => {
authenticatedUser = await admin.toJson()
const expected = {
data: {
unpinPost: {
id: 'post-to-be-unpinned',
pinnedBy: null,
pinnedAt: null,
},
},
errors: undefined,
}
await expect(mutate({ mutation: unpinPost, variables })).resolves.toMatchObject(expected)
})
it('unsets `pinned` property', async () => {
const expected = {
data: {
unpinPost: {
id: 'post-to-be-unpinned',
pinned: null,
},
},
errors: undefined,
}
await expect(mutate({ mutation: unpinPost, variables })).resolves.toMatchObject(expected)
})
})
})
describe('DeletePost', () => {
let author
beforeEach(async () => {
author = await Factory.build('user')
await Factory.build(
'post',
{
id: 'p4711',
title: 'I will be deleted',
content: 'To be deleted',
},
{
image: Factory.build('image', {
url: 'http://localhost/path/to/some/image',
}),
author,
categoryIds,
},
)
variables = { ...variables, id: 'p4711' }
})
describe('unauthenticated', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({ mutation: DeletePost, variables })
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
describe('authenticated but not the author', () => {
beforeEach(async () => {
authenticatedUser = await user.toJson()
})
it('throws authorization error', async () => {
const { errors } = await mutate({ mutation: DeletePost, variables })
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
describe('authenticated as author', () => {
beforeEach(async () => {
authenticatedUser = await author.toJson()
})
it('marks the post as deleted and blacks out attributes', async () => {
const expected = {
data: {
DeletePost: {
id: 'p4711',
deleted: true,
content: 'UNAVAILABLE',
contentExcerpt: 'UNAVAILABLE',
image: null,
comments: [],
},
},
}
await expect(mutate({ mutation: DeletePost, variables })).resolves.toMatchObject(expected)
})
describe('if there are comments on the post', () => {
beforeEach(async () => {
await Factory.build(
'comment',
{
content: 'to be deleted comment content',
contentExcerpt: 'to be deleted comment content',
},
{
postId: 'p4711',
},
)
})
it('marks the comments as deleted', async () => {
const expected = {
data: {
DeletePost: {
id: 'p4711',
deleted: true,
content: 'UNAVAILABLE',
contentExcerpt: 'UNAVAILABLE',
image: null,
comments: [
{
deleted: true,
// Should we black out the comment content in the database, too?
content: 'UNAVAILABLE',
contentExcerpt: 'UNAVAILABLE',
},
],
},
},
}
await expect(mutate({ mutation: DeletePost, variables })).resolves.toMatchObject(expected)
})
})
})
})
describe('emotions', () => {
let author, postToEmote
beforeEach(async () => {
author = await database.neode.create('User', { id: 'u257' })
postToEmote = await Factory.build(
'post',
{
id: 'p1376',
},
{
author,
categoryIds,
},
)
variables = {
...variables,
to: { id: 'p1376' },
data: { emotion: 'happy' },
}
})
describe('AddPostEmotions', () => {
let postsEmotionsQueryVariables
beforeEach(() => {
postsEmotionsQueryVariables = { id: 'p1376' }
})
describe('unauthenticated', () => {
beforeEach(() => {
authenticatedUser = null
})
it('throws authorization error', async () => {
const addPostEmotions = await mutate({
mutation: AddPostEmotions,
variables,
})
expect(addPostEmotions.errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
describe('authenticated and not the author', () => {
beforeEach(async () => {
authenticatedUser = await user.toJson()
})
it('adds an emotion to the post', async () => {
const expected = {
data: {
AddPostEmotions: {
from: { id: 'current-user' },
to: { id: 'p1376' },
emotion: 'happy',
},
},
}
await expect(mutate({ mutation: AddPostEmotions, variables })).resolves.toEqual(
expect.objectContaining(expected),
)
})
it('limits the addition of the same emotion to 1', async () => {
const expected = {
data: {
Post: [
{
emotionsCount: 1,
},
],
},
}
await mutate({ mutation: AddPostEmotions, variables })
await mutate({ mutation: AddPostEmotions, variables })
await expect(
query({ query: Post, variables: postsEmotionsQueryVariables }),
).resolves.toMatchObject(expected)
})
it('allows a user to add more than one emotion', async () => {
const expected = {
data: {
Post: [
{
emotions: expect.arrayContaining([
{ emotion: 'happy', User: { id: 'current-user' } },
{ emotion: 'surprised', User: { id: 'current-user' } },
]),
},
],
},
}
await mutate({ mutation: AddPostEmotions, variables })
variables = { ...variables, data: { emotion: 'surprised' } }
await mutate({ mutation: AddPostEmotions, variables })
await expect(
query({ query: Post, variables: postsEmotionsQueryVariables }),
).resolves.toMatchObject(expected)
})
})
describe('authenticated as author', () => {
beforeEach(async () => {
authenticatedUser = await author.toJson()
})
it('adds an emotion to the post', async () => {
const expected = {
data: {
AddPostEmotions: {
from: { id: 'u257' },
to: { id: 'p1376' },
emotion: 'happy',
},
},
}
await expect(mutate({ mutation: AddPostEmotions, variables })).resolves.toEqual(
expect.objectContaining(expected),
)
})
})
})
describe('RemovePostEmotions', () => {
let removePostEmotionsVariables, postsEmotionsQueryVariables
beforeEach(async () => {
await author.relateTo(postToEmote, 'emoted', { emotion: 'happy' })
await user.relateTo(postToEmote, 'emoted', { emotion: 'cry' })
postsEmotionsQueryVariables = { id: 'p1376' }
removePostEmotionsVariables = {
to: { id: 'p1376' },
data: { emotion: 'cry' },
}
})
describe('unauthenticated', () => {
beforeEach(() => {
authenticatedUser = null
})
it('throws authorization error', async () => {
const removePostEmotions = await mutate({
mutation: RemovePostEmotions,
variables: removePostEmotionsVariables,
})
expect(removePostEmotions.errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
describe('authenticated', () => {
describe('but not the emoter', () => {
beforeEach(async () => {
authenticatedUser = await author.toJson()
})
it('returns null if the emotion could not be found', async () => {
const removePostEmotions = await mutate({
mutation: RemovePostEmotions,
variables: removePostEmotionsVariables,
})
expect(removePostEmotions).toEqual(
expect.objectContaining({ data: { RemovePostEmotions: null } }),
)
})
})
describe('as the emoter', () => {
beforeEach(async () => {
authenticatedUser = await user.toJson()
})
it('removes an emotion from a post', async () => {
const expected = {
data: {
RemovePostEmotions: {
to: { id: 'p1376' },
from: { id: 'current-user' },
emotion: 'cry',
},
},
}
await expect(
mutate({
mutation: RemovePostEmotions,
variables: removePostEmotionsVariables,
}),
).resolves.toEqual(expect.objectContaining(expected))
})
it('removes only the requested emotion, not all emotions', async () => {
const expectedEmotions = [{ emotion: 'happy', User: { id: 'u257' } }]
const expectedResponse = {
data: { Post: [{ emotions: expect.arrayContaining(expectedEmotions) }] },
}
await mutate({
mutation: RemovePostEmotions,
variables: removePostEmotionsVariables,
})
await expect(
query({ query: Post, variables: postsEmotionsQueryVariables }),
).resolves.toMatchObject(expectedResponse)
})
})
})
})
describe('posts emotions count', () => {
let PostsEmotionsCountByEmotionVariables
let PostsEmotionsByCurrentUserVariables
beforeEach(async () => {
await user.relateTo(postToEmote, 'emoted', { emotion: 'cry' })
PostsEmotionsCountByEmotionVariables = {
postId: 'p1376',
data: { emotion: 'cry' },
}
PostsEmotionsByCurrentUserVariables = { postId: 'p1376' }
})
describe('PostsEmotionsCountByEmotion', () => {
it("returns a post's emotions count", async () => {
const expectedResponse = { data: { PostsEmotionsCountByEmotion: 1 } }
await expect(
query({
query: PostsEmotionsCountByEmotion,
variables: PostsEmotionsCountByEmotionVariables,
}),
).resolves.toEqual(expect.objectContaining(expectedResponse))
})
})
describe('PostsEmotionsByCurrentUser', () => {
describe('authenticated', () => {
beforeEach(async () => {
authenticatedUser = await user.toJson()
})
it("returns a currentUser's emotions on a post", async () => {
const expectedResponse = { data: { PostsEmotionsByCurrentUser: ['cry'] } }
await expect(
query({
query: PostsEmotionsByCurrentUser,
variables: PostsEmotionsByCurrentUserVariables,
}),
).resolves.toEqual(expect.objectContaining(expectedResponse))
})
})
})
})
})