-
Notifications
You must be signed in to change notification settings - Fork 45
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(android): implement bug reporting
- Loading branch information
Showing
120 changed files
with
4,251 additions
and
310 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
63 changes: 63 additions & 0 deletions
63
android/measure/src/androidTest/java/sh/measure/android/EspressoHelpers.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package sh.measure.android | ||
|
||
import android.view.View | ||
import android.view.ViewGroup | ||
import androidx.test.espresso.Espresso.onView | ||
import androidx.test.espresso.NoMatchingViewException | ||
import androidx.test.espresso.assertion.ViewAssertions.matches | ||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed | ||
import org.hamcrest.Description | ||
import org.hamcrest.Matcher | ||
import org.hamcrest.TypeSafeMatcher | ||
|
||
/** | ||
* Helper function to wait for a view to be displayed with a timeout. | ||
* @param viewMatcher The matcher for the view to find | ||
* @param timeoutMillis Maximum time to wait in milliseconds | ||
* @param intervalMillis Time to wait between checks in milliseconds | ||
* @return True if the view was found within the timeout, false otherwise | ||
*/ | ||
fun waitForViewToBeDisplayed( | ||
viewMatcher: Matcher<View>, | ||
timeoutMillis: Long = 3000, | ||
intervalMillis: Long = 100, | ||
): Boolean { | ||
val startTime = System.currentTimeMillis() | ||
var viewFound = false | ||
|
||
while (!viewFound && System.currentTimeMillis() - startTime < timeoutMillis) { | ||
try { | ||
onView(viewMatcher).check(matches(isDisplayed())) | ||
viewFound = true | ||
} catch (e: NoMatchingViewException) { | ||
Thread.sleep(intervalMillis) | ||
} | ||
} | ||
|
||
return viewFound | ||
} | ||
|
||
/** | ||
* Returns a matcher that matches a view that is the nth child of a parent view that matches the given parent matcher. | ||
* | ||
* This matcher can be used to find a specific child view at a certain position within a parent view, | ||
* which is useful when multiple similar child views exist and you need to target a specific one by position. | ||
* | ||
* @param parentMatcher The matcher that will match the parent of the view | ||
* @param childPosition The position of the child view to match (0-based index) | ||
* @return A Matcher<View> that matches a view at the specified child position within a parent matching parentMatcher | ||
*/ | ||
fun nthChildOf(parentMatcher: Matcher<View>, childPosition: Int): Matcher<View> { | ||
return object : TypeSafeMatcher<View>() { | ||
override fun describeTo(description: Description) { | ||
description.appendText("with $childPosition child view of type parentMatcher") | ||
} | ||
|
||
override fun matchesSafely(view: View): Boolean { | ||
if (view.parent !is ViewGroup) return false | ||
val parent = view.parent as ViewGroup | ||
|
||
return parentMatcher.matches(parent) && parent.getChildAt(childPosition) == view | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
22 changes: 22 additions & 0 deletions
22
android/measure/src/androidTest/java/sh/measure/android/FakeSignalStore.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package sh.measure.android | ||
|
||
import sh.measure.android.events.Event | ||
import sh.measure.android.storage.SignalStore | ||
import sh.measure.android.tracing.SpanData | ||
|
||
internal class FakeSignalStore : SignalStore { | ||
val trackedEvents = mutableListOf<Event<*>>() | ||
val trackedSpans = mutableListOf<SpanData>() | ||
|
||
override fun <T> store(event: Event<T>) { | ||
trackedEvents.add(event) | ||
} | ||
|
||
override fun store(spanData: SpanData) { | ||
trackedSpans.add(spanData) | ||
} | ||
|
||
override fun flush() { | ||
// No-op | ||
} | ||
} |
105 changes: 105 additions & 0 deletions
105
android/measure/src/androidTest/java/sh/measure/android/MsrBugReportActivityRobot.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
package sh.measure.android | ||
|
||
import android.app.Application | ||
import android.content.res.Configuration | ||
import android.view.ViewGroup | ||
import androidx.test.espresso.Espresso | ||
import androidx.test.espresso.Espresso.onView | ||
import androidx.test.espresso.action.ViewActions.click | ||
import androidx.test.espresso.action.ViewActions.closeSoftKeyboard | ||
import androidx.test.espresso.action.ViewActions.replaceText | ||
import androidx.test.espresso.assertion.ViewAssertions.doesNotExist | ||
import androidx.test.espresso.assertion.ViewAssertions.matches | ||
import androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA | ||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed | ||
import androidx.test.espresso.matcher.ViewMatchers.withId | ||
import androidx.test.platform.app.InstrumentationRegistry | ||
import androidx.test.uiautomator.UiDevice | ||
import org.hamcrest.core.AllOf.allOf | ||
import org.junit.Assert | ||
import sh.measure.android.config.MeasureConfig | ||
import sh.measure.android.events.EventType | ||
|
||
internal class MsrBugReportActivityRobot { | ||
private val instrumentation = InstrumentationRegistry.getInstrumentation() | ||
private val context = instrumentation.context.applicationContext | ||
private val device = UiDevice.getInstance(instrumentation) | ||
private val signalStore = FakeSignalStore() | ||
|
||
fun initializeMeasure(config: MeasureConfig = MeasureConfig()): TestMeasureInitializer { | ||
val initializer = TestMeasureInitializer( | ||
application = context as Application, | ||
inputConfig = config, | ||
signalStore = signalStore, | ||
) | ||
Measure.initForInstrumentationTest(initializer) | ||
return initializer | ||
} | ||
|
||
fun assertSendCtaEnabled(enabled: Boolean) { | ||
onView(withId(R.id.tv_send)).check(matches(isDisplayed())).check { view, _ -> | ||
Assert.assertEquals(enabled, view.isEnabled) | ||
} | ||
} | ||
|
||
fun enterDescription(length: Int = 100) { | ||
onView(withId(R.id.et_description)).perform(replaceText("a".repeat(length))) | ||
.perform(closeSoftKeyboard()) | ||
device.waitForIdle() | ||
} | ||
|
||
fun assertBugReportActivityLaunched() { | ||
waitForViewToBeDisplayed(withId(R.id.tv_title), 3000) | ||
onView(withId(R.id.tv_title)).check(matches(isDisplayed())) | ||
} | ||
|
||
fun assertBugReportActivityNotVisible() { | ||
device.waitForIdle() | ||
onView(withId(R.id.tv_title)).check(doesNotExist()) | ||
} | ||
|
||
fun assertTotalScreenshots(value: Int) { | ||
onView(withId(R.id.sl_screenshots_container)).check { view, _ -> | ||
val viewGroup = view as ViewGroup | ||
Assert.assertEquals(value, viewGroup.childCount) | ||
} | ||
} | ||
|
||
fun clickCloseButton() { | ||
onView(withId(R.id.btn_close)).perform(click()) | ||
} | ||
|
||
fun removeScreenshot(index: Int) { | ||
onView( | ||
allOf( | ||
withId(R.id.closeButton), | ||
isDescendantOfA(nthChildOf(withId(R.id.sl_screenshots_container), index)), | ||
), | ||
).perform(click()) | ||
} | ||
|
||
fun clickSendCTA() { | ||
Espresso.closeSoftKeyboard() | ||
device.waitForIdle() | ||
onView(withId(R.id.tv_send)).perform(click()) | ||
} | ||
|
||
fun assetBugReportTracked(attachmentCount: Int = 0, userDefinedAttrCount: Int = 0) { | ||
device.waitForIdle() | ||
val event = signalStore.trackedEvents.find { | ||
it.type == EventType.BUG_REPORT | ||
} | ||
Assert.assertNotNull(event) | ||
Assert.assertEquals(attachmentCount, event?.attachments?.size) | ||
Assert.assertEquals(userDefinedAttrCount, event?.userDefinedAttributes?.size) | ||
} | ||
|
||
fun triggerConfigurationChange() { | ||
val currentOrientation = context.resources.configuration.orientation | ||
when (currentOrientation) { | ||
Configuration.ORIENTATION_PORTRAIT -> device.setOrientationLeft() | ||
Configuration.ORIENTATION_LANDSCAPE -> device.setOrientationNatural() | ||
} | ||
device.waitForIdle() | ||
} | ||
} |
Oops, something went wrong.