From dc98883379480dc3be07019fd5eaaff66197c7cc Mon Sep 17 00:00:00 2001 From: Jacob Relkin Date: Fri, 29 Aug 2014 19:19:04 -0700 Subject: [PATCH 1/3] WIP --- .../SLIntegrationTestsAppDelegate.m | 1 + Integration Tests/Tests/SLTextFieldTest.m | 52 +++++++++++++++++++ .../Tests/SLTextFieldTestViewController.m | 33 +++++++++++- .../NSObject+SLAccessibilityHierarchy.m | 1 - 4 files changed, 85 insertions(+), 2 deletions(-) diff --git a/Integration Tests/SLIntegrationTestsAppDelegate.m b/Integration Tests/SLIntegrationTestsAppDelegate.m index 83ad799..f27a82b 100644 --- a/Integration Tests/SLIntegrationTestsAppDelegate.m +++ b/Integration Tests/SLIntegrationTestsAppDelegate.m @@ -110,6 +110,7 @@ - (void)verifyTerminalConnectionAndRunTests { } // Otherwise, run the tests + [[SLTestController sharedTestController] setShouldWaitToStartTesting:YES]; [[SLTestController sharedTestController] runTests:[NSSet setWithArray:_tests] withCompletionBlock:nil]; } diff --git a/Integration Tests/Tests/SLTextFieldTest.m b/Integration Tests/Tests/SLTextFieldTest.m index 7e25148..528546b 100644 --- a/Integration Tests/Tests/SLTextFieldTest.m +++ b/Integration Tests/Tests/SLTextFieldTest.m @@ -21,6 +21,7 @@ // #import "SLIntegrationTest.h" +#import @interface SLTextFieldTest : SLIntegrationTest @@ -36,9 +37,51 @@ + (NSString *)testCaseViewControllerClassName { return @"SLTextFieldTestViewController"; } +- (void)testDidEncounterFailure:(SLTestFailure *)failure; +{ + SLLog(@"[EXCEPTION STACK] %@", [failure.exception callStackSymbols]); + [self _logViewHierarchy]; + [self _logUIAccessibilityHierarchy]; + [self _dumpFullElementTree]; + SLLog(@"TEST FAILURE ENCOUNTERED!"); + +} + +- (void)_logViewHierarchy; +{ + dispatch_sync(dispatch_get_main_queue(), ^{ + NSArray *windows = [[UIApplication sharedApplication] windows]; + UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow]; + NSUInteger keyWindowIndex = [windows indexOfObject:keyWindow]; + + if (keyWindowIndex == NSNotFound) { + SLLogAsync(@"[NON-APP KEY WINDOW] %@", [keyWindow slRecursiveAccessibilityDescription]); + } else { + for (NSUInteger windowIndex = keyWindowIndex; windowIndex < [windows count]; windowIndex++) { + SLLogAsync(@"[APP KEY WINDOW #%lu] %@", (unsigned long)windowIndex, [windows[windowIndex] slRecursiveAccessibilityDescription]); + } + } + }); +} + +- (void)_logUIAccessibilityHierarchy; +{ + [[SLTerminal sharedTerminal] eval:@"UIATarget.localTarget().logElementTree()"]; +} + +- (void)_dumpFullElementTree; +{ + [SLTestController logFullySwizzledUIAElementTree]; +} + - (void)setUpTestCaseWithSelector:(SEL)testSelector { [super setUpTestCaseWithSelector:testSelector]; + if (testSelector == @selector(testSetTextWithinTableViewCellUnderControl)) { + _textField = [SLTextField elementWithAccessibilityIdentifier:@"text field"]; + return; + } + if (testSelector == @selector(testSetText) || testSelector == @selector(testSetTextWithinTableViewCell) || testSelector == @selector(testSetTextCanHandleTapHoldCharacters) || @@ -76,6 +119,15 @@ - (void)testSetTextWithinTableViewCell { SLAssertTrue([SLAskApp(text) isEqualToString:expectedText], @"Text was not set to expected value."); } +- (void)focus_testSetTextWithinTableViewCellUnderControl { + NSString *const expectedText = @"Fooness"; + _textField = [SLElement elementWithAccessibilityIdentifier:@"text field"]; +// SLButton *button = [SLButton elementWithAccessibilityIdentifier:@"thebutton"]; +// SLAssertNoThrow([UIAelement([button tap], @"Should not have thrown."); + SLAssertNoThrow(/*[UIAElement(_textField) setText:expectedText]*/[UIAElement(_textField) tap], @"Should not have thrown."); + SLAssertTrue([SLAskApp(text) isEqualToString:expectedText], @"Text was not set to expected value."); +} + - (void)testSetTextCanHandleTapHoldCharacters { NSString *const expectedText = @"foo’s a difficult string to type!"; SLAssertNoThrow([UIAElement(_textField) setText:expectedText], @"Should not have thrown."); diff --git a/Integration Tests/Tests/SLTextFieldTestViewController.m b/Integration Tests/Tests/SLTextFieldTestViewController.m index 36b1df2..acf18b9 100644 --- a/Integration Tests/Tests/SLTextFieldTestViewController.m +++ b/Integration Tests/Tests/SLTextFieldTestViewController.m @@ -42,6 +42,7 @@ - (void)loadViewForTestCase:(SEL)testCase { if (testCase == @selector(testSetText) || testCase == @selector(testSetTextWithinTableViewCell) || + testCase == @selector(testSetTextWithinTableViewCellUnderControl) || testCase == @selector(testSetTextCanHandleTapHoldCharacters) || testCase == @selector(testSetTextClearsCurrentText) || testCase == @selector(testSetTextClearsCurrentTextWithinTableViewCell) || @@ -59,6 +60,14 @@ - (void)loadViewForTestCase:(SEL)testCase { _tableView = [[UITableView alloc] initWithFrame:(CGRect){CGPointZero, CGSizeMake(320.0f, 44.0f)}]; _tableView.dataSource = self; [view addSubview:_tableView]; + } else if (testCase == @selector(testSetTextWithinTableViewCellUnderControl)) { + UIControl *control = [[UIControl alloc] initWithFrame:view.bounds]; + control.userInteractionEnabled = YES; + control.accessibilityIdentifier = @"text field"; +// _textField.userInteractionEnabled = YES; + [control addSubview:_textField]; + [view addSubview:control]; +// [view addSubview:_textField]; } else { [view addSubview:_textField]; } @@ -161,6 +170,7 @@ - (NSString *)text { NSString *text; if (self.testCase == @selector(testSetText) || self.testCase == @selector(testSetTextWithinTableViewCell) || + self.testCase == @selector(testSetTextWithinTableViewCellUnderControl) || self.testCase == @selector(testSetTextCanHandleTapHoldCharacters) || self.testCase == @selector(testSetTextClearsCurrentText) || self.testCase == @selector(testSetTextClearsCurrentTextWithinTableViewCell) || @@ -201,7 +211,28 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N _textField.textColor = [UIColor blackColor]; [_textField becomeFirstResponder]; - [tableViewCell.contentView addSubview:_textField]; + if (self.testCase == @selector(testSetTextWithinTableViewCellUnderControl)) { + UIControl *control = [[UIControl alloc] initWithFrame:self.view.bounds]; + control.userInteractionEnabled = NO; + + UITextField *field = [[UITextField alloc] initWithFrame:CGRectMake(10, 5, 25, 20)]; + + field.placeholder = @"FOO!"; + field.accessibilityIdentifier = @"text field"; + field.userInteractionEnabled = YES; + +// UIButton *btn = [UIButton buttonWithType:UIButtonTypeInfoDark]; +// [btn setFrame:CGRectMake(80, 0, 20, 20)]; +// [btn setBackgroundColor:[UIColor blueColor]]; +// [btn setAccessibilityIdentifier:@"thebutton"]; + + [control addSubview:field]; +// [control addSubview:btn]; + + [tableViewCell.contentView addSubview:control]; + } else { + [tableViewCell.contentView addSubview:_textField]; + } return tableViewCell; } diff --git a/Sources/Classes/UIAutomation/User Interface Elements/NSObject+SLAccessibilityHierarchy.m b/Sources/Classes/UIAutomation/User Interface Elements/NSObject+SLAccessibilityHierarchy.m index 6777bd4..066742d 100644 --- a/Sources/Classes/UIAutomation/User Interface Elements/NSObject+SLAccessibilityHierarchy.m +++ b/Sources/Classes/UIAutomation/User Interface Elements/NSObject+SLAccessibilityHierarchy.m @@ -393,7 +393,6 @@ - (BOOL)classForcesPresenceOfMockingViewsInAccessibilityHierarchy { } @end - // On iOS 6, collection view cells themselves appear in the accessibility hierarchy. // On iOS 7, mock cells (instances of `UICollectionViewCellAccessibilityElement`) appear in the hierarchy instead. @implementation UICollectionViewCell (SLAccessibilityHierarchy) From 1c2391b7d6be3665b15edb0d6c504825b26e1879 Mon Sep 17 00:00:00 2001 From: Jacob Relkin Date: Tue, 2 Sep 2014 21:03:52 -0700 Subject: [PATCH 2/3] Hack fix for nested UIControls in a UITableView --- .../NSObject+SLAccessibilityHierarchy.m | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/Sources/Classes/UIAutomation/User Interface Elements/NSObject+SLAccessibilityHierarchy.m b/Sources/Classes/UIAutomation/User Interface Elements/NSObject+SLAccessibilityHierarchy.m index 066742d..299827b 100644 --- a/Sources/Classes/UIAutomation/User Interface Elements/NSObject+SLAccessibilityHierarchy.m +++ b/Sources/Classes/UIAutomation/User Interface Elements/NSObject+SLAccessibilityHierarchy.m @@ -449,9 +449,40 @@ - (BOOL)classForcesPresenceInAccessibilityHierarchy { @implementation UIControl (SLAccessibilityHierarchy) + - (BOOL)classForcesPresenceInAccessibilityHierarchy { - return YES; + BOOL forcesPresence = YES; + BOOL containedWithinTableView = NO; + + id parent = self; + do { + parent = [parent slAccessibilityParent]; + + if ([parent isKindOfClass:[UITableView class]]) { + containedWithinTableView = YES; + } + } while (parent && !containedWithinTableView); + + if (containedWithinTableView) { + NSMutableArray *children = [[self slChildAccessibilityElementsFavoringSubviews:YES] mutableCopy]; + NSUInteger i = 0; + while (i < children.count) { + id child = children[i]; + + if ([child isKindOfClass:[UIControl class]]) { + forcesPresence = NO; + break; + } + + [children addObjectsFromArray:[child slChildAccessibilityElementsFavoringSubviews:YES]]; + + ++i; + } + } + + return forcesPresence; } + @end From 4d707bf71a428c32b3de03842dadf775da2b5707 Mon Sep 17 00:00:00 2001 From: Jacob Relkin Date: Tue, 2 Sep 2014 21:46:26 -0700 Subject: [PATCH 3/3] Cleanup tests and supply simple fix. --- .../SLIntegrationTestsAppDelegate.m | 1 - Integration Tests/Tests/SLTextFieldTest.m | 54 ++----------------- .../Tests/SLTextFieldTestViewController.m | 28 ++-------- .../NSObject+SLAccessibilityHierarchy.m | 1 + 4 files changed, 10 insertions(+), 74 deletions(-) diff --git a/Integration Tests/SLIntegrationTestsAppDelegate.m b/Integration Tests/SLIntegrationTestsAppDelegate.m index f27a82b..83ad799 100644 --- a/Integration Tests/SLIntegrationTestsAppDelegate.m +++ b/Integration Tests/SLIntegrationTestsAppDelegate.m @@ -110,7 +110,6 @@ - (void)verifyTerminalConnectionAndRunTests { } // Otherwise, run the tests - [[SLTestController sharedTestController] setShouldWaitToStartTesting:YES]; [[SLTestController sharedTestController] runTests:[NSSet setWithArray:_tests] withCompletionBlock:nil]; } diff --git a/Integration Tests/Tests/SLTextFieldTest.m b/Integration Tests/Tests/SLTextFieldTest.m index 528546b..d00c3ab 100644 --- a/Integration Tests/Tests/SLTextFieldTest.m +++ b/Integration Tests/Tests/SLTextFieldTest.m @@ -21,7 +21,6 @@ // #import "SLIntegrationTest.h" -#import @interface SLTextFieldTest : SLIntegrationTest @@ -37,51 +36,9 @@ + (NSString *)testCaseViewControllerClassName { return @"SLTextFieldTestViewController"; } -- (void)testDidEncounterFailure:(SLTestFailure *)failure; -{ - SLLog(@"[EXCEPTION STACK] %@", [failure.exception callStackSymbols]); - [self _logViewHierarchy]; - [self _logUIAccessibilityHierarchy]; - [self _dumpFullElementTree]; - SLLog(@"TEST FAILURE ENCOUNTERED!"); - -} - -- (void)_logViewHierarchy; -{ - dispatch_sync(dispatch_get_main_queue(), ^{ - NSArray *windows = [[UIApplication sharedApplication] windows]; - UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow]; - NSUInteger keyWindowIndex = [windows indexOfObject:keyWindow]; - - if (keyWindowIndex == NSNotFound) { - SLLogAsync(@"[NON-APP KEY WINDOW] %@", [keyWindow slRecursiveAccessibilityDescription]); - } else { - for (NSUInteger windowIndex = keyWindowIndex; windowIndex < [windows count]; windowIndex++) { - SLLogAsync(@"[APP KEY WINDOW #%lu] %@", (unsigned long)windowIndex, [windows[windowIndex] slRecursiveAccessibilityDescription]); - } - } - }); -} - -- (void)_logUIAccessibilityHierarchy; -{ - [[SLTerminal sharedTerminal] eval:@"UIATarget.localTarget().logElementTree()"]; -} - -- (void)_dumpFullElementTree; -{ - [SLTestController logFullySwizzledUIAElementTree]; -} - - (void)setUpTestCaseWithSelector:(SEL)testSelector { [super setUpTestCaseWithSelector:testSelector]; - if (testSelector == @selector(testSetTextWithinTableViewCellUnderControl)) { - _textField = [SLTextField elementWithAccessibilityIdentifier:@"text field"]; - return; - } - if (testSelector == @selector(testSetText) || testSelector == @selector(testSetTextWithinTableViewCell) || testSelector == @selector(testSetTextCanHandleTapHoldCharacters) || @@ -90,7 +47,8 @@ - (void)setUpTestCaseWithSelector:(SEL)testSelector { testSelector == @selector(testSetTextWhenFieldClearsOnBeginEditing) || testSelector == @selector(testGetText) || testSelector == @selector(testDoNotMatchEditorAccessibilityObjects) || - testSelector == @selector(testClearTextButton)) { + testSelector == @selector(testClearTextButton) || + testSelector == @selector(testSetTextWithinTableViewCellUnderControl)) { _textField = [SLTextField elementWithAccessibilityLabel:@"test element"]; } else if (testSelector == @selector(testMatchesSearchBarTextField) || testSelector == @selector(testSetSearchBarText) || @@ -119,12 +77,10 @@ - (void)testSetTextWithinTableViewCell { SLAssertTrue([SLAskApp(text) isEqualToString:expectedText], @"Text was not set to expected value."); } -- (void)focus_testSetTextWithinTableViewCellUnderControl { +- (void)testSetTextWithinTableViewCellUnderControl { NSString *const expectedText = @"Fooness"; - _textField = [SLElement elementWithAccessibilityIdentifier:@"text field"]; -// SLButton *button = [SLButton elementWithAccessibilityIdentifier:@"thebutton"]; -// SLAssertNoThrow([UIAelement([button tap], @"Should not have thrown."); - SLAssertNoThrow(/*[UIAElement(_textField) setText:expectedText]*/[UIAElement(_textField) tap], @"Should not have thrown."); + SLAssertNoThrow([UIAElement(_textField) setText:expectedText], @"Should not have thrown."); + SLAssertNoThrow([UIAElement(_textField) tap], @"Should not have thrown."); SLAssertTrue([SLAskApp(text) isEqualToString:expectedText], @"Text was not set to expected value."); } diff --git a/Integration Tests/Tests/SLTextFieldTestViewController.m b/Integration Tests/Tests/SLTextFieldTestViewController.m index acf18b9..9b1c3c0 100644 --- a/Integration Tests/Tests/SLTextFieldTestViewController.m +++ b/Integration Tests/Tests/SLTextFieldTestViewController.m @@ -56,18 +56,11 @@ - (void)loadViewForTestCase:(SEL)testCase { _textField = [[UITextField alloc] initWithFrame:kTextFieldFrame]; if (testCase == @selector(testSetTextWithinTableViewCell) || - testCase == @selector(testSetTextClearsCurrentTextWithinTableViewCell)) { + testCase == @selector(testSetTextClearsCurrentTextWithinTableViewCell) || + testCase == @selector(testSetTextWithinTableViewCellUnderControl)) { _tableView = [[UITableView alloc] initWithFrame:(CGRect){CGPointZero, CGSizeMake(320.0f, 44.0f)}]; _tableView.dataSource = self; [view addSubview:_tableView]; - } else if (testCase == @selector(testSetTextWithinTableViewCellUnderControl)) { - UIControl *control = [[UIControl alloc] initWithFrame:view.bounds]; - control.userInteractionEnabled = YES; - control.accessibilityIdentifier = @"text field"; -// _textField.userInteractionEnabled = YES; - [control addSubview:_textField]; - [view addSubview:control]; -// [view addSubview:_textField]; } else { [view addSubview:_textField]; } @@ -110,6 +103,7 @@ - (void)viewDidLoad { [super viewDidLoad]; _textField.accessibilityLabel = @"test element"; + _textField.autocorrectionType = UITextAutocorrectionTypeNo; _textField.borderStyle = UITextBorderStyleRoundedRect; if (self.testCase == @selector(testClearTextButton)) { _textField.clearButtonMode = UITextFieldViewModeAlways; @@ -213,21 +207,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N if (self.testCase == @selector(testSetTextWithinTableViewCellUnderControl)) { UIControl *control = [[UIControl alloc] initWithFrame:self.view.bounds]; - control.userInteractionEnabled = NO; - - UITextField *field = [[UITextField alloc] initWithFrame:CGRectMake(10, 5, 25, 20)]; - - field.placeholder = @"FOO!"; - field.accessibilityIdentifier = @"text field"; - field.userInteractionEnabled = YES; - -// UIButton *btn = [UIButton buttonWithType:UIButtonTypeInfoDark]; -// [btn setFrame:CGRectMake(80, 0, 20, 20)]; -// [btn setBackgroundColor:[UIColor blueColor]]; -// [btn setAccessibilityIdentifier:@"thebutton"]; - - [control addSubview:field]; -// [control addSubview:btn]; + [control addSubview:_textField]; [tableViewCell.contentView addSubview:control]; } else { diff --git a/Sources/Classes/UIAutomation/User Interface Elements/NSObject+SLAccessibilityHierarchy.m b/Sources/Classes/UIAutomation/User Interface Elements/NSObject+SLAccessibilityHierarchy.m index 299827b..5c68780 100644 --- a/Sources/Classes/UIAutomation/User Interface Elements/NSObject+SLAccessibilityHierarchy.m +++ b/Sources/Classes/UIAutomation/User Interface Elements/NSObject+SLAccessibilityHierarchy.m @@ -393,6 +393,7 @@ - (BOOL)classForcesPresenceOfMockingViewsInAccessibilityHierarchy { } @end + // On iOS 6, collection view cells themselves appear in the accessibility hierarchy. // On iOS 7, mock cells (instances of `UICollectionViewCellAccessibilityElement`) appear in the hierarchy instead. @implementation UICollectionViewCell (SLAccessibilityHierarchy)