[quick_actions] backfill some unit tests for `FLTQuickActionsPlugin` class (#6206)

diff --git a/packages/quick_actions/quick_actions_ios/CHANGELOG.md b/packages/quick_actions/quick_actions_ios/CHANGELOG.md
index 9235afa..53817b8 100644
--- a/packages/quick_actions/quick_actions_ios/CHANGELOG.md
+++ b/packages/quick_actions/quick_actions_ios/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.6.0+13
+
+* Adds some unit tests for `FLTQuickActionsPlugin` class. 
+
 ## 0.6.0+12
 
 * Adds a custom module map with a Test submodule for unit tests on iOS platform.
diff --git a/packages/quick_actions/quick_actions_ios/example/ios/Runner.xcodeproj/project.pbxproj b/packages/quick_actions/quick_actions_ios/example/ios/Runner.xcodeproj/project.pbxproj
index a517b09..b70dc29 100644
--- a/packages/quick_actions/quick_actions_ios/example/ios/Runner.xcodeproj/project.pbxproj
+++ b/packages/quick_actions/quick_actions_ios/example/ios/Runner.xcodeproj/project.pbxproj
@@ -18,6 +18,7 @@
 		97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
 		97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
 		97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
+		E0C09C29289C729D00E6977E /* FLTQuickActionsPluginTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C09C28289C729D00E6977E /* FLTQuickActionsPluginTests.m */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -76,6 +77,7 @@
 		97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		9D27FE1F0F21D4D47DDA16DE /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
 		C35AD3650AB6BF850E016715 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+		E0C09C28289C729D00E6977E /* FLTQuickActionsPluginTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLTQuickActionsPluginTests.m; sourceTree = "<group>"; };
 		F0609304FBCAEC2289164BD5 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
@@ -111,6 +113,7 @@
 			children = (
 				33E20B3426EFCDFC00A4A191 /* RunnerTests.m */,
 				33E20B3626EFCDFC00A4A191 /* Info.plist */,
+				E0C09C28289C729D00E6977E /* FLTQuickActionsPluginTests.m */,
 			);
 			path = RunnerTests;
 			sourceTree = "<group>";
@@ -413,6 +416,7 @@
 			buildActionMask = 2147483647;
 			files = (
 				33E20B3526EFCDFC00A4A191 /* RunnerTests.m in Sources */,
+				E0C09C29289C729D00E6977E /* FLTQuickActionsPluginTests.m in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
diff --git a/packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/FLTQuickActionsPluginTests.m b/packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/FLTQuickActionsPluginTests.m
new file mode 100644
index 0000000..0d4070b
--- /dev/null
+++ b/packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/FLTQuickActionsPluginTests.m
@@ -0,0 +1,156 @@
+// 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 Flutter;
+@import quick_actions_ios;
+@import quick_actions_ios.Test;
+@import XCTest;
+#import <OCMock/OCMock.h>
+
+@interface FLTQuickActionsPluginTests : XCTestCase
+
+@end
+
+@implementation FLTQuickActionsPluginTests
+
+// A dummy `UIApplicationShortcutItem`.
+- (UIApplicationShortcutItem *)searchTheThingShortcutItem {
+  return [[UIApplicationShortcutItem alloc]
+           initWithType:@"SearchTheThing"
+         localizedTitle:@"Search the thing"
+      localizedSubtitle:nil
+                   icon:[UIApplicationShortcutIcon
+                            iconWithTemplateImageName:@"search_the_thing.png"]
+               userInfo:nil];
+}
+
+// A dummy raw shortcut item.
+- (NSDictionary<NSString *, NSString *> *)searchTheThingRawItem {
+  return @{
+    @"type" : @"SearchTheThing",
+    @"localizedTitle" : @"Search the thing",
+    @"icon" : @"search_the_thing.png",
+  };
+}
+
+- (void)testHandleMethodCall_setShortcutItems {
+  FlutterMethodCall *call =
+      [FlutterMethodCall methodCallWithMethodName:@"setShortcutItems"
+                                        arguments:@[ [self searchTheThingRawItem] ]];
+
+  FLTQuickActionsPlugin *plugin =
+      [[FLTQuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class])];
+  XCTestExpectation *resultExpectation =
+      [self expectationWithDescription:@"result block must be called."];
+  [plugin handleMethodCall:call
+                    result:^(id _Nullable result) {
+                      XCTAssertNil(result, @"result block must be called with nil.");
+                      XCTAssertEqualObjects(UIApplication.sharedApplication.shortcutItems,
+                                            @[ [self searchTheThingShortcutItem] ],
+                                            @"shortcut items should be set correctly.");
+                      [resultExpectation fulfill];
+                    }];
+  [self waitForExpectationsWithTimeout:1 handler:nil];
+}
+
+- (void)testHandleMethodCall_clearShortcutItems {
+  FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"clearShortcutItems"
+                                                              arguments:nil];
+
+  FLTQuickActionsPlugin *plugin =
+      [[FLTQuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class])];
+  XCTestExpectation *resultExpectation =
+      [self expectationWithDescription:@"result block must be called."];
+  [plugin handleMethodCall:call
+                    result:^(id _Nullable result) {
+                      XCTAssertNil(result, @"result block must be called with nil.");
+                      XCTAssertEqual(UIApplication.sharedApplication.shortcutItems.count, 0,
+                                     @"shortcut items should be cleared");
+                      [resultExpectation fulfill];
+                    }];
+  [self waitForExpectationsWithTimeout:1 handler:nil];
+}
+
+- (void)testHandleMethodCall_getLaunchAction {
+  FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"getLaunchAction"
+                                                              arguments:nil];
+
+  FLTQuickActionsPlugin *plugin =
+      [[FLTQuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class])];
+  XCTestExpectation *resultExpectation =
+      [self expectationWithDescription:@"result block must be called."];
+  [plugin handleMethodCall:call
+                    result:^(id _Nullable result) {
+                      XCTAssertNil(result, @"result block must be called with nil.");
+                      [resultExpectation fulfill];
+                    }];
+  [self waitForExpectationsWithTimeout:1 handler:nil];
+}
+
+- (void)testHandleMethodCall_nonExistMethods {
+  FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"nonExist" arguments:nil];
+
+  FLTQuickActionsPlugin *plugin =
+      [[FLTQuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class])];
+  XCTestExpectation *resultExpectation =
+      [self expectationWithDescription:@"result must be called."];
+  [plugin
+      handleMethodCall:call
+                result:^(id _Nullable result) {
+                  XCTAssertEqual(result, FlutterMethodNotImplemented,
+                                 @"result block must be called with FlutterMethodNotImplemented");
+                  [resultExpectation fulfill];
+                }];
+
+  [self waitForExpectationsWithTimeout:1 handler:nil];
+}
+
+- (void)testApplicationPerformActionForShortcutItem {
+  id mockChannel = OCMClassMock([FlutterMethodChannel class]);
+  FLTQuickActionsPlugin *plugin = [[FLTQuickActionsPlugin alloc] initWithChannel:mockChannel];
+
+  UIApplicationShortcutItem *item = [self searchTheThingShortcutItem];
+
+  BOOL actionResult = [plugin application:[UIApplication sharedApplication]
+             performActionForShortcutItem:item
+                        completionHandler:^(BOOL succeeded){/* no-op */}];
+  XCTAssert(actionResult, @"performActionForShortcutItem must return true.");
+  OCMVerify([mockChannel invokeMethod:@"launch" arguments:item.type]);
+}
+
+- (void)testApplicationDidFinishLaunchingWithOptions_launchWithShortcut {
+  FLTQuickActionsPlugin *plugin =
+      [[FLTQuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class])];
+
+  UIApplicationShortcutItem *item = [self searchTheThingShortcutItem];
+
+  BOOL launchResult = [plugin application:[UIApplication sharedApplication]
+            didFinishLaunchingWithOptions:@{UIApplicationLaunchOptionsShortcutItemKey : item}];
+
+  XCTAssertFalse(launchResult,
+                 @"didFinishLaunchingWithOptions must return false if launched from shortcut.");
+}
+
+- (void)testApplicationDidFinishLaunchingWithOptions_launchWithoutShortcut {
+  FLTQuickActionsPlugin *plugin =
+      [[FLTQuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class])];
+  BOOL launchResult = [plugin application:[UIApplication sharedApplication]
+            didFinishLaunchingWithOptions:@{}];
+  XCTAssertTrue(launchResult,
+                @"didFinishLaunchingWithOptions must return true if not launched from shortcut.");
+}
+
+- (void)testApplicationDidBecomeActive {
+  UIApplicationShortcutItem *item = [self searchTheThingShortcutItem];
+  id mockChannel = OCMClassMock([FlutterMethodChannel class]);
+  FLTQuickActionsPlugin *plugin = [[FLTQuickActionsPlugin alloc] initWithChannel:mockChannel];
+  plugin.launchingShortcutType = item.type;
+  [plugin applicationDidBecomeActive:[UIApplication sharedApplication]];
+
+  OCMVerify([mockChannel invokeMethod:@"launch" arguments:item.type]);
+  XCTAssertNil(plugin.launchingShortcutType,
+               @"Must reset launchingShortcutType to nil after being used.");
+}
+
+@end
diff --git a/packages/quick_actions/quick_actions_ios/ios/Classes/FLTQuickActionsPlugin.m b/packages/quick_actions/quick_actions_ios/ios/Classes/FLTQuickActionsPlugin.m
index dc1c866..798af0a 100644
--- a/packages/quick_actions/quick_actions_ios/ios/Classes/FLTQuickActionsPlugin.m
+++ b/packages/quick_actions/quick_actions_ios/ios/Classes/FLTQuickActionsPlugin.m
@@ -9,7 +9,6 @@
 
 @interface FLTQuickActionsPlugin ()
 @property(nonatomic, retain) FlutterMethodChannel *channel;
-@property(nonatomic, retain) NSString *shortcutType;
 @end
 
 @implementation FLTQuickActionsPlugin
@@ -65,7 +64,7 @@
     // Keep hold of the shortcut type and handle it in the
     // `applicationDidBecomeActure:` method once the Dart MethodChannel
     // is initialized.
-    self.shortcutType = shortcutItem.type;
+    self.launchingShortcutType = shortcutItem.type;
 
     // Return NO to indicate we handled the quick action to ensure
     // the `application:performActionFor:` method is not called (as
@@ -77,9 +76,9 @@
 }
 
 - (void)applicationDidBecomeActive:(UIApplication *)application {
-  if (self.shortcutType) {
-    [self handleShortcut:self.shortcutType];
-    self.shortcutType = nil;
+  if (self.launchingShortcutType) {
+    [self handleShortcut:self.launchingShortcutType];
+    self.launchingShortcutType = nil;
   }
 }
 
diff --git a/packages/quick_actions/quick_actions_ios/ios/Classes/PrivateHeaders/FLTQuickActionsPlugin_Test.h b/packages/quick_actions/quick_actions_ios/ios/Classes/PrivateHeaders/FLTQuickActionsPlugin_Test.h
index c5fa43d..bd2d756 100644
--- a/packages/quick_actions/quick_actions_ios/ios/Classes/PrivateHeaders/FLTQuickActionsPlugin_Test.h
+++ b/packages/quick_actions/quick_actions_ios/ios/Classes/PrivateHeaders/FLTQuickActionsPlugin_Test.h
@@ -7,7 +7,11 @@
 NS_ASSUME_NONNULL_BEGIN
 
 /// APIs exposed for unit tests.
-@interface FLTQuickActionsPlugin (Test)
+@interface FLTQuickActionsPlugin ()
+
+/// The type of the shortcut item selected when launching the app.
+/// API exposed for unit tests.
+@property(nonatomic, strong, nullable) NSString *launchingShortcutType;
 
 /// Initializes a FLTQuickActionsPlugin with the given method channel.
 /// API exposed for unit tests.
diff --git a/packages/quick_actions/quick_actions_ios/pubspec.yaml b/packages/quick_actions/quick_actions_ios/pubspec.yaml
index 08ea406..42dbde3 100644
--- a/packages/quick_actions/quick_actions_ios/pubspec.yaml
+++ b/packages/quick_actions/quick_actions_ios/pubspec.yaml
@@ -2,7 +2,7 @@
 description: An implementation for the iOS platform of the Flutter `quick_actions` plugin.
 repository: https://github.com/flutter/plugins/tree/main/packages/quick_actions/quick_actions_ios
 issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22
-version: 0.6.0+12
+version: 0.6.0+13
 
 environment:
   sdk: ">=2.15.0 <3.0.0"