From be28767a96418d4eded809aa8dc561b7e2348dd6 Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Sat, 24 Aug 2019 05:37:03 -0400 Subject: [PATCH 01/41] Removed `exclude group: 'com.android.support'` (#317) You could add in the AndroidX excludes as well, but it seemed like it was going to make this pretty complicated in onboarding instructions, and not everyone uses those androidx packages: ``` exclude group: 'androidx.annotation' exclude group: 'androidx.legacy' exclude group: 'androidx.recyclerview' exclude group: 'androidx.vectordrawable' ``` *Dependencies* * Old version with support library: https://mvnrepository.com/artifact/com.schibsted.spain/barista/2.10.0 * This version with Android X: https://mvnrepository.com/artifact/com.schibsted.spain/barista/3.2.0 --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 6c58806bc..1d0a1b86f 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,6 @@ Barista makes developing UI test faster, easier and more predictable. Built on t Import Barista as a testing dependency: ```gradle androidTestImplementation('com.schibsted.spain:barista:3.2.0') { - exclude group: 'com.android.support' exclude group: 'org.jetbrains.kotlin' // Only if you already use Kotlin in your project } ``` From acd9cd60645bf3522d0a5023d2b24429166c13ae Mon Sep 17 00:00:00 2001 From: Roc Boronat Date: Tue, 3 Sep 2019 21:32:07 +0200 Subject: [PATCH 02/41] Use always the id list in the example (#318) It's just a way to keep a style and ease glancing at the readme --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 1d0a1b86f..f571634fc 100644 --- a/README.md +++ b/README.md @@ -78,13 +78,13 @@ clearText(R.id.edittext) clickListItem(R.id.list, 4); clickListItemChild(R.id.list, 3, R.id.row_button); scrollListToPosition(R.id.list, 4); -assertListItemCount(R.id.listId, 5) -assertListNotEmpty(R.id.listId) -assertDisplayedAtPosition(R.id.recycler, 0, "text"); -assertDisplayedAtPosition(R.id.listId, 0, R.id.text_field, "text"); -assertDisplayedAtPosition(R.id.recycler, 0, R.string.hello_world); -assertDisplayedAtPosition(R.id.listId, 0, R.id.text_field, R.string.hello_world); -assertCustomAssertionAtPosition(R.id.recycler, 0, customViewAssertion); +assertListItemCount(R.id.list, 5) +assertListNotEmpty(R.id.list) +assertDisplayedAtPosition(R.id.list, 0, "text"); +assertDisplayedAtPosition(R.id.list, 0, R.id.text_field, "text"); +assertDisplayedAtPosition(R.id.list, 0, R.string.hello_world); +assertDisplayedAtPosition(R.id.list, 0, R.id.text_field, R.string.hello_world); +assertCustomAssertionAtPosition(R.id.list, 0, customViewAssertion); clickSpinnerItem(R.id.spinner, 1); ``` From fb42c47e4dd84119a66a099f5e341c53d23177dc Mon Sep 17 00:00:00 2001 From: Roc Boronat Date: Tue, 3 Sep 2019 21:32:47 +0200 Subject: [PATCH 03/41] Use this_style instead of thisStyle as ids in the readme (#319) Let's use the same naming everywhere --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f571634fc..728bc10a8 100644 --- a/README.md +++ b/README.md @@ -293,12 +293,12 @@ assertNotContains(R.id.textview, R.string.text); #### Check text is given color ```java -assertTextColorIs(R.id.someRedText, R.color.red); -assertTextColorIs(R.id.someColorListText, R.color.state_list); +assertTextColorIs(R.id.some_red_text, R.color.red); +assertTextColorIs(R.id.some_color_list_text, R.color.state_list); // ...or not? -assertTextColorIsNot(R.id.someRedText, R.color.blue); -assertTextColorIsNot(R.id.someColorListText, R.color.another_state_list); +assertTextColorIsNot(R.id.some_red_text, R.color.blue); +assertTextColorIsNot(R.id.some_color_list_text, R.color.another_state_list); ``` #### Check recyclerView item count against expected item count From 12a269fc472cf7aac9b1b055f6ae4ab3463b2e7b Mon Sep 17 00:00:00 2001 From: Esteve Aguilera Date: Wed, 30 Oct 2019 12:54:53 +0100 Subject: [PATCH 04/41] Fix a typo in the issue template (#327) --- .github/ISSUE_TEMPLATE/Feature_request.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/Feature_request.md b/.github/ISSUE_TEMPLATE/Feature_request.md index 0b48fdce3..ae0c732db 100644 --- a/.github/ISSUE_TEMPLATE/Feature_request.md +++ b/.github/ISSUE_TEMPLATE/Feature_request.md @@ -8,7 +8,7 @@ about: Suggest an idea for this library **Description what you'd like to happen:** -A clear description if the feature or behavior you'd like implemented. +A clear description of the feature or behavior you'd like implemented. **Example espresso code if available** From 5e882f063a27f571f7ef50fb14c84d64dbe312e0 Mon Sep 17 00:00:00 2001 From: glureau-betclic <35920611+glureau-betclic@users.noreply.github.com> Date: Wed, 30 Oct 2019 12:56:34 +0100 Subject: [PATCH 05/41] Add scrollTo() signatures to support scrolling on a custom view matcher (#323) --- .../interaction/BaristaScrollInteractions.kt | 86 +++++++++++-------- .../spain/barista/sample/ScrollsTest.java | 19 ++++ 2 files changed, 67 insertions(+), 38 deletions(-) diff --git a/library/src/main/java/com/schibsted/spain/barista/interaction/BaristaScrollInteractions.kt b/library/src/main/java/com/schibsted/spain/barista/interaction/BaristaScrollInteractions.kt index 267bdcfa2..3d59982aa 100644 --- a/library/src/main/java/com/schibsted/spain/barista/interaction/BaristaScrollInteractions.kt +++ b/library/src/main/java/com/schibsted/spain/barista/interaction/BaristaScrollInteractions.kt @@ -1,9 +1,9 @@ package com.schibsted.spain.barista.interaction +import android.view.View import androidx.test.espresso.Espresso.onView import androidx.test.espresso.PerformException import androidx.test.espresso.matcher.ViewMatchers.withText -import android.view.View import com.schibsted.spain.barista.internal.failurehandler.SpyFailureHandler import com.schibsted.spain.barista.internal.failurehandler.description import com.schibsted.spain.barista.internal.util.resourceMatcher @@ -16,43 +16,53 @@ import org.hamcrest.Matcher */ object BaristaScrollInteractions { - // This value has been mathematically calculated and proven to be precisely the exact number of retries needed to always work. - // Not really, we just tried hundreds of times with different values and this seems to be the best one. - private val MAX_SCROLL_ATTEMPTS = 50 - - @JvmStatic - fun scrollTo(id: Int) { - scrollWithMultipleAttempts(id.resourceMatcher(), failAtEnd = true) - } - - @JvmStatic - fun scrollTo(text: String) { - scrollWithMultipleAttempts(withText(text), failAtEnd = true) - } - - @JvmStatic - fun safelyScrollTo(id: Int) { - scrollWithMultipleAttempts(id.resourceMatcher(), failAtEnd = false) - } - - @JvmStatic - fun safelyScrollTo(text: String) { - scrollWithMultipleAttempts(withText(text), failAtEnd = false) - } - - private fun scrollWithMultipleAttempts(matcher: Matcher, failAtEnd: Boolean) { - val spyFailureHandler = SpyFailureHandler() - for (i in 1..MAX_SCROLL_ATTEMPTS) { - try { - onView(matcher) - .withFailureHandler(spyFailureHandler) - .perform(nestedScrollToAction()) - } catch (exception: PerformException) { - if (i == MAX_SCROLL_ATTEMPTS && failAtEnd) { - spyFailureHandler - .resendLastError("Could not scroll to ${matcher.description()}. Retried ${MAX_SCROLL_ATTEMPTS} times but all failed") + // This value has been mathematically calculated and proven to be precisely the exact number of retries needed to always work. + // Not really, we just tried hundreds of times with different values and this seems to be the best one. + private val MAX_SCROLL_ATTEMPTS = 50 + + @JvmStatic + fun scrollTo(matcher: Matcher) { + scrollWithMultipleAttempts(matcher, failAtEnd = true) + } + + @JvmStatic + fun scrollTo(id: Int) { + scrollWithMultipleAttempts(id.resourceMatcher(), failAtEnd = true) + } + + @JvmStatic + fun scrollTo(text: String) { + scrollWithMultipleAttempts(withText(text), failAtEnd = true) + } + + @JvmStatic + fun safelyScrollTo(matcher: Matcher) { + scrollWithMultipleAttempts(matcher, failAtEnd = false) + } + + @JvmStatic + fun safelyScrollTo(id: Int) { + scrollWithMultipleAttempts(id.resourceMatcher(), failAtEnd = false) + } + + @JvmStatic + fun safelyScrollTo(text: String) { + scrollWithMultipleAttempts(withText(text), failAtEnd = false) + } + + private fun scrollWithMultipleAttempts(matcher: Matcher, failAtEnd: Boolean) { + val spyFailureHandler = SpyFailureHandler() + for (i in 1..MAX_SCROLL_ATTEMPTS) { + try { + onView(matcher) + .withFailureHandler(spyFailureHandler) + .perform(nestedScrollToAction()) + } catch (exception: PerformException) { + if (i == MAX_SCROLL_ATTEMPTS && failAtEnd) { + spyFailureHandler + .resendLastError("Could not scroll to ${matcher.description()}. Retried ${MAX_SCROLL_ATTEMPTS} times but all failed") + } + } } - } } - } } diff --git a/sample/src/androidTest/java/com/schibsted/spain/barista/sample/ScrollsTest.java b/sample/src/androidTest/java/com/schibsted/spain/barista/sample/ScrollsTest.java index 125db8282..3727de406 100644 --- a/sample/src/androidTest/java/com/schibsted/spain/barista/sample/ScrollsTest.java +++ b/sample/src/androidTest/java/com/schibsted/spain/barista/sample/ScrollsTest.java @@ -8,6 +8,7 @@ import org.junit.Test; import org.junit.runner.RunWith; +import static androidx.test.espresso.matcher.ViewMatchers.withId; import static com.schibsted.spain.barista.assertion.BaristaVisibilityAssertions.assertDisplayed; import static com.schibsted.spain.barista.assertion.BaristaVisibilityAssertions.assertNotDisplayed; import static com.schibsted.spain.barista.interaction.BaristaScrollInteractions.safelyScrollTo; @@ -22,6 +23,15 @@ public class ScrollsTest { @Rule public FailureHandlerValidatorRule handlerValidator = new FailureHandlerValidatorRule(); + @Test + public void scrollsInsideScrollable_byCustomMatcher() { + assertTopVisible(); + + scrollTo(withId(R.id.really_far_away_button)); + + assertBottomVisible(); + } + @Test public void scrollsInsideScrollable_byId() { assertTopVisible(); @@ -56,6 +66,15 @@ public void scrollsOutsideScrollableFails() throws Exception { scrollTo(R.id.centered_button); } + @Test + public void safelyScrollsInsideScrollable_byCustomMatcher() throws Exception { + assertTopVisible(); + + safelyScrollTo(withId(R.id.really_far_away_button)); + + assertBottomVisible(); + } + @Test public void safelyScrollsInsideScrollable_byId() throws Exception { assertTopVisible(); From 7cbb2e4d05c48485acd9f1039d6fd3d26bfb2f73 Mon Sep 17 00:00:00 2001 From: Roc Boronat Date: Tue, 12 Nov 2019 15:56:45 +0100 Subject: [PATCH 06/41] Better explain how to mock the camera (#325) Right now, we're only showing the method that mocks the camera. But we don't share how to do it. --- README.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 728bc10a8..b0e213f50 100644 --- a/README.md +++ b/README.md @@ -353,10 +353,17 @@ assertAny(R.id.radioGroup, "selected option is the second one") { } ``` -## Barista’s Intents API +## Mocking the Intent results + +Mocking the Android Camera Intent is a tricky thing to do. To accomplish it in no time, Barista gives a way to do it in one line: the method `mockAndroidCamera()`. This method does all the magic to mock the result of the camera. One more thing to do: you have to call `Intents.init()` before calling `mockAndroidCamera()`, and `Intents.release()` after doing the action that launches the camera. You could also use `IntentsTestRule` instead of the common `ActivityTestRule` to skip it, but as we recommend the use of `BaristaRule`, it's easier to just call both methods manually when needed. + +Here's an example to copy paste: + ```java -// Creates a Bitmap on a camera provided URI +Intents.init(); mockAndroidCamera(); +clickOn(R.id.launch_camera); +Intents.release(); ``` ## Runtime Permissions From 0b37abbe13cb7310da29d2d30a016a90c63b9407 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernat=20Borr=C3=A1s=20Paronella?= Date: Mon, 18 Nov 2019 23:02:51 +0100 Subject: [PATCH 07/41] Add chips interactions and assertions (#321) * Add mdc dependency * Add basic chips test * Add chips selected check * Add close chip test * Fix lint * Rollback to material stable version 1.0.0 * Test that the assertions fail when their check fails * Specify how the expected Exception should be * Assert by text instead of id to ease understanding the test * Change the test name to clarify that the tested code is not the assertion but the closeChip() method --- library/build.gradle | 2 + .../assertion/BaristaChipAssertions.kt | 16 +++ .../interaction/BaristaChipInteractions.kt | 13 ++ .../internal/viewaction/ChipViewActions.kt | 29 ++++ sample/build.gradle | 1 + .../spain/barista/sample/ChipsTest.kt | 79 +++++++++++ sample/src/main/AndroidManifest.xml | 1 + .../spain/barista/sample/CameraActivity.kt | 2 + .../spain/barista/sample/ChipsActivity.kt | 20 +++ sample/src/main/res/layout/activity_chips.xml | 132 ++++++++++++++++++ sample/src/main/res/values/styles.xml | 2 + 11 files changed, 297 insertions(+) create mode 100644 library/src/main/java/com/schibsted/spain/barista/assertion/BaristaChipAssertions.kt create mode 100644 library/src/main/java/com/schibsted/spain/barista/interaction/BaristaChipInteractions.kt create mode 100644 library/src/main/java/com/schibsted/spain/barista/internal/viewaction/ChipViewActions.kt create mode 100644 sample/src/androidTest/java/com/schibsted/spain/barista/sample/ChipsTest.kt create mode 100644 sample/src/main/java/com/schibsted/spain/barista/sample/ChipsActivity.kt create mode 100644 sample/src/main/res/layout/activity_chips.xml diff --git a/library/build.gradle b/library/build.gradle index 7d1725595..6015575fc 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -28,6 +28,8 @@ dependencies { implementation 'androidx.vectordrawable:vectordrawable-animated:1.0.0' implementation 'androidx.recyclerview:recyclerview:1.0.0' + implementation 'com.google.android.material:material:1.0.0' + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" testImplementation 'junit:junit:4.12' diff --git a/library/src/main/java/com/schibsted/spain/barista/assertion/BaristaChipAssertions.kt b/library/src/main/java/com/schibsted/spain/barista/assertion/BaristaChipAssertions.kt new file mode 100644 index 000000000..19c12eb37 --- /dev/null +++ b/library/src/main/java/com/schibsted/spain/barista/assertion/BaristaChipAssertions.kt @@ -0,0 +1,16 @@ +package com.schibsted.spain.barista.assertion + +import android.view.View +import androidx.annotation.IdRes +import com.google.android.material.chip.ChipGroup +import com.schibsted.spain.barista.assertion.BaristaAssertions.assertAny + +object BaristaChipAssertions { + + @JvmStatic + fun assertAnyChipSelected(@IdRes viewId: Int) { + assertAny(viewId) { + it.checkedChipId != View.NO_ID + } + } +} \ No newline at end of file diff --git a/library/src/main/java/com/schibsted/spain/barista/interaction/BaristaChipInteractions.kt b/library/src/main/java/com/schibsted/spain/barista/interaction/BaristaChipInteractions.kt new file mode 100644 index 000000000..2d3f62f1f --- /dev/null +++ b/library/src/main/java/com/schibsted/spain/barista/interaction/BaristaChipInteractions.kt @@ -0,0 +1,13 @@ +package com.schibsted.spain.barista.interaction + +import com.schibsted.spain.barista.internal.performAction +import com.schibsted.spain.barista.internal.util.resourceMatcher +import com.schibsted.spain.barista.internal.viewaction.ChipViewActions.removeChip + +object BaristaChipInteractions { + + @JvmStatic + fun closeChip(resId: Int) { + resId.resourceMatcher().performAction(removeChip()) + } +} diff --git a/library/src/main/java/com/schibsted/spain/barista/internal/viewaction/ChipViewActions.kt b/library/src/main/java/com/schibsted/spain/barista/internal/viewaction/ChipViewActions.kt new file mode 100644 index 000000000..d4e28cc9c --- /dev/null +++ b/library/src/main/java/com/schibsted/spain/barista/internal/viewaction/ChipViewActions.kt @@ -0,0 +1,29 @@ +package com.schibsted.spain.barista.internal.viewaction + +import android.view.View +import androidx.test.espresso.UiController +import androidx.test.espresso.ViewAction +import androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import com.google.android.material.chip.Chip +import org.hamcrest.Matcher +import org.hamcrest.Matchers.allOf + +object ChipViewActions { + + @JvmStatic + fun removeChip(): ViewAction { + return object : ViewAction { + override fun getConstraints(): Matcher { + return allOf(isDisplayed(), isAssignableFrom(Chip::class.java)) + } + + override fun perform(uiController: UiController, view: View) { + val chip = view as Chip + chip.performCloseIconClick() + } + + override fun getDescription(): String = "Close chip" + } + } +} \ No newline at end of file diff --git a/sample/build.gradle b/sample/build.gradle index 7ae4df582..4bbe4c7a0 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -33,6 +33,7 @@ dependencies { implementation 'com.google.android.material:material:1.0.0' implementation 'androidx.annotation:annotation:1.0.2' implementation 'com.github.bumptech.glide:glide:4.7.1' + implementation 'com.google.android.material:material:1.0.0' androidTestImplementation project(':library') androidTestImplementation "org.assertj:assertj-core:2.9.1" diff --git a/sample/src/androidTest/java/com/schibsted/spain/barista/sample/ChipsTest.kt b/sample/src/androidTest/java/com/schibsted/spain/barista/sample/ChipsTest.kt new file mode 100644 index 000000000..c2c3668a9 --- /dev/null +++ b/sample/src/androidTest/java/com/schibsted/spain/barista/sample/ChipsTest.kt @@ -0,0 +1,79 @@ +package com.schibsted.spain.barista.sample + +import androidx.test.rule.ActivityTestRule +import com.schibsted.spain.barista.assertion.BaristaCheckedAssertions.assertChecked +import com.schibsted.spain.barista.assertion.BaristaCheckedAssertions.assertUnchecked +import com.schibsted.spain.barista.assertion.BaristaChipAssertions.assertAnyChipSelected +import com.schibsted.spain.barista.assertion.BaristaVisibilityAssertions.assertDisplayed +import com.schibsted.spain.barista.interaction.BaristaChipInteractions.closeChip +import com.schibsted.spain.barista.interaction.BaristaClickInteractions.clickOn +import com.schibsted.spain.barista.internal.failurehandler.BaristaException +import com.schibsted.spain.barista.sample.util.SpyFailureHandlerRule +import junit.framework.AssertionFailedError +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.ThrowableAssert.catchThrowable +import org.junit.Rule +import org.junit.Test + +class ChipsTest { + + @get:Rule + var activityRule = ActivityTestRule(ChipsActivity::class.java) + + @get:Rule + var spyFailureHandlerRule = SpyFailureHandlerRule() + + @Test + fun assertCheckedChip() { + assertChecked(R.id.checkedChip) + + spyFailureHandlerRule.assertNoEspressoFailures() + } + + @Test + fun assertUncheckedChip() { + assertUnchecked(R.id.uncheckedChip) + + spyFailureHandlerRule.assertNoEspressoFailures() + } + + @Test + fun assertUncheckedChip_failsWhenNeeded() { + val thrown = catchThrowable{assertUnchecked(R.id.checkedChip)} + + spyFailureHandlerRule.assertEspressoFailures(1) + + assertThat(thrown).isInstanceOf(BaristaException::class.java) + .hasMessage("View (with id: com.schibsted.spain.barista.sample:id/checkedChip) didn't match condition (not with checkbox state: is )") + .hasCauseInstanceOf(AssertionFailedError::class.java) + } + + @Test + fun checkOnChipOnSingleChoiceChipGroup_hasOnlyOneSelectedChip() { + clickOn(R.id.chip1) + + assertAnyChipSelected(R.id.singleSelectionChildGroup) + + spyFailureHandlerRule.assertNoEspressoFailures() + } + + @Test + fun checkOnChipOnSingleChoiceChipGroup_hasOnlyOneSelectedChip_failsWhenNeeded() { + val thrown = catchThrowable{assertAnyChipSelected(R.id.singleSelectionChildGroup)} + + spyFailureHandlerRule.assertEspressoFailures(1) + + assertThat(thrown).isInstanceOf(BaristaException::class.java) + .hasMessage("View (with id: com.schibsted.spain.barista.sample:id/singleSelectionChildGroup) didn't match condition (custom condition [use `assertionDescription` parameter on `assertAny` to setup descriptive message])") + .hasCauseInstanceOf(AssertionFailedError::class.java) + } + + @Test + fun testCloseChip() { + closeChip(R.id.closeChip) + + assertDisplayed("A chip has been closed") + + spyFailureHandlerRule.assertNoEspressoFailures() + } +} \ No newline at end of file diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml index ad1df3fe4..66252af12 100644 --- a/sample/src/main/AndroidManifest.xml +++ b/sample/src/main/AndroidManifest.xml @@ -36,6 +36,7 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sample/src/main/res/values/styles.xml b/sample/src/main/res/values/styles.xml index 1a9bc9698..44a6ae68f 100644 --- a/sample/src/main/res/values/styles.xml +++ b/sample/src/main/res/values/styles.xml @@ -9,6 +9,8 @@ @color/colorAccent + + + + + + + + + From 92d78992056caad95afad84cbbda95bbc070eae0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernat=20Borr=C3=A1s?= Date: Wed, 15 Apr 2020 14:17:56 +0200 Subject: [PATCH 15/41] Release 3.4.0 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index bff7407cf..2fe064dbe 100644 --- a/build.gradle +++ b/build.gradle @@ -28,4 +28,4 @@ ext.compileSdkVersionDeclared = 29 ext.supportLibVersion = '27.1.1' ext.espressoVersion = '3.0.2' ext.uiAutomatorVersion = '2.1.3' -ext.baristaVersion = '3.3.0' +ext.baristaVersion = '3.4.0' From ea4593fa5f1bff135192c21590fe2c64880a2a7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernat=20Borr=C3=A1s?= Date: Wed, 15 Apr 2020 14:19:00 +0200 Subject: [PATCH 16/41] Update version on radme to 3.4.0 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f4fe2035e..388fb97dd 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Barista makes developing UI test faster, easier and more predictable. Built on t Import Barista as a testing dependency: ```gradle -androidTestImplementation('com.schibsted.spain:barista:3.3.0') { +androidTestImplementation('com.schibsted.spain:barista:3.4.0') { exclude group: 'org.jetbrains.kotlin' // Only if you already use Kotlin in your project } ``` From 7e7df5304dc75924d7d7654b93e1f153df9ef56b Mon Sep 17 00:00:00 2001 From: Javier Marsicano Date: Thu, 7 May 2020 04:28:49 -0300 Subject: [PATCH 17/41] Exclude Room metadata when clearing DB (#343) * -provide Room metadata table name and method to exclude it from clearing DB process -implement test case: doesNotDeleteRoomMetadata * -replace deprecated method: InstrumentationRegistry.getTargetContext() * -fix to exclude always Room metadata from clearing Co-authored-by: Javier Marsicano --- .../barista/rule/cleardata/ClearDatabaseRule.kt | 2 ++ .../rule/cleardata/internal/DatabaseOperations.kt | 4 ++-- .../barista/cleardata/ClearDatabaseRuleTest.java | 13 +++++++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/library/src/main/java/com/schibsted/spain/barista/rule/cleardata/ClearDatabaseRule.kt b/library/src/main/java/com/schibsted/spain/barista/rule/cleardata/ClearDatabaseRule.kt index 17860f75d..8273becdf 100644 --- a/library/src/main/java/com/schibsted/spain/barista/rule/cleardata/ClearDatabaseRule.kt +++ b/library/src/main/java/com/schibsted/spain/barista/rule/cleardata/ClearDatabaseRule.kt @@ -14,6 +14,7 @@ class ClearDatabaseRule(private val databaseOperations: DatabaseOperations = Dat companion object { @JvmField internal val UNWANTED_FILENAME_SUFFIXES = arrayOf("-journal", "-shm", "-uid", "-wal") + const val ROOM_METADATA = "room_master_table" } private var excludeTablesRegex: Regex? = null @@ -42,6 +43,7 @@ class ClearDatabaseRule(private val databaseOperations: DatabaseOperations = Dat .use { database -> getTableNames(database) .filterNot { excludeTablesRegex?.matches(it) ?: false } + .filterNot { it == ROOM_METADATA } .forEach { tableName -> deleteTableContent(database, tableName) } diff --git a/library/src/main/java/com/schibsted/spain/barista/rule/cleardata/internal/DatabaseOperations.kt b/library/src/main/java/com/schibsted/spain/barista/rule/cleardata/internal/DatabaseOperations.kt index 8b0923742..64d7ee005 100644 --- a/library/src/main/java/com/schibsted/spain/barista/rule/cleardata/internal/DatabaseOperations.kt +++ b/library/src/main/java/com/schibsted/spain/barista/rule/cleardata/internal/DatabaseOperations.kt @@ -1,14 +1,14 @@ package com.schibsted.spain.barista.rule.cleardata.internal import android.database.sqlite.SQLiteDatabase -import androidx.test.InstrumentationRegistry +import androidx.test.platform.app.InstrumentationRegistry import java.io.File import java.util.ArrayList class DatabaseOperations { fun getAllDatabaseFiles(): List { - return InstrumentationRegistry.getTargetContext() + return InstrumentationRegistry.getInstrumentation().targetContext .let { context -> context.databaseList() .map { context.getDatabasePath(it) } diff --git a/library/src/test/java/com/schibsted/spain/barista/cleardata/ClearDatabaseRuleTest.java b/library/src/test/java/com/schibsted/spain/barista/cleardata/ClearDatabaseRuleTest.java index fbc47f1e3..2c2df2906 100644 --- a/library/src/test/java/com/schibsted/spain/barista/cleardata/ClearDatabaseRuleTest.java +++ b/library/src/test/java/com/schibsted/spain/barista/cleardata/ClearDatabaseRuleTest.java @@ -117,6 +117,19 @@ public void doesNotDeleteTable_whenNameMatchesRegex() throws Throwable { verify(operations, atLeastOnce()).deleteTableContent(DB_1, "some_table"); } + @Test + public void doesNotDeleteRoomMetadata() throws Throwable { + String givenTableName = "some_table"; + given(operations.getAllDatabaseFiles()).willReturn(singletonList(DB_1_FILE)); + given(operations.getTableNames(DB_1)).willReturn(asList(givenTableName, ClearDatabaseRule.ROOM_METADATA)); + + ClearDatabaseRule rule = new ClearDatabaseRule(operations); + executeRule(rule); + + verify(operations, never()).deleteTableContent(DB_1, ClearDatabaseRule.ROOM_METADATA); + verify(operations, atLeastOnce()).deleteTableContent(DB_1, givenTableName); + } + private void executeRule(ClearDatabaseRule rule) throws Throwable { rule.apply(dummyStatement, dummyDescription).evaluate(); } From c7f16bc7016f3f1977657026a9926e8a8a62b5ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=20V=C3=A1zquez?= Date: Thu, 7 May 2020 09:32:08 +0200 Subject: [PATCH 18/41] Release 3.5.0 --- README.md | 2 +- build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 388fb97dd..86928f1d1 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Barista makes developing UI test faster, easier and more predictable. Built on t Import Barista as a testing dependency: ```gradle -androidTestImplementation('com.schibsted.spain:barista:3.4.0') { +androidTestImplementation('com.schibsted.spain:barista:3.5.0') { exclude group: 'org.jetbrains.kotlin' // Only if you already use Kotlin in your project } ``` diff --git a/build.gradle b/build.gradle index 2fe064dbe..570995c40 100644 --- a/build.gradle +++ b/build.gradle @@ -28,4 +28,4 @@ ext.compileSdkVersionDeclared = 29 ext.supportLibVersion = '27.1.1' ext.espressoVersion = '3.0.2' ext.uiAutomatorVersion = '2.1.3' -ext.baristaVersion = '3.4.0' +ext.baristaVersion = '3.5.0' From b88507a42d279b24bbf03c366712d1a9f32f9fd0 Mon Sep 17 00:00:00 2001 From: Cristian Garrido Date: Wed, 20 May 2020 10:45:02 +0200 Subject: [PATCH 19/41] Change the .kotlin_module generated name (#347) This avoid taking the name of the current folder to generate the kotlin_module generated into the .aar/META-INF. In practice, rename "META-INF/library_release.kotlin_module" to "META-INF/barista.kotlin_module". This avoid Barista consumers to exclude this file if another library generated the same one. See: https://stackoverflow.com/q/44509608 Co-authored-by: Cristian Garrido --- library/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/library/build.gradle b/library/build.gradle index 6015575fc..ce276e05e 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -15,6 +15,7 @@ android { lintOptions { disable 'InvalidPackage' } + kotlinOptions.freeCompilerArgs += ["-module-name", "barista"] } dependencies { From 68e3877854571b70497d573b9a73a9ee622d1f50 Mon Sep 17 00:00:00 2001 From: Roc Boronat Date: Mon, 25 May 2020 15:34:36 +0200 Subject: [PATCH 20/41] Move the assertions tricky feature to the end of the list (#348) Just to keep it coherent --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 86928f1d1..58f9ba1be 100644 --- a/README.md +++ b/README.md @@ -325,10 +325,6 @@ assertTextColorIsNot(R.id.customTextView, R.styleable.SampleCustomView, R.style. assertRecyclerViewItemCount(R.id.recycler, 10); ``` -#### And another tricky feature -```java -assertThatBackButtonClosesTheApp(); -``` #### Is this ImageView showing a drawable? ```java assertHasAnyDrawable(R.id.image_view); @@ -345,6 +341,11 @@ assertProgressIsMin(R.id.seek_bar) assertProgressIsMax(R.id.seek_bar) ``` +#### And another tricky feature +```java +assertThatBackButtonClosesTheApp(); +``` + ### Custom assertions If you have a special case not covered by the given assertions API, we encourage you to assert these special cases with our custom assertions API. It's a convenient way to replace plain `Matcher`s with complex assertions. With Barista, you can match any kind of view by knowing its type and passing its `viewId`, `text`, or a `Matcher`. Once you matched it, you will be able to assert all its properties without adding any complex `Matcher` to your project. From 962a70dff9dd5297013c63aa7e0ae1e76458783f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=20V=C3=A1zquez?= Date: Thu, 28 May 2020 11:57:15 +0200 Subject: [PATCH 21/41] Add Sponsor button linking to our "Thank you" post (#349) I just learned we can use custom links for the Sponsor button in GitHub! So we can link to our "Thank you Barista " issue. We don't want money, just "thank you" notes :) Here's more info about the button: https://help.github.com/en/github/administering-a-repository/displaying-a-sponsor-button-in-your-repository --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..e62e15f50 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +custom: "https://github.com/AdevintaSpain/Barista/issues/344" From 14c603e7c71a9e2d147129c592ed7cf5bdc5f39d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Brais=20Gab=C3=ADn?= Date: Thu, 28 May 2020 14:08:15 +0200 Subject: [PATCH 22/41] Remove Jetifier (#350) * Update mockito * Update glide * Remove Jetifier --- gradle.properties | 1 - library/build.gradle | 2 +- sample/build.gradle | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/gradle.properties b/gradle.properties index 7ac1b74f2..af89d3512 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,2 @@ -android.enableJetifier=true android.useAndroidX=true org.gradle.jvmargs=-Xmx1536m diff --git a/library/build.gradle b/library/build.gradle index ce276e05e..e10f82281 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -36,7 +36,7 @@ dependencies { testImplementation 'junit:junit:4.12' testImplementation 'pl.pragmatists:JUnitParams:1.1.0' testImplementation 'org.assertj:assertj-core:1.7.0' - testImplementation 'org.mockito:mockito-core:2.13.0' + testImplementation 'org.mockito:mockito-core:2.28.2' } publish { diff --git a/sample/build.gradle b/sample/build.gradle index 8f3978fc3..3d60fd2b5 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -32,14 +32,14 @@ dependencies { implementation 'androidx.recyclerview:recyclerview:1.0.0' implementation 'com.google.android.material:material:1.0.0' implementation 'androidx.annotation:annotation:1.0.2' - implementation 'com.github.bumptech.glide:glide:4.7.1' + implementation 'com.github.bumptech.glide:glide:4.10.0' implementation 'com.google.android.material:material:1.0.0' implementation 'androidx.core:core-ktx:1.0.1' androidTestImplementation project(':library') androidTestImplementation "org.assertj:assertj-core:2.9.1" androidTestImplementation "com.nhaarman:mockito-kotlin:1.5.0" - androidTestImplementation "org.mockito:mockito-android:2.16.0" + androidTestImplementation "org.mockito:mockito-android:2.28.2" testImplementation 'junit:junit:4.12' From 7005df6524e5adf459a3b2327bec576e2a6bf11d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernat=20Borr=C3=A1s=20Paronella?= Date: Thu, 28 May 2020 15:00:58 +0200 Subject: [PATCH 23/41] Fix color state list color check (#351) --- .../internal/matcher/TextColorMatcher.kt | 33 +++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/library/src/main/java/com/schibsted/spain/barista/internal/matcher/TextColorMatcher.kt b/library/src/main/java/com/schibsted/spain/barista/internal/matcher/TextColorMatcher.kt index 5409b054c..a5e2fb701 100644 --- a/library/src/main/java/com/schibsted/spain/barista/internal/matcher/TextColorMatcher.kt +++ b/library/src/main/java/com/schibsted/spain/barista/internal/matcher/TextColorMatcher.kt @@ -45,8 +45,15 @@ class TextColorMatcher(private val expectedColor: Int) : BoundedMatcher listOf(state, -state) }.map { state -> intArrayOf(state) } + + return allStates.all { state -> + val currentStateColor = currentColorList.getColorForState(state, currentColorList.defaultColor) + val expectedStateColor = expectedColorList.getColorForState(state, expectedColorList.defaultColor) + currentStateColor == expectedStateColor + } } override fun describeTo(description: Description) { @@ -62,4 +69,26 @@ class TextColorMatcher(private val expectedColor: Int) : BoundedMatcher Date: Thu, 28 May 2020 17:38:52 +0200 Subject: [PATCH 24/41] Update disabled states for color state list (#353) * disable states in a more understandable way * Extract and document negative state --- .../internal/matcher/TextColorMatcher.kt | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/library/src/main/java/com/schibsted/spain/barista/internal/matcher/TextColorMatcher.kt b/library/src/main/java/com/schibsted/spain/barista/internal/matcher/TextColorMatcher.kt index a5e2fb701..fe3627eb6 100644 --- a/library/src/main/java/com/schibsted/spain/barista/internal/matcher/TextColorMatcher.kt +++ b/library/src/main/java/com/schibsted/spain/barista/internal/matcher/TextColorMatcher.kt @@ -47,15 +47,19 @@ class TextColorMatcher(private val expectedColor: Int) : BoundedMatcher listOf(state, -state) }.map { state -> intArrayOf(state) } - - return allStates.all { state -> + return getAllColorStateListStates().all { state -> val currentStateColor = currentColorList.getColorForState(state, currentColorList.defaultColor) val expectedStateColor = expectedColorList.getColorForState(state, expectedColorList.defaultColor) currentStateColor == expectedStateColor } } + private fun getAllColorStateListStates(): List { + return ALL_COLOR_STATE_LIST_STATES + .flatMap { state -> listOf(state, createDisabledState(state)) } + .map { state -> intArrayOf(state) } + } + override fun describeTo(description: Description) { if (colorName != null) { val text = when (expectedColor.colorResourceType) { @@ -70,6 +74,13 @@ class TextColorMatcher(private val expectedColor: Int) : BoundedMatcherCheck documentation on StackOverflow + */ + private fun createDisabledState(state: Int) = -state + companion object { private val ALL_COLOR_STATE_LIST_STATES = listOf( android.R.attr.state_focused, From 9a9aa3c789217cfccdd8fe6d90408ba8b5a43cb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernat=20Borr=C3=A1s=20Paronella?= Date: Tue, 2 Jun 2020 11:03:57 +0200 Subject: [PATCH 25/41] Entry point on github actions (#334) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Entry point on github actions * Try adding tests * Tr cache * Add flaky * Add flaky * Update main.yml * Update main.yml * Update emulator runner * Use Flaky instead of ignore * Add emulators matrix * Add profile * Use only one emulator Co-authored-by: Bernat Borrás --- .github/workflows/main.yml | 31 +++++++++++++++++++ .../spain/barista/sample/MenuClickTest.java | 4 +++ 2 files changed, 35 insertions(+) create mode 100644 .github/workflows/main.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 000000000..dae60308f --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,31 @@ +name: CI + +on: + pull_request: + branches: + - master + push: + branches: + - master + +jobs: + tests: + runs-on: macOS-latest + + steps: + - uses: actions/checkout@v1 + name: Checkout + - uses: reactivecircus/android-emulator-runner@v2 + name: Run tests + with: + api-level: 29 + profile: Nexus 6 + headless: true + disable-animations: true + script: ./gradlew connectedCheck + + - uses: actions/cache@v1 + with: + path: ~/.gradle/caches + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties') }} + restore-keys: ${{ runner.os }}-gradle- diff --git a/sample/src/androidTest/java/com/schibsted/spain/barista/sample/MenuClickTest.java b/sample/src/androidTest/java/com/schibsted/spain/barista/sample/MenuClickTest.java index 351b97ce9..d13c0652c 100644 --- a/sample/src/androidTest/java/com/schibsted/spain/barista/sample/MenuClickTest.java +++ b/sample/src/androidTest/java/com/schibsted/spain/barista/sample/MenuClickTest.java @@ -1,5 +1,6 @@ package com.schibsted.spain.barista.sample; +import androidx.test.filters.FlakyTest; import androidx.test.rule.ActivityTestRule; import androidx.test.runner.AndroidJUnit4; import com.schibsted.spain.barista.internal.failurehandler.BaristaException; @@ -46,18 +47,21 @@ public void iconMenuClick_byTitle() { } @Test + @FlakyTest(detail = "This tests passes on local, but fails on CI") public void overflowMenuClick_byId() { clickMenu(R.id.menu_action_3); assertDisplayed("Third menu option"); } @Test + @FlakyTest(detail = "This tests passes on local, but fails on CI") public void overflowMenuClick_byTitle() { clickMenu("Menu 3"); assertDisplayed("Third menu option"); } @Test + @FlakyTest(detail = "This tests passes on local, but fails on CI") public void openOverflowMenu_withoutClickingAnyOption() { openMenu(); assertDisplayed("Menu 3"); From 6411a87807155348ba342e3d5688e2e00682e8ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=20V=C3=A1zquez?= Date: Tue, 4 Aug 2020 13:54:52 +0200 Subject: [PATCH 26/41] Simplify PermissionGranterTest (#360) * Simplify PermissionGranterTest * Extract ActivityScenario * Ignore failing test until fix is merged --- .../barista/sample/PermissionGranterTest.kt | 101 ++++++++++++------ sample/src/main/AndroidManifest.xml | 4 +- .../spain/barista/sample/EmptyActivity.kt | 5 + .../sample/RuntimePermissionActivity.java | 47 -------- .../layout/activity_runtime_permission.xml | 26 ----- 5 files changed, 76 insertions(+), 107 deletions(-) create mode 100644 sample/src/main/java/com/schibsted/spain/barista/sample/EmptyActivity.kt delete mode 100644 sample/src/main/java/com/schibsted/spain/barista/sample/RuntimePermissionActivity.java delete mode 100644 sample/src/main/res/layout/activity_runtime_permission.xml diff --git a/sample/src/androidTest/java/com/schibsted/spain/barista/sample/PermissionGranterTest.kt b/sample/src/androidTest/java/com/schibsted/spain/barista/sample/PermissionGranterTest.kt index 6c8f983a3..cd09dff0e 100644 --- a/sample/src/androidTest/java/com/schibsted/spain/barista/sample/PermissionGranterTest.kt +++ b/sample/src/androidTest/java/com/schibsted/spain/barista/sample/PermissionGranterTest.kt @@ -1,60 +1,95 @@ package com.schibsted.spain.barista.sample import android.Manifest -import android.content.Context import android.content.pm.PackageManager import android.os.Build -import androidx.annotation.RequiresApi -import androidx.test.core.app.ApplicationProvider.getApplicationContext -import com.schibsted.spain.barista.interaction.BaristaClickInteractions.clickOn +import androidx.core.app.ActivityCompat +import androidx.core.content.ContextCompat +import androidx.test.core.app.ActivityScenario import com.schibsted.spain.barista.interaction.PermissionGranter -import com.schibsted.spain.barista.internal.failurehandler.BaristaException -import com.schibsted.spain.barista.rule.BaristaRule -import org.junit.Assume.assumeFalse +import org.junit.Assert.assertEquals import org.junit.Assume.assumeTrue import org.junit.Before -import org.junit.Rule +import org.junit.Ignore import org.junit.Test -import org.junit.rules.ExpectedException class PermissionGranterTest { - @get:Rule - var thrown = ExpectedException.none() - - @get:Rule - var activityRule = BaristaRule.create(RuntimePermissionActivity::class.java) - @Before - @Throws(Exception::class) fun setUp() { - assumeTrue("This test needs to run on a device with Android 23 or above", - Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) - assumeFalse("This test expects you to not have the permission granted. Remember to clear data.", - hasNeededPermission(getApplicationContext(), RuntimePermissionActivity.SOME_PERMISSION)) + assumeTrue("This test needs to run on a device with Android 23 or above", Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) + } + + @Test + fun grants_simple_permission() { + launchActivity { + verifyPermissionNotGranted(SIMPLE_PERMISSION_1) + requestPermission(SIMPLE_PERMISSION_1) + + PermissionGranter.allowPermissionsIfNeeded(SIMPLE_PERMISSION_1) + + verifyPermissionGranted(SIMPLE_PERMISSION_1) + } } - @Test(expected = BaristaException::class) - fun fails_when_using_permission() { - activityRule.launchActivity() + /** + * Since Android Q the Location permission dialog is different to the others + */ + @Test + @Ignore("Won't pass without the fix in https://github.com/AdevintaSpain/Barista/pull/356") + fun grants_location_permission() { + launchActivity { + verifyPermissionNotGranted(LOCATION_PERMISSION) + requestPermission(LOCATION_PERMISSION) - clickOn(R.id.use_permission_button) + PermissionGranter.allowPermissionsIfNeeded(LOCATION_PERMISSION) + + verifyPermissionGranted(LOCATION_PERMISSION) + } } @Test - @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) - fun works_after_granting_permission() { - activityRule.launchActivity() + fun ignores_already_granted_permission() { + launchActivity { + verifyPermissionNotGranted(SIMPLE_PERMISSION_2) + requestPermission(SIMPLE_PERMISSION_2) + PermissionGranter.allowPermissionsIfNeeded(SIMPLE_PERMISSION_2) + verifyPermissionGranted(SIMPLE_PERMISSION_2) + + PermissionGranter.allowPermissionsIfNeeded(SIMPLE_PERMISSION_2) + } + } + + +} + +// We can't reuse permission from one test to another, because they stay granted after each test +private const val SIMPLE_PERMISSION_1 = Manifest.permission.READ_CONTACTS +private const val SIMPLE_PERMISSION_2 = Manifest.permission.CAMERA +private const val LOCATION_PERMISSION = Manifest.permission.ACCESS_FINE_LOCATION - clickOn(R.id.request_permission_button) +private fun ActivityScenario<*>.requestPermission(permission: String) { + onActivity { activity -> + ActivityCompat.requestPermissions(activity, arrayOf(permission), 1) + } +} - PermissionGranter.allowPermissionsIfNeeded(Manifest.permission.READ_CONTACTS) +private fun ActivityScenario<*>.verifyPermissionNotGranted(permission: String) { + onActivity { activity -> + val permissionValue = ContextCompat.checkSelfPermission(activity, permission) + assertEquals("Permission $permission expected to be denied but was granted;", PackageManager.PERMISSION_DENIED, permissionValue) + } +} - clickOn(R.id.use_permission_button) +private fun ActivityScenario<*>.verifyPermissionGranted(permission: String) { + onActivity { activity -> + val permissionValue = ContextCompat.checkSelfPermission(activity, permission) + assertEquals("Expected permission $permission was not granted;", PackageManager.PERMISSION_GRANTED, permissionValue) } +} - private fun hasNeededPermission(context: Context, permissionNeeded: String): Boolean { - val permissionStatus = context.checkPermission(permissionNeeded, android.os.Process.myPid(), android.os.Process.myUid()) - return permissionStatus == PackageManager.PERMISSION_GRANTED +private fun launchActivity(block: ActivityScenario<*>.() -> Unit) { + ActivityScenario.launch(EmptyActivity::class.java).use { scenario -> + scenario.block() } } diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml index 6ab5e2368..7da368cc6 100644 --- a/sample/src/main/AndroidManifest.xml +++ b/sample/src/main/AndroidManifest.xml @@ -4,6 +4,8 @@ > + + - + - - -