Skip to content

Conversation

@Piyushrathoree
Copy link
Collaborator

Proposed change

Resolves #2040

generated a Release component which is removing the code duplicacy and used a single component in recentRelease component and new release component on snapshot page.

Screenshot From 2025-09-03 23-21-00 Screenshot From 2025-09-03 23-28-09

Checklist

  • I've read and followed the contributing guidelines.
  • I've run make check-test locally; all checks and tests passed.

…nd used a single compoenent in recentRelease and new release on snapshot page
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 3, 2025

Summary by CodeRabbit

  • New Features
    • Introduced a reusable Release card with avatar tooltip, release title linking to GitHub, published date, and repository navigation (opens internal repo page when available).
    • Snapshot details now include organization, repository, and author info for richer release displays.
  • Refactor
    • Recent Releases now renders via the new Release component and shows a “No recent releases.” fallback when empty.
  • Style
    • Updated New Releases header for improved dark-mode readability.
  • Tests
    • Added comprehensive unit tests for Release component rendering, links, tooltips, navigation, and edge cases.

Walkthrough

Adds a reusable Release component, updates RecentReleases and snapshots page to render releases via that component, extends the snapshot GraphQL selection and test fixtures with organization/repository/author fields, and adds unit tests for the Release component covering rendering and edge cases.

Changes

Cohort / File(s) Summary
Unit tests: Release component
frontend/__tests__/unit/components/Release.test.tsx
New comprehensive unit tests for Release: rendering, links, navigation, avatar/tooltip behavior, fallbacks, edge cases, and default props.
Test fixtures update
frontend/__tests__/unit/data/mockSnapshotData.ts
Extends mockSnapshotDetailsData.newReleases entries with organizationName, repositoryName, and author (id, avatarUrl, login, name).
New reusable component
frontend/src/components/Release.tsx
New default-exported Release React component rendering avatar (with tooltip), release title (external GitHub link), published date, and repo navigation button (internal router.push), with guards and TruncatedText usage.
Refactor RecentReleases to use Release
frontend/src/components/RecentReleases.tsx
Replaces inline per-item JSX with usage, updates prop typing to ReleaseType[], adds empty-state "No recent releases." and adjusts imports.
Snapshots page use Release
frontend/src/app/snapshots/[id]/page.tsx
Renders newReleases via components and imports Release; minor header className adjustment.
GraphQL query extension
frontend/src/server/queries/snapshotQueries.ts
Extends GET_SNAPSHOT_DETAILS selection for newReleases to include organizationName, repositoryName, and author { id, avatarUrl, login, name }.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Assessment against linked issues

Objective Addressed Explanation
Extract release block into standalone Release component [#2040]
Replace release blocks in RecentReleases with Release [#2040]
Update Snapshots page to use Release [#2040]
Update GraphQL query to include fields required by Release [#2040]
Add tests for Release and update existing tests [#2040]

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
Header styling tweak for “New Releases” (frontend/src/app/snapshots/[id]/page.tsx) Styling/className change not requested by #2040; purely presentational and not required to satisfy the extraction/query/testing objectives.
Added "No recent releases." empty-state message (frontend/src/components/RecentReleases.tsx) New fallback message alters behavior/content beyond the specified refactor objectives in #2040.

Possibly related PRs

Suggested reviewers

  • arkid15r
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (12)
frontend/src/app/snapshots/[id]/page.tsx (1)

168-173: Use a stable key; drop redundant prop

  • Prefer a deterministic key over index/tagName to avoid reconciliation issues.
  • showAvatar defaults to true; no need to pass explicitly.
-              <Release
-                key={`${release.tagName}-${index}`}
-                release={release}
-                showAvatar={true}
-                index={index}
-              />
+              <Release
+                key={`release-${release.organizationName ?? ''}-${release.repositoryName ?? ''}-${release.tagName ?? index}`}
+                release={release}
+                index={index}
+              />

If release.id is available in the type, prefer key={release.id}.

frontend/src/components/RecentReleases.tsx (1)

33-33: Avoid index as key; include stable identity

Use a composite of org/repo/tag for stability; keep passing index for tooltip IDs.

-            <Release key={index} release={item} showAvatar={showAvatar} index={index} />
+            <Release
+              key={`release-${item.organizationName ?? ''}-${item.repositoryName ?? ''}-${item.tagName ?? index}`}
+              release={item}
+              showAvatar={showAvatar}
+              index={index}
+            />
frontend/__tests__/unit/components/Release.test.tsx (4)

125-126: Tighten the date assertion

Avoid allowing 5-digit years; assert a 4-digit year and fixed month tokens.

-    expect(screen.getByText(/\w{3} \d{1,2}, \d{4,5}/)).toBeInTheDocument() // Date format
+    expect(
+      screen.getByText(/\b(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\b \d{1,2}, \d{4}$/)
+    ).toBeInTheDocument()

144-149: Type-safety for author absence

Release.author is non-optional in the type; this test sets it to undefined. Either make author optional in types/release.ts (preferred) or cast in the test to satisfy TS.

Option A (prefer): make author?: User | null in types/release.ts.

Option B (test-only cast):

-    const releaseWithoutAuthor = { ...mockReleases[0], author: undefined }
+    const releaseWithoutAuthor = ({ ...mockReleases[0], author: undefined } as unknown) as ReleaseType

166-169: Query buttons by accessible name

getByRole('button') can become ambiguous as tests/components grow. Query by name to target the correct control.

Example:

-    const repoButton = screen.getByRole('button')
+    const repoButton = screen.getByRole('button', { name: /our-awesome-project/i })

Repeat similarly for other occurrences using the repository name in each case.

Also applies to: 174-180, 186-188, 243-245, 252-254, 261-263


191-195: Reduce brittleness of tooltip selection

Relying on closest('div') + id is fragile. Prefer a data-testid exposed by the component and assert on that.

Test change:

-    const tooltip = screen.getByAltText('Test User').closest('div')
-    expect(tooltip).toHaveAttribute('id', 'avatar-tooltip-0')
+    const tooltip = screen.getByTestId('avatar-tooltip-0')
+    expect(tooltip).toBeInTheDocument()

Component change (Release.tsx) to support this:

<Tooltip id={`avatar-tooltip-${index}`} data-testid={`avatar-tooltip-${index}`} ...>
  ...
</Tooltip>
frontend/src/components/Release.tsx (6)

31-53: Avoid “dead” avatar links; add fallbacks for missing avatarUrl and better a11y.

  • Rendering a Link to "#" when login is absent is misleading.
  • Next/Image will error if avatarUrl is empty/null; add a safe fallback.
  • Add an aria-label for SR users; use empty alt when non-clickable.

Apply:

-              <Link
-                className="shrink-0 text-blue-400 hover:underline"
-                href={release.author.login ? `/members/${release.author.login}` : '#'}
-              >
-                <Image
-                  alt={release.author.name || release.author.login}
-                  className="mr-2 h-6 w-6 rounded-full"
-                  height={24}
-                  src={release.author.avatarUrl}
-                  width={24}
-                />
-              </Link>
+              {release.author.login ? (
+                <Link
+                  className="shrink-0"
+                  href={`/members/${release.author.login}`}
+                  aria-label={`View ${release.author.name || release.author.login}'s profile`}
+                >
+                  <Image
+                    alt={release.author.name || release.author.login || 'Author avatar'}
+                    className="mr-2 h-6 w-6 rounded-full"
+                    height={24}
+                    src={release.author.avatarUrl || '/images/avatar-placeholder.png'}
+                    width={24}
+                  />
+                </Link>
+              ) : (
+                <span className="shrink-0">
+                  <Image
+                    alt=""
+                    className="mr-2 h-6 w-6 rounded-full"
+                    height={24}
+                    src={release.author.avatarUrl || '/images/avatar-placeholder.png'}
+                    width={24}
+                  />
+                </span>
+              )}

33-37: Drop the hardcoded tooltip id to avoid duplicates across lists.

Using index defaults to 0 can create repeated ids. Tooltip doesn’t require a stable id here.

Apply:

-              id={`avatar-tooltip-${index}`}

55-63: Use a plain anchor for external GitHub links (Next Link is for internal routes).

Improves semantics and avoids unnecessary client-side routing.

Apply:

-            <Link
-              className="text-blue-400 hover:underline"
-              href={`https://github.com/${release.organizationName}/${release.repositoryName}/releases/tag/${release.tagName}`}
-              target="_blank"
-              rel="noopener noreferrer"
-            >
+            <a
+              className="text-blue-400 hover:underline"
+              href={`https://github.com/${release.organizationName}/${release.repositoryName}/releases/tag/${release.tagName}`}
+              target="_blank"
+              rel="noopener noreferrer"
+            >
               <TruncatedText text={release.name || release.tagName} />
-            </Link>
+            </a>

66-69: Harden date rendering against bad inputs.

formatDate throws on invalid dates. Wrap with a safe helper or guard to avoid runtime errors if data is malformed.

Add a safe helper (in utils/dateFormatter.ts):

export const tryFormatDate = (input?: number | string | null) => {
  try { return input ? formatDate(input as any) : '' } catch { return '-' }
}

Then update here:

-            <span>{formatDate(release.publishedAt)}</span>
+            <span>{tryFormatDate(release.publishedAt) || '-'}</span>

Please confirm GraphQL guarantees publishedAt to be a valid ISO string for all releases; if not, the safe helper is recommended.


71-83: Prefer declarative navigation via Link for the repo pill; improves a11y and prefetch.

Switching from router.push to Link enables middle-click/new-tab and prefetch. Render a non-interactive span when org/repo are missing.

Apply:

-import { useRouter } from 'next/navigation'
+import Link from 'next/link'
@@
-  const router = useRouter()
@@
-            <button
-              className="cursor-pointer overflow-hidden text-ellipsis whitespace-nowrap text-gray-600 hover:underline dark:text-gray-400"
-              disabled={!release.organizationName || !release.repositoryName}
-              onClick={() => {
-                const org = release.organizationName || ''
-                const repo = release.repositoryName || ''
-                if (!org || !repo) return
-                router.push(`/organizations/${org}/repositories/${repo}`)
-              }}
-            >
-              <TruncatedText text={release.repositoryName} />
-            </button>
+            {release.organizationName && release.repositoryName ? (
+              <Link
+                className="overflow-hidden text-ellipsis whitespace-nowrap text-gray-600 hover:underline dark:text-gray-400"
+                href={`/organizations/${release.organizationName}/repositories/${release.repositoryName}`}
+                prefetch
+                aria-label={`Open ${release.repositoryName} repository`}
+              >
+                <TruncatedText text={release.repositoryName} />
+              </Link>
+            ) : (
+              <span
+                className="overflow-hidden text-ellipsis whitespace-nowrap text-gray-400"
+                aria-disabled="true"
+              >
+                <TruncatedText text={release.repositoryName || 'Unknown repository'} />
+              </span>
+            )}

Also applies to: 6-6, 25-25


12-17: Avoid exposing and defaulting an “index” prop solely for DOM ids.

Index defaults to 0 can repeat; prefer a stable identifier (release.id) or no id at all.

Would you like me to remove index from the public API and migrate Tooltip to be id-less?

Also applies to: 35-35

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between a6092fe and 65e2e74.

⛔ Files ignored due to path filters (1)
  • frontend/src/types/__generated__/snapshotQueries.generated.ts is excluded by !**/__generated__/**
📒 Files selected for processing (6)
  • frontend/__tests__/unit/components/Release.test.tsx (1 hunks)
  • frontend/__tests__/unit/data/mockSnapshotData.ts (1 hunks)
  • frontend/src/app/snapshots/[id]/page.tsx (2 hunks)
  • frontend/src/components/RecentReleases.tsx (2 hunks)
  • frontend/src/components/Release.tsx (1 hunks)
  • frontend/src/server/queries/snapshotQueries.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
frontend/src/app/snapshots/[id]/page.tsx (1)
frontend/src/types/release.ts (1)
  • Release (3-13)
frontend/src/components/Release.tsx (2)
frontend/src/components/TruncatedText.tsx (1)
  • TruncatedText (3-45)
frontend/src/utils/dateFormatter.ts (1)
  • formatDate (1-20)
frontend/src/components/RecentReleases.tsx (1)
frontend/src/types/release.ts (1)
  • Release (3-13)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Run backend tests
  • GitHub Check: Run frontend e2e tests
  • GitHub Check: Run frontend unit tests
🔇 Additional comments (9)
frontend/src/app/snapshots/[id]/page.tsx (2)

19-19: Good move importing the reusable component

Importing and using the shared Release component here reduces duplication.


163-165: Heading tweaks look fine

Dark-mode color added; consistent with other sections.

frontend/src/components/RecentReleases.tsx (2)

9-9: Prop typing update LGTM

Switch to ReleaseType[] aligns this component with the new shared type.


1-1: Imports look correct

Icons, types, and component import changes are consistent with the refactor.

Also applies to: 3-3, 5-5

frontend/__tests__/unit/data/mockSnapshotData.ts (1)

17-24: Add release.id and standardize date types in mocks

  • Add id to each newReleases entry in frontend/__tests__/unit/data/mockSnapshotData.ts to mirror your GraphQL selection set and support stable UI keys.
  • Ensure all date fields (publishedAt, updatedAt, etc.) use a single consistent type (e.g. ISO string) across mocks and components.
         tagName: 'v0.9.2',
         projectName: 'test-project-1',
+        id: 'release-1',
         organizationName: 'owasp',
         repositoryName: 'test-project-1',
frontend/__tests__/unit/components/Release.test.tsx (2)

224-237: Good multi-item coverage

Verifies unique tooltip IDs across instances; helpful regression guard.


1-114: Overall test suite quality

Broad coverage (rendering, links, navigation, disabled states, fallbacks). Once the typing and selector nits above are addressed, this looks solid.

Also applies to: 115-286

frontend/src/components/Release.tsx (2)

12-17: Nice, focused prop surface for a reusable card.

The props are minimal and cover the reuse scenarios well. Good defaulting of optional props.


44-49: No action required: avatars.githubusercontent.com is already whitelisted in Next/Image remotePatterns.

@Piyushrathoree
Copy link
Collaborator Author

@kasya Done with the PR as I said , please review this

@sonarqubecloud
Copy link

sonarqubecloud bot commented Sep 4, 2025

Copy link
Collaborator

@kasya kasya left a comment

Choose a reason for hiding this comment

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

@Piyushrathoree thanks for working on this issue!
The changes look good 👍🏼

Please resolve Code Rabbit suggestions before requesting a PR review next time 👌🏼 If the suggestion is not relevant - just mark it as resolved.

@kasya kasya enabled auto-merge September 4, 2025 00:33
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
frontend/src/server/queries/snapshotQueries.ts (1)

11-25: Missing isPreRelease in selection; align with Release props and past feedback.

To keep props consistent across pages using , add isPreRelease. Also double-check publishedAt typing (GraphQL usually returns an ISO string) against your TS type for Release.

Apply this minimal change:

       newReleases {
         id
         name
         organizationName
         projectName
         publishedAt
         repositoryName
         tagName
+        isPreRelease
         author {
           avatarUrl
           id
           login
           name
         }
       }

Optional: extract a fragment to prevent drift across queries:

// near the top of this file
export const RELEASE_CARD_FIELDS = gql`
  fragment ReleaseCardFields on Release {
    id
    name
    organizationName
    projectName
    publishedAt
    repositoryName
    tagName
    isPreRelease
    author { avatarUrl id login name }
  }
`;

// then use in GET_SNAPSHOT_DETAILS
newReleases { ...ReleaseCardFields }

To verify impact and typing, run:

#!/usr/bin/env bash
# Check Release props and isPreRelease usage
rg -n --glob 'frontend/**' 'isPreRelease|<Release|ReleaseProps'

# Inspect Release type and publishedAt typing
rg -nP --glob 'frontend/**' -C3 'export\s+(interface|type)\s+Release\b|publishedAt'
🧹 Nitpick comments (2)
frontend/src/server/queries/snapshotQueries.ts (2)

17-17: repositoryName added — consider also fetching a canonical repo URL (optional).

If the UI builds links, prefer a backend-provided repositoryUrl/htmlUrl (if available in your schema) to avoid client-side string composition and drift.


19-24: Author block looks good; ensure null-safe handling in .

Some releases may have a missing/nullable author. Verify the component gracefully renders without avatar/name when author is null. If you link to profiles, consider adding authorUrl (if exposed) to avoid crafting URLs on the client.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 65e2e74 and 754150c.

⛔ Files ignored due to path filters (1)
  • frontend/src/types/__generated__/snapshotQueries.generated.ts is excluded by !**/__generated__/**
📒 Files selected for processing (1)
  • frontend/src/server/queries/snapshotQueries.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Run frontend e2e tests
  • GitHub Check: Run frontend unit tests
🔇 Additional comments (1)
frontend/src/server/queries/snapshotQueries.ts (1)

14-15: Good addition: organizationName and projectName fetched for Release.

This aligns the Snapshots query with the new component props and avoids ad-hoc derivations.

@kasya kasya added this pull request to the merge queue Sep 4, 2025
Merged via the queue into OWASP:main with commit 51cf8fb Sep 4, 2025
25 checks passed
@Piyushrathoree Piyushrathoree deleted the task/update-release-component-snapshot-page branch September 4, 2025 07:21
arkid15r pushed a commit that referenced this pull request Sep 4, 2025
#2179)

* generated a Release compoenent which is removing the code duplicacy and used a single compoenent in recentRelease and new release on snapshot page

* Apply order

---------

Co-authored-by: Kate Golovanova <[email protected]>
Dishant1804 pushed a commit to Dishant1804/Nest that referenced this pull request Sep 6, 2025
OWASP#2179)

* generated a Release compoenent which is removing the code duplicacy and used a single compoenent in recentRelease and new release on snapshot page

* Apply order

---------

Co-authored-by: Kate Golovanova <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Update Snapshots Page to Use Reusable <Release /> Component

2 participants