From 0721bc42f625aad38c504303e57bae13bc919eb7 Mon Sep 17 00:00:00 2001 From: Cedric van Putten Date: Sat, 18 Jan 2020 22:16:17 +0100 Subject: [PATCH] fix(battery): unify tests and fix callback reuse (#64) --- packages/battery/src/use-battery-level.ts | 10 ++-- .../battery/src/use-battery-low-power-mode.ts | 10 ++-- packages/battery/src/use-battery-state.ts | 10 ++-- packages/battery/src/use-battery.ts | 10 ++-- .../battery/tests/use-battery-level.test.ts | 56 +++++++++++------- .../tests/use-battery-low-power-mode.test.ts | 56 +++++++++++------- .../battery/tests/use-battery-state.test.ts | 58 ++++++++++++------- packages/battery/tests/use-battery.test.ts | 42 +++++++++++--- 8 files changed, 159 insertions(+), 93 deletions(-) diff --git a/packages/battery/src/use-battery-level.ts b/packages/battery/src/use-battery-level.ts index 5692caef..ef72aaf2 100644 --- a/packages/battery/src/use-battery-level.ts +++ b/packages/battery/src/use-battery-level.ts @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import { getBatteryLevelAsync, addBatteryLevelListener } from 'expo-battery'; /** @@ -21,9 +21,9 @@ export function useBatteryLevel( listen = true, } = options; - function getBatteryLevel() { - return getBatteryLevelAsync().then(setData); - } + const getBatteryLevel = useCallback(() => ( + getBatteryLevelAsync().then(setData) + ), []); useEffect(() => { if (get) { @@ -33,7 +33,7 @@ export function useBatteryLevel( if (listen) { return addBatteryLevelListener(state => setData(state.batteryLevel)).remove; } - }, [get, listen]); + }, [get, getBatteryLevel, listen]); return [data, getBatteryLevel]; } diff --git a/packages/battery/src/use-battery-low-power-mode.ts b/packages/battery/src/use-battery-low-power-mode.ts index bf9647f8..33f97a29 100644 --- a/packages/battery/src/use-battery-low-power-mode.ts +++ b/packages/battery/src/use-battery-low-power-mode.ts @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import { isLowPowerModeEnabledAsync, addLowPowerModeListener } from 'expo-battery'; /** @@ -22,9 +22,9 @@ export function useBatteryLowPowerMode( listen = true, } = options; - function getBatteryLowPowerMode() { - return isLowPowerModeEnabledAsync().then(setData); - } + const getBatteryLowPowerMode = useCallback(() => ( + isLowPowerModeEnabledAsync().then(setData) + ), []); useEffect(() => { if (get) { @@ -34,7 +34,7 @@ export function useBatteryLowPowerMode( if (listen) { return addLowPowerModeListener(state => setData(state.lowPowerMode)).remove; } - }, [get, listen]); + }, [get, getBatteryLowPowerMode, listen]); return [data, getBatteryLowPowerMode]; } diff --git a/packages/battery/src/use-battery-state.ts b/packages/battery/src/use-battery-state.ts index 1221081e..0f7b1444 100644 --- a/packages/battery/src/use-battery-state.ts +++ b/packages/battery/src/use-battery-state.ts @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import { BatteryState, getBatteryStateAsync, addBatteryStateListener } from 'expo-battery'; /** @@ -24,9 +24,9 @@ export function useBatteryState( listen = true, } = options; - function getBatteryState() { - return getBatteryStateAsync().then(setData); - } + const getBatteryState = useCallback(() => ( + getBatteryStateAsync().then(setData) + ), []); useEffect(() => { if (get) { @@ -36,7 +36,7 @@ export function useBatteryState( if (listen) { return addBatteryStateListener(state => setData(state.batteryState)).remove; } - }, [get, listen]); + }, [get, getBatteryState, listen]); return [data, getBatteryState]; } diff --git a/packages/battery/src/use-battery.ts b/packages/battery/src/use-battery.ts index 28f867d9..5ef96b2b 100644 --- a/packages/battery/src/use-battery.ts +++ b/packages/battery/src/use-battery.ts @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react'; +import { useEffect, useState, useCallback } from 'react'; import { PowerState, getPowerStateAsync } from 'expo-battery'; /** @@ -21,15 +21,15 @@ export function useBattery( const [data, setData] = useState(); const { get = true } = options; - function getBatteryPowerState() { - return getPowerStateAsync().then(setData); - } + const getBatteryPowerState = useCallback(() => ( + getPowerStateAsync().then(setData) + ), []); useEffect(() => { if (get) { getBatteryPowerState(); } - }, [get]); + }, [get, getBatteryPowerState]); return [data, getBatteryPowerState]; } diff --git a/packages/battery/tests/use-battery-level.test.ts b/packages/battery/tests/use-battery-level.test.ts index 29c6d31a..bb4da9f3 100644 --- a/packages/battery/tests/use-battery-level.test.ts +++ b/packages/battery/tests/use-battery-level.test.ts @@ -12,18 +12,30 @@ it('returns data and get callback when mounted', () => { expect(hook.result.current[GET]).toBeInstanceOf(Function); }); -it('updates data with get callback', async () => { - jest.spyOn(Battery, 'getBatteryLevelAsync').mockResolvedValue(1); +describe('get callback', () => { + it('updates data with get callback', async () => { + jest.spyOn(Battery, 'getBatteryLevelAsync') + .mockResolvedValue(1); - const hook = renderHook(() => useBatteryLevel({ get: false, listen: false })); - await act(() => hook.result.current[GET]()); + const hook = renderHook(() => useBatteryLevel({ get: false, listen: false })); + await act(() => hook.result.current[GET]()); + + expect(hook.result.current[DATA]).toBe(1); + }); + + it('uses the same get callback when rerendered', async () => { + const hook = renderHook(() => useBatteryLevel({ get: false, listen: false })); + const getter = hook.result.current[GET]; + hook.rerender({ get: false, listen: false }); - expect(hook.result.current[DATA]).toBe(1); + expect(getter).toBe(hook.result.current[GET]); + }); }); -describe('default behavior', () => { +describe('get option', () => { it('gets battery level when mounted', async () => { - jest.spyOn(Battery, 'getBatteryLevelAsync').mockResolvedValue(0.1337); + jest.spyOn(Battery, 'getBatteryLevelAsync') + .mockResolvedValue(0.1337); const hook = renderHook(() => useBatteryLevel({ listen: false })); await hook.waitForNextUpdate(); @@ -31,21 +43,9 @@ describe('default behavior', () => { expect(hook.result.current[DATA]).toBe(0.1337); expect(Battery.getBatteryLevelAsync).toBeCalled(); }); - - it('listens to battery level when mounted', async () => { - const subscription = { remove: jest.fn() }; - const listener = jest.spyOn(Battery, 'addBatteryLevelListener').mockReturnValue(subscription); - - const hook = renderHook(() => useBatteryLevel({ get: false })); - const handler = listener.mock.calls[0][0]; - act(() => handler({ batteryLevel: 0.75 })); - - expect(hook.result.current[DATA]).toBe(0.75); - expect(Battery.addBatteryLevelListener).toBeCalled(); - }); }); -describe('event listener', () => { +describe('listen option', () => { it('subscribes when mounted', () => { const listener = jest.spyOn(Battery, 'addBatteryLevelListener'); @@ -56,11 +56,25 @@ describe('event listener', () => { it('unsubscribes when unmounted', () => { const subscription = { remove: jest.fn() }; - jest.spyOn(Battery, 'addBatteryLevelListener').mockReturnValue(subscription); + jest.spyOn(Battery, 'addBatteryLevelListener') + .mockReturnValue(subscription); const hook = renderHook(() => useBatteryLevel({ get: false, listen: true })); hook.unmount(); expect(subscription.remove).toBeCalled(); }); + + it('updates the battery level state when mounted', async () => { + const subscription = { remove: jest.fn() }; + const listener = jest.spyOn(Battery, 'addBatteryLevelListener') + .mockReturnValue(subscription); + + const hook = renderHook(() => useBatteryLevel({ get: false })); + const handler = listener.mock.calls[0][0]; + act(() => handler({ batteryLevel: 0.75 })); + + expect(hook.result.current[DATA]).toBe(0.75); + expect(Battery.addBatteryLevelListener).toBeCalled(); + }); }); diff --git a/packages/battery/tests/use-battery-low-power-mode.test.ts b/packages/battery/tests/use-battery-low-power-mode.test.ts index 6459c023..55ecc3b0 100644 --- a/packages/battery/tests/use-battery-low-power-mode.test.ts +++ b/packages/battery/tests/use-battery-low-power-mode.test.ts @@ -12,18 +12,30 @@ it('returns data and get callback when mounted', () => { expect(hook.result.current[GET]).toBeInstanceOf(Function); }); -it('updates data with get callback', async () => { - jest.spyOn(Battery, 'isLowPowerModeEnabledAsync').mockResolvedValue(true); +describe('get callback', () => { + it('updates data with get callback', async () => { + jest.spyOn(Battery, 'isLowPowerModeEnabledAsync') + .mockResolvedValue(true); - const hook = renderHook(() => useBatteryLowPowerMode({ get: false, listen: false })); - await act(() => hook.result.current[GET]()); + const hook = renderHook(() => useBatteryLowPowerMode({ get: false, listen: false })); + await act(() => hook.result.current[GET]()); + + expect(hook.result.current[DATA]).toBe(true); + }); - expect(hook.result.current[DATA]).toBe(true); + it('uses the same get callback when rerendered', async () => { + const hook = renderHook(() => useBatteryLowPowerMode({ get: false, listen: false })); + const getter = hook.result.current[GET]; + hook.rerender({ get: false, listen: false }); + + expect(getter).toBe(hook.result.current[GET]); + }); }); -describe('default behavior', () => { +describe('get option', () => { it('gets battery low power mode when mounted', async () => { - jest.spyOn(Battery, 'isLowPowerModeEnabledAsync').mockResolvedValueOnce(true); + jest.spyOn(Battery, 'isLowPowerModeEnabledAsync') + .mockResolvedValueOnce(true); const hook = renderHook(() => useBatteryLowPowerMode({ listen: false })); await hook.waitForNextUpdate(); @@ -31,21 +43,9 @@ describe('default behavior', () => { expect(hook.result.current[DATA]).toBe(true); expect(Battery.isLowPowerModeEnabledAsync).toBeCalled(); }); - - it('listens to battery low power mode when mounted', async () => { - const subscription = { remove: jest.fn() }; - const listener = jest.spyOn(Battery, 'addLowPowerModeListener').mockReturnValue(subscription); - - const hook = renderHook(() => useBatteryLowPowerMode({ get: false })); - const handler = listener.mock.calls[0][0]; - act(() => handler({ lowPowerMode: false })); - - expect(hook.result.current[DATA]).toBe(false); - expect(Battery.addLowPowerModeListener).toBeCalled(); - }); }); -describe('event listener', () => { +describe('listen option', () => { it('subscribes when mounted', () => { const listener = jest.spyOn(Battery, 'addLowPowerModeListener'); @@ -55,11 +55,25 @@ describe('event listener', () => { it('unsubscribes when unmounted', () => { const subscription = { remove: jest.fn() }; - jest.spyOn(Battery, 'addLowPowerModeListener').mockReturnValue(subscription); + jest.spyOn(Battery, 'addLowPowerModeListener') + .mockReturnValue(subscription); const hook = renderHook(() => useBatteryLowPowerMode({ get: false, listen: true })); hook.unmount(); expect(subscription.remove).toBeCalled(); }); + + it('listens to battery low power mode when mounted', async () => { + const subscription = { remove: jest.fn() }; + const listener = jest.spyOn(Battery, 'addLowPowerModeListener') + .mockReturnValue(subscription); + + const hook = renderHook(() => useBatteryLowPowerMode({ get: false })); + const handler = listener.mock.calls[0][0]; + act(() => handler({ lowPowerMode: false })); + + expect(hook.result.current[DATA]).toBe(false); + expect(Battery.addLowPowerModeListener).toBeCalled(); + }); }); diff --git a/packages/battery/tests/use-battery-state.test.ts b/packages/battery/tests/use-battery-state.test.ts index 0780703d..0a528f7b 100644 --- a/packages/battery/tests/use-battery-state.test.ts +++ b/packages/battery/tests/use-battery-state.test.ts @@ -12,18 +12,30 @@ it('returns data and get callback when mounted', () => { expect(hook.result.current[GET]).toBeInstanceOf(Function); }); -it('updates data with get callback', async () => { - jest.spyOn(Battery, 'getBatteryStateAsync').mockResolvedValue(Battery.BatteryState.FULL); +describe('get callback', () => { + it('updates data with get callback', async () => { + jest.spyOn(Battery, 'getBatteryStateAsync') + .mockResolvedValue(Battery.BatteryState.FULL); - const hook = renderHook(() => useBatteryState({ get: false, listen: false })); - await act(() => hook.result.current[GET]()); + const hook = renderHook(() => useBatteryState({ get: false, listen: false })); + await act(() => hook.result.current[GET]()); + + expect(hook.result.current[DATA]).toBe(Battery.BatteryState.FULL); + }); + + it('uses the same get callback when rerendered', async () => { + const hook = renderHook(() => useBatteryState({ get: false, listen: false })); + const getter = hook.result.current[GET]; + hook.rerender({ get: false, listen: false }); - expect(hook.result.current[DATA]).toBe(Battery.BatteryState.FULL); + expect(getter).toBe(hook.result.current[GET]); + }); }); -describe('default behavior', () => { +describe('get option', () => { it('gets battery state when mounted', async () => { - jest.spyOn(Battery, 'getBatteryStateAsync').mockResolvedValue(Battery.BatteryState.UNPLUGGED); + jest.spyOn(Battery, 'getBatteryStateAsync') + .mockResolvedValue(Battery.BatteryState.UNPLUGGED); const hook = renderHook(() => useBatteryState({ listen: false })); await hook.waitForNextUpdate(); @@ -31,21 +43,9 @@ describe('default behavior', () => { expect(hook.result.current[DATA]).toBe(Battery.BatteryState.UNPLUGGED); expect(Battery.getBatteryStateAsync).toBeCalled(); }); +}) - it('listens to battery state when mounted', async () => { - const subscription = { remove: jest.fn() }; - const listener = jest.spyOn(Battery, 'addBatteryStateListener').mockReturnValue(subscription); - - const hook = renderHook(() => useBatteryState({ get: false })); - const handler = listener.mock.calls[0][0]; - act(() => handler({ batteryState: Battery.BatteryState.UNKNOWN })); - - expect(hook.result.current[DATA]).toBe(Battery.BatteryState.UNKNOWN); - expect(Battery.addBatteryStateListener).toBeCalled(); - }); -}); - -describe('event listener', () => { +describe('listen option', () => { it('subscribes when mounted', () => { const listener = jest.spyOn(Battery, 'addBatteryStateListener'); @@ -55,11 +55,25 @@ describe('event listener', () => { it('unsubscribes when unmounted', () => { const subscription = { remove: jest.fn() }; - jest.spyOn(Battery, 'addBatteryStateListener').mockReturnValue(subscription); + jest.spyOn(Battery, 'addBatteryStateListener') + .mockReturnValue(subscription); const hook = renderHook(() => useBatteryState({ get: false, listen: true })); hook.unmount(); expect(subscription.remove).toBeCalled(); }); + + it('updates the battery state when mounted', async () => { + const subscription = { remove: jest.fn() }; + const listener = jest.spyOn(Battery, 'addBatteryStateListener') + .mockReturnValue(subscription); + + const hook = renderHook(() => useBatteryState({ get: false, listen: true })); + const handler = listener.mock.calls[0][0]; + act(() => handler({ batteryState: Battery.BatteryState.UNKNOWN })); + + expect(hook.result.current[DATA]).toBe(Battery.BatteryState.UNKNOWN); + expect(Battery.addBatteryStateListener).toBeCalled(); + }); }); diff --git a/packages/battery/tests/use-battery.test.ts b/packages/battery/tests/use-battery.test.ts index e748b68e..d5bd9c54 100644 --- a/packages/battery/tests/use-battery.test.ts +++ b/packages/battery/tests/use-battery.test.ts @@ -18,23 +18,47 @@ it('returns data and get callback when mounted', () => { expect(hook.result.current[GET]).toBeInstanceOf(Function); }); -it('updates data with get callback', async () => { - jest.spyOn(Battery, 'getPowerStateAsync').mockResolvedValue(powerState); +describe('get callback', () => { + it('updates data with get callback', async () => { + jest.spyOn(Battery, 'getPowerStateAsync') + .mockResolvedValue(powerState); - const hook = renderHook(() => useBattery({ get: false })); - await act(() => hook.result.current[GET]()); + const hook = renderHook(() => useBattery({ get: false })); + expect(hook.result.current[DATA]).toBeUndefined(); + await act(() => hook.result.current[GET]()); + + expect(hook.result.current[DATA]).toMatchObject(powerState); + }); - expect(hook.result.current[DATA]).toMatchObject(powerState); + it('uses the same get callback when rerendered', async () => { + const hook = renderHook(() => useBattery({ get: false })); + const getter = hook.result.current[GET]; + hook.rerender({ get: false }); + + expect(getter).toBe(hook.result.current[GET]); + }); }); -describe('default behavior', () => { +describe('get option', () => { it('gets battery power state when mounted', async () => { - const getter = jest.spyOn(Battery, 'getPowerStateAsync').mockResolvedValue(powerState); + jest.spyOn(Battery, 'getPowerStateAsync') + .mockResolvedValue(powerState); + + const hook = renderHook(() => useBattery({ get: true })); + await hook.waitForNextUpdate(); + + expect(hook.result.current[DATA]).toBe(powerState); + }); + + it('updates data with get option when rerendered', async () => { + jest.spyOn(Battery, 'getPowerStateAsync') + .mockResolvedValue(powerState); - const hook = renderHook(() => useBattery()); + const hook = renderHook(useBattery, { initialProps: { get: false } }); + expect(hook.result.current[DATA]).toBeUndefined(); + hook.rerender({ get: true }) await hook.waitForNextUpdate(); - expect(getter).toBeCalled(); expect(hook.result.current[DATA]).toBe(powerState); }); });