[NEW] Create useEndpointData for call endpoints using hooks (#4342)

* create basic useEndpointData for call endpoints using hooks

* remove cache

* create base for useEndpointData test

* create basic useEndpointData for call endpoints using hooks

* remove cache

* create base for useEndpointData test

* fix preset

* update tests

* change order

* create ErrorResult and add error to return

* update tests
This commit is contained in:
Gleidson Daniel Silva 2022-08-09 09:28:14 -03:00 committed by GitHub
parent 2b08b683d7
commit 89820b2bec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 168 additions and 1 deletions

View File

@ -91,3 +91,5 @@ export type ResultFor<TMethod extends Method, TPathPattern extends PathPattern>
| SuccessResult<OperationResult<TMethod, TPathPattern>>
| FailureResult<unknown, unknown, unknown, unknown>
| UnauthorizedResult<unknown>;
export type ErrorResult = FailureResult<unknown, unknown, unknown, unknown>;

View File

@ -0,0 +1,87 @@
import { renderHook } from '@testing-library/react-hooks';
import { render, waitFor } from '@testing-library/react-native';
import React from 'react';
import { View, Text } from 'react-native';
import { useEndpointData } from './useEndpointData';
import sdk from '../services/sdk';
const url = 'chat.getMessage';
export const message = {
_id: '9tYkmJ67wMwmvQouD',
t: 'uj',
rid: 'GENERAL',
ts: '2022-07-05T19:34:30.146Z',
msg: 'xdani',
u: {
_id: 'ombax8oEZnE7N3Mtt',
username: 'xdani',
name: 'xdani'
},
groupable: false,
_updatedAt: '2022-07-05T19:34:30.146Z'
};
// mock sdk
jest.mock('../services/sdk', () => ({
get: jest.fn(() => new Promise(resolve => setTimeout(() => resolve({ success: true, message }), 1000)))
}));
function Render() {
const { loading } = useEndpointData(url, { msgId: message._id });
if (loading) {
return (
<View>
<Text testID='loading'>loading</Text>
</View>
);
}
return (
<View>
<Text testID='load complete'>load complete</Text>
</View>
);
}
describe('useFetch', () => {
it('should return data after fetch', async () => {
const { result, waitForNextUpdate } = renderHook(() => useEndpointData(url, { msgId: message._id }));
expect(result.current.loading).toEqual(true);
expect(result.current.result).toEqual(undefined);
await waitForNextUpdate();
expect(result.current.loading).toEqual(false);
expect(result.current.result).toEqual({ success: true, message });
});
it('should component load correctly', async () => {
const renderComponent = render(<Render />);
const loading = await renderComponent.findByTestId('loading');
expect(loading.props.children).toBe('loading');
await waitFor(
() => {
expect(renderComponent.getByText('load complete')).toBeTruthy();
},
{ timeout: 2000 }
);
});
it('should return error after fetch', async () => {
const spy = jest
.spyOn(sdk, 'get')
.mockImplementation(
jest.fn(() => new Promise(resolve => setTimeout(() => resolve({ success: false, error: null }), 1000)))
);
const { result, waitForNextUpdate } = renderHook(() => useEndpointData(url, { msgId: message._id }));
expect(result.current.loading).toEqual(true);
expect(result.current.result).toEqual(undefined);
expect(result.current.error).toEqual(undefined);
await waitForNextUpdate();
expect(result.current.loading).toEqual(false);
expect(result.current.result).toEqual(undefined);
expect(result.current.error).toEqual({ success: false, error: null });
spy.mockRestore();
});
});

View File

@ -0,0 +1,62 @@
import isEqual from 'lodash/isEqual';
import { useCallback, useEffect, useRef, useState } from 'react';
import { ErrorResult, MatchPathPattern, OperationParams, PathFor, ResultFor, Serialized } from '../../definitions/rest/helpers';
import sdk from '../services/sdk';
export const useEndpointData = <TPath extends PathFor<'GET'>>(
endpoint: TPath,
params: void extends OperationParams<'GET', MatchPathPattern<TPath>>
? void
: Serialized<OperationParams<'GET', MatchPathPattern<TPath>>> = undefined as void extends OperationParams<
'GET',
MatchPathPattern<TPath>
>
? void
: Serialized<OperationParams<'GET', MatchPathPattern<TPath>>>
): {
result: Serialized<ResultFor<'GET', MatchPathPattern<TPath>>> | undefined;
loading: boolean;
reload: Function;
error: ErrorResult | undefined;
} => {
const [loading, setLoading] = useState(true);
const [result, setResult] = useState<Serialized<ResultFor<'GET', MatchPathPattern<TPath>>> | undefined>();
const [error, setError] = useState<ErrorResult | undefined>();
const paramsRef = useRef(params);
if (!isEqual(paramsRef.current, params)) {
paramsRef.current = params;
}
const fetchData = useCallback(() => {
if (!endpoint) return;
setLoading(true);
sdk
.get(endpoint, params)
.then(e => {
setLoading(false);
if (e.success) {
setResult(e);
} else {
setError(e as ErrorResult);
}
})
.catch((e: ErrorResult) => {
setLoading(false);
setError(e);
});
}, [paramsRef.current]);
useEffect(() => {
fetchData();
}, [fetchData]);
return {
result,
loading,
reload: fetchData,
error
};
};

View File

@ -1,4 +1,4 @@
const jestRN = require('react-native/jest-preset');
const jestRN = require('@testing-library/react-native/jest-preset/index.js');
const jestExpo = require('jest-expo/jest-preset.js');
module.exports = {

View File

@ -154,6 +154,7 @@
"@storybook/addon-storyshots": "5.3.21",
"@storybook/react-native": "5.3.25",
"@testing-library/jest-native": "^4.0.4",
"@testing-library/react-hooks": "^8.0.1",
"@testing-library/react-native": "^9.0.0",
"@types/bytebuffer": "^5.0.43",
"@types/ejson": "^2.1.3",

View File

@ -5314,6 +5314,14 @@
ramda "^0.26.1"
redent "^2.0.0"
"@testing-library/react-hooks@^8.0.1":
version "8.0.1"
resolved "https://registry.yarnpkg.com/@testing-library/react-hooks/-/react-hooks-8.0.1.tgz#0924bbd5b55e0c0c0502d1754657ada66947ca12"
integrity sha512-Aqhl2IVmLt8IovEVarNDFuJDVWVvhnr9/GCU6UUnrYXwgDFF9h2L2o2P9KBni1AST5sT6riAyoukFLyjQUgD/g==
dependencies:
"@babel/runtime" "^7.12.5"
react-error-boundary "^3.1.0"
"@testing-library/react-native@^9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@testing-library/react-native/-/react-native-9.0.0.tgz#e9c63411e93d2e8e70d744b12aeb78c58025c5fc"
@ -16424,6 +16432,13 @@ react-draggable@^4.0.3:
classnames "^2.2.5"
prop-types "^15.6.0"
react-error-boundary@^3.1.0:
version "3.1.4"
resolved "https://registry.yarnpkg.com/react-error-boundary/-/react-error-boundary-3.1.4.tgz#255db92b23197108757a888b01e5b729919abde0"
integrity sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==
dependencies:
"@babel/runtime" "^7.12.5"
react-error-overlay@^6.0.3:
version "6.0.9"
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.9.tgz#3c743010c9359608c375ecd6bc76f35d93995b0a"