ActionSheetTests.swift 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518
  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. // TODO: Improve these tests, since much logic has changed.
  9. import Quick
  10. import Nimble
  11. @testable import Sheeeeeeeeet
  12. class ActionSheetTests: QuickSpec {
  13. override func spec() {
  14. func createButton(_ title: String) -> ActionSheetButton {
  15. return ActionSheetOkButton(title: title)
  16. }
  17. func createItem(_ title: String) -> ActionSheetItem {
  18. return ActionSheetItem(title: title)
  19. }
  20. func createSheet(_ items: [ActionSheetItem] = []) -> MockActionSheet {
  21. return MockActionSheet(items: items, action: { _, _ in })
  22. }
  23. func createTableView() -> ActionSheetTableView {
  24. return ActionSheetTableView(frame: .zero)
  25. }
  26. // MARK: - Initialization
  27. describe("when initialized with parameters") {
  28. it("applies provided items") {
  29. let item1 = createItem("foo")
  30. let item2 = createItem("bar")
  31. let sheet = createSheet([item1, item2])
  32. expect(sheet.items.count).to(equal(2))
  33. expect(sheet.items[0]).to(be(item1))
  34. expect(sheet.items[1]).to(be(item2))
  35. }
  36. it("separates provided items and buttons") {
  37. let button = createButton("Sheeeeeeeeet")
  38. let item1 = createItem("foo")
  39. let item2 = createItem("bar")
  40. let sheet = createSheet([button, item1, item2])
  41. expect(sheet.items.count).to(equal(2))
  42. expect(sheet.items[0]).to(be(item1))
  43. expect(sheet.items[1]).to(be(item2))
  44. expect(sheet.buttons.count).to(equal(1))
  45. expect(sheet.buttons[0]).to(be(button))
  46. }
  47. it("applies default presenter if none is provided") {
  48. let sheet = createSheet()
  49. let isStandard = sheet.presenter is ActionSheetStandardPresenter
  50. let isPopover = sheet.presenter is ActionSheetPopoverPresenter
  51. let isValid = isStandard || isPopover
  52. expect(isValid).to(beTrue())
  53. }
  54. it("applies provided presenter") {
  55. let presenter = ActionSheetPopoverPresenter()
  56. let sheet = MockActionSheet(items: [], presenter: presenter, action: { _, _ in })
  57. expect(sheet.presenter).to(be(presenter))
  58. }
  59. it("applies provided action") {
  60. var counter = 0
  61. let sheet = MockActionSheet(items: []) { _, _ in counter += 1 }
  62. sheet.selectAction(sheet, createItem("foo"))
  63. expect(counter).to(equal(1))
  64. }
  65. }
  66. // MARK: - Properties
  67. describe("appearance") {
  68. it("is initially a copy of standard appearance") {
  69. let original = ActionSheetAppearance.standard.popover.width
  70. ActionSheetAppearance.standard.popover.width = -1
  71. let sheet = createSheet()
  72. let appearance = sheet.appearance
  73. ActionSheetAppearance.standard.popover.width = original
  74. expect(appearance.popover.width).to(equal(-1))
  75. }
  76. }
  77. // MARK: - Item Properties
  78. describe("items height") {
  79. it("is sum of all item appearances") {
  80. let item1 = createItem("foo")
  81. let item2 = createItem("bar")
  82. let item3 = createButton("baz")
  83. item1.appearance.height = 100
  84. item2.appearance.height = 110
  85. item3.appearance.height = 120
  86. let sheet = createSheet([item1, item2, item3])
  87. expect(sheet.itemsHeight).to(equal(210))
  88. }
  89. }
  90. describe("item handler") {
  91. it("has correct item type") {
  92. let sheet = createSheet()
  93. expect(sheet.itemHandler.itemType).to(equal(.items))
  94. }
  95. it("has correct items") {
  96. let item1 = createItem("foo")
  97. let item2 = createItem("bar")
  98. let item3 = createButton("baz")
  99. let sheet = createSheet([item1, item2, item3])
  100. expect(sheet.itemHandler.items.count).to(equal(2))
  101. expect(sheet.itemHandler.items[0]).to(be(item1))
  102. expect(sheet.itemHandler.items[1]).to(be(item2))
  103. }
  104. }
  105. describe("item table view") {
  106. it("is correctly setup when view is loaded") {
  107. let sheet = createSheet()
  108. let view = createTableView()
  109. sheet.itemsTableView = view
  110. sheet.viewDidLoad()
  111. expect(view.delegate).to(be(sheet.itemHandler))
  112. expect(view.dataSource).to(be(sheet.itemHandler))
  113. expect(view.estimatedRowHeight).to(equal(44))
  114. expect(view.rowHeight).to(equal(UITableView.automaticDimension))
  115. expect(view.cellLayoutMarginsFollowReadableWidth).to(beFalse())
  116. }
  117. }
  118. // MARK: - Button Properties
  119. describe("buttons height") {
  120. it("is sum of all button appearances") {
  121. let item1 = createItem("foo")
  122. let item2 = createButton("bar")
  123. let item3 = createButton("baz")
  124. item1.appearance.height = 100
  125. item2.appearance.height = 110
  126. item3.appearance.height = 120
  127. let sheet = createSheet([item1, item2, item3])
  128. expect(sheet.buttonsHeight).to(equal(230))
  129. }
  130. }
  131. describe("button handler") {
  132. it("has correct item type") {
  133. let sheet = createSheet()
  134. expect(sheet.buttonHandler.itemType).to(equal(.buttons))
  135. }
  136. it("has correct items") {
  137. let item1 = createItem("foo")
  138. let item2 = createButton("bar")
  139. let item3 = createButton("baz")
  140. let sheet = createSheet([item1, item2, item3])
  141. expect(sheet.buttonHandler.items.count).to(equal(2))
  142. expect(sheet.buttonHandler.items[0]).to(be(item2))
  143. expect(sheet.buttonHandler.items[1]).to(be(item3))
  144. }
  145. }
  146. describe("button table view") {
  147. it("is correctly setup when view is loaded") {
  148. let sheet = createSheet()
  149. let view = createTableView()
  150. sheet.buttonsTableView = view
  151. sheet.viewDidLoad()
  152. expect(view.delegate).to(be(sheet.buttonHandler))
  153. expect(view.dataSource).to(be(sheet.buttonHandler))
  154. expect(view.estimatedRowHeight).to(equal(44))
  155. expect(view.rowHeight).to(equal(UITableView.automaticDimension))
  156. expect(view.cellLayoutMarginsFollowReadableWidth).to(beFalse())
  157. }
  158. }
  159. // MARK: - Presentation Functions
  160. context("presentation") {
  161. var presenter: MockActionSheetPresenter!
  162. func createSheet() -> MockActionSheet {
  163. presenter = MockActionSheetPresenter()
  164. return MockActionSheet(items: [], presenter: presenter, action: { _, _ in })
  165. }
  166. describe("when dismissed") {
  167. it("dismisses itself by calling presenter") {
  168. var counter = 0
  169. let completion = { counter += 1 }
  170. let sheet = createSheet()
  171. sheet.dismiss(completion: completion)
  172. presenter.dismissInvokeCompletions[0]()
  173. expect(presenter.dismissInvokeCount).to(equal(1))
  174. expect(counter).to(equal(1))
  175. }
  176. }
  177. describe("when presented from view") {
  178. it("refreshes itself") {
  179. let sheet = createSheet()
  180. sheet.present(in: UIViewController(), from: UIView())
  181. expect(sheet.refreshInvokeCount).to(equal(1))
  182. }
  183. it("presents itself by calling presenter") {
  184. var counter = 0
  185. let completion = { counter += 1 }
  186. let sheet = createSheet()
  187. let vc = UIViewController()
  188. let view = UIView()
  189. sheet.present(in: vc, from: view, completion: completion)
  190. presenter.presentInvokeCompletions[0]()
  191. expect(presenter.presentInvokeCount).to(equal(1))
  192. expect(presenter.presentInvokeViewControllers[0]).to(be(vc))
  193. expect(presenter.presentInvokeViews[0]).to(be(view))
  194. expect(counter).to(equal(1))
  195. }
  196. }
  197. describe("when presented from bar button item") {
  198. it("refreshes itself") {
  199. let sheet = createSheet()
  200. sheet.present(in: UIViewController(), from: UIBarButtonItem())
  201. expect(sheet.refreshInvokeCount).to(equal(1))
  202. }
  203. it("presents itself by calling presenter") {
  204. var counter = 0
  205. let completion = { counter += 1 }
  206. let sheet = createSheet()
  207. let vc = UIViewController()
  208. let item = UIBarButtonItem()
  209. sheet.present(in: vc, from: item, completion: completion)
  210. presenter.presentInvokeCompletions[0]()
  211. expect(presenter.presentInvokeCount).to(equal(1))
  212. expect(presenter.presentInvokeViewControllers[0]).to(be(vc))
  213. expect(presenter.presentInvokeItems[0]).to(be(item))
  214. expect(counter).to(equal(1))
  215. }
  216. }
  217. }
  218. // MARK: - Refresh Functions
  219. describe("when refreshing") {
  220. var sheet: MockActionSheet!
  221. var headerViewContainer: UIView!
  222. var itemsView: ActionSheetTableView!
  223. var buttonsView: ActionSheetTableView!
  224. var stackView: UIStackView!
  225. beforeEach {
  226. sheet = createSheet()
  227. sheet.appearance.groupMargins = 123
  228. sheet.appearance.cornerRadius = 90
  229. headerViewContainer = UIView(frame: .zero)
  230. itemsView = createTableView()
  231. buttonsView = createTableView()
  232. stackView = UIStackView(frame: .zero)
  233. sheet.headerViewContainer = headerViewContainer
  234. sheet.itemsTableView = itemsView
  235. sheet.buttonsTableView = buttonsView
  236. sheet.stackView = stackView
  237. }
  238. context("sheet") {
  239. it("applies round corners") {
  240. sheet.refresh()
  241. expect(headerViewContainer.layer.cornerRadius).to(equal(90))
  242. expect(itemsView.layer.cornerRadius).to(equal(90))
  243. expect(buttonsView.layer.cornerRadius).to(equal(90))
  244. }
  245. it("applies stack view spacing") {
  246. sheet.refresh()
  247. expect(sheet.stackView?.spacing).to(equal(123))
  248. }
  249. it("asks presenter to refresh sheet") {
  250. let presenter = MockActionSheetPresenter()
  251. let sheet = MockActionSheet(items: [], presenter: presenter) { (_, _) in }
  252. sheet.refresh()
  253. expect(presenter.refreshActionSheetInvokeCount).to(equal(1))
  254. }
  255. }
  256. context("header") {
  257. it("refreshes header visibility") {
  258. sheet.refresh()
  259. expect(sheet.refreshHeaderInvokeCount).to(equal(1))
  260. }
  261. it("adds header view to header container") {
  262. let header = UIView(frame: .zero)
  263. sheet.headerView = header
  264. expect(header.constraints.count).to(equal(0))
  265. sheet.refresh()
  266. expect(headerViewContainer.subviews.count).to(equal(1))
  267. expect(headerViewContainer.subviews[0]).to(be(header))
  268. expect(header.translatesAutoresizingMaskIntoConstraints).to(beFalse())
  269. }
  270. }
  271. context("header visibility") {
  272. it("hides header container if header view is nil") {
  273. sheet.refreshHeader()
  274. expect(headerViewContainer.isHidden).to(beTrue())
  275. }
  276. it("shows header container if header view is nil") {
  277. sheet.headerView = UIView(frame: .zero)
  278. sheet.refreshHeader()
  279. expect(headerViewContainer.isHidden).to(beFalse())
  280. }
  281. }
  282. context("items") {
  283. it("applies appearances to all items") {
  284. let item1 = MockActionSheetItem(title: "foo")
  285. let item2 = MockActionSheetItem(title: "foo")
  286. sheet.setup(items: [item1, item2])
  287. sheet.refresh()
  288. expect(item1.applyAppearanceInvokeCount).to(equal(1))
  289. expect(item2.applyAppearanceInvokeCount).to(equal(1))
  290. expect(item1.applyAppearanceInvokeAppearances[0]).to(be(sheet.appearance))
  291. expect(item2.applyAppearanceInvokeAppearances[0]).to(be(sheet.appearance))
  292. }
  293. it("applies background color") {
  294. sheet.appearance.itemsBackgroundColor = .yellow
  295. let view = createTableView()
  296. sheet.itemsTableView = view
  297. sheet.refresh()
  298. expect(view.backgroundColor).to(equal(.yellow))
  299. }
  300. it("applies separator color") {
  301. sheet.appearance.itemsSeparatorColor = .yellow
  302. let view = createTableView()
  303. sheet.itemsTableView = view
  304. sheet.refresh()
  305. expect(view.separatorColor).to(equal(.yellow))
  306. }
  307. }
  308. context("buttons") {
  309. it("refreshes buttons visibility") {
  310. sheet.refresh()
  311. expect(sheet.refreshButtonsInvokeCount).to(equal(1))
  312. }
  313. it("applies appearances to all buttons") {
  314. let item1 = MockActionSheetButton(title: "foo", value: true)
  315. let item2 = MockActionSheetButton(title: "foo", value: true)
  316. sheet.setup(items: [item1, item2])
  317. sheet.refresh()
  318. expect(item1.applyAppearanceInvokeCount).to(equal(1))
  319. expect(item2.applyAppearanceInvokeCount).to(equal(1))
  320. expect(item1.applyAppearanceInvokeAppearances[0]).to(be(sheet.appearance))
  321. expect(item2.applyAppearanceInvokeAppearances[0]).to(be(sheet.appearance))
  322. }
  323. it("applies background color") {
  324. sheet.appearance.buttonsBackgroundColor = .yellow
  325. let view = createTableView()
  326. sheet.buttonsTableView = view
  327. sheet.refresh()
  328. expect(view.backgroundColor).to(equal(.yellow))
  329. }
  330. it("applies separator color") {
  331. sheet.appearance.buttonsSeparatorColor = .yellow
  332. let view = createTableView()
  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.refreshButtons()
  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.refreshButtons()
  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. }