feat: rework remote config call + some code improvements

master
TyFonDev 2024-01-25 22:17:44 -03:00
parent 9664a5bb2d
commit a7a565ceb6
11 changed files with 5169 additions and 371 deletions

View File

@ -5,14 +5,11 @@ import DevicesRepositoryImpl from '../../repositories/DevicesRepositoryImpl';
import mockResponse from '../__mocks__/mockResponse.json'; import mockResponse from '../__mocks__/mockResponse.json';
import {getNumber} from '../../../../__mocks__/@react-native-firebase/remote-config'; import {getNumber} from '../../../../__mocks__/@react-native-firebase/remote-config';
jest.useFakeTimers();
describe('useDevices tests', () => { describe('useDevices tests', () => {
beforeEach(() => { beforeEach(() => {
jest.clearAllMocks(); jest.clearAllMocks();
jest.useFakeTimers();
});
afterEach(() => {
jest.useRealTimers();
}); });
beforeAll(() => { beforeAll(() => {
@ -24,7 +21,7 @@ describe('useDevices tests', () => {
{ {
lineNumber: '803010', lineNumber: '803010',
description: 'Tucapel', description: 'Tucapel',
locomotionType: 1, locomotionType: '1',
backgroundColor: 'Hexadecimal', backgroundColor: 'Hexadecimal',
letterColor: 'Hexadecimal', letterColor: 'Hexadecimal',
lineMessage: '', lineMessage: '',
@ -52,7 +49,7 @@ describe('useDevices tests', () => {
{ {
lineNumber: '5487', lineNumber: '5487',
description: 'Centauro', description: 'Centauro',
locomotionType: 1, locomotionType: '1',
backgroundColor: 'Hexadecimal', backgroundColor: 'Hexadecimal',
letterColor: 'Hexadecimal', letterColor: 'Hexadecimal',
lineMessage: 'Sin info. GPS, la informacion es estimada', lineMessage: 'Sin info. GPS, la informacion es estimada',
@ -84,8 +81,6 @@ describe('useDevices tests', () => {
stopNumber: '37477', stopNumber: '37477',
stopName: "O'Higgins - entre Angol y Salas", stopName: "O'Higgins - entre Angol y Salas",
}); });
getNumber.mockReturnValue(20000).mockReturnValue(60000);
}); });
it('should be defined', () => { it('should be defined', () => {
@ -139,7 +134,7 @@ describe('useDevices tests', () => {
letterColor: 'Hexadecimal', letterColor: 'Hexadecimal',
lineMessage: '', lineMessage: '',
lineNumber: '803010', lineNumber: '803010',
locomotionType: 1, locomotionType: '1',
}, },
{ {
arrivals: [ arrivals: [
@ -167,7 +162,7 @@ describe('useDevices tests', () => {
letterColor: 'Hexadecimal', letterColor: 'Hexadecimal',
lineMessage: 'Sin info. GPS, la informacion es estimada', lineMessage: 'Sin info. GPS, la informacion es estimada',
lineNumber: '5487', lineNumber: '5487',
locomotionType: 1, locomotionType: '1',
}, },
]); ]);
}); });
@ -176,6 +171,7 @@ describe('useDevices tests', () => {
}); });
it('should refresh devices list after 20000 miliseconds', async () => { it('should refresh devices list after 20000 miliseconds', async () => {
getNumber.mockReturnValue(20000);
const mockDate = new Date('2023-01-01T00:01:00Z'); const mockDate = new Date('2023-01-01T00:01:00Z');
const spy = jest.spyOn(global, 'Date').mockImplementation(() => mockDate); const spy = jest.spyOn(global, 'Date').mockImplementation(() => mockDate);
jest jest
@ -188,7 +184,9 @@ describe('useDevices tests', () => {
expect(result.current.state.currentIndex).toBe(0); expect(result.current.state.currentIndex).toBe(0);
}); });
act(() => {
jest.advanceTimersByTime(20000); jest.advanceTimersByTime(20000);
});
await waitFor(() => { await waitFor(() => {
expect(result.current.state.currentIndex).toBe(22); expect(result.current.state.currentIndex).toBe(22);
@ -198,6 +196,7 @@ describe('useDevices tests', () => {
}); });
it('should refresh devices list after 60000 miliseconds', async () => { it('should refresh devices list after 60000 miliseconds', async () => {
getNumber.mockReturnValue(60000);
const mockDate = new Date('2023-01-01T00:01:00Z'); const mockDate = new Date('2023-01-01T00:01:00Z');
const spy = jest.spyOn(global, 'Date').mockImplementation(() => mockDate); const spy = jest.spyOn(global, 'Date').mockImplementation(() => mockDate);
const spp = jest.spyOn(DevicesRepositoryImpl.prototype, 'getDeviceInfo'); const spp = jest.spyOn(DevicesRepositoryImpl.prototype, 'getDeviceInfo');

View File

@ -48,20 +48,4 @@ describe('useRemoteconfig tests', () => {
}); });
}); });
}); });
describe('get string', () => {
it('should be defined', async () => {
const {result} = renderHook(() => useRemoteConfig());
expect(result.current.getString).toBeDefined();
});
it('should return a value', async () => {
getString.mockReturnValueOnce('someValue');
const {result} = renderHook(() => useRemoteConfig());
expect(result.current.getString('someKey')).toStrictEqual('someValue');
});
});
}); });

View File

@ -30,7 +30,7 @@ const useDevices = () => {
stopMessage: '', stopMessage: '',
displayedLines: [], displayedLines: [],
lines: [], lines: [],
stopName: 'Sin información - Sin información', stopName: '',
}); });
const baseURL = remoteConfig().getString(RemoteConfigKeys.BASE_URL); const baseURL = remoteConfig().getString(RemoteConfigKeys.BASE_URL);
@ -180,7 +180,7 @@ const useDevices = () => {
status: Status.SUCCESS, status: Status.SUCCESS,
}, },
})); }));
} catch (error) { } catch (error: unknown) {
setState((prevState: State) => ({ setState((prevState: State) => ({
...prevState, ...prevState,
status: Status.ERROR, status: Status.ERROR,
@ -199,8 +199,8 @@ const useDevices = () => {
authRepository, authRepository,
devicesRepository, devicesRepository,
password, password,
username,
updateBusesListInterval, updateBusesListInterval,
username,
]); ]);
useEffect(() => { useEffect(() => {
@ -212,8 +212,9 @@ const useDevices = () => {
const init = () => { const init = () => {
setState(prevState => { setState(prevState => {
const currentIndex = prevState.currentIndex || 1;
const isGreatherThanLinesLength = const isGreatherThanLinesLength =
(prevState.currentIndex || 1) + BUSES_BY_PAGE >= state.lines.length; currentIndex + BUSES_BY_PAGE >= state.lines.length;
return { return {
...prevState, ...prevState,
@ -224,7 +225,7 @@ const useDevices = () => {
}; };
}); });
setTimeout(init, changePageInterval); timeout = setTimeout(init, changePageInterval);
}; };
init(); init();
@ -232,7 +233,7 @@ const useDevices = () => {
return () => { return () => {
clearTimeout(timeout); clearTimeout(timeout);
}; };
}, [state.lines, state.status, changePageInterval]); }, [changePageInterval, state.lines.length]);
return {state}; return {state};
}; };

View File

@ -1,4 +1,4 @@
import {useCallback, useEffect, useState} from 'react'; import {useEffect, useState} from 'react';
import remoteConfig from '@react-native-firebase/remote-config'; import remoteConfig from '@react-native-firebase/remote-config';
import Status from '../../utils/Status'; import Status from '../../utils/Status';
@ -7,11 +7,6 @@ const ONE_HOUR = 3600000;
const useRemoteConfig = () => { const useRemoteConfig = () => {
const [status, setStatus] = useState(Status.LOADING); const [status, setStatus] = useState(Status.LOADING);
const getString = useCallback(
(key: string) => remoteConfig().getString(key),
[],
);
useEffect(() => { useEffect(() => {
const init = async () => { const init = async () => {
try { try {
@ -29,11 +24,9 @@ const useRemoteConfig = () => {
UPDATE_BUSES_LIST_INTERVAL: 0, // in miliseconds UPDATE_BUSES_LIST_INTERVAL: 0, // in miliseconds
}); });
const isActivated = await remoteConfig().fetchAndActivate(); await remoteConfig().fetchAndActivate();
if (isActivated) {
setStatus(Status.SUCCESS); setStatus(Status.SUCCESS);
}
} catch (error) { } catch (error) {
setStatus(Status.ERROR); setStatus(Status.ERROR);
} }
@ -42,7 +35,7 @@ const useRemoteConfig = () => {
init(); init();
}, []); }, []);
return {status, getString}; return {status};
}; };
export default useRemoteConfig; export default useRemoteConfig;

View File

@ -19,17 +19,33 @@ const ERROR_MESSAGE = 'Ha ocurrido un error';
const EMPTY_MESSAGE = 'No buses available'; const EMPTY_MESSAGE = 'No buses available';
const BusList = ({buses, status, style}: BusListProps) => { const BusList = ({buses, status, style}: BusListProps) => {
if (status === Status.LOADING) {
return (
<View style={style}>
<ActivityIndicator testID="LOADING" />
</View>
);
}
if (status === Status.ERROR) {
return ( return (
<View style={style}> <View style={style}>
{status === Status.LOADING && <ActivityIndicator testID="LOADING" />}
{status === Status.ERROR && (
<Text style={styles.text}>{ERROR_MESSAGE}</Text> <Text style={styles.text}>{ERROR_MESSAGE}</Text>
)} </View>
{status === Status.SUCCESS && buses.length === 0 ? ( );
}
if (status === Status.SUCCESS && buses.length === 0) {
return (
<View style={style}>
<Text style={styles.text}>{EMPTY_MESSAGE}</Text> <Text style={styles.text}>{EMPTY_MESSAGE}</Text>
) : ( </View>
);
}
return (
<View style={style}>
<Table data={buses} /> <Table data={buses} />
)}
</View> </View>
); );
}; };

View File

@ -36,8 +36,8 @@ const Header = ({title, subTitle, style, image}: HeaderProps) => {
}; };
const defaultProps = { const defaultProps = {
title: 'Sin información', title: '',
subTitle: 'Sin información', subTitle: '',
}; };
Header.defaultProps = defaultProps; Header.defaultProps = defaultProps;

View File

@ -5,288 +5,6 @@ exports[`BusList tests should render correctly with status LOADING 1`] = `
<ActivityIndicator <ActivityIndicator
testID="LOADING" testID="LOADING"
/> />
<View
style={
{
"backgroundColor": "white",
"flex": 1,
}
}
>
<View
style={
{
"flex": 1,
"flexDirection": "row",
}
}
>
<View
style={
{
"borderColor": "grey",
"borderWidth": 1,
"flex": 1,
"flexDirection": "column",
}
}
/>
<View
style={
{
"borderColor": "grey",
"borderWidth": 1,
"flex": 1,
"flexDirection": "column",
}
}
/>
<View
style={
{
"borderColor": "grey",
"borderWidth": 1,
"flex": 1,
"flexDirection": "column",
}
}
/>
</View>
<View
style={
{
"flex": 1,
"flexDirection": "row",
}
}
>
<View
style={
{
"borderColor": "grey",
"borderWidth": 1,
"flex": 1,
"flexDirection": "column",
}
}
/>
<View
style={
{
"borderColor": "grey",
"borderWidth": 1,
"flex": 1,
"flexDirection": "column",
}
}
/>
<View
style={
{
"borderColor": "grey",
"borderWidth": 1,
"flex": 1,
"flexDirection": "column",
}
}
/>
</View>
<View
style={
{
"flex": 1,
"flexDirection": "row",
}
}
>
<View
style={
{
"borderColor": "grey",
"borderWidth": 1,
"flex": 1,
"flexDirection": "column",
}
}
/>
<View
style={
{
"borderColor": "grey",
"borderWidth": 1,
"flex": 1,
"flexDirection": "column",
}
}
/>
<View
style={
{
"borderColor": "grey",
"borderWidth": 1,
"flex": 1,
"flexDirection": "column",
}
}
/>
</View>
<View
style={
{
"flex": 1,
"flexDirection": "row",
}
}
>
<View
style={
{
"borderColor": "grey",
"borderWidth": 1,
"flex": 1,
"flexDirection": "column",
}
}
/>
<View
style={
{
"borderColor": "grey",
"borderWidth": 1,
"flex": 1,
"flexDirection": "column",
}
}
/>
<View
style={
{
"borderColor": "grey",
"borderWidth": 1,
"flex": 1,
"flexDirection": "column",
}
}
/>
</View>
<View
style={
{
"flex": 1,
"flexDirection": "row",
}
}
>
<View
style={
{
"borderColor": "grey",
"borderWidth": 1,
"flex": 1,
"flexDirection": "column",
}
}
/>
<View
style={
{
"borderColor": "grey",
"borderWidth": 1,
"flex": 1,
"flexDirection": "column",
}
}
/>
<View
style={
{
"borderColor": "grey",
"borderWidth": 1,
"flex": 1,
"flexDirection": "column",
}
}
/>
</View>
<View
style={
{
"flex": 1,
"flexDirection": "row",
}
}
>
<View
style={
{
"borderColor": "grey",
"borderWidth": 1,
"flex": 1,
"flexDirection": "column",
}
}
/>
<View
style={
{
"borderColor": "grey",
"borderWidth": 1,
"flex": 1,
"flexDirection": "column",
}
}
/>
<View
style={
{
"borderColor": "grey",
"borderWidth": 1,
"flex": 1,
"flexDirection": "column",
}
}
/>
</View>
<View
style={
{
"flex": 1,
"flexDirection": "row",
}
}
>
<View
style={
{
"borderColor": "grey",
"borderWidth": 1,
"flex": 1,
"flexDirection": "column",
}
}
/>
<View
style={
{
"borderColor": "grey",
"borderWidth": 1,
"flex": 1,
"flexDirection": "column",
}
}
/>
<View
style={
{
"borderColor": "grey",
"borderWidth": 1,
"flex": 1,
"flexDirection": "column",
}
}
/>
</View>
</View>
</View> </View>
`; `;

View File

@ -1,7 +1,28 @@
import {SafeAreaView, StyleSheet} from 'react-native'; import {ActivityIndicator, SafeAreaView, StyleSheet, Text} from 'react-native';
import BusStopInfoScreen from './BusStopInfoScreen'; import BusStopInfoScreen from './BusStopInfoScreen';
import useRemoteConfig from '../../infraestructure/hooks/useRemoteConfig';
import Status from '../../utils/Status';
const ERROR_MESSAGE = 'Ha ocurrido un error';
const App = () => { const App = () => {
const {status} = useRemoteConfig();
if (status === Status.LOADING) {
return (
<SafeAreaView style={styles.centeredContainer}>
<ActivityIndicator testID="LOADING" />
</SafeAreaView>
);
}
if (status === Status.ERROR) {
return (
<SafeAreaView style={styles.centeredContainer}>
<Text>{ERROR_MESSAGE}</Text>
</SafeAreaView>
);
}
return ( return (
<SafeAreaView style={styles.container}> <SafeAreaView style={styles.container}>
<BusStopInfoScreen /> <BusStopInfoScreen />
@ -13,6 +34,11 @@ const styles = StyleSheet.create({
container: { container: {
flex: 1, flex: 1,
}, },
centeredContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
}); });
export default App; export default App;

View File

@ -1,43 +1,25 @@
import {ActivityIndicator, Image, StyleSheet, Text, View} from 'react-native'; import {Image, StyleSheet, View} from 'react-native';
import remoteConfig from '@react-native-firebase/remote-config';
import Container from '../components/Container'; import Container from '../components/Container';
import Header from '../components/Header'; import Header from '../components/Header';
import useDevices from '../../infraestructure/hooks/useDevices'; import useDevices from '../../infraestructure/hooks/useDevices';
import Banner from '../components/Banner'; import Banner from '../components/Banner';
import RemoteConfigKeys from '../../utils/RemoteConfigKeys'; import RemoteConfigKeys from '../../utils/RemoteConfigKeys';
import BusList from '../components/BusList'; import BusList from '../components/BusList';
import useRemoteConfig from '../../infraestructure/hooks/useRemoteConfig';
import Status from '../../utils/Status';
const BANNER_TEXT = 'Buses que se detienen en esta parada'; const BANNER_TEXT = 'Buses que se detienen en esta parada';
const ERROR_MESSAGE = 'Ha ocurrido un error';
const BusStopInfoScreen = () => { const BusStopInfoScreen = () => {
const { const {
state: {status, displayedLines, stopName}, state: {status, displayedLines, stopName},
} = useDevices(); } = useDevices();
const {status: remoteConfigStatus, getString} = useRemoteConfig();
const image = getString(RemoteConfigKeys.HEADER_IMAGE_URL); const image = remoteConfig().getString(RemoteConfigKeys.HEADER_IMAGE_URL);
const [first, second] = stopName.split('-'); const [first, second] = stopName.split('-');
const title = first ? first.trim() : ''; const title = first ? first.trim() : '';
const subTitle = second ? second.trim() : ''; const subTitle = second ? second.trim() : '';
if (remoteConfigStatus === Status.LOADING) {
return (
<Container style={styles.containerCenter}>
<ActivityIndicator />
</Container>
);
}
if (remoteConfigStatus === Status.ERROR) {
return (
<Container style={styles.containerCenter}>
<Text>{ERROR_MESSAGE}</Text>
</Container>
);
}
return ( return (
<Container style={styles.container}> <Container style={styles.container}>
<Header <Header
@ -89,7 +71,7 @@ const styles = StyleSheet.create({
flex: 1, flex: 1,
width: '100%', width: '100%',
height: '100%', height: '100%',
resizeMode: 'contain', resizeMode: 'stretch',
}, },
}); });

View File

@ -2,6 +2,7 @@ import {render} from '@testing-library/react-native';
import BusStopInfoScreen from '../BusStopInfoScreen'; import BusStopInfoScreen from '../BusStopInfoScreen';
import * as useDevices from '../../../infraestructure/hooks/useDevices'; import * as useDevices from '../../../infraestructure/hooks/useDevices';
import * as useRemoteConfig from '../../../infraestructure/hooks/useRemoteConfig'; import * as useRemoteConfig from '../../../infraestructure/hooks/useRemoteConfig';
import {getString} from '../../../../__mocks__/@react-native-firebase/remote-config';
const mockState: useDevices.State = { const mockState: useDevices.State = {
status: 1, status: 1,
@ -576,6 +577,7 @@ describe('BusStopInfoScreen tests', () => {
jest.spyOn(useDevices, 'default').mockReturnValue({ jest.spyOn(useDevices, 'default').mockReturnValue({
state: mockState, state: mockState,
}); });
getString.mockReturnValue('https://www.google.com');
}); });
it('should be defined', () => { it('should be defined', () => {
@ -585,7 +587,6 @@ describe('BusStopInfoScreen tests', () => {
it('should render the screen with status loading', () => { it('should render the screen with status loading', () => {
jest.spyOn(useRemoteConfig, 'default').mockReturnValue({ jest.spyOn(useRemoteConfig, 'default').mockReturnValue({
status: 0, status: 0,
getString: () => 'https://www.google.com',
}); });
const {toJSON} = render(<BusStopInfoScreen />); const {toJSON} = render(<BusStopInfoScreen />);
@ -595,7 +596,6 @@ describe('BusStopInfoScreen tests', () => {
it('should render the screen with status error', () => { it('should render the screen with status error', () => {
jest.spyOn(useRemoteConfig, 'default').mockReturnValue({ jest.spyOn(useRemoteConfig, 'default').mockReturnValue({
status: 2, status: 2,
getString: () => 'https://www.google.com',
}); });
const {toJSON} = render(<BusStopInfoScreen />); const {toJSON} = render(<BusStopInfoScreen />);
@ -606,7 +606,6 @@ describe('BusStopInfoScreen tests', () => {
it('should render the screen with status success', () => { it('should render the screen with status success', () => {
jest.spyOn(useRemoteConfig, 'default').mockReturnValue({ jest.spyOn(useRemoteConfig, 'default').mockReturnValue({
status: 1, status: 1,
getString: () => 'https://www.google.com',
}); });
const {toJSON} = render(<BusStopInfoScreen />); const {toJSON} = render(<BusStopInfoScreen />);