Repository: tlienart/Franklin.jl
Pull request #546 (2020-07-08 14:23:04)

Fixing hanging ps most of the time
Over time it has been flagged multiple times that Franklin introduces `<p>` somewhat erratically #492 #236 #40 #65 

This is an attempt to rationalise that, it won't fully avoid the issue as there is an ambiguity when resolving a command, whether the command should be treated as a "block" (paragraph) or not. To help avoid this, users are invited to make use of skipped lines to clearly indicate the intent, this will help make their markdown more readable and will help Franklin figure out what they want. For instance:

```
A

\com_introducing_a_block

B
```

another recommendation is to avoid commands that mix "inline" and "block" mode, if you do that the paragraphs may not be closed properly as it's ambiguous.

waiting for @Wikunia 's feedback to ensure there's no big issue


Patch:
diff --git a/src/Franklin.jl b/src/Franklin.jl
index 0e51c651..3525897e 100644
--- a/src/Franklin.jl
+++ b/src/Franklin.jl
@@ -113,7 +113,7 @@ function logger(t)
     LOGGING[] || return nothing
     print(Crayon(bold=true, foreground=:yellow), "LOG: ")
     print(Crayon(bold=false, foreground=:blue), rpad(t[1], 20))
-    println(Crayon(reset=true), t[2])
+    println(Crayon(reset=true), escape_string(t[2]))
     return nothing
 end
 
diff --git a/src/converter/html/html.jl b/src/converter/html/html.jl
index 0ad87eac..c1ec5f61 100644
--- a/src/converter/html/html.jl
+++ b/src/converter/html/html.jl
@@ -5,6 +5,7 @@ blocks).
 function convert_html(hs::AS; isoptim::Bool=false)::String
     isempty(hs) && return hs
 
+    (:convert_html, "input hs: '$hs'") |> logger
     (:convert_html, "hasmath/code: $(locvar.((:hasmath, :hascode)))") |> logger
 
     # Tokenize
@@ -20,23 +21,21 @@ function convert_html(hs::AS; isoptim::Bool=false)::String
 
     fhs = process_html_qblocks(hs, qblocks)
 
+    (:convert_html, "fhs (pre fix): '$fhs'") |> logger
+
     # See issue #204, basically not all markdown links are processed  as
     # per common mark with the JuliaMarkdown, so this is a patch that kind
     # of does
     if locvar("reflinks")
         fhs = find_and_fix_md_links(fhs)
     end
-    # if it ends with </p>\n but doesn't start with <p>, chop it off
-    # this may happen if the first element parsed is an ocblock (not text)
-    δ = ifelse(endswith(fhs, "</p>\n") && !startswith(fhs, "<p>"), 5, 0)
-
     isempty(fhs) && return ""
 
     if !isempty(globvar("prepath")) && isoptim
         fhs = fix_links(fhs)
     end
 
-    return String(chop(fhs, tail=δ))
+    return String(fhs)
 end
 
 
diff --git a/src/converter/html/link_fixer.jl b/src/converter/html/link_fixer.jl
index 443b762c..46ea9a62 100644
--- a/src/converter/html/link_fixer.jl
+++ b/src/converter/html/link_fixer.jl
@@ -8,7 +8,7 @@ Direct inline-style links are properly processed by Julia's Markdown processor b
 * (we don't either) `[link title](https://www.google.com "Google's Homepage")`
 """
 function find_and_fix_md_links(hs::String)::String
-    # 1. find all occurences of -- [...]: link
+    # 1. find all occurences of things that look like links
     m_link_refs = collect(eachmatch(ESC_LINK_PAT, hs))
 
     # recuperate the appropriate id which has a chance to match def_names
@@ -16,10 +16,14 @@ function find_and_fix_md_links(hs::String)::String
         # no second bracket or empty second bracket ?
         # >> true then the id is in the first bracket   A --> [id] or [id][]
         # >> false then the id is in the second bracket B --> [...][id]
-        ifelse(isnothing(ref.captures[3]) || isempty(ref.captures[3]),
-                    ref.captures[2],    # A. first bracket
-                    ref.captures[3])    # B. second bracket
-                    for ref in m_link_refs]
+        ifelse(isnothing(ref.captures[3]) ||
+               isempty(ref.captures[3]),
+                         ref.captures[2], # A. first bracket
+                         ref.captures[3]) # B. second bracket
+                #
+                for ref in m_link_refs]
+
+    isempty(ref_names) || (ref_names = strip.(ref_names))
 
     # reconstruct the text
     h = IOBuffer()
diff --git a/src/converter/latex/io.jl b/src/converter/latex/io.jl
index 5634f0bb..eed48fe2 100644
--- a/src/converter/latex/io.jl
+++ b/src/converter/latex/io.jl
@@ -225,7 +225,7 @@ function lx_literate(lxc::LxCom, lxd::Vector{LxDef})
         set_var!(LOCAL_VARS, "reeval", true)
     end
     # then reprocess
-    return reprocess(read(opath, String), lxd)
+    return reprocess(read(opath, String), lxd, nostripp=true)
 end
 
 #
diff --git a/src/converter/latex/latex.jl b/src/converter/latex/latex.jl
index f37d154a..f12a2e88 100644
--- a/src/converter/latex/latex.jl
+++ b/src/converter/latex/latex.jl
@@ -51,8 +51,9 @@ function resolve_lxcom(lxc::LxCom, lxdefs::Vector{LxDef};
 end
 
 """Convenience function to take a string and re-parse it."""
-function reprocess(s::AS, lxdefs::Vector{LxDef})
+function reprocess(s::AS, lxdefs::Vector{LxDef}; nostripp=false)
     r = convert_md(s, lxdefs;
-                   isrecursive=true, isconfig=false, has_mddefs=false)
+                   isrecursive=true, isconfig=false, has_mddefs=false,
+                   nostripp=nostripp)
     return r
 end
diff --git a/src/converter/markdown/md.jl b/src/converter/markdown/md.jl
index 7edb6e9b..7c197d6e 100644
--- a/src/converter/markdown/md.jl
+++ b/src/converter/markdown/md.jl
@@ -27,6 +27,7 @@ function convert_md(mds::AbstractString,
                     isconfig::Bool=false,
                     has_mddefs::Bool=true,
                     pagevar::Bool=false, # whether it's called from pagevar
+                    nostripp::Bool=false
                     )::String
     # instantiate page dictionaries
     isrecursive || isinternal || set_page_env()
@@ -41,6 +42,7 @@ function convert_md(mds::AbstractString,
     # ------------------------------------------------------------------------
     #> 1. Tokenize
     tokens = find_tokens(mds, MD_TOKENS, MD_1C_TOKENS)
+    (:convert_md, "convert_md: '$([t.name for t in tokens])'") |> logger
     # distinguish fnref/fndef
     validate_footnotes!(tokens)
     # ignore header tokens that are not at the start of a line
@@ -144,11 +146,16 @@ function convert_md(mds::AbstractString,
 
     #> 2. Form intermediate markdown + html
     inter_md, mblocks = form_inter_md(mds, b2insert, lxdefs)
-    inter_html = md2html(inter_md; stripp=isrecursive)
+    inter_html = md2html(inter_md; stripp=isrecursive && !nostripp)
+
+    (:convert_md, "inter_md: '$inter_md'") |> logger
+    (:convert_md, "inter_html: '$inter_html'") |> logger
 
     #> 3. Plug resolved blocks in partial html to form the final html
     hstring = convert_inter_html(inter_html, mblocks, lxdefs)
 
+    (:convert_md, "hstring: '$hstring'") |> logger
+
     # final var adjustment, infer title if not given
     if isnothing(locvar("title")) && !isempty(PAGE_HEADERS)
         title = first(values(PAGE_HEADERS))[1]
@@ -229,13 +236,20 @@ end
 INSERT
 
 String that is plugged as a placeholder of blocks that need further processing.
-The spaces allow to handle overzealous inclusion of `<p>...</p>` from the base
-Markdown to HTML conversion.
+Note: left space in the pattern is to preserve lists.
+"""
+const INSERT     = " ##FDINSERT##"
+const INSERT_PAT = Regex("((?<!<li>)<p>)?(\\s*)$(strip(INSERT))(</p>)?")
+
+
+"""
+CLOSE_INSERT
+
+String that is plugged as a placeholder of blocks that need further processing in a place
+where any open paragraph must be closed first. For instance this will be the replacement
+for a header.
 """
-const INSERT     = " ##FDINSERT## "
-const INSERT_    = strip(INSERT)
-const INSERT_PAT = Regex(INSERT_)
-const INSERT_LEN = length(INSERT_)
+const CLOSEP_INSERT = "\n\n##FDINSERT##\n\n"
 
 
 """
@@ -287,12 +301,8 @@ function form_inter_md(mds::AS, blocks::Vector{<:AbstractBlock},
             if isa(β, OCBlock) && β.name ∈ MD_OCB_IGNORE
                 head = nextind(mds, to(β))
             else
-                if isa(β, OCBlock) && β.name ∈ MD_HEADER
-                    # this is a trick to allow whatever follows the title to be
-                    # properly parsed by Markdown.parse; it could otherwise
-                    # cause issues for instance if a table starts immediately
-                    # after the title
-                    write(intermd, INSERT * "\n ")
+                if isa(β, OCBlock) && β.name ∈ MD_CLOSEP
+                    write(intermd, CLOSEP_INSERT)
                 else
                     write(intermd, INSERT)
                 end
@@ -322,66 +332,47 @@ end
 """
 $(SIGNATURES)
 
-Take a partial markdown string with the `INSERT` marker and plug in the
-appropriately processed block.
+Take a partial markdown string with the `INSERT` markers and
+plug in the appropriately processed block.
 
 **Arguments**
 
-* `ihtml`:  the intermediary html string (with `INSERT`)
+* `ihtml`:  the intermediary html string (with `INSERT` markers)
 * `blocks`: vector of blocks
 * `lxdefs`: latex context
 """
 function convert_inter_html(ihtml::AS,
                             blocks::Vector{<:AbstractBlock},
                             lxdefs::Vector{LxDef})::String
+
+    (:convert_inter_html, "ihtml: '$ihtml'") |> logger
+
     # Find the INSERT indicators
     allmatches = collect(eachmatch(INSERT_PAT, ihtml))
-    strlen = lastindex(ihtml)
+    isempty(allmatches) && return ihtml
 
+    strlen = lastindex(ihtml)
     # write the pieces of the final html in order, gradually processing the
     # blocks to insert
     htmls = IOBuffer()
     head  = 1
     for (i, m) ∈ enumerate(allmatches)
-        # two cases can happen based on whitespaces around an insertion that
-        # we want to get rid of, potentially both happen simultaneously.
-        # 1. <p>##FDINSERT##...
-        # 2. ...##FDINSERT##</p>
-        # exceptions,
-        # - list items introduce <li><p> and </p>\n</li> which shouldn't remove
-        # - end of doc introduces </p>(\n?) which should not be removed
-        δ1, δ2 = 0, 0 # keep track of the offset at the front / back
-        # => case 1
-        c10 = prevind(ihtml, m.offset, 7) # *li><p>
-        c1a = prevind(ihtml, m.offset, 3) # *p>
-        c1b = prevind(ihtml, m.offset)    # <p*
-
-        hasli1 = (c10 > 0) && ihtml[c10:c1b] == "<li><p>"
-        !(hasli1) && (c1a > 0) && ihtml[c1a:c1b] == "<p>" && (δ1 = 3)
-
-        # => case 2
-        iend = m.offset + INSERT_LEN
-        c2a  = nextind(ihtml, iend)
-        c2b  = nextind(ihtml, iend, 4)  # </p*
-        c20  = nextind(ihtml, iend, 10) # </p>\n</li*
-
-        hasli2 = (c20 ≤ strlen) && ihtml[c2a:c20] == "</p>\n</li>"
-        !(hasli2) && (c2b ≤ strlen - 4) && ihtml[c2a:c2b] == "</p>" && (δ2 = 4)
-
-        # write whatever is at the front, skip the extra space if still present
-        prev = prevind(ihtml, m.offset - δ1)
-        if prev > 0 && ihtml[prev] == ' '
-            prev = prevind(ihtml, prev)
-        end
-        (head ≤ prev) && write(htmls, subs(ihtml, head:prev))
-        # move head appropriately
-        head = iend + δ2
-        if head ≤ strlen
-            head = ifelse(ihtml[head] in (' ', '>'), nextind(ihtml, head), head)
+        # check whether there's <p> or </p> around the insert
+        leftp  = !isnothing(m.captures[1])
+        lefts  = ifelse(length(m.captures[2])>1, " ", "")
+        rightp = !isnothing(m.captures[3])
+        prev   = prevind(ihtml, m.offset)
+        write(htmls, subs(ihtml, head:prev))
+        if leftp && !rightp
+            write(htmls, "<p>")
         end
-        # store the resolved block
+        write(htmls, lefts)
         resolved = convert_block(blocks[i], lxdefs)
         write(htmls, resolved)
+        if rightp && !leftp
+            write(htmls, "</p>")
+        end
+        head = nextind(ihtml, m.offset, length(m.match))
     end
     # store whatever is after the last INSERT if anything
     (head ≤ strlen) && write(htmls, subs(ihtml, head:strlen))
diff --git a/src/parser/markdown/tokens.jl b/src/parser/markdown/tokens.jl
index af811914..044abd75 100644
--- a/src/parser/markdown/tokens.jl
+++ b/src/parser/markdown/tokens.jl
@@ -221,7 +221,6 @@ Combination of all `MD_OCB` in order.
 """
 const MD_OCB_ALL = vcat(MD_OCB, MD_OCB2, MD_OCB_MATH) # order matters
 
-
 """
     MD_OCB_IGNORE
 
@@ -229,13 +228,19 @@ List of names of blocks that will need to be dropped at compile time.
 """
 const MD_OCB_IGNORE = (:COMMENT, :MD_DEF)
 
+"""
+    MATH_DISPLAY_BLOCKS_NAMES
+
+List of names of maths environments (display mode).
+"""
+const MATH_DISPLAY_BLOCKS_NAMES = collect(e.name for e ∈ MD_OCB_MATH if e.name != :MATH_A)
 
 """
     MATH_BLOCKS_NAMES
 
-List of names of maths environments.
+List of names of all maths environments.
 """
-const MATH_BLOCKS_NAMES = [e.name for e ∈ MD_OCB_MATH]
+const MATH_BLOCKS_NAMES = tuple(:MATH_A, MATH_DISPLAY_BLOCKS_NAMES...)
 
 """
 CODE_BLOCKS_NAMES
@@ -244,6 +249,14 @@ List of names of code blocks environments.
 """
 const CODE_BLOCKS_NAMES = (:CODE_BLOCK_LANG, :CODE_BLOCK, :CODE_BLOCK_IND)
 
+"""
+    MD_CLOSEP
+
+Blocks which, upon insertion, should close any open paragraph.
+Order doesn't matter.
+"""
+const MD_CLOSEP = [MD_HEADER..., :DIV, CODE_BLOCKS_NAMES..., MATH_DISPLAY_BLOCKS_NAMES...]
+
 """
     MD_OCB_NO_INNER
 
diff --git a/src/parser/markdown/validate.jl b/src/parser/markdown/validate.jl
index a41f81ee..858365f8 100644
--- a/src/parser/markdown/validate.jl
+++ b/src/parser/markdown/validate.jl
@@ -89,8 +89,7 @@ function validate_and_store_link_defs!(blocks::Vector{OCBlock})::Nothing
                 id = subs(parent, nextind(parent, k), ini)
                 # issue #266 in case there's formatting in the link
                 id = fd2html(id, internal=true)
-                id = replace(id, r"^<p>"=>"")
-                id = replace(id, r"<\/p>\n$"=>"")
+                id = replace(id, r"^\s*(?:<p>)?(.*?)(?:<\/p>)?\s*$" => s"\1")
                 lk = β |> content |> strip |> string
                 PAGE_LINK_DEFS[id] = lk
                 # replace the block by a full one so that it can be fully
diff --git a/src/parser/tokens.jl b/src/parser/tokens.jl
index c304031d..d7f9294c 100644
--- a/src/parser/tokens.jl
+++ b/src/parser/tokens.jl
@@ -148,8 +148,12 @@ SPACE_CHARS
 
 Convenience list of characters that would correspond to a `\\s` regex. see also
 https://github.com/JuliaLang/julia/blob/master/base/strings/unicode.jl.
+To this are added things that would definitely switch token:
+* EOS (end of string)
+* '}' (if in command)
 """
-const SPACE_CHAR = (' ', '\n', '\t', '\v', '\f', '\r', '\u85', '\ua0', EOS)
+const SPACE_CHAR = (' ', '\n', '\t', '\v', '\f', '\r', '\u85', '\ua0',
+                    EOS, '}')
 
 """
 SPACER


Test patch:
diff --git a/test/converter/lx/input.jl b/test/converter/lx/input.jl
index 479c7f42..6640d9da 100644
--- a/test/converter/lx/input.jl
+++ b/test/converter/lx/input.jl
@@ -89,9 +89,16 @@ end
         """ |> fd2html_td
     @test isapproxstr(s, """
         <p>AA
-        <p><span style="color:red;">// Couldn't find a file when trying to resolve an input request with relative path: `foo/baz`. //</span></p>
-        <p><span style="color:red;">// Couldn't find an output directory associated with 'foo/baz' when trying to input a plot. //</span></p>
-        <p><span style="color:red;">// Couldn't find a file when trying to resolve an input request with relative path: `foo/bar`. //</span></p></p>
+          <p>
+            <span style="color:red;">// Couldn't find a file when trying to resolve an input request with relative path: `foo/baz`. //</span>
+          </p>
+          <p>
+            <span style="color:red;">// Couldn't find an output directory associated with 'foo/baz' when trying to input a plot. //</span>
+          </p>
+          <p>
+            <span style="color:red;">// Couldn't find a file when trying to resolve an input request with relative path: `foo/bar`. //</span>
+          </p>
+        </p>
         """)
 
     fs2()
@@ -104,6 +111,6 @@ end
         \tableinput{}{./foo.csv}
         """ |> fd2html_td
     @test isapproxstr(s, """
-        <p><span style="color:red;">// Table matching '/assets/index/foo.csv' not found. //</span></p></p>
+        <p><span style="color:red;">// Table matching '/assets/index/foo.csv' not found. //</span></p>
         """)
 end
diff --git a/test/converter/md/hyperref.jl b/test/converter/md/hyperref.jl
index ba107a3b..e6c1daad 100644
--- a/test/converter/md/hyperref.jl
+++ b/test/converter/md/hyperref.jl
@@ -34,17 +34,29 @@
     @test isapproxstr(h, """
         <p>
           Some string
-          <a id="$h1" class=\"anchor\"></a>\\[ x = x \\]
-          then as per <span class="bibref"><a href="#$h2">Amari and Douglas., 1998</a></span>  also this <span class="bibref">(<a href="#$h3">Bardenet et al., 2017</a>)</span>  and
-          <span class="bibref"><a href="#$h2">Amari and Douglas., 1998</a>, <a href="#$h3">Bardenet et al., 2017</a></span>
-          Reference to equation: <span class="eqref">(<a href="#$h1">1</a>)</span> .
+        </p>
+        <a id="$h1" class=\"anchor\"></a>
+        \\[ x = x \\]
+        <p>
+          then as per
+            <span class="bibref"><a href="#$h2">Amari and Douglas., 1998</a></span>
+          also this
+            <span class="bibref">(<a href="#$h3">Bardenet et al., 2017</a>)</span>
+          and
+            <span class="bibref"><a href="#$h2">Amari and Douglas., 1998</a>, <a href="#$h3">Bardenet et al., 2017</a></span>
+          Reference to equation:
+            <span class="eqref">(<a href="#$h1">1</a>)</span>.
         </p>
         <p>
           Then maybe some text etc.
         </p>
         <ul>
-          <li><p><a id="$h2" class=\"anchor\"></a>  <strong>Amari</strong> and <strong>Douglas</strong>: <em>Why Natural Gradient</em>, 1998.</p></li>
-          <li><p><a id="$h3" class=\"anchor\"></a>  <strong>Bardenet</strong>, <strong>Doucet</strong> and <strong>Holmes</strong>: <em>On Markov Chain Monte Carlo Methods for Tall Data</em>, 2017.</p></li>
+          <li><p><a id="$h2" class=\"anchor\"></a>
+            <strong>Amari</strong> and <strong>Douglas</strong>: <em>Why Natural Gradient</em>, 1998.</p>
+          </li>
+          <li><p><a id="$h3" class=\"anchor\"></a>
+            <strong>Bardenet</strong>, <strong>Doucet</strong> and <strong>Holmes</strong>: <em>On Markov Chain Monte Carlo Methods for Tall Data</em>, 2017.</p>
+          </li>
         </ul>
         """)
 end
diff --git a/test/converter/md/markdown.jl b/test/converter/md/markdown.jl
index 5ef16a50..8b66e471 100644
--- a/test/converter/md/markdown.jl
+++ b/test/converter/md/markdown.jl
@@ -21,9 +21,23 @@
     b2insert, = steps[:b2insert]
 
     inter_md, mblocks = F.form_inter_md(st, b2insert, lxdefs)
-    @test inter_md == "\n\nA list\n*  ##FDINSERT##  and  ##FDINSERT## \n*  ##FDINSERT##  is a function\n* a last element\n"
+    @test inter_md // """
+            A list
+            *  ##FDINSERT## and  ##FDINSERT##
+            *  ##FDINSERT## is a function
+            * a last element"""
     inter_html = F.md2html(inter_md)
-    @test inter_html == "<p>A list</p>\n<ul>\n<li><p>##FDINSERT##  and  ##FDINSERT## </p>\n</li>\n<li><p>##FDINSERT##  is a function</p>\n</li>\n<li><p>a last element</p>\n</li>\n</ul>\n"
+    @test inter_html // """
+        <p>A list</p>
+        <ul>
+        <li><p>##FDINSERT## and  ##FDINSERT##</p>
+        </li>
+        <li><p>##FDINSERT## is a function</p>
+        </li>
+        <li><p>a last element</p>
+        </li>
+        </ul>
+        """
 end
 
 
@@ -42,7 +56,12 @@ end
         """
 
     inter_md, = explore_md_steps(st)[:inter_md]
-    @test inter_md == " ##FDINSERT## \nfinally ⊙⊙𝛴⊙ and\n ##FDINSERT## \ndone\n"
+    @test inter_md // """
+                     ##FDINSERT##
+                    finally ⊙⊙𝛴⊙ and
+                     ##FDINSERT##
+                    done
+                    """
 end
 
 
@@ -56,7 +75,14 @@ end
     lxdefs, tokens, braces, blocks, lxcoms = steps[:latex]
     b2insert, = steps[:b2insert]
     inter_md, mblocks = steps[:inter_md]
-    @test inter_md == "ab ##FDINSERT## \n ##FDINSERT## \n"
+    @test inter_md // """
+                ab
+
+                ##FDINSERT##
+
+
+                 ##FDINSERT##
+                """
 
     inter_html, = steps[:inter_html]
 
@@ -64,12 +90,9 @@ end
     @test isapproxstr(F.convert_block(b2insert[2], lxdefs), "\\[\\begin{array}{c} \\sin^2(x)+\\cos^2(x) &=& 1\\end{array}\\]")
     hstring = F.convert_inter_html(inter_html, b2insert, lxdefs)
     @test isapproxstr(hstring, raw"""
-                        <p>
-                          ab<div class="d">.</div>
-                          \[\begin{array}{c}
-                            \sin^2(x)+\cos^2(x) &=& 1
-                          \end{array}\]
-                        </p>""")
+                    <p>ab</p>
+                    <div class="d">.</div>
+                    \[\begin{array}{c} \sin^2(x)+\cos^2(x) &=& 1\end{array}\]""")
 end
 
 
@@ -90,19 +113,18 @@ end
     inter_html, = steps[:inter_html]
 
     @test isapproxstr(inter_md, """
-                                text A1 text A2  ##FDINSERT##  and
-                                ##FDINSERT##
-                                text C1  ##FDINSERT##  text C2
-                                then  ##FDINSERT## .""")
+            text A1 text A2  ##FDINSERT## and
+             ##FDINSERT##
+             text C1  ##FDINSERT## text C2
+             then  ##FDINSERT##.""")
 
-    @test isapproxstr(inter_html, """<p>text A1 text A2  ##FDINSERT##  and  ##FDINSERT##   text C1  ##FDINSERT##  text C2  then  ##FDINSERT## .</p>""")
+    @test isapproxstr(inter_html, """<p>text A1 text A2  ##FDINSERT## and  ##FDINSERT##  text C1  ##FDINSERT## text C2  then  ##FDINSERT##.</p>""")
 
     hstring = F.convert_inter_html(inter_html, b2insert, lxdefs)
-    @test isapproxstr(hstring, """
-                                <p>text A1 text A2 blah and
-                                escape B1
-                                text C1 \\(\\mathrm{ b}\\) text C2
-                                then part1: AA and part2: BB.</p>""")
+    @test isapproxstr(hstring, raw"""
+                    <p>text A1 text A2 blah and
+                    escape B1
+                    text C1 \(\mathrm{ b}\) text C2  then part1: AA and part2: BB.</p>""")
 end
 
 
@@ -114,10 +136,9 @@ end
         ## Subtitle cool!
         done
         """ |> seval
-    @test isapproxstr(h, """
-                        <h1 id="title"><a href="#title">Title</a></h1>
-                        and then
-                        <h2 id="subtitle_cool"><a href="#subtitle_cool">Subtitle cool&#33;</a></h2>
-                        done
-                        """)
+    @test h // """
+        <h1 id="title"><a href="#title">Title</a></h1>
+        <p>and then</p>
+        <h2 id="subtitle_cool"><a href="#subtitle_cool">Subtitle cool&#33;</a></h2>
+        <p>done</p>"""
 end
diff --git a/test/converter/md/markdown2.jl b/test/converter/md/markdown2.jl
index 0463b08a..0b4ad580 100644
--- a/test/converter/md/markdown2.jl
+++ b/test/converter/md/markdown2.jl
@@ -8,23 +8,23 @@ end
 @testset "issue163" begin
     st = raw"""A _B `C` D_ E"""
     imd, ih = inter(st)
-    @test imd == "A _B  ##FDINSERT##  D_ E"
-    @test ih == "<p>A <em>B  ##FDINSERT##  D</em> E</p>\n"
+    @test imd == "A _B  ##FDINSERT## D_ E"
+    @test isapproxstr(ih, "<p>A <em>B  ##FDINSERT##  D</em> E</p>")
 
     st = raw"""A _`B` C D_ E"""
     imd, ih = inter(st)
-    @test imd == "A _ ##FDINSERT##  C D_ E"
-    @test ih == "<p>A <em>##FDINSERT##  C D</em> E</p>\n"
+    @test isapproxstr(imd, "A _ ##FDINSERT##  C D_ E")
+    @test isapproxstr(ih, "<p>A <em>##FDINSERT##  C D</em> E</p>")
 
     st = raw"""A _B C `D`_ E"""
     imd, ih = inter(st)
-    @test imd == "A _B C  ##FDINSERT## _ E"
-    @test ih == "<p>A <em>B C  ##FDINSERT##</em> E</p>\n"
+    @test isapproxstr(imd, "A _B C  ##FDINSERT## _ E")
+    @test isapproxstr(ih, "<p>A <em>B C  ##FDINSERT##</em> E</p>")
 
     st = raw"""A _`B` C `D`_ E"""
     imd, ih = inter(st)
-    @test imd == "A _ ##FDINSERT##  C  ##FDINSERT## _ E"
-    @test ih == "<p>A <em>##FDINSERT##  C  ##FDINSERT##</em> E</p>\n"
+    @test isapproxstr(imd, "A _ ##FDINSERT##  C  ##FDINSERT## _ E")
+    @test isapproxstr(ih, "<p>A <em>##FDINSERT##  C  ##FDINSERT##</em> E</p>")
 end
 
 
@@ -45,17 +45,28 @@ end
             <li>
               <a href="#hello_fd">Hello <code>fd</code></a>
               <ol>
-                <li><ol><li><a href="#weirdly_nested">weirdly nested</a></li></ol></li>
-                <li><a href="#goodbye">Goodbye&#33;</a></li>
-              </ol>
-            </li>
-            <li><a href="#done">Done</a></li>
-          </ol>
-        </div>
-        <h2 id="hello_fd"><a href="#hello_fd">Hello <code>fd</code></a></h2>
-        <h4 id="weirdly_nested"><a href="#weirdly_nested">weirdly nested</a></h4>
-        <h3 id="goodbye"><a href="#goodbye">Goodbye&#33;</a></h3>
-        <h2 id="done"><a href="#done">Done</a></h2>done.
+                <li>
+                  <ol>
+                    <li>
+                      <a href="#weirdly_nested">weirdly nested</a>
+                    </li>
+                  </ol>
+                </li>
+              <li>
+                <a href="#goodbye">Goodbye&#33;</a>
+              </li>
+            </ol>
+          </li>
+          <li>
+            <a href="#done">Done</a>
+          </li>
+        </ol>
+      </div>
+      <h2 id="hello_fd"><a href="#hello_fd">Hello <code>fd</code></a></h2>
+      <h4 id="weirdly_nested"><a href="#weirdly_nested">weirdly nested</a></h4>
+      <h3 id="goodbye"><a href="#goodbye">Goodbye&#33;</a></h3>
+      <h2 id="done"><a href="#done">Done</a></h2>
+      <p>done.</p>
         """)
 end
 
@@ -94,7 +105,8 @@ end
         <h4 id="c"><a href="#c">C</a></h4>
         <h3 id="d"><a href="#d">D</a></h3>
         <h2 id="e"><a href="#e">E</a></h2>
-        <h3 id="f"><a href="#f">F</a></h3> done.
+        <h3 id="f"><a href="#f">F</a></h3>
+        <p>done.</p>
         """)
 end
 
@@ -125,7 +137,8 @@ end
         <h2 id="hello_fd"><a href="#hello_fd">Hello <code>fd</code></a></h2>
         <h4 id="weirdly_nested"><a href="#weirdly_nested">weirdly nested</a></h4>
         <h3 id="goodbye"><a href="#goodbye">Goodbye&#33;</a></h3>
-        <h2 id="done"><a href="#done">Done</a></h2>done.
+        <h2 id="done"><a href="#done">Done</a></h2>
+        <p>done.</p>
         """)
 end
 
@@ -164,6 +177,7 @@ end
         <h4 id="c"><a href="#c">C</a></h4>
         <h3 id="d"><a href="#d">D</a></h3>
         <h2 id="e"><a href="#e">E</a></h2>
-        <h3 id="f"><a href="#f">F</a></h3> done.
+        <h3 id="f"><a href="#f">F</a></h3>
+        <p>done.</p>
         """)
 end
diff --git a/test/converter/md/markdown3.jl b/test/converter/md/markdown3.jl
index 0651b61c..137ceb89 100644
--- a/test/converter/md/markdown3.jl
+++ b/test/converter/md/markdown3.jl
@@ -5,9 +5,11 @@
     st = raw"""
         Hello \ blah \ end
         and `B \ c` end and
+
         ```
         A \ b
         ```
+
         done
         """
 
@@ -32,13 +34,12 @@
 
     inter_html, = steps[:inter_html]
 
-    @test isapproxstr(inter_html, "<p>Hello  ##FDINSERT##  blah  ##FDINSERT##  end and  ##FDINSERT##  end and  ##FDINSERT##  done</p>")
+    @test isapproxstr(inter_html, "<p>Hello  ##FDINSERT##  blah  ##FDINSERT##  end and  ##FDINSERT##  end and </p><p> ##FDINSERT##</p> <p>done</p>")
 
     @test isapproxstr(st |> seval, raw"""
-                <p>Hello &#92; blah &#92; end
-                and <code>B \ c</code> end and
+                <p>Hello &#92; blah &#92; end and <code>B \ c</code> end and</p>
                 <pre><code class="language-julia">A \ b</code></pre>
-                done</p>
+                <p>done</p>
                 """)
 end
 
@@ -46,9 +47,11 @@ end
     st = raw"""
         Hello \ blah \ end
         and `B \ c` end \\ and
+
         ```
         A \ b
         ```
+
         done
         """
     steps = explore_md_steps(st)
@@ -57,9 +60,9 @@ end
     h = st |> seval
     @test isapproxstr(st |> seval, raw"""
                         <p>Hello &#92; blah &#92; end
-                        and <code>B \ c</code> end <br/> and
+                        and <code>B \ c</code> end <br/> and</p>
                         <pre><code class="language-julia">A \ b</code></pre>
-                        done</p>
+                        <p>done</p>
                         """)
 end
 
@@ -237,16 +240,14 @@ end
         end
         """
     @test isapproxstr(st |> seval, raw"""
-        <p>
-            A
+        <p>A</p>
             <pre><code class="language-julia">a = 1+1
             if a > 1
                 @show a
             end
             b = 2
             @show a+b</code></pre>
-            end
-        </p>""")
+        <p>end</p>""")
 
     st = raw"""
         @def indented_code = true
@@ -260,62 +261,63 @@ end
         end
         """
     @test isapproxstr(st |> seval, raw"""
-                        <p>
-                        A <code>single</code> and
+                        <p>A <code>single</code> and </p>
                         <pre><code class="language-python">blah</code></pre>
-                        and<pre><code class="language-julia">a = 1+1</code></pre>
-                        then
-                        </p>
+                        <p>and</p>
+                        <pre><code class="language-julia">a = 1+1</code></pre>
+                        <p>then</p>
+                        <ul>
+                        <li><p>blah</p>
                         <ul>
-                          <li><p>blah</p>
-                            <ul>
-                              <li><p>blih</p></li>
-                              <li><p>bloh</p></li>
-                            </ul>
-                          </li>
+                        <li><p>blih</p>
+                        </li>
+                        <li><p>bloh</p>
+                        </li>
+                        </ul>
+                        </li>
                         </ul>
                         <p>end</p>
                         """)
 
-    st = raw"""
-        @def indented_code = true
-        A
-
-            function foo()
-
-                return 2
-
-            end
-
-            function bar()
-                return 3
-            end
-
-        B
-
-            function baz()
-                return 5
-
-            end
-
-        C
-        """
-    isapproxstr(st |> seval, raw"""
-                            <p>A <pre><code class="language-julia">function foo()
-
-                                return 2
-
-                            end
-
-                            function bar()
-                                return 3
-                            end</code></pre>
-                            B <pre><code class="language-julia">function baz()
-                                return 5
-
-                            end</code></pre>
-                            C</p>
-                            """)
+    # st = raw"""
+    #     @def indented_code = true
+    #     A
+    #
+    #         function foo()
+    #
+    #             return 2
+    #
+    #         end
+    #
+    #         function bar()
+    #             return 3
+    #         end
+    #
+    #     B
+    #
+    #         function baz()
+    #             return 5
+    #
+    #         end
+    #
+    #     C
+    #     """
+    # isapproxstr(st |> seval, raw"""
+    #                         <p>A</p> <pre><code class="language-julia">function foo()
+    #
+    #                             return 2
+    #
+    #                         end
+    #
+    #                         function bar()
+    #                             return 3
+    #                         end</code></pre>
+    #                         B <pre><code class="language-julia">function baz()
+    #                             return 5
+    #
+    #                         end</code></pre>
+    #                         C</p>
+    #                         """)
 end
 
 @testset "More ``" begin
@@ -345,9 +347,9 @@ end
        end
        """
     @test isapproxstr(s |> fd2html_td, """
-        <p>blah
+        <p>blah</p>
         <pre><code class=\"language-TOML\">socrates</code></pre>
-    end</p>""")
+        <p>end</p>""")
     s = raw"""
        blah
        ```julia-repl
@@ -356,7 +358,7 @@ end
        end
        """
     @test isapproxstr(s |> fd2html_td, """
-        <p>blah
+        <p>blah</p>
         <pre><code class=\"language-julia-repl\">socrates</code></pre>
-    end</p>""")
+        <p>end</p>""")
 end
diff --git a/test/converter/md/markdown4.jl b/test/converter/md/markdown4.jl
index 0def50cc..271f5d99 100644
--- a/test/converter/md/markdown4.jl
+++ b/test/converter/md/markdown4.jl
@@ -19,22 +19,31 @@
 
     s = raw"""
     \newcommand{\eqa}[1]{\begin{eqnarray}#1\end{eqnarray}}
+
     \eqa{A\\
         D
-    }E
+    }
+
+    E
     """ |> fd2html_td
     @test isapproxstr(s, raw"""
         \[\begin{array}{c} A\\
-        D\end{array}\]E
+        D\end{array}\]
+        <p>E</p>
         """)
+
     s = raw"""
     @def indented_code = false
     \newcommand{\eqa}[1]{\begin{eqnarray}#1\end{eqnarray}}
+
     \eqa{A\\
-        D}E""" |> fd2html_td
+        D}
+
+    E""" |> fd2html_td
     @test isapproxstr(s, raw"""
         \[\begin{array}{c} A\\
-        D\end{array}\]E
+        D\end{array}\]
+        <p>E</p>
            """)
 end
 
@@ -56,19 +65,22 @@ end
     D
     ```
     """ |> fd2html_td
-    @test isapproxstr(s, """<p>A <pre><code class="language-julia">C\n    B\n        E\nD</code></pre></p>\n""")
+    @test isapproxstr(s, """<p>A</p> <pre><code class="language-julia">C\n    B\n        E\nD</code></pre>""")
 end
 
 @testset "auto html esc" begin
     s = raw"""
     Blah
+
     ```html
     <div class="foo">Blah</div>
     ```
+
     End
     """ |> fd2html_td
     @test isapproxstr(s, """
         <p>
-        Blah <pre><code class="language-html">&lt;div class&#61;&quot;foo&quot;&gt;Blah&lt;/div&gt;</code></pre>
-        End</p>""")
+        Blah</p>
+        <pre><code class="language-html">&lt;div class&#61;&quot;foo&quot;&gt;Blah&lt;/div&gt;</code></pre>
+        <p>End</p>""")
 end
diff --git a/test/eval/eval.jl b/test/eval/eval.jl
index e04d40cd..29c8c639 100644
--- a/test/eval/eval.jl
+++ b/test/eval/eval.jl
@@ -10,7 +10,9 @@ set_curpath("index.md")
         print(a^2)
         ```
         then:
+
         \output{./code/exca1}
+
         done.
         """ |> seval
 
@@ -21,7 +23,9 @@ set_curpath("index.md")
         print(a^2)
         ```
         then:
+
         \output{excb1}
+
         done.
         """ |> seval
 
@@ -40,12 +44,12 @@ set_curpath("index.md")
     @test read(opath, String) == "25"
 
     @test isapproxstr(h, raw"""
-            <p>Simple code:
-            <pre><code class="language-julia">a = 5
-            print(a^2)</code></pre>
-            then:
-            <pre><code class="plaintext">25</code></pre>
-            done.</p>""")
+                <p>Simple code:</p>
+                <pre><code class="language-julia">a = 5
+                print(a^2)</code></pre>
+                <p>then:</p>
+                <pre><code class="plaintext">25</code></pre>
+                <p>done.</p>""")
 end
 
 @testset "Eval (errs)" begin
@@ -61,10 +65,10 @@ end
     @test_logs (:warn, "Evaluation of non-Julia code blocks is not yet supported.") (h = s |> seval)
 
     @test isapproxstr(h, raw"""
-            <p>Simple code:
+            <p>Simple code:</p>
             <pre><code class="language-python">a = 5
             print(a**2)</code></pre>
-            done.</p>""")
+            <p>done.</p>""")
 end
 
 @testset "Eval (rinput)" begin
@@ -75,7 +79,9 @@ end
         print(a^2)
         ```
         then:
+
         \output{/assets/scripts/test2}
+
         done.
         """ |> seval
 
@@ -88,12 +94,12 @@ end
     @test read(opath, String) == "25"
 
     @test isapproxstr(h, """
-            <p>Simple code:
+            <p>Simple code:</p>
             <pre><code class="language-julia">a = 5
             print(a^2)</code></pre>
-            then:
+            <p>then:</p>
             <pre><code class="plaintext">25</code></pre>
-            done.</p>""")
+            <p>done.</p>""")
 
     # ------------
 
@@ -105,7 +111,9 @@ end
         print(a^2)
         ```
         then:
+
         \output{./code/abc2}
+
         done.
         """ |> seval
 
@@ -118,11 +126,12 @@ end
     @test read(opath, String) == "25"
 
     @test isapproxstr(h, """
-            <p>Simple code:
+            <p>Simple code:</p>
             <pre><code class="language-julia">a = 5
             print(a^2)</code></pre>
-            then:
-            <pre><code class="plaintext">25</code></pre>  done.</p>""")
+            <p>then:</p>
+            <pre><code class="plaintext">25</code></pre>
+            <p>done.</p>""")
 end
 
 @testset "Eval (module)" begin
@@ -134,11 +143,20 @@ end
         print(dot(a, a))
         ```
         then:
+
         \output{scripts/test1}
+
         done.
         """ |> seval
     # dot(a, a) == 54
-    @test occursin("""then: <pre><code class="plaintext">54</code></pre> done.""", h)
+    @test h // raw"""
+                <p>Simple code:</p>
+                <pre><code class="language-julia">using LinearAlgebra
+                a = [5, 2, 3, 4]
+                print(dot(a, a))</code></pre>
+                <p>then:</p>
+                <pre><code class="plaintext">54</code></pre>
+                <p>done.</p>"""
 end
 
 @testset "Eval (img)" begin
@@ -150,10 +168,18 @@ end
         write(joinpath(@OUTPUT, "tv2.png"), "blah")
         ```
         then:
+
         \input{plot}{tv2}
+
         done.
         """ |> seval
-    @test occursin("then: <img src=\"/assets/index/code/output/tv2.png\" alt=\"\"> done.", h)
+    @test h // raw"""
+                <p>Simple code:</p>
+
+                <p>then:</p>
+                <img src="/assets/index/code/output/tv2.png" alt="">
+                <p>done.</p>
+                """
 end
 
 @testset "Eval (throw)" begin
@@ -164,7 +190,9 @@ end
         sqrt(-1)
         ```
         then:
+
         \output{scripts/test1}
+
         done.
         """
     global h
@@ -172,10 +200,15 @@ end
     @test_logs (:warn, "There was an error of type DomainError running the code.") (global h; h = s |> seval)
     # errors silently
     if VERSION >= v"1.2"
-        @test occursin("""<p>Simple code: <pre><code class="language-julia">sqrt(-1)</code></pre> then: <pre><code class="plaintext">DomainError with -1.0:
-    sqrt will only return a complex result if called with a complex argument. Try sqrt(Complex(x)).
-    </code></pre> done.</p>
-    """, h)
+        @test h // raw"""
+                    <p>Simple code:</p>
+                    <pre><code class="language-julia">sqrt(-1)</code></pre>
+                    <p>then:</p>
+                    <pre><code class="plaintext">DomainError with -1.0:
+                    sqrt will only return a complex result if called with a complex argument. Try sqrt(Complex(x)).
+                    </code></pre>
+                    <p>done.</p>
+                    """
     end
 end
 
@@ -188,7 +221,10 @@ end
         done.
         """
 
-    @test (@test_logs (:warn, "Evaluation of non-Julia code blocks is not yet supported.") h |> seval) == "<p>Simple code: <pre><code class=\"language-python\">sqrt(-1)</code></pre> done.</p>\n"
+    @test (@test_logs (:warn, "Evaluation of non-Julia code blocks is not yet supported.") h |> seval) // raw"""
+            <p>Simple code:</p>
+            <pre><code class="language-python">sqrt(-1)</code></pre>
+            <p>done.</p>"""
 end
 
 # temporary fix for 186: make error appear and also use `abspath` in internal include
@@ -204,17 +240,21 @@ end
         rm(fn)
         ```
         done.
+
         \output{scripts/test186}
         """ |> seval
     @test isapproxstr(h, raw"""
-            <p>Simple code: <pre><code class="language-julia">fn = "tempf.jl"
+            <p>Simple code:</p>
+            <pre><code class="language-julia">fn = "tempf.jl"
             write(fn, "a = 1+1")
             println("Is this a file? $(isfile(fn))")
             include(abspath(fn))
             println("Now: $a")
-            rm(fn)</code></pre> done. <pre><code class="plaintext">Is this a file? true
+            rm(fn)</code></pre>
+            <p>done.</p>
+            <pre><code class="plaintext">Is this a file? true
             Now: 2
-            </code></pre></p>
+            </code></pre>
             """)
 end
 
@@ -227,13 +267,13 @@ end
         a = 5
         a *= 2
         ```
+
         \show{ex}
         """ |> fd2html_td
-    @test isapproxstr(h, """
-        <pre><code class="language-julia">a = 5
-        a *= 2</code></pre>
-        <pre><code class="plaintext">10</code></pre>
-        """)
+    @test h // raw"""
+               <pre><code class="language-julia">a = 5
+               a *= 2</code></pre>
+               <pre><code class="plaintext">10</code></pre>"""
 
     # Show with stdout
     h = raw"""
@@ -244,13 +284,14 @@ end
         println("hello")
         a *= 2
         ```
+
         \show{ex}
         """ |> fd2html_td
-    @test isapproxstr(h, """
-        <pre><code class="language-julia">a = 5
-        println("hello")
-        a *= 2</code></pre>
-        <pre><code class="plaintext">hello
-        10</code></pre>
-        """)
+    @test h // raw"""
+                <pre><code class="language-julia">a = 5
+                println("hello")
+                a *= 2</code></pre>
+                <pre><code class="plaintext">hello
+                10</code></pre>
+                """
 end
diff --git a/test/eval/eval_fs2.jl b/test/eval/eval_fs2.jl
index df0a30ca..15e1c8d5 100644
--- a/test/eval/eval_fs2.jl
+++ b/test/eval/eval_fs2.jl
@@ -10,7 +10,9 @@ set_curpath("index.md")
         print(a^2)
         ```
         then:
+
         \output{./code/exca1}
+
         done.
         """ |> seval
 
@@ -21,7 +23,9 @@ set_curpath("index.md")
         print(a^2)
         ```
         then:
+
         \output{excb1}
+
         done.
         """ |> seval
 
@@ -39,13 +43,13 @@ set_curpath("index.md")
     @test isfile(opath)
     @test read(opath, String) == "25"
 
-    @test isapproxstr(h, raw"""
-            <p>Simple code:
-            <pre><code class="language-julia">a = 5
-            print(a^2)</code></pre>
-            then:
-            <pre><code class="plaintext">25</code></pre>
-            done.</p>""")
+    @test h // raw"""
+                <p>Simple code:</p>
+                <pre><code class="language-julia">a = 5
+                print(a^2)</code></pre>
+                <p>then:</p>
+                <pre><code class="plaintext">25</code></pre>
+                <p>done.</p>"""
 end
 
 @testset "Eval (errs)" begin
@@ -61,10 +65,10 @@ end
     @test_logs (:warn, "Evaluation of non-Julia code blocks is not yet supported.") (h = s |> seval)
 
     @test isapproxstr(h, raw"""
-            <p>Simple code:
-            <pre><code class="language-python">a = 5
-            print(a**2)</code></pre>
-            done.</p>""")
+                        <p>Simple code:</p>
+                        <pre><code class="language-python">a = 5
+                        print(a**2)</code></pre>
+                        <p>done.</p>""")
 end
 
 @testset "Eval (rinput)" begin
@@ -75,7 +79,9 @@ end
         print(a^2)
         ```
         then:
+
         \output{/assets/scripts/test2}
+
         done.
         """ |> seval
 
@@ -87,13 +93,13 @@ end
     @test isfile(opath)
     @test read(opath, String) == "25"
 
-    @test isapproxstr(h, """
-            <p>Simple code:
+    @test h // raw"""
+            <p>Simple code:</p>
             <pre><code class="language-julia">a = 5
             print(a^2)</code></pre>
-            then:
+            <p>then:</p>
             <pre><code class="plaintext">25</code></pre>
-            done.</p>""")
+            <p>done.</p>"""
 
     # ------------
 
@@ -105,7 +111,9 @@ end
         print(a^2)
         ```
         then:
+
         \output{./code/abc2}
+
         done.
         """ |> seval
 
@@ -117,12 +125,13 @@ end
     @test isfile(opath)
     @test read(opath, String) == "25"
 
-    @test isapproxstr(h, """
-            <p>Simple code:
-            <pre><code class="language-julia">a = 5
-            print(a^2)</code></pre>
-            then:
-            <pre><code class="plaintext">25</code></pre>  done.</p>""")
+    @test h // raw"""
+                <p>Simple code:</p>
+                <pre><code class="language-julia">a = 5
+                print(a^2)</code></pre>
+                <p>then:</p>
+                <pre><code class="plaintext">25</code></pre>
+                <p>done.</p>"""
 end
 
 @testset "Eval (module)" begin
@@ -134,11 +143,20 @@ end
         print(dot(a, a))
         ```
         then:
+
         \output{scripts/test1}
+
         done.
         """ |> seval
     # dot(a, a) == 54
-    @test occursin("""then: <pre><code class="plaintext">54</code></pre> done.""", h)
+    @test h // raw"""
+                <p>Simple code:</p>
+                <pre><code class="language-julia">using LinearAlgebra
+                a = [5, 2, 3, 4]
+                print(dot(a, a))</code></pre>
+                <p>then:</p>
+                <pre><code class="plaintext">54</code></pre>
+                <p>done.</p>"""
 end
 
 @testset "Eval (img)" begin
@@ -150,10 +168,18 @@ end
         write(joinpath(@OUTPUT, "tv2.png"), "blah")
         ```
         then:
+
         \input{plot}{tv2}
+
         done.
         """ |> seval
-    @test occursin("then: <img src=\"/assets/index/code/output/tv2.png\" alt=\"\"> done.", h)
+    @test h // raw"""
+                <p>Simple code:</p>
+
+                <p>then:</p>
+                <img src="/assets/index/code/output/tv2.png" alt="">
+                <p>done.</p>
+                """
 end
 
 @testset "Eval (throw)" begin
@@ -164,7 +190,9 @@ end
         sqrt(-1)
         ```
         then:
+
         \output{scripts/test1}
+
         done.
         """
     global h
@@ -172,10 +200,14 @@ end
     @test_logs (:warn, "There was an error of type DomainError running the code.") (global h; h = s |> seval)
     # errors silently
     if VERSION >= v"1.2"
-        @test occursin("""<p>Simple code: <pre><code class="language-julia">sqrt(-1)</code></pre> then: <pre><code class="plaintext">DomainError with -1.0:
-    sqrt will only return a complex result if called with a complex argument. Try sqrt(Complex(x)).
-    </code></pre> done.</p>
-    """, h)
+        @test h // raw"""
+                    <p>Simple code:</p>
+                    <pre><code class="language-julia">sqrt(-1)</code></pre>
+                    <p>then:</p>
+                    <pre><code class="plaintext">DomainError with -1.0:
+                    sqrt will only return a complex result if called with a complex argument. Try sqrt(Complex(x)).
+                    </code></pre>
+                    <p>done.</p>"""
     end
 end
 
@@ -188,7 +220,11 @@ end
         done.
         """
 
-    @test (@test_logs (:warn, "Evaluation of non-Julia code blocks is not yet supported.") h |> seval) == "<p>Simple code: <pre><code class=\"language-python\">sqrt(-1)</code></pre> done.</p>\n"
+    @test (@test_logs (:warn, "Evaluation of non-Julia code blocks is not yet supported.") h |> seval) // raw"""
+        <p>Simple code:</p>
+        <pre><code class="language-python">sqrt(-1)</code></pre>
+        <p>done.</p>
+        """
 end
 
 # temporary fix for 186: make error appear and also use `abspath` in internal include
@@ -204,18 +240,22 @@ end
         rm(fn)
         ```
         done.
+
         \output{scripts/test186}
         """ |> seval
-    @test isapproxstr(h, raw"""
-            <p>Simple code: <pre><code class="language-julia">fn = "tempf.jl"
+    @test h // raw"""
+            <p>Simple code:</p>
+            <pre><code class="language-julia">fn = "tempf.jl"
             write(fn, "a = 1+1")
             println("Is this a file? $(isfile(fn))")
             include(abspath(fn))
             println("Now: $a")
-            rm(fn)</code></pre> done. <pre><code class="plaintext">Is this a file? true
+            rm(fn)</code></pre>
+            <p>done.</p>
+            <pre><code class="plaintext">Is this a file? true
             Now: 2
-            </code></pre></p>
-            """)
+            </code></pre>
+            """
 end
 
 
@@ -227,13 +267,13 @@ end
         a = 5
         a *= 2
         ```
+
         \show{ex}
         """ |> fd2html_td
-    @test isapproxstr(h, """
-        <pre><code class="language-julia">a = 5
-        a *= 2</code></pre>
-        <pre><code class="plaintext">10</code></pre>
-        """)
+    @test h // raw"""
+                <pre><code class="language-julia">a = 5
+                a *= 2</code></pre>
+                <pre><code class="plaintext">10</code></pre>"""
 
     # Show with stdout
     h = raw"""
@@ -244,15 +284,15 @@ end
         println("hello")
         a *= 2
         ```
+
         \show{ex}
         """ |> fd2html_td
-    @test isapproxstr(h, """
-        <pre><code class="language-julia">a = 5
-        println("hello")
-        a *= 2</code></pre>
-        <pre><code class="plaintext">hello
-        10</code></pre>
-        """)
+    @test h // raw"""
+                <pre><code class="language-julia">a = 5
+                println("hello")
+                a *= 2</code></pre>
+                <pre><code class="plaintext">hello
+                10</code></pre>"""
 
     # issue 427
     h = raw"""
@@ -263,12 +303,12 @@ end
         a *= 2
         # hello
         ```
+
         \show{ex}
         """ |> fd2html_td
-    @test isapproxstr(h, """
-        <pre><code class="language-julia">a = 5
-        a *= 2
-        # hello</code></pre>
-        <pre><code class="plaintext">10</code></pre>
-        """)
+    @test h // raw"""
+            <pre><code class="language-julia">a = 5
+            a *= 2
+            # hello</code></pre>
+            <pre><code class="plaintext">10</code></pre>"""
 end
diff --git a/test/eval/run.jl b/test/eval/run.jl
index 5c9cf7d9..ace9f3b0 100644
--- a/test/eval/run.jl
+++ b/test/eval/run.jl
@@ -84,9 +84,12 @@ end
        1 # hide
        ```
        \show{ex}
+
        B""" |> fd2html_td
     @test isapproxstr(s, """
-        <p>A <pre><code class="plaintext">1</code></pre> B</p>
+        <p>A</p>
+        <pre><code class="plaintext">1</code></pre>
+        <p>B</p>
         """)
     s = raw"""
        A
@@ -94,8 +97,11 @@ end
        "hello" # hide
        ```
        \show{ex}
+
        B""" |> fd2html_td
     @test isapproxstr(s, """
-        <p>A <pre><code class="plaintext">"hello"</code></pre> B</p>
+        <p>A</p>
+        <pre><code class="plaintext">"hello"</code></pre>
+        <p>B</p>
         """)
 end
diff --git a/test/global/cases1.jl b/test/global/cases1.jl
index 0314dee5..f62ba2c5 100644
--- a/test/global/cases1.jl
+++ b/test/global/cases1.jl
@@ -34,9 +34,7 @@ end
         \newcommand{\com}[1]{◲!#1◲}
         \com{A}
         """
-    steps = st |> explore_md_steps
-    @test steps[:inter_md].inter_md == "\n ##FDINSERT## \n\n ##FDINSERT## \n"
-    @test st |> conv == "⭒A⭒\n◲A◲"
+    @test st |> conv == "⭒A⭒\n◲A◲\n"
 end
 
 
@@ -47,11 +45,11 @@ end
         done
         """
     @test isapproxstr(st |> conv,
-            """<p>
-            Etc and <code>~~~</code> but hey.
-            <div class=\"dd\">but <code>x</code> and <code>y</code>? </div>
-            done
-            </p>""")
+            """
+            <p>Etc and <code>~~~</code> but hey.</p>
+            <div class="dd">but <code>x</code> and <code>y</code>?</div>
+            <p>done</p>
+            """)
 end
 
 
@@ -66,14 +64,13 @@ end
         done
         """
     @test isapproxstr(st |> conv,
-            """<p>
-            Some code
-            <pre><code class=\"language-julia\">
-            struct P
+            """
+            <p>Some code</p>
+            <pre><code class="language-julia">struct P
                 x::Real
-            end
-            </code></pre>
-            done</p>""")
+            end</code></pre>
+            <p>done</p>
+            """)
 end
 
 
@@ -104,7 +101,7 @@ end
 
         def.
         """
-    @test st |> conv == "<p>abc</p>\n⭒A⭒⭒B⭒\n<p>def.</p>\n"
+    @test st |> conv == "<p>abc</p>\n<p>⭒A⭒⭒B⭒</p>\n<p>def.</p>\n"
 
     st = raw"""
         \newcommand{\com}[1]{⭒!#1⭒}
@@ -114,7 +111,12 @@ end
 
         def.
         """
-    @test st |> conv == "<p>abc</p>\n⭒A⭒\n<p>def.</p>\n"
+    @test (st |> conv) //
+            """
+            <p>abc</p>
+            ⭒A⭒
+            <p>def.</p>
+            """
 
     st = raw"""
         \newcommand{\com}[1]{†!#1†}
@@ -123,7 +125,7 @@ end
         * ss \com{bbb}
         """
     @test isapproxstr(st |> conv,
-            """<p>blah †a†
+            """<p>blah †a†</p>
             <ul>
                <li><p>†aaa† tt</p></li>
                <li><p>ss †bbb†</p></li>
diff --git a/test/global/cases2.jl b/test/global/cases2.jl
index ee1cefd7..40ab46a0 100644
--- a/test/global/cases2.jl
+++ b/test/global/cases2.jl
@@ -159,7 +159,7 @@ end
     etc
     ~~~{{fill title}}~~~
     """ |> fd2html_td
-    @test isapproxstr(s, raw"""<h1 id="aaa"><a href="#aaa">AAA</a></h1>  etc AAA""")
+    @test isapproxstr(s, raw"""<h1 id="aaa"><a href="#aaa">AAA</a></h1>  <p>etc AAA</p>""")
 end
 
 @testset "i 430" begin
@@ -169,14 +169,13 @@ end
         [^ö]: world
         """ |> fd2html_td
     @test isapproxstr(s, """
-        <p>Hello<sup id="fnref:ö"><a href="#fndef:ö" class="fnref">[1]</a></sup>
+        <p>Hello<sup id="fnref:ö"><a href="#fndef:ö" class="fnref">[1]</a></sup></p>
         <table class="fndef" id="fndef:ö">
           <tr>
             <td class="fndef-backref"><a href="#fnref:ö">[1]</a></td>
             <td class="fndef-content">world</td>
           </tr>
         </table>
-        </p>
         """)
 end
 
@@ -187,7 +186,7 @@ end
         {{hasmath}} {{hascode}}
         $x = 5$
         """ |> fdi
-    @test isapproxstr(s, "true false \\(x = 5\\)")
+    @test isapproxstr(s, "<p>true false \\(x = 5\\)</p>")
     F.set_var!(F.LOCAL_VARS, "hascode", false)
     F.set_var!(F.LOCAL_VARS, "hasmath", false)
     s = raw"""
@@ -197,7 +196,7 @@ end
         ```
         """ |> fdi
     @test isapproxstr(s, """
-        false true
+        <p>false true</p>
         <pre><code class=\"language-r\">blah</code></pre>
         """)
 end
diff --git a/test/global/eval.jl b/test/global/eval.jl
index b553ffbc..3e28bf98 100644
--- a/test/global/eval.jl
+++ b/test/global/eval.jl
@@ -34,7 +34,7 @@
     h = foo |> fd2html_td
 
     @test isapproxstr(h, """
-                <pre><code class="language-julia">println(randn())</code></pre> <pre><code class=\"plaintext\">$a</code></pre> etc
+                <pre><code class="language-julia">println(randn())</code></pre> <p><pre><code class=\"plaintext\">$a</code></pre> etc</p>
                 """)
 
     # XXX CODE ADDITION --> NO REEVAL OF FIRST BLOCK
@@ -53,9 +53,9 @@
     h = foo |> fd2html_td
 
     @test isapproxstr(h, """
-                <pre><code class="language-julia">println(randn())</code></pre> <pre><code class=\"plaintext\">$a</code></pre> etc
-                <pre><code class="language-julia">println(randn())</code></pre> <pre><code class=\"plaintext\">$b</code></pre>
-                <pre><code class="language-julia">println(randn())</code></pre> <pre><code class=\"plaintext\">$c</code></pre>
+        <pre><code class="language-julia">println(randn())</code></pre> <p><pre><code class=\"plaintext\">$a</code></pre> etc</p>
+        <pre><code class="language-julia">println(randn())</code></pre> <pre><code class=\"plaintext\">$b</code></pre>
+        <pre><code class="language-julia">println(randn())</code></pre> <pre><code class=\"plaintext\">$c</code></pre>
                 """)
 
     # XXX CODE MODIFICATION --> REEVAL OF BLOCK AND AFTER
diff --git a/test/global/ordering.jl b/test/global/ordering.jl
index 82cfe9f8..d91a74a7 100644
--- a/test/global/ordering.jl
+++ b/test/global/ordering.jl
@@ -34,11 +34,11 @@ end
     @test blocks[1].name == :MATH_EQA
 
     @test isapproxstr(st |> seval, raw"""
-            <p>A
+            <p>A</p>
             \[\begin{array}{c}
                 1 + 1 &=& 2
             \end{array}\]
-            B</p>""")
+            <p>B</p>""")
 end
 
 @testset "Ordering-3" begin
@@ -64,11 +64,11 @@ end
     @test blocks[2].name == :COMMENT
 
     @test isapproxstr(st |> seval, raw"""
-            <p>A
+            <p>A</p>
             \[\begin{array}{c}
                 1 + 1 &=& 2
             \end{array}\]
-            B</p>
+            <p>B</p>
             <p>C</p>""")
 end
 
diff --git a/test/html/closep.jl b/test/html/closep.jl
new file mode 100644
index 00000000..0173db3a
--- /dev/null
+++ b/test/html/closep.jl
@@ -0,0 +1,101 @@
+
+
+
+
+
+# environments
+# ------------
+# - images
+# - headers
+# - links
+# - code inline
+# - code blocks
+# - maths inline
+# - maths blocks
+# - div blocks
+# - lx commands
+# - hfun commands
+# - tables
+# - admonition (is it a thing?)
+# - blockquotes
+# - escaped blocks
+# - def blocks
+# - indented blocks
+# - lists
+# - raw html inclusion
+# - raw html block with separating line
+# - footnotes
+# - citations
+
+@testset "close:headers" begin
+    h = """
+    A
+    # H
+    B
+    """ |> fd2html
+    @test h // """
+        <p>A</p>
+        <h1 id="h"><a href="#h">H</a></h1>
+        <p>B</p>"""
+    h = """
+    A
+    # H1
+    ## H2
+    B
+    ## H2
+    C
+    """ |> fd2html
+    @test h // """
+        <p>A</p>
+        <h1 id="h1"><a href="#h1">H1</a></h1>
+        <h2 id="h2"><a href="#h2">H2</a></h2>
+        <p>B</p>
+        <h2 id="h2__2"><a href="#h2__2">H2</a></h2>
+        <p>C</p>"""
+end
+
+@testset "close:div" begin
+    h = """
+    A
+    @@b,c D @@
+    E
+    """ |> fd2html
+    @test h // """
+        <p>A</p>
+        <div class="b c">D</div>
+        <p>E</p>"""
+end
+
+# NOTE: will fail upon use of CommonMark (list)
+@testset "close:list+i" begin
+    h = """
+    A
+    * B
+    * `C`
+    E
+    """ |> fd2html
+    @test h // """
+        <p>A</p>
+        <ul>
+        <li><p>B</p>
+        </li>
+        <li><p><code>C</code></p>
+        </li>
+        </ul>
+        <p>E</p>"""
+end
+
+
+@testset "ending p" begin
+    # Franklin.LOGGING[] = true
+    h = raw"""
+    @def date_format = "e, d u Y"
+    ```julia:ex1
+    #hideall
+    using Dates
+    println(Franklin.fd_date(DateTime("1996-01-01T12:30:00")))
+    ```
+    A \textoutput{ex1} B
+    """ |> fd2html
+    @test h // "<p>A Mon, 1 Jan 1996 B</p>"
+end
diff --git a/test/html/closep_lx.jl b/test/html/closep_lx.jl
new file mode 100644
index 00000000..3d3e9750
--- /dev/null
+++ b/test/html/closep_lx.jl
@@ -0,0 +1,130 @@
+#= NOTE:
+
+- general rule: use latex command for either block or inline, avoid mixing (1, 2, 3)
+- if a command defines a block, use empty lines to separate otherwise it will be plugged in a paragraph (3)
+- if mixing inline and block, you may get unexpected stuff
+    - ok (4.a)
+    - nok (4.b)
+- nesting (5)
+=#
+
+@testset "1/noblock nop" begin
+    h = raw"""
+        \newcommand{\foo}{A **B** C}
+        1 \foo 2
+        """ |> fd2html
+    @test h // "<p>1 A <strong>B</strong> C 2</p>"
+end
+
+@testset "2/block nop" begin
+    h = raw"""
+        \newcommand{\foo}{```julia
+        x = 5
+        @show x
+        ```}
+        1 \foo 2
+        """ |> fd2html
+    @test h // """<p>1 <pre><code class="language-julia">x = 5
+                  @show x</code></pre> 2</p>"""
+end
+
+@testset "3/block wp" begin
+    h = raw"""
+        \newcommand{\foo}{```julia
+        x = 5
+        @show x
+        ```}
+        1
+
+        \foo
+
+        2""" |> fd2html
+    @test h // """<p>1</p>
+                  <pre><code class="language-julia">x = 5
+                  @show x</code></pre>
+                  <p>2</p>"""
+end
+
+@testset "4/mixing" begin
+    # OK (4.a)
+    h = raw"""
+        \newcommand{\foo}{A $$ x = 5 $$ B}
+        1 \foo 2
+        """ |> fd2html
+    @test h // raw"""
+               <p>1 A </p>
+               \[ x = 5 \]
+               <p>B 2</p>
+               """
+    # XXX NOK (4.b)
+    h = raw"""
+       \newcommand{\foo}{A $$ x = 5 $$ B}
+       1
+
+       \foo
+
+       2
+       """ |> fd2html
+    @test h // raw"""
+               <p>1</p>
+               A </p>
+               \[ x = 5 \]
+               <p>B
+               <p>2</p>"""
+end
+
+@testset "5/nesting" begin
+    h = raw"""
+        \newcommand{\foo}{A `B` C}
+        \newcommand{\bar}{1 \foo 2}
+        aa \bar bb
+        """ |> fd2html
+    @test h // "<p>aa 1 A <code>B</code> C 2 bb</p>"
+    h = raw"""
+        \newcommand{\foo}[1]{@@b #1 @@}
+        \newcommand{\bar}[1]{A `!#1` \foo{g} C}
+        aa \bar{hh} bb
+        """ |> fd2html
+    @test h // """
+               <p>aa A <code>hh</code> <div class="b">g</div> C bb</p>
+               """
+    # nesting with block // mixing
+    h = raw"""
+        \newcommand{\foo}[1]{```python
+        !#1
+        ```}
+        \newcommand{\bar}[1]{ABC \foo{!#1} DEF}
+        aa \bar{x=1} bb
+        """ |> fd2html
+    @test h // raw"""
+               <p>aa ABC <pre><code class="language-python">x=1</code></pre> DEF bb</p>
+               """
+end
+
+@testset "6/div-mix" begin
+    s = raw"""
+        \newcommand{\note}[1]{@@note #1 @@}
+
+        \note{A}
+
+        \note{A `B` C}
+
+        \note{A @@cc B @@ D}
+
+        \note{A @@cc B `D` E @@ F}
+        """ |> fd2html_td
+    @test isapproxstr(s, """
+        <div class="note">A</div>
+        <div class="note">A <code>B</code> C</div>
+        <div class="note">A
+                </p>
+            <div class="cc">B</div>
+                <p>D
+        </div>
+        <div class="note">A
+                </p>
+            <div class="cc">B <code>D</code> E</div>
+                <p>F
+        </div>
+        """)
+end
diff --git a/test/integration/literate.jl b/test/integration/literate.jl
index 6ae23a8f..cc44023f 100644
--- a/test/integration/literate.jl
+++ b/test/integration/literate.jl
@@ -1,11 +1,12 @@
-fs2()
+fs1()
 
-mkpath(F.path(:literate))
+scripts = joinpath(F.PATHS[:folder], "literate-scripts")
+mkpath(scripts)
 
 @testset "Literate-0" begin
     @test_throws ErrorException literate_folder("foo/")
-    litpath = literate_folder("_literate/")
-    @test litpath == literate_folder(F.path(:literate))
+    litpath = literate_folder("literate-scripts/")
+    @test litpath == joinpath(F.PATHS[:folder], "literate-scripts/")
 end
 
 @testset "Literate-a" begin
@@ -55,10 +56,10 @@ end
 
         z = x + y
         """
-    path = joinpath(F.path(:literate), "tutorial.jl")
+    path = joinpath(scripts, "tutorial.jl")
     write(path, s)
-    opath, = F.literate_to_franklin("/_literate/tutorial")
-    @test endswith(opath, joinpath(F.PATHS[:site], "assets", "literate", "tutorial.md"))
+    opath, = F.literate_to_franklin("/literate-scripts/tutorial")
+    @test endswith(opath, joinpath(F.PATHS[:assets], "literate", "tutorial.md"))
     out = read(opath, String)
     @test out == """
         <!--This file was generated, do not modify it.-->
@@ -87,7 +88,7 @@ end
         @def showall = true
         @def reeval = true
 
-        \literate{/_literate/tutorial.jl}
+        \literate{/literate-scripts/tutorial.jl}
         """ |> fd2html_td
     @test isapproxstr(h, """
         <h1 id="rational_numbers"><a href="#rational_numbers">Rational numbers</a></h1>
@@ -110,5 +111,5 @@ end
     s = raw"""
         \literate{/foo}
         """
-    @test @test_logs (:warn, "File not found when trying to convert a literate file ($(joinpath(F.PATHS[:folder], "foo.jl"))).") (s |> fd2html_td) == """<p><span style="color:red;">// Literate file matching '/foo' not found. //</span></p></p>\n"""
+    @test @test_logs (:warn, "File not found when trying to convert a literate file ($(joinpath(F.PATHS[:folder], "foo.jl"))).") (s |> fd2html_td) == """<p><span style="color:red;">// Literate file matching '/foo' not found. //</span></p>\n"""
 end
diff --git a/test/integration/literate_extras.jl b/test/integration/literate_extras.jl
index 83c2816c..28ab8213 100644
--- a/test/integration/literate_extras.jl
+++ b/test/integration/literate_extras.jl
@@ -22,12 +22,14 @@ fs2()
     s = raw"""
         @def showall = true
         INI
+
         \literate{/_literate/ex1}
         """
 
     h = s |> fd2html_td
     @test isapproxstr(h, """
-        <p>INI A</p>
+        <p>INI</p>
+        <p>A</p>
         <pre><code class="language-julia">1 + 1</code></pre><pre><code class="plaintext">2</code></pre>
         <p>B</p>
         <pre><code class="language-julia">2^2;</code></pre>
diff --git a/test/integration/literate_fs2.jl b/test/integration/literate_fs2.jl
index c71a8eeb..57d867c9 100644
--- a/test/integration/literate_fs2.jl
+++ b/test/integration/literate_fs2.jl
@@ -1,12 +1,11 @@
-fs1()
+fs2()
 
-scripts = joinpath(F.PATHS[:folder], "literate-scripts")
-mkpath(scripts)
+mkpath(F.path(:literate))
 
 @testset "Literate-0" begin
     @test_throws ErrorException literate_folder("foo/")
-    litpath = literate_folder("literate-scripts/")
-    @test litpath == joinpath(F.PATHS[:folder], "literate-scripts/")
+    litpath = literate_folder("_literate/")
+    @test litpath == literate_folder(F.path(:literate))
 end
 
 @testset "Literate-a" begin
@@ -56,10 +55,10 @@ end
 
         z = x + y
         """
-    path = joinpath(scripts, "tutorial.jl")
+    path = joinpath(F.path(:literate), "tutorial.jl")
     write(path, s)
-    opath, = F.literate_to_franklin("/literate-scripts/tutorial")
-    @test endswith(opath, joinpath(F.PATHS[:assets], "literate", "tutorial.md"))
+    opath, = F.literate_to_franklin("/_literate/tutorial")
+    @test endswith(opath, joinpath(F.PATHS[:site], "assets", "literate", "tutorial.md"))
     out = read(opath, String)
     @test out == """
         <!--This file was generated, do not modify it.-->
@@ -88,18 +87,16 @@ end
         @def showall = true
         @def reeval = true
 
-        \literate{/literate-scripts/tutorial.jl}
+        \literate{/_literate/tutorial.jl}
         """ |> fd2html_td
     @test isapproxstr(h, """
         <h1 id="rational_numbers"><a href="#rational_numbers">Rational numbers</a></h1>
         <p>In julia rational numbers can be constructed with the <code>//</code> operator. Lets define two rational numbers, <code>x</code> and <code>y</code>:</p>
         <pre><code class="language-julia"># Define variable x and y
         x = 1//3
-        y = 2//5</code></pre>
-        <pre><code class=\"plaintext\">2//5</code></pre>
+        y = 2//5</code></pre><pre><code class="plaintext">2//5</code></pre>
         <p>When adding <code>x</code> and <code>y</code> together we obtain a new rational number:</p>
-        <pre><code class="language-julia">z = x + y</code></pre>
-        <pre><code class=\"plaintext\">11//15</code></pre>
+        <pre><code class="language-julia">z = x + y</code></pre><pre><code class="plaintext">11//15</code></pre>
         """)
 end
 
@@ -111,5 +108,5 @@ end
     s = raw"""
         \literate{/foo}
         """
-    @test @test_logs (:warn, "File not found when trying to convert a literate file ($(joinpath(F.PATHS[:folder], "foo.jl"))).") (s |> fd2html_td) == """<p><span style="color:red;">// Literate file matching '/foo' not found. //</span></p></p>\n"""
+    @test @test_logs (:warn, "File not found when trying to convert a literate file ($(joinpath(F.PATHS[:folder], "foo.jl"))).") (s |> fd2html_td) == """<p><span style="color:red;">// Literate file matching '/foo' not found. //</span></p>\n"""
 end
diff --git a/test/manager/config.jl b/test/manager/config.jl
index f2a83fac..5c8ec347 100644
--- a/test/manager/config.jl
+++ b/test/manager/config.jl
@@ -36,7 +36,7 @@ foofig(p, s) = (write(joinpath(p, "src", "config.md"), s); F.process_config())
           F.GLOBAL_LXDEFS["\\hellob"].from < F.GLOBAL_LXDEFS["\\hellob"].to <
           F.GLOBAL_LXDEFS["\\helloc"].from < F.GLOBAL_LXDEFS["\\helloc"].to
 
-    @test fd2html(raw"""\helloc"""; dir=p, internal=true) == "goodbye"
+    @test fd2html(raw"""\helloc"""; dir=p, internal=true) // "goodbye"
 
     # ================================
     # go back and cleanup
diff --git a/test/manager/config_fs2.jl b/test/manager/config_fs2.jl
index f29e99fc..ff775b43 100644
--- a/test/manager/config_fs2.jl
+++ b/test/manager/config_fs2.jl
@@ -30,7 +30,7 @@ foofig(s) = (write(joinpath(td, "config.md"), s); F.process_config())
           F.GLOBAL_LXDEFS["\\hellob"].from < F.GLOBAL_LXDEFS["\\hellob"].to <
           F.GLOBAL_LXDEFS["\\helloc"].from < F.GLOBAL_LXDEFS["\\helloc"].to
 
-    @test fd2html(raw"""\helloc"""; dir=td, internal=true) == "goodbye"
+    @test fd2html(raw"""\helloc"""; dir=td, internal=true) // "goodbye"
 end
 
 @testset "i381" begin
diff --git a/test/parser/footnotes+links.jl b/test/parser/footnotes+links.jl
index 5b4c92fb..bae138ac 100644
--- a/test/parser/footnotes+links.jl
+++ b/test/parser/footnotes+links.jl
@@ -47,6 +47,7 @@ end
        """
     @test isapproxstr(s |> fd2html_td, """
         <pre><code class="language-markdown">this has[^1]
+
         [^1]: def
-        </code></pre> blah""")
+        </code></pre> <p>blah</p>""")
 end
diff --git a/test/parser/indentation++.jl b/test/parser/indentation++.jl
index 0a303d11..94afe32f 100644
--- a/test/parser/indentation++.jl
+++ b/test/parser/indentation++.jl
@@ -40,8 +40,10 @@
     b2i = steps[:b2insert].b2insert
     @test b2i[2].name == :CODE_BLOCK_IND
     @test isapproxstr(mds |> fd2html_td, """
-        <p>A <pre><code class="language-julia">B
-        C</code></pre>D</p>
+        <p>A</p>
+        <pre><code class="language-julia">B
+        C</code></pre>
+        <p>D</p>
         """)
 end
 
diff --git a/test/parser/latex++.jl b/test/parser/latex++.jl
index fec61d18..bdd15add 100644
--- a/test/parser/latex++.jl
+++ b/test/parser/latex++.jl
@@ -4,19 +4,6 @@
     # 444
     s = "\\newcommand{\\note}[1]{#1} \\note{A `B` C} D" |> fd2html_td
     @test isapproxstr(s, """
-        A <code>B</code> C D
-        """)
-    s = raw"""
-        \newcommand{\note}[1]{@@note #1 @@}
-        \note{A}
-        \note{A `B` C}
-        \note{A @@cc B @@ D}
-        \note{A @@cc B `D` E @@ F}
-        """ |> fd2html_td
-    @test isapproxstr(s, """
-        <div class="note">A</div>
-        <div class="note">A <code>B</code> C</div>
-        <div class="note">A <div class="cc">B</div> D</div>
-        <div class="note">A <div class="cc">B <code>D</code> E</div> F</div>
+        <p>A <code>B</code> C D</p>
         """)
 end
diff --git a/test/parser/markdown+latex.jl b/test/parser/markdown+latex.jl
index 0f384b61..4b873102 100644
--- a/test/parser/markdown+latex.jl
+++ b/test/parser/markdown+latex.jl
@@ -261,7 +261,7 @@ end
 
     set_curpath("index.md")
 
-    h = raw"""
+    h = """
         # t1
         1
         ## t2
@@ -277,17 +277,17 @@ end
         """ |> seval
     @test isapproxstr(h, """
         <h1 id="t1"><a href="#t1">t1</a></h1>
-        1
+        <p>1</p>
         <h2 id="t2"><a href="#t2">t2</a></h2>
-        2
+        <p>2</p>
         <h2 id="t3_blah_etc"><a href="#t3_blah_etc">t3 <code>blah</code> etc</a></h2>
-        3
+        <p>3</p>
         <h3 id="t4"><a href="#t4">t4 </a></h3>
-        4
+        <p>4</p>
         <h3 id="t2__2"><a href="#t2__2">t2</a></h3>
-        5
+        <p>5</p>
         <h3 id="t2__3"><a href="#t2__3">t2</a></h3>
-        6
+        <p>6</p>
         """)
 
     # pathological issue 241
@@ -301,11 +301,11 @@ end
         """ |> seval
     @test  isapproxstr(h, """
         <h2 id="example"><a href="#example">example</a></h2>
-        A
+        <p>A</p>
         <h2 id="example__2"><a href="#example__2">example</a></h2>
-        B
+        <p>B</p>
         <h2 id="example_2"><a href="#example_2">example 2</a></h2>
-        C
+        <p>C</p>
         """)
 end
 
@@ -318,28 +318,28 @@ end
 
 @testset "Header+lx" begin
     h = "# blah" |> fd2html_td
-    @test h == raw"""<h1 id="blah"><a href="#blah">blah</a></h1>"""
+    @test h // """<h1 id="blah"><a href="#blah">blah</a></h1>"""
     h = raw"""
         \newcommand{\foo}{foo}
         \newcommand{\header}{# hello}
         \foo
         \header
         """ |> fd2html_td
-    @test h == raw"""foo <h1 id="hello"><a href="#hello">hello</a></h1>"""
+    @test h // """<p>foo <h1 id="hello"><a href="#hello">hello</a></h1></p>"""
     h = raw"""
         \newcommand{\foo}{foo}
         \foo hello
         """ |> fd2html_td
-    @test h == raw"""foo hello"""
+    @test h // """<p>foo hello</p>"""
     h = raw"""
         \newcommand{\foo}{blah}
         # \foo hello
         """ |> fd2html_td
-    @test h == raw"""<h1 id="blah_hello"><a href="#blah_hello">blah hello</a></h1>"""
+    @test h // """<h1 id="blah_hello"><a href="#blah_hello">blah hello</a></h1>"""
     h = raw"""
         \newcommand{\foo}{foo}
         \newcommand{\header}[2]{!#1 \foo #2}
         \header{##}{hello}
         """ |> fd2html_td
-    @test h == raw"""<h2 id="foo_hello"><a href="#foo_hello">foo  hello</a></h2>"""
+    @test h // """<h2 id="foo_hello"><a href="#foo_hello">foo  hello</a></h2>"""
 end
diff --git a/test/parser/markdown-extra.jl b/test/parser/markdown-extra.jl
index 554e9e2b..e5b66558 100644
--- a/test/parser/markdown-extra.jl
+++ b/test/parser/markdown-extra.jl
@@ -51,9 +51,9 @@ end
         """ |> fd2html_td
 
     @test isapproxstr(h, raw"""
-            <p>A
-            <pre><code class="language-markdown">B
-            </code></pre> C</p>
+            <p>A</p>
+            <pre><code class="language-markdown">B</code></pre>
+            <p>C</p>
             """)
 
     h = raw"""
@@ -67,11 +67,12 @@ end
         """ |> fd2html_td
 
     @test isapproxstr(h, raw"""
-            <p>A
+            <p>A</p>
             <pre><code class="language-markdown">```julia
             B
             ```
-            </code></pre> C</p>
+            </code></pre>
+            <p>C</p>
             """)
 end
 
@@ -117,13 +118,13 @@ end
         @def title = "hello"
         {{title}}{{title}}
         """ |> fd2html_td
-    @test isapproxstr(s, "hellohello")
+    @test isapproxstr(s, "<p>hellohello</p>")
     s = """
         @def a_b = "hello"
         @def c_d = "goodbye"
         {{a_b}}{{c_d}}
         """ |> fd2html_td
-    @test isapproxstr(s, "hellogoodbye")
+    @test isapproxstr(s, "<p>hellogoodbye</p>")
 end
 
 # issue 424 with double braces
@@ -133,8 +134,8 @@ end
         {{title}}
         $\rho=\frac{e^{-\beta \mathcal{E}_{s}}} {\mathcal{Z}} $
         """ |> fd2html_td
-    @test isapproxstr(s, """
-        hello \\(\\rho=\\frac{e^{-\\beta \\mathcal{E}_{s}}} {\\mathcal{Z}} \\)""")
+    @test s // raw"""
+                <p>hello \(\rho=\frac{e^{-\beta \mathcal{E}_{s}}} {\mathcal{Z}} \)</p>"""
 end
 
 # issue 432 and consequences
@@ -148,8 +149,8 @@ end
         ---
         """ |> fd2html_td
     @test isapproxstr(s, """
-        <p>hello<sup id="fnref:a"><a href="#fndef:a" class="fnref">[1]</a></sup>
-        <table class="fndef" id="fndef:a">
+        <p>hello<sup id="fnref:a"><a href="#fndef:a" class="fnref">[1]</a></sup></p>
+        <p><table class="fndef" id="fndef:a">
             <tr>
                 <td class="fndef-backref"><a href="#fnref:a">[1]</a></td>
                 <td class="fndef-content">world</td>
diff --git a/test/runtests.jl b/test/runtests.jl
index 8f744b17..d13af1a6 100644
--- a/test/runtests.jl
+++ b/test/runtests.jl
@@ -119,6 +119,10 @@ include("integration/literate_extras.jl")
 flush_td()
 cd(joinpath(dirname(dirname(pathof(Franklin)))))
 
+println("HTML validation")
+include("html/closep.jl")
+include("html/closep_lx.jl")
+
 println("COVERAGE")
 include("coverage/extras1.jl")
 include("coverage/paths.jl")
diff --git a/test/test_utils.jl b/test/test_utils.jl
index 6f80459c..09296e80 100644
--- a/test/test_utils.jl
+++ b/test/test_utils.jl
@@ -17,6 +17,9 @@ set_curpath(path) =
 isapproxstr(s1::AbstractString, s2::AbstractString) =
     isequal(map(s->replace(s, r"\s|\n"=>""), String.((s1, s2)))...)
 
+# stricter than isapproxstr, just strips the outside.
+(//)(s1::String, s2::String) = strip(s1) == strip(s2)
+
 # this is a slightly ridiculous requirement but apparently the `eval` blocks
 # don't play well with Travis nor windows while testing, so you just need to forcibly
 # specify that LinearAlgebra and Random are used (even though the included block says
diff --git a/test/utils/errors.jl b/test/utils/errors.jl
index 1b112ff6..29973edf 100644
--- a/test/utils/errors.jl
+++ b/test/utils/errors.jl
@@ -59,7 +59,7 @@ end
         \hello
         End
         """ |> fd2html_td
-    @test isapproxstr(s, "<p>Foo</p> goodbye End</p>")
+    @test isapproxstr(s, "<p>Foo</p><p>goodbye End</p>")
 
     s2 = raw"""
         Foo