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 {getNumber} from '../../../../__mocks__/@react-native-firebase/remote-config';
jest.useFakeTimers();
describe('useDevices tests', () => {
beforeEach(() => {
jest.clearAllMocks();
jest.useFakeTimers();
});
afterEach(() => {
jest.useRealTimers();
});
beforeAll(() => {
@ -24,7 +21,7 @@ describe('useDevices tests', () => {
{
lineNumber: '803010',
description: 'Tucapel',
locomotionType: 1,
locomotionType: '1',
backgroundColor: 'Hexadecimal',
letterColor: 'Hexadecimal',
lineMessage: '',
@ -52,7 +49,7 @@ describe('useDevices tests', () => {
{
lineNumber: '5487',
description: 'Centauro',
locomotionType: 1,
locomotionType: '1',
backgroundColor: 'Hexadecimal',
letterColor: 'Hexadecimal',
lineMessage: 'Sin info. GPS, la informacion es estimada',
@ -84,8 +81,6 @@ describe('useDevices tests', () => {
stopNumber: '37477',
stopName: "O'Higgins - entre Angol y Salas",
});
getNumber.mockReturnValue(20000).mockReturnValue(60000);
});
it('should be defined', () => {
@ -139,7 +134,7 @@ describe('useDevices tests', () => {
letterColor: 'Hexadecimal',
lineMessage: '',
lineNumber: '803010',
locomotionType: 1,
locomotionType: '1',
},
{
arrivals: [
@ -167,7 +162,7 @@ describe('useDevices tests', () => {
letterColor: 'Hexadecimal',
lineMessage: 'Sin info. GPS, la informacion es estimada',
lineNumber: '5487',
locomotionType: 1,
locomotionType: '1',
},
]);
});
@ -176,6 +171,7 @@ describe('useDevices tests', () => {
});
it('should refresh devices list after 20000 miliseconds', async () => {
getNumber.mockReturnValue(20000);
const mockDate = new Date('2023-01-01T00:01:00Z');
const spy = jest.spyOn(global, 'Date').mockImplementation(() => mockDate);
jest
@ -188,7 +184,9 @@ describe('useDevices tests', () => {
expect(result.current.state.currentIndex).toBe(0);
});
act(() => {
jest.advanceTimersByTime(20000);
});
await waitFor(() => {
expect(result.current.state.currentIndex).toBe(22);
@ -198,6 +196,7 @@ describe('useDevices tests', () => {
});
it('should refresh devices list after 60000 miliseconds', async () => {
getNumber.mockReturnValue(60000);
const mockDate = new Date('2023-01-01T00:01:00Z');
const spy = jest.spyOn(global, 'Date').mockImplementation(() => mockDate);
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: '',
displayedLines: [],
lines: [],
stopName: 'Sin información - Sin información',
stopName: '',
});
const baseURL = remoteConfig().getString(RemoteConfigKeys.BASE_URL);
@ -180,7 +180,7 @@ const useDevices = () => {
status: Status.SUCCESS,
},
}));
} catch (error) {
} catch (error: unknown) {
setState((prevState: State) => ({
...prevState,
status: Status.ERROR,
@ -199,8 +199,8 @@ const useDevices = () => {
authRepository,
devicesRepository,
password,
username,
updateBusesListInterval,
username,
]);
useEffect(() => {
@ -212,8 +212,9 @@ const useDevices = () => {
const init = () => {
setState(prevState => {
const currentIndex = prevState.currentIndex || 1;
const isGreatherThanLinesLength =
(prevState.currentIndex || 1) + BUSES_BY_PAGE >= state.lines.length;
currentIndex + BUSES_BY_PAGE >= state.lines.length;
return {
...prevState,
@ -224,7 +225,7 @@ const useDevices = () => {
};
});
setTimeout(init, changePageInterval);
timeout = setTimeout(init, changePageInterval);
};
init();
@ -232,7 +233,7 @@ const useDevices = () => {
return () => {
clearTimeout(timeout);
};
}, [state.lines, state.status, changePageInterval]);
}, [changePageInterval, state.lines.length]);
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 Status from '../../utils/Status';
@ -7,11 +7,6 @@ const ONE_HOUR = 3600000;
const useRemoteConfig = () => {
const [status, setStatus] = useState(Status.LOADING);
const getString = useCallback(
(key: string) => remoteConfig().getString(key),
[],
);
useEffect(() => {
const init = async () => {
try {
@ -29,11 +24,9 @@ const useRemoteConfig = () => {
UPDATE_BUSES_LIST_INTERVAL: 0, // in miliseconds
});
const isActivated = await remoteConfig().fetchAndActivate();
await remoteConfig().fetchAndActivate();
if (isActivated) {
setStatus(Status.SUCCESS);
}
} catch (error) {
setStatus(Status.ERROR);
}
@ -42,7 +35,7 @@ const useRemoteConfig = () => {
init();
}, []);
return {status, getString};
return {status};
};
export default useRemoteConfig;

View File

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

View File

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

View File

@ -5,288 +5,6 @@ exports[`BusList tests should render correctly with status LOADING 1`] = `
<ActivityIndicator
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 File

@ -1,7 +1,28 @@
import {SafeAreaView, StyleSheet} from 'react-native';
import {ActivityIndicator, SafeAreaView, StyleSheet, Text} from 'react-native';
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 {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 (
<SafeAreaView style={styles.container}>
<BusStopInfoScreen />
@ -13,6 +34,11 @@ const styles = StyleSheet.create({
container: {
flex: 1,
},
centeredContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
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 Header from '../components/Header';
import useDevices from '../../infraestructure/hooks/useDevices';
import Banner from '../components/Banner';
import RemoteConfigKeys from '../../utils/RemoteConfigKeys';
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 ERROR_MESSAGE = 'Ha ocurrido un error';
const BusStopInfoScreen = () => {
const {
state: {status, displayedLines, stopName},
} = 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 title = first ? first.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 (
<Container style={styles.container}>
<Header
@ -89,7 +71,7 @@ const styles = StyleSheet.create({
flex: 1,
width: '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 * as useDevices from '../../../infraestructure/hooks/useDevices';
import * as useRemoteConfig from '../../../infraestructure/hooks/useRemoteConfig';
import {getString} from '../../../../__mocks__/@react-native-firebase/remote-config';
const mockState: useDevices.State = {
status: 1,
@ -576,6 +577,7 @@ describe('BusStopInfoScreen tests', () => {
jest.spyOn(useDevices, 'default').mockReturnValue({
state: mockState,
});
getString.mockReturnValue('https://www.google.com');
});
it('should be defined', () => {
@ -585,7 +587,6 @@ describe('BusStopInfoScreen tests', () => {
it('should render the screen with status loading', () => {
jest.spyOn(useRemoteConfig, 'default').mockReturnValue({
status: 0,
getString: () => 'https://www.google.com',
});
const {toJSON} = render(<BusStopInfoScreen />);
@ -595,7 +596,6 @@ describe('BusStopInfoScreen tests', () => {
it('should render the screen with status error', () => {
jest.spyOn(useRemoteConfig, 'default').mockReturnValue({
status: 2,
getString: () => 'https://www.google.com',
});
const {toJSON} = render(<BusStopInfoScreen />);
@ -606,7 +606,6 @@ describe('BusStopInfoScreen tests', () => {
it('should render the screen with status success', () => {
jest.spyOn(useRemoteConfig, 'default').mockReturnValue({
status: 1,
getString: () => 'https://www.google.com',
});
const {toJSON} = render(<BusStopInfoScreen />);