0.2.3 #99
15 changed files with 161 additions and 161 deletions
|
|
@ -28,49 +28,6 @@ export const receiveCategories = categories => ({
|
|||
export const requestCategory = () => ({ type: REQUEST_CATEGORY });
|
||||
export const requestCategories = () => ({ type: REQUEST_CATEGORIES });
|
||||
|
||||
export const fetchCategories = () => {
|
||||
return dispatch => {
|
||||
dispatch(requestCategories());
|
||||
|
||||
return fetch('/api/categories/')
|
||||
.then(response => response.json())
|
||||
.then(json => {
|
||||
const categories = {};
|
||||
|
||||
json.forEach(category => {
|
||||
categories[category.id] = { ...category };
|
||||
});
|
||||
|
||||
dispatch(receiveCategories(categories));
|
||||
return json;
|
||||
})
|
||||
.then(json => {
|
||||
const promises = json.map(category => {
|
||||
return fetch(`/api/categories/${category.id}/rules/`);
|
||||
});
|
||||
|
||||
dispatch(requestRules());
|
||||
return Promise.all(promises);
|
||||
})
|
||||
.then(responses => {
|
||||
return Promise.all(responses.map(response => response.json()));
|
||||
})
|
||||
.then(responseData => {
|
||||
let rules = {};
|
||||
|
||||
responseData.forEach(json => {
|
||||
const data = Object.values(json);
|
||||
|
||||
data.forEach(item => {
|
||||
rules = { ...rules, [item.id]: item };
|
||||
});
|
||||
});
|
||||
|
||||
dispatch(receiveRules(rules));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export const fetchCategory = category => {
|
||||
return (dispatch, getState) => {
|
||||
const { selected } = getState();
|
||||
|
|
@ -93,3 +50,28 @@ export const fetchCategory = category => {
|
|||
});
|
||||
};
|
||||
};
|
||||
|
||||
export const fetchCategories = () => {
|
||||
return dispatch => {
|
||||
dispatch(requestCategories());
|
||||
|
||||
return fetch('/api/categories/')
|
||||
.then(response => response.json())
|
||||
.then(categories => {
|
||||
dispatch(receiveCategories(categories));
|
||||
|
||||
return categories;
|
||||
})
|
||||
.then(categories => {
|
||||
dispatch(requestRules());
|
||||
|
||||
const promises = categories.map(category => {
|
||||
return fetch(`/api/categories/${category.id}/rules/`);
|
||||
});
|
||||
|
||||
return Promise.all(promises);
|
||||
})
|
||||
.then(responses => Promise.all(responses.map(response => response.json())))
|
||||
.then(nestedRules => dispatch(receiveRules(nestedRules.flat())));
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -75,15 +75,7 @@ export const fetchPostsBySection = (section, page = false) => {
|
|||
|
||||
return fetch(url)
|
||||
.then(response => response.json())
|
||||
.then(json => {
|
||||
const posts = {};
|
||||
|
||||
json.results.forEach(post => {
|
||||
posts[post.id] = post;
|
||||
});
|
||||
|
||||
dispatch(receivePosts(posts, json.next));
|
||||
})
|
||||
.then(posts => dispatch(receivePosts(posts.results, posts.next)))
|
||||
.catch(error => {
|
||||
if (error instanceof TypeError) {
|
||||
console.log(`Unable to parse posts from request: ${error}`);
|
||||
|
|
|
|||
|
|
@ -61,14 +61,6 @@ export const fetchRulesByCategory = category => {
|
|||
|
||||
return fetch(`/api/categories/${category.id}/rules/`)
|
||||
.then(response => response.json())
|
||||
.then(responseData => {
|
||||
const rules = {};
|
||||
|
||||
responseData.forEach(rule => {
|
||||
rules[rule.id] = { ...rule };
|
||||
});
|
||||
|
||||
dispatch(receiveRules(rules));
|
||||
});
|
||||
.then(rules => dispatch(receiveRules(rules)));
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ import { isEqual } from 'lodash';
|
|||
|
||||
import { CATEGORY_TYPE, RULE_TYPE } from '../constants.js';
|
||||
|
||||
import { objectsFromArray } from '../../../utils.js';
|
||||
|
||||
import {
|
||||
RECEIVE_CATEGORY,
|
||||
RECEIVE_CATEGORIES,
|
||||
|
|
@ -26,13 +28,7 @@ export const categories = (state = { ...defaultState }, action) => {
|
|||
isFetching: false,
|
||||
};
|
||||
case RECEIVE_CATEGORIES:
|
||||
const receivedCategories = {};
|
||||
|
||||
Object.values({ ...action.categories }).forEach(category => {
|
||||
receivedCategories[category.id] = {
|
||||
...category,
|
||||
};
|
||||
});
|
||||
const receivedCategories = objectsFromArray(action.categories, 'id');
|
||||
|
||||
return {
|
||||
...state,
|
||||
|
|
@ -41,10 +37,7 @@ export const categories = (state = { ...defaultState }, action) => {
|
|||
};
|
||||
case REQUEST_CATEGORIES:
|
||||
case REQUEST_CATEGORY:
|
||||
return {
|
||||
...state,
|
||||
isFetching: true,
|
||||
};
|
||||
return { ...state, isFetching: true };
|
||||
case MARK_POST_READ:
|
||||
let category = {};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import { isEqual } from 'lodash';
|
||||
|
||||
import { objectsFromArray } from '../../../utils.js';
|
||||
import { CATEGORY_TYPE, RULE_TYPE } from '../constants.js';
|
||||
|
||||
import {
|
||||
|
|
@ -15,12 +17,6 @@ const defaultState = { items: {}, isFetching: false };
|
|||
|
||||
export const posts = (state = { ...defaultState }, action) => {
|
||||
switch (action.type) {
|
||||
case RECEIVE_POSTS:
|
||||
return {
|
||||
...state,
|
||||
isFetching: false,
|
||||
items: { ...state.items, ...action.posts },
|
||||
};
|
||||
case REQUEST_POSTS:
|
||||
return { ...state, isFetching: true };
|
||||
case RECEIVE_POST:
|
||||
|
|
@ -28,6 +24,14 @@ export const posts = (state = { ...defaultState }, action) => {
|
|||
...state,
|
||||
items: { ...state.items, [action.post.id]: { ...action.post } },
|
||||
};
|
||||
case RECEIVE_POSTS:
|
||||
const receivedItems = objectsFromArray(action.posts, 'id');
|
||||
|
||||
return {
|
||||
...state,
|
||||
isFetching: false,
|
||||
items: { ...state.items, ...receivedItems },
|
||||
};
|
||||
case MARK_SECTION_READ:
|
||||
const updatedPosts = {};
|
||||
let relatedPosts = [];
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import { isEqual } from 'lodash';
|
||||
|
||||
import { objectsFromArray } from '../../../utils.js';
|
||||
|
||||
import { CATEGORY_TYPE, RULE_TYPE } from '../constants.js';
|
||||
|
||||
import {
|
||||
|
|
@ -17,14 +19,13 @@ export const rules = (state = { ...defaultState }, action) => {
|
|||
switch (action.type) {
|
||||
case REQUEST_RULE:
|
||||
case REQUEST_RULES:
|
||||
return {
|
||||
...state,
|
||||
isFetching: true,
|
||||
};
|
||||
return { ...state, isFetching: true };
|
||||
case RECEIVE_RULES:
|
||||
const receivedItems = objectsFromArray(action.rules, 'id');
|
||||
|
||||
return {
|
||||
...state,
|
||||
items: { ...state.items, ...action.rules },
|
||||
items: { ...state.items, ...receivedItems },
|
||||
isFetching: false,
|
||||
};
|
||||
case RECEIVE_RULE:
|
||||
|
|
|
|||
|
|
@ -67,15 +67,9 @@ export const selected = (state = { ...defaultState }, action) => {
|
|||
...state,
|
||||
};
|
||||
case SELECT_POST:
|
||||
return {
|
||||
...state,
|
||||
post: action.post,
|
||||
};
|
||||
return { ...state, post: action.post };
|
||||
case UNSELECT_POST:
|
||||
return {
|
||||
...state,
|
||||
post: {},
|
||||
};
|
||||
return { ...state, post: {} };
|
||||
case MARK_POST_READ:
|
||||
return {
|
||||
...state,
|
||||
|
|
|
|||
|
|
@ -96,22 +96,13 @@ describe('category actions', () => {
|
|||
});
|
||||
|
||||
it('should create multiple actions when fetching categories', () => {
|
||||
const categories = {
|
||||
1: { id: 1, name: 'Tech', unread: 29 },
|
||||
2: { id: 2, name: 'World news', unread: 956 },
|
||||
};
|
||||
const categories = [
|
||||
{ id: 1, name: 'Tech', unread: 29 },
|
||||
{ id: 2, name: 'World news', unread: 956 },
|
||||
];
|
||||
|
||||
const rules = {
|
||||
4: {
|
||||
id: 4,
|
||||
name: 'BBC',
|
||||
url: 'http://feeds.bbci.co.uk/news/world/rss.xml',
|
||||
favicon:
|
||||
'https://m.files.bbci.co.uk/modules/bbc-morph-news-waf-page-meta/2.5.2/apple-touch-icon-57x57-precomposed.png',
|
||||
category: 2,
|
||||
unread: 345,
|
||||
},
|
||||
5: {
|
||||
const rules = [
|
||||
{
|
||||
id: 5,
|
||||
name: 'Ars Technica',
|
||||
url: 'http://feeds.arstechnica.com/arstechnica/index?fmt=xml',
|
||||
|
|
@ -119,19 +110,28 @@ describe('category actions', () => {
|
|||
category: 1,
|
||||
unread: 7,
|
||||
},
|
||||
};
|
||||
{
|
||||
id: 6,
|
||||
name: 'BBC',
|
||||
url: 'http://feeds.bbci.co.uk/news/world/rss.xml',
|
||||
favicon:
|
||||
'https://m.files.bbci.co.uk/modules/bbc-morph-news-waf-page-meta/2.5.2/apple-touch-icon-57x57-precomposed.png',
|
||||
category: 2,
|
||||
unread: 345,
|
||||
},
|
||||
];
|
||||
|
||||
fetchMock
|
||||
.get('/api/categories/', {
|
||||
body: Object.values({ ...categories }),
|
||||
body: categories,
|
||||
headers: { 'content-type': 'application/json' },
|
||||
})
|
||||
.get('/api/categories/1/rules/', {
|
||||
body: [{ ...rules[5] }],
|
||||
body: [{ ...rules[0] }],
|
||||
headers: { 'content-type': 'application/json' },
|
||||
})
|
||||
.get('/api/categories/2/rules/', {
|
||||
body: [{ ...rules[4] }],
|
||||
body: [{ ...rules[1] }],
|
||||
headers: { 'content-type': 'application/json' },
|
||||
});
|
||||
|
||||
|
|
@ -161,8 +161,8 @@ describe('category actions', () => {
|
|||
unread: 0,
|
||||
};
|
||||
|
||||
const rules = {
|
||||
1: {
|
||||
const rules = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Ars Technica',
|
||||
url: 'http://feeds.arstechnica.com/arstechnica/index?fmt=xml',
|
||||
|
|
@ -170,7 +170,7 @@ describe('category actions', () => {
|
|||
category: 1,
|
||||
unread: 200,
|
||||
},
|
||||
2: {
|
||||
{
|
||||
id: 2,
|
||||
name: 'Hacker News',
|
||||
url: 'https://news.ycombinator.com/rss',
|
||||
|
|
@ -178,7 +178,7 @@ describe('category actions', () => {
|
|||
category: 1,
|
||||
unread: 350,
|
||||
},
|
||||
};
|
||||
];
|
||||
|
||||
fetchMock
|
||||
.get('/api/categories/1', {
|
||||
|
|
@ -186,7 +186,7 @@ describe('category actions', () => {
|
|||
headers: { 'content-type': 'application/json' },
|
||||
})
|
||||
.get('/api/categories/1/rules/', {
|
||||
body: Object.values({ ...rules }),
|
||||
body: rules,
|
||||
headers: { 'content-type': 'application/json' },
|
||||
});
|
||||
|
||||
|
|
@ -197,7 +197,7 @@ describe('category actions', () => {
|
|||
category: { ...category, unread: 500 },
|
||||
},
|
||||
{ type: ruleActions.REQUEST_RULES },
|
||||
{ type: ruleActions.RECEIVE_RULES, rules: { ...rules } },
|
||||
{ type: ruleActions.RECEIVE_RULES, rules },
|
||||
];
|
||||
|
||||
const store = mockStore({
|
||||
|
|
|
|||
|
|
@ -163,8 +163,8 @@ describe('rule actions', () => {
|
|||
});
|
||||
|
||||
it('should create multiple actions to fetch posts by rule', () => {
|
||||
const posts = {
|
||||
2067: {
|
||||
const posts = [
|
||||
{
|
||||
id: 2067,
|
||||
remoteIdentifier: 'https://arstechnica.com/?p=1648607',
|
||||
title:
|
||||
|
|
@ -177,7 +177,7 @@ describe('rule actions', () => {
|
|||
rule: 4,
|
||||
read: false,
|
||||
},
|
||||
2141: {
|
||||
{
|
||||
id: 2141,
|
||||
remoteIdentifier: 'https://arstechnica.com/?p=1648757',
|
||||
title: 'The most complete brain map ever is here: A fly’s “connectome”',
|
||||
|
|
@ -189,7 +189,7 @@ describe('rule actions', () => {
|
|||
rule: 4,
|
||||
read: false,
|
||||
},
|
||||
};
|
||||
];
|
||||
|
||||
const rule = {
|
||||
id: 4,
|
||||
|
|
@ -206,7 +206,7 @@ describe('rule actions', () => {
|
|||
count: 2,
|
||||
next: 'https://durp.com/api/rules/4/posts/?page=2&read=false',
|
||||
previous: null,
|
||||
results: Object.values({ ...posts }),
|
||||
results: posts,
|
||||
},
|
||||
headers: { 'content-type': 'application/json' },
|
||||
});
|
||||
|
|
@ -233,8 +233,8 @@ describe('rule actions', () => {
|
|||
});
|
||||
|
||||
it('should create multiple actions to fetch posts by category', () => {
|
||||
const posts = {
|
||||
2067: {
|
||||
const posts = [
|
||||
{
|
||||
id: 2067,
|
||||
remoteIdentifier: 'https://arstechnica.com/?p=1648607',
|
||||
title:
|
||||
|
|
@ -247,7 +247,7 @@ describe('rule actions', () => {
|
|||
rule: 4,
|
||||
read: false,
|
||||
},
|
||||
2141: {
|
||||
{
|
||||
id: 2141,
|
||||
remoteIdentifier: 'https://arstechnica.com/?p=1648757',
|
||||
title: 'The most complete brain map ever is here: A fly’s “connectome”',
|
||||
|
|
@ -259,7 +259,7 @@ describe('rule actions', () => {
|
|||
rule: 4,
|
||||
read: false,
|
||||
},
|
||||
};
|
||||
];
|
||||
|
||||
const category = {
|
||||
id: 1,
|
||||
|
|
@ -273,7 +273,7 @@ describe('rule actions', () => {
|
|||
count: 2,
|
||||
next: 'https://durp.com/api/categories/4/posts/?page=2&read=false',
|
||||
previous: null,
|
||||
results: Object.values({ ...posts }),
|
||||
results: posts,
|
||||
},
|
||||
headers: { 'content-type': 'application/json' },
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ import configureMockStore from 'redux-mock-store';
|
|||
import thunk from 'redux-thunk';
|
||||
import fetchMock from 'fetch-mock';
|
||||
|
||||
import { objectsFromArray } from '../../../utils.js';
|
||||
|
||||
import * as actions from '../../../pages/homepage/actions/rules.js';
|
||||
import * as constants from '../../../pages/homepage/constants.js';
|
||||
import * as categoryActions from '../../../pages/homepage/actions/categories.js';
|
||||
|
|
@ -63,8 +65,8 @@ describe('rule actions', () => {
|
|||
});
|
||||
|
||||
it('should create an action to receive multiple rules', () => {
|
||||
const rules = {
|
||||
1: {
|
||||
const rules = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Test rule',
|
||||
unread: 100,
|
||||
|
|
@ -72,7 +74,7 @@ describe('rule actions', () => {
|
|||
url: 'http://feeds.arstechnica.com/arstechnica/index?fmt=xml',
|
||||
favicon: 'https://cdn.arstechnica.net/favicon.ico',
|
||||
},
|
||||
2: {
|
||||
{
|
||||
id: 2,
|
||||
name: 'Test rule 2',
|
||||
unread: 50,
|
||||
|
|
@ -80,7 +82,7 @@ describe('rule actions', () => {
|
|||
url: 'https://xkcd.com/atom.xml',
|
||||
favicon: null,
|
||||
},
|
||||
};
|
||||
];
|
||||
|
||||
const expectedAction = {
|
||||
type: actions.RECEIVE_RULES,
|
||||
|
|
@ -211,8 +213,8 @@ describe('rule actions', () => {
|
|||
unread: 0,
|
||||
};
|
||||
|
||||
const rules = {
|
||||
1: {
|
||||
const rules = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Ars Technica',
|
||||
url: 'http://feeds.arstechnica.com/arstechnica/index?fmt=xml',
|
||||
|
|
@ -220,7 +222,7 @@ describe('rule actions', () => {
|
|||
category: 1,
|
||||
unread: 200,
|
||||
},
|
||||
2: {
|
||||
{
|
||||
id: 2,
|
||||
name: 'Hacker News',
|
||||
url: 'https://news.ycombinator.com/rss',
|
||||
|
|
@ -228,10 +230,10 @@ describe('rule actions', () => {
|
|||
category: 1,
|
||||
unread: 350,
|
||||
},
|
||||
};
|
||||
];
|
||||
|
||||
fetchMock.getOnce('/api/categories/1/rules/', {
|
||||
body: Object.values({ ...rules }),
|
||||
body: rules,
|
||||
headers: { 'content-type': 'application/json' },
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import { categories as reducer } from '../../../pages/homepage/reducers/categories.js';
|
||||
|
||||
import { objectsFromArray } from '../../../utils.js';
|
||||
|
||||
import * as actions from '../../../pages/homepage/actions/categories.js';
|
||||
import * as postActions from '../../../pages/homepage/actions/posts.js';
|
||||
import * as selectedActions from '../../../pages/homepage/actions/selected.js';
|
||||
|
|
@ -25,19 +27,15 @@ describe('category reducer', () => {
|
|||
});
|
||||
|
||||
it('should return state after receiving multiple categories', () => {
|
||||
const receivedCategories = {
|
||||
0: { id: 9, name: 'Tech', unread: 291 },
|
||||
1: { id: 2, name: 'World news', unread: 444 },
|
||||
};
|
||||
const receivedCategories = [
|
||||
{ id: 9, name: 'Tech', unread: 291 },
|
||||
{ id: 2, name: 'World news', unread: 444 },
|
||||
];
|
||||
|
||||
const action = { type: actions.RECEIVE_CATEGORIES, categories: receivedCategories };
|
||||
|
||||
const items = {};
|
||||
|
||||
Object.values({ ...receivedCategories }).forEach(category => {
|
||||
items[category.id] = category;
|
||||
});
|
||||
|
||||
const expectedState = { ...defaultState, items };
|
||||
const expectedCategories = objectsFromArray(receivedCategories, 'id');
|
||||
const expectedState = { ...defaultState, items: expectedCategories };
|
||||
|
||||
expect(reducer(undefined, action)).toEqual(expectedState);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import { posts as reducer } from '../../../pages/homepage/reducers/posts.js';
|
||||
|
||||
import { objectsFromArray } from '../../../utils.js';
|
||||
|
||||
import * as actions from '../../../pages/homepage/actions/posts.js';
|
||||
import * as selectedActions from '../../../pages/homepage/actions/selected.js';
|
||||
import * as constants from '../../../pages/homepage/constants.js';
|
||||
|
|
@ -45,8 +47,8 @@ describe('post actions', () => {
|
|||
});
|
||||
|
||||
it('should return state after receiving posts', () => {
|
||||
const posts = {
|
||||
2067: {
|
||||
const posts = [
|
||||
{
|
||||
id: 2067,
|
||||
remoteIdentifier: 'https://arstechnica.com/?p=1648607',
|
||||
title:
|
||||
|
|
@ -59,7 +61,7 @@ describe('post actions', () => {
|
|||
rule: 4,
|
||||
read: false,
|
||||
},
|
||||
2141: {
|
||||
{
|
||||
id: 2141,
|
||||
remoteIdentifier: 'https://arstechnica.com/?p=1648757',
|
||||
title: 'The most complete brain map ever is here: A fly’s “connectome”',
|
||||
|
|
@ -71,7 +73,7 @@ describe('post actions', () => {
|
|||
rule: 4,
|
||||
read: false,
|
||||
},
|
||||
};
|
||||
];
|
||||
|
||||
const action = {
|
||||
type: actions.RECEIVE_POSTS,
|
||||
|
|
@ -79,10 +81,11 @@ describe('post actions', () => {
|
|||
posts,
|
||||
};
|
||||
|
||||
const expectedPosts = objectsFromArray(posts, 'id');
|
||||
const expectedState = {
|
||||
...defaultState,
|
||||
isFetching: false,
|
||||
items: posts,
|
||||
items: expectedPosts,
|
||||
};
|
||||
|
||||
expect(reducer(undefined, action)).toEqual(expectedState);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import { rules as reducer } from '../../../pages/homepage/reducers/rules.js';
|
||||
|
||||
import { objectsFromArray } from '../../../utils.js';
|
||||
|
||||
import * as actions from '../../../pages/homepage/actions/rules.js';
|
||||
import * as postActions from '../../../pages/homepage/actions/posts.js';
|
||||
import * as selectedActions from '../../../pages/homepage/actions/selected.js';
|
||||
|
|
@ -49,8 +51,8 @@ describe('category reducer', () => {
|
|||
});
|
||||
|
||||
it('should return state after receiving multiple rules', () => {
|
||||
const rules = {
|
||||
1: {
|
||||
const rules = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Test rule',
|
||||
unread: 100,
|
||||
|
|
@ -58,7 +60,7 @@ describe('category reducer', () => {
|
|||
url: 'http://feeds.arstechnica.com/arstechnica/index?fmt=xml',
|
||||
favicon: 'https://cdn.arstechnica.net/favicon.ico',
|
||||
},
|
||||
2: {
|
||||
{
|
||||
id: 2,
|
||||
name: 'Another Test rule',
|
||||
unread: 444,
|
||||
|
|
@ -66,11 +68,12 @@ describe('category reducer', () => {
|
|||
url: 'http://feeds.arstechnica.com/arstechnica/index?fmt=xml',
|
||||
favicon: 'https://cdn.arstechnica.net/favicon.ico',
|
||||
},
|
||||
};
|
||||
];
|
||||
|
||||
const action = { type: actions.RECEIVE_RULES, rules };
|
||||
|
||||
const expectedState = { ...defaultState, items: { ...rules } };
|
||||
const mappedRules = objectsFromArray(rules, 'id');
|
||||
const expectedState = { ...defaultState, items: { ...mappedRules } };
|
||||
|
||||
expect(reducer(undefined, action)).toEqual(expectedState);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -211,8 +211,8 @@ describe('selected reducer', () => {
|
|||
});
|
||||
|
||||
it('should return state after receiving posts', () => {
|
||||
const posts = {
|
||||
2067: {
|
||||
const posts = [
|
||||
{
|
||||
id: 2067,
|
||||
remoteIdentifier: 'https://arstechnica.com/?p=1648607',
|
||||
title:
|
||||
|
|
@ -225,7 +225,7 @@ describe('selected reducer', () => {
|
|||
rule: 4,
|
||||
read: false,
|
||||
},
|
||||
2141: {
|
||||
{
|
||||
id: 2141,
|
||||
remoteIdentifier: 'https://arstechnica.com/?p=1648757',
|
||||
title: 'The most complete brain map ever is here: A fly’s “connectome”',
|
||||
|
|
@ -237,7 +237,7 @@ describe('selected reducer', () => {
|
|||
rule: 4,
|
||||
read: false,
|
||||
},
|
||||
};
|
||||
];
|
||||
|
||||
const action = {
|
||||
type: postActions.RECEIVE_POSTS,
|
||||
|
|
@ -254,7 +254,7 @@ describe('selected reducer', () => {
|
|||
expect(reducer(undefined, action)).toEqual(expectedState);
|
||||
});
|
||||
|
||||
it('should return state after receiving a post', () => {
|
||||
it('should return state after receiving a post which is selected', () => {
|
||||
const post = {
|
||||
id: 2067,
|
||||
remoteIdentifier: 'https://arstechnica.com/?p=1648607',
|
||||
|
|
@ -280,6 +280,32 @@ describe('selected reducer', () => {
|
|||
expect(reducer(state, action)).toEqual(expectedState);
|
||||
});
|
||||
|
||||
it('should return state after receiving a post with none selected', () => {
|
||||
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: 4,
|
||||
read: false,
|
||||
};
|
||||
|
||||
const action = {
|
||||
type: postActions.RECEIVE_POST,
|
||||
post: { ...post, rule: 6 },
|
||||
};
|
||||
|
||||
const state = { ...defaultState, post: {} };
|
||||
const expectedState = { ...defaultState, post: {} };
|
||||
|
||||
expect(reducer(state, action)).toEqual(expectedState);
|
||||
});
|
||||
|
||||
it('should return state after selecting a post', () => {
|
||||
const post = {
|
||||
id: 2067,
|
||||
|
|
|
|||
|
|
@ -12,3 +12,13 @@ export const formatDatetime = dateString => {
|
|||
|
||||
return date.toLocaleDateString(locale, dateOptions);
|
||||
};
|
||||
|
||||
export const objectsFromArray = (array, key) => {
|
||||
const arrayEntries = array
|
||||
.filter(object => key in object)
|
||||
.map(object => {
|
||||
return [object[key], { ...object }];
|
||||
});
|
||||
|
||||
return Object.fromEntries(arrayEntries);
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue