Skip to content

Fix functional tests #43

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Then add the following to the <a target="_blank" rel="noopener noreferrer" href=
dependencies {
...

implementation "com.couchbase.lite:couchbase-lite-android-ktx:3.0.2"
implementation "com.couchbase.lite:couchbase-lite-android-ktx:3.0.5"
}
```

Expand Down
8 changes: 4 additions & 4 deletions src/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -137,11 +137,11 @@ dependencies {
testImplementation 'junit:junit:4.13.2'
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_tests_version"
androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_tests_version"
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.4'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0'
androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_ui_version"
androidTestImplementation 'androidx.test:runner:1.4.0'
androidTestUtil 'androidx.test:orchestrator:1.4.1'
androidTestImplementation 'androidx.test:runner:1.5.1'
androidTestUtil 'androidx.test:orchestrator:1.4.2'
debugImplementation "androidx.compose.ui:ui-tooling:$compose_ui_version"
debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_ui_version"
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@ import com.couchbase.learningpath.data.warehouse.WarehouseRepositoryDb
import com.couchbase.learningpath.models.*
import com.couchbase.learningpath.services.MockAuthenticationService
import com.couchbase.lite.CouchbaseLiteException
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.test.runTest
import kotlinx.serialization.ExperimentalSerializationApi
import org.junit.*
import org.junit.Assert.*
import org.junit.runner.RunWith

@OptIn(ExperimentalSerializationApi::class, ExperimentalCoroutinesApi::class)
@RunWith(AndroidJUnit4::class)
class DatabaseIntegrationTests {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,19 @@
package com.couchbase.learningpath

import androidx.compose.material.*
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.test.*
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.compose.rememberNavController
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.accompanist.insets.ProvideWindowInsets
import kotlinx.coroutines.launch
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

import com.couchbase.learningpath.services.MockAuthenticationService
import com.couchbase.learningpath.ui.MainActivity
import com.couchbase.learningpath.ui.components.Drawer
import com.couchbase.learningpath.ui.theme.LearningPathTheme
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.serialization.ExperimentalSerializationApi

@OptIn(ExperimentalMaterialApi::class, ExperimentalSerializationApi::class, ExperimentalCoroutinesApi::class)
@RunWith(AndroidJUnit4::class)
class FunctionalTests {
//LoginView keys
Expand All @@ -45,7 +35,7 @@ class FunctionalTests {

//sample data
private val testUsername1 = "demo@example.com"
private val testPassword = "password"
private val testPassword = "P@ssw0rd12"
private val testFirstName1 = "Bob"
private val testLastName1 = "Smith"
private val testJobTitle1 = "Developer"
Expand All @@ -55,19 +45,17 @@ class FunctionalTests {
private val testLastName2 = "Doe"
private val testJobTitle2 = "Sr. Developer Advocate"

@OptIn(ExperimentalMaterialApi::class)
@get:Rule
val composeTestRule = createAndroidComposeRule<MainActivity>()

@Before
fun resetData() {
}

@OptIn(ExperimentalMaterialApi::class)
@Test
fun testApplicationWorkflow() {

var resources = launchLoginScreenWithNavGraph()
val resources = getResources()
composeTestRule.waitForIdle()

//authenticate as testUser1
Expand Down Expand Up @@ -116,10 +104,15 @@ class FunctionalTests {
//authenticate as testUser1
testAuthentication(testUsername1, testPassword, resources)

composeTestRule.onNodeWithContentDescription(resources[keyAppBarMenu].toString())
.performClick()

composeTestRule.onNodeWithText("Update User Profile")
.performClick()

assertUserProfile(testFirstName1, testLastName1, testJobTitle1, testUsername1, resources)
}

@OptIn(ExperimentalMaterialApi::class)
private fun testAuthentication(
username: String,
password: String,
Expand All @@ -140,22 +133,32 @@ class FunctionalTests {
composeTestRule.waitForIdle()
}

@OptIn(ExperimentalMaterialApi::class)
private fun testUserProfileForm(
firstName: String,
lastName: String,
jobTitle: String,
resources: Map<String, String>
) {
composeTestRule.onNodeWithContentDescription(resources[keyTfFirstName].toString())
.performTextInput(firstName)
composeTestRule.onNodeWithContentDescription(resources[keyTfLastName].toString())
.performTextInput(lastName)
composeTestRule.onNodeWithContentDescription(resources[keyTfJobTitle].toString())
.performTextInput(jobTitle)
composeTestRule.onNodeWithContentDescription(resources[keyAppBarMenu].toString())
.performClick()

composeTestRule.onNodeWithText("Update User Profile")
.performClick()

composeTestRule.onNodeWithContentDescription(resources[keyTfFirstName].toString()).apply {
performTextClearance()
performTextInput(firstName)
}
composeTestRule.onNodeWithContentDescription(resources[keyTfLastName].toString()).apply {
performTextClearance()
performTextInput(lastName)
}
composeTestRule.onNodeWithContentDescription(resources[keyTfJobTitle].toString()).apply {
performTextClearance()
performTextInput(jobTitle)
}
}

@OptIn(ExperimentalMaterialApi::class)
private fun assertUserProfile(
firstName: String,
lastName: String,
Expand All @@ -176,7 +179,6 @@ class FunctionalTests {
.assert(hasText(jobTitle, ignoreCase = true))
}

@OptIn(ExperimentalMaterialApi::class)
private fun logout(resources: Map<String, String>) {
composeTestRule.onNodeWithContentDescription(resources[keyAppBarMenu].toString())
.performClick()
Expand All @@ -185,101 +187,26 @@ class FunctionalTests {
.performClick()
}

@OptIn(ExperimentalMaterialApi::class)
private fun launchLoginScreenWithNavGraph(): Map<String, String> {
var resources = HashMap<String, String>()
composeTestRule.setContent {

private fun getResources(): Map<String, String> {
val resources = HashMap<String, String>()
with(composeTestRule.activity.resources) {
// ** get resources for testing **
// ** LoginView
resources[keyTfUsername] = stringResource(id = R.string.tfUsername)
resources[keyTfPassword] = stringResource(id = R.string.tfPassword)
resources[keyBtnLogin] = stringResource(id = R.string.btnLogin)
resources[keyTfUsername] = getString(R.string.tfUsername)
resources[keyTfPassword] = getString(R.string.tfPassword)
resources[keyBtnLogin] = getString(R.string.btnLogin)

//** UserProfileView
resources[keyLblEmail] = stringResource(id = R.string.lblEmail)
resources[keyTfFirstName] = stringResource(id = R.string.tfGivenName)
resources[keyTfLastName] = stringResource(id = R.string.tfSurname)
resources[keyTfJobTitle] = stringResource(id = R.string.tfJobTitle)
resources[keyBtnSave] = stringResource(id = R.string.btnSave)
resources[keyLblEmail] = getString(R.string.lblEmail)
resources[keyTfFirstName] = getString(R.string.tfGivenName)
resources[keyTfLastName] = getString(R.string.tfSurname)
resources[keyTfJobTitle] = getString(R.string.tfJobTitle)
resources[keyBtnSave] = getString(R.string.btnSave)

//* Overflow Menu
resources[keyAppBarMenu] = stringResource(id = R.string.btnAppBarMenu)
resources[keyBtnMenu] = stringResource(id = R.string.btnMenu)
resources[keyBtnLogout] = stringResource(id = R.string.btnLogout)

ProvideWindowInsets {

val context = LocalContext.current

val scope = rememberCoroutineScope()
val navController = rememberNavController()
val scaffoldState = rememberScaffoldState()
val authService = MockAuthenticationService()

//we need a drawer overflow menu on multiple screens
//so we need top level scaffold. An event to open the drawer is passed
//to each screen that needs it.
val drawerState = rememberDrawerState(DrawerValue.Closed)
val openDrawer = {
scope.launch {
drawerState.open()
}
}

LearningPathTheme() {
Scaffold(scaffoldState = scaffoldState,
snackbarHost = {
scaffoldState.snackbarHostState
}) {
ModalDrawer(
modifier = Modifier.semantics { contentDescription = "overflowMenu" },
drawerState = drawerState,
gesturesEnabled = drawerState.isOpen,
drawerContent = {
Drawer(
modifier = Modifier.semantics {
contentDescription = "overflowMenu1"
},
firstName = "",
lastName = "",
email = "",
team = "",
profilePicture = null,
onClicked = { route ->
scope.launch {
drawerState.close()
}
when (route) {
MainDestinations.LOGOUT_ROUTE -> {
authService.logout()
navController.navigate(MainDestinations.LOGIN_ROUTE) {
popUpTo(navController.graph.findStartDestination().id) {
inclusive = true
}
}
}
else -> {
navController.navigate(route) {
popUpTo(navController.graph.startDestinationId)
launchSingleTop = true
}
}
}
}
)
}
) {
InventoryNavGraph(
openDrawer = { openDrawer() },
navController = navController,
scaffoldState = scaffoldState,
scope = scope
)
}
}
}
}
resources[keyAppBarMenu] = getString(R.string.btnAppBarMenu)
resources[keyBtnMenu] = getString(R.string.btnMenu)
resources[keyBtnLogout] = getString(R.string.btnLogout)
}
return resources
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,8 @@ fun InventoryNavGraph(
auditId = it
}
val viewModel = getViewModel<AuditEditorViewModel>()
viewModel.getAudit(projectId = projectId, auditId = auditId)
viewModel.navigateToListSelection = actions.navigateToStockItemListSelector
viewModel.getAudit(projectId = projectId, auditId = auditId)
viewModel.navigateToListSelection = actions.navigateToStockItemListSelector

AuditEditorView(
viewModel = viewModel,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,10 @@ import android.util.Log
import com.couchbase.learningpath.data.DatabaseManager
import com.couchbase.learningpath.models.Audit
import com.couchbase.learningpath.models.AuditDao
import com.couchbase.learningpath.models.Project
import com.couchbase.learningpath.models.StockItem
import com.couchbase.learningpath.services.AuthenticationService
import com.couchbase.lite.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.withContext
import kotlinx.serialization.ExperimentalSerializationApi
Expand All @@ -21,7 +19,6 @@ import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import java.util.*

@OptIn(ExperimentalCoroutinesApi::class)
class AuditRepositoryDb(
var context: Context,
private val authenticationService: AuthenticationService
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ package com.couchbase.learningpath.data.stockItem

import android.content.Context
import android.util.Log
import androidx.compose.ui.text.toLowerCase
import com.couchbase.learningpath.data.DatabaseManager
import com.couchbase.learningpath.models.StockItem
import com.couchbase.learningpath.models.StockItemDao
Expand Down Expand Up @@ -34,7 +33,7 @@ class StockItemRepositoryDb(
db?.let { database ->
val query =
database.createQuery("SELECT * FROM _ AS item WHERE documentType=\"$documentType\"")
var results = query.execute().allResults()
val results = query.execute().allResults()
results.forEach { result ->
val stockItem = Json.decodeFromString<StockItemDao>(result.toJSON()).item
stockItems.add(stockItem)
Expand Down Expand Up @@ -76,7 +75,7 @@ class StockItemRepositoryDb(
db?.let { database ->
var queryString =
"SELECT * FROM _ as item WHERE documentType=\"item\" AND lower(name) LIKE ('%' || \$parameterName || '%')" // 1
var parameters = Parameters() // 2
val parameters = Parameters() // 2
parameters.setString("parameterName", searchName.lowercase()) // 3
searchDescription?.let { description ->
if (description.isNotEmpty()) { // 4
Expand All @@ -88,9 +87,9 @@ class StockItemRepositoryDb(
) // 6
}
}
var query = database.createQuery(queryString) // 7
val query = database.createQuery(queryString) // 7
query.parameters = parameters // 8
var results = query.execute().allResults() // 9
val results = query.execute().allResults() // 9
results.forEach { result -> // 10
val stockItem =
Json.decodeFromString<StockItemDao>(result.toJSON()).item // 11
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,11 @@ import com.couchbase.learningpath.data.DatabaseManager
import com.couchbase.learningpath.data.replicator.ReplicatorConfig
import com.couchbase.learningpath.data.replicator.ReplicatorManager
import com.couchbase.lite.*
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.InternalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import java.net.URI

@InternalCoroutinesApi
@OptIn( ExperimentalCoroutinesApi::class)
class ReplicatorServiceDb (
private val authenticationService: AuthenticationService,
val context: Context) : ReplicatorService
Expand All @@ -28,16 +26,16 @@ class ReplicatorServiceDb (
//if your sync gateway server is running on a different IP change it here
override var replicationConfig = mutableStateOf(
ReplicatorConfig(
username = loggedInUser.username,
password = loggedInUser.password,
username = loggedInUser.username,
password = loggedInUser.password,
endpointUrl = "ws://10.0.2.2:4984/projects",
replicatorType = "PUSH AND PULL",
heartBeat = 60L,
continuous = true,
selfSignedCert = true)
replicatorType = "PUSH AND PULL",
heartBeat = 60L,
continuous = true,
selfSignedCert = true
)
)


override val replicationStatus = mutableStateOf("")
override val replicationTypes = listOf("PUSH AND PULL", "PUSH", "PULL")
override val canStartReplication = mutableStateOf(false)
Expand Down
Loading