Skip to content

Instantly share code, notes, and snippets.

@joshallenit
Last active December 1, 2021 03:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save joshallenit/959213022791c86f1fcee40b04e8fc3c to your computer and use it in GitHub Desktop.
Save joshallenit/959213022791c86f1fcee40b04e8fc3c to your computer and use it in GitHub Desktop.
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import androidx.fragment.app.Fragment
import androidx.fragment.app.testing.launchFragmentInContainer
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.typeText
import androidx.test.espresso.matcher.ViewMatchers.withClassName
import com.google.common.truth.Truth.assertThat
import org.hamcrest.Matchers
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import org.robolectric.annotation.Implementation
import org.robolectric.annotation.Implements
import org.robolectric.annotation.RealObject
import org.robolectric.shadow.api.Shadow
import org.robolectric.shadows.ShadowView
@Implements(className="android.widget.Editor\$InsertionHandleView")
class ShadowInsertionHandleView: ShadowView() { {
@RealObject
private lateinit var realInsertionHandleView: Object
@Suppress("unused", "ProtectedInFinal") // Used by Robolectric and protected recommended to limit api surface.
@Implementation
protected fun getCurrentCursorOffset(): Int {
return try {
Shadow.directlyOn(
realInsertionHandleView,
"android.widget.Editor\$InsertionHandleView",
"getCurrentCursorOffset"
)
} catch (ex: Exception) {
0
}
}
}
/**
* Class with same class structure as [android.widget.Editor.InsertionHandleView] to demonstrate that inner classes
* can normally access parent this during construction.
*/
class Editor(val mTextView: EditText) {
fun onTouchUpEvent() {
InsertionPointCursorController().show()
}
abstract inner class HandleView {
init {
updateDrawable(true)
}
open fun updateDrawable(updateDrawableWhenDragging: Boolean) {
println("getCurrentCursorOffset " + getCurrentCursorOffset())
}
abstract fun getCurrentCursorOffset(): Int
}
inner class InsertionHandleView: HandleView() {
override fun updateDrawable(updateDrawableWhenDragging: Boolean) {
super.updateDrawable(updateDrawableWhenDragging)
println("hi")
}
override fun getCurrentCursorOffset(): Int {
return mTextView.selectionStart
}
}
inner class InsertionPointCursorController {
fun show() {
getHandle()
}
fun getHandle(): InsertionHandleView {
return InsertionHandleView()
}
}
}
@RunWith(RobolectricTestRunner::class)
@Config(shadows = [ShadowInsertionHandleView::class])
class EditTextTest {
@Test
fun innerClasses_whenUnderConstruction_canAccessParentThis() {
val scenario = launchFragmentInContainer<TestFragment>()
scenario.onFragment {
val underTest = it.view as EditText
Editor(underTest).onTouchUpEvent()
}
}
@Test
fun editText_whenClickedOnWithText_doesNotCrash() {
val scenario = launchFragmentInContainer<TestFragment>()
scenario.onFragment {
val underTest = it.view as EditText
onView(withClassName(Matchers.equalTo(underTest::class.qualifiedName)))
.perform(typeText("Hello "))
.perform(typeText("c")) // No crash anymore
assertThat(underTest.text.toString()).isEqualTo("Hello c")
}
}
class TestFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return EditText(requireContext())
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment