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()
+ })
+ })
+ })
})