package main import ( "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.bigb.es/confluence-md-utilities/converter" "go.bigb.es/confluence-md-utilities/format" ) // --- normalizeForVerify --- func TestNormalizeForVerify_RemovesEmptyBrParagraph(t *testing.T) { tests := []struct { name string input string want string }{ { name: "br with space before slash", input: `


text

`, want: `

text

`, }, { name: "br without space", input: `


text

`, want: `

text

`, }, { name: "br with whitespace around", input: "

\n
\n

text

", want: "

text

", }, { name: "no empty paragraphs", input: `

hello

`, want: `

hello

`, }, { name: "multiple empty paragraphs", input: `



text

`, want: `

text

`, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { assert.Equal(t, tt.want, normalizeForVerify(tt.input)) }) } } func TestNormalizeForVerify_SpanInsideCode(t *testing.T) { tests := []struct { name string input string want string }{ { name: "span inside code unwrapped", input: `hello world`, want: `hello world`, }, { name: "span with attributes inside code", input: `a : `, want: `a : `, }, { name: "no span inside code", input: `plain`, want: `plain`, }, { name: "span outside code also stripped (decorative wrappers don't survive markdown)", input: `

text

`, want: `

text

`, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { assert.Equal(t, tt.want, normalizeForVerify(tt.input)) }) } } func TestNormalizeForVerify_AdjacentCodeMerged(t *testing.T) { tests := []struct { name string input string want string }{ { name: "directly adjacent", input: `helloworld`, want: `helloworld`, }, { name: "with whitespace between", input: `hello world`, want: `hello world`, }, { name: "single code element untouched", input: `hello`, want: `hello`, }, { name: "combined: span inside + adjacent merge", input: `plan : vclock`, want: `plan : vclock`, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { assert.Equal(t, tt.want, normalizeForVerify(tt.input)) }) } } func TestNormalizeForVerify_BareTextAfterHeading(t *testing.T) { tests := []struct { name string input string want string }{ { name: "genuine bare text is wrapped", input: `

Title

bare text
x
`, want: `

Title

bare text

x
`, }, { // Regression: \S used to match the '<' of the following tag, so the // capture swallowed and wrapped it in

...

, // splitting a code macro from its body and breaking round-trip. name: "structured-macro after heading is left intact", input: "

Heading

\n", want: "

Heading

\n", }, { name: "paragraph after heading is left intact", input: "

T

\n

already a paragraph

", want: "

T

\n

already a paragraph

", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { assert.Equal(t, tt.want, normalizeForVerify(tt.input)) }) } } // TestVerifyRoundTrip_CodeBlockAfterHeading is the end-to-end regression for the // no-language fenced code block immediately after a heading: it must survive // xml2md → md2xml with its body intact. func TestVerifyRoundTrip_CodeBlockAfterHeading(t *testing.T) { xml := "

Heading

\n" + `` + `` + `` + "\n

After.

" norm := normalizeForVerify(xml) md, err := converter.ConfluenceToMarkdown(norm) require.NoError(t, err) rt, err := converter.MarkdownToConfluence([]byte(md)) require.NoError(t, err) assert.Equal(t, format.PrettyXML(norm, " "), format.PrettyXML(normalizeForVerify(rt), " "), "code-block body must survive the round-trip") } func TestNormalizeForVerify_TaskID(t *testing.T) { // Confluence-assigned task ids are renumbered by the round-trip; verify must // ignore the value by zeroing it on both sides. in := `9complete` want := `0complete` assert.Equal(t, want, normalizeForVerify(in)) } func TestNormalizeForVerify_ListStyleAttr(t *testing.T) { // Markdown can't carry list bullet styling, so presentational attributes on //