Skip to content

Commit cef8389

Browse files
committed
Add ConnectScreen tests
1 parent 65d3f11 commit cef8389

File tree

2 files changed

+190
-2
lines changed

2 files changed

+190
-2
lines changed
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
package com.pcapplusplus.toyvpn
2+
3+
import androidx.compose.material3.Text
4+
import androidx.compose.ui.test.*
5+
import androidx.compose.ui.test.junit4.createComposeRule
6+
import androidx.lifecycle.MutableLiveData
7+
import androidx.navigation.compose.NavHost
8+
import androidx.navigation.compose.composable
9+
import androidx.navigation.compose.rememberNavController
10+
import com.pcapplusplus.toyvpn.model.VpnConnectionState
11+
import com.pcapplusplus.toyvpn.ui.theme.ToyVpnPcapPlusPlusTheme
12+
import io.mockk.*
13+
import org.junit.Before
14+
import org.junit.Rule
15+
import org.junit.Test
16+
import org.junit.experimental.runners.Enclosed
17+
import org.junit.runner.RunWith
18+
import org.junit.runners.Parameterized
19+
20+
open class BaseTest {
21+
@get:Rule
22+
val composeTestRule = createComposeRule()
23+
24+
protected lateinit var mockViewModel: ToyVpnViewModel
25+
26+
@Before
27+
fun setUp() {
28+
mockViewModel = mockk(relaxed = true)
29+
30+
composeTestRule.setContent {
31+
ToyVpnPcapPlusPlusTheme {
32+
val navController = rememberNavController()
33+
34+
NavHost(
35+
navController = navController,
36+
startDestination = "connect_screen",
37+
) {
38+
composable("connect_screen") {
39+
ConnectScreen(
40+
navController,
41+
mockViewModel,
42+
)
43+
}
44+
composable("stats_screen") {
45+
Text("Stats Screen")
46+
}
47+
}
48+
}
49+
}
50+
51+
composeTestRule.onNodeWithText("Server Address").performTextClearance()
52+
composeTestRule.onNodeWithText("Server Port").performTextClearance()
53+
composeTestRule.onNodeWithText("Secret").performTextClearance()
54+
}
55+
}
56+
57+
@RunWith(Enclosed::class)
58+
class ConnectScreenTest {
59+
@RunWith(Parameterized::class)
60+
class ServerAddressValidationTest(private val inputValue: String, private val isValid: Boolean) : BaseTest() {
61+
companion object {
62+
@JvmStatic
63+
@Parameterized.Parameters(name = "{index}: testServerAddressValidation({0}) = {1}")
64+
fun serverAddressData(): Collection<Array<Any>> {
65+
return listOf(
66+
arrayOf("178.27.12.113", true),
67+
arrayOf("invalid_address", false),
68+
arrayOf("256.256.256.256", false),
69+
arrayOf("192.168.0.1", true),
70+
arrayOf("10.0.0.1", true),
71+
arrayOf("0.0.0.0", false),
72+
arrayOf("10.0.0.abc", false),
73+
arrayOf("1.127", false),
74+
arrayOf("10.-1.0.4", false),
75+
)
76+
}
77+
}
78+
79+
@Test
80+
fun testServerAddressValidation() {
81+
composeTestRule.onNodeWithText("Server Address")
82+
.performTextInput(inputValue)
83+
84+
composeTestRule.onNodeWithText("Connect").performClick()
85+
86+
if (isValid) {
87+
composeTestRule.onNodeWithText("Invalid server address. Please enter a valid IPv4 address.")
88+
.assertDoesNotExist()
89+
} else {
90+
composeTestRule.onNodeWithText("Invalid server address. Please enter a valid IPv4 address.")
91+
.assertIsDisplayed()
92+
}
93+
}
94+
}
95+
96+
@RunWith(Parameterized::class)
97+
class ServerPortValidationTest(private val inputValue: String, private val isValid: Boolean) : BaseTest() {
98+
companion object {
99+
@JvmStatic
100+
@Parameterized.Parameters(name = "{index}: testServerPortValidation({0}) = {1}")
101+
fun serverPortData(): Collection<Array<Any>> {
102+
return listOf(
103+
arrayOf("8080", true),
104+
arrayOf("70000", false),
105+
arrayOf("-1", false),
106+
arrayOf("65535", true),
107+
arrayOf("0", false),
108+
arrayOf("invalid", false),
109+
)
110+
}
111+
}
112+
113+
@Test
114+
fun testServerPortValidation() {
115+
composeTestRule.onNodeWithText("Server Port")
116+
.performTextInput(inputValue)
117+
118+
composeTestRule.onNodeWithText("Connect").performClick()
119+
120+
if (isValid) {
121+
composeTestRule.onNodeWithText("Invalid port number. Must be between 0 and 65535.")
122+
.assertDoesNotExist()
123+
} else {
124+
composeTestRule.onNodeWithText("Invalid port number. Must be between 0 and 65535.")
125+
.assertIsDisplayed()
126+
}
127+
}
128+
}
129+
130+
class OtherTests : BaseTest() {
131+
@Test
132+
fun testElementsAreDisplayed() {
133+
composeTestRule.onNodeWithContentDescription("Logo").assertIsDisplayed()
134+
135+
composeTestRule.onNodeWithText("Server Address").assertIsDisplayed()
136+
composeTestRule.onNodeWithText("Server Port").assertIsDisplayed()
137+
composeTestRule.onNodeWithText("Secret").assertIsDisplayed()
138+
composeTestRule.onNodeWithText("PcapPlusPlus Toy VPN").assertIsDisplayed()
139+
140+
composeTestRule.onNodeWithText("Connect").assertIsDisplayed().assertIsEnabled()
141+
}
142+
143+
@Test
144+
fun testSecretValidation() {
145+
composeTestRule.onNodeWithText("Connect").performClick()
146+
147+
composeTestRule.onNodeWithText("Secret cannot be empty").assertIsDisplayed()
148+
}
149+
150+
@Test
151+
fun testConnectButtonClick() {
152+
composeTestRule.onNodeWithText("Server Address").performTextInput("192.168.1.1")
153+
composeTestRule.onNodeWithText("Server Port").performTextInput("8080")
154+
composeTestRule.onNodeWithText("Secret").performTextInput("validSecret")
155+
156+
composeTestRule.onNodeWithText("Connect").performClick()
157+
158+
verify { mockViewModel.connectVpn("192.168.1.1", 8080, "validSecret") }
159+
}
160+
161+
@Test
162+
fun testVpnConnectionState_Connecting() {
163+
val vpnConnectionStateLiveData = MutableLiveData(VpnConnectionState.CONNECTING)
164+
every { mockViewModel.vpnConnectionState } returns vpnConnectionStateLiveData
165+
166+
composeTestRule.onNodeWithText("Connecting...")
167+
.assertIsDisplayed().assertIsNotEnabled()
168+
}
169+
170+
@Test
171+
fun testVpnConnectionState_Connected() {
172+
val vpnConnectionStateLiveData = MutableLiveData(VpnConnectionState.CONNECTED)
173+
every { mockViewModel.vpnConnectionState } returns vpnConnectionStateLiveData
174+
175+
composeTestRule.onNodeWithText("Stats Screen")
176+
.assertIsDisplayed()
177+
}
178+
179+
@Test
180+
fun testVpnConnectionError() {
181+
val vpnConnectionErrorLiveData = MutableLiveData("Some error occurred")
182+
every { mockViewModel.vpnConnectionError } returns vpnConnectionErrorLiveData
183+
184+
composeTestRule.onNodeWithText("Some error occurred")
185+
.assertIsDisplayed()
186+
}
187+
}
188+
}

app/src/main/java/com/pcapplusplus/toyvpn/ConnectScreen.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,14 +64,14 @@ fun ConnectScreen(
6464
fun validateIpv4Address(address: String): Boolean {
6565
val ipv4Pattern =
6666
Pattern.compile(
67-
"^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$",
67+
"^((25[0-5]|2[0-4][0-9]|1[0-9]{1,2}|0?[1-9][0-9]{0,2}|)\\.)((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){2}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$",
6868
)
6969
return ipv4Pattern.matcher(address).matches()
7070
}
7171

7272
fun validatePort(port: String): Boolean {
7373
return port.toIntOrNull()?.let {
74-
it in 0..65535
74+
it in 1..65535
7575
} ?: false
7676
}
7777

0 commit comments

Comments
 (0)