React Component Testing
Tags: webdev, react
Published: 2022-11-07 17:10:51

I have a complicated question about React development, creating component and testing them. 

I created an app using React and Typescript. It worked but now I'm interest in testing that app using **Cypress**. I can override my API Calls and test the software but as I'm reading, I try to separate the component from API for betting architecture.

Here's an example of fetching and show a data list.

Model

```
import { useEffect, useState } from "react"
import * as SquadsAPI from "../../servercalls/squadsdatafetcher";
import { ISquadPlayer } from "../../../../common/squads";
type UIState = 'show' | 'loading' | 'error'

export const useSquadsPageModel = (careerId: string): {
    isLoading: boolean,
    isReady: boolean,
    isOffline: boolean,
    squadList: ISquadPlayer[],
    nationnalities: INationnality[],
    avgPotentialPerPosition: IAvgPotentialPosition[]
    deletePlayer: (playerId: string) => void
} => {
    const [uiState, setUiState] = useState<UIState>('loading');
    const [playerList, setPlayerList] = useState<ISquadPlayer[]>([]);
    useEffect(() => {
        const loadSquads = async () => {
            setUiState('loading')
            try {
                const request = await SquadsAPI.fetchSquads({
                    careerId: careerId
                })
                if (!request.success) {
                    throw new Error('server request error')
                }
                setPlayerList(request.squadsPlayers.map(player => {
                    if (parseInt(player.potential) > 0)
                        return player;
                    return {
                        ...player,
                        potential: ((parseInt(player.maxPotential) - parseInt(player.minPotential)) / 2 + parseInt(player.minPotential)).toFixed(0)
                    }
                }))
                setUiState('show')
            } catch (err) {
                setUiState('error')
            }
        }
        loadSquads();
    }, [careerId]);

    const onDeletePlayer = async (playerId: string) => {
        const newList = playerList.filter(player => player.id !== playerId);
        setPlayerList(newList);
    }
    return {
        isLoading: uiState === 'loading',
        isReady: uiState === 'show',
        isOffline: uiState === 'error',
        squadList: playerList,
        deletePlayer: onDeletePlayer
    }
}
```
View
```
const SquadsPageComponent = () => {
    const classes = useSquadsPageStyle();
    const buttonClass = useButtonStyle();
    const { selectedCareerId } = useContext(CareersContext);
    const {
        isLoading,
        squadList,
    } = useSquadsPageModel(selectedCareerId);

    if (isLoading) {
        return (
            <LoadingComponent />
        )
    }

    return (
        <div className={classes.mainContent}>
            <div className={classes.commandLayout}>
                <Link className={buttonClass.greenBorderedButton} to={SitesRoutes.NewRecruitPlayerPage.link}>Add Recruits</Link>
                <Link className={buttonClass.greenBorderedButton} to={SitesRoutes.NewSquadPlayerPage.link}>Add Player</Link>
            </div>
            <SquadsTable players={squadList} />
        </div>
    )
}
```

I would like some tips to refactor my code with the test using cypress.

Would it be a better approach to send my {UiState} from the model with the array of data to another component (2 parameters in) ? 
-My page component would not have the if loading, it will only call my model and send everything as props to a single component.

Or inside my Page component, keep it with the if statement ?

It's easy to find on use a design pattern for that but how to bring all to together for the "frontend" unit testing, component testing and End to End testing while thinking in architecture for best scaling and maintaining the software.

Hope I'm as clear as possible for my question and my explanation. 

Thank in advance.