diff --git a/src/newsreader/js/pages/homepage/actions/posts.js b/src/newsreader/js/pages/homepage/actions/posts.js index 6f6f250..23bd3f6 100644 --- a/src/newsreader/js/pages/homepage/actions/posts.js +++ b/src/newsreader/js/pages/homepage/actions/posts.js @@ -13,29 +13,30 @@ export const MARKING_POST = 'MARKING_POST'; export const MARK_POST_SAVED = 'MARK_POST_SAVED'; export const MARK_POST_UNSAVED = 'MARK_POST_UNSAVED'; + export const TOGGLING_POST = 'TOGGLING_POST'; +export const TOGGLED_POST = 'TOGGLED_POST'; export const requestPosts = () => ({ type: REQUEST_POSTS }); - +export const receivePost = post => ({ type: RECEIVE_POST, post }); export const receivePosts = (posts, next) => ({ type: RECEIVE_POSTS, posts, next, }); -export const receivePost = post => ({ type: RECEIVE_POST, post }); - export const selectPost = post => ({ type: SELECT_POST, post }); - export const unSelectPost = () => ({ type: UNSELECT_POST }); +export const markingPostRead = () => ({ type: MARKING_POST }); export const postRead = (post, section) => ({ type: MARK_POST_READ, post, section, }); -export const markingPostRead = () => ({ type: MARKING_POST }); +export const togglingPost = () => ({ type: TOGGLING_POST }); +export const postToggled = post => ({ type: TOGGLED_POST, post }); export const markPostRead = (post, token) => { return (dispatch, getState) => { @@ -68,10 +69,9 @@ export const markPostRead = (post, token) => { }; }; -// TODO add missing methods (postSaved & postUnsaved) export const toggleSaved = (post, token) => { return (dispatch, getState) => { - dispatch(togglingPostSaved()); + dispatch(togglingPost()); const url = `/api/posts/${post.id}/`; const options = { @@ -87,12 +87,7 @@ export const toggleSaved = (post, token) => { .then(response => response.json()) .then(updatedPost => { dispatch(receivePost({ ...updatedPost })); - - if (updatedPost.saved) { - dispatch(postSaved({ ...updatedPost })); - } else { - dispatch(postUnsaved({ ...updatedPost })); - } + dispatch(postToggled({ ...updatedPost })); }) .catch(error => { dispatch(receivePost({})); diff --git a/src/newsreader/js/pages/homepage/reducers/posts.js b/src/newsreader/js/pages/homepage/reducers/posts.js index 771d4f2..3f9067c 100644 --- a/src/newsreader/js/pages/homepage/reducers/posts.js +++ b/src/newsreader/js/pages/homepage/reducers/posts.js @@ -11,6 +11,7 @@ import { RECEIVE_POSTS, REQUEST_POSTS, TOGGLING_POST, + TOGGLED_POST, } from '../actions/posts.js'; import { SELECT_CATEGORY } from '../actions/categories.js'; import { SELECT_RULE } from '../actions/rules.js'; @@ -72,9 +73,7 @@ export const posts = (state = { ...defaultState }, action) => { return { ...state, isUpdating: true }; case MARK_POST_READ: return { ...state, isUpdating: false }; - case MARK_POST_SAVED: - return { ...state, isUpdating: false }; - case MARK_POST_UNSAVED: + case TOGGLED_POST: return { ...state, isUpdating: false }; default: return state; diff --git a/src/newsreader/js/tests/homepage/actions/post.test.js b/src/newsreader/js/tests/homepage/actions/post.test.js index ce2ffdc..9d78aab 100644 --- a/src/newsreader/js/tests/homepage/actions/post.test.js +++ b/src/newsreader/js/tests/homepage/actions/post.test.js @@ -39,6 +39,7 @@ describe('post actions', () => { url: 'https://arstechnica.com/?p=1648607', rule: 5, read: false, + saved: false, }; const expectedAction = { @@ -62,6 +63,7 @@ describe('post actions', () => { url: 'https://arstechnica.com/?p=1648607', rule: 5, read: false, + saved: false, }; const expectedAction = { @@ -91,6 +93,7 @@ describe('post actions', () => { url: 'https://arstechnica.com/?p=1648607', rule: 5, read: false, + saved: false, }; const rule = { @@ -111,6 +114,29 @@ describe('post actions', () => { expect(actions.postRead(post, rule)).toEqual(expectedAction); }); + it('should create an action toggling post saved', () => { + const post = { + id: 2067, + remoteIdentifier: 'https://arstechnica.com/?p=1648607', + title: + 'This amazing glitch puts Star Fox 64 ships in an unmodified Zelda cartridge', + body: + '"Stale-reference manipulation," 300-character file names, and a clash between worlds.', + author: 'Kyle Orland', + publicationDate: '2020-01-24T19:50:12Z', + url: 'https://arstechnica.com/?p=1648607', + rule: 5, + read: false, + saved: false, + }; + + const expectedAction = { + type: actions.TOGGLING_POST, + }; + + expect(actions.togglingPost(post)).toEqual(expectedAction); + }); + it('should create multiple actions to mark post read', () => { const post = { id: 2067, @@ -124,6 +150,7 @@ describe('post actions', () => { url: 'https://arstechnica.com/?p=1648607', rule: 5, read: false, + saved: false, }; const rule = { @@ -143,7 +170,7 @@ describe('post actions', () => { const store = mockStore({ categories: { items: {}, isFetching: false }, rules: { items: {}, isFetching: false }, - posts: { items: {}, isFetching: false }, + posts: { items: {}, isFetching: false, isUpdating: false }, selected: { item: rule, next: false, @@ -170,6 +197,65 @@ describe('post actions', () => { }); }); + it('should create multiple actions to toggle a post saved', () => { + const post = { + id: 2067, + remoteIdentifier: 'https://arstechnica.com/?p=1648607', + title: + 'This amazing glitch puts Star Fox 64 ships in an unmodified Zelda cartridge', + body: + '"Stale-reference manipulation," 300-character file names, and a clash between worlds.', + author: 'Kyle Orland', + publicationDate: '2020-01-24T19:50:12Z', + url: 'https://arstechnica.com/?p=1648607', + rule: 5, + read: false, + saved: false, + }; + + const rule = { + id: 1, + name: 'Test rule', + unread: 100, + category: 1, + url: 'http://feeds.arstechnica.com/arstechnica/index?fmt=xml', + favicon: 'https://cdn.arstechnica.net/favicon.ico', + }; + + fetchMock.patchOnce('/api/posts/2067/', { + body: { ...post, saved: true }, + headers: { 'content-type': 'application/json' }, + }); + + const store = mockStore({ + categories: { items: {}, isFetching: false }, + rules: { items: {}, isFetching: false }, + posts: { items: {}, isFetching: false, isUpdating: false }, + selected: { + item: rule, + next: false, + lastReached: false, + post: {}, + }, + }); + + const expectedActions = [ + { type: actions.TOGGLING_POST }, + { + type: actions.RECEIVE_POST, + post: { ...post, saved: true }, + }, + { + type: actions.TOGGLED_POST, + post: { ...post, saved: true }, + }, + ]; + + return store.dispatch(actions.toggleSaved(post, 'TOKEN')).then(() => { + expect(store.getActions()).toEqual(expectedActions); + }); + }); + it('should create multiple actions to fetch posts by rule', () => { const posts = [ { @@ -184,6 +270,7 @@ describe('post actions', () => { url: 'https://arstechnica.com/?p=1648607', rule: 4, read: false, + saved: false, }, { id: 2141, @@ -196,6 +283,7 @@ describe('post actions', () => { url: 'https://arstechnica.com/?p=1648757', rule: 4, read: false, + saved: false, }, ]; @@ -222,7 +310,7 @@ describe('post actions', () => { const store = mockStore({ categories: { items: {}, isFetching: false }, rules: { items: {}, isFetching: false }, - posts: { items: {}, isFetching: false }, + posts: { items: {}, isFetching: false, isUpdating: false }, selected: { item: {}, next: false, lastReached: false, post: {} }, }); @@ -254,6 +342,7 @@ describe('post actions', () => { url: 'https://arstechnica.com/?p=1648607', rule: 4, read: false, + saved: false, }, { id: 2141, @@ -266,6 +355,7 @@ describe('post actions', () => { url: 'https://arstechnica.com/?p=1648757', rule: 4, read: false, + saved: false, }, ]; @@ -289,7 +379,7 @@ describe('post actions', () => { const store = mockStore({ categories: { items: {}, isFetching: false }, rules: { items: {}, isFetching: false }, - posts: { items: {}, isFetching: false }, + posts: { items: {}, isFetching: false, isUpdating: false }, selected: { item: {}, next: false, lastReached: false, post: {} }, }); @@ -320,7 +410,7 @@ describe('post actions', () => { const store = mockStore({ categories: { items: {}, isFetching: false }, rules: { items: {}, isFetching: false }, - posts: { items: {}, isFetching: false }, + posts: { items: {}, isFetching: false, isUpdating: false }, selected: { item: {}, next: false, lastReached: false, post: {} }, }); @@ -344,6 +434,7 @@ describe('post actions', () => { url: 'https://arstechnica.com/?p=1648607', rule: 5, read: false, + saved: false, }; const rule = { @@ -364,7 +455,7 @@ describe('post actions', () => { const store = mockStore({ categories: { items: {}, isFetching: false }, rules: { items: {}, isFetching: false }, - posts: { items: {}, isFetching: false }, + posts: { items: {}, isFetching: false, isUpdating: false }, selected: { item: { ...rule }, next: false, lastReached: false, post: {} }, }); @@ -379,6 +470,55 @@ describe('post actions', () => { }); }); + it('should handle exceptions when toggling a post saved/unsaved', () => { + const post = { + id: 2067, + remoteIdentifier: 'https://arstechnica.com/?p=1648607', + title: + 'This amazing glitch puts Star Fox 64 ships in an unmodified Zelda cartridge', + body: + '"Stale-reference manipulation," 300-character file names, and a clash between worlds.', + author: 'Kyle Orland', + publicationDate: '2020-01-24T19:50:12Z', + url: 'https://arstechnica.com/?p=1648607', + rule: 5, + read: false, + saved: false, + }; + + const rule = { + id: 4, + name: 'Ars Technica', + unread: 100, + category: 1, + url: 'http://feeds.arstechnica.com/arstechnica/index?fmt=xml', + favicon: 'https://cdn.arstechnica.net/favicon.ico', + }; + + const errorMessage = 'Permission denied'; + + fetchMock.patch(`/api/posts/${post.id}/`, () => { + throw new Error(errorMessage); + }); + + const store = mockStore({ + categories: { items: {}, isFetching: false }, + rules: { items: {}, isFetching: false }, + posts: { items: {}, isFetching: false, isUpdating: false }, + selected: { item: { ...rule }, next: false, lastReached: false, post: {} }, + }); + + const expectedActions = [ + { type: actions.TOGGLING_POST }, + { type: actions.RECEIVE_POST, post: {} }, + { type: errorActions.RECEIVE_API_ERROR, error: Error(errorMessage) }, + ]; + + return store.dispatch(actions.toggleSaved(post, 'FAKE_TOKEN')).then(() => { + expect(store.getActions()).toEqual(expectedActions); + }); + }); + it('should handle exceptions when fetching posts by section', () => { const rule = { id: 4, @@ -399,7 +539,7 @@ describe('post actions', () => { const store = mockStore({ categories: { items: {}, isFetching: false }, rules: { items: {}, isFetching: false }, - posts: { items: {}, isFetching: false }, + posts: { items: {}, isFetching: false, isUpdating: false }, selected: { item: { ...rule }, next: false, lastReached: false, post: {} }, }); diff --git a/src/newsreader/js/tests/homepage/reducers/post.test.js b/src/newsreader/js/tests/homepage/reducers/post.test.js index 6fe728f..adb8983 100644 --- a/src/newsreader/js/tests/homepage/reducers/post.test.js +++ b/src/newsreader/js/tests/homepage/reducers/post.test.js @@ -12,7 +12,7 @@ describe('post actions', () => { it('should return state after requesting posts', () => { const action = { type: actions.REQUEST_POSTS }; - const expectedState = { ...defaultState, isFetching: true, isMarking: false }; + const expectedState = { ...defaultState, isFetching: true, isUpdating: false }; expect(reducer(undefined, action)).toEqual(expectedState); }); @@ -30,6 +30,7 @@ describe('post actions', () => { url: 'https://arstechnica.com/?p=1648607', rule: 4, read: false, + saved: false, }; const action = { @@ -40,7 +41,7 @@ describe('post actions', () => { const expectedState = { ...defaultState, isFetching: false, - isMarking: false, + isUpdating: false, items: { [post.id]: post }, }; @@ -61,6 +62,7 @@ describe('post actions', () => { url: 'https://arstechnica.com/?p=1648607', rule: 4, read: false, + saved: false, }, { id: 2141, @@ -73,6 +75,7 @@ describe('post actions', () => { url: 'https://arstechnica.com/?p=1648757', rule: 4, read: false, + saved: false, }, ]; @@ -86,7 +89,7 @@ describe('post actions', () => { const expectedState = { ...defaultState, isFetching: false, - isMarking: false, + isUpdating: false, items: expectedPosts, }; @@ -131,6 +134,7 @@ describe('post actions', () => { url: 'https://www.bbc.co.uk/news/world-asia-china-51299195', rule: 4, read: false, + saved: false, }, 4638: { id: 4638, @@ -143,6 +147,7 @@ describe('post actions', () => { url: 'https://www.bbc.co.uk/news/world-europe-51294305', rule: 4, read: false, + saved: false, }, }; @@ -189,6 +194,7 @@ describe('post actions', () => { url: 'https://arstechnica.com/?p=1648607', rule: 5, read: false, + saved: false, }, 2141: { id: 2141, @@ -201,6 +207,7 @@ describe('post actions', () => { url: 'https://arstechnica.com/?p=1648757', rule: 5, read: false, + saved: false, }, 4637: { id: 4637, @@ -213,6 +220,7 @@ describe('post actions', () => { url: 'https://www.bbc.co.uk/news/world-asia-china-51299195', rule: 4, read: false, + saved: false, }, 4638: { id: 4638, @@ -225,6 +233,7 @@ describe('post actions', () => { url: 'https://www.bbc.co.uk/news/world-europe-51294305', rule: 4, read: false, + saved: false, }, 4589: { id: 4589, @@ -238,6 +247,7 @@ describe('post actions', () => { 'https://tweakers.net/nieuws/162878/analyse-nintendo-verdiende-miljard-dollar-aan-mobiele-games.html', rule: 7, read: false, + saved: false, }, 4594: { id: 4594, @@ -251,6 +261,7 @@ describe('post actions', () => { 'https://tweakers.net/nieuws/162870/samsung-kondigt-eerste-tablet-met-5g-aan.html', rule: 7, read: false, + saved: false, }, };