Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Testing #48

Merged
merged 21 commits into from
Apr 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .github/workflows/dynamic-analysis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Dynamic Analysis

on:
push:
branches:
- main
- dev
pull_request:
branches:
- main
- dev

jobs:
tests:
name: Tests
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v3

- name: Coverage Report
uses: ArtiomTr/jest-coverage-report-action@v2
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,5 @@ ios
.env
google-service-account-key.json

coverage

11 changes: 11 additions & 0 deletions .run/All Tests.run.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="All Tests" type="JavaScriptTestRunnerJest" nameIsGenerated="true">
<node-interpreter value="project" />
<jest-package value="$PROJECT_DIR$/node_modules/jest" />
<working-dir value="$PROJECT_DIR$" />
<jest-options value="--watch" />
<envs />
<scope-kind value="ALL" />
<method v="2" />
</configuration>
</component>
30 changes: 30 additions & 0 deletions __tests__/components/base/screen.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { render } from '@testing-library/react-native';

import { Screen } from '../../../components/base/screen';
import { AnalyticsService } from '../../../services/analytics.service';

jest.mock('../../../services/analytics.service', () => ({
AnalyticsService: {
sendPageView: jest.fn(),
},
}));

beforeEach(() => {
jest.clearAllMocks();
});

it('should render screen', () => {
const { getByTestId } = render(<Screen testID='testId' />);
expect(getByTestId('testId')).toBeTruthy();
});

it('should send analytics event if provided', () => {
const { getByTestId } = render(<Screen testID='testId' analyticsScreenName='testLocation' />);
expect(getByTestId('testId')).toBeTruthy();
expect(AnalyticsService.sendPageView).toHaveBeenCalledWith('testLocation');
});

it('should not send analytics event if not provided', () => {
render(<Screen testID='testId' />);
expect(AnalyticsService.sendPageView).not.toHaveBeenCalled();
});
31 changes: 31 additions & 0 deletions __tests__/components/common/error-boundary.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { render, userEvent } from '@testing-library/react-native';

import { ErrorBoundary } from '../../../components/common/error-boundary';

const user = userEvent.setup();
jest.useFakeTimers();

jest.mock('react-i18next', () => ({
useTranslation: () => ({ t: (key: string) => key }),
}));

jest.mock('react-native-safe-area-context', () => ({
useSafeAreaInsets: () => ({ top: 0, bottom: 0, left: 0, right: 0 }),
}));

it('should render error message', () => {
const { queryByTestId, getByText } = render(
<ErrorBoundary error={{ name: 'Error name', message: 'Error message' }} retry={jest.fn()} />
);
expect(queryByTestId('error-message')).toBeTruthy();
expect(getByText('Error message')).toBeTruthy();
});

it('should call retry function', async () => {
const retry = jest.fn();
const { getByTestId } = render(
<ErrorBoundary error={{ name: 'Error name', message: 'Error message' }} retry={retry} />
);
await user.press(getByTestId('error-retry'));
expect(retry).toHaveBeenCalled();
});
43 changes: 43 additions & 0 deletions __tests__/components/common/header.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { render } from '@testing-library/react-native';
import * as router from 'expo-router';
import { View } from 'react-native';

import { Header } from '../../../components/common/header';
import { Title } from '../../../components/common/title';

jest.mock('expo-router', () => ({
useNavigation: jest.fn().mockReturnValue({
canGoBack: jest.fn().mockReturnValue(true),
goBack: jest.fn(),
}),
}));

beforeEach(() => {
jest.clearAllMocks();
});

it('should render the header with a back button', () => {
const { queryByTestId } = render(
<Header>
<Title testID='title'>Test Title</Title>
</Header>
);
expect(queryByTestId('header-container')).toBeTruthy();
expect(queryByTestId('title')).toBeTruthy();
expect(queryByTestId('back-button')).toBeTruthy();
});

it("should not render the back button if it can't go back", () => {
jest.spyOn(router, 'useNavigation').mockReturnValue({
canGoBack: jest.fn().mockReturnValue(false),
});

const { queryByTestId } = render(<Header />);
expect(queryByTestId('header-container')).toBeTruthy();
expect(queryByTestId('back-button')).toBeFalsy();
});

it('should render the header with a corner', () => {
const { queryByTestId } = render(<Header corner={<View testID='corner' />} />);
expect(queryByTestId('corner')).toBeTruthy();
});
32 changes: 32 additions & 0 deletions __tests__/components/common/styled-button.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { render, waitFor } from '@testing-library/react-native';

import { StyledButton } from '../../../components/common/styled-button';

beforeEach(() => {
jest.clearAllMocks();
});

it('should have children if provided', () => {
const { queryByText } = render(<StyledButton>Click me</StyledButton>);
expect(queryByText('Click me')).toBeTruthy();
});

it('should have left icon if provided', async () => {
const { queryByTestId } = render(<StyledButton leftIcon='arrow-left' />);
await waitFor(() => expect(queryByTestId('left-icon')).toBeTruthy());
});

it("should not have left icon if it's not provided", () => {
const { queryByTestId } = render(<StyledButton />);
expect(queryByTestId('left-icon')).toBeFalsy();
});

it('should have right icon if provided', async () => {
const { queryByTestId } = render(<StyledButton rightIcon='arrow-right' />);
await waitFor(() => expect(queryByTestId('right-icon')).toBeTruthy());
});

it("should not have right icon if it's not provided", () => {
const { queryByTestId } = render(<StyledButton />);
expect(queryByTestId('right-icon')).toBeFalsy();
});
60 changes: 60 additions & 0 deletions __tests__/components/schedule/elements/favorite-button.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { render, userEvent } from '@testing-library/react-native';

import { FavoriteButton } from '../../../../components/schedule/elements/favorite-button';
import { useFavoritePresentations } from '../../../../contexts/favorite-presentations.context';
import { PresentationDto } from '../../../../types/conference-api.type';

jest.mock('../../../../contexts/favorite-presentations.context', () => ({
useFavoritePresentations: jest.fn().mockReturnValue({
isFavoritePresentation: jest.fn().mockReturnValue(true),
addFavoritePresentation: jest.fn().mockImplementation(() => {}),
removeFavoritePresentation: jest.fn().mockImplementation(() => {}),
}),
}));

const user = userEvent.setup();
jest.useFakeTimers();

const PresentationMock: PresentationDto = {
description: 'presentation description',
endTime: new Date().toISOString(),
language: 'en',
presenter: {
name: 'presenter name',
rank: 'presenter rank',
pictureUrl: 'example.com/picture.jpg',
},
questionsUrl: 'example.com/questions',
room: 'room',
startTime: new Date().toISOString(),
title: 'presentation title',
slug: 'presentation-slug',
};

beforeEach(() => {
jest.clearAllMocks();
});

it('should add presentation to favorites if it is not favorite', async () => {
const { isFavoritePresentation, addFavoritePresentation, removeFavoritePresentation } = useFavoritePresentations();

(isFavoritePresentation as jest.Mock).mockReturnValue(false);

const { getByTestId } = render(<FavoriteButton presentation={PresentationMock} />);
await user.press(getByTestId('favorite-button'));

expect(addFavoritePresentation).toHaveBeenCalledWith(PresentationMock);
expect(removeFavoritePresentation).not.toHaveBeenCalled();
});

it('should remove presentation from favorites if it is favorite', async () => {
const { isFavoritePresentation, addFavoritePresentation, removeFavoritePresentation } = useFavoritePresentations();

(isFavoritePresentation as jest.Mock).mockReturnValue(true);

const { getByTestId } = render(<FavoriteButton presentation={PresentationMock} />);
await user.press(getByTestId('favorite-button'));

expect(addFavoritePresentation).not.toHaveBeenCalled();
expect(removeFavoritePresentation).toHaveBeenCalledWith(PresentationMock.slug);
});
81 changes: 81 additions & 0 deletions __tests__/components/schedule/elements/location-filter.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { render, userEvent } from '@testing-library/react-native';

import { LocationFilter } from '../../../../components/schedule/elements/location-filter';

jest.mock('react-i18next', () => ({
useTranslation: () => ({ t: (key: string) => key }),
}));

const user = userEvent.setup();
jest.useFakeTimers();

beforeEach(() => {
jest.clearAllMocks();
});

function isOptionSelected(element: any) {
return element.props.style.some((sp: Record<string, number | string>) => sp.backgroundColor === '#fff');
}

it('should display available options', () => {
const { getByText } = render(
<LocationFilter current='option1' options={['option1', 'option2']} onChange={jest.fn()} />
);

expect(getByText('option1')).toBeDefined();
expect(getByText('option2')).toBeDefined();
});

it("should display 'All' option", () => {
const { getByTestId } = render(
<LocationFilter current={undefined} options={['option1', 'option2']} onChange={jest.fn()} />
);

expect(getByTestId('location-filter-option-all')).toBeDefined();
});

it('should call onChange with selected option', async () => {
const onChange = jest.fn();
const { getByText } = render(
<LocationFilter current='option1' options={['option1', 'option2']} onChange={onChange} />
);

await user.press(getByText('option2'));

expect(onChange).toHaveBeenCalledWith('option2');
});

it('should call onChange with undefined when selecting All', async () => {
const onChange = jest.fn();
const { getByTestId } = render(
<LocationFilter current='option1' options={['option1', 'option2']} onChange={onChange} />
);

await user.press(getByTestId('location-filter-option-all'));

expect(onChange).toHaveBeenCalledWith(undefined);
});

it('should have all option selected when current is undefined', () => {
const { getByTestId } = render(
<LocationFilter current={undefined} options={['option1', 'option2']} onChange={jest.fn()} />
);

expect(isOptionSelected(getByTestId('location-filter-option-all'))).toBeTruthy();

expect(isOptionSelected(getByTestId('location-filter-option-option1'))).toBeFalsy();

expect(isOptionSelected(getByTestId('location-filter-option-option2'))).toBeFalsy();
});

it("should have selected option's background color white", () => {
const { getByTestId } = render(
<LocationFilter current='option1' options={['option1', 'option2']} onChange={jest.fn()} />
);

expect(isOptionSelected(getByTestId('location-filter-option-all'))).toBeFalsy();

expect(isOptionSelected(getByTestId('location-filter-option-option1'))).toBeTruthy();

expect(isOptionSelected(getByTestId('location-filter-option-option2'))).toBeFalsy();
});
Loading
Loading