diff --git a/src/pages/PullRequestPage/PullCoverage/routes/FilesChangedTab/FilesChanged/FileDiff/FileDiff.jsx b/src/pages/PullRequestPage/PullCoverage/routes/FilesChangedTab/FilesChanged/FileDiff/FileDiff.jsx index 036efc538c..d4c1275aca 100644 --- a/src/pages/PullRequestPage/PullCoverage/routes/FilesChangedTab/FilesChanged/FileDiff/FileDiff.jsx +++ b/src/pages/PullRequestPage/PullCoverage/routes/FilesChangedTab/FilesChanged/FileDiff/FileDiff.jsx @@ -6,6 +6,7 @@ import { useParams } from 'react-router-dom' import { useNavLinks } from 'services/navigation' import { useSingularImpactedFileComparison } from 'services/pull' import { useRepoOverview } from 'services/repo' +import { useFlags } from 'shared/featureFlags' import { CODE_RENDERER_TYPE, STICKY_PADDING_SIZES, @@ -16,6 +17,7 @@ import CodeRendererInfoRow from 'ui/CodeRenderer/CodeRendererInfoRow' import CriticalFileLabel from 'ui/CodeRenderer/CriticalFileLabel' import DiffLine from 'ui/CodeRenderer/DiffLine' import Spinner from 'ui/Spinner' +import { VirtualDiffRenderer } from 'ui/VirtualFileRenderer' const Loader = () => (
@@ -27,7 +29,7 @@ function FileDiff({ path }) { const { pullFileView } = useNavLinks() const { provider, owner, repo, pullId } = useParams() const { data: overview } = useRepoOverview({ provider, owner, repo }) - + const { virtualDiffRenderer } = useFlags({ virtualDiffRenderer: false }) const { data, isLoading } = useSingularImpactedFileComparison({ provider, owner, @@ -58,6 +60,27 @@ function FileDiff({ path }) { {isCriticalFile && } {segments?.map((segment, segmentIndex) => { const content = segment.lines.map((line) => line.content).join('\n') + + let newDiffContent = '' + const lineData = [] + if (virtualDiffRenderer) { + segment.lines.forEach((line, lineIndex) => { + newDiffContent += line.content + + if (lineIndex < segment.lines.length - 1) { + newDiffContent += '\n' + } + + lineData.push({ + headNumber: line?.headNumber, + baseNumber: line?.baseNumber, + headCoverage: line?.headCoverage, + baseCoverage: line?.baseCoverage, + hitCount: undefined, + }) + }) + } + return ( @@ -73,23 +96,33 @@ function FileDiff({ path }) {
- ( - = segment.lines.length - 3} - path={data?.hashedPath} - stickyPadding={stickyPadding} - {...props} - {...segment.lines[i]} - /> - )} - /> + {virtualDiffRenderer ? ( + + ) : ( + ( + = segment.lines.length - 3} + path={data?.hashedPath} + stickyPadding={stickyPadding} + {...props} + {...segment.lines[i]} + /> + )} + /> + )} ) })} diff --git a/src/pages/PullRequestPage/PullCoverage/routes/FilesChangedTab/FilesChanged/FileDiff/FileDiff.test.jsx b/src/pages/PullRequestPage/PullCoverage/routes/FilesChangedTab/FilesChanged/FileDiff/FileDiff.test.jsx index a3d3856f8a..a2b674ba0b 100644 --- a/src/pages/PullRequestPage/PullCoverage/routes/FilesChangedTab/FilesChanged/FileDiff/FileDiff.test.jsx +++ b/src/pages/PullRequestPage/PullCoverage/routes/FilesChangedTab/FilesChanged/FileDiff/FileDiff.test.jsx @@ -1,5 +1,5 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query' -import { render, screen } from '@testing-library/react' +import { render, screen, within } from '@testing-library/react' import { graphql, HttpResponse } from 'msw' import { setupServer } from 'msw/node' import { MemoryRouter, Route } from 'react-router-dom' @@ -7,7 +7,10 @@ import { MemoryRouter, Route } from 'react-router-dom' import FileDiff from './FileDiff' const mocks = vi.hoisted(() => ({ + useFlags: vi.fn(), useScrollToLine: vi.fn(), + withProfiler: (component) => component, + captureMessage: vi.fn(), })) vi.mock('ui/CodeRenderer/hooks/useScrollToLine', async () => { @@ -18,9 +21,56 @@ vi.mock('ui/CodeRenderer/hooks/useScrollToLine', async () => { } }) -window.requestAnimationFrame = (cb) => cb() +vi.mock('shared/featureFlags', () => ({ + useFlags: mocks.useFlags, +})) + +vi.mock('@sentry/react', () => { + const originalModule = vi.importActual('@sentry/react') + return { + ...originalModule, + withProfiler: mocks.withProfiler, + captureMessage: mocks.captureMessage, + } +}) + +window.requestAnimationFrame = (cb) => { + cb(1) + return 1 +} window.cancelAnimationFrame = () => {} +const scrollToMock = vi.fn() +window.scrollTo = scrollToMock +window.scrollY = 100 + +class ResizeObserverMock { + callback = (x) => null + + constructor(callback) { + this.callback = callback + } + + observe() { + this.callback([ + { + contentRect: { width: 100 }, + target: { + getAttribute: () => ({ scrollWidth: 100 }), + getBoundingClientRect: () => ({ top: 100 }), + }, + }, + ]) + } + unobserve() { + // do nothing + } + disconnect() { + // do nothing + } +} +global.window.ResizeObserver = ResizeObserverMock + const baseMock = (impactedFile) => ({ owner: { repository: { @@ -113,9 +163,14 @@ afterAll(() => server.close()) describe('FileDiff', () => { function setup( - { impactedFile = mockImpactedFile, bundleAnalysisEnabled = false } = { + { + impactedFile = mockImpactedFile, + bundleAnalysisEnabled = false, + featureFlag = false, + } = { impactedFile: mockImpactedFile, bundleAnalysisEnabled: false, + featureFlag: false, } ) { mocks.useScrollToLine.mockImplementation(() => ({ @@ -124,6 +179,10 @@ describe('FileDiff', () => { targeted: false, })) + mocks.useFlags.mockImplementation(() => ({ + virtualDiffRenderer: featureFlag, + })) + server.use( graphql.query('ImpactedFileComparison', (info) => { return HttpResponse.json({ data: baseMock(impactedFile) }) @@ -143,20 +202,6 @@ describe('FileDiff', () => { expect(changeHeader).toBeInTheDocument() }) - it('renders the lines of a segment', async () => { - setup() - render(, { wrapper }) - - const calculator = await screen.findByText(/Calculator/) - expect(calculator).toBeInTheDocument() - - const value = await screen.findByText(/value/) - expect(value).toBeInTheDocument() - - const calcMode = await screen.findByText(/calcMode/) - expect(calcMode).toBeInTheDocument() - }) - describe('when only coverage is enabled', () => { it('renders the commit redirect url', async () => { setup() @@ -234,4 +279,62 @@ describe('FileDiff', () => { expect(criticalFile).toBeInTheDocument() }) }) + + describe('code renderer', () => { + describe('flag is true', () => { + it('renders the text area', async () => { + setup({ featureFlag: true }) + render(, { wrapper }) + + const textArea = await screen.findByTestId( + 'virtual-file-renderer-text-area' + ) + expect(textArea).toBeInTheDocument() + + const calculator = await within(textArea).findByText(/Calculator/) + expect(calculator).toBeInTheDocument() + + const value = await within(textArea).findByText(/value/) + expect(value).toBeInTheDocument() + + const calcMode = await within(textArea).findByText(/calcMode/) + expect(calcMode).toBeInTheDocument() + }) + + it('renders the lines of a segment', async () => { + setup({ featureFlag: true }) + render(, { wrapper }) + + const codeDisplayOverlay = await screen.findByTestId( + 'virtual-file-renderer-overlay' + ) + + const calculator = + await within(codeDisplayOverlay).findByText(/Calculator/) + expect(calculator).toBeInTheDocument() + + const value = await within(codeDisplayOverlay).findByText(/value/) + expect(value).toBeInTheDocument() + + const calcMode = await within(codeDisplayOverlay).findByText(/calcMode/) + expect(calcMode).toBeInTheDocument() + }) + }) + + describe('flag is false', () => { + it('renders the lines of a segment', async () => { + setup({ featureFlag: false }) + render(, { wrapper }) + + const calculator = await screen.findByText(/Calculator/) + expect(calculator).toBeInTheDocument() + + const value = await screen.findByText(/value/) + expect(value).toBeInTheDocument() + + const calcMode = await screen.findByText(/calcMode/) + expect(calcMode).toBeInTheDocument() + }) + }) + }) })