Compare commits

...

32 Commits

Author SHA1 Message Date
Corentin RICHARD 829714e9df Mise à jour de 'Readme.md'
2 years ago
Corentin RICHARD d1f1268395 Merge pull request 'CardDetail' (#9) from CardDetail into master
2 years ago
Corentin RICHARD ec10ce594a Fix de bugs venant du merge
2 years ago
Pierre Ferreira 33ce875c5a merge, les item on un lien avec le detail et le fav mais l'affichage des fav ne marche pas car item defini comme undefin
2 years ago
Corentin RICHARD 6e55e5b9a9 Merge pull request 'cleanUp' (#8) from cleanUp into master
2 years ago
Corentin RICHARD bd1c895ff2 clean up of the project
2 years ago
Corentin RICHARD b344802346 clean up of the project
2 years ago
Corentin RICHARD 1a01c57590 Mise à jour de 'Readme.md'
2 years ago
Corentin RICHARD 1e1fa6ba1f Mise à jour de 'Readme.md'
2 years ago
Corentin R 065b819a1f MAJ du ReadMe
2 years ago
Corentin RICHARD a019416698 Merge pull request 'filter' (#7) from unitTesting into master
2 years ago
Corentin RICHARD 39d7cacfae merge
2 years ago
Corentin RICHARD 071c3640b7 filter
2 years ago
Corentin RICHARD 0536205848 Merge pull request 'adding test and ui to home page' (#6) from unitTesting into master
2 years ago
Corentin RICHARD 0d796d08f9 adding test and ui to home page
2 years ago
Corentin RICHARD 2f9223012c Merge pull request 'asyncStorage' (#5) from asyncStorage into master
2 years ago
Corentin RICHARD 0e763163c8 favs et utilisation du async storage
2 years ago
Corentin RICHARD 3c08ebeeb4 Adding and removing favs
2 years ago
Pierre Ferreira aa6cda5ed3 ajout d'info pour les details 📦
2 years ago
Corentin RICHARD 88a6612e77 favorite update
2 years ago
Pierre Ferreira 32d27faf92 ajout d'une table dans le detail, avec mana, health, attack
2 years ago
Pierre Ferreira 142ea02d7b 📦 recupération de l'attck, health, mana et flavor de l'api, probleme de depedancies 🍺
2 years ago
Pierre Ferreira 26e00b4449 🎨 Page detail classique qui rend pas trop mauvais
2 years ago
Corentin RICHARD 731c69ccc6 merge
2 years ago
Corentin RICHARD 565cf8fec1 Adding asynStorage methods
2 years ago
Corentin RICHARD 28b597d0cc Adding asynStorage methods
2 years ago
Pierre Ferreira 55d22852b8 transformation de listItem en component indépendant, et utilisation d'une child props
2 years ago
Pierre Ferreira 67aea8e360 Ajout de l'option de recherche dans la liste de carte ! 💥
2 years ago
Corentin RICHARD 98bc1dc9a5 Adding onPress action
2 years ago
Corentin RICHARD dccaa55303 pull from master
2 years ago
Corentin RICHARD ff57cd576e Add first Async Storage
2 years ago
Corentin RICHARD b3908be975 update
2 years ago

@ -1,32 +0,0 @@
# React-Native BUT2 Notation
Duration : 10 * 2h
## Documentation (4 pts)
* Application sketches
## Basics (10 pts)
* Navigation (2 pts)
* Tab bottom navigation + at least one button
* Store (2 pts)
* Read data from redux store
* Actions (1 pts)
* Update data to redux store
* Display list of items (2 pts)
* FlatList, VirtualizedList or SectionList
* Display image (1 pts)
* Child props (1 pts)
* TextInput (1 pts)
## Application features (6 pts)
* Retrieve data using the Web API (2 pts)
* Store favorite data into phone storage (2 pts)
* Write Tests (2 pts)
## Bonus (only taken into account if the basics are all mastered)
* Dark/Light mode switch (2pts)
* Sexy UI (2 pts)

@ -1 +1,79 @@
[Slides](https://iutsa01.blob.core.windows.net/react-native/ReactNative.pdf) <div align = center>
<img src="assets/banner.png" width="1050" height="">
</div>
<div align = center>
---
&nbsp; ![Android](https://img.shields.io/badge/Android-3DDC84?style=for-the-badge&logo=android&logoColor=white)
&nbsp; ![Java](https://img.shields.io/badge/JavaScript-F7DF1E?style=for-the-badge&logo=javascript&logoColor=black)
---
</div>
<div align = center>
# **DesckStone**
</div>
# :bookmark: Présentation
DeckStone est une application master detail créé grâce au framework React Native en TypeScript et en JavaScript.
Elle vous permet la gestion des cartes hearstone que vous posséder avec la consultation des informations des cartes et l'utilisation de favoris persistants.
# Notation :white_check_mark:
## ``` Documentation ```
:white_check_mark: **ReadMe**
:white_check_mark: **Sketches** : ![Sketchs](./assets/Sketchs.PNG)
## ```Bases```
:white_check_mark: **Navigation** : Nous avons une barre de navigation fonctionnel nous permettant de naviguer entre les trois écrans.
:white_check_mark: **Store** : Nous utilisons le store pour charger nos données et les stocker mais aussi pour gérer les favoris.
:white_check_mark: **Actions** : Nous utilisons les actions pour charger les données depuis le redux mais aussi pour ajouter et supprimer les favoris.
:white_check_mark: **Display List of Items** : Nous affichons la liste des cartes et celles des favoris dans 2 écrans différents.
:white_check_mark: **Display image** : Chaque cartes sont affiché par un component qui affiche l'image de la carte.
:white_check_mark: **Child props** : Nous utilisons un props pour passer la carte au component qui l'affiche.
:white_check_mark: **TextInput** : Une recherche par nom peut être effectué par dans les 2 listes.
## ```Application features```
:construction: **API** : Nous utilisons l'API officielle du jeu pour récupérer nos données, cependant nous n'avons pas pu gérer le changement de token, nous le changeons donc à la main.
***Source*** : https://develop.battle.net/documentation/hearthstone/game-data-apis
:white_check_mark: **Store favorite data into phone storage** : Nous utilison l'AsyncStorage pour sauvegarder la liste des favoris dans le téléphone.
:construction: **Write Tests** : Nous testons les actions, le reducer et les composants UI que nous avons implémenter, cependant les éléments utilisant indirectement l'AsyncStorage ne passent pas les tests à cause d'un problème d'utilisation du mockAsyncStorage.
## ``Bonus``
* :construction: **Dark/Light mode switch** : Nous n'avons mis en place cette gestion seulement dans la branche dédié par souci de visuel
* :construction: **Sexy UI** : A vous de juger !
# :construction: Développeurs
- Corentin RICHARD : corentin.richard@etu.uca.fr
- Pierre FERREIRA : pierre.ferreira@etu.uca.fr
<div align="center">
<a href = "https://codefirst.iut.uca.fr/git/corentin.richard">
<img src="https://codefirst.iut.uca.fr/git/avatars/4372364870f18ab9104f13222fa84d2e?size=870" width="50" >
</a>
<a href = "https://codefirst.iut.uca.fr/git/pierre.ferreira">
<img src="https://codefirst.iut.uca.fr/git/avatars/edbacace5f621ae77077f206ebdcee27?size=870" width="50" >
</a>
© IUT - Auvergne
</div>

@ -0,0 +1,9 @@
// somewhere in your configuration files
import AsyncStorageMock from '@react-native-async-storage/async-storage/jest/async-storage-mock';
AsyncStorageMock.multiGet = jest.fn(([keys], callback) => {
// do something here to retrieve data
callback([]);
});
export default AsyncStorageMock;

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 KiB

@ -0,0 +1,18 @@
import { View } from 'react-native';
import { Image } from 'react-native';
type ItemProps = {
url : string //Image URL
}
export function ListItemComponent(props : ItemProps){
return (
<View>
<Image
source={{uri: props.url}}
style={{flex:1, minHeight:250, minWidth:180}}
/>
</View>
)
}

@ -0,0 +1,55 @@
import { useDispatch } from "react-redux";
import { Card } from "../models/Card";
import { setFavList } from "../redux/actions/action_setFavList";
import { Image, TouchableHighlight, View } from "react-native";
import React, { useEffect } from "react";
import {CardProps} from "../props/favProps"
import { FontAwesome } from '@expo/vector-icons';
import { StyleSheet} from 'react-native';
export default function Item(props: CardProps){ // a mettre dans components et definir une props pour passer le param
const {route} = props;
const item: Card = route.card;
const bool: boolean = route.bool;
const dispatch = useDispatch()
const HandleAddFav = (props : CardProps) => {
dispatch(setFavList(props));
}
return(
/* dispatch , */
<View style={styles.item}>
<TouchableHighlight testID="button" style={item.fav?styles.favoriteButtonFav:styles.favoriteButtonNonFav} onPress={() => HandleAddFav(props)} >
<FontAwesome name="heart-o" size={50} color="#fff" />
</TouchableHighlight>
<Image
source={{uri:item.img}}
style={{flex:1, minHeight:250, minWidth:180}}/>
</View>
);
}
const styles = StyleSheet.create({
item: {
zIndex:0
},
favoriteButtonNonFav: {
position: 'absolute',
top: 10,
right: 10,
backgroundColor: 'red',
borderRadius: 50,
padding: 10,
zIndex:1
},
favoriteButtonFav: {
position: 'absolute',
top: 10,
right: 10,
backgroundColor: 'red',
borderRadius: 50,
padding: 10,
zIndex:1
},});

@ -1,41 +0,0 @@
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View, Button } from 'react-native';
import React, { useState } from "react";
// @ts-ignore
export default function Main(props : mainProps){
const [count, setCount] = useState(0);
return (
<View style={styles.container}>
<View style={styles.border}>
<Text>Maman, prend la caméra ! !</Text>
<StatusBar style="auto" />
<Text>{count}</Text>
<Button onPress={()=> setCount(count+1)} title="+1"/>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#ffffff',
alignItems: 'center',
justifyContent: 'center',
borderWidth: 5,
borderColor : "#ff00ff",
},
border: {
flex: 1,
backgroundColor: '#ff0000',
maxHeight : 100,
borderWidth : 15,
borderRadius : 15,
borderColor : '#00ffaa',
alignItems: 'center',
justifyContent: 'center',
}
});

@ -1,14 +0,0 @@
// export class ApiDataManager{
// public async getCards() : Promise<String[]> {
// const CardPromise = await fetch('https://omgvamp-hearthstone-v1.p.rapidapi.com/cards')
// const CardListJson = await CardPromise.json();
// const CardList: String[] = Array.of(CardListJson);
// return CardList;
// }
// }

@ -1,13 +0,0 @@
import { Card } from "../models/Card"
export class StubLib {
public getCards(): Card[] {
const NOUNOURS_LIST : Card[] = [
]
return NOUNOURS_LIST
}
}

@ -1,51 +1,37 @@
import {isValidNumber} from "react-native-gesture-handler/lib/typescript/web_hammer/utils";
import {CardSet} from "./CardSet";
import {Type} from "./Type";
import {Classe} from "./Classe";
export class Card { export class Card {
constructor(id: number,name :string, img : string, imgGold : string){//,set : CardSet,type : Type,clas : Classe,rarity : string,cost : number,attack : number, health : number, desc : string,flavor : string,artist : string,collectible : boolean,elite : boolean,race : string, cropImg :string) { constructor(id: string,name :string,health : string, attack : string, manaCost : string, rarityId : string, flavorText : string, classId : string, multiClassIds : any, img : string, imgGold : string, cropImage : string, artistName : string, fav : boolean){
this._id=id this.id=id
this._name=name this.name=name
//this._set=set //this._set=set
// this._type=type // this._type=type
// this._class=clas // this._class=clas
// this._rarity=rarity // this._rarity=rarity
// this._cost=cost this.manaCost=manaCost
// this._attack = attack this.attack = attack
// this._health = health this.health = health
this.rarityId = rarityId
// this._desc = desc // this._desc = desc
// this._flavor = flavor this.flavorText = flavorText
// this._artist = artist
this.classId = classId
this.multiClassIds = multiClassIds
this.artistName = artistName
// this._collectible = collectible // this._collectible = collectible
this._img = img this.img = img
this._imgGold = imgGold this.imgGold = imgGold
// this._cropImg = cropImg this.cropImage = cropImage
this.fav = fav
} }
// ID // // ID //
public id: string;
private _id : number;
get id(): number {
return this._id
}
// NAME // // NAME //
private _name : string; public name: string
get name(): string {
return this._name;
}
set name(value: string) {
this._name = value;
}
// private _set : CardSet;
// get set(): CardSet{
// return this._set
// }
// set set(value : CardSet){ // set set(value : CardSet){
// this._set = value // this._set = value
@ -76,29 +62,15 @@ export class Card {
// set rarity(value: string) { // set rarity(value: string) {
// this._rarity = value ; // this._rarity = value ;
// } // }
// private _cost : number
// get cost(): number { public manaCost : string;
// return this._cost;
// }
// set cost(value: number) {
// this._cost = value ;
// } public attack : string;
public health : string;
// private _attack : number
// get attack(): number {
// return this._attack;
// }
// set attack(value: number) {
// this._attack = value ;
// }
// private _health : number
// get health(): number {
// return this._health;
// }
// set health(value: number) {
// this._health = value ;
// }
// private _desc : string // private _desc : string
// get desc(): string { // get desc(): string {
// return this._desc; // return this._desc;
@ -106,23 +78,17 @@ export class Card {
// set desc(value: string) { // set desc(value: string) {
// this._desc = value ; // this._desc = value ;
// } // }
public classId : string;
// private _flavor : string public flavorText : string
// get flavor(): string { public rarityId : string
// return this._flavor;
// }
// set flavor(value: string) {
// this._flavor = value ;
// }
// private _artist : string
// get artist(): string { public multiClassIds : any;
// return this._artist;
// }
// set artist(value: string) { public artistName : string;
// this._artist = value ;
// }
// private _collectible : boolean // private _collectible : boolean
// get collectible(): boolean { // get collectible(): boolean {
// return this._collectible; // return this._collectible;
@ -131,28 +97,11 @@ export class Card {
// this._collectible = value; // this._collectible = value;
// } // }
private _img : string public img : string;
public imgGold : string;
get img(): string {
return this._img; public cropImage : string;
} //FAV//
set img(value: string) { public fav : boolean;
this._img = value ;
}
private _imgGold : string
get imgGold(): string {
return this._imgGold;
}
set imgGold(value: string) {
this._imgGold = value ;
}
// private _cropImg : string
// get cropImg(): string {
// return this._cropImg;
// }
// set cropImg(value: string) {
// this._cropImg = value;
// }
} }

@ -1,37 +0,0 @@
export class CardSet {
constructor(id: number,name : string, type : string ) {
this._id = id
this._name =name
this._type = type
}
private _id : number
get id(): number {
return this._id
}
// NAME //
private _name : string;
get name(): string {
return this._name;
}
set name(value: string) {
this._name = value;
}
private _type : string;
get type(): string {
return this._type;
}
set type(value: string) {
this._type = value;
}
}

@ -1,4 +0,0 @@
export class Classe {
constructor(private id : number, private name : string) {
}
}

@ -1,5 +0,0 @@
export class Type{
constructor(private id : number, private name : string) {
}
}

@ -0,0 +1,17 @@
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import {createNativeStackNavigator} from '@react-navigation/native-stack';
import ListScreen from '../screens/ListScreen';
import DetailCard from '../screens/DetailCard';
export default function DetailStackNav() {
const Stack = createStackNavigator();
return (
<Stack.Navigator initialRouteName="CardList">
<Stack.Screen name="CardList" component={ListScreen}/>
<Stack.Screen name="DetailCard" component={DetailCard}/>
</Stack.Navigator>
)
}

@ -1,21 +0,0 @@
// import { NavigationContainer } from '@react-navigation/native';
// import { createStackNavigator } from '@react-navigation/stack';
// import HomeScreen from '../screens/HomeScreen';
// import ListScreen from '../screens/ListScreen';
// import ListFav from '../screens/ListFav';
// export default function ListNavigator() {
// const Stack = createStackNavigator();
// return (
// <Stack.Navigator initialRouteName="List">
// <Stack.Screen name="List" component={ListScreen}/>
// <Stack.Screen name="ListFav" component={ListFav}/>
// </Stack.Navigator>
// )
// }

@ -1,26 +1,20 @@
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View, Button } from 'react-native';
import React, { useState } from "react"; import React, { useState } from "react";
import { NavigationContainer } from '@react-navigation/native'; import { NavigationContainer } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import HomeScreen from '../screens/HomeScreen';
import ListScreen from '../screens/ListScreen'; import ListScreen from '../screens/ListScreen';
import ListFav from '../screens/ListFav'; import ListFav from '../screens/ListFav';
import TabBarIcon from '../components/TabBarIcon'; import TabBarIcon from '../components/TabBarIcon';
import StackNavigation from './StackNavigation'; import StackNavigation from './StackNavigation';
import DetailStackNav from './DetailStackNav';
export default function Navigation() { export default function Navigation() {
const BottomTabNavigator = createBottomTabNavigator(); const BottomTabNavigator = createBottomTabNavigator();
return ( return (
<NavigationContainer> <NavigationContainer>
<BottomTabNavigator.Navigator initialRouteName="Home"> <BottomTabNavigator.Navigator initialRouteName="Home">
<BottomTabNavigator.Screen name="List" component={ListScreen} <BottomTabNavigator.Screen name="List" component={DetailStackNav}
options={{ options={{
title: 'List', title: 'List',
tabBarIcon: ({color}) => <TabBarIcon name="th-large" color={color}/>, tabBarIcon: ({color}) => <TabBarIcon name="th-large" color={color}/>,

@ -11,12 +11,10 @@ import ListFav from '../screens/ListFav';
export default function StackNavigation() { export default function StackNavigation() {
const Stack = createStackNavigator(); const Stack = createStackNavigator();
return ( return (
//<NavigationContainer>
<Stack.Navigator initialRouteName="Home"> <Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen}/> <Stack.Screen name="Home" component={HomeScreen}/>
<Stack.Screen name="ListScreen" component={ListScreen}/> <Stack.Screen name="ListScreen" component={ListScreen}/>
<Stack.Screen name="ListFav" component={ListFav}/> <Stack.Screen name="ListFav" component={ListFav}/>
</Stack.Navigator> </Stack.Navigator>
//</NavigationContainer>
) )
} }

26364
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -6,29 +6,61 @@
"start": "expo start", "start": "expo start",
"android": "expo start --android", "android": "expo start --android",
"ios": "expo start --ios", "ios": "expo start --ios",
"web": "expo start --web" "web": "expo start --web",
"test": "jest"
},
"jest": {
"setupFiles": [
"./test/setup/jestSetupFile.js"
],
"preset": "jest-expo",
"verbose": true,
"transformIgnorePatterns": [
"node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|react-native-svg)"
],
"testMatch": [
"**.test.js"
],
"testEnvironment": "node",
"testEnvironmentOptions": {
"browsers": [
"chrome",
"firefox",
"safari"
]
}
}, },
"dependencies": { "dependencies": {
"@jest/globals": "^29.5.0",
"@react-native-async-storage/async-storage": "1.17.11",
"@react-navigation/bottom-tabs": "^6.5.5", "@react-navigation/bottom-tabs": "^6.5.5",
"@react-navigation/native": "^6.1.4", "@react-navigation/native": "^6.1.4",
"@react-navigation/native-stack": "^6.9.10", "@react-navigation/native-stack": "^6.9.10",
"@react-navigation/stack": "^6.3.14", "@react-navigation/stack": "^6.3.14",
"@reduxjs/toolkit": "^1.9.3", "@reduxjs/toolkit": "^1.9.3",
"@testing-library/jest-native": "^5.4.2",
"@testing-library/react-native": "^12.0.1",
"expo": "^48.0.0", "expo": "^48.0.0",
"expo-status-bar": "~1.4.4", "expo-status-bar": "~1.4.4",
"jest": "^29.2.1",
"jest-expo": "^48.0.2",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0", "react-dom": "18.2.0",
"react-native": "0.71.3", "react-native": "0.71.3",
"react-native-gesture-handler": "~2.9.0", "react-native-gesture-handler": "~2.9.0",
"react-native-safe-area-context": "4.5.0", "react-native-safe-area-context": "4.5.0",
"react-native-table-component": "^1.2.2",
"react-native-web": "~0.18.11", "react-native-web": "~0.18.11",
"react-redux": "^8.0.5", "react-redux": "^8.0.5",
"redux": "^4.2.1" "redux": "^4.2.1"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.20.0", "@babel/core": "^7.20.0",
"@react-native-async-storage/async-storage": "^1.18.1",
"@types/jest": "^29.5.0",
"@types/react": "~18.0.27", "@types/react": "~18.0.27",
"@types/react-native": "~0.70.6", "@types/react-native": "~0.70.6",
"@types/react-native-table-component": "^1.2.4",
"typescript": "^4.9.4" "typescript": "^4.9.4"
}, },
"private": true "private": true

@ -0,0 +1,8 @@
import { Card } from "../models/Card";
export interface CardProps{
route : {
card: Card;
bool: boolean;
}
}

@ -1,85 +1,53 @@
import { setCardsList } from "./action_setCardsList"; import { setCardsList } from "./action_setCardsList";
import { Card } from "../../models/Card"; import { Card } from "../../models/Card";
//! se fichier devra possiblement changer de dossier !!! //Fonction chargé de l'appel à l'API
//! classe pour tester
// export class Card {
// cardId : String
// name : String
// manaCost : number
// attack : number
// health : number
// desc : String
// // constructor() {
// // this.cardId = "cardId";
// // this.name = "name";
// // this.manaCost = 0;
// // this.attack = 0;
// // this.health = 0;
// // this.desc = "desc";
// // }
// constructor(cardId : String, name : String, manaCost : number, attack : number, health : number, desc : String){
// this.cardId = cardId;
// this.name = name;
// this.manaCost = manaCost;
// this.attack = attack;
// this.health = health;
// this.desc = desc;
// }
// }
//Define your action creators that will be responsible for asynchronous operations
export const getAllCards = () => { export const getAllCards = () => {
//In order to use await your callback must be asynchronous using async keyword.
console.log("getallcard")
//@ts-ignore //@ts-ignore
return async dispatch => { return async dispatch => {
//Then perform your asynchronous operations.
try { try {
//Have it first fetch data from our starwars url.
const options = { const options = {
method: 'GET', method: 'GET',
headers: { headers: {
'content-length':'9505', 'content-length': '9508',
'content-type':'application/json; charset=utf-8', 'content-type': 'application/json; charset=utf-8',
'etag':'W/"74bb-QMT8DIj6saBS1wT4u5WWcEmZAdw"' 'etag': 'W/"74bb-d4gMlMNks7UGES3Jmn6wzUTXaLI"',
} }
}; };
const CardsPromise = await fetch('https://us.api.blizzard.com/hearthstone/cards?locale=en_US&access_token=EURTWhjBC2SRb4Ez42BT1kx8R2NcJc07kL', options); //! Actualisation de l'API (19/04) :
//console.log("FETCH") const CardsPromise = await fetch('https://us.api.blizzard.com/hearthstone/cards?locale=en_US&access_token=EUo8Snb09AfE3zQR4CoaB71gq1q3qvSmgL', options);
//console.log(CardsPromise)
//Then use the json method to get json data from api/ //Then use the json method to get json data from api/
const CardsListJson = await CardsPromise.json(); const CardsListJson = await CardsPromise.json();
//console.log(CardsListJson['cards'])
//@ts-ignore //@ts-ignore
const CardsList: Card[] = CardsListJson['cards'].map(elt => new Card(elt["id"] ? elt["id"] : 1, const CardsList: Card[] = CardsListJson['cards'].map(elt => new Card(elt["id"] ? elt["id"] : 1,
elt["name"] ? elt["name"] : "", elt["name"] ? elt["name"] : "",
elt["health"] ? elt["health"] : 0,
elt["attack"] ? elt["attack"] : 0,
elt["manaCost"] ? elt["manaCost"] : 0,
elt["rarityId"] ? elt["rarityId"] : 0,
elt["flavorText"] ? elt["flavorText"] : "",
elt["classId"] ? elt["classId"] : 0,
elt["multiClassIds"] ? elt["multiClassIds"] : "Nothing",
elt["image"] ? elt["image"] : "", elt["image"] ? elt["image"] : "",
elt["imageGold"] ? elt["imageGold"] : "", elt["imageGold"] ? elt["imageGold"] : "",
elt["cropImage"] ? elt["cropImage"] : "",
elt["artistName"] ? elt["artistName"] : "",
)); //, elt["cardSet"], elt["type"], elt["faction"], elt["rarity"], elt["cost"], elt["attack"], elt["health"],elt["text"], elt["flavor"], elt["artist"], elt["collectible"], elt["elite"], elt["race"], elt["img"], elt["imgGold"] )); //, elt["cardSet"], elt["type"], elt["faction"], elt["rarity"], elt["cost"], elt["attack"], elt["health"],elt["text"], elt["flavor"], elt["artist"], elt["collectible"], elt["elite"], elt["race"], elt["img"], elt["imgGold"]
//elt["cardId"] == null ? elt["cardId"] : "" //elt["cardId"] == null ? elt["cardId"] : ""
//console.log("TOTO")
//console.log(CardsList)
//call the action //call the action
dispatch(setCardsList(CardsList)); dispatch(setCardsList(CardsList));
} catch (error) { } catch (error) {
console.log('Error---------', error); console.log('Error---------', error);
//You can dispatch to another action if you want to display an error message in the application
//dispatch(fetchDataRejected(error))
} }
} }
} }

@ -1,8 +1,5 @@
import {ADD_FAVORITE_DATA, FETCH_DATA} from '../constants'; import { Card } from '../../models/Card';
import {FETCH_DATA} from '../constants';
//? Changer cette importe quand la classe sera definit dans un fichier correctement.
import {Card} from '../../models/Card'
export const setCardsList = (List: Card[]) => { export const setCardsList = (List: Card[]) => {
return { return {
type: FETCH_DATA, type: FETCH_DATA,

@ -1,8 +1,10 @@
import {ADD_FAVORITE_DATA, FETCH_DATA} from '../constants'; import { Card } from '../../models/Card';
import { CardProps } from '../../props/favProps';
import {ADD_FAVORITE_DATA} from '../constants';
export const setFavList = (List: String[]) => { export const setFavList = (props : CardProps) => {
return { return {
type: ADD_FAVORITE_DATA, type: ADD_FAVORITE_DATA,
payload: List, payload: props,
}; };
} }

@ -0,0 +1,9 @@
import { Card } from '../../models/Card';
import {SET_FAVS} from '../constants';
export const setList = (list: []) => {
return {
type: SET_FAVS,
payload: list
};
}

@ -1,15 +0,0 @@
import {ADD_FAVORITE_DATA, FETCH_DATA} from '../constants';
export const setList = (List: String[]) => {
return {
type: FETCH_DATA,
payload: List,
};
}
// export const setFavList = (List: String[]) => {
// return {
// type: ADD_FAVORITE_DATA,
// payload: List,
// };
// }

@ -1,7 +1,6 @@
export const FETCH_DATA = "FETCH_DATA" export const FETCH_DATA = "FETCH_DATA"
export const ADD_FAVORITE_DATA = "ADD_FAVORITE_DATA" export const ADD_FAVORITE_DATA = "ADD_FAVORITE_DATA"
export const DISPLAY_ALL_CARD = "DISPLAY_ALL_CARD" export const SET_FAVS = "SET_FAVS"

@ -1,22 +1,49 @@
import {FETCH_DATA, ADD_FAVORITE_DATA} from '../constants' import { CardProps } from '../../props/favProps'
import {FETCH_DATA, ADD_FAVORITE_DATA, SET_FAVS} from '../constants'
import StorageHeart from '../../service/AsyncStorage'
const initialState = { const initialState = {
cards: [], cards: [],
favoriteCards: [], favoriteCards: []
// cards: ["C_ace", "C_K", "C_Q", "C_J"],
// favoriteCards: [ "C_ace", "C_K"],
} }
// @ts-ignore // @ts-ignore
export default appReducer = (state = initialState, action) => { export default appReducer = (state = initialState, action) => {
switch (action.type) { switch (action.type) {
case ADD_FAVORITE_DATA: case ADD_FAVORITE_DATA: //Ajout d'une carte aux favoris
// @ts-ignore
return {...state, favoriteCards: state.favoriteCards.push(action.payload)}; const a : CardProps = action.payload
case FETCH_DATA: if(a.route.bool ==false){
// @ts-ignore if(state.favoriteCards == undefined){
const tab = [a.route.card]
StorageHeart.setItem("favoriteList",tab)
return {...state, favoriteCards : tab};
}
//@ts-ignore
if( Array.from(state.favoriteCards).every((elem) => elem.id != a.route.card.id)){
//@ts-ignore
const tab = state.favoriteCards.concat([a.route.card])
StorageHeart.setItem("favoriteList",tab)
return {...state, favoriteCards : tab};
}
return {...state}
}
else{
const tab = state.favoriteCards.filter((item) => item!= a.route.card)
StorageHeart.setItem("favoriteList",tab)
return {...state, favoriteCards : tab };
}
case FETCH_DATA: //Récupération des données des cartes depuis l'API
return {...state, cards: action.payload}; return {...state, cards: action.payload};
case SET_FAVS: //Récupération des favoris depuis l'async storage
//@ts-ignore
return {...state, favoriteCards: action.payload}
default: default:
return state; return state;
} }

@ -1,7 +1,6 @@
import {configureStore} from '@reduxjs/toolkit' import {configureStore} from '@reduxjs/toolkit'
import appReducer from './reducers/appReducer'; import appReducer from './reducers/appReducer';
// Reference here all your application reducers
const reducer = { const reducer = {
appReducer: appReducer, appReducer: appReducer,
} }
@ -10,7 +9,6 @@ const reducer = {
const store = configureStore({ const store = configureStore({
// @ts-ignore // @ts-ignore
reducer, reducer,
// @ts-ignore // @ts-ignore
middleware: (getDefaultMiddleware) => getDefaultMiddleware({serializableCheck : false}), //desactive le check de la serialization (primitif) middleware: (getDefaultMiddleware) => getDefaultMiddleware({serializableCheck : false}), //desactive le check de la serialization (primitif)
},); },);

@ -0,0 +1,91 @@
import { StyleSheet, Text, View, Button, FlatList } from 'react-native';
import { StatusBar } from 'expo-status-bar';
import React, { useState } from "react";
import { Card } from '../models/Card';
import { Table, Row, Rows } from 'react-native-table-component';
//* Components
import {ListItemComponent} from '../components/ListItemComponent'
import { ScrollView } from 'react-native-gesture-handler';
//@ts-ignore
export default function DetailMain({ route }){
const { card, other } = route.params;
const tableHead = ['Stat', 'Value'];
const tableData = [
['Mana cost :', card.manaCost],
['Attack : ', card.attack],
['Health : ', card.health],
['Rarity : ', card.rarity],
['Artist : ', card.artistName],
['Class : ', card.classId],
]
let pressed : Boolean = false
const [titleText, setTitleText] = useState(card.name)
const onPressTitle = () => {
pressed ? setTitleText(titleText + '\n' + 'Id : ' + card.id) : setTitleText(card.name)
pressed ? pressed = false :pressed = true
};
return (
<View style={styles.container}>
<View style={styles.titleView}>
<Text style={styles.title} onPress={onPressTitle} >{titleText}</Text>
<Text style={styles.flavor}>{card.flavorText}</Text>
</View>
<View style={styles.item}>
<ListItemComponent url={card.img}/>
</View>
<ScrollView>
<Table borderStyle={{borderWidth: 5, borderColor: '#c8e1ff'}}>
<Row data={tableHead} style={styles.head} textStyle={styles.text}/>
<Rows data={tableData} textStyle={styles.text}/>
</Table>
</ScrollView>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
item: {
borderRadius : 15,
backgroundColor: '#efefef',
padding: 15,
maxHeight:300,
maxWidth:350,
},
title: {
fontStyle: "italic",
fontWeight: "bold",
fontSize: 20,
},
flavor: {
fontStyle: "italic",
},
titleView: {
flex: 1,
justifyContent: 'center',
paddingVertical: 0,
paddingHorizontal : 15,
backgroundColor: '#fff',
borderRadius : 10,
borderWidth: 10,
borderColor: '#efefef',
maxHeight:100,
minHeight: 100
},
head: { height: 40, backgroundColor: '#f1f8ff', minWidth: '90%'},
text: { margin: 6 }
});

@ -1,36 +1,62 @@
import { StyleSheet, Text, View, TouchableNativeFeedback, Image } from 'react-native';
import { useDispatch } from 'react-redux';
import { useEffect } from 'react';
import { getAllCards } from "../redux/actions/actionSelection"
import StorageHeart from '../service/AsyncStorage';
import { setList } from '../redux/actions/action_setFavs';
import Navigation from '../navigation/Navigation';
import { StyleSheet, Text, View, TouchableNativeFeedback } from 'react-native';
import { useNavigation } from '@react-navigation/native';
import { NavigationContainer } from '@react-navigation/native';
import StackNavigation from '../navigation/StackNavigation'
import { Colors } from 'react-native/Libraries/NewAppScreen';
//import Button from 'react-bootstrap/Button'; // @ts-ignore //
// @ts-ignore //(ta gueule pour l'erreur sur navigation)
export default function HomeScreen({navigation}) { export default function HomeScreen({navigation}) {
const dispatch = useDispatch();
//chargement des listes
useEffect(() => {
const loadFavCards = async () => {
//@ts-ignore
const list = await StorageHeart.getItem("favoriteList")
if(list!=null){
//@ts-ignore
dispatch(setList(list))
}
};
loadFavCards();
}, [dispatch]);
useEffect(() => {
const loadCards = async () => {
//@ts-ignore
await dispatch(getAllCards());
};
loadCards();
}, [dispatch]);
// affichage de la homePage
return ( return (
<View style={styles.container}> <View style={styles.container}>
<View style={styles.centered}> <View style={styles.centered}>
<Text style={styles.title}>Mes super Nounours !</Text> <Text style={styles.title}>DeckStone</Text>
</View> </View>
<Text>Mon super texte ...</Text> <Text style={styles.txt}>Votre gestionnaire de cartes Hearthstone </Text>
{/* <MyCustomComponent /> */} {/* <MyCustomComponent /> */}
<View style={styles.MidArea}> <View style={styles.MidArea}>
<Text style={styles.textStyle}>Nous sommes actuellement dans l'écran d'accueil !</Text> <Image source={require("../assets/logo.png")} style={styles.ige} resizeMode='cover' ></Image>
</View> </View>
<View> <View style={styles.butContain}>
<Text style={styles.t3}> Vous cherchez une entités ? </Text>
<TouchableNativeFeedback onPress={() => navigation.navigate("ListScreen")}> <TouchableNativeFeedback onPress={() => navigation.navigate("ListScreen")}>
<Text style={styles.ButtonStyle}> Consulter la liste global !</Text> <Text style={styles.ButtonStyle}>List</Text>
</TouchableNativeFeedback> </TouchableNativeFeedback>
</View>
<View>
<Text style={styles.t3}> Vous avez des entités favorites ? </Text>
<TouchableNativeFeedback onPress={() => navigation.navigate("ListFav")}> <TouchableNativeFeedback onPress={() => navigation.navigate("ListFav")}>
<Text style={styles.ButtonStyle}>Aller sur la page de favoris !</Text> <Text style={styles.ButtonStyle}>Favoris</Text>
</TouchableNativeFeedback> </TouchableNativeFeedback>
</View> </View>
</View> </View>
@ -40,38 +66,43 @@ export default function HomeScreen({navigation}) {
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
flex: 1, flex: 1,
backgroundColor: "darksalmon", backgroundColor: "#ac9585",
alignItems: "center" alignItems: "center"
}, },
centered: { centered: {
alignItems: "center" alignItems: "center"
}, },
title: { title: {
fontSize: 20, fontSize: 56,
fontWeight: 'bold', fontWeight: 'bold',
}, },
MidArea: { MidArea: {
justifyContent: "center", justifyContent: "center",
backgroundColor: "white",
paddingTop: 50,
paddingBottom: 50,
margin: 40,
borderRadius: 15,
}, },
textStyle: { txt: {
textAlign: "center",
fontSize: 20, fontSize: 20,
}, },
butContain: {
flexDirection: 'row'
},
ige:{
maxWidth: "110%",
maxHeight: 400,
},
ButtonStyle :{ ButtonStyle :{
backgroundColor: "#2E8AE6", backgroundColor: "#F5F5F5",
borderRadius: 15, borderRadius: 15,
padding: 20, padding: 20,
color: "white", color: "black",
fontSize : 20, fontSize : 36,
width: "45%",
textAlign: 'center',
margin: 10,
fontWeight: 'bold', fontWeight: 'bold',
}, },
t3 :{
fontSize : 20,
fontWeight: 'bold',
}
}); });

@ -1,91 +1,52 @@
import { StyleSheet, Text, View, Button, FlatList } from 'react-native';
import { StatusBar } from 'expo-status-bar';
import React, { useState } from "react";
import {setFavList } from '../redux/actions/action_setFavList'; import { StyleSheet, View, TouchableHighlight, TextInput } from 'react-native';
import React, { useState} from "react";
import { FlatList } from 'react-native-gesture-handler';
import {useSelector} from 'react-redux';
//redux import { Card } from '../models/Card';
import {useDispatch, useSelector} from 'react-redux'; import Item from '../components/ListeFavComponent';
import {useEffect} from 'react';
export const Cardslist = [
{
id: '1',
title: "premier élément",
},
{
id: '2',
title: "second élément",
},
{
id: '3',
title: "élément",
},
{
id: '4',
title: "barman douteux",
},
{
id: '10',
title: "dernier élément",
}
];
//@ts-ignore //@ts-ignore
const Item = ({title}) => ( export default function ListScreen({navigation}){
<View style={styles.item}>
<Text style={styles.title}>{title}</Text>
</View>
);
//TODO
// export const getFavList = () => {
// //In order to use await your callback must be asynchronous using async keyword.
// return async dispatch => {
// //Then perform your asynchronous operations.
// try {
// //Have it first fetch data from our starwars url.
// //const nounoursPromise = await fetch('https://iut-weather-api.azurewebsites.net/nounours');
// //Then use the json method to get json data from api/
// //const nounoursListJson = await nounoursPromise.json();
// //const nounoursList: Nounours[] = nounoursListJson.map(elt => new Nounours(elt["name"], elt["age"], elt["nbPoils"], elt["image"]));
// dispatch(setFavList(Array{id,title})); //@ts-ignore
// } catch (error) { var nList : Card[] = useSelector(state => state.appReducer.favoriteCards);
// console.log('Error---------', error);
// //You can dispatch to another action if you want to display an error message in the application
// //dispatch(fetchDataRejected(error))
// }
// }
// }
const [searchValue, setSearchValue] = useState('');
const filteredList = nList.filter(item => item.name.toLowerCase().includes(searchValue.toLowerCase()));
export default function Main(){
const [count, setCount] = useState(0);
return ( return (
<View style={styles.container}> <View style={styles.container}>
<Text>Maman, J4AI UNE LISTE DE FAVORIS ! !</Text>
<StatusBar style="auto" /> <TextInput
<Text>{count}</Text> style={styles.textInput}
<Button onPress={()=> setCount(count+1)} title="+1"/> value={searchValue}
<Button onPress={()=> setCount(count+2)} title="+2"/> onChangeText={text => setSearchValue(text)}
<Button onPress={()=> setCount(count+10)} title="+10"/> placeholder="Rechercher une carte...."
/>
<FlatList data={Cardslist}
renderItem={({item}) => <Item title={item.title} />} <FlatList
keyExtractor={item => item.id}/> numColumns={2}
data={filteredList}
renderItem={({item}) =>
<TouchableHighlight onPress={() => navigation.navigate("DetailCard", {card :item, other : 'anything'})}>
<Item route={{
card: item,
bool: true
}} />
</TouchableHighlight>
}
keyExtractor={(item: Card) => item.id}
/>
</View> </View>
); );
} }
@ -94,17 +55,18 @@ export default function Main(){
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
flex: 1, flex: 1,
backgroundColor: '#fff', backgroundColor: '#ac9585',
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'space-evenly',
}, },
item: { textInput: {
padding: 15,
margin: 5,
width:200,
backgroundColor: '#ffffff',
borderRadius : 15, borderRadius : 15,
backgroundColor: '#efefef', shadowColor: 'grey',
padding: 20, textAlign:'center'
margin : 10,
},
title: {
fontStyle: "italic",
} }
}); });

@ -1,92 +1,61 @@
import { StyleSheet, Text, View, Button, TouchableHighlight } from 'react-native'; import { StyleSheet,View,TouchableHighlight, TextInput} from 'react-native';
import { StatusBar } from 'expo-status-bar'; import React, { useState} from "react";
import React, { useState, useEffect } from "react";
import { FlatList } from 'react-native-gesture-handler'; import { FlatList } from 'react-native-gesture-handler';
import {useDispatch, useSelector} from 'react-redux'; import {useSelector} from 'react-redux';
import { ThunkAction } from 'redux-thunk';
//? possiblement à supprimer
import { getAllCards } from "../redux/actions/actionSelection"
import { StubLib } from '../data/stub';
import { Card } from '../models/Card'; import { Card } from '../models/Card';
import { Image } from 'react-native'; import { FontAwesome } from '@expo/vector-icons';
import { ImageURISource } from 'react-native'; import { useDispatch } from "react-redux";
import {CardProps} from "../props/favProps";
import { setFavList } from "../redux/actions/action_setFavList";
//@ts-ignore //* Components
const Item = ({url}) => ( import { ListItemComponent } from '../components/ListItemComponent';
// <View style={styles.item}> import Item from '../components/ListeFavComponent';
// <Text style={styles.title}>{title}</Text>
// </View>
<View>
<Image
source={{uri:url}}
style={{flex:1, minHeight:250, minWidth:180}}/>
</View>
);
//@ts-ignore //@ts-ignore
export default function ListScreen({navigation}){ export default function ListScreen({navigation}){
const [count, setCount] = useState(0);
// // Initialize the binding content with the application initial state
//@ts-ignore //@ts-ignore
const nList = useSelector(state => state.appReducer.cards); var nList = useSelector(state => state.appReducer.cards);
// Create a const that will hold the react-redux events dispatcher
const dispatch = useDispatch();
// Let's define a hook that will be used to update the rendered state after the return will be called
// You cannot perform side-effects outside of a useEffect hook
useEffect(() => {
console.log("USEEFFECT")
const loadCards = async () => {
//@ts-ignore
await dispatch(getAllCards());
};
loadCards();
}, [dispatch]);
//* Search :
const [searchValue, setSearchValue] = useState('');
//* Stub //@ts-ignore
// const {getCards} = new StubLib(); const filteredList = nList.filter(item => item.name.toLowerCase().includes(searchValue.toLowerCase()));
// const list: Card[] = getCards();
// const req = fetch('https://omgvamp-hearthstone-v1.p.rapidapi.com/cards')
//https://us.api.blizzard.com/hearthstone/cards/678?locale=en_US const dispatch = useDispatch()
const HandleAddFav = (props : CardProps) => {
dispatch(setFavList(props));
}
return ( return (
<View style={styles.container}> <View style={styles.container}>
{/* <FlatList data={nList}
renderItem={({item}) => <Item title={item.name} />} <TextInput
keyExtractor={item => item.id}/> */} style={styles.textInput}
value={searchValue}
onChangeText={text => setSearchValue(text)}
placeholder="Rechercher une carte..."
/>
<FlatList <FlatList
numColumns={2} numColumns={2}
data={nList} data={filteredList}
renderItem={({item}) => renderItem={({item}) =>
<View>
//<TouchableHighlight onPress={() => navigation.navigate("CardsDetails", {"card": item})}> //* mettre la page de detail ici, renvoi a home pour l'instant
<TouchableHighlight onPress={() => navigation.navigate("ListFav")}>
<Item url={item.img}/> <TouchableHighlight style={styles.imageItem} onPress={() => navigation.navigate("DetailCard", {card :item, other : 'anything'})}>
</TouchableHighlight> <Item route={{
card: item,
// //<Text>{item.name}</Text> bool: false
// // <View> }} ></Item>
// // <Image </TouchableHighlight>
// // source={{uri:item.img}} </View>
// // style={{flex:1, minHeight:250, minWidth:180}}/> }
keyExtractor={(item: Card) => item.id}
// // </View> />
} keyExtractor={(item: Card) => item.id.toString()}/>
</View> </View>
@ -94,8 +63,10 @@ export default function ListScreen({navigation}){
} }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
imageItem:{
zIndex : 0
},
container: { container: {
flex: 1, flex: 1,
backgroundColor: '#ac9585', backgroundColor: '#ac9585',
@ -103,23 +74,32 @@ const styles = StyleSheet.create({
justifyContent: 'space-evenly', justifyContent: 'space-evenly',
}, },
border: { textInput: {
flex: 1, padding: 15,
backgroundColor: '#ff0000', margin: 5,
maxHeight : 100, width:200,
borderWidth : 15, backgroundColor: '#ffffff',
borderRadius : 15, borderRadius : 15,
borderColor : '#00ffaa', shadowColor: 'grey',
alignItems: 'center', textAlign:'center'
justifyContent: 'center',
}, },
item: { favoriteButtonNonFav: {
borderRadius : 15, position: 'absolute',
backgroundColor: '#efefef', top: 10,
padding: 20, right: 10,
margin : 10, backgroundColor: 'red',
borderRadius: 50,
padding: 10,
zIndex : 1,
}, },
title: { favoriteButtonFav: {
fontStyle: "italic", position: 'absolute',
top: 10,
right: 10,
backgroundColor: 'red',
borderRadius: 50,
padding: 10,
zIndex : 1,
} }
}); });

@ -0,0 +1,28 @@
import { Card } from "../models/Card";
import AsyncStorage from "@react-native-async-storage/async-storage";
export default class StorageHeart {
static async getItem(key: string): Promise<any> {
try {
const value = await AsyncStorage.getItem(key);
if (value !== null) {
return JSON.parse(value);
}
} catch (e) {
console.error(`AsyncStorage getItem error: ${e}`);
}
return null;
}
static async setItem(key: string, value: any): Promise<void> {
try {
await AsyncStorage.setItem(key, JSON.stringify(value));
} catch (e) {
console.error(`AsyncStorage setItem error: ${e}`);
}
}
}

@ -0,0 +1,36 @@
import { configureStore } from "@reduxjs/toolkit";
import testReducer from "../setup/testReducer";
import { Provider } from "react-redux";
import { render } from "react-dom";
import Item from "../../components/ListItemComponent";
jest.useFakeTimers();
const store = configureStore({
reducer: {
appReducer: testReducer,
},
middleware: (getDefaultMiddleWare) =>
getDefaultMiddleWare({
serializableCheck: false
})
});
const Wrapper = ({children}) => (<Provider store={store}>{children}</Provider>);
describe('<Item/>', ()=> {
test('Assert displayed values', () =>{
const expectedCardInfos = store.getState().appReducer.cards[0];
render(<Wrapper>
<Item route={{
card: expectedCardInfos,
bool: false
}} ></Item>
</Wrapper>)
expect(screen.getByTestId('card-url')).toHaveProperty("source", {uri: expectedCardInfos.img})
})
})

@ -0,0 +1,44 @@
import { configureStore } from "@reduxjs/toolkit";
import testReducer from "../setup/testReducer";
import { Provider } from "react-redux";
import { render } from "react-dom";
import Item from "../../components/ListItemComponent";
import { fireEvent } from "@testing-library/react-native";
import AsyncStorageMock from "../../__mocks__/@react-native-community/async-storage";
jest.useFakeTimers();
const store = configureStore({
reducer: {
appReducer: testReducer,
},
middleware: (getDefaultMiddleWare) =>
getDefaultMiddleWare({
serializableCheck: false
})
});
const Wrapper = ({children}) => (<Provider store={store}>{children}</Provider>);
describe('<Item/>', ()=> {
test('Assert displayed values for fav list', () =>{
const expectedCardInfos = store.getState().appReducer.favoriteCards[0];
render(<Wrapper>
<Item route={{
card: expectedCardInfos,
bool: true,
}} ></Item>
</Wrapper>)
expect(screen.getByTestId('card-url')).toHaveProperty("source", {uri: expectedCardInfos.img})
let size = store.getState().appReducer.favoriteCards.length;
fireEvent.press(screen.getByTestId("button"))
expect(store.getState().appReducer.favoriteCards.length).toBe(size - 1)
})
});

@ -0,0 +1,14 @@
import { Card } from "../../models/Card";
import { setCardsList } from "../../redux/actions/action_setCardsList";
describe('setCardLIst',() => {
it('should take the list', () => {
const payload = [new Card("1","test1","",""),new Card("2","test2","","",true)];
const expectation = {
type: "FETCH_DATA",
payload: payload,
};
expect(setCardsList(payload)).toEqual(expectation);
})
})

@ -0,0 +1,16 @@
import { Card } from "../../models/Card";
import { setFavList } from "../../redux/actions/action_setFavList";
import { setList } from "../../redux/actions/action_setFavs";
describe('setFavs',() => {
it('should take the list', () => {
const payload = [new Card("1","test1","",""),new Card("2","test2","","",true)];
const expectation = {
type: "SET_FAVS",
payload: payload,
};
expect(setList(payload)).toEqual(expectation);
})
})

@ -0,0 +1,25 @@
import appReducer from "../../redux/reducers/appReducer";
describe('Test Reducer', () => {
let initialState = {
cards: [],
favoriteCards: []
}
it('should return initial state', () => {
expect(appReducer(undefined, {})).toEqual(initialState);
});
it('should handle FETCH_DATA', () => {
const payload = [new Card("1","test1","",""),new Card("2","test2","","",true)];
expect(
appReducer(initialState, {
type: "FETCH_DATA",
payload,
})
).toEqual({
cards: payload,
favoriteCards: [],
});
});
});

@ -0,0 +1,3 @@
import mockAsyncStorage from '@react-native-async-storage/async-storage/jest/async-storage-mock';
jest.mock('@react-native-community/async-storage', () => mockAsyncStorage);

@ -0,0 +1,13 @@
import { Card } from "../../models/Card";
import appReducer from "../../redux/reducers/appReducer"
const initialState = {
cards: [new Card("1","test1","url1","urlGold2")],
favoriteCards: [new Card("1","test1","url1","urlGold2")]
}
//@ts-ignore
export default testReducer = (state = initialState, action) => {
//@ts-ignore
return appReducer(initialState,action);
}

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save