Skip to content

Commit 98ad0e4

Browse files
committed
feat: add maestro test for login
Signed-off-by: Brandon McAnsh <git@bmcreations.dev>
1 parent b145360 commit 98ad0e4

11 files changed

Lines changed: 133 additions & 10 deletions

File tree

apps/flipcash/app/src/main/kotlin/com/flipcash/app/MainActivity.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import com.getcode.opencode.compose.LocalTransactionController
4242
import com.getcode.opencode.controllers.TransactionController
4343
import com.getcode.opencode.exchange.Exchange
4444
import com.getcode.solana.rpc.RpcConfig
45+
import com.getcode.ui.testing.LocalUiTesting
4546
import com.getcode.util.permissions.LocalPermissionChecker
4647
import com.getcode.util.permissions.PermissionChecker
4748
import com.getcode.util.resources.LocalResources
@@ -162,6 +163,7 @@ class MainActivity : FragmentActivity() {
162163
LocalPhoneUtils provides phoneUtils,
163164
LocalBillPlaygroundController provides billPlaygroundController,
164165
LocalAppUpdater provides appUpdater,
166+
LocalUiTesting provides intent.getBooleanExtra(UI_TEST, false),
165167
) {
166168
Rinku {
167169
App(
@@ -172,6 +174,10 @@ class MainActivity : FragmentActivity() {
172174
}
173175
}
174176
}
177+
178+
companion object {
179+
private const val UI_TEST = "isUiTest"
180+
}
175181
}
176182

177183
private fun Activity.handleUncaughtException() {

apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/internal/LoginScreenContent.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import androidx.compose.ui.Alignment
2222
import androidx.compose.ui.Modifier
2323
import androidx.compose.ui.graphics.Color
2424
import androidx.compose.ui.platform.LocalContext
25+
import androidx.compose.ui.platform.testTag
2526
import androidx.compose.ui.res.painterResource
2627
import androidx.compose.ui.res.stringResource
2728
import androidx.compose.ui.text.SpanStyle
@@ -74,7 +75,8 @@ internal fun LoginRouterScreenContent(
7475
CodeButton(
7576
modifier = Modifier
7677
.fillMaxWidth()
77-
.padding(horizontal = CodeTheme.dimens.inset),
78+
.padding(horizontal = CodeTheme.dimens.inset)
79+
.testTag("create_account_button"),
7880
enabled = !isLoggingIn.loading && !isLoggingIn.success,
7981
onClick = createAccount,
8082
text = stringResource(R.string.action_createNewAccount),
@@ -83,7 +85,8 @@ internal fun LoginRouterScreenContent(
8385
CodeButton(
8486
modifier = Modifier
8587
.fillMaxWidth()
86-
.padding(horizontal = CodeTheme.dimens.inset),
88+
.padding(horizontal = CodeTheme.dimens.inset)
89+
.testTag("login_button"),
8790
onClick = login,
8891
isLoading = isLoggingIn.loading,
8992
isSuccess = isLoggingIn.success,

apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/internal/SeedInputContent.kt

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,12 @@ import androidx.compose.ui.focus.focusRequester
3333
import androidx.compose.ui.graphics.ColorFilter
3434
import androidx.compose.ui.platform.LocalFocusManager
3535
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
36+
import androidx.compose.ui.platform.testTag
3637
import androidx.compose.ui.res.painterResource
3738
import androidx.compose.ui.res.stringResource
3839
import androidx.compose.ui.text.font.FontWeight
3940
import androidx.compose.ui.text.input.KeyboardType
4041
import androidx.compose.ui.text.input.VisualTransformation
41-
import androidx.compose.ui.text.style.TextAlign
4242
import androidx.compose.ui.unit.dp
4343
import androidx.compose.ui.unit.sp
4444
import cafe.adriel.voyager.core.registry.ScreenRegistry
@@ -52,8 +52,11 @@ import com.getcode.navigation.core.CodeNavigator
5252
import com.getcode.navigation.core.LocalCodeNavigator
5353
import com.getcode.theme.CodeTheme
5454
import com.getcode.theme.inputColors
55+
import com.getcode.ui.core.MaskWithSpacesTransformation
5556
import com.getcode.ui.core.rememberAnimationScale
5657
import com.getcode.ui.core.scaled
58+
import com.getcode.ui.core.textFieldTestTag
59+
import com.getcode.ui.testing.LocalUiTesting
5760
import com.getcode.ui.theme.ButtonState
5861
import com.getcode.ui.theme.CodeButton
5962
import com.getcode.ui.theme.CodeScaffold
@@ -146,9 +149,15 @@ private fun SeedInputContent(
146149
.padding(top = CodeTheme.dimens.grid.x3)
147150
.fillMaxWidth()
148151
.height(120.dp)
149-
.focusRequester(focusRequester),
152+
.focusRequester(focusRequester)
153+
.textFieldTestTag("seed_input_field"),
150154
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
151-
visualTransformation = VisualTransformation.None,
155+
visualTransformation = if (LocalUiTesting.current) {
156+
// mask words if running in UI testing to not leak recovery phrase
157+
MaskWithSpacesTransformation()
158+
} else {
159+
VisualTransformation.None
160+
},
152161
value = state.wordsString,
153162
onValueChange = { onTextChange(it) },
154163
textStyle = CodeTheme.typography.textLarge.copy(
@@ -189,7 +198,8 @@ private fun SeedInputContent(
189198
CodeButton(
190199
modifier = Modifier
191200
.fillMaxWidth()
192-
.padding(bottom = CodeTheme.dimens.grid.x4),
201+
.padding(bottom = CodeTheme.dimens.grid.x4)
202+
.testTag("login_confirm_button"),
193203
onClick = {
194204
focusManager.clearFocus()
195205
onLogin()

maestro/login.yaml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# maestro/login.yaml
2+
appId: com.flipcash.app.android.dev
3+
---
4+
- launchApp:
5+
clearState: true
6+
arguments:
7+
isUiTest: true
8+
- extendedWaitUntil:
9+
visible:
10+
id: "login_screen"
11+
- tapOn:
12+
id: "login_button"
13+
- extendedWaitUntil:
14+
visible:
15+
id: seed_input_screen
16+
- tapOn:
17+
id: "seed_input_field"
18+
- inputText: ${SEED_PHRASE}
19+
- tapOn:
20+
id: "login_confirm_button"
21+
- extendedWaitUntil:
22+
visible:
23+
id: "scanner_screen"

settings.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ include(
155155
":ui:navigation",
156156
":ui:resources",
157157
":ui:scanner",
158+
":ui:testing",
158159
":ui:theme",
159160

160161
// 3rd party imported dependencies

ui/core/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,6 @@ dependencies {
4949
implementation(Libs.compose_material)
5050

5151
api(project(":ui:resources"))
52+
api(project(":ui:testing"))
5253
implementation(project(":ui:theme"))
5354
}

ui/core/src/main/kotlin/com/getcode/ui/core/Modifier.kt

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,10 @@ import androidx.compose.ui.input.pointer.pointerInteropFilter
4545
import androidx.compose.ui.layout.layout
4646
import androidx.compose.ui.layout.onPlaced
4747
import androidx.compose.ui.platform.LocalDensity
48+
import androidx.compose.ui.platform.testTag
4849
import androidx.compose.ui.semantics.Role
50+
import androidx.compose.ui.semantics.contentDescription
51+
import androidx.compose.ui.semantics.semantics
4952
import androidx.compose.ui.unit.Dp
5053
import androidx.compose.ui.unit.DpSize
5154
import androidx.compose.ui.unit.IntSize
@@ -214,9 +217,9 @@ fun Modifier.punchRectangle(brush: Brush, blendMode: BlendMode = BlendMode.Src)
214217
}
215218

216219
fun Modifier.punchCircle(brush: Brush, blendMode: BlendMode = BlendMode.Src) = drawWithContent {
217-
drawCircle(brush = brush, blendMode = blendMode)
218-
drawContent()
219-
}
220+
drawCircle(brush = brush, blendMode = blendMode)
221+
drawContent()
222+
}
220223

221224
@Composable
222225
fun Modifier.withTopBorder(color: Color = CodeTheme.colors.brandLight) = drawBehind {
@@ -501,4 +504,9 @@ fun Modifier.patternBlend(
501504
alpha = strength.coerceIn(0f, 1f),
502505
blendMode = blendMode ?: BlendMode.SrcOver
503506
)
504-
}
507+
}
508+
509+
fun Modifier.textFieldTestTag(tag: String): Modifier =
510+
this
511+
.testTag(tag)
512+
.semantics { contentDescription = tag }
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.getcode.ui.core
2+
3+
import androidx.compose.ui.text.AnnotatedString
4+
import androidx.compose.ui.text.input.OffsetMapping
5+
import androidx.compose.ui.text.input.TransformedText
6+
import androidx.compose.ui.text.input.VisualTransformation
7+
8+
class MaskWithSpacesTransformation : VisualTransformation {
9+
override fun filter(text: AnnotatedString): TransformedText {
10+
val maskedText = text.text.map { char ->
11+
if (char == ' ') ' ' else ''
12+
}.joinToString("")
13+
14+
val offsetMapping = object : OffsetMapping {
15+
override fun originalToTransformed(offset: Int) = offset
16+
override fun transformedToOriginal(offset: Int) = offset
17+
}
18+
19+
return TransformedText(AnnotatedString(maskedText), offsetMapping)
20+
}
21+
}

ui/testing/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/build

ui/testing/build.gradle.kts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
2+
3+
plugins {
4+
id(Plugins.android_library)
5+
id(Plugins.kotlin_android)
6+
id(Plugins.kotlin_serialization)
7+
id(Plugins.jetbrains_compose_compiler)
8+
}
9+
10+
android {
11+
namespace = "${Gradle.codeNamespace}.ui.testing"
12+
compileSdk = Android.compileSdkVersion
13+
defaultConfig {
14+
minSdk = Android.minSdkVersion
15+
testInstrumentationRunner = Android.testInstrumentationRunner
16+
}
17+
18+
buildFeatures {
19+
compose = true
20+
}
21+
}
22+
23+
kotlin {
24+
jvmToolchain {
25+
languageVersion.set(JavaLanguageVersion.of(Versions.java))
26+
}
27+
28+
compilerOptions {
29+
jvmTarget.set(JvmTarget.fromTarget(Versions.java))
30+
optIn.addAll(
31+
"kotlin.time.ExperimentalTime",
32+
"kotlin.ExperimentalUnsignedTypes",
33+
"kotlin.RequiresOptIn"
34+
)
35+
}
36+
}
37+
38+
dependencies {
39+
implementation(platform(Libs.compose_bom))
40+
implementation(Libs.compose_ui)
41+
implementation(Libs.compose_foundation)
42+
43+
implementation(Libs.timber)
44+
}

0 commit comments

Comments
 (0)