blob: 8b23f8d9369f09ba2e24727aa7c4ec6d9b07d225 [file] [log] [blame]
#import <XCTest/XCTest.h>
#include "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/SemanticsObject.h"
#import "third_party/ocmock/Source/OCMock/OCMock.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 AccessibilityFocusDidChange(int32_t id) override {}
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)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);
}
@end