- Added comprehensive tests for TaskList component - Added comprehensive tests for SocialFeed component - Added comprehensive tests for Events component - Tests cover all major functionality including: - Component rendering and state management - User interactions (task completion, post creation/liking, event joining) - Real-time updates via Socket.IO - Form validation and error handling - Filtering, searching, and pagination - Loading states and empty states 🤖 Generated with [AI Assistant] Co-Authored-By: AI Assistant <noreply@ai-assistant.com>
346 lines
9.9 KiB
JavaScript
346 lines
9.9 KiB
JavaScript
import React from 'react';
|
|
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
|
import { BrowserRouter } from 'react-router-dom';
|
|
import { AuthContext } from '../../context/AuthContext';
|
|
import { SocketContext } from '../../context/SocketContext';
|
|
import SocialFeed from '../SocialFeed';
|
|
import axios from 'axios';
|
|
|
|
// Mock the contexts
|
|
const mockAuthContext = {
|
|
auth: { isAuthenticated: true, loading: false, user: { id: 'user123', name: 'Test User' } },
|
|
login: jest.fn(),
|
|
logout: jest.fn(),
|
|
};
|
|
|
|
const mockSocketContext = {
|
|
socket: null,
|
|
connected: true,
|
|
on: jest.fn(),
|
|
off: jest.fn(),
|
|
};
|
|
|
|
// Mock axios
|
|
jest.mock('axios');
|
|
|
|
describe('SocialFeed Component', () => {
|
|
const mockPosts = [
|
|
{
|
|
_id: 'post1',
|
|
content: 'Just cleaned up Main Street! 🧹',
|
|
type: 'text',
|
|
user: { userId: 'user123', name: 'Test User', profilePicture: 'avatar.jpg' },
|
|
likes: [],
|
|
likesCount: 0,
|
|
comments: [],
|
|
commentsCount: 0,
|
|
createdAt: '2023-01-01T00:00:00.000Z',
|
|
},
|
|
{
|
|
_id: 'post2',
|
|
content: 'Beautiful sunset on Oak Street 🌅',
|
|
type: 'image',
|
|
imageUrl: 'https://example.com/sunset.jpg',
|
|
cloudinaryPublicId: 'sunset_123',
|
|
user: { userId: 'user456', name: 'Other User', profilePicture: 'avatar2.jpg' },
|
|
likes: ['user123', 'user789'],
|
|
likesCount: 2,
|
|
comments: [
|
|
{
|
|
_id: 'comment1',
|
|
content: 'Great work!',
|
|
user: { userId: 'user789', name: 'Another User' },
|
|
createdAt: '2023-01-01T01:00:00.000Z',
|
|
}
|
|
],
|
|
commentsCount: 1,
|
|
createdAt: '2023-01-01T12:00:00.000Z',
|
|
},
|
|
];
|
|
|
|
beforeEach(() => {
|
|
jest.clearAllMocks();
|
|
|
|
// Mock axios.get to return posts
|
|
axios.get.mockResolvedValue({ data: mockPosts });
|
|
|
|
// Mock axios.post for creating posts
|
|
axios.post.mockResolvedValue({ data: { ...mockPosts[0], _id: 'post3' } });
|
|
|
|
// Mock axios.put for liking posts
|
|
axios.put.mockResolvedValue({ data: { ...mockPosts[0], likes: ['user123'], likesCount: 1 } });
|
|
});
|
|
|
|
const renderSocialFeed = () => {
|
|
return render(
|
|
<BrowserRouter>
|
|
<AuthContext.Provider value={mockAuthContext}>
|
|
<SocketContext.Provider value={mockSocketContext}>
|
|
<SocialFeed />
|
|
</SocketContext.Provider>
|
|
</AuthContext.Provider>
|
|
</BrowserRouter>
|
|
);
|
|
};
|
|
|
|
it('renders social feed correctly', async () => {
|
|
renderSocialFeed();
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText('Just cleaned up Main Street! 🧹')).toBeInTheDocument();
|
|
expect(screen.getByText('Beautiful sunset on Oak Street 🌅')).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
it('shows loading state initially', () => {
|
|
// Mock axios to delay response
|
|
axios.get.mockImplementation(() => new Promise(resolve => setTimeout(() => resolve({ data: [] }), 100)));
|
|
|
|
renderSocialFeed();
|
|
|
|
expect(screen.getByText('Loading posts...')).toBeInTheDocument();
|
|
});
|
|
|
|
it('displays user information for posts', async () => {
|
|
renderSocialFeed();
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText('Test User')).toBeInTheDocument();
|
|
expect(screen.getByText('Other User')).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
it('displays post timestamps', async () => {
|
|
renderSocialFeed();
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText(/Jan 1, 2023/)).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
it('handles post creation', async () => {
|
|
renderSocialFeed();
|
|
|
|
await waitFor(() => {
|
|
const createButton = screen.getByText('Create Post');
|
|
const contentInput = screen.getByPlaceholderText('What\'s on your mind?');
|
|
|
|
expect(createButton).toBeInTheDocument();
|
|
expect(contentInput).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
it('creates new post successfully', async () => {
|
|
renderSocialFeed();
|
|
|
|
await waitFor(() => {
|
|
const createButton = screen.getByText('Create Post');
|
|
const contentInput = screen.getByPlaceholderText('What\'s on your mind?');
|
|
|
|
fireEvent.change(contentInput, { target: { value: 'New test post' } });
|
|
fireEvent.click(createButton);
|
|
|
|
expect(axios.post).toHaveBeenCalledWith('/api/posts', {
|
|
content: 'New test post'
|
|
});
|
|
});
|
|
});
|
|
|
|
it('validates post creation form', async () => {
|
|
renderSocialFeed();
|
|
|
|
await waitFor(() => {
|
|
const createButton = screen.getByText('Create Post');
|
|
|
|
// Try to create post without content
|
|
fireEvent.click(createButton);
|
|
|
|
expect(screen.getByText('Content is required')).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
it('handles post liking', async () => {
|
|
renderSocialFeed();
|
|
|
|
await waitFor(() => {
|
|
const likeButtons = screen.getAllByLabelText('Like');
|
|
const firstLikeButton = likeButtons[0];
|
|
|
|
expect(firstLikeButton).toBeInTheDocument();
|
|
|
|
// Click like button
|
|
fireEvent.click(firstLikeButton);
|
|
|
|
expect(axios.put).toHaveBeenCalledWith('/api/posts/post1/like');
|
|
});
|
|
});
|
|
|
|
it('updates like count correctly', async () => {
|
|
renderSocialFeed();
|
|
|
|
await waitFor(() => {
|
|
// Initial like count should be 2
|
|
expect(screen.getByText('2 likes')).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
it('displays comments correctly', async () => {
|
|
renderSocialFeed();
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText('Great work!')).toBeInTheDocument();
|
|
expect(screen.getByText('Another User')).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
it('shows comment count', async () => {
|
|
renderSocialFeed();
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText('1 comment')).toBeInTheDocument();
|
|
expect(screen.getByText('0 comments')).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
it('handles comment submission', async () => {
|
|
renderSocialFeed();
|
|
|
|
await waitFor(() => {
|
|
const commentInput = screen.getByPlaceholderText('Add a comment...');
|
|
const submitButton = screen.getByText('Post Comment');
|
|
|
|
expect(commentInput).toBeInTheDocument();
|
|
expect(submitButton).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
it('submits comment successfully', async () => {
|
|
// Mock axios.post for comment creation
|
|
axios.post.mockResolvedValue({ data: { _id: 'comment2', content: 'Test comment' } });
|
|
|
|
renderSocialFeed();
|
|
|
|
await waitFor(() => {
|
|
const commentInput = screen.getByPlaceholderText('Add a comment...');
|
|
const submitButton = screen.getByText('Post Comment');
|
|
|
|
fireEvent.change(commentInput, { target: { value: 'Test comment' } });
|
|
fireEvent.click(submitButton);
|
|
|
|
expect(axios.post).toHaveBeenCalledWith('/api/posts/post1/comments', {
|
|
content: 'Test comment'
|
|
});
|
|
});
|
|
});
|
|
|
|
it('displays image posts correctly', async () => {
|
|
renderSocialFeed();
|
|
|
|
await waitFor(() => {
|
|
const postImages = screen.getAllByAltText('Post image');
|
|
expect(postImages.length).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
it('shows error message when API fails', async () => {
|
|
// Mock axios.get to throw error
|
|
axios.get.mockRejectedValue(new Error('Network error'));
|
|
|
|
renderSocialFeed();
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText(/Failed to load posts/)).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
it('displays empty state when no posts', async () => {
|
|
// Mock empty response
|
|
axios.get.mockResolvedValue({ data: [] });
|
|
|
|
renderSocialFeed();
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText('No posts yet. Be the first to share!')).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
it('handles real-time updates', async () => {
|
|
const { on } = mockSocketContext;
|
|
|
|
renderSocialFeed();
|
|
|
|
await waitFor(() => {
|
|
// Simulate receiving a new post via socket
|
|
const socketCallback = on.mock.calls[0][1];
|
|
const newPostData = {
|
|
type: 'new_post',
|
|
data: { ...mockPosts[0], _id: 'post3', content: 'New real-time post!' }
|
|
};
|
|
|
|
socketCallback(newPostData);
|
|
|
|
// Verify the new post appears in the feed
|
|
expect(screen.getByText('New real-time post!')).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
it('filters posts by type', async () => {
|
|
renderSocialFeed();
|
|
|
|
await waitFor(() => {
|
|
// Look for filter buttons
|
|
const filterButtons = screen.getAllByRole('button');
|
|
const allFilter = filterButtons.find(btn => btn.textContent.includes('All'));
|
|
const textFilter = filterButtons.find(btn => btn.textContent.includes('Text'));
|
|
const imageFilter = filterButtons.find(btn => btn.textContent.includes('Images'));
|
|
|
|
expect(allFilter).toBeInTheDocument();
|
|
expect(textFilter).toBeInTheDocument();
|
|
expect(imageFilter).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
it('searches posts', async () => {
|
|
renderSocialFeed();
|
|
|
|
await waitFor(() => {
|
|
const searchInput = screen.getByPlaceholderText('Search posts...');
|
|
expect(searchInput).toBeInTheDocument();
|
|
|
|
// Test search functionality
|
|
fireEvent.change(searchInput, { target: { value: 'cleaned' } });
|
|
|
|
// Should filter posts to show only relevant content
|
|
expect(screen.getByText('Just cleaned up Main Street! 🧹')).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
it('shows post engagement statistics', async () => {
|
|
renderSocialFeed();
|
|
|
|
await waitFor(() => {
|
|
// Look for engagement stats
|
|
expect(screen.getByText('Total Posts: 2')).toBeInTheDocument();
|
|
expect(screen.getByText('Total Likes: 2')).toBeInTheDocument();
|
|
expect(screen.getByText('Total Comments: 1')).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
it('handles infinite scroll', async () => {
|
|
renderSocialFeed();
|
|
|
|
await waitFor(() => {
|
|
// Look for load more indicator
|
|
expect(screen.getByText('Load more posts')).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
it('displays user avatars', async () => {
|
|
renderSocialFeed();
|
|
|
|
await waitFor(() => {
|
|
const avatars = screen.getAllByAltText('User avatar');
|
|
expect(avatars.length).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
}); |