ActionSheetTests.swift 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528
  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. func createButton(_ title: String) -> ActionSheetButton {
  14. return ActionSheetOkButton(title: title)
  15. }
  16. func createItem(_ title: String) -> ActionSheetItem {
  17. return ActionSheetItem(title: title)
  18. }
  19. func createSheet(_ items: [ActionSheetItem] = []) -> MockActionSheet {
  20. return MockActionSheet(items: items, action: { _, _ in })
  21. }
  22. // MARK: - Initialization
  23. describe("when initialized with parameters") {
  24. it("applies provided items") {
  25. let item1 = createItem("foo")
  26. let item2 = createItem("bar")
  27. let sheet = createSheet([item1, item2])
  28. expect(sheet.items.count).to(equal(2))
  29. expect(sheet.items[0]).to(be(item1))
  30. expect(sheet.items[1]).to(be(item2))
  31. }
  32. it("separates provided items and buttons") {
  33. let button = createButton("Sheeeeeeeeet")
  34. let item1 = createItem("foo")
  35. let item2 = createItem("bar")
  36. let sheet = createSheet([button, item1, item2])
  37. expect(sheet.items.count).to(equal(2))
  38. expect(sheet.items[0]).to(be(item1))
  39. expect(sheet.items[1]).to(be(item2))
  40. expect(sheet.buttons.count).to(equal(1))
  41. expect(sheet.buttons[0]).to(be(button))
  42. }
  43. it("applies default presenter if none is provided") {
  44. let sheet = createSheet()
  45. let isStandard = sheet.presenter is ActionSheetStandardPresenter
  46. let isPopover = sheet.presenter is ActionSheetPopoverPresenter
  47. let isValid = isStandard || isPopover
  48. expect(isValid).to(beTrue())
  49. }
  50. it("applies provided presenter") {
  51. let presenter = ActionSheetPopoverPresenter()
  52. let sheet = MockActionSheet(items: [], presenter: presenter, action: { _, _ in })
  53. expect(sheet.presenter).to(be(presenter))
  54. }
  55. it("applies provided action") {
  56. var counter = 0
  57. let sheet = MockActionSheet(items: []) { _, _ in counter += 1 }
  58. sheet.selectAction(sheet, createItem("foo"))
  59. expect(counter).to(equal(1))
  60. }
  61. }
  62. // MARK: - Properties
  63. describe("appearance") {
  64. it("is initially a copy of standard appearance") {
  65. let original = ActionSheetAppearance.standard.popover.width
  66. ActionSheetAppearance.standard.popover.width = -1
  67. let sheet = createSheet()
  68. let appearance = sheet.appearance
  69. ActionSheetAppearance.standard.popover.width = original
  70. expect(appearance.popover.width).to(equal(-1))
  71. }
  72. }
  73. // MARK: - Header Properties
  74. describe("header view") {
  75. it("refreshes the sheet when set") {
  76. let sheet = createSheet()
  77. expect(sheet.refreshInvokeCount).to(equal(0))
  78. sheet.headerView = UIView()
  79. expect(sheet.refreshInvokeCount).to(equal(1))
  80. }
  81. }
  82. describe("header view container") {
  83. it("gets clear background color when set") {
  84. let sheet = createSheet()
  85. let view = UIView()
  86. view.backgroundColor = .red
  87. sheet.headerViewContainer = view
  88. expect(view.backgroundColor).to(equal(.clear))
  89. }
  90. }
  91. // MARK: - Item Properties
  92. describe("items height") {
  93. it("is sum of all item appearances") {
  94. let item1 = createItem("foo")
  95. let item2 = createItem("bar")
  96. let item3 = createButton("baz")
  97. item1.appearance.height = 100
  98. item2.appearance.height = 110
  99. item3.appearance.height = 120
  100. let sheet = createSheet([item1, item2, item3])
  101. expect(sheet.itemsHeight).to(equal(210))
  102. }
  103. }
  104. describe("item handler") {
  105. it("has correct item type") {
  106. let sheet = createSheet()
  107. expect(sheet.itemHandler.itemType).to(equal(.items))
  108. }
  109. it("has correct items") {
  110. let item1 = createItem("foo")
  111. let item2 = createItem("bar")
  112. let item3 = createButton("baz")
  113. let sheet = createSheet([item1, item2, item3])
  114. expect(sheet.itemHandler.items.count).to(equal(2))
  115. expect(sheet.itemHandler.items[0]).to(be(item1))
  116. expect(sheet.itemHandler.items[1]).to(be(item2))
  117. }
  118. }
  119. describe("item table view") {
  120. it("is correctly setup when set") {
  121. let sheet = createSheet()
  122. let view = UITableView(frame: .zero)
  123. sheet.itemsTableView = view
  124. expect(view.delegate).to(be(sheet.itemHandler))
  125. expect(view.dataSource).to(be(sheet.itemHandler))
  126. expect(view.estimatedRowHeight).to(equal(44))
  127. expect(view.rowHeight).to(equal(UITableView.automaticDimension))
  128. expect(view.cellLayoutMarginsFollowReadableWidth).to(beFalse())
  129. }
  130. }
  131. // MARK: - Button Properties
  132. describe("buttons height") {
  133. it("is sum of all button appearances") {
  134. let item1 = createItem("foo")
  135. let item2 = createButton("bar")
  136. let item3 = createButton("baz")
  137. item1.appearance.height = 100
  138. item2.appearance.height = 110
  139. item3.appearance.height = 120
  140. let sheet = createSheet([item1, item2, item3])
  141. expect(sheet.buttonsHeight).to(equal(230))
  142. }
  143. }
  144. describe("button handler") {
  145. it("has correct item type") {
  146. let sheet = createSheet()
  147. expect(sheet.buttonHandler.itemType).to(equal(.buttons))
  148. }
  149. it("has correct items") {
  150. let item1 = createItem("foo")
  151. let item2 = createButton("bar")
  152. let item3 = createButton("baz")
  153. let sheet = createSheet([item1, item2, item3])
  154. expect(sheet.buttonHandler.items.count).to(equal(2))
  155. expect(sheet.buttonHandler.items[0]).to(be(item2))
  156. expect(sheet.buttonHandler.items[1]).to(be(item3))
  157. }
  158. }
  159. describe("button table view") {
  160. it("is correctly setup when set") {
  161. let sheet = createSheet()
  162. let view = UITableView(frame: .zero)
  163. sheet.buttonsTableView = view
  164. expect(view.delegate).to(be(sheet.buttonHandler))
  165. expect(view.dataSource).to(be(sheet.buttonHandler))
  166. expect(view.estimatedRowHeight).to(equal(44))
  167. expect(view.rowHeight).to(equal(UITableView.automaticDimension))
  168. expect(view.cellLayoutMarginsFollowReadableWidth).to(beFalse())
  169. }
  170. }
  171. // MARK: - Presentation Functions
  172. context("presentation") {
  173. var presenter: MockActionSheetPresenter!
  174. func createSheet() -> MockActionSheet {
  175. presenter = MockActionSheetPresenter()
  176. return MockActionSheet(items: [], presenter: presenter, action: { _, _ in })
  177. }
  178. describe("when dismissed") {
  179. it("dismisses itself by calling presenter") {
  180. var counter = 0
  181. let completion = { counter += 1 }
  182. let sheet = createSheet()
  183. sheet.dismiss(completion: completion)
  184. presenter.dismissInvokeCompletions[0]()
  185. expect(presenter.dismissInvokeCount).to(equal(1))
  186. expect(counter).to(equal(1))
  187. }
  188. }
  189. describe("when presented from view") {
  190. it("refreshes itself") {
  191. let sheet = createSheet()
  192. sheet.present(in: UIViewController(), from: UIView())
  193. expect(sheet.refreshInvokeCount).to(equal(1))
  194. }
  195. it("presents itself by calling presenter") {
  196. var counter = 0
  197. let completion = { counter += 1 }
  198. let sheet = createSheet()
  199. let vc = UIViewController()
  200. let view = UIView()
  201. sheet.present(in: vc, from: view, completion: completion)
  202. presenter.presentInvokeCompletions[0]()
  203. expect(presenter.presentInvokeCount).to(equal(1))
  204. expect(presenter.presentInvokeViewControllers[0]).to(be(vc))
  205. expect(presenter.presentInvokeViews[0]).to(be(view))
  206. expect(counter).to(equal(1))
  207. }
  208. }
  209. describe("when presented from bar button item") {
  210. it("refreshes itself") {
  211. let sheet = createSheet()
  212. sheet.present(in: UIViewController(), from: UIBarButtonItem())
  213. expect(sheet.refreshInvokeCount).to(equal(1))
  214. }
  215. it("presents itself by calling presenter") {
  216. var counter = 0
  217. let completion = { counter += 1 }
  218. let sheet = createSheet()
  219. let vc = UIViewController()
  220. let item = UIBarButtonItem()
  221. sheet.present(in: vc, from: item, completion: completion)
  222. presenter.presentInvokeCompletions[0]()
  223. expect(presenter.presentInvokeCount).to(equal(1))
  224. expect(presenter.presentInvokeViewControllers[0]).to(be(vc))
  225. expect(presenter.presentInvokeItems[0]).to(be(item))
  226. expect(counter).to(equal(1))
  227. }
  228. }
  229. }
  230. // MARK: - Refresh Functions
  231. describe("when refreshing") {
  232. var sheet: MockActionSheet!
  233. var headerViewContainer: UIView!
  234. var itemsView: UITableView!
  235. var buttonsView: UITableView!
  236. var stackView: UIStackView!
  237. beforeEach {
  238. sheet = createSheet()
  239. sheet.appearance.groupMargins = 123
  240. sheet.appearance.cornerRadius = 90
  241. headerViewContainer = UIView(frame: .zero)
  242. itemsView = UITableView(frame: .zero)
  243. buttonsView = UITableView(frame: .zero)
  244. stackView = UIStackView(frame: .zero)
  245. sheet.headerViewContainer = headerViewContainer
  246. sheet.itemsTableView = itemsView
  247. sheet.buttonsTableView = buttonsView
  248. sheet.stackView = stackView
  249. sheet.refreshButtonsVisibilityInvokeCount = 0
  250. sheet.refreshHeaderVisibilityInvokeCount = 0
  251. }
  252. context("sheet") {
  253. it("applies round corners") {
  254. sheet.refresh()
  255. expect(headerViewContainer.layer.cornerRadius).to(equal(90))
  256. expect(itemsView.layer.cornerRadius).to(equal(90))
  257. expect(buttonsView.layer.cornerRadius).to(equal(90))
  258. }
  259. it("applies stack view spacing") {
  260. sheet.refresh()
  261. expect(sheet.stackView?.spacing).to(equal(123))
  262. }
  263. it("asks presenter to refresh sheet") {
  264. let presenter = MockActionSheetPresenter()
  265. let sheet = MockActionSheet(items: [], presenter: presenter) { (_, _) in }
  266. sheet.refresh()
  267. expect(presenter.refreshActionSheetInvokeCount).to(equal(1))
  268. }
  269. }
  270. context("header") {
  271. it("refreshes header visibility") {
  272. sheet.refresh()
  273. expect(sheet.refreshHeaderVisibilityInvokeCount).to(equal(1))
  274. }
  275. it("adds header view to header container") {
  276. let header = UIView(frame: .zero)
  277. sheet.headerView = header
  278. expect(header.constraints.count).to(equal(0))
  279. sheet.refresh()
  280. expect(headerViewContainer.subviews.count).to(equal(1))
  281. expect(headerViewContainer.subviews[0]).to(be(header))
  282. expect(header.translatesAutoresizingMaskIntoConstraints).to(beFalse())
  283. }
  284. }
  285. context("header visibility") {
  286. it("hides header container if header view is nil") {
  287. sheet.refreshHeaderVisibility()
  288. expect(headerViewContainer.isHidden).to(beTrue())
  289. }
  290. it("shows header container if header view is nil") {
  291. sheet.headerView = UIView(frame: .zero)
  292. sheet.refreshHeaderVisibility()
  293. expect(headerViewContainer.isHidden).to(beFalse())
  294. }
  295. }
  296. context("items") {
  297. it("applies appearances to all items") {
  298. let item1 = MockActionSheetItem(title: "foo")
  299. let item2 = MockActionSheetItem(title: "foo")
  300. sheet.setup(items: [item1, item2])
  301. sheet.refresh()
  302. expect(item1.applyAppearanceInvokeCount).to(equal(1))
  303. expect(item2.applyAppearanceInvokeCount).to(equal(1))
  304. expect(item1.applyAppearanceInvokeAppearances[0]).to(be(sheet.appearance))
  305. expect(item2.applyAppearanceInvokeAppearances[0]).to(be(sheet.appearance))
  306. }
  307. it("applies separator color") {
  308. sheet.appearance.itemsSeparatorColor = .yellow
  309. let view = UITableView(frame: .zero)
  310. sheet.itemsTableView = view
  311. sheet.refresh()
  312. expect(view.separatorColor).to(equal(.yellow))
  313. }
  314. }
  315. context("buttons") {
  316. it("refreshes buttons visibility") {
  317. sheet.refresh()
  318. expect(sheet.refreshButtonsVisibilityInvokeCount).to(equal(1))
  319. }
  320. it("applies appearances to all buttons") {
  321. let item1 = MockActionSheetButton(title: "foo", value: true)
  322. let item2 = MockActionSheetButton(title: "foo", value: true)
  323. sheet.setup(items: [item1, item2])
  324. sheet.refresh()
  325. expect(item1.applyAppearanceInvokeCount).to(equal(1))
  326. expect(item2.applyAppearanceInvokeCount).to(equal(1))
  327. expect(item1.applyAppearanceInvokeAppearances[0]).to(be(sheet.appearance))
  328. expect(item2.applyAppearanceInvokeAppearances[0]).to(be(sheet.appearance))
  329. }
  330. it("applies separator color") {
  331. sheet.appearance.buttonsSeparatorColor = .yellow
  332. let view = UITableView(frame: .zero)
  333. sheet.buttonsTableView = view
  334. sheet.refresh()
  335. expect(view.separatorColor).to(equal(.yellow))
  336. }
  337. }
  338. context("button visibility") {
  339. it("hides buttons if sheet has no buttons") {
  340. sheet.refreshButtonsVisibility()
  341. expect(buttonsView.isHidden).to(beTrue())
  342. }
  343. it("shows buttons if sheet has buttons") {
  344. sheet.setup(items: [MockActionSheetButton(title: "foo", value: true)])
  345. sheet.refreshButtonsVisibility()
  346. expect(buttonsView.isHidden).to(beFalse())
  347. }
  348. }
  349. }
  350. // MARK: - Protected Functions
  351. describe("handling tap on item") {
  352. it("reloads data") {
  353. let sheet = createSheet()
  354. sheet.reloadDataInvokeCount = 0
  355. sheet.handleTap(on: createItem(""))
  356. expect(sheet.reloadDataInvokeCount).to(equal(1))
  357. }
  358. it("calls select action without dismiss if item has none tap action") {
  359. var count = 0
  360. let sheet = MockActionSheet(items: []) { (_, _) in count += 1 }
  361. let item = createItem("")
  362. item.tapBehavior = .none
  363. sheet.handleTap(on: item)
  364. expect(count).to(equal(1))
  365. expect(sheet.dismissInvokeCount).to(equal(0))
  366. }
  367. it("calls select action after dismiss if item has dismiss tap action") {
  368. var count = 0
  369. let sheet = MockActionSheet(items: []) { (_, _) in count += 1 }
  370. let item = createItem("")
  371. item.tapBehavior = .dismiss
  372. sheet.handleTap(on: item)
  373. // expect(count).toEventually(equal(1), time) TODO
  374. // expect(sheet.dismissInvokeCount).to(equal(1)) TODO
  375. }
  376. }
  377. describe("margin at position") {
  378. it("uses apperance if no superview value exists") {
  379. let sheet = createSheet()
  380. sheet.appearance.contentInset = 80
  381. expect(sheet.margin(at: .top)).to(equal(80))
  382. expect(sheet.margin(at: .left)).to(equal(80))
  383. expect(sheet.margin(at: .right)).to(equal(80))
  384. expect(sheet.margin(at: .bottom)).to(equal(80))
  385. }
  386. }
  387. describe("reloading data") {
  388. it("reloads both table views") {
  389. let view1 = MockTableView(frame: .zero)
  390. let view2 = MockTableView(frame: .zero)
  391. let sheet = createSheet()
  392. sheet.itemsTableView = view1
  393. sheet.buttonsTableView = view2
  394. sheet.reloadData()
  395. expect(view1.reloadDataInvokeCount).to(equal(1))
  396. expect(view2.reloadDataInvokeCount).to(equal(1))
  397. }
  398. }
  399. }
  400. }