From 4666ad23ae50c74900b06ecadf729817b91b9cdf Mon Sep 17 00:00:00 2001 From: Dishant1804 Date: Fri, 28 Mar 2025 18:32:08 +0530 Subject: [PATCH 1/7] about page added --- frontend/__tests__/e2e/pages/About.spec.ts | 60 ++++++++ frontend/__tests__/unit/data/mockAboutData.ts | 24 ++++ frontend/__tests__/unit/pages/About.test.tsx | 50 +++++++ frontend/src/App.tsx | 2 + .../src/components/ToggleContributors.tsx | 12 +- frontend/src/components/UserCard.tsx | 4 +- frontend/src/pages/About.tsx | 136 ++++++++++++++++++ frontend/src/pages/Users.tsx | 1 + frontend/src/types/card.ts | 1 + frontend/src/types/project.ts | 1 + frontend/src/utils/aboutData.ts | 31 ++++ frontend/src/utils/constants.ts | 2 +- 12 files changed, 317 insertions(+), 7 deletions(-) create mode 100644 frontend/__tests__/e2e/pages/About.spec.ts create mode 100644 frontend/__tests__/unit/data/mockAboutData.ts create mode 100644 frontend/__tests__/unit/pages/About.test.tsx create mode 100644 frontend/src/pages/About.tsx create mode 100644 frontend/src/utils/aboutData.ts diff --git a/frontend/__tests__/e2e/pages/About.spec.ts b/frontend/__tests__/e2e/pages/About.spec.ts new file mode 100644 index 0000000000..f2860c70a1 --- /dev/null +++ b/frontend/__tests__/e2e/pages/About.spec.ts @@ -0,0 +1,60 @@ +import { test, expect } from '@playwright/test' +import { mockAboutData } from '@unit/data/mockAboutData' + +test.describe('About Page', () => { + test.beforeEach(async ({ page }) => { + await page.route('**/idx/', async (route) => { + await route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify({ + hits: [mockAboutData.project], + nbPages: 1, + }), + }) + }) + + await page.goto('/about') + }) + + test('renders main sections correctly', async ({ page }) => { + await expect(page.getByRole('heading', { name: 'About' })).toBeVisible() + await expect(page.getByRole('heading', { name: 'Project history' })).toBeVisible() + await expect(page.getByRole('heading', { name: 'Leaders' })).toBeVisible() + await expect(page.getByRole('heading', { name: 'Roadmap' })).toBeVisible() + }) + + test('displays leader information correctly', async ({ page }) => { + await expect(page.getByRole('button', { name: 'Arkadii Yakovets Arkadii' })).toBeVisible() + await expect(page.getByRole('button', { name: 'Kateryna Golovanova Kateryna' })).toBeVisible() + await expect(page.getByRole('button', { name: 'Starr Brown Starr Brown OWASP' })).toBeVisible() + }) + + test('displays contributor information when data is loaded', async ({ page }) => { + await expect(page.getByText('Contributor 1')).toBeVisible() + await expect(page.getByText('Contributor 2')).toBeVisible() + }) + + test('loads roadmap items correctly', async ({ page }) => { + await expect(page.getByRole('heading', { name: 'Roadmap' })).toBeVisible() + expect(await page.locator('li').count()).toBeGreaterThan(0) + }) + + test('displays animated counters with correct values', async ({ page }) => { + await expect(page.getByText('1.2K+Contributors')).toBeVisible() + await expect(page.getByText('40+Issues')).toBeVisible() + await expect(page.getByText('60+Forks')).toBeVisible() + await expect(page.getByText('890+Stars')).toBeVisible() + }) + + test('opens GitHub profile in new window when leader button is clicked', async ({ + page, + context, + }) => { + const pagePromise = context.waitForEvent('page') + await page.getByRole('button', { name: 'View profile' }).first().click() + const newPage = await pagePromise + await newPage.waitForLoadState() + expect(newPage.url()).toContain('github.com') + }) +}) diff --git a/frontend/__tests__/unit/data/mockAboutData.ts b/frontend/__tests__/unit/data/mockAboutData.ts new file mode 100644 index 0000000000..64b6a5aac7 --- /dev/null +++ b/frontend/__tests__/unit/data/mockAboutData.ts @@ -0,0 +1,24 @@ +export const mockAboutData = { + project: { + contributors_count: 1200, + issues_count: 40, + forks_count: 60, + stars_count: 890, + key: 'nest', + top_contributors: Array.from({ length: 15 }, (_, i) => ({ + avatar_url: `https://example.com/avatar${i + 1}.jpg`, + contributions_count: 30 - i, + login: `contributor${i + 1}`, + name: `Contributor ${i + 1}`, + })), + is_active: true, + languages: ['Python', 'GraphQL', 'JavaScript'], + level: 'Lab', + name: 'Test Project', + repositories_count: 3, + summary: 'Test summary', + topics: ['security', 'web'], + type: 'Tool', + url: 'https://github.com/test-project', + }, +} diff --git a/frontend/__tests__/unit/pages/About.test.tsx b/frontend/__tests__/unit/pages/About.test.tsx new file mode 100644 index 0000000000..3f6a4831f6 --- /dev/null +++ b/frontend/__tests__/unit/pages/About.test.tsx @@ -0,0 +1,50 @@ +import { mockAboutData } from '@unit/data/mockAboutData' +import { fetchAlgoliaData } from 'api/fetchAlgoliaData' +import { render, screen, waitFor } from 'wrappers/testUtil' +import About from 'pages/About' + +jest.mock('api/fetchAlgoliaData', () => ({ + fetchAlgoliaData: jest.fn(), +})) + +describe('About Page Component', () => { + beforeEach(() => { + ;(fetchAlgoliaData as jest.Mock).mockResolvedValue({ + hits: [mockAboutData.project], + }) + }) + + afterEach(() => { + jest.clearAllMocks() + }) + + test('renders project data after loading', async () => { + render() + await waitFor(() => { + expect(screen.getByText('About')).toBeInTheDocument() + expect(screen.getByText('Project history')).toBeInTheDocument() + expect(screen.getByText('Leaders')).toBeInTheDocument() + expect(screen.getByText('Roadmap')).toBeInTheDocument() + }) + }) + + test('displays "No data available." when no project data is found', async () => { + ;(fetchAlgoliaData as jest.Mock).mockResolvedValue({ hits: [] }) + render() + await waitFor(() => { + expect(screen.getByText('No data available.')).toBeInTheDocument() + }) + }) + + test('renders top contributors correctly', async () => { + render() + await waitFor(() => { + const firstContributor = mockAboutData.project.top_contributors[0] + expect(screen.getByText(firstContributor.name)).toBeInTheDocument() + const SecondContributor = mockAboutData.project.top_contributors[1] + expect(screen.getByText(SecondContributor.name)).toBeInTheDocument() + const thirdContributor = mockAboutData.project.top_contributors[2] + expect(screen.getByText(thirdContributor.name)).toBeInTheDocument() + }) + }) +}) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 4e7da678a3..282439b330 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -20,6 +20,7 @@ import Footer from 'components/Footer' import Header from 'components/Header' import ScrollToTop from 'components/ScrollToTop' import { Toaster } from 'components/ui/toaster' +import About from 'pages/About' function App() { const location = useLocation() @@ -33,6 +34,7 @@ function App() {
+ }> }> }> }> diff --git a/frontend/src/components/ToggleContributors.tsx b/frontend/src/components/ToggleContributors.tsx index 49581aee2b..33e29c2bcf 100644 --- a/frontend/src/components/ToggleContributors.tsx +++ b/frontend/src/components/ToggleContributors.tsx @@ -4,7 +4,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { useState } from 'react' import { useNavigate } from 'react-router-dom' -import { TopContributorsTypeGraphql } from 'types/contributor' +import { TopContributorsTypeAlgolia, TopContributorsTypeGraphql } from 'types/contributor' import { capitalize } from 'utils/capitalize' const TopContributors = ({ contributors, @@ -13,7 +13,7 @@ const TopContributors = ({ className = '', type, }: { - contributors: TopContributorsTypeGraphql[] + contributors: TopContributorsTypeGraphql[] | TopContributorsTypeAlgolia[] label?: string maxInitialDisplay?: number className?: string @@ -43,7 +43,11 @@ const TopContributors = ({ >
- {item?.name} + {item?.name}

{capitalize(item.name) || capitalize(item.login)}

@@ -52,7 +56,7 @@ const TopContributors = ({
{type === 'contributor' - ? `${item.contributionsCount ?? 0} contributions` + ? `${(item.contributionsCount || item.contributions_count) ?? 0} contributions` : item.projectName}
diff --git a/frontend/src/components/UserCard.tsx b/frontend/src/components/UserCard.tsx index 02e6a587f1..4e8f4b3aed 100644 --- a/frontend/src/components/UserCard.tsx +++ b/frontend/src/components/UserCard.tsx @@ -3,11 +3,11 @@ import { faChevronRight, faUser } from '@fortawesome/free-solid-svg-icons' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { UserCardProps } from 'types/card' -const UserCard = ({ avatar, name, company, email, location, button }: UserCardProps) => { +const UserCard = ({ avatar, name, company, email, location, button, className }: UserCardProps) => { return (