From 19d970e9a8980b6a564715bd3cc1e2bd4332804f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Muhammed=20Erkam=20G=C3=B6kcep=C4=B1nar?= Date: Mon, 16 Dec 2024 21:23:23 +0300 Subject: [PATCH] feat: add mobile unit tests --- mobile/__tests__/CreatePost.test.js | 78 +++++++++++++ mobile/__tests__/Post.test.js | 170 ++++++++++++++++++++++++++++ mobile/package-lock.json | 13 ++- mobile/package.json | 1 + 4 files changed, 259 insertions(+), 3 deletions(-) create mode 100644 mobile/__tests__/CreatePost.test.js create mode 100644 mobile/__tests__/Post.test.js diff --git a/mobile/__tests__/CreatePost.test.js b/mobile/__tests__/CreatePost.test.js new file mode 100644 index 00000000..06258433 --- /dev/null +++ b/mobile/__tests__/CreatePost.test.js @@ -0,0 +1,78 @@ +import React from 'react'; +import { render, fireEvent, waitFor } from '@testing-library/react-native'; +import CreatePost from '../src/pages/CreatePost'; // Adjust the import as necessary +import { useAuth } from '../src/hooks/useAuth'; // Adjust the import path as necessary + +// Mock useAuth hook +jest.mock('../src/hooks/useAuth', () => ({ + useAuth: () => ({ + accessToken: 'mockAccessToken', // Provide a default or mock value + userId: 'mockUserId', + }), +})); + +jest.mock('node-fetch', () => require('jest-fetch-mock')); + +describe('CreatePost Component', () => { + + it('toggles tag selection', () => { + const { getByText, getByTestId } = render(); + + const tagChip = getByText('Tech'); // Assuming 'Tech' is the text of the tag chip + fireEvent.press(tagChip); + + expect(getByText("Tech").props.style.backgroundColor).toBe("#007BFF"); + }); + + it('creates a post successfully', async () => { + const postData = { + title: "Test Post Title", + content: "This is the content of the post.", + tags: ["Tech", "Science"] + }; + + fetch.mockResolvedValueOnce({ + ok: true, + json: async () => postData, + }); + + const { getByPlaceholderText, getByText } = render(); + const titleInput = getByPlaceholderText("Title"); + const contentInput = getByPlaceholderText("Content..."); + + fireEvent.changeText(titleInput, postData.title); + fireEvent.changeText(contentInput, postData.content); + + fireEvent.press(getByText("Tech")); + fireEvent.press(getByText("Science")); + + fireEvent.press(getByText("Create Post")); + + await waitFor(() => getByText("Post created successfully!")); + + expect(getByText("Post created successfully!")).toBeTruthy(); + }); + + it('handles stock search correctly', async () => { + const { getByPlaceholderText, findByText } = render(); + + const searchInput = getByPlaceholderText("Search stocks..."); + fireEvent.changeText(searchInput, "Apple"); + + const stockName = await findByText("Apple (AAPL)"); + + expect(stockName).toBeTruthy(); + }); + + it('displays an error when stock not found', async () => { + const { getByPlaceholderText, queryByText } = render(); + + const searchInput = getByPlaceholderText("Search stocks..."); + fireEvent.changeText(searchInput, "NonExistentStock"); + + const errorMessage = await waitFor(() => queryByText("Stock not found")); + + expect(errorMessage).toBeTruthy(); + }); + +}); diff --git a/mobile/__tests__/Post.test.js b/mobile/__tests__/Post.test.js new file mode 100644 index 00000000..f918a04a --- /dev/null +++ b/mobile/__tests__/Post.test.js @@ -0,0 +1,170 @@ +import React from 'react'; +import renderer from 'react-test-renderer'; +import { render, waitFor, fireEvent } from '@testing-library/react-native'; +import Post from '../src/pages/Post'; // Adjust the import as necessary + +jest.mock('node-fetch', () => require('jest-fetch-mock')); + +describe('Post Component', () => { + it('should match snapshot', () => { + const postData = { + postId: '1', + author: 'John Doe', + userMap: {}, + post: { + title: 'Test Post Title', + created_at: '2024-12-16T00:00:00Z', + tags: [{ id: 1, name: 'Test Tag' }], + content: 'This is a test post content.', + stocks: [1], + liked_by: [], + }, + }; + + const tree = renderer.create().toJSON(); + expect(tree).toMatchSnapshot(); + }); + + it('should fetch post data and update state', async () => { + const mockPost = { + title: 'Test Post Title', + created_at: '2024-12-16T00:00:00Z', + tags: [{ id: 1, name: 'Test Tag' }], + content: 'This is a test post content.', + stocks: [1], + liked_by: [], + }; + + fetch.mockResolvedValueOnce({ + ok: true, + json: async () => mockPost, + }); + + const { getByText } = render(); + + await waitFor(() => getByText('Test Post Title')); + + expect(getByText('Test Post Title')).toBeTruthy(); + }); + + it('should handle fetch error', async () => { + fetch.mockResolvedValueOnce({ + ok: false, + }); + + const { queryByText } = render(); + + await waitFor(() => queryByText('Loading...')); + + expect(queryByText('Test Post Title')).toBeNull(); + }); + + it('should fetch comments and update state', async () => { + const mockComments = [ + { id: 1, user_id: '2', content: 'Test comment 1' }, + { id: 2, user_id: '3', content: 'Test comment 2' }, + ]; + + fetch.mockResolvedValueOnce({ + ok: true, + json: async () => mockComments, + }); + + const { getByText } = render(); + + await waitFor(() => getByText('Test comment 1')); + + expect(getByText('Test comment 1')).toBeTruthy(); + expect(getByText('Test comment 2')).toBeTruthy(); + }); + + it('should handle fetch comments error', async () => { + fetch.mockResolvedValueOnce({ + ok: false, + }); + + const { queryByText } = render(); + + await waitFor(() => queryByText('Loading...')); + + expect(queryByText('Test comment 1')).toBeNull(); + }); + + it('should post a comment and update the state', async () => { + const newComment = { post_id: '1', content: 'New comment' }; + const mockResponse = { id: 3, user_id: '4', content: 'New comment' }; + + fetch.mockResolvedValueOnce({ + ok: true, + json: async () => mockResponse, + }); + + const { getByText, getByPlaceholderText, getByTestId } = render( + + ); + + fireEvent.changeText(getByPlaceholderText('Write a comment...'), 'New comment'); + fireEvent.press(getByTestId('submit-button')); + + await waitFor(() => getByText('New comment')); + + expect(getByText('New comment')).toBeTruthy(); + }); + + it('should handle post comment error', async () => { + fetch.mockResolvedValueOnce({ + ok: false, + }); + + const { getByPlaceholderText } = render( + + ); + + fireEvent.changeText(getByPlaceholderText('Write a comment...'), 'New comment'); + fireEvent.press(getByTestId('submit-button')); + + await waitFor(() => expect(Alert.alert).toHaveBeenCalledWith('Error liking the post: 400')); + + expect(Alert.alert).toHaveBeenCalled(); + }); + + it('should post a like and update the state', async () => { + fetch.mockResolvedValueOnce({ + ok: true, + }); + + const { getByText } = render(); + + fireEvent.press(getByText('👍 Like')); + + await waitFor(() => getByText('👍 Liked')); + + expect(getByText('👍 Liked')).toBeTruthy(); + }); + + it('should handle post like error', async () => { + fetch.mockResolvedValueOnce({ + ok: false, + }); + + const { getByText } = render(); + + fireEvent.press(getByText('👍 Like')); + + await waitFor(() => expect(Alert.alert).toHaveBeenCalledWith('Error liking the post: 400')); + + expect(Alert.alert).toHaveBeenCalled(); + }); + + it('should open and close the comment input modal', () => { + const { getByText, queryByText } = render(); + + fireEvent.press(getByText('💬 Add Comment')); + + expect(queryByText('Write a comment...')).toBeTruthy(); + + fireEvent.press(getByText('Cancel')); + + expect(queryByText('Write a comment...')).toBeNull(); + }); +}); diff --git a/mobile/package-lock.json b/mobile/package-lock.json index 8c8e48c7..798e6d61 100644 --- a/mobile/package-lock.json +++ b/mobile/package-lock.json @@ -8,6 +8,7 @@ "name": "mobile", "version": "0.0.1", "dependencies": { + "@react-native-picker/picker": "^2.10.2", "@react-navigation/bottom-tabs": "^6.6.1", "@react-navigation/drawer": "^6.7.2", "@react-navigation/native": "^6.1.18", @@ -3886,6 +3887,15 @@ "node": ">=8" } }, + "node_modules/@react-native-picker/picker": { + "version": "2.10.2", + "resolved": "https://registry.npmjs.org/@react-native-picker/picker/-/picker-2.10.2.tgz", + "integrity": "sha512-kr3OvCRwTYjR/OKlb52k4xmQVU7dPRIALqpyiihexdJxEgvc1smnepgqCeM9oXmNSG4YaV5/RSxFlLC5Z/T/Eg==", + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, "node_modules/@react-native/assets-registry": { "version": "0.75.4", "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.75.4.tgz", @@ -10880,10 +10890,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz", "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==", - - "license": "MIT", - "engines": { "node": ">=18" } diff --git a/mobile/package.json b/mobile/package.json index cb8c920c..2826d412 100644 --- a/mobile/package.json +++ b/mobile/package.json @@ -10,6 +10,7 @@ "test": "jest" }, "dependencies": { + "@react-native-picker/picker": "^2.10.2", "@react-navigation/bottom-tabs": "^6.6.1", "@react-navigation/drawer": "^6.7.2", "@react-navigation/native": "^6.1.18",