Convert UIManagerModuleConstantsHelper to Kotlin (#52358)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/52358

Another class going from Java to Kotlin.
This is quite involved due to the amount of Raw generics we were using so I'd appreaciate a couple of further eyes here.

Changelog:
[Android] [Changed] - Convert UIManagerModuleConstantsHelper to Kotlin

Reviewed By: mdvacca, javache

Differential Revision: D77589975

fbshipit-source-id: 477c1e2a8dfd31db60047fd1252f6d47c177f5c7
This commit is contained in:
Nicola Corti
2025-07-03 02:52:01 -07:00
committed by Facebook GitHub Bot
parent 6cb8dc37c7
commit 45fd7feb9f
6 changed files with 252 additions and 272 deletions
@@ -4428,13 +4428,6 @@ public abstract interface class com/facebook/react/uimanager/UIManagerModule$Cus
public abstract fun resolveCustomEventName (Ljava/lang/String;)Ljava/lang/String;
}
public class com/facebook/react/uimanager/UIManagerModuleConstantsHelper {
public fun <init> ()V
public static fun createConstants (Ljava/util/List;Ljava/util/Map;Ljava/util/Map;)Ljava/util/Map;
public static fun createConstantsForViewManager (Lcom/facebook/react/uimanager/ViewManager;Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;)Ljava/util/Map;
public static fun getDefaultExportableEventTypes ()Ljava/util/Map;
}
public abstract interface class com/facebook/react/uimanager/UIManagerModuleListener {
public abstract fun willDispatchViewUpdates (Lcom/facebook/react/uimanager/UIManagerModule;)V
}
@@ -203,7 +203,7 @@ internal class ReactInstance(
// initialized.
// This happens inside getTurboModuleManagerDelegate getter.
if (ReactNativeFeatureFlags.useNativeViewConfigsInBridgelessMode()) {
val customDirectEvents: Map<String, Any> = HashMap()
val customDirectEvents: MutableMap<String, Any> = HashMap()
UIConstantsProviderBinding.install(
// Use unbuffered RuntimeExecutor to install binding
@@ -214,9 +214,7 @@ internal class ReactInstance(
// 2. genericBubblingEventTypes.
// 3. genericDirectEventTypes.
// We want to match this beahavior.
{
Arguments.makeNativeMap(UIManagerModuleConstantsHelper.getDefaultExportableEventTypes())
},
{ Arguments.makeNativeMap(UIManagerModuleConstantsHelper.defaultExportableEventTypes) },
ConstantsForViewManagerProvider { viewManagerName: String ->
val viewManager =
viewManagerResolver.getViewManager(viewManagerName)
@@ -573,8 +571,8 @@ internal class ReactInstance(
}
private fun createConstants(
viewManagers: List<ViewManager<*, *>>,
customDirectEvents: Map<String, Any>?
viewManagers: List<ViewManager<in Nothing, in Nothing>>,
customDirectEvents: MutableMap<String, Any>?
): MutableMap<String, Any> {
ReactMarker.logMarker(ReactMarkerConstants.CREATE_UI_MANAGER_MODULE_CONSTANTS_START)
SystraceMessage.beginSection(Systrace.TRACE_TAG_REACT, "CreateUIManagerConstants")
@@ -591,7 +589,7 @@ internal class ReactInstance(
private fun getConstantsForViewManager(
viewManager: ViewManager<*, *>,
customDirectEvents: Map<String, Any>
customDirectEvents: MutableMap<String, Any>
): NativeMap {
SystraceMessage.beginSection(
Systrace.TRACE_TAG_REACT, "ReactInstance.getConstantsForViewManager")
@@ -248,7 +248,7 @@ public class UIManagerModule extends ReactContextBaseJavaModule
.arg("Lazy", true)
.flush();
try {
return UIManagerModuleConstantsHelper.createConstants(viewManagerResolver);
return UIManagerModuleConstantsHelper.internal_createConstants(viewManagerResolver);
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT);
ReactMarker.logMarker(CREATE_UI_MANAGER_MODULE_CONSTANTS_END);
@@ -264,7 +264,7 @@ public class UIManagerModule extends ReactContextBaseJavaModule
.arg("Lazy", false)
.flush();
try {
return UIManagerModuleConstantsHelper.createConstants(
return UIManagerModuleConstantsHelper.internal_createConstants(
viewManagers, customBubblingEvents, customDirectEvents);
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT);
@@ -291,7 +291,7 @@ public class UIManagerModule extends ReactContextBaseJavaModule
.flush();
try {
Map<String, Object> viewManagerConstants =
UIManagerModuleConstantsHelper.createConstantsForViewManager(
UIManagerModuleConstantsHelper.internal_createConstantsForViewManager(
viewManager, null, null, null, customDirectEvents);
if (viewManagerConstants != null) {
return Arguments.makeNativeMap(viewManagerConstants);
@@ -1,244 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.uimanager;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.facebook.common.logging.FLog;
import com.facebook.infer.annotation.Nullsafe;
import com.facebook.react.common.MapBuilder;
import com.facebook.react.common.build.ReactBuildConfig;
import com.facebook.react.internal.featureflags.ReactNativeNewArchitectureFeatureFlags;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Helps generate constants map for {@link UIManagerModule} by collecting and merging constants from
* registered view managers.
*/
@Nullsafe(Nullsafe.Mode.LOCAL)
public class UIManagerModuleConstantsHelper {
private static final String TAG = "UIManagerModuleConstantsHelper";
private static final String BUBBLING_EVENTS_KEY = "bubblingEventTypes";
private static final String DIRECT_EVENTS_KEY = "directEventTypes";
/**
* Generates a lazy discovery enabled version of {@link UIManagerModule} constants. It only
* contains a list of view manager names, so that JS side is aware of the managers there are.
* Actual ViewManager instantiation happens when {@code
* UIManager.getViewManagerConfig('SpecificViewManager')} call happens. The View Manager is then
* registered on the JS side with the help of {@code UIManagerModule.getConstantsForViewManager}.
*/
/* package */ static Map<String, Object> createConstants(ViewManagerResolver resolver) {
Map<String, Object> constants = UIManagerModuleConstants.constants;
constants.put("ViewManagerNames", new ArrayList<>(resolver.getViewManagerNames()));
constants.put("LazyViewManagersEnabled", true);
return constants;
}
public static Map<String, Object> getDefaultExportableEventTypes() {
return MapBuilder.<String, Object>of(
BUBBLING_EVENTS_KEY, UIManagerModuleConstants.bubblingEventTypeConstants,
DIRECT_EVENTS_KEY, UIManagerModuleConstants.directEventTypeConstants);
}
private static void validateDirectEventNames(
String viewManagerName, @Nullable Map<String, Object> directEvents) {
if (!ReactBuildConfig.DEBUG || directEvents == null) {
return;
}
for (String key : directEvents.keySet()) {
Object value = directEvents.get(key);
if (value != null && (value instanceof Map)) {
String regName = (String) ((Map) value).get("registrationName");
if (regName != null
&& key.startsWith("top")
&& regName.startsWith("on")
&& !key.substring(3).equals(regName.substring(2))) {
FLog.e(
TAG,
String.format(
"Direct event name for '%s' doesn't correspond to the naming convention,"
+ " expected 'topEventName'->'onEventName', got '%s'->'%s'",
viewManagerName, key, regName));
}
}
}
}
/**
* Generates map of constants that is then exposed by {@link UIManagerModule}. Provided list of
* {@param viewManagers} is then used to populate content of those predefined fields using {@link
* ViewManager#getExportedCustomBubblingEventTypeConstants} and {@link
* ViewManager#getExportedCustomDirectEventTypeConstants} respectively. Each view manager is in
* addition allowed to expose viewmanager-specific constants that are placed under the key that
* corresponds to the view manager's name (see {@link ViewManager#getName}). Constants are merged
* into the map of {@link UIManagerModule} base constants that is stored in {@link
* UIManagerModuleConstants}. TODO(6845124): Create a test for this
*/
// NOTE: When converted to Kotlin this method should be `internal` due to
// visibility restriction for `ReactInstance`
public static Map<String, Object> createConstants(
List<ViewManager> viewManagers,
@Nullable Map<String, Object> allBubblingEventTypes,
@Nullable Map<String, Object> allDirectEventTypes) {
Map<String, Object> constants = UIManagerModuleConstants.constants;
// Generic/default event types:
// All view managers are capable of dispatching these events.
// They will be automatically registered with React Fiber.
Map genericBubblingEventTypes = UIManagerModuleConstants.bubblingEventTypeConstants;
Map genericDirectEventTypes = UIManagerModuleConstants.directEventTypeConstants;
// Cumulative event types:
// View manager specific event types are collected as views are loaded.
// This information is used later when events are dispatched.
if (allBubblingEventTypes != null) {
allBubblingEventTypes.putAll(genericBubblingEventTypes);
}
if (allDirectEventTypes != null) {
allDirectEventTypes.putAll(genericDirectEventTypes);
}
for (ViewManager viewManager : viewManagers) {
final String viewManagerName = viewManager.getName();
Map viewManagerConstants =
createConstantsForViewManager(
viewManager, null, null, allBubblingEventTypes, allDirectEventTypes);
if (!viewManagerConstants.isEmpty()) {
constants.put(viewManagerName, viewManagerConstants);
}
}
constants.put("genericBubblingEventTypes", genericBubblingEventTypes);
constants.put("genericDirectEventTypes", genericDirectEventTypes);
return constants;
}
// NOTE: When converted to Kotlin this method should be `internal` due to
// visibility restriction for `ReactInstance`
public static Map<String, Object> createConstantsForViewManager(
ViewManager viewManager,
@Nullable Map defaultBubblingEvents,
@Nullable Map defaultDirectEvents,
@Nullable Map cumulativeBubblingEventTypes,
@Nullable Map cumulativeDirectEventTypes) {
Map<String, Object> viewManagerConstants = MapBuilder.newHashMap();
Map viewManagerBubblingEvents = viewManager.getExportedCustomBubblingEventTypeConstants();
if (viewManagerBubblingEvents != null) {
if (ReactNativeNewArchitectureFeatureFlags.enableFabricRenderer()
&& ReactNativeNewArchitectureFeatureFlags.useFabricInterop()) {
// For Fabric, events needs to be fired with a "top" prefix.
// For the sake of Fabric Interop, here we normalize events adding "top" in their
// name if the user hasn't provided it.
viewManagerBubblingEvents = normalizeEventTypes(viewManagerBubblingEvents);
}
recursiveMerge(cumulativeBubblingEventTypes, viewManagerBubblingEvents);
recursiveMerge(viewManagerBubblingEvents, defaultBubblingEvents);
viewManagerConstants.put(BUBBLING_EVENTS_KEY, viewManagerBubblingEvents);
} else if (defaultBubblingEvents != null) {
viewManagerConstants.put(BUBBLING_EVENTS_KEY, defaultBubblingEvents);
}
Map viewManagerDirectEvents = viewManager.getExportedCustomDirectEventTypeConstants();
validateDirectEventNames(viewManager.getName(), viewManagerDirectEvents);
if (viewManagerDirectEvents != null) {
if (ReactNativeNewArchitectureFeatureFlags.enableFabricRenderer()
&& ReactNativeNewArchitectureFeatureFlags.useFabricInterop()) {
// For Fabric, events needs to be fired with a "top" prefix.
// For the sake of Fabric Interop, here we normalize events adding "top" in their
// name if the user hasn't provided it.
viewManagerDirectEvents = normalizeEventTypes(viewManagerDirectEvents);
}
recursiveMerge(cumulativeDirectEventTypes, viewManagerDirectEvents);
recursiveMerge(viewManagerDirectEvents, defaultDirectEvents);
viewManagerConstants.put(DIRECT_EVENTS_KEY, viewManagerDirectEvents);
} else if (defaultDirectEvents != null) {
viewManagerConstants.put(DIRECT_EVENTS_KEY, defaultDirectEvents);
}
Map customViewConstants = viewManager.getExportedViewConstants();
if (customViewConstants != null) {
viewManagerConstants.put("Constants", customViewConstants);
}
Map viewManagerCommands = viewManager.getCommandsMap();
if (viewManagerCommands != null) {
viewManagerConstants.put("Commands", viewManagerCommands);
}
Map<String, String> viewManagerNativeProps = viewManager.getNativeProps();
if (!viewManagerNativeProps.isEmpty()) {
viewManagerConstants.put("NativeProps", viewManagerNativeProps);
}
return viewManagerConstants;
}
@VisibleForTesting
/* package */ static @Nullable Map normalizeEventTypes(@Nullable Map events) {
if (events == null) {
return null;
}
Set<String> keysToNormalize = new HashSet<>();
for (Object key : events.keySet()) {
if (key instanceof String) {
String keyString = (String) key;
if (!keyString.startsWith("top")) {
keysToNormalize.add(keyString);
}
}
}
// When providing one event in Kotlin, it will create a SingletonMap by default
// which will throw on trying to add new element to it
if (!(events instanceof HashMap)) {
events = new HashMap(events);
}
for (String oldKey : keysToNormalize) {
Object value = events.get(oldKey);
String baseKey = "";
if (oldKey.startsWith("on")) {
// Drop "on" prefix.
baseKey = oldKey.substring(2);
} else {
// Capitalize first letter.
baseKey = oldKey.substring(0, 1).toUpperCase() + oldKey.substring(1);
}
String newKey = "top" + baseKey;
events.put(newKey, value);
}
return events;
}
/** Merges {@param source} map into {@param dest} map recursively */
private static void recursiveMerge(@Nullable Map dest, @Nullable Map source) {
if (dest == null || source == null || source.isEmpty()) {
return;
}
for (Object key : source.keySet()) {
Object sourceValue = source.get(key);
Object destValue = dest.get(key);
if (destValue != null && (sourceValue instanceof Map) && (destValue instanceof Map)) {
// Since event maps are client based Map interface, it could be immutable
if (!(destValue instanceof HashMap)) {
destValue = new HashMap((Map) destValue);
dest.replace(key, (Map) destValue);
}
recursiveMerge((Map) destValue, (Map) sourceValue);
} else {
dest.put(key, sourceValue);
}
}
}
}
@@ -0,0 +1,238 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.uimanager
import androidx.annotation.VisibleForTesting
import com.facebook.common.logging.FLog
import com.facebook.react.common.build.ReactBuildConfig
import com.facebook.react.internal.featureflags.ReactNativeNewArchitectureFeatureFlags.enableFabricRenderer
import com.facebook.react.internal.featureflags.ReactNativeNewArchitectureFeatureFlags.useFabricInterop
import java.util.Locale
/**
* Helps generate constants map for [UIManagerModule] by collecting and merging constants from
* registered view managers.
*/
internal object UIManagerModuleConstantsHelper {
private const val TAG = "UIManagerModuleConstantsHelper"
private const val BUBBLING_EVENTS_KEY = "bubblingEventTypes"
private const val DIRECT_EVENTS_KEY = "directEventTypes"
/**
* Generates a lazy discovery enabled version of [UIManagerModule] constants. It only contains a
* list of view manager names, so that JS side is aware of the managers there are. Actual
* ViewManager instantiation happens when `UIManager.getViewManagerConfig('SpecificViewManager')`
* call happens. The View Manager is then registered on the JS side with the help of
* `UIManagerModule.getConstantsForViewManager`.
*/
@JvmStatic
@JvmName("internal_createConstants")
internal fun createConstants(resolver: ViewManagerResolver): Map<String, Any> =
UIManagerModuleConstants.constants.plus(
mapOf(
"ViewManagerNames" to ArrayList<String?>(resolver.getViewManagerNames()),
"LazyViewManagersEnabled" to true))
@JvmStatic
val defaultExportableEventTypes: Map<String, Any>
get() =
mapOf(
BUBBLING_EVENTS_KEY to UIManagerModuleConstants.bubblingEventTypeConstants,
DIRECT_EVENTS_KEY to UIManagerModuleConstants.directEventTypeConstants)
private fun validateDirectEventNames(
viewManagerName: String,
directEvents: MutableMap<String, Any>?
) {
if (!ReactBuildConfig.DEBUG || directEvents == null) {
return
}
for ((key, value) in directEvents) {
if (value is MutableMap<*, *>) {
val regName = value["registrationName"] as String?
if (regName != null &&
key.startsWith("top") &&
regName.startsWith("on") &&
(key.substring(3) != regName.substring(2))) {
FLog.e(
TAG,
"Direct event name for '$viewManagerName' doesn't correspond to the naming convention," +
" expected 'topEventName'->'onEventName', got '$key'->'$regName'")
}
}
}
}
/**
* Generates map of constants that is then exposed by [UIManagerModule]. Provided list of {@param
* viewManagers} is then used to populate content of those predefined fields using
* [ViewManager.getExportedCustomBubblingEventTypeConstants] and
* [ViewManager.getExportedCustomDirectEventTypeConstants] respectively. Each view manager is in
* addition allowed to expose viewmanager-specific constants that are placed under the key that
* corresponds to the view manager's name (see [ViewManager.getName]). Constants are merged into
* the map of [UIManagerModule] base constants that is stored in [UIManagerModuleConstants].
*
* TODO(6845124): Create a test for this
*/
@JvmStatic
@JvmName("internal_createConstants")
internal fun createConstants(
viewManagers: List<ViewManager<in Nothing, in Nothing>>,
allBubblingEventTypes: MutableMap<String, Any>?,
allDirectEventTypes: MutableMap<String, Any>?
): MutableMap<String, Any> {
val constants: MutableMap<String, Any> = UIManagerModuleConstants.constants.toMutableMap()
// Generic/default event types:
// All view managers are capable of dispatching these events.
// They will be automatically registered with React Fiber.
val genericBubblingEventTypes: Map<String, Any> =
UIManagerModuleConstants.bubblingEventTypeConstants
val genericDirectEventTypes: Map<String, Any> =
UIManagerModuleConstants.directEventTypeConstants
// Cumulative event types:
// View manager specific event types are collected as views are loaded.
// This information is used later when events are dispatched.
allBubblingEventTypes?.putAll(genericBubblingEventTypes)
allDirectEventTypes?.putAll(genericDirectEventTypes)
for (viewManager in viewManagers) {
val viewManagerName = viewManager.getName()
val viewManagerConstants: MutableMap<*, *> =
createConstantsForViewManager(
viewManager, null, null, allBubblingEventTypes, allDirectEventTypes)
if (!viewManagerConstants.isEmpty()) {
constants[viewManagerName] = viewManagerConstants
}
}
constants["genericBubblingEventTypes"] = genericBubblingEventTypes
constants["genericDirectEventTypes"] = genericDirectEventTypes
return constants
}
@JvmStatic
@JvmName("internal_createConstantsForViewManager")
internal fun createConstantsForViewManager(
viewManager: ViewManager<in Nothing, in Nothing>,
defaultBubblingEvents: MutableMap<String, Any>?,
defaultDirectEvents: MutableMap<String, Any>?,
cumulativeBubblingEventTypes: MutableMap<String, Any>?,
cumulativeDirectEventTypes: MutableMap<String, Any>?
): MutableMap<String, Any> {
val viewManagerConstants: MutableMap<String, Any> = mutableMapOf()
var viewManagerBubblingEvents: MutableMap<String, Any>? =
viewManager.exportedCustomBubblingEventTypeConstants
if (viewManagerBubblingEvents != null) {
if (enableFabricRenderer() && useFabricInterop()) {
// For Fabric, events needs to be fired with a "top" prefix.
// For the sake of Fabric Interop, here we normalize events adding "top" in their
// name if the user hasn't provided it.
viewManagerBubblingEvents = normalizeEventTypes(viewManagerBubblingEvents)
}
recursiveMerge(cumulativeBubblingEventTypes, viewManagerBubblingEvents)
recursiveMerge(viewManagerBubblingEvents, defaultBubblingEvents)
viewManagerConstants.put(BUBBLING_EVENTS_KEY, viewManagerBubblingEvents)
} else if (defaultBubblingEvents != null) {
viewManagerConstants.put(BUBBLING_EVENTS_KEY, defaultBubblingEvents)
}
var viewManagerDirectEvents: MutableMap<String, Any>? =
viewManager.exportedCustomDirectEventTypeConstants
validateDirectEventNames(viewManager.getName(), viewManagerDirectEvents)
if (viewManagerDirectEvents != null) {
if (enableFabricRenderer() && useFabricInterop()) {
// For Fabric, events needs to be fired with a "top" prefix.
// For the sake of Fabric Interop, here we normalize events adding "top" in their
// name if the user hasn't provided it.
viewManagerDirectEvents = normalizeEventTypes(viewManagerDirectEvents)
}
recursiveMerge(cumulativeDirectEventTypes, viewManagerDirectEvents)
recursiveMerge(viewManagerDirectEvents, defaultDirectEvents)
viewManagerConstants.put(DIRECT_EVENTS_KEY, viewManagerDirectEvents)
} else if (defaultDirectEvents != null) {
viewManagerConstants.put(DIRECT_EVENTS_KEY, defaultDirectEvents)
}
val customViewConstants: MutableMap<String, Any>? = viewManager.exportedViewConstants
if (customViewConstants != null) {
viewManagerConstants.put("Constants", customViewConstants)
}
val viewManagerCommands: MutableMap<String, Int>? = viewManager.commandsMap
if (viewManagerCommands != null) {
viewManagerConstants.put("Commands", viewManagerCommands)
}
val viewManagerNativeProps = viewManager.nativeProps
if (!viewManagerNativeProps.isEmpty()) {
viewManagerConstants.put("NativeProps", viewManagerNativeProps)
}
return viewManagerConstants
}
@VisibleForTesting
internal fun normalizeEventTypes(
eventsToNormalize: MutableMap<String, Any>
): MutableMap<String, Any> {
var events = eventsToNormalize
val keysToNormalize: MutableSet<String> = hashSetOf()
for (key in events.keys) {
val keyString = key
if (!keyString.startsWith("top")) {
keysToNormalize.add(keyString)
}
}
// When providing one event in Kotlin, it will create a SingletonMap by default
// which will throw on trying to add new element to it
if (events !is HashMap<*, *>) {
events = HashMap(events)
}
for (oldKey in keysToNormalize) {
val value = checkNotNull(events[oldKey])
val baseKey =
if (oldKey.startsWith("on")) {
// Drop "on" prefix.
oldKey.substring(2)
} else {
// Capitalize first letter.
oldKey.substring(0, 1).uppercase(Locale.getDefault()) + oldKey.substring(1)
}
val newKey = "top$baseKey"
events.put(newKey, value)
}
return events
}
/** Merges [source] map into [dest] map recursively */
private fun recursiveMerge(dest: MutableMap<String, Any>?, source: MutableMap<String, Any>?) {
if (dest == null || source == null || source.isEmpty()) {
return
}
for ((key, sourceValue) in source) {
var destValue = dest[key]
if (destValue != null &&
(sourceValue is MutableMap<*, *>) &&
(destValue is MutableMap<*, *>)) {
// Since event maps are client based Map interface, it could be immutable
if (destValue !is HashMap<*, *>) {
destValue = HashMap(destValue)
dest.replace(key, destValue as MutableMap<*, *>)
}
@Suppress("UNCHECKED_CAST")
recursiveMerge(destValue as MutableMap<String, Any>, sourceValue as MutableMap<String, Any>)
} else {
dest.put(key, sourceValue)
}
}
}
}
@@ -19,27 +19,22 @@ class UIManagerModuleConstantsHelperTest {
ReactNativeFeatureFlagsForTests.setUp()
}
@Test
fun normalizeEventTypes_withNull_doesNothing() {
assertThat(UIManagerModuleConstantsHelper.normalizeEventTypes(null)).isNull()
}
@Test
fun normalizeEventTypes_withEmptyMap_doesNothing() {
val emptyMap = mutableMapOf<String, Any?>()
val emptyMap = mutableMapOf<String, Any>()
assertThat(UIManagerModuleConstantsHelper.normalizeEventTypes(emptyMap)).isEmpty()
}
@Test
fun normalizeEventTypes_withOnEvent_doesNormalize() {
val onClickMap = mutableMapOf("onClick" to "¯\\_(ツ)_/¯")
val onClickMap = mutableMapOf<String, Any>("onClick" to "¯\\_(ツ)_/¯")
assertThat(UIManagerModuleConstantsHelper.normalizeEventTypes(onClickMap))
.containsKeys("topClick", "onClick")
}
@Test
fun normalizeEventTypes_withTopEvent_doesNormalize() {
val onClickMap = mutableMapOf("topOnClick" to "¯\\_(ツ)_/¯")
val onClickMap = mutableMapOf<String, Any>("topOnClick" to "¯\\_(ツ)_/¯")
assertThat(UIManagerModuleConstantsHelper.normalizeEventTypes(onClickMap))
.containsKey("topOnClick")
.doesNotContainKey("onClick")
@@ -49,11 +44,11 @@ class UIManagerModuleConstantsHelperTest {
@Test
fun normalizeEventTypes_withNestedObjects_doesNotLoseThem() {
val nestedObjects =
mutableMapOf(
mutableMapOf<String, Any>(
"onColorChanged" to
mutableMapOf(
mutableMapOf<String, Any>(
"phasedRegistrationNames" to
mutableMapOf(
mutableMapOf<String, Any>(
"bubbled" to "onColorChanged",
"captured" to "onColorChangedCapture",
)))