Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Layout tests #100

Merged
merged 29 commits into from
Jan 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
fe8182e
Translate work type statuses correctly
hueachilles Jan 5, 2024
f017966
Move app header bar into top bar for Menu screen
hueachilles Jan 6, 2024
ff6ffe5
Layout main navigation according to screen orientation
hueachilles Jan 6, 2024
fa0052f
Refactor app nav construction
hueachilles Jan 6, 2024
bc93f3e
Layout view case screen for landscape orientation
hueachilles Jan 6, 2024
b59b8f2
Layout Case photos view for landscape
hueachilles Jan 6, 2024
0c36152
Layout create/edit case screen for portrait vs landscape
hueachilles Jan 6, 2024
b9a8e7e
Layout change location for portrait or list-detail
hueachilles Jan 6, 2024
93f2db6
Correct Cases screen loading layout to fill view
hueachilles Jan 9, 2024
5e300fc
Update nav icon color correctly
hueachilles Jan 9, 2024
6a3e11f
Add sandbox app for troubleshooting features in isolation
hueachilles Jan 9, 2024
28cfc36
Add sandbox app navigation
hueachilles Jan 9, 2024
87bb894
Add bottom nav to sandbox example
hueachilles Jan 9, 2024
d76539e
Set nav item icon color according to editable state
hueachilles Jan 9, 2024
9182a1a
Increase zoom threshold for rendering markers on map view
hueachilles Jan 15, 2024
c935f9f
Decouple map dots and markers rendering
hueachilles Jan 15, 2024
59fcf15
Layout search screen for list-detail
hueachilles Jan 15, 2024
4d5dcf7
Layout invite teammate for list-detail
hueachilles Jan 15, 2024
9e08f3e
Consolidate chip component
hueachilles Jan 16, 2024
00f6036
Indicate and explain when wrong address flag is set
hueachilles Jan 16, 2024
859e013
Speed up local worksite search
hueachilles Jan 17, 2024
244c078
Show consistent visuals when searching Cases
hueachilles Jan 17, 2024
c4c2e29
Show Case with matching Case number first in search results
hueachilles Jan 17, 2024
6787c15
Correct style modifier extensions
hueachilles Jan 18, 2024
c4821a5
Fix zoom level and photo/image carousel heights
hueachilles Jan 23, 2024
3c544ac
Update incident display name to short name (for consistency)
hueachilles Jan 23, 2024
985942f
Start on automating screenshots
hueachilles Jan 23, 2024
8bf0ee9
Update auth screen for consistent UI testing
hueachilles Jan 23, 2024
9ef5ddb
Add notes for useful Maestro techniques
hueachilles Jan 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .maestro/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.png
14 changes: 14 additions & 0 deletions .maestro/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Target app
`MAESTRO_APP_ID=com.crisiscleanup.demo maestro test auth-tests`

[Specify device](https://maestro.mobile.dev/advanced/specify-a-device)
`maestro --device emulator-5554 test auth-tests`

List devices

- Android `adb devices`
- iOS `xcrun simctl list devices booted`

Full test command
`MAESTRO_APP_ID=com.crisiscleanup.demo.debug maestro --device emulator-5554 test auth-tests`
`MAESTRO_APP_ID=com.crisiscleanup.dev maestro --device device-uuid-from-list test auth-tests`
52 changes: 52 additions & 0 deletions .maestro/advanced-techniques.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
[Cross platform techniques](https://blog.mobile.dev/best-practices-for-cross-platform-maestro-ui-testing-for-android-and-ios-98d1c471a838)

## Platform specific init scripts

Specify platform specific IDs.

In a test.yaml

```yaml
- runScript:
when:
platform: iOS
file: ios/init.js

#... more runScripts (for other platforms)

- tapOn:
id: ${output.wallpapers.wallpaperItem}
index: 0
```

ios/init.js

```js
output.wallpapers = {
wallpaperItem: 'imageItem',
}
```

## Run platform specific flows

In a test.yaml this runs `Date.yaml` only on iOS. `Date.yaml` is expected to specify tests.

```yaml
- runFlow:
when:
platform: iOS
file: Date.yaml
```

See further down page on how to test platform share window.

## Inline platform specific tests

```yaml
- runFlow:
when:
platform: Android
commands:
- assertVisible:
id: "setWallpaperIconButton"
```
12 changes: 12 additions & 0 deletions .maestro/screenshot-tests/auth-screens.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
name: Auth screenshots
appId: ${MAESTRO_APP_ID}
tags:
- auth
- screenshots
---
- clearState
- launchApp
- extendedWaitUntil:
visible:
id: loginHeaderText
- takeScreenshot: AuthRoot
File renamed without changes.
85 changes: 85 additions & 0 deletions app-sandbox/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import com.google.samples.apps.nowinandroid.NiaBuildType

plugins {
id("nowinandroid.android.application")
id("nowinandroid.android.application.compose")
id("nowinandroid.android.application.flavors")
id("nowinandroid.android.hilt")
}

android {
defaultConfig {
applicationId = "com.crisiscleanup.sandbox"
versionCode = 1
versionName = "0.0.1"

vectorDrawables {
useSupportLibrary = true
}
}

buildTypes {
val debug by getting {
applicationIdSuffix = NiaBuildType.DEBUG.applicationIdSuffix
}
val release by getting {
applicationIdSuffix = NiaBuildType.RELEASE.applicationIdSuffix
}
}

packaging {
resources {
excludes.add("/META-INF/{AL2.0,LGPL2.1}")
}
}
namespace = "com.crisiscleanup.sandbox"
}

dependencies {
implementation(project(":feature:caseeditor"))

implementation(project(":core:appnav"))
implementation(project(":core:common"))
implementation(project(":core:commoncase"))
implementation(project(":core:data"))
implementation(project(":core:designsystem"))
implementation(project(":core:network"))
implementation(project(":core:model"))
implementation(project(":core:ui"))

androidTestImplementation(libs.androidx.navigation.testing)
androidTestImplementation(libs.accompanist.testharness)
androidTestImplementation(kotlin("test"))
debugImplementation(libs.androidx.compose.ui.testManifest)
debugImplementation(project(":ui-test-hilt-manifest"))

implementation(libs.kotlinx.serialization.json)

implementation(libs.accompanist.systemuicontroller)
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.appcompat)
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.core.splashscreen)
implementation(libs.androidx.compose.runtime)
implementation(libs.androidx.lifecycle.runtimeCompose)
implementation(libs.androidx.compose.runtime.tracing)
implementation(libs.androidx.compose.material3.windowSizeClass)
implementation(libs.androidx.hilt.navigation.compose)
implementation(libs.androidx.navigation.compose)
implementation(libs.androidx.window.manager)
implementation(libs.androidx.profileinstaller)

implementation(libs.coil.kt)

implementation(libs.kotlinx.coroutines.playservices)
implementation(libs.playservices.maps)
}

// androidx.test is forcing JUnit, 4.12. This forces it to use 4.13
configurations.configureEach {
resolutionStrategy {
force(libs.junit4)
// Temporary workaround for https://issuetracker.google.com/174733673
force("org.objenesis:objenesis:2.6")
}
}
26 changes: 26 additions & 0 deletions app-sandbox/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<application
android:name=".SandboxApplication"
android:icon="@mipmap/ic_launcher"
android:label="cc-sandbox"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:targetApi="31">
<profileable
android:shell="true"
tools:targetApi="31" />

<activity
android:name=".SandboxActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.crisiscleanup.sandbox

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi
import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass
import androidx.core.view.WindowCompat
import com.crisiscleanup.core.designsystem.theme.CrisisCleanupTheme
import com.google.android.gms.maps.MapsInitializer
import dagger.hilt.android.AndroidEntryPoint

@OptIn(ExperimentalMaterial3WindowSizeClassApi::class)
@AndroidEntryPoint
class SandboxActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
MapsInitializer.initialize(this, MapsInitializer.Renderer.LATEST) {}

// Turn off the decor fitting system windows, which allows us to handle insets,
// including IME animations
WindowCompat.setDecorFitsSystemWindows(window, false)

setContent {
CrisisCleanupTheme {
SandboxApp(
windowSizeClass = calculateWindowSizeClass(this),
)
}
}
}
}
93 changes: 93 additions & 0 deletions app-sandbox/src/main/java/com/crisiscleanup/sandbox/SandboxApp.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package com.crisiscleanup.sandbox

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.windowsizeclass.WindowSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import com.crisiscleanup.core.designsystem.component.CrisisCleanupBackground
import com.crisiscleanup.core.designsystem.component.CrisisCleanupTextButton
import com.crisiscleanup.core.designsystem.theme.listItemSpacedBy
import com.crisiscleanup.sandbox.navigation.SandboxNavHost
import com.crisiscleanup.sandbox.navigation.chipsRoute
import com.crisiscleanup.sandbox.navigation.navigateToBottomNav
import com.crisiscleanup.sandbox.navigation.navigateToCheckboxes
import com.crisiscleanup.sandbox.navigation.navigateToChips

@Composable
fun SandboxApp(
windowSizeClass: WindowSizeClass,
appState: SandboxAppState = rememberAppState(
windowSizeClass = windowSizeClass,
),
) {
CrisisCleanupBackground {
Box(Modifier.fillMaxSize()) {
val snackbarHostState = remember { SnackbarHostState() }

Scaffold(
containerColor = Color.Transparent,
contentColor = MaterialTheme.colorScheme.onBackground,
contentWindowInsets = WindowInsets(0, 0, 0, 0),
snackbarHost = { SnackbarHost(snackbarHostState) },
topBar = {},
bottomBar = {},
) { padding ->
Column(
Modifier
.fillMaxSize()
.padding(padding)
.consumeWindowInsets(padding)
.windowInsetsPadding(WindowInsets.safeDrawing),
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
SandboxNavHost(
appState.navController,
chipsRoute,
)
}
}
}
}
}

@OptIn(ExperimentalLayoutApi::class)
@Composable
fun RootRoute(navController: NavController) {
Column {
Spacer(Modifier.weight(1f))
FlowRow(
horizontalArrangement = listItemSpacedBy,
verticalArrangement = listItemSpacedBy,
maxItemsInEachRow = 6,
) {
CrisisCleanupTextButton(text = "Bottom nav") {
navController.navigateToBottomNav()
}
CrisisCleanupTextButton(text = "Checkboxes") {
navController.navigateToCheckboxes()
}
CrisisCleanupTextButton(text = "Chips") {
navController.navigateToChips()
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.crisiscleanup.sandbox

import androidx.compose.material3.windowsizeclass.WindowSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController
import kotlinx.coroutines.CoroutineScope

@Composable
fun rememberAppState(
windowSizeClass: WindowSizeClass,
coroutineScope: CoroutineScope = rememberCoroutineScope(),
navController: NavHostController = rememberNavController(),
): SandboxAppState {
return remember(navController, coroutineScope, windowSizeClass) {
SandboxAppState(navController, coroutineScope, windowSizeClass)
}
}

@Stable
class SandboxAppState(
val navController: NavHostController,
val coroutineScope: CoroutineScope,
val windowSizeClass: WindowSizeClass,
) {
fun onBack() {
navController.popBackStack()
}
}
Loading
Loading