Skip to content

Commit

Permalink
chore: add tests (rtl + maestro) (#107)
Browse files Browse the repository at this point in the history
* chore: add tests (rtl + maestro)

* ci: add github action
  • Loading branch information
theo-mesnil authored Aug 11, 2024
1 parent 502d873 commit d1127d6
Show file tree
Hide file tree
Showing 22 changed files with 1,686 additions and 179 deletions.
7 changes: 7 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,13 @@ module.exports = {
'react-native/no-inline-styles': 2,
'react-native/no-single-element-style-arrays': 2
},
overrides: [
{
// Test files only
files: ['**/__tests__/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[jt]s?(x)'],
extends: ['plugin:testing-library/react']
}
],
settings: {
'import/resolve': {
moduleDirectory: ['node_modules', 'src']
Expand Down
13 changes: 13 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
name: Tests

on: push

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install modules
run: yarn
- name: Run tests
run: yarn jest
9 changes: 3 additions & 6 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,14 @@ npm-debug.*
*.mobileprovision
*.orig.*
web-build/
.env
coverage

# macOS
.DS_Store

.env




# @generated expo-cli sync-2b81b286409207a5da26e14c78851eb30d8ccbdb
# The following patterns were generated by expo-cli

expo-env.d.ts
# @end expo-cli
# @end expo-cli
2 changes: 1 addition & 1 deletion .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1 +1 @@
yarn translate && git diff --exit-code src/locales && yarn lint
yarn translate && git diff --exit-code src/locales && yarn lint && yarn test:coverage --forceExit
8 changes: 8 additions & 0 deletions __mocks__/expo-router.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const Link = ({ children }: { children: React.ReactNode }) => {
return <>{children}</>;
};

const useNavigation = jest.fn().mockReturnValue({ setOptions: jest.fn() });
const useLocalSearchParams = jest.fn().mockReturnValue({ id: '1' });

export { Link, useNavigation, useLocalSearchParams };
20 changes: 20 additions & 0 deletions __tests__/Movie.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import Movie from 'app/movie/[id]';

import { MOCK_MOVIE } from 'api/__mocks__/movie';

import * as movie from '../src/api/movie';
import { render, screen } from '../tests/render';

describe('<Movie />', () => {
test('Text renders correctly on HomeScreen', () => {
jest
.spyOn(movie, 'useGetMovie')
.mockReturnValue(MOCK_MOVIE as movie.UseMovie);

render(<Movie />);

const textLabel = screen.getByTestId('release-date');

expect(textLabel).toHaveTextContent('June 20, 2024');
});
});
13 changes: 13 additions & 0 deletions jest.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"preset": "jest-expo",
"collectCoverage": true,
"collectCoverageFrom": [
"src/**/*.{ts,tsx,js,jsx}",
"!**/coverage/**",
"!**/node_modules/**",
"!**/babel.config.js",
"!**/expo-env.d.ts",
"!**/.expo/**"
],
"setupFilesAfterEnv": ["./tests/setup.tsx"]
}
7 changes: 7 additions & 0 deletions maestro/index.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
appId: com.theomesnil.WhatToWatch
---
- launchApp
- waitForAnimationToEnd:
timeout: 5000
- assertVisible: 'Top 10 series'
- takeScreenshot: 'maestro/screenshots/Home'
7 changes: 7 additions & 0 deletions maestro/movie.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
appId: com.theomesnil.WhatToWatch
---
- launchApp
- tapOn: 'Search, tab, 2 of 3'
- waitForAnimationToEnd:
timeout: 5000
- takeScreenshot: 'maestro/screenshots/Search'
Binary file added maestro/screenshots/Home.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added maestro/screenshots/Search.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 12 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
"lint": "yarn lint:js && yarn lint:ts",
"lint:js": "npx eslint src --max-warnings 0",
"lint:ts": "npx tsc --noEmit",
"test": "jest --watchAll --coverage=false",
"test:coverage": "jest",
"test:e2e": "maestro test maestro/",
"test:ci": "jest --coverage=false --forceExit",
"translate": "yarn extract-messages 'src/**/*.{tsx,ts}' -l en-US,fr-FR -o src/locales -d en-US --flat",
"generate:api:types": "npx openapi-typescript https://developer.themoviedb.org/openapi/64542913e1f86100738e227f -o ./src/api/types.d.ts",
"prepare": "husky",
Expand All @@ -30,7 +34,7 @@
"@react-navigation/native": "^6.1.17",
"@react-navigation/stack": "^6.3.29",
"@tanstack/react-query": "^5.36.2",
"axios": "^1.6.8",
"axios": "^1.7.3",
"date-fns": "^3.6.0",
"expo": "~51.0.24",
"expo-blur": "~13.0.2",
Expand Down Expand Up @@ -67,7 +71,9 @@
"@babel/preset-env": "^7.24.3",
"@react-native-community/eslint-config": "^3.2.0",
"@react-native/babel-preset": "^0.74.83",
"@tanstack/eslint-plugin-query": "^5.35.6",
"@tanstack/eslint-plugin-query": "^5.51.15",
"@testing-library/react-native": "^12.5.3",
"@types/jest": "^29.5.12",
"@types/lodash.debounce": "^4.0.9",
"@types/lodash.merge": "^4.6.9",
"@types/node": "^20.12.12",
Expand All @@ -77,11 +83,14 @@
"babel-plugin-react-intl-auto": "^3.3.0",
"eslint": "^8.57.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-prettier": "5.1.3",
"eslint-plugin-prettier": "5.2.1",
"eslint-plugin-sort-destructure-keys": "^2.0.0",
"eslint-plugin-testing-library": "^6.3.0",
"eslint-plugin-typescript-sort-keys": "^3.2.0",
"extract-react-intl-messages": "^4.1.1",
"husky": "^9.0.11",
"jest": "^29.7.0",
"jest-expo": "^51.0.3",
"prettier": "^3.2.5",
"typescript": "~5.3.3"
},
Expand Down
20 changes: 20 additions & 0 deletions src/api/__mocks__/movie.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { NETWORK_NETFLIX_ID } from 'constants/networks';
import type { NetworkId } from 'types/content';

export const MOCK_MOVIE = {
data: {
coverUrl: '/lgkPzcOSnTvjeMnuFzozRO5HHw1.jpg',
genres: 'Animation - Family',
networkLink: {
id: NETWORK_NETFLIX_ID as NetworkId,
link: 'link'
},
overview:
'Gru and Lucy and their girls—Margo, Edith and Agnes—welcome a new member to the Gru family, Gru Jr., who is intent on tormenting his dad. Gru also faces a new nemesis in Maxime Le Mal and his femme fatale girlfriend Valentina, forcing the family to go on the run.',
rating: { count: 840, votes: 7.4 },
releaseDate: '2024-06-20',
runtime: 94,
tagline: 'Things just got a little more despicable.',
title: 'Despicable Me 4'
}
};
24 changes: 23 additions & 1 deletion src/api/movie.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { UseQueryResult } from '@tanstack/react-query';
import { useQuery } from '@tanstack/react-query';
import type { AxiosResponse } from 'axios';
import axios from 'axios';
Expand Down Expand Up @@ -32,7 +33,28 @@ export type UseGetMovieEnabledApiProps = UseGetMovieApiProps & {
enabled: boolean;
};

export function useGetMovie(props?: UseGetMovieApiProps) {
export type UseMovie = UseQueryResult<
{
coverUrl?: string;
genres: string;
networkLink?: {
id: NetworkId;
link: string;
};
overview: string;
rating?: {
count: number;
votes: number;
};
releaseDate: string;
runtime: number;
tagline: string;
title: string;
} | null,
Error
>;

export function useGetMovie(props?: UseGetMovieApiProps): UseMovie {
const { id } = props || {};

const { queryUrl } = getApi({
Expand Down
5 changes: 1 addition & 4 deletions src/api/search.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import type { UseInfiniteQueryOptions } from '@tanstack/react-query';
import { useInfiniteQuery } from '@tanstack/react-query';
import type { AxiosResponse } from 'axios';
import axios from 'axios';
Expand All @@ -16,13 +15,12 @@ export type UseGetSearchApiParams =
paths['/3/search/multi']['get']['parameters']['query'];

export type UseGetSearchApiProps = {
enabled?: UseInfiniteQueryOptions['enabled'];
maxPages?: number;
params?: SpecificApiParam<UseGetSearchApiParams>[];
};

export function useGetSearch(props?: UseGetSearchApiProps) {
const { enabled = true, maxPages = 30, params } = props || {};
const { maxPages = 30, params } = props || {};

const { queryParams, queryUrl } = getApi({
query: 'search/multi',
Expand All @@ -37,7 +35,6 @@ export function useGetSearch(props?: UseGetSearchApiProps) {
);
return data;
},
enabled,
initialPageParam: 1,
getNextPageParam: ({ page }) => {
return page + 1 <= maxPages ? page + 1 : undefined;
Expand Down
3 changes: 1 addition & 2 deletions src/app/(tabs)/search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@ export default function Search() {
hasNextPage: hasSearchNextPage,
isLoading: isSearchLoading
} = useGetSearch({
params: [{ name: 'query', value: querySearch }],
enabled: !!querySearch
params: [{ name: 'query', value: querySearch }]
});

const results = querySearch
Expand Down
6 changes: 4 additions & 2 deletions src/app/movie/[id]/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { useLocalSearchParams } from 'expo-router';
import * as React from 'react';
import { FormattedDate, FormattedMessage } from 'react-intl';
import { type ListRenderItemInfo, StyleSheet, View } from 'react-native';
import type { ListRenderItemInfo } from 'react-native';
import { StyleSheet, View } from 'react-native';
import { moviePath, personPath } from 'routes';
import { globalStyles } from 'styles';
import { theme } from 'theme';
Expand Down Expand Up @@ -38,6 +39,7 @@ export default function Movie() {
const movieID = Number(params?.id);

const { data, isLoading } = useGetMovie({ id: movieID });

const { data: logo, isLoading: isLoadingLogo } = useGetContentLogo({
id: movieID,
type: 'movie'
Expand Down Expand Up @@ -105,7 +107,7 @@ export default function Movie() {
!isLoading && (
<>
{!!releaseDate && (
<Badge>
<Badge testID="release-date">
<FormattedDate
day="numeric"
year="numeric"
Expand Down
5 changes: 3 additions & 2 deletions src/components/Badge/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ import { Text } from 'components/Text';
export type BadgeProps = {
children: React.ReactNode;
icon?: IconElement;
testID?: string;
};

export function Badge({ children, icon }: BadgeProps) {
export function Badge({ children, icon, testID }: BadgeProps) {
return (
<View style={styles.wrapper}>
<View style={styles.wrapper} testID={testID}>
{icon && <Icon icon={icon} size={13} />}
<Text style={styles.text}>{children}</Text>
</View>
Expand Down
25 changes: 25 additions & 0 deletions tests/render.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { render } from '@testing-library/react-native';
import { IntlMessages } from 'locales';
import * as React from 'react';

type AllTheProvidersProps = {
children: React.ReactNode;
};

const AllTheProviders = ({ children }: AllTheProvidersProps) => {
const queryClient = new QueryClient();

return (
<IntlMessages>
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
</IntlMessages>
);
};

const customRender = (ui: React.ReactElement, options?: unknown[]) =>
render(ui, { wrapper: AllTheProviders, ...options });

export * from '@testing-library/react-native';

export { customRender as render };
19 changes: 19 additions & 0 deletions tests/setup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import '@testing-library/react-native/extend-expect';

jest.useFakeTimers();

jest.mock('expo-localization', () => ({
getLocales: () => [
{
languageCode: 'en',
regionCode: 'EN'
}
]
}));

jest.mock('react-native-safe-area-context', () => ({
useSafeAreaInsets: () => ({
top: 0,
bottom: 0
})
}));
16 changes: 4 additions & 12 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,9 @@
"compilerOptions": {
"baseUrl": "./src",
"noImplicitAny": true,
"types": [
"node"
]
"types": ["node", "jest", "@testing-library/react-native"],
"module": "esnext"
},
"include": [
"**/*.ts",
"**/*.tsx",
".expo/types/**/*.ts",
"expo-env.d.ts"
],
"exclude": [
"src/api/types.d.ts"
]
"include": ["**/*.ts", "**/*.tsx", ".expo/types/**/*.ts", "expo-env.d.ts"],
"exclude": ["src/api/types.d.ts"]
}
Loading

0 comments on commit d1127d6

Please sign in to comment.