Skip to content

Commit

Permalink
Merge pull request #189 from MyPureCloud/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
AfanasievAnton authored Jun 21, 2023
2 parents d8cfd74 + 3e992a0 commit 2560b48
Show file tree
Hide file tree
Showing 95 changed files with 3,191 additions and 470 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,4 @@ fastlane/test_output
iOSInjectionProject/
secure.properties
deployment.properties
okta.properties
21 changes: 21 additions & 0 deletions Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ pipeline{
environment {
DEPLOYMENT_ID = credentials("messenger-mobile-sdk-deployment-id")
DEPLOYMENT_DOMAIN = 'inindca.com'
OKTA_DOMAIN = 'dev-2518047.okta.com'
CLIENT_ID = credentials("messenger-mobile-sdk-okta-client-id")
SIGN_IN_REDIRECT_URI = 'com.okta.dev-2518047://oauth2/code'
SIGN_OUT_REDIRECT_URI = 'com.okta.dev-2518047:/'
OKTA_STATE = credentials("messenger-mobile-sdk-okta-state")
CODE_CHALLENGE = 'Cc6VZuBMOjDa9wKlFZLK-9lLPr_Q5e7mJsnVooFnBWA'
CODE_CHALLENGE_METHOD = 'S256'
CODE_VERIFIER = 'BtNSLgCNFlZPEOodtxgIp7c-SlnC0RaLilxRaYuZ7DI'
HOME = """${sh(
returnStdout: true,
script: 'if [ -z "$HOME" ]; then echo "/Users/$(whoami)"; else echo "$HOME"; fi'
Expand Down Expand Up @@ -85,6 +93,19 @@ pipeline{
echo "deploymentId=${DEPLOYMENT_ID}" >> deployment.properties
echo "deploymentDomain=${DEPLOYMENT_DOMAIN}" >> deployment.properties
fi
if [ -e okta.properties ]; then
echo "okta.properties file already exists"
else
echo "creating okta.properties file based on environment variables"
echo "oktaDomain=${OKTA_DOMAIN}" >> okta.properties
echo "clientId=${CLIENT_ID}" >> okta.properties
echo "signInRedirectUri=${SIGN_IN_REDIRECT_URI}" >> okta.properties
echo "signOutRedirectUri=${SIGN_OUT_REDIRECT_URI}" >> okta.properties
echo "oktaState=${OKTA_STATE}" >> okta.properties
echo "codeChallenge=${CODE_CHALLENGE}" >> okta.properties
echo "codeChallengeMethod=${CODE_CHALLENGE_METHOD}" >> okta.properties
echo "codeVerifier=${CODE_VERIFIER}" >> okta.properties
fi
./gradlew -p "transport" :transport:syncFramework \
-Pkotlin.native.cocoapods.platform=iphoneos\
-Pkotlin.native.cocoapods.archs="arm64" \
Expand Down
32 changes: 29 additions & 3 deletions androidComposePrototype/build.gradle
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@

buildscript {
ext {
deploymentProperties = new Properties()
oktaProperties = new Properties()
if (System.env.JENKINS_HOME != null) {
// when executing on jenkins, use environment variable-injected credentials
deploymentProperties["deploymentId"] = System.env.DEPLOYMENT_ID
deploymentProperties["deploymentDomain"] = System.env.DEPLOYMENT_DOMAIN
//Okta variables
oktaProperties["oktaDomain"] = System.env.OKTA_DOMAIN
oktaProperties["clientId"] = System.env.CLIENT_ID
oktaProperties["signInRedirectUri"] = System.env.SIGN_IN_REDIRECT_URI
oktaProperties["signOutRedirectUri"] = System.env.SIGN_OUT_REDIRECT_URI
oktaProperties["oktaState"] = System.env.OKTA_STATE
oktaProperties["codeChallenge"] = System.env.CODE_CHALLENGE
oktaProperties["codeChallengeMethod"] = System.env.CODE_CHALLENGE_METHOD
oktaProperties["codeVerifier"] = System.env.CODE_VERIFIER
} else {
// when executing elsewhere (dev laptops, etc), use the secure.properties file on disk
deploymentPropertiesFile = new File(rootDir.absolutePath + "/deployment.properties")
Expand All @@ -14,6 +23,12 @@ buildscript {
} else {
throw new GradleException("Deployment id and domain must be specified in either environmental variables for Jenkins or ${rootDir.absolutePath}/deployment.properties locally.")
}
oktaPropertiesFile = new File(rootDir.absolutePath + "/okta.properties")
if (oktaPropertiesFile.exists()) {
oktaProperties.load(oktaPropertiesFile.newReader())
} else {
logger.warn("okta.properties does not exist in the path and will be ignored.")
}
}
}
}
Expand All @@ -32,12 +47,21 @@ android {
defaultConfig {
applicationId "com.genesys.cloud.webmessaging.androidcomposeprototype"
minSdkVersion 21
targetSdkVersion 30
targetSdkVersion 33
versionCode 1
versionName "1.0"

buildConfigField "String", "DEPLOYMENT_ID", '"' + deploymentProperties["deploymentId"] + '"'
buildConfigField "String", "DEPLOYMENT_DOMAIN", '"' + deploymentProperties["deploymentDomain"] + '"'
// OKTA
buildConfigField "String", "OKTA_DOMAIN", '"' + oktaProperties["oktaDomain"] + '"'
buildConfigField "String", "CLIENT_ID", '"' + oktaProperties["clientId"] + '"'
buildConfigField "String", "SIGN_IN_REDIRECT_URI", '"' + oktaProperties["signInRedirectUri"] + '"'
buildConfigField "String", "SIGN_OUT_REDIRECT_URI", '"' + oktaProperties["signOutRedirectUri"] + '"'
buildConfigField "String", "OKTA_STATE", '"' + oktaProperties["oktaState"] + '"'
buildConfigField "String", 'CODE_CHALLENGE', "\"${oktaProperties.getProperty('codeChallenge')}\""
buildConfigField "String", 'CODE_CHALLENGE_METHOD', "\"${oktaProperties.getProperty('codeChallengeMethod')}\""
buildConfigField "String", 'CODE_VERIFIER', "\"${oktaProperties.getProperty('codeVerifier')}\""

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
Expand Down Expand Up @@ -97,9 +121,11 @@ dependencies {
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_viewmodel_compose_version"
// Integration with activities
implementation "androidx.activity:activity-compose:$activity_compose_version"
// Chrome Custom Tabs
implementation 'androidx.browser:browser:1.4.0'

testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
androidTestImplementation "com.jaredrummler:android-device-names:2.0.0"
androidTestImplementation 'org.awaitility:awaitility-kotlin:3.1.6'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import androidx.test.uiautomator.Until
open class BasePage(val activity: Activity) {

val waitTime = 40.toLong()
val shortWaitTime = 5.toLong()

// Initialize activity
init {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package com.genesys.cloud.messenger.uitest.page

import android.app.Activity
import android.view.KeyEvent
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.UiSelector
import org.awaitility.Awaitility
import org.awaitility.Awaitility.await
import java.lang.Thread.sleep
import java.util.concurrent.TimeUnit.SECONDS

Expand All @@ -20,6 +23,14 @@ class MessengerPage(activity: Activity) : BasePage(activity) {
private val disconnectEventText = "Event\$ConversationDisconnect"
private val newChatText = "newChat"
private val readOnlyText = "ReadOnly"
private val noUAuthText = "NoAuth"
private val closeButton = "Close tab"
private val welcomeText = "Welcome to Chrome"
private val acceptText = "Accept & continue"
private val noThanksText = "No thanks"
private val signInText = "Sign In"
private val signInUserNameId = "okta-signin-username"
private val signInPasswordId = "okta-signin-password"

// Wait until android compose prototype begins
fun verifyPageIsVisible(waitTime: Long = 20) {
Expand Down Expand Up @@ -55,6 +66,18 @@ class MessengerPage(activity: Activity) : BasePage(activity) {
return commandBox.getText()
}

fun getAuthStateResponse(): String {
val mDevice = UiDevice.getInstance(
InstrumentationRegistry.getInstrumentation()
)
val commandBox = mDevice.findObject(
UiSelector()
.className(commandClass)
.index(4)
)
return commandBox.getText()
}

// Wait for client to return proper response
fun waitForProperResponse(response: String) {
Awaitility.await().atMost(waitTime, SECONDS)
Expand All @@ -63,31 +86,52 @@ class MessengerPage(activity: Activity) : BasePage(activity) {
}
}

fun checkForUnAuthenticatedResponse(rejectionText: String) {
waitForElementWithUIAutomator(rejectionText)
tapWithUIAutomator(closeButton)
waitForAuthMsgReceived(noUAuthText)
}

fun waitForAuthMsgReceived(messageToBeReceived: String) {
await().atMost(waitTime, SECONDS)
.until {
(
getAuthStateResponse().contains(
messageToBeReceived,
ignoreCase = true
)
)
}
}

// Wait for configure response
fun waitForConfigured() {
Awaitility.await().atMost(waitTime, SECONDS)
var clientResponse: String = ""
await().atMost(waitTime, SECONDS)
.until {
(getClientResponse().contains("Configured", ignoreCase = true) || (getClientResponse().contains("ReadOnly", ignoreCase = true)))
clientResponse = getClientResponse()
(clientResponse.contains("Configured", ignoreCase = true) || (clientResponse.contains("ReadOnly", ignoreCase = true)))
}
if (getClientResponse().contains("ReadOnly", ignoreCase = true)) {
if (clientResponse.contains("ReadOnly", ignoreCase = true)) {
enterCommand(newChatText)
Awaitility.await().atMost(waitTime, SECONDS)
await().atMost(waitTime, SECONDS)
.until {
getClientResponse().contains("Configured", ignoreCase = true)
clientResponse = getClientResponse()
clientResponse.contains("Configured", ignoreCase = true)
}
}
}

fun waitForReadOnly() {
Awaitility.await().atMost(waitTime, SECONDS)
await().atMost(waitTime, SECONDS)
.until {
getClientResponse().contains("ReadOnly", ignoreCase = true)
}
}

// Wait for client to be closed
fun waitForClosed() {
Awaitility.await().atMost(waitTime, SECONDS)
await().atMost(waitTime, SECONDS)
.until {
getClientResponse().contains("Closed", ignoreCase = true)
}
Expand All @@ -98,8 +142,12 @@ class MessengerPage(activity: Activity) : BasePage(activity) {
val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
val responseBox = mDevice.findObject(
UiSelector()
.className(responseClass)
.index(4)
.fromParent(
UiSelector()
.className(responseClass)
.index(5)
)
.className("android.widget.TextView")
)
return responseBox.getText()
}
Expand Down Expand Up @@ -148,4 +196,66 @@ class MessengerPage(activity: Activity) : BasePage(activity) {
if (!(response.contains("fileName=", ignoreCase = true))) AssertionError("Response does not contain: filename=")
if (!(response.contains("Deleted", ignoreCase = true))) AssertionError("Response does not contain: Deleted")
}

fun loginWithOkta(email: String, password: String) {
waitForElementWithUIAutomator(acceptText, shortWaitTime)
if (hasTextView(acceptText)) {
tapTextWithUIAutomator(acceptText)
waitForElementWithUIAutomator(noThanksText, shortWaitTime)
tapTextWithUIAutomator(noThanksText)
}
waitForElementWithUIAutomator(signInText)
typeWithUIAutomator(signInUserNameId, email)
pressTab()
typeWithUIAutomator(signInPasswordId, password)
pressEnterKey()
}

protected fun typeWithUIAutomator(id: String, text: String) {
val uiAutomatorInstance = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
val target = uiAutomatorInstance.findObject(UiSelector().resourceId(id))
target.clearTextField()
target.click()
target.setText(text)
}

protected fun pressTab() {
await().atMost(3, SECONDS).ignoreExceptions()
.untilAsserted {
UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) != null
}
UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()).pressKeyCode(KeyEvent.KEYCODE_TAB)
}

protected fun pressEnterKey() {
await().atMost(3, SECONDS).ignoreExceptions()
.untilAsserted {
UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) != null
}
UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()).pressEnter()
}

fun pressBackKey() {
await().atMost(3, SECONDS).ignoreExceptions()
.untilAsserted {
UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) != null
}
UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()).pressBack()
}

protected fun tapWithUIAutomator(contentDescription: String) {
val uiAutomatorInstance = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
val itemToTap = uiAutomatorInstance.findObject(UiSelector().descriptionContains(contentDescription))
itemToTap.click()
}
protected fun hasTextView(text: String): Boolean {
val uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
return uiDevice.hasObject(By.textContains(text))
}
protected fun tapTextWithUIAutomator(text: String) {
val uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
val itemToTap = uiDevice.findObject(UiSelector().text(text))
itemToTap.waitForExists(waitTime)
itemToTap.click()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class OpeningPage(activity: Activity) : BasePage(activity) {

val title = "Deployment ID"
val regionDefault = "inindca.com"
val tcaEnvironment = "inintca.com"
val prodRegion = ""

// Wait until android compose prototype begins
Expand Down Expand Up @@ -42,8 +43,13 @@ class OpeningPage(activity: Activity) : BasePage(activity) {
if (dcaView != null) {
dcaView.click()
}
} else if (testConfig.domain == tcaEnvironment) {
val tcaView = listOfSrollView.getChild(UiSelector().className("android.view.View").index(1))
if (tcaView != null) {
tcaView.click()
}
} else {
val prodView = listOfSrollView.getChild(UiSelector().className("android.view.View").index(1))
val prodView = listOfSrollView.getChild(UiSelector().className("android.view.View").index(2))
if (prodView != null) {
prodView.click()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class API : IdlingResource {
val url = URL("${testConfig.apiBaseAddress}$httpURL")
var output: JsonNode? = null
setIdle(false)
println("Sending $httpMethod to $httpURL.")
println("Sending $httpMethod to $url.")

with(url.openConnection() as HttpURLConnection) {
requestMethod = httpMethod
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,15 @@ data class Config(
val humanizeDisableDeploymentId: String,
val botDeploymentId: String,
val agentDisconnectDeploymentId: String,
val authDeploymentId: String,
val botName: String,
val password: String,
val domain: String,
val apiBaseAddress: String
val apiBaseAddress: String,
val oktaUsername: String,
val oktaPassword: String,
val oktaUser2name: String,
val oktaPassword2: String
)

private fun pullConfig(): Config {
Expand Down
Loading

0 comments on commit 2560b48

Please sign in to comment.