Skip to content

Commit

Permalink
chore: add a few tests
Browse files Browse the repository at this point in the history
  • Loading branch information
a-ghorbani committed Dec 6, 2024
1 parent a6f62f8 commit 0c484bd
Show file tree
Hide file tree
Showing 8 changed files with 529 additions and 1 deletion.
2 changes: 2 additions & 0 deletions __mocks__/stores/chatSessionStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ export const mockChatSessionStore = {
updateMessage: jest.fn(),
updateMessageToken: jest.fn(),
exitEditMode: jest.fn(),
enterEditMode: jest.fn(),
removeMessagesFromId: jest.fn(),
};

Object.defineProperty(mockChatSessionStore, 'currentSessionMessages', {
Expand Down
77 changes: 77 additions & 0 deletions src/api/__tests__/hf.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import axios from 'axios';
import {fetchGGUFSpecs, fetchModelFilesDetails, fetchModels} from '../hf';

jest.mock('axios');
const mockedAxios = axios as jest.Mocked<typeof axios>;

describe('fetchModels', () => {
it('should fetch models with basic parameters', async () => {
const mockResponse = {
data: [{id: 'model1'}],
headers: {link: 'next-page-link'},
};
mockedAxios.get.mockResolvedValueOnce(mockResponse);

const result = await fetchModels({search: 'test'});

expect(mockedAxios.get).toHaveBeenCalledWith(
expect.any(String),
expect.objectContaining({
params: expect.objectContaining({search: 'test'}),
}),
);
expect(result).toEqual({
models: [{id: 'model1'}],
nextLink: 'next-page-link',
});
});

it('should handle missing pagination link', async () => {
const mockResponse = {
data: [{id: 'model1'}],
headers: {},
};
mockedAxios.get.mockResolvedValueOnce(mockResponse);

const result = await fetchModels({});
expect(result.nextLink).toBeNull();
});
});

describe('API error handling', () => {
it('should handle network errors in fetchModels', async () => {
const error = new Error('Network error');
mockedAxios.get.mockRejectedValueOnce(error);

await expect(fetchModels({})).rejects.toThrow('Network error');
});

it('should handle non-ok responses in fetchModelFilesDetails', async () => {
global.fetch = jest.fn().mockResolvedValueOnce({
ok: false,
statusText: 'Not Found',
});

await expect(fetchModelFilesDetails('model1')).rejects.toThrow(
'Error fetching model files: Not Found',
);
});
});

describe('fetchGGUFSpecs', () => {
it('should parse GGUF specs correctly', async () => {
const mockSpecs = {
gguf: {
params: 7,
type: 'f16',
},
};
global.fetch = jest.fn().mockResolvedValueOnce({
ok: true,
json: () => Promise.resolve(mockSpecs),
});

const result = await fetchGGUFSpecs('model1');
expect(result).toEqual(mockSpecs);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import React from 'react';
import {render, fireEvent} from '@testing-library/react-native';
import {BottomSheetSearchbar} from '../BottomSheetSearchbar';
import {useBottomSheetInternal} from '@gorhom/bottom-sheet';

jest.mock('@gorhom/bottom-sheet', () => ({
useBottomSheetInternal: jest.fn(),
}));

describe('BottomSheetSearchbar', () => {
const mockShouldHandleKeyboardEvents = {value: false};

beforeEach(() => {
(useBottomSheetInternal as jest.Mock).mockReturnValue({
shouldHandleKeyboardEvents: mockShouldHandleKeyboardEvents,
});
});

it('should handle focus event correctly', () => {
const onFocus = jest.fn();
const {getByTestId} = render(
<BottomSheetSearchbar
testID="searchbar"
onFocus={onFocus}
value="test"
/>,
);

fireEvent(getByTestId('searchbar'), 'focus');

expect(mockShouldHandleKeyboardEvents.value).toBe(true);
expect(onFocus).toHaveBeenCalled();
});

it('should handle blur event correctly', () => {
const onBlur = jest.fn();
const {getByTestId} = render(
<BottomSheetSearchbar testID="searchbar" onBlur={onBlur} value="test" />,
);

fireEvent(getByTestId('searchbar'), 'blur');

expect(mockShouldHandleKeyboardEvents.value).toBe(false);
expect(onBlur).toHaveBeenCalled();
});

it('should reset keyboard events flag on unmount', () => {
const {unmount} = render(<BottomSheetSearchbar value="test" />);

unmount();

expect(mockShouldHandleKeyboardEvents.value).toBe(false);
});

it('should forward props to Searchbar component', () => {
const placeholder = 'Search...';
const value = 'test';
const onChangeText = jest.fn();

const {getByPlaceholderText} = render(
<BottomSheetSearchbar
placeholder={placeholder}
value={value}
onChangeText={onChangeText}
/>,
);

const searchbar = getByPlaceholderText(placeholder);
expect(searchbar.props.value).toBe(value);

fireEvent.changeText(searchbar, 'new value');
expect(onChangeText).toHaveBeenCalledWith('new value');
});
});
2 changes: 1 addition & 1 deletion src/components/Menu/MenuItem/MenuItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export const MenuItem: React.FC<MenuItemProps> = ({
styles.leadingContainer,
menuItemProps.disabled && styles.itemDisabled,
]}>
{selected && <Icon source="check" size={18} />}
{selected && <Icon testID="selected-icon" source="check" size={18} />}
{leadingIcon &&
(typeof leadingIcon === 'function' ? (
leadingIcon({...props, size: 18})
Expand Down
64 changes: 64 additions & 0 deletions src/components/Menu/MenuItem/__tests__/MenuItem.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import React from 'react';
import {MenuItem} from '../MenuItem';
import {useTheme} from '../../../../hooks';
import {fireEvent, render} from '../../../../../jest/test-utils';

describe('MenuItem', () => {
beforeEach(() => {
(useTheme as jest.Mock).mockReturnValue({
colors: {
menuText: '#000000',
menuDangerText: '#FF0000',
menuBackgroundActive: '#E0E0E0',
},
fonts: {
bodySmall: {},
},
});
});

it('renders basic menu item correctly', () => {
const onPress = jest.fn();
const {getByText} = render(
<MenuItem label="Test Item" onPress={onPress} />,
);

expect(getByText('Test Item')).toBeTruthy();
});

it('handles press events', () => {
const onPress = jest.fn();
const {getByText} = render(
<MenuItem label="Test Item" onPress={onPress} />,
);

fireEvent.press(getByText('Test Item'));
expect(onPress).toHaveBeenCalled();
});

it('renders leading icon when provided', () => {
const {UNSAFE_getByProps} = render(
<MenuItem label="Test Item" leadingIcon="check" onPress={() => {}} />,
);

expect(UNSAFE_getByProps({source: 'check'})).toBeTruthy();
});

it('renders trailing icon when provided', () => {
const {UNSAFE_getByProps} = render(
<MenuItem label="Test Item" trailingIcon="close" onPress={() => {}} />,
);

expect(UNSAFE_getByProps({source: 'close'})).toBeTruthy();
});

it('handles disabled state correctly', () => {
const onPress = jest.fn();
const {getByText} = render(
<MenuItem label="Test Item" onPress={onPress} disabled={true} />,
);

fireEvent.press(getByText('Test Item'));
expect(onPress).not.toHaveBeenCalled();
});
});
49 changes: 49 additions & 0 deletions src/components/Menu/SubMenu/__tests__/SubMenu.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React from 'react';
import {render} from '../../../../../jest/test-utils';
import {SubMenu} from '../SubMenu';
import {MenuItem} from '../../MenuItem';

describe('SubMenu', () => {
it('renders when visible', () => {
const {getByText} = render(
<SubMenu
visible={true}
onDismiss={() => {}}
anchorPosition={{x: 100, y: 100}}>
<MenuItem label="SubMenu Item" onPress={() => {}} />
</SubMenu>,
);

expect(getByText('SubMenu Item')).toBeTruthy();
});

it('does not render when not visible', () => {
const {queryByText} = render(
<SubMenu
visible={false}
onDismiss={() => {}}
anchorPosition={{x: 100, y: 100}}>
<MenuItem label="SubMenu Item" onPress={() => {}} />
</SubMenu>,
);

expect(queryByText('SubMenu Item')).toBeNull();
});

it('handles multiple menu items', () => {
const {getByText} = render(
<SubMenu
visible={true}
onDismiss={() => {}}
anchorPosition={{x: 100, y: 100}}>
<MenuItem label="Item 1" onPress={() => {}} />
<MenuItem label="Item 2" onPress={() => {}} />
<MenuItem label="Item 3" onPress={() => {}} />
</SubMenu>,
);

expect(getByText('Item 1')).toBeTruthy();
expect(getByText('Item 2')).toBeTruthy();
expect(getByText('Item 3')).toBeTruthy();
});
});
35 changes: 35 additions & 0 deletions src/components/Menu/__tests__/Menu.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React from 'react';
import {render} from '../../../../jest/test-utils';
import {Menu} from '../Menu';

describe('Menu', () => {
it('renders menu items correctly', () => {
const {getByText} = render(
<Menu visible={true} onDismiss={() => {}} anchor={undefined}>
<Menu.Item label="Item 1" onPress={() => {}} />
<Menu.Item label="Item 2" onPress={() => {}} />
</Menu>,
);

expect(getByText('Item 1')).toBeTruthy();
expect(getByText('Item 2')).toBeTruthy();
});

it('renders separators correctly', () => {
const {UNSAFE_getAllByType} = render(
<Menu visible={true} onDismiss={() => {}} anchor={undefined}>
<Menu.Item label="Item 1" onPress={() => {}} />
<Menu.Separator />
<Menu.Item label="Item 2" onPress={() => {}} />
<Menu.GroupSeparator />
<Menu.Item label="Item 3" onPress={() => {}} />
</Menu>,
);

const separators = UNSAFE_getAllByType(Menu.Separator);
const groupSeparators = UNSAFE_getAllByType(Menu.GroupSeparator);

expect(separators).toHaveLength(1);
expect(groupSeparators).toHaveLength(1);
});
});
Loading

0 comments on commit 0c484bd

Please sign in to comment.