// Copyright 2020 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #import "TextSemanticsFocusTest.h" FLUTTER_ASSERT_ARC @interface XCUIElement (ftr_waitForNonExistence) /// Keeps waiting until the element doesn't exist. Returns NO if the timeout is /// reached before it doesn't exist. - (BOOL)ftr_waitForNonExistenceWithTimeout:(NSTimeInterval)timeout; /// Waits the full duration to ensure something doesn't exist for that duration. /// Returns NO if at some point the element exists during the duration. - (BOOL)ftr_waitForNonExistenceForDuration:(NSTimeInterval)duration; @end @implementation XCUIElement (ftr_waitForNonExistence) - (BOOL)ftr_waitForNonExistenceWithTimeout:(NSTimeInterval)timeout { NSTimeInterval delta = 0.5; while (timeout > 0.0) { if (!self.exists) { return YES; } usleep(delta * 1000000); timeout -= delta; } return NO; } - (BOOL)ftr_waitForNonExistenceForDuration:(NSTimeInterval)duration { return ![self waitForExistenceWithTimeout:duration]; } @end @implementation TextSemanticsFocusTest - (void)setUp { [super setUp]; self.continueAfterFailure = NO; self.application = [[XCUIApplication alloc] init]; self.application.launchArguments = @[ @"--text-semantics-focus" ]; [self.application launch]; } - (void)testAccessibilityFocusOnTextSemanticsProducesCorrectIosViews { NSTimeInterval timeout = 10.0; // Find the initial TextInputSemanticsObject which was sent from the mock framework on first // frame. XCUIElement* textInputSemanticsObject = [[[self.application textFields] matchingIdentifier:@"flutter textfield"] element]; XCTAssertTrue([textInputSemanticsObject waitForExistenceWithTimeout:timeout]); XCTAssertEqualObjects([textInputSemanticsObject valueForKey:@"hasKeyboardFocus"], @(NO)); // Since the first mock framework text field isn't focused on, it shouldn't produce a UITextInput // in the view hierarchy. XCUIElement* delegateTextInput = [[self.application textViews] element]; XCTAssertTrue([delegateTextInput ftr_waitForNonExistenceWithTimeout:timeout]); // Nor should there be a keyboard for text entry. XCUIElement* keyboard = [[self.application keyboards] element]; XCTAssertTrue([keyboard ftr_waitForNonExistenceWithTimeout:timeout]); // The tap location doesn't matter. The mock framework just sends a focused text field on tap. [textInputSemanticsObject tap]; // The new TextInputSemanticsObject now has keyboard focus (the only trait accessible through // UI tests on a XCUIElement). textInputSemanticsObject = [[[self.application textFields] matchingIdentifier:@"focused flutter textfield"] element]; XCTAssertTrue([textInputSemanticsObject waitForExistenceWithTimeout:timeout]); XCTAssertEqualObjects([textInputSemanticsObject valueForKey:@"hasKeyboardFocus"], @(YES)); // The delegate UITextInput is also inserted on the window but we make only the // TextInputSemanticsObject visible and not the FlutterTextInputView to avoid confusing // accessibility, it shouldn't be visible to the UI test either. delegateTextInput = [[self.application textViews] element]; XCTAssertTrue([delegateTextInput ftr_waitForNonExistenceForDuration:3.0]); // But since there is focus, the soft keyboard is visible on the simulator. keyboard = [[self.application keyboards] element]; XCTAssertTrue([keyboard waitForExistenceWithTimeout:timeout]); } @end