--- .../controllers/mermaid_controller.js | 23 ++++++++++++++++--- test/system/mermaid_rendering_test.rb | 22 ++++++++++-------- 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/app/javascript/controllers/mermaid_controller.js b/app/javascript/controllers/mermaid_controller.js index 9013336ff..cde65793b 100644 --- a/app/javascript/controllers/mermaid_controller.js +++ b/app/javascript/controllers/mermaid_controller.js @@ -67,10 +67,27 @@ export default class extends Controller { container.textContent = this.element.textContent.trim(); (pre || this.element).insertAdjacentElement('afterend', container); - // mermaid.js draws its own error diagram on failure, so the code block is replaced either way. - // The rejection is left for the browser to report. - mermaid.run({ nodes: [container], suppressErrors: false }).finally(() => { + // mermaid.js draws its own error diagram on failure + // replace it with a redmine flash message while keeping the source block available + // so the invalid diagram can be inspected and copied + mermaid.run({ nodes: [container], suppressErrors: false }).then(() => { if (pre) pre.style.display = 'none'; + }).catch((error) => { + container.remove(); + + const errorElement = document.createElement('div'); + errorElement.className = 'flash error'; + errorElement.innerHTML = '
Failed to render mermaid diagram:
'; + + const message = document.createElement('p'); + message.textContent = error?.message || String(error); + + errorElement.appendChild(message); + + const wrapper = pre.closest('.pre-wrapper'); + if (wrapper) { + wrapper.insertAdjacentElement('beforebegin', errorElement); + } }); } } diff --git a/test/system/mermaid_rendering_test.rb b/test/system/mermaid_rendering_test.rb index 1307512d5..5fa2f6e3e 100644 --- a/test/system/mermaid_rendering_test.rb +++ b/test/system/mermaid_rendering_test.rb @@ -58,20 +58,24 @@ class MermaidRenderingTest < ApplicationSystemTestCase assert_not_equal 'none', page.evaluate_script("getComputedStyle(document.querySelector('div.mermaid svg')).display") page.driver.browser.execute_cdp('Emulation.setEmulatedMedia', media: '') - # An invalid diagram renders mermaid.js's own error diagram, - # which replaces the code block just like a successful one does. + assert_selector '#header' + assert_selector '#content' + ensure + page.driver.browser.execute_cdp('Emulation.setEmulatedMedia', media: '') + end + + def test_should_show_flash_message_for_invalid_mermaid_diagram + issue = Issue.find(1) issue.update_column(:description, "```mermaid\nthis is not a valid mermaid diagram(((\n```") + + log_user('jsmith', 'jsmith') visit "/issues/#{issue.id}" within('div.description') do - assert_selector 'div.mermaid svg .error-icon' - assert_selector 'code[data-controller=mermaid]', visible: :all - assert_no_selector 'code[data-controller=mermaid]' + assert_selector '.flash.error', text: 'Failed to render mermaid diagram' + assert_selector 'pre' + assert_no_selector 'div.mermaid svg' end - assert_selector '#header' - assert_selector '#content' - ensure - page.driver.browser.execute_cdp('Emulation.setEmulatedMedia', media: '') end def test_should_render_mermaid_in_description_preview -- 2.43.0