mirror of
https://gitlab.linphone.org/BC/public/linphone-android.git
synced 2026-04-17 12:58:28 +00:00
Trying AI generated UI tests
This commit is contained in:
parent
721e379b50
commit
0aca829ba7
7 changed files with 197 additions and 9 deletions
|
|
@ -267,6 +267,12 @@ dependencies {
|
|||
implementation(libs.openid.appauth)
|
||||
|
||||
implementation(libs.linphone)
|
||||
|
||||
testImplementation(libs.junit)
|
||||
testImplementation(libs.androidx.test.core)
|
||||
testImplementation(libs.androidx.arch.core.testing)
|
||||
testImplementation(libs.kotlinx.coroutines.test)
|
||||
testImplementation(libs.mockk)
|
||||
}
|
||||
|
||||
configure<org.jlleitschuh.gradle.ktlint.KtlintExtension> {
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@
|
|||
android:theme="@style/Theme.Linphone"
|
||||
android:appCategory="social"
|
||||
android:largeHeap="true"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
tools:targetApi="35">
|
||||
|
||||
<!-- Required for chat message & call notifications to be displayed in Android auto -->
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2023 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-android
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.linphone.ui.main.sso.utils
|
||||
|
||||
import android.net.Uri
|
||||
import net.openid.appauth.connectivity.ConnectionBuilder
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
import java.security.cert.X509Certificate
|
||||
import javax.net.ssl.*
|
||||
|
||||
class AppAuthConnectionBuilder(private val trustAll: Boolean) : ConnectionBuilder {
|
||||
override fun openConnection(uri: Uri): HttpURLConnection {
|
||||
val conn = URL(uri.toString()).openConnection() as HttpURLConnection
|
||||
if (trustAll && conn is HttpsURLConnection) {
|
||||
conn.sslSocketFactory = TRUSTING_CONTEXT.socketFactory
|
||||
conn.hostnameVerifier = HostnameVerifier { _, _ -> true }
|
||||
}
|
||||
return conn
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TRUSTING_CONTEXT: SSLContext by lazy {
|
||||
val context = SSLContext.getInstance("TLS")
|
||||
context.init(null, arrayOf<TrustManager>(object : X509TrustManager {
|
||||
override fun checkClientTrusted(chain: Array<out X509Certificate>?, authType: String?) {}
|
||||
override fun checkServerTrusted(chain: Array<out X509Certificate>?, authType: String?) {}
|
||||
override fun getAcceptedIssuers(): Array<X509Certificate> = arrayOf()
|
||||
}), java.security.SecureRandom())
|
||||
context
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -21,10 +21,12 @@ package org.linphone.ui.main.sso.viewmodel
|
|||
|
||||
import android.content.Intent
|
||||
import androidx.annotation.UiThread
|
||||
import androidx.core.net.toUri
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import java.io.File
|
||||
import kotlinx.coroutines.launch
|
||||
import net.openid.appauth.AppAuthConfiguration
|
||||
import net.openid.appauth.AuthState
|
||||
import net.openid.appauth.AuthorizationException
|
||||
import net.openid.appauth.AuthorizationRequest
|
||||
|
|
@ -39,10 +41,10 @@ import org.linphone.R
|
|||
import org.linphone.core.Factory
|
||||
import org.linphone.core.tools.Log
|
||||
import org.linphone.ui.GenericViewModel
|
||||
import org.linphone.ui.main.sso.utils.AppAuthConnectionBuilder
|
||||
import org.linphone.utils.Event
|
||||
import org.linphone.utils.FileUtils
|
||||
import org.linphone.utils.TimestampUtils
|
||||
import androidx.core.net.toUri
|
||||
|
||||
class SingleSignOnViewModel
|
||||
@UiThread
|
||||
|
|
@ -127,9 +129,12 @@ class SingleSignOnViewModel
|
|||
@UiThread
|
||||
private fun singleSignOn() {
|
||||
Log.i("$TAG Fetch from issuer [$singleSignOnUrl]")
|
||||
AuthorizationServiceConfiguration.fetchFromIssuer(
|
||||
singleSignOnUrl.toUri(),
|
||||
AuthorizationServiceConfiguration.RetrieveConfigurationCallback { serviceConfiguration, ex ->
|
||||
val connectionBuilder = AppAuthConnectionBuilder(true)
|
||||
val callback = object : AuthorizationServiceConfiguration.RetrieveConfigurationCallback {
|
||||
override fun onFetchConfigurationCompleted(
|
||||
serviceConfiguration: AuthorizationServiceConfiguration?,
|
||||
ex: AuthorizationException?
|
||||
) {
|
||||
if (ex != null) {
|
||||
Log.e(
|
||||
"$TAG Failed to fetch configuration from issuer [$singleSignOnUrl]: ${ex.errorDescription}"
|
||||
|
|
@ -137,13 +142,13 @@ class SingleSignOnViewModel
|
|||
onErrorEvent.postValue(
|
||||
Event("Failed to fetch configuration from issuer $singleSignOnUrl")
|
||||
)
|
||||
return@RetrieveConfigurationCallback
|
||||
return
|
||||
}
|
||||
|
||||
if (serviceConfiguration == null) {
|
||||
Log.e("$TAG Service configuration is null!")
|
||||
onErrorEvent.postValue(Event("Service configuration is null"))
|
||||
return@RetrieveConfigurationCallback
|
||||
return
|
||||
}
|
||||
|
||||
if (!::authState.isInitialized) {
|
||||
|
|
@ -169,10 +174,19 @@ class SingleSignOnViewModel
|
|||
}
|
||||
|
||||
val authRequest = authRequestBuilder.build()
|
||||
authService = AuthorizationService(coreContext.context)
|
||||
val authConfig = AppAuthConfiguration.Builder()
|
||||
.setConnectionBuilder(connectionBuilder)
|
||||
.build()
|
||||
authService = AuthorizationService(coreContext.context, authConfig)
|
||||
val authIntent = authService.getAuthorizationRequestIntent(authRequest)
|
||||
startAuthIntentEvent.postValue(Event(authIntent))
|
||||
}
|
||||
}
|
||||
|
||||
AuthorizationServiceConfiguration.fetchFromIssuer(
|
||||
singleSignOnUrl.toUri(),
|
||||
callback,
|
||||
connectionBuilder
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -180,7 +194,10 @@ class SingleSignOnViewModel
|
|||
private fun performRefreshToken() {
|
||||
if (::authState.isInitialized) {
|
||||
if (!::authService.isInitialized) {
|
||||
authService = AuthorizationService(coreContext.context)
|
||||
val authConfig = AppAuthConfiguration.Builder()
|
||||
.setConnectionBuilder(AppAuthConnectionBuilder(true))
|
||||
.build()
|
||||
authService = AuthorizationService(coreContext.context, authConfig)
|
||||
}
|
||||
|
||||
val authStateJsonFile = File(corePreferences.ssoCacheFile)
|
||||
|
|
|
|||
9
app/src/main/res/xml/network_security_config.xml
Normal file
9
app/src/main/res/xml/network_security_config.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<base-config>
|
||||
<trust-anchors>
|
||||
<certificates src="system" />
|
||||
<certificates src="user" />
|
||||
</trust-anchors>
|
||||
</base-config>
|
||||
</network-security-config>
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2023 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-android
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.linphone.ui.assistant.viewmodel
|
||||
|
||||
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkObject
|
||||
import io.mockk.unmockkAll
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.linphone.LinphoneApplication
|
||||
import org.linphone.core.CoreContext
|
||||
import org.linphone.core.CorePreferences
|
||||
|
||||
class AccountLoginViewModelTest {
|
||||
@get:Rule
|
||||
val instantTaskExecutorRule = InstantTaskExecutorRule()
|
||||
|
||||
private lateinit var viewModel: AccountLoginViewModel
|
||||
private val coreContext: CoreContext = mockk(relaxed = true)
|
||||
private val corePreferences: CorePreferences = mockk(relaxed = true)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
mockkObject(LinphoneApplication.Companion)
|
||||
every { LinphoneApplication.coreContext } returns coreContext
|
||||
every { LinphoneApplication.corePreferences } returns corePreferences
|
||||
|
||||
// Mock postOnCoreThread to execute the lambda immediately
|
||||
every { coreContext.postOnCoreThread(any()) } answers {
|
||||
val lambda = invocation.args[0] as (org.linphone.core.Core) -> Unit
|
||||
lambda(mockk(relaxed = true))
|
||||
}
|
||||
|
||||
viewModel = AccountLoginViewModel()
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
unmockkAll()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLoginButtonEnabledOnlyWhenBothFieldsAreFilled() {
|
||||
// MediatorLiveData needs an observer to be active and trigger its logic
|
||||
viewModel.loginEnabled.observeForever { }
|
||||
|
||||
// Initial state: both empty
|
||||
viewModel.sipIdentity.value = ""
|
||||
viewModel.password.value = ""
|
||||
assertEquals("Login button should be disabled when both fields are empty", false, viewModel.loginEnabled.value)
|
||||
|
||||
// Only username filled
|
||||
viewModel.sipIdentity.value = "testuser"
|
||||
viewModel.password.value = ""
|
||||
assertEquals("Login button should be disabled when password is empty", false, viewModel.loginEnabled.value)
|
||||
|
||||
// Only password filled
|
||||
viewModel.sipIdentity.value = ""
|
||||
viewModel.password.value = "testpassword"
|
||||
assertEquals("Login button should be disabled when username is empty", false, viewModel.loginEnabled.value)
|
||||
|
||||
// Both filled
|
||||
viewModel.sipIdentity.value = "testuser"
|
||||
viewModel.password.value = "testpassword"
|
||||
assertEquals("Login button should be enabled when both fields are filled", true, viewModel.loginEnabled.value)
|
||||
|
||||
// Username with only spaces
|
||||
viewModel.sipIdentity.value = " "
|
||||
viewModel.password.value = "testpassword"
|
||||
assertEquals("Login button should be disabled when username is only whitespace", false, viewModel.loginEnabled.value)
|
||||
}
|
||||
}
|
||||
|
|
@ -32,6 +32,11 @@ dotsIndicator = "5.1.0"
|
|||
photoview = "2.3.0"
|
||||
openidAppauth = "0.11.1"
|
||||
linphone = "5.5.+"
|
||||
junit = "4.13.2"
|
||||
androidxTestCore = "1.6.1"
|
||||
androidxArchCore = "2.2.0"
|
||||
kotlinxCoroutinesTest = "1.10.1"
|
||||
mockk = "1.13.16"
|
||||
|
||||
[libraries]
|
||||
androidx-annotations = { group = "androidx.annotation", name = "annotation", version.ref = "annotations" }
|
||||
|
|
@ -69,6 +74,12 @@ openid-appauth = { group = "net.openid", name = "appauth", version.ref = "openid
|
|||
|
||||
linphone = { group = "org.linphone", name = "linphone-sdk-android", version.ref = "linphone" }
|
||||
|
||||
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
||||
androidx-test-core = { group = "androidx.test", name = "core", version.ref = "androidxTestCore" }
|
||||
androidx-arch-core-testing = { group = "androidx.arch.core", name = "core-testing", version.ref = "androidxArchCore" }
|
||||
kotlinx-coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "kotlinxCoroutinesTest" }
|
||||
mockk = { group = "io.mockk", name = "mockk", version.ref = "mockk" }
|
||||
|
||||
[plugins]
|
||||
androidApplication = { id = "com.android.application", version.ref = "agp" }
|
||||
jetbrainsKotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||
|
|
@ -76,4 +87,4 @@ kapt = { id = "org.jetbrains.kotlin.kapt", version.ref = "kotlin" }
|
|||
navigation = { id = "androidx.navigation.safeargs.kotlin", version.ref = "navigation" }
|
||||
crashlytics = { id = "com.google.firebase.crashlytics", version.ref = "firebaseCrashlytics" }
|
||||
googleGmsServices = { id = "com.google.gms.google-services", version.ref = "gmsGoogleServices" }
|
||||
ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint" }
|
||||
ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint" }
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue