Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions frontend/__tests__/unit/components/ItemCardList.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -715,7 +715,7 @@ describe('ItemCardList Component', () => {
expect(avatarImage).toHaveAttribute('alt', `${issueWithoutAuthor.author.login}'s avatar`)
})

it('uses generic fallback alt text when author is missing', () => {
it('shows fallback icon when author is missing', () => {
const issueWithoutAuthor = {
...mockIssue,
author: null,
Expand All @@ -730,11 +730,11 @@ describe('ItemCardList Component', () => {
/>
)

const avatarImage = screen.getByTestId('avatar-image')
expect(avatarImage).toHaveAttribute('alt', "Author's avatar")
const avatarImage = screen.queryByTestId('avatar-image')
expect(avatarImage).not.toBeInTheDocument()
})

it('uses generic fallback alt text when author name and login are missing', () => {
it('shows fallback icon when author name and login are missing', () => {
const issueWithEmptyAuthor = {
...mockIssue,
author: {
Expand All @@ -753,8 +753,8 @@ describe('ItemCardList Component', () => {
/>
)

const avatarImage = screen.getByTestId('avatar-image')
expect(avatarImage).toHaveAttribute('alt', "Author's avatar")
const avatarImage = screen.queryByTestId('avatar-image')
expect(avatarImage).not.toBeInTheDocument()
})

it('opens external links in new tab', () => {
Expand Down
5 changes: 3 additions & 2 deletions frontend/__tests__/unit/components/RecentIssues.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ describe('<RecentIssues />', () => {
expect(screen.getByAltText("User One's avatar")).toBeInTheDocument()
})

it('uses fallback alt text when author name and login are missing', () => {
it('shows fallback icon when author name and login are missing', () => {
const issueWithEmptyAuthor = {
...baseIssue,
author: {
Expand All @@ -207,6 +207,7 @@ describe('<RecentIssues />', () => {
},
}
render(<RecentIssues data={[issueWithEmptyAuthor]} />)
expect(screen.getByAltText("Author's avatar")).toBeInTheDocument()
const avatarImage = screen.queryByAltText("Author's avatar")
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Feb 3, 2026

Choose a reason for hiding this comment

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

P2: This test is titled “shows fallback icon…” but only asserts the avatar image is absent. It doesn’t verify the fallback icon is rendered, so the test would still pass if nothing is shown. Add an assertion for the fallback icon within the avatar tooltip.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At frontend/__tests__/unit/components/RecentIssues.test.tsx, line 210:

<comment>This test is titled “shows fallback icon…” but only asserts the avatar image is absent. It doesn’t verify the fallback icon is rendered, so the test would still pass if nothing is shown. Add an assertion for the fallback icon within the avatar tooltip.</comment>

<file context>
@@ -207,6 +207,7 @@ describe('<RecentIssues />', () => {
     }
     render(<RecentIssues data={[issueWithEmptyAuthor]} />)
-    expect(screen.getByAltText("Author's avatar")).toBeInTheDocument()
+    const avatarImage = screen.queryByAltText("Author's avatar")
+    expect(avatarImage).not.toBeInTheDocument()
   })
</file context>
Fix with Cubic

expect(avatarImage).not.toBeInTheDocument()
})
})
59 changes: 43 additions & 16 deletions frontend/src/components/ItemCardList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,55 @@ import Image from 'next/image'
import Link from 'next/link'
import React, { JSX } from 'react'
import type { IconType } from 'react-icons'
import { FaUser } from 'react-icons/fa'
import type { Issue } from 'types/issue'
import type { Milestone } from 'types/milestone'
import type { PullRequest } from 'types/pullRequest'
import type { Release } from 'types/release'
import SecondaryCard from 'components/SecondaryCard'
import { TruncatedText } from 'components/TruncatedText'

interface AuthorAvatarProps {
author: {
avatarUrl: string
login: string
name: string
}
}

const AuthorAvatar = ({ author }: AuthorAvatarProps): JSX.Element => {
const hasAuthorInfo = author?.name || author?.login
const hasLogin = author?.login
const hasAvatarUrl = Boolean(author?.avatarUrl)

const fallbackIcon = <FaUser className="mr-2 h-6 w-6 text-gray-400 dark:text-gray-500" />

if (hasAuthorInfo) {
const avatarContent = hasAvatarUrl ? (
<Image
height={24}
width={24}
src={author.avatarUrl}
alt={`${author.name || author.login}'s avatar`}
className="mr-2 rounded-full"
/>
) : (
fallbackIcon
)

if (hasLogin) {
return (
<Link className="shrink-0 text-blue-400 hover:underline" href={`/members/${author.login}`}>
{avatarContent}
</Link>
)
}
return <div className="shrink-0">{avatarContent}</div>
}

return <div className="shrink-0">{fallbackIcon}</div>
}

const ItemCardList = ({
title,
data,
Expand Down Expand Up @@ -60,22 +102,7 @@ const ItemCardList = ({
placement="bottom"
showArrow
>
<Link
className="shrink-0 text-blue-400 hover:underline"
href={`/members/${item?.author?.login}`}
>
<Image
height={24}
width={24}
src={item?.author?.avatarUrl}
alt={
item.author && (item.author.name || item.author.login)
? `${item.author.name || item.author.login}'s avatar`
: "Author's avatar"
}
className="mr-2 rounded-full"
/>
</Link>
<AuthorAvatar author={item.author} />
</Tooltip>
)}
<h3 className="min-w-0 flex-1 overflow-hidden font-semibold text-ellipsis whitespace-nowrap">
Expand Down