Flutter iOS Embedder
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
FlutterUndoManagerPluginTest.mm
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
6 
7 #import <OCMock/OCMock.h>
8 #import <XCTest/XCTest.h>
9 
12 
14 
16 
17 @property(readonly) NSUInteger undoCount;
18 @property(readonly) NSUInteger redoCount;
19 @property(nonatomic, nullable) NSUndoManager* undoManager;
20 @property(nonatomic, nullable) UIView<UITextInput>* activeTextInputView;
21 
22 - (instancetype)initWithUndoManager:(NSUndoManager*)undoManager
23  activeTextInputView:(UIView<UITextInput>*)activeTextInputView;
24 
25 @end
26 
27 @implementation FakeFlutterUndoManagerDelegate
28 
29 - (instancetype)initWithUndoManager:(NSUndoManager*)undoManager
30  activeTextInputView:(UIView<UITextInput>*)activeTextInputView {
31  self = [super init];
32  if (self) {
33  _undoManager = undoManager;
34  _activeTextInputView = activeTextInputView;
35  }
36  return self;
37 }
38 
39 - (void)handleUndoWithDirection:(FlutterUndoRedoDirection)direction {
40  if (direction == FlutterUndoRedoDirectionUndo) {
41  _undoCount++;
42  } else {
43  _redoCount++;
44  }
45 }
46 
47 @end
48 
49 @interface FlutterUndoManagerPluginTest : XCTestCase
50 @property(nonatomic) FakeFlutterUndoManagerDelegate* undoManagerDelegate;
51 @property(nonatomic) FlutterUndoManagerPlugin* undoManagerPlugin;
52 @property(nonatomic) UIView<UITextInput>* activeTextInputView;
53 @property(nonatomic) NSUndoManager* undoManager;
54 @end
55 
56 @implementation FlutterUndoManagerPluginTest
57 
58 - (void)setUp {
59  [super setUp];
60 
61  self.undoManager = OCMClassMock([NSUndoManager class]);
62  self.activeTextInputView = OCMClassMock([FlutterTextInputView class]);
63 
64  self.undoManagerDelegate =
65  [[FakeFlutterUndoManagerDelegate alloc] initWithUndoManager:self.undoManager
66  activeTextInputView:self.activeTextInputView];
67 
68  self.undoManagerPlugin =
69  [[FlutterUndoManagerPlugin alloc] initWithDelegate:self.undoManagerDelegate];
70 }
71 
72 - (void)testSetUndoState {
73  __block int registerUndoCount = 0;
74  __block void (^undoHandler)(id target);
75  OCMStub([self.undoManager registerUndoWithTarget:self.undoManagerPlugin handler:[OCMArg any]])
76  .andDo(^(NSInvocation* invocation) {
77  registerUndoCount++;
78  __weak void (^handler)(id target);
79  [invocation retainArguments];
80  [invocation getArgument:&handler atIndex:3];
81  undoHandler = handler;
82  });
83  __block int removeAllActionsCount = 0;
84  OCMStub([self.undoManager removeAllActionsWithTarget:self.undoManagerPlugin])
85  .andDo(^(NSInvocation* invocation) {
86  removeAllActionsCount++;
87  });
88  __block int undoCount = 0;
89  OCMStub([self.undoManager undo]).andDo(^(NSInvocation* invocation) {
90  undoCount++;
91  undoHandler(self.undoManagerPlugin);
92  });
93 
94  // If canUndo and canRedo are false, only removeAllActionsWithTarget is called.
95  FlutterMethodCall* setUndoStateCall =
96  [FlutterMethodCall methodCallWithMethodName:@"UndoManager.setUndoState"
97  arguments:@{@"canUndo" : @NO, @"canRedo" : @NO}];
98  [self.undoManagerPlugin handleMethodCall:setUndoStateCall
99  result:^(id _Nullable result){
100  }];
101  XCTAssertEqual(1, removeAllActionsCount);
102  XCTAssertEqual(0, registerUndoCount);
103 
104  // If canUndo is true, an undo will be registered.
105  setUndoStateCall =
106  [FlutterMethodCall methodCallWithMethodName:@"UndoManager.setUndoState"
107  arguments:@{@"canUndo" : @YES, @"canRedo" : @NO}];
108  [self.undoManagerPlugin handleMethodCall:setUndoStateCall
109  result:^(id _Nullable result){
110  }];
111  XCTAssertEqual(2, removeAllActionsCount);
112  XCTAssertEqual(1, registerUndoCount);
113 
114  // Invoking the undo handler will invoke the handleUndo delegate method with "undo".
115  undoHandler(self.undoManagerPlugin);
116  XCTAssertEqual(1UL, self.undoManagerDelegate.undoCount);
117  XCTAssertEqual(0UL, self.undoManagerDelegate.redoCount);
118  XCTAssertEqual(2, registerUndoCount);
119 
120  // Invoking the redo handler will invoke the handleUndo delegate method with "redo".
121  undoHandler(self.undoManagerPlugin);
122  XCTAssertEqual(1UL, self.undoManagerDelegate.undoCount);
123  XCTAssertEqual(1UL, self.undoManagerDelegate.redoCount);
124  XCTAssertEqual(3, registerUndoCount);
125 
126  // If canRedo is true, an undo will be registered and undo will be called.
127  setUndoStateCall =
128  [FlutterMethodCall methodCallWithMethodName:@"UndoManager.setUndoState"
129  arguments:@{@"canUndo" : @NO, @"canRedo" : @YES}];
130  [self.undoManagerPlugin handleMethodCall:setUndoStateCall
131  result:^(id _Nullable result){
132  }];
133  XCTAssertEqual(3, removeAllActionsCount);
134  XCTAssertEqual(5, registerUndoCount);
135  XCTAssertEqual(1, undoCount);
136 
137  // Invoking the redo handler will invoke the handleUndo delegate method with "redo".
138  undoHandler(self.undoManagerPlugin);
139  XCTAssertEqual(1UL, self.undoManagerDelegate.undoCount);
140  XCTAssertEqual(2UL, self.undoManagerDelegate.redoCount);
141 }
142 
143 - (void)testSetUndoStateDoesInteractWithInputDelegate {
144  // Regression test for https://github.com/flutter/flutter/issues/133424
145  FlutterMethodCall* setUndoStateCall =
146  [FlutterMethodCall methodCallWithMethodName:@"UndoManager.setUndoState"
147  arguments:@{@"canUndo" : @NO, @"canRedo" : @NO}];
148  [self.undoManagerPlugin handleMethodCall:setUndoStateCall
149  result:^(id _Nullable result){
150  }];
151 
152  OCMVerify(never(), [self.activeTextInputView inputDelegate]);
153 }
154 
155 - (void)testDeallocRemovesAllUndoManagerActions {
156  __weak FlutterUndoManagerPlugin* weakUndoManagerPlugin;
157  // Use a real undo manager.
158  NSUndoManager* undoManager = [[NSUndoManager alloc] init];
159  @autoreleasepool {
160  id activeTextInputView = OCMClassMock([FlutterTextInputView class]);
161 
163  [[FakeFlutterUndoManagerDelegate alloc] initWithUndoManager:undoManager
164  activeTextInputView:activeTextInputView];
165 
167  [[FlutterUndoManagerPlugin alloc] initWithDelegate:undoManagerDelegate];
168  weakUndoManagerPlugin = undoManagerPlugin;
169 
170  FlutterMethodCall* setUndoStateCall =
171  [FlutterMethodCall methodCallWithMethodName:@"UndoManager.setUndoState"
172  arguments:@{@"canUndo" : @YES, @"canRedo" : @YES}];
173  [undoManagerPlugin handleMethodCall:setUndoStateCall
174  result:^(id _Nullable result){
175  }];
176  XCTAssertTrue(undoManager.canUndo);
177  XCTAssertTrue(undoManager.canRedo);
178  // Fake out the undoManager being nil, which happens when the FlutterViewController deallocs and
179  // the undo manager can't be fetched from the FlutterEngine delegate.
181  }
182  XCTAssertNil(weakUndoManagerPlugin);
183  // Regression test for https://github.com/flutter/flutter/issues/150408.
184  // Undo manager undo and redo stack should be empty after the plugin deallocs.
185  XCTAssertFalse(undoManager.canUndo);
186  XCTAssertFalse(undoManager.canRedo);
187 }
188 
189 @end
+[FlutterMethodCall methodCallWithMethodName:arguments:]
instancetype methodCallWithMethodName:arguments:(NSString *method,[arguments] id _Nullable arguments)
FlutterUndoManagerPluginTest::undoManagerDelegate
FakeFlutterUndoManagerDelegate * undoManagerDelegate
Definition: FlutterUndoManagerPluginTest.mm:50
FakeFlutterUndoManagerDelegate::undoManager
NSUndoManager * undoManager
Definition: FlutterUndoManagerPluginTest.mm:19
FlutterUndoManagerPlugin.h
FlutterTextInputPlugin.h
FakeFlutterUndoManagerDelegate::redoCount
NSUInteger redoCount
Definition: FlutterUndoManagerPluginTest.mm:18
FlutterMacros.h
FakeFlutterUndoManagerDelegate::activeTextInputView
UIView< UITextInput > * activeTextInputView
Definition: FlutterUndoManagerPluginTest.mm:20
FlutterUndoManagerPluginTest::undoManagerPlugin
FlutterUndoManagerPlugin * undoManagerPlugin
Definition: FlutterUndoManagerPluginTest.mm:51
FlutterUndoManagerPluginTest::undoManager
NSUndoManager * undoManager
Definition: FlutterUndoManagerPluginTest.mm:53
FakeFlutterUndoManagerDelegate::undoCount
NSUInteger undoCount
Definition: FlutterUndoManagerPluginTest.mm:17
-[FlutterUndoManagerPlugin handleMethodCall:result:]
void handleMethodCall:result:(FlutterMethodCall *call,[result] FlutterResult result)
Definition: FlutterUndoManagerPlugin.mm:38
FlutterTextInputView
Definition: FlutterTextInputPlugin.mm:809
FlutterMethodCall
Definition: FlutterCodecs.h:220
FlutterUndoManagerPluginTest
Definition: FlutterUndoManagerPluginTest.mm:49
FakeFlutterUndoManagerDelegate
Definition: FlutterUndoManagerPluginTest.mm:15
inputDelegate
id< UITextInputDelegate > inputDelegate
Definition: FlutterTextInputPlugin.h:141
FlutterUndoManagerDelegate-p
Definition: FlutterUndoManagerDelegate.h:23
FlutterUndoManagerPlugin
Definition: FlutterUndoManagerPlugin.h:13
FLUTTER_ASSERT_ARC
Definition: FlutterChannelKeyResponder.mm:13