in

Kotlin/JS testing – take a screenshot of my site and compare it with a screenshot


I want to write unit tests in Kotlin/JS in which I can compare the code-under-test with a screenshot. This would be much nicer than a HTML comparison (which I have asked about previously), as it would be more clear about what the actual and expected results are, and what the differences are.

I am very unfamiliar with the Javascript ecosystem, and I suspect that wrapping this behind Kotlin/JS is not making it easier!

Project setup

  • Kotlin/JS 1.6.10
  • Kotlin/JS is targeting Legacy and IR, browers, testing with Karma and Chrome Headless
    kotlin {
        js {
            compilations.all {
                kotlinOptions {
                    moduleKind = "umd"
                }
            }
            nodejs {
                testTask {
                    useKarma {
                        useChromeHeadless()
    //                    useDebuggableChrome()
                    }
                }
            }
        }
    }
    

What I’ve tried

html2canvas

KodeinKoders/Kodein-Code2Image has an example where html2canvas does work! But when I’ve tried it in unit tests it produces a completely blank screenshot. Might this be an issue with using html2canvas in a test and Chrome headless?

test config
    @Test
    fun testScreenshot() {
        // example document created with kotlinx.html
        val testStringSimple = document.create.html {
            body {
                div {
                    p { text("test blah blah blah") }
                    text("test")
                }
            }
        }
        testStringSimple.scrollIntoView()

        // the options don't make a difference, except for the background colour!
        val canvas = takeScreenshot(element) {
//            width = window.innerWidth
//            height = window.innerHeight
//            scrollX = window.pageXOffset
//            scrollY = window.pageYOffset
//            foreignObjectRendering = true
            backgroundColor = "#5FACA1" // a nice teal colour
//            allowTaint = true
//            scale = 0.5f
        }
        val data = canvas.toDataURL("image/png")
        println("nn---nncanvas data = $datann---nn")
    }

    @OptIn(ExperimentalCoroutinesApi::class)
    suspend fun takeScreenshot(
        element: HTMLElement,
        options: Html2CanvasOptions.() -> Unit = {}
    ): HTMLCanvasElement {
//        document.body?.style?.overflowY = "hidden"

        val h2cOpts = js("{}").unsafeCast<Html2CanvasOptions>()
        h2cOpts.options()

        return html2canvas(document.body!!, h2cOpts)
            .asDeferred()
            .await()
    }
output
#1', '0ms', 
'Starting document clone with size 783x600 scrolled to 0,0#1', '75ms', 
'Document cloned, element located at 0,0 with size 783x600 using computed rendering#1', '75ms', 
'Starting DOM parsing#1', '79ms', 
'Starting renderer for element at 0,0 with size 783x600#1', '82ms', 
'Canvas renderer initialized (783x600) with scale 1#1', '84ms', 
'Finished rendering
---

canvas data = data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAw8AAAJYCAYAAAAkINotAAAAAXNSR0IArs4c6QAAIABJREFUeF7t1zERACAQBDFQijZ6PIIEVkC+/ipzzc519h2OAAECBAgQIECAAAECH4EpHmyEAAECBAgQIECAAIEiIB6Kkh8CBAgQIECAAAECBIZ4MAICBAgQIECAAAECBJKAeEhMnggQIECAAAECBAgQEA82QIAAAQIE...<output trimmed>

html2canvas Kotlin externals

I added an npm dependency and created the following externals to allow for html2canvas interop.

    implementation(npm("html2canvas", "1.3.4"))
package externals.html2canvas

import kotlin.js.Promise
import org.w3c.dom.Document
import org.w3c.dom.Element
import org.w3c.dom.HTMLCanvasElement
import org.w3c.dom.HTMLElement

@JsNonModule
@JsModule("html2canvas")
external fun html2canvas(element: HTMLElement, options: Html2CanvasOptions = definedExternally): Promise<HTMLCanvasElement>

/** [`https://github.com/niklasvh/html2canvas/blob/v1.3.4/src/index.ts#L10`](https://github.com/niklasvh/html2canvas/blob/v1.3.4/src/index.ts#L10) */
@JsName("Options")
external interface Html2CanvasOptions  { 
    // the options fields are defined here...
}

Puppeteer

Puppeteer can take screenshots… but I’m not sure how to configure this in a Kotlin/JS test environment.

I see that Puppeteer is configured in the kotlin-wrappers project, but when I’ve tried copying this configuration and I run the tests, they freeze. Even if this did work I’m not sure what the next step would be to be able to connect Puppeteer to the Chrome instance, and then to initiate a screenshot.

Puppeteer is also configured in this project: https://github.com/sakebook/kotlin-js-headless-chrome, but I’m not sure how to translate this so it’s available during a unit test.

main/src/main/kotlin/Main.kt#L86
suspend fun capturePage(url: String, tmpDir: String) = coroutineScope {
    kotlin.js.console.log("capturePage start")
    val browser = Puppeteer.launch(options = object : LaunchOptions {
        override var args: Array<String>? = arrayOf("--no-sandbox", "--disable-setuid-sandbox", "--single-process")
    }).await()
    val page = browser.newPage().await()
    page.goto(url).await()
    page.waitForTimeout(5000).await()
    page.screenshot(object : PageScreenshotOptions {
        override var path: String? = "${tmpDir}/$FILE_NAME"
        override var fullPage: Boolean? = true
    }).await()
    page.close().await()
    browser.close().await()
}

Screen Capture API

I haven’t investigated, but is using the Screen Capture API possible?

CRI Library? Command line arg?

There’s lots of options for taking screenshots described on this page
https://developers.google.com/web/updates/2017/04/headless-chrome, but I don’t know how to use them during a Kotlin/JS test.



Source: https://stackoverflow.com/questions/70554798/kotlin-js-testing-take-a-screenshot-of-my-site-and-compare-it-with-a-screensho

Nomadago — Share your travel calendar and schedule trips with friends

A Video Conference App With Flutter