blob: 04212197e4ee1877ad83af3681433555d4b9e281 [file] [log] [blame]
// Copyright 2013 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 <OCMock/OCMock.h>
#import <XCTest/XCTest.h>
#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/SemanticsObject.h"
FLUTTER_ASSERT_ARC
namespace flutter {
namespace {
class SemanticsActionObservation {
public:
SemanticsActionObservation(int32_t observed_id, SemanticsAction observed_action)
: id(observed_id), action(observed_action) {}
int32_t id;
SemanticsAction action;
};
class MockAccessibilityBridge : public AccessibilityBridgeIos {
public:
MockAccessibilityBridge() : observations({}) { view_ = [[UIView alloc] init]; }
UIView* view() const override { return view_; }
UIView<UITextInput>* textInputView() override { return nil; }
void DispatchSemanticsAction(int32_t id, SemanticsAction action) override {
SemanticsActionObservation observation(id, action);
observations.push_back(observation);
}
void DispatchSemanticsAction(int32_t id,
SemanticsAction action,
std::vector<uint8_t> args) override {
SemanticsActionObservation observation(id, action);
observations.push_back(observation);
}
void AccessibilityObjectDidBecomeFocused(int32_t id) override {}
void AccessibilityObjectDidLoseFocus(int32_t id) override {}
std::shared_ptr<FlutterPlatformViewsController> GetPlatformViewsController() const override {
return nil;
}
std::vector<SemanticsActionObservation> observations;
private:
UIView* view_;
};
} // namespace
} // namespace flutter
@interface SemanticsObjectTest : XCTestCase
@end
@implementation SemanticsObjectTest
- (void)testCreate {
fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
new flutter::MockAccessibilityBridge());
fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
SemanticsObject* object = [[SemanticsObject alloc] initWithBridge:bridge uid:0];
XCTAssertNotNil(object);
}
- (void)testSetChildren {
fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
new flutter::MockAccessibilityBridge());
fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
SemanticsObject* parent = [[SemanticsObject alloc] initWithBridge:bridge uid:0];
SemanticsObject* child = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
parent.children = @[ child ];
XCTAssertEqual(parent, child.parent);
parent.children = @[];
XCTAssertNil(child.parent);
}
- (void)testReplaceChildAtIndex {
fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
new flutter::MockAccessibilityBridge());
fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
SemanticsObject* parent = [[SemanticsObject alloc] initWithBridge:bridge uid:0];
SemanticsObject* child1 = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
SemanticsObject* child2 = [[SemanticsObject alloc] initWithBridge:bridge uid:2];
parent.children = @[ child1 ];
[parent replaceChildAtIndex:0 withChild:child2];
XCTAssertNil(child1.parent);
XCTAssertEqual(parent, child2.parent);
XCTAssertEqualObjects(parent.children, @[ child2 ]);
}
- (void)testPlainSemanticsObjectWithLabelHasStaticTextTrait {
fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
new flutter::MockAccessibilityBridge());
fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
flutter::SemanticsNode node;
node.label = "foo";
FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
[object setSemanticsNode:&node];
XCTAssertEqual([object accessibilityTraits], UIAccessibilityTraitStaticText);
}
- (void)testIntresetingSemanticsObjectWithLabelHasStaticTextTrait {
fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
new flutter::MockAccessibilityBridge());
fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
flutter::SemanticsNode node;
node.label = "foo";
FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
SemanticsObject* child1 = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
object.children = @[ child1 ];
[object setSemanticsNode:&node];
XCTAssertEqual([object accessibilityTraits], UIAccessibilityTraitNone);
}
- (void)testIntresetingSemanticsObjectWithLabelHasStaticTextTrait1 {
fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
new flutter::MockAccessibilityBridge());
fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
flutter::SemanticsNode node;
node.label = "foo";
node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kIsTextField);
FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
[object setSemanticsNode:&node];
XCTAssertEqual([object accessibilityTraits], UIAccessibilityTraitNone);
}
- (void)testIntresetingSemanticsObjectWithLabelHasStaticTextTrait2 {
fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
new flutter::MockAccessibilityBridge());
fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
flutter::SemanticsNode node;
node.label = "foo";
node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kIsButton);
FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
[object setSemanticsNode:&node];
XCTAssertEqual([object accessibilityTraits], UIAccessibilityTraitButton);
}
- (void)testShouldTriggerAnnouncement {
fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
new flutter::MockAccessibilityBridge());
fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
SemanticsObject* object = [[SemanticsObject alloc] initWithBridge:bridge uid:0];
// Handle nil with no node set.
XCTAssertFalse([object nodeShouldTriggerAnnouncement:nil]);
// Handle initial setting of node with liveRegion
flutter::SemanticsNode node;
node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kIsLiveRegion);
node.label = "foo";
XCTAssertTrue([object nodeShouldTriggerAnnouncement:&node]);
// Handle nil with node set.
[object setSemanticsNode:&node];
XCTAssertFalse([object nodeShouldTriggerAnnouncement:nil]);
// Handle new node, still has live region, same label.
XCTAssertFalse([object nodeShouldTriggerAnnouncement:&node]);
// Handle update node with new label, still has live region.
flutter::SemanticsNode updatedNode;
updatedNode.flags = static_cast<int32_t>(flutter::SemanticsFlags::kIsLiveRegion);
updatedNode.label = "bar";
XCTAssertTrue([object nodeShouldTriggerAnnouncement:&updatedNode]);
// Handle dropping the live region flag.
updatedNode.flags = 0;
XCTAssertFalse([object nodeShouldTriggerAnnouncement:&updatedNode]);
// Handle adding the flag when the label has not changed.
updatedNode.label = "foo";
[object setSemanticsNode:&updatedNode];
XCTAssertTrue([object nodeShouldTriggerAnnouncement:&node]);
}
- (void)testShouldDispatchShowOnScreenActionForHeader {
fml::WeakPtrFactory<flutter::MockAccessibilityBridge> factory(
new flutter::MockAccessibilityBridge());
fml::WeakPtr<flutter::MockAccessibilityBridge> bridge = factory.GetWeakPtr();
SemanticsObject* object = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
// Handle initial setting of node with header.
flutter::SemanticsNode node;
node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kIsHeader);
node.label = "foo";
[object setSemanticsNode:&node];
// Simulate accessibility focus.
[object accessibilityElementDidBecomeFocused];
XCTAssertTrue(bridge->observations.size() == 1);
XCTAssertTrue(bridge->observations[0].id == 1);
XCTAssertTrue(bridge->observations[0].action == flutter::SemanticsAction::kShowOnScreen);
}
- (void)testShouldDispatchShowOnScreenActionForHidden {
fml::WeakPtrFactory<flutter::MockAccessibilityBridge> factory(
new flutter::MockAccessibilityBridge());
fml::WeakPtr<flutter::MockAccessibilityBridge> bridge = factory.GetWeakPtr();
SemanticsObject* object = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
// Handle initial setting of node with hidden.
flutter::SemanticsNode node;
node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kIsHidden);
node.label = "foo";
[object setSemanticsNode:&node];
// Simulate accessibility focus.
[object accessibilityElementDidBecomeFocused];
XCTAssertTrue(bridge->observations.size() == 1);
XCTAssertTrue(bridge->observations[0].id == 1);
XCTAssertTrue(bridge->observations[0].action == flutter::SemanticsAction::kShowOnScreen);
}
- (void)testSemanticsObjectAndPlatformViewSemanticsContainerDontHaveRetainCycle {
fml::WeakPtrFactory<flutter::MockAccessibilityBridge> factory(
new flutter::MockAccessibilityBridge());
fml::WeakPtr<flutter::MockAccessibilityBridge> bridge = factory.GetWeakPtr();
SemanticsObject* object = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
object.platformViewSemanticsContainer =
[[FlutterPlatformViewSemanticsContainer alloc] initWithSemanticsObject:object];
__weak SemanticsObject* weakObject = object;
object = nil;
XCTAssertNil(weakObject);
}
- (void)testFlutterSwitchSemanticsObjectForwardsCalls {
SemanticsObject* mockSemanticsObject = OCMClassMock([SemanticsObject class]);
FlutterSwitchSemanticsObject* switchObj =
[[FlutterSwitchSemanticsObject alloc] initWithSemanticsObject:mockSemanticsObject];
OCMStub([mockSemanticsObject accessibilityActivate]).andReturn(YES);
OCMStub([mockSemanticsObject accessibilityScroll:UIAccessibilityScrollDirectionRight])
.andReturn(NO);
OCMStub([mockSemanticsObject accessibilityPerformEscape]).andReturn(YES);
XCTAssertTrue([switchObj accessibilityActivate]);
OCMVerify([mockSemanticsObject accessibilityActivate]);
[switchObj accessibilityIncrement];
OCMVerify([mockSemanticsObject accessibilityIncrement]);
[switchObj accessibilityDecrement];
OCMVerify([mockSemanticsObject accessibilityDecrement]);
XCTAssertFalse([switchObj accessibilityScroll:UIAccessibilityScrollDirectionRight]);
OCMVerify([mockSemanticsObject accessibilityScroll:UIAccessibilityScrollDirectionRight]);
XCTAssertTrue([switchObj accessibilityPerformEscape]);
OCMVerify([mockSemanticsObject accessibilityPerformEscape]);
[switchObj accessibilityElementDidBecomeFocused];
OCMVerify([mockSemanticsObject accessibilityElementDidBecomeFocused]);
[switchObj accessibilityElementDidLoseFocus];
OCMVerify([mockSemanticsObject accessibilityElementDidLoseFocus]);
}
@end