Skip to content

Commit 4bf0ac7

Browse files
committed
resolve merge conflict in jest.config.js - keep improved configuration
2 parents 6404fe6 + 279ff77 commit 4bf0ac7

14 files changed

+510
-28
lines changed
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { render, screen, fireEvent } from '@testing-library/react'
2+
import '@testing-library/jest-dom'
3+
import { beforeAll, beforeEach, describe, expect, jest, test } from '@jest/globals'
4+
import BackToTopButton from '../buttons/BackToTopButton'
5+
import '@testing-library/jest-dom'
6+
7+
describe('BackToTopButton Test', () => {
8+
beforeAll(() => {
9+
window.scrollTo = jest.fn()
10+
11+
Object.defineProperty(window, 'scrollY', { value: 0, writable: true })
12+
})
13+
14+
test('Not displayed when scrollY < 300', () => {
15+
render(<BackToTopButton />)
16+
fireEvent.scroll(window)
17+
expect(screen.queryByRole('button')).toBeNull()
18+
})
19+
20+
test('Displayed when scrollY > 300', () => {
21+
render(<BackToTopButton />)
22+
Object.defineProperty(window, 'scrollY', { value: 400, writable: true })
23+
fireEvent.scroll(window)
24+
expect(screen.getByRole('button'))
25+
})
26+
27+
test('When clicked, it calls scrollTo with the correct parameters', () => {
28+
render(<BackToTopButton />)
29+
Object.defineProperty(window, 'scrollY', { value: 400, writable: true })
30+
fireEvent.scroll(window)
31+
32+
const button = screen.getByRole('button')
33+
fireEvent.click(button)
34+
35+
expect(window.scrollTo).toHaveBeenCalledWith({
36+
top: 0,
37+
behavior: 'smooth'
38+
})
39+
})
40+
})
41+
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { render, screen, fireEvent } from '@testing-library/react'
2+
import '@testing-library/jest-dom'
3+
import { describe, expect, test } from '@jest/globals'
4+
import { Contributors } from '../index/Contributors'
5+
6+
describe('Contributors', () => {
7+
const mockContributors = [
8+
{ id: '1', url: 'https://github.com/alice', avatar: '/alice.png', name: 'Alice' },
9+
{ id: '2', url: 'https://github.com/bob', avatar: '/bob.png', name: 'Bob' }
10+
]
11+
12+
test('renders all contributor images', () => {
13+
render(<Contributors contributors={mockContributors} />)
14+
15+
const aliceImage = screen.getByAltText('Alice')
16+
const bobImage = screen.getByAltText('Bob')
17+
18+
expect(aliceImage).not.toBeNull()
19+
expect(bobImage).not.toBeNull()
20+
21+
const aliceLink = screen.getByRole('link', { name: /alice/i })
22+
const bobLink = screen.getByRole('link', { name: /bob/i })
23+
24+
expect(aliceLink.getAttribute('href')).toBe('https://github.com/alice')
25+
expect(bobLink.getAttribute('href')).toBe('https://github.com/bob')
26+
})
27+
28+
test('renders fallback when image fails to load', () => {
29+
render(<Contributors contributors={mockContributors} />)
30+
31+
const aliceImage = screen.getByAltText('Alice')
32+
fireEvent.error(aliceImage)
33+
34+
const fallback = screen.getByText('A')
35+
expect(fallback).not.toBeNull()
36+
})
37+
})
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { render, screen, waitFor } from '@testing-library/react'
2+
import React from 'react'
3+
import Footer from '../layout/Footer'
4+
5+
jest.mock('../buttons/Coffee', () => () => <div data-testid="coffee-button" />)
6+
jest.mock('../buttons/Sponsor', () => () => <div data-testid="sponsor-button" />)
7+
8+
describe('Footer', () => {
9+
beforeEach(() => {
10+
global.fetch = jest.fn(() =>
11+
Promise.resolve({
12+
json: () => Promise.resolve({ stargazers_count: 123 })
13+
} as Response)
14+
) as jest.Mock
15+
})
16+
17+
afterEach(() => {
18+
jest.resetAllMocks()
19+
})
20+
21+
test('renders footer links and components', async () => {
22+
render(<Footer />)
23+
24+
expect(screen.getByText('Technology')).toBeInTheDocument()
25+
expect(screen.getByText('Information')).toBeInTheDocument()
26+
expect(screen.getByText('Helpful Links')).toBeInTheDocument()
27+
28+
expect(screen.getByTestId('coffee-button')).toBeInTheDocument()
29+
expect(screen.getByTestId('sponsor-button')).toBeInTheDocument()
30+
31+
await waitFor(() => {
32+
expect(screen.getByText('123')).toBeInTheDocument()
33+
})
34+
})
35+
})
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
import { render, screen, fireEvent, waitFor, act } from '@testing-library/react'
2+
import { useUser } from '@clerk/nextjs'
3+
import { useRouter } from 'next/router'
4+
import Header from '../layout/Header'
5+
6+
jest.mock('@clerk/nextjs', () => ({
7+
useUser: jest.fn(),
8+
UserButton: jest.fn(() => <div data-testid="user-button" />),
9+
}))
10+
11+
jest.mock('next/router', () => ({
12+
useRouter: jest.fn(),
13+
}))
14+
15+
describe('Header component', () => {
16+
const pushMock = jest.fn()
17+
const onMock = jest.fn()
18+
const offMock = jest.fn()
19+
20+
beforeEach(() => {
21+
(useRouter as jest.Mock).mockReturnValue({
22+
push: pushMock,
23+
events: { on: onMock, off: offMock },
24+
})
25+
26+
global.fetch = jest.fn(() =>
27+
Promise.resolve({
28+
json: () => Promise.resolve({ stargazers_count: 123 }),
29+
}) as any
30+
)
31+
})
32+
33+
afterEach(() => {
34+
jest.clearAllMocks()
35+
})
36+
37+
const renderHeader = async () => {
38+
await act(async () => {
39+
render(<Header />)
40+
})
41+
}
42+
43+
test('renders logo and main links', async () => {
44+
;(useUser as jest.Mock).mockReturnValue({ user: null, isLoaded: true })
45+
46+
await renderHeader()
47+
48+
expect(screen.getAllByAltText('Logo')[0]).toBeInTheDocument()
49+
expect(screen.getByText('Languages')).toBeInTheDocument()
50+
expect(screen.getByText('Frameworks')).toBeInTheDocument()
51+
expect(screen.getByText('Git')).toBeInTheDocument()
52+
expect(screen.getByText('Roadmap')).toBeInTheDocument()
53+
expect(screen.getAllByText('Sign In')[0]).toBeInTheDocument()
54+
})
55+
56+
test('fetches GitHub stars and displays them', async () => {
57+
;(useUser as jest.Mock).mockReturnValue({ user: null, isLoaded: true })
58+
59+
await renderHeader()
60+
61+
await waitFor(() => {
62+
expect(screen.getAllByText('123')[0]).toBeInTheDocument()
63+
})
64+
})
65+
66+
test('toggles dropdown menus', async () => {
67+
;(useUser as jest.Mock).mockReturnValue({ user: null, isLoaded: true })
68+
69+
await renderHeader()
70+
71+
const languagesButton = screen.getByText('Languages')
72+
fireEvent.click(languagesButton)
73+
expect(screen.getByText('JavaScript')).toBeInTheDocument()
74+
75+
fireEvent.click(languagesButton)
76+
expect(screen.queryByText('JavaScript')).not.toBeInTheDocument()
77+
})
78+
79+
test('navigates when dropdown item clicked', async () => {
80+
;(useUser as jest.Mock).mockReturnValue({ user: null, isLoaded: true })
81+
82+
await renderHeader()
83+
84+
fireEvent.click(screen.getByText('Languages'))
85+
fireEvent.click(screen.getByText('JavaScript'))
86+
expect(pushMock).toHaveBeenCalledWith('/languages/javascript')
87+
})
88+
89+
test('mobile menu toggles', async () => {
90+
;(useUser as jest.Mock).mockReturnValue({ user: null, isLoaded: true })
91+
await renderHeader()
92+
93+
const menuButton = screen.getByTestId('mobile-menu-button')
94+
95+
fireEvent.click(menuButton)
96+
await waitFor(() => {
97+
const lang = screen.getAllByText('Languages')
98+
expect(lang.length).toBeGreaterThan(0)
99+
})
100+
101+
fireEvent.click(menuButton)
102+
await waitFor(() => {
103+
const lang = screen.queryAllByText('Languages')
104+
expect(lang.length).toBeGreaterThan(0)
105+
})
106+
})
107+
108+
test('renders UserButton if user is logged in', async () => {
109+
;(useUser as jest.Mock).mockReturnValue({ user: { id: '1' }, isLoaded: true })
110+
111+
await renderHeader()
112+
113+
expect(screen.getByTestId('user-button')).toBeInTheDocument()
114+
})
115+
116+
test('handles fetch error gracefully', async () => {
117+
;(useUser as jest.Mock).mockReturnValue({ user: null, isLoaded: true })
118+
;(global.fetch as jest.Mock).mockRejectedValueOnce(new Error('Network error'))
119+
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {})
120+
121+
await renderHeader()
122+
123+
await waitFor(() => {
124+
expect(errorSpy).toHaveBeenCalled()
125+
})
126+
errorSpy.mockRestore()
127+
})
128+
129+
test('registers and cleans up router.events listeners', async () => {
130+
;(useUser as jest.Mock).mockReturnValue({ user: null, isLoaded: true })
131+
132+
const { unmount } = render(<Header />)
133+
134+
await waitFor(() => {
135+
expect(onMock).toHaveBeenCalledWith('routeChangeStart', expect.any(Function))
136+
})
137+
138+
unmount()
139+
expect(offMock).toHaveBeenCalledWith('routeChangeStart', expect.any(Function))
140+
})
141+
142+
test('toggleSideNav opens and closes side navigation', async () => {
143+
;(useUser as jest.Mock).mockReturnValue({ user: null, isLoaded: true })
144+
await renderHeader()
145+
146+
const menuButton = screen.getByTestId('mobile-menu-button')
147+
fireEvent.click(menuButton)
148+
fireEvent.click(menuButton)
149+
expect(menuButton).toBeInTheDocument()
150+
})
151+
152+
test('dispatches custom event when dropdowns open', async () => {
153+
;(useUser as jest.Mock).mockReturnValue({ user: null, isLoaded: true })
154+
const eventSpy = jest.fn()
155+
window.addEventListener('dropdownOpened', eventSpy)
156+
157+
await renderHeader()
158+
159+
const langBtn = screen.getAllByText('Languages')[0]
160+
const fwBtn = screen.getAllByText('Frameworks')[0]
161+
const gitBtn = screen.getAllByText('Git')[0]
162+
const roadmapBtn = screen.getAllByText('Roadmap')[0]
163+
164+
fireEvent.click(langBtn)
165+
fireEvent.click(fwBtn)
166+
fireEvent.click(gitBtn)
167+
fireEvent.click(roadmapBtn)
168+
169+
expect(eventSpy).toHaveBeenCalledTimes(4)
170+
171+
window.removeEventListener('dropdownOpened', eventSpy)
172+
})
173+
174+
test('navigates for framework', async () => {
175+
;(useUser as jest.Mock).mockReturnValue({ user: null, isLoaded: true })
176+
177+
await renderHeader()
178+
179+
const fwBtn = screen.getAllByText('Frameworks')[0]
180+
fireEvent.click(fwBtn)
181+
fireEvent.click(await screen.findByText('React'))
182+
expect(pushMock).toHaveBeenCalledWith('/frameworks/react')
183+
184+
185+
})
186+
})
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { render, screen } from '@testing-library/react'
2+
import '@testing-library/jest-dom'
3+
import { useInView } from 'react-intersection-observer'
4+
import { describe, expect, test } from '@jest/globals'
5+
import React from 'react'
6+
import LazyImage from '../index/LazyImage'
7+
8+
jest.mock('react-intersection-observer', () => ({
9+
useInView: jest.fn()
10+
}))
11+
12+
const mockedUseInView = useInView as unknown as jest.Mock
13+
14+
describe('LazyImage', () => {
15+
const mockProps = {
16+
src: '/test.png',
17+
alt: 'Test Image',
18+
width: 100,
19+
height: 100
20+
}
21+
22+
test('does not render image when not in view', () => {
23+
mockedUseInView.mockReturnValue({ ref: jest.fn(), inView: false })
24+
25+
render(<LazyImage {...mockProps} />)
26+
27+
expect(screen.queryByAltText('Test Image')).toBeNull()
28+
})
29+
30+
test('renders image when in view', () => {
31+
mockedUseInView.mockReturnValue({ ref: jest.fn(), inView: true })
32+
33+
render(<LazyImage {...mockProps} />)
34+
35+
const img = screen.getByAltText('Test Image')
36+
expect(img).not.toBeNull()
37+
expect(img?.getAttribute('src')).toContain('test.png')
38+
})
39+
})
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { render, screen } from '@testing-library/react'
2+
import LoadingPage from '../layout/LoadingPage'
3+
import { useRouter } from 'next/router'
4+
import { act } from 'react-dom/test-utils'
5+
6+
jest.mock('next/router', () => ({
7+
useRouter: jest.fn(),
8+
}))
9+
10+
describe('LoadingPage', () => {
11+
let pushMock: jest.Mock
12+
13+
beforeEach(() => {
14+
jest.useFakeTimers()
15+
pushMock = jest.fn()
16+
;(useRouter as jest.Mock).mockReturnValue({
17+
push: pushMock,
18+
})
19+
})
20+
21+
afterEach(() => {
22+
jest.clearAllMocks()
23+
jest.useRealTimers()
24+
})
25+
26+
test('renders logo, title, bouncing dots and redirect text', () => {
27+
render(<LoadingPage />)
28+
29+
expect(screen.getByAltText('FCM Logo')).toBeInTheDocument()
30+
31+
expect(screen.getByText('Welcome to Fork, Commit, Merge')).toBeInTheDocument()
32+
33+
expect(screen.getByText('Redirecting you to the homepage...')).toBeInTheDocument()
34+
35+
const dots = document.querySelectorAll('.animate-bounce')
36+
expect(dots.length).toBe(3)
37+
})
38+
39+
test('redirects to home after 3 seconds', () => {
40+
render(<LoadingPage />)
41+
42+
act(() => {
43+
jest.advanceTimersByTime(3000)
44+
})
45+
46+
expect(pushMock).toHaveBeenCalledWith('/')
47+
})
48+
})

0 commit comments

Comments
 (0)