diff --git a/backend/jest.config.cjs b/backend/jest.config.cjs index c78de9155..285654e3a 100644 --- a/backend/jest.config.cjs +++ b/backend/jest.config.cjs @@ -18,7 +18,7 @@ module.exports = { ], coverageThreshold: { global: { - lines: 92, + lines: 93, }, }, testMatch: ['**/src/**/?(*.)+(spec|test).ts?(x)'], diff --git a/backend/src/db/models/Post.ts b/backend/src/db/models/Post.ts index 6697faa30..5a42a4182 100644 --- a/backend/src/db/models/Post.ts +++ b/backend/src/db/models/Post.ts @@ -18,7 +18,7 @@ export default { }, title: { type: 'string', disallow: [null], min: 3 }, slug: { type: 'string', allow: [null], unique: 'true' }, - content: { type: 'string', disallow: [null], min: 3 }, + content: { type: 'string', disallow: [null], required: true, min: 3 }, contentExcerpt: { type: 'string', allow: [null] }, deleted: { type: 'boolean', default: false }, disabled: { type: 'boolean', default: false }, diff --git a/backend/src/graphql/queries/CreateComment.ts b/backend/src/graphql/queries/CreateComment.ts index c236b9826..c80678331 100644 --- a/backend/src/graphql/queries/CreateComment.ts +++ b/backend/src/graphql/queries/CreateComment.ts @@ -8,6 +8,8 @@ export const CreateComment = gql` author { name } + isPostObservedByMe + postObservingUsersCount } } ` diff --git a/backend/src/graphql/queries/CreatePost.ts b/backend/src/graphql/queries/CreatePost.ts index 3c0ed8384..3ddf35191 100644 --- a/backend/src/graphql/queries/CreatePost.ts +++ b/backend/src/graphql/queries/CreatePost.ts @@ -45,6 +45,7 @@ export const CreatePost = gql` } isObservedByMe observingUsersCount + language } } ` diff --git a/backend/src/graphql/queries/CreateSocialMedia.ts b/backend/src/graphql/queries/CreateSocialMedia.ts new file mode 100644 index 000000000..3058415b4 --- /dev/null +++ b/backend/src/graphql/queries/CreateSocialMedia.ts @@ -0,0 +1,14 @@ +import gql from 'graphql-tag' + +export const CreateSocialMedia = gql` + mutation ($url: String!) { + CreateSocialMedia(url: $url) { + id + url + url + ownedBy { + name + } + } + } +` diff --git a/backend/src/graphql/queries/Post.ts b/backend/src/graphql/queries/Post.ts index 7bebcdcf1..e1bb7fe94 100644 --- a/backend/src/graphql/queries/Post.ts +++ b/backend/src/graphql/queries/Post.ts @@ -6,10 +6,34 @@ export const Post = gql` id title content + contentExcerpt eventStart pinned createdAt pinnedAt + isObservedByMe + observingUsersCount + clickedCount + emotionsCount + emotions { + emotion + User { + id + } + } + author { + id + name + } + shoutedBy { + id + } + tags { + id + } + comments { + content + } } } ` diff --git a/backend/src/graphql/queries/SignupVerification.ts b/backend/src/graphql/queries/SignupVerification.ts index a3fb9bec4..08ff8009f 100644 --- a/backend/src/graphql/queries/SignupVerification.ts +++ b/backend/src/graphql/queries/SignupVerification.ts @@ -8,6 +8,8 @@ export const SignupVerification = gql` $slug: String $nonce: String! $termsAndConditionsAgreedVersion: String! + $about: String + $locale: String ) { SignupVerification( email: $email @@ -16,9 +18,13 @@ export const SignupVerification = gql` slug: $slug nonce: $nonce termsAndConditionsAgreedVersion: $termsAndConditionsAgreedVersion + about: $about + locale: $locale ) { id slug + termsAndConditionsAgreedVersion + termsAndConditionsAgreedAt } } ` diff --git a/backend/src/graphql/queries/UpdatePost.ts b/backend/src/graphql/queries/UpdatePost.ts index 978ca8a04..73f70fdeb 100644 --- a/backend/src/graphql/queries/UpdatePost.ts +++ b/backend/src/graphql/queries/UpdatePost.ts @@ -1,10 +1,44 @@ import gql from 'graphql-tag' export const UpdatePost = gql` - mutation ($id: ID!, $title: String!, $postContent: String!, $categoryIds: [ID]!) { - UpdatePost(id: $id, content: $postContent, title: $title, categoryIds: $categoryIds) { + 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 + } } } ` diff --git a/backend/src/graphql/queries/UpdateUser.ts b/backend/src/graphql/queries/UpdateUser.ts index cb201f0cb..d58cf68a1 100644 --- a/backend/src/graphql/queries/UpdateUser.ts +++ b/backend/src/graphql/queries/UpdateUser.ts @@ -1,9 +1,38 @@ import gql from 'graphql-tag' export const UpdateUser = gql` - mutation ($id: ID!, $name: String) { - UpdateUser(id: $id, name: $name) { + mutation ( + $id: ID! + $name: String + $termsAndConditionsAgreedVersion: String + $locationName: String # empty string '' sets it to null + $emailNotificationSettings: [EmailNotificationSettingsInput] + ) { + UpdateUser( + id: $id + name: $name + termsAndConditionsAgreedVersion: $termsAndConditionsAgreedVersion + locationName: $locationName + emailNotificationSettings: $emailNotificationSettings + ) { + id name + termsAndConditionsAgreedVersion + termsAndConditionsAgreedAt + locationName + location { + name + nameDE + nameEN + nameRU + } + emailNotificationSettings { + type + settings { + name + value + } + } } } ` diff --git a/backend/src/graphql/queries/User.ts b/backend/src/graphql/queries/User.ts index 567ea8386..111b1fd74 100644 --- a/backend/src/graphql/queries/User.ts +++ b/backend/src/graphql/queries/User.ts @@ -1,9 +1,165 @@ import gql from 'graphql-tag' export const User = gql` - query ($name: String) { - User(name: $name) { - email + query ($id: ID, $name: String, $email: String) { + User(id: $id, name: $name, email: $email) { + id + name + badgeTrophiesCount + badgeTrophies { + id + } + badgeVerification { + id + isDefault + } + badgeTrophiesSelected { + id + isDefault + } + followedBy { + id + } + followedByCurrentUser + following { + name + slug + about + avatar { + url + } + comments { + content + contentExcerpt + } + contributions { + title + slug + image { + url + } + content + contentExcerpt + } + } + isMuted + isBlocked + location { + distanceToMe + } + activeCategories + } + } +` + +export const UserEmailNotificationSettings = gql` + query ($id: ID, $name: String, $email: String) { + User(id: $id, name: $name, email: $email) { + id + name + badgeTrophiesCount + badgeTrophies { + id + } + badgeVerification { + id + isDefault + } + badgeTrophiesSelected { + id + isDefault + } + followedBy { + id + } + followedByCurrentUser + following { + name + slug + about + avatar { + url + } + comments { + content + contentExcerpt + } + contributions { + title + slug + image { + url + } + content + contentExcerpt + } + } + isMuted + isBlocked + location { + distanceToMe + } + emailNotificationSettings { + type + settings { + name + value + } + } + activeCategories + } + } +` + +export const UserEmail = gql` + query ($id: ID, $name: String, $email: String) { + User(id: $id, name: $name, email: $email) { + id + name + email + badgeTrophiesCount + badgeTrophies { + id + } + badgeVerification { + id + isDefault + } + badgeTrophiesSelected { + id + isDefault + } + followedBy { + id + } + followedByCurrentUser + following { + name + slug + about + avatar { + url + } + comments { + content + contentExcerpt + } + contributions { + title + slug + image { + url + } + content + contentExcerpt + } + } + isMuted + isBlocked + location { + distanceToMe + } + activeCategories } } ` diff --git a/backend/src/graphql/queries/currentUser.ts b/backend/src/graphql/queries/currentUser.ts index 753fe5288..9cdaf64c7 100644 --- a/backend/src/graphql/queries/currentUser.ts +++ b/backend/src/graphql/queries/currentUser.ts @@ -3,6 +3,15 @@ import gql from 'graphql-tag' export const currentUser = gql` query currentUser { currentUser { + id + slug + name + avatar { + url + } + email + role + activeCategories following { name } diff --git a/backend/src/graphql/queries/followUser.ts b/backend/src/graphql/queries/followUser.ts index f82656e6e..9fb71b7d4 100644 --- a/backend/src/graphql/queries/followUser.ts +++ b/backend/src/graphql/queries/followUser.ts @@ -3,6 +3,7 @@ import gql from 'graphql-tag' export const followUser = gql` mutation ($id: ID!) { followUser(id: $id) { + id name followedBy { id diff --git a/backend/src/graphql/queries/markAllAsRead.ts b/backend/src/graphql/queries/markAllAsRead.ts index 47b6c0779..c86405941 100644 --- a/backend/src/graphql/queries/markAllAsRead.ts +++ b/backend/src/graphql/queries/markAllAsRead.ts @@ -3,6 +3,7 @@ import gql from 'graphql-tag' export const markAllAsRead = gql` mutation { markAllAsRead { + id from { __typename ... on Post { diff --git a/backend/src/graphql/queries/notifications.ts b/backend/src/graphql/queries/notifications.ts index dc1aadc0d..23ab65e9b 100644 --- a/backend/src/graphql/queries/notifications.ts +++ b/backend/src/graphql/queries/notifications.ts @@ -3,14 +3,23 @@ import gql from 'graphql-tag' export const notifications = gql` query ($read: Boolean, $orderBy: NotificationOrdering) { notifications(read: $read, orderBy: $orderBy) { + reason + relatedUser { + id + } from { __typename ... on Post { + id content } ... on Comment { + id content } + ... on Group { + id + } } read createdAt diff --git a/backend/src/graphql/queries/searchPosts.ts b/backend/src/graphql/queries/searchPosts.ts index 121d4783f..0bdea80ff 100644 --- a/backend/src/graphql/queries/searchPosts.ts +++ b/backend/src/graphql/queries/searchPosts.ts @@ -5,6 +5,7 @@ export const searchPosts = gql` searchPosts(query: $query, firstPosts: $firstPosts, postsOffset: $postsOffset) { postCount posts { + __typename id title content diff --git a/backend/src/graphql/resolvers/badges.spec.ts b/backend/src/graphql/resolvers/badges.spec.ts index a351fd2cf..f41c2f1bc 100644 --- a/backend/src/graphql/resolvers/badges.spec.ts +++ b/backend/src/graphql/resolvers/badges.spec.ts @@ -1,14 +1,13 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ -import gql from 'graphql-tag' - import { TROPHY_BADGES_SELECTED_MAX } from '@constants/badges' import Factory, { cleanDatabase } from '@db/factories' import { revokeBadge } from '@graphql/queries/revokeBadge' import { rewardTrophyBadge } from '@graphql/queries/rewardTrophyBadge' import { setTrophyBadgeSelected } from '@graphql/queries/setTrophyBadgeSelected' import { setVerificationBadge } from '@graphql/queries/setVerificationBadge' +import { User } from '@graphql/queries/User' import type { ApolloTestSetup } from '@root/test/helpers' import { createApolloTestSetup } from '@root/test/helpers' import type { Context } from '@src/context' @@ -800,24 +799,6 @@ describe('Badges', () => { describe('check test setup', () => { it('user has one badge and has it selected', async () => { authenticatedUser = await regularUser.toJson() - const userQuery = gql` - { - User(id: "regular-user-id") { - badgeTrophiesCount - badgeTrophies { - id - } - badgeVerification { - id - isDefault - } - badgeTrophiesSelected { - id - isDefault - } - } - } - ` const expected = { data: { User: [ @@ -871,7 +852,9 @@ describe('Badges', () => { }, errors: undefined, } - await expect(query({ query: userQuery })).resolves.toMatchObject(expected) + await expect( + query({ query: User, variables: { id: 'regular-user-id' } }), + ).resolves.toMatchObject(expected) }) }) diff --git a/backend/src/graphql/resolvers/follow.spec.ts b/backend/src/graphql/resolvers/follow.spec.ts index 311524caf..a3169db43 100644 --- a/backend/src/graphql/resolvers/follow.spec.ts +++ b/backend/src/graphql/resolvers/follow.spec.ts @@ -3,12 +3,12 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ import { createTestClient } from 'apollo-server-testing' -import gql from 'graphql-tag' import Factory, { cleanDatabase } from '@db/factories' import { getDriver, getNeode } from '@db/neo4j' import { followUser } from '@graphql/queries/followUser' import { unfollowUser } from '@graphql/queries/unfollowUser' +import { User } from '@graphql/queries/User' import createServer from '@src/server' const driver = getDriver() @@ -22,17 +22,6 @@ let user1 let user2 let variables -const userQuery = gql` - query ($id: ID) { - User(id: $id) { - followedBy { - id - } - followedByCurrentUser - } - } -` - beforeAll(async () => { await cleanDatabase() @@ -152,7 +141,7 @@ describe('follow', () => { } await expect( query({ - query: userQuery, + query: User, variables: { id: user1.id }, }), ).resolves.toMatchObject({ diff --git a/backend/src/graphql/resolvers/locations.spec.ts b/backend/src/graphql/resolvers/locations.spec.ts index 8b3c5b779..ad2fe05eb 100644 --- a/backend/src/graphql/resolvers/locations.spec.ts +++ b/backend/src/graphql/resolvers/locations.spec.ts @@ -2,10 +2,11 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ import { createTestClient } from 'apollo-server-testing' -import gql from 'graphql-tag' import Factory, { cleanDatabase } from '@db/factories' import { getNeode, getDriver } from '@db/neo4j' +import { UpdateUser } from '@graphql/queries/UpdateUser' +import { User } from '@graphql/queries/User' import createServer from '@src/server' let query, mutate, authenticatedUser @@ -22,6 +23,9 @@ beforeAll(async () => { driver, neode, user: authenticatedUser, + cypherParams: { + currentUserId: authenticatedUser ? authenticatedUser.id : null, + }, } }, }) @@ -43,17 +47,6 @@ describe('resolvers', () => { describe('Location', () => { describe('custom mutation, not handled by neo4j-graphql-js', () => { let variables - const updateUserMutation = gql` - mutation ($id: ID!, $name: String) { - UpdateUser(id: $id, name: $name) { - name - location { - name: nameRU - nameEN - } - } - } - ` beforeEach(async () => { variables = { @@ -78,12 +71,12 @@ describe('resolvers', () => { }) it('returns `null` if location translation is not available', async () => { - await expect(mutate({ mutation: updateUserMutation, variables })).resolves.toMatchObject({ + await expect(mutate({ mutation: UpdateUser, variables })).resolves.toMatchObject({ data: { UpdateUser: { name: 'John Doughnut', location: { - name: null, + nameRU: null, nameEN: 'Paris', }, }, @@ -95,15 +88,6 @@ describe('resolvers', () => { }) }) -const distanceToMeQuery = gql` - query ($id: ID!) { - User(id: $id) { - location { - distanceToMe - } - } - } -` let user, myPlaceUser, otherPlaceUser, noCordsPlaceUser, noPlaceUser describe('distanceToMe', () => { @@ -191,21 +175,19 @@ describe('distanceToMe', () => { authenticatedUser = await user.toJson() const targetUser = await user.toJson() await expect( - query({ query: distanceToMeQuery, variables: { id: targetUser.id } }), - ).resolves.toEqual( - expect.objectContaining({ - data: { - User: [ - { - location: { - distanceToMe: 0, - }, + query({ query: User, variables: { id: targetUser.id } }), + ).resolves.toMatchObject({ + data: { + User: [ + expect.objectContaining({ + location: { + distanceToMe: 0, }, - ], - }, - errors: undefined, - }), - ) + }), + ], + }, + errors: undefined, + }) }) }) @@ -214,21 +196,19 @@ describe('distanceToMe', () => { authenticatedUser = await user.toJson() const targetUser = await myPlaceUser.toJson() await expect( - query({ query: distanceToMeQuery, variables: { id: targetUser.id } }), - ).resolves.toEqual( - expect.objectContaining({ - data: { - User: [ - { - location: { - distanceToMe: 0, - }, + query({ query: User, variables: { id: targetUser.id } }), + ).resolves.toMatchObject({ + data: { + User: [ + expect.objectContaining({ + location: { + distanceToMe: 0, }, - ], - }, - errors: undefined, - }), - ) + }), + ], + }, + errors: undefined, + }) }) }) @@ -237,21 +217,19 @@ describe('distanceToMe', () => { authenticatedUser = await user.toJson() const targetUser = await otherPlaceUser.toJson() await expect( - query({ query: distanceToMeQuery, variables: { id: targetUser.id } }), - ).resolves.toEqual( - expect.objectContaining({ - data: { - User: [ - { - location: { - distanceToMe: 746, - }, + query({ query: User, variables: { id: targetUser.id } }), + ).resolves.toMatchObject({ + data: { + User: [ + expect.objectContaining({ + location: { + distanceToMe: 746, }, - ], - }, - errors: undefined, - }), - ) + }), + ], + }, + errors: undefined, + }) }) }) @@ -260,21 +238,19 @@ describe('distanceToMe', () => { authenticatedUser = await user.toJson() const targetUser = await noCordsPlaceUser.toJson() await expect( - query({ query: distanceToMeQuery, variables: { id: targetUser.id } }), - ).resolves.toEqual( - expect.objectContaining({ - data: { - User: [ - { - location: { - distanceToMe: null, - }, + query({ query: User, variables: { id: targetUser.id } }), + ).resolves.toMatchObject({ + data: { + User: [ + expect.objectContaining({ + location: { + distanceToMe: null, }, - ], - }, - errors: undefined, - }), - ) + }), + ], + }, + errors: undefined, + }) }) }) @@ -283,19 +259,17 @@ describe('distanceToMe', () => { authenticatedUser = await user.toJson() const targetUser = await noPlaceUser.toJson() await expect( - query({ query: distanceToMeQuery, variables: { id: targetUser.id } }), - ).resolves.toEqual( - expect.objectContaining({ - data: { - User: [ - { - location: null, - }, - ], - }, - errors: undefined, - }), - ) + query({ query: User, variables: { id: targetUser.id } }), + ).resolves.toMatchObject({ + data: { + User: [ + expect.objectContaining({ + location: null, + }), + ], + }, + errors: undefined, + }) }) }) }) diff --git a/backend/src/graphql/resolvers/notifications.spec.ts b/backend/src/graphql/resolvers/notifications.spec.ts index b12dfd41b..8d84a0527 100644 --- a/backend/src/graphql/resolvers/notifications.spec.ts +++ b/backend/src/graphql/resolvers/notifications.spec.ts @@ -165,44 +165,46 @@ describe('given some notifications', () => { describe('no filters', () => { it('returns all notifications of current user', async () => { - const expected = [ - { - from: { - __typename: 'Comment', - content: 'You have seen this comment mentioning already', - }, - read: true, - createdAt: '2019-08-30T15:33:48.651Z', - }, - { - from: { - __typename: 'Post', - content: 'Already seen post mention', - }, - read: true, - createdAt: '2019-08-30T17:33:48.651Z', - }, - { - from: { - __typename: 'Comment', - content: 'You have been mentioned in a comment', - }, - read: false, - createdAt: '2019-08-30T19:33:48.651Z', - }, - { - from: { - __typename: 'Post', - content: 'You have been mentioned in a post', - }, - read: false, - createdAt: '2019-08-31T17:33:48.651Z', - }, - ] - await expect(query({ query: notifications, variables })).resolves.toMatchObject({ data: { - notifications: expect.arrayContaining(expected), + notifications: expect.arrayContaining([ + expect.objectContaining({ + from: { + __typename: 'Comment', + content: 'You have seen this comment mentioning already', + id: 'c1', + }, + read: true, + createdAt: '2019-08-30T15:33:48.651Z', + }), + expect.objectContaining({ + from: { + __typename: 'Post', + content: 'Already seen post mention', + id: 'p2', + }, + read: true, + createdAt: '2019-08-30T17:33:48.651Z', + }), + expect.objectContaining({ + from: { + __typename: 'Comment', + content: 'You have been mentioned in a comment', + id: 'c2', + }, + read: false, + createdAt: '2019-08-30T19:33:48.651Z', + }), + expect.objectContaining({ + from: { + __typename: 'Post', + content: 'You have been mentioned in a post', + id: 'p3', + }, + read: false, + createdAt: '2019-08-31T17:33:48.651Z', + }), + ]), }, errors: undefined, }) @@ -211,33 +213,34 @@ describe('given some notifications', () => { describe('filter for read: false', () => { it('returns only unread notifications of current user', async () => { - const expected = expect.objectContaining({ - data: { - notifications: expect.arrayContaining([ - { - from: { - __typename: 'Comment', - content: 'You have been mentioned in a comment', - }, - read: false, - createdAt: '2019-08-30T19:33:48.651Z', - }, - { - from: { - __typename: 'Post', - content: 'You have been mentioned in a post', - }, - read: false, - createdAt: '2019-08-31T17:33:48.651Z', - }, - ]), - }, - }) const response = await query({ query: notifications, variables: { ...variables, read: false }, }) - await expect(response).toMatchObject(expected) + await expect(response).toMatchObject({ + data: { + notifications: expect.arrayContaining([ + expect.objectContaining({ + from: { + __typename: 'Comment', + content: 'You have been mentioned in a comment', + id: 'c2', + }, + read: false, + createdAt: '2019-08-30T19:33:48.651Z', + }), + expect.objectContaining({ + from: { + __typename: 'Post', + content: 'You have been mentioned in a post', + id: 'p3', + }, + read: false, + createdAt: '2019-08-31T17:33:48.651Z', + }), + ]), + }, + }) await expect(response.data?.notifications).toHaveLength(2) // double-check }) @@ -394,11 +397,13 @@ describe('given some notifications', () => { { createdAt: '2019-08-30T19:33:48.651Z', from: { __typename: 'Comment', content: 'You have been mentioned in a comment' }, + id: 'mentioned_in_comment/c2/you', read: true, }, { createdAt: '2019-08-31T17:33:48.651Z', from: { __typename: 'Post', content: 'You have been mentioned in a post' }, + id: 'mentioned_in_post/p3/you', read: true, }, ]), diff --git a/backend/src/graphql/resolvers/posts.observe.spec.ts b/backend/src/graphql/resolvers/posts.observe.spec.ts index e183a03ee..e76a77e24 100644 --- a/backend/src/graphql/resolvers/posts.observe.spec.ts +++ b/backend/src/graphql/resolvers/posts.observe.spec.ts @@ -1,10 +1,10 @@ /* 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 { CreateComment } from '@graphql/queries/CreateComment' import { CreatePost } from '@graphql/queries/CreatePost' +import { Post } from '@graphql/queries/Post' import { toggleObservePost } from '@graphql/queries/toggleObservePost' import type { ApolloTestSetup } from '@root/test/helpers' import { createApolloTestSetup } from '@root/test/helpers' @@ -20,25 +20,6 @@ let query: ApolloTestSetup['query'] let database: ApolloTestSetup['database'] let server: ApolloTestSetup['server'] -const createCommentMutation = gql` - mutation ($id: ID, $postId: ID!, $content: String!) { - CreateComment(id: $id, postId: $postId, content: $content) { - id - isPostObservedByMe - postObservingUsersCount - } - } -` - -const postQuery = gql` - query Post($id: ID) { - Post(id: $id) { - isObservedByMe - observingUsersCount - } - } -` - beforeAll(async () => { await cleanDatabase() const apolloSetup = createApolloTestSetup({ context }) @@ -101,7 +82,7 @@ describe('observing posts', () => { it('has another user NOT observing the post BEFORE commenting it', async () => { await expect( query({ - query: postQuery, + query: Post, variables: { id: 'p2' }, }), ).resolves.toMatchObject({ @@ -120,7 +101,7 @@ describe('observing posts', () => { it('has another user observing the post AFTER commenting it', async () => { await expect( mutate({ - mutation: createCommentMutation, + mutation: CreateComment, variables: { postId: 'p2', content: 'After commenting the post, I should observe the post automatically', @@ -137,7 +118,7 @@ describe('observing posts', () => { await expect( query({ - query: postQuery, + query: Post, variables: { id: 'p2' }, }), ).resolves.toMatchObject({ @@ -185,7 +166,7 @@ describe('observing posts', () => { it('does NOT alter the observation state', async () => { await expect( mutate({ - mutation: createCommentMutation, + mutation: CreateComment, variables: { postId: 'p2', content: @@ -203,7 +184,7 @@ describe('observing posts', () => { await expect( query({ - query: postQuery, + query: Post, variables: { id: 'p2' }, }), ).resolves.toMatchObject({ diff --git a/backend/src/graphql/resolvers/posts.spec.ts b/backend/src/graphql/resolvers/posts.spec.ts index 8cccf3523..d1e8a89d3 100644 --- a/backend/src/graphql/resolvers/posts.spec.ts +++ b/backend/src/graphql/resolvers/posts.spec.ts @@ -2,8 +2,6 @@ /* 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 { AddPostEmotions } from '@graphql/queries/AddPostEmotions' @@ -18,6 +16,7 @@ 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' @@ -133,18 +132,14 @@ describe('Post', () => { 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({ + await expect(query({ query: Post, variables })).resolves.toMatchObject({ data: { - Post: expect.arrayContaining(expected), + Post: expect.arrayContaining([ + expect.objectContaining({ id: 'happy-post' }), + expect.objectContaining({ id: 'cry-post' }), + expect.objectContaining({ id: 'post-by-followed-user' }), + ]), }, }) }) @@ -178,17 +173,6 @@ describe('Post', () => { }) */ 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: { @@ -202,30 +186,25 @@ describe('Post', () => { } await user.relateTo(happyPost, 'emoted', { emotion: 'happy' }) variables = { ...variables, filter: { emotions_some: { emotion_in: ['happy'] } } } - await expect( - query({ query: postQueryFilteredByEmotions, variables }), - ).resolves.toMatchObject(expected) + await expect(query({ query: Post, 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({ + await expect(query({ query: Post, variables })).resolves.toMatchObject({ data: { - Post: expect.arrayContaining(expected), + 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, }) @@ -233,22 +212,9 @@ describe('Post', () => { }) 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({ + await expect(query({ query: Post, variables })).resolves.toMatchObject({ data: { Post: [ { @@ -655,48 +621,6 @@ describe('CreatePost', () => { 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() @@ -719,7 +643,7 @@ describe('UpdatePost', () => { describe('unauthenticated', () => { it('throws authorization error', async () => { authenticatedUser = null - await expect(mutate({ mutation: updatePostMutation, variables })).resolves.toMatchObject({ + await expect(mutate({ mutation: UpdatePost, variables })).resolves.toMatchObject({ errors: [{ message: 'Not Authorized!' }], data: { UpdatePost: null }, }) @@ -732,7 +656,7 @@ describe('UpdatePost', () => { }) it('throws authorization error', async () => { - const { errors } = await mutate({ mutation: updatePostMutation, variables }) + const { errors } = await mutate({ mutation: UpdatePost, variables }) expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!') }) }) @@ -747,9 +671,7 @@ describe('UpdatePost', () => { data: { UpdatePost: { id: newlyCreatedPost.id, content: 'New content' } }, errors: undefined, } - await expect(mutate({ mutation: updatePostMutation, variables })).resolves.toMatchObject( - expected, - ) + await expect(mutate({ mutation: UpdatePost, variables })).resolves.toMatchObject(expected) }) it('updates a post, but maintains non-updated attributes', async () => { @@ -763,18 +685,16 @@ describe('UpdatePost', () => { }, errors: undefined, } - await expect(mutate({ mutation: updatePostMutation, variables })).resolves.toMatchObject( - expected, - ) + await expect(mutate({ mutation: UpdatePost, 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) + 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', () => { @@ -788,9 +708,7 @@ describe('UpdatePost', () => { }, errors: undefined, } - await expect(mutate({ mutation: updatePostMutation, variables })).resolves.toMatchObject( - expected, - ) + await expect(mutate({ mutation: UpdatePost, variables })).resolves.toMatchObject(expected) }) }) @@ -809,9 +727,7 @@ describe('UpdatePost', () => { }, errors: undefined, } - await expect(mutate({ mutation: updatePostMutation, variables })).resolves.toMatchObject( - expected, - ) + await expect(mutate({ mutation: UpdatePost, variables })).resolves.toMatchObject(expected) }) }) @@ -820,7 +736,7 @@ describe('UpdatePost', () => { it('throws an error', async () => { await expect( mutate({ - mutation: updatePostMutation, + mutation: UpdatePost, variables: { ...variables, postType: 'Event' }, }), ).resolves.toMatchObject({ @@ -837,7 +753,7 @@ describe('UpdatePost', () => { it('throws an error', async () => { await expect( mutate({ - mutation: updatePostMutation, + mutation: UpdatePost, variables: { ...variables, postType: 'Event', @@ -861,7 +777,7 @@ describe('UpdatePost', () => { const now = new Date() await expect( mutate({ - mutation: updatePostMutation, + mutation: UpdatePost, variables: { ...variables, postType: 'Event', @@ -885,7 +801,7 @@ describe('UpdatePost', () => { const now = new Date() await expect( mutate({ - mutation: updatePostMutation, + mutation: UpdatePost, variables: { ...variables, postType: 'Event', @@ -910,7 +826,7 @@ describe('UpdatePost', () => { const now = new Date() await expect( mutate({ - mutation: updatePostMutation, + mutation: UpdatePost, variables: { ...variables, postType: 'Event', @@ -936,7 +852,7 @@ describe('UpdatePost', () => { const now = new Date() await expect( mutate({ - mutation: updatePostMutation, + mutation: UpdatePost, variables: { ...variables, postType: 'Event', @@ -976,7 +892,7 @@ describe('UpdatePost', () => { await expect( database.neode.first('Image', { sensitive: true }, undefined), ).resolves.toBeFalsy() - await mutate({ mutation: updatePostMutation, variables }) + await mutate({ mutation: UpdatePost, variables }) await expect( database.neode.first('Image', { sensitive: true }, undefined), ).resolves.toBeTruthy() @@ -989,7 +905,7 @@ describe('UpdatePost', () => { }) it('deletes the image', async () => { await expect(database.neode.all('Image')).resolves.toHaveLength(6) - await mutate({ mutation: updatePostMutation, variables }) + await mutate({ mutation: UpdatePost, variables }) await expect(database.neode.all('Image')).resolves.toHaveLength(5) }) }) @@ -1002,7 +918,7 @@ describe('UpdatePost', () => { await expect( database.neode.first('Image', { sensitive: true }, undefined), ).resolves.toBeFalsy() - await mutate({ mutation: updatePostMutation, variables }) + await mutate({ mutation: UpdatePost, variables }) await expect( database.neode.first('Image', { sensitive: true }, undefined), ).resolves.toBeFalsy() @@ -2131,25 +2047,6 @@ describe('DeletePost', () => { 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' }) @@ -2226,8 +2123,8 @@ describe('emotions', () => { await mutate({ mutation: AddPostEmotions, variables }) await mutate({ mutation: AddPostEmotions, variables }) await expect( - query({ query: PostsEmotionsCountQuery, variables: postsEmotionsQueryVariables }), - ).resolves.toEqual(expect.objectContaining(expected)) + query({ query: Post, variables: postsEmotionsQueryVariables }), + ).resolves.toMatchObject(expected) }) it('allows a user to add more than one emotion', async () => { @@ -2247,8 +2144,8 @@ describe('emotions', () => { variables = { ...variables, data: { emotion: 'surprised' } } await mutate({ mutation: AddPostEmotions, variables }) await expect( - query({ query: PostsEmotionsQuery, variables: postsEmotionsQueryVariables }), - ).resolves.toEqual(expect.objectContaining(expected)) + query({ query: Post, variables: postsEmotionsQueryVariables }), + ).resolves.toMatchObject(expected) }) }) @@ -2351,8 +2248,8 @@ describe('emotions', () => { variables: removePostEmotionsVariables, }) await expect( - query({ query: PostsEmotionsQuery, variables: postsEmotionsQueryVariables }), - ).resolves.toEqual(expect.objectContaining(expectedResponse)) + query({ query: Post, variables: postsEmotionsQueryVariables }), + ).resolves.toMatchObject(expectedResponse) }) }) }) diff --git a/backend/src/graphql/resolvers/registration.spec.ts b/backend/src/graphql/resolvers/registration.spec.ts index a86ea2d64..e4adc65f2 100644 --- a/backend/src/graphql/resolvers/registration.spec.ts +++ b/backend/src/graphql/resolvers/registration.spec.ts @@ -1,12 +1,11 @@ /* 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 EmailAddress from '@db/models/EmailAddress' import User from '@db/models/User' import { Signup } from '@graphql/queries/Signup' +import { SignupVerification } from '@graphql/queries/SignupVerification' import type { ApolloTestSetup } from '@root/test/helpers' import { createApolloTestSetup } from '@root/test/helpers' import type { Context } from '@src/context' @@ -158,31 +157,6 @@ describe('Signup', () => { }) describe('SignupVerification', () => { - const mutation = gql` - mutation ( - $name: String! - $password: String! - $email: String! - $nonce: String! - $about: String - $termsAndConditionsAgreedVersion: String! - $locale: String - ) { - SignupVerification( - name: $name - password: $password - email: $email - nonce: $nonce - about: $about - termsAndConditionsAgreedVersion: $termsAndConditionsAgreedVersion - locale: $locale - ) { - id - termsAndConditionsAgreedVersion - termsAndConditionsAgreedAt - } - } - ` describe('given valid password and email', () => { beforeEach(() => { variables = { @@ -219,7 +193,9 @@ describe('SignupVerification', () => { }) it('rejects', async () => { - await expect(mutate({ mutation, variables })).resolves.toMatchObject({ + await expect( + mutate({ mutation: SignupVerification, variables }), + ).resolves.toMatchObject({ errors: [{ message: 'Invalid email or nonce' }], }) }) @@ -237,7 +213,9 @@ describe('SignupVerification', () => { describe('sending a valid nonce', () => { it('creates a user account', async () => { - await expect(mutate({ mutation, variables })).resolves.toMatchObject({ + await expect( + mutate({ mutation: SignupVerification, variables }), + ).resolves.toMatchObject({ data: { SignupVerification: expect.objectContaining({ id: expect.any(String), @@ -247,7 +225,7 @@ describe('SignupVerification', () => { }) it('sets `verifiedAt` attribute of EmailAddress', async () => { - await mutate({ mutation, variables }) + await mutate({ mutation: SignupVerification, variables }) const email = await database.neode.first( 'EmailAddress', { email: 'john@example.org' }, @@ -265,14 +243,14 @@ describe('SignupVerification', () => { MATCH(email:EmailAddress)-[:BELONGS_TO]->(u:User {name: $name}) RETURN email ` - await mutate({ mutation, variables }) + await mutate({ mutation: SignupVerification, variables }) const { records: emails } = await database.neode.cypher(cypher, { name: 'John Doe' }) expect(emails).toHaveLength(1) }) it('sets `about` attribute of User', async () => { variables = { ...variables, about: 'Find this description in the user profile' } - await mutate({ mutation, variables }) + await mutate({ mutation: SignupVerification, variables }) const user = await database.neode.first( 'User', { name: 'John Doe' }, @@ -285,7 +263,9 @@ describe('SignupVerification', () => { it('allowing the about field to be an empty string', async () => { variables = { ...variables, about: '' } - await expect(mutate({ mutation, variables })).resolves.toMatchObject({ + await expect( + mutate({ mutation: SignupVerification, variables }), + ).resolves.toMatchObject({ data: { SignupVerification: expect.objectContaining({ id: expect.any(String), @@ -299,13 +279,15 @@ describe('SignupVerification', () => { MATCH(email:EmailAddress)<-[:PRIMARY_EMAIL]-(u:User {name: $name}) RETURN email ` - await mutate({ mutation, variables }) + await mutate({ mutation: SignupVerification, variables }) const { records: emails } = await database.neode.cypher(cypher, { name: 'John Doe' }) expect(emails).toHaveLength(1) }) it('updates termsAndConditionsAgreedVersion', async () => { - await expect(mutate({ mutation, variables })).resolves.toMatchObject({ + await expect( + mutate({ mutation: SignupVerification, variables }), + ).resolves.toMatchObject({ data: { SignupVerification: expect.objectContaining({ termsAndConditionsAgreedVersion: '0.1.0', @@ -315,7 +297,9 @@ describe('SignupVerification', () => { }) it('updates termsAndConditionsAgreedAt', async () => { - await expect(mutate({ mutation, variables })).resolves.toMatchObject({ + await expect( + mutate({ mutation: SignupVerification, variables }), + ).resolves.toMatchObject({ data: { SignupVerification: expect.objectContaining({ termsAndConditionsAgreedAt: expect.any(String), @@ -326,7 +310,9 @@ describe('SignupVerification', () => { it('rejects if version of terms and conditions is missing', async () => { variables = { ...variables, termsAndConditionsAgreedVersion: null } - await expect(mutate({ mutation, variables })).resolves.toMatchObject({ + await expect( + mutate({ mutation: SignupVerification, variables }), + ).resolves.toMatchObject({ errors: [ { message: @@ -338,7 +324,9 @@ describe('SignupVerification', () => { it('rejects if version of terms and conditions has wrong format', async () => { variables = { ...variables, termsAndConditionsAgreedVersion: 'invalid version format' } - await expect(mutate({ mutation, variables })).resolves.toMatchObject({ + await expect( + mutate({ mutation: SignupVerification, variables }), + ).resolves.toMatchObject({ errors: [{ message: 'Invalid version format!' }], }) }) @@ -350,7 +338,9 @@ describe('SignupVerification', () => { }) it('rejects', async () => { - await expect(mutate({ mutation, variables })).resolves.toMatchObject({ + await expect( + mutate({ mutation: SignupVerification, variables }), + ).resolves.toMatchObject({ errors: [{ message: 'Invalid email or nonce' }], }) }) diff --git a/backend/src/graphql/resolvers/searches.spec.ts b/backend/src/graphql/resolvers/searches.spec.ts index 0c515c610..f42c7b1b2 100644 --- a/backend/src/graphql/resolvers/searches.spec.ts +++ b/backend/src/graphql/resolvers/searches.spec.ts @@ -2,10 +2,10 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ import { createTestClient } from 'apollo-server-testing' -import gql from 'graphql-tag' import Factory, { cleanDatabase } from '@db/factories' import { getNeode, getDriver } from '@db/neo4j' +import { searchPosts } from '@graphql/queries/searchPosts' import { searchResults } from '@graphql/queries/searchResults' import createServer from '@src/server' @@ -34,19 +34,6 @@ afterAll(async () => { await driver.close() neode.close() }) -const searchPostQuery = gql` - query ($query: String!, $firstPosts: Int, $postsOffset: Int) { - searchPosts(query: $query, firstPosts: $firstPosts, postsOffset: $postsOffset) { - postCount - posts { - __typename - id - title - content - } - } - } -` describe('resolvers/searches', () => { let variables @@ -605,7 +592,7 @@ und hinter tausend Stäben keine Welt.`, describe('query with limit 1', () => { it('has a count greater than 1', async () => { variables = { query: 'beitrag', firstPosts: 1, postsOffset: 0 } - await expect(query({ query: searchPostQuery, variables })).resolves.toMatchObject({ + await expect(query({ query: searchPosts, variables })).resolves.toMatchObject({ data: { searchPosts: { postCount: 2, diff --git a/backend/src/graphql/resolvers/shout.spec.ts b/backend/src/graphql/resolvers/shout.spec.ts index d3a34638c..d24cb9813 100644 --- a/backend/src/graphql/resolvers/shout.spec.ts +++ b/backend/src/graphql/resolvers/shout.spec.ts @@ -3,10 +3,10 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ import { createTestClient } from 'apollo-server-testing' -import gql from 'graphql-tag' import Factory, { cleanDatabase } from '@db/factories' import { getNeode, getDriver } from '@db/neo4j' +import { Post } from '@graphql/queries/Post' import { shout } from '@graphql/queries/shout' import { unshout } from '@graphql/queries/unshout' import createServer from '@src/server' @@ -14,16 +14,6 @@ import createServer from '@src/server' let mutate, query, authenticatedUser, variables const instance = getNeode() const driver = getDriver() -const queryPost = gql` - query ($id: ID!) { - Post(id: $id) { - id - shoutedBy { - id - } - } - } -` describe('shout and unshout posts', () => { let currentUser, postAuthor @@ -38,6 +28,9 @@ describe('shout and unshout posts', () => { driver, neode: instance, user: authenticatedUser, + cypherParams: { + currentUserId: authenticatedUser ? authenticatedUser.id : null, + }, } }, }) @@ -122,7 +115,7 @@ describe('shout and unshout posts', () => { await expect(mutate({ mutation: shout, variables })).resolves.toMatchObject({ data: { shout: true }, }) - await expect(query({ query: queryPost, variables })).resolves.toMatchObject({ + await expect(query({ query: Post, variables })).resolves.toMatchObject({ data: { Post: [{ id: 'another-user-post-id', shoutedBy: [{ id: 'current-user-id' }] }] }, errors: undefined, }) @@ -149,7 +142,7 @@ describe('shout and unshout posts', () => { await expect(mutate({ mutation: shout, variables })).resolves.toMatchObject({ data: { shout: false }, }) - await expect(query({ query: queryPost, variables })).resolves.toMatchObject({ + await expect(query({ query: Post, variables })).resolves.toMatchObject({ data: { Post: [{ id: 'current-user-post-id', shoutedBy: [] }] }, errors: undefined, }) @@ -191,7 +184,7 @@ describe('shout and unshout posts', () => { await expect(mutate({ mutation: unshout, variables })).resolves.toMatchObject({ data: { unshout: true }, }) - await expect(query({ query: queryPost, variables })).resolves.toMatchObject({ + await expect(query({ query: Post, variables })).resolves.toMatchObject({ data: { Post: [{ id: 'posted-by-another-user', shoutedBy: [] }] }, errors: undefined, }) diff --git a/backend/src/graphql/resolvers/socialMedia.spec.ts b/backend/src/graphql/resolvers/socialMedia.spec.ts index 0ef4a1e18..ba75c6707 100644 --- a/backend/src/graphql/resolvers/socialMedia.spec.ts +++ b/backend/src/graphql/resolvers/socialMedia.spec.ts @@ -4,10 +4,10 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-call */ import { createTestClient } from 'apollo-server-testing' -import gql from 'graphql-tag' import Factory, { cleanDatabase } from '@db/factories' import { getDriver } from '@db/neo4j' +import { CreateSocialMedia } from '@graphql/queries/CreateSocialMedia' import { DeleteSocialMedia } from '@graphql/queries/DeleteSocialMedia' import { UpdateSocialMedia } from '@graphql/queries/UpdateSocialMedia' import createServer from '@src/server' @@ -84,24 +84,16 @@ describe('SocialMedia', () => { }) describe('create social media', () => { - let mutation, variables + let variables beforeEach(() => { - mutation = gql` - mutation ($url: String!) { - CreateSocialMedia(url: $url) { - id - url - } - } - ` variables = { url } }) describe('unauthenticated', () => { it('throws authorization error', async () => { const user = null - const result = await socialMediaAction(user, mutation, variables) + const result = await socialMediaAction(user, CreateSocialMedia, variables) expect(result.errors[0]).toHaveProperty('message', 'Not Authorized!') }) @@ -115,21 +107,19 @@ describe('SocialMedia', () => { }) it('creates social media with the given url', async () => { - await expect(socialMediaAction(user, mutation, variables)).resolves.toEqual( - expect.objectContaining({ - data: { - CreateSocialMedia: { - id: expect.any(String), - url, - }, + await expect(socialMediaAction(user, CreateSocialMedia, variables)).resolves.toMatchObject({ + data: { + CreateSocialMedia: { + id: expect.any(String), + url, }, - }), - ) + }, + }) }) it('rejects an empty string as url', async () => { variables = { url: '' } - const result = await socialMediaAction(user, mutation, variables) + const result = await socialMediaAction(user, CreateSocialMedia, variables) expect(result.errors[0].message).toEqual( expect.stringContaining('"url" is not allowed to be empty'), @@ -138,7 +128,7 @@ describe('SocialMedia', () => { it('rejects invalid urls', async () => { variables = { url: 'not-a-url' } - const result = await socialMediaAction(user, mutation, variables) + const result = await socialMediaAction(user, CreateSocialMedia, variables) expect(result.errors[0].message).toEqual( expect.stringContaining('"url" must be a valid uri'), @@ -147,28 +137,13 @@ describe('SocialMedia', () => { }) describe('ownedBy', () => { - beforeEach(() => { - mutation = gql` - mutation ($url: String!) { - CreateSocialMedia(url: $url) { - url - ownedBy { - name - } - } - } - ` - }) - it('resolves', async () => { const user = someUser - await expect(socialMediaAction(user, mutation, variables)).resolves.toEqual( - expect.objectContaining({ - data: { - CreateSocialMedia: { url, ownedBy: { name: 'Kalle Blomqvist' } }, - }, - }), - ) + await expect(socialMediaAction(user, CreateSocialMedia, variables)).resolves.toMatchObject({ + data: { + CreateSocialMedia: { url, ownedBy: { name: 'Kalle Blomqvist' } }, + }, + }) }) }) }) diff --git a/backend/src/graphql/resolvers/user_management.spec.ts b/backend/src/graphql/resolvers/user_management.spec.ts index f272e9fba..50e0d8ca6 100644 --- a/backend/src/graphql/resolvers/user_management.spec.ts +++ b/backend/src/graphql/resolvers/user_management.spec.ts @@ -6,12 +6,12 @@ /* eslint-disable promise/prefer-await-to-callbacks */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable jest/unbound-method */ -import gql from 'graphql-tag' import { verify } from 'jsonwebtoken' import { categories } from '@constants/categories' import Factory, { cleanDatabase } from '@db/factories' import { changePassword } from '@graphql/queries/changePassword' +import { currentUser } from '@graphql/queries/currentUser' import { login } from '@graphql/queries/login' import { saveCategorySettings } from '@graphql/queries/saveCategorySettings' import { decode } from '@jwt/decode' @@ -86,24 +86,8 @@ afterEach(async () => { }) describe('currentUser', () => { - const currentUserQuery = gql` - { - currentUser { - id - slug - name - avatar { - url - } - email - role - activeCategories - } - } - ` - const respondsWith = async (expected) => { - await expect(query({ query: currentUserQuery, variables })).resolves.toMatchObject(expected) + await expect(query({ query: currentUser, variables })).resolves.toMatchObject(expected) } describe('unauthenticated', () => { @@ -211,7 +195,7 @@ describe('currentUser', () => { }) it('returns only the saved active categories', async () => { - const result = await query({ query: currentUserQuery, variables }) + const result = await query({ query: currentUser, variables }) expect(result.data?.currentUser.activeCategories).toHaveLength(4) expect(result.data?.currentUser.activeCategories).toContain('cat1') expect(result.data?.currentUser.activeCategories).toContain('cat3') diff --git a/backend/src/graphql/resolvers/users.spec.ts b/backend/src/graphql/resolvers/users.spec.ts index 93e6be4c6..ca27efd7c 100644 --- a/backend/src/graphql/resolvers/users.spec.ts +++ b/backend/src/graphql/resolvers/users.spec.ts @@ -3,8 +3,6 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unsafe-call */ -import gql from 'graphql-tag' - import { categories } from '@constants/categories' import pubsubContext from '@context/pubsub' import Factory, { cleanDatabase } from '@db/factories' @@ -15,6 +13,8 @@ import { saveCategorySettings } from '@graphql/queries/saveCategorySettings' import { setTrophyBadgeSelected } from '@graphql/queries/setTrophyBadgeSelected' import { switchUserRole } from '@graphql/queries/switchUserRole' import { updateOnlineStatus } from '@graphql/queries/updateOnlineStatus' +import { UpdateUser } from '@graphql/queries/UpdateUser' +import { UserEmailNotificationSettings, User as userQuery } from '@graphql/queries/User' import type { ApolloTestSetup } from '@root/test/helpers' import { createApolloTestSetup } from '@root/test/helpers' import type { Context } from '@src/context' @@ -63,21 +63,12 @@ afterEach(async () => { describe('User', () => { describe('query by email address', () => { - let userQuery - beforeEach(async () => { const user = await Factory.build('user', { id: 'user', role: 'user', }) authenticatedUser = await user.toJson() - userQuery = gql` - query ($email: String) { - User(email: $email) { - name - } - } - ` variables = { email: 'any-email-address@example.org', } @@ -131,35 +122,7 @@ describe('User', () => { }) describe('UpdateUser', () => { - let updateUserMutation - beforeEach(async () => { - updateUserMutation = gql` - mutation ( - $id: ID! - $name: String - $termsAndConditionsAgreedVersion: String - $locationName: String # empty string '' sets it to null - ) { - UpdateUser( - id: $id - name: $name - termsAndConditionsAgreedVersion: $termsAndConditionsAgreedVersion - locationName: $locationName - ) { - id - name - termsAndConditionsAgreedVersion - termsAndConditionsAgreedAt - locationName - location { - name - nameDE - nameEN - } - } - } - ` variables = { id: 'u47', name: 'John Doughnut', @@ -196,8 +159,10 @@ describe('UpdateUser', () => { }) it('is not allowed to change other user accounts', async () => { - const { errors } = await mutate({ mutation: updateUserMutation, variables }) - expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!') + await expect(mutate({ mutation: UpdateUser, variables })).resolves.toMatchObject({ + data: { UpdateUser: null }, + errors: [{ message: 'Not Authorized!' }], + }) }) }) @@ -216,9 +181,7 @@ describe('UpdateUser', () => { }, errors: undefined, } - await expect(mutate({ mutation: updateUserMutation, variables })).resolves.toMatchObject( - expected, - ) + await expect(mutate({ mutation: UpdateUser, variables })).resolves.toMatchObject(expected) }) describe('given a new agreed version of terms and conditions', () => { @@ -236,9 +199,7 @@ describe('UpdateUser', () => { errors: undefined, } - await expect(mutate({ mutation: updateUserMutation, variables })).resolves.toMatchObject( - expected, - ) + await expect(mutate({ mutation: UpdateUser, variables })).resolves.toMatchObject(expected) }) }) @@ -257,9 +218,7 @@ describe('UpdateUser', () => { errors: undefined, } - await expect(mutate({ mutation: updateUserMutation, variables })).resolves.toMatchObject( - expected, - ) + await expect(mutate({ mutation: UpdateUser, variables })).resolves.toMatchObject(expected) }) }) @@ -268,7 +227,7 @@ describe('UpdateUser', () => { ...variables, termsAndConditionsAgreedVersion: 'invalid version format', } - const { errors } = await mutate({ mutation: updateUserMutation, variables }) + const { errors } = await mutate({ mutation: UpdateUser, variables }) expect(errors?.[0]).toHaveProperty('message', 'Invalid version format!') }) @@ -276,7 +235,7 @@ describe('UpdateUser', () => { describe('change location to "Hamburg, New Jersey, United States"', () => { it('has updated location to "Hamburg, New Jersey, United States"', async () => { variables = { ...variables, locationName: 'Hamburg, New Jersey, United States' } - await expect(mutate({ mutation: updateUserMutation, variables })).resolves.toMatchObject({ + await expect(mutate({ mutation: UpdateUser, variables })).resolves.toMatchObject({ data: { UpdateUser: { locationName: 'Hamburg, New Jersey, United States', @@ -295,7 +254,7 @@ describe('UpdateUser', () => { describe('change location to unset location', () => { it('has updated location to unset location', async () => { variables = { ...variables, locationName: '' } - await expect(mutate({ mutation: updateUserMutation, variables })).resolves.toMatchObject({ + await expect(mutate({ mutation: UpdateUser, variables })).resolves.toMatchObject({ data: { UpdateUser: { locationName: null, @@ -548,15 +507,10 @@ describe('switch user role', () => { id: 'user', role: 'admin', } - await expect(mutate({ mutation: switchUserRole, variables })).resolves.toEqual( - expect.objectContaining({ - errors: [ - expect.objectContaining({ - message: 'Not Authorized!', - }), - ], - }), - ) + await expect(mutate({ mutation: switchUserRole, variables })).resolves.toMatchObject({ + data: { switchUserRole: null }, + errors: [{ message: 'Not Authorized!' }], + }) }) }) @@ -598,33 +552,6 @@ describe('switch user role', () => { }) let anotherUser -const emailNotificationSettingsQuery = gql` - query ($id: ID!) { - User(id: $id) { - emailNotificationSettings { - type - settings { - name - value - } - } - } - } -` - -const emailNotificationSettingsMutation = gql` - mutation ($id: ID!, $emailNotificationSettings: [EmailNotificationSettingsInput]!) { - UpdateUser(id: $id, emailNotificationSettings: $emailNotificationSettings) { - emailNotificationSettings { - type - settings { - name - value - } - } - } - } -` describe('emailNotificationSettings', () => { beforeEach(async () => { @@ -644,16 +571,11 @@ describe('emailNotificationSettings', () => { authenticatedUser = await anotherUser.toJson() const targetUser = await user.toJson() await expect( - query({ query: emailNotificationSettingsQuery, variables: { id: targetUser.id } }), - ).resolves.toEqual( - expect.objectContaining({ - errors: [ - expect.objectContaining({ - message: 'Not Authorized!', - }), - ], - }), - ) + query({ query: UserEmailNotificationSettings, variables: { id: targetUser.id } }), + ).resolves.toMatchObject({ + data: { User: [null] }, + errors: [{ message: 'Not Authorized!' }], + }) }) }) @@ -662,112 +584,13 @@ describe('emailNotificationSettings', () => { authenticatedUser = await user.toJson() await expect( query({ - query: emailNotificationSettingsQuery, + query: UserEmailNotificationSettings, variables: { id: authenticatedUser?.id }, }), - ).resolves.toEqual( - expect.objectContaining({ - data: { - User: [ - { - emailNotificationSettings: [ - { - type: 'post', - settings: [ - { - name: 'commentOnObservedPost', - value: true, - }, - { - name: 'mention', - value: true, - }, - { - name: 'followingUsers', - value: true, - }, - { - name: 'postInGroup', - value: true, - }, - ], - }, - { - type: 'chat', - settings: [ - { - name: 'chatMessage', - value: true, - }, - ], - }, - { - type: 'group', - settings: [ - { - name: 'groupMemberJoined', - value: true, - }, - { - name: 'groupMemberLeft', - value: true, - }, - { - name: 'groupMemberRemoved', - value: true, - }, - { - name: 'groupMemberRoleChanged', - value: true, - }, - ], - }, - ], - }, - ], - }, - }), - ) - }) - }) - }) - - describe('mutate the field', () => { - const emailNotificationSettings = [{ name: 'mention', value: false }] - - describe('as another user', () => { - it('throws an error', async () => { - authenticatedUser = await anotherUser.toJson() - const targetUser = await user.toJson() - await expect( - mutate({ - mutation: emailNotificationSettingsMutation, - variables: { id: targetUser.id, emailNotificationSettings }, - }), - ).resolves.toEqual( - expect.objectContaining({ - errors: [ - expect.objectContaining({ - message: 'Not Authorized!', - }), - ], - }), - ) - }) - }) - - describe('as self', () => { - it('updates the emailNotificationSettings', async () => { - authenticatedUser = (await user.toJson()) as DecodedUser - await expect( - mutate({ - mutation: emailNotificationSettingsMutation, - variables: { id: authenticatedUser.id, emailNotificationSettings }, - }), - ).resolves.toEqual( - expect.objectContaining({ - data: { - UpdateUser: { + ).resolves.toMatchObject({ + data: { + User: [ + { emailNotificationSettings: [ { type: 'post', @@ -778,7 +601,7 @@ describe('emailNotificationSettings', () => { }, { name: 'mention', - value: false, + value: true, }, { name: 'followingUsers', @@ -822,9 +645,99 @@ describe('emailNotificationSettings', () => { }, ], }, - }, + ], + }, + }) + }) + }) + }) + + describe('mutate the field', () => { + const emailNotificationSettings = [{ name: 'mention', value: false }] + + describe('as another user', () => { + it('throws an error', async () => { + authenticatedUser = await anotherUser.toJson() + const targetUser = await user.toJson() + await expect( + mutate({ + mutation: UpdateUser, + variables: { id: targetUser.id, emailNotificationSettings }, }), - ) + ).resolves.toMatchObject({ + data: { UpdateUser: null }, + errors: [{ message: 'Not Authorized!' }], + }) + }) + }) + + describe('as self', () => { + it('updates the emailNotificationSettings', async () => { + authenticatedUser = (await user.toJson()) as DecodedUser + await expect( + mutate({ + mutation: UpdateUser, + variables: { id: authenticatedUser.id, emailNotificationSettings }, + }), + ).resolves.toMatchObject({ + data: { + UpdateUser: { + emailNotificationSettings: [ + { + type: 'post', + settings: [ + { + name: 'commentOnObservedPost', + value: true, + }, + { + name: 'mention', + value: false, + }, + { + name: 'followingUsers', + value: true, + }, + { + name: 'postInGroup', + value: true, + }, + ], + }, + { + type: 'chat', + settings: [ + { + name: 'chatMessage', + value: true, + }, + ], + }, + { + type: 'group', + settings: [ + { + name: 'groupMemberJoined', + value: true, + }, + { + name: 'groupMemberLeft', + value: true, + }, + { + name: 'groupMemberRemoved', + value: true, + }, + { + name: 'groupMemberRoleChanged', + value: true, + }, + ], + }, + ], + }, + }, + }) }) }) }) @@ -860,15 +773,10 @@ describe('save category settings', () => { }) it('throws an error', async () => { - await expect(mutate({ mutation: saveCategorySettings, variables })).resolves.toEqual( - expect.objectContaining({ - errors: [ - expect.objectContaining({ - message: 'Not Authorized!', - }), - ], - }), - ) + await expect(mutate({ mutation: saveCategorySettings, variables })).resolves.toMatchObject({ + data: { saveCategorySettings: null }, + errors: [{ message: 'Not Authorized!' }], + }) }) }) @@ -877,14 +785,6 @@ describe('save category settings', () => { authenticatedUser = await user.toJson() }) - const userQuery = gql` - query ($id: ID) { - User(id: $id) { - activeCategories - } - } - ` - describe('no categories saved', () => { it('returns true for active categories mutation', async () => { await expect(mutate({ mutation: saveCategorySettings, variables })).resolves.toEqual( @@ -902,17 +802,15 @@ describe('save category settings', () => { it('returns the active categories when user is queried', async () => { await expect( query({ query: userQuery, variables: { id: authenticatedUser?.id } }), - ).resolves.toEqual( - expect.objectContaining({ - data: { - User: [ - { - activeCategories: expect.arrayContaining(['cat1', 'cat3', 'cat5']), - }, - ], - }, - }), - ) + ).resolves.toMatchObject({ + data: { + User: [ + { + activeCategories: expect.arrayContaining(['cat1', 'cat3', 'cat5']), + }, + ], + }, + }) }) }) }) @@ -944,23 +842,21 @@ describe('save category settings', () => { it('returns the new active categories when user is queried', async () => { await expect( query({ query: userQuery, variables: { id: authenticatedUser?.id } }), - ).resolves.toEqual( - expect.objectContaining({ - data: { - User: [ - { - activeCategories: expect.arrayContaining([ - 'cat10', - 'cat11', - 'cat12', - 'cat8', - 'cat9', - ]), - }, - ], - }, - }), - ) + ).resolves.toMatchObject({ + data: { + User: [ + { + activeCategories: expect.arrayContaining([ + 'cat10', + 'cat11', + 'cat12', + 'cat8', + 'cat9', + ]), + }, + ], + }, + }) }) }) }) @@ -984,15 +880,10 @@ describe('updateOnlineStatus', () => { }) it('throws an error', async () => { - await expect(mutate({ mutation: updateOnlineStatus, variables })).resolves.toEqual( - expect.objectContaining({ - errors: [ - expect.objectContaining({ - message: 'Not Authorized!', - }), - ], - }), - ) + await expect(mutate({ mutation: updateOnlineStatus, variables })).resolves.toMatchObject({ + data: null, + errors: [{ message: 'Not Authorized!' }], + }) }) }) @@ -1122,15 +1013,10 @@ describe('setTrophyBadgeSelected', () => { mutation: setTrophyBadgeSelected, variables: { slot: 0, badgeId: 'trophy_bear' }, }), - ).resolves.toEqual( - expect.objectContaining({ - errors: [ - expect.objectContaining({ - message: 'Not Authorized!', - }), - ], - }), - ) + ).resolves.toMatchObject({ + data: { setTrophyBadgeSelected: null }, + errors: [{ message: 'Not Authorized!' }], + }) }) }) @@ -1500,15 +1386,10 @@ describe('resetTrophyBadgesSelected', () => { }) it('throws an error', async () => { - await expect(mutate({ mutation: resetTrophyBadgesSelected })).resolves.toEqual( - expect.objectContaining({ - errors: [ - expect.objectContaining({ - message: 'Not Authorized!', - }), - ], - }), - ) + await expect(mutate({ mutation: resetTrophyBadgesSelected })).resolves.toMatchObject({ + data: { resetTrophyBadgesSelected: null }, + errors: [{ message: 'Not Authorized!' }], + }) }) }) diff --git a/backend/src/graphql/resolvers/users.ts b/backend/src/graphql/resolvers/users.ts index bb8f85434..6720ec0fb 100644 --- a/backend/src/graphql/resolvers/users.ts +++ b/backend/src/graphql/resolvers/users.ts @@ -638,6 +638,7 @@ export default { 'allowEmbedIframes', 'showShoutsPublicly', 'locale', + 'activeCategories', ], boolean: { followedByCurrentUser: diff --git a/backend/src/graphql/resolvers/users/blockedUsers.spec.ts b/backend/src/graphql/resolvers/users/blockedUsers.spec.ts index 06f79ee76..07f1615de 100644 --- a/backend/src/graphql/resolvers/users/blockedUsers.spec.ts +++ b/backend/src/graphql/resolvers/users/blockedUsers.spec.ts @@ -2,12 +2,12 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ -import gql from 'graphql-tag' - import { cleanDatabase } from '@db/factories' import { blockedUsers } from '@graphql/queries/blockedUsers' import { blockUser } from '@graphql/queries/blockUser' +import { Post } from '@graphql/queries/Post' import { unblockUser } from '@graphql/queries/unblockUser' +import { User } from '@graphql/queries/User' import { createApolloTestSetup } from '@root/test/helpers' import type { ApolloTestSetup } from '@root/test/helpers' import type { Context } from '@src/context' @@ -155,56 +155,37 @@ describe('blockUser', () => { it('unfollows the user when blocking', async () => { await currentUser.relateTo(blockedUser, 'following') - const queryUser = gql` - query { - User(id: "u2") { - id - isBlocked - followedByCurrentUser - } - } - ` - await expect(query({ query: queryUser })).resolves.toMatchObject({ + await expect(query({ query: User, variables: { id: 'u2' } })).resolves.toMatchObject({ data: { User: [{ id: 'u2', isBlocked: false, followedByCurrentUser: true }] }, }) await mutate({ mutation: blockUser, variables: { id: 'u2' } }) - await expect(query({ query: queryUser })).resolves.toMatchObject({ + await expect(query({ query: User, variables: { id: 'u2' } })).resolves.toMatchObject({ data: { User: [{ id: 'u2', isBlocked: true, followedByCurrentUser: false }] }, }) }) describe('given both the current user and the to-be-blocked user write a post', () => { - let postQuery - beforeEach(async () => { const post1 = await database.neode.create('Post', { id: 'p12', title: 'A post written by the current user', + content: 'content', }) const post2 = await database.neode.create('Post', { id: 'p23', title: 'A post written by the blocked user', + content: 'content', }) await Promise.all([ post1.relateTo(currentUser, 'author'), post2.relateTo(blockedUser, 'author'), ]) - postQuery = gql` - query { - Post(orderBy: createdAt_asc) { - id - title - author { - id - name - } - } - } - ` }) const bothPostsAreInTheNewsfeed = async () => { - await expect(query({ query: postQuery })).resolves.toMatchObject({ + await expect( + query({ query: Post, variables: { orderBy: 'createdAt_asc' } }), + ).resolves.toMatchObject({ data: { Post: [ { @@ -238,7 +219,9 @@ describe('blockUser', () => { // TODO: clarify proper behaviour it("the blocked user's post still shows up in the newsfeed of the current user", async () => { - await expect(query({ query: postQuery })).resolves.toMatchObject({ + await expect( + query({ query: Post, variables: { orderBy: 'createdAt_asc' } }), + ).resolves.toMatchObject({ data: { Post: [ { @@ -276,19 +259,21 @@ describe('blockUser', () => { }) it("the current user's post will show up in the newsfeed of the blocked user", async () => { - await expect(query({ query: postQuery })).resolves.toMatchObject({ + await expect( + query({ query: Post, variables: { orderBy: 'createdAt_asc' } }), + ).resolves.toMatchObject({ data: { Post: expect.arrayContaining([ - { + expect.objectContaining({ id: 'p23', title: 'A post written by the blocked user', author: { name: 'Blocked User', id: 'u2' }, - }, - { + }), + expect.objectContaining({ id: 'p12', title: 'A post written by the current user', author: { name: 'Current User', id: 'u1' }, - }, + }), ]), }, }) diff --git a/backend/src/graphql/resolvers/users/location.spec.ts b/backend/src/graphql/resolvers/users/location.spec.ts index 11ed0de10..83495fce0 100644 --- a/backend/src/graphql/resolvers/users/location.spec.ts +++ b/backend/src/graphql/resolvers/users/location.spec.ts @@ -1,10 +1,9 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-call */ -import gql from 'graphql-tag' - import Factory, { cleanDatabase } from '@db/factories' import { queryLocations } from '@graphql/queries/queryLocations' +import { UpdateUser } from '@graphql/queries/UpdateUser' import type { ApolloTestSetup } from '@root/test/helpers' import { createApolloTestSetup } from '@root/test/helpers' import type { Context } from '@src/context' @@ -19,13 +18,6 @@ let query: any // eslint-disable-line @typescript-eslint/no-explicit-any let database: ApolloTestSetup['database'] let server: ApolloTestSetup['server'] -const updateUserMutation = gql` - mutation ($id: ID!, $name: String!, $locationName: String) { - UpdateUser(id: $id, name: $name, locationName: $locationName) { - locationName - } - } -` const newlyCreatedNodesWithLocales = [ { city: { @@ -203,7 +195,7 @@ describe('userMiddleware', () => { name: 'Updating user', locationName: 'Welzheim, Baden-Württemberg, Germany', } - await mutate({ mutation: updateUserMutation, variables }) + await mutate({ mutation: UpdateUser, variables }) const locations = await database.neode.cypher( `MATCH (city:Location)-[:IS_IN]->(district:Location)-[:IS_IN]->(state:Location)-[:IS_IN]->(country:Location) return city {.*}, state {.*}, country {.*}`, {}, diff --git a/backend/src/graphql/resolvers/users/mutedUsers.spec.ts b/backend/src/graphql/resolvers/users/mutedUsers.spec.ts index b7b5494fa..3202a0274 100644 --- a/backend/src/graphql/resolvers/users/mutedUsers.spec.ts +++ b/backend/src/graphql/resolvers/users/mutedUsers.spec.ts @@ -3,13 +3,14 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-call */ import { createTestClient } from 'apollo-server-testing' -import gql from 'graphql-tag' import { cleanDatabase } from '@db/factories' import { getNeode, getDriver } from '@db/neo4j' import { mutedUsers } from '@graphql/queries/mutedUsers' import { muteUser } from '@graphql/queries/muteUser' +import { Post } from '@graphql/queries/Post' import { unmuteUser } from '@graphql/queries/unmuteUser' +import { User } from '@graphql/queries/User' import createServer from '@src/server' const driver = getDriver() @@ -152,85 +153,68 @@ describe('muteUser', () => { it('unfollows the user', async () => { await currentUser.relateTo(mutedUser, 'following') - const queryUser = gql` - query { - User(id: "u2") { - id - isMuted - followedByCurrentUser - } - } - ` const { query } = createTestClient(server) - await expect(query({ query: queryUser })).resolves.toEqual( - expect.objectContaining({ - data: { User: [{ id: 'u2', isMuted: false, followedByCurrentUser: true }] }, - }), - ) + await expect(query({ query: User, variables: { id: 'u2' } })).resolves.toMatchObject({ + data: { + User: expect.arrayContaining([ + expect.objectContaining({ id: 'u2', isMuted: false, followedByCurrentUser: true }), + ]), + }, + }) await muteAction({ id: 'u2' }) - await expect(query({ query: queryUser })).resolves.toEqual( - expect.objectContaining({ - data: { User: [{ id: 'u2', isMuted: true, followedByCurrentUser: false }] }, - }), - ) + await expect(query({ query: User, variables: { id: 'u2' } })).resolves.toMatchObject({ + data: { + User: expect.arrayContaining([ + expect.objectContaining({ id: 'u2', isMuted: true, followedByCurrentUser: false }), + ]), + }, + }) }) describe('given both the current user and the to-be-muted user write a post', () => { - let postQuery - beforeEach(async () => { const post1 = await neode.create('Post', { id: 'p12', title: 'A post written by the current user', + content: 'content', }) const post2 = await neode.create('Post', { id: 'p23', title: 'A post written by the muted user', + content: 'content', }) await Promise.all([ post1.relateTo(currentUser, 'author'), post2.relateTo(mutedUser, 'author'), ]) - postQuery = gql` - query { - Post(orderBy: createdAt_asc) { - id - title - author { - id - name - } - } - } - ` }) const bothPostsAreInTheNewsfeed = async () => { const { query } = createTestClient(server) - await expect(query({ query: postQuery })).resolves.toEqual( - expect.objectContaining({ - data: { - Post: [ - { - id: 'p12', - title: 'A post written by the current user', - author: { - name: 'Current User', - id: 'u1', - }, + await expect( + query({ query: Post, variables: { orderBy: 'createdAt_asc' } }), + ).resolves.toMatchObject({ + data: { + Post: expect.arrayContaining([ + expect.objectContaining({ + id: 'p12', + title: 'A post written by the current user', + author: { + name: 'Current User', + id: 'u1', }, - { - id: 'p23', - title: 'A post written by the muted user', - author: { - name: 'Muted User', - id: 'u2', - }, + }), + expect.objectContaining({ + id: 'p23', + title: 'A post written by the muted user', + author: { + name: 'Muted User', + id: 'u2', }, - ], - }, - }), - ) + }), + ]), + }, + }) } describe('from the perspective of the current user', () => { @@ -243,19 +227,19 @@ describe('muteUser', () => { it("the muted user's post won't show up in the newsfeed of the current user", async () => { const { query } = createTestClient(server) - await expect(query({ query: postQuery })).resolves.toEqual( - expect.objectContaining({ - data: { - Post: [ - { - id: 'p12', - title: 'A post written by the current user', - author: { name: 'Current User', id: 'u1' }, - }, - ], - }, - }), - ) + await expect( + query({ query: Post, variables: { orderBy: 'createdAt_asc' } }), + ).resolves.toMatchObject({ + data: { + Post: [ + expect.objectContaining({ + id: 'p12', + title: 'A post written by the current user', + author: { name: 'Current User', id: 'u1' }, + }), + ], + }, + }) }) }) }) @@ -273,24 +257,24 @@ describe('muteUser', () => { it("the current user's post will show up in the newsfeed of the muted user", async () => { const { query } = createTestClient(server) - await expect(query({ query: postQuery })).resolves.toEqual( - expect.objectContaining({ - data: { - Post: expect.arrayContaining([ - { - id: 'p23', - title: 'A post written by the muted user', - author: { name: 'Muted User', id: 'u2' }, - }, - { - id: 'p12', - title: 'A post written by the current user', - author: { name: 'Current User', id: 'u1' }, - }, - ]), - }, - }), - ) + await expect( + query({ query: Post, variables: { orderBy: 'createdAt_asc' } }), + ).resolves.toMatchObject({ + data: { + Post: expect.arrayContaining([ + expect.objectContaining({ + id: 'p23', + title: 'A post written by the muted user', + author: { name: 'Muted User', id: 'u2' }, + }), + expect.objectContaining({ + id: 'p12', + title: 'A post written by the current user', + author: { name: 'Current User', id: 'u1' }, + }), + ]), + }, + }) }) }) }) diff --git a/backend/src/middleware/hashtags/hashtagsMiddleware.spec.ts b/backend/src/middleware/hashtags/hashtagsMiddleware.spec.ts index dfcf9b722..7f0e911ab 100644 --- a/backend/src/middleware/hashtags/hashtagsMiddleware.spec.ts +++ b/backend/src/middleware/hashtags/hashtagsMiddleware.spec.ts @@ -1,9 +1,9 @@ /* 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 { cleanDatabase } from '@db/factories' +import { CreatePost } from '@graphql/queries/CreatePost' +import { Post } from '@graphql/queries/Post' import { UpdatePost } from '@graphql/queries/UpdatePost' import type { ApolloTestSetup } from '@root/test/helpers' import { createApolloTestSetup } from '@root/test/helpers' @@ -17,15 +17,6 @@ let query: any // eslint-disable-line @typescript-eslint/no-explicit-any let database: ApolloTestSetup['database'] let server: ApolloTestSetup['server'] const categoryIds = ['cat9'] -const createPostMutation = gql` - mutation ($id: ID, $title: String!, $postContent: String!, $categoryIds: [ID]!) { - CreatePost(id: $id, title: $title, content: $postContent, categoryIds: $categoryIds) { - id - title - content - } - } -` beforeAll(async () => { await cleanDatabase() @@ -64,7 +55,7 @@ afterEach(async () => { describe('hashtags', () => { const id = 'p135' const title = 'Two Hashtags' - const postContent = ` + const content = `

Hey Dude, { for everyone.

` - const postWithHastagsQuery = gql` - query ($id: ID) { - Post(id: $id) { - tags { - id - } - } - } - ` const postWithHastagsVariables = { id, } @@ -106,46 +88,43 @@ describe('hashtags', () => { describe('create a Post with Hashtags', () => { beforeEach(async () => { await mutate({ - mutation: createPostMutation, + mutation: CreatePost, variables: { id, title, - postContent, + content, categoryIds, }, }) }) it('both hashtags are created with the "id" set to their "name"', async () => { - const expected = [ - { - id: 'Democracy', - }, - { - id: 'Liberty', - }, - ] await expect( query({ - query: postWithHastagsQuery, + query: Post, variables: postWithHastagsVariables, }), - ).resolves.toEqual( - expect.objectContaining({ - data: { - Post: [ - { - tags: expect.arrayContaining(expected), - }, - ], - }, - }), - ) + ).resolves.toMatchObject({ + data: { + Post: [ + { + tags: expect.arrayContaining([ + expect.objectContaining({ + id: 'Democracy', + }), + expect.objectContaining({ + id: 'Liberty', + }), + ]), + }, + ], + }, + }) }) describe('updates the Post by removing, keeping and adding one hashtag respectively', () => { // The already existing hashtag has no class at this point. - const postContent = ` + const content = `

Hey Dude, { variables: { id, title, - postContent, + content, categoryIds, }, }) - const expected = [ - { - id: 'Elections', - }, - { - id: 'Liberty', - }, - ] await expect( query({ - query: postWithHastagsQuery, + query: Post, variables: postWithHastagsVariables, }), - ).resolves.toEqual( - expect.objectContaining({ - data: { - Post: [ - { - tags: expect.arrayContaining(expected), - }, - ], - }, - }), - ) + ).resolves.toMatchObject({ + data: { + Post: [ + { + tags: expect.arrayContaining([ + expect.objectContaining({ + id: 'Elections', + }), + expect.objectContaining({ + id: 'Liberty', + }), + ]), + }, + ], + }, + }) }) }) }) diff --git a/backend/src/middleware/languages/languages.spec.ts b/backend/src/middleware/languages/languages.spec.ts index 2f0214446..87ab723be 100644 --- a/backend/src/middleware/languages/languages.spec.ts +++ b/backend/src/middleware/languages/languages.spec.ts @@ -1,9 +1,8 @@ /* 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 { CreatePost } from '@graphql/queries/CreatePost' import type { ApolloTestSetup } from '@root/test/helpers' import { createApolloTestSetup } from '@root/test/helpers' import type { Context } from '@src/context' @@ -30,14 +29,6 @@ afterAll(async () => { database.neode.close() }) -const createPostMutation = gql` - mutation ($title: String!, $content: String!, $categoryIds: [ID]) { - CreatePost(title: $title, content: $content, categoryIds: $categoryIds) { - language - } - } -` - describe('languagesMiddleware', () => { variables = { title: 'Test post languages', @@ -61,7 +52,7 @@ describe('languagesMiddleware', () => { } await expect( mutate({ - mutation: createPostMutation, + mutation: CreatePost, variables, }), ).resolves.toMatchObject({ @@ -80,7 +71,7 @@ describe('languagesMiddleware', () => { } await expect( mutate({ - mutation: createPostMutation, + mutation: CreatePost, variables, }), ).resolves.toMatchObject({ @@ -99,7 +90,7 @@ describe('languagesMiddleware', () => { } await expect( mutate({ - mutation: createPostMutation, + mutation: CreatePost, variables, }), ).resolves.toMatchObject({ @@ -119,7 +110,7 @@ describe('languagesMiddleware', () => { } await expect( mutate({ - mutation: createPostMutation, + mutation: CreatePost, variables, }), ).resolves.toMatchObject({ @@ -139,7 +130,7 @@ describe('languagesMiddleware', () => { } await expect( mutate({ - mutation: createPostMutation, + mutation: CreatePost, variables, }), ).resolves.toMatchObject({ diff --git a/backend/src/middleware/notifications/notificationsMiddleware.emails.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.emails.spec.ts index 681b0e13b..ae5e83b09 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.emails.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.emails.spec.ts @@ -1,13 +1,14 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ - -import gql from 'graphql-tag' - import Factory, { cleanDatabase } from '@db/factories' +import { CreateComment } from '@graphql/queries/CreateComment' import { CreateGroup } from '@graphql/queries/CreateGroup' import { CreatePost } from '@graphql/queries/CreatePost' +import { followUser } from '@graphql/queries/followUser' import { JoinGroup } from '@graphql/queries/JoinGroup' +import { markAllAsRead } from '@graphql/queries/markAllAsRead' +import { notifications } from '@graphql/queries/notifications' import type { ApolloTestSetup } from '@root/test/helpers' import { createApolloTestSetup } from '@root/test/helpers' import type { Context } from '@src/context' @@ -32,61 +33,6 @@ const mentionString = ` @group-member @email-less-member` -const createCommentMutation = gql` - mutation ($id: ID, $postId: ID!, $content: String!) { - CreateComment(id: $id, postId: $postId, content: $content) { - id - content - } - } -` - -const notificationQuery = gql` - query ($read: Boolean) { - notifications(read: $read, orderBy: updatedAt_desc) { - read - reason - createdAt - relatedUser { - id - } - from { - __typename - ... on Post { - id - content - } - ... on Comment { - id - content - } - ... on Group { - id - } - } - } - } -` - -const followUserMutation = gql` - mutation ($id: ID!) { - followUser(id: $id) { - id - } - } -` - -const markAllAsRead = async () => - mutate({ - mutation: gql` - mutation { - markAllAsRead { - id - } - } - `, - }) - beforeAll(async () => { await cleanDatabase() const apolloSetup = createApolloTestSetup({ context }) @@ -154,7 +100,7 @@ describe('emails sent for notifications', () => { }, }) await mutate({ - mutation: followUserMutation, + mutation: followUser, variables: { id: 'post-author' }, }) authenticatedUser = await emaillessMember.toJson() @@ -166,7 +112,7 @@ describe('emails sent for notifications', () => { }, }) await mutate({ - mutation: followUserMutation, + mutation: followUser, variables: { id: 'post-author' }, }) }) @@ -206,7 +152,8 @@ describe('emails sent for notifications', () => { authenticatedUser = await groupMember.toJson() await expect( query({ - query: notificationQuery, + query: notifications, + variables: { orderBy: 'updatedAt_desc' }, }), ).resolves.toMatchObject({ data: { @@ -284,7 +231,8 @@ describe('emails sent for notifications', () => { authenticatedUser = await groupMember.toJson() await expect( query({ - query: notificationQuery, + query: notifications, + variables: { orderBy: 'updatedAt_desc' }, }), ).resolves.toMatchObject({ data: { @@ -363,7 +311,8 @@ describe('emails sent for notifications', () => { authenticatedUser = await groupMember.toJson() await expect( query({ - query: notificationQuery, + query: notifications, + variables: { orderBy: 'updatedAt_desc' }, }), ).resolves.toMatchObject({ data: { @@ -437,7 +386,8 @@ describe('emails sent for notifications', () => { authenticatedUser = await groupMember.toJson() await expect( query({ - query: notificationQuery, + query: notifications, + variables: { orderBy: 'updatedAt_desc' }, }), ).resolves.toMatchObject({ data: { @@ -503,18 +453,20 @@ describe('emails sent for notifications', () => { }) authenticatedUser = await groupMember.toJson() await mutate({ - mutation: createCommentMutation, + mutation: CreateComment, variables: { id: 'comment', content: `Hello, my beloved author.`, postId: 'post', }, }) - await markAllAsRead() + await mutate({ + mutation: markAllAsRead, + }) jest.clearAllMocks() authenticatedUser = await postAuthor.toJson() await mutate({ - mutation: createCommentMutation, + mutation: CreateComment, variables: { id: 'comment-2', content: `Hello, ${mentionString}, my trusty followers.`, @@ -537,8 +489,9 @@ describe('emails sent for notifications', () => { authenticatedUser = await groupMember.toJson() await expect( query({ - query: notificationQuery, + query: notifications, variables: { + orderBy: 'updatedAt_desc', read: false, }, }), @@ -591,18 +544,20 @@ describe('emails sent for notifications', () => { }) authenticatedUser = await groupMember.toJson() await mutate({ - mutation: createCommentMutation, + mutation: CreateComment, variables: { id: 'comment', content: `Hello, my beloved author.`, postId: 'post', }, }) - await markAllAsRead() + await mutate({ + mutation: markAllAsRead, + }) jest.clearAllMocks() authenticatedUser = await postAuthor.toJson() await mutate({ - mutation: createCommentMutation, + mutation: CreateComment, variables: { id: 'comment-2', content: `Hello, ${mentionString}, my trusty followers.`, @@ -625,8 +580,9 @@ describe('emails sent for notifications', () => { authenticatedUser = await groupMember.toJson() await expect( query({ - query: notificationQuery, + query: notifications, variables: { + orderBy: 'updatedAt_desc', read: false, }, }), @@ -680,18 +636,20 @@ describe('emails sent for notifications', () => { }) authenticatedUser = await groupMember.toJson() await mutate({ - mutation: createCommentMutation, + mutation: CreateComment, variables: { id: 'comment', content: `Hello, my beloved author.`, postId: 'post', }, }) - await markAllAsRead() + await mutate({ + mutation: markAllAsRead, + }) jest.clearAllMocks() authenticatedUser = await postAuthor.toJson() await mutate({ - mutation: createCommentMutation, + mutation: CreateComment, variables: { id: 'comment-2', content: `Hello, ${mentionString}, my trusty followers.`, @@ -708,8 +666,9 @@ describe('emails sent for notifications', () => { authenticatedUser = await groupMember.toJson() await expect( query({ - query: notificationQuery, + query: notifications, variables: { + orderBy: 'updatedAt_desc', read: false, }, }), diff --git a/backend/src/middleware/notifications/notificationsMiddleware.followed-users.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.followed-users.spec.ts index 7bd8c8c46..bf745d4c7 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.followed-users.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.followed-users.spec.ts @@ -1,12 +1,11 @@ /* 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 { CreateGroup } from '@graphql/queries/CreateGroup' import { CreatePost } from '@graphql/queries/CreatePost' +import { followUser } from '@graphql/queries/followUser' +import { notifications } from '@graphql/queries/notifications' import type { ApolloTestSetup } from '@root/test/helpers' import { createApolloTestSetup } from '@root/test/helpers' import type { Context } from '@src/context' @@ -26,41 +25,6 @@ let server: ApolloTestSetup['server'] let postAuthor, firstFollower, secondFollower, thirdFollower, emaillessFollower -const notificationQuery = gql` - query ($read: Boolean) { - notifications(read: $read, orderBy: updatedAt_desc) { - read - reason - createdAt - relatedUser { - id - } - from { - __typename - ... on Post { - id - content - } - ... on Comment { - id - content - } - ... on Group { - id - } - } - } - } -` - -const followUserMutation = gql` - mutation ($id: ID!) { - followUser(id: $id) { - id - } - } -` - beforeAll(async () => { await cleanDatabase() const apolloSetup = createApolloTestSetup({ context }) @@ -135,22 +99,22 @@ describe('following users notifications', () => { await secondFollower.update({ emailNotificationsFollowingUsers: false }) authenticatedUser = await firstFollower.toJson() await mutate({ - mutation: followUserMutation, + mutation: followUser, variables: { id: 'post-author' }, }) authenticatedUser = await secondFollower.toJson() await mutate({ - mutation: followUserMutation, + mutation: followUser, variables: { id: 'post-author' }, }) authenticatedUser = await thirdFollower.toJson() await mutate({ - mutation: followUserMutation, + mutation: followUser, variables: { id: 'post-author' }, }) authenticatedUser = await emaillessFollower.toJson() await mutate({ - mutation: followUserMutation, + mutation: followUser, variables: { id: 'post-author' }, }) jest.clearAllMocks() @@ -172,7 +136,8 @@ describe('following users notifications', () => { it('sends NO notification to the post author', async () => { await expect( query({ - query: notificationQuery, + query: notifications, + variables: { orderBy: 'updatedAt_desc' }, }), ).resolves.toMatchObject({ data: { @@ -186,7 +151,8 @@ describe('following users notifications', () => { authenticatedUser = await firstFollower.toJson() await expect( query({ - query: notificationQuery, + query: notifications, + variables: { orderBy: 'updatedAt_desc' }, }), ).resolves.toMatchObject({ data: { @@ -209,7 +175,8 @@ describe('following users notifications', () => { authenticatedUser = await secondFollower.toJson() await expect( query({ - query: notificationQuery, + query: notifications, + variables: { orderBy: 'updatedAt_desc' }, }), ).resolves.toMatchObject({ data: { @@ -232,7 +199,8 @@ describe('following users notifications', () => { authenticatedUser = await emaillessFollower.toJson() await expect( query({ - query: notificationQuery, + query: notifications, + variables: { orderBy: 'updatedAt_desc' }, }), ).resolves.toMatchObject({ data: { @@ -295,7 +263,8 @@ describe('following users notifications', () => { it('sends NO notification to the post author', async () => { await expect( query({ - query: notificationQuery, + query: notifications, + variables: { orderBy: 'updatedAt_desc' }, }), ).resolves.toMatchObject({ data: { @@ -309,7 +278,8 @@ describe('following users notifications', () => { authenticatedUser = await firstFollower.toJson() await expect( query({ - query: notificationQuery, + query: notifications, + variables: { orderBy: 'updatedAt_desc' }, }), ).resolves.toMatchObject({ data: { @@ -364,7 +334,8 @@ describe('following users notifications', () => { it('sends NO notification to the post author', async () => { await expect( query({ - query: notificationQuery, + query: notifications, + variables: { orderBy: 'updatedAt_desc' }, }), ).resolves.toMatchObject({ data: { @@ -378,7 +349,8 @@ describe('following users notifications', () => { authenticatedUser = await firstFollower.toJson() await expect( query({ - query: notificationQuery, + query: notifications, + variables: { orderBy: 'updatedAt_desc' }, }), ).resolves.toMatchObject({ data: { @@ -433,7 +405,8 @@ describe('following users notifications', () => { it('sends NO notification to the post author', async () => { await expect( query({ - query: notificationQuery, + query: notifications, + variables: { orderBy: 'updatedAt_desc' }, }), ).resolves.toMatchObject({ data: { @@ -447,7 +420,8 @@ describe('following users notifications', () => { authenticatedUser = await firstFollower.toJson() await expect( query({ - query: notificationQuery, + query: notifications, + variables: { orderBy: 'updatedAt_desc' }, }), ).resolves.toMatchObject({ data: { diff --git a/backend/src/middleware/notifications/notificationsMiddleware.mentions-in-groups.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.mentions-in-groups.spec.ts index 82cbd01f3..2beac88d5 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.mentions-in-groups.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.mentions-in-groups.spec.ts @@ -1,14 +1,14 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ - -import gql from 'graphql-tag' - import Factory, { cleanDatabase } from '@db/factories' import { ChangeGroupMemberRole } from '@graphql/queries/ChangeGroupMemberRole' +import { CreateComment } from '@graphql/queries/CreateComment' import { CreateGroup } from '@graphql/queries/CreateGroup' import { CreatePost } from '@graphql/queries/CreatePost' import { JoinGroup } from '@graphql/queries/JoinGroup' +import { markAllAsRead } from '@graphql/queries/markAllAsRead' +import { notifications } from '@graphql/queries/notifications' import type { ApolloTestSetup } from '@root/test/helpers' import { createApolloTestSetup } from '@root/test/helpers' import type { Context } from '@src/context' @@ -35,53 +35,6 @@ const mentionString = ` @email-less-member. ` -const createCommentMutation = gql` - mutation ($id: ID, $postId: ID!, $commentContent: String!) { - CreateComment(id: $id, postId: $postId, content: $commentContent) { - id - content - } - } -` - -const notificationQuery = gql` - query ($read: Boolean) { - notifications(read: $read, orderBy: updatedAt_desc) { - read - reason - createdAt - relatedUser { - id - } - from { - __typename - ... on Post { - id - content - } - ... on Comment { - id - content - } - ... on Group { - id - } - } - } - } -` - -const markAllAsRead = async () => - mutate({ - mutation: gql` - mutation { - markAllAsRead { - id - } - } - `, - }) - beforeAll(async () => { await cleanDatabase() const apolloSetup = createApolloTestSetup({ context }) @@ -285,9 +238,9 @@ describe('mentions in groups', () => { }, }) authenticatedUser = await groupMember.toJson() - await markAllAsRead() + await mutate({ mutation: markAllAsRead }) authenticatedUser = await emaillessMember.toJson() - await markAllAsRead() + await mutate({ mutation: markAllAsRead }) }) afterEach(async () => { @@ -313,8 +266,9 @@ describe('mentions in groups', () => { authenticatedUser = await noMember.toJson() await expect( query({ - query: notificationQuery, + query: notifications, variables: { + orderBy: 'updatedAt_desc', read: false, }, }), @@ -339,8 +293,9 @@ describe('mentions in groups', () => { authenticatedUser = await groupMember.toJson() await expect( query({ - query: notificationQuery, + query: notifications, variables: { + orderBy: 'updatedAt_desc', read: false, }, }), @@ -419,8 +374,9 @@ describe('mentions in groups', () => { authenticatedUser = await noMember.toJson() await expect( query({ - query: notificationQuery, + query: notifications, variables: { + orderBy: 'updatedAt_desc', read: false, }, }), @@ -436,8 +392,9 @@ describe('mentions in groups', () => { authenticatedUser = await pendingMember.toJson() await expect( query({ - query: notificationQuery, + query: notifications, variables: { + orderBy: 'updatedAt_desc', read: false, }, }), @@ -453,8 +410,9 @@ describe('mentions in groups', () => { authenticatedUser = await groupMember.toJson() await expect( query({ - query: notificationQuery, + query: notifications, variables: { + orderBy: 'updatedAt_desc', read: false, }, }), @@ -521,8 +479,9 @@ describe('mentions in groups', () => { authenticatedUser = await noMember.toJson() await expect( query({ - query: notificationQuery, + query: notifications, variables: { + orderBy: 'updatedAt_desc', read: false, }, }), @@ -538,8 +497,9 @@ describe('mentions in groups', () => { authenticatedUser = await pendingMember.toJson() await expect( query({ - query: notificationQuery, + query: notifications, variables: { + orderBy: 'updatedAt_desc', read: false, }, }), @@ -555,8 +515,9 @@ describe('mentions in groups', () => { authenticatedUser = await groupMember.toJson() await expect( query({ - query: notificationQuery, + query: notifications, variables: { + orderBy: 'updatedAt_desc', read: false, }, }), @@ -618,15 +579,15 @@ describe('mentions in groups', () => { }, }) authenticatedUser = await groupMember.toJson() - await markAllAsRead() + await mutate({ mutation: markAllAsRead }) authenticatedUser = await postAuthor.toJson() jest.clearAllMocks() await mutate({ - mutation: createCommentMutation, + mutation: CreateComment, variables: { id: 'public-comment', postId: 'public-post', - commentContent: `Hey everyone ${mentionString}! Please read this`, + content: `Hey everyone ${mentionString}! Please read this`, }, }) }) @@ -635,8 +596,9 @@ describe('mentions in groups', () => { authenticatedUser = await noMember.toJson() await expect( query({ - query: notificationQuery, + query: notifications, variables: { + orderBy: 'updatedAt_desc', read: false, }, }), @@ -661,8 +623,9 @@ describe('mentions in groups', () => { authenticatedUser = await groupMember.toJson() await expect( query({ - query: notificationQuery, + query: notifications, variables: { + orderBy: 'updatedAt_desc', read: false, }, }), @@ -713,15 +676,15 @@ describe('mentions in groups', () => { }, }) authenticatedUser = await groupMember.toJson() - await markAllAsRead() + await mutate({ mutation: markAllAsRead }) authenticatedUser = await postAuthor.toJson() jest.clearAllMocks() await mutate({ - mutation: createCommentMutation, + mutation: CreateComment, variables: { id: 'closed-comment', postId: 'closed-post', - commentContent: `Hey members ${mentionString}! Please read this`, + content: `Hey members ${mentionString}! Please read this`, }, }) }) @@ -730,8 +693,9 @@ describe('mentions in groups', () => { authenticatedUser = await noMember.toJson() await expect( query({ - query: notificationQuery, + query: notifications, variables: { + orderBy: 'updatedAt_desc', read: false, }, }), @@ -747,8 +711,9 @@ describe('mentions in groups', () => { authenticatedUser = await pendingMember.toJson() await expect( query({ - query: notificationQuery, + query: notifications, variables: { + orderBy: 'updatedAt_desc', read: false, }, }), @@ -764,8 +729,9 @@ describe('mentions in groups', () => { authenticatedUser = await groupMember.toJson() await expect( query({ - query: notificationQuery, + query: notifications, variables: { + orderBy: 'updatedAt_desc', read: false, }, }), @@ -810,15 +776,15 @@ describe('mentions in groups', () => { }, }) authenticatedUser = await groupMember.toJson() - await markAllAsRead() + await mutate({ mutation: markAllAsRead }) authenticatedUser = await postAuthor.toJson() jest.clearAllMocks() await mutate({ - mutation: createCommentMutation, + mutation: CreateComment, variables: { id: 'hidden-comment', postId: 'hidden-post', - commentContent: `Hey hiders ${mentionString}! Please read this`, + content: `Hey hiders ${mentionString}! Please read this`, }, }) }) @@ -827,8 +793,9 @@ describe('mentions in groups', () => { authenticatedUser = await noMember.toJson() await expect( query({ - query: notificationQuery, + query: notifications, variables: { + orderBy: 'updatedAt_desc', read: false, }, }), @@ -844,8 +811,9 @@ describe('mentions in groups', () => { authenticatedUser = await pendingMember.toJson() await expect( query({ - query: notificationQuery, + query: notifications, variables: { + orderBy: 'updatedAt_desc', read: false, }, }), @@ -861,8 +829,9 @@ describe('mentions in groups', () => { authenticatedUser = await groupMember.toJson() await expect( query({ - query: notificationQuery, + query: notifications, variables: { + orderBy: 'updatedAt_desc', read: false, }, }), diff --git a/backend/src/middleware/notifications/notificationsMiddleware.observing-posts.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.observing-posts.spec.ts index 4ba22dac1..7d1bd7956 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.observing-posts.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.observing-posts.spec.ts @@ -1,10 +1,10 @@ /* 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 { CreateComment } from '@graphql/queries/CreateComment' import { CreatePost } from '@graphql/queries/CreatePost' +import { notifications } from '@graphql/queries/notifications' import { toggleObservePost } from '@graphql/queries/toggleObservePost' import type { ApolloTestSetup } from '@root/test/helpers' import { createApolloTestSetup } from '@root/test/helpers' @@ -25,41 +25,6 @@ let server: ApolloTestSetup['server'] let postAuthor, firstCommenter, secondCommenter, emaillessObserver -const createCommentMutation = gql` - mutation ($id: ID, $postId: ID!, $content: String!) { - CreateComment(id: $id, postId: $postId, content: $content) { - id - content - } - } -` - -const notificationQuery = gql` - query ($read: Boolean) { - notifications(read: $read, orderBy: updatedAt_desc) { - read - reason - createdAt - relatedUser { - id - } - from { - __typename - ... on Post { - id - content - } - ... on Comment { - id - content - } - ... on Group { - id - } - } - } - } -` beforeAll(async () => { await cleanDatabase() const apolloSetup = createApolloTestSetup({ context }) @@ -142,7 +107,7 @@ describe('notifications for users that observe a post', () => { beforeAll(async () => { authenticatedUser = await firstCommenter.toJson() await mutate({ - mutation: createCommentMutation, + mutation: CreateComment, variables: { postId: 'post', id: 'c-1', @@ -154,7 +119,8 @@ describe('notifications for users that observe a post', () => { it('sends NO notification to the commenter', async () => { await expect( query({ - query: notificationQuery, + query: notifications, + variables: { orderBy: 'updatedAt_desc' }, }), ).resolves.toMatchObject({ data: { @@ -168,7 +134,8 @@ describe('notifications for users that observe a post', () => { authenticatedUser = await postAuthor.toJson() await expect( query({ - query: notificationQuery, + query: notifications, + variables: { orderBy: 'updatedAt_desc' }, }), ).resolves.toMatchObject({ data: { @@ -202,7 +169,7 @@ describe('notifications for users that observe a post', () => { jest.clearAllMocks() authenticatedUser = await secondCommenter.toJson() await mutate({ - mutation: createCommentMutation, + mutation: CreateComment, variables: { postId: 'post', id: 'c-2', @@ -214,7 +181,8 @@ describe('notifications for users that observe a post', () => { it('sends NO notification to the commenter', async () => { await expect( query({ - query: notificationQuery, + query: notifications, + variables: { orderBy: 'updatedAt_desc' }, }), ).resolves.toMatchObject({ data: { @@ -228,7 +196,8 @@ describe('notifications for users that observe a post', () => { authenticatedUser = await postAuthor.toJson() await expect( query({ - query: notificationQuery, + query: notifications, + variables: { orderBy: 'updatedAt_desc' }, }), ).resolves.toMatchObject({ data: { @@ -259,7 +228,8 @@ describe('notifications for users that observe a post', () => { authenticatedUser = await firstCommenter.toJson() await expect( query({ - query: notificationQuery, + query: notifications, + variables: { orderBy: 'updatedAt_desc' }, }), ).resolves.toMatchObject({ data: { @@ -309,7 +279,7 @@ describe('notifications for users that observe a post', () => { authenticatedUser = await postAuthor.toJson() await mutate({ - mutation: createCommentMutation, + mutation: CreateComment, variables: { postId: 'post', id: 'c-3', @@ -321,7 +291,8 @@ describe('notifications for users that observe a post', () => { it('sends no new notification to the post author', async () => { await expect( query({ - query: notificationQuery, + query: notifications, + variables: { orderBy: 'updatedAt_desc' }, }), ).resolves.toMatchObject({ data: { @@ -352,7 +323,8 @@ describe('notifications for users that observe a post', () => { authenticatedUser = await firstCommenter.toJson() await expect( query({ - query: notificationQuery, + query: notifications, + variables: { orderBy: 'updatedAt_desc' }, }), ).resolves.toMatchObject({ data: { @@ -375,7 +347,8 @@ describe('notifications for users that observe a post', () => { authenticatedUser = await secondCommenter.toJson() await expect( query({ - query: notificationQuery, + query: notifications, + variables: { orderBy: 'updatedAt_desc' }, }), ).resolves.toMatchObject({ data: { diff --git a/backend/src/middleware/notifications/notificationsMiddleware.posts-in-groups.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.posts-in-groups.spec.ts index afcde7569..95c90aceb 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.posts-in-groups.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.posts-in-groups.spec.ts @@ -1,15 +1,14 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ - -import gql from 'graphql-tag' - import Factory, { cleanDatabase } from '@db/factories' import { ChangeGroupMemberRole } from '@graphql/queries/ChangeGroupMemberRole' import { CreateGroup } from '@graphql/queries/CreateGroup' import { CreatePost } from '@graphql/queries/CreatePost' import { JoinGroup } from '@graphql/queries/JoinGroup' +import { markAllAsRead } from '@graphql/queries/markAllAsRead' import { muteGroup } from '@graphql/queries/muteGroup' +import { notifications } from '@graphql/queries/notifications' import { unmuteGroup } from '@graphql/queries/unmuteGroup' import type { ApolloTestSetup } from '@root/test/helpers' import { createApolloTestSetup } from '@root/test/helpers' @@ -30,44 +29,6 @@ let server: ApolloTestSetup['server'] let postAuthor, groupMember, pendingMember, emaillessMember -const notificationQuery = gql` - query ($read: Boolean) { - notifications(read: $read, orderBy: updatedAt_desc) { - read - reason - createdAt - relatedUser { - id - } - from { - __typename - ... on Post { - id - content - } - ... on Comment { - id - content - } - ... on Group { - id - } - } - } - } -` - -const markAllAsRead = async () => - mutate({ - mutation: gql` - mutation { - markAllAsRead { - id - } - } - `, - }) - beforeAll(async () => { await cleanDatabase() const apolloSetup = createApolloTestSetup({ context }) @@ -190,9 +151,9 @@ describe('notify group members of new posts in group', () => { beforeEach(async () => { jest.clearAllMocks() authenticatedUser = await groupMember.toJson() - await markAllAsRead() + await mutate({ mutation: markAllAsRead }) authenticatedUser = await postAuthor.toJson() - await markAllAsRead() + await mutate({ mutation: markAllAsRead }) await mutate({ mutation: CreatePost, variables: { @@ -207,8 +168,9 @@ describe('notify group members of new posts in group', () => { it('sends NO notification to the author of the post', async () => { await expect( query({ - query: notificationQuery, + query: notifications, variables: { + orderBy: 'updatedAt_desc', read: false, }, }), @@ -224,8 +186,9 @@ describe('notify group members of new posts in group', () => { authenticatedUser = await pendingMember.toJson() await expect( query({ - query: notificationQuery, + query: notifications, variables: { + orderBy: 'updatedAt_desc', read: false, }, }), @@ -241,8 +204,9 @@ describe('notify group members of new posts in group', () => { authenticatedUser = await groupMember.toJson() await expect( query({ - query: notificationQuery, + query: notifications, variables: { + orderBy: 'updatedAt_desc', read: false, }, }), @@ -298,8 +262,9 @@ describe('notify group members of new posts in group', () => { it('sends NO notification when another post is posted', async () => { await expect( query({ - query: notificationQuery, + query: notifications, variables: { + orderBy: 'updatedAt_desc', read: false, }, }), @@ -330,7 +295,7 @@ describe('notify group members of new posts in group', () => { it('sends notification when another post is posted', async () => { authenticatedUser = await groupMember.toJson() - await markAllAsRead() + await mutate({ mutation: markAllAsRead }) authenticatedUser = await postAuthor.toJson() await mutate({ mutation: CreatePost, @@ -344,8 +309,9 @@ describe('notify group members of new posts in group', () => { authenticatedUser = await groupMember.toJson() await expect( query({ - query: notificationQuery, + query: notifications, variables: { + orderBy: 'updatedAt_desc', read: false, }, }), @@ -376,7 +342,7 @@ describe('notify group members of new posts in group', () => { beforeEach(async () => { await groupMember.relateTo(postAuthor, 'blocked') authenticatedUser = await groupMember.toJson() - await markAllAsRead() + await mutate({ mutation: markAllAsRead }) jest.clearAllMocks() authenticatedUser = await postAuthor.toJson() await mutate({ @@ -394,8 +360,9 @@ describe('notify group members of new posts in group', () => { authenticatedUser = await groupMember.toJson() await expect( query({ - query: notificationQuery, + query: notifications, variables: { + orderBy: 'updatedAt_desc', read: false, }, }), @@ -416,7 +383,7 @@ describe('notify group members of new posts in group', () => { beforeEach(async () => { await groupMember.relateTo(postAuthor, 'muted') authenticatedUser = await groupMember.toJson() - await markAllAsRead() + await mutate({ mutation: markAllAsRead }) jest.clearAllMocks() authenticatedUser = await postAuthor.toJson() await mutate({ @@ -434,8 +401,9 @@ describe('notify group members of new posts in group', () => { authenticatedUser = await groupMember.toJson() await expect( query({ - query: notificationQuery, + query: notifications, variables: { + orderBy: 'updatedAt_desc', read: false, }, }), diff --git a/backend/src/middleware/notifications/notificationsMiddleware.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.spec.ts index f97c46fe3..216cb5543 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.spec.ts @@ -4,16 +4,18 @@ /* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-return */ -import gql from 'graphql-tag' - import pubsubContext from '@context/pubsub' import Factory, { cleanDatabase } from '@db/factories' import { ChangeGroupMemberRole } from '@graphql/queries/ChangeGroupMemberRole' +import { CreateComment } from '@graphql/queries/CreateComment' import { CreateGroup } from '@graphql/queries/CreateGroup' import { CreateMessage } from '@graphql/queries/CreateMessage' +import { CreatePost } from '@graphql/queries/CreatePost' import { CreateRoom } from '@graphql/queries/CreateRoom' import { JoinGroup } from '@graphql/queries/JoinGroup' import { LeaveGroup } from '@graphql/queries/LeaveGroup' +import { markAsRead } from '@graphql/queries/markAsRead' +import { notifications } from '@graphql/queries/notifications' import { RemoveUserFromGroup } from '@graphql/queries/RemoveUserFromGroup' import { UpdatePost } from '@graphql/queries/UpdatePost' import type { ApolloTestSetup } from '@root/test/helpers' @@ -46,23 +48,6 @@ let database: ApolloTestSetup['database'] let server: ApolloTestSetup['server'] const categoryIds = ['cat9'] -const createPostMutation = gql` - mutation ($id: ID, $title: String!, $postContent: String!, $categoryIds: [ID]!) { - CreatePost(id: $id, title: $title, content: $postContent, categoryIds: $categoryIds) { - id - title - content - } - } -` -const createCommentMutation = gql` - mutation ($id: ID, $postId: ID!, $commentContent: String!) { - CreateComment(id: $id, postId: $postId, content: $commentContent) { - id - content - } - } -` beforeAll(async () => { await cleanDatabase() @@ -106,33 +91,6 @@ afterEach(async () => { }) describe('notifications', () => { - const notificationQuery = gql` - query ($read: Boolean) { - notifications(read: $read, orderBy: updatedAt_desc) { - read - reason - createdAt - relatedUser { - id - } - from { - __typename - ... on Post { - id - content - } - ... on Comment { - id - content - } - ... on Group { - id - } - } - } - } - ` - describe('authenticated', () => { beforeEach(async () => { authenticatedUser = await notifiedUser.toJson() @@ -146,11 +104,11 @@ describe('notifications', () => { const createPostAction = async () => { authenticatedUser = await postAuthor.toJson() await mutate({ - mutation: createPostMutation, + mutation: CreatePost, variables: { id: 'p47', title, - postContent, + content: postContent, categoryIds, }, }) @@ -163,11 +121,11 @@ describe('notifications', () => { await createPostAction() authenticatedUser = await commentAuthor.toJson() await mutate({ - mutation: createCommentMutation, + mutation: CreateComment, variables: { id: 'c47', postId: 'p47', - commentContent, + content: commentContent, }, }) authenticatedUser = await notifiedUser.toJson() @@ -202,8 +160,9 @@ describe('notifications', () => { await createCommentOnPostAction() await expect( query({ - query: notificationQuery, + query: notifications, variables: { + orderBy: 'updatedAt_desc', read: false, }, }), @@ -243,8 +202,9 @@ describe('notifications', () => { await createCommentOnPostAction() await expect( query({ - query: notificationQuery, + query: notifications, variables: { + orderBy: 'updatedAt_desc', read: false, }, }), @@ -283,8 +243,9 @@ describe('notifications', () => { await expect( query({ - query: notificationQuery, + query: notifications, variables: { + orderBy: 'updatedAt_desc', read: false, }, }), @@ -302,8 +263,9 @@ describe('notifications', () => { await expect( query({ - query: notificationQuery, + query: notifications, variables: { + orderBy: 'updatedAt_desc', read: false, }, }), @@ -326,8 +288,9 @@ describe('notifications', () => { await expect( query({ - query: notificationQuery, + query: notifications, variables: { + orderBy: 'updatedAt_desc', read: false, }, }), @@ -366,8 +329,9 @@ describe('notifications', () => { 'Hey @al-capone how do you do?' await expect( query({ - query: notificationQuery, + query: notifications, variables: { + orderBy: 'updatedAt_desc', read: false, }, }), @@ -407,8 +371,9 @@ describe('notifications', () => { 'Hey @al-capone how do you do?' await expect( query({ - query: notificationQuery, + query: notifications, variables: { + orderBy: 'updatedAt_desc', read: false, }, }), @@ -473,7 +438,7 @@ describe('notifications', () => { variables: { id: 'p47', title, - postContent: updatedContent, + content: updatedContent, categoryIds, }, }) @@ -504,8 +469,9 @@ describe('notifications', () => { }) await expect( query({ - query: notificationQuery, + query: notifications, variables: { + orderBy: 'updatedAt_desc', read: false, }, }), @@ -513,27 +479,19 @@ describe('notifications', () => { }) describe('if the notification was marked as read earlier', () => { - const markAsReadAction = async () => { - const mutation = gql` - mutation ($id: ID!) { - markAsRead(id: $id) { - read - } - } - ` - await mutate({ mutation, variables: { id: 'p47' } }) - } - describe('but the next mention happens after the notification was marked as read', () => { it('sets the `read` attribute to false again', async () => { await createPostAction() - await markAsReadAction() + await mutate({ mutation: markAsRead, variables: { id: 'p47' } }) const { data: { notifications: [{ read: readBefore }], }, } = await query({ - query: notificationQuery, + query: notifications, + variables: { + orderBy: 'updatedAt_desc', + }, }) await updatePostAction() const { @@ -541,7 +499,11 @@ describe('notifications', () => { notifications: [{ read: readAfter }], }, } = await query({ - query: notificationQuery, + query: notifications, + variables: { + orderBy: 'updatedAt_desc', + read: false, + }, }) expect(readBefore).toEqual(true) expect(readAfter).toEqual(false) @@ -549,13 +511,16 @@ describe('notifications', () => { it('does not update the `createdAt` attribute', async () => { await createPostAction() - await markAsReadAction() + await mutate({ mutation: markAsRead, variables: { id: 'p47' } }) const { data: { notifications: [{ createdAt: createdAtBefore }], }, } = await query({ - query: notificationQuery, + query: notifications, + variables: { + orderBy: 'updatedAt_desc', + }, }) await updatePostAction() const { @@ -563,7 +528,11 @@ describe('notifications', () => { notifications: [{ createdAt: createdAtAfter }], }, } = await query({ - query: notificationQuery, + query: notifications, + variables: { + orderBy: 'updatedAt_desc', + read: false, + }, }) expect(createdAtBefore).toBeTruthy() expect(Date.parse(createdAtBefore)).toEqual(expect.any(Number)) @@ -588,8 +557,9 @@ describe('notifications', () => { await expect( query({ - query: notificationQuery, + query: notifications, variables: { + orderBy: 'updatedAt_desc', read: false, }, }), @@ -630,8 +600,9 @@ describe('notifications', () => { await expect( query({ - query: notificationQuery, + query: notifications, variables: { + orderBy: 'updatedAt_desc', read: false, }, }), @@ -704,8 +675,9 @@ describe('notifications', () => { await expect( query({ - query: notificationQuery, + query: notifications, variables: { + orderBy: 'updatedAt_desc', read: false, }, }), @@ -739,8 +711,9 @@ describe('notifications', () => { await expect( query({ - query: notificationQuery, + query: notifications, variables: { + orderBy: 'updatedAt_desc', read: false, }, }), @@ -771,8 +744,9 @@ describe('notifications', () => { await createCommentOnPostAction() await expect( query({ - query: notificationQuery, + query: notifications, variables: { + orderBy: 'updatedAt_desc', read: false, }, }), @@ -822,8 +796,9 @@ describe('notifications', () => { await createCommentOnPostAction() await expect( query({ - query: notificationQuery, + query: notifications, variables: { + orderBy: 'updatedAt_desc', read: false, }, }), @@ -1118,7 +1093,11 @@ describe('notifications', () => { await joinGroupAction() await expect( query({ - query: notificationQuery, + query: notifications, + variables: { + orderBy: 'updatedAt_desc', + read: false, + }, }), ).resolves.toMatchObject({ data: { @@ -1156,7 +1135,11 @@ describe('notifications', () => { await joinGroupAction() await expect( query({ - query: notificationQuery, + query: notifications, + variables: { + orderBy: 'updatedAt_desc', + read: false, + }, }), ).resolves.toMatchObject({ data: { @@ -1213,7 +1196,11 @@ describe('notifications', () => { await leaveGroupAction() await expect( query({ - query: notificationQuery, + query: notifications, + variables: { + orderBy: 'updatedAt_desc', + read: false, + }, }), ).resolves.toMatchObject({ data: { @@ -1269,7 +1256,11 @@ describe('notifications', () => { await leaveGroupAction() await expect( query({ - query: notificationQuery, + query: notifications, + variables: { + orderBy: 'updatedAt_desc', + read: false, + }, }), ).resolves.toMatchObject({ data: { @@ -1340,7 +1331,11 @@ describe('notifications', () => { await changeGroupMemberRoleAction() await expect( query({ - query: notificationQuery, + query: notifications, + variables: { + orderBy: 'updatedAt_desc', + read: false, + }, }), ).resolves.toMatchObject({ data: { @@ -1378,7 +1373,11 @@ describe('notifications', () => { await changeGroupMemberRoleAction() await expect( query({ - query: notificationQuery, + query: notifications, + variables: { + orderBy: 'updatedAt_desc', + read: false, + }, }), ).resolves.toMatchObject({ data: { @@ -1436,7 +1435,11 @@ describe('notifications', () => { await removeUserFromGroupAction() await expect( query({ - query: notificationQuery, + query: notifications, + variables: { + orderBy: 'updatedAt_desc', + read: false, + }, }), ).resolves.toMatchObject({ data: { @@ -1474,7 +1477,11 @@ describe('notifications', () => { await removeUserFromGroupAction() await expect( query({ - query: notificationQuery, + query: notifications, + variables: { + orderBy: 'updatedAt_desc', + read: false, + }, }), ).resolves.toMatchObject({ data: { diff --git a/backend/src/middleware/orderByMiddleware.spec.ts b/backend/src/middleware/orderByMiddleware.spec.ts index b98c21c0b..78f5a1988 100644 --- a/backend/src/middleware/orderByMiddleware.spec.ts +++ b/backend/src/middleware/orderByMiddleware.spec.ts @@ -1,9 +1,8 @@ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ import { createTestClient } from 'apollo-server-testing' -import gql from 'graphql-tag' import { cleanDatabase } from '@db/factories' import { getNeode, getDriver } from '@db/neo4j' +import { Post } from '@graphql/queries/Post' import createServer from '@src/server' const neode = getNeode() @@ -15,6 +14,9 @@ const { server } = createServer({ user: null, neode, driver, + cypherParams: { + currentUserId: null, + }, } }, }) @@ -30,10 +32,10 @@ afterAll(async () => { }) beforeEach(async () => { - await neode.create('Post', { title: 'first' }) - await neode.create('Post', { title: 'second' }) - await neode.create('Post', { title: 'third' }) - await neode.create('Post', { title: 'last' }) + await neode.create('Post', { title: 'first', content: 'content' }) + await neode.create('Post', { title: 'second', content: 'content' }) + await neode.create('Post', { title: 'third', content: 'content' }) + await neode.create('Post', { title: 'last', content: 'content' }) }) // TODO: avoid database clean after each test in the future if possible for performance and flakyness reasons by filling the database step by step, see issue https://github.com/Ocelot-Social-Community/Ocelot-Social/issues/4543 @@ -45,46 +47,35 @@ describe('Query', () => { describe('Post', () => { describe('orderBy', () => { it('createdAt descending is default', async () => { - const posts = [ - { title: 'last' }, - { title: 'third' }, - { title: 'second' }, - { title: 'first' }, - ] - const expected = expect.objectContaining({ data: { Post: posts } }) - await expect( - query({ - query: gql` - { - Post { - title - } - } - `, - }), - ).resolves.toEqual(expected) + await expect(query({ query: Post })).resolves.toMatchObject({ + data: { + Post: [ + expect.objectContaining({ title: 'last' }), + expect.objectContaining({ title: 'third' }), + expect.objectContaining({ title: 'second' }), + expect.objectContaining({ title: 'first' }), + ], + }, + }) }) describe('(orderBy: createdAt_asc)', () => { it('orders by createdAt ascending', async () => { - const posts = [ - { title: 'first' }, - { title: 'second' }, - { title: 'third' }, - { title: 'last' }, - ] - const expected = expect.objectContaining({ data: { Post: posts } }) await expect( query({ - query: gql` - { - Post(orderBy: createdAt_asc) { - title - } - } - `, + query: Post, + variables: { orderBy: 'createdAt_asc' }, }), - ).resolves.toEqual(expected) + ).resolves.toMatchObject({ + data: { + Post: [ + expect.objectContaining({ title: 'first' }), + expect.objectContaining({ title: 'second' }), + expect.objectContaining({ title: 'third' }), + expect.objectContaining({ title: 'last' }), + ], + }, + }) }) }) }) diff --git a/backend/src/middleware/orderByMiddleware.ts b/backend/src/middleware/orderByMiddleware.ts index c2d2ce447..cf579e9d3 100644 --- a/backend/src/middleware/orderByMiddleware.ts +++ b/backend/src/middleware/orderByMiddleware.ts @@ -6,13 +6,20 @@ import cloneDeep from 'lodash/cloneDeep' const defaultOrderBy = (resolve, root, args, context, resolveInfo) => { const copy = cloneDeep(resolveInfo) - const newestFirst = { - kind: 'Argument', - name: { kind: 'Name', value: 'orderBy' }, - value: { kind: 'EnumValue', value: 'sortDate_desc' }, - } const [fieldNode] = copy.fieldNodes - if (fieldNode) fieldNode.arguments.push(newestFirst) + if (fieldNode) { + const orderByArg = fieldNode.arguments.find((arg) => arg.name.value === 'orderBy') + + if (!orderByArg) { + fieldNode.arguments.push({ + kind: 'Argument', + name: { kind: 'Name', value: 'orderBy' }, + value: { kind: 'EnumValue', value: 'sortDate_desc' }, + }) + } else if (args.orderBy === undefined) { + orderByArg.value = { kind: 'EnumValue', value: 'sortDate_desc' } + } + } return resolve(root, args, context, copy) } diff --git a/backend/src/middleware/permissionsMiddleware.spec.ts b/backend/src/middleware/permissionsMiddleware.spec.ts index c99a0d8b3..c49a1dd21 100644 --- a/backend/src/middleware/permissionsMiddleware.spec.ts +++ b/backend/src/middleware/permissionsMiddleware.spec.ts @@ -3,7 +3,7 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ import Factory, { cleanDatabase } from '@db/factories' import { Signup } from '@graphql/queries/Signup' -import { User } from '@graphql/queries/User' +import { User, UserEmail } from '@graphql/queries/User' import type { ApolloTestSetup } from '@root/test/helpers' import { createApolloTestSetup } from '@root/test/helpers' import type { Context } from '@src/context' @@ -116,7 +116,7 @@ describe('authorization', () => { it("exposes the owner's email address", async () => { variables = { name: 'Owner' } - await expect(query({ query: User, variables })).resolves.toMatchObject({ + await expect(query({ query: UserEmail, variables })).resolves.toMatchObject({ data: { User: [{ email: 'owner@example.org' }] }, errors: undefined, }) @@ -130,7 +130,7 @@ describe('authorization', () => { it("throws an error and does not expose the owner's email address", async () => { await expect( - query({ query: User, variables: { name: 'Owner' } }), + query({ query: UserEmail, variables: { name: 'Owner' } }), ).resolves.toMatchObject({ errors: [{ message: 'Not Authorized!' }], data: { User: [null] }, @@ -145,7 +145,7 @@ describe('authorization', () => { it("throws an error and does not expose the owner's email address", async () => { await expect( - query({ query: User, variables: { name: 'Owner' } }), + query({ query: UserEmail, variables: { name: 'Owner' } }), ).resolves.toMatchObject({ errors: [{ message: 'Not Authorized!' }], data: { User: [null] }, @@ -160,7 +160,7 @@ describe('authorization', () => { it("exposes the owner's email address", async () => { variables = { name: 'Owner' } - await expect(query({ query: User, variables })).resolves.toMatchObject({ + await expect(query({ query: UserEmail, variables })).resolves.toMatchObject({ data: { User: [{ email: 'owner@example.org' }] }, errors: undefined, }) diff --git a/backend/src/middleware/softDelete/softDeleteMiddleware.spec.ts b/backend/src/middleware/softDelete/softDeleteMiddleware.spec.ts index 19c583301..f080040b2 100644 --- a/backend/src/middleware/softDelete/softDeleteMiddleware.spec.ts +++ b/backend/src/middleware/softDelete/softDeleteMiddleware.spec.ts @@ -3,15 +3,14 @@ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ -import gql from 'graphql-tag' - import Factory, { cleanDatabase } from '@db/factories' +import { Post } from '@graphql/queries/Post' +import { User } from '@graphql/queries/User' import type { ApolloTestSetup } from '@root/test/helpers' import { createApolloTestSetup } from '@root/test/helpers' import type { Context } from '@src/context' const categoryIds = ['cat9'] -let graphqlQuery let moderator let user let troll @@ -21,10 +20,6 @@ let query: ApolloTestSetup['query'] let database: ApolloTestSetup['database'] let server: ApolloTestSetup['server'] -const action = () => { - return query({ query: graphqlQuery }) -} - beforeAll(async () => { await cleanDatabase() const apolloSetup = createApolloTestSetup({ context }) @@ -208,58 +203,15 @@ describe('softDeleteMiddleware', () => { describe('read disabled content', () => { let subject const beforeComment = async () => { - graphqlQuery = gql` - { - User(id: "u1") { - following { - comments { - content - contentExcerpt - } - } - } - } - ` - const { data } = await action() + const { data } = await query({ query: User, variables: { id: 'u1' } }) subject = (data as any).User[0].following[0].comments[0] } const beforeUser = async () => { - graphqlQuery = gql` - { - User(id: "u1") { - following { - name - slug - about - avatar { - url - } - } - } - } - ` - const { data } = await action() + const { data } = await query({ query: User, variables: { id: 'u1' } }) subject = (data as any).User[0].following[0] } const beforePost = async () => { - graphqlQuery = gql` - { - User(id: "u1") { - following { - contributions { - title - slug - image { - url - } - content - contentExcerpt - } - } - } - } - ` - const { data } = await action() + const { data } = await query({ query: User, variables: { id: 'u1' } }) subject = (data as any).User[0].following[0].contributions[0] } @@ -341,16 +293,6 @@ describe('softDeleteMiddleware', () => { describe('Query', () => { describe('Post', () => { - beforeEach(async () => { - graphqlQuery = gql` - { - Post { - title - } - } - ` - }) - describe('as user', () => { beforeEach(async () => { authenticatedUser = await user.toJson() @@ -358,7 +300,7 @@ describe('softDeleteMiddleware', () => { it('hides deleted or disabled posts', async () => { const expected = { data: { Post: [{ title: 'Publicly visible post' }] } } - await expect(action()).resolves.toMatchObject(expected) + await expect(query({ query: Post })).resolves.toMatchObject(expected) }) }) @@ -368,27 +310,18 @@ describe('softDeleteMiddleware', () => { }) it('shows disabled but hides deleted posts', async () => { - const expected = [{ title: 'Disabled post' }, { title: 'Publicly visible post' }] - const { data } = await action() - const { Post } = data as any - expect(Post).toEqual(expect.arrayContaining(expected)) + const { data } = await query({ query: Post }) + const { Post: PostData } = data as any + expect(PostData).toEqual( + expect.arrayContaining([ + expect.objectContaining({ title: 'Disabled post' }), + expect.objectContaining({ title: 'Publicly visible post' }), + ]), + ) }) }) describe('.comments', () => { - beforeEach(async () => { - graphqlQuery = gql` - { - Post(id: "p3") { - title - comments { - content - } - } - } - ` - }) - describe('as user', () => { beforeEach(async () => { authenticatedUser = await user.toJson() @@ -399,7 +332,7 @@ describe('softDeleteMiddleware', () => { { content: 'Enabled comment on public post' }, { content: 'UNAVAILABLE' }, ] - const { data } = await action() + const { data } = await query({ query: Post, variables: { id: 'p3' } }) const { Post: [{ comments }], } = data as any @@ -417,7 +350,7 @@ describe('softDeleteMiddleware', () => { { content: 'Enabled comment on public post' }, { content: 'Disabled comment' }, ] - const { data } = await action() + const { data } = await query({ query: Post, variables: { id: 'p3' } }) const { Post: [{ comments }], } = data as any diff --git a/backend/src/middleware/userInteractions.spec.ts b/backend/src/middleware/userInteractions.spec.ts index 61d92ff83..3b3bccbe7 100644 --- a/backend/src/middleware/userInteractions.spec.ts +++ b/backend/src/middleware/userInteractions.spec.ts @@ -2,10 +2,10 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ import { createTestClient } from 'apollo-server-testing' -import gql from 'graphql-tag' import Factory, { cleanDatabase } from '@db/factories' import { getNeode, getDriver } from '@db/neo4j' +import { Post } from '@graphql/queries/Post' import createServer from '@src/server' let query, aUser, bUser, post, authenticatedUser, variables @@ -13,14 +13,6 @@ let query, aUser, bUser, post, authenticatedUser, variables const driver = getDriver() const neode = getNeode() -const postQuery = gql` - query ($id: ID) { - Post(id: $id) { - clickedCount - } - } -` - beforeAll(async () => { await cleanDatabase() @@ -38,6 +30,9 @@ beforeAll(async () => { driver, neode, user: authenticatedUser, + cypherParams: { + currentUserId: authenticatedUser ? authenticatedUser.id : null, + }, } }, }) @@ -52,12 +47,12 @@ afterAll(async () => { describe('middleware/userInteractions', () => { describe('given one post', () => { it('does not change clickedCount when queried without ID', async () => { - await expect(query({ query: postQuery, variables })).resolves.toMatchObject({ + await expect(query({ query: Post, variables })).resolves.toMatchObject({ data: { Post: expect.arrayContaining([ - { + expect.objectContaining({ clickedCount: 0, - }, + }), ]), }, }) @@ -65,24 +60,24 @@ describe('middleware/userInteractions', () => { it('changes clickedCount when queried with ID', async () => { variables = { id: post.get('id') } - await expect(query({ query: postQuery, variables })).resolves.toMatchObject({ + await expect(query({ query: Post, variables })).resolves.toMatchObject({ data: { Post: expect.arrayContaining([ - { + expect.objectContaining({ clickedCount: 1, - }, + }), ]), }, }) }) it('does not change clickedCount when same user queries the post again', async () => { - await expect(query({ query: postQuery, variables })).resolves.toMatchObject({ + await expect(query({ query: Post, variables })).resolves.toMatchObject({ data: { Post: expect.arrayContaining([ - { + expect.objectContaining({ clickedCount: 1, - }, + }), ]), }, }) @@ -90,12 +85,12 @@ describe('middleware/userInteractions', () => { it('changes clickedCount when another user queries the post', async () => { authenticatedUser = await bUser.toJson() - await expect(query({ query: postQuery, variables })).resolves.toMatchObject({ + await expect(query({ query: Post, variables })).resolves.toMatchObject({ data: { Post: expect.arrayContaining([ - { + expect.objectContaining({ clickedCount: 2, - }, + }), ]), }, }) diff --git a/backend/src/middleware/validation/validationMiddleware.spec.ts b/backend/src/middleware/validation/validationMiddleware.spec.ts index bc8d3c883..d30358f9d 100644 --- a/backend/src/middleware/validation/validationMiddleware.spec.ts +++ b/backend/src/middleware/validation/validationMiddleware.spec.ts @@ -2,11 +2,13 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ import { createTestClient } from 'apollo-server-testing' -import gql from 'graphql-tag' import Factory, { cleanDatabase } from '@db/factories' import { getNeode, getDriver } from '@db/neo4j' +import { CreateComment } from '@graphql/queries/CreateComment' +import { fileReport } from '@graphql/queries/fileReport' import { review } from '@graphql/queries/review' +import { UpdateComment } from '@graphql/queries/UpdateComment' import { UpdateUser } from '@graphql/queries/UpdateUser' import createServer from '@src/server' @@ -22,32 +24,6 @@ let authenticatedUser, moderatingUser, commentingUser -const createCommentMutation = gql` - mutation ($id: ID, $postId: ID!, $content: String!) { - CreateComment(id: $id, postId: $postId, content: $content) { - id - } - } -` -const updateCommentMutation = gql` - mutation ($content: String!, $id: ID!) { - UpdateComment(content: $content, id: $id) { - id - } - } -` - -const reportMutation = gql` - mutation ($resourceId: ID!, $reasonCategory: ReasonCategory!, $reasonDescription: String!) { - fileReport( - resourceId: $resourceId - reasonCategory: $reasonCategory - reasonDescription: $reasonDescription - ) { - reportId - } - } -` beforeAll(async () => { await cleanDatabase() @@ -135,7 +111,7 @@ describe('validateCreateComment', () => { it('throws an error if content is empty', async () => { createCommentVariables = { ...createCommentVariables, postId: 'post-4-commenting' } await expect( - mutate({ mutation: createCommentMutation, variables: createCommentVariables }), + mutate({ mutation: CreateComment, variables: createCommentVariables }), ).resolves.toMatchObject({ data: { CreateComment: null }, errors: [{ message: 'Comment must be at least 1 character long!' }], @@ -145,7 +121,7 @@ describe('validateCreateComment', () => { it('sanitizes content and throws an error if not longer than 1 character', async () => { createCommentVariables = { postId: 'post-4-commenting', content: '' } await expect( - mutate({ mutation: createCommentMutation, variables: createCommentVariables }), + mutate({ mutation: CreateComment, variables: createCommentVariables }), ).resolves.toMatchObject({ data: { CreateComment: null }, errors: [{ message: 'Comment must be at least 1 character long!' }], @@ -159,7 +135,7 @@ describe('validateCreateComment', () => { content: 'valid content', } await expect( - mutate({ mutation: createCommentMutation, variables: createCommentVariables }), + mutate({ mutation: CreateComment, variables: createCommentVariables }), ).resolves.toMatchObject({ data: { CreateComment: null }, errors: [{ message: 'Comment cannot be created without a post!' }], @@ -188,7 +164,7 @@ describe('validateCreateComment', () => { it('throws an error if content is empty', async () => { updateCommentVariables = { ...updateCommentVariables, id: 'comment-id' } await expect( - mutate({ mutation: updateCommentMutation, variables: updateCommentVariables }), + mutate({ mutation: UpdateComment, variables: updateCommentVariables }), ).resolves.toMatchObject({ data: { UpdateComment: null }, errors: [{ message: 'Comment must be at least 1 character long!' }], @@ -198,7 +174,7 @@ describe('validateCreateComment', () => { it('sanitizes content and throws an error if not longer than 1 character', async () => { updateCommentVariables = { id: 'comment-id', content: '' } await expect( - mutate({ mutation: updateCommentMutation, variables: updateCommentVariables }), + mutate({ mutation: UpdateComment, variables: updateCommentVariables }), ).resolves.toMatchObject({ data: { UpdateComment: null }, errors: [{ message: 'Comment must be at least 1 character long!' }], @@ -212,7 +188,7 @@ describe('validateReport', () => { authenticatedUser = await reportingUser.toJson() reportVariables = { ...reportVariables, resourceId: 'reporting-user' } await expect( - mutate({ mutation: reportMutation, variables: reportVariables }), + mutate({ mutation: fileReport, variables: reportVariables }), ).resolves.toMatchObject({ data: { fileReport: null }, errors: [{ message: 'You cannot report yourself!' }],