diff --git a/.buildkite/jobs/pipeline.ios_demo_app_rn_76.yml b/.buildkite/jobs/pipeline.ios_demo_app_rn_76_new_arch.yml similarity index 75% rename from .buildkite/jobs/pipeline.ios_demo_app_rn_76.yml rename to .buildkite/jobs/pipeline.ios_demo_app_rn_76_new_arch.yml index aff5caf84a..0a3b3d6f98 100644 --- a/.buildkite/jobs/pipeline.ios_demo_app_rn_76.yml +++ b/.buildkite/jobs/pipeline.ios_demo_app_rn_76_new_arch.yml @@ -1,9 +1,10 @@ - - label: ":ios::react: RN .76 + iOS: Demo app" + - 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_75.yml b/.buildkite/jobs/pipeline.ios_rn_76_new_arch.yml similarity index 67% rename from .buildkite/jobs/pipeline.ios_rn_75.yml rename to .buildkite/jobs/pipeline.ios_rn_76_new_arch.yml index 71332023d9..5ff264ed17 100644 --- a/.buildkite/jobs/pipeline.ios_rn_75.yml +++ b/.buildkite/jobs/pipeline.ios_rn_76_new_arch.yml @@ -1,9 +1,10 @@ - - label: ":ios::detox: RN .75 + iOS: Tests app" + - label: ":new::ios::detox: RN .76 + New Arch + iOS: Tests app" command: - "nvm install" - "./scripts/ci.ios.sh" env: - REACT_NATIVE_VERSION: 0.75.4 + 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" diff --git a/.buildkite/pipeline_common.sh b/.buildkite/pipeline_common.sh index 4afa7835b7..907bfad145 100755 --- a/.buildkite/pipeline_common.sh +++ b/.buildkite/pipeline_common.sh @@ -2,10 +2,10 @@ 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.yml +cat .buildkite/jobs/pipeline.ios_demo_app_rn_76_new_arch.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 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 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!))" : "") } 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 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 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 diff --git a/detox/test/detox_copilot_cache.json b/detox/test/detox_copilot_cache.json index 531ac66dfb..18461248f4 100644 --- a/detox/test/detox_copilot_cache.json +++ b/detox/test/detox_copilot_cache.json @@ -66,5 +66,56 @@ "{\"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\")", + "{\"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 diff --git a/detox/test/e2e/17.datePicker.test.js b/detox/test/e2e/17.datePicker.test.js index f664941e67..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('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 14e0f3a852..258d448ebf 100644 --- a/detox/test/e2e/17.picker.test.js +++ b/detox/test/e2e/17.picker.test.js @@ -1,4 +1,4 @@ -describe(":ios: Picker", () => { +describe.skipIfNewArchOnIOS(":ios: Picker", () => { beforeEach(async () => { await device.reloadReactNative(); await element(by.text("Picker")).tap(); 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 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.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-web-view.71.ios.txt b/detox/test/e2e/assets/view-hierarchy-web-view.76.ios.txt similarity index 55% rename from detox/test/e2e/assets/view-hierarchy-web-view.71.ios.txt rename to detox/test/e2e/assets/view-hierarchy-web-view.76.ios.txt index cdf809ae6d..135ffe0a28 100644 --- a/detox/test/e2e/assets/view-hierarchy-web-view.71.ios.txt +++ b/detox/test/e2e/assets/view-hierarchy-web-view.76.ios.txt @@ -1,35 +1,34 @@ - + - - - - - - - + + + + + - - - - - + + + + + + + - - - - + + +

First Webview

Form

@@ -38,18 +37,21 @@
+

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.

+ ]]> -
-
-
+ +
@@ -59,6 +61,5 @@
- -
-
+ + \ No newline at end of file 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-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-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.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.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 + + 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.76.ios.txt similarity index 51% rename from detox/test/e2e/assets/view-hierarchy-without-test-id-injection.71.ios.txt rename to detox/test/e2e/assets/view-hierarchy-without-test-id-injection.76.ios.txt index 47f5778eaa..03777012f2 100644 --- 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.76.ios.txt @@ -1,20 +1,18 @@ - + - - - - - - - - + + + + + + @@ -22,6 +20,5 @@ - - - + + \ No newline at end of file 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 ad35350efd..afba3912e0 100644 --- a/detox/test/e2e/copilot/09.copilot.datepicker.test.js +++ b/detox/test/e2e/copilot/09.copilot.datepicker.test.js @@ -1,58 +1,59 @@ -const { describeForCopilotEnv } = 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 () => { +describe.skipIfNewArchOnIOS('DatePicker', () => { + describe.forCopilot('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/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/detox.config.js b/detox/test/e2e/detox.config.js index d2c8fde741..aa105e2969 100644 --- a/detox/test/e2e/detox.config.js +++ b/detox/test/e2e/detox.config.js @@ -52,8 +52,8 @@ const config = { 'ios.debug': { 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', + 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,8 +61,8 @@ const config = { 'ios.release': { 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', + 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', }, 'android.debug': { 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 d44f4872a4..ee0013e348 100644 --- a/detox/test/e2e/utils/custom-describes.js +++ b/detox/test/e2e/utils/custom-describes.js @@ -1,19 +1,32 @@ const axios = require('axios'); const PromptHandler = require("./PromptHandler"); -const describeOrDescribeSkip = process.env.CI === 'true' ? describe.skip : describe; +describe.skipIfCI = (title, fn) => { + const isCI = process.env.CI === 'true'; + return isCI ? describe.skip(title, fn) : describe(title, fn); +}; + +describe.skipIfNewArchOnIOS = (title, fn) => { + const isNewArch = process.env.RCT_NEW_ARCH_ENABLED === '1'; + 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); + } + return describe(title, fn); +}; -describeForCopilotEnv = (description, fn) => { - describeOrDescribeSkip(':ios: Copilot', () => { +describe.forCopilot = (description, fn) => { + return describe.skipIfCI(':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 { await copilot.init(new PromptHandler()); } catch (error) { if (error.message.includes('Copilot has already been initialized')) { + // Ignore already initialized error } else { throw error; } @@ -25,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; @@ -34,7 +47,3 @@ checkVpnStatus = async () => { return false; } }; - -module.exports = { - describeForCopilotEnv -}; diff --git a/detox/test/ios/.xcode.env b/detox/test/ios/.xcode.env index ce9964f6b7..3d5782c715 100644 --- a/detox/test/ios/.xcode.env +++ b/detox/test/ios/.xcode.env @@ -1,11 +1,11 @@ # 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) +# 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/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 new file mode 100644 index 0000000000..b4d7740c79 --- /dev/null +++ b/detox/test/ios/AppDelegate Extensions/AppDelegate+Notifications.swift @@ -0,0 +1,30 @@ +// +// 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 + showOverlayMessage(withMessage: title) + } + + func userNotificationCenter(_ center: UNUserNotificationCenter, + didReceive response: UNNotificationResponse, + withCompletionHandler completionHandler: @escaping () -> Void) { + + let title = response.notification.request.content.title + showOverlayMessage(withMessage: title) + + completionHandler() + } +} 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..fdd58fb886 --- /dev/null +++ b/detox/test/ios/AppDelegate Extensions/AppDelegate+OverlayView.swift @@ -0,0 +1,56 @@ +// +// AppDelegate+OverlayView.swift (example) +// Created by Asaf Korem (Wix.com) on 2024. +// + +import UIKit +import React + +extension AppDelegate { + private var overlayStackView: UIStackView? { + guard + let rootView = window?.rootViewController?.view as? RCTRootView, + let contentView = rootView.value(forKey: "contentView") as? UIView + else { + return nil + } + + 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([ + stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), + stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), + stackView.topAnchor.constraint(equalTo: contentView.safeAreaLayoutGuide.topAnchor) + ]) + + 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/AppDelegate.swift b/detox/test/ios/AppDelegate.swift new file mode 100644 index 0000000000..34a9fcea7a --- /dev/null +++ b/detox/test/ios/AppDelegate.swift @@ -0,0 +1,85 @@ +import UIKit +import React + +// 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() + setupApplicationStateObservers() + setupShakeDetection() + + 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) + } + + private func setupShakeDetection() { + UIViewController.swizzleMotionEnded() + } +} + +// 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/Podfile b/detox/test/ios/Podfile index 030471de41..ceeda69c06 100644 --- a/detox/test/ios/Podfile +++ b/detox/test/ios/Podfile @@ -1,23 +1,19 @@ -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 +if ENV['RCT_NEW_ARCH_ENABLED'] == '1' + puts 'React Native new arch enabled (RCT_NEW_ARCH_ENABLED = 1)' +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 @@ -48,75 +44,28 @@ setup_permissions([ 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], + :app_path => "#{Pod::Config.instance.installation_root}/.." + ) - 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 + # Shared pods + pod 'react-native-slider', :path => '../node_modules/@react-native-community/slider' end target 'example' do shared_pods - pod 'react-native-slider', :path => '../node_modules/@react-native-community/slider' end -target 'example_ci' do +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 + config[:reactNativePath], + :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/ReactModules/NativeModule.swift b/detox/test/ios/ReactModules/NativeModule.swift new file mode 100644 index 0000000000..40e2d8543b --- /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..012ee712f4 --- /dev/null +++ b/detox/test/ios/ReactModules/ShakeEventEmitter.swift @@ -0,0 +1,35 @@ +// +// ShakeEventEmitter.swift (example) +// Created by Asaf Korem (Wix.com) on 2024. +// + +import Foundation +import React + +@objc(ShakeEventEmitter) +class ShakeEventEmitter: RCTEventEmitter { + + 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/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/UI/OverlayMessageView.swift b/detox/test/ios/UI/OverlayMessageView.swift new file mode 100644 index 0000000000..0b1298c3a8 --- /dev/null +++ b/detox/test/ios/UI/OverlayMessageView.swift @@ -0,0 +1,71 @@ +// +// OverlayMessageView.swift (example) +// Created by Asaf Korem (Wix.com) on 2024. +// + +import UIKit + +class OverlayMessageView: UIView { + + private let messageLabel: 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 + }() + + 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) + + messageLabel.text = message + + addSubview(messageLabel) + 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), + 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: 2.0, repeats: false) { [weak self] _ in + self?.removeFromSuperview() + } + } + + @objc private func didTapClose() { + removeFromSuperview() + } +} 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-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 95434ed05e..8c779b3a1c 100644 --- a/detox/test/ios/example.xcodeproj/project.pbxproj +++ b/detox/test/ios/example.xcodeproj/project.pbxproj @@ -3,119 +3,95 @@ archiveVersion = 1; classes = { }; - objectVersion = 51; + objectVersion = 54; 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 */; }; + 213E1077F464B28612297B3E /* libPods-example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 467D00885FA51E1E9B0E8063 /* 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 */; }; + 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 */; }; + 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 */; }; + 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 */; }; + 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 */ - 39A0778B1E5450E700A53A07 /* PBXContainerItemProxy */ = { + 609DDB842D10C20800028574 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 39B0445B1DAED76400431EC5 /* Detox.xcodeproj */; + containerPortal = 609DDB772D10C20700028574 /* 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; + remoteGlobalIDString = 394767971DBF985400D72256; remoteInfo = Detox; }; - 39B71FCE24643AEA00CC9A88 /* PBXContainerItemProxy */ = { + 609DDB862D10C20800028574 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 13B07F861A680F5B00A75B9A; - remoteInfo = example; + containerPortal = 609DDB772D10C20700028574 /* Detox.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 3928EFA51E47404900C19B6E; + remoteInfo = DetoxUserNotificationTests; }; /* 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 = ""; }; + 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 = ""; }; + 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 = ""; }; + 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 = ""; }; + 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 PBXFrameworksBuildPhase section */ @@ -123,25 +99,16 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 3953CC0D229AA78F005DD98C /* JavaScriptCore.framework in Frameworks */, - 39A34C791E30F3A000BEBB59 /* Detox.framework in Frameworks */, - A6246A8AF2D035D89BA61CA6 /* libPods-example.a in Frameworks */, + 609DDB892D10C21800028574 /* Detox.framework in Frameworks */, + 213E1077F464B28612297B3E /* 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 */ = { + 60493BE12D10E7E4002853A0 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + CA25A6405AF58BA742F7FD47 /* libPods-example-ci.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -151,120 +118,115 @@ 13B07FAE1A68108700A75B9A /* example */ = { isa = PBXGroup; children = ( - 6027065D2B1DF4DD00CD52CF /* example.entitlements */, - 008F07F21AC5B25A0029DE68 /* main.jsbundle */, - 13B07FAF1A68108700A75B9A /* AppDelegate.h */, - 13B07FB01A68108700A75B9A /* AppDelegate.m */, + 6088689A2D1A9F070070D199 /* AppDelegate.swift */, + 60E207952D21B0B400E6DBD2 /* AppDelegate Extensions */, + 60493BD72D10D967002853A0 /* example.entitlements */, + 6088689B2D1A9F070070D199 /* example-Bridging-Header.h */, + 6088689C2D1A9F070070D199 /* example-ci-Bridging-Header.h */, 13B07FB51A68108700A75B9A /* Images.xcassets */, 13B07FB61A68108700A75B9A /* Info.plist */, - 13B07FB11A68108700A75B9A /* LaunchScreen.xib */, - 13B07FB71A68108700A75B9A /* main.m */, - ADB9A7A225C82082005236EE /* ReactModules */, - ADB9A79F25C81FF7005236EE /* UI */, - 75C5679B8378704E8D3D9C66 /* PrivacyInfo.xcprivacy */, + 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */, + 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */, + 6088689F2D1A9F070070D199 /* ReactModules */, + 608868B12D1AA1FB0070D199 /* UI */, ); name = example; sourceTree = ""; }; - 160A423EA1148BC845CDF95E /* Pods */ = { + 2D16E6871FA4F8E400B85C8A /* Frameworks */ = { 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 */, + ED297162215061F000B7C4FE /* JavaScriptCore.framework */, + 19F6CBCC0A4E27FBF8BF4A61 /* libPods-example-exampleTests.a */, + 1CB0FE045763B5363B2E7054 /* libPods-example-ci.a */, + 467D00885FA51E1E9B0E8063 /* libPods-example.a */, ); - path = Pods; + name = Frameworks; sourceTree = ""; }; - 39B0445C1DAED76400431EC5 /* Products */ = { + 6088689F2D1A9F070070D199 /* ReactModules */ = { isa = PBXGroup; children = ( - 39B044621DAED76400431EC5 /* Detox.framework */, - 39A0778C1E5450E700A53A07 /* DetoxUserNotificationTests.xctest */, + 608868C42D1C3D0E0070D199 /* ReactModulesBridge.m */, + 6088689D2D1A9F070070D199 /* NativeModule.swift */, + 6088689E2D1A9F070070D199 /* ShakeEventEmitter.swift */, ); - name = Products; + path = ReactModules; sourceTree = ""; }; - 39B71FCA24643AEA00CC9A88 /* exampleUITests */ = { + 608868B12D1AA1FB0070D199 /* UI */ = { isa = PBXGroup; children = ( - 39B71FCB24643AEA00CC9A88 /* exampleUITests.m */, - 39B71FCD24643AEA00CC9A88 /* Info.plist */, + 60A403A82D21EADE004344C3 /* UIViewController+Shake.swift */, + 608868B02D1AA1FB0070D199 /* CustomKeyboardDelegate.swift */, + 608868AA2D1AA19B0070D199 /* NativeScreenManager.swift */, + 608868D12D1D696E0070D199 /* OverlayMessageView.swift */, ); - path = exampleUITests; + path = UI; sourceTree = ""; }; - 39FC9CFD202899D10033C11A /* JS */ = { + 609DDB802D10C20800028574 /* Products */ = { isa = PBXGroup; children = ( - 39FC9D23202899F90033C11A /* e2e */, - 39FC9D24202899F90033C11A /* src */, + 609DDB852D10C20800028574 /* Detox.framework */, + 609DDB872D10C20800028574 /* DetoxUserNotificationTests.xctest */, ); - 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; + name = Products; sourceTree = ""; - tabWidth = 4; }; - 83CBBA001A601CBA00E9B192 /* Products */ = { + 60E207952D21B0B400E6DBD2 /* AppDelegate Extensions */ = { isa = PBXGroup; children = ( - 13B07F961A680F5B00A75B9A /* example.app */, - 399B4E061ED587120098D2AC /* example.app */, - 39B71FC924643AEA00CC9A88 /* exampleUITests.xctest */, + 60E207902D21B0B400E6DBD2 /* AppDelegate+ApplicationState.swift */, + 60E207912D21B0B400E6DBD2 /* AppDelegate+Linking.swift */, + 60E207922D21B0B400E6DBD2 /* AppDelegate+Notifications.swift */, + 60E207932D21B0B400E6DBD2 /* AppDelegate+OverlayView.swift */, ); - name = Products; + path = "AppDelegate Extensions"; sourceTree = ""; }; - ADB9A79F25C81FF7005236EE /* UI */ = { + 832341AE1AAA6A7D00B99B32 /* Libraries */ = { isa = PBXGroup; children = ( - ADB9A7A125C82033005236EE /* ViewControllers */, ); - name = UI; + name = Libraries; sourceTree = ""; }; - ADB9A7A125C82033005236EE /* ViewControllers */ = { + 83CBB9F61A601CBA00E9B192 = { isa = PBXGroup; children = ( - 4FB97BDD2636490800B7B57C /* CustomKeyboardViewController.h */, - 4FB97BDE2636490800B7B57C /* CustomKeyboardViewController.m */, + 609DDB772D10C20700028574 /* Detox.xcodeproj */, + 13B07FAE1A68108700A75B9A /* example */, + 832341AE1AAA6A7D00B99B32 /* Libraries */, + 83CBBA001A601CBA00E9B192 /* Products */, + 2D16E6871FA4F8E400B85C8A /* Frameworks */, + BBD78D7AC51CEA395F1C20DB /* Pods */, ); - name = ViewControllers; + indentWidth = 2; sourceTree = ""; + tabWidth = 2; + usesTabs = 0; }; - ADB9A7A225C82082005236EE /* ReactModules */ = { + 83CBBA001A601CBA00E9B192 /* Products */ = { isa = PBXGroup; children = ( - CC17D3301D60A24300267B0C /* NativeModule.h */, - CC17D3311D60A24300267B0C /* NativeModule.m */, + 13B07F961A680F5B00A75B9A /* example.app */, + 60493BEE2D10E7E4002853A0 /* example-ci.app */, ); - name = ReactModules; + name = Products; sourceTree = ""; }; - CCFA7DE41D11D22600E15EDF /* Frameworks */ = { + BBD78D7AC51CEA395F1C20DB /* Pods */ = { isa = PBXGroup; children = ( - 39ED920022916437005EDB56 /* JavaScriptCore.framework */, - 86172A40F266BB07F101EB18 /* libPods-example.a */, - 6CEAF92A5314EC6824AB3DE3 /* libPods-example_ci.a */, + 3B4392A12AC88292D35C810B /* Pods-example.debug.xcconfig */, + 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 */, ); - name = Frameworks; + path = Pods; sourceTree = ""; }; /* End PBXGroup section */ @@ -274,80 +236,62 @@ isa = PBXNativeTarget; buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "example" */; buildPhases = ( - DC59FF1BA0BA7FA633F256AA /* [CP] Check Pods Manifest.lock */, + C38B50BA6285516D6DCD4F65 /* [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 */, + 00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */, + E235C05ADACE081382539298 /* [CP] Copy Pods Resources */, ); buildRules = ( ); dependencies = ( - 39B044641DAED76800431EC5 /* PBXTargetDependency */, ); name = example; - productName = "Hello World"; + productName = example; productReference = 13B07F961A680F5B00A75B9A /* example.app */; productType = "com.apple.product-type.application"; }; - 399B4DE81ED587120098D2AC /* example_ci */ = { + 60493BDB2D10E7E4002853A0 /* example-ci */ = { isa = PBXNativeTarget; - buildConfigurationList = 399B4E031ED587120098D2AC /* Build configuration list for PBXNativeTarget "example_ci" */; + buildConfigurationList = 60493BEB2D10E7E4002853A0 /* 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 */, + 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 = "Hello World"; - productReference = 399B4E061ED587120098D2AC /* example.app */; + name = "example-ci"; + productName = example; + productReference = 60493BEE2D10E7E4002853A0 /* example-ci.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; + LastUpgradeCheck = 1210; TargetAttributes = { - 39B71FC824643AEA00CC9A88 = { - CreatedOnToolsVersion = 11.5; - TestTargetID = 13B07F861A680F5B00A75B9A; + 13B07F861A680F5B00A75B9A = { + LastSwiftMigration = 1600; + }; + 60493BDB2D10E7E4002853A0 = { + LastSwiftMigration = 1600; }; }; }; buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "example" */; - compatibilityVersion = "Xcode 10.0"; + compatibilityVersion = "Xcode 12.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( @@ -359,32 +303,31 @@ projectDirPath = ""; projectReferences = ( { - ProductGroup = 39B0445C1DAED76400431EC5 /* Products */; - ProjectRef = 39B0445B1DAED76400431EC5 /* Detox.xcodeproj */; + ProductGroup = 609DDB802D10C20800028574 /* Products */; + ProjectRef = 609DDB772D10C20700028574 /* Detox.xcodeproj */; }, ); projectRoot = ""; targets = ( 13B07F861A680F5B00A75B9A /* example */, - 399B4DE81ED587120098D2AC /* example_ci */, - 39B71FC824643AEA00CC9A88 /* exampleUITests */, + 60493BDB2D10E7E4002853A0 /* example-ci */, ); }; /* End PBXProject section */ /* Begin PBXReferenceProxy section */ - 39A0778C1E5450E700A53A07 /* DetoxUserNotificationTests.xctest */ = { + 609DDB852D10C20800028574 /* Detox.framework */ = { isa = PBXReferenceProxy; - fileType = wrapper.cfbundle; - path = DetoxUserNotificationTests.xctest; - remoteRef = 39A0778B1E5450E700A53A07 /* PBXContainerItemProxy */; + fileType = wrapper.framework; + path = Detox.framework; + remoteRef = 609DDB842D10C20800028574 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 39B044621DAED76400431EC5 /* Detox.framework */ = { + 609DDB872D10C20800028574 /* DetoxUserNotificationTests.xctest */ = { isa = PBXReferenceProxy; - fileType = wrapper.framework; - path = Detox.framework; - remoteRef = 39B044611DAED76400431EC5 /* PBXContainerItemProxy */; + fileType = wrapper.cfbundle; + path = DetoxUserNotificationTests.xctest; + remoteRef = 609DDB862D10C20800028574 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXReferenceProxy section */ @@ -394,26 +337,19 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */, 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 */, + 90CCD0BA1322566EB1285DC2 /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; - 39B71FC724643AEA00CC9A88 /* Resources */ = { + 60493BE42D10E7E4002853A0 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 60493BE52D10E7E4002853A0 /* LaunchScreen.storyboard in Resources */, + 60493BE62D10E7E4002853A0 /* Images.xcassets in Resources */, + 60493BE72D10E7E4002853A0 /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -426,29 +362,34 @@ files = ( ); inputPaths = ( + "$(SRCROOT)/.xcode.env.local", + "$(SRCROOT)/.xcode.env", ); 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"; + 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"; }; - 399B4E001ED587120098D2AC /* Bundle React Native code and images */ = { + 00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); - inputPaths = ( + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); - name = "Bundle React Native code and images"; - outputPaths = ( + 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 = "../node_modules/react-native/scripts/react-native-xcode.sh\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks.sh\"\n"; + showEnvVarsInLog = 0; }; - 5D18F0A8E13DBF942B818AE3 /* [CP] Check Pods Manifest.lock */ = { + 60493BDC2D10E7E4002853A0 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -463,48 +404,64 @@ outputFileListPaths = ( ); outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-example_ci-checkManifestLockResult.txt", + "$(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 */ = { + 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-resources-${CONFIGURATION}-input-files.xcfilelist", + "${PODS_ROOT}/Target Support Files/Pods-example-ci/Pods-example-ci-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); - name = "[CP] Copy Pods Resources"; + name = "[CP] Embed Pods Frameworks"; 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-frameworks-${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-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - 9C024938B44AFDD355A0B654 /* [CP] Copy Pods Resources */ = { + 60493BEA2D10E7E4002853A0 /* [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", + "${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", + "${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"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example-ci/Pods-example-ci-resources.sh\"\n"; showEnvVarsInLog = 0; }; - DC59FF1BA0BA7FA633F256AA /* [CP] Check Pods Manifest.lock */ = { + C38B50BA6285516D6DCD4F65 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -526,87 +483,83 @@ 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; + E235C05ADACE081382539298 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( - CC17D3321D60A24300267B0C /* NativeModule.m in Sources */, - 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */, - 4FB97BDF2636490900B7B57C /* CustomKeyboardViewController.m in Sources */, - 13B07FC11A68108700A75B9A /* main.m in Sources */, + ); + 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; }; - 399B4DEB1ED587120098D2AC /* Sources */ = { +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 13B07F871A680F5B00A75B9A /* 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 */, + 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 */, + 608868A32D1A9F070070D199 /* NativeModule.swift in Sources */, + 608868A42D1A9F070070D199 /* ShakeEventEmitter.swift in Sources */, + 608868AB2D1AA19B0070D199 /* NativeScreenManager.swift in Sources */, + 608868C52D1C3D130070D199 /* ReactModulesBridge.m in Sources */, + 608868D32D1D696E0070D199 /* OverlayMessageView.swift in Sources */, + 608868A52D1A9F070070D199 /* AppDelegate.swift in Sources */, + 608868B22D1AA1FB0070D199 /* CustomKeyboardDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; - 39B71FC524643AEA00CC9A88 /* Sources */ = { + 60493BDD2D10E7E4002853A0 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 39B71FCC24643AEA00CC9A88 /* exampleUITests.m in Sources */, + 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 */, + 608868A02D1A9F070070D199 /* NativeModule.swift in Sources */, + 608868A12D1A9F070070D199 /* ShakeEventEmitter.swift in Sources */, + 608868AC2D1AA19B0070D199 /* NativeScreenManager.swift in Sources */, + 608868C62D1C3D130070D199 /* ReactModulesBridge.m in Sources */, + 608868D22D1D696E0070D199 /* OverlayMessageView.swift in Sources */, + 608868A22D1A9F070070D199 /* AppDelegate.swift in Sources */, + 608868B32D1AA1FB0070D199 /* CustomKeyboardDelegate.swift 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 */; + baseConfigurationReference = 3B4392A12AC88292D35C810B /* 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; + CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = example/example.entitlements; - DEAD_CODE_STRIPPING = NO; - DEVELOPMENT_TEAM = ""; - GCC_PREPROCESSOR_DEFINITIONS = ( - "EXTERNAL_LOGGER=1", - "$(inherited)", - ); + CURRENT_PROJECT_VERSION = 1; + ENABLE_BITCODE = NO; INFOPLIST_FILE = example/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.1; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); + MARKETING_VERSION = 1.0; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -614,29 +567,28 @@ ); PRODUCT_BUNDLE_IDENTIFIER = "com.wix.detox-example"; PRODUCT_NAME = example; - TARGETED_DEVICE_FAMILY = "1,2"; + SWIFT_OBJC_BRIDGING_HEADER = "example-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; }; name = Debug; }; 13B07F951A680F5B00A75B9A /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = CC2D09EF9818766C1EE13DEE /* Pods-example.release.xcconfig */; + baseConfigurationReference = 5709B34CF0A7D63546082F79 /* 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; + CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = example/example.entitlements; - DEVELOPMENT_TEAM = ""; - GCC_PREPROCESSOR_DEFINITIONS = ( - "EXTERNAL_LOGGER=1", - "$(inherited)", - ); + CURRENT_PROJECT_VERSION = 1; INFOPLIST_FILE = example/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.1; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); + MARKETING_VERSION = 1.0; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -644,113 +596,67 @@ ); PRODUCT_BUNDLE_IDENTIFIER = "com.wix.detox-example"; PRODUCT_NAME = example; - TARGETED_DEVICE_FAMILY = "1,2"; + SWIFT_OBJC_BRIDGING_HEADER = "example-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; }; name = Release; }; - 399B4E041ED587120098D2AC /* Debug */ = { + 60493BEC2D10E7E4002853A0 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = A398C1445E8C769F5903CD2D /* Pods-example_ci.debug.xcconfig */; + baseConfigurationReference = 4D1FDAD5197836A927FA2D03 /* 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; + 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++", - "-Wl,-U,___dtx_send_external_log", - "$(inherited)", ); PRODUCT_BUNDLE_IDENTIFIER = "com.wix.detox-example"; - PRODUCT_NAME = 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"; }; name = Debug; }; - 399B4E051ED587120098D2AC /* Release */ = { + 60493BED2D10E7E4002853A0 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = F08DEFD1BF85073C69F1A95E /* Pods-example_ci.release.xcconfig */; + baseConfigurationReference = 80C930B7D5D6C25DA27372A5 /* 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; + 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++", - "-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; + SWIFT_OBJC_BRIDGING_HEADER = "example-ci-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; }; name = Release; }; @@ -758,7 +664,6 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - CC = ""; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "c++20"; CLANG_CXX_LIBRARY = "libc++"; @@ -786,8 +691,6 @@ 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*]" = ""; @@ -798,7 +701,6 @@ 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; @@ -807,18 +709,31 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; - LD = ""; - LDPLUSPLUS = ""; + 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_CFLAGS = "$(inherited)"; - OTHER_CPLUSPLUSFLAGS = "$(inherited)"; - OTHER_LDFLAGS = "$(inherited)"; + OTHER_CPLUSPLUSFLAGS = ( + "$(OTHER_CFLAGS)", + "-DFOLLY_NO_CONFIG", + "-DFOLLY_MOBILE=1", + "-DFOLLY_USE_LIBCPP=1", + "-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 = false; + USE_HERMES = true; }; name = Debug; }; @@ -826,7 +741,6 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - CC = ""; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "c++20"; CLANG_CXX_LIBRARY = "libc++"; @@ -853,34 +767,41 @@ 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; + 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_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 = ""; + 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_CFLAGS = "$(inherited)"; - OTHER_CPLUSPLUSFLAGS = "$(inherited)"; - OTHER_LDFLAGS = "$(inherited)"; + OTHER_CPLUSPLUSFLAGS = ( + "$(OTHER_CFLAGS)", + "-DFOLLY_NO_CONFIG", + "-DFOLLY_MOBILE=1", + "-DFOLLY_USE_LIBCPP=1", + "-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 = false; + USE_HERMES = true; VALIDATE_PRODUCT = YES; }; name = Release; @@ -897,20 +818,11 @@ 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" */ = { + 60493BEB2D10E7E4002853A0 /* Build configuration list for PBXNativeTarget "example-ci" */ = { isa = XCConfigurationList; buildConfigurations = ( - 39B71FD024643AEA00CC9A88 /* Debug */, - 39B71FD124643AEA00CC9A88 /* Release */, + 60493BEC2D10E7E4002853A0 /* Debug */, + 60493BED2D10E7E4002853A0 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; diff --git a/detox/test/ios/example.xcodeproj/xcshareddata/xcschemes/example.xcscheme b/detox/test/ios/example.xcodeproj/xcshareddata/xcschemes/example.xcscheme index 996c109aef..1c65e14823 100644 --- a/detox/test/ios/example.xcodeproj/xcshareddata/xcschemes/example.xcscheme +++ b/detox/test/ios/example.xcodeproj/xcshareddata/xcschemes/example.xcscheme @@ -1,25 +1,11 @@ - - - - @@ -58,7 +44,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - disableMainThreadChecker = "YES" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" @@ -149,16 +134,26 @@ isEnabled = "NO"> - - + + + + + - - + isEnabled = "NO"> + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 - - - diff --git a/detox/test/ios/example/AppDelegate.h b/detox/test/ios/example/AppDelegate.h deleted file mode 100644 index 9231535b24..0000000000 --- a/detox/test/ios/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/example/AppDelegate.m b/detox/test/ios/example/AppDelegate.m deleted file mode 100644 index b4bc4f1de0..0000000000 --- a/detox/test/ios/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/example/Base.lproj/LaunchScreen.xib b/detox/test/ios/example/Base.lproj/LaunchScreen.xib deleted file mode 100644 index 9e04807a83..0000000000 --- a/detox/test/ios/example/Base.lproj/LaunchScreen.xib +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/detox/test/ios/example/CustomKeyboardViewController.h b/detox/test/ios/example/CustomKeyboardViewController.h deleted file mode 100644 index 8ee742fb12..0000000000 --- a/detox/test/ios/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/example/CustomKeyboardViewController.m b/detox/test/ios/example/CustomKeyboardViewController.m deleted file mode 100644 index 4dc90129f3..0000000000 --- a/detox/test/ios/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/example/Images.xcassets/AppIcon.appiconset/Contents.json b/detox/test/ios/example/Images.xcassets/AppIcon.appiconset/Contents.json index 19882d568a..81213230de 100644 --- a/detox/test/ios/example/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/detox/test/ios/example/Images.xcassets/AppIcon.appiconset/Contents.json @@ -2,52 +2,52 @@ "images" : [ { "idiom" : "iphone", - "size" : "20x20", - "scale" : "2x" + "scale" : "2x", + "size" : "20x20" }, { "idiom" : "iphone", - "size" : "20x20", - "scale" : "3x" + "scale" : "3x", + "size" : "20x20" }, { "idiom" : "iphone", - "size" : "29x29", - "scale" : "2x" + "scale" : "2x", + "size" : "29x29" }, { "idiom" : "iphone", - "size" : "29x29", - "scale" : "3x" + "scale" : "3x", + "size" : "29x29" }, { "idiom" : "iphone", - "size" : "40x40", - "scale" : "2x" + "scale" : "2x", + "size" : "40x40" }, { "idiom" : "iphone", - "size" : "40x40", - "scale" : "3x" + "scale" : "3x", + "size" : "40x40" }, { "idiom" : "iphone", - "size" : "60x60", - "scale" : "2x" + "scale" : "2x", + "size" : "60x60" }, { "idiom" : "iphone", - "size" : "60x60", - "scale" : "3x" + "scale" : "3x", + "size" : "60x60" }, { "idiom" : "ios-marketing", - "size" : "1024x1024", - "scale" : "1x" + "scale" : "1x", + "size" : "1024x1024" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} 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 index 45242fcbe7..81b3f8f622 100644 --- a/detox/test/ios/example/Info.plist +++ b/detox/test/ios/example/Info.plist @@ -87,7 +87,7 @@ LaunchScreen UIRequiredDeviceCapabilities - armv7 + arm64 UIRequiresFullScreen 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/NativeModule.h b/detox/test/ios/example/NativeModule.h deleted file mode 100644 index 7bde08a0f8..0000000000 --- a/detox/test/ios/example/NativeModule.h +++ /dev/null @@ -1,6 +0,0 @@ -#import -#import - -@interface NativeModule : NSObject - -@end diff --git a/detox/test/ios/example/NativeModule.m b/detox/test/ios/example/NativeModule.m deleted file mode 100644 index 08f07b45fc..0000000000 --- a/detox/test/ios/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/example/main.m b/detox/test/ios/example/main.m deleted file mode 100644 index 389f54704d..0000000000 --- a/detox/test/ios/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/exampleUITests/Info.plist b/detox/test/ios/exampleUITests/Info.plist deleted file mode 100644 index 64d65ca495..0000000000 --- a/detox/test/ios/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/exampleUITests/exampleUITests.m b/detox/test/ios/exampleUITests/exampleUITests.m deleted file mode 100644 index 28f9790cc4..0000000000 --- a/detox/test/ios/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/example_ci.entitlements b/detox/test/ios/example_ci.entitlements deleted file mode 100644 index 21d95c45f3..0000000000 --- a/detox/test/ios/example_ci.entitlements +++ /dev/null @@ -1,8 +0,0 @@ - - - - - com.apple.developer.siri - - - 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