FIRIAMDisplayExecutorTests.m 47 KB

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