mirror of
https://github.com/IT4Change/Ocelot-Social.git
synced 2025-12-13 07:45:56 +00:00
This is a side quest of #8558. The motivation is to be able to do dependency injection in the tests without overwriting global data. I saw the first merge conflict from #8551 and voila: It seems @Mogge could have used this already. refactor: follow @Mogge's review See: https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8603#pullrequestreview-2880714796 refactor: better test helper methods wip: continue refactoring wip: continue posts continue wip: continue groups continue registration registration continue messages continue observeposts continue categories continue posts in groups continue invite codes refactor: continue notificationsMiddleware continue statistics spec followed-users online-status mentions-in-groups posts-in-groups email spec finish all tests improve typescript missed one test remove one more reference of CONFIG eliminate one more global import of CONFIG fix language spec test fix two more test suites refactor: completely mock out 3rd part API request refactor test fixed user_management spec fixed more locatoin specs install types for jsonwebtoken one more fetchmock fixed one more suite fix one more spec yet another spec fix spec delete whitespaces remove beforeAll that the same as the default fix merge conflict fix e2e test refactor: use single callback function for `context` setup refactor: display logs from backend during CI Because why not? fix seeds fix login refactor: one unnecessary naming refactor: better editor support refactor: fail early Interestingly, I've had to destructure `context.user` in order to make typescript happy. Weird. refactor: undo changes to workflows - no effect We're running in `--detached` mode on CI, so I guess we won't be able to see the logs anyways. refactor: remove fetch from context after review See: refactor: found an easier way for required props Co-authored-by: Max <maxharz@gmail.com> Co-authored-by: Ulf Gebhardt <ulf.gebhardt@webcraft-media.de>
2522 lines
70 KiB
TypeScript
2522 lines
70 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 gql from 'graphql-tag'
|
|
|
|
import Factory, { cleanDatabase } from '@db/factories'
|
|
import Image from '@db/models/Image'
|
|
import { createGroupMutation } from '@graphql/queries/createGroupMutation'
|
|
import { createPostMutation } from '@graphql/queries/createPostMutation'
|
|
import { Post } from '@graphql/queries/Post'
|
|
import { pushPost } from '@graphql/queries/pushPost'
|
|
import { unpushPost } from '@graphql/queries/unpushPost'
|
|
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 () => {
|
|
const postQueryNoFilters = gql`
|
|
query Post($filter: _PostFilter) {
|
|
Post(filter: $filter) {
|
|
id
|
|
}
|
|
}
|
|
`
|
|
const expected = [{ id: 'happy-post' }, { id: 'cry-post' }, { id: 'post-by-followed-user' }]
|
|
variables = { filter: {} }
|
|
await expect(query({ query: postQueryNoFilters, variables })).resolves.toMatchObject({
|
|
data: {
|
|
Post: expect.arrayContaining(expected),
|
|
},
|
|
})
|
|
})
|
|
})
|
|
|
|
/* 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', () => {
|
|
const postQueryFilteredByEmotions = gql`
|
|
query Post($filter: _PostFilter) {
|
|
Post(filter: $filter) {
|
|
id
|
|
emotions {
|
|
emotion
|
|
}
|
|
}
|
|
}
|
|
`
|
|
|
|
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: postQueryFilteredByEmotions, variables }),
|
|
).resolves.toMatchObject(expected)
|
|
})
|
|
|
|
it('filters by multiple emotions', async () => {
|
|
const expected = [
|
|
{
|
|
id: 'happy-post',
|
|
emotions: [{ emotion: 'happy' }],
|
|
},
|
|
{
|
|
id: 'cry-post',
|
|
emotions: [{ emotion: 'cry' }],
|
|
},
|
|
]
|
|
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: postQueryFilteredByEmotions, variables }),
|
|
).resolves.toMatchObject({
|
|
data: {
|
|
Post: expect.arrayContaining(expected),
|
|
},
|
|
errors: undefined,
|
|
})
|
|
})
|
|
})
|
|
|
|
it('by followed-by', async () => {
|
|
const postQueryFilteredByUsersFollowed = gql`
|
|
query Post($filter: _PostFilter) {
|
|
Post(filter: $filter) {
|
|
id
|
|
author {
|
|
name
|
|
}
|
|
}
|
|
}
|
|
`
|
|
|
|
await user.relateTo(followedUser, 'following')
|
|
variables = { filter: { author: { followedBy_some: { id: 'current-user' } } } }
|
|
await expect(
|
|
query({ query: postQueryFilteredByUsersFollowed, 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: createPostMutation(), 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: createPostMutation(), 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: createPostMutation(), variables })).resolves.toMatchObject(
|
|
expected,
|
|
)
|
|
})
|
|
|
|
it('`disabled` and `deleted` default to `false`', async () => {
|
|
const expected = { data: { CreatePost: { disabled: false, deleted: false } } }
|
|
await expect(mutate({ mutation: createPostMutation(), variables })).resolves.toMatchObject(
|
|
expected,
|
|
)
|
|
})
|
|
|
|
it('has label "Article" as default', async () => {
|
|
await expect(mutate({ mutation: createPostMutation(), variables })).resolves.toMatchObject({
|
|
data: { CreatePost: { postType: ['Article'] } },
|
|
})
|
|
})
|
|
|
|
describe('with invalid post type', () => {
|
|
it('throws an error', async () => {
|
|
await expect(
|
|
mutate({
|
|
mutation: createPostMutation(),
|
|
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: createPostMutation(),
|
|
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: createPostMutation(),
|
|
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: createPostMutation(),
|
|
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: createPostMutation(),
|
|
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: createPostMutation(),
|
|
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: createPostMutation(),
|
|
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: createPostMutation(),
|
|
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: createPostMutation(),
|
|
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: createPostMutation(),
|
|
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: createPostMutation(),
|
|
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: createPostMutation(),
|
|
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: createPostMutation(),
|
|
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
|
|
const updatePostMutation = gql`
|
|
mutation (
|
|
$id: ID!
|
|
$title: String!
|
|
$content: String!
|
|
$image: ImageInput
|
|
$categoryIds: [ID]
|
|
$postType: PostType
|
|
$eventInput: _EventInput
|
|
) {
|
|
UpdatePost(
|
|
id: $id
|
|
title: $title
|
|
content: $content
|
|
image: $image
|
|
categoryIds: $categoryIds
|
|
postType: $postType
|
|
eventInput: $eventInput
|
|
) {
|
|
id
|
|
title
|
|
content
|
|
author {
|
|
name
|
|
slug
|
|
}
|
|
createdAt
|
|
updatedAt
|
|
categories {
|
|
id
|
|
}
|
|
postType
|
|
eventStart
|
|
eventLocationName
|
|
eventVenue
|
|
eventLocation {
|
|
lng
|
|
lat
|
|
}
|
|
}
|
|
}
|
|
`
|
|
beforeEach(async () => {
|
|
author = await Factory.build('user', { slug: 'the-author' })
|
|
authenticatedUser = await author.toJson()
|
|
const { data } = await mutate({
|
|
mutation: createPostMutation(),
|
|
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: updatePostMutation, 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: updatePostMutation, 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: updatePostMutation, 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: updatePostMutation, variables })).resolves.toMatchObject(
|
|
expected,
|
|
)
|
|
})
|
|
|
|
it('updates the updatedAt attribute', async () => {
|
|
const {
|
|
data: { UpdatePost },
|
|
} = (await mutate({ mutation: updatePostMutation, variables })) as any // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
expect(UpdatePost.updatedAt).toBeTruthy()
|
|
expect(Date.parse(UpdatePost.updatedAt)).toEqual(expect.any(Number))
|
|
expect(newlyCreatedPost.updatedAt).not.toEqual(UpdatePost.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: updatePostMutation, 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: updatePostMutation, 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: updatePostMutation,
|
|
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: updatePostMutation,
|
|
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: updatePostMutation,
|
|
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: updatePostMutation,
|
|
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: updatePostMutation,
|
|
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: updatePostMutation,
|
|
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: updatePostMutation, 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: updatePostMutation, 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: updatePostMutation, 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
|
|
const pinPostMutation = gql`
|
|
mutation ($id: ID!) {
|
|
pinPost(id: $id) {
|
|
id
|
|
title
|
|
content
|
|
author {
|
|
name
|
|
slug
|
|
}
|
|
pinnedBy {
|
|
id
|
|
name
|
|
role
|
|
}
|
|
createdAt
|
|
updatedAt
|
|
pinnedAt
|
|
pinned
|
|
}
|
|
}
|
|
`
|
|
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: pinPostMutation, variables })).resolves.toMatchObject({
|
|
errors: [{ message: 'Not Authorized!' }],
|
|
data: { pinPost: null },
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('ordinary users', () => {
|
|
it('throws authorization error', async () => {
|
|
await expect(mutate({ mutation: pinPostMutation, 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: pinPostMutation, 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: pinPostMutation, 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: pinPostMutation, 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: pinPostMutation, 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: pinPostMutation, 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: pinPostMutation, 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: pinPostMutation, 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: pinPostMutation, 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: pinPostMutation, 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: pinPostMutation, 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: createGroupMutation(),
|
|
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: createPostMutation(),
|
|
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: pinPostMutation, 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: createGroupMutation(),
|
|
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: createPostMutation(),
|
|
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: pinPostMutation, variables })).resolves.toMatchObject({
|
|
data: {
|
|
pinPost: null,
|
|
},
|
|
errors: undefined,
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('post in hidden group', () => {
|
|
beforeEach(async () => {
|
|
await mutate({
|
|
mutation: createGroupMutation(),
|
|
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: createPostMutation(),
|
|
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: pinPostMutation, 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: pinPostMutation, 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: pinPostMutation, 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: pinPostMutation, 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: pinPostMutation, 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
|
|
const unpinPostMutation = gql`
|
|
mutation ($id: ID!) {
|
|
unpinPost(id: $id) {
|
|
id
|
|
title
|
|
content
|
|
author {
|
|
name
|
|
slug
|
|
}
|
|
pinnedBy {
|
|
id
|
|
name
|
|
role
|
|
}
|
|
createdAt
|
|
updatedAt
|
|
pinned
|
|
pinnedAt
|
|
}
|
|
}
|
|
`
|
|
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: unpinPostMutation, variables })).resolves.toMatchObject({
|
|
errors: [{ message: 'Not Authorized!' }],
|
|
data: { unpinPost: null },
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('users cannot unpin posts', () => {
|
|
it('throws authorization error', async () => {
|
|
await expect(mutate({ mutation: unpinPostMutation, 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: unpinPostMutation, 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: unpinPostMutation, 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: unpinPostMutation, variables })).resolves.toMatchObject(
|
|
expected,
|
|
)
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('DeletePost', () => {
|
|
let author
|
|
const deletePostMutation = gql`
|
|
mutation ($id: ID!) {
|
|
DeletePost(id: $id) {
|
|
id
|
|
deleted
|
|
content
|
|
contentExcerpt
|
|
image {
|
|
url
|
|
}
|
|
comments {
|
|
deleted
|
|
content
|
|
contentExcerpt
|
|
}
|
|
}
|
|
}
|
|
`
|
|
|
|
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: deletePostMutation, 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: deletePostMutation, 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: deletePostMutation, 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: deletePostMutation, variables })).resolves.toMatchObject(
|
|
expected,
|
|
)
|
|
})
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('emotions', () => {
|
|
let author, postToEmote
|
|
const PostsEmotionsCountQuery = gql`
|
|
query ($id: ID!) {
|
|
Post(id: $id) {
|
|
emotionsCount
|
|
}
|
|
}
|
|
`
|
|
const PostsEmotionsQuery = gql`
|
|
query ($id: ID!) {
|
|
Post(id: $id) {
|
|
emotions {
|
|
emotion
|
|
User {
|
|
id
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`
|
|
|
|
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', () => {
|
|
const addPostEmotionsMutation = gql`
|
|
mutation ($to: _PostInput!, $data: _EMOTEDInput!) {
|
|
AddPostEmotions(to: $to, data: $data) {
|
|
from {
|
|
id
|
|
}
|
|
to {
|
|
id
|
|
}
|
|
emotion
|
|
}
|
|
}
|
|
`
|
|
let postsEmotionsQueryVariables
|
|
|
|
beforeEach(() => {
|
|
postsEmotionsQueryVariables = { id: 'p1376' }
|
|
})
|
|
|
|
describe('unauthenticated', () => {
|
|
beforeEach(() => {
|
|
authenticatedUser = null
|
|
})
|
|
|
|
it('throws authorization error', async () => {
|
|
const addPostEmotions = await mutate({
|
|
mutation: addPostEmotionsMutation,
|
|
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: addPostEmotionsMutation, 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: addPostEmotionsMutation, variables })
|
|
await mutate({ mutation: addPostEmotionsMutation, variables })
|
|
await expect(
|
|
query({ query: PostsEmotionsCountQuery, variables: postsEmotionsQueryVariables }),
|
|
).resolves.toEqual(expect.objectContaining(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: addPostEmotionsMutation, variables })
|
|
variables = { ...variables, data: { emotion: 'surprised' } }
|
|
await mutate({ mutation: addPostEmotionsMutation, variables })
|
|
await expect(
|
|
query({ query: PostsEmotionsQuery, variables: postsEmotionsQueryVariables }),
|
|
).resolves.toEqual(expect.objectContaining(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: addPostEmotionsMutation, variables })).resolves.toEqual(
|
|
expect.objectContaining(expected),
|
|
)
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('RemovePostEmotions', () => {
|
|
let removePostEmotionsVariables, postsEmotionsQueryVariables
|
|
const removePostEmotionsMutation = gql`
|
|
mutation ($to: _PostInput!, $data: _EMOTEDInput!) {
|
|
RemovePostEmotions(to: $to, data: $data) {
|
|
from {
|
|
id
|
|
}
|
|
to {
|
|
id
|
|
}
|
|
emotion
|
|
}
|
|
}
|
|
`
|
|
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: removePostEmotionsMutation,
|
|
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: removePostEmotionsMutation,
|
|
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: removePostEmotionsMutation,
|
|
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: removePostEmotionsMutation,
|
|
variables: removePostEmotionsVariables,
|
|
})
|
|
await expect(
|
|
query({ query: PostsEmotionsQuery, variables: postsEmotionsQueryVariables }),
|
|
).resolves.toEqual(expect.objectContaining(expectedResponse))
|
|
})
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('posts emotions count', () => {
|
|
let PostsEmotionsCountByEmotionVariables
|
|
let PostsEmotionsByCurrentUserVariables
|
|
|
|
const PostsEmotionsCountByEmotionQuery = gql`
|
|
query ($postId: ID!, $data: _EMOTEDInput!) {
|
|
PostsEmotionsCountByEmotion(postId: $postId, data: $data)
|
|
}
|
|
`
|
|
|
|
const PostsEmotionsByCurrentUserQuery = gql`
|
|
query ($postId: ID!) {
|
|
PostsEmotionsByCurrentUser(postId: $postId)
|
|
}
|
|
`
|
|
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: PostsEmotionsCountByEmotionQuery,
|
|
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: PostsEmotionsByCurrentUserQuery,
|
|
variables: PostsEmotionsByCurrentUserVariables,
|
|
}),
|
|
).resolves.toEqual(expect.objectContaining(expectedResponse))
|
|
})
|
|
})
|
|
})
|
|
})
|
|
})
|