Skip to content
Closed
101 changes: 101 additions & 0 deletions frontend/__tests__/e2e/pages/ProjectDetails.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { test, expect } from '@playwright/test'
import { mockProjectDetailsData } from '@unit/data/mockProjectDetailsData'

test.describe('Project Details Page', () => {
test.beforeEach(async ({ page }) => {
await page.route('**/graphql/', async (route) => {
await route.fulfill({
status: 200,
json: { data: mockProjectDetailsData },
})
})
await page.goto('/projects/test-project')
})

test('should have a heading and summary', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'Test Project' })).toBeVisible()
await expect(
page.getByText('An example project showcasing GraphQL and Django integration.')
).toBeVisible()
})

test('should have project details block', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'Project Details' })).toBeVisible()
await expect(page.getByText('Last Updated: Feb 7, 2025')).toBeVisible()
await expect(page.getByText('Level: Lab')).toBeVisible()
await expect(page.getByText('Project Leaders: alice, bob')).toBeVisible()
await expect(page.getByText('URL: https://github.com/')).toBeVisible()
})

test('should have project statics block', async ({ page }) => {
await expect(page.getByText('2.2K Stars')).toBeVisible()
await expect(page.getByText('10 Forks')).toBeVisible()
await expect(page.getByText('1.2K Contributors')).toBeVisible()
await expect(page.getByText('10 Issues')).toBeVisible()
await expect(page.getByText('3 Repositories')).toBeVisible()
})

test('should have project topics', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'Topics' })).toBeVisible()
await expect(page.getByText('graphql', { exact: true })).toBeVisible()
await expect(page.getByText('django', { exact: true })).toBeVisible()
await expect(page.getByText('backend')).toBeVisible()
})

test('should have project languages', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'Languages' })).toBeVisible()
await expect(page.getByText('Python', { exact: true })).toBeVisible()
await expect(page.getByText('GraphQL', { exact: true })).toBeVisible()
await expect(page.getByText('JavaScript', { exact: true })).toBeVisible()
})

test('should have top contributors', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'Top Contributors' })).toBeVisible()
await expect(page.getByRole('img', { name: 'Contributor 1' })).toBeVisible()
await expect(page.getByText('Contributor 1')).toBeVisible()
await expect(page.getByText('30 Contributions')).toBeVisible()
await expect(page.getByRole('img', { name: 'Contributor 2' })).toBeVisible()
await expect(page.getByText('Contributor 2')).toBeVisible()
await expect(page.getByText('29 Contributions')).toBeVisible()
})

test('toggle top contributors', async ({ page }) => {
await expect(page.getByRole('button', { name: 'Show more' })).toBeVisible()
await page.getByRole('button', { name: 'Show more' }).click()
await expect(page.getByRole('button', { name: 'Show less' })).toBeVisible()
await page.getByRole('button', { name: 'Show less' }).click()
await expect(page.getByRole('button', { name: 'Show more' })).toBeVisible()
})

test('should have project recent issues', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'Recent Issues' })).toBeVisible()
await expect(page.getByRole('heading', { name: 'Fix authentication bug' })).toBeVisible()
await expect(page.getByRole('img', { name: 'Dave Debugger' })).toBeVisible()
await expect(page.getByText('Feb 5, 2025')).toBeVisible()
await expect(page.getByText('5 comments')).toBeVisible()
})

test('should have project recent releases', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'Recent Releases' })).toBeVisible()
await expect(page.getByRole('heading', { name: 'V1.2.0' })).toBeVisible()
await expect(page.getByRole('img', { name: 'Charlie Dev' })).toBeVisible()
await expect(page.getByText('Jan 20, 2025')).toBeVisible()
})

test('should have project repositories', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'Repositories' })).toBeVisible()
await expect(page.getByText('Repo One')).toBeVisible()
await expect(page.getByText('Stars95')).toBeVisible()
await expect(page.getByText('Forks12')).toBeVisible()
await expect(page.getByText('Contributors40')).toBeVisible()
await expect(page.getByText('Issues6')).toBeVisible()
await expect(page.getByText('Repo Two')).toBeVisible()
await expect(page.getByText('Stars60')).toBeVisible()
await expect(page.getByText('Forks8')).toBeVisible()
await expect(page.getByText('Contributors30')).toBeVisible()
await expect(page.getByText('Issues3', { exact: true })).toBeVisible()

await page.getByText('Repo One').click()
expect(page.url()).toContain('repositories/repo-1')
})
})
79 changes: 79 additions & 0 deletions frontend/__tests__/e2e/pages/RepositoryDetails.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { test, expect } from '@playwright/test'
import { mockRepositoryData } from '@unit/data/mockRepositoryData'

test.describe('Repository Details Page', () => {
test.beforeEach(async ({ page }) => {
await page.route('**/graphql/', async (route) => {
await route.fulfill({
status: 200,
json: { data: mockRepositoryData },
})
})
await page.goto('/projects/test-project/repositories/test-repository')
})

test('should have a heading and summary', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'Test Repo' })).toBeVisible()
await expect(page.getByText('A sample test repository')).toBeVisible()
})

test('should have repository details block', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'Repository Details' })).toBeVisible()
await expect(page.getByText('Last Updated: Jan 1, 2024')).toBeVisible()
await expect(page.getByText('License: MIT')).toBeVisible()
await expect(page.getByText('Size: 1200 KB')).toBeVisible()
await expect(page.getByRole('link', { name: 'https://github.com/test-repo' })).toBeVisible()
})
Comment on lines +24 to +26
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify the GitHub URL format in the test

This test verifies that the repository URL is displayed and clickable, which directly relates to the PR's goal of fixing GitHub redirection. Ensure this URL format aligns with the actual URL pattern used in production.


🏁 Script executed:

#!/bin/bash
# Check how repository URLs are formatted in the actual code
rg -A 2 "github.com" --glob "*.tsx" --glob "*.ts" frontend/src/components/

Length of output: 1894


Update the expected GitHub URL in the test to match the production format

The production code builds GitHub URLs using a two-segment structure (e.g., https://github.com/OWASP/Nest or dynamically as https://github.com/<owner>/<repo>) while the RepositoryDetails test currently asserts the presence of a link with text "https://github.com/test-repo". Please update the test to include both the owner and repository segments (for example, "https://github.com/testOwner/test-repo" or an equivalent that mirrors the actual URL pattern in production) to fully align with the redirect fix requirements.

  • Action required: Modify the expected URL in frontend/__tests__/e2e/pages/RepositoryDetails.spec.ts (around lines 24-26) so that it reflects the correct two-segment URL format.


test('should have statics block', async ({ page }) => {
await expect(page.getByText('50K Stars')).toBeVisible()
await expect(page.getByText('3K Forks')).toBeVisible()
await expect(page.getByText('5 Contributors')).toBeVisible()
await expect(page.getByText('2 Issues')).toBeVisible()
await expect(page.getByText('10 Commits')).toBeVisible()
})

test('should have topics', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'Topics' })).toBeVisible()
await expect(page.getByText('JavaScript', { exact: true })).toBeVisible()
await expect(page.getByText('TypeScript', { exact: true })).toBeVisible()
})

test('should have languages', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'Languages' })).toBeVisible()
await expect(page.getByText('web', { exact: true })).toBeVisible()
await expect(page.getByText('security', { exact: true })).toBeVisible()
})
test('should have top contributors', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'Top Contributors' })).toBeVisible()
await expect(page.getByRole('img', { name: 'Contributor 1' })).toBeVisible()
await expect(page.getByText('Contributor 1')).toBeVisible()
await expect(page.getByText('30 Contributions')).toBeVisible()
await expect(page.getByRole('img', { name: 'Contributor 2' })).toBeVisible()
await expect(page.getByText('Contributor 2')).toBeVisible()
await expect(page.getByText('29 Contributions')).toBeVisible()
})

test('toggle top contributors', async ({ page }) => {
await expect(page.getByRole('button', { name: 'Show more' })).toBeVisible()
await page.getByRole('button', { name: 'Show more' }).click()
await expect(page.getByRole('button', { name: 'Show less' })).toBeVisible()
await page.getByRole('button', { name: 'Show less' }).click()
await expect(page.getByRole('button', { name: 'Show more' })).toBeVisible()
})

test('should have recent issues', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'Recent Issues' })).toBeVisible()
await expect(page.getByRole('heading', { name: 'Bug fix required' })).toBeVisible()
await expect(page.getByRole('img', { name: 'Test User 1' })).toBeVisible()
await expect(page.getByText('Jan 2, 2024')).toBeVisible()
await expect(page.getByText('4 comments')).toBeVisible()
})

test('should have recent releases', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'Recent Releases' })).toBeVisible()
await expect(page.getByRole('heading', { name: 'v1.0.0' })).toBeVisible()
await expect(page.getByRole('img', { name: 'Test User 2' })).toBeVisible()
await expect(page.getByText('Jan 1, 2024', { exact: true })).toBeVisible()
})
})
1 change: 0 additions & 1 deletion frontend/__tests__/e2e/pages/home.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ test.describe('Home Page', () => {
test('should have recent issues', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'Recent Issues' })).toBeVisible()
await expect(page.getByRole('heading', { name: 'Issue 1' })).toBeVisible()
await expect(page.getByText('Author 1').first()).toBeVisible()
await expect(page.getByText('Feb 24,').first()).toBeVisible()
await expect(page.getByText('5 comments')).toBeVisible()
})
Expand Down
3 changes: 0 additions & 3 deletions frontend/__tests__/unit/pages/ProjectDetails.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,6 @@ describe('ProjectDetailsPage', () => {

issues.forEach((issue) => {
expect(screen.getByText(issue.title)).toBeInTheDocument()

expect(screen.getAllByText(issue.author.name).length).toBeGreaterThan(0)

expect(screen.getByText(`${issue.commentsCount} comments`)).toBeInTheDocument()
})
})
Expand Down
3 changes: 0 additions & 3 deletions frontend/__tests__/unit/pages/RepositoryDetails.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,6 @@ describe('RepositoryDetailsPage', () => {

issues.forEach((issue) => {
expect(screen.getByText(issue.title)).toBeInTheDocument()

expect(screen.getAllByText(issue.author.name).length).toBeGreaterThan(0)

expect(screen.getByText(`${issue.commentsCount} comments`)).toBeInTheDocument()
})
})
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/api/queries/projectQueries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ export const GET_PROJECT_DATA = gql`
title
commentsCount
createdAt
url
author {
avatarUrl
login
name
url
}
}
recentReleases {
Expand Down
8 changes: 6 additions & 2 deletions frontend/src/components/CardDetailsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,12 @@ const DetailsCard = ({
<div className="mt-2 flex items-center text-sm text-gray-600 dark:text-gray-400">
<FontAwesomeIcon icon={faCalendar} className="mr-2 h-4 w-4" />
<span>{formatDate(item.createdAt)}</span>
<FontAwesomeIcon icon={faFileCode} className="ml-4 mr-2 h-4 w-4" />
<span>{item.commentsCount} comments</span>
{item?.commentsCount ? (
<>
<FontAwesomeIcon icon={faFileCode} className="ml-4 mr-2 h-4 w-4" />
<span>{item.commentsCount} comments</span>
</>
) : null}
</div>
)}
/>
Expand Down
29 changes: 13 additions & 16 deletions frontend/src/components/ItemCardList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,29 +20,26 @@ const ItemCardList = ({
{data && data.length > 0 ? (
<div className="h-64 overflow-y-auto pr-2">
{data.map((item, index) => (
<div key={index} className="mb-4 rounded-lg bg-gray-200 p-4 dark:bg-gray-700">
<h3 className="overflow-hidden text-ellipsis whitespace-nowrap font-semibold">
<a href={item?.url} className="text-blue-500 hover:underline dark:text-blue-400">
{item.title || item.name}
</a>
</h3>
<div className="flex flex-grow-0 flex-col justify-between lg:flex-row">
<div className="mt-2 flex items-center">
<div className="flex items-center">
<div key={index} className="mb-4 w-full rounded-lg bg-gray-200 p-4 dark:bg-gray-700">
<div className="flex w-full flex-col justify-between">
<div className="flex w-full items-center">
<a
href={`/community/users/${item?.author?.login}`}
className="flex-shrink-0 text-blue-400 hover:underline dark:text-blue-200"
>
<img
src={item?.author?.avatarUrl}
alt={item?.author?.name}
className="mr-2 h-6 w-6 rounded-full"
/>
<a
href={item?.author?.url}
className="text-blue-400 hover:underline dark:text-blue-200"
>
{item?.author?.name || item?.author?.login}
</a>
<h3 className="flex-1 overflow-hidden text-ellipsis whitespace-nowrap font-semibold">
<a href={item?.url} className="text-blue-500 hover:underline dark:text-blue-400">
{item.title || item.name}
</a>
</div>
</h3>
</div>
<div>{renderDetails(item)}</div>
<div className="ml-0.5 w-full">{renderDetails(item)}</div>
</div>
</div>
))}
Expand Down