FIRIAMDisplayExecutorTests.m 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943
  1. /*
  2. * Copyright 2017 Google
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import <OCMock/OCMock.h>
  17. #import <XCTest/XCTest.h>
  18. #import "FIRIAMActionURLFollower.h"
  19. #import "FIRIAMDisplayExecutor.h"
  20. #import "FIRIAMDisplayTriggerDefinition.h"
  21. #import "FIRIAMMessageContentData.h"
  22. #import "FIRInAppMessaging.h"
  23. // A class implementing protocol FIRIAMMessageContentData to be used for unit testing
  24. @interface FIRIAMMessageContentDataForTesting : NSObject <FIRIAMMessageContentData>
  25. @property(nonatomic, readwrite, nonnull) NSString *titleText;
  26. @property(nonatomic, readwrite, nonnull) NSString *bodyText;
  27. @property(nonatomic, nullable) NSString *actionButtonText;
  28. @property(nonatomic, nullable) NSString *secondaryActionButtonText;
  29. @property(nonatomic, nullable) NSURL *actionURL;
  30. @property(nonatomic, nullable) NSURL *secondaryActionURL;
  31. @property(nonatomic, nullable) NSURL *imageURL;
  32. @property(nonatomic, nullable) NSURL *landscapeImageURL;
  33. @property BOOL errorEncountered;
  34. - (instancetype)initWithMessageTitle:(NSString *)title
  35. messageBody:(NSString *)body
  36. actionButtonText:(nullable NSString *)actionButtonText
  37. secondaryActionButtonText:(nullable NSString *)secondaryActionButtonText
  38. actionURL:(nullable NSURL *)actionURL
  39. secondaryActionURL:(nullable NSURL *)secondaryActionURL
  40. imageURL:(nullable NSURL *)imageURL
  41. landscapeImageURL:(nullable NSURL *)landscapeImageURL
  42. hasImageError:(BOOL)hasImageError;
  43. @end
  44. @implementation FIRIAMMessageContentDataForTesting
  45. - (instancetype)initWithMessageTitle:(NSString *)title
  46. messageBody:(NSString *)body
  47. actionButtonText:(nullable NSString *)actionButtonText
  48. secondaryActionButtonText:(nullable NSString *)secondaryActionButtonText
  49. actionURL:(nullable NSURL *)actionURL
  50. secondaryActionURL:(nullable NSURL *)secondaryActionURL
  51. imageURL:(nullable NSURL *)imageURL
  52. landscapeImageURL:(nullable NSURL *)landscapeImageURL
  53. hasImageError:(BOOL)hasImageError {
  54. if (self = [super init]) {
  55. _titleText = title;
  56. _bodyText = body;
  57. _imageURL = imageURL;
  58. _landscapeImageURL = landscapeImageURL;
  59. _actionButtonText = actionButtonText;
  60. _secondaryActionButtonText = secondaryActionButtonText;
  61. _actionURL = actionURL;
  62. _secondaryActionURL = secondaryActionURL;
  63. _errorEncountered = hasImageError;
  64. }
  65. return self;
  66. }
  67. - (void)loadImageDataWithBlock:(void (^)(NSData *_Nullable imageData,
  68. NSData *_Nullable landscapeImageData,
  69. NSError *_Nullable error))block {
  70. if (self.errorEncountered) {
  71. block(nil, nil, [NSError errorWithDomain:@"image error" code:0 userInfo:nil]);
  72. } else {
  73. NSData *imageData = [@"image data" dataUsingEncoding:NSUTF8StringEncoding];
  74. NSData *landscapeImageData = [@"landscape image data" dataUsingEncoding:NSUTF8StringEncoding];
  75. block(imageData, landscapeImageData, nil);
  76. }
  77. }
  78. @end
  79. // Defines how the message display component triggers the delegate in unit testing.
  80. typedef NS_ENUM(NSInteger, FIRInAppMessagingDelegateInteraction) {
  81. // Message display component triggers messageDismissedWithType:.
  82. FIRInAppMessagingDelegateInteractionDismiss,
  83. // Message display component triggers messageClicked:.
  84. FIRInAppMessagingDelegateInteractionClick,
  85. // Message display component triggers displayErrorEncountered:.
  86. FIRInAppMessagingDelegateInteractionError,
  87. // Message has finished a valid impression, but it's not getting closed by the user.
  88. FIRInAppMessagingDelegateInteractionImpressionDetected,
  89. };
  90. // A class implementing protocol FIRInAppMessagingDisplay to be used for unit testing
  91. @interface FIRIAMMessageDisplayForTesting : NSObject <FIRInAppMessagingDisplay>
  92. @property FIRInAppMessagingDelegateInteraction delegateInteraction;
  93. @property(nonatomic, nullable, copy) FIRInAppMessagingAction *action;
  94. // used for interaction verification
  95. @property FIRInAppMessagingDisplayMessage *message;
  96. - (instancetype)initWithDelegateInteraction:(FIRInAppMessagingDelegateInteraction)interaction
  97. action:(nullable FIRInAppMessagingAction *)actionURL;
  98. - (instancetype)initWithDelegateInteraction:(FIRInAppMessagingDelegateInteraction)interaction;
  99. @end
  100. @implementation FIRIAMMessageDisplayForTesting
  101. - (instancetype)initWithDelegateInteraction:(FIRInAppMessagingDelegateInteraction)interaction
  102. action:(nullable FIRInAppMessagingAction *)action {
  103. if (self = [super init]) {
  104. _delegateInteraction = interaction;
  105. _action = action;
  106. }
  107. return self;
  108. }
  109. - (instancetype)initWithDelegateInteraction:(FIRInAppMessagingDelegateInteraction)interaction {
  110. return [self initWithDelegateInteraction:interaction action:nil];
  111. }
  112. - (void)displayMessage:(FIRInAppMessagingDisplayMessage *)messageForDisplay
  113. displayDelegate:(id<FIRInAppMessagingDisplayDelegate>)displayDelegate {
  114. self.message = messageForDisplay;
  115. switch (self.delegateInteraction) {
  116. case FIRInAppMessagingDelegateInteractionClick:
  117. [displayDelegate messageClicked:messageForDisplay withAction:self.action];
  118. break;
  119. case FIRInAppMessagingDelegateInteractionDismiss:
  120. [displayDelegate messageDismissed:messageForDisplay
  121. dismissType:FIRInAppMessagingDismissTypeAuto];
  122. break;
  123. case FIRInAppMessagingDelegateInteractionError:
  124. [displayDelegate displayErrorForMessage:messageForDisplay
  125. error:[NSError errorWithDomain:NSURLErrorDomain
  126. code:0
  127. userInfo:nil]];
  128. break;
  129. case FIRInAppMessagingDelegateInteractionImpressionDetected:
  130. [displayDelegate impressionDetectedForMessage:messageForDisplay];
  131. break;
  132. }
  133. }
  134. @end
  135. @interface FIRInAppMessagingDisplayTestDelegate : NSObject <FIRInAppMessagingDisplayDelegate>
  136. @property(nonatomic) BOOL receivedMessageErrorCallback;
  137. @property(nonatomic) BOOL receivedMessageImpressionCallback;
  138. @property(nonatomic) BOOL receivedMessageClickedCallback;
  139. @property(nonatomic) BOOL receivedMessageDismissedCallback;
  140. @end
  141. @implementation FIRInAppMessagingDisplayTestDelegate
  142. - (void)displayErrorForMessage:(nonnull FIRInAppMessagingDisplayMessage *)inAppMessage
  143. error:(nonnull NSError *)error {
  144. self.receivedMessageErrorCallback = YES;
  145. }
  146. - (void)impressionDetectedForMessage:(nonnull FIRInAppMessagingDisplayMessage *)inAppMessage {
  147. self.receivedMessageImpressionCallback = YES;
  148. }
  149. - (void)messageClicked:(FIRInAppMessagingDisplayMessage *)inAppMessage
  150. withAction:(FIRInAppMessagingAction *)action {
  151. self.receivedMessageClickedCallback = YES;
  152. }
  153. - (void)messageDismissed:(nonnull FIRInAppMessagingDisplayMessage *)inAppMessage
  154. dismissType:(FIRInAppMessagingDismissType)dismissType {
  155. self.receivedMessageDismissedCallback = YES;
  156. }
  157. @end
  158. @interface FIRIAMDisplayExecutor (Testing)
  159. - (FIRInAppMessagingDisplayMessage *)
  160. displayMessageWithMessageDefinition:(FIRIAMMessageDefinition *)definition
  161. imageData:(FIRInAppMessagingImageData *)imageData
  162. landscapeImageData:(nullable FIRInAppMessagingImageData *)landscapeImageData
  163. triggerType:(FIRInAppMessagingDisplayTriggerType)triggerType;
  164. @end
  165. @interface FIRIAMDisplayExecutorTests : XCTestCase
  166. @property(nonatomic) FIRIAMDisplaySetting *displaySetting;
  167. @property FIRIAMMessageClientCache *clientMessageCache;
  168. @property id<FIRIAMBookKeeper> mockBookkeeper;
  169. @property id<FIRIAMTimeFetcher> mockTimeFetcher;
  170. @property FIRIAMDisplayExecutor *displayExecutor;
  171. @property FIRIAMActivityLogger *mockActivityLogger;
  172. @property FIRInAppMessaging *mockInAppMessaging;
  173. @property id<FIRIAMAnalyticsEventLogger> mockAnalyticsEventLogger;
  174. @property FIRIAMActionURLFollower *mockActionURLFollower;
  175. @property id<FIRInAppMessagingDisplay> mockMessageDisplayComponent;
  176. // three pre-defined messages
  177. @property FIRIAMMessageDefinition *m1, *m2, *m3, *m4;
  178. @end
  179. @implementation FIRIAMDisplayExecutorTests
  180. - (void)setupMessageTexture {
  181. // startTime, endTime here ensures messages with them are active
  182. NSTimeInterval activeStartTime = 0;
  183. NSTimeInterval activeEndTime = [[NSDate date] timeIntervalSince1970] + 10000;
  184. // m1 & m3 will be of contextual trigger
  185. FIRIAMDisplayTriggerDefinition *contextualTriggerDefinition =
  186. [[FIRIAMDisplayTriggerDefinition alloc] initWithFirebaseAnalyticEvent:@"test_event"];
  187. // m2 and m4 will be of app open trigger
  188. FIRIAMDisplayTriggerDefinition *appOpentriggerDefinition =
  189. [[FIRIAMDisplayTriggerDefinition alloc] initForAppForegroundTrigger];
  190. FIRIAMMessageContentDataForTesting *m1ContentData = [[FIRIAMMessageContentDataForTesting alloc]
  191. initWithMessageTitle:@"m1 title"
  192. messageBody:@"message body"
  193. actionButtonText:nil
  194. secondaryActionButtonText:nil
  195. actionURL:[NSURL URLWithString:@"http://google.com"]
  196. secondaryActionURL:nil
  197. imageURL:[NSURL URLWithString:@"https://google.com/image"]
  198. landscapeImageURL:nil
  199. hasImageError:NO];
  200. FIRIAMRenderingEffectSetting *renderSetting1 =
  201. [FIRIAMRenderingEffectSetting getDefaultRenderingEffectSetting];
  202. renderSetting1.viewMode = FIRIAMRenderAsBannerView;
  203. FIRIAMMessageRenderData *renderData1 =
  204. [[FIRIAMMessageRenderData alloc] initWithMessageID:@"m1"
  205. messageName:@"name"
  206. contentData:m1ContentData
  207. renderingEffect:renderSetting1];
  208. self.m1 = [[FIRIAMMessageDefinition alloc] initWithRenderData:renderData1
  209. startTime:activeStartTime
  210. endTime:activeEndTime
  211. triggerDefinition:@[ contextualTriggerDefinition ]];
  212. FIRIAMMessageContentDataForTesting *m2ContentData = [[FIRIAMMessageContentDataForTesting alloc]
  213. initWithMessageTitle:@"m2 title"
  214. messageBody:@"message body"
  215. actionButtonText:nil
  216. secondaryActionButtonText:nil
  217. actionURL:[NSURL URLWithString:@"http://google.com"]
  218. secondaryActionURL:nil
  219. imageURL:[NSURL URLWithString:@"https://unsplash.it/300/400"]
  220. landscapeImageURL:nil
  221. hasImageError:NO];
  222. FIRIAMRenderingEffectSetting *renderSetting2 =
  223. [FIRIAMRenderingEffectSetting getDefaultRenderingEffectSetting];
  224. renderSetting2.viewMode = FIRIAMRenderAsModalView;
  225. FIRIAMMessageRenderData *renderData2 =
  226. [[FIRIAMMessageRenderData alloc] initWithMessageID:@"m2"
  227. messageName:@"name"
  228. contentData:m2ContentData
  229. renderingEffect:renderSetting2];
  230. self.m2 = [[FIRIAMMessageDefinition alloc] initWithRenderData:renderData2
  231. startTime:activeStartTime
  232. endTime:activeEndTime
  233. triggerDefinition:@[ appOpentriggerDefinition ]];
  234. FIRIAMMessageContentDataForTesting *m3ContentData = [[FIRIAMMessageContentDataForTesting alloc]
  235. initWithMessageTitle:@"m3 title"
  236. messageBody:@"message body"
  237. actionButtonText:nil
  238. secondaryActionButtonText:nil
  239. actionURL:[NSURL URLWithString:@"http://google.com"]
  240. secondaryActionURL:nil
  241. imageURL:[NSURL URLWithString:@"https://google.com/image"]
  242. landscapeImageURL:nil
  243. hasImageError:NO];
  244. FIRIAMRenderingEffectSetting *renderSetting3 =
  245. [FIRIAMRenderingEffectSetting getDefaultRenderingEffectSetting];
  246. renderSetting3.viewMode = FIRIAMRenderAsImageOnlyView;
  247. FIRIAMMessageRenderData *renderData3 =
  248. [[FIRIAMMessageRenderData alloc] initWithMessageID:@"m3"
  249. messageName:@"name"
  250. contentData:m3ContentData
  251. renderingEffect:renderSetting3];
  252. self.m3 = [[FIRIAMMessageDefinition alloc] initWithRenderData:renderData3
  253. startTime:activeStartTime
  254. endTime:activeEndTime
  255. triggerDefinition:@[ contextualTriggerDefinition ]];
  256. FIRIAMMessageContentDataForTesting *m4ContentData = [[FIRIAMMessageContentDataForTesting alloc]
  257. initWithMessageTitle:@"m4 title"
  258. messageBody:@"message body"
  259. actionButtonText:nil
  260. secondaryActionButtonText:nil
  261. actionURL:[NSURL URLWithString:@"http://google.com"]
  262. secondaryActionURL:nil
  263. imageURL:[NSURL URLWithString:@"https://google.com/image"]
  264. landscapeImageURL:nil
  265. hasImageError:NO];
  266. FIRIAMRenderingEffectSetting *renderSetting4 =
  267. [FIRIAMRenderingEffectSetting getDefaultRenderingEffectSetting];
  268. renderSetting4.viewMode = FIRIAMRenderAsImageOnlyView;
  269. FIRIAMMessageRenderData *renderData4 =
  270. [[FIRIAMMessageRenderData alloc] initWithMessageID:@"m4"
  271. messageName:@"name"
  272. contentData:m4ContentData
  273. renderingEffect:renderSetting4];
  274. self.m4 = [[FIRIAMMessageDefinition alloc] initWithRenderData:renderData4
  275. startTime:activeStartTime
  276. endTime:activeEndTime
  277. triggerDefinition:@[ appOpentriggerDefinition ]
  278. appData:@{@"a" : @"b", @"up" : @"dog"}
  279. isTestMessage:NO];
  280. }
  281. NSTimeInterval DISPLAY_MIN_INTERVALS = 1;
  282. - (void)setUp {
  283. [super setUp];
  284. [self setupMessageTexture];
  285. self.displaySetting = [[FIRIAMDisplaySetting alloc] init];
  286. self.displaySetting.displayMinIntervalInMinutes = DISPLAY_MIN_INTERVALS;
  287. self.mockBookkeeper = OCMProtocolMock(@protocol(FIRIAMBookKeeper));
  288. FIRIAMFetchResponseParser *parser =
  289. [[FIRIAMFetchResponseParser alloc] initWithTimeFetcher:[[FIRIAMTimerWithNSDate alloc] init]];
  290. self.clientMessageCache = [[FIRIAMMessageClientCache alloc] initWithBookkeeper:self.mockBookkeeper
  291. usingResponseParser:parser];
  292. self.mockTimeFetcher = OCMProtocolMock(@protocol(FIRIAMTimeFetcher));
  293. self.mockActivityLogger = OCMClassMock([FIRIAMActivityLogger class]);
  294. self.mockAnalyticsEventLogger = OCMProtocolMock(@protocol(FIRIAMAnalyticsEventLogger));
  295. self.mockInAppMessaging = OCMClassMock([FIRInAppMessaging class]);
  296. self.mockActionURLFollower = OCMClassMock([FIRIAMActionURLFollower class]);
  297. self.displayExecutor =
  298. [[FIRIAMDisplayExecutor alloc] initWithInAppMessaging:self.mockInAppMessaging
  299. setting:self.displaySetting
  300. messageCache:self.clientMessageCache
  301. timeFetcher:self.mockTimeFetcher
  302. bookKeeper:self.mockBookkeeper
  303. actionURLFollower:self.mockActionURLFollower
  304. activityLogger:self.mockActivityLogger
  305. analyticsEventLogger:self.mockAnalyticsEventLogger];
  306. OCMStub([self.mockBookkeeper recordNewImpressionForMessage:[OCMArg any]
  307. withStartTimestampInSeconds:1000]);
  308. }
  309. - (void)testRegularMessageAvailableCase {
  310. // This setup allows next message to be displayed from display interval perspective.
  311. OCMStub([self.mockTimeFetcher currentTimestampInSeconds])
  312. .andReturn(DISPLAY_MIN_INTERVALS * 60 + 100);
  313. FIRIAMMessageDisplayForTesting *display = [[FIRIAMMessageDisplayForTesting alloc]
  314. initWithDelegateInteraction:FIRInAppMessagingDelegateInteractionClick];
  315. self.displayExecutor.messageDisplayComponent = display;
  316. [self.clientMessageCache setMessageData:@[ self.m2, self.m4 ]];
  317. [self.displayExecutor checkAndDisplayNextAppForegroundMessage];
  318. NSInteger remainingMsgCount = [self.clientMessageCache allRegularMessages].count;
  319. XCTAssertEqual(1, remainingMsgCount);
  320. // Verify that the message content handed to display component is expected
  321. XCTAssertEqualObjects(self.m2.renderData.messageID, display.message.campaignInfo.messageID);
  322. }
  323. - (void)testFollowingActionURL {
  324. // This setup allows next message to be displayed from display interval perspective.
  325. OCMStub([self.mockTimeFetcher currentTimestampInSeconds])
  326. .andReturn(DISPLAY_MIN_INTERVALS * 60 + 100);
  327. FIRInAppMessagingAction *testAction =
  328. [[FIRInAppMessagingAction alloc] initWithActionText:@"test"
  329. actionURL:self.m2.renderData.contentData.actionURL];
  330. FIRIAMMessageDisplayForTesting *display = [[FIRIAMMessageDisplayForTesting alloc]
  331. initWithDelegateInteraction:FIRInAppMessagingDelegateInteractionClick
  332. action:testAction];
  333. self.displayExecutor.messageDisplayComponent = display;
  334. [self.clientMessageCache setMessageData:@[ self.m2 ]];
  335. // not expecting triggering analytics recording
  336. OCMExpect([self.mockActionURLFollower
  337. followActionURL:[OCMArg isEqual:self.m2.renderData.contentData.actionURL]
  338. withCompletionBlock:[OCMArg any]]);
  339. [self.displayExecutor checkAndDisplayNextAppForegroundMessage];
  340. OCMVerifyAll((id)self.mockActionURLFollower);
  341. }
  342. - (void)testFollowingActionURLForTestMessage {
  343. // This setup allows next message to be displayed from display interval perspective.
  344. OCMStub([self.mockTimeFetcher currentTimestampInSeconds])
  345. .andReturn(DISPLAY_MIN_INTERVALS * 60 + 100);
  346. FIRIAMMessageDefinition *testMessage =
  347. [[FIRIAMMessageDefinition alloc] initTestMessageWithRenderData:self.m1.renderData];
  348. FIRInAppMessagingAction *testAction = [[FIRInAppMessagingAction alloc]
  349. initWithActionText:@"test"
  350. actionURL:testMessage.renderData.contentData.actionURL];
  351. FIRIAMMessageDisplayForTesting *display = [[FIRIAMMessageDisplayForTesting alloc]
  352. initWithDelegateInteraction:FIRInAppMessagingDelegateInteractionClick
  353. action:testAction];
  354. self.displayExecutor.messageDisplayComponent = display;
  355. [self.clientMessageCache setMessageData:@[ testMessage ]];
  356. // not expecting triggering analytics recording
  357. OCMExpect([self.mockActionURLFollower
  358. followActionURL:[OCMArg isEqual:testMessage.renderData.contentData.actionURL]
  359. withCompletionBlock:[OCMArg any]]);
  360. [self.displayExecutor checkAndDisplayNextAppForegroundMessage];
  361. OCMVerifyAll((id)self.mockActionURLFollower);
  362. }
  363. - (void)testClientTestMessageAvailableCase {
  364. // When test message is present in cache, even if the display time interval has not been
  365. // reached, we still render.
  366. // 10 seconds is less than DISPLAY_MIN_INTERVALS minutes, so we have not reached
  367. // minimal display time interval yet.
  368. OCMStub([self.mockTimeFetcher currentTimestampInSeconds]).andReturn(10);
  369. FIRIAMMessageDefinition *testMessage =
  370. [[FIRIAMMessageDefinition alloc] initTestMessageWithRenderData:self.m1.renderData];
  371. [self.clientMessageCache setMessageData:@[ self.m2, testMessage, self.m4 ]];
  372. // We have test message in the cache now.
  373. XCTAssertTrue([self.clientMessageCache hasTestMessage]);
  374. FIRIAMMessageDisplayForTesting *display = [[FIRIAMMessageDisplayForTesting alloc]
  375. initWithDelegateInteraction:FIRInAppMessagingDelegateInteractionClick];
  376. self.displayExecutor.messageDisplayComponent = display;
  377. [self.displayExecutor checkAndDisplayNextAppForegroundMessage];
  378. // No more test message in the cache now.
  379. XCTAssertFalse([self.clientMessageCache hasTestMessage]);
  380. }
  381. // If a message is still being displayed, we won't try to display a second one on top of it
  382. - (void)testNoDualDisplay {
  383. // This setup allows next message to be displayed from display interval perspective.
  384. OCMStub([self.mockTimeFetcher currentTimestampInSeconds])
  385. .andReturn(DISPLAY_MIN_INTERVALS * 60 + 100);
  386. // This display component only detects a valid impression, but does not end the rendering
  387. FIRIAMMessageDisplayForTesting *display = [[FIRIAMMessageDisplayForTesting alloc]
  388. initWithDelegateInteraction:FIRInAppMessagingDelegateInteractionImpressionDetected];
  389. self.displayExecutor.messageDisplayComponent = display;
  390. [self.clientMessageCache setMessageData:@[ self.m2, self.m4 ]];
  391. [self.displayExecutor checkAndDisplayNextAppForegroundMessage];
  392. // m2 is being rendered
  393. XCTAssertEqualObjects(self.m2.renderData.messageID, display.message.campaignInfo.messageID);
  394. NSInteger remainingMsgCount = [self.clientMessageCache allRegularMessages].count;
  395. XCTAssertEqual(1, remainingMsgCount);
  396. // try to display again when the in-display flag is already turned on (and not turned off yet)
  397. [self.displayExecutor checkAndDisplayNextAppForegroundMessage];
  398. // Verify that the message in display component is still m2
  399. XCTAssertEqualObjects(self.m2.renderData.messageID, display.message.campaignInfo.messageID);
  400. // message in cache remain unchanged for the second checkAndDisplayNext call
  401. remainingMsgCount = [self.clientMessageCache allRegularMessages].count;
  402. XCTAssertEqual(1, remainingMsgCount);
  403. }
  404. // this test case contracts testNoAnalyticsTrackingOnTestMessage to cover both positive
  405. // and negative cases
  406. - (void)testDoesAnalyticsTrackingOnNonTestMessage {
  407. // This setup allows next message to be displayed from display interval perspective.
  408. OCMStub([self.mockTimeFetcher currentTimestampInSeconds])
  409. .andReturn(DISPLAY_MIN_INTERVALS * 60 + 100);
  410. // not expecting triggering analytics recording
  411. OCMExpect([self.mockAnalyticsEventLogger
  412. logAnalyticsEventForType:FIRIAMAnalyticsEventMessageImpression
  413. forCampaignID:[OCMArg isEqual:self.m2.renderData.messageID]
  414. withCampaignName:[OCMArg any]
  415. eventTimeInMs:[OCMArg any]
  416. completion:[OCMArg any]]);
  417. [self.clientMessageCache setMessageData:@[ self.m2 ]];
  418. FIRIAMMessageDisplayForTesting *display = [[FIRIAMMessageDisplayForTesting alloc]
  419. initWithDelegateInteraction:FIRInAppMessagingDelegateInteractionClick];
  420. self.displayExecutor.messageDisplayComponent = display;
  421. [self.displayExecutor checkAndDisplayNextAppForegroundMessage];
  422. OCMVerifyAll((id)self.mockAnalyticsEventLogger);
  423. }
  424. - (void)testDoesAnalyticsTrackingOnDisplayError {
  425. // 1000 seconds is larger than DISPLAY_MIN_INTERVALS minutes
  426. // last display time is set to 0 by default
  427. OCMStub([self.mockTimeFetcher currentTimestampInSeconds]).andReturn(1000);
  428. // not expecting triggering analytics recording
  429. OCMExpect([self.mockAnalyticsEventLogger
  430. logAnalyticsEventForType:FIRIAMAnalyticsEventImageFetchError
  431. forCampaignID:[OCMArg isEqual:self.m2.renderData.messageID]
  432. withCampaignName:[OCMArg any]
  433. eventTimeInMs:[OCMArg any]
  434. completion:[OCMArg any]]);
  435. [self.clientMessageCache setMessageData:@[ self.m2 ]];
  436. FIRIAMMessageDisplayForTesting *display = [[FIRIAMMessageDisplayForTesting alloc]
  437. initWithDelegateInteraction:FIRInAppMessagingDelegateInteractionError];
  438. self.displayExecutor.messageDisplayComponent = display;
  439. [self.displayExecutor checkAndDisplayNextAppForegroundMessage];
  440. OCMVerifyAll((id)self.mockAnalyticsEventLogger);
  441. }
  442. - (void)testAnalyticsTrackingOnMessageDismissCase {
  443. // This setup allows next message to be displayed from display interval perspective.
  444. OCMStub([self.mockTimeFetcher currentTimestampInSeconds])
  445. .andReturn(DISPLAY_MIN_INTERVALS * 60 + 100);
  446. // not expecting triggering analytics recording
  447. OCMExpect([self.mockAnalyticsEventLogger
  448. logAnalyticsEventForType:FIRIAMAnalyticsEventMessageDismissAuto
  449. forCampaignID:[OCMArg isEqual:self.m2.renderData.messageID]
  450. withCampaignName:[OCMArg any]
  451. eventTimeInMs:[OCMArg any]
  452. completion:[OCMArg any]]);
  453. // Make sure we don't log the url follow event.
  454. OCMReject([self.mockAnalyticsEventLogger
  455. logAnalyticsEventForType:FIRIAMAnalyticsEventActionURLFollow
  456. forCampaignID:[OCMArg isEqual:self.m2.renderData.messageID]
  457. withCampaignName:[OCMArg any]
  458. eventTimeInMs:[OCMArg any]
  459. completion:[OCMArg any]]);
  460. [self.clientMessageCache setMessageData:@[ self.m2 ]];
  461. FIRIAMMessageDisplayForTesting *display = [[FIRIAMMessageDisplayForTesting alloc]
  462. initWithDelegateInteraction:FIRInAppMessagingDelegateInteractionDismiss];
  463. self.displayExecutor.messageDisplayComponent = display;
  464. [self.displayExecutor checkAndDisplayNextAppForegroundMessage];
  465. OCMVerifyAll((id)self.mockAnalyticsEventLogger);
  466. }
  467. - (void)testAnalyticsTrackingOnMessageClickCase {
  468. // This setup allows next message to be displayed from display interval perspective.
  469. OCMStub([self.mockTimeFetcher currentTimestampInSeconds])
  470. .andReturn(DISPLAY_MIN_INTERVALS * 60 + 100);
  471. // We expect two analytics events for a click action:
  472. // An impression event and an action URL follow event
  473. OCMExpect([self.mockAnalyticsEventLogger
  474. logAnalyticsEventForType:FIRIAMAnalyticsEventMessageImpression
  475. forCampaignID:[OCMArg isEqual:self.m2.renderData.messageID]
  476. withCampaignName:[OCMArg any]
  477. eventTimeInMs:[OCMArg any]
  478. completion:[OCMArg any]]);
  479. OCMExpect([self.mockAnalyticsEventLogger
  480. logAnalyticsEventForType:FIRIAMAnalyticsEventActionURLFollow
  481. forCampaignID:[OCMArg isEqual:self.m2.renderData.messageID]
  482. withCampaignName:[OCMArg any]
  483. eventTimeInMs:[OCMArg any]
  484. completion:[OCMArg any]]);
  485. [self.clientMessageCache setMessageData:@[ self.m2 ]];
  486. FIRIAMMessageDisplayForTesting *display = [[FIRIAMMessageDisplayForTesting alloc]
  487. initWithDelegateInteraction:FIRInAppMessagingDelegateInteractionClick];
  488. self.displayExecutor.messageDisplayComponent = display;
  489. [self.displayExecutor checkAndDisplayNextAppForegroundMessage];
  490. OCMVerifyAll((id)self.mockAnalyticsEventLogger);
  491. }
  492. - (void)testAnalyticsTrackingOnTestMessageClickCase {
  493. // 1000 seconds is larger than DISPLAY_MIN_INTERVALS minutes
  494. // last display time is set to 0 by default
  495. OCMStub([self.mockTimeFetcher currentTimestampInSeconds]).andReturn(1000);
  496. // We expect two analytics events for a click action:
  497. // An test message impression event and a test message click event
  498. OCMExpect([self.mockAnalyticsEventLogger
  499. logAnalyticsEventForType:FIRIAMAnalyticsEventTestMessageImpression
  500. forCampaignID:[OCMArg isEqual:self.m2.renderData.messageID]
  501. withCampaignName:[OCMArg any]
  502. eventTimeInMs:[OCMArg any]
  503. completion:[OCMArg any]]);
  504. OCMExpect([self.mockAnalyticsEventLogger
  505. logAnalyticsEventForType:FIRIAMAnalyticsEventTestMessageClick
  506. forCampaignID:[OCMArg isEqual:self.m2.renderData.messageID]
  507. withCampaignName:[OCMArg any]
  508. eventTimeInMs:[OCMArg any]
  509. completion:[OCMArg any]]);
  510. FIRIAMMessageDefinition *testMessage =
  511. [[FIRIAMMessageDefinition alloc] initTestMessageWithRenderData:self.m2.renderData];
  512. [self.clientMessageCache setMessageData:@[ testMessage ]];
  513. FIRIAMMessageDisplayForTesting *display = [[FIRIAMMessageDisplayForTesting alloc]
  514. initWithDelegateInteraction:FIRInAppMessagingDelegateInteractionClick];
  515. self.displayExecutor.messageDisplayComponent = display;
  516. [self.displayExecutor checkAndDisplayNextAppForegroundMessage];
  517. OCMVerifyAll((id)self.mockAnalyticsEventLogger);
  518. }
  519. - (void)testAnalyticsTrackingOnTestMessageDismissCase {
  520. // 1000 seconds is larger than DISPLAY_MIN_INTERVALS minutes
  521. // last display time is set to 0 by default
  522. OCMStub([self.mockTimeFetcher currentTimestampInSeconds]).andReturn(1000);
  523. // We expect a test message impression
  524. OCMExpect([self.mockAnalyticsEventLogger
  525. logAnalyticsEventForType:FIRIAMAnalyticsEventTestMessageImpression
  526. forCampaignID:[OCMArg isEqual:self.m2.renderData.messageID]
  527. withCampaignName:[OCMArg any]
  528. eventTimeInMs:[OCMArg any]
  529. completion:[OCMArg any]]);
  530. // No click event
  531. OCMReject([self.mockAnalyticsEventLogger
  532. logAnalyticsEventForType:FIRIAMAnalyticsEventTestMessageClick
  533. forCampaignID:[OCMArg isEqual:self.m2.renderData.messageID]
  534. withCampaignName:[OCMArg any]
  535. eventTimeInMs:[OCMArg any]
  536. completion:[OCMArg any]]);
  537. FIRIAMMessageDefinition *testMessage =
  538. [[FIRIAMMessageDefinition alloc] initTestMessageWithRenderData:self.m2.renderData];
  539. [self.clientMessageCache setMessageData:@[ testMessage ]];
  540. FIRIAMMessageDisplayForTesting *display = [[FIRIAMMessageDisplayForTesting alloc]
  541. initWithDelegateInteraction:FIRInAppMessagingDelegateInteractionDismiss];
  542. self.displayExecutor.messageDisplayComponent = display;
  543. [self.displayExecutor checkAndDisplayNextAppForegroundMessage];
  544. OCMVerifyAll((id)self.mockAnalyticsEventLogger);
  545. }
  546. - (void)testAnalyticsTrackingImpressionOnValidImpressionDetectedCase {
  547. // This setup allows next message to be displayed from display interval perspective.
  548. OCMStub([self.mockTimeFetcher currentTimestampInSeconds])
  549. .andReturn(DISPLAY_MIN_INTERVALS * 60 + 100);
  550. // not expecting triggering analytics recording
  551. OCMExpect([self.mockAnalyticsEventLogger
  552. logAnalyticsEventForType:FIRIAMAnalyticsEventMessageImpression
  553. forCampaignID:[OCMArg isEqual:self.m2.renderData.messageID]
  554. withCampaignName:[OCMArg any]
  555. eventTimeInMs:[OCMArg any]
  556. completion:[OCMArg any]]);
  557. [self.clientMessageCache setMessageData:@[ self.m2 ]];
  558. FIRIAMMessageDisplayForTesting *display = [[FIRIAMMessageDisplayForTesting alloc]
  559. initWithDelegateInteraction:FIRInAppMessagingDelegateInteractionImpressionDetected];
  560. self.displayExecutor.messageDisplayComponent = display;
  561. [self.displayExecutor checkAndDisplayNextAppForegroundMessage];
  562. OCMVerifyAll((id)self.mockAnalyticsEventLogger);
  563. }
  564. - (void)testNoAnalyticsTrackingOnTestMessage {
  565. // This setup allows next message to be displayed from display interval perspective.
  566. OCMStub([self.mockTimeFetcher currentTimestampInSeconds])
  567. .andReturn(DISPLAY_MIN_INTERVALS * 60 + 100);
  568. FIRIAMMessageDefinition *testMessage =
  569. [[FIRIAMMessageDefinition alloc] initTestMessageWithRenderData:self.m1.renderData];
  570. // not expecting triggering analytics recording
  571. OCMReject([self.mockAnalyticsEventLogger
  572. logAnalyticsEventForType:FIRIAMAnalyticsEventMessageImpression
  573. forCampaignID:[OCMArg isEqual:self.m1.renderData.messageID]
  574. withCampaignName:[OCMArg any]
  575. eventTimeInMs:[OCMArg any]
  576. completion:[OCMArg any]]);
  577. [self.clientMessageCache setMessageData:@[ testMessage ]];
  578. FIRIAMMessageDisplayForTesting *display = [[FIRIAMMessageDisplayForTesting alloc]
  579. initWithDelegateInteraction:FIRInAppMessagingDelegateInteractionClick];
  580. self.displayExecutor.messageDisplayComponent = display;
  581. [self.displayExecutor checkAndDisplayNextAppForegroundMessage];
  582. OCMVerifyAll((id)self.mockAnalyticsEventLogger);
  583. }
  584. - (void)testNoMessageAvailableCase {
  585. // This setup allows next message to be displayed from display interval perspective.
  586. OCMStub([self.mockTimeFetcher currentTimestampInSeconds])
  587. .andReturn(DISPLAY_MIN_INTERVALS * 60 + 100);
  588. FIRIAMMessageDisplayForTesting *display = [[FIRIAMMessageDisplayForTesting alloc]
  589. initWithDelegateInteraction:FIRInAppMessagingDelegateInteractionClick];
  590. self.displayExecutor.messageDisplayComponent = display;
  591. [self.clientMessageCache setMessageData:@[]];
  592. [self.displayExecutor checkAndDisplayNextAppForegroundMessage];
  593. // No display has happened so the message stored in the display component should be nil
  594. XCTAssertNil(display.message);
  595. NSInteger remainingMsgCount = [self.clientMessageCache allRegularMessages].count;
  596. XCTAssertEqual(0, remainingMsgCount);
  597. }
  598. - (void)testIntervalBetweenOnAppOpenDisplays {
  599. self.displaySetting.displayMinIntervalInMinutes = 10;
  600. // last display time is set to 0 by default
  601. // 10 seconds is not long enough for satisfying the 10-min internal requirement
  602. OCMStub([self.mockTimeFetcher currentTimestampInSeconds]).andReturn(10);
  603. FIRIAMMessageDisplayForTesting *display = [[FIRIAMMessageDisplayForTesting alloc]
  604. initWithDelegateInteraction:FIRInAppMessagingDelegateInteractionClick];
  605. self.displayExecutor.messageDisplayComponent = display;
  606. [self.clientMessageCache setMessageData:@[ self.m1 ]];
  607. [self.displayExecutor checkAndDisplayNextAppForegroundMessage];
  608. NSInteger remainingMsgCount = [self.clientMessageCache allRegularMessages].count;
  609. // No display has happened so the message stored in the display component should be nil
  610. XCTAssertNil(display.message);
  611. // still got one in the queue
  612. XCTAssertEqual(1, remainingMsgCount);
  613. }
  614. // making sure that we match on the event names for analytics based events
  615. - (void)testOnFirebaseAnalyticsEventDisplayMessages {
  616. // This setup allows next message to be displayed from display interval perspective.
  617. OCMStub([self.mockTimeFetcher currentTimestampInSeconds])
  618. .andReturn(DISPLAY_MIN_INTERVALS * 60 + 100);
  619. FIRIAMMessageDisplayForTesting *display = [[FIRIAMMessageDisplayForTesting alloc]
  620. initWithDelegateInteraction:FIRInAppMessagingDelegateInteractionClick];
  621. self.displayExecutor.messageDisplayComponent = display;
  622. // m1 and m3 are messages triggered by 'test_event' analytics events
  623. [self.clientMessageCache setMessageData:@[ self.m1, self.m3 ]];
  624. [self.displayExecutor checkAndDisplayNextContextualMessageForAnalyticsEvent:@"different event"];
  625. NSInteger remainingMsgCount = [self.clientMessageCache allRegularMessages].count;
  626. // No message matching event "different event", so no message is nil
  627. XCTAssertNil(display.message);
  628. // still got 2 in the queue
  629. XCTAssertEqual(2, remainingMsgCount);
  630. // now trigger it with 'test_event' and we would expect one message to be displayed and removed
  631. // from cache
  632. [self.displayExecutor checkAndDisplayNextContextualMessageForAnalyticsEvent:@"test_event"];
  633. // Expecting the m1 being used for display
  634. XCTAssertEqualObjects(self.m1.renderData.messageID, display.message.campaignInfo.messageID);
  635. remainingMsgCount = [self.clientMessageCache allRegularMessages].count;
  636. // Now only one message remaining in the queue
  637. XCTAssertEqual(1, remainingMsgCount);
  638. }
  639. // no regular message rendering if suppress message display flag is turned on
  640. - (void)testNoRenderingIfMessageDisplayIsSuppressed {
  641. // This setup allows next message to be displayed from display interval perspective.
  642. OCMStub([self.mockTimeFetcher currentTimestampInSeconds])
  643. .andReturn(DISPLAY_MIN_INTERVALS * 60 + 100);
  644. [self.clientMessageCache setMessageData:@[ self.m2, self.m4 ]];
  645. FIRIAMMessageDisplayForTesting *display = [[FIRIAMMessageDisplayForTesting alloc]
  646. initWithDelegateInteraction:FIRInAppMessagingDelegateInteractionClick];
  647. self.displayExecutor.messageDisplayComponent = display;
  648. self.displayExecutor.suppressMessageDisplay = YES;
  649. [self.displayExecutor checkAndDisplayNextAppForegroundMessage];
  650. // no message display has happened
  651. XCTAssertNil(display.message);
  652. NSInteger remainingMsgCount = [self.clientMessageCache allRegularMessages].count;
  653. // no message is removed from the cache
  654. XCTAssertEqual(2, remainingMsgCount);
  655. // now allow message rendering again
  656. self.displayExecutor.suppressMessageDisplay = NO;
  657. [self.displayExecutor checkAndDisplayNextAppForegroundMessage];
  658. NSInteger remainingMsgCount2 = [self.clientMessageCache allRegularMessages].count;
  659. // one message was rendered and removed from the cache
  660. XCTAssertEqual(1, remainingMsgCount2);
  661. }
  662. // No contextual message rendering if suppress message display flag is turned on
  663. - (void)testNoContextualMsgRenderingIfMessageDisplayIsSuppressed {
  664. // This setup allows next message to be displayed from display interval perspective.
  665. OCMStub([self.mockTimeFetcher currentTimestampInSeconds])
  666. .andReturn(DISPLAY_MIN_INTERVALS * 60 + 100);
  667. [self.clientMessageCache setMessageData:@[ self.m1, self.m3 ]];
  668. FIRIAMMessageDisplayForTesting *display = [[FIRIAMMessageDisplayForTesting alloc]
  669. initWithDelegateInteraction:FIRInAppMessagingDelegateInteractionClick];
  670. self.displayExecutor.messageDisplayComponent = display;
  671. self.displayExecutor.suppressMessageDisplay = YES;
  672. [self.displayExecutor checkAndDisplayNextContextualMessageForAnalyticsEvent:@"test_event"];
  673. // no message display has happened
  674. XCTAssertNil(display.message);
  675. NSInteger remainingMsgCount = [self.clientMessageCache allRegularMessages].count;
  676. // No message is removed from the cache.
  677. XCTAssertEqual(2, remainingMsgCount);
  678. // now re-enable message rendering again
  679. self.displayExecutor.suppressMessageDisplay = NO;
  680. [self.displayExecutor checkAndDisplayNextContextualMessageForAnalyticsEvent:@"test_event"];
  681. NSInteger remainingMsgCount2 = [self.clientMessageCache allRegularMessages].count;
  682. // one message was rendered and removed from the cache
  683. XCTAssertEqual(1, remainingMsgCount2);
  684. }
  685. - (void)testMessageClickedCallback {
  686. FIRInAppMessagingDisplayTestDelegate *delegate =
  687. [[FIRInAppMessagingDisplayTestDelegate alloc] init];
  688. self.mockInAppMessaging.delegate = delegate;
  689. // This setup allows next message to be displayed from display interval perspective.
  690. OCMStub([self.mockTimeFetcher currentTimestampInSeconds])
  691. .andReturn(DISPLAY_MIN_INTERVALS * 60 + 100);
  692. OCMStub(self.mockInAppMessaging.delegate).andReturn(delegate);
  693. FIRIAMMessageDisplayForTesting *display = [[FIRIAMMessageDisplayForTesting alloc]
  694. initWithDelegateInteraction:FIRInAppMessagingDelegateInteractionClick];
  695. self.displayExecutor.messageDisplayComponent = display;
  696. [self.clientMessageCache setMessageData:@[ self.m2, self.m4 ]];
  697. [self.displayExecutor checkAndDisplayNextAppForegroundMessage];
  698. XCTAssertTrue(delegate.receivedMessageClickedCallback);
  699. }
  700. - (void)testMessageImpressionCallback {
  701. FIRInAppMessagingDisplayTestDelegate *delegate =
  702. [[FIRInAppMessagingDisplayTestDelegate alloc] init];
  703. self.mockInAppMessaging.delegate = delegate;
  704. // This setup allows next message to be displayed from display interval perspective.
  705. OCMStub([self.mockTimeFetcher currentTimestampInSeconds])
  706. .andReturn(DISPLAY_MIN_INTERVALS * 60 + 100);
  707. OCMStub(self.mockInAppMessaging.delegate).andReturn(delegate);
  708. FIRIAMMessageDisplayForTesting *display = [[FIRIAMMessageDisplayForTesting alloc]
  709. initWithDelegateInteraction:FIRInAppMessagingDelegateInteractionImpressionDetected];
  710. self.displayExecutor.messageDisplayComponent = display;
  711. [self.clientMessageCache setMessageData:@[ self.m2, self.m4 ]];
  712. [self.displayExecutor checkAndDisplayNextAppForegroundMessage];
  713. // Verify that the message content handed to display component is expected
  714. XCTAssertTrue(delegate.receivedMessageImpressionCallback);
  715. }
  716. - (void)testMessageErrorCallback {
  717. FIRInAppMessagingDisplayTestDelegate *delegate =
  718. [[FIRInAppMessagingDisplayTestDelegate alloc] init];
  719. self.mockInAppMessaging.delegate = delegate;
  720. // This setup allows next message to be displayed from display interval perspective.
  721. OCMStub([self.mockTimeFetcher currentTimestampInSeconds])
  722. .andReturn(DISPLAY_MIN_INTERVALS * 60 + 100);
  723. OCMStub(self.mockInAppMessaging.delegate).andReturn(delegate);
  724. FIRIAMMessageDisplayForTesting *display = [[FIRIAMMessageDisplayForTesting alloc]
  725. initWithDelegateInteraction:FIRInAppMessagingDelegateInteractionError];
  726. self.displayExecutor.messageDisplayComponent = display;
  727. [self.clientMessageCache setMessageData:@[ self.m2, self.m4 ]];
  728. [self.displayExecutor checkAndDisplayNextAppForegroundMessage];
  729. // Verify that the message content handed to display component is expected
  730. XCTAssertTrue(delegate.receivedMessageErrorCallback);
  731. }
  732. - (void)testMessageDismissedCallback {
  733. FIRInAppMessagingDisplayTestDelegate *delegate =
  734. [[FIRInAppMessagingDisplayTestDelegate alloc] init];
  735. self.mockInAppMessaging.delegate = delegate;
  736. // This setup allows next message to be displayed from display interval perspective.
  737. OCMStub([self.mockTimeFetcher currentTimestampInSeconds])
  738. .andReturn(DISPLAY_MIN_INTERVALS * 60 + 100);
  739. OCMStub(self.mockInAppMessaging.delegate).andReturn(delegate);
  740. FIRIAMMessageDisplayForTesting *display = [[FIRIAMMessageDisplayForTesting alloc]
  741. initWithDelegateInteraction:FIRInAppMessagingDelegateInteractionDismiss];
  742. self.displayExecutor.messageDisplayComponent = display;
  743. [self.clientMessageCache setMessageData:@[ self.m2, self.m4 ]];
  744. [self.displayExecutor checkAndDisplayNextAppForegroundMessage];
  745. // Verify that the message content handed to display component is expected
  746. XCTAssertTrue(delegate.receivedMessageDismissedCallback);
  747. }
  748. - (void)testMessageWithDataBundle {
  749. FIRInAppMessagingDisplayMessage *displayMessage = [self.displayExecutor
  750. displayMessageWithMessageDefinition:self.m4
  751. imageData:nil
  752. landscapeImageData:nil
  753. triggerType:FIRInAppMessagingDisplayTriggerTypeOnAppForeground];
  754. XCTAssertEqual(displayMessage.appData.count, 2);
  755. XCTAssertEqualObjects(displayMessage.appData[@"a"], @"b");
  756. XCTAssertEqualObjects(displayMessage.appData[@"up"], @"dog");
  757. }
  758. - (void)testMessageWithoutDataBundle {
  759. FIRInAppMessagingDisplayMessage *displayMessage = [self.displayExecutor
  760. displayMessageWithMessageDefinition:self.m3
  761. imageData:nil
  762. landscapeImageData:nil
  763. triggerType:FIRInAppMessagingDisplayTriggerTypeOnAppForeground];
  764. XCTAssertNil(displayMessage.appData);
  765. }
  766. @end