diff --git a/core/assets/test/components/CalendarChart/base.png b/core/assets/test/components/CalendarChart/base.png index a42177547..762877164 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 691599a90..16744a297 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 000000000..75b3d8f20 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 e192b8780..c146b916d 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 a8bf60e65..4673ef0f3 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 d5ab438b4..c3ffb68de 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 867bf1c7e..235de2f62 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 e61c9c24a..d5e80f455 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 3b243ba6f..000000000 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 11b14b813..000000000 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 c6f70b9fd..000000000 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 212260482..000000000 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 3d7861df0..000000000 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 a42407263..000000000 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 0fefe39b0..000000000 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 3d7861df0..000000000 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 95c71e671..000000000 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 9fbec7ea3..000000000 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 f2a712b89..000000000 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 860b326d4..000000000 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 4ea430a67..000000000 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 e0bfe6fd3..000000000 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 b550f9814..000000000 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 11ec0904b..000000000 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 e34f4d671..000000000 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 df13dae29..000000000 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 9eb67f547..000000000 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 6701961bc..000000000 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 9eb67f547..000000000 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 346a81e77..000000000 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 46ac8fd03..000000000 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 1f5aed305..000000000 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 68b695bca..000000000 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 28d40d4b3..000000000 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 6991525e2..000000000 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 207910f1f..000000000 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 f06396cbf..000000000 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 5833a07ad..000000000 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 6ea8309f5..f451b99c3 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 e1dde3c56..647bf7b97 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 50b2faa3d..36ba11cfb 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 924b33e5b..6abbc4675 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 f8f34055d..416ca5fd2 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 263eaa3c6..2092794d6 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 2e30fddae..289e99b11 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 5d3b15223..2d4856885 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 36cedaae5..f039abc93 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 000000000..b80683406 --- /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 000000000..59c554eaf --- /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 dae5670aa..dee24ccf8 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 76% 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 91714e88e..9a325917d 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 3173328ca..41c4d9e5b 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 000000000..8aedb4349 --- /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 89% 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 e017d8737..a91c4a056 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 423319080..2775e8998 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 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 createCanvas(width: Int, height: Int): Canvas { - val canvasElement = document.getElementById("canvas") as HTMLCanvasElement + 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" - return HtmlCanvas(canvasElement) + canvasElement.className = "canvasTest" + document.body?.appendChild(canvasElement) + return JsCanvas(canvasElement, 2.0) } - override fun exportCanvas(canvas: Canvas, filename: String) { + 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 ccb2d8688..c0819ef20 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 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) + } + + 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 image = BufferedImage(width, height, BufferedImage.TYPE_INT_RGB) - return JavaCanvas(image, pixelScale = 1.0) + val widthPx = width * 2 + val heightPx = height * 2 + val image = BufferedImage(widthPx, heightPx, BufferedImage.TYPE_INT_ARGB) + return JavaCanvas(image, pixelScale = 2.0) } - override fun exportCanvas(canvas: Canvas, filename: String) { - val javaCanvas = canvas as JavaCanvas - ImageIO.write(javaCanvas.image, "png", File("/tmp/$filename")) + 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 32704be35..000000000 --- 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 47228f016..000000000 --- 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 0c3aff7cb..a005b14f4 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 e14133a85..3ac6aa335 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 05f48df4d..eabf089bf 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 7ac2480f1..7db16233f 100644 --- a/web/src/test/index.html +++ b/web/src/test/index.html @@ -3,20 +3,47 @@ Mocha Tests + + - + + +   +   +   +
- - - - + + +