react native unit test: received value must be an HTMLElement or an SVGElement - react-native

I am new to react native (expo) and to unit testing. I have been trying to test the style of a custom text component but many of the jest matchers aren't working.
This is the custom text component:
import { styled } from 'nativewind';
import React from 'react';
import type { TextProps } from 'react-native';
import { StyleSheet, Text as NNText } from 'react-native';
const SText = styled(NNText);
export interface CustomTextProps extends TextProps {
variant?: keyof typeof textVariants;
className?: string;
}
export const textVariants = {
//.....
};
export const Text = ({
variant = 'md',
className = '',
style,
children,
...props
}: CustomTextProps) => {
return (
<SText
className={`
${textVariants[variant]}
${className}
`}
style={StyleSheet.flatten([
{ writingDirection: isRTL ? 'rtl' : 'ltr' },
style,
])}
{...props}
>
{children}
</SText>
);
};
And this is the testing code:
import '#testing-library/jest-native/extend-expect';
import { render, screen } from '#testing-library/react-native';
import { CustomTextProps, Text } from '../text';
describe('text component', () => {
const renderComponent = (props: CustomTextProps) =>
render(<Text {...props} />);
it('should accept variant and className props', async () => {
renderComponent({
variant: 'xs',
className: 'bg-primary-50',
children: 'tested text',
});
const text = screen.getByText('tested text');
expect(text).toBeTruthy();
expect(text).toHaveStyle({ fontSize: 16 }); //error is here
});
});
This is the error I'm getting
expect(received).toHaveStyle()
received value must be an HTMLElement or an SVGElement.
Received has type: object
Received has value: {"_fiber": {"_debugHookTypes": null, "_debugNeedsRemount": false, "_debugOwner": [FiberNode], "_debugSource": null, "actualDuration": 0, "actualStartTime": -1, "alternate": null, "child": [FiberNode], "childLanes": 0, "deletions": null, "dependencies": null, "elementType": [Function Component], "flags": 1, "index": 0, "key": null, "lanes": 0, "memoizedProps": [Object], "memoizedState": null, "mode": 0, "pendingProps": [Object], "ref": null, "return": [FiberNode], "selfBaseDuration": 0, "sibling": null, "stateNode": [Component], "subtreeFlags": 0, "tag": 1, "treeBaseDuration": 0, "type": [Function Component], "updateQueue": [Object]}}
18 | expect(text).toBeTruthy();
19 |
> 20 | expect(text).toHaveStyle({ fontSize: 16 });
| ^
21 | });
22 | });
23 |
at __EXTERNAL_MATCHER_TRAP__ (node_modules/expect/build/index.js:342:30)
at Object.toHaveStyle (node_modules/expect/build/index.js:343:15)
at Object.<anonymous> (src/ui/core/__test__/text.test.tsx:20:18)
at asyncGeneratorStep (node_modules/#babel/runtime/helpers/asyncToGenerator.js:3:24)
at _next (node_modules/#babel/runtime/helpers/asyncToGenerator.js:22:9)
at node_modules/#babel/runtime/helpers/asyncToGenerator.js:27:7
at tryCallTwo (node_modules/promise/lib/core.js:45:5)
at doResolve (node_modules/promise/lib/core.js:200:13)
at new Promise (node_modules/promise/lib/core.js:66:3)
I have tried to work with jest native and avoid dom matchers but even those don't work.

Related

how can I access data from array inside array

I need to access an image inside 2 arrays, but I have an error to do a map inside another one any suggestions?
all data printed successfully but I need to access an image which is an array inside another array
I need to access an image inside 2 arrays, but I have an error to do a map inside another one any suggestions?
here is the code
`
import { StyleSheet, Text, View, FlatList, Image,Dimensions } from 'react-native'
import React, { useEffect, useState } from 'react'
import Artist from '../components/Artist'
import SearchHeader from '../components/SearchHeader';
import { useNavigation, useRoute } from "#react-navigation/native";
import { SafeAreaView } from 'react-native-safe-area-context';
import axios from 'axios';
import { Artistfu } from '../services.js/services';
const ArtistSearch = () => {
const route = useRoute()
let token = route.params.token
const navigation = useNavigation();
const [artistt, setArtistt] = useState([]);
const [error, setError] = useState('');
const [images, setImages] = useState([])
useEffect(() => {
Artistfu().then(artists => {
setArtistt(artists)
setImages(artists[0].images)
}).catch(err => {
setError(err);
});
}, []);
console.log(artistt)
return (
<View>
{
artistt.map((ar, i) => (
<View
key={i}
>
<Text>{ar.followers.total}</Text>
{images.map((img)=>(
<Image source={{ uri: img.images.url }} style={{ height: 210, width: Dimensions.get('window').width - 40, resizeMode: 'cover' }} />
))}
<Text >{ar.name}</Text>
<Text>{ar.popularity}</Text>
</View>
))
}
</View>
)
}
export default ArtistSearch
const styles = StyleSheet.create({
})
services file :
import axios from 'axios';
const apiUrl = 'https://api.spotify.com'
const token = 'query=remaster%2520track%3ADoxy%2520artist%3AMiles%2520Davis&type=artist&market=ES&locale=en-US%2Cen%3Bq%3D0.9&offset=5&limit=10'
export const Artistfu = async () => {
const resp = await axios.get(`${apiUrl}/v1/search?${token}`, {
method: "GET",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Authorization: "Bearer " + token,
},
})
return resp.data.artists.items
};
`
and the data look like json:
`
{
"artists": {
"href": "https://api.spotify.com/v1/search?query=remaster%2520track%3ADoxy%2520artist%3AMiles%2520Davis&type=artist&market=ES&locale=en-US%2Cen%3Bq%3D0.9&offset=5&limit=10",
"items": [
{
"external_urls": {
"spotify": "https://open.spotify.com/artist/1MVFEpYdHtdV3k8Bgs79Pl"
},
"followers": {
"href": null,
"total": 404
},
"genres": [],
"href": "https://api.spotify.com/v1/artists/1MVFEpYdHtdV3k8Bgs79Pl",
"id": "1MVFEpYdHtdV3k8Bgs79Pl",
"images": [
{
"height": 640,
"url": "https://i.scdn.co/image/ab6761610000e5ebd1578a57ff89ac9aeed23863",
"width": 640
},
{
"height": 320,
"url": "https://i.scdn.co/image/ab67616100005174d1578a57ff89ac9aeed23863",
"width": 320
},
{
"height": 160,
"url": "https://i.scdn.co/image/ab6761610000f178d1578a57ff89ac9aeed23863",
"width": 160
}
],
"name": "DOXY",
"popularity": 14,
"type": "artist",
"uri": "spotify:artist:1MVFEpYdHtdV3k8Bgs79Pl"
},
]
}
}
`
The below should work
artists.items[0].images

example of react-native multi-select with formik

Does anybody have a working example of a react-native multi-select with formik? I already tried
import SectionedMultiSelect from 'react-native-sectioned-multi-select';
but the value array doesn't get submitted, but instead get an empty array.
Thank you.
there you have, hope you're reading the docs for both libraries. I'm doing that and it was just joining the code. it should work.
// Formik x React Native example
import React from 'react';
import { Button, TextInput, View } from 'react-native';
import { Formik } from 'formik';
import Icon from 'react-native-vector-icons/MaterialIcons';
import SectionedMultiSelect from 'react-native-sectioned-multi-select';
const items = [
{
name: 'Fruits',
id: 0,
// these are the children or 'sub items'
children: [
{ name: 'Apple', id: 10 },
{ name: 'Strawberry', id: 17 },
],
},
];
export const MyReactNativeForm = props => (
<Formik
initialValues={{ items: [] }}
onSubmit={values => console.log(values)}
>
{({ handleChange, handleBlur, handleSubmit, values }) => (
<View>
<SectionedMultiSelect
items={items}
IconRenderer={Icon}
uniqueKey="id"
subKey="children"
selectText="Choose some things..."
showDropDowns={true}
readOnlyHeadings={true}
onSelectedItemsChange={handleChange('items')}
selectedItems={values.items}
/>
<Button onPress={handleSubmit} title="Submit" />
</View>
)}
</Formik>
);
this is the example
import React, { Component } from 'react';
import { View } from 'react-native';
import Icon from 'react-native-vector-icons/MaterialIcons';
import SectionedMultiSelect from 'react-native-sectioned-multi-select';
const items = [
// this is the parent or 'item'
{
name: 'Fruits',
id: 0,
// these are the children or 'sub items'
children: [
{
name: 'Apple',
id: 10,
},
{
name: 'Strawberry',
id: 17,
},
{
name: 'Pineapple',
id: 13,
},
{
name: 'Banana',
id: 14,
},
{
name: 'Watermelon',
id: 15,
},
{
name: 'Kiwi fruit',
id: 16,
},
],
},
{
// next parent item
...
},
];
export default class App extends Component {
constructor() {
super();
this.state = {
selectedItems: [],
};
}
onSelectedItemsChange = (selectedItems) => {
this.setState({ selectedItems });
};
render() {
return (
<View>
<SectionedMultiSelect
items={items}
IconRenderer={Icon}
uniqueKey="id"
subKey="children"
selectText="Choose some things..."
showDropDowns={true}
readOnlyHeadings={true}
onSelectedItemsChange={this.onSelectedItemsChange}
selectedItems={this.state.selectedItems}
/>
</View>
);
}
}

Render children of ViroARImageMarker onAnchorFound

I'm currently working with the ViroReact Community Package in React Native to display a video in AR when a specific image is found. However the onTargetFound function of the ViroARImageMarker is not triggered, and the children of the ViroARImageMarker are not displayed.
When I added the onAnchorFound function to the ARScene (parent) the onAnchorFound method was triggered, however the children of the ViroARImageMarker still weren't rendered. Why is the function not triggered and therefore the children not displayed? How do I fix this?
The image is a 12x12cm black card with a bright orange logo (about 3cm) in the center. Neither of the targets are found in the ViroARImageMarker.
Here's my code:
Image Recognition Class
import React, { useEffect, useState } from 'react';
const {
ViroARScene,
ViroARImageMarker,
ViroARTrackingTargets,
ViroAnimations,
ViroVideo,
ViroMaterials,
ViroBox
} = require('#viro-community/react-viro');
const NewViroTracker = () => {
const videoPath = require('#assets/videos/wham.mp4');
const [videoAnimationName] = useState('showVideo');
const [playAnim, setPlayAnim] = useState(false);
function _onAnchorFound(evt: any) {
console.log('Anchor found in Marker :', evt);
setPlayAnim(true);
}
return (
<ViroARScene>
<ViroARImageMarker
target={'inviteCard'}
onAnchorFound={_onAnchorFound}>
<ViroVideo source={videoPath} />
</ViroARImageMarker>
<ViroARImageMarker
target={'logo'}>
<ViroBox position={[0, 0.25, 0]} scale={[0.5, 0.5, 0.5]} />
</ViroARImageMarker>
</ViroARScene>
);
};
ViroARTrackingTargets.createTargets({
inviteCard: {
source: require('#assets/images/invite-card.png'),
orientation: 'Up',
physicalWidth: 0.12 // real world width in meters
},
logo: {
source: require('#assets/images/logo-empty.png'),
orientation: 'Up',
physicalWidth: 0.0287 // real world width in meters
}
});
ViroMaterials.createMaterials({
chromaKeyFilteredVideo: {
chromaKeyFilteringColor: '#00FF00'
}
});
ViroAnimations.registerAnimations({
showVideo: {
properties: { scaleX: 1, scaleY: 1, scaleZ: 1 },
duration: 1000
},
closeVideo: {
properties: { scaleX: 0, scaleY: 0, scaleZ: 0 },
duration: 1
}
});
export default NewViroTracker;
App
import React from 'react';
const { ViroARSceneNavigator } = require('#viro-community/react-viro');
import styled from 'styled-components/native';
import NewViroTracker from 'components/NewViroTracker';
const App = () => {
return (
<ViroWrapper
autofocus={true}
initialScene={{
scene: NewViroTracker
}}
/>
);
};
export default App;
const ViroWrapper = styled(ViroARSceneNavigator)`
flex: 1;
`;
Dependencies:
"#viro-community/react-viro": "^2.21.1",
"react": "17.0.2",
"react-native": "0.66.3",

Unable to get information on a Reducer using Redux

I have a question with a Reducer, the problem is that I'm used to get data from an API which brings just Key Value Pairs something like:
{
"-MObNVR5e180MfhvtbRY": {
"altura": "179 cm",
"apellido": "Cuéllar De León",
"edad": 31,
"fecha_nacimiento": "20/10/1989",
"imagen": "https://firebasestorage.googleapis.com/v0/b/alianzafc2021.appspot.com/o/yimmi%20cuellar.jpg?alt=media&token=144fa106-eabb-40c8-83b6-2e4b45034eda",
"iso_code": "SV",
"lugar_nacimiento": "San Julián",
"nacionalidad": "El Salvador",
"nombre_completo": "Yimmy Rodrigo Cuéllar De León",
"nombre_corto": "Yimmy Cuéllar",
"nombres": "Yimmy Rodrigo",
"numero": 1,
"pais": "El Salvador",
"peso": "66 kg",
"player_id": 108911,
"posicion": "Portero"
},
}
Now I need to get the data from an API that has Arrays within the Array like this:
{
"api": {
"results": 13,
"players": [
{
"player_id": 79308,
"player_name": "Felipe Ponce Ramírez",
"firstname": "Felipe",
"lastname": "Ponce Ramírez",
"number": null,
"position": "Midfielder",
"age": 32,
"birth_date": "29/03/1988",
"birth_place": "Ciudad Lerdo",
"birth_country": "Mexico",
"nationality": "Mexico",
"height": "177 cm",
"weight": "67 kg",
"injured": null,
"rating": null,
"team_id": 4299,
"team_name": "Alianza",
"league_id": 2979,
"league": "Primera Division",
"season": "2020-2021",
"captain": 0,
"shots": {
"total": 0,
"on": 0
},
"goals": {
"total": 4,
"conceded": 0,
"assists": 0,
"saves": 0
},
"passes": {
"total": 0,
"key": 0,
"accuracy": 0
},
"tackles": {
"total": 0,
"blocks": 0,
"interceptions": 0
},
"duels": {
"total": 0,
"won": 0
},
"dribbles": {
"attempts": 0,
"success": 0
},
"fouls": {
"drawn": 0,
"committed": 0
},
"cards": {
"yellow": 2,
"yellowred": 0,
"red": 0
},
"penalty": {
"won": 0,
"commited": 0,
"success": 0,
"missed": 0,
"saved": 0
},
"games": {
"appearences": 6,
"minutes_played": 194,
"lineups": 2
},
"substitutes": {
"in": 4,
"out": 2,
"bench": 0
}
},
}
Someone told me that I will need to create a Data Model for each SubArray so I did and I used it in my Action File like this:
export const fetchEstadistica = player_id => {
return async (dispatch) => {
//any async code here!!!
try {
const response = await fetch(
`https://api-football-v1.p.rapidapi.com/v2/players/player/${player_id}.json`,
{
method: 'GET',
headers: new Headers({
'x-rapidapi-key': //Here Goes My API Key which I keep for Security Reasons,
'x-rapidapi-host': 'api-football-v1.p.rapidapi.com',
'useQueryString': 'true'
})
}
);
if (!response.ok) {
throw new Error('Algo salio Mal!');
}
const resData = await response.json();
const loadesApiResult = [];
console.log(resData);
//Arrays de la Estadistica del Jugador
const loadedEstadistica = [];
const loadedCards = [];
const loadedGoals = [];
const loadedGames = [];
for (const key in resData) {
loadesApiResult.push(
new ResultadoEstadistica(
key,
resData[key].results,
resData[key].players
)
);
}
const apiData = loadesApiResult.players;
for (const key in apiData){
loadedEstadistica.push(
new PlayerEstadistica (
apiData[key].player_id,
apiData[key].player_name,
apiData[key].firstname,
apiData[key].lastname,
apiData[key].number,
apiData[key].position,
apiData[key].age,
apiData[key].birth_date,
apiData[key].birth_place,
apiData[key].birth_country,
apiData[key].nationality,
apiData[key].height,
apiData[key].weight,
apiData[key].injured,
apiData[key].rating,
apiData[key].team_id,
apiData[key].team_name,
apiData[key].league_id,
apiData[key].league,
apiData[key].season,
apiData[key].captain,
apiData[key].shots,
apiData[key].goals,
apiData[key].passes,
apiData[key].duels,
apiData[key].dribbles,
apiData[key].fouls,
apiData[key].cards,
apiData[key].penalty,
apiData[key].games,
apiData[key].substitutes,
)
);
}
const playerDataGames = loadedEstadistica.games;
for (const key in playerDataGames){
loadedGames.push(
new Games(
playerDataGames[key].apperences,
playerDataGames[key].minutes_played,
playerDataGames[key].lineups
)
);
};
const playerDataGoals = loadedEstadistica.goals;
for (const key in playerDataGoals){
loadedGoals.push(
new Goals(
playerDataGoals[key].total,
playerDataGoals[key].conceded,
playerDataGoals[key].assists,
playerDataGoals[key].saves
)
);
};
const playerDataCards = loadedEstadistica.cards;
for (const key in playerDataCards){
loadedCards.push(
new Cards(
playerDataCards[key].yellow,
playerDataCards[key].yellowred,
playerDataCards[key].red
)
);
};
dispatch({ type: SET_ESTADISTICA, estadistica: loadedEstadistica, goles: loadedGoals, juegos: loadedGames, tarjetas: loadedCards });
} catch (err) {
throw err;
}
};
};
Then I get this into my Readucer with the Type:
import { SET_JUGADORES, SET_ESTADISTICA } from "../actions/jugadores";
const initialState = {
availablePlayers: [],
estadistica: [],
playerGoals: [],
playerCards: [],
playerGames: [],
}
export default (state = initialState, action) => {
switch (action.type) {
case SET_JUGADORES:
return {
availablePlayers: action.players,
};
case SET_ESTADISTICA:
return{
estadistica: estadistica,
// playerGoals: action.goles,
// playerCards: action.tarjetas,
// playerGames: action.juegos
};
}
return state;
};
Finally, I go and call the Reducer from:
import React, {useState, useCallback, useEffect} from 'react';
import { ScrollView, Text, Image, StyleSheet, View } from 'react-native';
import { useSelector, useDispatch } from 'react-redux';
const ProductDetailScreen = props => {
const playerId = props.route.params.id;
const estadId = props.route.params.statId;
const selectedPlayer = useSelector(state => state.jugadores.availablePlayers.find(prod => prod.id === playerId));
const [isLoading, setIsLoading] = useState(false);
const [isRefreshing, setIsRefreshing] = useState(false);
const [error, setError] = useState();
const goles = useSelector(state => state.jugadores.estadistica);
const dispatch = useDispatch();
const loadEstad = useCallback (async (estadId) => {
setError(null);
setIsRefreshing(true);
try {
await dispatch(userActions.fetchEstadistica(estadId));
} catch (err){
setError(err.message);
}
setIsRefreshing(false);
}, [dispatch, setIsLoading, setError]);
useEffect(() => {
setIsLoading(true);
loadEstad(estadId).then(() => {
setIsLoading(false);
});
}, [dispatch, loadEstad]);
console.log(estadId);
console.log(goles);
return (
<ScrollView>
<Image style={styles.image} source={{ uri: selectedPlayer.imagen }} />
<View style={styles.dataContainer}>
<Text style={styles.description}>Numero: <Text style={styles.subtitle}>{selectedPlayer.numero}</Text></Text>
<Text style={styles.description}>Nombre Completo: <Text style={styles.subtitle}>{selectedPlayer.nombre_completo}</Text></Text>
<Text style={styles.description}>Posicion: <Text style={styles.subtitle}>{selectedPlayer.posicion}</Text> </Text>
<Text style={styles.description}>Edad: <Text style={styles.subtitle}>{selectedPlayer.edad}</Text></Text>
<Text style={styles.description}>Nacionalidad: <Text style={styles.subtitle}>{selectedPlayer.nacionalidad}</Text></Text>
</View>
</ScrollView>
);
};
export const screenOptions = navData => {
return {
headerTitle: navData.route.params.nombre,
}
};
const styles = StyleSheet.create({
image: {
width: '100%',
height: 300,
},
subtitle: {
fontSize: 16,
textAlign: 'justify',
marginVertical: 20,
fontWeight:'normal',
},
description: {
fontSize: 16,
textAlign: 'center',
marginVertical: 20,
fontWeight: 'bold',
},
dataContainer:{
width: '80%',
alignItems: 'center',
marginHorizontal: 40,
},
actions: {
marginVertical: 10,
alignItems: 'center',
},
});
export default ProductDetailScreen;
However when I go to watch the Terminal I see that when I do the console.log(goles) which receives the slice of the Reducer I get Undefined.
I'm not sure what am I doing Wrong, any ideas?
Kind Regards
Try this
import { SET_JUGADORES, SET_ESTADISTICA } from "../actions/jugadores";
const initialState = {
availablePlayers: [],
estadistica: [],
playerGoals: [],
playerCards: [],
playerGames: [],
}
export default (state = initialState, action) => {
switch (action.type) {
case SET_JUGADORES:
return {
...state,
availablePlayers: action.players,
};
case SET_ESTADISTICA:
return{
...state,
estadistica: state.estadistica,
// playerGoals: action.goles,
// playerCards: action.tarjetas,
// playerGames: action.juegos
};
}
return state;
};

How to add a "reset values" button to a react-admin edit form

Is there a way to have a button in a react-admin form so that when I click the button, the values are reset to the edited record's initial values?
I don't mean a Cancel button (that would close the form and redirect). I mean a reset-to-initial-values button that would discard any changes from the last save.
New implementation related to replacing 'redux-form 'with' react-final-form':
import React, {
useCallback,
} from 'react'
import PropTypes from 'prop-types'
import classnames from 'classnames'
import { Button } from 'react-admin'
import { useForm } from 'react-final-form'
import { makeStyles } from '#material-ui/core/styles'
import IconClear from '#material-ui/icons/Clear'
const useStyles = makeStyles(theme => ({
button: {
marginLeft: '10px',
position: 'relative',
},
leftIcon: {
marginRight: theme.spacing(1),
},
icon: {
fontSize: 18,
},
}), { name: 'ClearButton' })
const sanitizeRestProps = ({
basePath,
invalid,
pristine,
//reset,
saving,
submitOnEnter,
handleSubmit,
handleSubmitWithRedirect,
undoable,
onSave,
...rest
}) => rest
const ClearButton = ({ className, icon, ...rest}) => {
const classes = useStyles()
const form = useForm()
const handleClick = useCallback(() => {
form.setConfig('keepDirtyOnReinitialize', false)
form.reset()
form.setConfig('keepDirtyOnReinitialize', true)
}, [form])
return (
<Button
className={classnames(classes.button, className)}
onClick={handleClick}
{...sanitizeRestProps(rest)}
>
{ icon ? React.cloneElement(icon, { className: classnames(classes.leftIcon, classes.icon) }) : null }
</Button>
)
}
ClearButton.propTypes = {
className: PropTypes.string,
classes: PropTypes.object,
icon: PropTypes.element,
}
ClearButton.defaultProps = {
icon: <IconClear />,
label: 'Clear',
}
export default ClearButton