Compare commits
17 Commits
Author | SHA1 | Date |
---|---|---|
|
f11ca02ec4 | 1 year ago |
|
a043f05832 | 1 year ago |
|
0eee3563de | 1 year ago |
|
d5786ea613 | 1 year ago |
|
cde37fcdd8 | 1 year ago |
|
22d856df67 | 1 year ago |
|
f6210d7825 | 1 year ago |
|
b49beeb1e0 | 1 year ago |
|
1c2518a02a | 1 year ago |
|
d2cfa7b992 | 1 year ago |
|
f6a01ff7db | 1 year ago |
|
6bd58278a0 | 1 year ago |
|
57a846972c | 1 year ago |
|
5bd99267bd | 1 year ago |
|
e69ed4beeb | 1 year ago |
|
3cc455b3a5 | 1 year ago |
|
7820822622 | 1 year ago |
@ -0,0 +1,17 @@
|
|||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: JokesApp
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: sonar-analyses
|
||||||
|
image: hub.codefirst.iut.uca.fr/camille.petitalot/drone-sonarplugin-reactnative:latest
|
||||||
|
commands:
|
||||||
|
- cd src
|
||||||
|
- npm install
|
||||||
|
- sonar-scanner -Dsonar.projectKey=JokesAppReact_A-PE -Dsonar.sources=. -Dsonar.host.url=$${PLUGIN_SONAR_HOST}
|
||||||
|
-Dsonar.login=$${PLUGIN_SONAR_TOKEN}
|
||||||
|
secrets: [ SECRET_SONAR_LOGIN ]
|
||||||
|
settings:
|
||||||
|
sonar_host: https://codefirst.iut.uca.fr/sonar/
|
||||||
|
sonar_token:
|
||||||
|
from_secret: SECRET_SONAR_LOGIN
|
@ -1,5 +1,32 @@
|
|||||||
|
import {Theme} from "@react-navigation/native";
|
||||||
|
|
||||||
export const indigoColor = "rgba(14, 14, 44, 1)";
|
export const indigoColor = "rgba(14, 14, 44, 1)";
|
||||||
export const purpleColor = "rgba(74, 74, 104, 1)";
|
export const purpleColor = "rgba(74, 74, 104, 1)";
|
||||||
export const darksalmonColor = "rgba(233, 150, 122, 1)";
|
export const darksalmonColor = "rgba(233, 150, 122, 1)";
|
||||||
export const greyColor = "rgba(140, 140, 161, 1)";
|
export const greyColor = "rgba(140, 140, 161, 1)";
|
||||||
export const whiteColor = "rgba(239, 239, 253, 1)";
|
export const whiteColor = "rgba(239, 239, 253, 1)";
|
||||||
|
|
||||||
|
|
||||||
|
export const LightTheme: Theme = {
|
||||||
|
dark: false,
|
||||||
|
colors: {
|
||||||
|
primary: darksalmonColor,
|
||||||
|
background: whiteColor,
|
||||||
|
card: greyColor,
|
||||||
|
text: "black",
|
||||||
|
border: whiteColor,
|
||||||
|
notification: 'rgb(255, 59, 48)',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DarkTheme: Theme = {
|
||||||
|
dark: true,
|
||||||
|
colors: {
|
||||||
|
primary: darksalmonColor,
|
||||||
|
background: purpleColor,
|
||||||
|
card: indigoColor,
|
||||||
|
text: whiteColor,
|
||||||
|
border: greyColor,
|
||||||
|
notification: 'rgb(255, 69, 58)',
|
||||||
|
},
|
||||||
|
};
|
After Width: | Height: | Size: 209 B |
After Width: | Height: | Size: 315 B |
After Width: | Height: | Size: 301 B |
@ -0,0 +1,26 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { shallow } from 'enzyme';
|
||||||
|
import Categ from './Categ';
|
||||||
|
import { Text, View } from 'react-native';
|
||||||
|
import {describe, expect, it} from "@jest/globals";
|
||||||
|
|
||||||
|
describe('Categ Component', () => {
|
||||||
|
it('renders correctly', () => {
|
||||||
|
const category = { name: 'Test Category' };
|
||||||
|
const wrapper = shallow(<Categ category={category} />);
|
||||||
|
expect(wrapper).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays category name', () => {
|
||||||
|
const category = { name: 'Test Category' };
|
||||||
|
const wrapper = shallow(<Categ category={category} />);
|
||||||
|
expect(wrapper.find(Text).prop('children')).toEqual(category.name);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('applies correct styles', () => {
|
||||||
|
const category = { name: 'Test Category' };
|
||||||
|
const wrapper = shallow(<Categ category={category} />);
|
||||||
|
const containerStyle = wrapper.find(View).prop('style');
|
||||||
|
expect(containerStyle).toEqual(expect.objectContaining(styles.bottomContainer));
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,36 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { shallow } from 'enzyme';
|
||||||
|
import Categs from './Categs';
|
||||||
|
import { FlatList } from 'react-native';
|
||||||
|
import Categ from './Categ';
|
||||||
|
import { Category } from '../model/Category';
|
||||||
|
import {describe, expect, it} from "@jest/globals";
|
||||||
|
|
||||||
|
describe('Categs Component', () => {
|
||||||
|
const categories: Category[] = [
|
||||||
|
new Category('Category 1', 10),
|
||||||
|
new Category('Category 2', 5),
|
||||||
|
new Category('Category 3', 15),
|
||||||
|
];
|
||||||
|
|
||||||
|
it('renders correctly', () => {
|
||||||
|
const wrapper = shallow(<Categs categories={categories} />);
|
||||||
|
expect(wrapper).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders FlatList with correct props', () => {
|
||||||
|
const wrapper = shallow(<Categs categories={categories} />);
|
||||||
|
const flatList = wrapper.find(FlatList);
|
||||||
|
expect(flatList.exists()).toBeTruthy();
|
||||||
|
expect(flatList.prop('showsHorizontalScrollIndicator')).toBe(false);
|
||||||
|
expect(flatList.prop('horizontal')).toBe(true);
|
||||||
|
expect(flatList.prop('data')).toEqual(categories.sort((a, b) => b.number - a.number));
|
||||||
|
expect(flatList.prop('keyExtractor')).toBeInstanceOf(Function);
|
||||||
|
expect(flatList.prop('renderItem')).toBeInstanceOf(Function);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders correct number of Categ components', () => {
|
||||||
|
const wrapper = shallow(<Categs categories={categories} />);
|
||||||
|
expect(wrapper.find(Categ)).toHaveLength(categories.length);
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,54 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { render, fireEvent } from '@testing-library/react-native';
|
||||||
|
import JokeDetail from '../path/to/JokeDetail';
|
||||||
|
import {describe} from "@jest/globals"; // Remplacez le chemin par le bon chemin
|
||||||
|
|
||||||
|
describe('JokeDetail Component', () => {
|
||||||
|
const sampleJoke = {
|
||||||
|
id: '1',
|
||||||
|
type: 'Sample',
|
||||||
|
image: 'sample-image-url',
|
||||||
|
description: () => 'Sample Joke Description'
|
||||||
|
};
|
||||||
|
|
||||||
|
const customJoke = {
|
||||||
|
id: '2',
|
||||||
|
type: 'Custom',
|
||||||
|
image: 'custom-image-url',
|
||||||
|
description: () => 'Custom Joke Description'
|
||||||
|
};
|
||||||
|
|
||||||
|
test('renders joke image', () => {
|
||||||
|
const { getByTestId } = render(<JokeDetail joke={sampleJoke} />);
|
||||||
|
const jokeImage = getByTestId('joke-image');
|
||||||
|
expect(jokeImage).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('renders joke description when toggled', () => {
|
||||||
|
const { getByText } = render(<JokeDetail joke={sampleJoke} />);
|
||||||
|
const toggleButton = getByText('LA CHUTE');
|
||||||
|
fireEvent.press(toggleButton);
|
||||||
|
const jokeDescription = getByText('Sample Joke Description');
|
||||||
|
expect(jokeDescription).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('toggles favorite icon when pressed', () => {
|
||||||
|
const { getByTestId } = render(<JokeDetail joke={sampleJoke} />);
|
||||||
|
const favoriteButton = getByTestId('favorite-button');
|
||||||
|
fireEvent.press(favoriteButton);
|
||||||
|
expect(favoriteButton.props.source).toEqual(require('../assets/plain_favorite_icon.png'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('calls deleteCustomJokes and updates list when delete button is pressed for custom joke', () => {
|
||||||
|
const deleteMock = jest.fn();
|
||||||
|
const dispatchMock = jest.fn();
|
||||||
|
jest.mock('../redux/thunk/DeleteThunk', () => ({ deleteCustomJoke: deleteMock }));
|
||||||
|
jest.mock('react-redux', () => ({ useDispatch: () => dispatchMock }));
|
||||||
|
|
||||||
|
const { getByTestId } = render(<JokeDetail joke={customJoke} />);
|
||||||
|
const deleteButton = getByTestId('delete-button');
|
||||||
|
fireEvent.press(deleteButton);
|
||||||
|
expect(deleteMock).toHaveBeenCalledWith(customJoke.id);
|
||||||
|
expect(dispatchMock).toHaveBeenCalledTimes(2); // Assuming one dispatch for delete and one for update list
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,25 @@
|
|||||||
|
import { Category } from './Category';
|
||||||
|
import {describe, expect, it} from "@jest/globals";
|
||||||
|
|
||||||
|
describe('Category Class Constructor', () => {
|
||||||
|
it('should create a new Category object', () => {
|
||||||
|
const category = new Category('name', 5);
|
||||||
|
expect(category).toBeDefined();
|
||||||
|
expect(category.name).toBe('name');
|
||||||
|
expect(category.number).toBe(5);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Category Class Accessors', () => {
|
||||||
|
it('should set and get the name correctly', () => {
|
||||||
|
const category = new Category('name', 5);
|
||||||
|
category.name = 'newName';
|
||||||
|
expect(category.name).toBe('newName');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set and get the number correctly', () => {
|
||||||
|
const category = new Category('name', 5);
|
||||||
|
category.number = 10;
|
||||||
|
expect(category.number).toBe(10);
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,35 @@
|
|||||||
|
import { CustomJoke } from './CustomJoke';
|
||||||
|
import {describe, expect, test} from "@jest/globals"; // Remplacez le chemin par le bon chemin
|
||||||
|
|
||||||
|
describe('CustomJoke Class', () => {
|
||||||
|
const id = '1';
|
||||||
|
const type = 'Custom';
|
||||||
|
const setup = 'Why did the developer go broke?';
|
||||||
|
const image = 'custom-image-url';
|
||||||
|
const punchline = 'Because he used up all his cache';
|
||||||
|
|
||||||
|
test('creates a new instance of CustomJoke', () => {
|
||||||
|
const customJoke = new CustomJoke(id, type, setup, image, punchline);
|
||||||
|
expect(customJoke).toBeInstanceOf(CustomJoke);
|
||||||
|
expect(customJoke.id).toEqual(id);
|
||||||
|
expect(customJoke.type).toEqual(type);
|
||||||
|
expect(customJoke.setup).toEqual(setup);
|
||||||
|
expect(customJoke.punchline).toEqual(punchline);
|
||||||
|
expect(customJoke.image).toEqual(image);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('updates CustomJoke properties', () => {
|
||||||
|
const newSetup = 'Why do programmers prefer dark mode?';
|
||||||
|
const newImage = 'new-custom-image-url';
|
||||||
|
const newPunchline = 'Because light attracts bugs';
|
||||||
|
const customJoke = new CustomJoke(id, type, setup, image, punchline);
|
||||||
|
|
||||||
|
customJoke.setup = newSetup;
|
||||||
|
customJoke.image = newImage;
|
||||||
|
customJoke.punchline = newPunchline;
|
||||||
|
|
||||||
|
expect(customJoke.setup).toEqual(newSetup);
|
||||||
|
expect(customJoke.image).toEqual(newImage);
|
||||||
|
expect(customJoke.punchline).toEqual(newPunchline);
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,61 @@
|
|||||||
|
const { Joke } = require('./Joke');
|
||||||
|
const {expect, it, beforeEach, describe} = require("@jest/globals");
|
||||||
|
|
||||||
|
// Mock class extending the abstract Joke class
|
||||||
|
class MockJoke extends Joke {
|
||||||
|
constructor(type, setup, punchline, image) {
|
||||||
|
super(type, setup, punchline, image);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test the Joke class
|
||||||
|
describe('Joke Class', () => {
|
||||||
|
let joke;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
joke = new MockJoke('type', 'setup', 'punchline', 'image');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test the constructor
|
||||||
|
it('should create a new Joke object', () => {
|
||||||
|
expect(joke).toBeDefined();
|
||||||
|
expect(joke.type).toBe('type');
|
||||||
|
expect(joke.setup).toBe('setup');
|
||||||
|
expect(joke.punchline).toBe('punchline');
|
||||||
|
expect(joke.image).toBe('image');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test the summary() method
|
||||||
|
it('should return a summary of the joke', () => {
|
||||||
|
expect(joke.summary()).toBe('punchline');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test the description() method
|
||||||
|
it('should return a textual description of the joke', () => {
|
||||||
|
expect(joke.description()).toBe('type, punchline');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test setting and getting the type
|
||||||
|
it('should set and get the type correctly', () => {
|
||||||
|
joke.type = 'newType';
|
||||||
|
expect(joke.type).toBe('newType');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test setting and getting the setup
|
||||||
|
it('should set and get the setup correctly', () => {
|
||||||
|
joke.setup = 'newSetup';
|
||||||
|
expect(joke.setup).toBe('newSetup');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test setting and getting the punchline
|
||||||
|
it('should set and get the punchline correctly', () => {
|
||||||
|
joke.punchline = 'newPunchline';
|
||||||
|
expect(joke.punchline).toBe('newPunchline');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test setting and getting the image
|
||||||
|
it('should set and get the image correctly', () => {
|
||||||
|
joke.image = 'newImage';
|
||||||
|
expect(joke.image).toBe('newImage');
|
||||||
|
});
|
||||||
|
});
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,23 @@
|
|||||||
|
import { ActionType, Action, setCategoriesList } from './CategoryAction';
|
||||||
|
import { Category } from '../../model/Category';
|
||||||
|
import {describe, expect, it} from "@jest/globals";
|
||||||
|
|
||||||
|
describe('Actions', () => {
|
||||||
|
describe('setCategoriesList', () => {
|
||||||
|
it('should create an action to set categories list', () => {
|
||||||
|
const categoriesList: Category[] = [
|
||||||
|
new Category('Category 1', 1),
|
||||||
|
new Category('Category 2', 2)
|
||||||
|
];
|
||||||
|
|
||||||
|
const expectedAction: Action = {
|
||||||
|
type: ActionType.FETCH_CATEGORIES,
|
||||||
|
payload: categoriesList,
|
||||||
|
};
|
||||||
|
|
||||||
|
const action = setCategoriesList(categoriesList);
|
||||||
|
|
||||||
|
expect(action).toEqual(expectedAction);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,56 @@
|
|||||||
|
import { ActionType, Action, setPostJoke, setCustomJokesList, setCustomJokeById, setDeleteJoke } from './CustomJoke';
|
||||||
|
import { CustomJoke } from '../../model/CustomJoke';
|
||||||
|
import {describe, expect, it} from "@jest/globals";
|
||||||
|
|
||||||
|
describe('Actions', () => {
|
||||||
|
describe('setPostJoke', () => {
|
||||||
|
it('should create an action to post a custom joke', () => {
|
||||||
|
const customJoke: CustomJoke = new CustomJoke('1', 'pun', 'Setup', 'https://example.com/image.jpg', 'Punchline');
|
||||||
|
const expectedAction: Action = {
|
||||||
|
type: ActionType.POST_CUSTOM_JOKE,
|
||||||
|
payload: customJoke,
|
||||||
|
};
|
||||||
|
const action = setPostJoke(customJoke);
|
||||||
|
expect(action).toEqual(expectedAction);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setCustomJokesList', () => {
|
||||||
|
it('should create an action to set custom jokes list', () => {
|
||||||
|
const customJokesList: CustomJoke[] = [
|
||||||
|
new CustomJoke('1', 'pun', 'Setup 1', 'https://example.com/image1.jpg', 'Punchline 1'),
|
||||||
|
new CustomJoke('2', 'pun', 'Setup 2', 'https://example.com/image2.jpg', 'Punchline 2')
|
||||||
|
];
|
||||||
|
const expectedAction: Action = {
|
||||||
|
type: ActionType.FETCH_CUSTOM_JOKES,
|
||||||
|
payload: customJokesList,
|
||||||
|
};
|
||||||
|
const action = setCustomJokesList(customJokesList);
|
||||||
|
expect(action).toEqual(expectedAction);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setCustomJokeById', () => {
|
||||||
|
it('should create an action to set custom joke by ID', () => {
|
||||||
|
const customJoke: CustomJoke = new CustomJoke('1', 'pun', 'Setup', 'https://example.com/image.jpg', 'Punchline');
|
||||||
|
const expectedAction: Action = {
|
||||||
|
type: ActionType.FETCH_JOKES_BY_ID,
|
||||||
|
payload: customJoke,
|
||||||
|
};
|
||||||
|
const action = setCustomJokeById(customJoke);
|
||||||
|
expect(action).toEqual(expectedAction);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setDeleteJoke', () => {
|
||||||
|
it('should create an action to delete a custom joke', () => {
|
||||||
|
const jokeId = '1';
|
||||||
|
const expectedAction: Action = {
|
||||||
|
type: ActionType.DELETE_CUSTOM_JOKE,
|
||||||
|
payload: jokeId,
|
||||||
|
};
|
||||||
|
const action = setDeleteJoke(jokeId);
|
||||||
|
expect(action).toEqual(expectedAction);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,18 @@
|
|||||||
|
|
||||||
|
export enum ActionType {
|
||||||
|
SET_THEME = 'SET_THEME',
|
||||||
|
}
|
||||||
|
|
||||||
|
type actionFetch = {
|
||||||
|
type: ActionType.SET_THEME;
|
||||||
|
payload: String;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Action = actionFetch;
|
||||||
|
|
||||||
|
export const setTheme = (theme) => {
|
||||||
|
return {
|
||||||
|
type: ActionType.SET_THEME,
|
||||||
|
payload: theme,
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
import {CustomJoke} from "../../model/CustomJoke";
|
||||||
|
import {SampleJoke} from "../../model/SampleJoke";
|
||||||
|
|
||||||
|
export enum ActionType {
|
||||||
|
ADD_FAVORITE = 'ADD_CUSTOM_FAVORITE',
|
||||||
|
REMOVE_FAVORITE = "REMOVE_CUSTOM_FAVORITE"
|
||||||
|
}
|
||||||
|
|
||||||
|
type actionAddFetch = {
|
||||||
|
type: ActionType.ADD_FAVORITE;
|
||||||
|
payload: string;
|
||||||
|
}
|
||||||
|
type actionRemoveFetch = {
|
||||||
|
type: ActionType.REMOVE_FAVORITE;
|
||||||
|
payload: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Action = actionAddFetch | actionRemoveFetch;
|
||||||
|
|
||||||
|
export const addFavorite = (joke: CustomJoke | SampleJoke) => ({
|
||||||
|
type: ActionType.ADD_FAVORITE,
|
||||||
|
payload: joke,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const removeFavorite = (joke: CustomJoke | SampleJoke) => ({
|
||||||
|
type: ActionType.REMOVE_FAVORITE,
|
||||||
|
payload: joke,
|
||||||
|
});
|
@ -0,0 +1,32 @@
|
|||||||
|
import { ActionType, Action, setJokesList, setJokeById } from './SampleAction';
|
||||||
|
import { SampleJoke } from '../../model/SampleJoke';
|
||||||
|
import {describe, expect, it} from "@jest/globals";
|
||||||
|
|
||||||
|
describe('Actions', () => {
|
||||||
|
describe('setJokesList', () => {
|
||||||
|
it('should create an action to set jokes list', () => {
|
||||||
|
const jokesList: SampleJoke[] = [
|
||||||
|
new SampleJoke(1, 'pun', 'Setup 1', 'https://example.com/image1.jpg', 'Punchline 1'),
|
||||||
|
new SampleJoke(2, 'pun', 'Setup 2', 'https://example.com/image2.jpg', 'Punchline 2')
|
||||||
|
];
|
||||||
|
const expectedAction: Action = {
|
||||||
|
type: ActionType.FETCH_JOKES,
|
||||||
|
payload: jokesList,
|
||||||
|
};
|
||||||
|
const action = setJokesList(jokesList);
|
||||||
|
expect(action).toEqual(expectedAction);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setJokeById', () => {
|
||||||
|
it('should create an action to set joke by ID', () => {
|
||||||
|
const joke: SampleJoke = new SampleJoke(1, 'pun', 'Setup', 'https://example.com/image.jpg', 'Punchline');
|
||||||
|
const expectedAction: Action = {
|
||||||
|
type: ActionType.FETCH_JOKES_BY_ID,
|
||||||
|
payload: joke,
|
||||||
|
};
|
||||||
|
const action = setJokeById(joke);
|
||||||
|
expect(action).toEqual(expectedAction);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,23 @@
|
|||||||
|
import categoryReducer from './CategoryReducer';
|
||||||
|
import { ActionType as CategoryActionType } from '../actions/CategoryAction';
|
||||||
|
import { Category } from '../../model/Category';
|
||||||
|
import {describe, expect, it} from "@jest/globals";
|
||||||
|
|
||||||
|
describe('Category Reducer', () => {
|
||||||
|
|
||||||
|
it('should handle FETCH_CATEGORIES', () => {
|
||||||
|
const categories = [
|
||||||
|
new Category('Category 1', 1),
|
||||||
|
new Category('Category 2', 2)
|
||||||
|
];
|
||||||
|
|
||||||
|
const action = {
|
||||||
|
type: CategoryActionType.FETCH_CATEGORIES,
|
||||||
|
payload: categories
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(categoryReducer(undefined, action)).toEqual({
|
||||||
|
categories: categories
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,29 @@
|
|||||||
|
import customReducer from './CustomReducer';
|
||||||
|
import { ActionType } from '../actions/CustomJoke';
|
||||||
|
import { CustomJoke } from '../../model/CustomJoke';
|
||||||
|
import {describe, expect, it} from "@jest/globals";
|
||||||
|
|
||||||
|
describe('Custom Reducer', () => {
|
||||||
|
it('should return the initial state', () => {
|
||||||
|
expect(customReducer(undefined, {})).toEqual({
|
||||||
|
customJokes: [],
|
||||||
|
customJoke: null,
|
||||||
|
jokeId: ''
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle POST_CUSTOM_JOKE', () => {
|
||||||
|
const customJoke: CustomJoke = new CustomJoke('1', 'pun', 'Setup', 'https://example.com/image.jpg', 'Punchline');
|
||||||
|
|
||||||
|
const action = {
|
||||||
|
type: ActionType.POST_CUSTOM_JOKE,
|
||||||
|
payload: customJoke
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(customReducer(undefined, action)).toEqual({
|
||||||
|
customJokes: [],
|
||||||
|
customJoke: customJoke,
|
||||||
|
jokeId: ''
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,21 @@
|
|||||||
|
import {Action, ActionType} from "../actions/DarkModeAction";
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
theme: String
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialState = {
|
||||||
|
theme: 'dark'
|
||||||
|
}
|
||||||
|
|
||||||
|
const themeReducer = (state: State = initialState, action: Action) => {
|
||||||
|
switch (action.type) {
|
||||||
|
case ActionType.SET_THEME:
|
||||||
|
// @ts-ignore
|
||||||
|
return {...state, theme: action.payload};
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default themeReducer;
|
@ -0,0 +1,22 @@
|
|||||||
|
import themeReducer from './DarkModeReducer';
|
||||||
|
import { ActionType } from '../actions/DarkModeAction';
|
||||||
|
import {describe, expect, it} from "@jest/globals";
|
||||||
|
|
||||||
|
describe('Theme Reducer', () => {
|
||||||
|
// it('should return the initial state', () => {
|
||||||
|
// expect(themeReducer(undefined, {})).toEqual({
|
||||||
|
// theme: 'dark'
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
|
||||||
|
it('should handle SET_THEME', () => {
|
||||||
|
const action = {
|
||||||
|
type: ActionType.SET_THEME,
|
||||||
|
payload: 'light'
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(themeReducer(undefined, action)).toEqual({
|
||||||
|
theme: 'light'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,26 @@
|
|||||||
|
import {Action, ActionType} from "../actions/FavoriteAction";
|
||||||
|
import {CustomJoke} from "../../model/CustomJoke";
|
||||||
|
import {SampleJoke} from "../../model/SampleJoke";
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
joke: CustomJoke | SampleJoke
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialState = {
|
||||||
|
joke: new CustomJoke('', '', '', '', '')
|
||||||
|
}
|
||||||
|
|
||||||
|
const favoriteReducer = (state: State = initialState, action: Action) => {
|
||||||
|
switch (action.type) {
|
||||||
|
case ActionType.ADD_FAVORITE:
|
||||||
|
// @ts-ignore
|
||||||
|
return {...state, joke: action.payload};
|
||||||
|
case ActionType.REMOVE_FAVORITE:
|
||||||
|
// @ts-ignore
|
||||||
|
return {...state, joke: action.payload};
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default favoriteReducer;
|
@ -0,0 +1,61 @@
|
|||||||
|
// Importez les réducteurs et les types d'action appropriés
|
||||||
|
import categoryReducer from './CategoryReducer';
|
||||||
|
import customReducer from './CustomReducer';
|
||||||
|
import themeReducer from './DarkModeReducer';
|
||||||
|
import sampleReducer from './SampleReducer';
|
||||||
|
import { ActionType as CategoryActionType } from '../actions/CategoryAction';
|
||||||
|
import { ActionType as CustomActionType } from '../actions/CustomJoke';
|
||||||
|
import { ActionType as ThemeActionType } from '../actions/DarkModeAction';
|
||||||
|
import { ActionType as SampleActionType } from '../actions/SampleAction';
|
||||||
|
import { Category } from '../../model/Category';
|
||||||
|
import { CustomJoke } from '../../model/CustomJoke';
|
||||||
|
import { SampleJoke } from '../../model/SampleJoke';
|
||||||
|
import {describe, expect, it} from "@jest/globals";
|
||||||
|
|
||||||
|
// Tester categoryReducer
|
||||||
|
describe('Category Reducer', () => {
|
||||||
|
it('should return the initial state', () => {
|
||||||
|
expect(categoryReducer(undefined, {})).toEqual({
|
||||||
|
categories: []
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Ajouter d'autres tests pour categoryReducer si nécessaire...
|
||||||
|
});
|
||||||
|
|
||||||
|
// Tester customReducer
|
||||||
|
describe('Custom Reducer', () => {
|
||||||
|
it('should return the initial state', () => {
|
||||||
|
expect(customReducer(undefined, {})).toEqual({
|
||||||
|
customJokes: [],
|
||||||
|
customJoke: null,
|
||||||
|
jokeId: ''
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Ajouter d'autres tests pour customReducer si nécessaire...
|
||||||
|
});
|
||||||
|
|
||||||
|
// Tester themeReducer
|
||||||
|
describe('Theme Reducer', () => {
|
||||||
|
it('should return the initial state', () => {
|
||||||
|
expect(themeReducer(undefined, {})).toEqual({
|
||||||
|
theme: 'dark'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Ajouter d'autres tests pour themeReducer si nécessaire...
|
||||||
|
});
|
||||||
|
|
||||||
|
// Tester sampleReducer
|
||||||
|
describe('Sample Reducer', () => {
|
||||||
|
it('should return the initial state', () => {
|
||||||
|
expect(sampleReducer(undefined, {})).toEqual({
|
||||||
|
jokes: [],
|
||||||
|
favoriteJokes: [],
|
||||||
|
joke: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Ajouter d'autres tests pour sampleReducer si nécessaire...
|
||||||
|
});
|
@ -0,0 +1,29 @@
|
|||||||
|
import store from './store';
|
||||||
|
import {describe, expect, it} from "@jest/globals";
|
||||||
|
|
||||||
|
describe('Redux Store Configuration', () => {
|
||||||
|
it('should have sampleReducer', () => {
|
||||||
|
expect(store.getState().sampleReducer).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have categoryReducer', () => {
|
||||||
|
expect(store.getState().categoryReducer).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have customReducer', () => {
|
||||||
|
expect(store.getState().customReducer).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have themeReducer', () => {
|
||||||
|
expect(store.getState().theme).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have favoriteReducer', () => {
|
||||||
|
expect(store.getState().favorite).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have a configured store', () => {
|
||||||
|
expect(store).toBeDefined();
|
||||||
|
expect(store.getState()).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,28 @@
|
|||||||
|
import { setDeleteJoke } from "../actions/CustomJoke";
|
||||||
|
|
||||||
|
export const deleteItem = (id: string) => {
|
||||||
|
return async dispatch => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`https://iut-weather-api.azurewebsites.net/jokes/${id}`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: {
|
||||||
|
Accept: "application/json",
|
||||||
|
"Content-Type": 'application/json',
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
dispatch(setDeleteJoke(id)); // Supprimer la blague dans le store Redux
|
||||||
|
console.log('Suppression de la blague réussie');
|
||||||
|
} else {
|
||||||
|
console.log('Erreur lors de la requête DELETE');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log('Erreur :', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteCustomJoke = (jokeId) => {
|
||||||
|
return deleteItem(jokeId)
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
import fetchMock from 'fetch-mock';
|
||||||
|
import thunk from 'redux-thunk';
|
||||||
|
import configureMockStore from 'redux-mock-store';
|
||||||
|
import { deleteItem } from './DeleteThunk'; // Assurez-vous d'importer correctement votre thunk
|
||||||
|
import { setDeleteJoke } from '../actions/CustomJoke';
|
||||||
|
import {afterEach, describe, expect, it, jest} from "@jest/globals";
|
||||||
|
|
||||||
|
const middlewares = [thunk];
|
||||||
|
// @ts-ignore
|
||||||
|
const mockStore = configureMockStore(middlewares);
|
||||||
|
|
||||||
|
describe('deleteItem Thunk', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
fetchMock.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('dispatches setDeleteJoke after successful DELETE request', async () => {
|
||||||
|
const jokeId = '123';
|
||||||
|
const expectedActions = [setDeleteJoke(jokeId)];
|
||||||
|
|
||||||
|
fetchMock.deleteOnce(`https://iut-weather-api.azurewebsites.net/jokes/${jokeId}`, {
|
||||||
|
status: 200,
|
||||||
|
});
|
||||||
|
|
||||||
|
const store = mockStore({});
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
await store.dispatch(deleteItem(jokeId));
|
||||||
|
|
||||||
|
expect(store.getActions()).toEqual(expectedActions);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('logs an error message if DELETE request fails', async () => {
|
||||||
|
const jokeId = '123';
|
||||||
|
|
||||||
|
fetchMock.deleteOnce(`https://iut-weather-api.azurewebsites.net/jokes/${jokeId}`, {
|
||||||
|
status: 500,
|
||||||
|
});
|
||||||
|
|
||||||
|
const consoleSpy = jest.spyOn(console, 'log');
|
||||||
|
consoleSpy.mockImplementation(() => {});
|
||||||
|
|
||||||
|
const store = mockStore({});
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
await store.dispatch(deleteItem(jokeId));
|
||||||
|
|
||||||
|
expect(consoleSpy).toHaveBeenCalledWith('Erreur lors de la requête DELETE');
|
||||||
|
|
||||||
|
consoleSpy.mockRestore();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,47 @@
|
|||||||
|
// Fonction pour ajouter une blague aux favoris
|
||||||
|
import AsyncStorage from "@react-native-async-storage/async-storage";
|
||||||
|
import {SampleJoke} from "../../model/SampleJoke";
|
||||||
|
import {CustomJoke} from "../../model/CustomJoke";
|
||||||
|
|
||||||
|
const addFavorite = async (joke: SampleJoke | CustomJoke) => {
|
||||||
|
try {
|
||||||
|
let favorites: { sampleJokes: SampleJoke[], customJokes: CustomJoke[] } = await AsyncStorage.getItem('@favorites');
|
||||||
|
if (!favorites) {
|
||||||
|
favorites = { sampleJokes: [], customJokes: [] };
|
||||||
|
}
|
||||||
|
if (joke instanceof SampleJoke) {
|
||||||
|
favorites.sampleJokes.push(joke);
|
||||||
|
} else if (joke instanceof CustomJoke) {
|
||||||
|
favorites.customJokes.push(joke);
|
||||||
|
}
|
||||||
|
await AsyncStorage.setItem('@favorites', JSON.stringify(favorites));
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error adding favorite:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeFavorite = async (jokeId: string | number) => {
|
||||||
|
try {
|
||||||
|
var favorites: { sampleJokes: SampleJoke[], customJokes: CustomJoke[] } = await AsyncStorage.getItem('@favorites');
|
||||||
|
if (!favorites) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
favorites.sampleJokes = favorites.sampleJokes.filter(joke => joke.id !== jokeId);
|
||||||
|
favorites.customJokes = favorites.customJokes.filter(joke => joke.id !== jokeId);
|
||||||
|
await AsyncStorage.setItem('@favorites', JSON.stringify(favorites));
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error removing favorite:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getFavorites = async () => {
|
||||||
|
try {
|
||||||
|
const favoritesString = await AsyncStorage.getItem('@favorites');
|
||||||
|
if (favoritesString !== null) {
|
||||||
|
return JSON.parse(favoritesString);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error getting favorites:', error);
|
||||||
|
}
|
||||||
|
return { sampleJokes: [], customJokes: [] };
|
||||||
|
};
|
@ -0,0 +1,81 @@
|
|||||||
|
import fetchMock from 'fetch-mock';
|
||||||
|
import thunk from 'redux-thunk';
|
||||||
|
import configureMockStore from 'redux-mock-store';
|
||||||
|
import { getItem, getJokeById, getCustomJokeById } from './GetByThunk'; // Assurez-vous d'importer correctement vos thunks
|
||||||
|
import { setJokeById } from '../actions/SampleAction';
|
||||||
|
import {setCustomJokeById} from "../actions/CustomJoke";
|
||||||
|
import { SampleJoke } from '../../model/SampleJoke';
|
||||||
|
import { CustomJoke } from '../../model/CustomJoke';
|
||||||
|
import {afterEach, describe, expect, it, jest} from "@jest/globals";
|
||||||
|
|
||||||
|
const middlewares = [thunk];
|
||||||
|
// @ts-ignore
|
||||||
|
const mockStore = configureMockStore(middlewares);
|
||||||
|
|
||||||
|
describe('getItem Thunk', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
fetchMock.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('dispatches setJokeById after successful GET request for SampleJoke', async () => {
|
||||||
|
const jokeId = 1;
|
||||||
|
const expectedJoke = new SampleJoke(jokeId, 'type', 'setup', 'image');
|
||||||
|
|
||||||
|
fetchMock.getOnce(`https://iut-weather-api.azurewebsites.net/jokes/samples/${jokeId}`, {
|
||||||
|
status: 200,
|
||||||
|
body: {
|
||||||
|
id: jokeId,
|
||||||
|
type: 'type',
|
||||||
|
setup: 'setup',
|
||||||
|
image: 'image',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const store = mockStore({});
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
await store.dispatch(getJokeById(parseInt(jokeId)));
|
||||||
|
|
||||||
|
expect(store.getActions()).toEqual([setJokeById(expectedJoke)]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('dispatches setCustomJokeById after successful GET request for CustomJoke', async () => {
|
||||||
|
const jokeId = '1';
|
||||||
|
const expectedJoke = new CustomJoke(jokeId, 'type', 'setup', 'image');
|
||||||
|
|
||||||
|
fetchMock.getOnce(`https://iut-weather-api.azurewebsites.net/jokes/${jokeId}`, {
|
||||||
|
status: 200,
|
||||||
|
body: {
|
||||||
|
id: jokeId,
|
||||||
|
type: 'type',
|
||||||
|
setup: 'setup',
|
||||||
|
image: 'image',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const store = mockStore({});
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
await store.dispatch(getCustomJokeById(jokeId));
|
||||||
|
|
||||||
|
expect(store.getActions()).toEqual([setCustomJokeById(expectedJoke)]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('logs an error message if GET request fails', async () => {
|
||||||
|
const jokeId = '1';
|
||||||
|
|
||||||
|
fetchMock.getOnce(`https://iut-weather-api.azurewebsites.net/jokes/samples/${jokeId}`, {
|
||||||
|
status: 500,
|
||||||
|
});
|
||||||
|
|
||||||
|
const consoleSpy = jest.spyOn(console, 'log');
|
||||||
|
consoleSpy.mockImplementation(() => {});
|
||||||
|
|
||||||
|
const store = mockStore({});
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
await store.dispatch(getJokeById(parseInt(jokeId)));
|
||||||
|
|
||||||
|
expect(consoleSpy).toHaveBeenCalledWith('Error---------');
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,84 @@
|
|||||||
|
import fetchMock from 'fetch-mock';
|
||||||
|
import configureMockStore from 'redux-mock-store';
|
||||||
|
import { getLastSampleJokesList, getCategoriesList } from './GetThunk';
|
||||||
|
import { setCategoriesList } from '../actions/CategoryAction';
|
||||||
|
|
||||||
|
import { SampleJoke } from '../../model/SampleJoke';
|
||||||
|
import { Category } from '../../model/Category';
|
||||||
|
import {afterEach, describe, expect, it, jest} from "@jest/globals";
|
||||||
|
import {setJokesList} from "../actions/SampleAction";
|
||||||
|
import thunk from "redux-thunk";
|
||||||
|
|
||||||
|
const middlewares = [thunk];
|
||||||
|
// @ts-ignore
|
||||||
|
const mockStore = configureMockStore(middlewares);
|
||||||
|
|
||||||
|
describe('getList Thunk', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
fetchMock.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('dispatches setJokesList after successful GET request for LastSampleJokesList', async () => {
|
||||||
|
const expectedJokesList = [
|
||||||
|
new SampleJoke('1', 'type1', 'setup1', 'image1'),
|
||||||
|
new SampleJoke('2', 'type2', 'setup2', 'image2'),
|
||||||
|
];
|
||||||
|
|
||||||
|
fetchMock.getOnce('https://iut-weather-api.azurewebsites.net/jokes/lasts', {
|
||||||
|
status: 200,
|
||||||
|
body: expectedJokesList.map(joke => ({
|
||||||
|
id: joke.id,
|
||||||
|
type: joke.type,
|
||||||
|
setup: joke.setup,
|
||||||
|
image: joke.image,
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
|
||||||
|
const store = mockStore({});
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
await store.dispatch(getLastSampleJokesList());
|
||||||
|
|
||||||
|
expect(store.getActions()).toEqual([setJokesList(expectedJokesList)]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('dispatches setCategoriesList after successful GET request for CategoriesList', async () => {
|
||||||
|
const expectedCategoriesList = [
|
||||||
|
new Category('Category1', 1),
|
||||||
|
new Category('Category2', 2),
|
||||||
|
];
|
||||||
|
|
||||||
|
fetchMock.getOnce('https://iut-weather-api.azurewebsites.net/jokes/categories/top', {
|
||||||
|
status: 200,
|
||||||
|
body: expectedCategoriesList.map(category => ({
|
||||||
|
name: category.name,
|
||||||
|
number: category.number,
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
|
||||||
|
const store = mockStore({});
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
await store.dispatch(getCategoriesList());
|
||||||
|
|
||||||
|
expect(store.getActions()).toEqual([setCategoriesList(expectedCategoriesList)]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Tests similaires pour les autres fonctions thunks
|
||||||
|
|
||||||
|
it('logs an error message if GET request fails', async () => {
|
||||||
|
fetchMock.getOnce('https://iut-weather-api.azurewebsites.net/jokes/lasts', {
|
||||||
|
status: 500,
|
||||||
|
});
|
||||||
|
|
||||||
|
const consoleSpy = jest.spyOn(console, 'log');
|
||||||
|
consoleSpy.mockImplementation(() => {});
|
||||||
|
|
||||||
|
const store = mockStore({});
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
await store.dispatch(getLastSampleJokesList());
|
||||||
|
|
||||||
|
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('Error In getList'));
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,67 @@
|
|||||||
|
import configureMockStore from 'redux-mock-store';
|
||||||
|
import thunk from 'redux-thunk';
|
||||||
|
import fetchMock from 'fetch-mock';
|
||||||
|
import { setPostJoke } from '../actions/CustomJoke';
|
||||||
|
import { setItem, postCustomJoke } from './PostThunk';
|
||||||
|
import {afterEach, describe, expect, it, jest} from "@jest/globals";
|
||||||
|
|
||||||
|
const middlewares = [thunk];
|
||||||
|
const mockStore = configureMockStore(middlewares);
|
||||||
|
|
||||||
|
describe('setItem Thunk', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
fetchMock.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('dispatches setPostJoke after successful POST request', async () => {
|
||||||
|
const mockResponse = { id: '123', type: 'pun', setup: 'Why was the math book sad?', punchline: 'Because it had too many problems.' };
|
||||||
|
fetchMock.postOnce('https://iut-weather-api.azurewebsites.net/jokes', {
|
||||||
|
body: mockResponse,
|
||||||
|
headers: { 'content-type': 'application/json' }
|
||||||
|
});
|
||||||
|
|
||||||
|
const expectedActions = [
|
||||||
|
setPostJoke(mockResponse)
|
||||||
|
];
|
||||||
|
const store = mockStore({});
|
||||||
|
|
||||||
|
await store.dispatch(setItem('https://iut-weather-api.azurewebsites.net/jokes', 'pun', 'Why was the math book sad?', 'Because it had too many problems.'));
|
||||||
|
expect(store.getActions()).toEqual(expectedActions);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('logs an error message if POST request fails', async () => {
|
||||||
|
fetchMock.postOnce('https://iut-weather-api.azurewebsites.net/jokes', 404);
|
||||||
|
|
||||||
|
const consoleSpy = jest.spyOn(console, 'log');
|
||||||
|
consoleSpy.mockImplementation(() => {});
|
||||||
|
|
||||||
|
const store = mockStore({});
|
||||||
|
|
||||||
|
await store.dispatch(setItem('https://iut-weather-api.azurewebsites.net/jokes', 'pun', 'Why was the math book sad?', 'Because it had too many problems.'));
|
||||||
|
expect(consoleSpy).toHaveBeenCalledWith('Erreur lors de la requête POST');
|
||||||
|
|
||||||
|
consoleSpy.mockRestore();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('postCustomJoke Thunk', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
fetchMock.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls setItem with correct parameters', async () => {
|
||||||
|
const uri = 'https://iut-weather-api.azurewebsites.net/jokes';
|
||||||
|
const type = 'pun';
|
||||||
|
const setup = 'Why was the math book sad?';
|
||||||
|
const punchline = 'Because it had too many problems.';
|
||||||
|
const store = mockStore({});
|
||||||
|
|
||||||
|
const setItemSpy = jest.spyOn(global, 'setItem');
|
||||||
|
setItemSpy.mockResolvedValueOnce();
|
||||||
|
|
||||||
|
await store.dispatch(postCustomJoke(type, setup, punchline));
|
||||||
|
expect(setItemSpy).toHaveBeenCalledWith(uri, type, setup, punchline);
|
||||||
|
|
||||||
|
setItemSpy.mockRestore();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,21 @@
|
|||||||
|
import AsyncStorage from "@react-native-async-storage/async-storage";
|
||||||
|
import {Theme} from "@react-navigation/native";
|
||||||
|
|
||||||
|
export const storeTheme = async (theme) => {
|
||||||
|
try {
|
||||||
|
const jsonValue = JSON.stringify(theme)
|
||||||
|
await AsyncStorage.setItem('@theme', jsonValue)
|
||||||
|
console.log("theme stored")
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getTheme = async () => {
|
||||||
|
try {
|
||||||
|
const jsonValue = await AsyncStorage.getItem('@theme')
|
||||||
|
return jsonValue != null ? JSON.parse(jsonValue) as Theme : null;
|
||||||
|
} catch(e) {
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
import AsyncStorage from "@react-native-async-storage/async-storage";
|
||||||
|
import { storeTheme, getTheme } from "./ThemeThunk";
|
||||||
|
import {afterEach, describe, expect, it, jest} from "@jest/globals";
|
||||||
|
|
||||||
|
describe('storeTheme Function', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('stores theme in AsyncStorage', async () => {
|
||||||
|
const theme = { colors: { background: 'white', text: 'black' } };
|
||||||
|
const jsonValue = JSON.stringify(theme);
|
||||||
|
AsyncStorage.setItem = jest.fn().mockResolvedValueOnce();
|
||||||
|
console.log = jest.fn();
|
||||||
|
|
||||||
|
await storeTheme(theme);
|
||||||
|
expect(AsyncStorage.setItem).toHaveBeenCalledWith('@theme', jsonValue);
|
||||||
|
expect(console.log).toHaveBeenCalledWith("theme stored");
|
||||||
|
});
|
||||||
|
|
||||||
|
it('logs error if theme storage fails', async () => {
|
||||||
|
const theme = { colors: { background: 'white', text: 'black' } };
|
||||||
|
AsyncStorage.setItem = jest.fn().mockRejectedValueOnce('Storage error');
|
||||||
|
console.error = jest.fn();
|
||||||
|
|
||||||
|
await storeTheme(theme);
|
||||||
|
expect(console.error).toHaveBeenCalledWith('Storage error');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getTheme Function', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('retrieves theme from AsyncStorage', async () => {
|
||||||
|
const theme = { colors: { background: 'white', text: 'black' } };
|
||||||
|
const jsonValue = JSON.stringify(theme);
|
||||||
|
AsyncStorage.getItem = jest.fn().mockResolvedValueOnce(jsonValue);
|
||||||
|
|
||||||
|
const result = await getTheme();
|
||||||
|
expect(result).toEqual(theme);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns null if theme is not found in AsyncStorage', async () => {
|
||||||
|
AsyncStorage.getItem = jest.fn().mockResolvedValueOnce(null);
|
||||||
|
|
||||||
|
const result = await getTheme();
|
||||||
|
expect(result).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('logs error if theme retrieval fails', async () => {
|
||||||
|
AsyncStorage.getItem = jest.fn().mockRejectedValueOnce('Retrieval error');
|
||||||
|
console.error = jest.fn();
|
||||||
|
|
||||||
|
await getTheme();
|
||||||
|
expect(console.error).toHaveBeenCalledWith('Retrieval error');
|
||||||
|
});
|
||||||
|
});
|
@ -1,21 +1,115 @@
|
|||||||
import { customJokeStub } from '../data/stub/CustomJokeStub';
|
|
||||||
import { sampleJokeStub } from '../data/stub/SampleJokeStub';
|
|
||||||
import JokeItems from "../components/JokeItems";
|
import JokeItems from "../components/JokeItems";
|
||||||
import '../types/extension';
|
import '../types/extension';
|
||||||
import {StyleSheet, View} from "react-native";
|
import {Image, SafeAreaView, StyleSheet, Text, TouchableOpacity, View} from "react-native";
|
||||||
import {purpleColor} from "../assets/Theme";
|
import {darksalmonColor, purpleColor, whiteColor} from "../assets/Theme";
|
||||||
|
import {useDispatch, useSelector} from "react-redux";
|
||||||
|
import React, {useEffect, useState} from "react";
|
||||||
|
import {getCustomJokesList, getSampleJokesList} from "../redux/thunk/GetThunk";
|
||||||
|
import {useTheme} from "@react-navigation/native";
|
||||||
|
|
||||||
|
export default function Favorites() {
|
||||||
|
// @ts-ignore
|
||||||
|
const sampleJokes = useSelector(state => state.sampleReducer.jokes);
|
||||||
|
// @ts-ignore
|
||||||
|
const customJokes = useSelector(state => state.customReducer.customJokes);
|
||||||
|
const [joke, setJoke] = useState([])
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const eye = require("../assets/eye_icon.png")
|
||||||
|
const hideEye = require("../assets/eye_off_icon.png")
|
||||||
|
|
||||||
|
const [showCustomJoke, setCustomJoke] = useState(false); // état local pour contrôler la visibilité de la description
|
||||||
|
const toggleDescription = () => {
|
||||||
|
setCustomJoke(!showCustomJoke);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if(!showCustomJoke) {
|
||||||
|
const loadSamplesJokes = async () => {
|
||||||
|
// @ts-ignore
|
||||||
|
await dispatch(getFavorites());
|
||||||
|
};
|
||||||
|
loadSamplesJokes();
|
||||||
|
setJoke(sampleJokes)
|
||||||
|
} else {
|
||||||
|
const loadCustomJokes = async () => {
|
||||||
|
// @ts-ignore
|
||||||
|
await dispatch(getCustomJokesList());
|
||||||
|
};
|
||||||
|
loadCustomJokes();
|
||||||
|
setJoke(customJokes)
|
||||||
|
}
|
||||||
|
}, [dispatch, customJokes, sampleJokes]);
|
||||||
|
|
||||||
|
const styles = themeSettings()
|
||||||
|
|
||||||
export default function Catalogue() {
|
|
||||||
const allJokes = [...customJokeStub, ...sampleJokeStub];
|
|
||||||
return (
|
return (
|
||||||
|
<SafeAreaView style={styles.container}>
|
||||||
|
<View style={styles.columnContainer}>
|
||||||
|
<Text style={styles.TextButton}>Afficher les exemples</Text>
|
||||||
|
<TouchableOpacity style={styles.Button} onPress={toggleDescription}>
|
||||||
|
<View style={styles.jokeTypeContainer}>
|
||||||
|
<Image
|
||||||
|
source={showCustomJoke ? hideEye : eye}
|
||||||
|
style={styles.imageButton}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<JokeItems jokes={allJokes}/>
|
<JokeItems jokes={joke}/>
|
||||||
</View>
|
</View>
|
||||||
|
</SafeAreaView>
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
|
export function themeSettings() {
|
||||||
|
const theme = useTheme();
|
||||||
|
const colors = theme.colors;
|
||||||
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
backgroundColor: purpleColor
|
backgroundColor: colors.background,
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
Button: {
|
||||||
|
borderRadius: 5,
|
||||||
|
backgroundColor: colors.background,
|
||||||
|
height: 40,
|
||||||
|
width: 60,
|
||||||
|
flexDirection: "row"
|
||||||
|
},
|
||||||
|
jokeTypeContainer: {
|
||||||
|
display: "flex",
|
||||||
|
flex: 1,
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center"
|
||||||
|
},
|
||||||
|
imageButton: {
|
||||||
|
margin: 10,
|
||||||
|
width: 40,
|
||||||
|
height: 30,
|
||||||
|
top: 5,
|
||||||
|
alignSelf: "center",
|
||||||
|
backgroundColor: "none",
|
||||||
|
tintColor: colors.notification,
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
},
|
||||||
|
TextButton: {
|
||||||
|
fontSize: 18,
|
||||||
|
color: colors.notification,
|
||||||
|
textAlign: 'center',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
margin: 10,
|
||||||
|
},
|
||||||
|
columnContainer: {
|
||||||
|
marginLeft: 20,
|
||||||
|
marginRight: 20,
|
||||||
|
width: '90%',
|
||||||
|
flexDirection: "row",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "center",
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
return styles
|
||||||
|
}
|
@ -1,21 +1,102 @@
|
|||||||
import { customJokeStub } from '../data/stub/CustomJokeStub';
|
|
||||||
import { sampleJokeStub } from '../data/stub/SampleJokeStub';
|
|
||||||
import JokeItems from "../components/JokeItems";
|
|
||||||
import '../types/extension';
|
import '../types/extension';
|
||||||
import {StyleSheet, View} from "react-native";
|
import {Image, StyleSheet, Switch, Text, View} from "react-native";
|
||||||
import {purpleColor} from "../assets/Theme";
|
import {
|
||||||
|
darksalmonColor, DarkTheme,
|
||||||
|
whiteColor
|
||||||
|
} from "../assets/Theme";
|
||||||
|
import React from "react";
|
||||||
|
import {DefaultTheme, useTheme} from "@react-navigation/native";
|
||||||
|
import {storeTheme} from "../redux/thunk/ThemeThunk";
|
||||||
|
|
||||||
export default function Catalogue() {
|
export default function Catalogue() {
|
||||||
const allJokes = [...customJokeStub, ...sampleJokeStub];
|
const light_mode = require("../assets/light_mode.png")
|
||||||
|
const dark_mode = require("../assets/dark_mode.png")
|
||||||
|
const [isDark, setDark] = React.useState(false)
|
||||||
|
|
||||||
|
const toggleTheme = () => {
|
||||||
|
setDark(previousState => {
|
||||||
|
const theme = !previousState;
|
||||||
|
const newTheme = theme ? DarkTheme : DefaultTheme;
|
||||||
|
storeTheme(newTheme);
|
||||||
|
return theme;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = themeSettings();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<JokeItems jokes={allJokes}/>
|
<View style={styles.topText}>
|
||||||
|
<Text style={styles.title}>Réglages</Text>
|
||||||
|
<View style={styles.switchMode}>
|
||||||
|
<View style={styles.textContainer}>
|
||||||
|
<Image
|
||||||
|
source={isDark? dark_mode: light_mode}
|
||||||
|
style={styles.imageButton} />
|
||||||
|
<Text style={styles.darkModeText}>Dark Mode</Text>
|
||||||
|
</View>
|
||||||
|
<Switch
|
||||||
|
value={isDark}
|
||||||
|
onValueChange={toggleTheme}
|
||||||
|
style={styles.switchMode}
|
||||||
|
trackColor={{false: darksalmonColor, true: darksalmonColor}}
|
||||||
|
thumbColor={whiteColor} />
|
||||||
</View>
|
</View>
|
||||||
)
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
export function themeSettings() {
|
||||||
|
const theme = useTheme();
|
||||||
|
const colors = theme.colors;
|
||||||
|
|
||||||
|
const styles= StyleSheet.create({
|
||||||
|
|
||||||
container: {
|
container: {
|
||||||
backgroundColor: purpleColor
|
paddingTop: 10,
|
||||||
|
paddingBottom: 10,
|
||||||
|
paddingLeft: 10,
|
||||||
|
paddingRight: 10,
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: 'center',
|
||||||
|
backgroundColor: colors.background,
|
||||||
|
flexDirection: 'column',
|
||||||
|
},
|
||||||
|
topText: {
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
padding: 10,
|
||||||
|
fontSize: 20,
|
||||||
|
color: colors.text,
|
||||||
|
fontWeight: 'bold'
|
||||||
|
},
|
||||||
|
imageButton : {
|
||||||
|
width: 30,
|
||||||
|
height:30,
|
||||||
|
alignSelf : "center",
|
||||||
|
tintColor: colors.primary,
|
||||||
|
},
|
||||||
|
switchMode: {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
backgroundColor: colors.card,
|
||||||
|
padding: 20,
|
||||||
|
margin: 10,
|
||||||
|
},
|
||||||
|
darkModeText: {
|
||||||
|
color: colors.text,
|
||||||
|
fontSize: 20,
|
||||||
|
marginLeft: 10,
|
||||||
|
paddingTop: 5,
|
||||||
|
},
|
||||||
|
textContainer: {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
return styles;
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue