diff --git a/core/assets/test/components/CalendarChart/base.png b/core/assets/test/components/CalendarChart/base.png index a4217754..76287716 100644 Binary files a/core/assets/test/components/CalendarChart/base.png and b/core/assets/test/components/CalendarChart/base.png differ diff --git a/core/assets/test/components/CalendarChart/scroll.png b/core/assets/test/components/CalendarChart/scroll.png index 691599a9..16744a29 100644 Binary files a/core/assets/test/components/CalendarChart/scroll.png and b/core/assets/test/components/CalendarChart/scroll.png differ diff --git a/core/assets/test/components/CanvasTest.png b/core/assets/test/components/CanvasTest.png new file mode 100644 index 00000000..75b3d8f2 Binary files /dev/null and b/core/assets/test/components/CanvasTest.png differ diff --git a/core/assets/test/components/HabitListHeader/light.png b/core/assets/test/components/HabitListHeader/light.png index e192b878..c146b916 100644 Binary files a/core/assets/test/components/HabitListHeader/light.png and b/core/assets/test/components/HabitListHeader/light.png differ diff --git a/core/assets/test/components/NumberButton/render_above.png b/core/assets/test/components/NumberButton/render_above.png index a8bf60e6..4673ef0f 100644 Binary files a/core/assets/test/components/NumberButton/render_above.png and b/core/assets/test/components/NumberButton/render_above.png differ diff --git a/core/assets/test/components/NumberButton/render_below.png b/core/assets/test/components/NumberButton/render_below.png index d5ab438b..c3ffb68d 100644 Binary files a/core/assets/test/components/NumberButton/render_below.png and b/core/assets/test/components/NumberButton/render_below.png differ diff --git a/core/assets/test/components/NumberButton/render_zero.png b/core/assets/test/components/NumberButton/render_zero.png index 867bf1c7..235de2f6 100644 Binary files a/core/assets/test/components/NumberButton/render_zero.png and b/core/assets/test/components/NumberButton/render_zero.png differ diff --git a/core/assets/test/components/Ring/draw1.png b/core/assets/test/components/Ring/draw1.png index e61c9c24..d5e80f45 100644 Binary files a/core/assets/test/components/Ring/draw1.png and b/core/assets/test/components/Ring/draw1.png differ diff --git a/core/assets/test/components/old/common/BarChart/render.png b/core/assets/test/components/old/common/BarChart/render.png deleted file mode 100644 index 3b243ba6..00000000 Binary files a/core/assets/test/components/old/common/BarChart/render.png and /dev/null differ diff --git a/core/assets/test/components/old/common/BarChart/renderDataOffset.png b/core/assets/test/components/old/common/BarChart/renderDataOffset.png deleted file mode 100644 index 11b14b81..00000000 Binary files a/core/assets/test/components/old/common/BarChart/renderDataOffset.png and /dev/null differ diff --git a/core/assets/test/components/old/common/BarChart/renderDifferentSize.png b/core/assets/test/components/old/common/BarChart/renderDifferentSize.png deleted file mode 100644 index c6f70b9f..00000000 Binary files a/core/assets/test/components/old/common/BarChart/renderDifferentSize.png and /dev/null differ diff --git a/core/assets/test/components/old/common/BarChart/renderTransparent.png b/core/assets/test/components/old/common/BarChart/renderTransparent.png deleted file mode 100644 index 21226048..00000000 Binary files a/core/assets/test/components/old/common/BarChart/renderTransparent.png and /dev/null differ diff --git a/core/assets/test/components/old/common/FrequencyChart/render.png b/core/assets/test/components/old/common/FrequencyChart/render.png deleted file mode 100644 index 3d7861df..00000000 Binary files a/core/assets/test/components/old/common/FrequencyChart/render.png and /dev/null differ diff --git a/core/assets/test/components/old/common/FrequencyChart/renderDataOffset.png b/core/assets/test/components/old/common/FrequencyChart/renderDataOffset.png deleted file mode 100644 index a4240726..00000000 Binary files a/core/assets/test/components/old/common/FrequencyChart/renderDataOffset.png and /dev/null differ diff --git a/core/assets/test/components/old/common/FrequencyChart/renderDifferentSize.png b/core/assets/test/components/old/common/FrequencyChart/renderDifferentSize.png deleted file mode 100644 index 0fefe39b..00000000 Binary files a/core/assets/test/components/old/common/FrequencyChart/renderDifferentSize.png and /dev/null differ diff --git a/core/assets/test/components/old/common/FrequencyChart/renderTransparent.png b/core/assets/test/components/old/common/FrequencyChart/renderTransparent.png deleted file mode 100644 index 3d7861df..00000000 Binary files a/core/assets/test/components/old/common/FrequencyChart/renderTransparent.png and /dev/null differ diff --git a/core/assets/test/components/old/common/HistoryChart/render.png b/core/assets/test/components/old/common/HistoryChart/render.png deleted file mode 100644 index 95c71e67..00000000 Binary files a/core/assets/test/components/old/common/HistoryChart/render.png and /dev/null differ diff --git a/core/assets/test/components/old/common/HistoryChart/renderDataOffset.png b/core/assets/test/components/old/common/HistoryChart/renderDataOffset.png deleted file mode 100644 index 9fbec7ea..00000000 Binary files a/core/assets/test/components/old/common/HistoryChart/renderDataOffset.png and /dev/null differ diff --git a/core/assets/test/components/old/common/HistoryChart/renderDifferentSize.png b/core/assets/test/components/old/common/HistoryChart/renderDifferentSize.png deleted file mode 100644 index f2a712b8..00000000 Binary files a/core/assets/test/components/old/common/HistoryChart/renderDifferentSize.png and /dev/null differ diff --git a/core/assets/test/components/old/common/HistoryChart/renderTransparent.png b/core/assets/test/components/old/common/HistoryChart/renderTransparent.png deleted file mode 100644 index 860b326d..00000000 Binary files a/core/assets/test/components/old/common/HistoryChart/renderTransparent.png and /dev/null differ diff --git a/core/assets/test/components/old/common/ScoreChart/render.png b/core/assets/test/components/old/common/ScoreChart/render.png deleted file mode 100644 index 4ea430a6..00000000 Binary files a/core/assets/test/components/old/common/ScoreChart/render.png and /dev/null differ diff --git a/core/assets/test/components/old/common/ScoreChart/renderDataOffset.png b/core/assets/test/components/old/common/ScoreChart/renderDataOffset.png deleted file mode 100644 index e0bfe6fd..00000000 Binary files a/core/assets/test/components/old/common/ScoreChart/renderDataOffset.png and /dev/null differ diff --git a/core/assets/test/components/old/common/ScoreChart/renderDifferentSize.png b/core/assets/test/components/old/common/ScoreChart/renderDifferentSize.png deleted file mode 100644 index b550f981..00000000 Binary files a/core/assets/test/components/old/common/ScoreChart/renderDifferentSize.png and /dev/null differ diff --git a/core/assets/test/components/old/common/ScoreChart/renderMonthly.png b/core/assets/test/components/old/common/ScoreChart/renderMonthly.png deleted file mode 100644 index 11ec0904..00000000 Binary files a/core/assets/test/components/old/common/ScoreChart/renderMonthly.png and /dev/null differ diff --git a/core/assets/test/components/old/common/ScoreChart/renderTransparent.png b/core/assets/test/components/old/common/ScoreChart/renderTransparent.png deleted file mode 100644 index e34f4d67..00000000 Binary files a/core/assets/test/components/old/common/ScoreChart/renderTransparent.png and /dev/null differ diff --git a/core/assets/test/components/old/common/ScoreChart/renderYearly.png b/core/assets/test/components/old/common/ScoreChart/renderYearly.png deleted file mode 100644 index df13dae2..00000000 Binary files a/core/assets/test/components/old/common/ScoreChart/renderYearly.png and /dev/null differ diff --git a/core/assets/test/components/old/common/StreakChart/render.png b/core/assets/test/components/old/common/StreakChart/render.png deleted file mode 100644 index 9eb67f54..00000000 Binary files a/core/assets/test/components/old/common/StreakChart/render.png and /dev/null differ diff --git a/core/assets/test/components/old/common/StreakChart/renderSmallSize.png b/core/assets/test/components/old/common/StreakChart/renderSmallSize.png deleted file mode 100644 index 6701961b..00000000 Binary files a/core/assets/test/components/old/common/StreakChart/renderSmallSize.png and /dev/null differ diff --git a/core/assets/test/components/old/common/StreakChart/renderTransparent.png b/core/assets/test/components/old/common/StreakChart/renderTransparent.png deleted file mode 100644 index 9eb67f54..00000000 Binary files a/core/assets/test/components/old/common/StreakChart/renderTransparent.png and /dev/null differ diff --git a/core/assets/test/components/old/widgets/CheckmarkWidget/render.png b/core/assets/test/components/old/widgets/CheckmarkWidget/render.png deleted file mode 100644 index 346a81e7..00000000 Binary files a/core/assets/test/components/old/widgets/CheckmarkWidget/render.png and /dev/null differ diff --git a/core/assets/test/components/old/widgets/CheckmarkWidgetView/checked.png b/core/assets/test/components/old/widgets/CheckmarkWidgetView/checked.png deleted file mode 100644 index 46ac8fd0..00000000 Binary files a/core/assets/test/components/old/widgets/CheckmarkWidgetView/checked.png and /dev/null differ diff --git a/core/assets/test/components/old/widgets/CheckmarkWidgetView/implicitly_checked.png b/core/assets/test/components/old/widgets/CheckmarkWidgetView/implicitly_checked.png deleted file mode 100644 index 1f5aed30..00000000 Binary files a/core/assets/test/components/old/widgets/CheckmarkWidgetView/implicitly_checked.png and /dev/null differ diff --git a/core/assets/test/components/old/widgets/CheckmarkWidgetView/large_size.png b/core/assets/test/components/old/widgets/CheckmarkWidgetView/large_size.png deleted file mode 100644 index 68b695bc..00000000 Binary files a/core/assets/test/components/old/widgets/CheckmarkWidgetView/large_size.png and /dev/null differ diff --git a/core/assets/test/components/old/widgets/CheckmarkWidgetView/unchecked.png b/core/assets/test/components/old/widgets/CheckmarkWidgetView/unchecked.png deleted file mode 100644 index 28d40d4b..00000000 Binary files a/core/assets/test/components/old/widgets/CheckmarkWidgetView/unchecked.png and /dev/null differ diff --git a/core/assets/test/components/old/widgets/FrequencyWidget/render.png b/core/assets/test/components/old/widgets/FrequencyWidget/render.png deleted file mode 100644 index 6991525e..00000000 Binary files a/core/assets/test/components/old/widgets/FrequencyWidget/render.png and /dev/null differ diff --git a/core/assets/test/components/old/widgets/HistoryWidget/render.png b/core/assets/test/components/old/widgets/HistoryWidget/render.png deleted file mode 100644 index 207910f1..00000000 Binary files a/core/assets/test/components/old/widgets/HistoryWidget/render.png and /dev/null differ diff --git a/core/assets/test/components/old/widgets/ScoreWidget/render.png b/core/assets/test/components/old/widgets/ScoreWidget/render.png deleted file mode 100644 index f06396cb..00000000 Binary files a/core/assets/test/components/old/widgets/ScoreWidget/render.png and /dev/null differ diff --git a/core/assets/test/components/old/widgets/StreakWidget/render.png b/core/assets/test/components/old/widgets/StreakWidget/render.png deleted file mode 100644 index 5833a07a..00000000 Binary files a/core/assets/test/components/old/widgets/StreakWidget/render.png and /dev/null differ diff --git a/core/src/main/common/org/isoron/platform/io/Log.kt b/core/src/main/common/org/isoron/platform/io/Log.kt index 6ea8309f..f451b99c 100644 --- a/core/src/main/common/org/isoron/platform/io/Log.kt +++ b/core/src/main/common/org/isoron/platform/io/Log.kt @@ -22,17 +22,22 @@ package org.isoron.platform.io interface Log { fun info(tag: String, msg: String) fun debug(tag: String, msg: String) + fun warn(tag: String, msg: String) } /** * A Log that prints to the standard output. */ class StandardLog : Log { + override fun warn(tag: String, msg: String) { + println(sprintf("W %-20s %s", tag, msg)) + } + override fun info(tag: String, msg: String) { - println("I/$tag $msg") + println(sprintf("I %-20s %s", tag, msg)) } override fun debug(tag: String, msg: String) { - println("D/$tag $msg") + println(sprintf("D %-20s %s", tag, msg)) } } \ No newline at end of file diff --git a/core/src/main/js/org/isoron/platform/gui/HtmlCanvas.kt b/core/src/main/js/org/isoron/platform/gui/JsCanvas.kt similarity index 58% rename from core/src/main/js/org/isoron/platform/gui/HtmlCanvas.kt rename to core/src/main/js/org/isoron/platform/gui/JsCanvas.kt index e1dde3c5..647bf7b9 100644 --- a/core/src/main/js/org/isoron/platform/gui/HtmlCanvas.kt +++ b/core/src/main/js/org/isoron/platform/gui/JsCanvas.kt @@ -19,18 +19,27 @@ package org.isoron.platform.gui +import kotlinx.coroutines.* import org.w3c.dom.* -import kotlin.browser.* +import kotlin.js.* import kotlin.math.* -class HtmlCanvas(val canvas: HTMLCanvasElement) : Canvas { +class JsCanvas(val element: HTMLCanvasElement, + val pixelScale: Double) : Canvas { - val ctx = canvas.getContext("2d") as CanvasRenderingContext2D + val ctx = element.getContext("2d") as CanvasRenderingContext2D var fontSize = 12.0 - var fontWeight = "" - var fontFamily = "sans-serif" + var fontFamily = "NotoRegular" var align = CanvasTextAlign.CENTER + private fun toPixel(x: Double): Double { + return pixelScale * x + } + + private fun toDp(x: Int): Double { + return x / pixelScale + } + override fun setColor(color: Color) { val c = "rgb(${color.red * 255}, ${color.green * 255}, ${color.blue * 255})" ctx.fillStyle = c; @@ -39,45 +48,54 @@ class HtmlCanvas(val canvas: HTMLCanvasElement) : Canvas { override fun drawLine(x1: Double, y1: Double, x2: Double, y2: Double) { ctx.beginPath() - ctx.moveTo(x1 + 0.5, y1 + 0.5) - ctx.lineTo(x2 + 0.5, y2 + 0.5) + ctx.moveTo(toPixel(x1), toPixel(y1)) + ctx.lineTo(toPixel(x2), toPixel(y2)) ctx.stroke() } override fun drawText(text: String, x: Double, y: Double) { - ctx.font = "${fontWeight} ${fontSize}px ${fontFamily}" + ctx.font = "${fontSize}px ${fontFamily}" ctx.textAlign = align ctx.textBaseline = CanvasTextBaseline.MIDDLE - ctx.fillText(text, x, y) + ctx.fillText(text, toPixel(x), toPixel(y + fontSize * 0.05)) } override fun fillRect(x: Double, y: Double, width: Double, height: Double) { - ctx.fillRect(x - 0.5, y - 0.5, width + 1.0, height + 1.0) + ctx.fillRect(toPixel(x), + toPixel(y), + toPixel(width), + toPixel(height)) } override fun drawRect(x: Double, y: Double, width: Double, height: Double) { - ctx.strokeRect(x - 0.5, y - 0.5, width + 1.0, height + 1.0) + ctx.strokeRect(toPixel(x), + toPixel(y), + toPixel(width), + toPixel(height)) } override fun getHeight(): Double { - return canvas.height.toDouble() + return toDp(element.height) } override fun getWidth(): Double { - return canvas.width.toDouble() + return toDp(element.width) } override fun setFont(font: Font) { - fontWeight = if (font == Font.BOLD) "bold" else "" - fontFamily = if (font == Font.FONT_AWESOME) "FontAwesome" else "sans-serif" + fontFamily = when(font) { + Font.REGULAR -> "NotoRegular" + Font.BOLD -> "NotoBold" + Font.FONT_AWESOME -> "FontAwesome" + } } override fun setFontSize(size: Double) { - fontSize = size + fontSize = size * pixelScale } override fun setStrokeWidth(size: Double) { - ctx.lineWidth = size + ctx.lineWidth = size * pixelScale } override fun fillArc(centerX: Double, @@ -85,18 +103,24 @@ class HtmlCanvas(val canvas: HTMLCanvasElement) : Canvas { radius: Double, startAngle: Double, swipeAngle: Double) { + val x = toPixel(centerX) + val y = toPixel(centerY) val from = startAngle / 180 * PI val to = (startAngle + swipeAngle) / 180 * PI ctx.beginPath() - ctx.moveTo(centerX, centerY) - ctx.arc(centerX, centerY, radius, -from, -to, swipeAngle >= 0) - ctx.lineTo(centerX, centerY) + ctx.moveTo(x, y) + ctx.arc(x, y, toPixel(radius), -from, -to, swipeAngle >= 0) + ctx.lineTo(x, y) ctx.fill() } override fun fillCircle(centerX: Double, centerY: Double, radius: Double) { ctx.beginPath() - ctx.arc(centerX, centerY, radius, 0.0, 2 * PI) + ctx.arc(toPixel(centerX), + toPixel(centerY), + toPixel(radius), + 0.0, + 2 * PI) ctx.fill() } @@ -107,4 +131,15 @@ class HtmlCanvas(val canvas: HTMLCanvasElement) : Canvas { TextAlign.RIGHT -> CanvasTextAlign.RIGHT } } + + suspend fun loadImage(src: String) { + Promise { resolve, reject -> + val img = Image() + img.onload = { + ctx.drawImage(img, 0.0, 0.0) + resolve(0) + } + img.src = src + }.await() + } } \ No newline at end of file diff --git a/core/src/main/js/org/isoron/platform/io/JsFiles.kt b/core/src/main/js/org/isoron/platform/io/JsFiles.kt index 50b2faa3..36ba11cf 100644 --- a/core/src/main/js/org/isoron/platform/io/JsFiles.kt +++ b/core/src/main/js/org/isoron/platform/io/JsFiles.kt @@ -24,6 +24,9 @@ import org.w3c.xhr.* import kotlin.js.* class JsFileStorage { + private val TAG = "JsFileStorage" + private val log = StandardLog() + private val indexedDB = eval("indexedDB") private var db: dynamic = null @@ -31,16 +34,16 @@ class JsFileStorage { private val OS_NAME = "Files" suspend fun init() { - console.log("Initializing JsFileStorage...") + log.info(TAG, "Initializing") Promise { resolve, reject -> val req = indexedDB.open(DB_NAME, 2) req.onerror = { reject(Exception("could not open IndexedDB")) } req.onupgradeneeded = { - console.log("Creating document store for JsFileStorage...") + log.info(TAG, "Creating document store") req.result.createObjectStore(OS_NAME) } req.onsuccess = { - console.log("JsFileStorage is ready.") + log.info(TAG, "Ready") db = req.result resolve(0) } diff --git a/core/src/main/js/org/isoron/platform/io/sprintf.kt b/core/src/main/js/org/isoron/platform/io/JsStrings.kt similarity index 95% rename from core/src/main/js/org/isoron/platform/io/sprintf.kt rename to core/src/main/js/org/isoron/platform/io/JsStrings.kt index 924b33e5..6abbc467 100644 --- a/core/src/main/js/org/isoron/platform/io/sprintf.kt +++ b/core/src/main/js/org/isoron/platform/io/JsStrings.kt @@ -20,5 +20,5 @@ package org.isoron.platform.io actual fun sprintf(format: String, vararg args: Any?): String { - TODO() + return js("vsprintf")(format, args) } \ No newline at end of file diff --git a/core/src/main/jvm/org/isoron/platform/gui/JavaCanvas.kt b/core/src/main/jvm/org/isoron/platform/gui/JavaCanvas.kt index f8f34055..416ca5fd 100644 --- a/core/src/main/jvm/org/isoron/platform/gui/JavaCanvas.kt +++ b/core/src/main/jvm/org/isoron/platform/gui/JavaCanvas.kt @@ -19,6 +19,7 @@ package org.isoron.platform.gui +import kotlinx.coroutines.* import org.isoron.platform.io.* import java.awt.* import java.awt.RenderingHints.* @@ -26,14 +27,6 @@ import java.awt.font.* import java.awt.image.* import kotlin.math.* -fun createFont(path: String): java.awt.Font { - val file = JavaFileOpener().openResourceFile(path) as JavaResourceFile - return java.awt.Font.createFont(0, file.stream()) -} - -private val NOTO_REGULAR_FONT = createFont("fonts/NotoSans-Regular.ttf") -private val NOTO_BOLD_FONT = createFont("fonts/NotoSans-Bold.ttf") -private val FONT_AWESOME_FONT = createFont("fonts/FontAwesome.ttf") class JavaCanvas(val image: BufferedImage, val pixelScale: Double = 2.0) : Canvas { @@ -46,6 +39,10 @@ class JavaCanvas(val image: BufferedImage, val heightPx = image.height val g2d = image.createGraphics() + private val NOTO_REGULAR_FONT = createFont("fonts/NotoSans-Regular.ttf") + private val NOTO_BOLD_FONT = createFont("fonts/NotoSans-Bold.ttf") + private val FONT_AWESOME_FONT = createFont("fonts/FontAwesome.ttf") + init { g2d.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON); g2d.setRenderingHint(KEY_TEXT_ANTIALIASING, VALUE_TEXT_ANTIALIAS_ON); @@ -73,6 +70,7 @@ class JavaCanvas(val image: BufferedImage, } override fun drawText(text: String, x: Double, y: Double) { + updateFont() val bounds = g2d.font.getStringBounds(text, frc) val bWidth = bounds.width.roundToInt() val bHeight = bounds.height.roundToInt() @@ -122,7 +120,7 @@ class JavaCanvas(val image: BufferedImage, } override fun setStrokeWidth(size: Double) { - g2d.setStroke(BasicStroke(size.toFloat())) + g2d.stroke = BasicStroke((size * pixelScale).toFloat()) } private fun updateFont() { @@ -158,4 +156,10 @@ class JavaCanvas(val image: BufferedImage, override fun setTextAlign(align: TextAlign) { this.textAlign = align } + + private fun createFont(path: String) = runBlocking { + val file = JavaFileOpener().openResourceFile(path) as JavaResourceFile + if (!file.exists()) throw RuntimeException("File not found: ${file.path}") + java.awt.Font.createFont(0, file.stream()) + } } \ No newline at end of file diff --git a/core/src/main/jvm/org/isoron/platform/io/JavaFiles.kt b/core/src/main/jvm/org/isoron/platform/io/JavaFiles.kt index 263eaa3c..2092794d 100644 --- a/core/src/main/jvm/org/isoron/platform/io/JavaFiles.kt +++ b/core/src/main/jvm/org/isoron/platform/io/JavaFiles.kt @@ -22,7 +22,7 @@ package org.isoron.platform.io import java.io.* import java.nio.file.* -class JavaResourceFile(private val path: String) : ResourceFile { +class JavaResourceFile(val path: String) : ResourceFile { private val javaPath: Path get() { val mainPath = Paths.get("assets/main/$path") @@ -41,7 +41,9 @@ class JavaResourceFile(private val path: String) : ResourceFile { override suspend fun copyTo(dest: UserFile) { if (dest.exists()) dest.delete() - Files.copy(javaPath, (dest as JavaUserFile).path) + val destPath = (dest as JavaUserFile).path + destPath.toFile().parentFile?.mkdirs() + Files.copy(javaPath, destPath) } fun stream(): InputStream { diff --git a/core/src/test/common/org/isoron/DependencyResolver.kt b/core/src/test/common/org/isoron/DependencyResolver.kt index 2e30fdda..289e99b1 100644 --- a/core/src/test/common/org/isoron/DependencyResolver.kt +++ b/core/src/test/common/org/isoron/DependencyResolver.kt @@ -29,7 +29,8 @@ enum class Locale { interface CanvasHelper { fun createCanvas(width: Int, height: Int): Canvas - fun exportCanvas(canvas: Canvas, filename: String) + suspend fun exportCanvas(canvas: Canvas, filename: String) + suspend fun compare(imageFile: ResourceFile, canvas: Canvas): Double } expect object DependencyResolver { diff --git a/core/src/test/common/org/isoron/platform/gui/CanvasTest.kt b/core/src/test/common/org/isoron/platform/gui/CanvasTest.kt index 5d3b1522..2d485688 100644 --- a/core/src/test/common/org/isoron/platform/gui/CanvasTest.kt +++ b/core/src/test/common/org/isoron/platform/gui/CanvasTest.kt @@ -20,12 +20,13 @@ package org.isoron.platform.gui import org.isoron.* +import org.isoron.uhabits.* import kotlin.test.* -class CanvasTest { +class CanvasTest: BaseViewTest() { @Test - fun run() { - if (!DependencyResolver.supportsCanvasTests) return + fun run() = asyncTest{ + if (!DependencyResolver.supportsCanvasTests) return@asyncTest val helper = DependencyResolver.getCanvasHelper() val canvas = helper.createCanvas(500, 400) @@ -67,6 +68,6 @@ class CanvasTest { canvas.setFont(Font.FONT_AWESOME) canvas.drawText(FontAwesome.CHECK, 250.0, 300.0) - helper.exportCanvas(canvas, "CanvasTest.png") + assertRenders("components/CanvasTest.png", canvas) } } \ No newline at end of file diff --git a/core/src/test/common/org/isoron/platform/io/FilesTest.kt b/core/src/test/common/org/isoron/platform/io/FilesTest.kt index 36cedaae..f039abc9 100644 --- a/core/src/test/common/org/isoron/platform/io/FilesTest.kt +++ b/core/src/test/common/org/isoron/platform/io/FilesTest.kt @@ -40,7 +40,7 @@ class FilesTest() { assertEquals("Hello World!", lines[0]) assertEquals("This is a resource.", lines[1]) - val helloCopy = fileOpener.openUserFile("hello-copy.txt") + val helloCopy = fileOpener.openUserFile("copies/hello.txt") hello.copyTo(helloCopy) lines = helloCopy.lines() assertEquals("Hello World!", lines[0]) diff --git a/core/src/test/common/org/isoron/platform/io/StringsTest.kt b/core/src/test/common/org/isoron/platform/io/StringsTest.kt new file mode 100644 index 00000000..b8068340 --- /dev/null +++ b/core/src/test/common/org/isoron/platform/io/StringsTest.kt @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2016-2019 Álinson Santos Xavier + * + * This file is part of Loop Habit Tracker. + * + * Loop Habit Tracker is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Loop Habit Tracker is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +package org.isoron.platform.io + +import kotlin.test.* + +class StringsTest { + @Test + fun testSprintf() { + assertEquals(" 5", sprintf("%3d", 5)) + assertEquals("005", sprintf("%03d", 5)) + assertEquals("005", sprintf("%03d", 5)) + assertEquals(" 45", sprintf("%3d", 45)) + assertEquals("145", sprintf("%3d", 145)) + assertEquals(" 9 9", sprintf("%3d%3d", 9, 9)) + assertEquals(" 13.42", sprintf("%8.2f", 13.419187263)) + assertEquals("00013.42", sprintf("%08.2f", 13.419187263)) + assertEquals("13.42 ", sprintf("%-8.2f", 13.419187263)) + } +} \ No newline at end of file diff --git a/core/src/test/common/org/isoron/uhabits/BaseViewTest.kt b/core/src/test/common/org/isoron/uhabits/BaseViewTest.kt new file mode 100644 index 00000000..59c554ea --- /dev/null +++ b/core/src/test/common/org/isoron/uhabits/BaseViewTest.kt @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2016-2019 Álinson Santos Xavier + * + * This file is part of Loop Habit Tracker. + * + * Loop Habit Tracker is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Loop Habit Tracker is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +package org.isoron.uhabits + +import org.isoron.* +import org.isoron.platform.gui.* +import org.isoron.uhabits.components.* +import kotlin.test.* + +var SIMILARITY_THRESHOLD = 5.0 + +open class BaseViewTest { + var theme = LightTheme() + suspend fun assertRenders(width: Int, + height: Int, + expectedPath: String, + component: Component) { + + val helper = DependencyResolver.getCanvasHelper() + val canvas = helper.createCanvas(width, height) + component.draw(canvas) + assertRenders(expectedPath, canvas) + } + + suspend fun assertRenders(expectedPath: String, + canvas: Canvas) { + + val helper = DependencyResolver.getCanvasHelper() + val fileOpener = DependencyResolver.getFileOpener() + val expectedFile = fileOpener.openResourceFile(expectedPath) + val actualPath = "/failed/${expectedPath}" + + if (expectedFile.exists()) { + val d = helper.compare(expectedFile, canvas) + if (d >= SIMILARITY_THRESHOLD) { + helper.exportCanvas(canvas, actualPath) + val expectedCopy = expectedPath.replace(".png", ".expected.png") + expectedFile.copyTo(fileOpener.openUserFile("/failed/$expectedCopy")) + fail("Images differ (distance=${d}). Actual rendered saved to ${actualPath}.") + } + } else { + helper.exportCanvas(canvas, actualPath) + fail("Expected file is missing. Actual render saved to $actualPath") + } + } +} \ No newline at end of file diff --git a/core/src/test/jvm/org/isoron/uhabits/components/CalendarChartTest.kt b/core/src/test/common/org/isoron/uhabits/components/CalendarChartTest.kt similarity index 80% rename from core/src/test/jvm/org/isoron/uhabits/components/CalendarChartTest.kt rename to core/src/test/common/org/isoron/uhabits/components/CalendarChartTest.kt index dae5670a..dee24ccf 100644 --- a/core/src/test/jvm/org/isoron/uhabits/components/CalendarChartTest.kt +++ b/core/src/test/common/org/isoron/uhabits/components/CalendarChartTest.kt @@ -19,32 +19,30 @@ package org.isoron.uhabits.components +import org.isoron.* import org.isoron.platform.time.* import org.isoron.uhabits.* -import org.junit.* -import java.util.* +import kotlin.test.* class CalendarChartTest : BaseViewTest() { val base = "components/CalendarChart" @Test - fun testDraw() { + fun testDraw() = asyncTest { + val fmt = DependencyResolver.getDateFormatter(Locale.US) val component = CalendarChart(LocalDate(2015, 1, 25), theme.color(4), theme, - JavaLocalDateFormatter(Locale.US)) + fmt) component.series = listOf(1.0, // today 0.2, 0.5, 0.7, 0.0, 0.3, 0.4, 0.6, 0.6, 0.0, 0.3, 0.6, 0.5, 0.8, 0.0, 0.0, 0.0, 0.0, 0.6, 0.5, 0.7, 0.7, 0.5, 0.5, 0.8, 0.9, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.5, 0.2) - assertRenders(800, 400, "$base/base.png", component) + assertRenders(400, 200, "$base/base.png", component) component.scrollPosition = 2 - assertRenders(800, 400, "$base/scroll.png", component) - - component.dateFormatter = JavaLocalDateFormatter(Locale.JAPAN) - assertRenders(800, 400, "$base/base-jp.png", component) + assertRenders(400, 200, "$base/scroll.png", component) } } \ No newline at end of file diff --git a/core/src/test/jvm/org/isoron/uhabits/components/CheckmarkButtonTest.kt b/core/src/test/common/org/isoron/uhabits/components/CheckmarkButtonTest.kt similarity index 77% rename from core/src/test/jvm/org/isoron/uhabits/components/CheckmarkButtonTest.kt rename to core/src/test/common/org/isoron/uhabits/components/CheckmarkButtonTest.kt index 91714e88..9a325917 100644 --- a/core/src/test/jvm/org/isoron/uhabits/components/CheckmarkButtonTest.kt +++ b/core/src/test/common/org/isoron/uhabits/components/CheckmarkButtonTest.kt @@ -19,27 +19,28 @@ package org.isoron.uhabits.components +import org.isoron.* import org.isoron.uhabits.* -import org.junit.* +import kotlin.test.* class CheckmarkButtonTest : BaseViewTest() { val base = "components/CheckmarkButton" @Test - fun testDrawExplicit() { + fun testDrawExplicit() = asyncTest { val component = CheckmarkButton(2, theme.color(8), theme) - assertRenders(96, 96, "$base/explicit.png", component) + assertRenders(48, 48, "$base/explicit.png", component) } @Test - fun testDrawImplicit() { + fun testDrawImplicit() = asyncTest { val component = CheckmarkButton(1, theme.color(8), theme) - assertRenders(96, 96, "$base/implicit.png", component) + assertRenders(48, 48, "$base/implicit.png", component) } @Test - fun testDrawUnchecked() { + fun testDrawUnchecked() = asyncTest { val component = CheckmarkButton(0, theme.color(8), theme) - assertRenders(96, 96, "$base/unchecked.png", component) + assertRenders(48, 48, "$base/unchecked.png", component) } } \ No newline at end of file diff --git a/core/src/test/jvm/org/isoron/uhabits/components/HabitListHeaderTest.kt b/core/src/test/common/org/isoron/uhabits/components/HabitListHeaderTest.kt similarity index 70% rename from core/src/test/jvm/org/isoron/uhabits/components/HabitListHeaderTest.kt rename to core/src/test/common/org/isoron/uhabits/components/HabitListHeaderTest.kt index 3173328c..41c4d9e5 100644 --- a/core/src/test/jvm/org/isoron/uhabits/components/HabitListHeaderTest.kt +++ b/core/src/test/common/org/isoron/uhabits/components/HabitListHeaderTest.kt @@ -19,20 +19,16 @@ package org.isoron.uhabits.components +import org.isoron.* import org.isoron.platform.time.* import org.isoron.uhabits.* -import org.junit.* -import java.util.* +import kotlin.test.* class HabitListHeaderTest : BaseViewTest() { @Test - fun testDraw() { - val header = HabitListHeader(LocalDate(2019, 3, 25), - 5, - theme, - JavaLocalDateFormatter(Locale.US)) - assertRenders(1200, 96, - "components/HabitListHeader/light.png", - header) + fun testDraw() = asyncTest { + val fmt = DependencyResolver.getDateFormatter(Locale.US) + val header = HabitListHeader(LocalDate(2019, 3, 25), 5, theme, fmt) + assertRenders(600, 48, "components/HabitListHeader/light.png", header) } } \ No newline at end of file diff --git a/core/src/test/common/org/isoron/uhabits/components/NumberButtonTest.kt b/core/src/test/common/org/isoron/uhabits/components/NumberButtonTest.kt new file mode 100644 index 00000000..8aedb434 --- /dev/null +++ b/core/src/test/common/org/isoron/uhabits/components/NumberButtonTest.kt @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2016-2019 Álinson Santos Xavier + * + * This file is part of Loop Habit Tracker. + * + * Loop Habit Tracker is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Loop Habit Tracker is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +package org.isoron.uhabits.components + +import org.isoron.* +import org.isoron.uhabits.* +import kotlin.test.* + +class NumberButtonTest : BaseViewTest() { + val base = "components/NumberButton" + + @Test + fun testFormatValue() = asyncTest{ + assertEquals("0.12", 0.1235.toShortString()) + assertEquals("0.1", 0.1000.toShortString()) + assertEquals("5", 5.0.toShortString()) + assertEquals("5.25", 5.25.toShortString()) + assertEquals("12.3", 12.3456.toShortString()) + assertEquals("123", 123.123.toShortString()) + assertEquals("321", 321.2.toShortString()) + assertEquals("4.3k", 4321.2.toShortString()) + assertEquals("54.3k", 54321.2.toShortString()) + assertEquals("654k", 654321.2.toShortString()) + assertEquals("7.7M", 7654321.2.toShortString()) + assertEquals("87.7M", 87654321.2.toShortString()) + assertEquals("988M", 987654321.2.toShortString()) + assertEquals("2.0G", 1987654321.2.toShortString()) + } + + @Test + fun testRenderAbove() = asyncTest { + val btn = NumberButton(theme.color(8), 500.0, 100.0, "steps", theme) + assertRenders(48, 48, "$base/render_above.png", btn) + } + + @Test + fun testRenderBelow() = asyncTest { + val btn = NumberButton(theme.color(8), 99.0, 100.0, "steps", theme) + assertRenders(48, 48, "$base/render_below.png", btn) + } + + @Test + fun testRenderZero() = asyncTest { + val btn = NumberButton(theme.color(8), 0.0, 100.0, "steps", theme) + assertRenders(48, 48, "$base/render_zero.png", btn) + } +} \ No newline at end of file diff --git a/core/src/test/jvm/org/isoron/uhabits/components/RingTest.kt b/core/src/test/common/org/isoron/uhabits/components/RingTest.kt similarity index 90% rename from core/src/test/jvm/org/isoron/uhabits/components/RingTest.kt rename to core/src/test/common/org/isoron/uhabits/components/RingTest.kt index e017d873..a91c4a05 100644 --- a/core/src/test/jvm/org/isoron/uhabits/components/RingTest.kt +++ b/core/src/test/common/org/isoron/uhabits/components/RingTest.kt @@ -19,20 +19,21 @@ package org.isoron.uhabits.components +import org.isoron.* import org.isoron.uhabits.* -import org.junit.* +import kotlin.test.* class RingTest : BaseViewTest() { val base = "components/Ring" @Test - fun testDraw() { + fun testDraw() = asyncTest { val component = Ring(theme.color(8), percentage = 0.30, thickness = 5.0, radius = 30.0, theme = theme, label = true) - assertRenders(120, 120, "$base/draw1.png", component) + assertRenders(60, 60, "$base/draw1.png", component) } } \ No newline at end of file diff --git a/core/src/test/js/org/isoron/DependencyResolver.kt b/core/src/test/js/org/isoron/DependencyResolver.kt index 42331908..2775e899 100644 --- a/core/src/test/js/org/isoron/DependencyResolver.kt +++ b/core/src/test/js/org/isoron/DependencyResolver.kt @@ -23,8 +23,10 @@ import org.isoron.platform.gui.* import org.isoron.platform.io.* import org.isoron.platform.time.* import org.isoron.uhabits.* +import org.khronos.webgl.* import org.w3c.dom.* import kotlin.browser.* +import kotlin.math.* actual object DependencyResolver { actual val supportsDatabaseTests = true @@ -52,7 +54,7 @@ actual object DependencyResolver { } actual fun getDateFormatter(locale: Locale): LocalDateFormatter { - return when(locale) { + return when (locale) { Locale.US -> JsDateFormatter("en-US") Locale.JAPAN -> JsDateFormatter("ja-JP") } @@ -60,14 +62,65 @@ actual object DependencyResolver { } class JsCanvasHelper : CanvasHelper { - override fun createCanvas(width: Int, height: Int): Canvas { - val canvasElement = document.getElementById("canvas") as HTMLCanvasElement - canvasElement.style.width = "${width}px" - canvasElement.style.height = "${height}px" - return HtmlCanvas(canvasElement) + override suspend fun compare(imageFile: ResourceFile, + canvas: Canvas): Double { + canvas as JsCanvas + imageFile as JsResourceFile + val width = canvas.element.width + val height = canvas.element.height + + val expectedCanvasElement = document.createElement("canvas") as HTMLCanvasElement + expectedCanvasElement.width = width + expectedCanvasElement.height = height + expectedCanvasElement.style.width = canvas.element.style.width + expectedCanvasElement.style.height = canvas.element.style.height + expectedCanvasElement.className = "canvasTest" + document.body?.appendChild(expectedCanvasElement) + val expectedCanvas = JsCanvas(expectedCanvasElement, 1.0) + expectedCanvas.loadImage("../assets/${imageFile.filename}") + + val actualData = canvas.ctx.getImageData(0.0, + 0.0, + width.toDouble(), + height.toDouble()).data + val expectedData = expectedCanvas.ctx.getImageData(0.0, + 0.0, + width.toDouble(), + height.toDouble()).data + + var distance = 0.0; + for (x in 0 until width) { + for (y in 0 until height) { + val k = (y * width + x) * 4 + distance += abs(actualData[k] - expectedData[k]) + distance += abs(actualData[k + 1] - expectedData[k + 1]) + distance += abs(actualData[k + 2] - expectedData[k + 2]) + distance += abs(actualData[k + 3] - expectedData[k + 3]) + } + } + + val adjustedDistance = distance / 255.0 / 4 / 1000 + + if (adjustedDistance > SIMILARITY_THRESHOLD) { + expectedCanvasElement.style.display = "block" + canvas.element.style.display = "block" + } + + return adjustedDistance } - override fun exportCanvas(canvas: Canvas, filename: String) { + override fun createCanvas(width: Int, height: Int): Canvas { + val canvasElement = document.createElement("canvas") as HTMLCanvasElement + canvasElement.width = width * 2 + canvasElement.height = height * 2 + canvasElement.style.width = "${width}px" + canvasElement.style.height = "${height}px" + canvasElement.className = "canvasTest" + document.body?.appendChild(canvasElement) + return JsCanvas(canvasElement, 2.0) + } + + override suspend fun exportCanvas(canvas: Canvas, filename: String) { // do nothing } } \ No newline at end of file diff --git a/core/src/test/jvm/org/isoron/DependencyResolver.kt b/core/src/test/jvm/org/isoron/DependencyResolver.kt index ccb2d868..c0819ef2 100644 --- a/core/src/test/jvm/org/isoron/DependencyResolver.kt +++ b/core/src/test/jvm/org/isoron/DependencyResolver.kt @@ -25,6 +25,8 @@ import org.isoron.platform.time.* import org.isoron.uhabits.* import java.awt.image.* import java.io.* +import java.lang.Math.* +import java.nio.file.* import javax.imageio.* actual object DependencyResolver { @@ -47,7 +49,7 @@ actual object DependencyResolver { } actual fun getDateFormatter(locale: Locale): LocalDateFormatter { - return when(locale) { + return when (locale) { Locale.US -> JavaLocalDateFormatter(java.util.Locale.US) Locale.JAPAN -> JavaLocalDateFormatter(java.util.Locale.JAPAN) } @@ -55,13 +57,42 @@ actual object DependencyResolver { } class JavaCanvasHelper : CanvasHelper { - override fun createCanvas(width: Int, height: Int): Canvas { - val image = BufferedImage(width, height, BufferedImage.TYPE_INT_RGB) - return JavaCanvas(image, pixelScale = 1.0) + override suspend fun compare(imageFile: ResourceFile, canvas: Canvas): Double { + val actual = (canvas as JavaCanvas).image + val expected = ImageIO.read((imageFile as JavaResourceFile).stream()) + return compare(expected, actual) } - override fun exportCanvas(canvas: Canvas, filename: String) { - val javaCanvas = canvas as JavaCanvas - ImageIO.write(javaCanvas.image, "png", File("/tmp/$filename")) + private fun compare(expected: BufferedImage, + actual: BufferedImage): Double { + + if (actual.width != expected.width) return Double.POSITIVE_INFINITY + if (actual.height != expected.height) return Double.POSITIVE_INFINITY + + var distance = 0.0; + for (x in 0 until actual.width) { + for (y in 0 until actual.height) { + val p1 = Color(actual.getRGB(x, y)) + val p2 = Color(expected.getRGB(x, y)) + distance += abs(p1.red - p2.red) + distance += abs(p1.green - p2.green) + distance += abs(p1.blue - p2.blue) + } + } + + return distance / 4.0 + } + + override fun createCanvas(width: Int, height: Int): Canvas { + val widthPx = width * 2 + val heightPx = height * 2 + val image = BufferedImage(widthPx, heightPx, BufferedImage.TYPE_INT_ARGB) + return JavaCanvas(image, pixelScale = 2.0) + } + + override suspend fun exportCanvas(canvas: Canvas, filename: String) { + val file = File("/tmp/$filename") + file.parentFile.mkdirs() + ImageIO.write((canvas as JavaCanvas).image, "png", file) } } \ No newline at end of file diff --git a/core/src/test/jvm/org/isoron/uhabits/BaseViewTest.kt b/core/src/test/jvm/org/isoron/uhabits/BaseViewTest.kt deleted file mode 100644 index 32704be3..00000000 --- a/core/src/test/jvm/org/isoron/uhabits/BaseViewTest.kt +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2016-2019 Álinson Santos Xavier - * - * This file is part of Loop Habit Tracker. - * - * Loop Habit Tracker is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by the - * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. - * - * Loop Habit Tracker is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - */ - -package org.isoron.uhabits - -import kotlinx.coroutines.* -import org.isoron.platform.gui.* -import org.isoron.platform.io.* -import org.isoron.uhabits.components.* -import java.awt.image.* -import java.io.* -import javax.imageio.* -import kotlin.math.* - -open class BaseViewTest { - val theme = LightTheme() - - private fun distance(actual: BufferedImage, - expected: BufferedImage): Double { - - if (actual.width != expected.width) return Double.POSITIVE_INFINITY - if (actual.height != expected.height) return Double.POSITIVE_INFINITY - - var distance = 0.0; - for (x in 0 until actual.width) { - for (y in 0 until actual.height) { - val p1 = Color(actual.getRGB(x, y)) - val p2 = Color(expected.getRGB(x, y)) - distance += abs(p1.red - p2.red) - distance += abs(p1.green - p2.green) - distance += abs(p1.blue - p2.blue) - } - } - - return 255 * distance / (actual.width * actual.height) - } - - fun assertRenders(width: Int, - height: Int, - expectedPath: String, - component: Component, - threshold: Double = 1e-3) { - - val actual = BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB) - val canvas = JavaCanvas(actual) - val expectedFile: JavaResourceFile - val actualPath = "/tmp/${expectedPath}" - - component.draw(canvas) - expectedFile = JavaFileOpener().openResourceFile(expectedPath) as JavaResourceFile - - runBlocking { - if (expectedFile.exists()) { - val expected = ImageIO.read(expectedFile.stream()) - val d = distance(actual, expected) - if (d >= threshold) { - File(actualPath).parentFile.mkdirs() - ImageIO.write(actual, "png", File(actualPath)) - ImageIO.write(expected, "png", File(actualPath.replace(".png", ".expected.png"))) - //fail("Images differ (distance=${d}). Actual rendered saved to ${actualPath}.") - } - } else { - File(actualPath).parentFile.mkdirs() - ImageIO.write(actual, "png", File(actualPath)) - //fail("Expected file is missing. Actual render saved to $actualPath") - } - } - } -} \ No newline at end of file diff --git a/core/src/test/jvm/org/isoron/uhabits/components/NumberButtonTest.kt b/core/src/test/jvm/org/isoron/uhabits/components/NumberButtonTest.kt deleted file mode 100644 index 47228f01..00000000 --- a/core/src/test/jvm/org/isoron/uhabits/components/NumberButtonTest.kt +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2016-2019 Álinson Santos Xavier - * - * This file is part of Loop Habit Tracker. - * - * Loop Habit Tracker is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by the - * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. - * - * Loop Habit Tracker is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - */ - -package org.isoron.uhabits.components - -import org.hamcrest.CoreMatchers.* -import org.isoron.uhabits.* -import org.junit.* -import org.junit.Assert.* - -class NumberButtonTest : BaseViewTest() { - val base = "components/NumberButton/" - - @Test - fun testFormatValue() { - assertThat(0.1235.toShortString(), equalTo("0.12")) - assertThat(0.1000.toShortString(), equalTo("0.1")) - assertThat(5.0.toShortString(), equalTo("5")) - assertThat(5.25.toShortString(), equalTo("5.25")) - assertThat(12.3456.toShortString(), equalTo("12.3")) - assertThat(123.123.toShortString(), equalTo("123")) - assertThat(321.2.toShortString(), equalTo("321")) - assertThat(4321.2.toShortString(), equalTo("4.3k")) - assertThat(54321.2.toShortString(), equalTo("54.3k")) - assertThat(654321.2.toShortString(), equalTo("654k")) - assertThat(7654321.2.toShortString(), equalTo("7.7M")) - assertThat(87654321.2.toShortString(), equalTo("87.7M")) - assertThat(987654321.2.toShortString(), equalTo("988M")) - assertThat(1987654321.2.toShortString(), equalTo("2.0G")) - } - - @Test - fun testRenderAbove() { - val btn = NumberButton(theme.color(8), 500.0, 100.0, "steps", theme) - assertRenders(96, 96, "$base/render_above.png", btn) - } - - @Test - fun testRenderBelow() { - val btn = NumberButton(theme.color(8), 99.0, 100.0, "steps", theme) - assertRenders(96, 96, "$base/render_below.png", btn) - } - - @Test - fun testRenderZero() { - val btn = NumberButton(theme.color(8), 0.0, 100.0, "steps", theme) - assertRenders(96, 96, "$base/render_zero.png", btn) - } -} \ No newline at end of file diff --git a/web/Makefile b/web/Makefile index 0c3aff7c..a005b14f 100644 --- a/web/Makefile +++ b/web/Makefile @@ -20,6 +20,7 @@ $(test_bundle): src/test/index.js core cp node_modules/mocha/mocha.css build/lib cp node_modules/mocha/mocha.js build/lib cp node_modules/sql.js/js/sql.js build/lib + cp node_modules/sprintf-js/dist/sprintf.min.js build/lib serve: npx serve build/ diff --git a/web/package-lock.json b/web/package-lock.json index e14133a8..3ac6aa33 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -315,6 +315,14 @@ "dev": true, "requires": { "sprintf-js": "~1.0.2" + }, + "dependencies": { + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + } } }, "arr-diff": { @@ -5111,10 +5119,9 @@ } }, "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==" }, "sql.js": { "version": "0.5.0", diff --git a/web/package.json b/web/package.json index 05f48df4..eabf089b 100644 --- a/web/package.json +++ b/web/package.json @@ -21,6 +21,7 @@ "kotlin": "^1.3.21", "kotlin-test": "^1.3.21", "kotlinx-coroutines-core": "^1.1.1", + "sprintf-js": "^1.1.2", "sql.js": "^0.5.0" }, "devDependencies": { diff --git a/web/src/test/index.html b/web/src/test/index.html index 7ac2480f..7db16233 100644 --- a/web/src/test/index.html +++ b/web/src/test/index.html @@ -3,20 +3,47 @@ Mocha Tests + + - + + +   +   +   +
- - - - + + +