Spaces:
Sleeping
Sleeping
wuyiqunLu
feat: add playwright e2e test for chat page with new and old data structure (#99)
06bd1d1
unverified
import React from 'react'; | |
import { CodeBlock } from './ui/CodeBlock'; | |
import { | |
Dialog, | |
DialogTrigger, | |
DialogContent, | |
DialogHeader, | |
DialogTitle, | |
} from './ui/Dialog'; | |
import { Button } from './ui/Button'; | |
import { IconLog, IconTerminalWindow } from './ui/Icons'; | |
import { Separator } from './ui/Separator'; | |
import Img from './ui/Img'; | |
import { | |
Carousel, | |
CarouselContent, | |
CarouselItem, | |
CarouselNext, | |
CarouselPrevious, | |
} from './ui/carousel'; | |
export interface CodeResultDisplayProps {} | |
const CodeResultDisplay: React.FC<{ | |
codeResult: PrismaJson.FinalCodeBody['payload']; | |
}> = ({ codeResult }) => { | |
const { code, test, result } = codeResult; | |
const getDetail = () => { | |
if (!result) return {}; | |
try { | |
// IMPORTANT: This is for backwards compatibility with old chat that save result as JSON string | |
// updated in https://github.com/landing-ai/vision-agent-ui/pull/86 | |
const detail = | |
typeof result === 'object' | |
? result | |
: (JSON.parse(result) as PrismaJson.StructuredResult); | |
return { | |
results: detail.results, | |
stderr: detail.logs.stderr, | |
stdout: detail.logs.stdout, | |
error: detail.error, | |
}; | |
} catch { | |
return {}; | |
} | |
}; | |
const { results = [], stderr, stdout, error } = getDetail(); | |
const imageResults = results?.filter(_ => !!_.png).map(_ => _.png); | |
const videoResults = results?.filter(_ => !!_.mp4).map(_ => _.mp4); | |
const finalResult = results?.find(_ => _.is_main_result)?.text; | |
return ( | |
<div | |
className="rounded-lg overflow-hidden relative max-w-5xl" | |
data-testid="code-result-display-container" | |
> | |
<CodeBlock language="python" value={code} /> | |
<div className="rounded-lg relative"> | |
<div className="absolute left-1/2 -translate-x-1/2 -top-4 z-10"> | |
<Dialog> | |
<DialogTrigger asChild> | |
<Button | |
data-testid="open-final-test-code-dialog-button" | |
variant="ghost" | |
size="icon" | |
className="size-8" | |
> | |
<IconTerminalWindow className="text-teal-500 size-4" /> | |
</Button> | |
</DialogTrigger> | |
<DialogContent className="max-w-5xl"> | |
<DialogHeader> | |
<DialogTitle>Test code</DialogTitle> | |
</DialogHeader> | |
<CodeBlock language="python" value={test} /> | |
</DialogContent> | |
</Dialog> | |
{Array.isArray(stderr) && !!stderr.join('').trim() && ( | |
<Dialog> | |
<DialogTrigger asChild> | |
<Button variant="ghost" size="icon" className="size-8"> | |
<IconLog className="text-gray-500 size-4" /> | |
</Button> | |
</DialogTrigger> | |
<DialogContent className="max-w-5xl"> | |
<CodeBlock language="vim" value={stderr.join('').trim()} /> | |
</DialogContent> | |
</Dialog> | |
)} | |
</div> | |
</div> | |
{Array.isArray(stdout) && !!stdout.join('').trim() && ( | |
<> | |
<Separator /> | |
<CodeBlock language="print" value={stdout.join('').trim()} /> | |
</> | |
)} | |
{!!error && ( | |
<> | |
<Separator /> | |
<CodeBlock | |
language="error" | |
value={ | |
error.name + | |
'\n' + | |
error.value + | |
'\n' + | |
error.traceback_raw.join('\n') | |
} | |
/> | |
</> | |
)} | |
{!!imageResults.length && ( | |
<div className="p-4 text-xs lowercase bg-zinc-900 space-y-4 border-t border-muted"> | |
<div className="flex items-center justify-between"> | |
<p>image output</p> | |
<Dialog> | |
<DialogTrigger asChild> | |
<Button | |
variant="outline" | |
size="sm" | |
data-testid="view-all-result-images-button" | |
> | |
View all | |
</Button> | |
</DialogTrigger> | |
<DialogContent className="max-w-5xl flex justify-center items-center"> | |
<Carousel className="w-3/4"> | |
<CarouselContent> | |
{imageResults.map((png, index) => ( | |
<CarouselItem key={'png' + index}> | |
<Img | |
src={png!} | |
width={1200} | |
alt={`detail-result-image-${index}`} | |
/> | |
</CarouselItem> | |
))} | |
</CarouselContent> | |
<CarouselPrevious /> | |
<CarouselNext /> | |
</Carousel> | |
</DialogContent> | |
</Dialog> | |
</div> | |
<div className="flex flex-row space-x-4 overflow-auto"> | |
{imageResults.map((png, index) => ( | |
<Img | |
key={'png' + index} | |
src={png!} | |
width={200} | |
alt={`result-image-${index}`} | |
/> | |
))} | |
</div> | |
</div> | |
)} | |
{!!videoResults.length && ( | |
<div className="p-4 text-xs lowercase bg-zinc-900 space-y-4 border-t border-muted"> | |
<p>video output</p> | |
<div className="flex flex-row space-x-4 overflow-auto"> | |
{videoResults.map((mp4, index) => ( | |
<Dialog key={'png' + index}> | |
<DialogTrigger asChild> | |
<video src={mp4} controls width={400} height={400} /> | |
</DialogTrigger> | |
<DialogContent className="max-w-5xl"> | |
<video src={mp4} controls width={400} height={400} /> | |
</DialogContent> | |
</Dialog> | |
))} | |
</div> | |
</div> | |
)} | |
{!!finalResult && <CodeBlock language="output" value={finalResult} />} | |
</div> | |
); | |
}; | |
export default CodeResultDisplay; | |