Skip to content

Add a test project covering the conversion logic - #41

Merged
trsdn merged 5 commits into
fix/16-markdown-escapingfrom
test/23-converter-tests
Aug 28, 2026
Merged

trsdn merged 5 commits into
fix/16-markdown-escapingfrom
test/23-converter-tests

Conversation

@trsdn

@trsdn trsdn commented Aug 19, 2026

Copy link
Copy Markdown
Owner

Fixes #23

Stacked. Based on fix/16-markdown-escaping, with fix/21-task-list-checkboxes and fix/14-markdown-html-detection merged in, because the tests assert the fixed behaviour. Merge the eight fix PRs first and this diff reduces to the test project itself. Only the last commit is unique to this branch.

Why

There were no tests, which is exactly why this batch of bugs shipped in the first place:

Every one of those is a two-line unit test and effectively invisible by eye.

Restructuring

None of the conversion code touches WinUI or the clipboard, but it lived in the app project, which targets net8.0-windows10.0.26100.0 and pulls in the Windows App SDK. An ordinary test project cannot reference that.

The four converter files move unchanged to a new md2loop.Core library (net8.0, no UI dependencies). The app references it and is otherwise untouched. Git records all four as pure renames with zero content changes, so this reshuffles nothing behaviourally.

md2loop            WinUI 3 app: window, clipboard access, UI
md2loop.Core       conversion logic, no UI or clipboard dependencies
tests/md2loop.Tests unit tests for md2loop.Core

Coverage

84 tests, 3 skipped. xUnit.

Area What it covers
ClipboardContentDetectorTests mode detection, scoring, RTF/HTML sniffing, and the #14 regression where Markdown discussing HTML was misreported
HtmlToMarkdownConverterTests every element the converter handles, plus explicit regressions for #15, #16, #17 and #19
LoopHtmlConverterTests Markdown to Loop HTML, including the #21 checkbox regression and an assertion that <input> is never emitted
RoundTripTests Markdown to Loop HTML and back, which is what a user does when they paste into Loop and copy it out later

The regression tests are written against the observable symptom rather than the implementation. For example Convert_NeverEmitsCheckboxInputs asserts the absence of <input, so it will fail again if a future Markdig upgrade changes the markup, which is precisely how #21 slipped through.

Two failures worth calling out

The suite found real problems while I was writing it, rather than just confirming what I expected:

  1. Convert_RendersHeadingsAndInlineFormatting asserted <h1>Title</h1>, but UseAdvancedExtensions() enables AutoIdentifiers, so the real output is <h1 id="title">. My assertion was wrong; corrected to match on content.

  2. FencedCode_SurvivesARoundTrip failed with ```csharp becoming ```. That is not a bad assertion, it is bug Class stripping removes language-* from code blocks, losing the code language #34 reproducing: the blanket class-stripping regex removes the language-* class that carries the code language. Left in place as a passing test documenting current behaviour, with a skipped test alongside it stating the correct behaviour.

Known bugs recorded as skipped tests

Rather than delete anything that failed, the three open bugs get a skipped test each that spells out the expected result. They flip to passing when the bug is fixed:

  • #32 - no blank line after a list, so a following block is swallowed
  • #33 - whitespace between block elements leaks a leading space
  • #34 - class stripping removes the code language

CI and housekeeping

  • build.yml gains a dotnet test step, so this actually guards master instead of only running locally.
  • bin/ and obj/ added to .gitignore. They were never listed; the repo stayed clean only because files were staged individually. With three projects in the tree that is no longer safe, and staging everything would have committed ~40 build artefacts.

Verification

  • dotnet test -c Release - 84 passed, 0 failed, 3 skipped.
  • dotnet build md2loop -c Release -r win-x64 - 0 warnings, 0 errors, confirming the app still builds against the extracted library.

trsdn and others added 4 commits August 19, 2026 08:57
Task lists were broken. The Unicode checkbox substitution matched literal
strings that Markdig 0.38 does not emit - it renders
<input disabled="disabled" type="checkbox" checked="checked" />, while the
replacement looked for <input checked="" disabled="" type="checkbox" />.
Neither replacement ever fired, so raw disabled checkbox elements were
pasted into Loop instead of the checkboxes it can display.

Replace the TaskList renderer instead, so the output does not depend on
Markdig's attribute order or spacing.

TransformTaskLists is removed. It scanned for a LiteralInline starting
with "[x] ", which the TaskList extension enabled by UseAdvancedExtensions
never produces, so it had no effect.

Before: <li><input disabled="disabled" type="checkbox" checked="checked" /> done</li>
After:  <li>&#9745; done</li>

Fixes #21

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b721ad5f-9add-4f55-b9c8-7e6d7b0fb3e7
GetMarkdownScore returned 0 as soon as the text contained anything looking
like an HTML tag. Markdown may legitimately embed raw HTML, and technical
writing routinely mentions tags inside inline code or fenced blocks, so a
single occurrence vetoed every other signal.

That only mattered when the clipboard carried both text and HTML, which is
exactly what editors and browsers publish - so copying Markdown that
mentions a tag was classified as rich text, and Ctrl+Enter converted a
document that was already Markdown.

Ignore markup inside code when checking, and weigh the remainder against
the other signals instead of vetoing them. Text that really is HTML still
scores low enough to be treated as rich text.

Fixes #14

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b721ad5f-9add-4f55-b9c8-7e6d7b0fb3e7
…nd 'origin/fix/14-markdown-html-detection' into test/23-converter-tests
The conversion code had no tests at all, which is why several of the bugs
fixed in this batch shipped: task list checkboxes were emitted as raw
<input> markup, nested lists were duplicated, and inline formatting was
dropped from list items and table cells. All of those are cheap to catch
with a unit test and impossible to catch by eye.

Nothing in the converters depends on WinUI or the clipboard, but they
lived in the app project, which targets net8.0-windows and cannot be
referenced by an ordinary test project. They move to a new md2loop.Core
library so they can be tested directly; the app now references it and is
otherwise unchanged.

The suite has 84 tests across the detector, both converters, and a
round-trip class that pushes Markdown through Loop HTML and back, which
is what a user actually does when they paste into Loop and copy the
result out later.

Three tests are marked Skip rather than deleted, one per known open bug
(#32, #33, #34). They record the expected behaviour and will start
passing when those bugs are fixed.

Also adds bin/ and obj/ to .gitignore. They were never listed and the
repo only stayed clean because files were staged individually, which is
no longer safe with three projects in the tree.

Fixes #23

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b721ad5f-9add-4f55-b9c8-7e6d7b0fb3e7
Markdig records a fenced code block's language as class="language-xxx" on the
<code> element. The cleanup regex stripped every class attribute, so the only
record of the language was destroyed and a round-trip turned ```csharp back
into a bare ```.

The HTML to Markdown direction already reads the language-* class correctly, so
the fix is limited to not throwing it away: the regex now skips class attributes
whose value starts with "language-" and continues to strip everything else,
which is what Loop needs.

Un-skips the round-trip test that documented the bug. Two tests that asserted
the old behaviour are updated: one claimed no class attribute survives at all,
and the other expected the language to be lost. Non-language stripping is now
covered by a custom container, which renders <div class="warning">.

Fixes #34

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b721ad5f-9add-4f55-b9c8-7e6d7b0fb3e7
@trsdn
trsdn merged commit 3cc7c40 into fix/16-markdown-escaping Aug 28, 2026
@trsdn
trsdn deleted the test/23-converter-tests branch August 28, 2026 21:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant