diff --git a/backend/src/schema/resolvers/posts.spec.js b/backend/src/schema/resolvers/posts.spec.js index 7c5e88d69..af2d8089c 100644 --- a/backend/src/schema/resolvers/posts.spec.js +++ b/backend/src/schema/resolvers/posts.spec.js @@ -91,7 +91,7 @@ afterEach(async () => { }) describe('Post', () => { - const postQuery = gql` + const postQueryFilteredByCategories = gql` query Post($filter: _PostFilter) { Post(filter: $filter) { id @@ -102,13 +102,28 @@ describe('Post', () => { } ` + const postQueryFilteredByEmotions = gql` + query Post($filter: _PostFilter) { + Post(filter: $filter) { + id + emotions { + emotion + } + } + } + ` + describe('can be filtered', () => { - it('by categories', async () => { - await Promise.all([ + let post31, post32 + beforeEach(async () => { + ;[post31, post32] = await Promise.all([ factory.create('Post', { id: 'p31', categoryIds: ['cat4'] }), factory.create('Post', { id: 'p32', categoryIds: ['cat15'] }), factory.create('Post', { id: 'p33', categoryIds: ['cat9'] }), ]) + }) + + it('by categories', async () => { const expected = { data: { Post: [ @@ -120,7 +135,50 @@ describe('Post', () => { }, } variables = { ...variables, filter: { categories_some: { id_in: ['cat9'] } } } - await expect(query({ query: postQuery, variables })).resolves.toMatchObject(expected) + await expect( + query({ query: postQueryFilteredByCategories, variables }), + ).resolves.toMatchObject(expected) + }) + + it('by emotions', async () => { + const expected = { + data: { + Post: [ + { + id: 'p31', + emotions: [{ emotion: 'happy' }], + }, + ], + }, + } + await user.relateTo(post31, 'emoted', { emotion: 'happy' }) + variables = { ...variables, filter: { emotions_some: { emotion_in: ['happy'] } } } + await expect(query({ query: postQueryFilteredByEmotions, variables })).resolves.toMatchObject( + expected, + ) + }) + + it('supports filtering by multiple emotions', async () => { + const expected = [ + { + id: 'p31', + emotions: [{ emotion: 'happy' }], + }, + { + id: 'p32', + emotions: [{ emotion: 'cry' }], + }, + ] + await user.relateTo(post31, 'emoted', { emotion: 'happy' }) + await user.relateTo(post32, 'emoted', { emotion: 'cry' }) + variables = { ...variables, filter: { emotions_some: { emotion_in: ['happy', 'cry'] } } } + await expect(query({ query: postQueryFilteredByEmotions, variables })).resolves.toMatchObject( + { + data: { + Post: expect.arrayContaining(expected), + }, + }, + ) }) }) }) diff --git a/backend/src/schema/types/type/EMOTED.gql b/backend/src/schema/types/type/EMOTED.gql index cb8d37d62..ee1576517 100644 --- a/backend/src/schema/types/type/EMOTED.gql +++ b/backend/src/schema/types/type/EMOTED.gql @@ -3,8 +3,8 @@ type EMOTED @relation(name: "EMOTED") { to: Post emotion: Emotion - #createdAt: DateTime - #updatedAt: DateTime + # createdAt: DateTime + # updatedAt: DateTime createdAt: String updatedAt: String } @@ -15,6 +15,12 @@ input _EMOTEDInput { updatedAt: String } +input _PostEMOTEDFilter { + emotion_in: [Emotion] + createdAt: String + updatedAt: String +} + type Mutation { AddPostEmotions(to: _PostInput!, data: _EMOTEDInput!): EMOTED RemovePostEmotions(to: _PostInput!, data: _EMOTEDInput!): EMOTED diff --git a/backend/src/seed/factories/posts.js b/backend/src/seed/factories/posts.js index e81251c53..3058204a1 100644 --- a/backend/src/seed/factories/posts.js +++ b/backend/src/seed/factories/posts.js @@ -30,7 +30,7 @@ export default function create() { let { categories, categoryIds } = args delete args.categories delete args.categoryIds - if (categories && categoryIds) throw new Error('You provided both category and categoryIds') + if (categories && categoryIds) throw new Error('You provided both categories and categoryIds') if (categoryIds) categories = await Promise.all(categoryIds.map(id => neodeInstance.find('Category', id))) categories = categories || (await Promise.all([factoryInstance.create('Category')])) diff --git a/webapp/components/FilterPosts/FilterPostsMenuItems.vue b/webapp/components/FilterPosts/CategoriesFilterMenuItems.vue similarity index 62% rename from webapp/components/FilterPosts/FilterPostsMenuItems.vue rename to webapp/components/FilterPosts/CategoriesFilterMenuItems.vue index c761070b7..7a35bf3cc 100644 --- a/webapp/components/FilterPosts/FilterPostsMenuItems.vue +++ b/webapp/components/FilterPosts/CategoriesFilterMenuItems.vue @@ -1,6 +1,5 @@ diff --git a/webapp/components/FilterPosts/FilterPosts.spec.js b/webapp/components/FilterPosts/FilterPosts.spec.js index 509ff32b7..0cbd3e962 100644 --- a/webapp/components/FilterPosts/FilterPosts.spec.js +++ b/webapp/components/FilterPosts/FilterPosts.spec.js @@ -19,6 +19,7 @@ describe('FilterPosts.vue', () => { let allCategoriesButton let environmentAndNatureButton let democracyAndPoliticsButton + let happyEmotionButton beforeEach(() => { mocks = { @@ -52,6 +53,7 @@ describe('FilterPosts.vue', () => { 'postsFilter/TOGGLE_FILTER_BY_FOLLOWED': jest.fn(), 'postsFilter/RESET_CATEGORIES': jest.fn(), 'postsFilter/TOGGLE_CATEGORY': jest.fn(), + 'postsFilter/TOGGLE_EMOTION': jest.fn(), } getters = { 'postsFilter/isActive': () => false, @@ -61,6 +63,7 @@ describe('FilterPosts.vue', () => { }, 'postsFilter/filteredCategoryIds': jest.fn(() => []), 'postsFilter/filteredByUsersFollowed': jest.fn(), + 'postsFilter/filteredByEmotions': jest.fn(() => []), } const openFilterPosts = () => { const store = new Vuex.Store({ mutations, getters }) @@ -120,5 +123,22 @@ describe('FilterPosts.vue', () => { expect(mutations['postsFilter/TOGGLE_FILTER_BY_FOLLOWED']).toHaveBeenCalledWith({}, 'u34') }) }) + + describe('click on an "emotions-buttons" button', () => { + it('calls TOGGLE_EMOTION when clicked', () => { + const wrapper = openFilterPosts() + happyEmotionButton = wrapper.findAll('button.emotions-buttons').at(1) + happyEmotionButton.trigger('click') + expect(mutations['postsFilter/TOGGLE_EMOTION']).toHaveBeenCalledWith({}, 'happy') + }) + + it('sets the attribute `src` to colorized image', () => { + getters['postsFilter/filteredByEmotions'] = jest.fn(() => ['happy']) + const wrapper = openFilterPosts() + happyEmotionButton = wrapper.findAll('button.emotions-buttons').at(1) + const happyEmotionButtonImage = happyEmotionButton.find('img') + expect(happyEmotionButtonImage.attributes().src).toEqual('/img/svg/emoji/happy_color.svg') + }) + }) }) }) diff --git a/webapp/components/FilterPosts/FilterPosts.vue b/webapp/components/FilterPosts/FilterPosts.vue index defc0eb0a..8a12ac633 100644 --- a/webapp/components/FilterPosts/FilterPosts.vue +++ b/webapp/components/FilterPosts/FilterPosts.vue @@ -11,20 +11,25 @@ + diff --git a/webapp/pages/index.vue b/webapp/pages/index.vue index 9b5c20977..361a7091c 100644 --- a/webapp/pages/index.vue +++ b/webapp/pages/index.vue @@ -27,12 +27,8 @@ diff --git a/webapp/store/postsFilter.js b/webapp/store/postsFilter.js index 487194bae..964b265c0 100644 --- a/webapp/store/postsFilter.js +++ b/webapp/store/postsFilter.js @@ -40,6 +40,12 @@ export const mutations = { if (isEmpty(get(filter, 'categories_some.id_in'))) delete filter.categories_some state.filter = filter }, + TOGGLE_EMOTION(state, emotion) { + const filter = clone(state.filter) + update(filter, 'emotions_some.emotion_in', emotions => xor(emotions, [emotion])) + if (isEmpty(get(filter, 'emotions_some.emotion_in'))) delete filter.emotions_some + state.filter = filter + }, } export const getters = { @@ -55,4 +61,7 @@ export const getters = { filteredByUsersFollowed(state) { return !!get(state.filter, 'author.followedBy_some.id') }, + filteredByEmotions(state) { + return get(state.filter, 'emotions_some.emotion_in') || [] + }, } diff --git a/webapp/store/postsFilter.spec.js b/webapp/store/postsFilter.spec.js index f06b4a31d..68a3dbd6e 100644 --- a/webapp/store/postsFilter.spec.js +++ b/webapp/store/postsFilter.spec.js @@ -43,6 +43,30 @@ describe('getters', () => { expect(getters.filteredByUsersFollowed(state)).toBe(false) }) }) + + describe('filteredByEmotions', () => { + it('returns an emotions array if filter is set', () => { + state = { filter: { emotions_some: { emotion_in: ['sad'] } } } + expect(getters.filteredByEmotions(state)).toEqual(['sad']) + }) + + it('returns an emotions array even when other filters are set', () => { + state = { + filter: { emotions_some: { emotion_in: ['sad'] }, categories_some: { id_in: [23] } }, + } + expect(getters.filteredByEmotions(state)).toEqual(['sad']) + }) + + it('returns empty array if filter is not set', () => { + state = { filter: {} } + expect(getters.filteredByEmotions(state)).toEqual([]) + }) + + it('returns empty array if another filter is set, but not emotions', () => { + state = { filter: { categories_some: { id_in: [23] } } } + expect(getters.filteredByEmotions(state)).toEqual([]) + }) + }) }) describe('mutations', () => {