我想在Kotlin/JS中编写单元测试,我可以在其中将被测代码与屏幕截图进行比较。这将比 HTML 比较(我之前询问过)要好得多,因为它会更清楚实际和预期的结果是什么,以及差异是什么。
我相信这也称为“快照测试”。
我对 Javascript 生态系统非常不熟悉,我怀疑将其封装在 Kotlin/JS 后面并没有让它变得更容易!
要求
- 我可以将要测试的 HTML 转换为图像
- 我可以从磁盘加载预期的图像
- 我可以比较预期图像和实际图像
我希望答案只需要 Kotlin/JS 浏览器,因为这是我正在使用的库已经在使用的。
项目设置
- 科特林/JS 1.6.10
- Kotlin/JS 的目标是浏览
Legacy
器IR
,使用 Karma 和 Chrome Headless 进行测试kotlin { js { compilations.all { kotlinOptions { moduleKind = "umd" } } nodejs { testTask { useKarma { useChromeHeadless() // useDebuggableChrome() } } } } }
我试过的
到目前为止,我专注于捕获图像,而不是加载预期的图像或生成比较。
html2canvas
KodeinKoders/Kodein-Code2Image有一个例子,其中html2canvas 确实有效!但是当我在单元测试中尝试过它时,它会产生一个完全空白的屏幕截图。这可能是在 Chrome 无头测试中使用 html2canvas 的问题吗?
测试
import kotlin.test.Test
import kotlinx.browser.document
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import kotlinx.html.body
import kotlinx.html.div
import kotlinx.html.dom.create
import kotlinx.html.html
import kotlinx.html.p
import kotlinx.coroutines.asDeferred
import org.w3c.dom.HTMLCanvasElement
import org.w3c.dom.HTMLElement
class ScreenshotTest {
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun addMapToRoot() = runTest {
// 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(testStringSimple) {
useCORS = true
// width = window.innerWidth
// height = window.innerHeight
// scrollX = window.pageXOffset
// scrollY = window.pageYOffset
foreignObjectRendering = true
// backgroundColor = "#5FACA1"
allowTaint = true
// scale = 0.5f
}
val data = canvas.toDataURL("image/png")
println("\n\n---\n\ncanvas data = $data\n\n---\n\n")
}
}
@OptIn(ExperimentalCoroutinesApi::class)
suspend fun takeScreenshot(
element: HTMLElement,
options: Html2CanvasOptions.() -> Unit = {}
): HTMLCanvasElement {
// document.body?.style?.overflowY = "hidden"
val h2cOpts = js("{}").unsafeCast<Html2CanvasOptions>().apply(options)
return html2canvas(element, h2cOpts)
.asDeferred()
.await()
}
输出
#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 外部
我添加了一个 npm 依赖项并创建了以下外部组件以允许 html2canvas 互操作。
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 可以截屏……但我不确定如何在 Kotlin/JS 测试环境中配置它。
我看到在kotlin-wrappers项目中配置了 Puppeteer,但是当我尝试复制此配置并运行测试时,它们冻结了。即使这确实有效,我也不确定下一步如何将 Puppeteer 连接到 Chrome 实例,然后启动屏幕截图。
在这个项目中也配置了 Puppeteer:https ://github.com/sakebook/kotlin-js-headless-chrome ,但我不知道如何翻译它,所以它在单元测试期间可用。
主/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()
}
截屏 API
我没有调查过,但是否可以使用Screen Capture API?
国际广播电台图书馆?命令行参数?
在此页面 https://developers.google.com/web/updates/2017/04/headless-chrome上有很多用于截取屏幕截图的选项,但我不知道如何在 Kotlin/JS 测试期间使用它们。