HudTests.m 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. //
  2. // HudTests.m
  3. // HudTests
  4. //
  5. // Created by Matej Bukovinski on 31. 01. 16.
  6. // Copyright © 2016 Matej Bukovinski. All rights reserved.
  7. //
  8. #import <XCTest/XCTest.h>
  9. #import "MBProgressHUD.h"
  10. #define weakify(var) __weak typeof(var) weak_##var = var;
  11. #define strongify(var) \
  12. _Pragma("clang diagnostic push") \
  13. _Pragma("clang diagnostic ignored \"-Wshadow\"") \
  14. __strong typeof(var) var = weak_##var; \
  15. _Pragma("clang diagnostic pop")
  16. #define MBTestHUDIsVisible(hud, rootView) \
  17. do { \
  18. XCTAssertEqualObjects(hud.superview, rootView, @"The hud should be added to the view."); \
  19. XCTAssertEqual(hud.alpha, 1.f, @"The HUD should be visible."); \
  20. XCTAssertFalse(hud.hidden, @"The HUD should be visible."); \
  21. XCTAssertEqual(hud.bezelView.alpha, 1.f, @"The HUD should be visible."); \
  22. } while (0)
  23. #define MBTestHUDIsHidenAndRemoved(hud, rootView) \
  24. do { \
  25. XCTAssertFalse([rootView.subviews containsObject:hud], @"The HUD should not be part of the view hierarchy."); \
  26. XCTAssertEqual(hud.alpha, 0.f, @"The hud should be faded out."); \
  27. XCTAssertNil(hud.superview, @"The HUD should not have a superview."); \
  28. } while (0)
  29. @interface HudTests : XCTestCase <MBProgressHUDDelegate>
  30. @property (nonatomic) XCTestExpectation *hideExpectation;
  31. @property (nonatomic, copy) dispatch_block_t hideChecks;
  32. @end
  33. @implementation HudTests
  34. #pragma mark - Convenience
  35. - (void)testNonAnimatedConvenienceHUDPresentation {
  36. UIViewController *rootViewController = UIApplication.sharedApplication.keyWindow.rootViewController;
  37. UIView *rootView = rootViewController.view;
  38. MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:rootView animated:NO];
  39. XCTAssertNotNil(hud, @"A HUD should be created.");
  40. MBTestHUDIsVisible(hud, rootView);
  41. XCTAssertEqual(hud.bezelView.alpha, 1.f, @"The HUD should be visible.");
  42. XCTAssertFalse([hud.bezelView.layer.animationKeys containsObject:@"opacity"], @"The opacity should NOT be animated.");
  43. XCTAssertEqualObjects([MBProgressHUD HUDForView:rootView], hud, @"The HUD should be found via the convenience operation.");
  44. XCTAssertTrue([MBProgressHUD hideHUDForView:rootView animated:NO], @"The HUD should be found and removed.");
  45. MBTestHUDIsHidenAndRemoved(hud, rootView);
  46. XCTAssertFalse([MBProgressHUD hideHUDForView:rootView animated:NO], @"A subsequent HUD hide operation should fail.");
  47. }
  48. - (void)testAnimatedConvenienceHUDPresentation {
  49. UIViewController *rootViewController = UIApplication.sharedApplication.keyWindow.rootViewController;
  50. UIView *rootView = rootViewController.view;
  51. self.hideExpectation = [self expectationWithDescription:@"The hudWasHidden: delegate should have been called."];
  52. MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:rootView animated:YES];
  53. hud.delegate = self;
  54. XCTAssertNotNil(hud, @"A HUD should be created.");
  55. MBTestHUDIsVisible(hud, rootView);
  56. XCTAssertEqual(hud.bezelView.alpha, 1.f, @"The HUD should be visible.");
  57. XCTAssertTrue([hud.bezelView.layer.animationKeys containsObject:@"opacity"], @"The opacity should be animated.");
  58. XCTAssertEqualObjects([MBProgressHUD HUDForView:rootView], hud, @"The HUD should be found via the convenience operation.");
  59. XCTAssertTrue([MBProgressHUD hideHUDForView:rootView animated:YES], @"The HUD should be found and removed.");
  60. XCTAssertTrue([rootView.subviews containsObject:hud], @"The HUD should still be part of the view hierarchy.");
  61. XCTAssertEqual(hud.alpha, 1.f, @"The hud should still be visible.");
  62. XCTAssertEqualObjects(hud.superview, rootView, @"The hud should be added to the view.");
  63. XCTAssertEqual(hud.bezelView.alpha, 0.f, @"The HUD bezel should be animated out.");
  64. XCTAssertTrue([hud.bezelView.layer.animationKeys containsObject:@"opacity"], @"The opacity should be animated.");
  65. weakify(self);
  66. self.hideChecks = ^{
  67. strongify(self);
  68. MBTestHUDIsHidenAndRemoved(hud, rootView);
  69. XCTAssertFalse([MBProgressHUD hideHUDForView:rootView animated:YES], @"A subsequent HUD hide operation should fail.");
  70. };
  71. [self waitForExpectationsWithTimeout:5. handler:nil];
  72. }
  73. - (void)testCompletionBlock {
  74. UIViewController *rootViewController = UIApplication.sharedApplication.keyWindow.rootViewController;
  75. UIView *rootView = rootViewController.view;
  76. self.hideExpectation = [self expectationWithDescription:@"The hudWasHidden: delegate should have been called."];
  77. XCTestExpectation *completionExpectation = [self expectationWithDescription:@"The completionBlock: should have been called."];
  78. MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:rootView animated:YES];
  79. hud.delegate = self;
  80. hud.completionBlock = ^{
  81. [completionExpectation fulfill];
  82. };
  83. [hud hideAnimated:YES];
  84. [self waitForExpectationsWithTimeout:5. handler:nil];
  85. }
  86. #pragma mark - Delay
  87. - (void)testDelayedHide {
  88. UIViewController *rootViewController = UIApplication.sharedApplication.keyWindow.rootViewController;
  89. UIView *rootView = rootViewController.view;
  90. self.hideExpectation = [self expectationWithDescription:@"The hudWasHidden: delegate should have been called."];
  91. MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:rootView animated:NO];
  92. hud.delegate = self;
  93. XCTAssertNotNil(hud, @"A HUD should be created.");
  94. [hud hideAnimated:NO afterDelay:2];
  95. MBTestHUDIsVisible(hud, rootView);
  96. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  97. MBTestHUDIsVisible(hud, rootView);
  98. });
  99. XCTestExpectation *hideCheckExpectation = [self expectationWithDescription:@"Hide check"];
  100. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  101. // After the grace time passes, the HUD should still not be shown.
  102. MBTestHUDIsHidenAndRemoved(hud, rootView);
  103. [hideCheckExpectation fulfill];
  104. });
  105. [self waitForExpectationsWithTimeout:5. handler:nil];
  106. MBTestHUDIsHidenAndRemoved(hud, rootView);
  107. }
  108. #pragma mark - Ruse
  109. - (void)testNonAnimatedHudReuse {
  110. UIViewController *rootViewController = UIApplication.sharedApplication.keyWindow.rootViewController;
  111. UIView *rootView = rootViewController.view;
  112. MBProgressHUD *hud = [[MBProgressHUD alloc] initWithView:rootView];
  113. [rootView addSubview:hud];
  114. [hud showAnimated:NO];
  115. XCTAssertNotNil(hud, @"A HUD should be created.");
  116. [hud hideAnimated:NO];
  117. [hud showAnimated:NO];
  118. MBTestHUDIsVisible(hud, rootView);
  119. [hud hideAnimated:NO];
  120. [hud removeFromSuperview];
  121. }
  122. - (void)testUnfinishedHidingAnimation {
  123. UIViewController *rootViewController = UIApplication.sharedApplication.keyWindow.rootViewController;
  124. UIView *rootView = rootViewController.view;
  125. MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:rootView animated:NO];
  126. [hud hideAnimated:YES];
  127. // Cancel all animations. It will cause `UIView+animate...` to call completionBlock with `finished = NO`.
  128. // It's same as if you call `[hud hideAnimated:YES]` while the app is in background.
  129. [hud.bezelView.layer removeAllAnimations];
  130. [hud.backgroundView.layer removeAllAnimations];
  131. XCTestExpectation *hideCheckExpectation = [self expectationWithDescription:@"Hide check"];
  132. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  133. // After the grace time passes, the HUD should still not be shown.
  134. MBTestHUDIsHidenAndRemoved(hud, rootView);
  135. [hideCheckExpectation fulfill];
  136. });
  137. [self waitForExpectationsWithTimeout:5. handler:nil];
  138. MBTestHUDIsHidenAndRemoved(hud, rootView);
  139. }
  140. - (void)testAnimatedImmediateHudReuse {
  141. UIViewController *rootViewController = UIApplication.sharedApplication.keyWindow.rootViewController;
  142. UIView *rootView = rootViewController.view;
  143. XCTestExpectation *hideExpectation = [self expectationWithDescription:@"The hud should have been hidden."];
  144. MBProgressHUD *hud = [[MBProgressHUD alloc] initWithView:rootView];
  145. [rootView addSubview:hud];
  146. [hud showAnimated:YES];
  147. XCTAssertNotNil(hud, @"A HUD should be created.");
  148. [hud hideAnimated:YES];
  149. [hud showAnimated:YES];
  150. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  151. MBTestHUDIsVisible(hud, rootView);
  152. [hud hideAnimated:NO];
  153. [hud removeFromSuperview];
  154. [hideExpectation fulfill];
  155. });
  156. [self waitForExpectationsWithTimeout:5. handler:nil];
  157. }
  158. #pragma mark - Min show time
  159. - (void)testMinShowTime {
  160. UIViewController *rootViewController = UIApplication.sharedApplication.keyWindow.rootViewController;
  161. UIView *rootView = rootViewController.view;
  162. self.hideExpectation = [self expectationWithDescription:@"The hudWasHidden: delegate should have been called."];
  163. MBProgressHUD *hud = [[MBProgressHUD alloc] initWithView:rootView];
  164. hud.delegate = self;
  165. hud.removeFromSuperViewOnHide = YES;
  166. hud.minShowTime = 2.;
  167. [rootView addSubview:hud];
  168. [hud showAnimated:YES];
  169. XCTAssertNotNil(hud, @"A HUD should be created.");
  170. [hud hideAnimated:YES];
  171. __block BOOL checkedAfterOneSecond = NO;
  172. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  173. // Check that the hud is still visible
  174. MBTestHUDIsVisible(hud, rootView);
  175. checkedAfterOneSecond = YES;
  176. });
  177. weakify(self);
  178. self.hideChecks = ^{
  179. strongify(self);
  180. XCTAssertTrue(checkedAfterOneSecond);
  181. };
  182. [self waitForExpectationsWithTimeout:5. handler:nil];
  183. MBTestHUDIsHidenAndRemoved(hud, rootView);
  184. }
  185. #pragma mark - Grace time
  186. - (void)testGraceTime {
  187. UIViewController *rootViewController = UIApplication.sharedApplication.keyWindow.rootViewController;
  188. UIView *rootView = rootViewController.view;
  189. self.hideExpectation = [self expectationWithDescription:@"The hudWasHidden: delegate should have been called."];
  190. MBProgressHUD *hud = [[MBProgressHUD alloc] initWithView:rootView];
  191. hud.delegate = self;
  192. hud.removeFromSuperViewOnHide = YES;
  193. hud.graceTime = 2.;
  194. [rootView addSubview:hud];
  195. [hud showAnimated:YES];
  196. XCTAssertNotNil(hud, @"A HUD should be created.");
  197. // The HUD should be added to the view but still hidden
  198. XCTAssertEqualObjects(hud.superview, rootView, @"The hud should be added to the view."); \
  199. XCTAssertEqual(hud.alpha, 0.f, @"The HUD should not be visible."); \
  200. XCTAssertFalse(hud.hidden, @"The HUD should be visible."); \
  201. XCTAssertEqual(hud.bezelView.alpha, 0.f, @"The HUD should not be visible."); \
  202. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  203. // The HUD should be added to the view but still hidden
  204. XCTAssertEqualObjects(hud.superview, rootView, @"The hud should be added to the view."); \
  205. XCTAssertEqual(hud.alpha, 0.f, @"The HUD should not be visible."); \
  206. XCTAssertFalse(hud.hidden, @"The HUD should be visible."); \
  207. XCTAssertEqual(hud.bezelView.alpha, 0.f, @"The HUD should not be visible."); \
  208. });
  209. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  210. // After the grace time passes, the HUD should be shown.
  211. MBTestHUDIsVisible(hud, rootView);
  212. [hud hideAnimated:YES];
  213. });
  214. [self waitForExpectationsWithTimeout:5. handler:nil];
  215. MBTestHUDIsHidenAndRemoved(hud, rootView);
  216. }
  217. - (void)testHideBeforeGraceTimeElapsed {
  218. UIViewController *rootViewController = UIApplication.sharedApplication.keyWindow.rootViewController;
  219. UIView *rootView = rootViewController.view;
  220. self.hideExpectation = [self expectationWithDescription:@"The hudWasHidden: delegate should have been called."];
  221. MBProgressHUD *hud = [[MBProgressHUD alloc] initWithView:rootView];
  222. hud.delegate = self;
  223. hud.removeFromSuperViewOnHide = YES;
  224. hud.graceTime = 2.;
  225. [rootView addSubview:hud];
  226. [hud showAnimated:YES];
  227. XCTAssertNotNil(hud, @"A HUD should be created.");
  228. // The HUD should be added to the view but still hidden
  229. XCTAssertEqualObjects(hud.superview, rootView, @"The hud should be added to the view."); \
  230. XCTAssertEqual(hud.alpha, 0.f, @"The HUD should not be visible."); \
  231. XCTAssertFalse(hud.hidden, @"The HUD should be visible."); \
  232. XCTAssertEqual(hud.bezelView.alpha, 0.f, @"The HUD should not be visible."); \
  233. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  234. // The HUD should be added to the view but still hidden
  235. XCTAssertEqualObjects(hud.superview, rootView, @"The hud should be added to the view."); \
  236. XCTAssertEqual(hud.alpha, 0.f, @"The HUD should not be visible."); \
  237. XCTAssertFalse(hud.hidden, @"The HUD should be visible."); \
  238. XCTAssertEqual(hud.bezelView.alpha, 0.f, @"The HUD should not be visible."); \
  239. [hud hideAnimated:YES];
  240. });
  241. XCTestExpectation *hideCheckExpectation = [self expectationWithDescription:@"Hide check"];
  242. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  243. // After the grace time passes, the HUD should still not be shown.
  244. MBTestHUDIsHidenAndRemoved(hud, rootView);
  245. [hideCheckExpectation fulfill];
  246. });
  247. [self waitForExpectationsWithTimeout:5. handler:nil];
  248. MBTestHUDIsHidenAndRemoved(hud, rootView);
  249. }
  250. #pragma mark - MBProgressHUDDelegate
  251. - (void)hudWasHidden:(MBProgressHUD *)hud {
  252. if (self.hideChecks) self.hideChecks();
  253. self.hideChecks = nil;
  254. [self.hideExpectation fulfill];
  255. self.hideExpectation = nil;
  256. }
  257. @end