Add rich viewer for files (non-diff) (#246)
* Add rich viewer for files (non-diff) Fixes #74 * Fix test typo * WIP toolbar and proper blob element injection * Add working Preview/Code/Blame toggle * Add missing aria-label * Add e2e test and classic/reactUI support * Re-enable headless e2e * Update e2e snap for linux * Lint * Improve style a bit * Reorg and cleanup
This commit is contained in:
@ -3,6 +3,7 @@ import { Octokit } from '@octokit/rest'
|
||||
import {
|
||||
KittycadUser,
|
||||
Message,
|
||||
MessageGetFileBlob,
|
||||
MessageGetFileDiff,
|
||||
MessageGetGithubCommitData,
|
||||
MessageGetGithubPullFilesData,
|
||||
@ -16,7 +17,7 @@ import {
|
||||
setStorageGithubToken,
|
||||
setStorageKittycadToken,
|
||||
} from './storage'
|
||||
import { getFileDiff } from './diff'
|
||||
import { getFileBlob, getFileDiff } from './diff'
|
||||
|
||||
let github: Octokit | undefined
|
||||
let kittycad: Client | undefined
|
||||
@ -168,5 +169,18 @@ chrome.runtime.onMessage.addListener(
|
||||
.catch(error => sendResponse({ error }))
|
||||
return true
|
||||
}
|
||||
|
||||
if (message.id === MessageIds.GetFileBlob) {
|
||||
if (!kittycad || !github) {
|
||||
sendResponse({ error: noClientError })
|
||||
return false
|
||||
}
|
||||
const { owner, repo, sha, filename } =
|
||||
message.data as MessageGetFileBlob
|
||||
getFileBlob(github, kittycad, owner, repo, sha, filename)
|
||||
.then(r => sendResponse(r))
|
||||
.catch(error => sendResponse({ error }))
|
||||
return true
|
||||
}
|
||||
}
|
||||
)
|
||||
|
@ -1,11 +1,13 @@
|
||||
import React from 'react'
|
||||
import { CadDiffPage } from '../components/diff/CadDiffPage'
|
||||
import { CadDiffPage } from '../components/viewer/CadDiffPage'
|
||||
import { CadBlobPage } from '../components/viewer/CadBlobPage'
|
||||
import { Commit, DiffEntry, MessageIds, Pull } from './types'
|
||||
import {
|
||||
getGithubPullUrlParams,
|
||||
mapInjectableDiffElements,
|
||||
getGithubCommitUrlParams,
|
||||
createReactRoot,
|
||||
getGithubBlobUrlParams,
|
||||
} from './web'
|
||||
import gitHubInjection from 'github-injection'
|
||||
|
||||
@ -30,6 +32,43 @@ async function injectDiff(
|
||||
root.render(cadDiffPage)
|
||||
}
|
||||
|
||||
async function injectBlob(
|
||||
owner: string,
|
||||
repo: string,
|
||||
sha: string,
|
||||
filename: string,
|
||||
document: Document
|
||||
) {
|
||||
let classicUi = false
|
||||
// React UI (as of 2023-06-23, for signed-in users only)
|
||||
const childWithProperClass = document.querySelector<HTMLElement>(
|
||||
'.react-blob-view-header-sticky'
|
||||
)
|
||||
let element = childWithProperClass?.parentElement
|
||||
if (!element) {
|
||||
// Classic UI
|
||||
const childWithProperClass =
|
||||
document.querySelector<HTMLElement>('.js-blob-header')
|
||||
element = childWithProperClass?.parentElement
|
||||
classicUi = !!element
|
||||
}
|
||||
|
||||
if (!element) {
|
||||
throw Error("Couldn't find blob html element to inject")
|
||||
}
|
||||
|
||||
element.classList.add('kittycad-injected-file')
|
||||
const cadBlobPage = React.createElement(CadBlobPage, {
|
||||
element,
|
||||
owner,
|
||||
repo,
|
||||
sha,
|
||||
filename,
|
||||
classicUi,
|
||||
})
|
||||
root.render(cadBlobPage)
|
||||
}
|
||||
|
||||
async function injectPullDiff(
|
||||
owner: string,
|
||||
repo: string,
|
||||
@ -88,6 +127,14 @@ async function run() {
|
||||
await injectCommitDiff(owner, repo, sha, window.document)
|
||||
return
|
||||
}
|
||||
|
||||
const blobParams = getGithubBlobUrlParams(url)
|
||||
if (blobParams) {
|
||||
const { owner, repo, sha, filename } = blobParams
|
||||
console.log('Found blob diff: ', owner, repo, sha, filename)
|
||||
await injectBlob(owner, repo, sha, filename, window.document)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
function waitForLateDiffNodes(callback: () => void) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Octokit } from '@octokit/rest'
|
||||
import { Client, file } from '@kittycad/lib'
|
||||
import { ContentFile, DiffEntry, FileDiff } from './types'
|
||||
import { ContentFile, DiffEntry, FileBlob, FileDiff } from './types'
|
||||
import {
|
||||
FileExportFormat_type,
|
||||
FileImportFormat_type,
|
||||
@ -132,3 +132,25 @@ export async function getFileDiff(
|
||||
|
||||
throw Error(`Unsupported status: ${status}`)
|
||||
}
|
||||
|
||||
export async function getFileBlob(
|
||||
github: Octokit,
|
||||
kittycad: Client,
|
||||
owner: string,
|
||||
repo: string,
|
||||
sha: string,
|
||||
filename: string
|
||||
): Promise<FileBlob> {
|
||||
const extension = filename.split('.').pop()
|
||||
if (!extension || !extensionToSrcFormat[extension]) {
|
||||
throw Error(
|
||||
`Unsupported extension. Given ${extension}, was expecting ${Object.keys(
|
||||
extensionToSrcFormat
|
||||
)}`
|
||||
)
|
||||
}
|
||||
|
||||
const rawBlob = await downloadFile(github, owner, repo, sha, filename)
|
||||
const blob = await convert(kittycad, rawBlob, extension)
|
||||
return { blob }
|
||||
}
|
||||
|
@ -20,6 +20,10 @@ export type FileDiff = {
|
||||
after?: string
|
||||
}
|
||||
|
||||
export type FileBlob = {
|
||||
blob?: string
|
||||
}
|
||||
|
||||
export enum MessageIds {
|
||||
GetGithubPullFiles = 'GetPullFiles',
|
||||
GetGithubUser = 'GetGitHubUser',
|
||||
@ -27,6 +31,7 @@ export enum MessageIds {
|
||||
SaveKittycadToken = 'SaveKittyCadToken',
|
||||
GetKittycadUser = 'GetKittyCadUser',
|
||||
GetFileDiff = 'GetFileDiff',
|
||||
GetFileBlob = 'GetFileBlob',
|
||||
GetGithubPull = 'GetGithubPull',
|
||||
GetGithubCommit = 'GetGithubCommit',
|
||||
}
|
||||
@ -51,6 +56,13 @@ export type MessageGetFileDiff = {
|
||||
file: DiffEntry
|
||||
}
|
||||
|
||||
export type MessageGetFileBlob = {
|
||||
owner: string
|
||||
repo: string
|
||||
sha: string
|
||||
filename: string
|
||||
}
|
||||
|
||||
export type MessageSaveToken = {
|
||||
token: string
|
||||
}
|
||||
@ -76,5 +88,6 @@ export type MessageResponse =
|
||||
| KittycadUser
|
||||
| MessageSaveToken
|
||||
| FileDiff
|
||||
| FileBlob
|
||||
| MessageError
|
||||
| void
|
||||
|
@ -7,6 +7,7 @@ import {
|
||||
mapInjectableDiffElements,
|
||||
getSupportedWebDiffElements,
|
||||
createReactRoot,
|
||||
getGithubBlobUrlParams,
|
||||
} from './web'
|
||||
|
||||
const githubPullHtmlSnippet = `
|
||||
@ -146,6 +147,27 @@ describe('Function getGithubCommitUrlParams', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('Function getGithubBlobUrlParams', () => {
|
||||
it('gets params out of a valid github blob link', () => {
|
||||
const url =
|
||||
'https://github.com/KittyCAD/diff-samples/blob/fd9eec79f0464833686ea6b5b34ea07145e32734/models/box.obj'
|
||||
const params = getGithubBlobUrlParams(url)
|
||||
expect(params).toBeDefined()
|
||||
const { owner, repo, sha, filename } = params!
|
||||
expect(owner).toEqual('KittyCAD')
|
||||
expect(repo).toEqual('diff-samples')
|
||||
expect(sha).toEqual('fd9eec79f0464833686ea6b5b34ea07145e32734')
|
||||
expect(filename).toEqual('models/box.obj')
|
||||
})
|
||||
|
||||
it("doesn't match other URLs", () => {
|
||||
expect(getGithubPullUrlParams('http://google.com')).toBeUndefined()
|
||||
expect(
|
||||
getGithubPullUrlParams('https://github.com/KittyCAD/litterbox')
|
||||
).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
it('finds web elements for supported files', () => {
|
||||
const elements = getSupportedWebDiffElements(githubPullHtmlDocument)
|
||||
expect(elements).toHaveLength(2)
|
||||
|
@ -44,6 +44,34 @@ export function getGithubCommitUrlParams(
|
||||
return { owner, repo, sha }
|
||||
}
|
||||
|
||||
export type GithubBlobUrlParams = {
|
||||
owner: string
|
||||
repo: string
|
||||
sha: string
|
||||
filename: string
|
||||
}
|
||||
|
||||
export function getGithubBlobUrlParams(
|
||||
url: string
|
||||
): GithubBlobUrlParams | undefined {
|
||||
const blobRe =
|
||||
/https:\/\/github\.com\/([a-zA-Z0-9_.-]+)\/([a-zA-Z0-9_.-]+)\/blob\/(\w+)\/([^\0]+)/
|
||||
const result = blobRe.exec(url)
|
||||
if (!result) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const [, owner, repo, sha, filename] = result
|
||||
console.log(
|
||||
'Found a supported Github Blob URL:',
|
||||
owner,
|
||||
repo,
|
||||
sha,
|
||||
filename
|
||||
)
|
||||
return { owner, repo, sha, filename }
|
||||
}
|
||||
|
||||
export function getSupportedWebDiffElements(document: Document): HTMLElement[] {
|
||||
const fileTypeSelectors = Object.keys(extensionToSrcFormat).map(
|
||||
t => `.file[data-file-type=".${t}"]`
|
||||
|
68
src/components/viewer/CadBlob.tsx
Normal file
68
src/components/viewer/CadBlob.tsx
Normal file
@ -0,0 +1,68 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import '@react-three/fiber'
|
||||
import { Box, Text, useTheme } from '@primer/react'
|
||||
import { FileBlob } from '../../chrome/types'
|
||||
import { Viewer3D } from './Viewer3D'
|
||||
import { BufferGeometry, Sphere } from 'three'
|
||||
import { WireframeColors, WireframeModel } from './WireframeModel'
|
||||
import { useRef } from 'react'
|
||||
import { loadGeometry } from '../../utils/three'
|
||||
import { OrbitControls } from 'three-stdlib'
|
||||
import { RecenterButton } from './RecenterButton'
|
||||
import { ErrorMessage } from './ErrorMessage'
|
||||
|
||||
export function CadBlob({ blob }: FileBlob): React.ReactElement {
|
||||
const [geometry, setGeometry] = useState<BufferGeometry>()
|
||||
const [boundingSphere, setBoundingSphere] = useState<Sphere>()
|
||||
const controlsRef = useRef<OrbitControls | null>(null)
|
||||
const [controlsAltered, setControlsAltered] = useState(false)
|
||||
const { theme } = useTheme()
|
||||
const colors: WireframeColors = {
|
||||
face: theme?.colors.fg.default,
|
||||
edge: theme?.colors.fg.muted,
|
||||
dashEdge: theme?.colors.fg.subtle,
|
||||
}
|
||||
useEffect(() => {
|
||||
let geometry: BufferGeometry | undefined = undefined
|
||||
if (blob) {
|
||||
geometry = loadGeometry(blob)
|
||||
setGeometry(geometry)
|
||||
if (geometry && geometry.boundingSphere) {
|
||||
setBoundingSphere(geometry.boundingSphere)
|
||||
}
|
||||
}
|
||||
}, [blob])
|
||||
return (
|
||||
<>
|
||||
{geometry && (
|
||||
<Box position="relative">
|
||||
<Box height={300} backgroundColor="canvas.subtle">
|
||||
<Viewer3D
|
||||
geometry={geometry}
|
||||
boundingSphere={boundingSphere}
|
||||
controlsRef={controlsRef}
|
||||
onControlsAltered={() =>
|
||||
!controlsAltered && setControlsAltered(true)
|
||||
}
|
||||
>
|
||||
<WireframeModel
|
||||
geometry={geometry}
|
||||
boundingSphere={boundingSphere}
|
||||
colors={colors}
|
||||
/>
|
||||
</Viewer3D>
|
||||
</Box>
|
||||
{controlsAltered && (
|
||||
<RecenterButton
|
||||
onClick={() => {
|
||||
controlsRef.current?.reset()
|
||||
setControlsAltered(false)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
{!geometry && <ErrorMessage />}
|
||||
</>
|
||||
)
|
||||
}
|
155
src/components/viewer/CadBlobPage.tsx
Normal file
155
src/components/viewer/CadBlobPage.tsx
Normal file
@ -0,0 +1,155 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import '@react-three/fiber'
|
||||
import { Box, SegmentedControl, ThemeProvider } from '@primer/react'
|
||||
import { FileBlob, MessageIds } from '../../chrome/types'
|
||||
import { createPortal } from 'react-dom'
|
||||
import { Loading } from '../Loading'
|
||||
import { CadBlob } from './CadBlob'
|
||||
|
||||
function CadBlobPortal({
|
||||
element,
|
||||
owner,
|
||||
repo,
|
||||
sha,
|
||||
filename,
|
||||
classicUi,
|
||||
}: {
|
||||
element: HTMLElement
|
||||
owner: string
|
||||
repo: string
|
||||
sha: string
|
||||
filename: string
|
||||
classicUi: boolean
|
||||
}): React.ReactElement {
|
||||
const [richBlob, setRichBlob] = useState<FileBlob>()
|
||||
const [richSelected, setRichSelected] = useState(true)
|
||||
const [toolbarContainer, setToolbarContainer] = useState<HTMLElement>()
|
||||
const [blobContainer, setBlobContainer] = useState<HTMLElement>()
|
||||
const [sourceElements, setSourceElements] = useState<HTMLElement[]>([])
|
||||
|
||||
useEffect(() => {
|
||||
let existingToggle: HTMLElement | undefined | null
|
||||
let toolbar: HTMLElement | undefined | null
|
||||
let blob: HTMLElement | undefined | null
|
||||
if (classicUi) {
|
||||
// no existing toggle
|
||||
toolbar = element.querySelector<HTMLElement>('.js-blob-header')
|
||||
blob = element.querySelector<HTMLElement>('.blob-wrapper')
|
||||
} else {
|
||||
existingToggle = element.querySelector<HTMLElement>(
|
||||
'ul[class*=SegmentedControl]'
|
||||
)
|
||||
toolbar = existingToggle?.parentElement
|
||||
blob = element.querySelector<HTMLElement>(
|
||||
'section[aria-labelledby="file-name-id"]'
|
||||
)
|
||||
}
|
||||
|
||||
if (toolbar != null) {
|
||||
setToolbarContainer(toolbar)
|
||||
if (existingToggle) {
|
||||
existingToggle.style.display = 'none'
|
||||
}
|
||||
}
|
||||
|
||||
if (blob != null) {
|
||||
setBlobContainer(blob)
|
||||
const sourceElements = Array.from(blob.children) as HTMLElement[]
|
||||
sourceElements.map(n => (n.style.display = 'none'))
|
||||
setSourceElements(sourceElements)
|
||||
}
|
||||
}, [element])
|
||||
|
||||
useEffect(() => {
|
||||
;(async () => {
|
||||
const response = await chrome.runtime.sendMessage({
|
||||
id: MessageIds.GetFileBlob,
|
||||
data: { owner, repo, sha, filename },
|
||||
})
|
||||
if ('error' in response) {
|
||||
console.log(response.error)
|
||||
} else {
|
||||
setRichBlob(response as FileBlob)
|
||||
}
|
||||
})()
|
||||
}, [owner, repo, sha, filename])
|
||||
|
||||
return (
|
||||
<>
|
||||
{toolbarContainer &&
|
||||
createPortal(
|
||||
<SegmentedControl
|
||||
sx={{ mr: classicUi ? 2 : 0, order: -1 }} // prepend in flex
|
||||
aria-label="File view"
|
||||
onChange={(index: number) => {
|
||||
if (index < 2) {
|
||||
setRichSelected(index === 0)
|
||||
sourceElements.map(
|
||||
n =>
|
||||
(n.style.display =
|
||||
index === 0 ? 'none' : 'block')
|
||||
)
|
||||
return
|
||||
}
|
||||
window.location.href = `https://github.com/${owner}/${repo}/blame/${sha}/${filename}`
|
||||
}}
|
||||
>
|
||||
<SegmentedControl.Button selected={richSelected}>
|
||||
Preview
|
||||
</SegmentedControl.Button>
|
||||
<SegmentedControl.Button selected={!richSelected}>
|
||||
Code
|
||||
</SegmentedControl.Button>
|
||||
{!classicUi && (
|
||||
<SegmentedControl.Button>
|
||||
Blame
|
||||
</SegmentedControl.Button>
|
||||
)}
|
||||
</SegmentedControl>,
|
||||
toolbarContainer
|
||||
)}
|
||||
{blobContainer &&
|
||||
createPortal(
|
||||
<Box sx={{ display: richSelected ? 'block' : 'none' }}>
|
||||
{richBlob ? (
|
||||
<CadBlob blob={richBlob.blob} />
|
||||
) : (
|
||||
<Loading />
|
||||
)}
|
||||
</Box>,
|
||||
blobContainer
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export type CadBlobPageProps = {
|
||||
element: HTMLElement
|
||||
owner: string
|
||||
repo: string
|
||||
sha: string
|
||||
filename: string
|
||||
classicUi: boolean
|
||||
}
|
||||
|
||||
export function CadBlobPage({
|
||||
element,
|
||||
owner,
|
||||
repo,
|
||||
sha,
|
||||
filename,
|
||||
classicUi,
|
||||
}: CadBlobPageProps): React.ReactElement {
|
||||
return (
|
||||
<ThemeProvider colorMode="auto">
|
||||
<CadBlobPortal
|
||||
element={element}
|
||||
owner={owner}
|
||||
repo={repo}
|
||||
sha={sha}
|
||||
filename={filename}
|
||||
classicUi={classicUi}
|
||||
/>
|
||||
</ThemeProvider>
|
||||
)
|
||||
}
|
@ -18,6 +18,8 @@ import { BeakerIcon } from '@primer/octicons-react'
|
||||
import { LegendBox, LegendLabel } from './Legend'
|
||||
import { getCommonSphere, loadGeometry } from '../../utils/three'
|
||||
import { OrbitControls } from 'three-stdlib'
|
||||
import { RecenterButton } from './RecenterButton'
|
||||
import { ErrorMessage } from './ErrorMessage'
|
||||
|
||||
function Viewer3D2Up({
|
||||
beforeGeometry,
|
||||
@ -88,17 +90,13 @@ function Viewer3D2Up({
|
||||
</Box>
|
||||
)}
|
||||
{controlsAltered && (
|
||||
<Box top={2} right={2} position="absolute">
|
||||
<Button
|
||||
<RecenterButton
|
||||
onClick={() => {
|
||||
afterControlsRef.current?.reset()
|
||||
beforeControlsRef.current?.reset()
|
||||
setControlsAltered(false)
|
||||
}}
|
||||
>
|
||||
Recenter
|
||||
</Button>
|
||||
</Box>
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
@ -158,16 +156,12 @@ function Viewer3DCombined({
|
||||
/>
|
||||
</LegendBox>
|
||||
{controlsAltered && (
|
||||
<Box top={2} right={2} position="absolute">
|
||||
<Button
|
||||
<RecenterButton
|
||||
onClick={() => {
|
||||
controlsRef.current?.reset()
|
||||
setControlsAltered(false)
|
||||
}}
|
||||
>
|
||||
Recenter
|
||||
</Button>
|
||||
</Box>
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
@ -273,13 +267,7 @@ export function CadDiff({ before, after }: FileDiff): React.ReactElement {
|
||||
</TabNav>
|
||||
</Box>
|
||||
)}
|
||||
{!beforeGeometry && !afterGeometry && (
|
||||
<Box p={3}>
|
||||
<Text>
|
||||
Sorry, the rich diff can't be displayed for this file.
|
||||
</Text>
|
||||
</Box>
|
||||
)}
|
||||
{!beforeGeometry && !afterGeometry && <ErrorMessage />}
|
||||
</>
|
||||
)
|
||||
}
|
14
src/components/viewer/ErrorMessage.test.tsx
Normal file
14
src/components/viewer/ErrorMessage.test.tsx
Normal file
@ -0,0 +1,14 @@
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import { ErrorMessage } from './ErrorMessage'
|
||||
|
||||
it('renders the error message', async () => {
|
||||
render(<ErrorMessage />)
|
||||
const text = await screen.findByText(/preview/)
|
||||
expect(text).toBeDefined()
|
||||
})
|
||||
|
||||
it('renders the error message with a custom message', async () => {
|
||||
render(<ErrorMessage message="custom" />)
|
||||
const text = await screen.findByText(/custom/)
|
||||
expect(text).toBeDefined()
|
||||
})
|
12
src/components/viewer/ErrorMessage.tsx
Normal file
12
src/components/viewer/ErrorMessage.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import { Box, Text } from '@primer/react'
|
||||
|
||||
export function ErrorMessage({ message }: { message?: string }) {
|
||||
return (
|
||||
<Box p={3}>
|
||||
<Text>
|
||||
{message ||
|
||||
"Sorry, the rich preview can't be displayed for this file."}
|
||||
</Text>
|
||||
</Box>
|
||||
)
|
||||
}
|
12
src/components/viewer/RecenterButton.test.tsx
Normal file
12
src/components/viewer/RecenterButton.test.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import { fireEvent, render, screen } from '@testing-library/react'
|
||||
import { vi } from 'vitest'
|
||||
import { RecenterButton } from './RecenterButton'
|
||||
|
||||
it('renders the recenter button', async () => {
|
||||
const callback = vi.fn()
|
||||
render(<RecenterButton onClick={callback} />)
|
||||
const button = await screen.findByRole('button')
|
||||
expect(callback.mock.calls).toHaveLength(0)
|
||||
fireEvent.click(button)
|
||||
expect(callback.mock.calls).toHaveLength(1)
|
||||
})
|
9
src/components/viewer/RecenterButton.tsx
Normal file
9
src/components/viewer/RecenterButton.tsx
Normal file
@ -0,0 +1,9 @@
|
||||
import { Box, Button } from '@primer/react'
|
||||
|
||||
export function RecenterButton({ onClick }: { onClick: () => void }) {
|
||||
return (
|
||||
<Box top={2} right={2} position="absolute">
|
||||
<Button onClick={onClick}>Recenter</Button>
|
||||
</Box>
|
||||
)
|
||||
}
|
@ -39,6 +39,19 @@ async function getFirstDiffScreenshot(
|
||||
return await element.screenshot()
|
||||
}
|
||||
|
||||
async function getBlobPreviewScreenshot(page: Page, url: string) {
|
||||
page.on('console', msg => console.log(msg.text()))
|
||||
await page.goto(url)
|
||||
|
||||
// waiting for the canvas (that holds the diff) to show up
|
||||
await page.waitForSelector('#repo-content-pjax-container canvas')
|
||||
|
||||
// screenshot the file diff with its toolbar
|
||||
const element = await page.waitForSelector('.kittycad-injected-file')
|
||||
await page.waitForTimeout(1000) // making sure the element fully settled in
|
||||
return await element.screenshot()
|
||||
}
|
||||
|
||||
test('pull request diff with an .obj file', async ({
|
||||
page,
|
||||
authorizedBackground,
|
||||
@ -76,3 +89,13 @@ test('commit diff with a .dae file as LFS', async ({
|
||||
const screenshot = await getFirstDiffScreenshot(page, url, 'dae')
|
||||
expect(screenshot).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('blob preview with an .obj file', async ({
|
||||
page,
|
||||
authorizedBackground,
|
||||
}) => {
|
||||
const url =
|
||||
'https://github.com/KittyCAD/diff-samples/blob/fd9eec79f0464833686ea6b5b34ea07145e32734/models/box.obj'
|
||||
const screenshot = await getBlobPreviewScreenshot(page, url)
|
||||
expect(screenshot).toMatchSnapshot()
|
||||
})
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 67 KiB |
Binary file not shown.
After Width: | Height: | Size: 67 KiB |
Reference in New Issue
Block a user