ActionSheetTests.swift 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  1. //
  2. // ActionSheetTests.swift
  3. // SheeeeeeeeetTests
  4. //
  5. // Created by Daniel Saidi on 2017-11-28.
  6. // Copyright © 2017 Daniel Saidi. All rights reserved.
  7. //
  8. import Quick
  9. import Nimble
  10. @testable import Sheeeeeeeeet
  11. class ActionSheetTests: QuickSpec {
  12. override func spec() {
  13. var sheet: MockActionSheet!
  14. func createSheet(_ items: [ActionSheetItem] = []) -> MockActionSheet {
  15. return MockActionSheet(items: items, action: { _, _ in })
  16. }
  17. // MARK: - Initialization
  18. describe("created instance") {
  19. context("default behavior") {
  20. it("use default presenter") {
  21. sheet = createSheet()
  22. let isStandard = sheet.presenter is ActionSheetStandardPresenter
  23. let isPopover = sheet.presenter is ActionSheetPopoverPresenter
  24. let isValid = isStandard || isPopover
  25. expect(isValid).to(beTrue())
  26. }
  27. it("applies no items and buttons") {
  28. sheet = createSheet()
  29. expect(sheet.items.count).to(equal(0))
  30. expect(sheet.buttons.count).to(equal(0))
  31. }
  32. }
  33. context("custom properties") {
  34. it("uses provided presenter") {
  35. let presenter = ActionSheetPopoverPresenter()
  36. sheet = MockActionSheet(items: [], presenter: presenter, action: { _, _ in })
  37. expect(sheet.presenter).to(be(presenter))
  38. }
  39. it("sets up provided items and buttons") {
  40. let items = [ActionSheetItem(title: "foo")]
  41. sheet = createSheet(items)
  42. expect(sheet.setupItemsInvokeCount).to(equal(1))
  43. expect(sheet.setupItemsInvokeItems[0]).to(be(items))
  44. }
  45. }
  46. it("uses provided action") {
  47. var counter = 0
  48. sheet = MockActionSheet(items: []) { _, _ in counter += 1 }
  49. sheet.selectAction(sheet, ActionSheetItem(title: "foo"))
  50. expect(counter).to(equal(1))
  51. }
  52. }
  53. describe("setup") {
  54. beforeEach {
  55. sheet = createSheet()
  56. }
  57. it("applies default preferred popover width") {
  58. sheet.setup()
  59. expect(sheet.preferredContentSize.width).to(equal(300))
  60. }
  61. it("applies custom preferred popover width") {
  62. sheet.preferredPopoverWidth = 200
  63. sheet.setup()
  64. expect(sheet.preferredContentSize.width).to(equal(200))
  65. }
  66. }
  67. describe("setup items") {
  68. beforeEach {
  69. sheet = createSheet()
  70. }
  71. it("applies empty collection") {
  72. sheet.setup(items: [])
  73. expect(sheet.items.count).to(equal(0))
  74. expect(sheet.buttons.count).to(equal(0))
  75. }
  76. it("separates items and buttons") {
  77. let item1 = ActionSheetItem(title: "foo")
  78. let item2 = ActionSheetItem(title: "bar")
  79. let button = ActionSheetOkButton(title: "baz")
  80. sheet.setup(items: [button, item1, item2])
  81. expect(sheet.items.count).to(equal(2))
  82. expect(sheet.items[0]).to(be(item1))
  83. expect(sheet.items[1]).to(be(item2))
  84. expect(sheet.buttons.count).to(equal(1))
  85. expect(sheet.buttons[0]).to(be(button))
  86. }
  87. it("reloads data") {
  88. sheet.reloadDataInvokeCount = 0
  89. sheet.setup(items: [])
  90. expect(sheet.reloadDataInvokeCount).to(equal(1))
  91. }
  92. }
  93. describe("loading view") {
  94. var itemsTableView: ActionSheetItemTableView!
  95. var buttonsTableView: ActionSheetButtonTableView!
  96. beforeEach {
  97. sheet = createSheet()
  98. itemsTableView = ActionSheetItemTableView(frame: .zero)
  99. buttonsTableView = ActionSheetButtonTableView(frame: .zero)
  100. sheet.itemsTableView = itemsTableView
  101. sheet.buttonsTableView = buttonsTableView
  102. sheet.viewDidLoad()
  103. }
  104. it("sets up action sheet") {
  105. expect(sheet.setupInvokeCount).to(equal(1))
  106. }
  107. it("sets up items table view") {
  108. expect(itemsTableView.delegate).to(be(sheet.itemHandler))
  109. expect(itemsTableView.dataSource).to(be(sheet.itemHandler))
  110. expect(itemsTableView.alwaysBounceVertical).to(beFalse())
  111. expect(itemsTableView.estimatedRowHeight).to(equal(44))
  112. expect(itemsTableView.rowHeight).to(equal(UITableView.automaticDimension))
  113. expect(itemsTableView.cellLayoutMarginsFollowReadableWidth).to(beFalse())
  114. }
  115. it("sets up buttons table view") {
  116. expect(buttonsTableView.delegate).to(be(sheet.buttonHandler))
  117. expect(buttonsTableView.dataSource).to(be(sheet.buttonHandler))
  118. expect(itemsTableView.alwaysBounceVertical).to(beFalse())
  119. expect(buttonsTableView.estimatedRowHeight).to(equal(44))
  120. expect(buttonsTableView.rowHeight).to(equal(UITableView.automaticDimension))
  121. expect(buttonsTableView.cellLayoutMarginsFollowReadableWidth).to(beFalse())
  122. }
  123. }
  124. describe("laying out subviews") {
  125. it("refreshes sheet") {
  126. sheet = createSheet()
  127. sheet.viewDidLayoutSubviews()
  128. expect(sheet.refreshInvokeCount).to(equal(1))
  129. }
  130. }
  131. describe("minimum content insets") {
  132. it("has correct default value") {
  133. sheet = createSheet()
  134. let expected = UIEdgeInsets(top: 15, left: 15, bottom: 15, right: 15)
  135. expect(sheet.minimumContentInsets).to(equal(expected))
  136. }
  137. }
  138. describe("preferred popover width") {
  139. it("has correct default value") {
  140. sheet = createSheet()
  141. let expected: CGFloat = 300
  142. expect(sheet.preferredPopoverWidth).to(equal(expected))
  143. }
  144. }
  145. describe("section margins") {
  146. it("has correct default value") {
  147. sheet = createSheet()
  148. let expected: CGFloat = 15
  149. expect(sheet.sectionMargins).to(equal(expected))
  150. }
  151. }
  152. describe("items height") {
  153. beforeEach {
  154. ActionSheetItem.height = 100
  155. ActionSheetSingleSelectItem.height = 110
  156. ActionSheetMultiSelectItem.height = 120
  157. ActionSheetOkButton.height = 120
  158. }
  159. it("is sum of all items") {
  160. let item1 = ActionSheetItem(title: "foo")
  161. let item2 = ActionSheetSingleSelectItem(title: "bar", isSelected: true)
  162. let item3 = ActionSheetMultiSelectItem(title: "baz", isSelected: false)
  163. let button = ActionSheetOkButton(title: "ok")
  164. sheet = createSheet([item1, item2, item3, button])
  165. expect(sheet.itemsHeight).to(equal(330))
  166. }
  167. }
  168. describe("item handler") {
  169. it("has correct item type") {
  170. sheet = createSheet()
  171. expect(sheet.itemHandler.itemType).to(equal(.items))
  172. }
  173. it("has correct items") {
  174. let item1 = ActionSheetItem(title: "foo")
  175. let item2 = ActionSheetItem(title: "bar")
  176. let button = ActionSheetOkButton(title: "ok")
  177. sheet = createSheet([item1, item2, button])
  178. expect(sheet.itemHandler.items.count).to(equal(2))
  179. expect(sheet.itemHandler.items[0]).to(be(item1))
  180. expect(sheet.itemHandler.items[1]).to(be(item2))
  181. }
  182. }
  183. describe("items height") {
  184. beforeEach {
  185. ActionSheetItem.height = 100
  186. ActionSheetOkButton.height = 110
  187. ActionSheetDangerButton.height = 120
  188. ActionSheetCancelButton.height = 130
  189. }
  190. it("is sum of all items") {
  191. let item = ActionSheetItem(title: "foo")
  192. let button1 = ActionSheetOkButton(title: "ok")
  193. let button2 = ActionSheetDangerButton(title: "ok")
  194. let button3 = ActionSheetCancelButton(title: "ok")
  195. sheet = createSheet([item, button1, button2, button3])
  196. expect(sheet.buttonsHeight).to(equal(360))
  197. }
  198. }
  199. describe("item handler") {
  200. it("has correct item type") {
  201. sheet = createSheet()
  202. expect(sheet.buttonHandler.itemType).to(equal(.buttons))
  203. }
  204. it("has correct items") {
  205. let item = ActionSheetItem(title: "foo")
  206. let button1 = ActionSheetOkButton(title: "ok")
  207. let button2 = ActionSheetOkButton(title: "ok")
  208. sheet = createSheet([item, button1, button2])
  209. expect(sheet.buttonHandler.items.count).to(equal(2))
  210. expect(sheet.buttonHandler.items[0]).to(be(button1))
  211. expect(sheet.buttonHandler.items[1]).to(be(button2))
  212. }
  213. }
  214. context("presentation") {
  215. var presenter: MockActionSheetPresenter!
  216. beforeEach {
  217. presenter = MockActionSheetPresenter()
  218. sheet = createSheet()
  219. sheet.presenter = presenter
  220. }
  221. describe("when dismissed") {
  222. it("it calls presenter") {
  223. var counter = 0
  224. let completion = { counter += 1 }
  225. sheet.dismiss(completion: completion)
  226. presenter.dismissInvokeCompletions[0]()
  227. expect(presenter.dismissInvokeCount).to(equal(1))
  228. expect(counter).to(equal(1))
  229. }
  230. }
  231. describe("when presented from view") {
  232. it("refreshes itself") {
  233. sheet.present(in: UIViewController(), from: UIView())
  234. expect(sheet.refreshInvokeCount).to(equal(1))
  235. }
  236. it("calls presenter") {
  237. var counter = 0
  238. let completion = { counter += 1 }
  239. let vc = UIViewController()
  240. let view = UIView()
  241. sheet.present(in: vc, from: view, completion: completion)
  242. presenter.presentInvokeCompletions[0]()
  243. expect(presenter.presentInvokeCount).to(equal(1))
  244. expect(presenter.presentInvokeViewControllers[0]).to(be(vc))
  245. expect(presenter.presentInvokeViews[0]).to(be(view))
  246. expect(counter).to(equal(1))
  247. }
  248. }
  249. describe("when presented from bar button item") {
  250. it("refreshes itself") {
  251. sheet.present(in: UIViewController(), from: UIBarButtonItem())
  252. expect(sheet.refreshInvokeCount).to(equal(1))
  253. }
  254. it("calls presenter") {
  255. var counter = 0
  256. let completion = { counter += 1 }
  257. let vc = UIViewController()
  258. let item = UIBarButtonItem()
  259. sheet.present(in: vc, from: item, completion: completion)
  260. presenter.presentInvokeCompletions[0]()
  261. expect(presenter.presentInvokeCount).to(equal(1))
  262. expect(presenter.presentInvokeViewControllers[0]).to(be(vc))
  263. expect(presenter.presentInvokeItems[0]).to(be(item))
  264. expect(counter).to(equal(1))
  265. }
  266. }
  267. }
  268. describe("refreshing") {
  269. var presenter: MockActionSheetPresenter!
  270. var stackView: UIStackView!
  271. beforeEach {
  272. presenter = MockActionSheetPresenter()
  273. stackView = UIStackView()
  274. sheet = createSheet()
  275. sheet.stackView = stackView
  276. sheet.presenter = presenter
  277. sheet.refresh()
  278. }
  279. it("refreshes header") {
  280. expect(sheet.refreshHeaderInvokeCount).to(equal(1))
  281. }
  282. it("refreshes items") {
  283. expect(sheet.refreshItemsInvokeCount).to(equal(1))
  284. }
  285. it("refreshes buttons") {
  286. expect(sheet.refreshButtonsInvokeCount).to(equal(1))
  287. }
  288. it("applies stack view spacing") {
  289. expect(stackView.spacing).to(equal(15))
  290. }
  291. it("calls presenter to refresh itself") {
  292. expect(presenter.refreshActionSheetInvokeCount).to(equal(1))
  293. }
  294. }
  295. describe("refreshing header") {
  296. var container: ActionSheetHeaderView!
  297. var height: NSLayoutConstraint!
  298. beforeEach {
  299. container = ActionSheetHeaderView()
  300. height = NSLayoutConstraint()
  301. sheet = createSheet()
  302. sheet.headerViewContainer = container
  303. sheet.headerViewContainerHeight = height
  304. }
  305. it("refreshes correctly if header view is nil") {
  306. sheet.refreshHeader()
  307. expect(container.isHidden).to(beTrue())
  308. expect(container.subviews.count).to(equal(0))
  309. expect(height.constant).to(equal(0))
  310. }
  311. it("refreshes correctly if header view is set") {
  312. let view = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 200))
  313. sheet.headerView = view
  314. sheet.refreshHeader()
  315. expect(container.isHidden).to(beFalse())
  316. expect(container.subviews.count).to(equal(1))
  317. expect(container.subviews[0]).to(be(view))
  318. expect(height.constant).to(equal(200))
  319. }
  320. }
  321. describe("refreshing items") {
  322. var height: NSLayoutConstraint!
  323. beforeEach {
  324. height = NSLayoutConstraint()
  325. sheet = createSheet()
  326. sheet.itemsTableViewHeight = height
  327. ActionSheetItem.height = 12
  328. ActionSheetOkButton.height = 13
  329. }
  330. it("refreshes correctly if no items are set") {
  331. sheet.refreshItems()
  332. expect(height.constant).to(equal(0))
  333. }
  334. it("refreshes correctly if items are set") {
  335. let item1 = ActionSheetItem(title: "foo")
  336. let item2 = ActionSheetItem(title: "foo")
  337. let button = ActionSheetOkButton(title: "foo")
  338. sheet.setup(items: [item1, item2, button])
  339. sheet.refreshItems()
  340. expect(height.constant).to(equal(24))
  341. }
  342. }
  343. describe("refreshing buttons") {
  344. var height: NSLayoutConstraint!
  345. beforeEach {
  346. height = NSLayoutConstraint()
  347. sheet = createSheet()
  348. sheet.buttonsTableViewHeight = height
  349. ActionSheetItem.height = 12
  350. ActionSheetOkButton.height = 13
  351. }
  352. it("refreshes correctly if no items are set") {
  353. sheet.refreshButtons()
  354. expect(height.constant).to(equal(0))
  355. }
  356. it("refreshes correctly if items are set") {
  357. let item = ActionSheetItem(title: "foo")
  358. let button1 = ActionSheetOkButton(title: "foo")
  359. let button2 = ActionSheetOkButton(title: "foo")
  360. sheet.setup(items: [item, button1, button2])
  361. sheet.refreshButtons()
  362. expect(height.constant).to(equal(26))
  363. }
  364. }
  365. describe("handling tap on item") {
  366. beforeEach {
  367. sheet = createSheet()
  368. sheet.reloadDataInvokeCount = 0
  369. }
  370. it("reloads data") {
  371. sheet.handleTap(on: ActionSheetItem(title: ""))
  372. expect(sheet.reloadDataInvokeCount).to(equal(1))
  373. }
  374. it("calls select action without dismiss if item has none tap action") {
  375. var count = 0
  376. sheet = MockActionSheet { (_, _) in count += 1 }
  377. let item = ActionSheetItem(title: "", tapBehavior: .none)
  378. sheet.handleTap(on: item)
  379. expect(count).to(equal(1))
  380. expect(sheet.dismissInvokeCount).to(equal(0))
  381. }
  382. it("calls select action after dismiss if item has dismiss tap action") {
  383. var count = 0
  384. sheet = MockActionSheet { (_, _) in count += 1 }
  385. let item = ActionSheetItem(title: "", tapBehavior: .dismiss)
  386. sheet.handleTap(on: item)
  387. expect(count).to(equal(1))
  388. expect(sheet.dismissInvokeCount).to(equal(1))
  389. }
  390. }
  391. describe("margin at position") {
  392. beforeEach {
  393. sheet = createSheet()
  394. }
  395. it("ignores custom edge margins with smaller value than the default ones") {
  396. let sheet = createSheet()
  397. sheet.minimumContentInsets = UIEdgeInsets(top: -1, left: -1, bottom: -1, right: -1)
  398. expect(sheet.margin(at: .top)).to(equal(sheet.view.safeAreaInsets.top))
  399. expect(sheet.margin(at: .left)).to(equal(sheet.view.safeAreaInsets.left))
  400. expect(sheet.margin(at: .right)).to(equal(sheet.view.safeAreaInsets.right))
  401. expect(sheet.margin(at: .bottom)).to(equal(sheet.view.safeAreaInsets.bottom))
  402. }
  403. it("uses custom edge margins with greated value than the default ones") {
  404. let sheet = createSheet()
  405. sheet.minimumContentInsets = UIEdgeInsets(top: 111, left: 222, bottom: 333, right: 444)
  406. expect(sheet.margin(at: .top)).to(equal(111))
  407. expect(sheet.margin(at: .left)).to(equal(222))
  408. expect(sheet.margin(at: .bottom)).to(equal(333))
  409. expect(sheet.margin(at: .right)).to(equal(444))
  410. }
  411. }
  412. describe("reloading data") {
  413. it("reloads both table views") {
  414. let view1 = MockItemTableView(frame: .zero)
  415. let view2 = MockButtonTableView(frame: .zero)
  416. sheet = createSheet()
  417. sheet.itemsTableView = view1
  418. sheet.buttonsTableView = view2
  419. sheet.reloadData()
  420. expect(view1.reloadDataInvokeCount).to(equal(1))
  421. expect(view2.reloadDataInvokeCount).to(equal(1))
  422. }
  423. }
  424. }
  425. }