Skip to content

Commit

Permalink
Add manual regression tests
Browse files Browse the repository at this point in the history
  • Loading branch information
mahozad committed Jan 12, 2024
1 parent 0c6bb9f commit beb9b1f
Show file tree
Hide file tree
Showing 3 changed files with 377 additions and 1 deletion.
24 changes: 24 additions & 0 deletions .run/Test (Desktop).run.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Test (Desktop)" type="GradleRunConfiguration" factoryName="Gradle">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$/library" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value="desktopTest" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" />
</configuration>
</component>
8 changes: 7 additions & 1 deletion library/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,13 @@ kotlin {
val androidMain by getting {}
val androidUnitTest by getting {}
val desktopMain by getting {}
val desktopTest by getting {}
val desktopTest by getting {
dependencies {
implementation(compose.desktop.uiTestJUnit4)
// Needed because of build errors; remove if tests run
implementation(compose.desktop.windows_x64)
}
}
val jsMain by getting {}
val jsTest by getting {}
val iosX64Main by getting {}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,346 @@
package ir.mahozad.multiplatform.wavyslider

import androidx.compose.foundation.border
import androidx.compose.foundation.layout.*
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.WindowPosition
import androidx.compose.ui.window.WindowState
import androidx.compose.ui.window.application
import ir.mahozad.multiplatform.wavyslider.WaveAnimationDirection.LTR
import org.junit.Ignore
import org.junit.Test
import androidx.compose.material3.MaterialTheme as MaterialTheme3
import ir.mahozad.multiplatform.wavyslider.material.WavySlider as WavySlider2
import ir.mahozad.multiplatform.wavyslider.material3.WavySlider as WavySlider3

class VisualTest {

// @get:Rule
// val rule = createComposeRule()

// @Test
// fun test() {
// var waveHeight by mutableStateOf(48.dp)
// rule.mainClock.autoAdvance = false // Pauses animations
// rule.setContent {
// WavySlider(0.5f, {}, waveHeight = waveHeight)
// }

// waveHeight = 0.dp
// rule.mainClock.advanceTimeByFrame()
// rule.mainClock.advanceTimeBy(517L)

// // val image = rule.onRoot().captureToImage()
// // ImageIO.write(image.toAwtImage(), "PNG", Path("output.png").outputStream())

// val referencePath = Path("output.png")
// val screenshot = Image.makeFromBitmap(rule.onRoot().captureToImage().asSkiaBitmap())
// val actualPath = Path("output1.png")
// val actualData = screenshot.encodeToData(EncodedImageFormat.PNG) ?: error("Could not encode image as png")
// actualPath.writeBytes(actualData.bytes)

// assert(actualPath.readBytes().contentEquals(referencePath.readBytes())) {
// "The screenshot '$actualPath' does not match the reference '$referencePath'"
// }
// }

private fun testApp(
name: String,
given: String,
expected: String,
wavySlider2: (@Composable ColumnScope.(value: Float, onChange: (Float) -> Unit) -> Unit)? = null,
wavySlider3: (@Composable ColumnScope.(value: Float, onChange: (Float) -> Unit) -> Unit)? = null,
content: (@Composable ColumnScope.() -> Unit)? = null
): Boolean {
var passed = false
application(exitProcessOnExit = false) {
Window(
title = name,
state = WindowState(position = WindowPosition(Alignment.Center)),
resizable = false,
onCloseRequest = ::exitApplication
) {
MaterialTheme3 {
Column(
verticalArrangement = Arrangement.spacedBy(16.dp),
modifier = Modifier.padding(16.dp)
) {
content?.invoke(this@Column) ?: run {
var value by remember { mutableStateOf(0.5f) }
Row(verticalAlignment = Alignment.CenterVertically) {
Text(text = "Material 2:")
wavySlider2?.invoke(this@Column, value) { value = it }
}
Row(verticalAlignment = Alignment.CenterVertically) {
Text(text = "Material 3:")
wavySlider3?.invoke(this@Column, value) { value = it }
}
}
Text(text = given)
Text(text = expected)
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
OutlinedButton(
onClick = { passed = false; exitApplication() },
colors = ButtonDefaults.buttonColors(containerColor = Color.Red.copy(alpha = 0.05f))
) {
Text(text = "Fail", color = Color.Red)
}
OutlinedButton(
onClick = { passed = true; exitApplication() },
colors = ButtonDefaults.buttonColors(containerColor = Color.Green.copy(alpha = 0.05f))
) {
Text(text = "Pass", color = Color.Green)
}
}
}
}
}
}
return passed
}

@Test
fun `Test 1`() {
val isPassed = testApp(
name = object {}.javaClass.enclosingMethod.name,
given = "Default wavy sliders with no arguments passed",
expected = "Should be displayed properly",
wavySlider2 = { value, onChange -> WavySlider2(value, onChange) },
wavySlider3 = { value, onChange -> WavySlider3(value, onChange) }
)
assert(isPassed)
}

@Test
fun `Test 2`() {
val isPassed = testApp(
name = object {}.javaClass.enclosingMethod.name,
given = "Wave height set to 0",
expected = "Should be exactly like a regular Slider",
wavySlider2 = { value, onChange -> WavySlider2(value, onChange, waveHeight = 0.dp) },
wavySlider3 = { value, onChange -> WavySlider3(value, onChange, waveHeight = 0.dp) }
)
assert(isPassed)
}

@Test
fun `Test 3`() {
val isPassed = testApp(
name = object {}.javaClass.enclosingMethod.name,
given = "Disabled",
expected = "Should not be able to drag the thumb",
wavySlider2 = { value, onChange -> WavySlider2(value, onChange, enabled = false) },
wavySlider3 = { value, onChange -> WavySlider3(value, onChange, enabled = false) }
)
assert(isPassed)
}

@Test
fun `Test 4`() {
val isPassed = testApp(
name = object {}.javaClass.enclosingMethod.name,
given = "LTR animation",
expected = "Should move from left to right",
wavySlider2 = { value, onChange -> WavySlider2(value, onChange, animationDirection = LTR) },
wavySlider3 = { value, onChange -> WavySlider3(value, onChange, animationDirection = LTR) }
)
assert(isPassed)
}

// FIXME
@Test
fun `Test 5`() {
val isPassed = testApp(
name = object {}.javaClass.enclosingMethod.name,
given = "Wave height set to more than thumb height",
expected = "Should take into account the height of wave in component overall height",
wavySlider2 = { value, onChange ->
WavySlider2(value, onChange, waveHeight = 172.dp, modifier = Modifier.border(1.dp, Color.Gray))
},
wavySlider3 = { value, onChange ->
WavySlider3(value, onChange, waveHeight = 172.dp, modifier = Modifier.border(1.dp, Color.Gray))
}
)
assert(isPassed)
}

@Test
fun `Test 6`() {
val isPassed = testApp(
name = object {}.javaClass.enclosingMethod.name,
given = "Flattened",
expected = "Should be flattened",
wavySlider2 = { value, onChange -> WavySlider2(value, onChange, shouldFlatten = true) },
wavySlider3 = { value, onChange -> WavySlider3(value, onChange, shouldFlatten = true) }
)
assert(isPassed)
}

@Test
fun `Test 7`() {
val isPassed = testApp(
name = object {}.javaClass.enclosingMethod.name,
given = "Wave height set to negative value",
expected = "Should have the same behaviour as if the size was positive",
wavySlider2 = { value, onChange -> WavySlider2(value, onChange, waveHeight = (-48).dp) },
wavySlider3 = { value, onChange -> WavySlider3(value, onChange, waveHeight = (-48).dp) }
)
assert(isPassed)
}

@Test
fun `Test 8`() {
val isPassed = testApp(
name = object {}.javaClass.enclosingMethod.name,
given = "Wave length set to a large value",
expected = "Should have proper wave length",
wavySlider2 = { value, onChange -> WavySlider2(value, onChange, waveLength = 128.dp) },
wavySlider3 = { value, onChange -> WavySlider3(value, onChange, waveLength = 128.dp) }
)
assert(isPassed)
}

@Ignore("FIXME")
@Test
fun `Test 9`() {
val isPassed = testApp(
name = object {}.javaClass.enclosingMethod.name,
given = "Wave length set to 0",
expected = "Should be exactly like a regular Slider",
wavySlider2 = { value, onChange -> WavySlider2(value, onChange, waveLength = 0.dp) },
wavySlider3 = { value, onChange -> WavySlider3(value, onChange, waveLength = 0.dp) }
)
assert(isPassed)
}

@Ignore("FIXME")
@Test
fun `Test 10`() {
val isPassed = testApp(
name = object {}.javaClass.enclosingMethod.name,
given = "Wave length set to a negative value",
expected = "Should have the same behaviour as if the size was positive",
wavySlider2 = { value, onChange -> WavySlider2(value, onChange, waveLength = (-48).dp) },
wavySlider3 = { value, onChange -> WavySlider3(value, onChange, waveLength = (-48).dp) }
)
assert(isPassed)
}

@Test
fun `Test 11`() {
val isPassed = testApp(
name = object {}.javaClass.enclosingMethod.name,
given = "Wave thickness set to a large value",
expected = "Should have proper wave thickness",
wavySlider2 = { value, onChange -> WavySlider2(value, onChange, waveThickness = 8.dp) },
wavySlider3 = { value, onChange -> WavySlider3(value, onChange, waveThickness = 8.dp) }
)
assert(isPassed)
}

@Ignore("FIXME")
@Test
fun `Test 12`() {
val isPassed = testApp(
name = object {}.javaClass.enclosingMethod.name,
given = "Wave thickness set to 0",
expected = "Should have the wave disappeared",
wavySlider2 = { value, onChange -> WavySlider2(value, onChange, waveThickness = 0.dp) },
wavySlider3 = { value, onChange -> WavySlider3(value, onChange, waveThickness = 0.dp) }
)
assert(isPassed)
}

@Ignore("FIXME")
@Test
fun `Test 13`() {
val isPassed = testApp(
name = object {}.javaClass.enclosingMethod.name,
given = "Wave thickness set to a negative value",
expected = "Should have the same behaviour as if the thickness was 0",
wavySlider2 = { value, onChange -> WavySlider2(value, onChange, waveThickness = (-10).dp) },
wavySlider3 = { value, onChange -> WavySlider3(value, onChange, waveThickness = (-10).dp) }
)
assert(isPassed)
}

@Test
fun `Test 14`() {
val isPassed = testApp(
name = object {}.javaClass.enclosingMethod.name,
given = "Track thickness set to a large value",
expected = "Should have proper track thickness",
wavySlider2 = { value, onChange -> WavySlider2(value, onChange, trackThickness = 18.dp) },
wavySlider3 = { value, onChange -> WavySlider3(value, onChange, trackThickness = 18.dp) }
)
assert(isPassed)
}

@Test
fun `Test 15`() {
val isPassed = testApp(
name = object {}.javaClass.enclosingMethod.name,
given = "Track thickness set to 0",
expected = "Should have the track disappeared",
wavySlider2 = { value, onChange -> WavySlider2(value, onChange, trackThickness = 0.dp) },
wavySlider3 = { value, onChange -> WavySlider3(value, onChange, trackThickness = 0.dp) }
)
assert(isPassed)
}

@Test
fun `Test 16`() {
val isPassed = testApp(
name = object {}.javaClass.enclosingMethod.name,
given = "Track thickness set to a negative value",
expected = "Should have the same behaviour as if the thickness was 0",
wavySlider2 = { value, onChange -> WavySlider2(value, onChange, trackThickness = (-10).dp) },
wavySlider3 = { value, onChange -> WavySlider3(value, onChange, trackThickness = (-10).dp) }
)
assert(isPassed)
}

@Test
fun `Test 17`() {
val isPassed = testApp(
name = object {}.javaClass.enclosingMethod.name,
given = "Track thickness set to a null",
expected = "Should have the same behaviour as if the thickness was 0",
wavySlider2 = { value, onChange -> WavySlider2(value, onChange, trackThickness = null) },
wavySlider3 = { value, onChange -> WavySlider3(value, onChange, trackThickness = null) }
)
assert(isPassed)
}

@Test
fun `Test 18`() {
val isPassed = testApp(
name = object {}.javaClass.enclosingMethod.name,
given = "Container layout direction set to RTL",
expected = "Should be reversed"
) {
var value by remember { mutableStateOf(0.5f) }
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
Row(verticalAlignment = Alignment.CenterVertically) {
Text(text = "Material 2:")
WavySlider2(value = value, onValueChange = { value = it })
}
Row(verticalAlignment = Alignment.CenterVertically) {
Text(text = "Material 3:")
WavySlider3(value = value, onValueChange = { value = it })
}
}
}
assert(isPassed)
}
}

0 comments on commit beb9b1f

Please sign in to comment.