Skip to content

Commit

Permalink
Us1762585 - iOS Refactor Native components (#81)
Browse files Browse the repository at this point in the history
* US1762570: create view manager to native AccessCheckoutEditText component; override onTextChange to support the react native onChangeText event; implement component in react native

* US1762550: update card details field to use new component; update RN module to use reference to AccessCheckoutEditText and not the text value

* US1762550: move AccessCheckoutEditTextManager to correct file path; update local android sdk files to most up-to-date version of master; update AccessCheckoutReactPackageTest

* US1782181: add unit tests for AccessCheckoutEditTextManagerTest; add mockito to build.gradle for unit tests

* US1762551: Expose AccessCheckoutUITextField to be able to use native components via the ios Bridge
Simplify ReactNativeViewLocator and implement/expose specific POC methods to avoid disruptions with previous implementation
Add POC toggle to demo app to be able to switch implementations
Expose new POC validation and sessionGeneration methods to avoid diruptions with previous implementations

* US1762551: Point client to npe environment

* US1762585: Refactor ios Bridge and demo application
Avoids having to handle inputs in states
Uses native components
Only a input field id is needed in order to configure validation and session generation for a field
Integrate the latest AccessCheckoutUITextField changes
Updates and renames cvcValue to cvcId
Updates and renames panValue to panId
Updates and renames expiryDateValue to expiryDateId

* US1762585: Rebuild lib and clean up files

* US1762585: Minor cleanup

* US1762585: Rebuild lib files
Rename AccessCheckoutEditText to AccessCheckoutInputText as EditText was specific to Android
Minor renaming of fields like panUITextField to panACUITextField to improve readability and make it known that we are dealing with AccessCheckoutUITextField in the iOS bridge

* US1762585:
Rename AccessCheckoutEditText to AccessCheckoutInputText in Android bridge

* US1762585:
Rename AccessCheckoutInputText to AccessCheckoutTextInput
Rebuild lib files

* US1762585: Update bitrise-step to use xcode-test v5 and update e2e tests (#82)

* US1762585: Update bitrise-step to use xcode-test v5

* US1762585: Use Optionals to mantain compatibility in both Xcode 13 and Xcode 15

* US1762585: Update e2e tests

* US1762585: Improve matcher based on platform using type

* US1762585: Remove TextInputPO as it is no longer used

---------

Co-authored-by: e5661323 <jason.dzelamensah@fisglobal.com>
  • Loading branch information
2 people authored and Olivier Chalet committed Jan 5, 2024
1 parent 482d9bd commit 7728bba
Show file tree
Hide file tree
Showing 83 changed files with 989 additions and 653 deletions.
4 changes: 1 addition & 3 deletions access-checkout-react-native-sdk/.prettierrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,5 @@
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5",
"useTabs": false,
"arrowParens": "avoid",
"bracketSameLine": true
"useTabs": false
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ dependencies {
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.assertj:assertj-core:3.21.0'
testImplementation 'org.robolectric:robolectric:4.4'
testImplementation 'org.mockito:mockito-core:5.8.0'


instrumentedTestsImplementation 'androidx.test:core:1.4.0'
instrumentedTestsImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package com.worldpay.access.checkout.reactnative
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.widget.EditText
import androidx.lifecycle.LifecycleOwner
import com.facebook.react.bridge.*
import com.facebook.react.uimanager.util.ReactFindViewUtil
Expand All @@ -22,6 +21,7 @@ import com.worldpay.access.checkout.reactnative.validation.CvcOnlyValidationConf
import com.worldpay.access.checkout.reactnative.validation.CvcOnlyValidationListener
import com.worldpay.access.checkout.session.AccessCheckoutClientDisposer
import com.worldpay.access.checkout.session.api.client.WpSdkHeader
import com.worldpay.access.checkout.ui.AccessCheckoutEditText

/**
* Module class that implements all the functionality that is required by Javascript for the end user
Expand Down Expand Up @@ -77,15 +77,22 @@ class AccessCheckoutReactNativeModule constructor(

sessionResponseListener.promise = promise

val rootView = reactContext.currentActivity?.window?.decorView?.rootView

val panView = ReactFindViewUtil.findView(rootView, config.panValue!!) as AccessCheckoutEditText
val expiryDateView =
ReactFindViewUtil.findView(rootView, config.expiryDateValue!!) as AccessCheckoutEditText
val cvcView = ReactFindViewUtil.findView(rootView, config.cvcValue!!) as AccessCheckoutEditText

val cardDetails: CardDetails = if (isCvcSessionOnly(config.sessionTypes)) {
CardDetails.Builder()
.cvc(config.cvcValue!!)
.cvc(cvcView)
.build()
} else {
CardDetails.Builder()
.pan(config.panValue!!)
.expiryDate(config.expiryDateValue!!)
.cvc(config.cvcValue!!)
.pan(panView)
.expiryDate(expiryDateView)
.cvc(cvcView)
.build()
}

Expand All @@ -111,10 +118,10 @@ class AccessCheckoutReactNativeModule constructor(
val config = CardValidationConfigConverter().fromReadableMap(readableMap)
val rootView = reactContext.currentActivity?.window?.decorView?.rootView

val panView = ReactFindViewUtil.findView(rootView, config.panId) as EditText
val panView = ReactFindViewUtil.findView(rootView, config.panId) as AccessCheckoutEditText
val expiryDateView =
ReactFindViewUtil.findView(rootView, config.expiryDateId) as EditText
val cvcView = ReactFindViewUtil.findView(rootView, config.cvcId) as EditText
ReactFindViewUtil.findView(rootView, config.expiryDateId) as AccessCheckoutEditText
val cvcView = ReactFindViewUtil.findView(rootView, config.cvcId) as AccessCheckoutEditText

val cardValidationConfigBuilder = CardValidationConfig.Builder()
.baseUrl(config.baseUrl)
Expand Down Expand Up @@ -156,7 +163,7 @@ class AccessCheckoutReactNativeModule constructor(
val config = CvcOnlyValidationConfigConverter().fromReadableMap(readableMap)
val rootView = reactContext.currentActivity?.window?.decorView?.rootView

val cvcView = ReactFindViewUtil.findView(rootView, config.cvcId) as EditText
val cvcView = ReactFindViewUtil.findView(rootView, config.cvcId) as AccessCheckoutEditText

val cvcOnlyValidationConfigBuilder = CvcValidationConfig.Builder()
.cvc(cvcView)
Expand Down Expand Up @@ -208,4 +215,3 @@ class AccessCheckoutReactNativeModule constructor(
return sessionType.count() == 1 && sessionType.first() == SessionType.CVC
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.worldpay.access.checkout.reactnative
import com.facebook.react.ReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.uimanager.ViewManager
import com.worldpay.access.checkout.reactnative.ui.AccessCheckoutTextInputManager

/**
* This class is responsible for registering native modules with React Native. (called upon initialisation)
Expand All @@ -13,8 +13,7 @@ class AccessCheckoutReactPackage : ReactPackage {
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
return listOf(AccessCheckoutReactNativeModule(reactContext))
}

override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
return emptyList()
}
override fun createViewManagers(
reactContext: ReactApplicationContext
) = listOf(AccessCheckoutTextInputManager(reactContext))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.worldpay.access.checkout.reactnative.ui

import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.uimanager.SimpleViewManager
import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.uimanager.ViewProps
import com.facebook.react.uimanager.annotations.ReactProp
import com.worldpay.access.checkout.ui.AccessCheckoutEditText

class AccessCheckoutTextInputManager(private val callerContext: ReactApplicationContext) :
SimpleViewManager<AccessCheckoutEditText>() {

override fun getName() = REACT_CLASS

companion object {
const val REACT_CLASS = "AccessCheckoutTextInput"
}

override fun createViewInstance(context: ThemedReactContext) = AccessCheckoutEditText(context)

/**
* Properties
*/
@ReactProp(name = ViewProps.COLOR)
fun setRTCTextColor(accessCheckoutEditText: AccessCheckoutEditText, color: Int) {
accessCheckoutEditText.setTextColor(color)
}

@ReactProp(name = "placeholder")
fun setRTCPlaceholder(accessCheckoutEditText: AccessCheckoutEditText, placeholder: String) {
accessCheckoutEditText.setHint(placeholder)
}

@ReactProp(name = "editable")
fun setRTCEditable(accessCheckoutEditText: AccessCheckoutEditText, editable: Boolean) {
accessCheckoutEditText.isEnabled = editable
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.worldpay.access.checkout.reactnative
import android.content.Context
import androidx.test.core.app.ApplicationProvider
import com.facebook.react.bridge.ReactApplicationContext
import com.worldpay.access.checkout.reactnative.ui.AccessCheckoutTextInputManager
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import org.junit.runner.RunWith
Expand All @@ -24,7 +25,8 @@ internal class AccessCheckoutReactPackageTest {
fun `createViewManagers() should not create view managers`() {
val viewManagers = acoReactPackage.createViewManagers(reactApplicationContext())

assertThat(viewManagers).isEmpty()
assertThat(viewManagers).hasSize(1)
assertThat(viewManagers[0]).isInstanceOf(AccessCheckoutTextInputManager::class.java)
}

private fun reactApplicationContext(): ReactApplicationContext {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.worldpay.access.checkout.reactnative.ui

import android.content.Context
import android.graphics.Color
import androidx.test.core.app.ApplicationProvider
import com.facebook.react.bridge.ReactApplicationContext
import com.worldpay.access.checkout.ui.AccessCheckoutEditText
import org.assertj.core.api.AssertionsForClassTypes.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.robolectric.RobolectricTestRunner

@RunWith(RobolectricTestRunner::class)
internal class AccessCheckoutTextInputManagerTest {
private val manager = AccessCheckoutTextInputManager(reactApplicationContext())
private val accessCheckoutEditTextMock = mock(AccessCheckoutEditText::class.java)

@Test
fun `getName() should return the name of the react class`() {
val expectedName = "AccessCheckoutTextInput"

assertThat(manager.name).isEqualTo(expectedName)
}

@Test
fun `setRTCTextColour() should call setTextColour on AccessEditText`() {
manager.setRTCTextColor(accessCheckoutEditTextMock, Color.RED)

verify(accessCheckoutEditTextMock).setTextColor(Color.RED)
}

@Test
fun `setRTCPlaceholder() should call setHint on AccessEditText`() {
manager.setRTCPlaceholder(accessCheckoutEditTextMock, "placeholder")

verify(accessCheckoutEditTextMock).setHint("placeholder")
}

@Test
fun `setRTCEditable() should call isEnabled on AccessEditText`() {
manager.setRTCEditable(accessCheckoutEditTextMock, true)

verify(accessCheckoutEditTextMock).isEnabled = true
}


private fun reactApplicationContext(): ReactApplicationContext {
val applicationContext: Context = ApplicationProvider.getApplicationContext()
return ReactApplicationContext(applicationContext)
}
}
Loading

0 comments on commit 7728bba

Please sign in to comment.