1

I'm writing a unit test where I'd like to compare some HTML that's generated by a library (KVision) to some expected HTML.

To do this I wanted to format the HTML so it's laid out nicely, and the expected and actual HTML can be compared easily.

Beautifying HTML isn't necessarily required. If there's some Kotlin/JS library that will do a HTML comparison so the differences are clearly reported, that would work!

Requirements

  1. To reduce maintenance requirements, I'd prefer to use a library to resolve this rather than custom code. And I'd prefer that the library is in Kotlin/JS, not a wrapped NPM dependency
  2. the formatter should be lenient, and not try to clean up invalid HTML, so that the test can fail if invalid HTML is provided or generated

The test

The test generates a LeafletJS map and adds it to a root element. (I'm developing a new feature for KVision, so the code below is not available yet.)

import io.kotest.matchers.shouldBe
import io.kvision.MapsModule
import io.kvision.maps.Maps
import io.kvision.panel.ContainerType
import io.kvision.panel.Root
import io.kvision.test.DomSpec
import io.kvision.utils.px
import kotlin.test.Test
import kotlin.test.assertTrue
import kotlinx.browser.document
import kotlinx.html.html
import kotlinx.html.stream.appendHTML
import kotlinx.html.unsafe
import nl.adaptivity.xmlutil.JSDomReader
import nl.adaptivity.xmlutil.serialization.DefaultXmlSerializationPolicy
import nl.adaptivity.xmlutil.serialization.XML
import nl.adaptivity.xmlutil.serialization.XmlSerializationPolicy


@Test
fun example() = run {

    val root = Root("test", containerType = ContainerType.FIXED)

    val map = Maps {
        width = 300.px
        height = 600.px

        configureMap {
            setView(LatLng(55, 33), 11)
            options.crs = CRS.Simple

            MapsModule.convertTileLayer(BaseTileLayer.EMPTY)
                .addTo(this)
        }
    }
    root.add(map)
    // 'document' is from kotlinx.browser
    val element = document.getElementById("test")!!

    val actualHtml : String = element.innerHTML
    val expectedHtml: String = """
        <div class="leaflet-container leaflet-touch leaflet-fade-anim leaflet-grab leaflet-touch-drag leaflet-touch-zoom" tabindex="0" style="width: 300px; height: 600px; position: relative;">
          <!-- some expected HTML... -->
        </div>
        """.trimIndent()

    actualHtml shouldBe expectedHtml
}

I'm using Kotest for the string-comparison, because that triggers IntelliJ's 'Click to see difference' link in the console.

'Click to see difference' in the console log

Comparison

However the generated code is compact HTML, and so the differences between the expected and actual HTML are impossible to see.

IntelliJ difference screen, showing expected HTML (that's pretty-formatted) vs the actual HTML (which is all on a single line)

I'd like to beautify both the expected and actual HTML so that the differences can easily be seen.

Attempts

Custom string manipulation

I've tried inserting newlines on every closing-angled-bracket >, and while consistent this isn't 'correct' and requires the HTML attributes are always displayed in a particular order, which might not always be the case.

// condense the expected-HTML to a single line
val expectedHtmlMinified: String = expectedHtml.split("\n").joinToString("") { it.trim() }

// split up the HTML so it's easier to compare
actualHtml.replace(">", ">\n") shouldBe expectedHtmlMinified.replace(">", ">\n")

The differences are now easier to see:

IntelliJ diff tool showing differences between expected and actual HTML, which are both multi-line and not condensed to a single line. Lines 5-9 have differences highlighted

XmlUtil

XmlUtil is available for Kotlin/JS, and has mentions of pretty-printing and setting indentation, but I think it requires setting up classes with annotations, or a custom Serialization strategy - and I'm not doing that for the whole HTML schema! If it's possible for XmlUtil to read in a w3c DOM Document and pretty-printing it out, that would be lovely.

kotlinx.html

kotlinx.html - it doesn't look like this can parse either a w3c DOM Document, or parse a HTML string.

I tried using the unsafe { } block (documented here), but while the HTML elements that are constructed with the DSL are formatted, the raw HTML is unformatted.

val actual = buildString {
    appendHTML(prettyPrint = true).html {
        unsafe {
            raw(element.innerHTML)
        }
    }
}
aSemy
  • 5,485
  • 2
  • 25
  • 51
  • not the best solution, but you can try adding [JSoup](https://jsoup.org/) as a test dependency and parse the HTML then call the `toString` method on the resulting `Document` object – user2340612 Dec 31 '21 at 11:55
  • 1
    @user2340612 JSoup is a Java library and so it's not compatible. Any solution has to be available in Javascript or Kotlin/JS. – aSemy Dec 31 '21 at 12:07
  • Perhaps HTML comparison can be done with https://github.com/petertrr/kotlin-multiplatform-diff – aSemy Jul 07 '22 at 09:07

0 Answers0