feat: add newsItemDetails
This commit is contained in:
parent
644cbcd464
commit
1826b80d4b
|
@ -18,6 +18,7 @@ const createApi = () => ({
|
|||
getClassmates: jest.fn(),
|
||||
getMenu: jest.fn(),
|
||||
getNews: jest.fn(),
|
||||
getNewsDetails: jest.fn(),
|
||||
getNotifications: jest.fn(),
|
||||
getSchedule: jest.fn(),
|
||||
getUser: jest.fn(),
|
||||
|
|
|
@ -130,6 +130,15 @@ export const useNews = (child: Child) => hook<NewsItem[]>(
|
|||
(api) => () => api.getNews(child),
|
||||
)
|
||||
|
||||
export const useNewsDetails = (child: Child, news: NewsItem) => hook<NewsItem>(
|
||||
'NEWS_DETAILS',
|
||||
`news_details_${news.id}`,
|
||||
news,
|
||||
(s) => s.newsDetails,
|
||||
(api) => () => api.getNewsDetails(child, news),
|
||||
)
|
||||
|
||||
|
||||
export const useNotifications = (child: Child) => hook<Notification[]>(
|
||||
'NOTIFICATIONS',
|
||||
`notifications_${child.id}`,
|
||||
|
|
|
@ -70,5 +70,6 @@ export const calendar = createReducer<CalendarItem[]>('CALENDAR')
|
|||
export const classmates = createReducer<Classmate[]>('CLASSMATES')
|
||||
export const menu = createReducer<MenuItem[]>('MENU')
|
||||
export const news = createReducer<NewsItem[]>('NEWS')
|
||||
export const newsDetails = createReducer<NewsItem[]>('NEWS_DETAILS')
|
||||
export const notifications = createReducer<Notification[]>('NOTIFICATIONS')
|
||||
export const schedule = createReducer<ScheduleItem[]>('SCHEDULE')
|
||||
|
|
|
@ -6,6 +6,7 @@ import {
|
|||
classmates,
|
||||
menu,
|
||||
news,
|
||||
newsDetails,
|
||||
notifications,
|
||||
schedule,
|
||||
user,
|
||||
|
@ -17,6 +18,7 @@ const appReducer = combineReducers({
|
|||
classmates,
|
||||
menu,
|
||||
news,
|
||||
newsDetails,
|
||||
notifications,
|
||||
schedule,
|
||||
user,
|
||||
|
|
|
@ -55,6 +55,7 @@ export type EntityName = 'USER'
|
|||
| 'CLASSMATES'
|
||||
| 'MENU'
|
||||
| 'NEWS'
|
||||
| 'NEWS_DETAILS'
|
||||
| 'NOTIFICATIONS'
|
||||
| 'SCHEDULE'
|
||||
| 'ALL'
|
||||
|
@ -76,6 +77,7 @@ export interface EntityStoreRootState {
|
|||
classmates: EntityMap<Classmate[]>,
|
||||
menu: EntityMap<MenuItem[]>,
|
||||
news: EntityMap<NewsItem[]>,
|
||||
newsDetails: EntityMap<NewsItem>,
|
||||
notifications: EntityMap<Notification[]>,
|
||||
schedule: EntityMap<ScheduleItem[]>,
|
||||
}
|
||||
|
|
|
@ -0,0 +1,216 @@
|
|||
import React from 'react'
|
||||
import { renderHook, act } from '@testing-library/react-hooks'
|
||||
import { ApiProvider } from './provider'
|
||||
import { useNewsDetails } from './hooks'
|
||||
import store from './store'
|
||||
import init from './__mocks__/@skolplattformen/embedded-api'
|
||||
import createStorage from './__mocks__/AsyncStorage'
|
||||
import reporter from './__mocks__/reporter'
|
||||
|
||||
const pause = (ms = 0) => new Promise((r) => setTimeout(r, ms))
|
||||
|
||||
describe('useNewsDetails(child, newsItem)', () => {
|
||||
let api
|
||||
let storage
|
||||
let response
|
||||
let child
|
||||
let newsItem
|
||||
const wrapper = ({ children }) => (
|
||||
<ApiProvider
|
||||
api={api}
|
||||
storage={storage}
|
||||
reporter={reporter}
|
||||
>
|
||||
{children}
|
||||
</ApiProvider>
|
||||
)
|
||||
beforeEach(() => {
|
||||
response = { id: '1337', modified: 'now' }
|
||||
api = init()
|
||||
api.getNewsDetails.mockImplementation(() => (
|
||||
new Promise((res) => {
|
||||
setTimeout(() => res(response), 50)
|
||||
})
|
||||
))
|
||||
storage = createStorage({
|
||||
news_details_1337: { id: '1337', modified: 'yesterday' },
|
||||
}, 2)
|
||||
child = { id: 10 }
|
||||
newsItem = { id: '1337', modified: 'this morning' }
|
||||
})
|
||||
afterEach(async () => {
|
||||
await act(async () => {
|
||||
await pause(70)
|
||||
store.dispatch({ entity: 'ALL', type: 'CLEAR' })
|
||||
})
|
||||
})
|
||||
it('returns correct initial value', () => {
|
||||
const { result } = renderHook(() => useNewsDetails(child, newsItem), { wrapper })
|
||||
|
||||
expect(result.current.status).toEqual('pending')
|
||||
})
|
||||
it('calls api', async () => {
|
||||
await act(async () => {
|
||||
api.isLoggedIn = true
|
||||
const { waitForNextUpdate } = renderHook(() => useNewsDetails(child, newsItem), { wrapper })
|
||||
|
||||
await waitForNextUpdate()
|
||||
await waitForNextUpdate()
|
||||
|
||||
expect(api.getNewsDetails).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
it('only calls api once', async () => {
|
||||
await act(async () => {
|
||||
api.isLoggedIn = true
|
||||
renderHook(() => useNewsDetails(child, newsItem), { wrapper })
|
||||
const { waitForNextUpdate } = renderHook(() => useNewsDetails(child, newsItem), { wrapper })
|
||||
|
||||
await waitForNextUpdate()
|
||||
renderHook(() => useNewsDetails(child, newsItem), { wrapper })
|
||||
await waitForNextUpdate()
|
||||
renderHook(() => useNewsDetails(child, newsItem), { wrapper })
|
||||
await waitForNextUpdate()
|
||||
|
||||
const { result } = renderHook(() => useNewsDetails(child, newsItem), { wrapper })
|
||||
|
||||
expect(api.getNewsDetails).toHaveBeenCalledTimes(1)
|
||||
expect(result.current.status).toEqual('loaded')
|
||||
})
|
||||
})
|
||||
it('calls cache', async () => {
|
||||
await act(async () => {
|
||||
api.isLoggedIn = true
|
||||
const { result, waitForNextUpdate } = renderHook(() => useNewsDetails(child, newsItem), { wrapper })
|
||||
|
||||
await waitForNextUpdate()
|
||||
await waitForNextUpdate()
|
||||
|
||||
expect(result.current.data).toEqual({ id: '1337' })
|
||||
})
|
||||
})
|
||||
it('updates status to loading', async () => {
|
||||
await act(async () => {
|
||||
api.isLoggedIn = true
|
||||
const { result, waitForNextUpdate } = renderHook(() => useNewsDetails(child, newsItem), { wrapper })
|
||||
|
||||
await waitForNextUpdate()
|
||||
await waitForNextUpdate()
|
||||
|
||||
expect(result.current.status).toEqual('loading')
|
||||
})
|
||||
})
|
||||
it('updates status to loaded', async () => {
|
||||
await act(async () => {
|
||||
api.isLoggedIn = true
|
||||
const { result, waitForNextUpdate } = renderHook(() => useNewsDetails(child, newsItem), { wrapper })
|
||||
|
||||
await waitForNextUpdate()
|
||||
await waitForNextUpdate()
|
||||
await waitForNextUpdate()
|
||||
|
||||
expect(result.current.status).toEqual('loaded')
|
||||
})
|
||||
})
|
||||
it('stores in cache if not fake', async () => {
|
||||
await act(async () => {
|
||||
api.isLoggedIn = true
|
||||
api.isFake = false
|
||||
|
||||
const { waitForNextUpdate } = renderHook(() => useNewsDetails(child, newsItem), { wrapper })
|
||||
|
||||
await waitForNextUpdate()
|
||||
await waitForNextUpdate()
|
||||
await waitForNextUpdate()
|
||||
await pause(20)
|
||||
|
||||
expect(storage.cache.news_1).toEqual('{"id":"1337"}')
|
||||
})
|
||||
})
|
||||
it('does not store in cache if fake', async () => {
|
||||
await act(async () => {
|
||||
api.isLoggedIn = true
|
||||
api.isFake = true
|
||||
|
||||
const { waitForNextUpdate } = renderHook(() => useNewsDetails(child, newsItem), { wrapper })
|
||||
|
||||
await waitForNextUpdate()
|
||||
await waitForNextUpdate()
|
||||
await pause(20)
|
||||
|
||||
expect(storage.cache.news_2).toEqual('{"id":"1337"}')
|
||||
})
|
||||
})
|
||||
it('retries if api fails', async () => {
|
||||
await act(async () => {
|
||||
api.isLoggedIn = true
|
||||
const error = new Error('fail')
|
||||
api.getNewsDetails.mockRejectedValueOnce(error)
|
||||
|
||||
const { result, waitForNextUpdate } = renderHook(() => useNewsDetails(child, newsItem), { wrapper })
|
||||
|
||||
await waitForNextUpdate()
|
||||
await waitForNextUpdate()
|
||||
await waitForNextUpdate()
|
||||
|
||||
expect(result.current.error).toEqual(error)
|
||||
expect(result.current.status).toEqual('loading')
|
||||
expect(result.current.data).toEqual({ id: '1337' })
|
||||
|
||||
jest.advanceTimersToNextTimer()
|
||||
|
||||
await waitForNextUpdate()
|
||||
await waitForNextUpdate()
|
||||
await waitForNextUpdate()
|
||||
|
||||
expect(result.current.status).toEqual('loaded')
|
||||
expect(result.current.data).toEqual({ id: '1337' })
|
||||
})
|
||||
})
|
||||
it('gives up after 3 retries', async () => {
|
||||
await act(async () => {
|
||||
api.isLoggedIn = true
|
||||
const error = new Error('fail')
|
||||
api.getNewsDetails.mockRejectedValueOnce(error)
|
||||
api.getNewsDetails.mockRejectedValueOnce(error)
|
||||
api.getNewsDetails.mockRejectedValueOnce(error)
|
||||
|
||||
const { result, waitForNextUpdate } = renderHook(() => useNewsDetails(child, newsItem), { wrapper })
|
||||
|
||||
await waitForNextUpdate()
|
||||
await waitForNextUpdate()
|
||||
await waitForNextUpdate()
|
||||
|
||||
expect(result.current.error).toEqual(error)
|
||||
expect(result.current.status).toEqual('loading')
|
||||
expect(result.current.data).toEqual({ id: '1337' })
|
||||
|
||||
jest.advanceTimersToNextTimer()
|
||||
|
||||
await waitForNextUpdate()
|
||||
await waitForNextUpdate()
|
||||
await waitForNextUpdate()
|
||||
|
||||
expect(result.current.error).toEqual(error)
|
||||
expect(result.current.status).toEqual('error')
|
||||
expect(result.current.data).toEqual({ id: '1337' })
|
||||
})
|
||||
})
|
||||
it('reports if api fails', async () => {
|
||||
await act(async () => {
|
||||
api.isLoggedIn = true
|
||||
const error = new Error('fail')
|
||||
api.getNewsDetails.mockRejectedValueOnce(error)
|
||||
|
||||
const { result, waitForNextUpdate } = renderHook(() => useNewsDetails(child, newsItem), { wrapper })
|
||||
|
||||
await waitForNextUpdate()
|
||||
await waitForNextUpdate()
|
||||
await waitForNextUpdate()
|
||||
|
||||
expect(result.current.error).toEqual(error)
|
||||
|
||||
expect(reporter.error).toHaveBeenCalledWith(error, 'Error getting NEWS_DETAILS from API')
|
||||
})
|
||||
})
|
||||
})
|
Loading…
Reference in New Issue