From b3529f479c5195ab7154805cf66d8a37146ea45a Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Mon, 16 Dec 2024 21:54:23 +0200 Subject: [PATCH 01/51] temp: migrate test-app `ios` files to `ios-old`. --- detox/test/{ios => ios-old}/.xcode.env | 0 detox/test/{ios => ios-old}/Podfile | 0 .../example.xcodeproj/project.pbxproj | 0 .../xcshareddata/xcschemes/example.xcscheme | 0 .../xcshareddata/xcschemes/example_ci.xcscheme | 0 detox/test/{ios => ios-old}/example/AppDelegate.h | 0 detox/test/{ios => ios-old}/example/AppDelegate.m | 0 .../example/Base.lproj/LaunchScreen.xib | 0 .../example/CustomKeyboardViewController.h | 0 .../example/CustomKeyboardViewController.m | 0 .../Images.xcassets/AppIcon.appiconset/Contents.json | 0 detox/test/{ios => ios-old}/example/Info.plist | 0 detox/test/{ios => ios-old}/example/NativeModule.h | 0 detox/test/{ios => ios-old}/example/NativeModule.m | 0 .../{ios => ios-old}/example/PrivacyInfo.xcprivacy | 0 .../{ios => ios-old}/example/example.entitlements | 0 detox/test/{ios => ios-old}/example/main.m | 0 .../test/{ios => ios-old}/exampleUITests/Info.plist | 0 .../{ios => ios-old}/exampleUITests/exampleUITests.m | 0 detox/test/{ios => ios-old}/example_ci.entitlements | 0 .../ios/example.xcworkspace/contents.xcworkspacedata | 10 ---------- .../xcshareddata/WorkspaceSettings.xcsettings | 12 ------------ 22 files changed, 22 deletions(-) rename detox/test/{ios => ios-old}/.xcode.env (100%) rename detox/test/{ios => ios-old}/Podfile (100%) rename detox/test/{ios => ios-old}/example.xcodeproj/project.pbxproj (100%) rename detox/test/{ios => ios-old}/example.xcodeproj/xcshareddata/xcschemes/example.xcscheme (100%) rename detox/test/{ios => ios-old}/example.xcodeproj/xcshareddata/xcschemes/example_ci.xcscheme (100%) rename detox/test/{ios => ios-old}/example/AppDelegate.h (100%) rename detox/test/{ios => ios-old}/example/AppDelegate.m (100%) rename detox/test/{ios => ios-old}/example/Base.lproj/LaunchScreen.xib (100%) rename detox/test/{ios => ios-old}/example/CustomKeyboardViewController.h (100%) rename detox/test/{ios => ios-old}/example/CustomKeyboardViewController.m (100%) rename detox/test/{ios => ios-old}/example/Images.xcassets/AppIcon.appiconset/Contents.json (100%) rename detox/test/{ios => ios-old}/example/Info.plist (100%) rename detox/test/{ios => ios-old}/example/NativeModule.h (100%) rename detox/test/{ios => ios-old}/example/NativeModule.m (100%) rename detox/test/{ios => ios-old}/example/PrivacyInfo.xcprivacy (100%) rename detox/test/{ios => ios-old}/example/example.entitlements (100%) rename detox/test/{ios => ios-old}/example/main.m (100%) rename detox/test/{ios => ios-old}/exampleUITests/Info.plist (100%) rename detox/test/{ios => ios-old}/exampleUITests/exampleUITests.m (100%) rename detox/test/{ios => ios-old}/example_ci.entitlements (100%) delete mode 100644 detox/test/ios/example.xcworkspace/contents.xcworkspacedata delete mode 100644 detox/test/ios/example.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/detox/test/ios/.xcode.env b/detox/test/ios-old/.xcode.env similarity index 100% rename from detox/test/ios/.xcode.env rename to detox/test/ios-old/.xcode.env diff --git a/detox/test/ios/Podfile b/detox/test/ios-old/Podfile similarity index 100% rename from detox/test/ios/Podfile rename to detox/test/ios-old/Podfile diff --git a/detox/test/ios/example.xcodeproj/project.pbxproj b/detox/test/ios-old/example.xcodeproj/project.pbxproj similarity index 100% rename from detox/test/ios/example.xcodeproj/project.pbxproj rename to detox/test/ios-old/example.xcodeproj/project.pbxproj diff --git a/detox/test/ios/example.xcodeproj/xcshareddata/xcschemes/example.xcscheme b/detox/test/ios-old/example.xcodeproj/xcshareddata/xcschemes/example.xcscheme similarity index 100% rename from detox/test/ios/example.xcodeproj/xcshareddata/xcschemes/example.xcscheme rename to detox/test/ios-old/example.xcodeproj/xcshareddata/xcschemes/example.xcscheme diff --git a/detox/test/ios/example.xcodeproj/xcshareddata/xcschemes/example_ci.xcscheme b/detox/test/ios-old/example.xcodeproj/xcshareddata/xcschemes/example_ci.xcscheme similarity index 100% rename from detox/test/ios/example.xcodeproj/xcshareddata/xcschemes/example_ci.xcscheme rename to detox/test/ios-old/example.xcodeproj/xcshareddata/xcschemes/example_ci.xcscheme diff --git a/detox/test/ios/example/AppDelegate.h b/detox/test/ios-old/example/AppDelegate.h similarity index 100% rename from detox/test/ios/example/AppDelegate.h rename to detox/test/ios-old/example/AppDelegate.h diff --git a/detox/test/ios/example/AppDelegate.m b/detox/test/ios-old/example/AppDelegate.m similarity index 100% rename from detox/test/ios/example/AppDelegate.m rename to detox/test/ios-old/example/AppDelegate.m diff --git a/detox/test/ios/example/Base.lproj/LaunchScreen.xib b/detox/test/ios-old/example/Base.lproj/LaunchScreen.xib similarity index 100% rename from detox/test/ios/example/Base.lproj/LaunchScreen.xib rename to detox/test/ios-old/example/Base.lproj/LaunchScreen.xib diff --git a/detox/test/ios/example/CustomKeyboardViewController.h b/detox/test/ios-old/example/CustomKeyboardViewController.h similarity index 100% rename from detox/test/ios/example/CustomKeyboardViewController.h rename to detox/test/ios-old/example/CustomKeyboardViewController.h diff --git a/detox/test/ios/example/CustomKeyboardViewController.m b/detox/test/ios-old/example/CustomKeyboardViewController.m similarity index 100% rename from detox/test/ios/example/CustomKeyboardViewController.m rename to detox/test/ios-old/example/CustomKeyboardViewController.m diff --git a/detox/test/ios/example/Images.xcassets/AppIcon.appiconset/Contents.json b/detox/test/ios-old/example/Images.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from detox/test/ios/example/Images.xcassets/AppIcon.appiconset/Contents.json rename to detox/test/ios-old/example/Images.xcassets/AppIcon.appiconset/Contents.json diff --git a/detox/test/ios/example/Info.plist b/detox/test/ios-old/example/Info.plist similarity index 100% rename from detox/test/ios/example/Info.plist rename to detox/test/ios-old/example/Info.plist diff --git a/detox/test/ios/example/NativeModule.h b/detox/test/ios-old/example/NativeModule.h similarity index 100% rename from detox/test/ios/example/NativeModule.h rename to detox/test/ios-old/example/NativeModule.h diff --git a/detox/test/ios/example/NativeModule.m b/detox/test/ios-old/example/NativeModule.m similarity index 100% rename from detox/test/ios/example/NativeModule.m rename to detox/test/ios-old/example/NativeModule.m diff --git a/detox/test/ios/example/PrivacyInfo.xcprivacy b/detox/test/ios-old/example/PrivacyInfo.xcprivacy similarity index 100% rename from detox/test/ios/example/PrivacyInfo.xcprivacy rename to detox/test/ios-old/example/PrivacyInfo.xcprivacy diff --git a/detox/test/ios/example/example.entitlements b/detox/test/ios-old/example/example.entitlements similarity index 100% rename from detox/test/ios/example/example.entitlements rename to detox/test/ios-old/example/example.entitlements diff --git a/detox/test/ios/example/main.m b/detox/test/ios-old/example/main.m similarity index 100% rename from detox/test/ios/example/main.m rename to detox/test/ios-old/example/main.m diff --git a/detox/test/ios/exampleUITests/Info.plist b/detox/test/ios-old/exampleUITests/Info.plist similarity index 100% rename from detox/test/ios/exampleUITests/Info.plist rename to detox/test/ios-old/exampleUITests/Info.plist diff --git a/detox/test/ios/exampleUITests/exampleUITests.m b/detox/test/ios-old/exampleUITests/exampleUITests.m similarity index 100% rename from detox/test/ios/exampleUITests/exampleUITests.m rename to detox/test/ios-old/exampleUITests/exampleUITests.m diff --git a/detox/test/ios/example_ci.entitlements b/detox/test/ios-old/example_ci.entitlements similarity index 100% rename from detox/test/ios/example_ci.entitlements rename to detox/test/ios-old/example_ci.entitlements diff --git a/detox/test/ios/example.xcworkspace/contents.xcworkspacedata b/detox/test/ios/example.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 7f5c3aab58..0000000000 --- a/detox/test/ios/example.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/detox/test/ios/example.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/detox/test/ios/example.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index 307a9da219..0000000000 --- a/detox/test/ios/example.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,12 +0,0 @@ - - - - - BuildSystemType - Latest - DisableBuildSystemDeprecationWarning - - PreviewsEnabled - - - From 1cc1f6b9c3117ec106baa6a5d60daf952f8a98bb Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Mon, 16 Dec 2024 21:59:15 +0200 Subject: [PATCH 02/51] test(app): create new iOS project from template. --- detox/test/ios/.xcode.env | 11 + detox/test/ios/Podfile | 40 + .../ios/example.xcodeproj/project.pbxproj | 690 ++++++++++++++++++ .../xcshareddata/xcschemes/example.xcscheme | 88 +++ detox/test/ios/example/AppDelegate.h | 6 + detox/test/ios/example/AppDelegate.mm | 31 + .../AppIcon.appiconset/Contents.json | 53 ++ .../ios/example/Images.xcassets/Contents.json | 6 + detox/test/ios/example/Info.plist | 52 ++ .../test/ios/example/LaunchScreen.storyboard | 47 ++ detox/test/ios/example/PrivacyInfo.xcprivacy | 37 + detox/test/ios/example/main.m | 10 + detox/test/ios/exampleTests/Info.plist | 24 + detox/test/ios/exampleTests/exampleTests.m | 66 ++ 14 files changed, 1161 insertions(+) create mode 100644 detox/test/ios/.xcode.env create mode 100644 detox/test/ios/Podfile create mode 100644 detox/test/ios/example.xcodeproj/project.pbxproj create mode 100644 detox/test/ios/example.xcodeproj/xcshareddata/xcschemes/example.xcscheme create mode 100644 detox/test/ios/example/AppDelegate.h create mode 100644 detox/test/ios/example/AppDelegate.mm create mode 100644 detox/test/ios/example/Images.xcassets/AppIcon.appiconset/Contents.json create mode 100644 detox/test/ios/example/Images.xcassets/Contents.json create mode 100644 detox/test/ios/example/Info.plist create mode 100644 detox/test/ios/example/LaunchScreen.storyboard create mode 100644 detox/test/ios/example/PrivacyInfo.xcprivacy create mode 100644 detox/test/ios/example/main.m create mode 100644 detox/test/ios/exampleTests/Info.plist create mode 100644 detox/test/ios/exampleTests/exampleTests.m diff --git a/detox/test/ios/.xcode.env b/detox/test/ios/.xcode.env new file mode 100644 index 0000000000..3d5782c715 --- /dev/null +++ b/detox/test/ios/.xcode.env @@ -0,0 +1,11 @@ +# This `.xcode.env` file is versioned and is used to source the environment +# used when running script phases inside Xcode. +# To customize your local environment, you can create an `.xcode.env.local` +# file that is not versioned. + +# NODE_BINARY variable contains the PATH to the node executable. +# +# Customize the NODE_BINARY variable here. +# For example, to use nvm with brew, add the following line +# . "$(brew --prefix nvm)/nvm.sh" --no-use +export NODE_BINARY=$(command -v node) diff --git a/detox/test/ios/Podfile b/detox/test/ios/Podfile new file mode 100644 index 0000000000..e0a72606ec --- /dev/null +++ b/detox/test/ios/Podfile @@ -0,0 +1,40 @@ +# Resolve react_native_pods.rb with node to allow for hoisting +require Pod::Executable.execute_command('node', ['-p', + 'require.resolve( + "react-native/scripts/react_native_pods.rb", + {paths: [process.argv[1]]}, + )', __dir__]).strip + +platform :ios, min_ios_version_supported +prepare_react_native_project! + +linkage = ENV['USE_FRAMEWORKS'] +if linkage != nil + Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green + use_frameworks! :linkage => linkage.to_sym +end + +target 'example' do + config = use_native_modules! + + use_react_native!( + :path => config[:reactNativePath], + # An absolute path to your application root. + :app_path => "#{Pod::Config.instance.installation_root}/.." + ) + + target 'exampleTests' do + inherit! :complete + # Pods for testing + end + + post_install do |installer| + # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202 + react_native_post_install( + installer, + config[:reactNativePath], + :mac_catalyst_enabled => false, + # :ccache_enabled => true + ) + end +end diff --git a/detox/test/ios/example.xcodeproj/project.pbxproj b/detox/test/ios/example.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..b588372d76 --- /dev/null +++ b/detox/test/ios/example.xcodeproj/project.pbxproj @@ -0,0 +1,690 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 00E356F31AD99517003FC87E /* exampleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* exampleTests.m */; }; + 0C80B921A6F3F58F76C31292 /* libPods-example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DCACB8F33CDC322A6C60F78 /* libPods-example.a */; }; + 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; }; + 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; + 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; + 7699B88040F8A987B510C191 /* libPods-example-exampleTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 19F6CBCC0A4E27FBF8BF4A61 /* libPods-example-exampleTests.a */; }; + 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 00E356F41AD99517003FC87E /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 13B07F861A680F5B00A75B9A; + remoteInfo = example; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 00E356EE1AD99517003FC87E /* exampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = exampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 00E356F21AD99517003FC87E /* exampleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = exampleTests.m; sourceTree = ""; }; + 13B07F961A680F5B00A75B9A /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = example.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = example/AppDelegate.h; sourceTree = ""; }; + 13B07FB01A68108700A75B9A /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = example/AppDelegate.mm; sourceTree = ""; }; + 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = example/Images.xcassets; sourceTree = ""; }; + 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = example/Info.plist; sourceTree = ""; }; + 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = example/main.m; sourceTree = ""; }; + 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = PrivacyInfo.xcprivacy; path = example/PrivacyInfo.xcprivacy; sourceTree = ""; }; + 19F6CBCC0A4E27FBF8BF4A61 /* libPods-example-exampleTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-example-exampleTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 3B4392A12AC88292D35C810B /* Pods-example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example.debug.xcconfig"; path = "Target Support Files/Pods-example/Pods-example.debug.xcconfig"; sourceTree = ""; }; + 5709B34CF0A7D63546082F79 /* Pods-example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example.release.xcconfig"; path = "Target Support Files/Pods-example/Pods-example.release.xcconfig"; sourceTree = ""; }; + 5B7EB9410499542E8C5724F5 /* Pods-example-exampleTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example-exampleTests.debug.xcconfig"; path = "Target Support Files/Pods-example-exampleTests/Pods-example-exampleTests.debug.xcconfig"; sourceTree = ""; }; + 5DCACB8F33CDC322A6C60F78 /* libPods-example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-example.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = example/LaunchScreen.storyboard; sourceTree = ""; }; + 89C6BE57DB24E9ADA2F236DE /* Pods-example-exampleTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example-exampleTests.release.xcconfig"; path = "Target Support Files/Pods-example-exampleTests/Pods-example-exampleTests.release.xcconfig"; sourceTree = ""; }; + ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 00E356EB1AD99517003FC87E /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 7699B88040F8A987B510C191 /* libPods-example-exampleTests.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 13B07F8C1A680F5B00A75B9A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 0C80B921A6F3F58F76C31292 /* libPods-example.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 00E356EF1AD99517003FC87E /* exampleTests */ = { + isa = PBXGroup; + children = ( + 00E356F21AD99517003FC87E /* exampleTests.m */, + 00E356F01AD99517003FC87E /* Supporting Files */, + ); + path = exampleTests; + sourceTree = ""; + }; + 00E356F01AD99517003FC87E /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 00E356F11AD99517003FC87E /* Info.plist */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 13B07FAE1A68108700A75B9A /* example */ = { + isa = PBXGroup; + children = ( + 13B07FAF1A68108700A75B9A /* AppDelegate.h */, + 13B07FB01A68108700A75B9A /* AppDelegate.mm */, + 13B07FB51A68108700A75B9A /* Images.xcassets */, + 13B07FB61A68108700A75B9A /* Info.plist */, + 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */, + 13B07FB71A68108700A75B9A /* main.m */, + 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */, + ); + name = example; + sourceTree = ""; + }; + 2D16E6871FA4F8E400B85C8A /* Frameworks */ = { + isa = PBXGroup; + children = ( + ED297162215061F000B7C4FE /* JavaScriptCore.framework */, + 5DCACB8F33CDC322A6C60F78 /* libPods-example.a */, + 19F6CBCC0A4E27FBF8BF4A61 /* libPods-example-exampleTests.a */, + ); + name = Frameworks; + sourceTree = ""; + }; + 832341AE1AAA6A7D00B99B32 /* Libraries */ = { + isa = PBXGroup; + children = ( + ); + name = Libraries; + sourceTree = ""; + }; + 83CBB9F61A601CBA00E9B192 = { + isa = PBXGroup; + children = ( + 13B07FAE1A68108700A75B9A /* example */, + 832341AE1AAA6A7D00B99B32 /* Libraries */, + 00E356EF1AD99517003FC87E /* exampleTests */, + 83CBBA001A601CBA00E9B192 /* Products */, + 2D16E6871FA4F8E400B85C8A /* Frameworks */, + BBD78D7AC51CEA395F1C20DB /* Pods */, + ); + indentWidth = 2; + sourceTree = ""; + tabWidth = 2; + usesTabs = 0; + }; + 83CBBA001A601CBA00E9B192 /* Products */ = { + isa = PBXGroup; + children = ( + 13B07F961A680F5B00A75B9A /* example.app */, + 00E356EE1AD99517003FC87E /* exampleTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + BBD78D7AC51CEA395F1C20DB /* Pods */ = { + isa = PBXGroup; + children = ( + 3B4392A12AC88292D35C810B /* Pods-example.debug.xcconfig */, + 5709B34CF0A7D63546082F79 /* Pods-example.release.xcconfig */, + 5B7EB9410499542E8C5724F5 /* Pods-example-exampleTests.debug.xcconfig */, + 89C6BE57DB24E9ADA2F236DE /* Pods-example-exampleTests.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 00E356ED1AD99517003FC87E /* exampleTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "exampleTests" */; + buildPhases = ( + A55EABD7B0C7F3A422A6CC61 /* [CP] Check Pods Manifest.lock */, + 00E356EA1AD99517003FC87E /* Sources */, + 00E356EB1AD99517003FC87E /* Frameworks */, + 00E356EC1AD99517003FC87E /* Resources */, + C59DA0FBD6956966B86A3779 /* [CP] Embed Pods Frameworks */, + F6A41C54EA430FDDC6A6ED99 /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + 00E356F51AD99517003FC87E /* PBXTargetDependency */, + ); + name = exampleTests; + productName = exampleTests; + productReference = 00E356EE1AD99517003FC87E /* exampleTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 13B07F861A680F5B00A75B9A /* example */ = { + isa = PBXNativeTarget; + buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "example" */; + buildPhases = ( + C38B50BA6285516D6DCD4F65 /* [CP] Check Pods Manifest.lock */, + 13B07F871A680F5B00A75B9A /* Sources */, + 13B07F8C1A680F5B00A75B9A /* Frameworks */, + 13B07F8E1A680F5B00A75B9A /* Resources */, + 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, + 00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */, + E235C05ADACE081382539298 /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = example; + productName = example; + productReference = 13B07F961A680F5B00A75B9A /* example.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 83CBB9F71A601CBA00E9B192 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1210; + TargetAttributes = { + 00E356ED1AD99517003FC87E = { + CreatedOnToolsVersion = 6.2; + TestTargetID = 13B07F861A680F5B00A75B9A; + }; + 13B07F861A680F5B00A75B9A = { + LastSwiftMigration = 1120; + }; + }; + }; + buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "example" */; + compatibilityVersion = "Xcode 12.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 83CBB9F61A601CBA00E9B192; + productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 13B07F861A680F5B00A75B9A /* example */, + 00E356ED1AD99517003FC87E /* exampleTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 00E356EC1AD99517003FC87E /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 13B07F8E1A680F5B00A75B9A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */, + 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/.xcode.env.local", + "$(SRCROOT)/.xcode.env", + ); + name = "Bundle React Native code and images"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "set -e\n\nWITH_ENVIRONMENT=\"$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"$REACT_NATIVE_PATH/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n"; + }; + 00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + A55EABD7B0C7F3A422A6CC61 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-example-exampleTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + C38B50BA6285516D6DCD4F65 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-example-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + C59DA0FBD6956966B86A3779 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-example-exampleTests/Pods-example-exampleTests-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-example-exampleTests/Pods-example-exampleTests-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example-exampleTests/Pods-example-exampleTests-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + E235C05ADACE081382539298 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-resources-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + F6A41C54EA430FDDC6A6ED99 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-example-exampleTests/Pods-example-exampleTests-resources-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-example-exampleTests/Pods-example-exampleTests-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example-exampleTests/Pods-example-exampleTests-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 00E356EA1AD99517003FC87E /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 00E356F31AD99517003FC87E /* exampleTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 13B07F871A680F5B00A75B9A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */, + 13B07FC11A68108700A75B9A /* main.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 00E356F51AD99517003FC87E /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 13B07F861A680F5B00A75B9A /* example */; + targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 00E356F61AD99517003FC87E /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 5B7EB9410499542E8C5724F5 /* Pods-example-exampleTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + INFOPLIST_FILE = exampleTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + OTHER_LDFLAGS = ( + "-ObjC", + "-lc++", + "$(inherited)", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example.app/example"; + }; + name = Debug; + }; + 00E356F71AD99517003FC87E /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 89C6BE57DB24E9ADA2F236DE /* Pods-example-exampleTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + COPY_PHASE_STRIP = NO; + INFOPLIST_FILE = exampleTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + OTHER_LDFLAGS = ( + "-ObjC", + "-lc++", + "$(inherited)", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example.app/example"; + }; + name = Release; + }; + 13B07F941A680F5B00A75B9A /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3B4392A12AC88292D35C810B /* Pods-example.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = 1; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = example/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + OTHER_LDFLAGS = ( + "$(inherited)", + "-ObjC", + "-lc++", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = example; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 13B07F951A680F5B00A75B9A /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 5709B34CF0A7D63546082F79 /* Pods-example.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = 1; + INFOPLIST_FILE = example/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + OTHER_LDFLAGS = ( + "$(inherited)", + "-ObjC", + "-lc++", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = example; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; + 83CBBA201A601CBA00E9B192 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_CXX_LANGUAGE_STANDARD = "c++20"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = ""; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.1; + LD_RUNPATH_SEARCH_PATHS = ( + /usr/lib/swift, + "$(inherited)", + ); + LIBRARY_SEARCH_PATHS = ( + "\"$(SDKROOT)/usr/lib/swift\"", + "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"", + "\"$(inherited)\"", + ); + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + OTHER_CPLUSPLUSFLAGS = ( + "$(OTHER_CFLAGS)", + "-DFOLLY_NO_CONFIG", + "-DFOLLY_MOBILE=1", + "-DFOLLY_USE_LIBCPP=1", + "-DFOLLY_CFG_NO_COROUTINES=1", + "-DFOLLY_HAVE_CLOCK_GETTIME=1", + ); + SDKROOT = iphoneos; + }; + name = Debug; + }; + 83CBBA211A601CBA00E9B192 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_CXX_LANGUAGE_STANDARD = "c++20"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = ""; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.1; + LD_RUNPATH_SEARCH_PATHS = ( + /usr/lib/swift, + "$(inherited)", + ); + LIBRARY_SEARCH_PATHS = ( + "\"$(SDKROOT)/usr/lib/swift\"", + "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"", + "\"$(inherited)\"", + ); + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_CPLUSPLUSFLAGS = ( + "$(OTHER_CFLAGS)", + "-DFOLLY_NO_CONFIG", + "-DFOLLY_MOBILE=1", + "-DFOLLY_USE_LIBCPP=1", + "-DFOLLY_CFG_NO_COROUTINES=1", + "-DFOLLY_HAVE_CLOCK_GETTIME=1", + ); + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "exampleTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 00E356F61AD99517003FC87E /* Debug */, + 00E356F71AD99517003FC87E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "example" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 13B07F941A680F5B00A75B9A /* Debug */, + 13B07F951A680F5B00A75B9A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "example" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 83CBBA201A601CBA00E9B192 /* Debug */, + 83CBBA211A601CBA00E9B192 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */; +} diff --git a/detox/test/ios/example.xcodeproj/xcshareddata/xcschemes/example.xcscheme b/detox/test/ios/example.xcodeproj/xcshareddata/xcschemes/example.xcscheme new file mode 100644 index 0000000000..fef9df78ab --- /dev/null +++ b/detox/test/ios/example.xcodeproj/xcshareddata/xcschemes/example.xcscheme @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/detox/test/ios/example/AppDelegate.h b/detox/test/ios/example/AppDelegate.h new file mode 100644 index 0000000000..5d2808256c --- /dev/null +++ b/detox/test/ios/example/AppDelegate.h @@ -0,0 +1,6 @@ +#import +#import + +@interface AppDelegate : RCTAppDelegate + +@end diff --git a/detox/test/ios/example/AppDelegate.mm b/detox/test/ios/example/AppDelegate.mm new file mode 100644 index 0000000000..ba286dc79e --- /dev/null +++ b/detox/test/ios/example/AppDelegate.mm @@ -0,0 +1,31 @@ +#import "AppDelegate.h" + +#import + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ + self.moduleName = @"example"; + // You can add your custom initial props in the dictionary below. + // They will be passed down to the ViewController used by React Native. + self.initialProps = @{}; + + return [super application:application didFinishLaunchingWithOptions:launchOptions]; +} + +- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge +{ + return [self bundleURL]; +} + +- (NSURL *)bundleURL +{ +#if DEBUG + return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; +#else + return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; +#endif +} + +@end diff --git a/detox/test/ios/example/Images.xcassets/AppIcon.appiconset/Contents.json b/detox/test/ios/example/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000000..81213230de --- /dev/null +++ b/detox/test/ios/example/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,53 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/detox/test/ios/example/Images.xcassets/Contents.json b/detox/test/ios/example/Images.xcassets/Contents.json new file mode 100644 index 0000000000..2d92bd53fd --- /dev/null +++ b/detox/test/ios/example/Images.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/detox/test/ios/example/Info.plist b/detox/test/ios/example/Info.plist new file mode 100644 index 0000000000..3241dd140b --- /dev/null +++ b/detox/test/ios/example/Info.plist @@ -0,0 +1,52 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + example + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(MARKETING_VERSION) + CFBundleSignature + ???? + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + LSRequiresIPhoneOS + + NSAppTransportSecurity + + + NSAllowsArbitraryLoads + + NSAllowsLocalNetworking + + + NSLocationWhenInUseUsageDescription + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + arm64 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/detox/test/ios/example/LaunchScreen.storyboard b/detox/test/ios/example/LaunchScreen.storyboard new file mode 100644 index 0000000000..a2139fff8b --- /dev/null +++ b/detox/test/ios/example/LaunchScreen.storyboard @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/detox/test/ios/example/PrivacyInfo.xcprivacy b/detox/test/ios/example/PrivacyInfo.xcprivacy new file mode 100644 index 0000000000..41b8317f06 --- /dev/null +++ b/detox/test/ios/example/PrivacyInfo.xcprivacy @@ -0,0 +1,37 @@ + + + + + NSPrivacyAccessedAPITypes + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryFileTimestamp + NSPrivacyAccessedAPITypeReasons + + C617.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryUserDefaults + NSPrivacyAccessedAPITypeReasons + + CA92.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategorySystemBootTime + NSPrivacyAccessedAPITypeReasons + + 35F9.1 + + + + NSPrivacyCollectedDataTypes + + NSPrivacyTracking + + + diff --git a/detox/test/ios/example/main.m b/detox/test/ios/example/main.m new file mode 100644 index 0000000000..d645c7246c --- /dev/null +++ b/detox/test/ios/example/main.m @@ -0,0 +1,10 @@ +#import + +#import "AppDelegate.h" + +int main(int argc, char *argv[]) +{ + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/detox/test/ios/exampleTests/Info.plist b/detox/test/ios/exampleTests/Info.plist new file mode 100644 index 0000000000..ba72822e87 --- /dev/null +++ b/detox/test/ios/exampleTests/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/detox/test/ios/exampleTests/exampleTests.m b/detox/test/ios/exampleTests/exampleTests.m new file mode 100644 index 0000000000..6f944301f0 --- /dev/null +++ b/detox/test/ios/exampleTests/exampleTests.m @@ -0,0 +1,66 @@ +#import +#import + +#import +#import + +#define TIMEOUT_SECONDS 600 +#define TEXT_TO_LOOK_FOR @"Welcome to React" + +@interface exampleTests : XCTestCase + +@end + +@implementation exampleTests + +- (BOOL)findSubviewInView:(UIView *)view matching:(BOOL (^)(UIView *view))test +{ + if (test(view)) { + return YES; + } + for (UIView *subview in [view subviews]) { + if ([self findSubviewInView:subview matching:test]) { + return YES; + } + } + return NO; +} + +- (void)testRendersWelcomeScreen +{ + UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController]; + NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; + BOOL foundElement = NO; + + __block NSString *redboxError = nil; +#ifdef DEBUG + RCTSetLogFunction( + ^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { + if (level >= RCTLogLevelError) { + redboxError = message; + } + }); +#endif + + while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { + [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; + [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; + + foundElement = [self findSubviewInView:vc.view + matching:^BOOL(UIView *view) { + if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { + return YES; + } + return NO; + }]; + } + +#ifdef DEBUG + RCTSetLogFunction(RCTDefaultLogFunction); +#endif + + XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); + XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); +} + +@end From 09eebd85e54e05645e2f0fd9eb544ffd4e8562c4 Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Mon, 16 Dec 2024 22:04:59 +0200 Subject: [PATCH 03/51] test(ios): remove example native tests. --- .../ios/example.xcodeproj/project.pbxproj | 216 +----------------- detox/test/ios/exampleTests/Info.plist | 24 -- detox/test/ios/exampleTests/exampleTests.m | 66 ------ 3 files changed, 9 insertions(+), 297 deletions(-) delete mode 100644 detox/test/ios/exampleTests/Info.plist delete mode 100644 detox/test/ios/exampleTests/exampleTests.m diff --git a/detox/test/ios/example.xcodeproj/project.pbxproj b/detox/test/ios/example.xcodeproj/project.pbxproj index b588372d76..be0da5db37 100644 --- a/detox/test/ios/example.xcodeproj/project.pbxproj +++ b/detox/test/ios/example.xcodeproj/project.pbxproj @@ -7,29 +7,15 @@ objects = { /* Begin PBXBuildFile section */ - 00E356F31AD99517003FC87E /* exampleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* exampleTests.m */; }; 0C80B921A6F3F58F76C31292 /* libPods-example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DCACB8F33CDC322A6C60F78 /* libPods-example.a */; }; 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; - 7699B88040F8A987B510C191 /* libPods-example-exampleTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 19F6CBCC0A4E27FBF8BF4A61 /* libPods-example-exampleTests.a */; }; 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; + 90CCD0BA1322566EB1285DC2 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */; }; /* End PBXBuildFile section */ -/* Begin PBXContainerItemProxy section */ - 00E356F41AD99517003FC87E /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 13B07F861A680F5B00A75B9A; - remoteInfo = example; - }; -/* End PBXContainerItemProxy section */ - /* Begin PBXFileReference section */ - 00E356EE1AD99517003FC87E /* exampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = exampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 00E356F21AD99517003FC87E /* exampleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = exampleTests.m; sourceTree = ""; }; 13B07F961A680F5B00A75B9A /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = example/AppDelegate.h; sourceTree = ""; }; 13B07FB01A68108700A75B9A /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = example/AppDelegate.mm; sourceTree = ""; }; @@ -48,14 +34,6 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - 00E356EB1AD99517003FC87E /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 7699B88040F8A987B510C191 /* libPods-example-exampleTests.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 13B07F8C1A680F5B00A75B9A /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -67,23 +45,6 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 00E356EF1AD99517003FC87E /* exampleTests */ = { - isa = PBXGroup; - children = ( - 00E356F21AD99517003FC87E /* exampleTests.m */, - 00E356F01AD99517003FC87E /* Supporting Files */, - ); - path = exampleTests; - sourceTree = ""; - }; - 00E356F01AD99517003FC87E /* Supporting Files */ = { - isa = PBXGroup; - children = ( - 00E356F11AD99517003FC87E /* Info.plist */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; 13B07FAE1A68108700A75B9A /* example */ = { isa = PBXGroup; children = ( @@ -120,7 +81,6 @@ children = ( 13B07FAE1A68108700A75B9A /* example */, 832341AE1AAA6A7D00B99B32 /* Libraries */, - 00E356EF1AD99517003FC87E /* exampleTests */, 83CBBA001A601CBA00E9B192 /* Products */, 2D16E6871FA4F8E400B85C8A /* Frameworks */, BBD78D7AC51CEA395F1C20DB /* Pods */, @@ -134,7 +94,6 @@ isa = PBXGroup; children = ( 13B07F961A680F5B00A75B9A /* example.app */, - 00E356EE1AD99517003FC87E /* exampleTests.xctest */, ); name = Products; sourceTree = ""; @@ -153,27 +112,6 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ - 00E356ED1AD99517003FC87E /* exampleTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "exampleTests" */; - buildPhases = ( - A55EABD7B0C7F3A422A6CC61 /* [CP] Check Pods Manifest.lock */, - 00E356EA1AD99517003FC87E /* Sources */, - 00E356EB1AD99517003FC87E /* Frameworks */, - 00E356EC1AD99517003FC87E /* Resources */, - C59DA0FBD6956966B86A3779 /* [CP] Embed Pods Frameworks */, - F6A41C54EA430FDDC6A6ED99 /* [CP] Copy Pods Resources */, - ); - buildRules = ( - ); - dependencies = ( - 00E356F51AD99517003FC87E /* PBXTargetDependency */, - ); - name = exampleTests; - productName = exampleTests; - productReference = 00E356EE1AD99517003FC87E /* exampleTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; 13B07F861A680F5B00A75B9A /* example */ = { isa = PBXNativeTarget; buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "example" */; @@ -203,10 +141,6 @@ attributes = { LastUpgradeCheck = 1210; TargetAttributes = { - 00E356ED1AD99517003FC87E = { - CreatedOnToolsVersion = 6.2; - TestTargetID = 13B07F861A680F5B00A75B9A; - }; 13B07F861A680F5B00A75B9A = { LastSwiftMigration = 1120; }; @@ -226,25 +160,18 @@ projectRoot = ""; targets = ( 13B07F861A680F5B00A75B9A /* example */, - 00E356ED1AD99517003FC87E /* exampleTests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ - 00E356EC1AD99517003FC87E /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; 13B07F8E1A680F5B00A75B9A /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */, 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, + 90CCD0BA1322566EB1285DC2 /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -284,28 +211,6 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - A55EABD7B0C7F3A422A6CC61 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-example-exampleTests-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; C38B50BA6285516D6DCD4F65 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -328,23 +233,6 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - C59DA0FBD6956966B86A3779 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-example-exampleTests/Pods-example-exampleTests-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-example-exampleTests/Pods-example-exampleTests-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example-exampleTests/Pods-example-exampleTests-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; E235C05ADACE081382539298 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -362,34 +250,9 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-resources.sh\"\n"; showEnvVarsInLog = 0; }; - F6A41C54EA430FDDC6A6ED99 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-example-exampleTests/Pods-example-exampleTests-resources-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Copy Pods Resources"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-example-exampleTests/Pods-example-exampleTests-resources-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example-exampleTests/Pods-example-exampleTests-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ - 00E356EA1AD99517003FC87E /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 00E356F31AD99517003FC87E /* exampleTests.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 13B07F871A680F5B00A75B9A /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -401,66 +264,7 @@ }; /* End PBXSourcesBuildPhase section */ -/* Begin PBXTargetDependency section */ - 00E356F51AD99517003FC87E /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 13B07F861A680F5B00A75B9A /* example */; - targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - /* Begin XCBuildConfiguration section */ - 00E356F61AD99517003FC87E /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 5B7EB9410499542E8C5724F5 /* Pods-example-exampleTests.debug.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - INFOPLIST_FILE = exampleTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 15.1; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - OTHER_LDFLAGS = ( - "-ObjC", - "-lc++", - "$(inherited)", - ); - PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example.app/example"; - }; - name = Debug; - }; - 00E356F71AD99517003FC87E /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 89C6BE57DB24E9ADA2F236DE /* Pods-example-exampleTests.release.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - COPY_PHASE_STRIP = NO; - INFOPLIST_FILE = exampleTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 15.1; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - OTHER_LDFLAGS = ( - "-ObjC", - "-lc++", - "$(inherited)", - ); - PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example.app/example"; - }; - name = Release; - }; 13B07F941A680F5B00A75B9A /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 3B4392A12AC88292D35C810B /* Pods-example.debug.xcconfig */; @@ -584,7 +388,11 @@ "-DFOLLY_CFG_NO_COROUTINES=1", "-DFOLLY_HAVE_CLOCK_GETTIME=1", ); + OTHER_LDFLAGS = "$(inherited) "; + REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG"; + USE_HERMES = true; }; name = Debug; }; @@ -649,7 +457,10 @@ "-DFOLLY_CFG_NO_COROUTINES=1", "-DFOLLY_HAVE_CLOCK_GETTIME=1", ); + OTHER_LDFLAGS = "$(inherited) "; + REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; + USE_HERMES = true; VALIDATE_PRODUCT = YES; }; name = Release; @@ -657,15 +468,6 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "exampleTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 00E356F61AD99517003FC87E /* Debug */, - 00E356F71AD99517003FC87E /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "example" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/detox/test/ios/exampleTests/Info.plist b/detox/test/ios/exampleTests/Info.plist deleted file mode 100644 index ba72822e87..0000000000 --- a/detox/test/ios/exampleTests/Info.plist +++ /dev/null @@ -1,24 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - BNDL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - - diff --git a/detox/test/ios/exampleTests/exampleTests.m b/detox/test/ios/exampleTests/exampleTests.m deleted file mode 100644 index 6f944301f0..0000000000 --- a/detox/test/ios/exampleTests/exampleTests.m +++ /dev/null @@ -1,66 +0,0 @@ -#import -#import - -#import -#import - -#define TIMEOUT_SECONDS 600 -#define TEXT_TO_LOOK_FOR @"Welcome to React" - -@interface exampleTests : XCTestCase - -@end - -@implementation exampleTests - -- (BOOL)findSubviewInView:(UIView *)view matching:(BOOL (^)(UIView *view))test -{ - if (test(view)) { - return YES; - } - for (UIView *subview in [view subviews]) { - if ([self findSubviewInView:subview matching:test]) { - return YES; - } - } - return NO; -} - -- (void)testRendersWelcomeScreen -{ - UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController]; - NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; - BOOL foundElement = NO; - - __block NSString *redboxError = nil; -#ifdef DEBUG - RCTSetLogFunction( - ^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { - if (level >= RCTLogLevelError) { - redboxError = message; - } - }); -#endif - - while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { - [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; - [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; - - foundElement = [self findSubviewInView:vc.view - matching:^BOOL(UIView *view) { - if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { - return YES; - } - return NO; - }]; - } - -#ifdef DEBUG - RCTSetLogFunction(RCTDefaultLogFunction); -#endif - - XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); - XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); -} - -@end From 687ff849dce028a4f0f49f90d38f9481d3b35657 Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Mon, 16 Dec 2024 22:08:31 +0200 Subject: [PATCH 04/51] chore(ios): update git to ignore generated files. --- .gitignore | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 3b3be7903f..04bcacd415 100644 --- a/.gitignore +++ b/.gitignore @@ -81,8 +81,8 @@ fabric.properties # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore ## Build generated -ios/build/ -ios/DerivedData/ +*/*/ios/build/ +*/*/ios/DerivedData/ ## Various settings *.pbxuser @@ -93,12 +93,12 @@ ios/DerivedData/ !default.mode2v3 *.perspectivev3 !default.perspectivev3 -ios/xcuserdata/ +*/*/ios/xcuserdata/ ## Other *.moved-aside *.xcuserstate -ios/.xcode.env.local +*/*/ios/.xcode.env.local ## Obj-C/Swift specific *.hmap From b6df6c9fc4e639616097fd913f432a727661b49c Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Mon, 16 Dec 2024 22:11:39 +0200 Subject: [PATCH 05/51] test(ios): update app's bundle-id and build commands. --- detox/test/e2e/detox.config.js | 4 ++-- detox/test/ios/example.xcodeproj/project.pbxproj | 4 ++-- detox/test/ios/example/Info.plist | 1 - 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/detox/test/e2e/detox.config.js b/detox/test/e2e/detox.config.js index d2c8fde741..591c4e3fdb 100644 --- a/detox/test/e2e/detox.config.js +++ b/detox/test/e2e/detox.config.js @@ -53,7 +53,7 @@ const config = { type: 'ios.app', name: 'example', binaryPath: 'ios/build/Build/Products/Debug-iphonesimulator/example.app', - build: 'set -o pipefail && xcodebuild -workspace ios/example.xcworkspace -UseNewBuildSystem=YES -scheme example_ci -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build -quiet', + build: 'set -o pipefail && xcodebuild -workspace ios/example.xcworkspace -scheme example -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build -quiet', start: 'react-native start', bundleId: 'com.wix.detox-example', }, @@ -62,7 +62,7 @@ const config = { type: 'ios.app', name: 'example', binaryPath: 'ios/build/Build/Products/Release-iphonesimulator/example.app', - build: 'set -o pipefail && export CODE_SIGNING_REQUIRED=NO && export RCT_NO_LAUNCH_PACKAGER=true && xcodebuild -workspace ios/example.xcworkspace -UseNewBuildSystem=YES -scheme example_ci -configuration Release -sdk iphonesimulator -derivedDataPath ios/build -quiet', + build: 'set -o pipefail && export CODE_SIGNING_REQUIRED=NO && export RCT_NO_LAUNCH_PACKAGER=true && xcodebuild -workspace ios/example.xcworkspace -scheme example -configuration Release -sdk iphonesimulator -derivedDataPath ios/build -quiet', }, 'android.debug': { diff --git a/detox/test/ios/example.xcodeproj/project.pbxproj b/detox/test/ios/example.xcodeproj/project.pbxproj index be0da5db37..0431ea1c42 100644 --- a/detox/test/ios/example.xcodeproj/project.pbxproj +++ b/detox/test/ios/example.xcodeproj/project.pbxproj @@ -285,7 +285,7 @@ "-ObjC", "-lc++", ); - PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_BUNDLE_IDENTIFIER = "com.wix.detox-example"; PRODUCT_NAME = example; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -312,7 +312,7 @@ "-ObjC", "-lc++", ); - PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_BUNDLE_IDENTIFIER = "com.wix.detox-example"; PRODUCT_NAME = example; SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; diff --git a/detox/test/ios/example/Info.plist b/detox/test/ios/example/Info.plist index 3241dd140b..a496c6c863 100644 --- a/detox/test/ios/example/Info.plist +++ b/detox/test/ios/example/Info.plist @@ -26,7 +26,6 @@ NSAppTransportSecurity - NSAllowsArbitraryLoads NSAllowsLocalNetworking From 7cad1c8705029bb1326e2b5751128a44627f1914 Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Mon, 16 Dec 2024 22:17:05 +0200 Subject: [PATCH 06/51] test(ios): link Detox framework with project. --- .../ios/example.xcodeproj/project.pbxproj | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/detox/test/ios/example.xcodeproj/project.pbxproj b/detox/test/ios/example.xcodeproj/project.pbxproj index 0431ea1c42..40f965f656 100644 --- a/detox/test/ios/example.xcodeproj/project.pbxproj +++ b/detox/test/ios/example.xcodeproj/project.pbxproj @@ -11,10 +11,28 @@ 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; + 609DDB892D10C21800028574 /* Detox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 609DDB852D10C20800028574 /* Detox.framework */; }; 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; 90CCD0BA1322566EB1285DC2 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */; }; /* End PBXBuildFile section */ +/* Begin PBXContainerItemProxy section */ + 609DDB842D10C20800028574 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 609DDB772D10C20700028574 /* Detox.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 394767971DBF985400D72256; + remoteInfo = Detox; + }; + 609DDB862D10C20800028574 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 609DDB772D10C20700028574 /* Detox.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 3928EFA51E47404900C19B6E; + remoteInfo = DetoxUserNotificationTests; + }; +/* End PBXContainerItemProxy section */ + /* Begin PBXFileReference section */ 13B07F961A680F5B00A75B9A /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = example/AppDelegate.h; sourceTree = ""; }; @@ -28,6 +46,7 @@ 5709B34CF0A7D63546082F79 /* Pods-example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example.release.xcconfig"; path = "Target Support Files/Pods-example/Pods-example.release.xcconfig"; sourceTree = ""; }; 5B7EB9410499542E8C5724F5 /* Pods-example-exampleTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example-exampleTests.debug.xcconfig"; path = "Target Support Files/Pods-example-exampleTests/Pods-example-exampleTests.debug.xcconfig"; sourceTree = ""; }; 5DCACB8F33CDC322A6C60F78 /* libPods-example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-example.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 609DDB772D10C20700028574 /* Detox.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Detox.xcodeproj; path = /Users/asafk/Development/Detox/detox/ios/Detox.xcodeproj; sourceTree = ""; }; 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = example/LaunchScreen.storyboard; sourceTree = ""; }; 89C6BE57DB24E9ADA2F236DE /* Pods-example-exampleTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example-exampleTests.release.xcconfig"; path = "Target Support Files/Pods-example-exampleTests/Pods-example-exampleTests.release.xcconfig"; sourceTree = ""; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; @@ -38,6 +57,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 609DDB892D10C21800028574 /* Detox.framework in Frameworks */, 0C80B921A6F3F58F76C31292 /* libPods-example.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -69,6 +89,15 @@ name = Frameworks; sourceTree = ""; }; + 609DDB802D10C20800028574 /* Products */ = { + isa = PBXGroup; + children = ( + 609DDB852D10C20800028574 /* Detox.framework */, + 609DDB872D10C20800028574 /* DetoxUserNotificationTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; 832341AE1AAA6A7D00B99B32 /* Libraries */ = { isa = PBXGroup; children = ( @@ -79,6 +108,7 @@ 83CBB9F61A601CBA00E9B192 = { isa = PBXGroup; children = ( + 609DDB772D10C20700028574 /* Detox.xcodeproj */, 13B07FAE1A68108700A75B9A /* example */, 832341AE1AAA6A7D00B99B32 /* Libraries */, 83CBBA001A601CBA00E9B192 /* Products */, @@ -157,6 +187,12 @@ mainGroup = 83CBB9F61A601CBA00E9B192; productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; projectDirPath = ""; + projectReferences = ( + { + ProductGroup = 609DDB802D10C20800028574 /* Products */; + ProjectRef = 609DDB772D10C20700028574 /* Detox.xcodeproj */; + }, + ); projectRoot = ""; targets = ( 13B07F861A680F5B00A75B9A /* example */, @@ -164,6 +200,23 @@ }; /* End PBXProject section */ +/* Begin PBXReferenceProxy section */ + 609DDB852D10C20800028574 /* Detox.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = Detox.framework; + remoteRef = 609DDB842D10C20800028574 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 609DDB872D10C20800028574 /* DetoxUserNotificationTests.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = DetoxUserNotificationTests.xctest; + remoteRef = 609DDB862D10C20800028574 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXReferenceProxy section */ + /* Begin PBXResourcesBuildPhase section */ 13B07F8E1A680F5B00A75B9A /* Resources */ = { isa = PBXResourcesBuildPhase; From 35cdb2d27329f8a043045b98e1746abf82ee4f72 Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Mon, 16 Dec 2024 22:27:11 +0200 Subject: [PATCH 07/51] test(ios): update Podfile with dependencies. --- detox/test/ios/Podfile | 58 +++++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 12 deletions(-) diff --git a/detox/test/ios/Podfile b/detox/test/ios/Podfile index e0a72606ec..8f849b7800 100644 --- a/detox/test/ios/Podfile +++ b/detox/test/ios/Podfile @@ -1,13 +1,51 @@ -# Resolve react_native_pods.rb with node to allow for hoisting -require Pod::Executable.execute_command('node', ['-p', - 'require.resolve( - "react-native/scripts/react_native_pods.rb", - {paths: [process.argv[1]]}, - )', __dir__]).strip +# Set RCT_NEW_ARCH_ENABLED to '0' if not defined +ENV['RCT_NEW_ARCH_ENABLED'] = '0' unless ENV['RCT_NEW_ARCH_ENABLED'] + +if ENV["REACT_NATIVE_VERSION"] && ENV["REACT_NATIVE_VERSION"].match(/0.71.*/) + require_relative '../node_modules/react-native/scripts/react_native_pods' + require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' + require_relative '../node_modules/react-native-permissions/scripts/setup' +else + # Resolve react_native_pods.rb with node to allow for hoisting + require Pod::Executable.execute_command('node', ['-p', + 'require.resolve( + "react-native/scripts/react_native_pods.rb", + {paths: [process.argv[1]]}, + )', __dir__]).strip + + require Pod::Executable.execute_command('node', ['-p', + 'require.resolve( + "react-native-permissions/scripts/setup.rb", + {paths: [process.argv[1]]}, + )', __dir__]).strip +end platform :ios, min_ios_version_supported prepare_react_native_project! +# Configure permissions +setup_permissions([ + 'AppTrackingTransparency', + 'Bluetooth', + 'Calendars', + 'Camera', + 'Contacts', + 'FaceID', + 'LocationAccuracy', + 'LocationAlways', + 'LocationWhenInUse', + 'MediaLibrary', + 'Microphone', + 'Motion', + 'Notifications', + 'PhotoLibrary', + 'PhotoLibraryAddOnly', + 'Reminders', + 'Siri', + 'SpeechRecognition', + 'StoreKit', +]) + linkage = ENV['USE_FRAMEWORKS'] if linkage != nil Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green @@ -19,17 +57,13 @@ target 'example' do use_react_native!( :path => config[:reactNativePath], - # An absolute path to your application root. :app_path => "#{Pod::Config.instance.installation_root}/.." ) - target 'exampleTests' do - inherit! :complete - # Pods for testing - end + # Add React Native Slider + pod 'react-native-slider', :path => '../node_modules/@react-native-community/slider' post_install do |installer| - # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202 react_native_post_install( installer, config[:reactNativePath], From b8a4b2f6426e9649dbbaef2ce7bb70405cb4c4e4 Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Mon, 16 Dec 2024 22:51:05 +0200 Subject: [PATCH 08/51] test(ios): update `AppDelegate.mm` with deps. --- detox/test/ios/example/AppDelegate.mm | 95 ++++++++++++++++++++++++--- 1 file changed, 86 insertions(+), 9 deletions(-) diff --git a/detox/test/ios/example/AppDelegate.mm b/detox/test/ios/example/AppDelegate.mm index ba286dc79e..fbb3aee217 100644 --- a/detox/test/ios/example/AppDelegate.mm +++ b/detox/test/ios/example/AppDelegate.mm @@ -1,30 +1,107 @@ #import "AppDelegate.h" - #import +#import +#import +#import + +// Shake event handling +@interface ShakeEventEmitter : RCTEventEmitter +@end + +static ShakeEventEmitter* _shakeInstance; + +@implementation ShakeEventEmitter +RCT_EXPORT_MODULE(); + +- (instancetype)init { + self = [super init]; + _shakeInstance = self; + return self; +} + +- (NSArray *)supportedEvents { + return @[@"ShakeEvent"]; +} + +- (void)sendShakeEvent { + [self sendEventWithName:@"ShakeEvent" body:nil]; +} + ++ (BOOL)requiresMainQueueSetup { + return YES; +} +@end + +// Custom ViewController for shake detection +@interface ShakeDetectViewController : UIViewController +@property (nonatomic, weak) RCTBridge* bridge; +@end + +@implementation ShakeDetectViewController +- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event { + if(event.subtype == UIEventSubtypeMotionShake) { + [_shakeInstance sendShakeEvent]; + } +} +@end + +@interface AppDelegate () +@end @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - self.moduleName = @"example"; - // You can add your custom initial props in the dictionary below. - // They will be passed down to the ViewController used by React Native. - self.initialProps = @{}; + self.moduleName = @"example"; + self.initialProps = @{}; + + // Setup notification delegate + [UNUserNotificationCenter currentNotificationCenter].delegate = self; - return [super application:application didFinishLaunchingWithOptions:launchOptions]; + return [super application:application didFinishLaunchingWithOptions:launchOptions]; +} + +// URL scheme handling +- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary *)options +{ + return [RCTLinkingManager application:application openURL:url options:options]; +} + +// Universal links and Spotlight search handling +- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void(^)(NSArray> *))restorationHandler +{ + return [RCTLinkingManager application:application continueUserActivity:userActivity restorationHandler:restorationHandler]; +} + +// Push notification handling +- (void)userNotificationCenter:(UNUserNotificationCenter *)center + willPresentNotification:(UNNotification *)notification + withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler +{ + completionHandler(UNNotificationPresentationOptionList | + UNNotificationPresentationOptionBanner | + UNNotificationPresentationOptionBadge | + UNNotificationPresentationOptionSound); +} + +- (void)userNotificationCenter:(UNUserNotificationCenter *)center +didReceiveNotificationResponse:(UNNotificationResponse *)response + withCompletionHandler:(void (^)(void))completionHandler +{ + completionHandler(); } - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge { - return [self bundleURL]; + return [self bundleURL]; } - (NSURL *)bundleURL { #if DEBUG - return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; + return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; #else - return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; + return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; #endif } From 336d83d2da18bb830cc52a959bea2dfc8920bced Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Mon, 16 Dec 2024 23:37:04 +0200 Subject: [PATCH 09/51] test(ios): add `NativeModule`. --- detox/test/ios/ReactModules/NativeModule.h | 6 + detox/test/ios/ReactModules/NativeModule.mm | 119 ++++++++++++++++++ .../ios/example.xcodeproj/project.pbxproj | 14 +++ 3 files changed, 139 insertions(+) create mode 100644 detox/test/ios/ReactModules/NativeModule.h create mode 100644 detox/test/ios/ReactModules/NativeModule.mm diff --git a/detox/test/ios/ReactModules/NativeModule.h b/detox/test/ios/ReactModules/NativeModule.h new file mode 100644 index 0000000000..7bde08a0f8 --- /dev/null +++ b/detox/test/ios/ReactModules/NativeModule.h @@ -0,0 +1,6 @@ +#import +#import + +@interface NativeModule : NSObject + +@end diff --git a/detox/test/ios/ReactModules/NativeModule.mm b/detox/test/ios/ReactModules/NativeModule.mm new file mode 100644 index 0000000000..08f07b45fc --- /dev/null +++ b/detox/test/ios/ReactModules/NativeModule.mm @@ -0,0 +1,119 @@ +#import "NativeModule.h" +#import +#import + +static int CALL_COUNTER = 0; + +@implementation NativeModule + +RCT_EXPORT_MODULE(); + +RCT_EXPORT_METHOD(echoWithoutResponse:(NSString *)str) +{ + // NSLog(@"NativeModule echoWithoutResponse called"); + CALL_COUNTER++; +} + +RCT_EXPORT_METHOD(echoWithResponse:(NSString *)str + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) +{ + CALL_COUNTER++; + resolve(str); + // NSLog(@"NativeModule echoWithResponse called"); +} + +RCT_EXPORT_METHOD(nativeSetTimeout:(NSTimeInterval)delay block:(RCTResponseSenderBlock)block) +{ + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + dispatch_async(dispatch_get_main_queue(), ^{ + block(@[]); + }); + }); +} + +RCT_EXPORT_METHOD(switchToNativeRoot) +{ + dispatch_async(dispatch_get_main_queue(), ^{ + UIViewController* newRoot = [UIViewController new]; + newRoot.view.backgroundColor = [UIColor whiteColor]; + UILabel* label = [UILabel new]; + label.text = @"this is a new native root"; + [label sizeToFit]; + [[newRoot view] addSubview:label]; + label.center = newRoot.view.center; + + id delegate = [[UIApplication sharedApplication] delegate]; + [[delegate window]setRootViewController:newRoot]; + [[delegate window] makeKeyAndVisible]; + }); +} + +RCT_EXPORT_METHOD(switchToMultipleReactRoots) +{ + dispatch_async(dispatch_get_main_queue(), ^{ + id delegate = [[UIApplication sharedApplication] delegate]; + RCTBridge* bridge = ((RCTRootView*)delegate.window.rootViewController.view).bridge; + + UIViewController* newRoot = [UIViewController new]; + newRoot.view = [[RCTRootView alloc]initWithBridge:bridge moduleName:@"example" initialProperties:nil]; + newRoot.tabBarItem.title = @"1"; + + + UIViewController* newRoot2 = [UIViewController new]; + newRoot2.view = [[RCTRootView alloc]initWithBridge:bridge moduleName:@"example" initialProperties:nil]; + newRoot2.tabBarItem.title = @"2"; + + UIViewController* newRoot3 = [UIViewController new]; + newRoot3.view = [[RCTRootView alloc]initWithBridge:bridge moduleName:@"example" initialProperties:nil]; + newRoot3.tabBarItem.title = @"3"; + + UIViewController* newRoot4 = [UIViewController new]; + newRoot4.view = [[RCTRootView alloc]initWithBridge:bridge moduleName:@"example" initialProperties:nil]; + newRoot4.tabBarItem.title = @"4"; + + UITabBarController* tbc = [UITabBarController new]; + tbc.viewControllers = @[newRoot, newRoot2, newRoot3, newRoot4]; + + [[delegate window]setRootViewController:tbc]; + [[delegate window] makeKeyAndVisible]; + }); +} + +RCT_EXPORT_METHOD(sendNotification:(NSString*)notification name:(NSString*)name) +{ + dispatch_async(dispatch_get_main_queue(), ^{ + [NSNotificationCenter.defaultCenter postNotificationName:notification object:nil userInfo:@{@"name": name}]; + }); +} + +RCT_EXPORT_METHOD(presentOverlayWindow) { + static UIWindow *overlayWindow; + + dispatch_async(dispatch_get_main_queue(), ^{ + CGRect screenBounds = UIScreen.mainScreen.bounds; + overlayWindow = [[UIWindow alloc] initWithFrame:screenBounds]; + overlayWindow.accessibilityIdentifier = @"OverlayWindow"; + + [overlayWindow setWindowLevel:UIWindowLevelStatusBar]; + [overlayWindow setHidden:NO]; + + [overlayWindow makeKeyAndVisible]; + }); +} + +RCT_EXPORT_METHOD(presentOverlayView) { + static UIView *overlayView; + + dispatch_async(dispatch_get_main_queue(), ^{ + CGRect screenBounds = UIScreen.mainScreen.bounds; + overlayView = [[UIView alloc] initWithFrame:screenBounds]; + overlayView.userInteractionEnabled = YES; + overlayView.accessibilityIdentifier = @"OverlayView"; + + UIWindow *keyWindow = UIApplication.sharedApplication.keyWindow; + [keyWindow addSubview:overlayView]; + }); +} + +@end diff --git a/detox/test/ios/example.xcodeproj/project.pbxproj b/detox/test/ios/example.xcodeproj/project.pbxproj index 40f965f656..4b28ac8a73 100644 --- a/detox/test/ios/example.xcodeproj/project.pbxproj +++ b/detox/test/ios/example.xcodeproj/project.pbxproj @@ -12,6 +12,7 @@ 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; 609DDB892D10C21800028574 /* Detox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 609DDB852D10C20800028574 /* Detox.framework */; }; + 609DDBE42D10D45500028574 /* NativeModule.mm in Sources */ = {isa = PBXBuildFile; fileRef = 609DDBE32D10D45500028574 /* NativeModule.mm */; }; 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; 90CCD0BA1322566EB1285DC2 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */; }; /* End PBXBuildFile section */ @@ -47,6 +48,8 @@ 5B7EB9410499542E8C5724F5 /* Pods-example-exampleTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example-exampleTests.debug.xcconfig"; path = "Target Support Files/Pods-example-exampleTests/Pods-example-exampleTests.debug.xcconfig"; sourceTree = ""; }; 5DCACB8F33CDC322A6C60F78 /* libPods-example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-example.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 609DDB772D10C20700028574 /* Detox.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Detox.xcodeproj; path = /Users/asafk/Development/Detox/detox/ios/Detox.xcodeproj; sourceTree = ""; }; + 609DDBE22D10D45500028574 /* NativeModule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NativeModule.h; sourceTree = ""; }; + 609DDBE32D10D45500028574 /* NativeModule.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = NativeModule.mm; sourceTree = ""; }; 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = example/LaunchScreen.storyboard; sourceTree = ""; }; 89C6BE57DB24E9ADA2F236DE /* Pods-example-exampleTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example-exampleTests.release.xcconfig"; path = "Target Support Files/Pods-example-exampleTests/Pods-example-exampleTests.release.xcconfig"; sourceTree = ""; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; @@ -75,6 +78,7 @@ 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */, 13B07FB71A68108700A75B9A /* main.m */, 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */, + 609DDBE12D10D44F00028574 /* ReactModules */, ); name = example; sourceTree = ""; @@ -98,6 +102,15 @@ name = Products; sourceTree = ""; }; + 609DDBE12D10D44F00028574 /* ReactModules */ = { + isa = PBXGroup; + children = ( + 609DDBE22D10D45500028574 /* NativeModule.h */, + 609DDBE32D10D45500028574 /* NativeModule.mm */, + ); + path = ReactModules; + sourceTree = ""; + }; 832341AE1AAA6A7D00B99B32 /* Libraries */ = { isa = PBXGroup; children = ( @@ -310,6 +323,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 609DDBE42D10D45500028574 /* NativeModule.mm in Sources */, 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */, 13B07FC11A68108700A75B9A /* main.m in Sources */, ); From c8589fa42d2cd6aedf4e102379555c72daa4bff0 Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Mon, 16 Dec 2024 23:53:31 +0200 Subject: [PATCH 10/51] test(ios): refactor legacy `NativeModule`. --- detox/test/ios/ReactModules/NativeModule.mm | 215 ++++++++++++-------- 1 file changed, 132 insertions(+), 83 deletions(-) diff --git a/detox/test/ios/ReactModules/NativeModule.mm b/detox/test/ios/ReactModules/NativeModule.mm index 08f07b45fc..bd80781919 100644 --- a/detox/test/ios/ReactModules/NativeModule.mm +++ b/detox/test/ios/ReactModules/NativeModule.mm @@ -2,118 +2,167 @@ #import #import -static int CALL_COUNTER = 0; +@interface NativeModule () +@property (nonatomic, strong) UIWindow *overlayWindow; +@property (nonatomic, strong) UIView *overlayView; +@property (nonatomic, assign) NSInteger callCounter; +@end @implementation NativeModule RCT_EXPORT_MODULE(); -RCT_EXPORT_METHOD(echoWithoutResponse:(NSString *)str) -{ - // NSLog(@"NativeModule echoWithoutResponse called"); - CALL_COUNTER++; +#pragma mark - Lifecycle Methods + +- (instancetype)init { + self = [super init]; + if (self) { + _callCounter = 0; + } + return self; +} + +#pragma mark - Echo Methods + +RCT_EXPORT_METHOD(echoWithoutResponse:(NSString *)str) { + self.callCounter++; } RCT_EXPORT_METHOD(echoWithResponse:(NSString *)str - resolver:(RCTPromiseResolveBlock)resolve - rejecter:(RCTPromiseRejectBlock)reject) -{ - CALL_COUNTER++; - resolve(str); - // NSLog(@"NativeModule echoWithResponse called"); -} - -RCT_EXPORT_METHOD(nativeSetTimeout:(NSTimeInterval)delay block:(RCTResponseSenderBlock)block) -{ - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - dispatch_async(dispatch_get_main_queue(), ^{ - block(@[]); - }); - }); -} - -RCT_EXPORT_METHOD(switchToNativeRoot) -{ - dispatch_async(dispatch_get_main_queue(), ^{ - UIViewController* newRoot = [UIViewController new]; - newRoot.view.backgroundColor = [UIColor whiteColor]; - UILabel* label = [UILabel new]; - label.text = @"this is a new native root"; - [label sizeToFit]; - [[newRoot view] addSubview:label]; - label.center = newRoot.view.center; + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) { + self.callCounter++; + resolve(str); +} - id delegate = [[UIApplication sharedApplication] delegate]; - [[delegate window]setRootViewController:newRoot]; - [[delegate window] makeKeyAndVisible]; - }); +#pragma mark - Timing Methods + +RCT_EXPORT_METHOD(nativeSetTimeout:(NSTimeInterval)delay + block:(RCTResponseSenderBlock)block) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), + dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + [self executeOnMainThread:^{ + block(@[]); + }]; + }); } -RCT_EXPORT_METHOD(switchToMultipleReactRoots) -{ - dispatch_async(dispatch_get_main_queue(), ^{ - id delegate = [[UIApplication sharedApplication] delegate]; - RCTBridge* bridge = ((RCTRootView*)delegate.window.rootViewController.view).bridge; +#pragma mark - Navigation Methods - UIViewController* newRoot = [UIViewController new]; - newRoot.view = [[RCTRootView alloc]initWithBridge:bridge moduleName:@"example" initialProperties:nil]; - newRoot.tabBarItem.title = @"1"; +RCT_EXPORT_METHOD(switchToNativeRoot) { + [self executeOnMainThread:^{ + UIViewController *newRoot = [self createNativeRootViewController]; + [self updateRootViewController:newRoot]; + }]; +} +RCT_EXPORT_METHOD(switchToMultipleReactRoots) { + [self executeOnMainThread:^{ + UITabBarController *tabController = [self createTabBarControllerWithBridge]; + [self updateRootViewController:tabController]; + }]; +} - UIViewController* newRoot2 = [UIViewController new]; - newRoot2.view = [[RCTRootView alloc]initWithBridge:bridge moduleName:@"example" initialProperties:nil]; - newRoot2.tabBarItem.title = @"2"; +#pragma mark - Notification Methods - UIViewController* newRoot3 = [UIViewController new]; - newRoot3.view = [[RCTRootView alloc]initWithBridge:bridge moduleName:@"example" initialProperties:nil]; - newRoot3.tabBarItem.title = @"3"; +RCT_EXPORT_METHOD(sendNotification:(NSString*)notification + name:(NSString*)name) { + [self executeOnMainThread:^{ + [NSNotificationCenter.defaultCenter postNotificationName:notification + object:nil + userInfo:@{@"name": name}]; + }]; +} - UIViewController* newRoot4 = [UIViewController new]; - newRoot4.view = [[RCTRootView alloc]initWithBridge:bridge moduleName:@"example" initialProperties:nil]; - newRoot4.tabBarItem.title = @"4"; +#pragma mark - Overlay Methods - UITabBarController* tbc = [UITabBarController new]; - tbc.viewControllers = @[newRoot, newRoot2, newRoot3, newRoot4]; +RCT_EXPORT_METHOD(presentOverlayWindow) { + [self executeOnMainThread:^{ + [self setupAndShowOverlayWindow]; + }]; +} - [[delegate window]setRootViewController:tbc]; - [[delegate window] makeKeyAndVisible]; - }); +RCT_EXPORT_METHOD(presentOverlayView) { + [self executeOnMainThread:^{ + [self setupAndShowOverlayView]; + }]; } -RCT_EXPORT_METHOD(sendNotification:(NSString*)notification name:(NSString*)name) -{ - dispatch_async(dispatch_get_main_queue(), ^{ - [NSNotificationCenter.defaultCenter postNotificationName:notification object:nil userInfo:@{@"name": name}]; - }); +#pragma mark - Private Helper Methods + +- (void)executeOnMainThread:(void (^)(void))block { + if ([NSThread isMainThread]) { + block(); + } else { + dispatch_async(dispatch_get_main_queue(), block); + } } -RCT_EXPORT_METHOD(presentOverlayWindow) { - static UIWindow *overlayWindow; +- (UIViewController *)createNativeRootViewController { + UIViewController *newRoot = [UIViewController new]; + newRoot.view.backgroundColor = UIColor.whiteColor; - dispatch_async(dispatch_get_main_queue(), ^{ - CGRect screenBounds = UIScreen.mainScreen.bounds; - overlayWindow = [[UIWindow alloc] initWithFrame:screenBounds]; - overlayWindow.accessibilityIdentifier = @"OverlayWindow"; + UILabel *label = [UILabel new]; + label.text = @"this is a new native root"; + [label sizeToFit]; + [newRoot.view addSubview:label]; + label.center = newRoot.view.center; - [overlayWindow setWindowLevel:UIWindowLevelStatusBar]; - [overlayWindow setHidden:NO]; + return newRoot; +} - [overlayWindow makeKeyAndVisible]; - }); +- (UITabBarController *)createTabBarControllerWithBridge { + RCTBridge *bridge = [self getCurrentBridge]; + NSArray *viewControllers = @[ + [self createReactRootViewController:bridge title:@"1"], + [self createReactRootViewController:bridge title:@"2"], + [self createReactRootViewController:bridge title:@"3"], + [self createReactRootViewController:bridge title:@"4"] + ]; + + UITabBarController *tabController = [UITabBarController new]; + tabController.viewControllers = viewControllers; + return tabController; } -RCT_EXPORT_METHOD(presentOverlayView) { - static UIView *overlayView; +- (UIViewController *)createReactRootViewController:(RCTBridge *)bridge + title:(NSString *)title { + UIViewController *viewController = [UIViewController new]; + viewController.view = [[RCTRootView alloc] initWithBridge:bridge + moduleName:@"example" + initialProperties:nil]; + viewController.tabBarItem.title = title; + return viewController; +} - dispatch_async(dispatch_get_main_queue(), ^{ - CGRect screenBounds = UIScreen.mainScreen.bounds; - overlayView = [[UIView alloc] initWithFrame:screenBounds]; - overlayView.userInteractionEnabled = YES; - overlayView.accessibilityIdentifier = @"OverlayView"; +- (RCTBridge *)getCurrentBridge { + id delegate = UIApplication.sharedApplication.delegate; + return ((RCTRootView *)delegate.window.rootViewController.view).bridge; +} - UIWindow *keyWindow = UIApplication.sharedApplication.keyWindow; - [keyWindow addSubview:overlayView]; - }); +- (void)updateRootViewController:(UIViewController *)viewController { + id delegate = UIApplication.sharedApplication.delegate; + [delegate.window setRootViewController:viewController]; + [delegate.window makeKeyAndVisible]; +} + +- (void)setupAndShowOverlayWindow { + CGRect screenBounds = UIScreen.mainScreen.bounds; + self.overlayWindow = [[UIWindow alloc] initWithFrame:screenBounds]; + self.overlayWindow.accessibilityIdentifier = @"OverlayWindow"; + [self.overlayWindow setWindowLevel:UIWindowLevelStatusBar]; + [self.overlayWindow setHidden:NO]; + [self.overlayWindow makeKeyAndVisible]; +} + +- (void)setupAndShowOverlayView { + CGRect screenBounds = UIScreen.mainScreen.bounds; + self.overlayView = [[UIView alloc] initWithFrame:screenBounds]; + self.overlayView.userInteractionEnabled = YES; + self.overlayView.accessibilityIdentifier = @"OverlayView"; + + UIWindow *keyWindow = UIApplication.sharedApplication.keyWindow; + [keyWindow addSubview:self.overlayView]; } @end From 294a164f2e59c5c5c4437dec66e766914a038b3b Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Mon, 16 Dec 2024 23:56:18 +0200 Subject: [PATCH 11/51] test(ios): add background modes capabilities. --- detox/test/ios/example.xcodeproj/project.pbxproj | 4 ++++ detox/test/ios/example/Info.plist | 5 +++++ detox/test/ios/example/example.entitlements | 8 ++++++++ 3 files changed, 17 insertions(+) create mode 100644 detox/test/ios/example/example.entitlements diff --git a/detox/test/ios/example.xcodeproj/project.pbxproj b/detox/test/ios/example.xcodeproj/project.pbxproj index 4b28ac8a73..a26cec6e87 100644 --- a/detox/test/ios/example.xcodeproj/project.pbxproj +++ b/detox/test/ios/example.xcodeproj/project.pbxproj @@ -47,6 +47,7 @@ 5709B34CF0A7D63546082F79 /* Pods-example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example.release.xcconfig"; path = "Target Support Files/Pods-example/Pods-example.release.xcconfig"; sourceTree = ""; }; 5B7EB9410499542E8C5724F5 /* Pods-example-exampleTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example-exampleTests.debug.xcconfig"; path = "Target Support Files/Pods-example-exampleTests/Pods-example-exampleTests.debug.xcconfig"; sourceTree = ""; }; 5DCACB8F33CDC322A6C60F78 /* libPods-example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-example.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 60493BD72D10D967002853A0 /* example.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = example.entitlements; path = example/example.entitlements; sourceTree = ""; }; 609DDB772D10C20700028574 /* Detox.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Detox.xcodeproj; path = /Users/asafk/Development/Detox/detox/ios/Detox.xcodeproj; sourceTree = ""; }; 609DDBE22D10D45500028574 /* NativeModule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NativeModule.h; sourceTree = ""; }; 609DDBE32D10D45500028574 /* NativeModule.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = NativeModule.mm; sourceTree = ""; }; @@ -71,6 +72,7 @@ 13B07FAE1A68108700A75B9A /* example */ = { isa = PBXGroup; children = ( + 60493BD72D10D967002853A0 /* example.entitlements */, 13B07FAF1A68108700A75B9A /* AppDelegate.h */, 13B07FB01A68108700A75B9A /* AppDelegate.mm */, 13B07FB51A68108700A75B9A /* Images.xcassets */, @@ -338,6 +340,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = example/example.entitlements; CURRENT_PROJECT_VERSION = 1; ENABLE_BITCODE = NO; INFOPLIST_FILE = example/Info.plist; @@ -366,6 +369,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = example/example.entitlements; CURRENT_PROJECT_VERSION = 1; INFOPLIST_FILE = example/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 15.1; diff --git a/detox/test/ios/example/Info.plist b/detox/test/ios/example/Info.plist index a496c6c863..edcff4bcd0 100644 --- a/detox/test/ios/example/Info.plist +++ b/detox/test/ios/example/Info.plist @@ -33,6 +33,11 @@ NSLocationWhenInUseUsageDescription + UIBackgroundModes + + fetch + remote-notification + UILaunchStoryboardName LaunchScreen UIRequiredDeviceCapabilities diff --git a/detox/test/ios/example/example.entitlements b/detox/test/ios/example/example.entitlements new file mode 100644 index 0000000000..21d95c45f3 --- /dev/null +++ b/detox/test/ios/example/example.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.developer.siri + + + From 47625f01e36d158c45aa502c3926bfe4fd28d16d Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Tue, 17 Dec 2024 00:01:10 +0200 Subject: [PATCH 12/51] test(ios): enable upside-down rotation. --- detox/test/ios/example/Info.plist | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/detox/test/ios/example/Info.plist b/detox/test/ios/example/Info.plist index edcff4bcd0..a7513950b8 100644 --- a/detox/test/ios/example/Info.plist +++ b/detox/test/ios/example/Info.plist @@ -46,9 +46,16 @@ UISupportedInterfaceOrientations + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + + UISupportedInterfaceOrientations~ipad + UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + UIInterfaceOrientationPortrait UIViewControllerBasedStatusBarAppearance From d1800f16aab9242059ddde2a22dada11f38c8169 Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Tue, 17 Dec 2024 00:03:03 +0200 Subject: [PATCH 13/51] test(ios): update `Info.plist` with missing keys. --- detox/test/ios/example/Info.plist | 67 ++++++++++++++++++++++++++----- 1 file changed, 56 insertions(+), 11 deletions(-) diff --git a/detox/test/ios/example/Info.plist b/detox/test/ios/example/Info.plist index a7513950b8..81b3f8f622 100644 --- a/detox/test/ios/example/Info.plist +++ b/detox/test/ios/example/Info.plist @@ -4,8 +4,6 @@ CFBundleDevelopmentRegion en - CFBundleDisplayName - example CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -17,11 +15,24 @@ CFBundlePackageType APPL CFBundleShortVersionString - $(MARKETING_VERSION) + 1.0 CFBundleSignature ???? + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + detoxtesturlscheme.test.app + CFBundleURLSchemes + + detoxtesturlscheme + + + CFBundleVersion - $(CURRENT_PROJECT_VERSION) + 1 LSRequiresIPhoneOS NSAppTransportSecurity @@ -31,8 +42,42 @@ NSAllowsLocalNetworking + NSAppleMusicUsageDescription + + NSBluetoothAlwaysUsageDescription + + NSBluetoothPeripheralUsageDescription + + NSCalendarsUsageDescription + + NSCameraUsageDescription + + NSContactsUsageDescription + + NSFaceIDUsageDescription + + NSLocationAlwaysAndWhenInUseUsageDescription + + NSLocationTemporaryUsageDescriptionDictionary + NSLocationWhenInUseUsageDescription + NSMicrophoneUsageDescription + + NSMotionUsageDescription + + NSPhotoLibraryAddUsageDescription + + NSPhotoLibraryUsageDescription + + NSRemindersUsageDescription + + NSSiriUsageDescription + + NSSpeechRecognitionUsageDescription + + NSUserTrackingUsageDescription + UIBackgroundModes fetch @@ -44,20 +89,20 @@ arm64 + UIRequiresFullScreen + UISupportedInterfaceOrientations + UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight - UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown - UISupportedInterfaceOrientations~ipad + UIViewControllerBasedStatusBarAppearance + + WKBackgroundModes - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - UIInterfaceOrientationPortrait + remote-notification - UIViewControllerBasedStatusBarAppearance - From e54f55826a951e8dbaf905be9eb1e4a98ac1666c Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Tue, 17 Dec 2024 00:10:27 +0200 Subject: [PATCH 14/51] test(ios): copy command line arguments from legacy ios project. --- .../xcshareddata/xcschemes/example.xcscheme | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/detox/test/ios/example.xcodeproj/xcshareddata/xcschemes/example.xcscheme b/detox/test/ios/example.xcodeproj/xcshareddata/xcschemes/example.xcscheme index fef9df78ab..ba996697dc 100644 --- a/detox/test/ios/example.xcodeproj/xcshareddata/xcschemes/example.xcscheme +++ b/detox/test/ios/example.xcodeproj/xcshareddata/xcschemes/example.xcscheme @@ -60,6 +60,80 @@ ReferencedContainer = "container:example.xcodeproj"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Date: Tue, 17 Dec 2024 00:15:09 +0200 Subject: [PATCH 15/51] feat(iOS): support new `RCTScrollViewComponentView`. --- detox/ios/Detox/Invocation/Element.swift | 35 +++++++++++++++--------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/detox/ios/Detox/Invocation/Element.swift b/detox/ios/Detox/Invocation/Element.swift index 70f8d8a426..8442e7fb91 100644 --- a/detox/ios/Detox/Invocation/Element.swift +++ b/detox/ios/Detox/Invocation/Element.swift @@ -73,19 +73,28 @@ class Element : NSObject { return element } - private func extractScrollView() -> UIScrollView { - if let view = self.view as? UIScrollView { - return view - } - else if let view = self.view as? WKWebView { - return view.scrollView - } else if ReactNativeSupport.isReactNativeApp && NSStringFromClass(type(of: view)) == "RCTScrollView" { - return (view.value(forKey: "scrollView") as! UIScrollView) - } - - dtx_fatalError("View “\(self.view.dtx_shortDescription)” is not an instance of “UIScrollView”", viewDescription: debugAttributes) - } - + private func extractScrollView() -> UIScrollView { + if let view = self.view as? UIScrollView { + return view + } + + if let webView = self.view as? WKWebView { + return webView.scrollView + } + + if ReactNativeSupport.isReactNativeApp { + let className = NSStringFromClass(type(of: view)) + switch className { + case "RCTScrollView", "RCTScrollViewComponentView": + return (view.value(forKey: "scrollView") as! UIScrollView) + default: + break + } + } + + dtx_fatalError("View “\(self.view.dtx_shortDescription)” is not an instance of “UIScrollView”", viewDescription: debugAttributes) + } + override var description: String { return String(format: "MATCHER(%@)%@", predicate.description, index != nil ? " AT INDEX(\(index!))" : "") } From 488c203ea7dada24b3df6bc677bcfb097ced4b99 Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Tue, 17 Dec 2024 00:34:41 +0200 Subject: [PATCH 16/51] feat(ios): support new-arch's `RCTParagraphComponentView`. --- detox/ios/Detox/Invocation/Predicate.swift | 8 +- .../ios/Detox/Utilities/NSObject+DontCrash.m | 97 ++++++++++++++----- 2 files changed, 77 insertions(+), 28 deletions(-) diff --git a/detox/ios/Detox/Invocation/Predicate.swift b/detox/ios/Detox/Invocation/Predicate.swift index cd264f5194..ffd19d873c 100644 --- a/detox/ios/Detox/Invocation/Predicate.swift +++ b/detox/ios/Detox/Invocation/Predicate.swift @@ -66,7 +66,9 @@ class Predicate : CustomStringConvertible, CustomDebugStringConvertible { return ValuePredicate(kind: kind, modifiers: modifiers, value: label, requiresAccessibilityElement: true, isRegex: isRegex) } else { //Will crash if RN app and neither class exists - let RCTTextViewClass : AnyClass = NSClassFromString("RCTText") ?? NSClassFromString("RCTTextView")! + let RCTTextViewClass : AnyClass = + NSClassFromString("RCTParagraphComponentView") ?? + NSClassFromString("RCTText") ?? NSClassFromString("RCTTextView")! let descendantPredicate = DescendantPredicate(predicate: AndCompoundPredicate(predicates: [ try KindOfPredicate(kind: Kind.type, modifiers: [], className: NSStringFromClass(RCTTextViewClass)), @@ -89,7 +91,9 @@ class Predicate : CustomStringConvertible, CustomDebugStringConvertible { if ReactNativeSupport.isReactNativeApp == true { //Will crash if RN app and neither class exists - let RCTTextViewClass : AnyClass = NSClassFromString("RCTText") ?? NSClassFromString("RCTTextView")! + let RCTTextViewClass : AnyClass = + NSClassFromString("RCTParagraphComponentView") ?? + NSClassFromString("RCTText") ?? NSClassFromString("RCTTextView")! orPredicates.append(try KindOfPredicate(kind: Kind.type, modifiers: [], className: NSStringFromClass(RCTTextViewClass))) } diff --git a/detox/ios/Detox/Utilities/NSObject+DontCrash.m b/detox/ios/Detox/Utilities/NSObject+DontCrash.m index eb72d4169c..f880071c2d 100644 --- a/detox/ios/Detox/Utilities/NSObject+DontCrash.m +++ b/detox/ios/Detox/Utilities/NSObject+DontCrash.m @@ -7,36 +7,81 @@ #import "NSObject+DontCrash.h" +@interface NSObject (DontCrashPrivate) ++ (Class)dtx_classForName:(NSString *)className; +- (nullable NSString *)dtx_extractTextFromRCTComponent; +@end + @implementation NSObject (DontCrash) -- (id)_dtx_text -{ - if([self respondsToSelector:@selector(text)]) - { - return [(UITextView*)self text]; - } - - static Class RCTTextView; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - RCTTextView = NSClassFromString(@"RCTTextView"); - }); - if(RCTTextView != nil && [self isKindOfClass:RCTTextView]) - { - return [(NSTextStorage*)[self valueForKey:@"textStorage"] string]; - } - - return nil; +#pragma mark - Public Methods + +- (id)_dtx_text { + if ([self respondsToSelector:@selector(text)]) { + return [(UITextView *)self text]; + } + + NSString *reactText = [self dtx_extractTextFromRCTComponent]; + if (reactText) { + return reactText; + } + + return nil; +} + +- (id)_dtx_placeholder { + if ([self respondsToSelector:@selector(placeholder)]) { + return [(UITextField *)self placeholder]; + } + return nil; +} + +@end + +@implementation NSObject (DontCrashPrivate) + +#pragma mark - Private Methods + ++ (Class)dtx_classForName:(NSString *)className { + static NSMutableDictionary *classCache; + static dispatch_once_t onceToken; + + dispatch_once(&onceToken, ^{ + classCache = [NSMutableDictionary dictionary]; + }); + + Class cachedClass = classCache[className]; + if (!cachedClass) { + cachedClass = NSClassFromString(className); + if (cachedClass) { + classCache[className] = cachedClass; + } + } + + return cachedClass; } -- (id)_dtx_placeholder -{ - if([self respondsToSelector:@selector(placeholder)]) - { - return [(UITextField*)self placeholder]; - } - - return nil; +- (nullable NSString *)dtx_extractTextFromRCTComponent { + static Class RCTTextViewClass; + static Class RCTParagraphComponentViewClass; + static dispatch_once_t onceToken; + + dispatch_once(&onceToken, ^{ + RCTTextViewClass = [self.class dtx_classForName:@"RCTTextView"]; + RCTParagraphComponentViewClass = [self.class dtx_classForName:@"RCTParagraphComponentView"]; + }); + + if (RCTTextViewClass && [self isKindOfClass:RCTTextViewClass]) { + NSTextStorage *textStorage = [self valueForKey:@"textStorage"]; + return [textStorage string]; + } + + if (RCTParagraphComponentViewClass && [self isKindOfClass:RCTParagraphComponentViewClass]) { + NSAttributedString *attributedText = [self valueForKey:@"attributedText"]; + return [attributedText string]; + } + + return nil; } @end From 67e1f6048fa425b62c76f9103b0bdbd1b625c27d Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Tue, 17 Dec 2024 00:49:47 +0200 Subject: [PATCH 17/51] feat(ios): support React Native new arch app-reloading. --- .../ReactNativeSupport/ReactNativeSupport.m | 50 +++++++++++-------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/detox/ios/Detox/Utilities/ReactNativeSupport/ReactNativeSupport.m b/detox/ios/Detox/Utilities/ReactNativeSupport/ReactNativeSupport.m index b04284cce2..ab54a09089 100644 --- a/detox/ios/Detox/Utilities/ReactNativeSupport/ReactNativeSupport.m +++ b/detox/ios/Detox/Utilities/ReactNativeSupport/ReactNativeSupport.m @@ -28,26 +28,36 @@ + (BOOL)isReactNativeApp + (void)reloadApp { - if([DTXReactNativeSupport hasReactNative] == NO) - { - return; - } - - id bridge = [NSClassFromString(@"RCTBridge") valueForKey:@"currentBridge"]; - - SEL reqRelSel = NSSelectorFromString(@"requestReload"); - if([bridge respondsToSelector:reqRelSel]) - { - //Call RN public API to request reload. - [bridge requestReload]; - } - else - { - //Legacy call to reload RN. - [[NSNotificationCenter defaultCenter] postNotificationName:RCTReloadNotification - object:nil - userInfo:nil]; - } + // Early return if React Native is not present + if (![DTXReactNativeSupport hasReactNative]) { + return; + } + + // Try legacy reload approach (without new arch) + id bridge = [NSClassFromString(@"RCTBridge") valueForKey:@"currentBridge"]; + if ([bridge respondsToSelector:@selector(requestReload)]) { + [bridge requestReload]; + return; + } + + // Try New Architecture reload approach + NSObject *appDelegate = UIApplication.sharedApplication.delegate; + NSObject *rootViewFactory = [appDelegate valueForKey:@"rootViewFactory"]; + NSObject *reactHost = [rootViewFactory valueForKey:@"reactHost"]; + + SEL reloadCommand = NSSelectorFromString(@"didReceiveReloadCommand"); + if (reactHost && [reactHost respondsToSelector:reloadCommand]) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + [reactHost performSelector:reloadCommand]; +#pragma clang diagnostic pop + return; + } + + // Fallback to legacy^2 reload approach + [[NSNotificationCenter defaultCenter] postNotificationName:RCTReloadNotification + object:nil + userInfo:nil]; } + (void)waitForReactNativeLoadWithCompletionHandler:(void (^)(void))handler From ba95461e604935f7d28907493f5da17617f64e1e Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Tue, 17 Dec 2024 01:03:47 +0200 Subject: [PATCH 18/51] test(ios): add example-ci scheme (no Detox linkage). --- detox/test/e2e/detox.config.js | 4 +- .../ios/example.xcodeproj/project.pbxproj | 196 ++++++++++++++++++ 2 files changed, 198 insertions(+), 2 deletions(-) diff --git a/detox/test/e2e/detox.config.js b/detox/test/e2e/detox.config.js index 591c4e3fdb..da4ee2381a 100644 --- a/detox/test/e2e/detox.config.js +++ b/detox/test/e2e/detox.config.js @@ -53,7 +53,7 @@ const config = { type: 'ios.app', name: 'example', binaryPath: 'ios/build/Build/Products/Debug-iphonesimulator/example.app', - build: 'set -o pipefail && xcodebuild -workspace ios/example.xcworkspace -scheme example -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build -quiet', + build: 'set -o pipefail && xcodebuild -workspace ios/example.xcworkspace -scheme example-ci -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build -quiet', start: 'react-native start', bundleId: 'com.wix.detox-example', }, @@ -62,7 +62,7 @@ const config = { type: 'ios.app', name: 'example', binaryPath: 'ios/build/Build/Products/Release-iphonesimulator/example.app', - build: 'set -o pipefail && export CODE_SIGNING_REQUIRED=NO && export RCT_NO_LAUNCH_PACKAGER=true && xcodebuild -workspace ios/example.xcworkspace -scheme example -configuration Release -sdk iphonesimulator -derivedDataPath ios/build -quiet', + build: 'set -o pipefail && export CODE_SIGNING_REQUIRED=NO && export RCT_NO_LAUNCH_PACKAGER=true && xcodebuild -workspace ios/example.xcworkspace -scheme example-ci -configuration Release -sdk iphonesimulator -derivedDataPath ios/build -quiet', }, 'android.debug': { diff --git a/detox/test/ios/example.xcodeproj/project.pbxproj b/detox/test/ios/example.xcodeproj/project.pbxproj index a26cec6e87..d4d24ad8a7 100644 --- a/detox/test/ios/example.xcodeproj/project.pbxproj +++ b/detox/test/ios/example.xcodeproj/project.pbxproj @@ -11,6 +11,13 @@ 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; + 60493BDE2D10E7E4002853A0 /* NativeModule.mm in Sources */ = {isa = PBXBuildFile; fileRef = 609DDBE32D10D45500028574 /* NativeModule.mm */; }; + 60493BDF2D10E7E4002853A0 /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; }; + 60493BE02D10E7E4002853A0 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; + 60493BE32D10E7E4002853A0 /* libPods-example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DCACB8F33CDC322A6C60F78 /* libPods-example.a */; }; + 60493BE52D10E7E4002853A0 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; + 60493BE62D10E7E4002853A0 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; + 60493BE72D10E7E4002853A0 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */; }; 609DDB892D10C21800028574 /* Detox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 609DDB852D10C20800028574 /* Detox.framework */; }; 609DDBE42D10D45500028574 /* NativeModule.mm in Sources */ = {isa = PBXBuildFile; fileRef = 609DDBE32D10D45500028574 /* NativeModule.mm */; }; 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; @@ -48,6 +55,7 @@ 5B7EB9410499542E8C5724F5 /* Pods-example-exampleTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example-exampleTests.debug.xcconfig"; path = "Target Support Files/Pods-example-exampleTests/Pods-example-exampleTests.debug.xcconfig"; sourceTree = ""; }; 5DCACB8F33CDC322A6C60F78 /* libPods-example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-example.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 60493BD72D10D967002853A0 /* example.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = example.entitlements; path = example/example.entitlements; sourceTree = ""; }; + 60493BEE2D10E7E4002853A0 /* example-ci.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "example-ci.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 609DDB772D10C20700028574 /* Detox.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Detox.xcodeproj; path = /Users/asafk/Development/Detox/detox/ios/Detox.xcodeproj; sourceTree = ""; }; 609DDBE22D10D45500028574 /* NativeModule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NativeModule.h; sourceTree = ""; }; 609DDBE32D10D45500028574 /* NativeModule.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = NativeModule.mm; sourceTree = ""; }; @@ -66,6 +74,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 60493BE12D10E7E4002853A0 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 60493BE32D10E7E4002853A0 /* libPods-example.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -139,6 +155,7 @@ isa = PBXGroup; children = ( 13B07F961A680F5B00A75B9A /* example.app */, + 60493BEE2D10E7E4002853A0 /* example-ci.app */, ); name = Products; sourceTree = ""; @@ -178,6 +195,27 @@ productReference = 13B07F961A680F5B00A75B9A /* example.app */; productType = "com.apple.product-type.application"; }; + 60493BDB2D10E7E4002853A0 /* example-ci */ = { + isa = PBXNativeTarget; + buildConfigurationList = 60493BEB2D10E7E4002853A0 /* Build configuration list for PBXNativeTarget "example-ci" */; + buildPhases = ( + 60493BDC2D10E7E4002853A0 /* [CP] Check Pods Manifest.lock */, + 60493BDD2D10E7E4002853A0 /* Sources */, + 60493BE12D10E7E4002853A0 /* Frameworks */, + 60493BE42D10E7E4002853A0 /* Resources */, + 60493BE82D10E7E4002853A0 /* Bundle React Native code and images */, + 60493BE92D10E7E4002853A0 /* [CP] Embed Pods Frameworks */, + 60493BEA2D10E7E4002853A0 /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "example-ci"; + productName = example; + productReference = 60493BEE2D10E7E4002853A0 /* example-ci.app */; + productType = "com.apple.product-type.application"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -211,6 +249,7 @@ projectRoot = ""; targets = ( 13B07F861A680F5B00A75B9A /* example */, + 60493BDB2D10E7E4002853A0 /* example-ci */, ); }; /* End PBXProject section */ @@ -243,6 +282,16 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 60493BE42D10E7E4002853A0 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 60493BE52D10E7E4002853A0 /* LaunchScreen.storyboard in Resources */, + 60493BE62D10E7E4002853A0 /* Images.xcassets in Resources */, + 60493BE72D10E7E4002853A0 /* PrivacyInfo.xcprivacy in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ @@ -279,6 +328,78 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; + 60493BDC2D10E7E4002853A0 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-example-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 60493BE82D10E7E4002853A0 /* Bundle React Native code and images */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/.xcode.env.local", + "$(SRCROOT)/.xcode.env", + ); + name = "Bundle React Native code and images"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "set -e\n\nWITH_ENVIRONMENT=\"$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"$REACT_NATIVE_PATH/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n"; + }; + 60493BE92D10E7E4002853A0 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 60493BEA2D10E7E4002853A0 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-resources-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; C38B50BA6285516D6DCD4F65 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -331,6 +452,16 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 60493BDD2D10E7E4002853A0 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 60493BDE2D10E7E4002853A0 /* NativeModule.mm in Sources */, + 60493BDF2D10E7E4002853A0 /* AppDelegate.mm in Sources */, + 60493BE02D10E7E4002853A0 /* main.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ @@ -390,6 +521,62 @@ }; name = Release; }; + 60493BEC2D10E7E4002853A0 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3B4392A12AC88292D35C810B /* Pods-example.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = example/example.entitlements; + CURRENT_PROJECT_VERSION = 1; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = example/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + OTHER_LDFLAGS = ( + "$(inherited)", + "-ObjC", + "-lc++", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.wix.detox-example"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 60493BED2D10E7E4002853A0 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 5709B34CF0A7D63546082F79 /* Pods-example.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = example/example.entitlements; + CURRENT_PROJECT_VERSION = 1; + INFOPLIST_FILE = example/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + OTHER_LDFLAGS = ( + "$(inherited)", + "-ObjC", + "-lc++", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.wix.detox-example"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; 83CBBA201A601CBA00E9B192 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -548,6 +735,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 60493BEB2D10E7E4002853A0 /* Build configuration list for PBXNativeTarget "example-ci" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 60493BEC2D10E7E4002853A0 /* Debug */, + 60493BED2D10E7E4002853A0 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "example" */ = { isa = XCConfigurationList; buildConfigurations = ( From d7f7f9762e4b55a6554d14326dc21c02ccfb1cd4 Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Tue, 17 Dec 2024 01:48:01 +0200 Subject: [PATCH 19/51] test(ios): refactor Podfile. --- detox/test/ios/Podfile | 84 ++++++++++++++++++++++++------------------ 1 file changed, 48 insertions(+), 36 deletions(-) diff --git a/detox/test/ios/Podfile b/detox/test/ios/Podfile index 8f849b7800..1f6f217d59 100644 --- a/detox/test/ios/Podfile +++ b/detox/test/ios/Podfile @@ -1,25 +1,53 @@ # Set RCT_NEW_ARCH_ENABLED to '0' if not defined ENV['RCT_NEW_ARCH_ENABLED'] = '0' unless ENV['RCT_NEW_ARCH_ENABLED'] -if ENV["REACT_NATIVE_VERSION"] && ENV["REACT_NATIVE_VERSION"].match(/0.71.*/) - require_relative '../node_modules/react-native/scripts/react_native_pods' - require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' - require_relative '../node_modules/react-native-permissions/scripts/setup' -else - # Resolve react_native_pods.rb with node to allow for hoisting - require Pod::Executable.execute_command('node', ['-p', - 'require.resolve( - "react-native/scripts/react_native_pods.rb", - {paths: [process.argv[1]]}, - )', __dir__]).strip +def load_react_native_config + if ENV["REACT_NATIVE_VERSION"] && ENV["REACT_NATIVE_VERSION"].match(/0.71.*/) + require_relative '../node_modules/react-native/scripts/react_native_pods' + require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' + require_relative '../node_modules/react-native-permissions/scripts/setup' + else + # Resolve react_native_pods.rb with node to allow for hoisting + require Pod::Executable.execute_command('node', ['-p', + 'require.resolve( + "react-native/scripts/react_native_pods.rb", + {paths: [process.argv[1]]}, + )', __dir__]).strip - require Pod::Executable.execute_command('node', ['-p', - 'require.resolve( - "react-native-permissions/scripts/setup.rb", - {paths: [process.argv[1]]}, - )', __dir__]).strip + require Pod::Executable.execute_command('node', ['-p', + 'require.resolve( + "react-native-permissions/scripts/setup.rb", + {paths: [process.argv[1]]}, + )', __dir__]).strip + end +end + +def configure_target(target_name) + target target_name do + config = use_native_modules! + + use_react_native!( + :path => config[:reactNativePath], + :app_path => "#{Pod::Config.instance.installation_root}/.." + ) + + # Add React Native Slider + pod 'react-native-slider', :path => '../node_modules/@react-native-community/slider' + + post_install do |installer| + react_native_post_install( + installer, + config[:reactNativePath], + :mac_catalyst_enabled => false, + # :ccache_enabled => true + ) + end + end end +# Load configurations +load_react_native_config + platform :ios, min_ios_version_supported prepare_react_native_project! @@ -46,29 +74,13 @@ setup_permissions([ 'StoreKit', ]) +# Configure frameworks linkage linkage = ENV['USE_FRAMEWORKS'] if linkage != nil Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green use_frameworks! :linkage => linkage.to_sym end -target 'example' do - config = use_native_modules! - - use_react_native!( - :path => config[:reactNativePath], - :app_path => "#{Pod::Config.instance.installation_root}/.." - ) - - # Add React Native Slider - pod 'react-native-slider', :path => '../node_modules/@react-native-community/slider' - - post_install do |installer| - react_native_post_install( - installer, - config[:reactNativePath], - :mac_catalyst_enabled => false, - # :ccache_enabled => true - ) - end -end +# Configure targets +configure_target('example') +configure_target('example-ci') From 23c726a284004d938ddb40cee84c7cd048554976 Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Tue, 17 Dec 2024 02:07:01 +0200 Subject: [PATCH 20/51] chore(deps): update DetoxSync with new-arch sync support. --- detox/ios/DetoxSync | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/detox/ios/DetoxSync b/detox/ios/DetoxSync index f69efdbd95..0567db9243 160000 --- a/detox/ios/DetoxSync +++ b/detox/ios/DetoxSync @@ -1 +1 @@ -Subproject commit f69efdbd9580f3c336ca5c3caed2926afb651793 +Subproject commit 0567db92434d1fc0232748f0cf2da55228fe699a From 5492e6aff608227b9cdb4153fcd886a026d490c7 Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Tue, 17 Dec 2024 02:07:36 +0200 Subject: [PATCH 21/51] chore(ios): add manual test env vars for debugging. --- .../xcshareddata/xcschemes/example.xcscheme | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/detox/test/ios/example.xcodeproj/xcshareddata/xcschemes/example.xcscheme b/detox/test/ios/example.xcodeproj/xcshareddata/xcschemes/example.xcscheme index ba996697dc..1c65e14823 100644 --- a/detox/test/ios/example.xcodeproj/xcshareddata/xcschemes/example.xcscheme +++ b/detox/test/ios/example.xcodeproj/xcshareddata/xcschemes/example.xcscheme @@ -134,6 +134,23 @@ isEnabled = "NO"> + + + + + + + + Date: Tue, 17 Dec 2024 02:25:57 +0200 Subject: [PATCH 22/51] test(ios): fix test-app Podfile. --- detox/test/ios/Podfile | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/detox/test/ios/Podfile b/detox/test/ios/Podfile index 1f6f217d59..1371970962 100644 --- a/detox/test/ios/Podfile +++ b/detox/test/ios/Podfile @@ -22,6 +22,7 @@ def load_react_native_config end end +# Configure target with common settings def configure_target(target_name) target target_name do config = use_native_modules! @@ -33,15 +34,6 @@ def configure_target(target_name) # Add React Native Slider pod 'react-native-slider', :path => '../node_modules/@react-native-community/slider' - - post_install do |installer| - react_native_post_install( - installer, - config[:reactNativePath], - :mac_catalyst_enabled => false, - # :ccache_enabled => true - ) - end end end @@ -84,3 +76,13 @@ end # Configure targets configure_target('example') configure_target('example-ci') + +# Post install hook (moved outside of targets) +post_install do |installer| + react_native_post_install( + installer, + config[:reactNativePath], + :mac_catalyst_enabled => false, + # :ccache_enabled => true + ) +end From 6c280b8bb02db7a6615c49da05c6bdb09b16e7bd Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Tue, 17 Dec 2024 09:01:17 +0200 Subject: [PATCH 23/51] test(ios): fix test-app Podfile. --- detox/test/ios/Podfile | 82 ++++++++----------- .../ios/example.xcodeproj/project.pbxproj | 48 +++++++---- 2 files changed, 66 insertions(+), 64 deletions(-) diff --git a/detox/test/ios/Podfile b/detox/test/ios/Podfile index 1371970962..cacd6701d5 100644 --- a/detox/test/ios/Podfile +++ b/detox/test/ios/Podfile @@ -1,49 +1,29 @@ -# Set RCT_NEW_ARCH_ENABLED to '0' if not defined -ENV['RCT_NEW_ARCH_ENABLED'] = '0' unless ENV['RCT_NEW_ARCH_ENABLED'] +ENV['RCT_NEW_ARCH_ENABLED'] = '0' -def load_react_native_config - if ENV["REACT_NATIVE_VERSION"] && ENV["REACT_NATIVE_VERSION"].match(/0.71.*/) +if ENV["REACT_NATIVE_VERSION"] && ENV["REACT_NATIVE_VERSION"].match(/0.(70|71).*/) require_relative '../node_modules/react-native/scripts/react_native_pods' require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' require_relative '../node_modules/react-native-permissions/scripts/setup' - else +else # Resolve react_native_pods.rb with node to allow for hoisting - require Pod::Executable.execute_command('node', ['-p', - 'require.resolve( - "react-native/scripts/react_native_pods.rb", - {paths: [process.argv[1]]}, - )', __dir__]).strip + def node_require(script) + # Resolve script with node to allow for hoisting + require Pod::Executable.execute_command('node', ['-p', + "require.resolve( + '#{script}', + {paths: [process.argv[1]]}, + )", __dir__]).strip + end - require Pod::Executable.execute_command('node', ['-p', - 'require.resolve( - "react-native-permissions/scripts/setup.rb", - {paths: [process.argv[1]]}, - )', __dir__]).strip - end + node_require('react-native/scripts/react_native_pods.rb') + node_require('react-native-permissions/scripts/setup.rb') end -# Configure target with common settings -def configure_target(target_name) - target target_name do - config = use_native_modules! - - use_react_native!( - :path => config[:reactNativePath], - :app_path => "#{Pod::Config.instance.installation_root}/.." - ) - - # Add React Native Slider - pod 'react-native-slider', :path => '../node_modules/@react-native-community/slider' - end -end - -# Load configurations -load_react_native_config - platform :ios, min_ios_version_supported -prepare_react_native_project! -# Configure permissions +install! 'cocoapods', :deterministic_uuids => false + +# Comment unwanted permissions setup_permissions([ 'AppTrackingTransparency', 'Bluetooth', @@ -66,23 +46,31 @@ setup_permissions([ 'StoreKit', ]) -# Configure frameworks linkage -linkage = ENV['USE_FRAMEWORKS'] -if linkage != nil - Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green - use_frameworks! :linkage => linkage.to_sym +def shared_pods + config = use_native_modules! + + use_react_native!( + :path => config[:reactNativePath], + :app_path => "#{Pod::Config.instance.installation_root}/.." + ) + + # Shared pods + pod 'react-native-slider', :path => '../node_modules/@react-native-community/slider' end -# Configure targets -configure_target('example') -configure_target('example-ci') +target 'example' do + shared_pods +end + +target 'example-ci' do + shared_pods +end -# Post install hook (moved outside of targets) post_install do |installer| + config = use_native_modules! react_native_post_install( installer, config[:reactNativePath], - :mac_catalyst_enabled => false, - # :ccache_enabled => true + :mac_catalyst_enabled => false ) end diff --git a/detox/test/ios/example.xcodeproj/project.pbxproj b/detox/test/ios/example.xcodeproj/project.pbxproj index d4d24ad8a7..856f9965fe 100644 --- a/detox/test/ios/example.xcodeproj/project.pbxproj +++ b/detox/test/ios/example.xcodeproj/project.pbxproj @@ -7,14 +7,14 @@ objects = { /* Begin PBXBuildFile section */ - 0C80B921A6F3F58F76C31292 /* libPods-example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DCACB8F33CDC322A6C60F78 /* libPods-example.a */; }; + 0C80B921A6F3F58F76C31292 /* BuildFile in Frameworks */ = {isa = PBXBuildFile; }; 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; + 213E1077F464B28612297B3E /* libPods-example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 467D00885FA51E1E9B0E8063 /* libPods-example.a */; }; 60493BDE2D10E7E4002853A0 /* NativeModule.mm in Sources */ = {isa = PBXBuildFile; fileRef = 609DDBE32D10D45500028574 /* NativeModule.mm */; }; 60493BDF2D10E7E4002853A0 /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; }; 60493BE02D10E7E4002853A0 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; - 60493BE32D10E7E4002853A0 /* libPods-example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DCACB8F33CDC322A6C60F78 /* libPods-example.a */; }; 60493BE52D10E7E4002853A0 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; 60493BE62D10E7E4002853A0 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 60493BE72D10E7E4002853A0 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */; }; @@ -22,6 +22,7 @@ 609DDBE42D10D45500028574 /* NativeModule.mm in Sources */ = {isa = PBXBuildFile; fileRef = 609DDBE32D10D45500028574 /* NativeModule.mm */; }; 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; 90CCD0BA1322566EB1285DC2 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */; }; + CA25A6405AF58BA742F7FD47 /* libPods-example-ci.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1CB0FE045763B5363B2E7054 /* libPods-example-ci.a */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -50,15 +51,18 @@ 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = example/main.m; sourceTree = ""; }; 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = PrivacyInfo.xcprivacy; path = example/PrivacyInfo.xcprivacy; sourceTree = ""; }; 19F6CBCC0A4E27FBF8BF4A61 /* libPods-example-exampleTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-example-exampleTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 1CB0FE045763B5363B2E7054 /* libPods-example-ci.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-example-ci.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 3B4392A12AC88292D35C810B /* Pods-example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example.debug.xcconfig"; path = "Target Support Files/Pods-example/Pods-example.debug.xcconfig"; sourceTree = ""; }; + 467D00885FA51E1E9B0E8063 /* libPods-example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-example.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 4D1FDAD5197836A927FA2D03 /* Pods-example-ci.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example-ci.debug.xcconfig"; path = "Target Support Files/Pods-example-ci/Pods-example-ci.debug.xcconfig"; sourceTree = ""; }; 5709B34CF0A7D63546082F79 /* Pods-example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example.release.xcconfig"; path = "Target Support Files/Pods-example/Pods-example.release.xcconfig"; sourceTree = ""; }; 5B7EB9410499542E8C5724F5 /* Pods-example-exampleTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example-exampleTests.debug.xcconfig"; path = "Target Support Files/Pods-example-exampleTests/Pods-example-exampleTests.debug.xcconfig"; sourceTree = ""; }; - 5DCACB8F33CDC322A6C60F78 /* libPods-example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-example.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 60493BD72D10D967002853A0 /* example.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = example.entitlements; path = example/example.entitlements; sourceTree = ""; }; 60493BEE2D10E7E4002853A0 /* example-ci.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "example-ci.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 609DDB772D10C20700028574 /* Detox.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Detox.xcodeproj; path = /Users/asafk/Development/Detox/detox/ios/Detox.xcodeproj; sourceTree = ""; }; 609DDBE22D10D45500028574 /* NativeModule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NativeModule.h; sourceTree = ""; }; 609DDBE32D10D45500028574 /* NativeModule.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = NativeModule.mm; sourceTree = ""; }; + 80C930B7D5D6C25DA27372A5 /* Pods-example-ci.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example-ci.release.xcconfig"; path = "Target Support Files/Pods-example-ci/Pods-example-ci.release.xcconfig"; sourceTree = ""; }; 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = example/LaunchScreen.storyboard; sourceTree = ""; }; 89C6BE57DB24E9ADA2F236DE /* Pods-example-exampleTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example-exampleTests.release.xcconfig"; path = "Target Support Files/Pods-example-exampleTests/Pods-example-exampleTests.release.xcconfig"; sourceTree = ""; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; @@ -70,7 +74,8 @@ buildActionMask = 2147483647; files = ( 609DDB892D10C21800028574 /* Detox.framework in Frameworks */, - 0C80B921A6F3F58F76C31292 /* libPods-example.a in Frameworks */, + 0C80B921A6F3F58F76C31292 /* BuildFile in Frameworks */, + 213E1077F464B28612297B3E /* libPods-example.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -78,7 +83,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 60493BE32D10E7E4002853A0 /* libPods-example.a in Frameworks */, + CA25A6405AF58BA742F7FD47 /* libPods-example-ci.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -105,8 +110,9 @@ isa = PBXGroup; children = ( ED297162215061F000B7C4FE /* JavaScriptCore.framework */, - 5DCACB8F33CDC322A6C60F78 /* libPods-example.a */, 19F6CBCC0A4E27FBF8BF4A61 /* libPods-example-exampleTests.a */, + 1CB0FE045763B5363B2E7054 /* libPods-example-ci.a */, + 467D00885FA51E1E9B0E8063 /* libPods-example.a */, ); name = Frameworks; sourceTree = ""; @@ -167,6 +173,8 @@ 5709B34CF0A7D63546082F79 /* Pods-example.release.xcconfig */, 5B7EB9410499542E8C5724F5 /* Pods-example-exampleTests.debug.xcconfig */, 89C6BE57DB24E9ADA2F236DE /* Pods-example-exampleTests.release.xcconfig */, + 4D1FDAD5197836A927FA2D03 /* Pods-example-ci.debug.xcconfig */, + 80C930B7D5D6C25DA27372A5 /* Pods-example-ci.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -343,7 +351,7 @@ outputFileListPaths = ( ); outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-example-checkManifestLockResult.txt", + "$(DERIVED_FILE_DIR)/Pods-example-ci-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -372,15 +380,15 @@ files = ( ); inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks-${CONFIGURATION}-input-files.xcfilelist", + "${PODS_ROOT}/Target Support Files/Pods-example-ci/Pods-example-ci-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks-${CONFIGURATION}-output-files.xcfilelist", + "${PODS_ROOT}/Target Support Files/Pods-example-ci/Pods-example-ci-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example-ci/Pods-example-ci-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; 60493BEA2D10E7E4002853A0 /* [CP] Copy Pods Resources */ = { @@ -389,15 +397,15 @@ files = ( ); inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-resources-${CONFIGURATION}-input-files.xcfilelist", + "${PODS_ROOT}/Target Support Files/Pods-example-ci/Pods-example-ci-resources-${CONFIGURATION}-input-files.xcfilelist", ); name = "[CP] Copy Pods Resources"; outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-resources-${CONFIGURATION}-output-files.xcfilelist", + "${PODS_ROOT}/Target Support Files/Pods-example-ci/Pods-example-ci-resources-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-resources.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example-ci/Pods-example-ci-resources.sh\"\n"; showEnvVarsInLog = 0; }; C38B50BA6285516D6DCD4F65 /* [CP] Check Pods Manifest.lock */ = { @@ -523,7 +531,7 @@ }; 60493BEC2D10E7E4002853A0 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 3B4392A12AC88292D35C810B /* Pods-example.debug.xcconfig */; + baseConfigurationReference = 4D1FDAD5197836A927FA2D03 /* Pods-example-ci.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -552,7 +560,7 @@ }; 60493BED2D10E7E4002853A0 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 5709B34CF0A7D63546082F79 /* Pods-example.release.xcconfig */; + baseConfigurationReference = 80C930B7D5D6C25DA27372A5 /* Pods-example-ci.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -646,7 +654,10 @@ "-DFOLLY_CFG_NO_COROUTINES=1", "-DFOLLY_HAVE_CLOCK_GETTIME=1", ); - OTHER_LDFLAGS = "$(inherited) "; + OTHER_LDFLAGS = ( + "$(inherited)", + " ", + ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG"; @@ -715,7 +726,10 @@ "-DFOLLY_CFG_NO_COROUTINES=1", "-DFOLLY_HAVE_CLOCK_GETTIME=1", ); - OTHER_LDFLAGS = "$(inherited) "; + OTHER_LDFLAGS = ( + "$(inherited)", + " ", + ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; USE_HERMES = true; From bcea8cdc4c71f25ae2de9f75a0900f434fc619b2 Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Tue, 17 Dec 2024 09:22:27 +0200 Subject: [PATCH 24/51] test(ios): update binary path to the non-detox-linked app (example-ci). --- detox/test/e2e/detox.config.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/detox/test/e2e/detox.config.js b/detox/test/e2e/detox.config.js index da4ee2381a..aa105e2969 100644 --- a/detox/test/e2e/detox.config.js +++ b/detox/test/e2e/detox.config.js @@ -52,7 +52,7 @@ const config = { 'ios.debug': { type: 'ios.app', name: 'example', - binaryPath: 'ios/build/Build/Products/Debug-iphonesimulator/example.app', + binaryPath: 'ios/build/Build/Products/Debug-iphonesimulator/example-ci.app', build: 'set -o pipefail && xcodebuild -workspace ios/example.xcworkspace -scheme example-ci -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build -quiet', start: 'react-native start', bundleId: 'com.wix.detox-example', @@ -61,7 +61,7 @@ const config = { 'ios.release': { type: 'ios.app', name: 'example', - binaryPath: 'ios/build/Build/Products/Release-iphonesimulator/example.app', + binaryPath: 'ios/build/Build/Products/Release-iphonesimulator/example-ci.app', build: 'set -o pipefail && export CODE_SIGNING_REQUIRED=NO && export RCT_NO_LAUNCH_PACKAGER=true && xcodebuild -workspace ios/example.xcworkspace -scheme example-ci -configuration Release -sdk iphonesimulator -derivedDataPath ios/build -quiet', }, From abca5b943764c3b085f07dc2a0d88a1c19c71ee3 Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Tue, 17 Dec 2024 12:04:08 +0200 Subject: [PATCH 25/51] fix(ios): temporary remove text fix for predicate. --- detox/ios/Detox/Invocation/Predicate.swift | 8 +- .../ios/Detox/Utilities/NSObject+DontCrash.m | 97 +++++-------------- 2 files changed, 28 insertions(+), 77 deletions(-) diff --git a/detox/ios/Detox/Invocation/Predicate.swift b/detox/ios/Detox/Invocation/Predicate.swift index ffd19d873c..cd264f5194 100644 --- a/detox/ios/Detox/Invocation/Predicate.swift +++ b/detox/ios/Detox/Invocation/Predicate.swift @@ -66,9 +66,7 @@ class Predicate : CustomStringConvertible, CustomDebugStringConvertible { return ValuePredicate(kind: kind, modifiers: modifiers, value: label, requiresAccessibilityElement: true, isRegex: isRegex) } else { //Will crash if RN app and neither class exists - let RCTTextViewClass : AnyClass = - NSClassFromString("RCTParagraphComponentView") ?? - NSClassFromString("RCTText") ?? NSClassFromString("RCTTextView")! + let RCTTextViewClass : AnyClass = NSClassFromString("RCTText") ?? NSClassFromString("RCTTextView")! let descendantPredicate = DescendantPredicate(predicate: AndCompoundPredicate(predicates: [ try KindOfPredicate(kind: Kind.type, modifiers: [], className: NSStringFromClass(RCTTextViewClass)), @@ -91,9 +89,7 @@ class Predicate : CustomStringConvertible, CustomDebugStringConvertible { if ReactNativeSupport.isReactNativeApp == true { //Will crash if RN app and neither class exists - let RCTTextViewClass : AnyClass = - NSClassFromString("RCTParagraphComponentView") ?? - NSClassFromString("RCTText") ?? NSClassFromString("RCTTextView")! + let RCTTextViewClass : AnyClass = NSClassFromString("RCTText") ?? NSClassFromString("RCTTextView")! orPredicates.append(try KindOfPredicate(kind: Kind.type, modifiers: [], className: NSStringFromClass(RCTTextViewClass))) } diff --git a/detox/ios/Detox/Utilities/NSObject+DontCrash.m b/detox/ios/Detox/Utilities/NSObject+DontCrash.m index f880071c2d..eb72d4169c 100644 --- a/detox/ios/Detox/Utilities/NSObject+DontCrash.m +++ b/detox/ios/Detox/Utilities/NSObject+DontCrash.m @@ -7,81 +7,36 @@ #import "NSObject+DontCrash.h" -@interface NSObject (DontCrashPrivate) -+ (Class)dtx_classForName:(NSString *)className; -- (nullable NSString *)dtx_extractTextFromRCTComponent; -@end - @implementation NSObject (DontCrash) -#pragma mark - Public Methods - -- (id)_dtx_text { - if ([self respondsToSelector:@selector(text)]) { - return [(UITextView *)self text]; - } - - NSString *reactText = [self dtx_extractTextFromRCTComponent]; - if (reactText) { - return reactText; - } - - return nil; -} - -- (id)_dtx_placeholder { - if ([self respondsToSelector:@selector(placeholder)]) { - return [(UITextField *)self placeholder]; - } - return nil; -} - -@end - -@implementation NSObject (DontCrashPrivate) - -#pragma mark - Private Methods - -+ (Class)dtx_classForName:(NSString *)className { - static NSMutableDictionary *classCache; - static dispatch_once_t onceToken; - - dispatch_once(&onceToken, ^{ - classCache = [NSMutableDictionary dictionary]; - }); - - Class cachedClass = classCache[className]; - if (!cachedClass) { - cachedClass = NSClassFromString(className); - if (cachedClass) { - classCache[className] = cachedClass; - } - } - - return cachedClass; +- (id)_dtx_text +{ + if([self respondsToSelector:@selector(text)]) + { + return [(UITextView*)self text]; + } + + static Class RCTTextView; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + RCTTextView = NSClassFromString(@"RCTTextView"); + }); + if(RCTTextView != nil && [self isKindOfClass:RCTTextView]) + { + return [(NSTextStorage*)[self valueForKey:@"textStorage"] string]; + } + + return nil; } -- (nullable NSString *)dtx_extractTextFromRCTComponent { - static Class RCTTextViewClass; - static Class RCTParagraphComponentViewClass; - static dispatch_once_t onceToken; - - dispatch_once(&onceToken, ^{ - RCTTextViewClass = [self.class dtx_classForName:@"RCTTextView"]; - RCTParagraphComponentViewClass = [self.class dtx_classForName:@"RCTParagraphComponentView"]; - }); - - if (RCTTextViewClass && [self isKindOfClass:RCTTextViewClass]) { - NSTextStorage *textStorage = [self valueForKey:@"textStorage"]; - return [textStorage string]; - } - - if (RCTParagraphComponentViewClass && [self isKindOfClass:RCTParagraphComponentViewClass]) { - NSAttributedString *attributedText = [self valueForKey:@"attributedText"]; - return [attributedText string]; - } - - return nil; +- (id)_dtx_placeholder +{ + if([self respondsToSelector:@selector(placeholder)]) + { + return [(UITextField*)self placeholder]; + } + + return nil; } @end From 2fd21b06975912d0c8d08da251456107ee884883 Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Wed, 18 Dec 2024 09:00:10 +0200 Subject: [PATCH 26/51] fix(ios): support new `RCTParagraphComponentView` component. --- detox/ios/Detox/Invocation/Predicate.swift | 56 ++++++++++++----- .../ios/Detox/Utilities/NSObject+DontCrash.m | 60 ++++++++++++------- 2 files changed, 78 insertions(+), 38 deletions(-) diff --git a/detox/ios/Detox/Invocation/Predicate.swift b/detox/ios/Detox/Invocation/Predicate.swift index cd264f5194..3dd18f7bd9 100644 --- a/detox/ios/Detox/Invocation/Predicate.swift +++ b/detox/ios/Detox/Invocation/Predicate.swift @@ -65,20 +65,32 @@ class Predicate : CustomStringConvertible, CustomDebugStringConvertible { if ReactNativeSupport.isReactNativeApp == false { return ValuePredicate(kind: kind, modifiers: modifiers, value: label, requiresAccessibilityElement: true, isRegex: isRegex) } else { - //Will crash if RN app and neither class exists - let RCTTextViewClass : AnyClass = NSClassFromString("RCTText") ?? NSClassFromString("RCTTextView")! - - let descendantPredicate = DescendantPredicate(predicate: AndCompoundPredicate(predicates: [ - try KindOfPredicate(kind: Kind.type, modifiers: [], className: NSStringFromClass(RCTTextViewClass)), - ValuePredicate(kind: kind, modifiers: modifiers, value: label, requiresAccessibilityElement: true, isRegex: isRegex) - ], modifiers: []), modifiers: [Modifier.not]) - descendantPredicate.hidden = true - - return AndCompoundPredicate(predicates: [ - ValuePredicate(kind: kind, modifiers: modifiers, value: label, requiresAccessibilityElement: true, isRegex: isRegex), - descendantPredicate - ], modifiers: []) + let possibleRNClasses: [AnyClass] = [ + NSClassFromString("RCTParagraphComponentView"), + NSClassFromString("RCTText"), + NSClassFromString("RCTTextView") + ].compactMap { $0 } + + guard !possibleRNClasses.isEmpty else { + fatalError("No React Native text component classes found") + } + + let typePredicates = possibleRNClasses.map { rnClass in + try! KindOfPredicate(kind: Kind.type, modifiers: [], className: NSStringFromClass(rnClass)) + } + + let descendantPredicate = DescendantPredicate(predicate: AndCompoundPredicate(predicates: [ + OrCompoundPredicate(predicates: typePredicates, modifiers: []), + ValuePredicate(kind: kind, modifiers: modifiers, value: label, requiresAccessibilityElement: true, isRegex: isRegex) + ], modifiers: []), modifiers: [Modifier.not]) + descendantPredicate.hidden = true + + return AndCompoundPredicate(predicates: [ + ValuePredicate(kind: kind, modifiers: modifiers, value: label, requiresAccessibilityElement: true, isRegex: isRegex), + descendantPredicate + ], modifiers: []) } + case Kind.text: let text = dictionaryRepresentation[Keys.value] as! String var orPredicates = [ @@ -88,9 +100,21 @@ class Predicate : CustomStringConvertible, CustomDebugStringConvertible { ] if ReactNativeSupport.isReactNativeApp == true { - //Will crash if RN app and neither class exists - let RCTTextViewClass : AnyClass = NSClassFromString("RCTText") ?? NSClassFromString("RCTTextView")! - orPredicates.append(try KindOfPredicate(kind: Kind.type, modifiers: [], className: NSStringFromClass(RCTTextViewClass))) + let possibleRNClasses: [AnyClass] = [ + NSClassFromString("RCTParagraphComponentView"), + NSClassFromString("RCTText"), + NSClassFromString("RCTTextView") + ].compactMap { $0 } + + guard !possibleRNClasses.isEmpty else { + fatalError("No React Native text component classes found") + } + + possibleRNClasses.forEach { rnClass in + let predicate = try! KindOfPredicate(kind: Kind.type, modifiers: [], className: NSStringFromClass(rnClass)) + + orPredicates.append(predicate) + } } let orCompoundPredicate = OrCompoundPredicate(predicates: orPredicates, modifiers: []) diff --git a/detox/ios/Detox/Utilities/NSObject+DontCrash.m b/detox/ios/Detox/Utilities/NSObject+DontCrash.m index eb72d4169c..d78f254cfc 100644 --- a/detox/ios/Detox/Utilities/NSObject+DontCrash.m +++ b/detox/ios/Detox/Utilities/NSObject+DontCrash.m @@ -11,32 +11,48 @@ @implementation NSObject (DontCrash) - (id)_dtx_text { - if([self respondsToSelector:@selector(text)]) - { - return [(UITextView*)self text]; - } - - static Class RCTTextView; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - RCTTextView = NSClassFromString(@"RCTTextView"); - }); - if(RCTTextView != nil && [self isKindOfClass:RCTTextView]) - { - return [(NSTextStorage*)[self valueForKey:@"textStorage"] string]; - } - - return nil; + if([self respondsToSelector:@selector(text)]) + { + return [(UITextView*)self text]; + } + + static Class RCTParagraphComponentViewClass; + static Class RCTTextClass; + static Class RCTTextViewClass; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + RCTParagraphComponentViewClass = NSClassFromString(@"RCTParagraphComponentView"); + RCTTextClass = NSClassFromString(@"RCTText"); + RCTTextViewClass = NSClassFromString(@"RCTTextView"); + }); + + if(RCTParagraphComponentViewClass != nil && [self isKindOfClass:RCTParagraphComponentViewClass]) + { + NSAttributedString *attributedText = [self valueForKey:@"attributedText"]; + return [attributedText string]; + } + + if(RCTTextClass != nil && [self isKindOfClass:RCTTextClass]) + { + return [(NSTextStorage*)[self valueForKey:@"textStorage"] string]; + } + + if(RCTTextViewClass != nil && [self isKindOfClass:RCTTextViewClass]) + { + return [(NSTextStorage*)[self valueForKey:@"textStorage"] string]; + } + + return nil; } - (id)_dtx_placeholder { - if([self respondsToSelector:@selector(placeholder)]) - { - return [(UITextField*)self placeholder]; - } - - return nil; + if([self respondsToSelector:@selector(placeholder)]) + { + return [(UITextField*)self placeholder]; + } + + return nil; } @end From 824c053febb68b5f419123d9b1d932d8cd7bf363 Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Sun, 22 Dec 2024 17:42:13 +0200 Subject: [PATCH 27/51] test: enable new arch from the Podfile. --- detox/test/ios/Podfile | 2 +- detox/test/ios/example.xcodeproj/project.pbxproj | 14 ++++---------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/detox/test/ios/Podfile b/detox/test/ios/Podfile index cacd6701d5..bb5256f319 100644 --- a/detox/test/ios/Podfile +++ b/detox/test/ios/Podfile @@ -1,4 +1,4 @@ -ENV['RCT_NEW_ARCH_ENABLED'] = '0' +ENV['RCT_NEW_ARCH_ENABLED'] = ENV['RCT_NEW_ARCH_ENABLED'] || 0 if ENV["REACT_NATIVE_VERSION"] && ENV["REACT_NATIVE_VERSION"].match(/0.(70|71).*/) require_relative '../node_modules/react-native/scripts/react_native_pods' diff --git a/detox/test/ios/example.xcodeproj/project.pbxproj b/detox/test/ios/example.xcodeproj/project.pbxproj index 856f9965fe..01f8a263ec 100644 --- a/detox/test/ios/example.xcodeproj/project.pbxproj +++ b/detox/test/ios/example.xcodeproj/project.pbxproj @@ -7,7 +7,7 @@ objects = { /* Begin PBXBuildFile section */ - 0C80B921A6F3F58F76C31292 /* BuildFile in Frameworks */ = {isa = PBXBuildFile; }; + 0C80B921A6F3F58F76C31292 /* (null) in Frameworks */ = {isa = PBXBuildFile; }; 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; @@ -74,7 +74,7 @@ buildActionMask = 2147483647; files = ( 609DDB892D10C21800028574 /* Detox.framework in Frameworks */, - 0C80B921A6F3F58F76C31292 /* BuildFile in Frameworks */, + 0C80B921A6F3F58F76C31292 /* (null) in Frameworks */, 213E1077F464B28612297B3E /* libPods-example.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -654,10 +654,7 @@ "-DFOLLY_CFG_NO_COROUTINES=1", "-DFOLLY_HAVE_CLOCK_GETTIME=1", ); - OTHER_LDFLAGS = ( - "$(inherited)", - " ", - ); + OTHER_LDFLAGS = "$(inherited) "; REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG"; @@ -726,10 +723,7 @@ "-DFOLLY_CFG_NO_COROUTINES=1", "-DFOLLY_HAVE_CLOCK_GETTIME=1", ); - OTHER_LDFLAGS = ( - "$(inherited)", - " ", - ); + OTHER_LDFLAGS = "$(inherited) "; REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; USE_HERMES = true; From e5f012734e13e8039e124e90e3bf4f9412d3d97b Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Sun, 22 Dec 2024 17:47:46 +0200 Subject: [PATCH 28/51] test(RN .76): update view-hierarchy snapshot tests. --- .../assets/view-hierarchy-web-view.76.ios.txt | 65 +++++++++++++++++++ ...ierarchy-with-test-id-injection.76.ios.txt | 24 +++++++ ...archy-without-test-id-injection.76.ios.txt | 24 +++++++ 3 files changed, 113 insertions(+) create mode 100644 detox/test/e2e/assets/view-hierarchy-web-view.76.ios.txt create mode 100644 detox/test/e2e/assets/view-hierarchy-with-test-id-injection.76.ios.txt create mode 100644 detox/test/e2e/assets/view-hierarchy-without-test-id-injection.76.ios.txt diff --git a/detox/test/e2e/assets/view-hierarchy-web-view.76.ios.txt b/detox/test/e2e/assets/view-hierarchy-web-view.76.ios.txt new file mode 100644 index 0000000000..135ffe0a28 --- /dev/null +++ b/detox/test/e2e/assets/view-hierarchy-web-view.76.ios.txt @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

First Webview

+

Form

+
+
+
+ +
+ +

Form Results

+

Your first name is: No input yet

+ +

Content Editable

+
Name:
+ +

Text and link

+

Some text and a link.

+

This is a bottom paragraph with class.

+ + +]]> +
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file diff --git a/detox/test/e2e/assets/view-hierarchy-with-test-id-injection.76.ios.txt b/detox/test/e2e/assets/view-hierarchy-with-test-id-injection.76.ios.txt new file mode 100644 index 0000000000..678cc758d9 --- /dev/null +++ b/detox/test/e2e/assets/view-hierarchy-with-test-id-injection.76.ios.txt @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/detox/test/e2e/assets/view-hierarchy-without-test-id-injection.76.ios.txt b/detox/test/e2e/assets/view-hierarchy-without-test-id-injection.76.ios.txt new file mode 100644 index 0000000000..03777012f2 --- /dev/null +++ b/detox/test/e2e/assets/view-hierarchy-without-test-id-injection.76.ios.txt @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From fc625fdf062e6adc7c0fe2b04f2cab0979991fed Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Mon, 23 Dec 2024 16:49:51 +0200 Subject: [PATCH 29/51] fix(iOS): fix implicit conversion of Integer in Podfile. --- detox/test/ios/Podfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/detox/test/ios/Podfile b/detox/test/ios/Podfile index bb5256f319..87c22bc8e3 100644 --- a/detox/test/ios/Podfile +++ b/detox/test/ios/Podfile @@ -1,4 +1,4 @@ -ENV['RCT_NEW_ARCH_ENABLED'] = ENV['RCT_NEW_ARCH_ENABLED'] || 0 +ENV['RCT_NEW_ARCH_ENABLED'] = ENV['RCT_NEW_ARCH_ENABLED'] || '0' if ENV["REACT_NATIVE_VERSION"] && ENV["REACT_NATIVE_VERSION"].match(/0.(70|71).*/) require_relative '../node_modules/react-native/scripts/react_native_pods' From 263a85fd227859f221809c7d1c8ff2efbb8f7240 Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Mon, 23 Dec 2024 21:12:33 +0200 Subject: [PATCH 30/51] fix(ios): remove `RCT_NEW_ARCH_ENABLED` assignment from Podfile. not needed as can be empty for no-new-arch. --- detox/test/ios/Podfile | 2 -- 1 file changed, 2 deletions(-) diff --git a/detox/test/ios/Podfile b/detox/test/ios/Podfile index 87c22bc8e3..6bf92b7c5e 100644 --- a/detox/test/ios/Podfile +++ b/detox/test/ios/Podfile @@ -1,5 +1,3 @@ -ENV['RCT_NEW_ARCH_ENABLED'] = ENV['RCT_NEW_ARCH_ENABLED'] || '0' - if ENV["REACT_NATIVE_VERSION"] && ENV["REACT_NATIVE_VERSION"].match(/0.(70|71).*/) require_relative '../node_modules/react-native/scripts/react_native_pods' require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' From b5bd5b4dec7ecfa728d9eb2e8184af338970ae42 Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Wed, 25 Dec 2024 15:44:35 +0200 Subject: [PATCH 31/51] fix: make keyboard tests pass on legacy arch. --- detox/test/ios/AppDelegate.swift | 127 ++++++++++++ detox/test/ios/ReactModules/NativeModule.h | 6 - detox/test/ios/ReactModules/NativeModule.mm | 168 --------------- .../test/ios/ReactModules/NativeModule.swift | 196 ++++++++++++++++++ .../ios/ReactModules/ReactModulesBridge.m | 36 ++++ .../ios/ReactModules/ShakeEventEmitter.swift | 28 +++ .../test/ios/UI/CustomKeyboardDelegate.swift | 110 ++++++++++ detox/test/ios/UI/NativeScreenManager.swift | 49 +++++ detox/test/ios/example-Bridging-Header.h | 4 + detox/test/ios/example-ci-Bridging-Header.h | 4 + .../ios/example.xcodeproj/project.pbxproj | 95 ++++++--- detox/test/ios/example/AppDelegate.h | 6 - detox/test/ios/example/AppDelegate.mm | 108 ---------- detox/test/ios/example/main.m | 10 - 14 files changed, 617 insertions(+), 330 deletions(-) create mode 100644 detox/test/ios/AppDelegate.swift delete mode 100644 detox/test/ios/ReactModules/NativeModule.h delete mode 100644 detox/test/ios/ReactModules/NativeModule.mm create mode 100644 detox/test/ios/ReactModules/NativeModule.swift create mode 100644 detox/test/ios/ReactModules/ReactModulesBridge.m create mode 100644 detox/test/ios/ReactModules/ShakeEventEmitter.swift create mode 100644 detox/test/ios/UI/CustomKeyboardDelegate.swift create mode 100644 detox/test/ios/UI/NativeScreenManager.swift create mode 100644 detox/test/ios/example-Bridging-Header.h create mode 100644 detox/test/ios/example-ci-Bridging-Header.h delete mode 100644 detox/test/ios/example/AppDelegate.h delete mode 100644 detox/test/ios/example/AppDelegate.mm delete mode 100644 detox/test/ios/example/main.m diff --git a/detox/test/ios/AppDelegate.swift b/detox/test/ios/AppDelegate.swift new file mode 100644 index 0000000000..026c6c77c6 --- /dev/null +++ b/detox/test/ios/AppDelegate.swift @@ -0,0 +1,127 @@ +import UIKit +import React +import UserNotifications +import CoreSpotlight + +// MARK: - App Delegate + +@UIApplicationMain +@objc(AppDelegate) +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + var moduleName: String = "example" + var initialProps: [String: Any] = [:] + private var screenManager: NativeScreenManaging? + + // MARK: - UIApplicationDelegate Methods + + func application(_ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + + setupReactNative(with: launchOptions) + setupNotifications() + setupScreenManager() + + return true + } + + // MARK: - Setup Methods + + private func setupReactNative(with launchOptions: [UIApplication.LaunchOptionsKey: Any]?) { + let bridge = RCTBridge(delegate: self, launchOptions: launchOptions) + let rootView = RCTRootView(bridge: bridge!, + moduleName: moduleName, + initialProperties: initialProps) + rootView.backgroundColor = .white + + let rootViewController = UIViewController() + rootViewController.view = rootView + + window = UIWindow(frame: UIScreen.main.bounds) + window?.rootViewController = rootViewController + window?.makeKeyAndVisible() + } + + private func setupNotifications() { + UNUserNotificationCenter.current().delegate = self + + NotificationCenter.default.addObserver( + forName: Notification.Name("ChangeScreen"), + object: nil, + queue: nil + ) { [weak self] notification in + self?.screenManager?.handle(notification) + } + } + + private func setupScreenManager() { + screenManager = NativeScreenManager(window: window) + } +} + +// MARK: - Shake Gesture Handling + +extension AppDelegate { + override func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) { + if motion == .motionShake { + if let rootView = window?.rootViewController?.view as? RCTRootView { + let bridge = rootView.bridge + let shakeModule = bridge.module(for: ShakeEventEmitter.self) as! ShakeEventEmitter + shakeModule.handleShake() + } + } else { + super.motionEnded(motion, with: event) + } + } +} + +// MARK: - URL and Universal Links Handling + +extension AppDelegate { + func application(_ app: UIApplication, + open url: URL, + options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { + return RCTLinkingManager.application(app, open: url, options: options) + } + + func application(_ application: UIApplication, + continue userActivity: NSUserActivity, + restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { + return RCTLinkingManager.application(application, + continue: userActivity, + restorationHandler: restorationHandler) + } +} + +// MARK: - UNUserNotificationCenterDelegate +extension AppDelegate: UNUserNotificationCenterDelegate { + func userNotificationCenter(_ center: UNUserNotificationCenter, + willPresent notification: UNNotification, + withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { + completionHandler([.list, .banner, .badge, .sound]) + } + + func userNotificationCenter(_ center: UNUserNotificationCenter, + didReceive response: UNNotificationResponse, + withCompletionHandler completionHandler: @escaping () -> Void) { + completionHandler() + } +} + +// MARK: - RCTBridgeDelegate + +extension AppDelegate: RCTBridgeDelegate { + func sourceURL(for bridge: RCTBridge) -> URL? { + return bundleURL() + } + + private func bundleURL() -> URL { +#if DEBUG + return RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index", + fallbackExtension: nil)! +#else + return Bundle.main.url(forResource: "main", withExtension: "jsbundle")! +#endif + } +} diff --git a/detox/test/ios/ReactModules/NativeModule.h b/detox/test/ios/ReactModules/NativeModule.h deleted file mode 100644 index 7bde08a0f8..0000000000 --- a/detox/test/ios/ReactModules/NativeModule.h +++ /dev/null @@ -1,6 +0,0 @@ -#import -#import - -@interface NativeModule : NSObject - -@end diff --git a/detox/test/ios/ReactModules/NativeModule.mm b/detox/test/ios/ReactModules/NativeModule.mm deleted file mode 100644 index bd80781919..0000000000 --- a/detox/test/ios/ReactModules/NativeModule.mm +++ /dev/null @@ -1,168 +0,0 @@ -#import "NativeModule.h" -#import -#import - -@interface NativeModule () -@property (nonatomic, strong) UIWindow *overlayWindow; -@property (nonatomic, strong) UIView *overlayView; -@property (nonatomic, assign) NSInteger callCounter; -@end - -@implementation NativeModule - -RCT_EXPORT_MODULE(); - -#pragma mark - Lifecycle Methods - -- (instancetype)init { - self = [super init]; - if (self) { - _callCounter = 0; - } - return self; -} - -#pragma mark - Echo Methods - -RCT_EXPORT_METHOD(echoWithoutResponse:(NSString *)str) { - self.callCounter++; -} - -RCT_EXPORT_METHOD(echoWithResponse:(NSString *)str - resolver:(RCTPromiseResolveBlock)resolve - rejecter:(RCTPromiseRejectBlock)reject) { - self.callCounter++; - resolve(str); -} - -#pragma mark - Timing Methods - -RCT_EXPORT_METHOD(nativeSetTimeout:(NSTimeInterval)delay - block:(RCTResponseSenderBlock)block) { - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), - dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - [self executeOnMainThread:^{ - block(@[]); - }]; - }); -} - -#pragma mark - Navigation Methods - -RCT_EXPORT_METHOD(switchToNativeRoot) { - [self executeOnMainThread:^{ - UIViewController *newRoot = [self createNativeRootViewController]; - [self updateRootViewController:newRoot]; - }]; -} - -RCT_EXPORT_METHOD(switchToMultipleReactRoots) { - [self executeOnMainThread:^{ - UITabBarController *tabController = [self createTabBarControllerWithBridge]; - [self updateRootViewController:tabController]; - }]; -} - -#pragma mark - Notification Methods - -RCT_EXPORT_METHOD(sendNotification:(NSString*)notification - name:(NSString*)name) { - [self executeOnMainThread:^{ - [NSNotificationCenter.defaultCenter postNotificationName:notification - object:nil - userInfo:@{@"name": name}]; - }]; -} - -#pragma mark - Overlay Methods - -RCT_EXPORT_METHOD(presentOverlayWindow) { - [self executeOnMainThread:^{ - [self setupAndShowOverlayWindow]; - }]; -} - -RCT_EXPORT_METHOD(presentOverlayView) { - [self executeOnMainThread:^{ - [self setupAndShowOverlayView]; - }]; -} - -#pragma mark - Private Helper Methods - -- (void)executeOnMainThread:(void (^)(void))block { - if ([NSThread isMainThread]) { - block(); - } else { - dispatch_async(dispatch_get_main_queue(), block); - } -} - -- (UIViewController *)createNativeRootViewController { - UIViewController *newRoot = [UIViewController new]; - newRoot.view.backgroundColor = UIColor.whiteColor; - - UILabel *label = [UILabel new]; - label.text = @"this is a new native root"; - [label sizeToFit]; - [newRoot.view addSubview:label]; - label.center = newRoot.view.center; - - return newRoot; -} - -- (UITabBarController *)createTabBarControllerWithBridge { - RCTBridge *bridge = [self getCurrentBridge]; - NSArray *viewControllers = @[ - [self createReactRootViewController:bridge title:@"1"], - [self createReactRootViewController:bridge title:@"2"], - [self createReactRootViewController:bridge title:@"3"], - [self createReactRootViewController:bridge title:@"4"] - ]; - - UITabBarController *tabController = [UITabBarController new]; - tabController.viewControllers = viewControllers; - return tabController; -} - -- (UIViewController *)createReactRootViewController:(RCTBridge *)bridge - title:(NSString *)title { - UIViewController *viewController = [UIViewController new]; - viewController.view = [[RCTRootView alloc] initWithBridge:bridge - moduleName:@"example" - initialProperties:nil]; - viewController.tabBarItem.title = title; - return viewController; -} - -- (RCTBridge *)getCurrentBridge { - id delegate = UIApplication.sharedApplication.delegate; - return ((RCTRootView *)delegate.window.rootViewController.view).bridge; -} - -- (void)updateRootViewController:(UIViewController *)viewController { - id delegate = UIApplication.sharedApplication.delegate; - [delegate.window setRootViewController:viewController]; - [delegate.window makeKeyAndVisible]; -} - -- (void)setupAndShowOverlayWindow { - CGRect screenBounds = UIScreen.mainScreen.bounds; - self.overlayWindow = [[UIWindow alloc] initWithFrame:screenBounds]; - self.overlayWindow.accessibilityIdentifier = @"OverlayWindow"; - [self.overlayWindow setWindowLevel:UIWindowLevelStatusBar]; - [self.overlayWindow setHidden:NO]; - [self.overlayWindow makeKeyAndVisible]; -} - -- (void)setupAndShowOverlayView { - CGRect screenBounds = UIScreen.mainScreen.bounds; - self.overlayView = [[UIView alloc] initWithFrame:screenBounds]; - self.overlayView.userInteractionEnabled = YES; - self.overlayView.accessibilityIdentifier = @"OverlayView"; - - UIWindow *keyWindow = UIApplication.sharedApplication.keyWindow; - [keyWindow addSubview:self.overlayView]; -} - -@end diff --git a/detox/test/ios/ReactModules/NativeModule.swift b/detox/test/ios/ReactModules/NativeModule.swift new file mode 100644 index 0000000000..9fd6856ef4 --- /dev/null +++ b/detox/test/ios/ReactModules/NativeModule.swift @@ -0,0 +1,196 @@ +// +// NativeModule.swift (example) +// Created by Asaf Korem (Wix.com) on 2024. +// + +import Foundation +import React +import UIKit + +@objc(NativeModule) +class NativeModule: NSObject, RCTBridgeModule { + + // MARK: - Properties + + var overlayWindow: UIWindow? + var overlayView: UIView? + var callCounter: Int = 0 + + // MARK: - RCTBridgeModule + + static func moduleName() -> String! { + return "NativeModule" + } + + static func requiresMainQueueSetup() -> Bool { + // Indicates that the module must be initialized on the main thread + return true + } + + // MARK: - Lifecycle Methods + + override init() { + super.init() + self.callCounter = 0 + } + + // MARK: - Echo Methods + + @objc + func echoWithoutResponse(_ str: String) { + self.callCounter += 1 + } + + @objc + func echoWithResponse(_ str: String, + resolver resolve: @escaping RCTPromiseResolveBlock, + rejecter reject: @escaping RCTPromiseRejectBlock) { + self.callCounter += 1 + resolve(str) + } + + // MARK: - Timing Methods + + @objc + func nativeSetTimeout(_ delay: TimeInterval, + block: @escaping RCTResponseSenderBlock) { + let dispatchTime = DispatchTime.now() + delay + DispatchQueue.global(qos: .default).asyncAfter(deadline: dispatchTime) { [weak self] in + self?.executeOnMainThread { + block([]) + } + } + } + + // MARK: - Navigation Methods + + @objc + func switchToNativeRoot() { + executeOnMainThread { [weak self] in + guard let self = self else { return } + let newRoot = self.createNativeRootViewController() + self.updateRootViewController(newRoot) + } + } + + @objc + func switchToMultipleReactRoots() { + executeOnMainThread { [weak self] in + guard let self = self else { return } + let tabController = self.createTabBarControllerWithBridge() + self.updateRootViewController(tabController) + } + } + + // MARK: - Notification Methods + + @objc + func sendNotification(_ notification: String, name: String) { + executeOnMainThread { + NotificationCenter.default.post(name: Notification.Name(notification), + object: nil, + userInfo: ["name": name]) + } + } + + // MARK: - Overlay Methods + + @objc + func presentOverlayWindow() { + executeOnMainThread { [weak self] in + self?.setupAndShowOverlayWindow() + } + } + + @objc + func presentOverlayView() { + executeOnMainThread { [weak self] in + self?.setupAndShowOverlayView() + } + } + + // MARK: - Private Helper Methods + + private func executeOnMainThread(_ block: @escaping () -> Void) { + if Thread.isMainThread { + block() + } else { + DispatchQueue.main.async { + block() + } + } + } + + private func createNativeRootViewController() -> UIViewController { + let newRoot = UIViewController() + newRoot.view.backgroundColor = .white + + let label = UILabel() + label.text = "This is a new native root" + label.sizeToFit() + label.center = newRoot.view.center + newRoot.view.addSubview(label) + + return newRoot + } + + private func createTabBarControllerWithBridge() -> UITabBarController { + guard let bridge = getCurrentBridge() else { + fatalError("RCTBridge is not available") + } + + let viewControllers = [ + createReactRootViewController(bridge: bridge, title: "1"), + createReactRootViewController(bridge: bridge, title: "2"), + createReactRootViewController(bridge: bridge, title: "3"), + createReactRootViewController(bridge: bridge, title: "4") + ] + + let tabController = UITabBarController() + tabController.viewControllers = viewControllers + return tabController + } + + private func createReactRootViewController(bridge: RCTBridge, title: String) -> UIViewController { + let viewController = UIViewController() + viewController.view = RCTRootView(bridge: bridge, moduleName: "example", initialProperties: nil) + viewController.tabBarItem.title = title + return viewController + } + + private func getCurrentBridge() -> RCTBridge? { + guard let delegate = UIApplication.shared.delegate as? AppDelegate, + let window = delegate.window, + let rootView = window.rootViewController?.view as? RCTRootView else { + return nil + } + return rootView.bridge + } + + private func updateRootViewController(_ viewController: UIViewController) { + guard let delegate = UIApplication.shared.delegate as? AppDelegate, + let window = delegate.window else { + return + } + window.rootViewController = viewController + window.makeKeyAndVisible() + } + + private func setupAndShowOverlayWindow() { + let screenBounds = UIScreen.main.bounds + overlayWindow = UIWindow(frame: screenBounds) + overlayWindow?.accessibilityIdentifier = "OverlayWindow" + overlayWindow?.windowLevel = UIWindow.Level.statusBar + overlayWindow?.isHidden = false + overlayWindow?.makeKeyAndVisible() + } + + private func setupAndShowOverlayView() { + guard let keyWindow = UIApplication.shared.keyWindow else { return } + let screenBounds = UIScreen.main.bounds + overlayView = UIView(frame: screenBounds) + overlayView?.isUserInteractionEnabled = true + overlayView?.accessibilityIdentifier = "OverlayView" + keyWindow.addSubview(overlayView!) + } +} diff --git a/detox/test/ios/ReactModules/ReactModulesBridge.m b/detox/test/ios/ReactModules/ReactModulesBridge.m new file mode 100644 index 0000000000..8cd211819a --- /dev/null +++ b/detox/test/ios/ReactModules/ReactModulesBridge.m @@ -0,0 +1,36 @@ +// +// ReactModulesBridge.m (example) +// Created by Asaf Korem (Wix.com) on 2024. +// + +#import "React/RCTBridgeModule.h" +#import "React/RCTEventEmitter.h" +#import "React/RCTViewManager.h" + +@interface RCT_EXTERN_MODULE(ShakeEventEmitter, RCTEventEmitter) + +@end + +@interface RCT_EXTERN_MODULE(NativeModule, NSObject) + +RCT_EXTERN_METHOD(echoWithoutResponse:(NSString *)str) + +RCT_EXTERN_METHOD(echoWithResponse:(NSString *)str + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(nativeSetTimeout:(double)delay + block:(RCTResponseSenderBlock)block) + +RCT_EXTERN_METHOD(switchToNativeRoot) + +RCT_EXTERN_METHOD(switchToMultipleReactRoots) + +RCT_EXTERN_METHOD(sendNotification:(NSString *)notification + name:(NSString *)name) + +RCT_EXTERN_METHOD(presentOverlayWindow) + +RCT_EXTERN_METHOD(presentOverlayView) + +@end diff --git a/detox/test/ios/ReactModules/ShakeEventEmitter.swift b/detox/test/ios/ReactModules/ShakeEventEmitter.swift new file mode 100644 index 0000000000..400488cdcc --- /dev/null +++ b/detox/test/ios/ReactModules/ShakeEventEmitter.swift @@ -0,0 +1,28 @@ +// +// ShakeEventEmitter.swift (example) +// Created by Asaf Korem (Wix.com) on 2024. +// + +import Foundation +import React + +@objc(ShakeEventEmitter) +class ShakeEventEmitter: RCTEventEmitter { + + // MARK: - RCTEventEmitter Overrides + + override static func requiresMainQueueSetup() -> Bool { + return true + } + + override func supportedEvents() -> [String]! { + return ["ShakeEvent"] + } + + // MARK: - Public Methods + + @objc + func handleShake() { + sendEvent(withName: "ShakeEvent", body: nil) + } +} diff --git a/detox/test/ios/UI/CustomKeyboardDelegate.swift b/detox/test/ios/UI/CustomKeyboardDelegate.swift new file mode 100644 index 0000000000..8a9a4d548d --- /dev/null +++ b/detox/test/ios/UI/CustomKeyboardDelegate.swift @@ -0,0 +1,110 @@ +// +// CustomKeyboardDelegate.swift (example) +// Created by Asaf Korem (Wix.com) on 2024. +// + + +import UIKit + +protocol CustomKeyboardDelegate: AnyObject { + func customKeyboardTappedButton(_ sender: CustomKeyboardView) +} + +class CustomKeyboardView: UIView { + weak var delegate: CustomKeyboardDelegate? + + override init(frame: CGRect) { + super.init(frame: frame) + loadView() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + loadView() + } + + func loadView() { + let kbButton = UIButton(type: .custom) + kbButton.translatesAutoresizingMaskIntoConstraints = false + kbButton.setTitle("Hello", for: .normal) + kbButton.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside) + kbButton.accessibilityIdentifier = "keyboardHelloButton" + + addSubview(kbButton) + + NSLayoutConstraint.activate([ + kbButton.widthAnchor.constraint(greaterThanOrEqualToConstant: 44), + kbButton.heightAnchor.constraint(equalToConstant: 44), + kbButton.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20), + kbButton.topAnchor.constraint(equalTo: topAnchor, constant: 20) + ]) + } + + @objc private func buttonTapped(_ sender: Any) { + delegate?.customKeyboardTappedButton(self) + } +} + +class CustomKeyboardViewController: UIViewController { + private var textField: UITextField! + + override func viewDidLoad() { + super.viewDidLoad() + + view.backgroundColor = .systemBackground + + let closeButton = UIButton(type: .system) + closeButton.setImage(UIImage(systemName: "xmark.circle.fill"), for: .normal) + closeButton.translatesAutoresizingMaskIntoConstraints = false + closeButton.accessibilityIdentifier = "closeButton" + closeButton.addTarget(self, action: #selector(close), for: .primaryActionTriggered) + + let inputView = CustomKeyboardView() + inputView.translatesAutoresizingMaskIntoConstraints = false + inputView.delegate = self + inputView.backgroundColor = .lightGray + + let text = UITextField() + text.translatesAutoresizingMaskIntoConstraints = false + text.inputView = inputView + text.borderStyle = .roundedRect + text.accessibilityIdentifier = "textWithCustomInput" + + let obscuredLabel = UILabel() + obscuredLabel.translatesAutoresizingMaskIntoConstraints = false + obscuredLabel.text = "Obscured by keyboard" + + textField = text + + view.addSubview(closeButton) + view.addSubview(text) + view.addSubview(obscuredLabel) + + NSLayoutConstraint.activate([ + text.heightAnchor.constraint(equalToConstant: 50), + text.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 20), + view.safeAreaLayoutGuide.trailingAnchor.constraint(equalTo: text.trailingAnchor, constant: 20), + text.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 50), + + inputView.widthAnchor.constraint(equalToConstant: view.frame.size.width), + + view.layoutMarginsGuide.trailingAnchor.constraint(equalTo: closeButton.trailingAnchor), + view.layoutMarginsGuide.topAnchor.constraint(equalTo: closeButton.topAnchor), + + obscuredLabel.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 20), + view.safeAreaLayoutGuide.trailingAnchor.constraint(greaterThanOrEqualTo: obscuredLabel.trailingAnchor, constant: 20), + view.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: obscuredLabel.bottomAnchor, constant: 50) + ]) + } + + @objc private func close() { + presentingViewController?.dismiss(animated: true) + } +} + +// MARK: - CustomKeyboardDelegate +extension CustomKeyboardViewController: CustomKeyboardDelegate { + func customKeyboardTappedButton(_ sender: CustomKeyboardView) { + textField.text = "World!" + } +} diff --git a/detox/test/ios/UI/NativeScreenManager.swift b/detox/test/ios/UI/NativeScreenManager.swift new file mode 100644 index 0000000000..cda456a9c7 --- /dev/null +++ b/detox/test/ios/UI/NativeScreenManager.swift @@ -0,0 +1,49 @@ +// +// NativeScreenManager.swift (example) +// Created by Asaf Korem (Wix.com) on 2024. +// + +import UIKit + +enum NativeScreen { + case customKeyboard + + var viewController: UIViewController { + switch self { + case .customKeyboard: + let vc = CustomKeyboardViewController() + vc.modalPresentationStyle = .fullScreen + return vc + } + } +} + +protocol NativeScreenManaging { + func present(_ screen: NativeScreen, from: UIViewController?, animated: Bool) + func handle(_ notification: Notification) +} + +class NativeScreenManager: NativeScreenManaging { + weak var window: UIWindow? + + init(window: UIWindow?) { + self.window = window + } + + func present(_ screen: NativeScreen, from: UIViewController?, animated: Bool) { + let presentingVC = from ?? window?.rootViewController + let viewController = screen.viewController + presentingVC?.present(viewController, animated: animated) + } + + func handle(_ notification: Notification) { + guard let name = notification.userInfo?["name"] as? String else { return } + + switch name { + case "customKeyboard": + present(.customKeyboard, from: nil, animated: true) + default: + print("Unknown screen: \(name)") + } + } +} diff --git a/detox/test/ios/example-Bridging-Header.h b/detox/test/ios/example-Bridging-Header.h new file mode 100644 index 0000000000..1b2cb5d6d0 --- /dev/null +++ b/detox/test/ios/example-Bridging-Header.h @@ -0,0 +1,4 @@ +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// + diff --git a/detox/test/ios/example-ci-Bridging-Header.h b/detox/test/ios/example-ci-Bridging-Header.h new file mode 100644 index 0000000000..1b2cb5d6d0 --- /dev/null +++ b/detox/test/ios/example-ci-Bridging-Header.h @@ -0,0 +1,4 @@ +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// + diff --git a/detox/test/ios/example.xcodeproj/project.pbxproj b/detox/test/ios/example.xcodeproj/project.pbxproj index 01f8a263ec..fcf919e2c3 100644 --- a/detox/test/ios/example.xcodeproj/project.pbxproj +++ b/detox/test/ios/example.xcodeproj/project.pbxproj @@ -7,19 +7,24 @@ objects = { /* Begin PBXBuildFile section */ - 0C80B921A6F3F58F76C31292 /* (null) in Frameworks */ = {isa = PBXBuildFile; }; - 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; - 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; 213E1077F464B28612297B3E /* libPods-example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 467D00885FA51E1E9B0E8063 /* libPods-example.a */; }; - 60493BDE2D10E7E4002853A0 /* NativeModule.mm in Sources */ = {isa = PBXBuildFile; fileRef = 609DDBE32D10D45500028574 /* NativeModule.mm */; }; - 60493BDF2D10E7E4002853A0 /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; }; - 60493BE02D10E7E4002853A0 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; 60493BE52D10E7E4002853A0 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; 60493BE62D10E7E4002853A0 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 60493BE72D10E7E4002853A0 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */; }; + 608868A02D1A9F070070D199 /* NativeModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6088689D2D1A9F070070D199 /* NativeModule.swift */; }; + 608868A12D1A9F070070D199 /* ShakeEventEmitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6088689E2D1A9F070070D199 /* ShakeEventEmitter.swift */; }; + 608868A22D1A9F070070D199 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6088689A2D1A9F070070D199 /* AppDelegate.swift */; }; + 608868A32D1A9F070070D199 /* NativeModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6088689D2D1A9F070070D199 /* NativeModule.swift */; }; + 608868A42D1A9F070070D199 /* ShakeEventEmitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6088689E2D1A9F070070D199 /* ShakeEventEmitter.swift */; }; + 608868A52D1A9F070070D199 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6088689A2D1A9F070070D199 /* AppDelegate.swift */; }; + 608868AB2D1AA19B0070D199 /* NativeScreenManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 608868AA2D1AA19B0070D199 /* NativeScreenManager.swift */; }; + 608868AC2D1AA19B0070D199 /* NativeScreenManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 608868AA2D1AA19B0070D199 /* NativeScreenManager.swift */; }; + 608868B22D1AA1FB0070D199 /* CustomKeyboardDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 608868B02D1AA1FB0070D199 /* CustomKeyboardDelegate.swift */; }; + 608868B32D1AA1FB0070D199 /* CustomKeyboardDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 608868B02D1AA1FB0070D199 /* CustomKeyboardDelegate.swift */; }; + 608868C52D1C3D130070D199 /* ReactModulesBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 608868C42D1C3D0E0070D199 /* ReactModulesBridge.m */; }; + 608868C62D1C3D130070D199 /* ReactModulesBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 608868C42D1C3D0E0070D199 /* ReactModulesBridge.m */; }; 609DDB892D10C21800028574 /* Detox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 609DDB852D10C20800028574 /* Detox.framework */; }; - 609DDBE42D10D45500028574 /* NativeModule.mm in Sources */ = {isa = PBXBuildFile; fileRef = 609DDBE32D10D45500028574 /* NativeModule.mm */; }; 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; 90CCD0BA1322566EB1285DC2 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */; }; CA25A6405AF58BA742F7FD47 /* libPods-example-ci.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1CB0FE045763B5363B2E7054 /* libPods-example-ci.a */; }; @@ -44,11 +49,8 @@ /* Begin PBXFileReference section */ 13B07F961A680F5B00A75B9A /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = example.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = example/AppDelegate.h; sourceTree = ""; }; - 13B07FB01A68108700A75B9A /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = example/AppDelegate.mm; sourceTree = ""; }; 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = example/Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = example/Info.plist; sourceTree = ""; }; - 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = example/main.m; sourceTree = ""; }; 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = PrivacyInfo.xcprivacy; path = example/PrivacyInfo.xcprivacy; sourceTree = ""; }; 19F6CBCC0A4E27FBF8BF4A61 /* libPods-example-exampleTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-example-exampleTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 1CB0FE045763B5363B2E7054 /* libPods-example-ci.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-example-ci.a"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -59,9 +61,15 @@ 5B7EB9410499542E8C5724F5 /* Pods-example-exampleTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example-exampleTests.debug.xcconfig"; path = "Target Support Files/Pods-example-exampleTests/Pods-example-exampleTests.debug.xcconfig"; sourceTree = ""; }; 60493BD72D10D967002853A0 /* example.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = example.entitlements; path = example/example.entitlements; sourceTree = ""; }; 60493BEE2D10E7E4002853A0 /* example-ci.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "example-ci.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 6088689A2D1A9F070070D199 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 6088689B2D1A9F070070D199 /* example-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "example-Bridging-Header.h"; sourceTree = ""; }; + 6088689C2D1A9F070070D199 /* example-ci-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "example-ci-Bridging-Header.h"; sourceTree = ""; }; + 6088689D2D1A9F070070D199 /* NativeModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeModule.swift; sourceTree = ""; }; + 6088689E2D1A9F070070D199 /* ShakeEventEmitter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShakeEventEmitter.swift; sourceTree = ""; }; + 608868AA2D1AA19B0070D199 /* NativeScreenManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeScreenManager.swift; sourceTree = ""; }; + 608868B02D1AA1FB0070D199 /* CustomKeyboardDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomKeyboardDelegate.swift; sourceTree = ""; }; + 608868C42D1C3D0E0070D199 /* ReactModulesBridge.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ReactModulesBridge.m; sourceTree = ""; }; 609DDB772D10C20700028574 /* Detox.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Detox.xcodeproj; path = /Users/asafk/Development/Detox/detox/ios/Detox.xcodeproj; sourceTree = ""; }; - 609DDBE22D10D45500028574 /* NativeModule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NativeModule.h; sourceTree = ""; }; - 609DDBE32D10D45500028574 /* NativeModule.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = NativeModule.mm; sourceTree = ""; }; 80C930B7D5D6C25DA27372A5 /* Pods-example-ci.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example-ci.release.xcconfig"; path = "Target Support Files/Pods-example-ci/Pods-example-ci.release.xcconfig"; sourceTree = ""; }; 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = example/LaunchScreen.storyboard; sourceTree = ""; }; 89C6BE57DB24E9ADA2F236DE /* Pods-example-exampleTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example-exampleTests.release.xcconfig"; path = "Target Support Files/Pods-example-exampleTests/Pods-example-exampleTests.release.xcconfig"; sourceTree = ""; }; @@ -74,7 +82,6 @@ buildActionMask = 2147483647; files = ( 609DDB892D10C21800028574 /* Detox.framework in Frameworks */, - 0C80B921A6F3F58F76C31292 /* (null) in Frameworks */, 213E1077F464B28612297B3E /* libPods-example.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -93,15 +100,16 @@ 13B07FAE1A68108700A75B9A /* example */ = { isa = PBXGroup; children = ( + 6088689A2D1A9F070070D199 /* AppDelegate.swift */, 60493BD72D10D967002853A0 /* example.entitlements */, - 13B07FAF1A68108700A75B9A /* AppDelegate.h */, - 13B07FB01A68108700A75B9A /* AppDelegate.mm */, + 6088689B2D1A9F070070D199 /* example-Bridging-Header.h */, + 6088689C2D1A9F070070D199 /* example-ci-Bridging-Header.h */, 13B07FB51A68108700A75B9A /* Images.xcassets */, 13B07FB61A68108700A75B9A /* Info.plist */, 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */, - 13B07FB71A68108700A75B9A /* main.m */, 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */, - 609DDBE12D10D44F00028574 /* ReactModules */, + 6088689F2D1A9F070070D199 /* ReactModules */, + 608868B12D1AA1FB0070D199 /* UI */, ); name = example; sourceTree = ""; @@ -117,22 +125,32 @@ name = Frameworks; sourceTree = ""; }; - 609DDB802D10C20800028574 /* Products */ = { + 6088689F2D1A9F070070D199 /* ReactModules */ = { isa = PBXGroup; children = ( - 609DDB852D10C20800028574 /* Detox.framework */, - 609DDB872D10C20800028574 /* DetoxUserNotificationTests.xctest */, + 608868C42D1C3D0E0070D199 /* ReactModulesBridge.m */, + 6088689D2D1A9F070070D199 /* NativeModule.swift */, + 6088689E2D1A9F070070D199 /* ShakeEventEmitter.swift */, ); - name = Products; + path = ReactModules; sourceTree = ""; }; - 609DDBE12D10D44F00028574 /* ReactModules */ = { + 608868B12D1AA1FB0070D199 /* UI */ = { isa = PBXGroup; children = ( - 609DDBE22D10D45500028574 /* NativeModule.h */, - 609DDBE32D10D45500028574 /* NativeModule.mm */, + 608868B02D1AA1FB0070D199 /* CustomKeyboardDelegate.swift */, + 608868AA2D1AA19B0070D199 /* NativeScreenManager.swift */, ); - path = ReactModules; + path = UI; + sourceTree = ""; + }; + 609DDB802D10C20800028574 /* Products */ = { + isa = PBXGroup; + children = ( + 609DDB852D10C20800028574 /* Detox.framework */, + 609DDB872D10C20800028574 /* DetoxUserNotificationTests.xctest */, + ); + name = Products; sourceTree = ""; }; 832341AE1AAA6A7D00B99B32 /* Libraries */ = { @@ -233,7 +251,10 @@ LastUpgradeCheck = 1210; TargetAttributes = { 13B07F861A680F5B00A75B9A = { - LastSwiftMigration = 1120; + LastSwiftMigration = 1600; + }; + 60493BDB2D10E7E4002853A0 = { + LastSwiftMigration = 1600; }; }; }; @@ -454,9 +475,12 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 609DDBE42D10D45500028574 /* NativeModule.mm in Sources */, - 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */, - 13B07FC11A68108700A75B9A /* main.m in Sources */, + 608868A32D1A9F070070D199 /* NativeModule.swift in Sources */, + 608868A42D1A9F070070D199 /* ShakeEventEmitter.swift in Sources */, + 608868AB2D1AA19B0070D199 /* NativeScreenManager.swift in Sources */, + 608868C52D1C3D130070D199 /* ReactModulesBridge.m in Sources */, + 608868A52D1A9F070070D199 /* AppDelegate.swift in Sources */, + 608868B22D1AA1FB0070D199 /* CustomKeyboardDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -464,9 +488,12 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 60493BDE2D10E7E4002853A0 /* NativeModule.mm in Sources */, - 60493BDF2D10E7E4002853A0 /* AppDelegate.mm in Sources */, - 60493BE02D10E7E4002853A0 /* main.m in Sources */, + 608868A02D1A9F070070D199 /* NativeModule.swift in Sources */, + 608868A12D1A9F070070D199 /* ShakeEventEmitter.swift in Sources */, + 608868AC2D1AA19B0070D199 /* NativeScreenManager.swift in Sources */, + 608868C62D1C3D130070D199 /* ReactModulesBridge.m in Sources */, + 608868A22D1A9F070070D199 /* AppDelegate.swift in Sources */, + 608868B32D1AA1FB0070D199 /* CustomKeyboardDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -496,6 +523,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = "com.wix.detox-example"; PRODUCT_NAME = example; + SWIFT_OBJC_BRIDGING_HEADER = "example-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; @@ -524,6 +552,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = "com.wix.detox-example"; PRODUCT_NAME = example; + SWIFT_OBJC_BRIDGING_HEADER = "example-Bridging-Header.h"; SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; }; @@ -552,6 +581,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = "com.wix.detox-example"; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "example-ci-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; @@ -580,6 +610,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = "com.wix.detox-example"; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "example-ci-Bridging-Header.h"; SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; }; diff --git a/detox/test/ios/example/AppDelegate.h b/detox/test/ios/example/AppDelegate.h deleted file mode 100644 index 5d2808256c..0000000000 --- a/detox/test/ios/example/AppDelegate.h +++ /dev/null @@ -1,6 +0,0 @@ -#import -#import - -@interface AppDelegate : RCTAppDelegate - -@end diff --git a/detox/test/ios/example/AppDelegate.mm b/detox/test/ios/example/AppDelegate.mm deleted file mode 100644 index fbb3aee217..0000000000 --- a/detox/test/ios/example/AppDelegate.mm +++ /dev/null @@ -1,108 +0,0 @@ -#import "AppDelegate.h" -#import -#import -#import -#import - -// Shake event handling -@interface ShakeEventEmitter : RCTEventEmitter -@end - -static ShakeEventEmitter* _shakeInstance; - -@implementation ShakeEventEmitter -RCT_EXPORT_MODULE(); - -- (instancetype)init { - self = [super init]; - _shakeInstance = self; - return self; -} - -- (NSArray *)supportedEvents { - return @[@"ShakeEvent"]; -} - -- (void)sendShakeEvent { - [self sendEventWithName:@"ShakeEvent" body:nil]; -} - -+ (BOOL)requiresMainQueueSetup { - return YES; -} -@end - -// Custom ViewController for shake detection -@interface ShakeDetectViewController : UIViewController -@property (nonatomic, weak) RCTBridge* bridge; -@end - -@implementation ShakeDetectViewController -- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event { - if(event.subtype == UIEventSubtypeMotionShake) { - [_shakeInstance sendShakeEvent]; - } -} -@end - -@interface AppDelegate () -@end - -@implementation AppDelegate - -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions -{ - self.moduleName = @"example"; - self.initialProps = @{}; - - // Setup notification delegate - [UNUserNotificationCenter currentNotificationCenter].delegate = self; - - return [super application:application didFinishLaunchingWithOptions:launchOptions]; -} - -// URL scheme handling -- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary *)options -{ - return [RCTLinkingManager application:application openURL:url options:options]; -} - -// Universal links and Spotlight search handling -- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void(^)(NSArray> *))restorationHandler -{ - return [RCTLinkingManager application:application continueUserActivity:userActivity restorationHandler:restorationHandler]; -} - -// Push notification handling -- (void)userNotificationCenter:(UNUserNotificationCenter *)center - willPresentNotification:(UNNotification *)notification - withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler -{ - completionHandler(UNNotificationPresentationOptionList | - UNNotificationPresentationOptionBanner | - UNNotificationPresentationOptionBadge | - UNNotificationPresentationOptionSound); -} - -- (void)userNotificationCenter:(UNUserNotificationCenter *)center -didReceiveNotificationResponse:(UNNotificationResponse *)response - withCompletionHandler:(void (^)(void))completionHandler -{ - completionHandler(); -} - -- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge -{ - return [self bundleURL]; -} - -- (NSURL *)bundleURL -{ -#if DEBUG - return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; -#else - return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; -#endif -} - -@end diff --git a/detox/test/ios/example/main.m b/detox/test/ios/example/main.m deleted file mode 100644 index d645c7246c..0000000000 --- a/detox/test/ios/example/main.m +++ /dev/null @@ -1,10 +0,0 @@ -#import - -#import "AppDelegate.h" - -int main(int argc, char *argv[]) -{ - @autoreleasepool { - return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); - } -} From 28358fbcd569c0668e3a525cfdfda433edf7c92b Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Thu, 26 Dec 2024 14:13:31 +0200 Subject: [PATCH 32/51] refactor(iOS): `AppDelegate` extensions. --- .../AppDelegate+Notifications.swift | 59 ++++++++++++++++++ .../AppDelegate+Shake.swift | 21 +++++++ detox/test/ios/AppDelegate.swift | 58 +++++------------- detox/test/ios/UI/InAppNotificationView.swift | 60 +++++++++++++++++++ .../ios/example.xcodeproj/project.pbxproj | 17 ++++++ 5 files changed, 172 insertions(+), 43 deletions(-) create mode 100644 detox/test/ios/AppDelegate Extensions/AppDelegate+Notifications.swift create mode 100644 detox/test/ios/AppDelegate Extensions/AppDelegate+Shake.swift create mode 100644 detox/test/ios/UI/InAppNotificationView.swift diff --git a/detox/test/ios/AppDelegate Extensions/AppDelegate+Notifications.swift b/detox/test/ios/AppDelegate Extensions/AppDelegate+Notifications.swift new file mode 100644 index 0000000000..885673dce0 --- /dev/null +++ b/detox/test/ios/AppDelegate Extensions/AppDelegate+Notifications.swift @@ -0,0 +1,59 @@ +// +// AppDelegate+Notifications.swift +// Created by Asaf Korem (Wix.com) on 2024. +// + +import UserNotifications +import UIKit +import React + +extension AppDelegate: UNUserNotificationCenterDelegate { + func userNotificationCenter(_ center: UNUserNotificationCenter, + willPresent notification: UNNotification, + withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { + + completionHandler([.list, .banner, .badge, .sound]) + + let title = notification.request.content.title + showInAppNotification(withTitle: title) + } + + func userNotificationCenter(_ center: UNUserNotificationCenter, + didReceive response: UNNotificationResponse, + withCompletionHandler completionHandler: @escaping () -> Void) { + + let title = response.notification.request.content.title + showInAppNotification(withTitle: title) + + completionHandler() + } +} + +// MARK: - Private Helpers + +extension AppDelegate { + + /// Displays the custom in-app notification banner on top of the React Native content view + private func showInAppNotification(withTitle title: String) { + + let bannerView = InAppNotificationView(title: title) + + guard + let rootView = window?.rootViewController?.view as? RCTRootView, + let contentView = rootView.value(forKey: "contentView") as? UIView + else { + return + } + + contentView.addSubview(bannerView) + + NSLayoutConstraint.activate([ + bannerView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), + bannerView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), + bannerView.topAnchor.constraint(equalTo: contentView.safeAreaLayoutGuide.topAnchor), + bannerView.heightAnchor.constraint(equalToConstant: 60) + ]) + + contentView.bringSubviewToFront(bannerView) + } +} diff --git a/detox/test/ios/AppDelegate Extensions/AppDelegate+Shake.swift b/detox/test/ios/AppDelegate Extensions/AppDelegate+Shake.swift new file mode 100644 index 0000000000..d8c1eec128 --- /dev/null +++ b/detox/test/ios/AppDelegate Extensions/AppDelegate+Shake.swift @@ -0,0 +1,21 @@ +// +// AppDelegate+Shake.swift (example) +// Created by Asaf Korem (Wix.com) on 2024. +// + +import UIKit +import React + +extension AppDelegate { + override func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) { + if motion == .motionShake { + if let rootView = window?.rootViewController?.view as? RCTRootView { + let bridge = rootView.bridge + let shakeModule = bridge.module(for: ShakeEventEmitter.self) as! ShakeEventEmitter + shakeModule.handleShake() + } + } else { + super.motionEnded(motion, with: event) + } + } +} diff --git a/detox/test/ios/AppDelegate.swift b/detox/test/ios/AppDelegate.swift index 026c6c77c6..0461a7d0ee 100644 --- a/detox/test/ios/AppDelegate.swift +++ b/detox/test/ios/AppDelegate.swift @@ -1,10 +1,8 @@ import UIKit import React -import UserNotifications import CoreSpotlight // MARK: - App Delegate - @UIApplicationMain @objc(AppDelegate) class AppDelegate: UIResponder, UIApplicationDelegate { @@ -16,9 +14,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // MARK: - UIApplicationDelegate Methods - func application(_ application: UIApplication, - didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - + func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { setupReactNative(with: launchOptions) setupNotifications() setupScreenManager() @@ -30,9 +29,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate { private func setupReactNative(with launchOptions: [UIApplication.LaunchOptionsKey: Any]?) { let bridge = RCTBridge(delegate: self, launchOptions: launchOptions) - let rootView = RCTRootView(bridge: bridge!, - moduleName: moduleName, - initialProperties: initialProps) + let rootView = RCTRootView( + bridge: bridge!, + moduleName: moduleName, + initialProperties: initialProps + ) rootView.backgroundColor = .white let rootViewController = UIViewController() @@ -44,8 +45,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } private func setupNotifications() { + // Set ourselves as the UNUserNotificationCenter delegate UNUserNotificationCenter.current().delegate = self + // Example: Listen for a custom "ChangeScreen" event NotificationCenter.default.addObserver( forName: Notification.Name("ChangeScreen"), object: nil, @@ -60,24 +63,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } } -// MARK: - Shake Gesture Handling - -extension AppDelegate { - override func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) { - if motion == .motionShake { - if let rootView = window?.rootViewController?.view as? RCTRootView { - let bridge = rootView.bridge - let shakeModule = bridge.module(for: ShakeEventEmitter.self) as! ShakeEventEmitter - shakeModule.handleShake() - } - } else { - super.motionEnded(motion, with: event) - } - } -} - // MARK: - URL and Universal Links Handling - extension AppDelegate { func application(_ app: UIApplication, open url: URL, @@ -94,23 +80,7 @@ extension AppDelegate { } } -// MARK: - UNUserNotificationCenterDelegate -extension AppDelegate: UNUserNotificationCenterDelegate { - func userNotificationCenter(_ center: UNUserNotificationCenter, - willPresent notification: UNNotification, - withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { - completionHandler([.list, .banner, .badge, .sound]) - } - - func userNotificationCenter(_ center: UNUserNotificationCenter, - didReceive response: UNNotificationResponse, - withCompletionHandler completionHandler: @escaping () -> Void) { - completionHandler() - } -} - // MARK: - RCTBridgeDelegate - extension AppDelegate: RCTBridgeDelegate { func sourceURL(for bridge: RCTBridge) -> URL? { return bundleURL() @@ -118,8 +88,10 @@ extension AppDelegate: RCTBridgeDelegate { private func bundleURL() -> URL { #if DEBUG - return RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index", - fallbackExtension: nil)! + return RCTBundleURLProvider.sharedSettings().jsBundleURL( + forBundleRoot: "index", + fallbackExtension: nil + )! #else return Bundle.main.url(forResource: "main", withExtension: "jsbundle")! #endif diff --git a/detox/test/ios/UI/InAppNotificationView.swift b/detox/test/ios/UI/InAppNotificationView.swift new file mode 100644 index 0000000000..ee84661807 --- /dev/null +++ b/detox/test/ios/UI/InAppNotificationView.swift @@ -0,0 +1,60 @@ +// +// InAppNotificationView.swift (example) +// Created by Asaf Korem (Wix.com) on 2024. +// + + +import UIKit + +class InAppNotificationView: UIView { + + private let titleLabel: UILabel = { + let label = UILabel() + label.translatesAutoresizingMaskIntoConstraints = false + label.numberOfLines = 2 + label.textAlignment = .center + label.textColor = .white + label.font = UIFont.boldSystemFont(ofSize: 16) + return label + }() + + private let closeButton: UIButton = { + let button = UIButton(type: .system) + button.translatesAutoresizingMaskIntoConstraints = false + button.setTitle("×", for: .normal) + button.titleLabel?.font = UIFont.systemFont(ofSize: 24) + button.setTitleColor(.white, for: .normal) + return button + }() + + // MARK: - Init + + init(title: String) { + super.init(frame: .zero) + translatesAutoresizingMaskIntoConstraints = false + backgroundColor = UIColor.black.withAlphaComponent(0.7) + + titleLabel.text = title + + addSubview(titleLabel) + addSubview(closeButton) + + NSLayoutConstraint.activate([ + titleLabel.centerYAnchor.constraint(equalTo: centerYAnchor), + titleLabel.centerXAnchor.constraint(equalTo: centerXAnchor), + + closeButton.topAnchor.constraint(equalTo: topAnchor, constant: 5), + closeButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -10), + ]) + + closeButton.addTarget(self, action: #selector(didTapClose), for: .touchUpInside) + } + + @objc private func didTapClose() { + removeFromSuperview() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} diff --git a/detox/test/ios/example.xcodeproj/project.pbxproj b/detox/test/ios/example.xcodeproj/project.pbxproj index fcf919e2c3..407b2bb788 100644 --- a/detox/test/ios/example.xcodeproj/project.pbxproj +++ b/detox/test/ios/example.xcodeproj/project.pbxproj @@ -24,6 +24,8 @@ 608868B32D1AA1FB0070D199 /* CustomKeyboardDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 608868B02D1AA1FB0070D199 /* CustomKeyboardDelegate.swift */; }; 608868C52D1C3D130070D199 /* ReactModulesBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 608868C42D1C3D0E0070D199 /* ReactModulesBridge.m */; }; 608868C62D1C3D130070D199 /* ReactModulesBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 608868C42D1C3D0E0070D199 /* ReactModulesBridge.m */; }; + 608868D22D1D696E0070D199 /* InAppNotificationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 608868D12D1D696E0070D199 /* InAppNotificationView.swift */; }; + 608868D32D1D696E0070D199 /* InAppNotificationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 608868D12D1D696E0070D199 /* InAppNotificationView.swift */; }; 609DDB892D10C21800028574 /* Detox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 609DDB852D10C20800028574 /* Detox.framework */; }; 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; 90CCD0BA1322566EB1285DC2 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */; }; @@ -69,6 +71,7 @@ 608868AA2D1AA19B0070D199 /* NativeScreenManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeScreenManager.swift; sourceTree = ""; }; 608868B02D1AA1FB0070D199 /* CustomKeyboardDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomKeyboardDelegate.swift; sourceTree = ""; }; 608868C42D1C3D0E0070D199 /* ReactModulesBridge.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ReactModulesBridge.m; sourceTree = ""; }; + 608868D12D1D696E0070D199 /* InAppNotificationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InAppNotificationView.swift; sourceTree = ""; }; 609DDB772D10C20700028574 /* Detox.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Detox.xcodeproj; path = /Users/asafk/Development/Detox/detox/ios/Detox.xcodeproj; sourceTree = ""; }; 80C930B7D5D6C25DA27372A5 /* Pods-example-ci.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example-ci.release.xcconfig"; path = "Target Support Files/Pods-example-ci/Pods-example-ci.release.xcconfig"; sourceTree = ""; }; 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = example/LaunchScreen.storyboard; sourceTree = ""; }; @@ -76,6 +79,10 @@ ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; /* End PBXFileReference section */ +/* Begin PBXFileSystemSynchronizedRootGroup section */ + 608868CB2D1D67DA0070D199 /* AppDelegate Extensions */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = "AppDelegate Extensions"; sourceTree = ""; }; +/* End PBXFileSystemSynchronizedRootGroup section */ + /* Begin PBXFrameworksBuildPhase section */ 13B07F8C1A680F5B00A75B9A /* Frameworks */ = { isa = PBXFrameworksBuildPhase; @@ -101,6 +108,7 @@ isa = PBXGroup; children = ( 6088689A2D1A9F070070D199 /* AppDelegate.swift */, + 608868CB2D1D67DA0070D199 /* AppDelegate Extensions */, 60493BD72D10D967002853A0 /* example.entitlements */, 6088689B2D1A9F070070D199 /* example-Bridging-Header.h */, 6088689C2D1A9F070070D199 /* example-ci-Bridging-Header.h */, @@ -138,6 +146,7 @@ 608868B12D1AA1FB0070D199 /* UI */ = { isa = PBXGroup; children = ( + 608868D12D1D696E0070D199 /* InAppNotificationView.swift */, 608868B02D1AA1FB0070D199 /* CustomKeyboardDelegate.swift */, 608868AA2D1AA19B0070D199 /* NativeScreenManager.swift */, ); @@ -216,6 +225,9 @@ ); dependencies = ( ); + fileSystemSynchronizedGroups = ( + 608868CB2D1D67DA0070D199 /* AppDelegate Extensions */, + ); name = example; productName = example; productReference = 13B07F961A680F5B00A75B9A /* example.app */; @@ -237,6 +249,9 @@ ); dependencies = ( ); + fileSystemSynchronizedGroups = ( + 608868CB2D1D67DA0070D199 /* AppDelegate Extensions */, + ); name = "example-ci"; productName = example; productReference = 60493BEE2D10E7E4002853A0 /* example-ci.app */; @@ -479,6 +494,7 @@ 608868A42D1A9F070070D199 /* ShakeEventEmitter.swift in Sources */, 608868AB2D1AA19B0070D199 /* NativeScreenManager.swift in Sources */, 608868C52D1C3D130070D199 /* ReactModulesBridge.m in Sources */, + 608868D32D1D696E0070D199 /* InAppNotificationView.swift in Sources */, 608868A52D1A9F070070D199 /* AppDelegate.swift in Sources */, 608868B22D1AA1FB0070D199 /* CustomKeyboardDelegate.swift in Sources */, ); @@ -492,6 +508,7 @@ 608868A12D1A9F070070D199 /* ShakeEventEmitter.swift in Sources */, 608868AC2D1AA19B0070D199 /* NativeScreenManager.swift in Sources */, 608868C62D1C3D130070D199 /* ReactModulesBridge.m in Sources */, + 608868D22D1D696E0070D199 /* InAppNotificationView.swift in Sources */, 608868A22D1A9F070070D199 /* AppDelegate.swift in Sources */, 608868B32D1AA1FB0070D199 /* CustomKeyboardDelegate.swift in Sources */, ); From 4dbe3c403e72f3028477d7774bbe81c8d9dee3d0 Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Sun, 29 Dec 2024 18:57:39 +0200 Subject: [PATCH 33/51] fix: support background searchable items. --- .../AppDelegate+ApplicationState.swift | 43 +++++++++++++ .../AppDelegate+Linking.swift | 42 +++++++++++++ .../AppDelegate+Notifications.swift | 33 +--------- .../AppDelegate+OverlayView.swift | 32 ++++++++++ detox/test/ios/AppDelegate.swift | 19 +----- ...ionView.swift => OverlayMessageView.swift} | 57 +++++++++-------- .../ios/example.xcodeproj/project.pbxproj | 61 +++++++++++++------ 7 files changed, 197 insertions(+), 90 deletions(-) create mode 100644 detox/test/ios/AppDelegate Extensions/AppDelegate+ApplicationState.swift create mode 100644 detox/test/ios/AppDelegate Extensions/AppDelegate+Linking.swift create mode 100644 detox/test/ios/AppDelegate Extensions/AppDelegate+OverlayView.swift rename detox/test/ios/UI/{InAppNotificationView.swift => OverlayMessageView.swift} (59%) diff --git a/detox/test/ios/AppDelegate Extensions/AppDelegate+ApplicationState.swift b/detox/test/ios/AppDelegate Extensions/AppDelegate+ApplicationState.swift new file mode 100644 index 0000000000..205c368c05 --- /dev/null +++ b/detox/test/ios/AppDelegate Extensions/AppDelegate+ApplicationState.swift @@ -0,0 +1,43 @@ +// +// AppDelegate+ApplicationState.swift (example) +// Created by Asaf Korem (Wix.com) on 2024. +// + +import UIKit + +extension AppDelegate { + func setupApplicationStateObservers() { + NotificationCenter.default.addObserver( + self, + selector: #selector(applicationDidBecomeActive), + name: UIApplication.didBecomeActiveNotification, + object: nil + ) + + NotificationCenter.default.addObserver( + self, + selector: #selector(applicationWillResignActive), + name: UIApplication.willResignActiveNotification, + object: nil + ) + + NotificationCenter.default.addObserver( + self, + selector: #selector(applicationDidEnterBackground), + name: UIApplication.didEnterBackgroundNotification, + object: nil + ) + } + + @objc private func applicationDidBecomeActive() { + showOverlayMessage(withMessage: "Active") + } + + @objc private func applicationWillResignActive() { + showOverlayMessage(withMessage: "Inactive") + } + + @objc private func applicationDidEnterBackground() { + showOverlayMessage(withMessage: "Background") + } +} diff --git a/detox/test/ios/AppDelegate Extensions/AppDelegate+Linking.swift b/detox/test/ios/AppDelegate Extensions/AppDelegate+Linking.swift new file mode 100644 index 0000000000..11de67b62b --- /dev/null +++ b/detox/test/ios/AppDelegate Extensions/AppDelegate+Linking.swift @@ -0,0 +1,42 @@ +// +// AppDelegate+Linking.swift (example) +// Created by Asaf Korem (Wix.com) on 2024. +// + +import UIKit +import React +import CoreSpotlight + +// MARK: - URL and Universal Links Handling +extension AppDelegate { + func application( + _ app: UIApplication, + open url: URL, + options: [UIApplication.OpenURLOptionsKey : Any] = [:] + ) -> Bool { + return RCTLinkingManager.application(app, open: url, options: options) + } + + func application( + _ application: UIApplication, + continue userActivity: NSUserActivity, + restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void + ) -> Bool { + if userActivity.activityType == CSSearchableItemActionType { + if let identifier = userActivity.userInfo?[CSSearchableItemActivityIdentifier] as? String, + let url = URL(string: "\(identifier)") { + + let customOptions: [UIApplication.OpenURLOptionsKey: Any] = [ + .sourceApplication: "", + .annotation: [:] + ] + + return RCTLinkingManager.application(application, open: url, options: customOptions) + } else { + return false + } + } + + return RCTLinkingManager.application(application, continue: userActivity, restorationHandler: restorationHandler) + } +} diff --git a/detox/test/ios/AppDelegate Extensions/AppDelegate+Notifications.swift b/detox/test/ios/AppDelegate Extensions/AppDelegate+Notifications.swift index 885673dce0..b4d7740c79 100644 --- a/detox/test/ios/AppDelegate Extensions/AppDelegate+Notifications.swift +++ b/detox/test/ios/AppDelegate Extensions/AppDelegate+Notifications.swift @@ -15,7 +15,7 @@ extension AppDelegate: UNUserNotificationCenterDelegate { completionHandler([.list, .banner, .badge, .sound]) let title = notification.request.content.title - showInAppNotification(withTitle: title) + showOverlayMessage(withMessage: title) } func userNotificationCenter(_ center: UNUserNotificationCenter, @@ -23,37 +23,8 @@ extension AppDelegate: UNUserNotificationCenterDelegate { withCompletionHandler completionHandler: @escaping () -> Void) { let title = response.notification.request.content.title - showInAppNotification(withTitle: title) + showOverlayMessage(withMessage: title) completionHandler() } } - -// MARK: - Private Helpers - -extension AppDelegate { - - /// Displays the custom in-app notification banner on top of the React Native content view - private func showInAppNotification(withTitle title: String) { - - let bannerView = InAppNotificationView(title: title) - - guard - let rootView = window?.rootViewController?.view as? RCTRootView, - let contentView = rootView.value(forKey: "contentView") as? UIView - else { - return - } - - contentView.addSubview(bannerView) - - NSLayoutConstraint.activate([ - bannerView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), - bannerView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), - bannerView.topAnchor.constraint(equalTo: contentView.safeAreaLayoutGuide.topAnchor), - bannerView.heightAnchor.constraint(equalToConstant: 60) - ]) - - contentView.bringSubviewToFront(bannerView) - } -} diff --git a/detox/test/ios/AppDelegate Extensions/AppDelegate+OverlayView.swift b/detox/test/ios/AppDelegate Extensions/AppDelegate+OverlayView.swift new file mode 100644 index 0000000000..49c19e1707 --- /dev/null +++ b/detox/test/ios/AppDelegate Extensions/AppDelegate+OverlayView.swift @@ -0,0 +1,32 @@ +// +// AppDelegate+OverlayView.swift (example) +// Created by Asaf Korem (Wix.com) on 2024. +// + + +import UIKit +import React + +extension AppDelegate { + func showOverlayMessage(withMessage message: String) { + guard + let rootView = window?.rootViewController?.view as? RCTRootView, + let contentView = rootView.value(forKey: "contentView") as? UIView + else { + return + } + + let messageView = OverlayMessageView(message: message) + contentView.addSubview(messageView) + + NSLayoutConstraint.activate([ + messageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), + messageView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), + messageView.topAnchor.constraint(equalTo: contentView.safeAreaLayoutGuide.topAnchor), + messageView.heightAnchor.constraint(equalToConstant: 60) + ]) + + contentView.bringSubviewToFront(messageView) + } +} + diff --git a/detox/test/ios/AppDelegate.swift b/detox/test/ios/AppDelegate.swift index 0461a7d0ee..e78726e852 100644 --- a/detox/test/ios/AppDelegate.swift +++ b/detox/test/ios/AppDelegate.swift @@ -1,6 +1,5 @@ import UIKit import React -import CoreSpotlight // MARK: - App Delegate @UIApplicationMain @@ -21,6 +20,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { setupReactNative(with: launchOptions) setupNotifications() setupScreenManager() + setupApplicationStateObservers() return true } @@ -63,23 +63,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } } -// MARK: - URL and Universal Links Handling -extension AppDelegate { - func application(_ app: UIApplication, - open url: URL, - options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { - return RCTLinkingManager.application(app, open: url, options: options) - } - - func application(_ application: UIApplication, - continue userActivity: NSUserActivity, - restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { - return RCTLinkingManager.application(application, - continue: userActivity, - restorationHandler: restorationHandler) - } -} - // MARK: - RCTBridgeDelegate extension AppDelegate: RCTBridgeDelegate { func sourceURL(for bridge: RCTBridge) -> URL? { diff --git a/detox/test/ios/UI/InAppNotificationView.swift b/detox/test/ios/UI/OverlayMessageView.swift similarity index 59% rename from detox/test/ios/UI/InAppNotificationView.swift rename to detox/test/ios/UI/OverlayMessageView.swift index ee84661807..c20b4448b6 100644 --- a/detox/test/ios/UI/InAppNotificationView.swift +++ b/detox/test/ios/UI/OverlayMessageView.swift @@ -1,14 +1,13 @@ // -// InAppNotificationView.swift (example) +// OverlayMessageView.swift (example) // Created by Asaf Korem (Wix.com) on 2024. // - import UIKit -class InAppNotificationView: UIView { - - private let titleLabel: UILabel = { +class OverlayMessageView: UIView { + + private let messageLabel: UILabel = { let label = UILabel() label.translatesAutoresizingMaskIntoConstraints = false label.numberOfLines = 2 @@ -17,7 +16,7 @@ class InAppNotificationView: UIView { label.font = UIFont.boldSystemFont(ofSize: 16) return label }() - + private let closeButton: UIButton = { let button = UIButton(type: .system) button.translatesAutoresizingMaskIntoConstraints = false @@ -26,35 +25,45 @@ class InAppNotificationView: UIView { button.setTitleColor(.white, for: .normal) return button }() - - // MARK: - Init - - init(title: String) { + + init(message: String) { super.init(frame: .zero) + setup(with: message) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setup(with message: String) { translatesAutoresizingMaskIntoConstraints = false backgroundColor = UIColor.black.withAlphaComponent(0.7) - - titleLabel.text = title - - addSubview(titleLabel) + + messageLabel.text = message + + addSubview(messageLabel) addSubview(closeButton) - + NSLayoutConstraint.activate([ - titleLabel.centerYAnchor.constraint(equalTo: centerYAnchor), - titleLabel.centerXAnchor.constraint(equalTo: centerXAnchor), - + messageLabel.centerYAnchor.constraint(equalTo: centerYAnchor), + messageLabel.centerXAnchor.constraint(equalTo: centerXAnchor), + messageLabel.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor, constant: 40), + messageLabel.trailingAnchor.constraint(lessThanOrEqualTo: trailingAnchor, constant: -40), + closeButton.topAnchor.constraint(equalTo: topAnchor, constant: 5), closeButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -10), + closeButton.widthAnchor.constraint(equalToConstant: 30), + closeButton.heightAnchor.constraint(equalToConstant: 30) ]) - + closeButton.addTarget(self, action: #selector(didTapClose), for: .touchUpInside) + + Timer.scheduledTimer(withTimeInterval: 1.0, repeats: false) { [weak self] _ in + self?.removeFromSuperview() + } } - + @objc private func didTapClose() { removeFromSuperview() } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } } diff --git a/detox/test/ios/example.xcodeproj/project.pbxproj b/detox/test/ios/example.xcodeproj/project.pbxproj index 407b2bb788..7a4fe3d392 100644 --- a/detox/test/ios/example.xcodeproj/project.pbxproj +++ b/detox/test/ios/example.xcodeproj/project.pbxproj @@ -24,9 +24,19 @@ 608868B32D1AA1FB0070D199 /* CustomKeyboardDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 608868B02D1AA1FB0070D199 /* CustomKeyboardDelegate.swift */; }; 608868C52D1C3D130070D199 /* ReactModulesBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 608868C42D1C3D0E0070D199 /* ReactModulesBridge.m */; }; 608868C62D1C3D130070D199 /* ReactModulesBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 608868C42D1C3D0E0070D199 /* ReactModulesBridge.m */; }; - 608868D22D1D696E0070D199 /* InAppNotificationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 608868D12D1D696E0070D199 /* InAppNotificationView.swift */; }; - 608868D32D1D696E0070D199 /* InAppNotificationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 608868D12D1D696E0070D199 /* InAppNotificationView.swift */; }; + 608868D22D1D696E0070D199 /* OverlayMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 608868D12D1D696E0070D199 /* OverlayMessageView.swift */; }; + 608868D32D1D696E0070D199 /* OverlayMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 608868D12D1D696E0070D199 /* OverlayMessageView.swift */; }; 609DDB892D10C21800028574 /* Detox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 609DDB852D10C20800028574 /* Detox.framework */; }; + 60E207962D21B0B400E6DBD2 /* AppDelegate+ApplicationState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60E207902D21B0B400E6DBD2 /* AppDelegate+ApplicationState.swift */; }; + 60E207972D21B0B400E6DBD2 /* AppDelegate+Linking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60E207912D21B0B400E6DBD2 /* AppDelegate+Linking.swift */; }; + 60E207982D21B0B400E6DBD2 /* AppDelegate+Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60E207922D21B0B400E6DBD2 /* AppDelegate+Notifications.swift */; }; + 60E207992D21B0B400E6DBD2 /* AppDelegate+OverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60E207932D21B0B400E6DBD2 /* AppDelegate+OverlayView.swift */; }; + 60E2079A2D21B0B400E6DBD2 /* AppDelegate+Shake.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60E207942D21B0B400E6DBD2 /* AppDelegate+Shake.swift */; }; + 60E2079B2D21B0B400E6DBD2 /* AppDelegate+ApplicationState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60E207902D21B0B400E6DBD2 /* AppDelegate+ApplicationState.swift */; }; + 60E2079C2D21B0B400E6DBD2 /* AppDelegate+Linking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60E207912D21B0B400E6DBD2 /* AppDelegate+Linking.swift */; }; + 60E2079D2D21B0B400E6DBD2 /* AppDelegate+Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60E207922D21B0B400E6DBD2 /* AppDelegate+Notifications.swift */; }; + 60E2079E2D21B0B400E6DBD2 /* AppDelegate+OverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60E207932D21B0B400E6DBD2 /* AppDelegate+OverlayView.swift */; }; + 60E2079F2D21B0B400E6DBD2 /* AppDelegate+Shake.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60E207942D21B0B400E6DBD2 /* AppDelegate+Shake.swift */; }; 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; 90CCD0BA1322566EB1285DC2 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */; }; CA25A6405AF58BA742F7FD47 /* libPods-example-ci.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1CB0FE045763B5363B2E7054 /* libPods-example-ci.a */; }; @@ -71,18 +81,19 @@ 608868AA2D1AA19B0070D199 /* NativeScreenManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeScreenManager.swift; sourceTree = ""; }; 608868B02D1AA1FB0070D199 /* CustomKeyboardDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomKeyboardDelegate.swift; sourceTree = ""; }; 608868C42D1C3D0E0070D199 /* ReactModulesBridge.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ReactModulesBridge.m; sourceTree = ""; }; - 608868D12D1D696E0070D199 /* InAppNotificationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InAppNotificationView.swift; sourceTree = ""; }; + 608868D12D1D696E0070D199 /* OverlayMessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverlayMessageView.swift; sourceTree = ""; }; 609DDB772D10C20700028574 /* Detox.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Detox.xcodeproj; path = /Users/asafk/Development/Detox/detox/ios/Detox.xcodeproj; sourceTree = ""; }; + 60E207902D21B0B400E6DBD2 /* AppDelegate+ApplicationState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+ApplicationState.swift"; sourceTree = ""; }; + 60E207912D21B0B400E6DBD2 /* AppDelegate+Linking.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+Linking.swift"; sourceTree = ""; }; + 60E207922D21B0B400E6DBD2 /* AppDelegate+Notifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+Notifications.swift"; sourceTree = ""; }; + 60E207932D21B0B400E6DBD2 /* AppDelegate+OverlayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+OverlayView.swift"; sourceTree = ""; }; + 60E207942D21B0B400E6DBD2 /* AppDelegate+Shake.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+Shake.swift"; sourceTree = ""; }; 80C930B7D5D6C25DA27372A5 /* Pods-example-ci.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example-ci.release.xcconfig"; path = "Target Support Files/Pods-example-ci/Pods-example-ci.release.xcconfig"; sourceTree = ""; }; 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = example/LaunchScreen.storyboard; sourceTree = ""; }; 89C6BE57DB24E9ADA2F236DE /* Pods-example-exampleTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example-exampleTests.release.xcconfig"; path = "Target Support Files/Pods-example-exampleTests/Pods-example-exampleTests.release.xcconfig"; sourceTree = ""; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; /* End PBXFileReference section */ -/* Begin PBXFileSystemSynchronizedRootGroup section */ - 608868CB2D1D67DA0070D199 /* AppDelegate Extensions */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = "AppDelegate Extensions"; sourceTree = ""; }; -/* End PBXFileSystemSynchronizedRootGroup section */ - /* Begin PBXFrameworksBuildPhase section */ 13B07F8C1A680F5B00A75B9A /* Frameworks */ = { isa = PBXFrameworksBuildPhase; @@ -108,7 +119,7 @@ isa = PBXGroup; children = ( 6088689A2D1A9F070070D199 /* AppDelegate.swift */, - 608868CB2D1D67DA0070D199 /* AppDelegate Extensions */, + 60E207952D21B0B400E6DBD2 /* AppDelegate Extensions */, 60493BD72D10D967002853A0 /* example.entitlements */, 6088689B2D1A9F070070D199 /* example-Bridging-Header.h */, 6088689C2D1A9F070070D199 /* example-ci-Bridging-Header.h */, @@ -146,9 +157,9 @@ 608868B12D1AA1FB0070D199 /* UI */ = { isa = PBXGroup; children = ( - 608868D12D1D696E0070D199 /* InAppNotificationView.swift */, 608868B02D1AA1FB0070D199 /* CustomKeyboardDelegate.swift */, 608868AA2D1AA19B0070D199 /* NativeScreenManager.swift */, + 608868D12D1D696E0070D199 /* OverlayMessageView.swift */, ); path = UI; sourceTree = ""; @@ -162,6 +173,18 @@ name = Products; sourceTree = ""; }; + 60E207952D21B0B400E6DBD2 /* AppDelegate Extensions */ = { + isa = PBXGroup; + children = ( + 60E207902D21B0B400E6DBD2 /* AppDelegate+ApplicationState.swift */, + 60E207912D21B0B400E6DBD2 /* AppDelegate+Linking.swift */, + 60E207922D21B0B400E6DBD2 /* AppDelegate+Notifications.swift */, + 60E207932D21B0B400E6DBD2 /* AppDelegate+OverlayView.swift */, + 60E207942D21B0B400E6DBD2 /* AppDelegate+Shake.swift */, + ); + path = "AppDelegate Extensions"; + sourceTree = ""; + }; 832341AE1AAA6A7D00B99B32 /* Libraries */ = { isa = PBXGroup; children = ( @@ -225,9 +248,6 @@ ); dependencies = ( ); - fileSystemSynchronizedGroups = ( - 608868CB2D1D67DA0070D199 /* AppDelegate Extensions */, - ); name = example; productName = example; productReference = 13B07F961A680F5B00A75B9A /* example.app */; @@ -249,9 +269,6 @@ ); dependencies = ( ); - fileSystemSynchronizedGroups = ( - 608868CB2D1D67DA0070D199 /* AppDelegate Extensions */, - ); name = "example-ci"; productName = example; productReference = 60493BEE2D10E7E4002853A0 /* example-ci.app */; @@ -490,11 +507,16 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 60E207962D21B0B400E6DBD2 /* AppDelegate+ApplicationState.swift in Sources */, + 60E207972D21B0B400E6DBD2 /* AppDelegate+Linking.swift in Sources */, + 60E207982D21B0B400E6DBD2 /* AppDelegate+Notifications.swift in Sources */, + 60E207992D21B0B400E6DBD2 /* AppDelegate+OverlayView.swift in Sources */, + 60E2079A2D21B0B400E6DBD2 /* AppDelegate+Shake.swift in Sources */, 608868A32D1A9F070070D199 /* NativeModule.swift in Sources */, 608868A42D1A9F070070D199 /* ShakeEventEmitter.swift in Sources */, 608868AB2D1AA19B0070D199 /* NativeScreenManager.swift in Sources */, 608868C52D1C3D130070D199 /* ReactModulesBridge.m in Sources */, - 608868D32D1D696E0070D199 /* InAppNotificationView.swift in Sources */, + 608868D32D1D696E0070D199 /* OverlayMessageView.swift in Sources */, 608868A52D1A9F070070D199 /* AppDelegate.swift in Sources */, 608868B22D1AA1FB0070D199 /* CustomKeyboardDelegate.swift in Sources */, ); @@ -504,11 +526,16 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 60E2079B2D21B0B400E6DBD2 /* AppDelegate+ApplicationState.swift in Sources */, + 60E2079C2D21B0B400E6DBD2 /* AppDelegate+Linking.swift in Sources */, + 60E2079D2D21B0B400E6DBD2 /* AppDelegate+Notifications.swift in Sources */, + 60E2079E2D21B0B400E6DBD2 /* AppDelegate+OverlayView.swift in Sources */, + 60E2079F2D21B0B400E6DBD2 /* AppDelegate+Shake.swift in Sources */, 608868A02D1A9F070070D199 /* NativeModule.swift in Sources */, 608868A12D1A9F070070D199 /* ShakeEventEmitter.swift in Sources */, 608868AC2D1AA19B0070D199 /* NativeScreenManager.swift in Sources */, 608868C62D1C3D130070D199 /* ReactModulesBridge.m in Sources */, - 608868D22D1D696E0070D199 /* InAppNotificationView.swift in Sources */, + 608868D22D1D696E0070D199 /* OverlayMessageView.swift in Sources */, 608868A22D1A9F070070D199 /* AppDelegate.swift in Sources */, 608868B32D1AA1FB0070D199 /* CustomKeyboardDelegate.swift in Sources */, ); From 35ba68ab714324c26f1bab3201c46f452ba1be32 Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Sun, 29 Dec 2024 19:23:59 +0200 Subject: [PATCH 34/51] chore: update detox-copilot cache. --- detox/test/detox_copilot_cache.json | 41 ++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/detox/test/detox_copilot_cache.json b/detox/test/detox_copilot_cache.json index 531ac66dfb..8bb236eae7 100644 --- a/detox/test/detox_copilot_cache.json +++ b/detox/test/detox_copilot_cache.json @@ -66,5 +66,44 @@ "{\"step\":\"Enable the second WebView\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the WebView screen\",\"code\":\"await element(by.text('WebView')).tap();\"}],\"viewHierarchyHash\":\"b8835bc4182c98f0bb33470103592e88\"}": "await element(by.id('toggle2ndWebviewButton')).tap();", "{\"step\":\"In the second WebView, verify the headline has the message \\\"This is a dummy webview.\\\"\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the WebView screen\",\"code\":\"await element(by.text('WebView')).tap();\"},{\"step\":\"Enable the second WebView\",\"code\":\"await element(by.id('toggle2ndWebviewButton')).tap();\"}],\"viewHierarchyHash\":\"32e8fc9416dc1fc3aee3dbb0d1ce2e32\"}": "await expect(web.element(by.web.id('message'))).toHaveText('This is a dummy webview.');", "{\"step\":\"In the second WebView, verify the headline has the message \\\"This is a dummy webview.\\\"\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the WebView screen\",\"code\":\"await element(by.text('WebView')).tap();\"},{\"step\":\"Enable the second WebView\",\"code\":\"await element(by.id('toggle2ndWebviewButton')).tap();\"},{\"step\":\"In the second WebView, verify the headline has the message \\\"This is a dummy webview.\\\"\",\"code\":\"await expect(web.element(by.web.id('message'))).toHaveText('This is a dummy webview.');\",\"result\":\"Caught an error while evaluating \\\"In the second WebView, verify the headline has the message \\\"This is a dummy webview.\\\"\\\", tried with generated code: \\\"await expect(web.element(by.web.id('message'))).toHaveText('This is a dummy webview.');\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"32e8fc9416dc1fc3aee3dbb0d1ce2e32\"}": "`await expect(web.element(by.web.id('message'))).toHaveText('This is a dummy webview.');`", - "{\"step\":\"Hide the second WebView\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the WebView screen\",\"code\":\"await element(by.text('WebView')).tap();\"},{\"step\":\"Enable the second WebView\",\"code\":\"await element(by.id('toggle2ndWebviewButton')).tap();\"},{\"step\":\"In the second WebView, verify the headline has the message \\\"This is a dummy webview.\\\"\",\"code\":\"`await expect(web.element(by.web.id('message'))).toHaveText('This is a dummy webview.');`\"}],\"viewHierarchyHash\":\"32e8fc9416dc1fc3aee3dbb0d1ce2e32\"}": "await element(by.id('toggle2ndWebviewButton')).tap();" + "{\"step\":\"Hide the second WebView\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the WebView screen\",\"code\":\"await element(by.text('WebView')).tap();\"},{\"step\":\"Enable the second WebView\",\"code\":\"await element(by.id('toggle2ndWebviewButton')).tap();\"},{\"step\":\"In the second WebView, verify the headline has the message \\\"This is a dummy webview.\\\"\",\"code\":\"`await expect(web.element(by.web.id('message'))).toHaveText('This is a dummy webview.');`\"}],\"viewHierarchyHash\":\"32e8fc9416dc1fc3aee3dbb0d1ce2e32\"}": "await element(by.id('toggle2ndWebviewButton')).tap();", + "{\"step\":\"Restart the React Native environment\",\"previous\":[],\"viewHierarchyHash\":\"7f840438086fdf188cb1ec5f909a8cfa\"}": "await device.reloadReactNative();", + "{\"step\":\"Go to the Actions screen\",\"previous\":[{\"step\":\"Restart the React Native environment\",\"code\":\"await device.reloadReactNative();\"}],\"viewHierarchyHash\":\"74f6292e255463a732384a5649407ee5\"}": "await element(by.text('Actions')).tap();", + "{\"step\":\"Tap the return key on the keyboard for the text input\",\"previous\":[{\"step\":\"Restart the React Native environment\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Go to the Actions screen\",\"code\":\"await element(by.text('Actions')).tap();\"}],\"viewHierarchyHash\":\"bb425e9320101560c151b2d020ce77b7\"}": "throw new Error(\"Multiple text inputs found in view hierarchy. Please specify which text input should receive the return key tap action.\");", + "{\"step\":\"Tap the return key on the keyboard for the text input\",\"previous\":[{\"step\":\"Restart the React Native environment\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Go to the Actions screen\",\"code\":\"await element(by.text('Actions')).tap();\"},{\"step\":\"Tap the return key on the keyboard for the text input\",\"code\":\"throw new Error(\\\"Multiple text inputs found in view hierarchy. Please specify which text input should receive the return key tap action.\\\");\",\"result\":\"Caught an error while evaluating \\\"Tap the return key on the keyboard for the text input\\\", tried with generated code: \\\"throw new Error(\\\"Multiple text inputs found in view hierarchy. Please specify which text input should receive the return key tap action.\\\");\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"bb425e9320101560c151b2d020ce77b7\"}": "throw new Error(\"Multiple text inputs found in the view hierarchy. Please specify which text input should receive the return key tap action.\");", + "{\"step\":\"Restart the React Native environment\",\"previous\":[],\"viewHierarchyHash\":\"70ca2d5c905bc2b6a32e3cda69a7aa33\"}": "await device.reloadReactNative();", + "{\"step\":\"The element with text \\\"Text1\\\" can be seen\",\"previous\":[{\"step\":\"Restart the React Native environment\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Go to the Actions screen\",\"code\":\"await element(by.text('Actions')).tap();\"}],\"viewHierarchyHash\":\"bb425e9320101560c151b2d020ce77b7\"}": "await expect(element(by.text('Text1'))).toBeVisible();", + "{\"step\":\"Swipe the view \\\"ScrollView161\\\" upwards\",\"previous\":[{\"step\":\"Restart the React Native environment\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Go to the Actions screen\",\"code\":\"await element(by.text('Actions')).tap();\"},{\"step\":\"The element with text \\\"Text1\\\" can be seen\",\"code\":\"await expect(element(by.text('Text1'))).toBeVisible();\"}],\"viewHierarchyHash\":\"bb425e9320101560c151b2d020ce77b7\"}": "throw new Error(\"Cannot find scroll view with ID 'ScrollView161' in the current view hierarchy.\");", + "{\"step\":\"Swipe the view \\\"ScrollView161\\\" upwards\",\"previous\":[{\"step\":\"Restart the React Native environment\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Go to the Actions screen\",\"code\":\"await element(by.text('Actions')).tap();\"},{\"step\":\"The element with text \\\"Text1\\\" can be seen\",\"code\":\"await expect(element(by.text('Text1'))).toBeVisible();\"},{\"step\":\"Swipe the view \\\"ScrollView161\\\" upwards\",\"code\":\"throw new Error(\\\"Cannot find scroll view with ID 'ScrollView161' in the current view hierarchy.\\\");\",\"result\":\"Caught an error while evaluating \\\"Swipe the view \\\"ScrollView161\\\" upwards\\\", tried with generated code: \\\"throw new Error(\\\"Cannot find scroll view with ID 'ScrollView161' in the current view hierarchy.\\\");\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"bb425e9320101560c151b2d020ce77b7\"}": "await element(by.id('ScrollView161')).swipe('up');", + "{\"step\":\"The Text1 element is no longer in view\",\"previous\":[{\"step\":\"Restart the React Native environment\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Go to the Actions screen\",\"code\":\"await element(by.text('Actions')).tap();\"},{\"step\":\"The element with text \\\"Text1\\\" can be seen\",\"code\":\"await expect(element(by.text('Text1'))).toBeVisible();\"},{\"step\":\"Swipe the view \\\"ScrollView161\\\" upwards\",\"code\":\"await element(by.id('ScrollView161')).swipe('up');\"}],\"viewHierarchyHash\":\"7a647a35084f285172ba19cacdfa312b\"}": "await expect(element(by.text('Text1'))).not.toBeVisible();", + "{\"step\":\"Swipe the element back up until the \\\"Text1\\\" element is visible\",\"previous\":[{\"step\":\"Restart the React Native environment\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Go to the Actions screen\",\"code\":\"await element(by.text('Actions')).tap();\"},{\"step\":\"The element with text \\\"Text1\\\" can be seen\",\"code\":\"await expect(element(by.text('Text1'))).toBeVisible();\"},{\"step\":\"Swipe the view \\\"ScrollView161\\\" upwards\",\"code\":\"await element(by.id('ScrollView161')).swipe('up');\"},{\"step\":\"The Text1 element is no longer in view\",\"code\":\"await expect(element(by.text('Text1'))).not.toBeVisible();\"}],\"viewHierarchyHash\":\"7a647a35084f285172ba19cacdfa312b\"}": "await waitFor(element(by.text('Text1')))\n .toBeVisible()\n .whileElement(by.id('ScrollView161'))\n .scroll(100, 'down');", + "{\"step\":\"Swipe the element back up until the \\\"Text1\\\" element is visible\",\"previous\":[{\"step\":\"Restart the React Native environment\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Go to the Actions screen\",\"code\":\"await element(by.text('Actions')).tap();\"},{\"step\":\"The element with text \\\"Text1\\\" can be seen\",\"code\":\"await expect(element(by.text('Text1'))).toBeVisible();\"},{\"step\":\"Swipe the view \\\"ScrollView161\\\" upwards\",\"code\":\"await element(by.id('ScrollView161')).swipe('up');\"},{\"step\":\"The Text1 element is no longer in view\",\"code\":\"await expect(element(by.text('Text1'))).not.toBeVisible();\"},{\"step\":\"Swipe the element back up until the \\\"Text1\\\" element is visible\",\"code\":\"await waitFor(element(by.text('Text1')))\\n .toBeVisible()\\n .whileElement(by.id('ScrollView161'))\\n .scroll(100, 'down');\",\"result\":\"Caught an error while evaluating \\\"Swipe the element back up until the \\\"Text1\\\" element is visible\\\", tried with generated code: \\\"await waitFor(element(by.text('Text1')))\\n .toBeVisible()\\n .whileElement(by.id('ScrollView161'))\\n .scroll(100, 'down');\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"7a647a35084f285172ba19cacdfa312b\"}": "await waitFor(element(by.text('Text1')))\n .toBeVisible()\n .whileElement(by.id('ScrollView161'))\n .scroll(100, 'down');", + "{\"step\":\"Remove the app and start a fresh instance\",\"previous\":[],\"viewHierarchyHash\":\"b16ef5d51f0f64d6a63d434b26d81efc\"}": "await device.launchApp({ newInstance: true, delete: true });", + "{\"step\":\"Navigate to the System Dialogs screen\",\"previous\":[{\"step\":\"Remove the app and start a fresh instance\",\"code\":\"await device.launchApp({ newInstance: true, delete: true });\"}],\"viewHierarchyHash\":\"b16ef5d51f0f64d6a63d434b26d81efc\"}": "```await element(by.text('System Dialogs')).tap();```", + "{\"step\":\"Navigate to the System Dialogs screen\",\"previous\":[{\"step\":\"Remove the app and start a fresh instance\",\"code\":\"await device.launchApp({ newInstance: true, delete: true });\"},{\"step\":\"Navigate to the System Dialogs screen\",\"code\":\"```await element(by.text('System Dialogs')).tap();```\",\"result\":\"Caught an error while evaluating \\\"Navigate to the System Dialogs screen\\\", tried with generated code: \\\"```await element(by.text('System Dialogs')).tap();```\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"b16ef5d51f0f64d6a63d434b26d81efc\"}": "```await element(by.text('System Dialogs')).tap();```", + "{\"step\":\"Restart the React Native state\",\"previous\":[],\"viewHierarchyHash\":\"0ca2c9cbebc7ab6248e6b4db4a70160d\"}": "await device.reloadReactNative();", + "{\"step\":\"Navigate to the DatePicker screen\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"}],\"viewHierarchyHash\":\"b16ef5d51f0f64d6a63d434b26d81efc\"}": "await element(by.label('DatePicker')).tap();", + "{\"step\":\"Verify there is an element with the text \\\"Compact Date Picker\\\"\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the DatePicker screen\",\"code\":\"await element(by.label('DatePicker')).tap();\"}],\"viewHierarchyHash\":\"338d7b478bd28a5a1c110d54da1748a7\"}": "await expect(element(by.text('Compact Date Picker'))).toExist();", + "{\"step\":\"Tap the element with the text \\\"Compact Date Picker\\\"\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the DatePicker screen\",\"code\":\"await element(by.label('DatePicker')).tap();\"},{\"step\":\"Verify there is an element with the text \\\"Compact Date Picker\\\"\",\"code\":\"await expect(element(by.text('Compact Date Picker'))).toExist();\"}],\"viewHierarchyHash\":\"338d7b478bd28a5a1c110d54da1748a7\"}": "await element(by.text('Compact Date Picker')).tap();", + "{\"step\":\"Verify there is an element with the text \\\"Inline Date Picker\\\"\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the DatePicker screen\",\"code\":\"await element(by.label('DatePicker')).tap();\"},{\"step\":\"Verify there is an element with the text \\\"Compact Date Picker\\\"\",\"code\":\"await expect(element(by.text('Compact Date Picker'))).toExist();\"},{\"step\":\"Tap the element with the text \\\"Compact Date Picker\\\"\",\"code\":\"await element(by.text('Compact Date Picker')).tap();\"}],\"viewHierarchyHash\":\"a6430970141975bb65460afdcfe68f60\"}": "await expect(element(by.text('Inline Date Picker'))).toExist();", + "{\"step\":\"Verify there is an element with today`s date at the bottom of the screen\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the DatePicker screen\",\"code\":\"await element(by.label('DatePicker')).tap();\"},{\"step\":\"Verify there is an element with the text \\\"Compact Date Picker\\\"\",\"code\":\"await expect(element(by.text('Compact Date Picker'))).toExist();\"},{\"step\":\"Tap the element with the text \\\"Compact Date Picker\\\"\",\"code\":\"await element(by.text('Compact Date Picker')).tap();\"},{\"step\":\"Verify there is an element with the text \\\"Inline Date Picker\\\"\",\"code\":\"await expect(element(by.text('Inline Date Picker'))).toExist();\"}],\"viewHierarchyHash\":\"a6430970141975bb65460afdcfe68f60\"}": "await expect(element(by.id('datePicker'))).toExist();\nawait expect(element(by.label('July 2023'))).toBeVisible();\nawait expect(element(by.label('Monday, 31 July'))).toBeVisible(); // Verifies date at bottom of calendar", + "{\"step\":\"Verify there is an element with today`s date at the bottom of the screen\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the DatePicker screen\",\"code\":\"await element(by.label('DatePicker')).tap();\"},{\"step\":\"Verify there is an element with the text \\\"Compact Date Picker\\\"\",\"code\":\"await expect(element(by.text('Compact Date Picker'))).toExist();\"},{\"step\":\"Tap the element with the text \\\"Compact Date Picker\\\"\",\"code\":\"await element(by.text('Compact Date Picker')).tap();\"},{\"step\":\"Verify there is an element with the text \\\"Inline Date Picker\\\"\",\"code\":\"await expect(element(by.text('Inline Date Picker'))).toExist();\"},{\"step\":\"Verify there is an element with today`s date at the bottom of the screen\",\"code\":\"await expect(element(by.id('datePicker'))).toExist();\\nawait expect(element(by.label('July 2023'))).toBeVisible();\\nawait expect(element(by.label('Monday, 31 July'))).toBeVisible(); // Verifies date at bottom of calendar\",\"result\":\"Caught an error while evaluating \\\"Verify there is an element with today`s date at the bottom of the screen\\\", tried with generated code: \\\"await expect(element(by.id('datePicker'))).toExist();\\nawait expect(element(by.label('July 2023'))).toBeVisible();\\nawait expect(element(by.label('Monday, 31 July'))).toBeVisible(); // Verifies date at bottom of calendar\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"a6430970141975bb65460afdcfe68f60\"}": "await expect(element(by.label('July 2023'))).toBeVisible();\nawait expect(element(by.label('31'))).toBeVisible();", + "{\"step\":\"Restart the React Native state\",\"previous\":[],\"viewHierarchyHash\":\"a6430970141975bb65460afdcfe68f60\"}": "await device.reloadReactNative();", + "{\"step\":\"Tap the element with the text \\\"Inline Date Picker\\\"\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the DatePicker screen\",\"code\":\"await element(by.label('DatePicker')).tap();\"},{\"step\":\"Verify there is an element with the text \\\"Compact Date Picker\\\"\",\"code\":\"await expect(element(by.text('Compact Date Picker'))).toExist();\"},{\"step\":\"Tap the element with the text \\\"Compact Date Picker\\\"\",\"code\":\"await element(by.text('Compact Date Picker')).tap();\"},{\"step\":\"Verify there is an element with the text \\\"Inline Date Picker\\\"\",\"code\":\"await expect(element(by.text('Inline Date Picker'))).toExist();\"}],\"viewHierarchyHash\":\"a6430970141975bb65460afdcfe68f60\"}": "await element(by.text('Inline Date Picker')).tap();", + "{\"step\":\"Verify that there is slider element at the bottom of the screen\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the DatePicker screen\",\"code\":\"await element(by.label('DatePicker')).tap();\"},{\"step\":\"Verify there is an element with the text \\\"Compact Date Picker\\\"\",\"code\":\"await expect(element(by.text('Compact Date Picker'))).toExist();\"},{\"step\":\"Tap the element with the text \\\"Compact Date Picker\\\"\",\"code\":\"await element(by.text('Compact Date Picker')).tap();\"},{\"step\":\"Verify there is an element with the text \\\"Inline Date Picker\\\"\",\"code\":\"await expect(element(by.text('Inline Date Picker'))).toExist();\"},{\"step\":\"Tap the element with the text \\\"Inline Date Picker\\\"\",\"code\":\"await element(by.text('Inline Date Picker')).tap();\"}],\"viewHierarchyHash\":\"dd6b04249fe6f709c315b5de2b08ec23\"}": "await expect(element(by.id('datePicker'))).toExist();\nconst attributes = await element(by.id('datePicker')).getAttributes();\njestExpect(attributes.y).toBeGreaterThan(500);", + "{\"step\":\"Verify that there is slider element at the bottom of the screen\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the DatePicker screen\",\"code\":\"await element(by.label('DatePicker')).tap();\"},{\"step\":\"Verify there is an element with the text \\\"Compact Date Picker\\\"\",\"code\":\"await expect(element(by.text('Compact Date Picker'))).toExist();\"},{\"step\":\"Tap the element with the text \\\"Compact Date Picker\\\"\",\"code\":\"await element(by.text('Compact Date Picker')).tap();\"},{\"step\":\"Verify there is an element with the text \\\"Inline Date Picker\\\"\",\"code\":\"await expect(element(by.text('Inline Date Picker'))).toExist();\"},{\"step\":\"Tap the element with the text \\\"Inline Date Picker\\\"\",\"code\":\"await element(by.text('Inline Date Picker')).tap();\"},{\"step\":\"Verify that there is slider element at the bottom of the screen\",\"code\":\"await expect(element(by.id('datePicker'))).toExist();\\nconst attributes = await element(by.id('datePicker')).getAttributes();\\njestExpect(attributes.y).toBeGreaterThan(500);\",\"result\":\"Caught an error while evaluating \\\"Verify that there is slider element at the bottom of the screen\\\", tried with generated code: \\\"await expect(element(by.id('datePicker'))).toExist();\\nconst attributes = await element(by.id('datePicker')).getAttributes();\\njestExpect(attributes.y).toBeGreaterThan(500);\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"dd6b04249fe6f709c315b5de2b08ec23\"}": "await expect(element(by.id('datePicker'))).toExist();\nconst attributes = await element(by.id('datePicker')).getAttributes();\njestExpect(attributes.y).toBeGreaterThan(500);", + "{\"step\":\"Reset react native state\",\"previous\":[],\"viewHierarchyHash\":\"b16ef5d51f0f64d6a63d434b26d81efc\"}": "await element(by.label('Switch Root')).tap();", + "{\"step\":\"Enter the \\\"Shape Matching\\\" game screen\",\"previous\":[{\"step\":\"Reset react native state\",\"code\":\"await element(by.label('Switch Root')).tap();\"}],\"viewHierarchyHash\":\"5d4d366cd4d5e8e92dff8bfcfc171e3a\"}": "throw new Error(\"Unable to find any elements related to 'Shape Matching' game screen in the current view hierarchy. The only visible elements are 'Switch to a new native root' and 'Switch to multiple react roots'.\");", + "{\"step\":\"Enter the \\\"Shape Matching\\\" game screen\",\"previous\":[{\"step\":\"Reset react native state\",\"code\":\"await element(by.label('Switch Root')).tap();\"},{\"step\":\"Enter the \\\"Shape Matching\\\" game screen\",\"code\":\"throw new Error(\\\"Unable to find any elements related to 'Shape Matching' game screen in the current view hierarchy. The only visible elements are 'Switch to a new native root' and 'Switch to multiple react roots'.\\\");\",\"result\":\"Caught an error while evaluating \\\"Enter the \\\"Shape Matching\\\" game screen\\\", tried with generated code: \\\"throw new Error(\\\"Unable to find any elements related to 'Shape Matching' game screen in the current view hierarchy. The only visible elements are 'Switch to a new native root' and 'Switch to multiple react roots'.\\\");\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"5d4d366cd4d5e8e92dff8bfcfc171e3a\"}": "throw new Error(\"Unable to find elements related to 'Shape Matching' game screen in the current view hierarchy. The only visible elements are 'Switch to a new native root' and 'Switch to multiple react roots'.\");", + "{\"step\":\"Restart the React Native state\",\"previous\":[],\"viewHierarchyHash\":\"b16ef5d51f0f64d6a63d434b26d81efc\"}": "await device.reloadReactNative();", + "{\"step\":\"Navigate to the Location screen\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"}],\"viewHierarchyHash\":\"b16ef5d51f0f64d6a63d434b26d81efc\"}": "await element(by.label('Location')).tap();", + "{\"step\":\"Launch the app with location permission denied\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Location screen\",\"code\":\"await element(by.label('Location')).tap();\"}],\"viewHierarchyHash\":\"e79b58a41e3e4e6b49202372b5c1cc79\"}": "await device.launchApp({\n newInstance: true,\n permissions: { \n location: \"never\"\n }\n});", + "{\"step\":\"Verify there is an element with the text \\\"Get location\\\"\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Location screen\",\"code\":\"await element(by.label('Location')).tap();\"},{\"step\":\"Launch the app with location permission denied\",\"code\":\"await device.launchApp({\\n newInstance: true,\\n permissions: { \\n location: \\\"never\\\"\\n }\\n});\"}],\"viewHierarchyHash\":\"b16ef5d51f0f64d6a63d434b26d81efc\"}": "await expect(element(by.text('Get location'))).toExist();", + "{\"step\":\"Verify there is an element with the text \\\"Get location\\\"\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Location screen\",\"code\":\"await element(by.label('Location')).tap();\"},{\"step\":\"Launch the app with location permission denied\",\"code\":\"await device.launchApp({\\n newInstance: true,\\n permissions: { \\n location: \\\"never\\\"\\n }\\n});\"},{\"step\":\"Verify there is an element with the text \\\"Get location\\\"\",\"code\":\"await expect(element(by.text('Get location'))).toExist();\",\"result\":\"Caught an error while evaluating \\\"Verify there is an element with the text \\\"Get location\\\"\\\", tried with generated code: \\\"await expect(element(by.text('Get location'))).toExist();\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"b16ef5d51f0f64d6a63d434b26d81efc\"}": "throw new Error(\"The text 'Get location' is not visible in the current view hierarchy or snapshot image\");", + "{\"step\":\"Launch the app with location permission always\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Location screen\",\"code\":\"await element(by.label('Location')).tap();\"}],\"viewHierarchyHash\":\"e79b58a41e3e4e6b49202372b5c1cc79\"}": "await device.launchApp({ \n newInstance: true,\n permissions: { location: 'always' }\n});", + "{\"step\":\"Verify there is an element with the text \\\"Get location\\\"\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Location screen\",\"code\":\"await element(by.label('Location')).tap();\"},{\"step\":\"Launch the app with location permission always\",\"code\":\"await device.launchApp({ \\n newInstance: true,\\n permissions: { location: 'always' }\\n});\"}],\"viewHierarchyHash\":\"b16ef5d51f0f64d6a63d434b26d81efc\"}": "throw new Error(\"No element with text 'Get location' found in the view hierarchy. Found 'Location' instead.\");", + "{\"step\":\"Verify there is an element with the text \\\"Get location\\\"\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Location screen\",\"code\":\"await element(by.label('Location')).tap();\"},{\"step\":\"Launch the app with location permission always\",\"code\":\"await device.launchApp({ \\n newInstance: true,\\n permissions: { location: 'always' }\\n});\"},{\"step\":\"Verify there is an element with the text \\\"Get location\\\"\",\"code\":\"throw new Error(\\\"No element with text 'Get location' found in the view hierarchy. Found 'Location' instead.\\\");\",\"result\":\"Caught an error while evaluating \\\"Verify there is an element with the text \\\"Get location\\\"\\\", tried with generated code: \\\"throw new Error(\\\"No element with text 'Get location' found in the view hierarchy. Found 'Location' instead.\\\");\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"b16ef5d51f0f64d6a63d434b26d81efc\"}": "throw new Error(\"No element with text 'Get location' found in the view hierarchy. Found 'Location' instead.\");", + "{\"step\":\"Launch the app with location permission just once\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Location screen\",\"code\":\"await element(by.label('Location')).tap();\"}],\"viewHierarchyHash\":\"e79b58a41e3e4e6b49202372b5c1cc79\"}": "await device.launchApp({\n newInstance: true,\n permissions: { location: 'inuse' }\n});", + "{\"step\":\"Verify there is an element with the text \\\"Get location\\\"\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Location screen\",\"code\":\"await element(by.label('Location')).tap();\"},{\"step\":\"Launch the app with location permission just once\",\"code\":\"await device.launchApp({\\n newInstance: true,\\n permissions: { location: 'inuse' }\\n});\"}],\"viewHierarchyHash\":\"b16ef5d51f0f64d6a63d434b26d81efc\"}": "await expect(element(by.text('Get location'))).toExist();", + "{\"step\":\"Verify there is an element with the text \\\"Get location\\\"\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Location screen\",\"code\":\"await element(by.label('Location')).tap();\"},{\"step\":\"Launch the app with location permission just once\",\"code\":\"await device.launchApp({\\n newInstance: true,\\n permissions: { location: 'inuse' }\\n});\"},{\"step\":\"Verify there is an element with the text \\\"Get location\\\"\",\"code\":\"await expect(element(by.text('Get location'))).toExist();\",\"result\":\"Caught an error while evaluating \\\"Verify there is an element with the text \\\"Get location\\\"\\\", tried with generated code: \\\"await expect(element(by.text('Get location'))).toExist();\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"b16ef5d51f0f64d6a63d434b26d81efc\"}": "throw new Error(\"Element with text 'Get location' is not found in the current view hierarchy\")" } \ No newline at end of file From 594c66c30ec896ac0b03bb48df11e91b767e8b0b Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Sun, 29 Dec 2024 22:10:36 +0200 Subject: [PATCH 35/51] test: update image snapshot tests. --- ...os.horiz.png => elementScreenshot.horiz.ios.png} | Bin ....ios.vert.png => elementScreenshot.vert.ios.png} | Bin 2 files changed, 0 insertions(+), 0 deletions(-) rename detox/test/e2e/assets/{elementScreenshot.ios.horiz.png => elementScreenshot.horiz.ios.png} (100%) rename detox/test/e2e/assets/{elementScreenshot.ios.vert.png => elementScreenshot.vert.ios.png} (100%) diff --git a/detox/test/e2e/assets/elementScreenshot.ios.horiz.png b/detox/test/e2e/assets/elementScreenshot.horiz.ios.png similarity index 100% rename from detox/test/e2e/assets/elementScreenshot.ios.horiz.png rename to detox/test/e2e/assets/elementScreenshot.horiz.ios.png diff --git a/detox/test/e2e/assets/elementScreenshot.ios.vert.png b/detox/test/e2e/assets/elementScreenshot.vert.ios.png similarity index 100% rename from detox/test/e2e/assets/elementScreenshot.ios.vert.png rename to detox/test/e2e/assets/elementScreenshot.vert.ios.png From 9c7b542974a9b462f4027a4bfebb9e7265d3a9af Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Sun, 29 Dec 2024 23:07:09 +0200 Subject: [PATCH 36/51] test(ios): support shake events from test-app. --- .../AppDelegate+Shake.swift | 21 ---------- detox/test/ios/AppDelegate.swift | 7 +++- .../ios/ReactModules/ShakeEventEmitter.swift | 39 +++++++++++-------- .../test/ios/UI/UIViewController+Shake.swift | 37 ++++++++++++++++++ .../ios/example.xcodeproj/project.pbxproj | 12 +++--- 5 files changed, 71 insertions(+), 45 deletions(-) delete mode 100644 detox/test/ios/AppDelegate Extensions/AppDelegate+Shake.swift create mode 100644 detox/test/ios/UI/UIViewController+Shake.swift diff --git a/detox/test/ios/AppDelegate Extensions/AppDelegate+Shake.swift b/detox/test/ios/AppDelegate Extensions/AppDelegate+Shake.swift deleted file mode 100644 index d8c1eec128..0000000000 --- a/detox/test/ios/AppDelegate Extensions/AppDelegate+Shake.swift +++ /dev/null @@ -1,21 +0,0 @@ -// -// AppDelegate+Shake.swift (example) -// Created by Asaf Korem (Wix.com) on 2024. -// - -import UIKit -import React - -extension AppDelegate { - override func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) { - if motion == .motionShake { - if let rootView = window?.rootViewController?.view as? RCTRootView { - let bridge = rootView.bridge - let shakeModule = bridge.module(for: ShakeEventEmitter.self) as! ShakeEventEmitter - shakeModule.handleShake() - } - } else { - super.motionEnded(motion, with: event) - } - } -} diff --git a/detox/test/ios/AppDelegate.swift b/detox/test/ios/AppDelegate.swift index e78726e852..34a9fcea7a 100644 --- a/detox/test/ios/AppDelegate.swift +++ b/detox/test/ios/AppDelegate.swift @@ -21,6 +21,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { setupNotifications() setupScreenManager() setupApplicationStateObservers() + setupShakeDetection() return true } @@ -45,10 +46,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } private func setupNotifications() { - // Set ourselves as the UNUserNotificationCenter delegate UNUserNotificationCenter.current().delegate = self - // Example: Listen for a custom "ChangeScreen" event NotificationCenter.default.addObserver( forName: Notification.Name("ChangeScreen"), object: nil, @@ -61,6 +60,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate { private func setupScreenManager() { screenManager = NativeScreenManager(window: window) } + + private func setupShakeDetection() { + UIViewController.swizzleMotionEnded() + } } // MARK: - RCTBridgeDelegate diff --git a/detox/test/ios/ReactModules/ShakeEventEmitter.swift b/detox/test/ios/ReactModules/ShakeEventEmitter.swift index 400488cdcc..012ee712f4 100644 --- a/detox/test/ios/ReactModules/ShakeEventEmitter.swift +++ b/detox/test/ios/ReactModules/ShakeEventEmitter.swift @@ -9,20 +9,27 @@ import React @objc(ShakeEventEmitter) class ShakeEventEmitter: RCTEventEmitter { - // MARK: - RCTEventEmitter Overrides - - override static func requiresMainQueueSetup() -> Bool { - return true - } - - override func supportedEvents() -> [String]! { - return ["ShakeEvent"] - } - - // MARK: - Public Methods - - @objc - func handleShake() { - sendEvent(withName: "ShakeEvent", body: nil) - } + static var reactInstance: ShakeEventEmitter? = nil + + override init() { + super.init() + ShakeEventEmitter.reactInstance = self + } + + // MARK: - RCTEventEmitter Overrides + + override static func requiresMainQueueSetup() -> Bool { + return true + } + + override func supportedEvents() -> [String]! { + return ["ShakeEvent"] + } + + // MARK: - Public Methods + + @objc + func handleShake() { + sendEvent(withName: "ShakeEvent", body: nil) + } } diff --git a/detox/test/ios/UI/UIViewController+Shake.swift b/detox/test/ios/UI/UIViewController+Shake.swift new file mode 100644 index 0000000000..db464fcd70 --- /dev/null +++ b/detox/test/ios/UI/UIViewController+Shake.swift @@ -0,0 +1,37 @@ +// +// UIViewController+Shake.swift (example) +// Created by Asaf Korem (Wix.com) on 2024. +// + +import UIKit +import React + +extension UIViewController { + + static func swizzleMotionEnded() { + guard let originalMethod = class_getInstanceMethod(UIViewController.self, #selector(motionEnded(_:with:))), + let swizzledMethod = class_getInstanceMethod(UIViewController.self, #selector(swizzled_motionEnded(_:with:)) + ) else { return } + + method_exchangeImplementations(originalMethod, swizzledMethod) + } + + @objc private func swizzled_motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) { + if motion == .motionShake { + handleGlobalShakeGesture() + } + + if self.responds(to: #selector(swizzled_motionEnded(_:with:))) { + self.swizzled_motionEnded(motion, with: event) + } + } + + + private func handleGlobalShakeGesture() { + guard let shakeModule = ShakeEventEmitter.reactInstance else { + return + } + + shakeModule.handleShake() + } +} diff --git a/detox/test/ios/example.xcodeproj/project.pbxproj b/detox/test/ios/example.xcodeproj/project.pbxproj index 7a4fe3d392..8c779b3a1c 100644 --- a/detox/test/ios/example.xcodeproj/project.pbxproj +++ b/detox/test/ios/example.xcodeproj/project.pbxproj @@ -27,16 +27,16 @@ 608868D22D1D696E0070D199 /* OverlayMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 608868D12D1D696E0070D199 /* OverlayMessageView.swift */; }; 608868D32D1D696E0070D199 /* OverlayMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 608868D12D1D696E0070D199 /* OverlayMessageView.swift */; }; 609DDB892D10C21800028574 /* Detox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 609DDB852D10C20800028574 /* Detox.framework */; }; + 60A403A92D21EAE3004344C3 /* UIViewController+Shake.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60A403A82D21EADE004344C3 /* UIViewController+Shake.swift */; }; + 60A403AA2D21EAE3004344C3 /* UIViewController+Shake.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60A403A82D21EADE004344C3 /* UIViewController+Shake.swift */; }; 60E207962D21B0B400E6DBD2 /* AppDelegate+ApplicationState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60E207902D21B0B400E6DBD2 /* AppDelegate+ApplicationState.swift */; }; 60E207972D21B0B400E6DBD2 /* AppDelegate+Linking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60E207912D21B0B400E6DBD2 /* AppDelegate+Linking.swift */; }; 60E207982D21B0B400E6DBD2 /* AppDelegate+Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60E207922D21B0B400E6DBD2 /* AppDelegate+Notifications.swift */; }; 60E207992D21B0B400E6DBD2 /* AppDelegate+OverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60E207932D21B0B400E6DBD2 /* AppDelegate+OverlayView.swift */; }; - 60E2079A2D21B0B400E6DBD2 /* AppDelegate+Shake.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60E207942D21B0B400E6DBD2 /* AppDelegate+Shake.swift */; }; 60E2079B2D21B0B400E6DBD2 /* AppDelegate+ApplicationState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60E207902D21B0B400E6DBD2 /* AppDelegate+ApplicationState.swift */; }; 60E2079C2D21B0B400E6DBD2 /* AppDelegate+Linking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60E207912D21B0B400E6DBD2 /* AppDelegate+Linking.swift */; }; 60E2079D2D21B0B400E6DBD2 /* AppDelegate+Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60E207922D21B0B400E6DBD2 /* AppDelegate+Notifications.swift */; }; 60E2079E2D21B0B400E6DBD2 /* AppDelegate+OverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60E207932D21B0B400E6DBD2 /* AppDelegate+OverlayView.swift */; }; - 60E2079F2D21B0B400E6DBD2 /* AppDelegate+Shake.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60E207942D21B0B400E6DBD2 /* AppDelegate+Shake.swift */; }; 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; 90CCD0BA1322566EB1285DC2 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */; }; CA25A6405AF58BA742F7FD47 /* libPods-example-ci.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1CB0FE045763B5363B2E7054 /* libPods-example-ci.a */; }; @@ -83,11 +83,11 @@ 608868C42D1C3D0E0070D199 /* ReactModulesBridge.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ReactModulesBridge.m; sourceTree = ""; }; 608868D12D1D696E0070D199 /* OverlayMessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverlayMessageView.swift; sourceTree = ""; }; 609DDB772D10C20700028574 /* Detox.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Detox.xcodeproj; path = /Users/asafk/Development/Detox/detox/ios/Detox.xcodeproj; sourceTree = ""; }; + 60A403A82D21EADE004344C3 /* UIViewController+Shake.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+Shake.swift"; sourceTree = ""; }; 60E207902D21B0B400E6DBD2 /* AppDelegate+ApplicationState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+ApplicationState.swift"; sourceTree = ""; }; 60E207912D21B0B400E6DBD2 /* AppDelegate+Linking.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+Linking.swift"; sourceTree = ""; }; 60E207922D21B0B400E6DBD2 /* AppDelegate+Notifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+Notifications.swift"; sourceTree = ""; }; 60E207932D21B0B400E6DBD2 /* AppDelegate+OverlayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+OverlayView.swift"; sourceTree = ""; }; - 60E207942D21B0B400E6DBD2 /* AppDelegate+Shake.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+Shake.swift"; sourceTree = ""; }; 80C930B7D5D6C25DA27372A5 /* Pods-example-ci.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example-ci.release.xcconfig"; path = "Target Support Files/Pods-example-ci/Pods-example-ci.release.xcconfig"; sourceTree = ""; }; 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = example/LaunchScreen.storyboard; sourceTree = ""; }; 89C6BE57DB24E9ADA2F236DE /* Pods-example-exampleTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example-exampleTests.release.xcconfig"; path = "Target Support Files/Pods-example-exampleTests/Pods-example-exampleTests.release.xcconfig"; sourceTree = ""; }; @@ -157,6 +157,7 @@ 608868B12D1AA1FB0070D199 /* UI */ = { isa = PBXGroup; children = ( + 60A403A82D21EADE004344C3 /* UIViewController+Shake.swift */, 608868B02D1AA1FB0070D199 /* CustomKeyboardDelegate.swift */, 608868AA2D1AA19B0070D199 /* NativeScreenManager.swift */, 608868D12D1D696E0070D199 /* OverlayMessageView.swift */, @@ -180,7 +181,6 @@ 60E207912D21B0B400E6DBD2 /* AppDelegate+Linking.swift */, 60E207922D21B0B400E6DBD2 /* AppDelegate+Notifications.swift */, 60E207932D21B0B400E6DBD2 /* AppDelegate+OverlayView.swift */, - 60E207942D21B0B400E6DBD2 /* AppDelegate+Shake.swift */, ); path = "AppDelegate Extensions"; sourceTree = ""; @@ -508,10 +508,10 @@ buildActionMask = 2147483647; files = ( 60E207962D21B0B400E6DBD2 /* AppDelegate+ApplicationState.swift in Sources */, + 60A403AA2D21EAE3004344C3 /* UIViewController+Shake.swift in Sources */, 60E207972D21B0B400E6DBD2 /* AppDelegate+Linking.swift in Sources */, 60E207982D21B0B400E6DBD2 /* AppDelegate+Notifications.swift in Sources */, 60E207992D21B0B400E6DBD2 /* AppDelegate+OverlayView.swift in Sources */, - 60E2079A2D21B0B400E6DBD2 /* AppDelegate+Shake.swift in Sources */, 608868A32D1A9F070070D199 /* NativeModule.swift in Sources */, 608868A42D1A9F070070D199 /* ShakeEventEmitter.swift in Sources */, 608868AB2D1AA19B0070D199 /* NativeScreenManager.swift in Sources */, @@ -527,10 +527,10 @@ buildActionMask = 2147483647; files = ( 60E2079B2D21B0B400E6DBD2 /* AppDelegate+ApplicationState.swift in Sources */, + 60A403A92D21EAE3004344C3 /* UIViewController+Shake.swift in Sources */, 60E2079C2D21B0B400E6DBD2 /* AppDelegate+Linking.swift in Sources */, 60E2079D2D21B0B400E6DBD2 /* AppDelegate+Notifications.swift in Sources */, 60E2079E2D21B0B400E6DBD2 /* AppDelegate+OverlayView.swift in Sources */, - 60E2079F2D21B0B400E6DBD2 /* AppDelegate+Shake.swift in Sources */, 608868A02D1A9F070070D199 /* NativeModule.swift in Sources */, 608868A12D1A9F070070D199 /* ShakeEventEmitter.swift in Sources */, 608868AC2D1AA19B0070D199 /* NativeScreenManager.swift in Sources */, From d4fe2ad250841128d83f816a14751487e7b1abdf Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Tue, 31 Dec 2024 17:39:32 +0200 Subject: [PATCH 37/51] test(app): support stack view for toasts. --- .../AppDelegate+OverlayView.swift | 46 ++++++++++++++----- detox/test/ios/UI/OverlayMessageView.swift | 4 +- 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/detox/test/ios/AppDelegate Extensions/AppDelegate+OverlayView.swift b/detox/test/ios/AppDelegate Extensions/AppDelegate+OverlayView.swift index 49c19e1707..fdd58fb886 100644 --- a/detox/test/ios/AppDelegate Extensions/AppDelegate+OverlayView.swift +++ b/detox/test/ios/AppDelegate Extensions/AppDelegate+OverlayView.swift @@ -3,30 +3,54 @@ // Created by Asaf Korem (Wix.com) on 2024. // - import UIKit import React extension AppDelegate { - func showOverlayMessage(withMessage message: String) { + private var overlayStackView: UIStackView? { guard let rootView = window?.rootViewController?.view as? RCTRootView, let contentView = rootView.value(forKey: "contentView") as? UIView else { - return + return nil } - let messageView = OverlayMessageView(message: message) - contentView.addSubview(messageView) + return contentView.subviews.compactMap { $0 as? UIStackView } + .first { $0.accessibilityIdentifier == "overlayStackView" } + } + + private func createOverlayStackView(in contentView: UIView) -> UIStackView { + let stackView = UIStackView() + stackView.axis = .vertical + stackView.distribution = .equalSpacing + stackView.spacing = 8 + stackView.translatesAutoresizingMaskIntoConstraints = false + + stackView.accessibilityIdentifier = "overlayStackView" + + contentView.addSubview(stackView) NSLayoutConstraint.activate([ - messageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), - messageView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), - messageView.topAnchor.constraint(equalTo: contentView.safeAreaLayoutGuide.topAnchor), - messageView.heightAnchor.constraint(equalToConstant: 60) + stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), + stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), + stackView.topAnchor.constraint(equalTo: contentView.safeAreaLayoutGuide.topAnchor) ]) - contentView.bringSubviewToFront(messageView) + return stackView } -} + func showOverlayMessage(withMessage message: String) { + guard + let rootView = window?.rootViewController?.view as? RCTRootView, + let contentView = rootView.value(forKey: "contentView") as? UIView + else { + return + } + + let stackView = overlayStackView ?? createOverlayStackView(in: contentView) + let messageView = OverlayMessageView(message: message) + + stackView.addArrangedSubview(messageView) + contentView.bringSubviewToFront(stackView) + } +} diff --git a/detox/test/ios/UI/OverlayMessageView.swift b/detox/test/ios/UI/OverlayMessageView.swift index c20b4448b6..0b1298c3a8 100644 --- a/detox/test/ios/UI/OverlayMessageView.swift +++ b/detox/test/ios/UI/OverlayMessageView.swift @@ -45,6 +45,8 @@ class OverlayMessageView: UIView { addSubview(closeButton) NSLayoutConstraint.activate([ + heightAnchor.constraint(equalToConstant: 60), + messageLabel.centerYAnchor.constraint(equalTo: centerYAnchor), messageLabel.centerXAnchor.constraint(equalTo: centerXAnchor), messageLabel.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor, constant: 40), @@ -58,7 +60,7 @@ class OverlayMessageView: UIView { closeButton.addTarget(self, action: #selector(didTapClose), for: .touchUpInside) - Timer.scheduledTimer(withTimeInterval: 1.0, repeats: false) { [weak self] _ in + Timer.scheduledTimer(withTimeInterval: 2.0, repeats: false) { [weak self] _ in self?.removeFromSuperview() } } From c27a0032352b2cf37643ba5cf9c83ff863c8dcb5 Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Tue, 31 Dec 2024 19:20:24 +0200 Subject: [PATCH 38/51] test: fix test text matcher. --- detox/test/e2e/08.stress-root.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/detox/test/e2e/08.stress-root.test.js b/detox/test/e2e/08.stress-root.test.js index 8e5113922c..277da273b2 100644 --- a/detox/test/e2e/08.stress-root.test.js +++ b/detox/test/e2e/08.stress-root.test.js @@ -10,7 +10,7 @@ describe('StressRoot', () => { it('should switch root view controller from RN to native', async () => { await element(by.text('Switch to a new native root')).tap(); - await expect(element(by.text('this is a new native root'))).toBeVisible(); + await expect(element(by.text('This is a new native root'))).toBeVisible(); }); it(':ios: should switch root view controller from RN to RN', async () => { From 88f22ff7ccab4fad193125322fd98d20b74a4c9a Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Wed, 1 Jan 2025 13:40:27 +0200 Subject: [PATCH 39/51] test(app): remove old iOS code. --- detox/test/ios-old/.xcode.env | 11 - detox/test/ios-old/Podfile | 122 --- .../ios-old/example.xcodeproj/project.pbxproj | 930 ------------------ .../xcshareddata/xcschemes/example.xcscheme | 184 ---- .../xcschemes/example_ci.xcscheme | 91 -- detox/test/ios-old/example/AppDelegate.h | 17 - detox/test/ios-old/example/AppDelegate.m | 370 ------- .../example/Base.lproj/LaunchScreen.xib | 42 - .../example/CustomKeyboardViewController.h | 17 - .../example/CustomKeyboardViewController.m | 117 --- .../AppIcon.appiconset/Contents.json | 53 - detox/test/ios-old/example/Info.plist | 108 -- detox/test/ios-old/example/NativeModule.h | 6 - detox/test/ios-old/example/NativeModule.m | 119 --- .../ios-old/example/PrivacyInfo.xcprivacy | 37 - .../test/ios-old/example/example.entitlements | 8 - detox/test/ios-old/example/main.m | 9 - detox/test/ios-old/exampleUITests/Info.plist | 22 - .../ios-old/exampleUITests/exampleUITests.m | 42 - detox/test/ios-old/example_ci.entitlements | 8 - 20 files changed, 2313 deletions(-) delete mode 100644 detox/test/ios-old/.xcode.env delete mode 100644 detox/test/ios-old/Podfile delete mode 100644 detox/test/ios-old/example.xcodeproj/project.pbxproj delete mode 100644 detox/test/ios-old/example.xcodeproj/xcshareddata/xcschemes/example.xcscheme delete mode 100644 detox/test/ios-old/example.xcodeproj/xcshareddata/xcschemes/example_ci.xcscheme delete mode 100644 detox/test/ios-old/example/AppDelegate.h delete mode 100644 detox/test/ios-old/example/AppDelegate.m delete mode 100644 detox/test/ios-old/example/Base.lproj/LaunchScreen.xib delete mode 100644 detox/test/ios-old/example/CustomKeyboardViewController.h delete mode 100644 detox/test/ios-old/example/CustomKeyboardViewController.m delete mode 100644 detox/test/ios-old/example/Images.xcassets/AppIcon.appiconset/Contents.json delete mode 100644 detox/test/ios-old/example/Info.plist delete mode 100644 detox/test/ios-old/example/NativeModule.h delete mode 100644 detox/test/ios-old/example/NativeModule.m delete mode 100644 detox/test/ios-old/example/PrivacyInfo.xcprivacy delete mode 100644 detox/test/ios-old/example/example.entitlements delete mode 100644 detox/test/ios-old/example/main.m delete mode 100644 detox/test/ios-old/exampleUITests/Info.plist delete mode 100644 detox/test/ios-old/exampleUITests/exampleUITests.m delete mode 100644 detox/test/ios-old/example_ci.entitlements diff --git a/detox/test/ios-old/.xcode.env b/detox/test/ios-old/.xcode.env deleted file mode 100644 index ce9964f6b7..0000000000 --- a/detox/test/ios-old/.xcode.env +++ /dev/null @@ -1,11 +0,0 @@ -# This `.xcode.env` file is versioned and is used to source the environment -2 # used when running script phases inside Xcode. -3 # To customize your local environment, you can create an `.xcode.env.local` -4 # file that is not versioned. -5 -6 # NODE_BINARY variable contains the PATH to the node executable. -7 # -8 # Customize the NODE_BINARY variable here. -9 # For example, to use nvm with brew, add the following line -10 # . "$(brew --prefix nvm)/nvm.sh" --no-use -11 export NODE_BINARY=$(command -v node) diff --git a/detox/test/ios-old/Podfile b/detox/test/ios-old/Podfile deleted file mode 100644 index 030471de41..0000000000 --- a/detox/test/ios-old/Podfile +++ /dev/null @@ -1,122 +0,0 @@ -ENV['RCT_NEW_ARCH_ENABLED'] = '0' - -if ENV["REACT_NATIVE_VERSION"] && ENV["REACT_NATIVE_VERSION"].match(/0.(70|71).*/) - require_relative '../node_modules/react-native/scripts/react_native_pods' - require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' - require_relative '../node_modules/react-native-permissions/scripts/setup' -else - # Resolve react_native_pods.rb with node to allow for hoisting - def node_require(script) - # Resolve script with node to allow for hoisting - require Pod::Executable.execute_command('node', ['-p', - "require.resolve( - '#{script}', - {paths: [process.argv[1]]}, - )", __dir__]).strip - end - - node_require('react-native/scripts/react_native_pods.rb') - node_require('react-native-permissions/scripts/setup.rb') -end -platform :ios, min_ios_version_supported - -install! 'cocoapods', :deterministic_uuids => false - -# Comment unwanted permissions -setup_permissions([ - 'AppTrackingTransparency', - 'Bluetooth', - 'Calendars', - 'Camera', - 'Contacts', - 'FaceID', - 'LocationAccuracy', - 'LocationAlways', - 'LocationWhenInUse', - 'MediaLibrary', - 'Microphone', - 'Motion', - 'Notifications', - 'PhotoLibrary', - 'PhotoLibraryAddOnly', - 'Reminders', - 'Siri', - 'SpeechRecognition', - 'StoreKit', -]) - -def shared_pods - config = use_native_modules! - - if ENV["REACT_NATIVE_VERSION"] && ENV["REACT_NATIVE_VERSION"].match(/0.(70).*/) - # Flags change depending on the env values. - flags = get_default_flags() - - use_react_native!( - :path => config[:reactNativePath], - :hermes_enabled => flags[:hermes_enabled], - :fabric_enabled => flags[:fabric_enabled], - # :flipper_configuration => FlipperConfiguration.enabled, - # An absolute path to your application root. - :app_path => "#{Pod::Config.instance.installation_root}/.." - ) - else - use_react_native!( - # To enable hermes on iOS, change `false` to `true` and then install pods - :hermes_enabled => false, - ) - end -end - -target 'example' do - shared_pods - pod 'react-native-slider', :path => '../node_modules/@react-native-community/slider' -end - -target 'example_ci' do - shared_pods -end - -def __apply_update_deployment_target_workaround(installer) - # This is a workaround for updating the deployment target of pod targets to the minimal supported version. - # See StackOverflow: https://stackoverflow.com/questions/72729591/fbreactnativespec-h-error-after-upgrading-from-0-68-x-to-0-69-0/75915794#75915794 - puts "Applying update deployment target workaround" - installer.pods_project.targets.each do |target| - target.build_configurations.each do |config| - config.build_settings['SWIFT_VERSION'] = '5.0' - if config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] < '12.4' - config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '12.4' - end - end - end -end - -def __apply_Xcode_15_post_install_workaround(installer) - # This is a workaround for Xcode 15, see: https://github.com/facebook/react-native/issues/37748. - puts "Applying Xcode 15 post install workaround" - installer.pods_project.targets.each do |target| - target.build_configurations.each do |config| - config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)', '_LIBCPP_ENABLE_CXX17_REMOVED_UNARY_BINARY_FUNCTION'] - end - end -end - -post_install do |installer| - __apply_update_deployment_target_workaround(installer) - __apply_Xcode_15_post_install_workaround(installer) - - config = use_native_modules! - - react_native_post_install( - installer, - config[:reactNativePath], - # Set `mac_catalyst_enabled` to `true` in order to apply patches - # necessary for Mac Catalyst builds - :mac_catalyst_enabled => false - ) - - # See https://github.com/wix/Detox/pull/3035#discussion_r774747705 - if ENV["REACT_NATIVE_VERSION"] && ENV["REACT_NATIVE_VERSION"].match(/0.(70|72).*/) - __apply_Xcode_12_5_M1_post_install_workaround(installer) - end -end diff --git a/detox/test/ios-old/example.xcodeproj/project.pbxproj b/detox/test/ios-old/example.xcodeproj/project.pbxproj deleted file mode 100644 index 95434ed05e..0000000000 --- a/detox/test/ios-old/example.xcodeproj/project.pbxproj +++ /dev/null @@ -1,930 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 51; - objects = { - -/* Begin PBXBuildFile section */ - 03815B1566F43B2C8ACBCCBB /* libPods-example_ci.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6CEAF92A5314EC6824AB3DE3 /* libPods-example_ci.a */; }; - 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; - 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; }; - 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; - 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; - 3953CC0D229AA78F005DD98C /* JavaScriptCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 39ED920022916437005EDB56 /* JavaScriptCore.framework */; }; - 399B4DEC1ED587120098D2AC /* NativeModule.m in Sources */ = {isa = PBXBuildFile; fileRef = CC17D3311D60A24300267B0C /* NativeModule.m */; }; - 399B4DED1ED587120098D2AC /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; - 399B4DEE1ED587120098D2AC /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; - 399B4DFE1ED587120098D2AC /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; - 399B4DFF1ED587120098D2AC /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; }; - 39A34C791E30F3A000BEBB59 /* Detox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 39B044621DAED76400431EC5 /* Detox.framework */; }; - 39B044651DAED76E00431EC5 /* Detox.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 39B044621DAED76400431EC5 /* Detox.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 39B71FCC24643AEA00CC9A88 /* exampleUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 39B71FCB24643AEA00CC9A88 /* exampleUITests.m */; }; - 39ED92302291643E005EDB56 /* JavaScriptCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 39ED920022916437005EDB56 /* JavaScriptCore.framework */; }; - 4FB97BDF2636490900B7B57C /* CustomKeyboardViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4FB97BDE2636490800B7B57C /* CustomKeyboardViewController.m */; }; - 4FB97BE02636490900B7B57C /* CustomKeyboardViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4FB97BDE2636490800B7B57C /* CustomKeyboardViewController.m */; }; - 89F67C3B2D0E9AF9FCED18F6 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 18AB08AE5A45E52755A8055D /* PrivacyInfo.xcprivacy */; }; - A6246A8AF2D035D89BA61CA6 /* libPods-example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 86172A40F266BB07F101EB18 /* libPods-example.a */; }; - CC17D3321D60A24300267B0C /* NativeModule.m in Sources */ = {isa = PBXBuildFile; fileRef = CC17D3311D60A24300267B0C /* NativeModule.m */; }; - F7E0527E5F2860339654E3EF /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 75C5679B8378704E8D3D9C66 /* PrivacyInfo.xcprivacy */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 39A0778B1E5450E700A53A07 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 39B0445B1DAED76400431EC5 /* Detox.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 3928EFA51E47404900C19B6E; - remoteInfo = DetoxUserNotificationTests; - }; - 39B044611DAED76400431EC5 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 39B0445B1DAED76400431EC5 /* Detox.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = CCE6D4371D11A76500F81E39; - remoteInfo = Detox; - }; - 39B044631DAED76800431EC5 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 39B0445B1DAED76400431EC5 /* Detox.xcodeproj */; - proxyType = 1; - remoteGlobalIDString = 394767961DBF985400D72256; - remoteInfo = Detox; - }; - 39B71FCE24643AEA00CC9A88 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 13B07F861A680F5B00A75B9A; - remoteInfo = example; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 399B4E011ED587120098D2AC /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; - CC0F353E1D461097008BB94F /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - 39B044651DAED76E00431EC5 /* Detox.framework in Embed Frameworks */, - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = ""; }; - 13B07F961A680F5B00A75B9A /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = example.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = example/AppDelegate.h; sourceTree = ""; tabWidth = 4; }; - 13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = example/AppDelegate.m; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; - 13B07FB21A68108700A75B9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; - 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = example/Images.xcassets; sourceTree = ""; }; - 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = example/Info.plist; sourceTree = ""; }; - 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = example/main.m; sourceTree = ""; }; - 18AB08AE5A45E52755A8055D /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = example/PrivacyInfo.xcprivacy; sourceTree = ""; }; - 399B4E061ED587120098D2AC /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = example.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 39B0445B1DAED76400431EC5 /* Detox.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Detox.xcodeproj; path = ../../ios/Detox.xcodeproj; sourceTree = ""; }; - 39B71FC924643AEA00CC9A88 /* exampleUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = exampleUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 39B71FCB24643AEA00CC9A88 /* exampleUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = exampleUITests.m; sourceTree = ""; }; - 39B71FCD24643AEA00CC9A88 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 39ED920022916437005EDB56 /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; - 39FC9D23202899F90033C11A /* e2e */ = {isa = PBXFileReference; lastKnownFileType = folder; name = e2e; path = ../e2e; sourceTree = ""; }; - 39FC9D24202899F90033C11A /* src */ = {isa = PBXFileReference; lastKnownFileType = folder; name = src; path = ../src; sourceTree = ""; }; - 4FB97BDD2636490800B7B57C /* CustomKeyboardViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = CustomKeyboardViewController.h; path = example/CustomKeyboardViewController.h; sourceTree = ""; }; - 4FB97BDE2636490800B7B57C /* CustomKeyboardViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = CustomKeyboardViewController.m; path = example/CustomKeyboardViewController.m; sourceTree = ""; usesTabs = 1; }; - 6027065D2B1DF4DD00CD52CF /* example.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = example.entitlements; path = example/example.entitlements; sourceTree = ""; }; - 6027065F2B1DF82400CD52CF /* example_ci.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = example_ci.entitlements; sourceTree = ""; }; - 6CEAF92A5314EC6824AB3DE3 /* libPods-example_ci.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-example_ci.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 75C5679B8378704E8D3D9C66 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = example/PrivacyInfo.xcprivacy; sourceTree = ""; }; - 86172A40F266BB07F101EB18 /* libPods-example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-example.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - A112C2B84196BF64AE1EFFA3 /* Pods-example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example.debug.xcconfig"; path = "Target Support Files/Pods-example/Pods-example.debug.xcconfig"; sourceTree = ""; }; - A398C1445E8C769F5903CD2D /* Pods-example_ci.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example_ci.debug.xcconfig"; path = "Target Support Files/Pods-example_ci/Pods-example_ci.debug.xcconfig"; sourceTree = ""; }; - CC17D3301D60A24300267B0C /* NativeModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NativeModule.h; path = example/NativeModule.h; sourceTree = ""; }; - CC17D3311D60A24300267B0C /* NativeModule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NativeModule.m; path = example/NativeModule.m; sourceTree = ""; }; - CC2D09EF9818766C1EE13DEE /* Pods-example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example.release.xcconfig"; path = "Target Support Files/Pods-example/Pods-example.release.xcconfig"; sourceTree = ""; }; - F08DEFD1BF85073C69F1A95E /* Pods-example_ci.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example_ci.release.xcconfig"; path = "Target Support Files/Pods-example_ci/Pods-example_ci.release.xcconfig"; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 13B07F8C1A680F5B00A75B9A /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 3953CC0D229AA78F005DD98C /* JavaScriptCore.framework in Frameworks */, - 39A34C791E30F3A000BEBB59 /* Detox.framework in Frameworks */, - A6246A8AF2D035D89BA61CA6 /* libPods-example.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 399B4DEF1ED587120098D2AC /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 39ED92302291643E005EDB56 /* JavaScriptCore.framework in Frameworks */, - 03815B1566F43B2C8ACBCCBB /* libPods-example_ci.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 39B71FC624643AEA00CC9A88 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 13B07FAE1A68108700A75B9A /* example */ = { - isa = PBXGroup; - children = ( - 6027065D2B1DF4DD00CD52CF /* example.entitlements */, - 008F07F21AC5B25A0029DE68 /* main.jsbundle */, - 13B07FAF1A68108700A75B9A /* AppDelegate.h */, - 13B07FB01A68108700A75B9A /* AppDelegate.m */, - 13B07FB51A68108700A75B9A /* Images.xcassets */, - 13B07FB61A68108700A75B9A /* Info.plist */, - 13B07FB11A68108700A75B9A /* LaunchScreen.xib */, - 13B07FB71A68108700A75B9A /* main.m */, - ADB9A7A225C82082005236EE /* ReactModules */, - ADB9A79F25C81FF7005236EE /* UI */, - 75C5679B8378704E8D3D9C66 /* PrivacyInfo.xcprivacy */, - ); - name = example; - sourceTree = ""; - }; - 160A423EA1148BC845CDF95E /* Pods */ = { - isa = PBXGroup; - children = ( - A112C2B84196BF64AE1EFFA3 /* Pods-example.debug.xcconfig */, - CC2D09EF9818766C1EE13DEE /* Pods-example.release.xcconfig */, - A398C1445E8C769F5903CD2D /* Pods-example_ci.debug.xcconfig */, - F08DEFD1BF85073C69F1A95E /* Pods-example_ci.release.xcconfig */, - ); - path = Pods; - sourceTree = ""; - }; - 39B0445C1DAED76400431EC5 /* Products */ = { - isa = PBXGroup; - children = ( - 39B044621DAED76400431EC5 /* Detox.framework */, - 39A0778C1E5450E700A53A07 /* DetoxUserNotificationTests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - 39B71FCA24643AEA00CC9A88 /* exampleUITests */ = { - isa = PBXGroup; - children = ( - 39B71FCB24643AEA00CC9A88 /* exampleUITests.m */, - 39B71FCD24643AEA00CC9A88 /* Info.plist */, - ); - path = exampleUITests; - sourceTree = ""; - }; - 39FC9CFD202899D10033C11A /* JS */ = { - isa = PBXGroup; - children = ( - 39FC9D23202899F90033C11A /* e2e */, - 39FC9D24202899F90033C11A /* src */, - ); - name = JS; - sourceTree = ""; - }; - 83CBB9F61A601CBA00E9B192 = { - isa = PBXGroup; - children = ( - 6027065F2B1DF82400CD52CF /* example_ci.entitlements */, - 39B0445B1DAED76400431EC5 /* Detox.xcodeproj */, - 13B07FAE1A68108700A75B9A /* example */, - 39FC9CFD202899D10033C11A /* JS */, - 39B71FCA24643AEA00CC9A88 /* exampleUITests */, - CCFA7DE41D11D22600E15EDF /* Frameworks */, - 83CBBA001A601CBA00E9B192 /* Products */, - 160A423EA1148BC845CDF95E /* Pods */, - 18AB08AE5A45E52755A8055D /* PrivacyInfo.xcprivacy */, - ); - indentWidth = 4; - sourceTree = ""; - tabWidth = 4; - }; - 83CBBA001A601CBA00E9B192 /* Products */ = { - isa = PBXGroup; - children = ( - 13B07F961A680F5B00A75B9A /* example.app */, - 399B4E061ED587120098D2AC /* example.app */, - 39B71FC924643AEA00CC9A88 /* exampleUITests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - ADB9A79F25C81FF7005236EE /* UI */ = { - isa = PBXGroup; - children = ( - ADB9A7A125C82033005236EE /* ViewControllers */, - ); - name = UI; - sourceTree = ""; - }; - ADB9A7A125C82033005236EE /* ViewControllers */ = { - isa = PBXGroup; - children = ( - 4FB97BDD2636490800B7B57C /* CustomKeyboardViewController.h */, - 4FB97BDE2636490800B7B57C /* CustomKeyboardViewController.m */, - ); - name = ViewControllers; - sourceTree = ""; - }; - ADB9A7A225C82082005236EE /* ReactModules */ = { - isa = PBXGroup; - children = ( - CC17D3301D60A24300267B0C /* NativeModule.h */, - CC17D3311D60A24300267B0C /* NativeModule.m */, - ); - name = ReactModules; - sourceTree = ""; - }; - CCFA7DE41D11D22600E15EDF /* Frameworks */ = { - isa = PBXGroup; - children = ( - 39ED920022916437005EDB56 /* JavaScriptCore.framework */, - 86172A40F266BB07F101EB18 /* libPods-example.a */, - 6CEAF92A5314EC6824AB3DE3 /* libPods-example_ci.a */, - ); - name = Frameworks; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 13B07F861A680F5B00A75B9A /* example */ = { - isa = PBXNativeTarget; - buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "example" */; - buildPhases = ( - DC59FF1BA0BA7FA633F256AA /* [CP] Check Pods Manifest.lock */, - 13B07F871A680F5B00A75B9A /* Sources */, - 13B07F8C1A680F5B00A75B9A /* Frameworks */, - 13B07F8E1A680F5B00A75B9A /* Resources */, - 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, - CC0F353E1D461097008BB94F /* Embed Frameworks */, - 7E5379D0D68561A29BC0458B /* [CP] Copy Pods Resources */, - ); - buildRules = ( - ); - dependencies = ( - 39B044641DAED76800431EC5 /* PBXTargetDependency */, - ); - name = example; - productName = "Hello World"; - productReference = 13B07F961A680F5B00A75B9A /* example.app */; - productType = "com.apple.product-type.application"; - }; - 399B4DE81ED587120098D2AC /* example_ci */ = { - isa = PBXNativeTarget; - buildConfigurationList = 399B4E031ED587120098D2AC /* Build configuration list for PBXNativeTarget "example_ci" */; - buildPhases = ( - 5D18F0A8E13DBF942B818AE3 /* [CP] Check Pods Manifest.lock */, - 399B4DEB1ED587120098D2AC /* Sources */, - 399B4DEF1ED587120098D2AC /* Frameworks */, - 399B4DFD1ED587120098D2AC /* Resources */, - 399B4E001ED587120098D2AC /* Bundle React Native code and images */, - 399B4E011ED587120098D2AC /* Embed Frameworks */, - 9C024938B44AFDD355A0B654 /* [CP] Copy Pods Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = example_ci; - productName = "Hello World"; - productReference = 399B4E061ED587120098D2AC /* example.app */; - productType = "com.apple.product-type.application"; - }; - 39B71FC824643AEA00CC9A88 /* exampleUITests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 39B71FD324643AEA00CC9A88 /* Build configuration list for PBXNativeTarget "exampleUITests" */; - buildPhases = ( - 39B71FC524643AEA00CC9A88 /* Sources */, - 39B71FC624643AEA00CC9A88 /* Frameworks */, - 39B71FC724643AEA00CC9A88 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 39B71FCF24643AEA00CC9A88 /* PBXTargetDependency */, - ); - name = exampleUITests; - productName = exampleUITests; - productReference = 39B71FC924643AEA00CC9A88 /* exampleUITests.xctest */; - productType = "com.apple.product-type.bundle.ui-testing"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 83CBB9F71A601CBA00E9B192 /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 1220; - ORGANIZATIONNAME = Wix; - TargetAttributes = { - 39B71FC824643AEA00CC9A88 = { - CreatedOnToolsVersion = 11.5; - TestTargetID = 13B07F861A680F5B00A75B9A; - }; - }; - }; - buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "example" */; - compatibilityVersion = "Xcode 10.0"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 83CBB9F61A601CBA00E9B192; - productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; - projectDirPath = ""; - projectReferences = ( - { - ProductGroup = 39B0445C1DAED76400431EC5 /* Products */; - ProjectRef = 39B0445B1DAED76400431EC5 /* Detox.xcodeproj */; - }, - ); - projectRoot = ""; - targets = ( - 13B07F861A680F5B00A75B9A /* example */, - 399B4DE81ED587120098D2AC /* example_ci */, - 39B71FC824643AEA00CC9A88 /* exampleUITests */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXReferenceProxy section */ - 39A0778C1E5450E700A53A07 /* DetoxUserNotificationTests.xctest */ = { - isa = PBXReferenceProxy; - fileType = wrapper.cfbundle; - path = DetoxUserNotificationTests.xctest; - remoteRef = 39A0778B1E5450E700A53A07 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 39B044621DAED76400431EC5 /* Detox.framework */ = { - isa = PBXReferenceProxy; - fileType = wrapper.framework; - path = Detox.framework; - remoteRef = 39B044611DAED76400431EC5 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; -/* End PBXReferenceProxy section */ - -/* Begin PBXResourcesBuildPhase section */ - 13B07F8E1A680F5B00A75B9A /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, - 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */, - F7E0527E5F2860339654E3EF /* PrivacyInfo.xcprivacy in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 399B4DFD1ED587120098D2AC /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 399B4DFE1ED587120098D2AC /* Images.xcassets in Resources */, - 399B4DFF1ED587120098D2AC /* LaunchScreen.xib in Resources */, - 89F67C3B2D0E9AF9FCED18F6 /* PrivacyInfo.xcprivacy in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 39B71FC724643AEA00CC9A88 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Bundle React Native code and images"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh"; - }; - 399B4E001ED587120098D2AC /* Bundle React Native code and images */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Bundle React Native code and images"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "../node_modules/react-native/scripts/react-native-xcode.sh\n"; - }; - 5D18F0A8E13DBF942B818AE3 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-example_ci-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - 7E5379D0D68561A29BC0458B /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-resources-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Copy Pods Resources"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-resources-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; - 9C024938B44AFDD355A0B654 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-example_ci/Pods-example_ci-resources-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Copy Pods Resources"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-example_ci/Pods-example_ci-resources-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example_ci/Pods-example_ci-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; - DC59FF1BA0BA7FA633F256AA /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-example-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 13B07F871A680F5B00A75B9A /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - CC17D3321D60A24300267B0C /* NativeModule.m in Sources */, - 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */, - 4FB97BDF2636490900B7B57C /* CustomKeyboardViewController.m in Sources */, - 13B07FC11A68108700A75B9A /* main.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 399B4DEB1ED587120098D2AC /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 399B4DEC1ED587120098D2AC /* NativeModule.m in Sources */, - 399B4DED1ED587120098D2AC /* AppDelegate.m in Sources */, - 4FB97BE02636490900B7B57C /* CustomKeyboardViewController.m in Sources */, - 399B4DEE1ED587120098D2AC /* main.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 39B71FC524643AEA00CC9A88 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 39B71FCC24643AEA00CC9A88 /* exampleUITests.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 39B044641DAED76800431EC5 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = Detox; - targetProxy = 39B044631DAED76800431EC5 /* PBXContainerItemProxy */; - }; - 39B71FCF24643AEA00CC9A88 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 13B07F861A680F5B00A75B9A /* example */; - targetProxy = 39B71FCE24643AEA00CC9A88 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin PBXVariantGroup section */ - 13B07FB11A68108700A75B9A /* LaunchScreen.xib */ = { - isa = PBXVariantGroup; - children = ( - 13B07FB21A68108700A75B9A /* Base */, - ); - name = LaunchScreen.xib; - path = example; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 13B07F941A680F5B00A75B9A /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = A112C2B84196BF64AE1EFFA3 /* Pods-example.debug.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = NO; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; - CODE_SIGN_ENTITLEMENTS = example/example.entitlements; - DEAD_CODE_STRIPPING = NO; - DEVELOPMENT_TEAM = ""; - GCC_PREPROCESSOR_DEFINITIONS = ( - "EXTERNAL_LOGGER=1", - "$(inherited)", - ); - INFOPLIST_FILE = example/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - OTHER_LDFLAGS = ( - "$(inherited)", - "-ObjC", - "-lc++", - ); - PRODUCT_BUNDLE_IDENTIFIER = "com.wix.detox-example"; - PRODUCT_NAME = example; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 13B07F951A680F5B00A75B9A /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = CC2D09EF9818766C1EE13DEE /* Pods-example.release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = NO; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; - CODE_SIGN_ENTITLEMENTS = example/example.entitlements; - DEVELOPMENT_TEAM = ""; - GCC_PREPROCESSOR_DEFINITIONS = ( - "EXTERNAL_LOGGER=1", - "$(inherited)", - ); - INFOPLIST_FILE = example/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - OTHER_LDFLAGS = ( - "$(inherited)", - "-ObjC", - "-lc++", - ); - PRODUCT_BUNDLE_IDENTIFIER = "com.wix.detox-example"; - PRODUCT_NAME = example; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Release; - }; - 399B4E041ED587120098D2AC /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = A398C1445E8C769F5903CD2D /* Pods-example_ci.debug.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = NO; - CODE_SIGN_ENTITLEMENTS = example_ci.entitlements; - DEAD_CODE_STRIPPING = NO; - DEVELOPMENT_TEAM = ""; - INFOPLIST_FILE = "$(SRCROOT)/example/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - OTHER_LDFLAGS = ( - "-ObjC", - "-lc++", - "-Wl,-U,___dtx_send_external_log", - "$(inherited)", - ); - PRODUCT_BUNDLE_IDENTIFIER = "com.wix.detox-example"; - PRODUCT_NAME = example; - }; - name = Debug; - }; - 399B4E051ED587120098D2AC /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = F08DEFD1BF85073C69F1A95E /* Pods-example_ci.release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = NO; - CODE_SIGN_ENTITLEMENTS = example_ci.entitlements; - DEVELOPMENT_TEAM = ""; - INFOPLIST_FILE = "$(SRCROOT)/example/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - OTHER_LDFLAGS = ( - "-ObjC", - "-lc++", - "-Wl,-U,___dtx_send_external_log", - "$(inherited)", - ); - PRODUCT_BUNDLE_IDENTIFIER = "com.wix.detox-example"; - PRODUCT_NAME = example; - }; - name = Release; - }; - 39B71FD024643AEA00CC9A88 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_STYLE = Automatic; - DEBUG_INFORMATION_FORMAT = dwarf; - GCC_C_LANGUAGE_STANDARD = gnu11; - INFOPLIST_FILE = exampleUITests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 13.5; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = com.wix.exampleUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_TARGET_NAME = example; - }; - name = Debug; - }; - 39B71FD124643AEA00CC9A88 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_STYLE = Automatic; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - GCC_C_LANGUAGE_STANDARD = gnu11; - INFOPLIST_FILE = exampleUITests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 13.5; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = com.wix.exampleUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_TARGET_NAME = example; - }; - name = Release; - }; - 83CBBA201A601CBA00E9B192 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CC = ""; - CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; - CLANG_CXX_LANGUAGE_STANDARD = "c++20"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - CXX = ""; - ENABLE_BITCODE = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = ""; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - _LIBCPP_ENABLE_CXX17_REMOVED_UNARY_BINARY_FUNCTION, - ); - GCC_SYMBOLS_PRIVATE_EXTERN = NO; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; - LD = ""; - LDPLUSPLUS = ""; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - OTHER_CFLAGS = "$(inherited)"; - OTHER_CPLUSPLUSFLAGS = "$(inherited)"; - OTHER_LDFLAGS = "$(inherited)"; - REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; - SDKROOT = iphoneos; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG"; - USE_HERMES = false; - }; - name = Debug; - }; - 83CBBA211A601CBA00E9B192 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CC = ""; - CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; - CLANG_CXX_LANGUAGE_STANDARD = "c++20"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - CXX = ""; - ENABLE_BITCODE = NO; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = ""; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_PREPROCESSOR_DEFINITIONS = ( - "$(inherited)", - _LIBCPP_ENABLE_CXX17_REMOVED_UNARY_BINARY_FUNCTION, - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; - LD = ""; - LDPLUSPLUS = ""; - MTL_ENABLE_DEBUG_INFO = NO; - OTHER_CFLAGS = "$(inherited)"; - OTHER_CPLUSPLUSFLAGS = "$(inherited)"; - OTHER_LDFLAGS = "$(inherited)"; - REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; - SDKROOT = iphoneos; - USE_HERMES = false; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "example" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 13B07F941A680F5B00A75B9A /* Debug */, - 13B07F951A680F5B00A75B9A /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 399B4E031ED587120098D2AC /* Build configuration list for PBXNativeTarget "example_ci" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 399B4E041ED587120098D2AC /* Debug */, - 399B4E051ED587120098D2AC /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 39B71FD324643AEA00CC9A88 /* Build configuration list for PBXNativeTarget "exampleUITests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 39B71FD024643AEA00CC9A88 /* Debug */, - 39B71FD124643AEA00CC9A88 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "example" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 83CBBA201A601CBA00E9B192 /* Debug */, - 83CBBA211A601CBA00E9B192 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */; -} diff --git a/detox/test/ios-old/example.xcodeproj/xcshareddata/xcschemes/example.xcscheme b/detox/test/ios-old/example.xcodeproj/xcshareddata/xcschemes/example.xcscheme deleted file mode 100644 index 996c109aef..0000000000 --- a/detox/test/ios-old/example.xcodeproj/xcshareddata/xcschemes/example.xcscheme +++ /dev/null @@ -1,184 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/detox/test/ios-old/example.xcodeproj/xcshareddata/xcschemes/example_ci.xcscheme b/detox/test/ios-old/example.xcodeproj/xcshareddata/xcschemes/example_ci.xcscheme deleted file mode 100644 index ac388e69f6..0000000000 --- a/detox/test/ios-old/example.xcodeproj/xcshareddata/xcschemes/example_ci.xcscheme +++ /dev/null @@ -1,91 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/detox/test/ios-old/example/AppDelegate.h b/detox/test/ios-old/example/AppDelegate.h deleted file mode 100644 index 9231535b24..0000000000 --- a/detox/test/ios-old/example/AppDelegate.h +++ /dev/null @@ -1,17 +0,0 @@ -#import - -@interface SomeMiddleman : UIWindow @end - -@interface AnnoyingWindow : SomeMiddleman - -@property (nonatomic, strong) UILabel* annoyingLabel; - -@end - -@interface DetoxApp : UIApplication @end - -@interface AppDelegate : UIResponder - -@property (nonatomic, strong) AnnoyingWindow *window; - -@end diff --git a/detox/test/ios-old/example/AppDelegate.m b/detox/test/ios-old/example/AppDelegate.m deleted file mode 100644 index b4bc4f1de0..0000000000 --- a/detox/test/ios-old/example/AppDelegate.m +++ /dev/null @@ -1,370 +0,0 @@ -#import "AppDelegate.h" -#import -#import - -#if RCT_NEW_ARCH_ENABLED -#import -#import -#import -#import -#import -#import -#import -#endif - -#import "CustomKeyboardViewController.h" -@import CoreSpotlight; - -@import UserNotifications; - -#if EXTERNAL_LOGGER -extern void __dtx_send_external_log(const char* log) __attribute__((weak)); -#define __dtx_external_logger(log) __dtx_send_external_log(log); -#else -#define __dtx_external_logger(log) -#endif - -@implementation SomeMiddleman @end - -@implementation AnnoyingWindow - -- (instancetype)initWithFrame:(CGRect)frame -{ - self = [super initWithFrame:frame]; - - if(self) - { - _annoyingLabel = [UILabel new]; - _annoyingLabel.translatesAutoresizingMaskIntoConstraints = NO; - - [self addSubview:_annoyingLabel]; - - NSLayoutYAxisAnchor* topAnchor = self.safeAreaLayoutGuide.topAnchor;; - NSLayoutConstraint* topConstraint = [_annoyingLabel.topAnchor constraintEqualToAnchor:topAnchor constant:-14]; - topConstraint.priority = UILayoutPriorityRequired - 1; - [NSLayoutConstraint activateConstraints:@[ - [_annoyingLabel.centerXAnchor constraintEqualToAnchor:self.centerXAnchor], - topConstraint, - [_annoyingLabel.topAnchor constraintGreaterThanOrEqualToAnchor:self.topAnchor], - ]]; - } - - return self; -} - -- (void)setHidden:(BOOL)hidden -{ - [super setHidden:hidden]; -} - -- (void)layoutSubviews -{ - [super layoutSubviews]; - - [self bringSubviewToFront:_annoyingLabel]; -} - -@end - -@interface ShakeEventEmitter : RCTEventEmitter @end -static ShakeEventEmitter* _instance; -@implementation ShakeEventEmitter - -RCT_EXPORT_MODULE(); - -- (instancetype)init -{ - self = [super init]; - _instance = self; - return self; -} - -- (NSArray *)supportedEvents -{ - return @[@"ShakeEvent"]; -} - -- (void)sendShakeEvent -{ - [self sendEventWithName:@"ShakeEvent" body:nil]; -} - -+ (BOOL)requiresMainQueueSetup -{ - return YES; -} - -@end - -@interface ShakeDetectViewController : UIViewController -@property (nonatomic, weak) RCTBridge* bridge; -@end -@implementation ShakeDetectViewController - -- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event -{ - if(event.subtype == UIEventSubtypeMotionShake) - { - [_instance sendShakeEvent]; - } - else - { - //This will disable RN dev menu even in debug as shake events are not passed further in responder chain. - [super motionEnded:motion withEvent:event]; - } -} - -@end - -@implementation DetoxApp @end - -#if RCT_NEW_ARCH_ENABLED -@interface AppDelegate () { - RCTTurboModuleManager *_turboModuleManager; - RCTSurfacePresenterBridgeAdapter *_bridgeAdapter; - std::shared_ptr _reactNativeConfig; - facebook::react::ContextContainer::Shared _contextContainer; -} -@end -#else -@interface AppDelegate () - -@end -#endif - -@implementation AppDelegate -{ - UILabel* _resignActive; -} - -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions -{ - // this conditional init loads Detox only when command line arguments are given - // in normal execution, Detox is not loaded and there's zero impact on the app - - NSURL *jsCodeLocation; - - // this is a simple variant over the default React Native starter project which loads the bundle - // in Debug from the packager (OPTION 1) and in Release from a local resource (OPTION 2) -#ifdef DEBUG - jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios&dev=true"]; -#else - jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; -#endif - - //RN 🤦‍♂️ - NSMutableDictionary* opts = launchOptions.mutableCopy; - opts[UIApplicationLaunchOptionsRemoteNotificationKey] = nil; - - RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation - moduleName:@"example" - initialProperties:nil - launchOptions:opts]; - -#if RCT_NEW_ARCH_ENABLED - _contextContainer = std::make_shared(); - _reactNativeConfig = std::make_shared(); - _contextContainer->insert("ReactNativeConfig", _reactNativeConfig); - _bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:bridge contextContainer:_contextContainer]; - rootView.bridge.surfacePresenter = _bridgeAdapter.surfacePresenter; -#endif - - rootView.backgroundColor = UIColor.whiteColor; - - self.window = [[AnnoyingWindow alloc] initWithFrame:UIScreen.mainScreen.bounds]; - - [self.window setIsAccessibilityElement:NO]; - [self.window setAccessibilityElementsHidden:NO]; - - ShakeDetectViewController *rootViewController = [ShakeDetectViewController new]; - rootViewController.bridge = rootView.bridge; - - rootViewController.view = rootView; - self.window.rootViewController = rootViewController; - [self.window makeKeyAndVisible]; - - [UNUserNotificationCenter currentNotificationCenter].delegate = self; - - self.window.annoyingLabel.text = @"App is inactive"; - self.window.annoyingLabel.backgroundColor = UIColor.redColor; - self.window.annoyingLabel.textColor = UIColor.whiteColor; - self.window.annoyingLabel.font = [UIFont systemFontOfSize:30]; - [self.window.annoyingLabel sizeToFit]; - - [NSNotificationCenter.defaultCenter addObserverForName:@"ChangeScreen" object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) { - NSString* name = note.userInfo[@"name"]; - - if ([name isEqualToString:@"customKeyboard"]) - { - CustomKeyboardViewController *vc = [[CustomKeyboardViewController alloc] init]; - vc.modalPresentationStyle = UIModalPresentationFullScreen; - [self.window.rootViewController presentViewController:vc animated:YES completion:nil]; - } - }]; - - return YES; -} - -- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary *)options -{ - __dtx_external_logger("Got openURL:"); - - BOOL rv = [RCTLinkingManager application:application - openURL:url - sourceApplication:options[UIApplicationOpenURLOptionsSourceApplicationKey] - annotation:options[UIApplicationOpenURLOptionsAnnotationKey]]; - - __dtx_external_logger("Finished openURL:"); - - return rv; -} - -- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler -{ - completionHandler(UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionSound); -} - -- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler -{ - UILabel* someLabel = [UILabel new]; - someLabel.translatesAutoresizingMaskIntoConstraints = NO; - - someLabel.text = response.notification.request.content.title; - someLabel.backgroundColor = UIColor.blackColor; - someLabel.textColor = UIColor.whiteColor; - someLabel.font = [UIFont systemFontOfSize:40]; - - RCTRootView* rv = (id)self.window.rootViewController.view; - //Add to the content view so that reloadReactNative() removes this label. - [[rv valueForKey:@"contentView"] addSubview:someLabel]; - - [NSLayoutConstraint activateConstraints:@[ - [someLabel.centerXAnchor constraintEqualToAnchor:self.window.centerXAnchor], - [someLabel.topAnchor constraintEqualToAnchor:self.window.annoyingLabel.bottomAnchor], - ]]; - - [someLabel.superview bringSubviewToFront:someLabel]; - - static id __prevObserver = nil; - - //Cleanup the previous observer - if(__prevObserver != nil) - { - [NSNotificationCenter.defaultCenter removeObserver:__prevObserver]; - __prevObserver = nil; - } - - __prevObserver = [NSNotificationCenter.defaultCenter addObserverForName:RCTContentDidAppearNotification object:self.window.rootViewController.view queue:nil usingBlock:^(NSNotification * _Nonnull note) { - if(someLabel.window == nil) - { - [NSNotificationCenter.defaultCenter removeObserver:__prevObserver]; - __prevObserver = nil; - return; - } - - [someLabel.superview bringSubviewToFront:someLabel]; - }]; -} - -- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void(^)(NSArray> * __nullable restorableObjects))restorationHandler -{ - if([userActivity.activityType isEqualToString:CSSearchableItemActionType]) - { - NSString* identifier = userActivity.userInfo[CSSearchableItemActivityIdentifier]; - - //Fake it here as if it is a URL, but actually it's a searchable item identifier. - return [RCTLinkingManager application:application - openURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@", identifier]] - sourceApplication:@"" - annotation:@{}]; - } - - return [RCTLinkingManager application:application continueUserActivity:userActivity restorationHandler:restorationHandler]; -} - -- (NSString*)_stringFromAppState -{ - switch (UIApplication.sharedApplication.applicationState) { - case UIApplicationStateActive: - return @"Active"; - case UIApplicationStateInactive: - return @"Inactive"; - case UIApplicationStateBackground: - return @"Background"; - } -} - -- (void)applicationDidEnterBackground:(UIApplication *)application -{ - self.window.annoyingLabel.text = [self _stringFromAppState]; - self.window.annoyingLabel.backgroundColor = UIColor.redColor; - [self.window.annoyingLabel sizeToFit]; - - __dtx_external_logger("DidEnterBackground"); -} - -- (void)applicationWillEnterForeground:(UIApplication *)application -{ - self.window.annoyingLabel.text = [self _stringFromAppState]; - self.window.annoyingLabel.backgroundColor = UIColor.redColor; - [self.window.annoyingLabel sizeToFit]; - - __dtx_external_logger("WillEnterForeground"); -} - -- (void)applicationWillResignActive:(UIApplication *)application -{ - self.window.annoyingLabel.text = [self _stringFromAppState]; - self.window.annoyingLabel.backgroundColor = UIColor.redColor; - [self.window.annoyingLabel sizeToFit]; - - __dtx_external_logger("WillResignActive"); -} - -- (void)applicationDidBecomeActive:(UIApplication *)application -{ - self.window.annoyingLabel.text = [self _stringFromAppState]; - self.window.annoyingLabel.backgroundColor = UIColor.greenColor; - [self.window.annoyingLabel sizeToFit]; - - __dtx_external_logger("DidBecomeActive"); -} - -#if RCT_NEW_ARCH_ENABLED - -#pragma mark - -#pragma mark - RCTCxxBridgeDelegate -#pragma mark - - -- (std::unique_ptr)jsExecutorFactoryForBridge:(RCTBridge *)bridge { - _turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge - delegate:self - jsInvoker:bridge.jsCallInvoker]; - return RCTAppSetupDefaultJsExecutorFactory(bridge, _turboModuleManager); -} - -#pragma mark - -#pragma mark RCTTurboModuleManagerDelegate -#pragma mark - - -- (Class)getModuleClassFromName:(const char *)name { - return RCTCoreModulesClassProvider(name); -} - -- (std::shared_ptr)getTurboModule:(const std::string &)name - jsInvoker:(std::shared_ptr)jsInvoker { - return nullptr; -} - -- (std::shared_ptr)getTurboModule:(const std::string &)name - initParams:(const facebook::react::ObjCTurboModule::InitParams &)params { - return nullptr; -} - -- (id)getModuleInstanceFromClass:(Class)moduleClass { - return RCTAppSetupDefaultModuleFromClass(moduleClass); -} - -#endif - -@end diff --git a/detox/test/ios-old/example/Base.lproj/LaunchScreen.xib b/detox/test/ios-old/example/Base.lproj/LaunchScreen.xib deleted file mode 100644 index 9e04807a83..0000000000 --- a/detox/test/ios-old/example/Base.lproj/LaunchScreen.xib +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/detox/test/ios-old/example/CustomKeyboardViewController.h b/detox/test/ios-old/example/CustomKeyboardViewController.h deleted file mode 100644 index 8ee742fb12..0000000000 --- a/detox/test/ios-old/example/CustomKeyboardViewController.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// CustomKeyboardViewController.h -// example -// -// Created by Tyrone Trevorrow on 26/4/21. -// Copyright © 2021 Wix. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface CustomKeyboardViewController : UIViewController - -@end - -NS_ASSUME_NONNULL_END diff --git a/detox/test/ios-old/example/CustomKeyboardViewController.m b/detox/test/ios-old/example/CustomKeyboardViewController.m deleted file mode 100644 index 4dc90129f3..0000000000 --- a/detox/test/ios-old/example/CustomKeyboardViewController.m +++ /dev/null @@ -1,117 +0,0 @@ -// -// CustomKeyboardViewController.m -// example -// -// Created by Tyrone Trevorrow on 26/4/21. -// Copyright © 2021 Wix. All rights reserved. -// - -#import "CustomKeyboardViewController.h" - -@class CustomKeyboardView; -@protocol CustomKeyboardDelegate -- (void) customKeyboardTappedButton: (CustomKeyboardView*) sender; -@end - -@interface CustomKeyboardView : UIView -@property (nonatomic, weak) id delegate; -- (void) loadView; -@end - -@implementation CustomKeyboardView - -- (void) loadView -{ - UIButton* kbButton = [UIButton buttonWithType: UIButtonTypeCustom]; - kbButton.translatesAutoresizingMaskIntoConstraints = NO; - [kbButton setTitle: @"Hello" forState: UIControlStateNormal]; - [kbButton addTarget: self action: @selector(buttonTapped:) forControlEvents: UIControlEventTouchUpInside]; - kbButton.accessibilityIdentifier = @"keyboardHelloButton"; - - [self addSubview: kbButton]; - - [NSLayoutConstraint activateConstraints: @[ - [kbButton.widthAnchor constraintGreaterThanOrEqualToConstant:44], - [kbButton.heightAnchor constraintEqualToConstant:44], - [kbButton.leadingAnchor constraintEqualToAnchor:self.leadingAnchor constant:20], - [kbButton.topAnchor constraintEqualToAnchor:self.topAnchor constant:20] - ]]; -} - -- (void) buttonTapped: (id) sender -{ - if (self.delegate) { - [self.delegate customKeyboardTappedButton: self]; - } -} - -@end - -@interface CustomKeyboardViewController () -@property (nonatomic, strong) UITextField* textField; -@end - -@implementation CustomKeyboardViewController - -- (void)viewDidLoad -{ - [super viewDidLoad]; - - self.view.backgroundColor = UIColor.systemBackgroundColor; - - UIButton* closeButton = [UIButton buttonWithType:UIButtonTypeSystem]; - [closeButton setImage:[UIImage systemImageNamed:@"xmark.circle.fill"] forState:UIControlStateNormal]; - closeButton.translatesAutoresizingMaskIntoConstraints = NO; - closeButton.accessibilityIdentifier = @"closeButton"; - [closeButton addTarget:self action:@selector(_close) forControlEvents:UIControlEventPrimaryActionTriggered]; - - CustomKeyboardView* inputView = [[CustomKeyboardView alloc] init]; - inputView.translatesAutoresizingMaskIntoConstraints = NO; - inputView.delegate = self; - [inputView setBackgroundColor: [UIColor lightGrayColor]]; - [inputView loadView]; - - UITextField* text = [[UITextField alloc] init]; - text.translatesAutoresizingMaskIntoConstraints = NO; - text.inputView = inputView; - text.borderStyle = UITextBorderStyleRoundedRect; - text.accessibilityIdentifier = @"textWithCustomInput"; - - UILabel* obscuredLabel = [[UILabel alloc] init]; - obscuredLabel.translatesAutoresizingMaskIntoConstraints = NO; - obscuredLabel.text = @"Obscured by keyboard"; - - self.textField = text; - - [self.view addSubview:closeButton]; - [self.view addSubview:text]; - [self.view addSubview:obscuredLabel]; - - [NSLayoutConstraint activateConstraints:@[ - [text.heightAnchor constraintEqualToConstant:50], - [text.leadingAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.leadingAnchor constant:20], - [self.view.safeAreaLayoutGuide.trailingAnchor constraintEqualToAnchor:text.trailingAnchor constant:20], - [text.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor constant:50], - - [inputView.widthAnchor constraintEqualToConstant:self.view.frame.size.width], - - [self.view.layoutMarginsGuide.trailingAnchor constraintEqualToAnchor:closeButton.trailingAnchor], - [self.view.layoutMarginsGuide.topAnchor constraintEqualToAnchor:closeButton.topAnchor], - - [obscuredLabel.leadingAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.leadingAnchor constant:20], - [self.view.safeAreaLayoutGuide.trailingAnchor constraintGreaterThanOrEqualToAnchor:obscuredLabel.trailingAnchor constant:20], - [self.view.safeAreaLayoutGuide.bottomAnchor constraintEqualToAnchor:obscuredLabel.bottomAnchor constant:50], - ]]; -} - -- (void)customKeyboardTappedButton:(CustomKeyboardView *)sender -{ - [self.textField setText:@"World!"]; -} - -- (void)_close -{ - [self.presentingViewController dismissViewControllerAnimated:YES completion:nil]; -} - -@end diff --git a/detox/test/ios-old/example/Images.xcassets/AppIcon.appiconset/Contents.json b/detox/test/ios-old/example/Images.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 19882d568a..0000000000 --- a/detox/test/ios-old/example/Images.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "images" : [ - { - "idiom" : "iphone", - "size" : "20x20", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "20x20", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "40x40", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "40x40", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "60x60", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "60x60", - "scale" : "3x" - }, - { - "idiom" : "ios-marketing", - "size" : "1024x1024", - "scale" : "1x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/detox/test/ios-old/example/Info.plist b/detox/test/ios-old/example/Info.plist deleted file mode 100644 index 45242fcbe7..0000000000 --- a/detox/test/ios-old/example/Info.plist +++ /dev/null @@ -1,108 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleURLTypes - - - CFBundleTypeRole - Editor - CFBundleURLName - detoxtesturlscheme.test.app - CFBundleURLSchemes - - detoxtesturlscheme - - - - CFBundleVersion - 1 - LSRequiresIPhoneOS - - NSAppTransportSecurity - - NSAllowsArbitraryLoads - - NSAllowsLocalNetworking - - - NSAppleMusicUsageDescription - - NSBluetoothAlwaysUsageDescription - - NSBluetoothPeripheralUsageDescription - - NSCalendarsUsageDescription - - NSCameraUsageDescription - - NSContactsUsageDescription - - NSFaceIDUsageDescription - - NSLocationAlwaysAndWhenInUseUsageDescription - - NSLocationTemporaryUsageDescriptionDictionary - - NSLocationWhenInUseUsageDescription - - NSMicrophoneUsageDescription - - NSMotionUsageDescription - - NSPhotoLibraryAddUsageDescription - - NSPhotoLibraryUsageDescription - - NSRemindersUsageDescription - - NSSiriUsageDescription - - NSSpeechRecognitionUsageDescription - - NSUserTrackingUsageDescription - - UIBackgroundModes - - fetch - remote-notification - - UILaunchStoryboardName - LaunchScreen - UIRequiredDeviceCapabilities - - armv7 - - UIRequiresFullScreen - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - UIInterfaceOrientationPortraitUpsideDown - - UIViewControllerBasedStatusBarAppearance - - WKBackgroundModes - - remote-notification - - - diff --git a/detox/test/ios-old/example/NativeModule.h b/detox/test/ios-old/example/NativeModule.h deleted file mode 100644 index 7bde08a0f8..0000000000 --- a/detox/test/ios-old/example/NativeModule.h +++ /dev/null @@ -1,6 +0,0 @@ -#import -#import - -@interface NativeModule : NSObject - -@end diff --git a/detox/test/ios-old/example/NativeModule.m b/detox/test/ios-old/example/NativeModule.m deleted file mode 100644 index 08f07b45fc..0000000000 --- a/detox/test/ios-old/example/NativeModule.m +++ /dev/null @@ -1,119 +0,0 @@ -#import "NativeModule.h" -#import -#import - -static int CALL_COUNTER = 0; - -@implementation NativeModule - -RCT_EXPORT_MODULE(); - -RCT_EXPORT_METHOD(echoWithoutResponse:(NSString *)str) -{ - // NSLog(@"NativeModule echoWithoutResponse called"); - CALL_COUNTER++; -} - -RCT_EXPORT_METHOD(echoWithResponse:(NSString *)str - resolver:(RCTPromiseResolveBlock)resolve - rejecter:(RCTPromiseRejectBlock)reject) -{ - CALL_COUNTER++; - resolve(str); - // NSLog(@"NativeModule echoWithResponse called"); -} - -RCT_EXPORT_METHOD(nativeSetTimeout:(NSTimeInterval)delay block:(RCTResponseSenderBlock)block) -{ - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - dispatch_async(dispatch_get_main_queue(), ^{ - block(@[]); - }); - }); -} - -RCT_EXPORT_METHOD(switchToNativeRoot) -{ - dispatch_async(dispatch_get_main_queue(), ^{ - UIViewController* newRoot = [UIViewController new]; - newRoot.view.backgroundColor = [UIColor whiteColor]; - UILabel* label = [UILabel new]; - label.text = @"this is a new native root"; - [label sizeToFit]; - [[newRoot view] addSubview:label]; - label.center = newRoot.view.center; - - id delegate = [[UIApplication sharedApplication] delegate]; - [[delegate window]setRootViewController:newRoot]; - [[delegate window] makeKeyAndVisible]; - }); -} - -RCT_EXPORT_METHOD(switchToMultipleReactRoots) -{ - dispatch_async(dispatch_get_main_queue(), ^{ - id delegate = [[UIApplication sharedApplication] delegate]; - RCTBridge* bridge = ((RCTRootView*)delegate.window.rootViewController.view).bridge; - - UIViewController* newRoot = [UIViewController new]; - newRoot.view = [[RCTRootView alloc]initWithBridge:bridge moduleName:@"example" initialProperties:nil]; - newRoot.tabBarItem.title = @"1"; - - - UIViewController* newRoot2 = [UIViewController new]; - newRoot2.view = [[RCTRootView alloc]initWithBridge:bridge moduleName:@"example" initialProperties:nil]; - newRoot2.tabBarItem.title = @"2"; - - UIViewController* newRoot3 = [UIViewController new]; - newRoot3.view = [[RCTRootView alloc]initWithBridge:bridge moduleName:@"example" initialProperties:nil]; - newRoot3.tabBarItem.title = @"3"; - - UIViewController* newRoot4 = [UIViewController new]; - newRoot4.view = [[RCTRootView alloc]initWithBridge:bridge moduleName:@"example" initialProperties:nil]; - newRoot4.tabBarItem.title = @"4"; - - UITabBarController* tbc = [UITabBarController new]; - tbc.viewControllers = @[newRoot, newRoot2, newRoot3, newRoot4]; - - [[delegate window]setRootViewController:tbc]; - [[delegate window] makeKeyAndVisible]; - }); -} - -RCT_EXPORT_METHOD(sendNotification:(NSString*)notification name:(NSString*)name) -{ - dispatch_async(dispatch_get_main_queue(), ^{ - [NSNotificationCenter.defaultCenter postNotificationName:notification object:nil userInfo:@{@"name": name}]; - }); -} - -RCT_EXPORT_METHOD(presentOverlayWindow) { - static UIWindow *overlayWindow; - - dispatch_async(dispatch_get_main_queue(), ^{ - CGRect screenBounds = UIScreen.mainScreen.bounds; - overlayWindow = [[UIWindow alloc] initWithFrame:screenBounds]; - overlayWindow.accessibilityIdentifier = @"OverlayWindow"; - - [overlayWindow setWindowLevel:UIWindowLevelStatusBar]; - [overlayWindow setHidden:NO]; - - [overlayWindow makeKeyAndVisible]; - }); -} - -RCT_EXPORT_METHOD(presentOverlayView) { - static UIView *overlayView; - - dispatch_async(dispatch_get_main_queue(), ^{ - CGRect screenBounds = UIScreen.mainScreen.bounds; - overlayView = [[UIView alloc] initWithFrame:screenBounds]; - overlayView.userInteractionEnabled = YES; - overlayView.accessibilityIdentifier = @"OverlayView"; - - UIWindow *keyWindow = UIApplication.sharedApplication.keyWindow; - [keyWindow addSubview:overlayView]; - }); -} - -@end diff --git a/detox/test/ios-old/example/PrivacyInfo.xcprivacy b/detox/test/ios-old/example/PrivacyInfo.xcprivacy deleted file mode 100644 index 41b8317f06..0000000000 --- a/detox/test/ios-old/example/PrivacyInfo.xcprivacy +++ /dev/null @@ -1,37 +0,0 @@ - - - - - NSPrivacyAccessedAPITypes - - - NSPrivacyAccessedAPIType - NSPrivacyAccessedAPICategoryFileTimestamp - NSPrivacyAccessedAPITypeReasons - - C617.1 - - - - NSPrivacyAccessedAPIType - NSPrivacyAccessedAPICategoryUserDefaults - NSPrivacyAccessedAPITypeReasons - - CA92.1 - - - - NSPrivacyAccessedAPIType - NSPrivacyAccessedAPICategorySystemBootTime - NSPrivacyAccessedAPITypeReasons - - 35F9.1 - - - - NSPrivacyCollectedDataTypes - - NSPrivacyTracking - - - diff --git a/detox/test/ios-old/example/example.entitlements b/detox/test/ios-old/example/example.entitlements deleted file mode 100644 index 21d95c45f3..0000000000 --- a/detox/test/ios-old/example/example.entitlements +++ /dev/null @@ -1,8 +0,0 @@ - - - - - com.apple.developer.siri - - - diff --git a/detox/test/ios-old/example/main.m b/detox/test/ios-old/example/main.m deleted file mode 100644 index 389f54704d..0000000000 --- a/detox/test/ios-old/example/main.m +++ /dev/null @@ -1,9 +0,0 @@ -#import - -#import "AppDelegate.h" - -int main(int argc, char *argv[]) { - @autoreleasepool { - return UIApplicationMain(argc, argv, NSStringFromClass(DetoxApp.class), NSStringFromClass(AppDelegate.class)); - } -} diff --git a/detox/test/ios-old/exampleUITests/Info.plist b/detox/test/ios-old/exampleUITests/Info.plist deleted file mode 100644 index 64d65ca495..0000000000 --- a/detox/test/ios-old/exampleUITests/Info.plist +++ /dev/null @@ -1,22 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - - diff --git a/detox/test/ios-old/exampleUITests/exampleUITests.m b/detox/test/ios-old/exampleUITests/exampleUITests.m deleted file mode 100644 index 28f9790cc4..0000000000 --- a/detox/test/ios-old/exampleUITests/exampleUITests.m +++ /dev/null @@ -1,42 +0,0 @@ -// -// exampleUITests.m -// exampleUITests -// -// Created by Leo Natan (Wix) on 5/7/20. -// Copyright © 2020 Facebook. All rights reserved. -// - -#import - -@interface exampleUITests : XCTestCase - -@end - -@implementation exampleUITests - -- (void)setUp { - // Put setup code here. This method is called before the invocation of each test method in the class. - - // In UI tests it is usually best to stop immediately when a failure occurs. - self.continueAfterFailure = NO; - - // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. -} - -- (void)tearDown { - // Put teardown code here. This method is called after the invocation of each test method in the class. -} - -- (void)testExample { - // UI tests must launch the application that they test. - XCUIApplication *app = [[XCUIApplication alloc] init]; - [app launch]; - - [NSThread sleepForTimeInterval:1]; - - [app.otherElements[@"Matchers"] tap]; - - [NSThread sleepUntilDate:NSDate.distantFuture]; -} - -@end diff --git a/detox/test/ios-old/example_ci.entitlements b/detox/test/ios-old/example_ci.entitlements deleted file mode 100644 index 21d95c45f3..0000000000 --- a/detox/test/ios-old/example_ci.entitlements +++ /dev/null @@ -1,8 +0,0 @@ - - - - - com.apple.developer.siri - - - From d8ec159a9e9c97d1924ac8cafe562169ca6c60da Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Wed, 1 Jan 2025 13:40:58 +0200 Subject: [PATCH 40/51] test(podfile): remove old RN versions check. --- detox/test/ios/Podfile | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/detox/test/ios/Podfile b/detox/test/ios/Podfile index 6bf92b7c5e..4aabdff2ed 100644 --- a/detox/test/ios/Podfile +++ b/detox/test/ios/Podfile @@ -1,22 +1,15 @@ -if ENV["REACT_NATIVE_VERSION"] && ENV["REACT_NATIVE_VERSION"].match(/0.(70|71).*/) - require_relative '../node_modules/react-native/scripts/react_native_pods' - require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' - require_relative '../node_modules/react-native-permissions/scripts/setup' -else - # Resolve react_native_pods.rb with node to allow for hoisting - def node_require(script) - # Resolve script with node to allow for hoisting - require Pod::Executable.execute_command('node', ['-p', - "require.resolve( - '#{script}', - {paths: [process.argv[1]]}, - )", __dir__]).strip - end - - node_require('react-native/scripts/react_native_pods.rb') - node_require('react-native-permissions/scripts/setup.rb') +def node_require(script) + # Resolve script with node to allow for hoisting + require Pod::Executable.execute_command('node', ['-p', + "require.resolve( + '#{script}', + {paths: [process.argv[1]]}, + )", __dir__]).strip end +node_require('react-native/scripts/react_native_pods.rb') +node_require('react-native-permissions/scripts/setup.rb') + platform :ios, min_ios_version_supported install! 'cocoapods', :deterministic_uuids => false From 865645d7ee79811def8deb31ceb94d83a6c09da2 Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Wed, 1 Jan 2025 14:05:10 +0200 Subject: [PATCH 41/51] chore: clean old snapshot test artifacts. --- .../view-hierarchy-web-view.71.android.txt | 62 ------------------ .../assets/view-hierarchy-web-view.71.ios.txt | 64 ------------------- ...rchy-with-test-id-injection.71.android.txt | 32 ---------- ...ierarchy-with-test-id-injection.71.ios.txt | 27 -------- ...y-without-test-id-injection.71.android.txt | 32 ---------- ...archy-without-test-id-injection.71.ios.txt | 27 -------- 6 files changed, 244 deletions(-) delete mode 100644 detox/test/e2e/assets/view-hierarchy-web-view.71.android.txt delete mode 100644 detox/test/e2e/assets/view-hierarchy-web-view.71.ios.txt delete mode 100644 detox/test/e2e/assets/view-hierarchy-with-test-id-injection.71.android.txt delete mode 100644 detox/test/e2e/assets/view-hierarchy-with-test-id-injection.71.ios.txt delete mode 100644 detox/test/e2e/assets/view-hierarchy-without-test-id-injection.71.android.txt delete mode 100644 detox/test/e2e/assets/view-hierarchy-without-test-id-injection.71.ios.txt diff --git a/detox/test/e2e/assets/view-hierarchy-web-view.71.android.txt b/detox/test/e2e/assets/view-hierarchy-web-view.71.android.txt deleted file mode 100644 index 3020bb4e38..0000000000 --- a/detox/test/e2e/assets/view-hierarchy-web-view.71.android.txt +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

First Webview

-

Form

-
-
-
- -
-

Form Results

-

Your first name is: No input yet

-

Content Editable

-
Name:
-

Text and link

-

Some text and a link.

-

This is a bottom paragraph with class.

- -"]]> -
-
-
-
-
-
-
-
-
-
-
-
- - -
-
diff --git a/detox/test/e2e/assets/view-hierarchy-web-view.71.ios.txt b/detox/test/e2e/assets/view-hierarchy-web-view.71.ios.txt deleted file mode 100644 index cdf809ae6d..0000000000 --- a/detox/test/e2e/assets/view-hierarchy-web-view.71.ios.txt +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

First Webview

-

Form

-
-
-
- -
-

Form Results

-

Your first name is: No input yet

-

Content Editable

-
Name:
-

Text and link

-

Some text and a link.

-

This is a bottom paragraph with class.

- -]]> -
-
-
-
-
-
-
-
-
-
-
-
- -
-
diff --git a/detox/test/e2e/assets/view-hierarchy-with-test-id-injection.71.android.txt b/detox/test/e2e/assets/view-hierarchy-with-test-id-injection.71.android.txt deleted file mode 100644 index 9613748ce4..0000000000 --- a/detox/test/e2e/assets/view-hierarchy-with-test-id-injection.71.android.txt +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/detox/test/e2e/assets/view-hierarchy-with-test-id-injection.71.ios.txt b/detox/test/e2e/assets/view-hierarchy-with-test-id-injection.71.ios.txt deleted file mode 100644 index c88d7f37c6..0000000000 --- a/detox/test/e2e/assets/view-hierarchy-with-test-id-injection.71.ios.txt +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/detox/test/e2e/assets/view-hierarchy-without-test-id-injection.71.android.txt b/detox/test/e2e/assets/view-hierarchy-without-test-id-injection.71.android.txt deleted file mode 100644 index f73e77f8ea..0000000000 --- a/detox/test/e2e/assets/view-hierarchy-without-test-id-injection.71.android.txt +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/detox/test/e2e/assets/view-hierarchy-without-test-id-injection.71.ios.txt b/detox/test/e2e/assets/view-hierarchy-without-test-id-injection.71.ios.txt deleted file mode 100644 index 47f5778eaa..0000000000 --- a/detox/test/e2e/assets/view-hierarchy-without-test-id-injection.71.ios.txt +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - From c469c8b9d9f6cad433f89ec49e17fb6335da9c29 Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Wed, 1 Jan 2025 17:17:41 +0200 Subject: [PATCH 42/51] revert(test): use old string for native root native module. --- detox/test/e2e/08.stress-root.test.js | 2 +- .../test/ios/ReactModules/NativeModule.swift | 68 +++++++++---------- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/detox/test/e2e/08.stress-root.test.js b/detox/test/e2e/08.stress-root.test.js index 277da273b2..8e5113922c 100644 --- a/detox/test/e2e/08.stress-root.test.js +++ b/detox/test/e2e/08.stress-root.test.js @@ -10,7 +10,7 @@ describe('StressRoot', () => { it('should switch root view controller from RN to native', async () => { await element(by.text('Switch to a new native root')).tap(); - await expect(element(by.text('This is a new native root'))).toBeVisible(); + await expect(element(by.text('this is a new native root'))).toBeVisible(); }); it(':ios: should switch root view controller from RN to RN', async () => { diff --git a/detox/test/ios/ReactModules/NativeModule.swift b/detox/test/ios/ReactModules/NativeModule.swift index 9fd6856ef4..40e2d8543b 100644 --- a/detox/test/ios/ReactModules/NativeModule.swift +++ b/detox/test/ios/ReactModules/NativeModule.swift @@ -9,38 +9,38 @@ import UIKit @objc(NativeModule) class NativeModule: NSObject, RCTBridgeModule { - + // MARK: - Properties - + var overlayWindow: UIWindow? var overlayView: UIView? var callCounter: Int = 0 - + // MARK: - RCTBridgeModule - + static func moduleName() -> String! { return "NativeModule" } - + static func requiresMainQueueSetup() -> Bool { // Indicates that the module must be initialized on the main thread return true } - + // MARK: - Lifecycle Methods - + override init() { super.init() self.callCounter = 0 } - + // MARK: - Echo Methods - + @objc func echoWithoutResponse(_ str: String) { self.callCounter += 1 } - + @objc func echoWithResponse(_ str: String, resolver resolve: @escaping RCTPromiseResolveBlock, @@ -48,9 +48,9 @@ class NativeModule: NSObject, RCTBridgeModule { self.callCounter += 1 resolve(str) } - + // MARK: - Timing Methods - + @objc func nativeSetTimeout(_ delay: TimeInterval, block: @escaping RCTResponseSenderBlock) { @@ -61,9 +61,9 @@ class NativeModule: NSObject, RCTBridgeModule { } } } - + // MARK: - Navigation Methods - + @objc func switchToNativeRoot() { executeOnMainThread { [weak self] in @@ -72,7 +72,7 @@ class NativeModule: NSObject, RCTBridgeModule { self.updateRootViewController(newRoot) } } - + @objc func switchToMultipleReactRoots() { executeOnMainThread { [weak self] in @@ -81,9 +81,9 @@ class NativeModule: NSObject, RCTBridgeModule { self.updateRootViewController(tabController) } } - + // MARK: - Notification Methods - + @objc func sendNotification(_ notification: String, name: String) { executeOnMainThread { @@ -92,25 +92,25 @@ class NativeModule: NSObject, RCTBridgeModule { userInfo: ["name": name]) } } - + // MARK: - Overlay Methods - + @objc func presentOverlayWindow() { executeOnMainThread { [weak self] in self?.setupAndShowOverlayWindow() } } - + @objc func presentOverlayView() { executeOnMainThread { [weak self] in self?.setupAndShowOverlayView() } } - + // MARK: - Private Helper Methods - + private func executeOnMainThread(_ block: @escaping () -> Void) { if Thread.isMainThread { block() @@ -120,44 +120,44 @@ class NativeModule: NSObject, RCTBridgeModule { } } } - + private func createNativeRootViewController() -> UIViewController { let newRoot = UIViewController() newRoot.view.backgroundColor = .white - + let label = UILabel() - label.text = "This is a new native root" + label.text = "this is a new native root" label.sizeToFit() label.center = newRoot.view.center newRoot.view.addSubview(label) - + return newRoot } - + private func createTabBarControllerWithBridge() -> UITabBarController { guard let bridge = getCurrentBridge() else { fatalError("RCTBridge is not available") } - + let viewControllers = [ createReactRootViewController(bridge: bridge, title: "1"), createReactRootViewController(bridge: bridge, title: "2"), createReactRootViewController(bridge: bridge, title: "3"), createReactRootViewController(bridge: bridge, title: "4") ] - + let tabController = UITabBarController() tabController.viewControllers = viewControllers return tabController } - + private func createReactRootViewController(bridge: RCTBridge, title: String) -> UIViewController { let viewController = UIViewController() viewController.view = RCTRootView(bridge: bridge, moduleName: "example", initialProperties: nil) viewController.tabBarItem.title = title return viewController } - + private func getCurrentBridge() -> RCTBridge? { guard let delegate = UIApplication.shared.delegate as? AppDelegate, let window = delegate.window, @@ -166,7 +166,7 @@ class NativeModule: NSObject, RCTBridgeModule { } return rootView.bridge } - + private func updateRootViewController(_ viewController: UIViewController) { guard let delegate = UIApplication.shared.delegate as? AppDelegate, let window = delegate.window else { @@ -175,7 +175,7 @@ class NativeModule: NSObject, RCTBridgeModule { window.rootViewController = viewController window.makeKeyAndVisible() } - + private func setupAndShowOverlayWindow() { let screenBounds = UIScreen.main.bounds overlayWindow = UIWindow(frame: screenBounds) @@ -184,7 +184,7 @@ class NativeModule: NSObject, RCTBridgeModule { overlayWindow?.isHidden = false overlayWindow?.makeKeyAndVisible() } - + private func setupAndShowOverlayView() { guard let keyWindow = UIApplication.shared.keyWindow else { return } let screenBounds = UIScreen.main.bounds From 58e775773a0a77a03b203556c5e51edacbbc7908 Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Wed, 1 Jan 2025 17:25:10 +0200 Subject: [PATCH 43/51] test: remove old RN workarounds from podfile. --- examples/demo-react-native/ios/Podfile | 21 +++++---------------- scripts/change_all_react_native_versions.sh | 0 2 files changed, 5 insertions(+), 16 deletions(-) mode change 100644 => 100755 scripts/change_all_react_native_versions.sh diff --git a/examples/demo-react-native/ios/Podfile b/examples/demo-react-native/ios/Podfile index f165377191..fd8c4dc722 100644 --- a/examples/demo-react-native/ios/Podfile +++ b/examples/demo-react-native/ios/Podfile @@ -1,14 +1,8 @@ -if ENV["REACT_NATIVE_VERSION"] && ENV["REACT_NATIVE_VERSION"].match(/0.(70|71).*/) - require_relative '../node_modules/react-native/scripts/react_native_pods' - require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' -else - # Resolve react_native_pods.rb with node to allow for hoisting - require Pod::Executable.execute_command('node', ['-p', - 'require.resolve( - "react-native/scripts/react_native_pods.rb", - {paths: [process.argv[1]]}, - )', __dir__]).strip -end +require Pod::Executable.execute_command('node', ['-p', + 'require.resolve( + "react-native/scripts/react_native_pods.rb", + {paths: [process.argv[1]]}, + )', __dir__]).strip platform :ios, min_ios_version_supported @@ -33,11 +27,6 @@ target 'example' do # necessary for Mac Catalyst builds :mac_catalyst_enabled => false ) - - # See https://github.com/wix/Detox/pull/3035#discussion_r774747705 - if ENV["REACT_NATIVE_VERSION"] && ENV["REACT_NATIVE_VERSION"].match(/0.(70|72).*/) - __apply_Xcode_12_5_M1_post_install_workaround(installer) - end end end diff --git a/scripts/change_all_react_native_versions.sh b/scripts/change_all_react_native_versions.sh old mode 100644 new mode 100755 From 207673c22905e13bb557e1fd7612e0b33d644a18 Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Wed, 1 Jan 2025 18:48:36 +0200 Subject: [PATCH 44/51] test: update snapshots. --- detox/test/e2e/assets/view-hierarchy-web-view.73.ios.txt | 9 ++++----- detox/test/e2e/assets/view-hierarchy-web-view.75.ios.txt | 9 ++++----- .../view-hierarchy-with-test-id-injection.73.ios.txt | 5 ++--- .../view-hierarchy-with-test-id-injection.75.ios.txt | 7 +++---- .../view-hierarchy-without-test-id-injection.73.ios.txt | 5 ++--- .../view-hierarchy-without-test-id-injection.75.ios.txt | 7 +++---- 6 files changed, 18 insertions(+), 24 deletions(-) diff --git a/detox/test/e2e/assets/view-hierarchy-web-view.73.ios.txt b/detox/test/e2e/assets/view-hierarchy-web-view.73.ios.txt index 22552e8c06..7f119af50e 100644 --- a/detox/test/e2e/assets/view-hierarchy-web-view.73.ios.txt +++ b/detox/test/e2e/assets/view-hierarchy-web-view.73.ios.txt @@ -1,6 +1,6 @@ - + @@ -48,7 +48,7 @@

Text and link

Some text and a link.

This is a bottom paragraph with class.

- + ]]> @@ -63,6 +63,5 @@
- -
-
\ No newline at end of file + + diff --git a/detox/test/e2e/assets/view-hierarchy-web-view.75.ios.txt b/detox/test/e2e/assets/view-hierarchy-web-view.75.ios.txt index 8e020b72e1..3a9af9cda7 100644 --- a/detox/test/e2e/assets/view-hierarchy-web-view.75.ios.txt +++ b/detox/test/e2e/assets/view-hierarchy-web-view.75.ios.txt @@ -1,6 +1,6 @@ - + @@ -47,7 +47,7 @@

Text and link

Some text and a link.

This is a bottom paragraph with class.

- + ]]> @@ -61,6 +61,5 @@
- -
-
\ No newline at end of file + + diff --git a/detox/test/e2e/assets/view-hierarchy-with-test-id-injection.73.ios.txt b/detox/test/e2e/assets/view-hierarchy-with-test-id-injection.73.ios.txt index c88d7f37c6..c13d85a1bb 100644 --- a/detox/test/e2e/assets/view-hierarchy-with-test-id-injection.73.ios.txt +++ b/detox/test/e2e/assets/view-hierarchy-with-test-id-injection.73.ios.txt @@ -1,6 +1,6 @@ - + @@ -22,6 +22,5 @@ - - + diff --git a/detox/test/e2e/assets/view-hierarchy-with-test-id-injection.75.ios.txt b/detox/test/e2e/assets/view-hierarchy-with-test-id-injection.75.ios.txt index fba29973c5..88f9d7e59f 100644 --- a/detox/test/e2e/assets/view-hierarchy-with-test-id-injection.75.ios.txt +++ b/detox/test/e2e/assets/view-hierarchy-with-test-id-injection.75.ios.txt @@ -1,6 +1,6 @@ - + @@ -20,6 +20,5 @@ - - - \ No newline at end of file + + diff --git a/detox/test/e2e/assets/view-hierarchy-without-test-id-injection.73.ios.txt b/detox/test/e2e/assets/view-hierarchy-without-test-id-injection.73.ios.txt index 47f5778eaa..6cf7b869f9 100644 --- a/detox/test/e2e/assets/view-hierarchy-without-test-id-injection.73.ios.txt +++ b/detox/test/e2e/assets/view-hierarchy-without-test-id-injection.73.ios.txt @@ -1,6 +1,6 @@ - + @@ -22,6 +22,5 @@ - - + diff --git a/detox/test/e2e/assets/view-hierarchy-without-test-id-injection.75.ios.txt b/detox/test/e2e/assets/view-hierarchy-without-test-id-injection.75.ios.txt index fd1fc31bac..7379b47549 100644 --- a/detox/test/e2e/assets/view-hierarchy-without-test-id-injection.75.ios.txt +++ b/detox/test/e2e/assets/view-hierarchy-without-test-id-injection.75.ios.txt @@ -1,6 +1,6 @@ - + @@ -20,6 +20,5 @@ - - - \ No newline at end of file + + From b1889a63992cc715f26a9a82a545240612e5610f Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Thu, 2 Jan 2025 10:47:02 +0200 Subject: [PATCH 45/51] ci: add new arch pipeline step for RN 76 + new arch + iOS. --- .buildkite/jobs/pipeline.ios_demo_app_rn_76.yml | 1 + .../jobs/pipeline.ios_demo_app_rn_76_new_arch.yml | 10 ++++++++++ .buildkite/jobs/pipeline.ios_rn_76.yml | 1 + .buildkite/jobs/pipeline.ios_rn_76_new_arch.yml | 11 +++++++++++ .buildkite/pipeline_common.sh | 2 ++ 5 files changed, 25 insertions(+) create mode 100644 .buildkite/jobs/pipeline.ios_demo_app_rn_76_new_arch.yml create mode 100644 .buildkite/jobs/pipeline.ios_rn_76_new_arch.yml diff --git a/.buildkite/jobs/pipeline.ios_demo_app_rn_76.yml b/.buildkite/jobs/pipeline.ios_demo_app_rn_76.yml index aff5caf84a..ad359422c9 100644 --- a/.buildkite/jobs/pipeline.ios_demo_app_rn_76.yml +++ b/.buildkite/jobs/pipeline.ios_demo_app_rn_76.yml @@ -4,6 +4,7 @@ - "./scripts/demo-projects.ios.sh" env: REACT_NATIVE_VERSION: 0.76.3 + RCT_NEW_ARCH_ENABLED: 0 artifact_paths: - "/Users/builder/uibuilder/work/coverage/**/*.lcov" - "/Users/builder/uibuilder/work/artifacts*.tar.gz" diff --git a/.buildkite/jobs/pipeline.ios_demo_app_rn_76_new_arch.yml b/.buildkite/jobs/pipeline.ios_demo_app_rn_76_new_arch.yml new file mode 100644 index 0000000000..0a3b3d6f98 --- /dev/null +++ b/.buildkite/jobs/pipeline.ios_demo_app_rn_76_new_arch.yml @@ -0,0 +1,10 @@ + - label: ":new::ios::react: RN .76 + iOS: Demo app" + command: + - "nvm install" + - "./scripts/demo-projects.ios.sh" + env: + REACT_NATIVE_VERSION: 0.76.3 + RCT_NEW_ARCH_ENABLED: 1 + artifact_paths: + - "/Users/builder/uibuilder/work/coverage/**/*.lcov" + - "/Users/builder/uibuilder/work/artifacts*.tar.gz" diff --git a/.buildkite/jobs/pipeline.ios_rn_76.yml b/.buildkite/jobs/pipeline.ios_rn_76.yml index b31ba4e00a..3b20cde7a5 100644 --- a/.buildkite/jobs/pipeline.ios_rn_76.yml +++ b/.buildkite/jobs/pipeline.ios_rn_76.yml @@ -4,6 +4,7 @@ - "./scripts/ci.ios.sh" env: REACT_NATIVE_VERSION: 0.76.3 + RCT_NEW_ARCH_ENABLED: 0 artifact_paths: - "/Users/builder/uibuilder/work/coverage/**/*.lcov" - "/Users/builder/uibuilder/work/**/allure-report-*.html" diff --git a/.buildkite/jobs/pipeline.ios_rn_76_new_arch.yml b/.buildkite/jobs/pipeline.ios_rn_76_new_arch.yml new file mode 100644 index 0000000000..5ff264ed17 --- /dev/null +++ b/.buildkite/jobs/pipeline.ios_rn_76_new_arch.yml @@ -0,0 +1,11 @@ + - label: ":new::ios::detox: RN .76 + New Arch + iOS: Tests app" + command: + - "nvm install" + - "./scripts/ci.ios.sh" + env: + REACT_NATIVE_VERSION: 0.76.3 + RCT_NEW_ARCH_ENABLED: 1 + artifact_paths: + - "/Users/builder/uibuilder/work/coverage/**/*.lcov" + - "/Users/builder/uibuilder/work/**/allure-report-*.html" + - "/Users/builder/uibuilder/work/artifacts*.tar.gz" diff --git a/.buildkite/pipeline_common.sh b/.buildkite/pipeline_common.sh index 4afa7835b7..54a62690cd 100755 --- a/.buildkite/pipeline_common.sh +++ b/.buildkite/pipeline_common.sh @@ -2,9 +2,11 @@ echo "steps:" +cat .buildkite/jobs/pipeline.ios_rn_76_new_arch.yml cat .buildkite/jobs/pipeline.ios_rn_76.yml cat .buildkite/jobs/pipeline.ios_rn_75.yml cat .buildkite/jobs/pipeline.ios_rn_73.yml +cat .buildkite/jobs/pipeline.ios_demo_app_rn_76_new_arch.yml cat .buildkite/jobs/pipeline.ios_demo_app_rn_76.yml cat .buildkite/jobs/pipeline.android_rn_76.yml cat .buildkite/jobs/pipeline.android_rn_75.yml From 24d519833c99581b99794c0847f78d8dab3634a8 Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Thu, 2 Jan 2025 12:15:02 +0200 Subject: [PATCH 46/51] test: update copilot cache. --- detox/test/detox_copilot_cache.json | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/detox/test/detox_copilot_cache.json b/detox/test/detox_copilot_cache.json index 8bb236eae7..18461248f4 100644 --- a/detox/test/detox_copilot_cache.json +++ b/detox/test/detox_copilot_cache.json @@ -105,5 +105,17 @@ "{\"step\":\"Verify there is an element with the text \\\"Get location\\\"\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Location screen\",\"code\":\"await element(by.label('Location')).tap();\"},{\"step\":\"Launch the app with location permission always\",\"code\":\"await device.launchApp({ \\n newInstance: true,\\n permissions: { location: 'always' }\\n});\"},{\"step\":\"Verify there is an element with the text \\\"Get location\\\"\",\"code\":\"throw new Error(\\\"No element with text 'Get location' found in the view hierarchy. Found 'Location' instead.\\\");\",\"result\":\"Caught an error while evaluating \\\"Verify there is an element with the text \\\"Get location\\\"\\\", tried with generated code: \\\"throw new Error(\\\"No element with text 'Get location' found in the view hierarchy. Found 'Location' instead.\\\");\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"b16ef5d51f0f64d6a63d434b26d81efc\"}": "throw new Error(\"No element with text 'Get location' found in the view hierarchy. Found 'Location' instead.\");", "{\"step\":\"Launch the app with location permission just once\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Location screen\",\"code\":\"await element(by.label('Location')).tap();\"}],\"viewHierarchyHash\":\"e79b58a41e3e4e6b49202372b5c1cc79\"}": "await device.launchApp({\n newInstance: true,\n permissions: { location: 'inuse' }\n});", "{\"step\":\"Verify there is an element with the text \\\"Get location\\\"\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Location screen\",\"code\":\"await element(by.label('Location')).tap();\"},{\"step\":\"Launch the app with location permission just once\",\"code\":\"await device.launchApp({\\n newInstance: true,\\n permissions: { location: 'inuse' }\\n});\"}],\"viewHierarchyHash\":\"b16ef5d51f0f64d6a63d434b26d81efc\"}": "await expect(element(by.text('Get location'))).toExist();", - "{\"step\":\"Verify there is an element with the text \\\"Get location\\\"\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Location screen\",\"code\":\"await element(by.label('Location')).tap();\"},{\"step\":\"Launch the app with location permission just once\",\"code\":\"await device.launchApp({\\n newInstance: true,\\n permissions: { location: 'inuse' }\\n});\"},{\"step\":\"Verify there is an element with the text \\\"Get location\\\"\",\"code\":\"await expect(element(by.text('Get location'))).toExist();\",\"result\":\"Caught an error while evaluating \\\"Verify there is an element with the text \\\"Get location\\\"\\\", tried with generated code: \\\"await expect(element(by.text('Get location'))).toExist();\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"b16ef5d51f0f64d6a63d434b26d81efc\"}": "throw new Error(\"Element with text 'Get location' is not found in the current view hierarchy\")" + "{\"step\":\"Verify there is an element with the text \\\"Get location\\\"\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Location screen\",\"code\":\"await element(by.label('Location')).tap();\"},{\"step\":\"Launch the app with location permission just once\",\"code\":\"await device.launchApp({\\n newInstance: true,\\n permissions: { location: 'inuse' }\\n});\"},{\"step\":\"Verify there is an element with the text \\\"Get location\\\"\",\"code\":\"await expect(element(by.text('Get location'))).toExist();\",\"result\":\"Caught an error while evaluating \\\"Verify there is an element with the text \\\"Get location\\\"\\\", tried with generated code: \\\"await expect(element(by.text('Get location'))).toExist();\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"b16ef5d51f0f64d6a63d434b26d81efc\"}": "throw new Error(\"Element with text 'Get location' is not found in the current view hierarchy\")", + "{\"step\":\"Remove the app and start a fresh instance\",\"previous\":[],\"viewHierarchyHash\":\"a14a74aaf07d6f92a7030643e4ac6895\"}": "await device.launchApp({\n delete: true,\n newInstance: true\n});", + "{\"step\":\"Navigate to the System Dialogs screen\",\"previous\":[{\"step\":\"Remove the app and start a fresh instance\",\"code\":\"await device.launchApp({\\n delete: true,\\n newInstance: true\\n});\"}],\"viewHierarchyHash\":\"a14a74aaf07d6f92a7030643e4ac6895\"}": "await element(by.text('System Dialogs')).tap();", + "{\"step\":\"Check that the initial permission status is \\\"denied\\\"\",\"previous\":[{\"step\":\"Remove the app and start a fresh instance\",\"code\":\"await device.launchApp({\\n delete: true,\\n newInstance: true\\n});\"},{\"step\":\"Navigate to the System Dialogs screen\",\"code\":\"await element(by.text('System Dialogs')).tap();\"}],\"viewHierarchyHash\":\"31263c8ad60867de323197ea33be2402\"}": "await expect(element(by.id('permissionStatus'))).toHaveText('denied');", + "{\"step\":\"Tap the button to request permission\",\"previous\":[{\"step\":\"Remove the app and start a fresh instance\",\"code\":\"await device.launchApp({\\n delete: true,\\n newInstance: true\\n});\"},{\"step\":\"Navigate to the System Dialogs screen\",\"code\":\"await element(by.text('System Dialogs')).tap();\"},{\"step\":\"Check that the initial permission status is \\\"denied\\\"\",\"code\":\"await expect(element(by.id('permissionStatus'))).toHaveText('denied');\"}],\"viewHierarchyHash\":\"31263c8ad60867de323197ea33be2402\"}": "await element(by.id('requestPermissionButton')).tap();", + "{\"step\":\"A system dialog appears asking for permission\",\"previous\":[{\"step\":\"Remove the app and start a fresh instance\",\"code\":\"await device.launchApp({\\n delete: true,\\n newInstance: true\\n});\"},{\"step\":\"Navigate to the System Dialogs screen\",\"code\":\"await element(by.text('System Dialogs')).tap();\"},{\"step\":\"Check that the initial permission status is \\\"denied\\\"\",\"code\":\"await expect(element(by.id('permissionStatus'))).toHaveText('denied');\"},{\"step\":\"Tap the button to request permission\",\"code\":\"await element(by.id('requestPermissionButton')).tap();\"}],\"viewHierarchyHash\":\"227afc5a25375e77c25cfdfc6a1c7fe6\"}": "await expect(system.element(by.system.label('Allow \"example-ci\" to track your activity across other companies' apps and websites?'))).toExist();", + "{\"step\":\"A system dialog appears asking for permission\",\"previous\":[{\"step\":\"Remove the app and start a fresh instance\",\"code\":\"await device.launchApp({\\n delete: true,\\n newInstance: true\\n});\"},{\"step\":\"Navigate to the System Dialogs screen\",\"code\":\"await element(by.text('System Dialogs')).tap();\"},{\"step\":\"Check that the initial permission status is \\\"denied\\\"\",\"code\":\"await expect(element(by.id('permissionStatus'))).toHaveText('denied');\"},{\"step\":\"Tap the button to request permission\",\"code\":\"await element(by.id('requestPermissionButton')).tap();\"},{\"step\":\"A system dialog appears asking for permission\",\"code\":\"await expect(system.element(by.system.label('Allow \\\"example-ci\\\" to track your activity across other companies' apps and websites?'))).toExist();\",\"result\":\"Caught an error while evaluating \\\"A system dialog appears asking for permission\\\", tried with generated code: \\\"await expect(system.element(by.system.label('Allow \\\"example-ci\\\" to track your activity across other companies' apps and websites?'))).toExist();\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"b85ab02e88af10d34333cce30397498f\"}": "await expect(system.element(by.system.label('Allow \"example-ci\" to track your activity across other companies' apps and websites?'))).toExist();", + "{\"step\":\"Remove the app and start a fresh instance\",\"previous\":[],\"viewHierarchyHash\":\"b85ab02e88af10d34333cce30397498f\"}": "await device.launchApp({\n newInstance: true,\n delete: true\n});", + "{\"step\":\"Restart the React Native state\",\"previous\":[],\"viewHierarchyHash\":\"66b21c68b8b89f4e8d7c1a733d789ea6\"}": "await device.reloadReactNative();", + "{\"step\":\"Navigate to the Assertions screen\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"}],\"viewHierarchyHash\":\"b16ef5d51f0f64d6a63d434b26d81efc\"}": "await element(by.text('Assertions')).tap();", + "{\"step\":\"Find an element with ID \\\"RandomJunk959\\\" in the screen\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Assertions screen\",\"code\":\"await element(by.text('Assertions')).tap();\"}],\"viewHierarchyHash\":\"66b21c68b8b89f4e8d7c1a733d789ea6\"}": "throw new Error(\"Element with ID 'RandomJunk959' not found in view hierarchy.\")", + "{\"step\":\"Verify there is a green text element\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Assertions screen\",\"code\":\"await element(by.text('Assertions')).tap();\"}],\"viewHierarchyHash\":\"66b21c68b8b89f4e8d7c1a733d789ea6\"}": "throw new Error(\"Cannot verify if text is green - color information is not available in the view hierarchy and cannot be visually confirmed from the snapshot.\");", + "{\"step\":\"Verify there is a green text element\",\"previous\":[{\"step\":\"Restart the React Native state\",\"code\":\"await device.reloadReactNative();\"},{\"step\":\"Navigate to the Assertions screen\",\"code\":\"await element(by.text('Assertions')).tap();\"},{\"step\":\"Verify there is a green text element\",\"code\":\"throw new Error(\\\"Cannot verify if text is green - color information is not available in the view hierarchy and cannot be visually confirmed from the snapshot.\\\");\",\"result\":\"Caught an error while evaluating \\\"Verify there is a green text element\\\", tried with generated code: \\\"throw new Error(\\\"Cannot verify if text is green - color information is not available in the view hierarchy and cannot be visually confirmed from the snapshot.\\\");\\\". Validate the code against the APIs and hierarchy and continue with a different approach. If can't, return a code that throws a descriptive error.\"}],\"viewHierarchyHash\":\"66b21c68b8b89f4e8d7c1a733d789ea6\"}": "throw new Error(\"Cannot verify if text is green - color attributes are not available through the testing framework APIs for verification\");" } \ No newline at end of file From abb9ae441bf1fbce675ad945d1127e6cedb4157c Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Thu, 2 Jan 2025 14:45:33 +0200 Subject: [PATCH 47/51] test: print if react native new arch is enabled. --- detox/test/ios/Podfile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/detox/test/ios/Podfile b/detox/test/ios/Podfile index 4aabdff2ed..ceeda69c06 100644 --- a/detox/test/ios/Podfile +++ b/detox/test/ios/Podfile @@ -1,3 +1,7 @@ +if ENV['RCT_NEW_ARCH_ENABLED'] == '1' + puts 'React Native new arch enabled (RCT_NEW_ARCH_ENABLED = 1)' +end + def node_require(script) # Resolve script with node to allow for hoisting require Pod::Executable.execute_command('node', ['-p', From 6902b2337261a2bb194f4ddf9f62aeaafc68735a Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Thu, 2 Jan 2025 15:37:09 +0200 Subject: [PATCH 48/51] test: skip picker tests on new arch. --- detox/test/e2e/17.datePicker.test.js | 3 +- detox/test/e2e/17.picker.test.js | 4 +- .../e2e/copilot/09.copilot.datepicker.test.js | 94 ++++++++++--------- detox/test/e2e/utils/custom-describes.js | 29 ++++-- 4 files changed, 76 insertions(+), 54 deletions(-) diff --git a/detox/test/e2e/17.datePicker.test.js b/detox/test/e2e/17.datePicker.test.js index f664941e67..131065b053 100644 --- a/detox/test/e2e/17.datePicker.test.js +++ b/detox/test/e2e/17.datePicker.test.js @@ -1,6 +1,7 @@ +const { describeNewArchNotSupported } = require('./utils/custom-describes'); const jestExpect = require('expect').default; -describe('DatePicker', () => { +describeNewArchNotSupported('DatePicker', () => { describe.each([ ['ios', 'compact', 0], ['ios', 'inline', 1], diff --git a/detox/test/e2e/17.picker.test.js b/detox/test/e2e/17.picker.test.js index 14e0f3a852..a6be677257 100644 --- a/detox/test/e2e/17.picker.test.js +++ b/detox/test/e2e/17.picker.test.js @@ -1,4 +1,6 @@ -describe(":ios: Picker", () => { +const { describeNewArchNotSupported } = require('./utils/custom-describes'); + +describeNewArchNotSupported(":ios: Picker", () => { beforeEach(async () => { await device.reloadReactNative(); await element(by.text("Picker")).tap(); diff --git a/detox/test/e2e/copilot/09.copilot.datepicker.test.js b/detox/test/e2e/copilot/09.copilot.datepicker.test.js index ad35350efd..d894508fa1 100644 --- a/detox/test/e2e/copilot/09.copilot.datepicker.test.js +++ b/detox/test/e2e/copilot/09.copilot.datepicker.test.js @@ -1,58 +1,60 @@ -const { describeForCopilotEnv } = require('../utils/custom-describes'); +const { describeForCopilotEnv, describeNewArchNotSupported } = require('../utils/custom-describes'); const { default: jestExpect } = require('expect'); -describeForCopilotEnv('DatePicker', () => { - beforeEach(async () => { - await copilot.perform( - 'Restart the React Native state', - 'Navigate to the DatePicker screen' - ); - }); - - describe('DatePicker Tests', () => { - - // Note: when writing "Date (UTC):" instead of "Date (UTC): " copilot failed the test - it('correct date and time', async () => { +describeNewArchNotSupported('DatePicker', () => { + describeForCopilotEnv('Copilot', () => { + beforeEach(async () => { await copilot.perform( - 'Verify there is element with the text "Date (UTC): "', - 'Verify the element value of current date UTC July 1st 2023', - 'Verify there is element with the text "Time (UTC): "', - 'Verify there is element with the text "Time Local: "', - 'Verify "Time Local: " value is 7:30 pm' + 'Restart the React Native state', + 'Navigate to the DatePicker screen' ); }); - it('compact date picker', async () => { - await copilot.perform( - 'Verify there is an element with the text "Compact Date Picker"', - 'Verify there is an element with today`s date at the bottom of the screen', - 'Set the date picker to September 9th, 2023' - ); - }); + describe('DatePicker Tests', () => { - it('inline date picker', async () => { - await copilot.perform( - 'Verify there is an element with the text "Compact Date Picker"', - 'Tap the element with the text "Compact Date Picker"', - 'Verify there is an element with the text "Inline Date Picker"', - 'Verify there is an element with today`s date at the bottom of the screen', - 'Set the date picker to September 9th, 2023' - ); - }); + // Note: when writing "Date (UTC):" instead of "Date (UTC): " copilot failed the test + it('correct date and time', async () => { + await copilot.perform( + 'Verify there is element with the text "Date (UTC): "', + 'Verify the element value of current date UTC July 1st 2023', + 'Verify there is element with the text "Time (UTC): "', + 'Verify there is element with the text "Time Local: "', + 'Verify "Time Local: " value is 7:30 pm' + ); + }); - it('switch to spinner date picker', async () => { - await copilot.perform( - 'Verify there is an element with the text "Compact Date Picker"', - 'Tap the element with the text "Compact Date Picker"', - 'Verify there is an element with the text "Inline Date Picker"', - 'Tap the element with the text "Inline Date Picker"', - 'Verify that there is slider element at the bottom of the screen', - 'Set the date picker to September 9th, 2023' - ); + it('compact date picker', async () => { + await copilot.perform( + 'Verify there is an element with the text "Compact Date Picker"', + 'Verify there is an element with today`s date at the bottom of the screen', + 'Set the date picker to September 9th, 2023' + ); + }); + + it('inline date picker', async () => { + await copilot.perform( + 'Verify there is an element with the text "Compact Date Picker"', + 'Tap the element with the text "Compact Date Picker"', + 'Verify there is an element with the text "Inline Date Picker"', + 'Verify there is an element with today`s date at the bottom of the screen', + 'Set the date picker to September 9th, 2023' + ); + }); + + it('switch to spinner date picker', async () => { + await copilot.perform( + 'Verify there is an element with the text "Compact Date Picker"', + 'Tap the element with the text "Compact Date Picker"', + 'Verify there is an element with the text "Inline Date Picker"', + 'Tap the element with the text "Inline Date Picker"', + 'Verify that there is slider element at the bottom of the screen', + 'Set the date picker to September 9th, 2023' + ); - await jestExpect(async () => - await copilot.perform('Set the date picker`s first column to 10th, so the date will be September 10th, 2023') - ).rejects.toThrowError(); + await jestExpect(async () => + await copilot.perform('Set the date picker`s first column to 10th, so the date will be September 10th, 2023') + ).rejects.toThrowError(); + }); }); }); }); diff --git a/detox/test/e2e/utils/custom-describes.js b/detox/test/e2e/utils/custom-describes.js index d44f4872a4..f66cb3f168 100644 --- a/detox/test/e2e/utils/custom-describes.js +++ b/detox/test/e2e/utils/custom-describes.js @@ -1,13 +1,14 @@ const axios = require('axios'); const PromptHandler = require("./PromptHandler"); -const describeOrDescribeSkip = process.env.CI === 'true' ? describe.skip : describe; - describeForCopilotEnv = (description, fn) => { - describeOrDescribeSkip(':ios: Copilot', () => { + const isCI = process.env.CI === 'true'; + const describeOrDescribeSkipIfCI = isCI ? describe.skip : describe; + + describeOrDescribeSkipIfCI(':ios: Copilot', () => { describe(description, () => { beforeAll(async () => { - if (!await checkVpnStatus()) { + if (!await _checkVpnStatus()) { console.warn('Cannot access the LLM service without Wix BO environment. Relying on cached responses only.'); } try { @@ -25,7 +26,7 @@ describeForCopilotEnv = (description, fn) => { }); }; -checkVpnStatus = async () => { +_checkVpnStatus = async () => { try { const response = await axios.get('https://bo.wix.com/_serverless/expert-toolkit/checkVpn'); return response.data.enabled === true; @@ -35,6 +36,22 @@ checkVpnStatus = async () => { } }; +describeNewArchNotSupported = (description, fn) => { + const isNewArch = process.env.RCT_NEW_ARCH_ENABLED === '1'; + const describeOrDescribeSkipIfNewArch = isNewArch ? describe.skip : describe; + + if (isNewArch) { + console.warn('Skipping tests for new architecture, as there are issues related to the new architecture.'); + } + + describeOrDescribeSkipIfNewArch('Legacy Arch (Paper)', () => { + describe(description, () => { + fn(); + }); + }); +} + module.exports = { - describeForCopilotEnv + describeForCopilotEnv, + describeNewArchNotSupported }; From e017d396dbb19c03e22b8e453bbd6c44ae68cca0 Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Mon, 6 Jan 2025 15:41:37 +0200 Subject: [PATCH 49/51] test(custom-describe): change to skip methods. --- detox/test/e2e/17.datePicker.test.js | 3 +- detox/test/e2e/17.picker.test.js | 4 +- .../e2e/copilot/01.copilot.sanity.test.js | 4 +- .../e2e/copilot/02.copilot.actions.test.js | 3 +- .../copilot/03.copilot.shape-match.test.js | 4 +- detox/test/e2e/copilot/04.webview.test.js | 4 +- detox/test/e2e/copilot/05.system.test.js | 3 +- detox/test/e2e/copilot/06.waitfor.test.js | 3 +- .../e2e/copilot/07.copilot.assertions.test.js | 3 +- .../e2e/copilot/08.copilot.location.test.js | 3 +- .../e2e/copilot/09.copilot.datepicker.test.js | 5 +-- .../e2e/copilot/10.copilot.visibility.test.js | 3 +- detox/test/e2e/setup.js | 1 + detox/test/e2e/utils/custom-describes.js | 40 ++++++++----------- 14 files changed, 30 insertions(+), 53 deletions(-) diff --git a/detox/test/e2e/17.datePicker.test.js b/detox/test/e2e/17.datePicker.test.js index 131065b053..f8bab7608a 100644 --- a/detox/test/e2e/17.datePicker.test.js +++ b/detox/test/e2e/17.datePicker.test.js @@ -1,7 +1,6 @@ -const { describeNewArchNotSupported } = require('./utils/custom-describes'); const jestExpect = require('expect').default; -describeNewArchNotSupported('DatePicker', () => { +describe.skipIfNewArch('DatePicker', () => { describe.each([ ['ios', 'compact', 0], ['ios', 'inline', 1], diff --git a/detox/test/e2e/17.picker.test.js b/detox/test/e2e/17.picker.test.js index a6be677257..a2cd329b39 100644 --- a/detox/test/e2e/17.picker.test.js +++ b/detox/test/e2e/17.picker.test.js @@ -1,6 +1,4 @@ -const { describeNewArchNotSupported } = require('./utils/custom-describes'); - -describeNewArchNotSupported(":ios: Picker", () => { +describe.skipIfNewArch(":ios: Picker", () => { beforeEach(async () => { await device.reloadReactNative(); await element(by.text("Picker")).tap(); diff --git a/detox/test/e2e/copilot/01.copilot.sanity.test.js b/detox/test/e2e/copilot/01.copilot.sanity.test.js index 56b5995a6f..9de6e4dac1 100644 --- a/detox/test/e2e/copilot/01.copilot.sanity.test.js +++ b/detox/test/e2e/copilot/01.copilot.sanity.test.js @@ -1,6 +1,4 @@ -const {describeForCopilotEnv} = require("../utils/custom-describes"); - -describeForCopilotEnv('Copilot Sanity', () => { +describe.forCopilot('Copilot Sanity', () => { beforeEach(async () => { await copilot.perform( 'Restart the React Native state', diff --git a/detox/test/e2e/copilot/02.copilot.actions.test.js b/detox/test/e2e/copilot/02.copilot.actions.test.js index bee228e8e8..f18e213505 100644 --- a/detox/test/e2e/copilot/02.copilot.actions.test.js +++ b/detox/test/e2e/copilot/02.copilot.actions.test.js @@ -1,7 +1,6 @@ -const {describeForCopilotEnv} = require("../utils/custom-describes"); const jestExpect = require('expect').default; -describeForCopilotEnv('Copilot Actions', () => { +describe.forCopilot('Copilot Actions', () => { beforeEach(async () => { await copilot.perform( 'Restart the React Native environment', diff --git a/detox/test/e2e/copilot/03.copilot.shape-match.test.js b/detox/test/e2e/copilot/03.copilot.shape-match.test.js index a9aab84752..50e3c3dad1 100644 --- a/detox/test/e2e/copilot/03.copilot.shape-match.test.js +++ b/detox/test/e2e/copilot/03.copilot.shape-match.test.js @@ -1,6 +1,4 @@ -const {describeForCopilotEnv} = require("../utils/custom-describes"); - -describeForCopilotEnv('Shape Match Game Screen', () => { +describe.forCopilot('Shape Match Game Screen', () => { beforeEach(async () => { await copilot.perform( 'Reset react native state', diff --git a/detox/test/e2e/copilot/04.webview.test.js b/detox/test/e2e/copilot/04.webview.test.js index c66c4c57ef..1e6a7346be 100644 --- a/detox/test/e2e/copilot/04.webview.test.js +++ b/detox/test/e2e/copilot/04.webview.test.js @@ -1,6 +1,4 @@ -const {describeForCopilotEnv} = require("../utils/custom-describes"); - -describeForCopilotEnv('WebView Interactions', () => { +describe.forCopilot('WebView Interactions', () => { beforeEach(async () => { await copilot.perform( 'Restart the React Native state', diff --git a/detox/test/e2e/copilot/05.system.test.js b/detox/test/e2e/copilot/05.system.test.js index ef6f5ad3df..3dbcc5a68a 100644 --- a/detox/test/e2e/copilot/05.system.test.js +++ b/detox/test/e2e/copilot/05.system.test.js @@ -1,7 +1,6 @@ -const {describeForCopilotEnv} = require("../utils/custom-describes"); const {expectToThrow} = require("../utils/custom-expects"); -describeForCopilotEnv(':ios: iOS Permission Dialogs', () => { +describe.forCopilot(':ios: iOS Permission Dialogs', () => { beforeEach(async () => { await copilot.perform( 'Remove the app and start a fresh instance', diff --git a/detox/test/e2e/copilot/06.waitfor.test.js b/detox/test/e2e/copilot/06.waitfor.test.js index 675397351a..ed9c7c0663 100644 --- a/detox/test/e2e/copilot/06.waitfor.test.js +++ b/detox/test/e2e/copilot/06.waitfor.test.js @@ -1,7 +1,6 @@ -const {describeForCopilotEnv} = require("../utils/custom-describes"); const {expectToThrow} = require("../utils/custom-expects"); -describeForCopilotEnv('WaitFor Functionality', () => { +describe.forCopilot('WaitFor Functionality', () => { beforeEach(async () => { await copilot.perform( 'Restart the React Native environment', diff --git a/detox/test/e2e/copilot/07.copilot.assertions.test.js b/detox/test/e2e/copilot/07.copilot.assertions.test.js index 1a9932031d..ef44b46007 100644 --- a/detox/test/e2e/copilot/07.copilot.assertions.test.js +++ b/detox/test/e2e/copilot/07.copilot.assertions.test.js @@ -1,7 +1,6 @@ -const { describeForCopilotEnv } = require('../utils/custom-describes'); const jestExpect = require('expect').default; -describeForCopilotEnv('Assertions', () => { +describe.forCopilot('Assertions', () => { beforeEach(async () => { await copilot.perform( 'Restart the React Native state', diff --git a/detox/test/e2e/copilot/08.copilot.location.test.js b/detox/test/e2e/copilot/08.copilot.location.test.js index ea7a1600cf..c03a1d8b67 100644 --- a/detox/test/e2e/copilot/08.copilot.location.test.js +++ b/detox/test/e2e/copilot/08.copilot.location.test.js @@ -1,10 +1,9 @@ -const { describeForCopilotEnv } = require('../utils/custom-describes'); const DUMMY_COORDINATE1_LONGITUDE = '66.5'; const DUMMY_COORDINATE1_LATITUDE = '-80.125'; const DUMMY_COORDINATE2_LONGITUDE = '-80.125'; const DUMMY_COORDINATE2_LATITUDE = '66.5'; -describeForCopilotEnv('Location', () => { +describe.forCopilot('Location', () => { beforeEach(async () => { await copilot.perform( 'Restart the React Native state', diff --git a/detox/test/e2e/copilot/09.copilot.datepicker.test.js b/detox/test/e2e/copilot/09.copilot.datepicker.test.js index d894508fa1..6d12f16d05 100644 --- a/detox/test/e2e/copilot/09.copilot.datepicker.test.js +++ b/detox/test/e2e/copilot/09.copilot.datepicker.test.js @@ -1,8 +1,7 @@ -const { describeForCopilotEnv, describeNewArchNotSupported } = require('../utils/custom-describes'); const { default: jestExpect } = require('expect'); -describeNewArchNotSupported('DatePicker', () => { - describeForCopilotEnv('Copilot', () => { +describe.skipIfNewArch('DatePicker', () => { + describe.forCopilot('Copilot', () => { beforeEach(async () => { await copilot.perform( 'Restart the React Native state', diff --git a/detox/test/e2e/copilot/10.copilot.visibility.test.js b/detox/test/e2e/copilot/10.copilot.visibility.test.js index 0d96e27bdc..3fedddf908 100644 --- a/detox/test/e2e/copilot/10.copilot.visibility.test.js +++ b/detox/test/e2e/copilot/10.copilot.visibility.test.js @@ -1,7 +1,6 @@ -const {describeForCopilotEnv} = require("../utils/custom-describes"); const { default: jestExpect } = require('expect'); -describeForCopilotEnv('Visibility', () => { +describe.forCopilot('Visibility', () => { describe('Visibility Expectation', () => { beforeEach(async () => { await copilot.perform( diff --git a/detox/test/e2e/setup.js b/detox/test/e2e/setup.js index a2bc5ea2a7..deda7dd184 100644 --- a/detox/test/e2e/setup.js +++ b/detox/test/e2e/setup.js @@ -1,4 +1,5 @@ const { device } = require('detox'); +require('./utils/custom-describes'); beforeAll(async () => { await device.selectApp('example'); diff --git a/detox/test/e2e/utils/custom-describes.js b/detox/test/e2e/utils/custom-describes.js index f66cb3f168..a42898101b 100644 --- a/detox/test/e2e/utils/custom-describes.js +++ b/detox/test/e2e/utils/custom-describes.js @@ -1,11 +1,22 @@ const axios = require('axios'); const PromptHandler = require("./PromptHandler"); -describeForCopilotEnv = (description, fn) => { +describe.skipIfCI = (title, fn) => { const isCI = process.env.CI === 'true'; - const describeOrDescribeSkipIfCI = isCI ? describe.skip : describe; + return isCI ? describe.skip(title, fn) : describe(title, fn); +}; + +describe.skipIfNewArch = (title, fn) => { + const isNewArch = process.env.RCT_NEW_ARCH_ENABLED === '1'; + if (isNewArch) { + console.warn('Skipping tests for new architecture, as there are issues related to the new architecture.'); + return describe.skip(title, fn); + } + return describe(title, fn); +}; - describeOrDescribeSkipIfCI(':ios: Copilot', () => { +describe.forCopilot = (description, fn) => { + return describe.skipIfCI(':ios: Copilot', () => { describe(description, () => { beforeAll(async () => { if (!await _checkVpnStatus()) { @@ -15,6 +26,7 @@ describeForCopilotEnv = (description, fn) => { await copilot.init(new PromptHandler()); } catch (error) { if (error.message.includes('Copilot has already been initialized')) { + // Ignore already initialized error } else { throw error; } @@ -26,7 +38,7 @@ describeForCopilotEnv = (description, fn) => { }); }; -_checkVpnStatus = async () => { +const _checkVpnStatus = async () => { try { const response = await axios.get('https://bo.wix.com/_serverless/expert-toolkit/checkVpn'); return response.data.enabled === true; @@ -35,23 +47,3 @@ _checkVpnStatus = async () => { return false; } }; - -describeNewArchNotSupported = (description, fn) => { - const isNewArch = process.env.RCT_NEW_ARCH_ENABLED === '1'; - const describeOrDescribeSkipIfNewArch = isNewArch ? describe.skip : describe; - - if (isNewArch) { - console.warn('Skipping tests for new architecture, as there are issues related to the new architecture.'); - } - - describeOrDescribeSkipIfNewArch('Legacy Arch (Paper)', () => { - describe(description, () => { - fn(); - }); - }); -} - -module.exports = { - describeForCopilotEnv, - describeNewArchNotSupported -}; From 30b0dacdf9bc21fae19d2fbbfe0e04f1c8faa69c Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Mon, 6 Jan 2025 15:50:46 +0200 Subject: [PATCH 50/51] ci: remove common & redundant steps from pipelines. --- .buildkite/jobs/pipeline.ios_demo_app_rn_76.yml | 10 ---------- .buildkite/jobs/pipeline.ios_rn_75.yml | 10 ---------- .buildkite/pipeline_common.sh | 2 -- 3 files changed, 22 deletions(-) delete mode 100644 .buildkite/jobs/pipeline.ios_demo_app_rn_76.yml delete mode 100644 .buildkite/jobs/pipeline.ios_rn_75.yml diff --git a/.buildkite/jobs/pipeline.ios_demo_app_rn_76.yml b/.buildkite/jobs/pipeline.ios_demo_app_rn_76.yml deleted file mode 100644 index ad359422c9..0000000000 --- a/.buildkite/jobs/pipeline.ios_demo_app_rn_76.yml +++ /dev/null @@ -1,10 +0,0 @@ - - label: ":ios::react: RN .76 + iOS: Demo app" - command: - - "nvm install" - - "./scripts/demo-projects.ios.sh" - env: - REACT_NATIVE_VERSION: 0.76.3 - RCT_NEW_ARCH_ENABLED: 0 - artifact_paths: - - "/Users/builder/uibuilder/work/coverage/**/*.lcov" - - "/Users/builder/uibuilder/work/artifacts*.tar.gz" diff --git a/.buildkite/jobs/pipeline.ios_rn_75.yml b/.buildkite/jobs/pipeline.ios_rn_75.yml deleted file mode 100644 index 71332023d9..0000000000 --- a/.buildkite/jobs/pipeline.ios_rn_75.yml +++ /dev/null @@ -1,10 +0,0 @@ - - label: ":ios::detox: RN .75 + iOS: Tests app" - command: - - "nvm install" - - "./scripts/ci.ios.sh" - env: - REACT_NATIVE_VERSION: 0.75.4 - artifact_paths: - - "/Users/builder/uibuilder/work/coverage/**/*.lcov" - - "/Users/builder/uibuilder/work/**/allure-report-*.html" - - "/Users/builder/uibuilder/work/artifacts*.tar.gz" diff --git a/.buildkite/pipeline_common.sh b/.buildkite/pipeline_common.sh index 54a62690cd..907bfad145 100755 --- a/.buildkite/pipeline_common.sh +++ b/.buildkite/pipeline_common.sh @@ -4,10 +4,8 @@ echo "steps:" cat .buildkite/jobs/pipeline.ios_rn_76_new_arch.yml cat .buildkite/jobs/pipeline.ios_rn_76.yml -cat .buildkite/jobs/pipeline.ios_rn_75.yml cat .buildkite/jobs/pipeline.ios_rn_73.yml cat .buildkite/jobs/pipeline.ios_demo_app_rn_76_new_arch.yml -cat .buildkite/jobs/pipeline.ios_demo_app_rn_76.yml cat .buildkite/jobs/pipeline.android_rn_76.yml cat .buildkite/jobs/pipeline.android_rn_75.yml cat .buildkite/jobs/pipeline.android_rn_73.yml From 0b9b92f281bcfbe0efa4eacaf746457389202161 Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Mon, 6 Jan 2025 16:06:03 +0200 Subject: [PATCH 51/51] test: change skip method to skip only on iOS. --- detox/test/e2e/17.datePicker.test.js | 2 +- detox/test/e2e/17.picker.test.js | 2 +- detox/test/e2e/copilot/09.copilot.datepicker.test.js | 2 +- detox/test/e2e/utils/custom-describes.js | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/detox/test/e2e/17.datePicker.test.js b/detox/test/e2e/17.datePicker.test.js index f8bab7608a..2f59ae6097 100644 --- a/detox/test/e2e/17.datePicker.test.js +++ b/detox/test/e2e/17.datePicker.test.js @@ -1,6 +1,6 @@ const jestExpect = require('expect').default; -describe.skipIfNewArch('DatePicker', () => { +describe.skipIfNewArchOnIOS('DatePicker', () => { describe.each([ ['ios', 'compact', 0], ['ios', 'inline', 1], diff --git a/detox/test/e2e/17.picker.test.js b/detox/test/e2e/17.picker.test.js index a2cd329b39..258d448ebf 100644 --- a/detox/test/e2e/17.picker.test.js +++ b/detox/test/e2e/17.picker.test.js @@ -1,4 +1,4 @@ -describe.skipIfNewArch(":ios: Picker", () => { +describe.skipIfNewArchOnIOS(":ios: Picker", () => { beforeEach(async () => { await device.reloadReactNative(); await element(by.text("Picker")).tap(); diff --git a/detox/test/e2e/copilot/09.copilot.datepicker.test.js b/detox/test/e2e/copilot/09.copilot.datepicker.test.js index 6d12f16d05..afba3912e0 100644 --- a/detox/test/e2e/copilot/09.copilot.datepicker.test.js +++ b/detox/test/e2e/copilot/09.copilot.datepicker.test.js @@ -1,6 +1,6 @@ const { default: jestExpect } = require('expect'); -describe.skipIfNewArch('DatePicker', () => { +describe.skipIfNewArchOnIOS('DatePicker', () => { describe.forCopilot('Copilot', () => { beforeEach(async () => { await copilot.perform( diff --git a/detox/test/e2e/utils/custom-describes.js b/detox/test/e2e/utils/custom-describes.js index a42898101b..ee0013e348 100644 --- a/detox/test/e2e/utils/custom-describes.js +++ b/detox/test/e2e/utils/custom-describes.js @@ -6,9 +6,9 @@ describe.skipIfCI = (title, fn) => { return isCI ? describe.skip(title, fn) : describe(title, fn); }; -describe.skipIfNewArch = (title, fn) => { +describe.skipIfNewArchOnIOS = (title, fn) => { const isNewArch = process.env.RCT_NEW_ARCH_ENABLED === '1'; - if (isNewArch) { + if (isNewArch && device.getPlatform() === 'ios') { console.warn('Skipping tests for new architecture, as there are issues related to the new architecture.'); return describe.skip(title, fn); }