common_actions.rb 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. module CommonActions
  2. delegate :app_host, to: Capybara
  3. # Performs a login with the given credentials and closes the clues (if present).
  4. # The 'remember me' can optionally be checked.
  5. #
  6. # @example
  7. # login(
  8. # username: 'master@example.com',
  9. # password: 'test',
  10. # )
  11. #
  12. # @example
  13. # login(
  14. # username: 'master@example.com',
  15. # password: 'test',
  16. # remember_me: true,
  17. # )
  18. #
  19. # return [nil]
  20. def login(username:, password:, remember_me: false)
  21. visit '/'
  22. within('#login') do
  23. fill_in 'username', with: username
  24. fill_in 'password', with: password
  25. # check via label because checkbox is hidden
  26. click('.checkbox-replacement') if remember_me
  27. # submit
  28. click_button
  29. end
  30. wait(4).until_exists do
  31. current_login
  32. end
  33. end
  34. # Checks if the current session is logged in.
  35. #
  36. # @example
  37. # logged_in?
  38. # => true
  39. #
  40. # @return [true, false]
  41. def logged_in?
  42. current_login.present?
  43. rescue Capybara::ElementNotFound
  44. false
  45. end
  46. # Returns the login of the currently logged in user.
  47. #
  48. # @example
  49. # current_login
  50. # => 'master@example.com'
  51. #
  52. # @return [String] the login of the currently logged in user.
  53. def current_login
  54. find('.user-menu .user a')[:title]
  55. end
  56. # Returns the User record for the currently logged in user.
  57. #
  58. # @example
  59. # current_user.login
  60. # => 'master@example.com'
  61. #
  62. # @example
  63. # current_user do |user|
  64. # user.group_names_access_map = group_names_access_map
  65. # user.save!
  66. # end
  67. #
  68. # @return [User] the current user record.
  69. def current_user
  70. ::User.find_by(login: current_login).tap do |user|
  71. yield user if block_given?
  72. end
  73. end
  74. # Logs out the currently logged in user.
  75. #
  76. # @example
  77. # logout
  78. #
  79. def logout
  80. visit('logout')
  81. end
  82. # Overwrites the Capybara::Session#visit method to allow SPA navigation
  83. # and visiting of external URLs.
  84. # All routes not starting with `/` will be handled as SPA routes.
  85. # All routes containing `://` will be handled as an external URL.
  86. #
  87. # @see Capybara::Session#visit
  88. #
  89. # @example
  90. # visit('logout')
  91. # => visited SPA route 'localhost:32435/#logout'
  92. #
  93. # @example
  94. # visit('/test/ui')
  95. # => visited regular route 'localhost:32435/test/ui'
  96. #
  97. # @example
  98. # visit('https://zammad.org')
  99. # => visited external URL 'https://zammad.org'
  100. #
  101. def visit(route)
  102. if route.include?('://')
  103. return without_port do
  104. super(route)
  105. end
  106. elsif !route.start_with?('/')
  107. route = "/##{route}"
  108. end
  109. super(route)
  110. end
  111. # Overwrites the global Capybara.always_include_port setting (true)
  112. # with false. This comes in handy when visiting external pages.
  113. #
  114. def without_port
  115. original = Capybara.current_session.config.always_include_port
  116. Capybara.current_session.config.always_include_port = false
  117. yield
  118. ensure
  119. Capybara.current_session.config.always_include_port = original
  120. end
  121. # This method is equivalent to Capybara::RSpecMatchers#have_current_path
  122. # but checks the SPA route instead of the actual path.
  123. #
  124. # @see Capybara::RSpecMatchers#have_current_path
  125. #
  126. # @example
  127. # expect(page).to have_current_route('login')
  128. # => checks for SPA route '/#login'
  129. #
  130. def have_current_route(route, **options)
  131. if route.is_a?(String)
  132. route = Regexp.new(Regexp.quote("/##{route}"))
  133. end
  134. # wait 1 sec by default because Firefox is slow
  135. options.reverse_merge!(wait: 1, url: true)
  136. have_current_path(route, **options)
  137. end
  138. # This is a convenient wrapper method around #have_current_route
  139. # which requires no previous `expect(page).to ` call.
  140. #
  141. # @example
  142. # expect_current_route('login')
  143. # => checks for SPA route '/#login'
  144. #
  145. def expect_current_route(route, **options)
  146. expect(page).to have_current_route(route, **options)
  147. end
  148. # Create and migrate an object manager attribute and verify that it exists. Returns the newly attribute.
  149. #
  150. # Create a select attribute:
  151. # @example
  152. # attribute = setup_attribute :object_manager_attribute_select
  153. #
  154. # Create a required text attribute:
  155. # @example
  156. # attribute = setup_attribute :object_manager_attribute_text,
  157. # screens: attributes_for(:required_screen)
  158. #
  159. # Create a date attribute with custom parameters:
  160. # @example
  161. # attribute = setup_attribute :object_manager_attribute_date,
  162. # data_option: {
  163. # 'future' => true,
  164. # 'past' => false,
  165. # 'diff' => 24,
  166. # 'null' => true,
  167. # }
  168. #
  169. # return [attribute]
  170. def create_attribute(attribute_name, attribute_parameters = {})
  171. attribute = create(attribute_name, attribute_parameters)
  172. ObjectManager::Attribute.migration_execute
  173. page.driver.browser.navigate.refresh
  174. attribute
  175. end
  176. # opens the macro list in the ticket view via click
  177. #
  178. # @example
  179. # open_macro_list
  180. #
  181. def open_macro_list
  182. click '.js-openDropdownMacro'
  183. end
  184. def open_article_meta
  185. retry_on_stale do
  186. wrapper = all('div.ticket-article-item').last
  187. wrapper.find('.article-content .textBubble').click
  188. wait(3).until do
  189. wrapper.find('.article-content-meta .article-meta.top').in_fixed_position
  190. end
  191. end
  192. end
  193. def use_template(template)
  194. wait(4).until do
  195. field = find('#form-template select[name="id"]')
  196. option = field.find(:option, template.name)
  197. option.select_option
  198. click '.sidebar-content .js-apply'
  199. # this is a workaround for a race condition where
  200. # the template selection get's re-rendered after
  201. # a selection was made. The selection is lost and
  202. # the apply click has no effect.
  203. template.options.any? do |attribute, value|
  204. selector = %([name="#{attribute}"])
  205. next if !page.has_css?(selector, wait: 0)
  206. find(selector, wait: 0, visible: false).value == value
  207. end
  208. end
  209. end
  210. # Checks if modal is ready
  211. #
  212. # @param timeout [Integer] seconds to wait
  213. def modal_ready(timeout: 4)
  214. wait(timeout).until_exists { find('.modal.in', wait: 0) }
  215. end
  216. # Checks if modal has disappeared
  217. #
  218. # @param timeout [Integer] seconds to wait
  219. def modal_disappear(timeout: 4)
  220. wait(timeout).until_disappears { find('.modal', wait: 0) }
  221. end
  222. # Executes action inside of modal. Makes sure modal has opened and closes
  223. #
  224. # @param timeout [Integer] seconds to wait
  225. # @param wait_for_disappear [Bool] wait for modal to close
  226. def in_modal(timeout: 4, disappears: true, &block)
  227. modal_ready(timeout: timeout)
  228. within('.modal', &block)
  229. modal_disappear(timeout: timeout) if disappears
  230. end
  231. end
  232. RSpec.configure do |config|
  233. config.include CommonActions, type: :system
  234. end