//// Copyright (C) 2026 Fluxer Contributors
////
//// This file is part of Fluxer.
////
//// Fluxer is free software: you can redistribute it and/or modify
//// it under the terms of the GNU Affero General Public License as published by
//// the Free Software Foundation, either version 3 of the License, or
//// (at your option) any later version.
////
//// Fluxer is distributed in the hope that it will be useful,
//// but WITHOUT ANY WARRANTY; without even the implied warranty of
//// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//// GNU Affero General Public License for more details.
////
//// You should have received a copy of the GNU Affero General Public License
//// along with Fluxer. If not, see .
import fluxer_marketing/help_center
import fluxer_marketing/locale
import fluxer_marketing/markdown_utils
import fluxer_marketing/render_md
import gleam/string
import gleeunit
import gleeunit/should
pub fn main() {
gleeunit.main()
}
fn empty_help_data() -> help_center.HelpCenterData {
help_center.HelpCenterData(categories: [], all_articles: [])
}
fn without_anchor_style(html: String) -> String {
html
|> string.replace(
" style=\"scroll-margin-top: var(--anchor-offset, 96px)\"",
"",
)
|> string.replace(
" style=\"scroll-margin-top: var(--anchor-offset, 200px)\"",
"",
)
}
pub fn render_simple_paragraph_test() {
render_md.render(
"Hello world!",
"https://example.com",
locale.EnUS,
empty_help_data(),
)
|> should.equal("
Hello world!
")
}
pub fn render_paragraph_with_line_breaks_test() {
render_md.render(
"Line one\nLine two",
"https://example.com",
locale.EnUS,
empty_help_data(),
)
|> should.equal("Line one
Line two
")
}
pub fn render_multiple_paragraphs_test() {
render_md.render(
"First paragraph\n\nSecond paragraph",
"https://example.com",
locale.EnUS,
empty_help_data(),
)
|> should.equal("First paragraph
\n\nSecond paragraph
")
}
pub fn render_header_followed_by_paragraph_without_blank_line_test() {
render_md.render(
"## Heading\nParagraph on next line",
"https://example.com",
locale.EnUS,
empty_help_data(),
)
|> without_anchor_style
|> should.equal(
"Heading
\n\nParagraph on next line
",
)
}
pub fn render_header_followed_by_list_without_blank_line_test() {
render_md.render(
"## Table of contents\n- One\n- Two",
"https://example.com",
locale.EnUS,
empty_help_data(),
)
|> without_anchor_style
|> should.equal(
"Table of contents
\n\n",
)
}
pub fn render_setext_header_followed_by_list_without_blank_line_test() {
render_md.render(
"Heading\n-----\n- First\n- Second",
"https://example.com",
locale.EnUS,
empty_help_data(),
)
|> without_anchor_style
|> should.equal(
"Heading
\n\n",
)
}
pub fn render_h1_atx_test() {
render_md.render(
"# Heading 1",
"https://example.com",
locale.EnUS,
empty_help_data(),
)
|> should.equal(
"Heading 1
",
)
}
pub fn render_h2_atx_test() {
render_md.render(
"## Heading 2",
"https://example.com",
locale.EnUS,
empty_help_data(),
)
|> should.equal(
"Heading 2
",
)
}
pub fn render_h3_atx_test() {
render_md.render(
"### Heading 3",
"https://example.com",
locale.EnUS,
empty_help_data(),
)
|> should.equal(
"Heading 3
",
)
}
pub fn render_h6_atx_test() {
render_md.render(
"###### Heading 6",
"https://example.com",
locale.EnUS,
empty_help_data(),
)
|> should.equal(
"Heading 6
",
)
}
pub fn render_header_with_custom_id_test() {
render_md.render(
"## Heading with anchor {#custom-id}",
"https://example.com",
locale.EnUS,
empty_help_data(),
)
|> should.equal(
"Heading with anchor
",
)
}
pub fn render_arabic_header_with_custom_id_test() {
render_md.render(
"## مرحبًا بك في Fluxer {#welcome-to-fluxer}",
"https://example.com",
locale.Ar,
empty_help_data(),
)
|> should.equal(
"مرحبًا بك في Fluxer
",
)
}
pub fn render_numbered_heading_slug_test() {
render_md.render(
"## 16. Contact Us",
"https://example.com",
locale.EnUS,
empty_help_data(),
)
|> without_anchor_style
|> should.equal("")
}
pub fn render_heading_with_punctuation_slug_test() {
render_md.render(
"### 9. Disclaimers, Limitation of Liability, and Indemnification",
"https://example.com",
locale.EnUS,
empty_help_data(),
)
|> without_anchor_style
|> should.equal(
"9. Disclaimers, Limitation of Liability, and Indemnification
",
)
}
pub fn render_h1_setext_test() {
render_md.render(
"Heading 1\n=========",
"https://example.com",
locale.EnUS,
empty_help_data(),
)
|> should.equal(
"Heading 1
",
)
}
pub fn render_h2_setext_test() {
render_md.render(
"Heading 2\n---------",
"https://example.com",
locale.EnUS,
empty_help_data(),
)
|> should.equal(
"Heading 2
",
)
}
pub fn render_horizontal_rule_test() {
render_md.render("---", "https://example.com", locale.EnUS, empty_help_data())
|> should.equal("
")
}
pub fn render_unordered_list_test() {
render_md.render(
"- Item 1\n- Item 2\n- Item 3",
"https://example.com",
locale.EnUS,
empty_help_data(),
)
|> should.equal(
"\n- Item 1
\n- Item 2
\n- Item 3
\n
",
)
}
pub fn render_ordered_list_test() {
render_md.render(
"1. Item 1\n2. Item 2\n3. Item 3",
"https://example.com",
locale.EnUS,
empty_help_data(),
)
|> should.equal(
"\n- Item 1
\n- Item 2
\n- Item 3
\n
",
)
}
pub fn render_mixed_list_markers_test() {
render_md.render(
"* Item 1\n+ Item 2\n- Item 3",
"https://example.com",
locale.EnUS,
empty_help_data(),
)
|> should.equal(
"\n- Item 1
\n- Item 2
\n- Item 3
\n
",
)
}
pub fn render_list_with_continuation_test() {
render_md.render(
"- Item 1\n continuation\n- Item 2",
"https://example.com",
locale.EnUS,
empty_help_data(),
)
|> should.equal("\n- Item 1 continuation
\n- Item 2
\n
")
}
pub fn render_list_continuation_with_link_test() {
let markdown =
"- **Request a data export:** Request a full export of your account data\n See [Exporting your account data](/help/en-us/articles/1445731738851475456-exporting-your-account-data) for current limits and details."
let expected =
""
render_md.render(
markdown,
"https://example.com",
locale.EnUS,
empty_help_data(),
)
|> without_anchor_style
|> should.equal(expected)
}
pub fn render_list_continuation_with_article_shortcut_test() {
let article =
help_center.HelpArticle(
title: "Exporting your account data",
description: "Export data",
category: "account-settings",
category_title: "Account Settings",
category_icon: "gear",
order: 1,
slug: "exporting-your-account-data",
snowflake_id: "111",
content: "",
)
let help_data =
help_center.HelpCenterData(categories: [], all_articles: [article])
let markdown =
"- **Request a data export:** Request a full export of your account data\n See <% article 111 %> for current limits and details."
let expected =
""
render_md.render(markdown, "https://example.com", locale.EnUS, help_data)
|> should.equal(expected)
}
pub fn render_bold_text_test() {
render_md.render(
"This is **bold** text",
"https://example.com",
locale.EnUS,
empty_help_data(),
)
|> should.equal("This is bold text
")
}
pub fn render_link_test() {
render_md.render(
"Visit [Google](https://google.com)",
"https://example.com",
locale.EnUS,
empty_help_data(),
)
|> should.equal(
"Visit Google
",
)
}
pub fn render_link_with_relative_url_test() {
render_md.render(
"Go to [home](/)",
"https://example.com",
locale.EnUS,
empty_help_data(),
)
|> should.equal(
"Go to home
",
)
}
pub fn render_relative_link_with_trailing_slash_base_test() {
render_md.render(
"Read our [privacy policy](/privacy).",
"https://example.com/",
locale.EnUS,
empty_help_data(),
)
|> should.equal(
"Read our privacy policy.
",
)
}
pub fn render_relative_link_preserves_absolute_test() {
render_md.render(
"See [docs](https://docs.example.com/help).",
"https://example.com",
locale.EnUS,
empty_help_data(),
)
|> should.equal(
"See docs.
",
)
}
pub fn render_bold_and_link_test() {
render_md.render(
"This is **bold** and [linked](http://example.com)",
"https://example.com",
locale.EnUS,
empty_help_data(),
)
|> should.equal(
"This is bold and linked
",
)
}
pub fn render_html_escaping_test() {
render_md.render(
"This has