Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
19 changes: 19 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
id("com.android.application")
id("kotlin-android")
id("org.jetbrains.kotlin.plugin.compose") version "2.2.21"
}

android {
Expand Down Expand Up @@ -34,6 +35,7 @@ android {
}
buildFeatures {
viewBinding = true
compose = true
}
buildTypes {
release {
Expand All @@ -52,6 +54,23 @@ dependencies {
implementation("com.google.android.material:material:1.13.0")
implementation(project(":MPChartLib"))

// Compose BOM
val composeBom = platform("androidx.compose:compose-bom:2024.11.00")
implementation(composeBom)
androidTestImplementation(composeBom)

// Compose dependencies
implementation("androidx.compose.material3:material3")
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.ui:ui-tooling-preview")
implementation("androidx.activity:activity-compose:1.9.3")
implementation("androidx.compose.material:material-icons-extended")
debugImplementation("androidx.compose.ui:ui-tooling")

// Compose testing
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
debugImplementation("androidx.compose.ui:ui-test-manifest")

androidTestImplementation("androidx.test.ext:junit-ktx:1.3.0")
androidTestImplementation("com.github.AppDevNext.Logcat:LogcatCoreLib:3.4")
androidTestUtil("androidx.test.services:test-services:1.6.0")
Expand Down
58 changes: 47 additions & 11 deletions app/src/androidTest/java/info/appdev/chartexample/StartTest.kt
Original file line number Diff line number Diff line change
@@ -1,27 +1,28 @@
package info.appdev.chartexample

import android.graphics.Bitmap
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createEmptyComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performScrollToIndex
import androidx.test.core.graphics.writeToTestStorage
import androidx.test.espresso.Espresso
import androidx.test.espresso.Espresso.onData
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
import androidx.test.espresso.action.ViewActions.captureToBitmap
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.intent.Intents
import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.ext.junit.rules.activityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import info.appdev.chartexample.notimportant.DemoBase.Companion.optionMenus
import info.appdev.chartexample.notimportant.MainActivity
import info.hannes.timber.DebugFormatTree
import org.hamcrest.CoreMatchers.allOf
import org.hamcrest.CoreMatchers.anything
import org.junit.After
import org.junit.Before
import org.junit.Rule
Expand All @@ -37,6 +38,9 @@ class StartTest {
@get:Rule
val activityScenarioRule = activityScenarioRule<MainActivity>()

@get:Rule
val composeTestRule = createEmptyComposeRule()

@get:Rule
var nameRule = TestName()

Expand All @@ -53,19 +57,47 @@ class StartTest {

@Test
fun smokeTestStart() {
// Wait for Compose to be ready
composeTestRule.waitForIdle()

onView(ViewMatchers.isRoot())
.perform(captureToBitmap { bitmap: Bitmap -> bitmap.writeToTestStorage("${javaClass.simpleName}_${nameRule.methodName}") })

var optionMenu = ""
// iterate samples
// iterate samples - only items with classes (not section headers)
MainActivity.menuItems.forEachIndexed { index, contentItem ->
contentItem.clazz?.let {
Timber.d("Intended ${index}-${it.simpleName}")
Timber.d("Intended ${index}-${it.simpleName}: ${contentItem.name}")

try {
onData(anything())
.inAdapterView(allOf(withId(R.id.listViewMain), isCompletelyDisplayed()))
.atPosition(index).perform(click())
// Use description to uniquely identify items since names can be duplicated
// If description exists, use it; otherwise fall back to name
val searchText = if (contentItem.desc.isNotEmpty()) {
contentItem.desc
} else {
contentItem.name
}

Timber.d("Searching for index $index: $searchText")

// Scroll to the item in the LazyColumn by index
// This ensures the item is composed and visible
try {
composeTestRule.onNodeWithTag("menuList")
.performScrollToIndex(index)
composeTestRule.waitForIdle()
} catch (e: Exception) {
Timber.w("Could not scroll to index $index: ${e.message}")
}

// Now click the item using its test tag
composeTestRule.onNodeWithTag("menuItem_$index")
.assertExists("Could not find menu item at index $index")
.performClick()

// Wait for the new activity to start
composeTestRule.waitForIdle()
Thread.sleep(300) // Increased delay for activity transition

Intents.intended(hasComponent(it.name))
onView(ViewMatchers.isRoot())
Expand All @@ -80,8 +112,12 @@ class StartTest {

//Thread.sleep(100)
Espresso.pressBack()

// Wait for MainActivity to be visible again
composeTestRule.waitForIdle()
Thread.sleep(200) // Small delay for back navigation
} catch (e: Exception) {
Timber.e(optionMenu + e.message!!)
Timber.e("Error at index $index: $optionMenu - ${e.message}", e)
onView(ViewMatchers.isRoot())
.perform(captureToBitmap { bitmap: Bitmap -> bitmap.writeToTestStorage("${javaClass.simpleName}_${nameRule.methodName}-${index}-${it.simpleName}-Error") })
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.view.WindowInsets
import android.view.WindowInsetsController
import android.view.WindowManager
import android.widget.Toast
import androidx.activity.OnBackPressedCallback
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import com.github.mikephil.charting.charts.Chart
import com.google.android.material.snackbar.Snackbar
import info.appdev.chartexample.R
Expand All @@ -40,20 +40,6 @@ abstract class DemoBase : AppCompatActivity(), ActivityCompat.OnRequestPermissio
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

// Enable fullscreen mode using modern API
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) {
window.insetsController?.let {
it.hide(WindowInsets.Type.statusBars())
it.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
}
} else {
@Suppress("DEPRECATION")
window.setFlags(
WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN
)
}

optionMenus.clear()

tfRegular = Typeface.createFromAsset(assets, "OpenSans-Regular.ttf")
Expand All @@ -62,6 +48,18 @@ abstract class DemoBase : AppCompatActivity(), ActivityCompat.OnRequestPermissio
onBackPressedDispatcher.addCallback(this, backPressedCallback)
}

override fun onStart() {
super.onStart()

// Hide status bars using modern WindowCompat API
// Note: We don't call setDecorFitsSystemWindows(false) because these activities
// use traditional AppCompat ActionBar which needs to fit within system windows
WindowCompat.getInsetsController(window, window.decorView).apply {
hide(WindowInsetsCompat.Type.statusBars())
systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
}
}

override fun onPrepareOptionsMenu(menu: Menu?): Boolean {
menu?.let {
for (i in 0 until menu.size) {
Expand Down
Loading
Loading