diff --git a/Source/ASExperimentalFeatures.h b/Source/ASExperimentalFeatures.h index 482296450..e8b987da0 100644 --- a/Source/ASExperimentalFeatures.h +++ b/Source/ASExperimentalFeatures.h @@ -28,6 +28,7 @@ typedef NS_OPTIONS(NSUInteger, ASExperimentalFeatures) { ASExperimentalFramesetterCache = 1 << 7, // exp_framesetter_cache ASExperimentalClearDataDuringDeallocation = 1 << 8, // exp_clear_data_during_deallocation ASExperimentalDidEnterPreloadSkipASMLayout = 1 << 9, // exp_did_enter_preload_skip_asm_layout + ASExperimentalDisableAccessibilityCache = 1 << 10, // exp_disable_a11y_cache ASExperimentalFeatureAll = 0xFFFFFFFF }; diff --git a/Source/ASExperimentalFeatures.mm b/Source/ASExperimentalFeatures.mm index 68a6871f0..a638bedc3 100644 --- a/Source/ASExperimentalFeatures.mm +++ b/Source/ASExperimentalFeatures.mm @@ -21,7 +21,8 @@ @"exp_collection_teardown", @"exp_framesetter_cache", @"exp_clear_data_during_deallocation", - @"exp_did_enter_preload_skip_asm_layout"])); + @"exp_did_enter_preload_skip_asm_layout", + @"exp_disable_a11y_cache"])); if (flags == ASExperimentalFeatureAll) { return allNames; diff --git a/Source/Details/_ASDisplayViewAccessiblity.mm b/Source/Details/_ASDisplayViewAccessiblity.mm index 2fdc72853..755a63c70 100644 --- a/Source/Details/_ASDisplayViewAccessiblity.mm +++ b/Source/Details/_ASDisplayViewAccessiblity.mm @@ -273,7 +273,8 @@ - (NSArray *)accessibilityElements if (viewNode == nil) { return @[]; } - if (_accessibilityElements == nil) { + + if (_accessibilityElements == nil || ASActivateExperimentalFeature(ASExperimentalDisableAccessibilityCache)) { _accessibilityElements = [viewNode accessibilityElements]; } return _accessibilityElements; diff --git a/Tests/ASConfigurationTests.mm b/Tests/ASConfigurationTests.mm index 6358ad49a..a6e936e4b 100644 --- a/Tests/ASConfigurationTests.mm +++ b/Tests/ASConfigurationTests.mm @@ -25,7 +25,8 @@ ASExperimentalCollectionTeardown, ASExperimentalFramesetterCache, ASExperimentalClearDataDuringDeallocation, - ASExperimentalDidEnterPreloadSkipASMLayout + ASExperimentalDidEnterPreloadSkipASMLayout, + ASExperimentalDisableAccessibilityCache }; @interface ASConfigurationTests : ASTestCase @@ -48,6 +49,7 @@ + (NSArray *)names { @"exp_framesetter_cache", @"exp_clear_data_during_deallocation", @"exp_did_enter_preload_skip_asm_layout", + @"exp_disable_a11y_cache" ]; } diff --git a/Tests/ASDisplayViewAccessibilityTests.mm b/Tests/ASDisplayViewAccessibilityTests.mm index 2bdc89e9f..f8b5510ca 100644 --- a/Tests/ASDisplayViewAccessibilityTests.mm +++ b/Tests/ASDisplayViewAccessibilityTests.mm @@ -14,12 +14,22 @@ #import #import +#import +#import "ASConfiguration.h" +#import "ASConfigurationInternal.h" @interface ASDisplayViewAccessibilityTests : XCTestCase @end @implementation ASDisplayViewAccessibilityTests +- (void)setUp +{ + ASConfiguration *config = [[ASConfiguration alloc] initWithDictionary:nil]; + config.experimentalFeatures = ASExperimentalDisableAccessibilityCache; + [ASConfigurationManager test_resetWithConfiguration:config]; +} + - (void)testAccessibilityElementsAccessors { // Setup nodes with accessibility info @@ -39,7 +49,8 @@ - (void)testAccessibilityElementsAccessors XCTAssertEqual([node.view indexOfAccessibilityElement:node.view.accessibilityElements.firstObject], 0);*/ } -- (void)testThatSubnodeAccessibilityLabelAggregationWorks { +- (void)testThatSubnodeAccessibilityLabelAggregationWorks +{ // Setup nodes ASDisplayNode *node = nil; ASDisplayNode *innerNode1 = nil; @@ -62,7 +73,8 @@ - (void)testThatSubnodeAccessibilityLabelAggregationWorks { [node.view.accessibilityElements.firstObject accessibilityLabel]); } -- (void)testThatContainerAccessibilityLabelOverrideStopsAggregation { +- (void)testThatContainerAccessibilityLabelOverrideStopsAggregation +{ // Setup nodes ASDisplayNode *node = nil; ASDisplayNode *innerNode = nil; @@ -82,4 +94,179 @@ - (void)testThatContainerAccessibilityLabelOverrideStopsAggregation { [node.view.accessibilityElements.firstObject accessibilityLabel]); } +- (void)testAccessibilityLayerbackedNodesOperationInContainer { + ASDisplayNode *contianer = [[ASDisplayNode alloc] init]; + contianer.frame = CGRectMake(50, 50, 200, 400); + contianer.backgroundColor = [UIColor grayColor]; + contianer.isAccessibilityContainer = YES; + // Do any additional setup after loading the view, typically from a nib. + ASTextNode *text1 = [[ASTextNode alloc] init]; + text1.layerBacked = YES; + text1.attributedText = [[NSAttributedString alloc] initWithString:@"hello"]; + text1.frame = CGRectMake(50, 100, 200, 200); + [contianer addSubnode:text1]; + [contianer layoutIfNeeded]; + [contianer.layer displayIfNeeded]; + NSArray *elements = contianer.view.accessibilityElements; + XCTAssertTrue(elements.count == 1); + XCTAssertTrue([[elements.firstObject accessibilityLabel] isEqualToString:@"hello"]); + ASTextNode *text2 = [[ASTextNode alloc] init]; + text2.layerBacked = YES; + text2.attributedText = [[NSAttributedString alloc] initWithString:@"world"]; + text2.frame = CGRectMake(50, 300, 200, 200); + [contianer addSubnode:text2]; + [contianer layoutIfNeeded]; + [contianer.layer displayIfNeeded]; + NSArray *updatedElements = contianer.view.accessibilityElements; + XCTAssertTrue(updatedElements.count == 1); + XCTAssertTrue([[updatedElements.firstObject accessibilityLabel] isEqualToString:@"hello, world"]); + ASTextNode *text3 = [[ASTextNode alloc] init]; + text3.attributedText = [[NSAttributedString alloc] initWithString:@"!!!!"]; + text3.frame = CGRectMake(50, 400, 200, 100); + text3.layerBacked = YES; + [text2 addSubnode:text3]; + [contianer layoutIfNeeded]; + [contianer.layer displayIfNeeded]; + NSArray *updatedElements2 = contianer.view.accessibilityElements; + XCTAssertTrue([[updatedElements2.firstObject accessibilityLabel] isEqualToString:@"hello, world, !!!!"]); +} + +- (void)testAccessibilityNonLayerbackedNodesOperationInContainer +{ + ASDisplayNode *contianer = [[ASDisplayNode alloc] init]; + contianer.frame = CGRectMake(50, 50, 200, 600); + contianer.backgroundColor = [UIColor grayColor]; + contianer.isAccessibilityContainer = YES; + // Do any additional setup after loading the view, typically from a nib. + ASTextNode *text1 = [[ASTextNode alloc] init]; + text1.attributedText = [[NSAttributedString alloc] initWithString:@"hello"]; + text1.frame = CGRectMake(50, 100, 200, 200); + [contianer addSubnode:text1]; + [contianer layoutIfNeeded]; + [contianer.layer displayIfNeeded]; + NSArray *elements = contianer.view.accessibilityElements; + XCTAssertTrue(elements.count == 1); + XCTAssertTrue([[elements.firstObject accessibilityLabel] isEqualToString:@"hello"]); + ASTextNode *text2 = [[ASTextNode alloc] init]; + text2.attributedText = [[NSAttributedString alloc] initWithString:@"world"]; + text2.frame = CGRectMake(50, 300, 200, 200); + [contianer addSubnode:text2]; + [contianer layoutIfNeeded]; + [contianer.layer displayIfNeeded]; + NSArray *updatedElements = contianer.view.accessibilityElements; + XCTAssertTrue(updatedElements.count == 1); + XCTAssertTrue([[updatedElements.firstObject accessibilityLabel] isEqualToString:@"hello, world"]); + ASTextNode *text3 = [[ASTextNode alloc] init]; + text3.attributedText = [[NSAttributedString alloc] initWithString:@"!!!!"]; + text3.frame = CGRectMake(50, 400, 200, 100); + [text2 addSubnode:text3]; + [contianer layoutIfNeeded]; + [contianer.layer displayIfNeeded]; + NSArray *updatedElements2 = contianer.view.accessibilityElements; + XCTAssertTrue([[updatedElements2.firstObject accessibilityLabel] isEqualToString:@"hello, world, !!!!"]); +} + +- (void)testAccessibilityNonLayerbackedNodesOperationInNonContainer +{ + ASDisplayNode *contianer = [[ASDisplayNode alloc] init]; + contianer.frame = CGRectMake(50, 50, 200, 600); + contianer.backgroundColor = [UIColor grayColor]; + // Do any additional setup after loading the view, typically from a nib. + ASTextNode *text1 = [[ASTextNode alloc] init]; + text1.attributedText = [[NSAttributedString alloc] initWithString:@"hello"]; + text1.frame = CGRectMake(50, 100, 200, 200); + [contianer addSubnode:text1]; + [contianer layoutIfNeeded]; + [contianer.layer displayIfNeeded]; + NSArray *elements = contianer.view.accessibilityElements; + XCTAssertTrue(elements.count == 1); + XCTAssertTrue([[elements.firstObject accessibilityLabel] isEqualToString:@"hello"]); + ASTextNode *text2 = [[ASTextNode alloc] init]; + text2.attributedText = [[NSAttributedString alloc] initWithString:@"world"]; + text2.frame = CGRectMake(50, 300, 200, 200); + [contianer addSubnode:text2]; + [contianer layoutIfNeeded]; + [contianer.layer displayIfNeeded]; + NSArray *updatedElements = contianer.view.accessibilityElements; + XCTAssertTrue(updatedElements.count == 2); + XCTAssertTrue([[updatedElements.firstObject accessibilityLabel] isEqualToString:@"hello"]); + XCTAssertTrue([[updatedElements.lastObject accessibilityLabel] isEqualToString:@"world"]); + ASTextNode *text3 = [[ASTextNode alloc] init]; + text3.attributedText = [[NSAttributedString alloc] initWithString:@"!!!!"]; + text3.frame = CGRectMake(50, 400, 200, 100); + [text2 addSubnode:text3]; + [contianer layoutIfNeeded]; + [contianer.layer displayIfNeeded]; + NSArray *updatedElements2 = contianer.view.accessibilityElements; + //text3 won't be read out cause it's overshadowed by text2 + XCTAssertTrue(updatedElements2.count == 2); + XCTAssertTrue([[updatedElements2.firstObject accessibilityLabel] isEqualToString:@"hello"]); + XCTAssertTrue([[updatedElements2.lastObject accessibilityLabel] isEqualToString:@"world"]); +} +- (void)testAccessibilityLayerbackedNodesOperationInNonContainer +{ + ASDisplayNode *contianer = [[ASDisplayNode alloc] init]; + contianer.frame = CGRectMake(50, 50, 200, 600); + contianer.backgroundColor = [UIColor grayColor]; + // Do any additional setup after loading the view, typically from a nib. + ASTextNode *text1 = [[ASTextNode alloc] init]; + text1.layerBacked = YES; + text1.attributedText = [[NSAttributedString alloc] initWithString:@"hello"]; + text1.frame = CGRectMake(50, 0, 100, 100); + [contianer addSubnode:text1]; + [contianer layoutIfNeeded]; + [contianer.layer displayIfNeeded]; + NSArray *elements = contianer.view.accessibilityElements; + XCTAssertTrue(elements.count == 1); + XCTAssertTrue([[elements.firstObject accessibilityLabel] isEqualToString:@"hello"]); + ASTextNode *text2 = [[ASTextNode alloc] init]; + text2.layerBacked = YES; + text2.attributedText = [[NSAttributedString alloc] initWithString:@"world"]; + text2.frame = CGRectMake(50, 100, 100, 100); + [contianer addSubnode:text2]; + [contianer layoutIfNeeded]; + [contianer.layer displayIfNeeded]; + NSArray *updatedElements = contianer.view.accessibilityElements; + XCTAssertTrue(updatedElements.count == 2); + XCTAssertTrue([[updatedElements.firstObject accessibilityLabel] isEqualToString:@"hello"]); + XCTAssertTrue([[updatedElements.lastObject accessibilityLabel] isEqualToString:@"world"]); + ASTextNode *text3 = [[ASTextNode alloc] init]; + text3.layerBacked = YES; + text3.attributedText = [[NSAttributedString alloc] initWithString:@"!!!!"]; + text3.frame = CGRectMake(50, 200, 100, 100); + [text2 addSubnode:text3]; + [contianer layoutIfNeeded]; + [contianer.layer displayIfNeeded]; + NSArray *updatedElements2 = contianer.view.accessibilityElements; + //text3 won't be read out cause it's overshadowed by text2 + XCTAssertTrue(updatedElements2.count == 2); + XCTAssertTrue([[updatedElements2.firstObject accessibilityLabel] isEqualToString:@"hello"]); + XCTAssertTrue([[updatedElements2.lastObject accessibilityLabel] isEqualToString:@"world"]); +} + +- (void)testAccessibilityUpdatesWithElementsChanges +{ + ASDisplayNode *contianer = [[ASDisplayNode alloc] init]; + contianer.frame = CGRectMake(50, 50, 200, 600); + contianer.backgroundColor = [UIColor grayColor]; + contianer.isAccessibilityContainer = YES; + // Do any additional setup after loading the view, typically from a nib. + ASTextNode *text1 = [[ASTextNode alloc] init]; + text1.layerBacked = YES; + text1.attributedText = [[NSAttributedString alloc] initWithString:@"hello"]; + text1.frame = CGRectMake(50, 0, 100, 100); + [contianer addSubnode:text1]; + [contianer layoutIfNeeded]; + [contianer.layer displayIfNeeded]; + NSArray *elements = contianer.view.accessibilityElements; + XCTAssertTrue(elements.count == 1); + XCTAssertTrue([[elements.firstObject accessibilityLabel] isEqualToString:@"hello"]); + text1.attributedText = [[NSAttributedString alloc] initWithString:@"greeting"]; + [contianer layoutIfNeeded]; + [contianer.layer displayIfNeeded]; + NSArray *elements2 = contianer.view.accessibilityElements; + XCTAssertTrue(elements2.count == 1); + XCTAssertTrue([[elements2.firstObject accessibilityLabel] isEqualToString:@"greeting"]); +} + @end