diff --git a/.eslintrc.js b/.eslintrc.js index f2f614a..23a8334 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,9 +1,15 @@ module.exports = { - extends: ['airbnb-typescript'], + extends: [ + 'airbnb-typescript', + 'plugin:jest/recommended' + ], parserOptions: { - project: `./tsconfig.json` + project: `./tsconfig.json`, }, rules: { - '@typescript-eslint/semi': ['error', 'never'] - } + '@typescript-eslint/semi': ['error', 'never'], + 'jest/no-mocks-import': [0], + 'max-len': [1, 110], + 'react/jsx-filename-extension': [1, { extensions: ['.js', '.jsx'] }], + }, } diff --git a/package.json b/package.json index 7fbe3f9..6f62039 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "eslint": "^7.19.0", "eslint-config-airbnb-typescript": "^12.0.0", "eslint-plugin-import": "^2.22.0", + "eslint-plugin-jest": "^24.1.3", "eslint-plugin-jsx-a11y": "^6.3.1", "eslint-plugin-react": "^7.20.3", "eslint-plugin-react-hooks": "^4.0.8", diff --git a/src/__mocks__/AsyncStorage.js b/src/__mocks__/AsyncStorage.js index 254b7ac..b03ded4 100644 --- a/src/__mocks__/AsyncStorage.js +++ b/src/__mocks__/AsyncStorage.js @@ -13,5 +13,10 @@ export default (init = {}, delay = 0) => { await pause(delay) cache[key] = val } - return { getItem, setItem, cache } + const clear = () => { + Object.keys(cache).forEach((key) => { cache[key] = undefined }) + } + return { + getItem, setItem, cache, clear, + } } diff --git a/src/fake.test.js b/src/fake.test.js new file mode 100644 index 0000000..a9dc29a --- /dev/null +++ b/src/fake.test.js @@ -0,0 +1,146 @@ +import React from 'react' +import { act, renderHook } from '@testing-library/react-hooks' +import { ApiProvider } from '.' +import createStorage from './__mocks__/AsyncStorage' +import { + useCalendar, + useChildList, + useClassmates, + useMenu, + useNews, + useNotifications, + useSchedule, + useUser, +} from './hooks' + +const { default: init } = jest.requireActual('@skolplattformen/embedded-api') + +describe('hooks with fake data', () => { + let api + let storage + const wrapper = ({ children }) => ( + {children} + ) + beforeEach(async () => { + api = init(() => { }, () => { }) + await api.login('121212121212') + + storage = createStorage({}) + }) + it('returns user', async () => { + await act(async () => { + const { + result, + waitForNextUpdate, + } = renderHook(() => useUser(), { wrapper }) + + await waitForNextUpdate() + await waitForNextUpdate() + + expect(result.current.data).toEqual({ + firstName: 'Namn', + lastName: 'Namnsson', + }) + }) + }) + it('returns child list', async () => { + await act(async () => { + const { + result, + waitForNextUpdate, + } = renderHook(() => useChildList(), { wrapper }) + + await waitForNextUpdate() + await waitForNextUpdate() + + expect(result.current.data).toHaveLength(2) + }) + }) + describe('data belonging to one child', () => { + let child + beforeAll(async () => { + [child] = await api.getChildren() + }) + it('returns calendar', async () => { + await act(async () => { + const { + result, + waitForNextUpdate, + } = renderHook(() => useCalendar(child), { wrapper }) + + await waitForNextUpdate() + await waitForNextUpdate() + + expect(result.current.data.length).toBeGreaterThan(1) + }) + }) + it('returns classmates', async () => { + await act(async () => { + const { + result, + waitForNextUpdate, + } = renderHook(() => useClassmates(child), { wrapper }) + + await waitForNextUpdate() + await waitForNextUpdate() + + expect(result.current.data.length).toBeGreaterThan(1) + }) + }) + it('returns menu', async () => { + await act(async () => { + const { + result, + waitForNextUpdate, + } = renderHook(() => useMenu(child), { wrapper }) + + await waitForNextUpdate() + await waitForNextUpdate() + + expect(result.current.data.length).toBeGreaterThan(1) + }) + }) + it('returns news', async () => { + await act(async () => { + const { + result, + waitForNextUpdate, + } = renderHook(() => useNews(child), { wrapper }) + + await waitForNextUpdate() + await waitForNextUpdate() + + expect(result.current.data.length).toBeGreaterThan(1) + }) + }) + it('returns notifications', async () => { + await act(async () => { + const { + result, + waitForNextUpdate, + } = renderHook(() => useNotifications(child), { wrapper }) + + await waitForNextUpdate() + await waitForNextUpdate() + + expect(result.current.data.length).toBeGreaterThan(1) + }) + }) + it('returns schedule', async () => { + const from = '2021-01-01' + const to = '2021-01-08' + await act(async () => { + const { + result, + waitForNextUpdate, + } = renderHook(() => useSchedule(child, from, to), { wrapper }) + + await waitForNextUpdate() + await waitForNextUpdate() + + // No fake schedule in embedded-api yet + expect(result.current.data.length).not.toBeGreaterThan(1) + }) + }) + }) +}) diff --git a/src/useCalendar.test.js b/src/useCalendar.test.js index a64deb3..bc8f198 100644 --- a/src/useCalendar.test.js +++ b/src/useCalendar.test.js @@ -6,26 +6,26 @@ import store from './store' import init from './__mocks__/@skolplattformen/embedded-api' import createStorage from './__mocks__/AsyncStorage' -const pause = (ms = 0) => new Promise(r => setTimeout(r, ms)) +const pause = (ms = 0) => new Promise((r) => setTimeout(r, ms)) describe('useCalendar(child)', () => { let api let storage - let result + let response let child const wrapper = ({ children }) => ( {children} ) beforeEach(() => { - result = [{ id: 1 }] + response = [{ id: 1 }] api = init() api.getCalendar.mockImplementation(() => ( new Promise((res) => { - setTimeout(() => res(result), 50) + setTimeout(() => res(response), 50) }) )) storage = createStorage({ - calendar_10: [{ id: 2 }] + calendar_10: [{ id: 2 }], }, 2) child = { id: 10 } }) @@ -69,7 +69,7 @@ describe('useCalendar(child)', () => { expect(result.current.status).toEqual('loaded') }) }) - it('calls cache', async () => { + it('retrieves data from cache', async () => { await act(async () => { api.isLoggedIn = true const { result, waitForNextUpdate } = renderHook(() => useCalendar(child), { wrapper }) @@ -80,6 +80,18 @@ describe('useCalendar(child)', () => { expect(result.current.data).toEqual([{ id: 2 }]) }) }) + it('works when cache is empty', async () => { + storage.clear() + await act(async () => { + api.isLoggedIn = true + const { result, waitForNextUpdate } = renderHook(() => useCalendar(child), { wrapper }) + + await waitForNextUpdate() + await waitForNextUpdate() + + expect(result.current.data).toEqual([{ id: 1 }]) + }) + }) it('updates status to loading', async () => { await act(async () => { api.isLoggedIn = true @@ -115,7 +127,7 @@ describe('useCalendar(child)', () => { await waitForNextUpdate() await pause(20) - expect(storage.cache['calendar_10']).toEqual('[{"id":1}]') + expect(storage.cache.calendar_10).toEqual('[{"id":1}]') }) }) it('does not store in cache if fake', async () => { @@ -130,7 +142,7 @@ describe('useCalendar(child)', () => { await waitForNextUpdate() await pause(20) - expect(storage.cache['calendar_10']).toEqual('[{"id":2}]') + expect(storage.cache.calendar_10).toEqual('[{"id":2}]') }) }) }) diff --git a/src/useChildlist.test.js b/src/useChildlist.test.js index 87c3be2..96a0f0c 100644 --- a/src/useChildlist.test.js +++ b/src/useChildlist.test.js @@ -6,25 +6,25 @@ import store from './store' import init from './__mocks__/@skolplattformen/embedded-api' import createStorage from './__mocks__/AsyncStorage' -const pause = (ms = 0) => new Promise(r => setTimeout(r, ms)) +const pause = (ms = 0) => new Promise((r) => setTimeout(r, ms)) describe('useChildList()', () => { let api let storage - let result + let response const wrapper = ({ children }) => ( {children} ) beforeEach(() => { - result = [{ id: 1 }] + response = [{ id: 1 }] api = init() api.getChildren.mockImplementation(() => ( new Promise((res) => { - setTimeout(() => res(result), 50) + setTimeout(() => res(response), 50) }) )) storage = createStorage({ - children: [{ id: 2 }] + children: [{ id: 2 }], }, 2) }) afterEach(async () => { @@ -113,7 +113,7 @@ describe('useChildList()', () => { await waitForNextUpdate() await pause(20) - expect(storage.cache['children']).toEqual('[{"id":1}]') + expect(storage.cache.children).toEqual('[{"id":1}]') }) }) it('does not store in cache if fake', async () => { @@ -128,7 +128,7 @@ describe('useChildList()', () => { await waitForNextUpdate() await pause(20) - expect(storage.cache['children']).toEqual('[{"id":2}]') + expect(storage.cache.children).toEqual('[{"id":2}]') }) }) }) diff --git a/src/useClassmates.test.js b/src/useClassmates.test.js index abb4a69..6e116d7 100644 --- a/src/useClassmates.test.js +++ b/src/useClassmates.test.js @@ -6,26 +6,26 @@ import store from './store' import init from './__mocks__/@skolplattformen/embedded-api' import createStorage from './__mocks__/AsyncStorage' -const pause = (ms = 0) => new Promise(r => setTimeout(r, ms)) +const pause = (ms = 0) => new Promise((r) => setTimeout(r, ms)) describe('useClassmates(child)', () => { let api let storage - let result + let response let child const wrapper = ({ children }) => ( {children} ) beforeEach(() => { - result = [{ id: 1 }] + response = [{ id: 1 }] api = init() api.getClassmates.mockImplementation(() => ( new Promise((res) => { - setTimeout(() => res(result), 50) + setTimeout(() => res(response), 50) }) )) storage = createStorage({ - classmates_10: [{ id: 2 }] + classmates_10: [{ id: 2 }], }, 2) child = { id: 10 } }) @@ -115,7 +115,7 @@ describe('useClassmates(child)', () => { await waitForNextUpdate() await pause(20) - expect(storage.cache['classmates_10']).toEqual('[{"id":1}]') + expect(storage.cache.classmates_10).toEqual('[{"id":1}]') }) }) it('does not store in cache if fake', async () => { @@ -130,7 +130,7 @@ describe('useClassmates(child)', () => { await waitForNextUpdate() await pause(20) - expect(storage.cache['classmates_10']).toEqual('[{"id":2}]') + expect(storage.cache.classmates_10).toEqual('[{"id":2}]') }) }) }) diff --git a/src/useMenu.test.js b/src/useMenu.test.js index ba53bcd..9be2217 100644 --- a/src/useMenu.test.js +++ b/src/useMenu.test.js @@ -6,26 +6,26 @@ import store from './store' import init from './__mocks__/@skolplattformen/embedded-api' import createStorage from './__mocks__/AsyncStorage' -const pause = (ms = 0) => new Promise(r => setTimeout(r, ms)) +const pause = (ms = 0) => new Promise((r) => setTimeout(r, ms)) describe('useMenu(child)', () => { let api let storage - let result + let response let child const wrapper = ({ children }) => ( {children} ) beforeEach(() => { - result = [{ id: 1 }] + response = [{ id: 1 }] api = init() api.getMenu.mockImplementation(() => ( new Promise((res) => { - setTimeout(() => res(result), 50) + setTimeout(() => res(response), 50) }) )) storage = createStorage({ - menu_10: [{ id: 2 }] + menu_10: [{ id: 2 }], }, 2) child = { id: 10 } }) @@ -115,7 +115,7 @@ describe('useMenu(child)', () => { await waitForNextUpdate() await pause(20) - expect(storage.cache['menu_10']).toEqual('[{"id":1}]') + expect(storage.cache.menu_10).toEqual('[{"id":1}]') }) }) it('does not store in cache if fake', async () => { @@ -130,7 +130,7 @@ describe('useMenu(child)', () => { await waitForNextUpdate() await pause(20) - expect(storage.cache['menu_10']).toEqual('[{"id":2}]') + expect(storage.cache.menu_10).toEqual('[{"id":2}]') }) }) }) diff --git a/src/useNews.test.js b/src/useNews.test.js index 61ae9ef..e395386 100644 --- a/src/useNews.test.js +++ b/src/useNews.test.js @@ -6,26 +6,26 @@ import store from './store' import init from './__mocks__/@skolplattformen/embedded-api' import createStorage from './__mocks__/AsyncStorage' -const pause = (ms = 0) => new Promise(r => setTimeout(r, ms)) +const pause = (ms = 0) => new Promise((r) => setTimeout(r, ms)) describe('useNews(child)', () => { let api let storage - let result + let response let child const wrapper = ({ children }) => ( {children} ) beforeEach(() => { - result = [{ id: 1 }] + response = [{ id: 1 }] api = init() api.getNews.mockImplementation(() => ( new Promise((res) => { - setTimeout(() => res(result), 50) + setTimeout(() => res(response), 50) }) )) storage = createStorage({ - news_10: [{ id: 2 }] + news_10: [{ id: 2 }], }, 2) child = { id: 10 } }) @@ -115,7 +115,7 @@ describe('useNews(child)', () => { await waitForNextUpdate() await pause(20) - expect(storage.cache['news_10']).toEqual('[{"id":1}]') + expect(storage.cache.news_10).toEqual('[{"id":1}]') }) }) it('does not store in cache if fake', async () => { @@ -130,7 +130,7 @@ describe('useNews(child)', () => { await waitForNextUpdate() await pause(20) - expect(storage.cache['news_10']).toEqual('[{"id":2}]') + expect(storage.cache.news_10).toEqual('[{"id":2}]') }) }) }) diff --git a/src/useNotifications.test.js b/src/useNotifications.test.js index 92f0523..ca37b45 100644 --- a/src/useNotifications.test.js +++ b/src/useNotifications.test.js @@ -6,26 +6,26 @@ import store from './store' import init from './__mocks__/@skolplattformen/embedded-api' import createStorage from './__mocks__/AsyncStorage' -const pause = (ms = 0) => new Promise(r => setTimeout(r, ms)) +const pause = (ms = 0) => new Promise((r) => setTimeout(r, ms)) describe('useNotifications(child)', () => { let api let storage - let result + let response let child const wrapper = ({ children }) => ( {children} ) beforeEach(() => { - result = [{ id: 1 }] + response = [{ id: 1 }] api = init() api.getNotifications.mockImplementation(() => ( new Promise((res) => { - setTimeout(() => res(result), 50) + setTimeout(() => res(response), 50) }) )) storage = createStorage({ - notifications_10: [{ id: 2 }] + notifications_10: [{ id: 2 }], }, 2) child = { id: 10 } }) @@ -115,7 +115,7 @@ describe('useNotifications(child)', () => { await waitForNextUpdate() await pause(20) - expect(storage.cache['notifications_10']).toEqual('[{"id":1}]') + expect(storage.cache.notifications_10).toEqual('[{"id":1}]') }) }) it('does not store in cache if fake', async () => { @@ -130,7 +130,7 @@ describe('useNotifications(child)', () => { await waitForNextUpdate() await pause(20) - expect(storage.cache['notifications_10']).toEqual('[{"id":2}]') + expect(storage.cache.notifications_10).toEqual('[{"id":2}]') }) }) }) diff --git a/src/useSchedule.test.js b/src/useSchedule.test.js index 835a953..e275638 100644 --- a/src/useSchedule.test.js +++ b/src/useSchedule.test.js @@ -6,12 +6,12 @@ import store from './store' import init from './__mocks__/@skolplattformen/embedded-api' import createStorage from './__mocks__/AsyncStorage' -const pause = (ms = 0) => new Promise(r => setTimeout(r, ms)) +const pause = (ms = 0) => new Promise((r) => setTimeout(r, ms)) describe('useSchedule(child, from, to)', () => { let api let storage - let result + let response let child let from let to @@ -19,15 +19,15 @@ describe('useSchedule(child, from, to)', () => { {children} ) beforeEach(() => { - result = [{ id: 1 }] + response = [{ id: 1 }] api = init() api.getSchedule.mockImplementation(() => ( new Promise((res) => { - setTimeout(() => res(result), 50) + setTimeout(() => res(response), 50) }) )) storage = createStorage({ - 'schedule_10_2021-01-01_2021-01-08': [{ id: 2 }] + 'schedule_10_2021-01-01_2021-01-08': [{ id: 2 }], }, 2) child = { id: 10 } from = '2021-01-01' diff --git a/src/useUser.test.js b/src/useUser.test.js index be644e7..3758897 100644 --- a/src/useUser.test.js +++ b/src/useUser.test.js @@ -6,25 +6,25 @@ import store from './store' import init from './__mocks__/@skolplattformen/embedded-api' import createStorage from './__mocks__/AsyncStorage' -const pause = (ms = 0) => new Promise(r => setTimeout(r, ms)) +const pause = (ms = 0) => new Promise((r) => setTimeout(r, ms)) describe('useUser()', () => { let api let storage - let result + let response const wrapper = ({ children }) => ( {children} ) beforeEach(() => { - result = { id: 1 } + response = { id: 1 } api = init() api.getUser.mockImplementation(() => ( new Promise((res) => { - setTimeout(() => res(result), 50) + setTimeout(() => res(response), 50) }) )) storage = createStorage({ - user: { id: 2 } + user: { id: 2 }, }, 2) }) afterEach(async () => { @@ -113,7 +113,7 @@ describe('useUser()', () => { await waitForNextUpdate() await pause(20) - expect(storage.cache['user']).toEqual('{"id":1}') + expect(storage.cache.user).toEqual('{"id":1}') }) }) it('does not store in cache if fake', async () => { @@ -128,7 +128,7 @@ describe('useUser()', () => { await waitForNextUpdate() await pause(20) - expect(storage.cache['user']).toEqual('{"id":2}') + expect(storage.cache.user).toEqual('{"id":2}') }) }) }) diff --git a/yarn.lock b/yarn.lock index 805750b..cec830c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1400,7 +1400,7 @@ semver "^7.3.2" tsutils "^3.17.1" -"@typescript-eslint/experimental-utils@4.14.2": +"@typescript-eslint/experimental-utils@4.14.2", "@typescript-eslint/experimental-utils@^4.0.1": version "4.14.2" resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.14.2.tgz#9df35049d1d36b6cbaba534d703648b9e1f05cbb" integrity sha512-mV9pmET4C2y2WlyHmD+Iun8SAEqkLahHGBkGqDVslHkmoj3VnxnGP4ANlwuxxfq1BsKdl/MPieDbohCEQgKrwA== @@ -2540,6 +2540,13 @@ eslint-plugin-import@^2.22.0: resolve "^1.17.0" tsconfig-paths "^3.9.0" +eslint-plugin-jest@^24.1.3: + version "24.1.3" + resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-24.1.3.tgz#fa3db864f06c5623ff43485ca6c0e8fc5fe8ba0c" + integrity sha512-dNGGjzuEzCE3d5EPZQ/QGtmlMotqnYWD/QpCZ1UuZlrMAdhG5rldh0N0haCvhGnUkSeuORS5VNROwF9Hrgn3Lg== + dependencies: + "@typescript-eslint/experimental-utils" "^4.0.1" + eslint-plugin-jsx-a11y@^6.3.1: version "6.4.1" resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.4.1.tgz#a2d84caa49756942f42f1ffab9002436391718fd"