Testing a react-query hook that uploads a binary file - javascript

There is a unit test that's failing on the following react-query hook.
The hook works fine otherwise, it uploads a binary file to an endpoint.
Here is the hook:
import { useMutation, useQueryClient } from 'react-query';
import { IUploadFieldRequest } from '../constants/types';
import { licenseServiceAPI } from '../services';
export const URI = () => '/licenses/upload';
export const useUploadLicense = () => {
const queryClient = useQueryClient();
const [apiService] = licenseServiceAPI<IUploadFieldRequest>({
headers: {
'Content-Type': 'multipart/form-data'
}
});
const {
data: response,
mutateAsync,
isLoading,
isSuccess
} = useMutation(
(formData: IUploadFieldRequest) => {
const form = new FormData();
Array.from(formData.files)?.forEach((file) => {
form.append('license-file', file);
});
return apiService.post(URI(), form);
},
{
onError: () => {
return queryClient.invalidateQueries('licenseUpload');
}
}
);
return {
data: response?.data,
uploadFiles: mutateAsync,
isLoading,
isSuccess
};
};
export default useUploadLicense;
And this is the test for it:
import { act, renderHook } from '#testing-library/react-hooks';
import { queryClientInstance } from '#oam/shared/test-utils';
import useUploadLicense from './use-upload-license';
const file = new File(['file'], 'license-file.json', { type: 'multipart/form-data' });
it('upload files', async () => {
const { result, waitFor } = renderHook(() => useUploadLicense(), {
wrapper: queryClientInstance
});
act(() => {
result.current.uploadFiles({ files: [file] });
});
await waitFor(() => expect(result.current.isLoading).toBeTruthy());
await waitFor(() => expect(result.current.isSuccess).toBeTruthy());
});
The error says it has Timed out in waitFor after 1000ms. and that's a Network error
Any ideas how to fix this?
Also, it could be useful to know the interface:
type IUploadFieldRequest = {
files: File[];
};
And the service:
import { AxiosRequestConfig } from 'axios';
import { requestConfig } from '#oam/shared/services';
import init, { Response } from '#oam/shared/utils/request-wrapper';
export const BASE_PATH =
process.env.NODE_ENV === 'development'
? '/myurl/v1'
: 'http://localhost:7999/v1';
export const licenseServiceAPI = <TRequest, TResponse = TRequest>(
config?: AxiosRequestConfig
): [Response<TRequest, TResponse>, AxiosRequestConfig] => {
const newConfig = requestConfig({
baseURL: BASE_PATH,
headers: {
Accept: 'application/json',
...config?.headers
},
responseType: config?.responseType ? config?.responseType : undefined
});
return [init(newConfig), newConfig];
};
export default licenseServiceAPI;

Related

About getting data from RTK Query to Firebase Realtime Database

I am currently trying to get data from a Firebase Realtime Database using RTK Query. However, the code here is giving me an error because the value returned in return is not correct. If anyone has knowledge of this, I would appreciate it if you could correct the code to the correct one.
import { createApi, fakeBaseQuery } from "#reduxjs/toolkit/query/react";
import { onValue, ref } from "firebase/database";
import { db } from "libs/firebase";
export const userApi = createApi({
baseQuery: fakeBaseQuery(),
endpoints: builder => ({
getUser: builder.query({
queryFn(uid) {
try {
onValue(ref(db, `users/user${uid}`), snapshot => {
return { data: snapshot.val() };
});
} catch (e) {
return { error: e };
}
},
}),
}),
});
export const { useGetUserQuery } = userApi;
import { configureStore } from "#reduxjs/toolkit";
import { userApi } from "./apiSlice";
export const store = configureStore({
reducer: {
[userApi.reducerPath]: userApi.reducer,
},
middleware: getDefaultMiddleware =>
getDefaultMiddleware().concat(userApi.middleware),
});
const { data: user, error, isLoading, isSuccess } = useGetUserQuery(1);
console.log(user);
error message
Try something along the lines of
export const userApi = createApi({
baseQuery: fakeBaseQuery(),
endpoints: (builder) => ({
getUser: builder.query({
async queryFn(uid) {
try {
const data = await new Promise((resolve, reject) =>
onValue(
ref(db, `users/user${uid}`),
(snapshot) => resolve(snapshot.toJSON()),
reject
)
);
return { data };
} catch (e) {
return { error: e };
}
},
}),
}),
});

Apollo client getServerSideProps

I'm using Apollo Client for my nextJs project. I'm trying to create a storefront Shopify app but i probably not understand how apollo query works.
This is the error:
Server Error
Error: Variable $handle of type String! was provided invalid value.
[product].jsx
import { useQuery } from '#apollo/client'
import GET_PRODUCT_BY_HANDLE from '../../lib/graphql/queries/getProductByHandle'
// import GET_PRODUCT from '../../lib/graphql/queries/getProduct'
import { initApollo } from '../../lib/apollo'
// const VARIABLE = 'struccante-oleoso'
export default function Product({ queryByHandle }) {
const { data, loading, error } = useQuery(GET_PRODUCT_BY_HANDLE, {
variables: { handle: queryByHandle },
})
if (loading) return <h1>Loading...</h1>
if (error || !data) return <h2>Error</h2>
if (data.productByHandle.length === 0) return <h2>404 | Not Found</h2>
return (
<div>
<h1>Home</h1>
<h2>{data.productByHandle.handle}</h2>
</div>
)
}
export const getServerSideProps = async ({ query }) => {
const queryByHandle = query.productByHandle // if i replace with "struccante-oleoso" works well
const apolloClient = initApollo()
await apolloClient.query({
query: GET_PRODUCT_BY_HANDLE,
variables: { handle: queryByHandle },
})
return {
props: {
initialApolloState: apolloClient.cache.extract(),
queryByHandle,
},
}
}
getProductByHandle.jsx
import { gql } from '#apollo/client'
const GET_PRODUCT_BY_HANDLE = gql`
query getProductByHandle($handle: String!) {
productByHandle(handle: $handle) {
id
handle
description
images(first: 5) {
edges {
node {
url
altText
}
}
}
options {
name
values
id
}
variants(first: 25) {
edges {
node {
selectedOptions {
name
value
}
image {
url
altText
}
title
id
price {
amount
}
}
}
}
}
}
`
export default GET_PRODUCT_BY_HANDLE
apollo.jsx
import { ApolloClient, HttpLink, InMemoryCache } from '#apollo/client'
import { useMemo } from 'react'
const domain = process.env.NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN
const storefrontAccessToken =
process.env.NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN
let uri = `https://${domain}/api/2022-10/graphql.json`
let apolloClient
function createApolloClient() {
return new ApolloClient({
cache: new InMemoryCache({}),
ssrMode: typeof window === 'undefined',
link: new HttpLink({
uri: `${uri}`,
headers: {
'X-Shopify-Storefront-Access-Token': storefrontAccessToken,
'Content-Type': 'application/json',
Accept: 'application/json',
},
}),
})
}
export function initApollo(initialState = null) {
const client = apolloClient || createApolloClient()
if (initialState) {
client.cache.restore({
...client.extract(),
...initialState,
})
}
if (typeof window === 'undefined') {
return client
}
if (!apolloClient) {
apolloClient = client
}
return client
}
export function useApollo(initialState) {
return useMemo(() => initApollo(initialState), [initialState])
}
I expect to retrieve handle as a string from gql query

Getting error when try to mock Auth.currentSession(). What I'm doing wrong?

Place where I'm using current session:
axios.interceptors.request.use(async (config) => {
await cognitoConfig();
const session = await Auth.currentSession();
const token = session.idToken.jwtToken;
if (config?.headers?.common) {
config.headers.common.Authorization = `Bearer ${token}`;
}
return config;
});
In my test I'm trying to mock Auth.currentSession() but always getting the same error:
Cannot read properties of undefined (reading 'idToken')
TypeError: Cannot read properties of undefined (reading 'idToken')
My code below:
jest.spyOn(Auth, 'currentSession').mockImplementation(() => ({
getIdToken: () => ({
getJwtToken: () => 'mock'
})
}));
What I'm doing wrong?
All my test:
import { screen } from '#testing-library/react';
import { Auth } from 'aws-amplify';
import { render } from '../../../../config/test/root-render';
import PageProvider from '../PageContext';
import Status from './Status';
const path = '/path';
const pageName = 'Page';
jest.mock('../../../../root/router', () => {
return {
generateUrlByKey: () => {
return path;
},
useCurrentRoute: () => ({
parent: {},
params: {},
name: pageName
})
};
});
jest.spyOn(Auth, 'currentSession').mockImplementation(() => ({
getIdToken: () => ({
getJwtToken: () => 'mock'
})
}));
describe('<Status />', () => {
it('Should verify that all general elements of component exist', async () => {
render(
<PageProvider>
<Status status="OPEN" isAdmin={true} />
</PageProvider>
);
const statusTitle = await screen.findByText('Status:');
const currentStatus = await screen.findByText('OPEN');
const editButton = await screen.findByText('edit');
expect(statusTitle).not.toBeUndefined();
expect(currentStatus).not.toBeUndefined();
expect(editButton).not.toBeUndefined();
});
});

React Native - {"name":"Invariant Violation","framesToPop":1}

I'm trying to implement meilisearch api in React native and it is working fine with my simulator and after I publish the app some of the users cannot see the data returning from meilisearch, the error is
{"name":"Invariant Violation","framesToPop":1}
This is my code
Meilisearch.js
import axios from 'axios';
import { meilisearchConfig } from '../Config';
const MeilisearchApi = async (payload, success, failed) => {
try {
const response = await axios({
method: 'post',
url: `${meilisearchConfig?.host}indexes/activities/search`,
data: payload,
headers: {
'X-Meili-API-Key': meilisearchConfig?.apiKey,
},
});
success?.(response?.data);
} catch (err) {
failed?.(err);
}
};
export default MeilisearchApi;
This is the normalizer for returning data
import moment from 'moment';
import { IActivity, IByDateGroupFilter } from 'reducers/types';
export const activityNormalizer = (state, { hits, offset }) => {
const {
melisearchActivityData: { byDate, dates, all },
} = state;
const isRefreshing = offset === 0;
const newAll = isRefreshing ? hits : [...all, ...hits];
const datesNew: string[] = isRefreshing ? [] : dates;
const byDateNew: any = isRefreshing ? {} : byDate;
const byDateGroup: IByDateGroupFilter[] = [];
hits.forEach((activity: IActivity) => {
const date = getFormattedDate(activity.created_at);
if (byDateNew[date]) byDateNew[date].push({ ...activity });
else {
byDateNew[date] = [{ ...activity }];
datesNew.push(date);
}
});
Object.keys(byDateNew).forEach((key) => {
byDateGroup.push({
title: key,
data: byDateNew[key],
});
});
return {
dates: datesNew,
byDate: byDateNew,
byDateGroup,
all: newAll,
};
};
This is how i call my Meilisearch API method
MeilisearchApi(
{
q: search,
filters: filters,
offset: newOffset,
limit: PAGE_SIZE,
},
({ hits }: { hits: any[] }) => {
setDataLoaded(true);
setMelisearchActivitiesToRedux({ hits, offset: newOffset });
if (newOffset === 0) {
sectionList?.current?.scrollToLocation({
itemIndex: 1,
});
}
},
(err: any) => {
setDataLoaded(true);
log(err)
},
);
No Idea how this error happens, when users kill the app and logging again this works fine

How do convert a datauri element into a file I can upload via react?

I am sending a file to an api endpoint via react webapp and for now as a mock up have been using file upload. I have since added camera capabilities as I plan for users to be able to take a picture and upload that picture. I'm using the react-html5-camera-photo which captures a datauri element. How do I convert this to a file to upload to the endpoint? Or am I approaching this incorrectly?
File Upload:
handleUploadImage=()=> {
console.log(this.state.selectedFile)
const data = new FormData();
data.append('file', this.state.selectedFile);
data.append('filename', this.state.selectedFile.name);
fetch('http://localhost:5000/upload', {
method: 'POST',
body: data,
})
.then((response) => {
response.json()
.then((response) => {
//this.setState({ imageURL: `http://localhost:5000/${body.file}` });
console.log(response.text)
this.setState({ text: response.text})
this.getPlate()
})
});
}
Camera Code:
startCamera = () => {
console.log('starting camera')
if(!this.state.cameraLoad) {
this.setState({ cameraLoad: true, camIconData: collapse}) ;
} else {
this.setState({ cameraLoad: false, camIconData: expand}) ;
}
}
onTakePhoto = (dataUri) => {
// Do stuff with the dataUri photo...
console.log('photo taken');
this.setState({ camColor: 'green', dataUri : dataUri })
}
// will upload photo after user confirms to upload
uploadPhoto = e => {
console.log('uploading photo')
console.log(this.state.dataUri)
this.setState({ camColor: 'green', dataUri: null, cameraLoad: false });
};
The dataUri element looks something like:
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABQAAAALQCAYAAADPfd1WAAAgAElEQVR4XlS9h5Nk63HdmeVdV5vpmXnzDAEQBAiKwlJmIzZiQ/u/72qDWongkpREQFjRgHh2/LQp7zd+52TeavSLfjPTXXXr3s/kl3n
Here is the complete example. Please write a CameraComponent as below where you will find submitPhoto() function and this function is responsible for uploading image to the api.
import React, {useState} from 'react';
import Camera from 'react-html5-camera-photo';
import 'react-html5-camera-photo/build/css/index.css';
import ImagePreview from "./ImagePreview";
import axios from 'axios';
function CameraComponent(props) {
const [dataUri, setDataUri] = useState('');
const isFullscreen = false;
const cameraRef = React.useRef();
function handleTakePhoto(dataUri) {
console.log('takePhoto');
}
function handleTakePhotoAnimationDone(dataUri) {
console.log(dataUri, 'takePhoto');
setDataUri(dataUri);
}
function handleCameraError(error) {
console.log('handleCameraError', error);
}
function handleCameraStart(stream) {
console.log('handleCameraStart');
}
function handleCameraStop() {
console.log('handleCameraStop');
}
function submitPhoto() {
const url = "http://localhost:3000/api/img";
const request = axios.post(url, {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify({
img: dataUri
})
}).catch(error => {
console.warn(error);
});
}
return (
<div>
<button onClick={submitPhoto}>Upload</button>
{(dataUri)
? <ImagePreview dataUri={dataUri}
isFullscreen={isFullscreen}
/> :
<Camera ref={cameraRef}
onTakePhoto={(dataUri) => {
handleTakePhoto(dataUri);
}}
onTakePhotoAnimationDone={(dataUri) => {
handleTakePhotoAnimationDone(dataUri);
}}
onCameraError={(error) => {
handleCameraError(error);
}}
onCameraStart={(stream) => {
handleCameraStart(stream);
}}
onCameraStop={() => {
handleCameraStop();
}}
/>
}
</div>
);
}
export default CameraComponent;
Here is the another component ImagePreview which will preview the image
import React from 'react';
import PropTypes from 'prop-types';
import './styles/imagePreview.css';
export const ImagePreview = ({ dataUri, isFullscreen }) => {
let classNameFullscreen = isFullscreen ? 'demo-image-preview-fullscreen' : '';
return (
<div className={'demo-image-preview ' + classNameFullscreen}>
<img src={dataUri} />
</div>
);
};
ImagePreview.propTypes = {
dataUri: PropTypes.string,
isFullscreen: PropTypes.bool
};
export default ImagePreview;
use this :
function dataURLtoFile(dataurl, filename) {
var arr = dataurl.split(","),
mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new File([u8arr], filename, { type: mime });
}
var convertedFile = dataURLtoFile(dataUri, `${Math.random(10)}.jpg`);

Categories

Resources