Implement the filed report notification on the notification page

- Get all the old tests running.
- Implement new necessary tests.
- Lint fixing.
This commit is contained in:
Wolfgang Huß 2020-01-20 18:00:12 +01:00
parent 48403471d5
commit 4ca18522f1
10 changed files with 305 additions and 169 deletions

View File

@ -362,7 +362,7 @@ When("I log in with the following credentials:", table => {
When("open the notification menu and click on the first item", () => {
cy.get(".notifications-menu").invoke('show').click(); // "invoke('show')" because of the delay for show the menu
cy.get(".notification-mention-post")
cy.get(".notification-link-for-test")
.first()
.click({
force: true

View File

@ -235,21 +235,21 @@ describe('Notification', () => {
expect(wrapper.text()).toContain('Mrs. Badwomen')
})
it('renders the identifier "notifications.filedReport.category"', () => {
wrapper = Wrapper()
expect(wrapper.text()).toContain('notifications.filedReport.category')
})
it('renders the identifier "notifications.filedReport.description"', () => {
wrapper = Wrapper()
expect(wrapper.text()).toContain('notifications.filedReport.description')
})
it('renders the users slug', () => {
it('renders the reported users slug', () => {
wrapper = Wrapper()
expect(wrapper.text()).toContain('@mrs.-badwomen')
})
it('renders the identifier "notifications.report.category"', () => {
wrapper = Wrapper()
expect(wrapper.text()).toContain('notifications.report.category')
})
it('renders the identifier "notifications.report.description"', () => {
wrapper = Wrapper()
expect(wrapper.text()).toContain('notifications.report.description')
})
it('has no class "read"', () => {
wrapper = Wrapper()
expect(wrapper.classes()).not.toContain('read')

View File

@ -1,43 +1,52 @@
<template>
<ds-space :class="{ read: notification.read, notification: true }" margin-bottom="x-small">
<ds-space :class="{ read: notificationData.read, notification: true }" margin-bottom="x-small">
<client-only>
<ds-space margin-bottom="x-small">
<hc-user :user="sourceData.triggerer" :date-time="sourceData.createdAt" :trunc="35" />
<hc-user
:user="notificationData.triggerer"
:date-time="notificationData.createdAt"
:trunc="35"
/>
</ds-space>
<ds-text class="reason-text-for-test" color="soft">
{{ $t(`notifications.reason.${notification.reason}` + sourceData.reasonExtention) }}
{{
$t(`notifications.reason.${notificationData.reason}` + notificationData.reasonExtention)
}}
</ds-text>
</client-only>
<ds-space margin-bottom="x-small" />
<nuxt-link class="notification-mention-post" :to="linkTo" @click.native="$emit('read')">
<nuxt-link
class="notification-link-for-test"
:to="notificationData.linkTo"
@click.native="$emit('read')"
>
<ds-space margin-bottom="x-small">
<ds-card :header="sourceData.title" hover space="x-small" class="notifications-card">
<ds-card :header="notificationData.title" hover space="x-small" class="notifications-card">
<ds-space margin-bottom="x-small" />
<div v-if="sourceData.user">
<hc-user :user="sourceData.user" :trunc="35" />
<div v-if="notificationData.user">
<hc-user :user="notificationData.user" :trunc="35" />
</div>
<div v-else-if="sourceData.contentExcerpt">
<span v-if="sourceData.comment" class="text-notification-header">
<div v-else-if="notificationData.contentExcerpt">
<span v-if="notificationData.comment" class="text-notification-header">
{{ $t(`notifications.comment`) }}:
</span>
{{ sourceData.contentExcerpt | removeHtml }}
{{ notificationData.contentExcerpt | removeHtml }}
</div>
<div v-if="sourceData.report">
<div v-if="notificationData.report">
<ds-space margin-bottom="x-small" />
<span class="text-notification-header">
{{ $t(`notifications.filedReport.category`) }}:
</span>
{{ this.$t('report.reason.category.options.' + sourceData.report.reasonCategory) }}
<span class="text-notification-header">{{ $t(`notifications.report.category`) }}:</span>
{{ $t('report.reason.category.options.' + notificationData.report.reasonCategory) }}
<br />
<span class="text-notification-header">
{{ $t(`notifications.filedReport.description`) }}:
{{ $t(`notifications.report.description`) }}:
</span>
<span
v-if="
sourceData.report.reasonDescription && sourceData.report.reasonDescription !== ''
notificationData.report.reasonDescription &&
notificationData.report.reasonDescription !== ''
"
>
{{ sourceData.report.reasonDescription }}
{{ notificationData.report.reasonDescription }}
</span>
<span v-else>
@ -51,6 +60,7 @@
<script>
import { mapGetters } from 'vuex'
import { extractNotificationDataOfCurrentUser } from '~/components/utils/Notifications'
import HcUser from '~/components/User/User'
export default {
@ -68,93 +78,8 @@ export default {
...mapGetters({
currentUser: 'auth/user',
}),
sourceData() {
const from = this.notification.from
let triggerer
const createdAt = this.notification.createdAt
let title
let author
let user = null
let post = null
let comment = null
let contentExcerpt = null
let report = null
let reasonExtention = ''
if (from.__typename === 'Post') {
post = from
triggerer = post.author
} else if (from.__typename === 'Comment') {
comment = from
triggerer = comment.author
post = comment.post
} else if (from.__typename === 'Report') {
report = {
reasonCategory: from.filed[0].reasonCategory,
reasonDescription: from.filed[0].reasonDescription,
}
triggerer = this.currentUser
if (from.filed[0].reportedResource.__typename === 'User') {
user = from.filed[0].reportedResource
reasonExtention = '.user'
} else if (from.filed[0].reportedResource.__typename === 'Post') {
post = from.filed[0].reportedResource
reasonExtention = '.post'
} else if (from.filed[0].reportedResource.__typename === 'Comment') {
comment = from.filed[0].reportedResource
post = from.filed[0].reportedResource.post
reasonExtention = '.comment'
}
}
if (user) {
title = user.name
author = user
} else {
title = post.title
if (comment) {
author = comment.author
contentExcerpt = comment.contentExcerpt
} else {
author = post.author
contentExcerpt = post.contentExcerpt
}
}
const data = {
triggerer,
createdAt,
title,
user,
post,
comment,
contentExcerpt,
author,
report,
reasonExtention,
}
return data
},
linkTo() {
const params = this.sourceData.user
? {
id: this.sourceData.user.id,
slug: this.sourceData.user.slug,
}
: this.sourceData.post
? {
id: this.sourceData.post.id,
slug: this.sourceData.post.slug,
}
: {}
const hashParam = this.sourceData.comment
? { hash: `#commentId-${this.sourceData.comment.id}` }
: {}
return {
name: this.sourceData.user ? 'profile-id-slug' : 'post-id-slug',
params,
...hashParam,
}
notificationData() {
return extractNotificationDataOfCurrentUser(this.notification, this.currentUser)
},
},
}

View File

@ -52,7 +52,7 @@ describe('NotificationList.vue', () => {
})
it('renders Notification.vue for each notification of the user', () => {
expect(wrapper.findAll(Notification)).toHaveLength(2)
expect(wrapper.findAll(Notification)).toHaveLength(3)
})
})
@ -73,7 +73,7 @@ describe('NotificationList.vue', () => {
describe('click on a notification', () => {
beforeEach(() => {
wrapper.find('.notification-mention-post').trigger('click')
wrapper.find('.notification-link-for-test').trigger('click')
})
it("emits 'markAsRead' with the id of the notification source", () => {

View File

@ -14,6 +14,7 @@ describe('NotificationsTable.vue', () => {
let wrapper, mocks, propsData, stubs
const postNotification = notifications[0]
const commentNotification = notifications[1]
const reportNotification = notifications[2]
beforeEach(() => {
mocks = {
@ -31,7 +32,9 @@ describe('NotificationsTable.vue', () => {
getters: {
'auth/isModerator': () => false,
'auth/user': () => {
return {}
return {
name: 'myName',
}
},
},
})
@ -101,17 +104,14 @@ describe('NotificationsTable.vue', () => {
expect(reason.exists()).toBe(true)
})
it('renders a link to the Post', () => {
const postLink = firstRowNotification.find('a.notification-mention-post')
it('renders a link to the post', () => {
const postLink = firstRowNotification.find('a.notification-link-for-test')
expect(postLink.text()).toEqual(postNotification.from.title)
})
it("renders the Post's content", () => {
const boldTags = firstRowNotification.findAll('b')
const content = boldTags.filter(
element => element.text() === postNotification.from.contentExcerpt,
)
expect(content.exists()).toBe(true)
it("renders the post's content", () => {
wrapper = Wrapper()
expect(wrapper.text()).toContain(postNotification.from.contentExcerpt)
})
})
@ -134,17 +134,66 @@ describe('NotificationsTable.vue', () => {
expect(reason.exists()).toBe(true)
})
it('renders a link to the Post', () => {
const postLink = secondRowNotification.find('a.notification-mention-post')
it('renders a link to the post', () => {
const postLink = secondRowNotification.find('a.notification-link-for-test')
expect(postLink.text()).toEqual(commentNotification.from.post.title)
})
it("renders the Post's content", () => {
const boldTags = secondRowNotification.findAll('b')
const content = boldTags.filter(
element => element.text() === commentNotification.from.contentExcerpt,
it("renders the comment's content", () => {
wrapper = Wrapper()
expect(wrapper.text()).toContain(commentNotification.from.contentExcerpt)
})
})
describe('Report', () => {
let thirdRowNotification
beforeEach(() => {
thirdRowNotification = wrapper.findAll('tbody tr').at(2)
})
it('renders me as the triggerer', () => {
const triggererName = thirdRowNotification.find('.username')
expect(triggererName.text()).toEqual('myName')
})
it('renders the reason for the notification', () => {
const dsTexts = thirdRowNotification.findAll('.ds-text')
const reason = dsTexts.filter(
element => element.text() === 'notifications.reason.filed_report_on_resource.user',
)
expect(content.exists()).toBe(true)
expect(reason.exists()).toBe(true)
})
it('renders a link to the user', () => {
const userLink = thirdRowNotification.find('a.notification-link-for-test')
expect(userLink.text()).toEqual(reportNotification.from.filed[0].reportedResource.name)
})
it('renders the reported users slug', () => {
wrapper = Wrapper()
expect(wrapper.text()).toContain('@mrs.-badwomen')
})
it('renders the identifier "notifications.report.category"', () => {
wrapper = Wrapper()
expect(wrapper.text()).toContain('notifications.report.category')
})
it('renders the reported category', () => {
wrapper = Wrapper()
expect(wrapper.text()).toContain(
'report.reason.category.options.' + reportNotification.from.filed[0].reasonCategory,
)
})
it('renders the identifier "notifications.report.description"', () => {
wrapper = Wrapper()
expect(wrapper.text()).toContain('notifications.report.description')
})
it('renders the reported description', () => {
wrapper = Wrapper()
expect(wrapper.text()).toContain(reportNotification.from.filed[0].reasonDescription)
})
})
@ -154,7 +203,7 @@ describe('NotificationsTable.vue', () => {
})
it('clicking on a Post link emits `markNotificationAsRead`', () => {
wrapper.find('a.notification-mention-post').trigger('click')
wrapper.find('a.notification-link-for-test').trigger('click')
expect(wrapper.emitted().markNotificationAsRead[0][0]).toEqual(postNotification.from.id)
})

View File

@ -1,55 +1,86 @@
<template>
<ds-table v-if="notifications && notifications.length" :data="notifications" :fields="fields">
<ds-table
v-if="notificationsData && notificationsData.length"
:data="notificationsData"
:fields="fields"
>
<template #icon="scope">
<base-icon
v-if="scope.row.from.post"
v-if="scope.row.report"
name="balance-scale"
v-tooltip="{ content: $t('notifications.report.name'), placement: 'right' }"
:class="{ 'notification-status': scope.row.read }"
/>
<base-icon
v-else-if="scope.row.comment"
name="comment"
v-tooltip="{ content: $t('notifications.comment'), placement: 'right' }"
:class="{ 'notification-status': scope.row.read }"
/>
<base-icon
v-else
name="bookmark"
v-tooltip="{ content: $t('notifications.post'), placement: 'right' }"
:class="{ 'notification-status': scope.row.read }"
/>
</template>
<template #user="scope">
<ds-space margin-bottom="base">
<client-only>
<hc-user
:user="scope.row.from.author"
:date-time="scope.row.from.createdAt"
:user="scope.row.triggerer"
:date-time="scope.row.createdAt"
:trunc="35"
:class="{ 'notification-status': scope.row.read }"
/>
</client-only>
</ds-space>
<ds-text :class="{ 'notification-status': scope.row.read, reason: true }">
{{ $t(`notifications.reason.${scope.row.reason}`) }}
{{ $t(`notifications.reason.${scope.row.reason}` + scope.row.reasonExtention) }}
</ds-text>
</template>
<template #post="scope">
<nuxt-link
class="notification-mention-post"
class="notification-link-for-test"
:class="{ 'notification-status': scope.row.read }"
:to="{
name: 'post-id-slug',
params: params(scope.row.from),
hash: hashParam(scope.row.from),
}"
@click.native="markNotificationAsRead(scope.row.from.id)"
:to="scope.row.linkTo"
@click.native="markNotificationAsRead(scope.row.id)"
>
<b>{{ scope.row.from.title || scope.row.from.post.title | truncate(50) }}</b>
<b>{{ scope.row.title | truncate(50) }}</b>
</nuxt-link>
</template>
<template #content="scope">
<b :class="{ 'notification-status': scope.row.read }">
{{ scope.row.from.contentExcerpt | removeHtml }}
</b>
<div v-if="scope.row.user" :class="{ 'notification-status': scope.row.read }">
<hc-user :user="scope.row.user" :trunc="35" />
</div>
<div v-else-if="scope.row.contentExcerpt" :class="{ 'notification-status': scope.row.read }">
<span v-if="scope.row.comment" class="text-notification-header">
{{ $t(`notifications.comment`) }}:
</span>
{{ scope.row.contentExcerpt | removeHtml }}
</div>
<div v-if="scope.row.report" :class="{ 'notification-status': scope.row.read }">
<ds-space margin-bottom="x-small" />
<span class="text-notification-header">{{ $t(`notifications.report.category`) }}:</span>
{{ $t('report.reason.category.options.' + scope.row.report.reasonCategory) }}
<br />
<span class="text-notification-header">{{ $t(`notifications.report.description`) }}:</span>
<span
v-if="scope.row.report.reasonDescription && scope.row.report.reasonDescription !== ''"
>
{{ scope.row.report.reasonDescription }}
</span>
<span v-else>
</span>
</div>
</template>
</ds-table>
<hc-empty v-else icon="alert" :message="$t('notifications.empty')" />
</template>
<script>
import { mapGetters } from 'vuex'
import { extractNotificationDataOfCurrentUser } from '~/components/utils/Notifications'
import HcUser from '~/components/User/User'
import HcEmpty from '~/components/Empty/Empty'
@ -62,26 +93,36 @@ export default {
notifications: { type: Array, default: () => [] },
},
computed: {
...mapGetters({
currentUser: 'auth/user',
}),
fields() {
return {
icon: {
label: ' ',
width: '5',
width: '3%',
},
user: {
label: this.$t('notifications.user'),
width: '45%',
label: ' ',
width: '25%',
},
post: {
label: this.$t('notifications.post'),
label: ' ',
width: '25%',
},
content: {
label: this.$t('notifications.content'),
width: '25%',
label: ' ',
width: '47%',
},
}
},
notificationsData() {
const data = []
this.notifications.forEach(notification => {
data.push(extractNotificationDataOfCurrentUser(notification, this.currentUser))
})
return data
},
},
methods: {
isComment(notificationSource) {
@ -107,4 +148,8 @@ export default {
.notification-status {
opacity: $opacity-soft;
}
.text-notification-header {
font-weight: 700;
margin-right: 0.1rem;
}
</style>

View File

@ -40,4 +40,117 @@ export const notifications = [
},
},
},
{
read: false,
reason: 'filed_report_on_resource',
from: {
__typename: 'Report',
id: 'reportOnUser',
filed: [
{
reasonCategory: 'discrimination_etc',
reasonDescription: 'This user is harassing me with bigoted remarks!',
reportedResource: {
__typename: 'User',
id: 'badWomen',
slug: 'mrs.-badwomen',
name: 'Mrs. Badwomen',
},
},
],
},
},
]
export const extractNotificationDataOfCurrentUser = (notification, currentUser) => {
const from = notification.from
let triggerer
const id = from.id
const createdAt = notification.createdAt
const read = notification.read
const reason = notification.reason
let title
let author
let user = null
let post = null
let comment = null
let contentExcerpt = null
let report = null
let reasonExtention = ''
if (from.__typename === 'Post') {
post = from
triggerer = post.author
} else if (from.__typename === 'Comment') {
comment = from
triggerer = comment.author
post = comment.post
} else if (from.__typename === 'Report') {
report = {
reasonCategory: from.filed[0].reasonCategory,
reasonDescription: from.filed[0].reasonDescription,
}
triggerer = currentUser
if (from.filed[0].reportedResource.__typename === 'User') {
user = from.filed[0].reportedResource
reasonExtention = '.user'
} else if (from.filed[0].reportedResource.__typename === 'Post') {
post = from.filed[0].reportedResource
reasonExtention = '.post'
} else if (from.filed[0].reportedResource.__typename === 'Comment') {
comment = from.filed[0].reportedResource
post = from.filed[0].reportedResource.post
reasonExtention = '.comment'
}
}
if (user) {
title = user.name
author = user
} else {
title = post.title
if (comment) {
author = comment.author
contentExcerpt = comment.contentExcerpt
} else {
author = post.author
contentExcerpt = post.contentExcerpt
}
}
const params = user
? {
id: user.id,
slug: user.slug,
}
: post
? {
id: post.id,
slug: post.slug,
}
: {}
const hashParam = comment ? { hash: `#commentId-${comment.id}` } : {}
const linkTo = {
name: user ? 'profile-id-slug' : 'post-id-slug',
params,
...hashParam,
}
const data = {
triggerer,
id,
createdAt,
read,
reason,
title,
user,
post,
comment,
contentExcerpt,
author,
report,
reasonExtention,
linkTo,
}
return data
}

View File

@ -60,6 +60,7 @@ export const minimisedUserQuery = () => {
export const notificationQuery = i18n => {
return gql`
${userFragment}
${userCountsFragment}
${commentFragment}
${postFragment}
@ -98,6 +99,7 @@ export const notificationQuery = i18n => {
__typename
... on User {
...user
...userCounts
}
... on Post {
...post

View File

@ -669,17 +669,18 @@
"pageLink": "Alle Benachrichtigungen",
"post": "Beitrag",
"user": "Benutzer",
"report": {
"name": "Meldung",
"category": "Kategorie der Meldung",
"description": "Beschreibung"
},
"content": "Inhalt",
"filterLabel": {
"all": "Alle",
"read": "Gelesen",
"unread": "Ungelesen"
},
"empty": "Bedaure, Du hast momentan keinerlei Benachrichtigungen.",
"filedReport": {
"category": "Kategorie der Meldung",
"description": "Beschreibung"
}
"empty": "Bedaure, du hast momentan keinerlei Benachrichtigungen."
},
"delete": {
"submit": "Löschen",

View File

@ -180,7 +180,7 @@
"reason": {
"commented_on_post": "Commented on your post …",
"filed_report_on_resource": {
"user": "You reported an user …",
"user": "You reported a user …",
"post": "You reported a post …",
"comment": "You reported a comment …"
},
@ -192,17 +192,18 @@
"pageLink": "All notifications",
"post": "Post",
"user": "User",
"report": {
"name": "Report",
"category": "Report category",
"description": "Description"
},
"content": "Content",
"filterLabel": {
"all": "All",
"read": "Read",
"unread": "Unread"
},
"empty": "Sorry, you don't have any notifications at the moment.",
"filedReport": {
"category": "Report category",
"description": "Description"
}
"empty": "Sorry, you don't have any notifications at the moment."
},
"search": {
"placeholder": "Search",