diff --git a/app/gui/integration-test/dashboard/actions/api.ts b/app/gui/integration-test/dashboard/actions/api.ts
index 9944c8bbc14f..09f7c15cbb5f 100644
--- a/app/gui/integration-test/dashboard/actions/api.ts
+++ b/app/gui/integration-test/dashboard/actions/api.ts
@@ -15,6 +15,7 @@ import * as actions from '.'
import { readFileSync } from 'node:fs'
import { dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'
+import invariant from 'tiny-invariant'
const __dirname = dirname(fileURLToPath(import.meta.url))
@@ -1129,17 +1130,23 @@ async function mockApiInternal({ page, setupAPI }: MockParams) {
})
})
- await get(remoteBackendPaths.getProjectAssetPath(GLOB_PROJECT_ID, '*'), (route, request) => {
- const maybeId = request.url().match(/[/]projects[/]([^?/]+)/)?.[1]
- if (!maybeId) return
- const projectId = backend.ProjectId(maybeId)
- called('getProjectAsset', { projectId })
- return route.fulfill({
- // This is a mock SVG image. Just a square with a black background.
- body: '/mock/svg.svg',
- contentType: 'text/plain',
- })
- })
+ await get(
+ remoteBackendPaths.getProjectAssetPath(GLOB_PROJECT_ID, '*'),
+ async (route, request) => {
+ const maybeId = request.url().match(/[/]projects[/]([^?/]+)/)?.[1]
+
+ invariant(maybeId, 'Unable to parse the ID provided')
+
+ const projectId = backend.ProjectId(maybeId)
+
+ called('getProjectAsset', { projectId })
+
+ return route.fulfill({
+ // This is a mock SVG image. Just a square with a black background.
+ path: join(__dirname, '../mock/example.png'),
+ })
+ },
+ )
await page.route('mock/svg.svg', (route) => {
return route.fulfill({ body: MOCK_SVG, contentType: 'image/svg+xml' })
diff --git a/app/gui/integration-test/dashboard/assetPanel.spec.ts b/app/gui/integration-test/dashboard/assetPanel.spec.ts
index 9282cf573724..37e8ffd05102 100644
--- a/app/gui/integration-test/dashboard/assetPanel.spec.ts
+++ b/app/gui/integration-test/dashboard/assetPanel.spec.ts
@@ -5,7 +5,7 @@ import { EmailAddress, UserId } from '#/services/Backend'
import { PermissionAction } from '#/utilities/permissions'
-import { mockAllAndLogin } from './actions'
+import { mockAllAndLogin, TEXT } from './actions'
/** Find an asset panel. */
function locateAssetPanel(page: Page) {
@@ -87,4 +87,25 @@ test('Asset Panel documentation view', ({ page }) =>
await expect(assetPanel.getByTestId('asset-panel-tab-panel-docs')).toBeVisible()
await expect(assetPanel.getByTestId('asset-docs-content')).toBeVisible()
await expect(assetPanel.getByTestId('asset-docs-content')).toHaveText(/Project Goal/)
+ await expect(assetPanel.getByText(TEXT.arbitraryFetchImageError)).not.toBeVisible()
}))
+
+test('Assets Panel docs images', ({ page }) => {
+ return mockAllAndLogin({
+ page,
+ setupAPI: (api) => {
+ api.addProject({})
+ },
+ })
+ .do(() => {})
+ .driveTable.clickRow(0)
+ .toggleDocsAssetPanel()
+ .withAssetPanel(async (assetPanel) => {
+ await expect(assetPanel.getByTestId('asset-docs-content')).toBeVisible()
+
+ for (const image of await assetPanel.getByRole('img').all()) {
+ await expect(image).toBeVisible()
+ await expect(image).toHaveJSProperty('complete', true)
+ }
+ })
+})
diff --git a/app/gui/integration-test/dashboard/mock/example.png b/app/gui/integration-test/dashboard/mock/example.png
new file mode 100644
index 000000000000..b4d6d8b3cb9c
Binary files /dev/null and b/app/gui/integration-test/dashboard/mock/example.png differ
diff --git a/app/gui/src/dashboard/components/MarkdownViewer/MarkdownViewer.tsx b/app/gui/src/dashboard/components/MarkdownViewer/MarkdownViewer.tsx
index 709d617623b8..ebd199958086 100644
--- a/app/gui/src/dashboard/components/MarkdownViewer/MarkdownViewer.tsx
+++ b/app/gui/src/dashboard/components/MarkdownViewer/MarkdownViewer.tsx
@@ -1,5 +1,6 @@
/** @file A Markdown viewer component. */
+import { useLogger } from '#/providers/LoggerProvider'
import { useText } from '#/providers/TextProvider'
import { useSuspenseQuery } from '@tanstack/react-query'
import type { RendererObject } from 'marked'
@@ -23,6 +24,7 @@ export function MarkdownViewer(props: MarkdownViewerProps) {
const { text, imgUrlResolver, renderer = defaultRenderer, testId } = props
const { getText } = useText()
+ const logger = useLogger()
const markedInstance = marked.use({ renderer: Object.assign({}, defaultRenderer, renderer) })
@@ -36,7 +38,15 @@ export function MarkdownViewer(props: MarkdownViewerProps) {
const href = token.href
token.raw = href
- token.href = await args.imgUrlResolver(href).catch(() => null)
+ token.href = await args
+ .imgUrlResolver(href)
+ .then((url) => {
+ return url
+ })
+ .catch((error) => {
+ logger.error(error)
+ return null
+ })
token.text = getText('arbitraryFetchImageError')
}
},
diff --git a/app/gui/src/dashboard/components/MarkdownViewer/defaultRenderer.ts b/app/gui/src/dashboard/components/MarkdownViewer/defaultRenderer.ts
index 032da7bf4f5d..ac2a909e2987 100644
--- a/app/gui/src/dashboard/components/MarkdownViewer/defaultRenderer.ts
+++ b/app/gui/src/dashboard/components/MarkdownViewer/defaultRenderer.ts
@@ -26,13 +26,11 @@ export const defaultRenderer: RendererObject = {
return `${this.parser.parseInline(tokens)}`
},
/** The renderer for images. */
- image({ href, title, raw, text }) {
+ image({ href, title, raw }) {
const alt = title ?? ''
return `
-
+
`
},
/** The renderer for code. */