Skip to content

Commit 5005a07

Browse files
a7medevHeshamMegid
authored andcommitted
[MOB-10730] Add More Android E2E Tests (#311)
* Refactor example app with reusable widgits * Setup Espresso for Flutter * UI test Android primary color changes * UI test Android invocation events * UI test Android repro steps * UI test Android manual surveys * UI test Android bug reporting onDismiss callback * Improve Android UI test stability * UI test Android report types * UI test Android floating button edge * Fix Android repro steps UI test * Improve report types Android UI test stability * UI test Android feature requests * Wait for options prompt in Android UI tests * Wait for elements in Android UI tests * Unify Espresso matchers usage in UI tests * Improve code quality
1 parent aa76587 commit 5005a07

16 files changed

+606
-242
lines changed

.circleci/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ jobs:
7878
additional-avd-args: -d "Nexus 5"
7979
post-emulator-launch-assemble-command: cd example && flutter build apk
8080
run-tests-working-directory: example/android
81-
test-command: ./gradlew app:connectedAndroidTest
81+
test-command: ./gradlew app:connectedAndroidTest -Ptarget=`pwd`/../test_driver/example.dart
8282
- android/run-tests:
8383
working-directory: example/android
8484
test-command: ./gradlew test

example/android/app/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ dependencies {
7878
testImplementation 'junit:junit:4.12'
7979
implementation 'com.android.support:multidex:1.0.3'
8080
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
81-
androidTestImplementation 'androidx.test:rules:1.1.1'
81+
androidTestImplementation 'androidx.test:rules:1.1.0'
8282
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0'
8383
androidTestImplementation 'com.android.support.test.uiautomator:uiautomator:2.1.3'
8484
testImplementation 'org.mockito:mockito-core:1.10.19'
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
package com.example.InstabugSample;
2+
3+
import androidx.test.espresso.action.ViewActions;
4+
import androidx.test.espresso.flutter.action.FlutterActions;
5+
import androidx.test.espresso.flutter.assertion.FlutterAssertions;
6+
import androidx.test.espresso.flutter.matcher.FlutterMatchers;
7+
import androidx.test.espresso.matcher.ViewMatchers;
8+
import androidx.test.rule.ActivityTestRule;
9+
import androidx.test.ext.junit.runners.AndroidJUnit4;
10+
import androidx.test.uiautomator.UiDevice;
11+
import androidx.test.uiautomator.UiObject;
12+
import androidx.test.uiautomator.UiObjectNotFoundException;
13+
import androidx.test.uiautomator.UiSelector;
14+
15+
import org.junit.Before;
16+
import org.junit.Rule;
17+
import org.junit.Test;
18+
import org.junit.runner.RunWith;
19+
20+
import static androidx.test.espresso.Espresso.onView;
21+
import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
22+
import static androidx.test.espresso.assertion.ViewAssertions.matches;
23+
import static androidx.test.espresso.flutter.EspressoFlutter.onFlutterWidget;
24+
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
25+
26+
import static com.example.InstabugSample.util.InstabugAssertions.assertViewWillBeVisible;
27+
import static com.example.InstabugSample.util.InstabugViewMatchers.isToTheLeft;
28+
29+
import static org.junit.Assert.assertTrue;
30+
31+
import android.app.Instrumentation;
32+
import android.graphics.Point;
33+
34+
import com.example.InstabugSample.util.Keyboard;
35+
36+
@RunWith(AndroidJUnit4.class)
37+
public class BugReportingUITest {
38+
Instrumentation instrumentation = getInstrumentation();
39+
UiDevice device = UiDevice.getInstance(instrumentation);
40+
41+
@Rule
42+
public ActivityTestRule<MainActivity> mActivityRule =
43+
new ActivityTestRule<>(MainActivity.class);
44+
45+
@Before
46+
public void setUp() {
47+
onFlutterWidget(FlutterMatchers.withText("Restart Instabug"))
48+
.perform(FlutterActions.click());
49+
}
50+
51+
private void assertOptionsPromptIsVisible() {
52+
assertViewWillBeVisible("instabug_main_prompt_container", 2000);
53+
}
54+
55+
@Test
56+
public void floatingButtonInvocationEvent() {
57+
onFlutterWidget(FlutterMatchers.withText("Floating Button")).perform(FlutterActions.click());
58+
onView(ViewMatchers.withResourceName("instabug_floating_button")).perform(ViewActions.click());
59+
60+
assertOptionsPromptIsVisible();
61+
}
62+
63+
@Test
64+
public void twoFingersSwipeLeftInvocationEvent() throws InterruptedException {
65+
onFlutterWidget(FlutterMatchers.withText("Two Fingers Swipe Left"))
66+
.perform(FlutterActions.click());
67+
68+
// Two-fingers swipe left
69+
UiObject text = device.findObject(new UiSelector().textContains("Hello"));
70+
int width = device.getDisplayWidth();
71+
text.performTwoPointerGesture(
72+
new Point(width - 50, 100),
73+
new Point(width - 50, 130),
74+
new Point(50, 100),
75+
new Point(50, 130),
76+
// Small steps number for fast swiping
77+
20
78+
);
79+
80+
Thread.sleep(1000);
81+
82+
assertOptionsPromptIsVisible();
83+
}
84+
85+
@Test
86+
public void noneInvocationEvent() {
87+
onFlutterWidget(FlutterMatchers.withText("None")).perform(FlutterActions.click());
88+
89+
onView(ViewMatchers.withResourceName("instabug_floating_button")).check(doesNotExist());
90+
}
91+
92+
@Test
93+
public void manualInvocation() {
94+
onFlutterWidget(FlutterMatchers.withText("Invoke")).perform(FlutterActions.click());
95+
96+
assertOptionsPromptIsVisible();
97+
}
98+
99+
@Test
100+
public void multipleScreenshotsInReproSteps() throws InterruptedException, UiObjectNotFoundException {
101+
String screen = "My Screen";
102+
103+
onFlutterWidget(FlutterMatchers.withText("Enter screen name"))
104+
.perform(FlutterActions.scrollTo(), FlutterActions.typeText(screen));
105+
Thread.sleep(1000);
106+
Keyboard.closeKeyboard();
107+
onFlutterWidget(FlutterMatchers.withText("Report Screen Change"))
108+
.perform(FlutterActions.scrollTo(), FlutterActions.click(), FlutterActions.click());
109+
onFlutterWidget(FlutterMatchers.withText("Send Bug Report"))
110+
.perform(FlutterActions.scrollTo(), FlutterActions.click());
111+
UiObject reproStepsDisclaimer = assertViewWillBeVisible("instabug_text_view_repro_steps_disclaimer", 2000);
112+
reproStepsDisclaimer.click();
113+
114+
String screenshotsListId = "instabug_vus_list";
115+
assertViewWillBeVisible(screenshotsListId, 5000);
116+
onView(ViewMatchers.withResourceName(screenshotsListId))
117+
.check(matches(ViewMatchers.hasChildCount(2)));
118+
}
119+
120+
@Test
121+
public void onDismissCallbackIsCalled() throws InterruptedException {
122+
onFlutterWidget(FlutterMatchers.withText("Set On Dismiss Callback"))
123+
.perform(FlutterActions.scrollTo(), FlutterActions.click());
124+
125+
onFlutterWidget(FlutterMatchers.withText("Invoke"))
126+
.perform(FlutterActions.scrollTo(), FlutterActions.click());
127+
Thread.sleep(1000);
128+
device.pressBack();
129+
130+
Thread.sleep(1000);
131+
132+
onFlutterWidget(FlutterMatchers.withText("onDismiss callback called with DismissType.cancel and ReportType.other"))
133+
.check(FlutterAssertions.matches(FlutterMatchers.isExisting()));
134+
}
135+
136+
@Test
137+
public void changeReportTypes() throws UiObjectNotFoundException {
138+
onFlutterWidget(FlutterMatchers.withText("Bug"))
139+
.perform(FlutterActions.scrollTo(), FlutterActions.click());
140+
onFlutterWidget(FlutterMatchers.withText("Invoke"))
141+
.perform(FlutterActions.scrollTo(), FlutterActions.click());
142+
143+
// Shows bug reporting screen
144+
assertViewWillBeVisible("instabug_edit_text_message", 2000);
145+
146+
// Close bug reporting screen
147+
device.pressBack();
148+
device.findObject(new UiSelector().text("DISCARD")).click();
149+
150+
// Enable feedback reports
151+
onFlutterWidget(FlutterMatchers.withText("Feedback"))
152+
.perform(FlutterActions.scrollTo(), FlutterActions.click());
153+
onFlutterWidget(FlutterMatchers.withText("Invoke"))
154+
.perform(FlutterActions.scrollTo(), FlutterActions.click());
155+
156+
// Shows both bug reporting and feature requests in prompt options
157+
assertOptionsPromptIsVisible();
158+
onView(ViewMatchers.withText("Report a bug"))
159+
.check(matches(ViewMatchers.isDisplayed()));
160+
onView(ViewMatchers.withText("Suggest an improvement"))
161+
.check(matches(ViewMatchers.isDisplayed()));
162+
onView(ViewMatchers.withText("Ask a question"))
163+
.check(doesNotExist());
164+
}
165+
166+
@Test
167+
public void changeFloatingButtonEdge() {
168+
onFlutterWidget(FlutterMatchers.withText("Floating Button"))
169+
.perform(FlutterActions.scrollTo(), FlutterActions.click());
170+
onFlutterWidget(FlutterMatchers.withText("Move Floating Button to Left"))
171+
.perform(FlutterActions.scrollTo(), FlutterActions.click());
172+
onView(ViewMatchers.withResourceName("instabug_floating_button"))
173+
.check(matches(isToTheLeft()));
174+
}
175+
}
176+
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.example.InstabugSample;
2+
3+
import static androidx.test.espresso.Espresso.onView;
4+
import static androidx.test.espresso.assertion.ViewAssertions.matches;
5+
import static androidx.test.espresso.flutter.EspressoFlutter.onFlutterWidget;
6+
7+
import androidx.test.espresso.flutter.action.FlutterActions;
8+
import androidx.test.espresso.flutter.matcher.FlutterMatchers;
9+
import androidx.test.espresso.matcher.ViewMatchers;
10+
import androidx.test.ext.junit.runners.AndroidJUnit4;
11+
import androidx.test.rule.ActivityTestRule;
12+
13+
import org.junit.Rule;
14+
import org.junit.Test;
15+
import org.junit.runner.RunWith;
16+
17+
18+
@RunWith(AndroidJUnit4.class)
19+
public class FeatureRequestsUITest {
20+
@Rule
21+
public ActivityTestRule<MainActivity> mActivityRule =
22+
new ActivityTestRule<>(MainActivity.class);
23+
24+
@Test
25+
public void showFeatureRequestsScreen() {
26+
onFlutterWidget(FlutterMatchers.withText("Show Feature Requests"))
27+
.perform(FlutterActions.scrollTo(), FlutterActions.click());
28+
29+
onView(ViewMatchers.withText("Feature Requests"))
30+
.check(matches(ViewMatchers.isDisplayed()));
31+
}
32+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package com.example.InstabugSample;
2+
3+
import static androidx.test.espresso.Espresso.onView;
4+
import static androidx.test.espresso.assertion.ViewAssertions.matches;
5+
import static androidx.test.espresso.flutter.EspressoFlutter.onFlutterWidget;
6+
7+
import static com.example.InstabugSample.util.InstabugViewMatchers.hasBackgroundColor;
8+
9+
import android.graphics.Color;
10+
11+
import androidx.test.espresso.flutter.action.FlutterActions;
12+
import androidx.test.espresso.flutter.matcher.FlutterMatchers;
13+
import androidx.test.espresso.matcher.ViewMatchers;
14+
import androidx.test.ext.junit.runners.AndroidJUnit4;
15+
import androidx.test.rule.ActivityTestRule;
16+
17+
import com.example.InstabugSample.util.Keyboard;
18+
19+
import org.junit.Rule;
20+
import org.junit.Test;
21+
import org.junit.runner.RunWith;
22+
23+
24+
@RunWith(AndroidJUnit4.class)
25+
public class InstabugUITest {
26+
@Rule
27+
public ActivityTestRule<MainActivity> mActivityRule =
28+
new ActivityTestRule<>(MainActivity.class);
29+
30+
@Test
31+
public void changePrimaryColor() {
32+
String color = "#FF0000";
33+
Color expected = Color.valueOf(0xFFFF0000);
34+
35+
onFlutterWidget(FlutterMatchers.withText("Enter primary color"))
36+
.perform(FlutterActions.typeText(color));
37+
Keyboard.closeKeyboard();
38+
onFlutterWidget(FlutterMatchers.withText("Change Primary Color"))
39+
.perform(FlutterActions.scrollTo(), FlutterActions.click());
40+
onFlutterWidget(FlutterMatchers.withText("Floating Button"))
41+
.perform(FlutterActions.scrollTo(), FlutterActions.click());
42+
43+
onView(ViewMatchers.withResourceName("instabug_floating_button"))
44+
.check(matches(hasBackgroundColor(expected)));
45+
}
46+
}

example/android/app/src/androidTest/java/com/example/InstabugSample/InvokeInstabugUITest.java

Lines changed: 0 additions & 52 deletions
This file was deleted.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.example.InstabugSample;
2+
3+
import static androidx.test.espresso.flutter.EspressoFlutter.onFlutterWidget;
4+
5+
import static com.example.InstabugSample.util.InstabugAssertions.assertViewWillBeVisible;
6+
7+
import androidx.test.espresso.flutter.action.FlutterActions;
8+
import androidx.test.espresso.flutter.matcher.FlutterMatchers;
9+
import androidx.test.ext.junit.runners.AndroidJUnit4;
10+
import androidx.test.rule.ActivityTestRule;
11+
12+
import org.junit.Rule;
13+
import org.junit.Test;
14+
import org.junit.runner.RunWith;
15+
16+
17+
@RunWith(AndroidJUnit4.class)
18+
public class SurveysUITest {
19+
@Rule
20+
public ActivityTestRule<MainActivity> mActivityRule =
21+
new ActivityTestRule<>(MainActivity.class);
22+
23+
@Test
24+
public void showManualSurvey() {
25+
onFlutterWidget(FlutterMatchers.withText("Show Manual Survey"))
26+
.perform(FlutterActions.scrollTo(), FlutterActions.click());
27+
28+
assertViewWillBeVisible("instabug_survey_dialog_container", 2000);
29+
}
30+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.example.InstabugSample.util;
2+
3+
import android.graphics.Bitmap;
4+
import android.graphics.Canvas;
5+
import android.graphics.Color;
6+
import android.view.View;
7+
8+
import org.hamcrest.Description;
9+
import org.hamcrest.TypeSafeMatcher;
10+
11+
public final class HasBackgroundColorMatcher extends TypeSafeMatcher<View> {
12+
13+
private final Color color;
14+
15+
public HasBackgroundColorMatcher(Color color) {
16+
this.color = color;
17+
}
18+
19+
@Override
20+
protected boolean matchesSafely(View view) {
21+
int width = view.getWidth();
22+
int height = view.getHeight();
23+
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
24+
Canvas canvas = new Canvas(bitmap);
25+
view.getBackground().draw(canvas);
26+
return bitmap.getColor(width / 2, height / 2).equals(color);
27+
}
28+
29+
@Override
30+
public void describeTo(Description description) {
31+
description.appendText("has background with color: " + color);
32+
}
33+
}

0 commit comments

Comments
 (0)