From 5e6f8d49daead57b20f91ecf49ef78edf6f72d3d Mon Sep 17 00:00:00 2001
From: Szymon Kazmierczak <szymonkaz@gmail.com>
Date: Wed, 21 Nov 2018 10:38:38 +0000
Subject: [PATCH] Fix for UITextView notifications not triggered when using
 replaceText

---
 AppFramework/Action/GREYActions.m             | 23 ++++++++++
 .../Sources/Swift/FTRSwiftTests.swift         | 26 +++++++++++
 Tests/TestRig/Sources/TypingViewController.h  |  1 +
 Tests/TestRig/Sources/TypingViewController.m  | 38 +++++++++------
 .../TestRig/Sources/TypingViewController.xib  | 46 +++++++++++--------
 5 files changed, 99 insertions(+), 35 deletions(-)

diff --git a/AppFramework/Action/GREYActions.m b/AppFramework/Action/GREYActions.m
index 3316d6a85..1b1963eaa 100644
--- a/AppFramework/Action/GREYActions.m
+++ b/AppFramework/Action/GREYActions.m
@@ -510,6 +510,8 @@ + (void)grey_setText:(NSString *)text onWebElement:(id)element {
             grey_execute_sync_on_main_thread(^{
               BOOL elementIsUIControl = [element isKindOfClass:[UIControl class]];
               BOOL elementIsUITextField = [element isKindOfClass:[UITextField class]];
+              BOOL elementIsUITextView = [element isKindOfClass:[UITextView class]];
+              
               // Did begin editing notifications.
               if (elementIsUIControl) {
                 [element sendActionsForControlEvents:UIControlEventEditingDidBegin];
@@ -521,6 +523,12 @@ + (void)grey_setText:(NSString *)text onWebElement:(id)element {
                                                   object:element];
                 [NSNotificationCenter.defaultCenter postNotification:notification];
               }
+              
+              if (elementIsUITextView) {
+                if ([((UITextView *)element).delegate respondsToSelector:@selector(textViewDidBeginEditing:)]) {
+                  [((UITextView *)element).delegate textViewDidBeginEditing:((UITextView *)element)];
+                }
+              }
 
               // Actually change the text.
               [element setText:text];
@@ -529,24 +537,39 @@ + (void)grey_setText:(NSString *)text onWebElement:(id)element {
               if (elementIsUIControl) {
                 [element sendActionsForControlEvents:UIControlEventEditingChanged];
               }
+              
               if (elementIsUITextField) {
                 NSNotification *notification =
                     [NSNotification notificationWithName:UITextFieldTextDidChangeNotification
                                                   object:element];
                 [NSNotificationCenter.defaultCenter postNotification:notification];
               }
+              
+              if (elementIsUITextView) {
+                if ([((UITextView *)element).delegate respondsToSelector:@selector(textViewDidChange:)]) {
+                  [((UITextView *)element).delegate textViewDidChange:((UITextView *)element)];
+                }
+              }
 
               // Did end editing notifications.
               if (elementIsUIControl) {
                 [element sendActionsForControlEvents:UIControlEventEditingDidEndOnExit];
                 [element sendActionsForControlEvents:UIControlEventEditingDidEnd];
               }
+              
               if (elementIsUITextField) {
                 NSNotification *notification =
                     [NSNotification notificationWithName:UITextFieldTextDidEndEditingNotification
                                                   object:element];
                 [NSNotificationCenter.defaultCenter postNotification:notification];
               }
+              
+              if (elementIsUITextView) {
+                if ([((UITextView *)element).delegate respondsToSelector:@selector(textViewDidEndEditing:)]) {
+                  [((UITextView *)element).delegate textViewDidEndEditing:((UITextView *)element)];
+                }
+              }
+              
             });
           }
           return YES;
diff --git a/Tests/FunctionalTests/Sources/Swift/FTRSwiftTests.swift b/Tests/FunctionalTests/Sources/Swift/FTRSwiftTests.swift
index 3ce537777..d68f97135 100644
--- a/Tests/FunctionalTests/Sources/Swift/FTRSwiftTests.swift
+++ b/Tests/FunctionalTests/Sources/Swift/FTRSwiftTests.swift
@@ -127,6 +127,32 @@ class FTRSwiftTests: XCTestCase {
       .assert(grey_text("FooBar"))
   }
 
+  func testTypingOnUITextView() {
+    openTestView(named: "Typing Views")
+    let typingField = grey_accessibilityID("TypingTextView")
+    let charCounter = grey_accessibilityID("charCounter")
+    
+    EarlGrey.selectElement(with: typingField)
+      .perform(grey_typeText("Simple"))
+    EarlGrey.selectElement(with: grey_text("Done"))
+      .perform(grey_tap())
+    EarlGrey.selectElement(with: charCounter)
+      .assert(grey_text("6"))
+  }
+  
+  func testReplacingTextOnUITextView() {
+    openTestView(named: "Typing Views")
+    let typingField = grey_accessibilityID("TypingTextView")
+    let charCounter = grey_accessibilityID("charCounter")
+    
+    EarlGrey.selectElement(with: typingField)
+      .perform(grey_replaceText("Simple"))
+    EarlGrey.selectElement(with: grey_text("Done"))
+      .perform(grey_tap())
+    EarlGrey.selectElement(with: charCounter)
+      .assert(grey_text("6"))
+  }
+  
   func testButtonPressWithGREYAllOf() {
     openTestView(named: "Basic Views")
     EarlGrey.selectElement(with: grey_text("Tab 2")).perform(grey_tap())
diff --git a/Tests/TestRig/Sources/TypingViewController.h b/Tests/TestRig/Sources/TypingViewController.h
index c3304dd71..395648fd0 100644
--- a/Tests/TestRig/Sources/TypingViewController.h
+++ b/Tests/TestRig/Sources/TypingViewController.h
@@ -27,6 +27,7 @@
 @property(nonatomic, retain) IBOutlet UITextField *inputAccessoryTextField;
 @property(nonatomic, retain) IBOutlet UIButton *inputButton;
 @property(nonatomic, retain) IBOutlet UITextField *textField;
+@property(nonatomic, retain) IBOutlet UILabel *charCounter;
 @property(nonatomic, retain) IBOutlet UITextField *nonTypingTextField;
 @property(nonatomic, retain) IBOutlet CustomTextView *customTextView;
 @property(nonatomic, retain) UIBarButtonItem *dismissKeyboardButton;
diff --git a/Tests/TestRig/Sources/TypingViewController.m b/Tests/TestRig/Sources/TypingViewController.m
index 5fe67180a..7f1a6baed 100644
--- a/Tests/TestRig/Sources/TypingViewController.m
+++ b/Tests/TestRig/Sources/TypingViewController.m
@@ -54,9 +54,9 @@ - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
   self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
   if (self) {
     _keyboardTypeStringArray = @[
-      @"Default", @"ASCIICapable", @"NumbersAndPunctuation", @"URL", @"NumberPad", @"PhonePad",
-      @"NamePhonePad", @"EmailAddress", @"DecimalPad", @"Twitter", @"WebSearch"
-    ];
+                                 @"Default", @"ASCIICapable", @"NumbersAndPunctuation", @"URL", @"NumberPad", @"PhonePad",
+                                 @"NamePhonePad", @"EmailAddress", @"DecimalPad", @"Twitter", @"WebSearch"
+                                 ];
     NSAssert([_keyboardTypeStringArray count] == kKeyboardTypeCount,
              @"count must be kKeyboardTypeCount");
     _keyboardTypeArray[0] = UIKeyboardTypeDefault;
@@ -76,47 +76,50 @@ - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
 
 - (void)viewDidLoad {
   [super viewDidLoad];
-
+  
   self.dismissKeyboardButton =
-      [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone
-                                                    target:self
-                                                    action:@selector(dismissKeyboard)];
-
+  [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone
+                                                target:self
+                                                action:@selector(dismissKeyboard)];
+  
   self.textField.delegate = self;
   self.textField.isAccessibilityElement = YES;
   self.textField.userInteractionEnabled = YES;
   self.textField.accessibilityIdentifier = @"TypingTextField";
   self.textField.autocorrectionType = UITextAutocorrectionTypeYes;
-
+  
+  self.charCounter.isAccessibilityElement = YES;
+  self.charCounter.accessibilityIdentifier = @"charCounter";
+  
   self.nonTypingTextField.delegate = self;
   self.nonTypingTextField.isAccessibilityElement = YES;
   self.nonTypingTextField.userInteractionEnabled = YES;
   self.nonTypingTextField.accessibilityIdentifier = @"NonTypingTextField";
-
+  
   self.textView.delegate = self;
   self.textView.isAccessibilityElement = YES;
   self.textView.userInteractionEnabled = YES;
   self.textView.accessibilityIdentifier = @"TypingTextView";
   self.textView.autocorrectionType = UITextAutocorrectionTypeYes;
-
+  
   self.inputAccessoryTextField.delegate = self;
   self.inputAccessoryTextField.isAccessibilityElement = YES;
   self.inputAccessoryTextField.userInteractionEnabled = YES;
   self.inputAccessoryTextField.accessibilityIdentifier = @"InputAccessoryTextField";
   self.inputAccessoryTextField.autocorrectionType = UITextAutocorrectionTypeYes;
   [self AddInputAccessoryViewtoKeyboard];
-
+  
   self.inputButton.accessibilityIdentifier = @"Input Button";
   [self.inputButton addTarget:self
                        action:@selector(buttonPressedForTyping)
              forControlEvents:UIControlEventTouchUpInside];
-
+  
   self.keyboardPicker.accessibilityIdentifier = @"KeyboardPicker";
-
+  
   self.customTextView.isAccessibilityElement = YES;
   self.customTextView.userInteractionEnabled = YES;
   self.customTextView.accessibilityIdentifier = @"CustomTextView";
-
+  
   [self.view sendSubviewToBack:_customKeyboardTracker];
   self.customKeyboardTracker.isAccessibilityElement = YES;
   self.customKeyboardTracker.accessibilityIdentifier = @"CustomKeyboardTracker";
@@ -151,6 +154,11 @@ - (void)textViewDidBeginEditing:(UITextView *)textView {
   self.navigationItem.rightBarButtonItem = self.dismissKeyboardButton;
 }
 
+- (void)textViewDidEndEditing:(UITextView *)textView {
+  NSUInteger len = textView.text.length;
+  _charCounter.text = [NSString stringWithFormat: @"%lu", (unsigned long)len];
+}
+
 - (void)dismissKeyboard {
   [self.textView resignFirstResponder];
   self.navigationItem.rightBarButtonItem = nil;
diff --git a/Tests/TestRig/Sources/TypingViewController.xib b/Tests/TestRig/Sources/TypingViewController.xib
index 62f5f6069..47338a0bf 100644
--- a/Tests/TestRig/Sources/TypingViewController.xib
+++ b/Tests/TestRig/Sources/TypingViewController.xib
@@ -1,13 +1,17 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="NO">
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14313.18" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait">
+        <adaptation id="fullscreen"/>
+    </device>
     <dependencies>
         <deployment identifier="iOS"/>
-        <development version="7000" identifier="xcode"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14283.14"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
     </dependencies>
     <objects>
         <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="TypingViewController">
             <connections>
+                <outlet property="charCounter" destination="MP1-3R-IW1" id="DQ1-eR-dg5"/>
                 <outlet property="customKeyboardTracker" destination="bnj-yZ-VR3" id="0ns-aw-ahE"/>
                 <outlet property="customTextView" destination="kf2-uc-vdE" id="XsY-7I-QdM"/>
                 <outlet property="inputAccessoryTextField" destination="Zla-Kl-dQS" id="XJs-DG-mGU"/>
@@ -21,7 +25,7 @@
         </placeholder>
         <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
         <view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT">
-            <rect key="frame" x="0.0" y="0.0" width="320" height="480"/>
+            <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
             <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
             <subviews>
                 <textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Input Accessory View TextField" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="Zla-Kl-dQS" userLabel="Input Accessory Text Field">
@@ -30,7 +34,7 @@
                     <fontDescription key="fontDescription" type="system" pointSize="14"/>
                     <textInputTraits key="textInputTraits"/>
                 </textField>
-                <textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Never First Responder" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="PBX-7b-uV2">
+                <textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Never First Responder" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="PBX-7b-uV2">
                     <rect key="frame" x="20" y="75" width="374" height="30"/>
                     <accessibility key="accessibilityConfiguration" label=""/>
                     <fontDescription key="fontDescription" type="system" pointSize="14"/>
@@ -42,20 +46,20 @@
                         <constraint firstAttribute="width" constant="133" id="Sle-WE-dbc"/>
                     </constraints>
                     <state key="normal" title="next returnKeyType">
-                        <color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="calibratedRGB"/>
+                        <color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                     </state>
                     <connections>
                         <action selector="changeReturnKeyType:" destination="-1" eventType="touchUpInside" id="jOw-jE-AVj"/>
                     </connections>
                 </button>
-                <textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Text Field" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="HbD-2h-bQc">
+                <textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Text Field" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="HbD-2h-bQc">
                     <rect key="frame" x="20" y="141" width="374" height="30"/>
                     <fontDescription key="fontDescription" type="system" pointSize="14"/>
                     <textInputTraits key="textInputTraits" autocorrectionType="yes" spellCheckingType="yes"/>
                 </textField>
-                <textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" keyboardDismissMode="interactive" translatesAutoresizingMaskIntoConstraints="NO" id="F8N-FE-LqS">
+                <textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" misplaced="YES" keyboardDismissMode="interactive" translatesAutoresizingMaskIntoConstraints="NO" id="F8N-FE-LqS">
                     <rect key="frame" x="20" y="179" width="374" height="53"/>
-                    <color key="backgroundColor" red="0.91907269021739135" green="0.91907269021739135" blue="0.91907269021739135" alpha="1" colorSpace="calibratedRGB"/>
+                    <color key="backgroundColor" red="0.91907269021739135" green="0.91907269021739135" blue="0.91907269021739135" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                     <constraints>
                         <constraint firstAttribute="height" constant="53" id="dkB-da-5H4"/>
                     </constraints>
@@ -66,7 +70,7 @@
                     <rect key="frame" x="226" y="31" width="46" height="30"/>
                     <state key="normal" title="Button"/>
                 </button>
-                <pickerView contentMode="scaleToFill" verticalCompressionResistancePriority="749" translatesAutoresizingMaskIntoConstraints="NO" id="D9d-aK-L7i">
+                <pickerView contentMode="scaleToFill" verticalCompressionResistancePriority="749" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="D9d-aK-L7i">
                     <rect key="frame" x="47" y="313" width="320" height="100"/>
                     <constraints>
                         <constraint firstAttribute="height" constant="100" id="EaZ-Z7-fey"/>
@@ -76,16 +80,16 @@
                         <outlet property="delegate" destination="-1" id="iXT-ja-Und"/>
                     </connections>
                 </pickerView>
-                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="kf2-uc-vdE" customClass="CustomTextView">
+                <view contentMode="scaleToFill" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="kf2-uc-vdE" customClass="CustomTextView">
                     <rect key="frame" x="74" y="240" width="266" height="51"/>
-                    <color key="backgroundColor" red="0.72336632013320923" green="0.72336632013320923" blue="0.72336632013320923" alpha="1" colorSpace="calibratedRGB"/>
+                    <color key="backgroundColor" red="0.72336632013320923" green="0.72336632013320923" blue="0.72336632013320923" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                     <accessibility key="accessibilityConfiguration" identifier="CustomTextView"/>
                     <constraints>
                         <constraint firstAttribute="width" constant="266" id="Jks-lU-kXW"/>
                         <constraint firstAttribute="height" constant="51" id="KQW-5b-HjZ"/>
                     </constraints>
                 </view>
-                <view contentMode="bottomLeft" translatesAutoresizingMaskIntoConstraints="NO" id="bnj-yZ-VR3" customClass="CustomKeyboardTracker">
+                <view contentMode="bottomLeft" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="bnj-yZ-VR3" customClass="CustomKeyboardTracker">
                     <rect key="frame" x="149" y="606" width="88" height="51"/>
                     <color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
                     <accessibility key="accessibilityConfiguration" identifier="CustomKeyboardTracker"/>
@@ -96,8 +100,15 @@
                         <constraint firstAttribute="width" relation="greaterThanOrEqual" constant="10" id="vJa-7j-eqY"/>
                     </constraints>
                 </view>
+                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="charCounter" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="MP1-3R-IW1" userLabel="CharCounter">
+                    <rect key="frame" x="16" y="112" width="96" height="21"/>
+                    <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                    <fontDescription key="fontDescription" type="system" pointSize="17"/>
+                    <nil key="textColor"/>
+                    <nil key="highlightedColor"/>
+                </label>
             </subviews>
-            <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
+            <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
             <constraints>
                 <constraint firstItem="ugr-WT-H7I" firstAttribute="top" secondItem="PBX-7b-uV2" secondAttribute="bottom" constant="-2" id="235-Sq-djF"/>
                 <constraint firstItem="D9d-aK-L7i" firstAttribute="top" secondItem="kf2-uc-vdE" secondAttribute="bottom" constant="22" id="89S-kl-2ip"/>
@@ -121,12 +132,7 @@
                 <constraint firstItem="HbD-2h-bQc" firstAttribute="trailing" secondItem="PBX-7b-uV2" secondAttribute="trailing" id="zCU-gO-eFg"/>
                 <constraint firstAttribute="bottom" secondItem="bnj-yZ-VR3" secondAttribute="bottom" constant="10" id="zv1-fF-81e"/>
             </constraints>
-            <point key="canvasLocation" x="-17.5" y="29.5"/>
+            <point key="canvasLocation" x="-28" y="26.53673163418291"/>
         </view>
     </objects>
-    <simulatedMetricsContainer key="defaultSimulatedMetrics">
-        <simulatedStatusBarMetrics key="statusBar"/>
-        <simulatedOrientationMetrics key="orientation"/>
-        <simulatedScreenMetrics key="destination"/>
-    </simulatedMetricsContainer>
 </document>