ActionSheetTests.swift 21 KB

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