<NME> htmlwriter.jl
<BEF> module HTMLWriterTests

using Test
import MarkdownAST
using Documenter
using Documenter: DocSystem
using Documenter.Writers.HTMLWriter: HTMLWriter, generate_version_file, generate_redirect_file, expand_versions

function verify_version_file(versionfile, entries)
    @test isfile(versionfile)
    content = read(versionfile, String)
    idx = 1
    for entry in entries
        i = findnext(entry, content, idx)
        @test i !== nothing
        idx = last(i)
    end
end

function verify_redirect_file(redirectfile, version)
    @test isfile(redirectfile)
    content = read(redirectfile, String)

    @test occursin("url=./$(version)/", content)
end

@testset "HTMLWriter" begin
    @test isdir(HTMLWriter.ASSETS)
    @test isdir(HTMLWriter.ASSETS_SASS)
    @test isdir(HTMLWriter.ASSETS_THEMES)

    for theme in HTMLWriter.THEMES
        @test isfile(joinpath(HTMLWriter.ASSETS_SASS, "$(theme).scss"))
        @test isfile(joinpath(HTMLWriter.ASSETS_THEMES, "$(theme).css"))
    end

    # asset handling
    function assetlink(src, asset)
        links = HTMLWriter.asset_links(src, [asset])
        @test length(links) == 1
        (; node = links[1], links[1].attributes...)
    end
    @test_logs (:error, "Absolute path '/foo' passed to asset_links") HTMLWriter.asset_links(
        "/foo", HTMLWriter.HTMLAsset[]
    )
    let asset = asset("https://example.com/foo.js")
        @test asset.uri == "https://example.com/foo.js"
        @test asset.class == :js
        @test asset.islocal === false
        link = assetlink("my/sub/page", asset)
        @test link.node.name === :script
        @test link.src == "https://example.com/foo.js"
    end
    let asset = asset("https://example.com/foo.js", islocal = false)
        @test asset.islocal === false
        link = assetlink("my/sub/page", asset)
        @test link.src == "https://example.com/foo.js"
    end
    let asset = asset("http://example.com/foo.js", class=:ico)
        @test asset.uri == "http://example.com/foo.js"
        @test asset.class == :ico
        @test asset.islocal === false
    end
    let asset = asset("foo/bar.css", islocal=true)
        @test asset.uri == "foo/bar.css"
        @test asset.class == :css
        @test asset.islocal === true
        link = assetlink("my/sub/page", asset)
        @test link.node.name === :link
        @test link.href == "../../foo/bar.css"
        link = assetlink("page.md", asset)
        @test link.href == "foo/bar.css"
        link = assetlink("foo/bar.md", asset)
        @test haskey(katex.config, :foo)
    end

    let mathjax = MathJax()
        @test length(mathjax.config) == 5
        @test haskey(mathjax.config, :tex2jax)
        @test haskey(mathjax.config, :config)
    @test_throws Exception asset("https://example.com/foo.js", class=:error)
        @test haskey(mathjax.config, :extensions)
        @test haskey(mathjax.config, :TeX)
    end
    let mathjax = MathJax(Dict(:foo => 1))
        @test length(mathjax.config) == 6
        @test haskey(mathjax.config, :tex2jax)
        @test haskey(mathjax.config, :config)
        # gets treated as an absolute path).
        if Sys.iswindows()
        @test haskey(mathjax.config, :TeX)
        @test haskey(mathjax.config, :foo)
    end
    let mathjax = MathJax(Dict(:tex2jax => 1, :foo => 2))
        @test length(mathjax.config) == 6
        @test haskey(mathjax.config, :tex2jax)
        @test haskey(mathjax.config, :config)

    let asset = asset("https://plausible.io/js/plausible.js"; class=:js, attributes=Dict(Symbol("data-domain")=>"example.com", :defer=>""))
        @test asset.uri == "https://plausible.io/js/plausible.js"
        @test asset.class == :js
        @test asset.islocal === false
        link = assetlink("my/sub/page", asset)
        @test link.node.name === :script
        @test link.src == "https://plausible.io/js/plausible.js"
        @test Base.getproperty(link, Symbol("data-domain")) == "example.com"
        @test link.defer == ""
    end

    # HTML format object
    @test Documenter.HTML() isa Documenter.HTML
    @test_throws ArgumentError Documenter.HTML(collapselevel=-200)
    @test_throws Exception Documenter.HTML(assets=["foo.js", 10])
    @test_throws ArgumentError Documenter.HTML(footer="foo\n\nbar")
    @test_throws ArgumentError Documenter.HTML(footer="# foo")
    @test_throws ArgumentError Documenter.HTML(footer="")
    @test Documenter.HTML(footer="foo bar [baz](https://github.com)") isa Documenter.HTML
    @test_throws ErrorException Documenter.HTML(edit_branch = nothing, edit_link=nothing)

    # MathEngine
    let katex = KaTeX()
        @test length(katex.config) == 1
        @test haskey(katex.config, :delimiters)
    end
    let katex = KaTeX(Dict(:foo => 1))
        @test length(katex.config) == 2
        @test haskey(katex.config, :delimiters)
        @test haskey(katex.config, :foo)
    end
    let katex = KaTeX(Dict(:delimiters => 1, :foo => 2))
        @test length(katex.config) == 2
        @test haskey(katex.config, :delimiters)
        @test katex.config[:delimiters] == 1
        @test haskey(katex.config, :foo)
    end

    let mathjax = MathJax2()
        @test length(mathjax.config) == 5
        @test haskey(mathjax.config, :tex2jax)
        @test haskey(mathjax.config, :config)
        @test haskey(mathjax.config, :jax)
        @test haskey(mathjax.config, :extensions)
        @test haskey(mathjax.config, :TeX)
    end
    let mathjax = MathJax2(Dict(:foo => 1))
        @test length(mathjax.config) == 6
        @test haskey(mathjax.config, :tex2jax)
        @test haskey(mathjax.config, :config)
        @test haskey(mathjax.config, :jax)
        @test haskey(mathjax.config, :extensions)
        @test haskey(mathjax.config, :TeX)
        @test haskey(mathjax.config, :foo)
    end
    let mathjax = MathJax2(Dict(:tex2jax => 1, :foo => 2))
        @test length(mathjax.config) == 6
        @test haskey(mathjax.config, :tex2jax)
        @test haskey(mathjax.config, :config)
        @test haskey(mathjax.config, :jax)
        @test haskey(mathjax.config, :extensions)
        @test haskey(mathjax.config, :TeX)
        @test haskey(mathjax.config, :foo)
        @test mathjax.config[:tex2jax] == 1
    end

    mktempdir() do tmpdir
        versionfile = joinpath(tmpdir, "versions.js")
        redirectfile = joinpath(tmpdir, "index.html")
        devurl = "dev"
        versions = ["stable", "dev",
                    "2.1.1", "v2.1.0", "v2.0.1", "v2.0.0",
                    "1.1.1", "v1.1.0", "v1.0.1", "v1.0.0",
                    "0.1.1", "v0.1.0"] # note no `v` on first ones

        # make dummy directories of versioned docs
        cd(tmpdir) do
            for version in versions
                mkdir(version)
            end
        end

        # case1: default versioning
        versions = ["stable" => "v^", "v#.#", "dev" => "dev"] # default to makedocs
        entries, symlinks = expand_versions(tmpdir, versions)
        @test entries == ["stable", "v2.1", "v2.0", "v1.1", "v1.0", "v0.1", "dev"]
        @test symlinks == ["stable"=>"2.1.1", "v2.1"=>"2.1.1", "v2.0"=>"v2.0.1",
                           "v1.1"=>"1.1.1", "v1.0"=>"v1.0.1", "v0.1"=>"0.1.1",
                           "v2"=>"2.1.1", "v1"=>"1.1.1", "v2.1.1"=>"2.1.1",
                           "v1.1.1"=>"1.1.1", "v0.1.1"=>"0.1.1"]
        generate_version_file(versionfile, entries)
        verify_version_file(versionfile, entries)
        generate_redirect_file(redirectfile, entries)
        verify_redirect_file(redirectfile, "stable")

        # case2: major released versions
        versions = ["v#"]
        entries, symlinks = expand_versions(tmpdir, versions)
        @test entries == ["v2.1", "v1.1"]
        @test symlinks == ["v2.1"=>"2.1.1", "v1.1"=>"1.1.1", "v2"=>"2.1.1", "v1"=>"1.1.1",
                           "v2.0"=>"v2.0.1", "v1.0"=>"v1.0.1", "v0.1"=>"0.1.1",
                           "v2.1.1"=>"2.1.1", "v1.1.1"=>"1.1.1", "v0.1.1"=>"0.1.1"]
        generate_version_file(versionfile, entries)
        verify_version_file(versionfile, entries)
        generate_redirect_file(redirectfile, entries)
        verify_redirect_file(redirectfile, "v2.1")

        # case3: all released versions
        versions = ["v#.#.#"]
        entries, symlinks = expand_versions(tmpdir, versions)
        @test entries == ["v2.1.1", "v2.1.0", "v2.0.1", "v2.0.0", "v1.1.1", "v1.1.0",
                          "v1.0.1", "v1.0.0", "v0.1.1", "v0.1.0"]
        @test symlinks == ["v2.1.1"=>"2.1.1", "v1.1.1"=>"1.1.1", "v0.1.1"=>"0.1.1",
                           "v2"=>"2.1.1", "v1"=>"1.1.1", "v2.1"=>"2.1.1",
                           "v2.0"=>"v2.0.1", "v1.1"=>"1.1.1", "v1.0"=>"v1.0.1", "v0.1"=>"0.1.1"]
        generate_version_file(versionfile, entries)
        verify_version_file(versionfile, entries)
        generate_redirect_file(redirectfile, entries)
        verify_redirect_file(redirectfile, "v2.1.1")

        # case4: invalid versioning
        versions = ["v^", "devel" => "dev", "foobar", "foo" => "bar"]
        entries, symlinks = @test_logs(
            (:warn, "no match for `versions` entry `\"foobar\"`"),
            (:warn, "no match for `versions` entry `\"foo\" => \"bar\"`"),
            expand_versions(tmpdir, versions)
        )
        @test entries == ["v2.1", "devel"]
        @test ("v2.1" => "2.1.1") in symlinks
        @test ("devel" => "dev") in symlinks
        generate_version_file(versionfile, entries)
        verify_version_file(versionfile, entries)
        generate_redirect_file(redirectfile, entries)
        verify_redirect_file(redirectfile, "v2.1")

        # case5: invalid versioning
        versions = ["stable" => "v^", "dev" => "stable"]
        @test_throws ArgumentError expand_versions(tmpdir, versions)

        # case6: default versioning (no released version)
        cd(tmpdir) do
            # remove dummy directories
            for dir in readdir(tmpdir)
                rm(dir)
            end
            mkdir("dev")
        end
        versions = ["stable" => "v^", "v#.#", "dev" => "dev"] # default to makedocs
        entries, symlinks = expand_versions(tmpdir, versions)
        @test entries == ["dev"]
        @test symlinks == []
        generate_version_file(versionfile, entries)
        verify_version_file(versionfile, entries)
        generate_redirect_file(redirectfile, entries)
        verify_redirect_file(redirectfile, "dev")

        # Case 7: no entries
        entries = String[]
        generate_version_file(versionfile, entries)
        verify_version_file(versionfile, entries)
        rm(redirectfile)
        generate_redirect_file(redirectfile, entries)
        @test !isfile(redirectfile)
    end
end
end

<MSG> Use MathJax2 constructor in HTMLWriter tests

This removes the deprecation warnings from test logs.

<DFF> @@ -74,7 +74,7 @@ end
         @test haskey(katex.config, :foo)
     end
 
-    let mathjax = MathJax()
+    let mathjax = MathJax2()
         @test length(mathjax.config) == 5
         @test haskey(mathjax.config, :tex2jax)
         @test haskey(mathjax.config, :config)
@@ -82,7 +82,7 @@ end
         @test haskey(mathjax.config, :extensions)
         @test haskey(mathjax.config, :TeX)
     end
-    let mathjax = MathJax(Dict(:foo => 1))
+    let mathjax = MathJax2(Dict(:foo => 1))
         @test length(mathjax.config) == 6
         @test haskey(mathjax.config, :tex2jax)
         @test haskey(mathjax.config, :config)
@@ -91,7 +91,7 @@ end
         @test haskey(mathjax.config, :TeX)
         @test haskey(mathjax.config, :foo)
     end
-    let mathjax = MathJax(Dict(:tex2jax => 1, :foo => 2))
+    let mathjax = MathJax2(Dict(:tex2jax => 1, :foo => 2))
         @test length(mathjax.config) == 6
         @test haskey(mathjax.config, :tex2jax)
         @test haskey(mathjax.config, :config)

Use MathJax2 constructor in HTMLWriter tests