mirror of
https://github.com/IT4Change/Ocelot-Social.git
synced 2025-12-13 07:45:56 +00:00
Refactor notification spec
Wow this took me the entire day: If you run `createServer` multiple times, more and more middlewares get added to the schema. That's why the test would create 2^n notifications for n times you called `createServer`. This is related to the following bug: https://github.com/prisma/graphql-middleware/issues/63
This commit is contained in:
parent
8de0195cf8
commit
bedbb21def
@ -1,12 +1,36 @@
|
||||
import { GraphQLClient } from 'graphql-request'
|
||||
import { host, login, gql } from '../../jest/helpers'
|
||||
import { gql } from '../../jest/helpers'
|
||||
import Factory from '../../seed/factories'
|
||||
import { createTestClient } from 'apollo-server-testing'
|
||||
import { neode, getDriver } from '../../bootstrap/neo4j'
|
||||
import createServer from '../../server'
|
||||
|
||||
const factory = Factory()
|
||||
let client
|
||||
const driver = getDriver()
|
||||
const instance = neode()
|
||||
let server
|
||||
let query
|
||||
let mutate
|
||||
let user
|
||||
let authenticatedUser
|
||||
|
||||
beforeAll(() => {
|
||||
const createServerResult = createServer({
|
||||
context: () => {
|
||||
return {
|
||||
user: authenticatedUser,
|
||||
neode: instance,
|
||||
driver,
|
||||
}
|
||||
},
|
||||
})
|
||||
server = createServerResult.server
|
||||
const createTestClientResult = createTestClient(server)
|
||||
query = createTestClientResult.query
|
||||
mutate = createTestClientResult.mutate
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
await factory.create('User', {
|
||||
user = await instance.create('User', {
|
||||
id: 'you',
|
||||
name: 'Al Capone',
|
||||
slug: 'al-capone',
|
||||
@ -19,8 +43,8 @@ afterEach(async () => {
|
||||
await factory.cleanDatabase()
|
||||
})
|
||||
|
||||
describe('currentUser { notifications }', () => {
|
||||
const query = gql`
|
||||
describe('notifications', () => {
|
||||
const notificationQuery = gql`
|
||||
query($read: Boolean) {
|
||||
currentUser {
|
||||
notifications(read: $read, orderBy: createdAt_desc) {
|
||||
@ -34,77 +58,54 @@ describe('currentUser { notifications }', () => {
|
||||
`
|
||||
|
||||
describe('authenticated', () => {
|
||||
let headers
|
||||
beforeEach(async () => {
|
||||
headers = await login({
|
||||
email: 'test@example.org',
|
||||
password: '1234',
|
||||
})
|
||||
client = new GraphQLClient(host, {
|
||||
headers,
|
||||
})
|
||||
authenticatedUser = user
|
||||
})
|
||||
|
||||
describe('given another user', () => {
|
||||
let authorClient
|
||||
let authorParams
|
||||
let authorHeaders
|
||||
|
||||
let author
|
||||
beforeEach(async () => {
|
||||
authorParams = {
|
||||
author = await instance.create('User', {
|
||||
email: 'author@example.org',
|
||||
password: '1234',
|
||||
id: 'author',
|
||||
}
|
||||
await factory.create('User', authorParams)
|
||||
authorHeaders = await login(authorParams)
|
||||
})
|
||||
})
|
||||
|
||||
describe('who mentions me in a post', () => {
|
||||
let post
|
||||
const title = 'Mentioning Al Capone'
|
||||
const content =
|
||||
'Hey <a class="mention" data-mention-id="you" href="/profile/you/al-capone">@al-capone</a> how do you do?'
|
||||
|
||||
beforeEach(async () => {
|
||||
const createPostMutation = gql`
|
||||
mutation($title: String!, $content: String!) {
|
||||
CreatePost(title: $title, content: $content) {
|
||||
mutation($id: ID, $title: String!, $content: String!) {
|
||||
CreatePost(id: $id, title: $title, content: $content) {
|
||||
id
|
||||
title
|
||||
content
|
||||
}
|
||||
}
|
||||
`
|
||||
authorClient = new GraphQLClient(host, {
|
||||
headers: authorHeaders,
|
||||
authenticatedUser = await author.toJson()
|
||||
await mutate({
|
||||
mutation: createPostMutation,
|
||||
variables: { id: 'p47', title, content },
|
||||
})
|
||||
const { CreatePost } = await authorClient.request(createPostMutation, {
|
||||
title,
|
||||
content,
|
||||
})
|
||||
post = CreatePost
|
||||
authenticatedUser = await user.toJson()
|
||||
})
|
||||
|
||||
it('sends you a notification', async () => {
|
||||
const expectedContent =
|
||||
'Hey <a class="mention" data-mention-id="you" href="/profile/you/al-capone" target="_blank">@al-capone</a> how do you do?'
|
||||
const expected = {
|
||||
currentUser: {
|
||||
notifications: [
|
||||
{
|
||||
read: false,
|
||||
post: {
|
||||
content: expectedContent,
|
||||
},
|
||||
},
|
||||
],
|
||||
const expected = expect.objectContaining({
|
||||
data: {
|
||||
currentUser: { notifications: [{ read: false, post: { content: expectedContent } }] },
|
||||
},
|
||||
}
|
||||
})
|
||||
const { query } = createTestClient(server)
|
||||
await expect(
|
||||
client.request(query, {
|
||||
read: false,
|
||||
}),
|
||||
query({ query: notificationQuery, variables: { read: false } }),
|
||||
).resolves.toEqual(expected)
|
||||
})
|
||||
|
||||
@ -132,41 +133,33 @@ describe('currentUser { notifications }', () => {
|
||||
}
|
||||
}
|
||||
`
|
||||
authorClient = new GraphQLClient(host, {
|
||||
headers: authorHeaders,
|
||||
})
|
||||
await authorClient.request(updatePostMutation, {
|
||||
id: post.id,
|
||||
title: post.title,
|
||||
content: updatedContent,
|
||||
authenticatedUser = await author.toJson()
|
||||
await mutate({
|
||||
mutation: updatePostMutation,
|
||||
variables: {
|
||||
id: 'p47',
|
||||
title,
|
||||
content: updatedContent,
|
||||
},
|
||||
})
|
||||
authenticatedUser = await user.toJson()
|
||||
})
|
||||
|
||||
it('creates exactly one more notification', async () => {
|
||||
const expectedContent =
|
||||
'<br>One more mention to<br><a data-mention-id="you" class="mention" href="/profile/you" target="_blank"><br>@al-capone<br></a><br>and again:<br><a data-mention-id="you" class="mention" href="/profile/you" target="_blank"><br>@al-capone<br></a><br>and again<br><a data-mention-id="you" class="mention" href="/profile/you" target="_blank"><br>@al-capone<br></a><br>'
|
||||
const expected = {
|
||||
currentUser: {
|
||||
notifications: [
|
||||
{
|
||||
read: false,
|
||||
post: {
|
||||
content: expectedContent,
|
||||
},
|
||||
},
|
||||
{
|
||||
read: false,
|
||||
post: {
|
||||
content: expectedContent,
|
||||
},
|
||||
},
|
||||
],
|
||||
const expected = expect.objectContaining({
|
||||
data: {
|
||||
currentUser: {
|
||||
notifications: [
|
||||
{ read: false, post: { content: expectedContent } },
|
||||
{ read: false, post: { content: expectedContent } },
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
await expect(
|
||||
client.request(query, {
|
||||
read: false,
|
||||
}),
|
||||
query({ query: notificationQuery, variables: { read: false } }),
|
||||
).resolves.toEqual(expected)
|
||||
})
|
||||
})
|
||||
@ -204,46 +197,40 @@ describe('Hashtags', () => {
|
||||
`
|
||||
|
||||
describe('authenticated', () => {
|
||||
let headers
|
||||
beforeEach(async () => {
|
||||
headers = await login({
|
||||
email: 'test@example.org',
|
||||
password: '1234',
|
||||
})
|
||||
client = new GraphQLClient(host, {
|
||||
headers,
|
||||
})
|
||||
authenticatedUser = await user.toJson()
|
||||
})
|
||||
|
||||
describe('create a Post with Hashtags', () => {
|
||||
beforeEach(async () => {
|
||||
await client.request(createPostMutation, {
|
||||
postId,
|
||||
postTitle,
|
||||
postContent,
|
||||
await mutate({
|
||||
mutation: createPostMutation,
|
||||
variables: {
|
||||
postId,
|
||||
postTitle,
|
||||
postContent,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('both Hashtags are created with the "id" set to thier "name"', async () => {
|
||||
it('both Hashtags are created with the "id" set to their "name"', async () => {
|
||||
const expected = [
|
||||
{
|
||||
id: 'Democracy',
|
||||
name: 'Democracy',
|
||||
},
|
||||
{
|
||||
id: 'Liberty',
|
||||
name: 'Liberty',
|
||||
},
|
||||
{ id: 'Democracy', name: 'Democracy' },
|
||||
{ id: 'Liberty', name: 'Liberty' },
|
||||
]
|
||||
await expect(
|
||||
client.request(postWithHastagsQuery, postWithHastagsVariables),
|
||||
).resolves.toEqual({
|
||||
Post: [
|
||||
{
|
||||
tags: expect.arrayContaining(expected),
|
||||
query({ query: postWithHastagsQuery, variables: postWithHastagsVariables }),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
data: {
|
||||
Post: [
|
||||
{
|
||||
tags: expect.arrayContaining(expected),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
describe('afterwards update the Post by removing a Hashtag, leaving a Hashtag and add a Hashtag', () => {
|
||||
@ -261,31 +248,28 @@ describe('Hashtags', () => {
|
||||
`
|
||||
|
||||
it('only one previous Hashtag and the new Hashtag exists', async () => {
|
||||
await client.request(updatePostMutation, {
|
||||
postId,
|
||||
postTitle,
|
||||
updatedPostContent,
|
||||
await mutate({
|
||||
mutation: updatePostMutation,
|
||||
variables: {
|
||||
postId,
|
||||
postTitle,
|
||||
updatedPostContent,
|
||||
},
|
||||
})
|
||||
|
||||
const expected = [
|
||||
{
|
||||
id: 'Elections',
|
||||
name: 'Elections',
|
||||
},
|
||||
{
|
||||
id: 'Liberty',
|
||||
name: 'Liberty',
|
||||
},
|
||||
{ id: 'Elections', name: 'Elections' },
|
||||
{ id: 'Liberty', name: 'Liberty' },
|
||||
]
|
||||
await expect(
|
||||
client.request(postWithHastagsQuery, postWithHastagsVariables),
|
||||
).resolves.toEqual({
|
||||
Post: [
|
||||
{
|
||||
tags: expect.arrayContaining(expected),
|
||||
query({ query: postWithHastagsQuery, variables: postWithHastagsVariables }),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
data: {
|
||||
Post: [{ tags: expect.arrayContaining(expected) }],
|
||||
},
|
||||
],
|
||||
})
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -3,11 +3,13 @@ import cheerio from 'cheerio'
|
||||
export default function(content) {
|
||||
if (!content) return []
|
||||
const $ = cheerio.load(content)
|
||||
let userIds = $('a.mention[data-mention-id]')
|
||||
const userIds = $('a.mention[data-mention-id]')
|
||||
.map((_, el) => {
|
||||
return $(el).attr('data-mention-id')
|
||||
})
|
||||
.get()
|
||||
userIds = userIds.map(id => id.trim()).filter(id => !!id)
|
||||
return userIds
|
||||
.map(id => id.trim())
|
||||
.filter(id => !!id)
|
||||
.filter((id, index, allIds) => allIds.indexOf(id) === index)
|
||||
}
|
||||
|
||||
@ -6,6 +6,8 @@ const contentEmptyMentions =
|
||||
'<p>Something inspirational about <a href="/profile/u2" data-mention-id="" target="_blank">@bob-der-baumeister</a> and <a href="/profile/u3/jenny-rostock" class="mention" data-mention-id target="_blank">@jenny-rostock</a>.</p>'
|
||||
const contentWithPlainLinks =
|
||||
'<p>Something inspirational about <a class="mention" href="/profile/u2" target="_blank">@bob-der-baumeister</a> and <a href="/profile/u3" target="_blank">@jenny-rostock</a>.</p>'
|
||||
const contentWithDuplicateIds =
|
||||
'One more mention to <a data-mention-id="you" class="mention" href="/profile/you"> @al-capone </a> and again: <a data-mention-id="you" class="mention" href="/profile/you"> @al-capone </a> and again <a data-mention-id="you" class="mention" href="/profile/you"> @al-capone </a>'
|
||||
|
||||
describe('extractMentionedUsers', () => {
|
||||
describe('content undefined', () => {
|
||||
@ -18,6 +20,10 @@ describe('extractMentionedUsers', () => {
|
||||
expect(extractMentionedUsers(contentWithPlainLinks)).toEqual([])
|
||||
})
|
||||
|
||||
it('removes duplicates', () => {
|
||||
expect(extractMentionedUsers(contentWithDuplicateIds)).toEqual(['you'])
|
||||
})
|
||||
|
||||
describe('given a link with .mention class and `data-mention-id` attribute ', () => {
|
||||
it('extracts ids', () => {
|
||||
expect(extractMentionedUsers(contentWithMentions)).toEqual(['u3'])
|
||||
|
||||
19
backend/src/models/Notification.js
Normal file
19
backend/src/models/Notification.js
Normal file
@ -0,0 +1,19 @@
|
||||
import uuid from 'uuid/v4'
|
||||
|
||||
module.exports = {
|
||||
id: { type: 'uuid', primary: true, default: uuid },
|
||||
createdAt: { type: 'string', isoDate: true, default: () => new Date().toISOString() },
|
||||
read: { type: 'boolean', default: false },
|
||||
user: {
|
||||
type: 'relationship',
|
||||
relationship: 'NOTIFIED',
|
||||
target: 'User',
|
||||
direction: 'out',
|
||||
},
|
||||
post: {
|
||||
type: 'relationship',
|
||||
relationship: 'NOTIFIED',
|
||||
target: 'Post',
|
||||
direction: 'in',
|
||||
},
|
||||
}
|
||||
@ -77,4 +77,10 @@ module.exports = {
|
||||
target: 'User',
|
||||
direction: 'out',
|
||||
},
|
||||
notifications: {
|
||||
type: 'relationship',
|
||||
relationship: 'NOTIFIED',
|
||||
target: 'Notification',
|
||||
direction: 'in',
|
||||
},
|
||||
}
|
||||
|
||||
@ -7,4 +7,5 @@ export default {
|
||||
EmailAddress: require('./EmailAddress.js'),
|
||||
SocialMedia: require('./SocialMedia.js'),
|
||||
Post: require('./Post.js'),
|
||||
Notification: require('./Notification.js'),
|
||||
}
|
||||
|
||||
@ -50,14 +50,6 @@ type Statistics {
|
||||
countShouts: Int!
|
||||
}
|
||||
|
||||
type Notification {
|
||||
id: ID!
|
||||
read: Boolean
|
||||
user: User @relation(name: "NOTIFIED", direction: "OUT")
|
||||
post: Post @relation(name: "NOTIFIED", direction: "IN")
|
||||
createdAt: String
|
||||
}
|
||||
|
||||
type Location {
|
||||
id: ID!
|
||||
name: String!
|
||||
|
||||
7
backend/src/schema/types/type/Notification.gql
Normal file
7
backend/src/schema/types/type/Notification.gql
Normal file
@ -0,0 +1,7 @@
|
||||
type Notification {
|
||||
id: ID!
|
||||
read: Boolean
|
||||
user: User @relation(name: "NOTIFIED", direction: "OUT")
|
||||
post: Post @relation(name: "NOTIFIED", direction: "IN")
|
||||
createdAt: String
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user