Add markdown rendering to TTC error toast, with component tests (#7170)
Closes #5792. I tried to move these over to our new component test bucket, but Remark doesn't play nice with that testing setup at least in my initial attempts.
This commit is contained in:
87
src/components/ToastTextToCad.test.tsx
Normal file
87
src/components/ToastTextToCad.test.tsx
Normal file
@ -0,0 +1,87 @@
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import { ToastTextToCadError } from '@src/components/ToastTextToCad'
|
||||
|
||||
describe('ToastTextToCadError tests', () => {
|
||||
const testData = {
|
||||
errorWithMarkdown: `422 Unprocessable Entity. Text-to-CAD is still improving, and some prompts may fail. Try adjusting your prompt for better results. We review failures to enhance the model over time. For prompt tips and best practices, check out our community on [Discord](https://discord.gg/JQEpHR7Nt2) or [Discourse](https://community.zoo.dev).`,
|
||||
errorWithPlainText: "I'M A TEAPOT",
|
||||
errorWithMalformedContent: `[bad-link)(h:/ya;lk3&"& th] WHICH ## Heading? *** benches bj;lkj,,,{]<ul>((()))bhwlqq!`,
|
||||
} as const
|
||||
|
||||
test('Happy path: renders markdown just fine', () => {
|
||||
render(
|
||||
<ToastTextToCadError
|
||||
toastId="test"
|
||||
newProjectName="newProject"
|
||||
projectName="currProject"
|
||||
method="whyDoWePassThisIn"
|
||||
prompt="Something complex like a tiger or a tree"
|
||||
message={testData.errorWithMarkdown}
|
||||
key="testKey"
|
||||
/>
|
||||
)
|
||||
|
||||
// Locators and other constants
|
||||
const editPromptButton = screen.getByRole('button', {
|
||||
name: /edit prompt/i,
|
||||
})
|
||||
const dismissButton = screen.getByRole('button', { name: /dismiss/i })
|
||||
// If an actual link is shown the renderer worked
|
||||
const errorLink = screen.getByRole('link', { name: /discourse/i })
|
||||
|
||||
expect(editPromptButton).toBeVisible()
|
||||
expect(dismissButton).toBeVisible()
|
||||
expect(errorLink).toBeVisible()
|
||||
})
|
||||
|
||||
test('Happy path: renders plaintext just fine', () => {
|
||||
render(
|
||||
<ToastTextToCadError
|
||||
toastId="test"
|
||||
newProjectName="newProject"
|
||||
projectName="currProject"
|
||||
method="whyDoWePassThisIn"
|
||||
prompt="Something complex like a tiger or a tree"
|
||||
message={testData.errorWithPlainText}
|
||||
key="testKey"
|
||||
/>
|
||||
)
|
||||
|
||||
// Locators and other constants
|
||||
const editPromptButton = screen.getByRole('button', {
|
||||
name: /edit prompt/i,
|
||||
})
|
||||
const dismissButton = screen.getByRole('button', { name: /dismiss/i })
|
||||
const errorParagraph = screen.getByText('TEAPOT', { exact: false })
|
||||
|
||||
expect(editPromptButton).toBeVisible()
|
||||
expect(dismissButton).toBeVisible()
|
||||
expect(errorParagraph).toBeVisible()
|
||||
})
|
||||
|
||||
test('Happy path: renders malformed text just fine', () => {
|
||||
render(
|
||||
<ToastTextToCadError
|
||||
toastId="test"
|
||||
newProjectName="newProject"
|
||||
projectName="currProject"
|
||||
method="whyDoWePassThisIn"
|
||||
prompt="Something complex like a tiger or a tree"
|
||||
message={testData.errorWithMalformedContent}
|
||||
key="testKey"
|
||||
/>
|
||||
)
|
||||
|
||||
// Locators and other constants
|
||||
const editPromptButton = screen.getByRole('button', {
|
||||
name: /edit prompt/i,
|
||||
})
|
||||
const dismissButton = screen.getByRole('button', { name: /dismiss/i })
|
||||
// If it renders the malformed link as a paragraph that's good
|
||||
const errorParagraph = screen.queryByText(/bad-link/, { exact: false })
|
||||
|
||||
expect(editPromptButton).toBeVisible()
|
||||
expect(dismissButton).toBeVisible()
|
||||
expect(errorParagraph).toBeVisible()
|
||||
})
|
||||
})
|
||||
@ -41,6 +41,13 @@ import {
|
||||
import { commandBarActor } from '@src/lib/singletons'
|
||||
import type { FileMeta } from '@src/lib/types'
|
||||
import type { RequestedKCLFile } from '@src/machines/systemIO/utils'
|
||||
import {
|
||||
Marked,
|
||||
type MarkedOptions,
|
||||
escape,
|
||||
unescape,
|
||||
} from '@ts-stack/markdown'
|
||||
import { SafeRenderer } from '@src/lib/markdown'
|
||||
|
||||
const CANVAS_SIZE = 128
|
||||
const PROMPT_TRUNCATE_LENGTH = 128
|
||||
@ -99,13 +106,26 @@ export function ToastTextToCadError({
|
||||
projectName: string
|
||||
newProjectName: string
|
||||
}) {
|
||||
const markedOptions: MarkedOptions = {
|
||||
gfm: true,
|
||||
breaks: true,
|
||||
sanitize: true,
|
||||
unescape,
|
||||
escape,
|
||||
}
|
||||
return (
|
||||
<div className="flex flex-col justify-between gap-6">
|
||||
<section>
|
||||
<h2>Text-to-CAD failed</h2>
|
||||
<p className="text-sm text-chalkboard-70 dark:text-chalkboard-30">
|
||||
{message}
|
||||
</p>
|
||||
<div
|
||||
className="parsed-markdown mt-4 text-sm text-chalkboard-70 dark:text-chalkboard-30 max-h-60 overflow-y-auto whitespace-normal"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: Marked.parse(message, {
|
||||
renderer: new SafeRenderer(markedOptions),
|
||||
...markedOptions,
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
</section>
|
||||
<div className="flex justify-between gap-8">
|
||||
<ActionButton
|
||||
|
||||
Reference in New Issue
Block a user