Skip to content

Commit

Permalink
Merge pull request #548 from bounswe/feature/MB-tests-portfolio-markets
Browse files Browse the repository at this point in the history
Feature/mb tests portfolio markets
  • Loading branch information
m-erkam authored Dec 16, 2024
2 parents a510dfe + ea66c47 commit dad0acc
Show file tree
Hide file tree
Showing 5 changed files with 469 additions and 11 deletions.
123 changes: 123 additions & 0 deletions mobile/__tests__/Markets.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import React from 'react';
import { render, fireEvent, waitFor } from '@testing-library/react-native';
import Markets from '../src/pages/Markets';

// Mock navigation
const mockNavigate = jest.fn();
const mockNavigation = { navigate: mockNavigate };

// Mock fetch globally
global.fetch = jest.fn();

describe('Markets Component', () => {
beforeEach(() => {
jest.clearAllMocks();
});

test('renders loading indicator initially', () => {
const { getByTestId } = render(<Markets navigation={mockNavigation} />);
expect(getByTestId('loading-indicator')).toBeTruthy();
});

test('displays stocks correctly after fetching', async () => {
const mockStocks = [
{ id: 1, name: 'Stock A', symbol: 'A', price: 100, currency: { code: 'USD' } },
{ id: 2, name: 'Stock B', symbol: 'B', price: 200, currency: { code: 'USD' } },
];

global.fetch.mockResolvedValueOnce({
ok: true,
json: () => Promise.resolve(mockStocks),
});

const { getByText } = render(<Markets navigation={mockNavigation} />);

await waitFor(() => expect(getByText('Stock A')).toBeTruthy());
expect(getByText('100.00 USD')).toBeTruthy();
expect(getByText('Stock B')).toBeTruthy();
expect(getByText('200.00 USD')).toBeTruthy();
});

test('shows an error alert if fetching fails', async () => {
global.fetch.mockResolvedValueOnce({
ok: false,
status: 500,
});

const alertSpy = jest.spyOn(Alert, 'alert');
render(<Markets navigation={mockNavigation} />);

await waitFor(() =>
expect(alertSpy).toHaveBeenCalledWith('Error', 'Unable to fetch stocks. Please try again later.')
);
});

test('search functionality filters results correctly', async () => {
const mockSearchResults = [
{ id: 3, name: 'Stock C', symbol: 'C', price: 300, currency: { code: 'USD' } },
];

global.fetch
.mockResolvedValueOnce({
ok: true,
json: () => Promise.resolve([]), // Initial fetch
})
.mockResolvedValueOnce({
ok: true,
json: () => Promise.resolve(mockSearchResults), // Search fetch
});

const { getByPlaceholderText, getByText } = render(<Markets navigation={mockNavigation} />);

const searchInput = getByPlaceholderText('Search');
fireEvent.changeText(searchInput, 'Stock C');

await waitFor(() => expect(getByText('Stock C')).toBeTruthy());
expect(getByText('300.00 USD')).toBeTruthy();
});

test('triggers navigation to StockDetails when a stock is pressed', async () => {
const mockStocks = [{ id: 1, name: 'Stock A', symbol: 'A', price: 100, currency: { code: 'USD' } }];

global.fetch.mockResolvedValueOnce({
ok: true,
json: () => Promise.resolve(mockStocks),
});

const { getByText } = render(<Markets navigation={mockNavigation} />);

await waitFor(() => expect(getByText('Stock A')).toBeTruthy());
fireEvent.press(getByText('Stock A'));
expect(mockNavigate).toHaveBeenCalledWith('StockDetails', { id: 1 });
});

test('loads more stocks when reaching the end of the list', async () => {
const mockStocksPage1 = [
{ id: 1, name: 'Stock A', symbol: 'A', price: 100, currency: { code: 'USD' } },
];
const mockStocksPage2 = [
{ id: 2, name: 'Stock B', symbol: 'B', price: 200, currency: { code: 'USD' } },
];

global.fetch
.mockResolvedValueOnce({
ok: true,
json: () => Promise.resolve(mockStocksPage1), // First page
})
.mockResolvedValueOnce({
ok: true,
json: () => Promise.resolve(mockStocksPage2), // Second page
});

const { getByText, getByTestId } = render(<Markets navigation={mockNavigation} />);

await waitFor(() => expect(getByText('Stock A')).toBeTruthy());

fireEvent.scroll(getByTestId('flatlist'), {
nativeEvent: { contentOffset: { y: 500 }, contentSize: { height: 1000 }, layoutMeasurement: { height: 500 } },
});

await waitFor(() => expect(getByText('Stock B')).toBeTruthy());
expect(global.fetch).toHaveBeenCalledTimes(2);
});
});
71 changes: 71 additions & 0 deletions mobile/__tests__/Portfolio.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import React from 'react';
import { render, fireEvent, waitFor } from '@testing-library/react-native';
import Portfolio from '../src/pages/Portfolio';
import { useAuth } from '../src/pages/context/AuthContext';

// Mock AuthContext
jest.mock('../context/AuthContext', () => ({
useAuth: jest.fn(),
}));

// Mock navigation
const mockNavigate = jest.fn();
const mockNavigation = { navigate: mockNavigate };

describe('Portfolio Component', () => {
beforeEach(() => {
jest.clearAllMocks();
useAuth.mockReturnValue({
userId: 'testUserId',
accessToken: 'testAccessToken',
});
});

test('renders loading indicator while fetching data', async () => {
const { getByTestId } = render(<Portfolio navigation={mockNavigation} />);
expect(getByTestId('loading-indicator')).toBeTruthy();
});

test('displays "No portfolios yet" when no data is available', async () => {
global.fetch = jest.fn(() =>
Promise.resolve({
ok: true,
json: () => Promise.resolve([]),
})
);

const { getByText } = render(<Portfolio navigation={mockNavigation} />);
await waitFor(() => expect(getByText('You have no portfolios yet.')).toBeTruthy());
});

test('renders portfolios correctly', async () => {
const mockPortfolios = [
{ id: '1', name: 'Portfolio 1', stocks: [], totalProfitOrLoss: 100 },
];
global.fetch = jest.fn(() =>
Promise.resolve({
ok: true,
json: () => Promise.resolve(mockPortfolios),
})
);

const { getByText } = render(<Portfolio navigation={mockNavigation} />);
await waitFor(() => expect(getByText('Portfolio 1')).toBeTruthy());
});

test('navigates to PortfolioDetails on card press', async () => {
const mockPortfolios = [
{ id: '1', name: 'Portfolio 1', stocks: [], totalProfitOrLoss: 100 },
];
global.fetch = jest.fn(() =>
Promise.resolve({
ok: true,
json: () => Promise.resolve(mockPortfolios),
})
);

const { getByText } = render(<Portfolio navigation={mockNavigation} />);
await waitFor(() => fireEvent.press(getByText('Portfolio 1')));
expect(mockNavigate).toHaveBeenCalledWith('PortfolioDetails', { portfolio: mockPortfolios[0] });
});
});
95 changes: 95 additions & 0 deletions mobile/__tests__/PortfolioDetails.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import React from 'react';
import { render, fireEvent, waitFor } from '@testing-library/react-native';
import PortfolioDetails from '../src/pages/PortfolioDetails';
import { useAuth } from '../src/pages/context/AuthContext';

// Mock AuthContext
jest.mock('../context/AuthContext', () => ({
useAuth: jest.fn(),
}));

describe('PortfolioDetails Component', () => {
const mockPortfolio = {
id: '1',
name: 'My Portfolio',
description: 'This is a test portfolio',
created_at: '2023-01-01T00:00:00Z',
stocks: [
{
stock: 'stock1',
name: 'Stock 1',
symbol: 'STK1',
quantity: 10,
price_bought: 100,
currentPrice: 150,
currency: 'USD',
},
],
};

beforeEach(() => {
jest.clearAllMocks();
useAuth.mockReturnValue({
accessToken: 'testAccessToken',
});
});

test('renders portfolio details', () => {
const { getByText } = render(<PortfolioDetails route={{ params: { portfolio: mockPortfolio } }} />);
expect(getByText('My Portfolio')).toBeTruthy();
expect(getByText('This is a test portfolio')).toBeTruthy();
expect(getByText('Created on: 1/1/2023')).toBeTruthy();
});

test('renders stock details correctly', () => {
const { getByText } = render(<PortfolioDetails route={{ params: { portfolio: mockPortfolio } }} />);
expect(getByText('Stock 1')).toBeTruthy();
expect(getByText('Quantity: 10, Bought at: 100.00 USD, Current: 150.00 USD')).toBeTruthy();
});

test('handles stock removal', async () => {
global.fetch = jest.fn(() =>
Promise.resolve({
ok: true,
json: () => Promise.resolve({}),
})
);

const { getByText, queryByText } = render(
<PortfolioDetails route={{ params: { portfolio: mockPortfolio } }} />
);

fireEvent.press(getByText('Remove'));
await waitFor(() => expect(queryByText('Stock 1')).toBeNull());
});

test('handles adding a new stock', async () => {
global.fetch = jest.fn(() =>
Promise.resolve({
ok: true,
json: () => Promise.resolve({
id: 'stock2',
name: 'Stock 2',
symbol: 'STK2',
price: 200,
currency: { code: 'USD' },
}),
})
);

const { getByPlaceholderText, getByText } = render(
<PortfolioDetails route={{ params: { portfolio: mockPortfolio } }} />
);

fireEvent.changeText(getByPlaceholderText('Search Stocks'), 'Stock 2');
fireEvent.press(getByText('Search'));

await waitFor(() => fireEvent.press(getByText('Stock 2 (STK2)')));

fireEvent.changeText(getByPlaceholderText('Price Bought (TRY)'), '200');
fireEvent.changeText(getByPlaceholderText('Quantity'), '5');
fireEvent.press(getByText('Add'));

await waitFor(() => expect(getByText('Stock 2')).toBeTruthy());
});
});
Loading

0 comments on commit dad0acc

Please sign in to comment.