1+ package com.hoc.flowmvi
2+
3+ import androidx.test.ext.junit.rules.ActivityScenarioRule
4+ import androidx.test.ext.junit.runners.AndroidJUnit4
5+ import androidx.test.filters.LargeTest
6+ import androidx.test.espresso.Espresso.onView
7+ import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
8+ import androidx.test.espresso.Espresso.pressBack
9+ import androidx.test.espresso.action.ViewActions.*
10+ import androidx.test.espresso.assertion.ViewAssertions.*
11+ import androidx.test.espresso.intent.Intents
12+ import androidx.test.espresso.matcher.ViewMatchers.*
13+ import androidx.test.platform.app.InstrumentationRegistry
14+ import com.hoc.flowmvi.ui.main.MainActivity
15+ import org.junit.After
16+ import org.junit.Before
17+ import org.junit.Rule
18+ import org.junit.Test
19+ import org.junit.runner.RunWith
20+
21+ /* *
22+ * Integration tests for end-to-end user flows.
23+ *
24+ * Tests complete user journeys:
25+ * - Navigate to Add, fill form, return to main
26+ * - Navigate to Search, perform search, return to main
27+ * - Multiple navigation flows
28+ */
29+ @RunWith(AndroidJUnit4 ::class )
30+ @LargeTest
31+ class IntegrationUITest {
32+
33+ @get:Rule
34+ val activityRule = ActivityScenarioRule (MainActivity ::class .java)
35+
36+ @Before
37+ fun setUp () {
38+ Intents .init ()
39+ }
40+
41+ @After
42+ fun tearDown () {
43+ Intents .release()
44+ }
45+
46+ @Test
47+ fun endToEndFlow_addUser () {
48+ // Start from MainActivity
49+ onView(withId(com.hoc.flowmvi.ui.main.R .id.usersRecycler))
50+ .check(matches(isDisplayed()))
51+
52+ // Navigate to Add activity
53+ openActionBarOverflowOrOptionsMenu(InstrumentationRegistry .getInstrumentation().targetContext)
54+ onView(withText(" Add" ))
55+ .perform(click())
56+
57+ // Fill the form in AddActivity
58+ onView(withId(com.hoc.flowmvi.ui.add.R .id.emailEditText))
59+ .perform(click())
60+ .perform(typeText(
" [email protected] " ))
61+
62+ onView(withId(com.hoc.flowmvi.ui.add.R .id.firstNameEditText))
63+ .perform(click())
64+ .perform(typeText(" Integration" ))
65+
66+ onView(withId(com.hoc.flowmvi.ui.add.R .id.lastNameEditText))
67+ .perform(click())
68+ .perform(typeText(" Test" ))
69+ .perform(closeSoftKeyboard())
70+
71+ // Note: We don't submit the form as it would require network/backend setup
72+ // Instead we just verify the form can be filled and navigate back
73+
74+ // Navigate back to MainActivity
75+ pressBack()
76+
77+ // Verify we're back at MainActivity
78+ onView(withId(com.hoc.flowmvi.ui.main.R .id.usersRecycler))
79+ .check(matches(isDisplayed()))
80+ }
81+
82+ @Test
83+ fun endToEndFlow_searchUser () {
84+ // Start from MainActivity
85+ onView(withId(com.hoc.flowmvi.ui.main.R .id.usersRecycler))
86+ .check(matches(isDisplayed()))
87+
88+ // Navigate to Search activity
89+ openActionBarOverflowOrOptionsMenu(InstrumentationRegistry .getInstrumentation().targetContext)
90+ onView(withText(" Search" ))
91+ .perform(click())
92+
93+ // Perform a search
94+ onView(withId(androidx.appcompat.R .id.search_src_text))
95+ .perform(click())
96+ .perform(typeText(" test query" ))
97+ .perform(pressImeActionButton())
98+
99+ // Verify search results area is displayed
100+ onView(withId(com.hoc.flowmvi.ui.search.R .id.usersRecycler))
101+ .check(matches(isDisplayed()))
102+
103+ // Navigate back to MainActivity
104+ pressBack()
105+
106+ // Verify we're back at MainActivity
107+ onView(withId(com.hoc.flowmvi.ui.main.R .id.usersRecycler))
108+ .check(matches(isDisplayed()))
109+ }
110+
111+ @Test
112+ fun multipleNavigation_addThenSearch () {
113+ // Navigate to Add
114+ openActionBarOverflowOrOptionsMenu(InstrumentationRegistry .getInstrumentation().targetContext)
115+ onView(withText(" Add" ))
116+ .perform(click())
117+
118+ // Verify we're in AddActivity
119+ onView(withId(com.hoc.flowmvi.ui.add.R .id.addButton))
120+ .check(matches(isDisplayed()))
121+
122+ // Go back
123+ pressBack()
124+
125+ // Navigate to Search
126+ openActionBarOverflowOrOptionsMenu(InstrumentationRegistry .getInstrumentation().targetContext)
127+ onView(withText(" Search" ))
128+ .perform(click())
129+
130+ // Verify we're in SearchActivity
131+ onView(withId(androidx.appcompat.R .id.search_src_text))
132+ .check(matches(isDisplayed()))
133+
134+ // Go back to main
135+ pressBack()
136+
137+ // Verify we're back at MainActivity
138+ onView(withId(com.hoc.flowmvi.ui.main.R .id.usersRecycler))
139+ .check(matches(isDisplayed()))
140+ }
141+
142+ @Test
143+ fun mainActivity_swipeRefresh_integration () {
144+ // Verify initial state
145+ onView(withId(com.hoc.flowmvi.ui.main.R .id.swipeRefreshLayout))
146+ .check(matches(isDisplayed()))
147+
148+ // Perform swipe to refresh
149+ onView(withId(com.hoc.flowmvi.ui.main.R .id.swipeRefreshLayout))
150+ .perform(swipeDown())
151+
152+ // Verify the layout is still functional after refresh
153+ onView(withId(com.hoc.flowmvi.ui.main.R .id.usersRecycler))
154+ .check(matches(isDisplayed()))
155+
156+ // Verify menu is still accessible after refresh
157+ openActionBarOverflowOrOptionsMenu(InstrumentationRegistry .getInstrumentation().targetContext)
158+ onView(withText(" Add" ))
159+ .check(matches(isDisplayed()))
160+
161+ // Close menu by pressing back
162+ pressBack()
163+ }
164+ }
0 commit comments