-
Notifications
You must be signed in to change notification settings - Fork 312
fix(web): show missing-file state in browse #1381
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
DivyamTalwar
wants to merge
7
commits into
sourcebot-dev:main
Choose a base branch
from
DivyamTalwar:divyamtalwar/fix-827-browse-file-not-found
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+276
−7
Open
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
6eeb568
Show a useful browse 404 for missing files
DivyamTalwar 1742737
Record browse missing-file state in changelog
DivyamTalwar 78b2eb2
Sync missing-file PR with current main
DivyamTalwar 47c3083
Keep missing-file preview recovery anchored
DivyamTalwar 529f2e0
Preserve browse context in preview headers
DivyamTalwar e0605ad
Keep preview close control as a link
DivyamTalwar 1386d65
Wrap missing-file path copy safely
DivyamTalwar File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
134 changes: 134 additions & 0 deletions
134
.../web/src/app/(app)/browse/[...path]/components/codePreviewPanel/codePreviewPanel.test.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,134 @@ | ||
| import { cleanup, render, screen } from '@testing-library/react'; | ||
| import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; | ||
| import { TooltipProvider } from '@/components/ui/tooltip'; | ||
| import { ErrorCode } from '@/lib/errorCodes'; | ||
|
|
||
| const mocks = vi.hoisted(() => ({ | ||
| getRepoInfoByName: vi.fn(), | ||
| getFileSource: vi.fn(), | ||
| getFileBlame: vi.fn(), | ||
| })); | ||
|
|
||
| vi.mock('@/actions', () => ({ | ||
| getRepoInfoByName: mocks.getRepoInfoByName, | ||
| })); | ||
|
|
||
| vi.mock('@/features/git', () => ({ | ||
| getFileSource: mocks.getFileSource, | ||
| getFileBlame: mocks.getFileBlame, | ||
| })); | ||
|
|
||
| vi.mock('@/app/(app)/components/pathHeader', () => ({ | ||
| PathHeader: ({ path, revisionName }: { path: string; revisionName?: string }) => ( | ||
| <div data-testid="path-header">Path: {path}; Revision: {revisionName ?? 'default'}</div> | ||
| ), | ||
| })); | ||
|
|
||
| vi.mock('./pureCodePreviewPanel', () => ({ | ||
| PureCodePreviewPanel: () => <div>Code preview</div>, | ||
| })); | ||
|
|
||
| vi.mock('@/ee/features/codeNav/components/symbolHoverPopup', () => ({ | ||
| SymbolHoverPopup: () => null, | ||
| })); | ||
|
|
||
| import { CodePreviewPanel } from './codePreviewPanel'; | ||
|
|
||
| const renderCodePreviewPanel = async (props: Parameters<typeof CodePreviewPanel>[0]) => { | ||
| return render( | ||
| <TooltipProvider> | ||
| {await CodePreviewPanel(props)} | ||
| </TooltipProvider> | ||
| ); | ||
| }; | ||
|
|
||
| afterEach(() => { | ||
| cleanup(); | ||
| }); | ||
|
|
||
| describe('CodePreviewPanel', () => { | ||
| beforeEach(() => { | ||
| vi.clearAllMocks(); | ||
|
|
||
| mocks.getRepoInfoByName.mockResolvedValue({ | ||
| name: 'github.com/sourcebot-dev/sourcebot', | ||
| displayName: 'sourcebot-dev/sourcebot', | ||
| codeHostType: 'github', | ||
| externalWebUrl: 'https://github.com/sourcebot-dev/sourcebot', | ||
| }); | ||
| }); | ||
|
|
||
| test('renders a browse 404 when the requested file does not exist', async () => { | ||
| mocks.getFileSource.mockResolvedValue({ | ||
| statusCode: 404, | ||
| errorCode: ErrorCode.FILE_NOT_FOUND, | ||
| message: 'File "src/missing.ts" not found in repository "github.com/sourcebot-dev/sourcebot"', | ||
| }); | ||
|
|
||
| await renderCodePreviewPanel({ | ||
| path: 'src/missing.ts', | ||
| repoName: 'github.com/sourcebot-dev/sourcebot', | ||
| revisionName: 'feature-branch', | ||
| }); | ||
|
|
||
| expect(screen.queryByText('File not found')).toBeTruthy(); | ||
| expect(screen.queryAllByText(/src\/missing\.ts/).length).toBeGreaterThan(0); | ||
| expect(screen.queryByText(/Error loading file source/)).toBeNull(); | ||
| expect(screen.getByRole('link', { name: 'Return to repository root' }).getAttribute('href')).toBe( | ||
| '/browse/github.com/sourcebot-dev/sourcebot@feature-branch/-/tree' | ||
| ); | ||
| }); | ||
|
|
||
| test('keeps preview 404 navigation anchored to the browse revision', async () => { | ||
| mocks.getFileSource.mockResolvedValue({ | ||
| statusCode: 404, | ||
| errorCode: ErrorCode.FILE_NOT_FOUND, | ||
| message: 'File "src/missing.ts" not found in repository "github.com/sourcebot-dev/sourcebot"', | ||
| }); | ||
|
|
||
| await renderCodePreviewPanel({ | ||
| path: 'src/missing.ts', | ||
| repoName: 'github.com/sourcebot-dev/sourcebot', | ||
| revisionName: 'main', | ||
| previewRef: 'abc123def456', | ||
| }); | ||
|
|
||
| expect(screen.queryByText('File not found')).toBeTruthy(); | ||
| expect(screen.getAllByText('abc123def456').length).toBeGreaterThan(0); | ||
| expect(screen.getByText('src/missing.ts').className).toContain('break-all'); | ||
| expect(screen.getAllByText('abc123def456').some((element) => element.className.includes('break-all'))).toBe(true); | ||
| expect(screen.getByRole('link', { name: 'Return to repository root' }).getAttribute('href')).toBe( | ||
| '/browse/github.com/sourcebot-dev/sourcebot@main/-/tree' | ||
| ); | ||
| expect(screen.getByRole('link', { name: 'Close preview' }).getAttribute('href')).toBe( | ||
| '/browse/github.com/sourcebot-dev/sourcebot@main/-/blob/src%2Fmissing.ts' | ||
| ); | ||
| }); | ||
|
|
||
| test('keeps successful preview header anchored to the browse revision', async () => { | ||
| mocks.getFileSource.mockResolvedValue({ | ||
| source: 'const value = 1;\n', | ||
| language: 'typescript', | ||
| }); | ||
|
|
||
| await renderCodePreviewPanel({ | ||
| path: 'src/index.ts', | ||
| repoName: 'github.com/sourcebot-dev/sourcebot', | ||
| revisionName: 'main', | ||
| previewRef: 'abc123def456', | ||
| }); | ||
|
|
||
| expect(mocks.getFileSource).toHaveBeenCalledWith({ | ||
| path: 'src/index.ts', | ||
| repo: 'github.com/sourcebot-dev/sourcebot', | ||
| ref: 'abc123def456', | ||
| }, { source: 'sourcebot-web-client' }); | ||
| expect(screen.getByTestId('path-header').textContent).toBe('Path: src/index.ts; Revision: main'); | ||
|
|
||
| const closePreviewLink = screen.getByRole('link', { name: 'Close preview' }); | ||
| expect(closePreviewLink.getAttribute('href')).toBe( | ||
| '/browse/github.com/sourcebot-dev/sourcebot@main/-/blob/src%2Findex.ts' | ||
| ); | ||
| expect(closePreviewLink.closest('button')).toBeNull(); | ||
| }); | ||
| }); | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
115 changes: 115 additions & 0 deletions
115
...ages/web/src/app/(app)/browse/[...path]/components/codePreviewPanel/fileNotFoundPanel.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,115 @@ | ||
| import { PathHeader } from "@/app/(app)/components/pathHeader"; | ||
| import { Button } from "@/components/ui/button"; | ||
| import { Separator } from "@/components/ui/separator"; | ||
| import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; | ||
| import { truncateSha } from "@/lib/utils"; | ||
| import { FileQuestion, X } from "lucide-react"; | ||
| import Link from "next/link"; | ||
| import { getBrowsePath } from "../../../hooks/utils"; | ||
| import type { CodeHostType } from "@sourcebot/db"; | ||
|
|
||
| interface FileNotFoundPanelProps { | ||
| path: string; | ||
| repoName: string; | ||
| browseRevisionName?: string; | ||
| missingRevisionName?: string; | ||
| previewRef?: string; | ||
| repo: { | ||
| codeHostType: CodeHostType; | ||
| displayName?: string; | ||
| externalWebUrl?: string; | ||
| }; | ||
| } | ||
|
|
||
| export const FileNotFoundPanel = ({ | ||
| path, | ||
| repoName, | ||
| browseRevisionName, | ||
| missingRevisionName, | ||
| previewRef, | ||
| repo, | ||
| }: FileNotFoundPanelProps) => { | ||
| return ( | ||
| <> | ||
| <div className="flex flex-row py-1 px-2 items-center justify-between"> | ||
| <PathHeader | ||
| path={path} | ||
| repo={{ | ||
| name: repoName, | ||
| codeHostType: repo.codeHostType, | ||
| displayName: repo.displayName, | ||
| externalWebUrl: repo.externalWebUrl, | ||
| }} | ||
| revisionName={browseRevisionName} | ||
| /> | ||
| </div> | ||
| <Separator /> | ||
| {previewRef && ( | ||
| <div className="flex flex-row items-center justify-between gap-2 px-4 py-2 border-b shrink-0"> | ||
| <span className="text-sm"> | ||
| Previewing file at revision{" "} | ||
| <Link | ||
| href={getBrowsePath({ | ||
| repoName, | ||
| revisionName: browseRevisionName, | ||
| path: '', | ||
| pathType: 'commit', | ||
| commitSha: previewRef, | ||
| })} | ||
| className="font-mono text-link hover:underline" | ||
| > | ||
| {truncateSha(previewRef)} | ||
| </Link> | ||
| </span> | ||
| <Tooltip key={previewRef}> | ||
| <TooltipTrigger asChild> | ||
| <Button | ||
| asChild | ||
| variant="ghost" | ||
| size="icon" | ||
| className="h-6 w-6 text-muted-foreground" | ||
| > | ||
| <Link | ||
| href={getBrowsePath({ | ||
| repoName, | ||
| revisionName: browseRevisionName, | ||
| path, | ||
| pathType: 'blob', | ||
| })} | ||
| aria-label="Close preview" | ||
| > | ||
| <X className="h-4 w-4" /> | ||
| </Link> | ||
| </Button> | ||
| </TooltipTrigger> | ||
| <TooltipContent>Close preview</TooltipContent> | ||
| </Tooltip> | ||
| </div> | ||
| )} | ||
| <div className="flex min-h-72 flex-col items-center justify-center gap-4 px-6 py-12 text-center"> | ||
| <div className="flex h-12 w-12 items-center justify-center rounded-md border bg-muted text-muted-foreground"> | ||
| <FileQuestion className="h-6 w-6" /> | ||
| </div> | ||
| <div className="space-y-1"> | ||
| <h2 className="text-lg font-semibold">File not found</h2> | ||
| <p className="max-w-xl break-words text-sm text-muted-foreground"> | ||
| The path <span className="break-all font-mono text-foreground">{path}</span> does not exist | ||
| {missingRevisionName ? <> at <span className="break-all font-mono text-foreground">{missingRevisionName}</span></> : null}. | ||
| </p> | ||
| </div> | ||
| <Button asChild variant="outline"> | ||
| <Link | ||
| href={getBrowsePath({ | ||
| repoName, | ||
| revisionName: browseRevisionName, | ||
| path: '', | ||
| pathType: 'tree', | ||
| })} | ||
| > | ||
| Return to repository root | ||
| </Link> | ||
| </Button> | ||
| </div> | ||
| </> | ||
| ); | ||
| } |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.