simultaneously_with_two_user_spec.rb 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. # Copyright (C) 2012-2025 Zammad Foundation, https://zammad-foundation.org/
  2. require 'rails_helper'
  3. RSpec.describe 'Ticket > Update > Simultaneously with two different user', performs_jobs: true, type: :system do
  4. let(:group) { Group.find_by(name: 'Users') }
  5. let(:ticket) { create(:ticket, group: group) }
  6. let(:agent) { User.find_by(login: 'agent1@example.com') }
  7. around { |example| perform_enqueued_jobs { example.run } }
  8. # rubocop:disable RSpec/InstanceVariable
  9. define :have_avatar do |expected|
  10. chain(:changed, :text)
  11. match do
  12. elem = find_element
  13. return false if elem.nil?
  14. return true if !@icon && !@no_icon
  15. return elem.has_no_css? '.icon' if @no_icon
  16. elem.has_css? ".icon-#{@icon}"
  17. end
  18. def find_element
  19. if expected.is_a? User
  20. actual.find "#{base_selector}#{select_by_user}"
  21. else
  22. actual.find base_selector, text: expected
  23. end
  24. rescue
  25. nil
  26. end
  27. match_when_negated do
  28. if expected.is_a? User
  29. return actual.has_no_css? "#{base_selector}#{select_by_user}"
  30. end
  31. actual.has_no_css? base_selector, text: expected
  32. end
  33. chain :changed! do
  34. @changed = true
  35. end
  36. chain :with_icon do |icon|
  37. @icon = icon
  38. end
  39. chain :with_no_icon! do
  40. @no_icon = true
  41. end
  42. def select_by_user
  43. "[data-id='#{expected.id}']"
  44. end
  45. def base_selector
  46. changed_class = @changed ? 'changed' : 'not-changed'
  47. ".js-attributeBar .js-avatar .avatar--#{changed_class}"
  48. end
  49. end
  50. # rubocop:enable RSpec/InstanceVariable
  51. def check_taskbar_tab(ticket_id, title: nil, modified: false)
  52. tab_data_key = "Ticket-#{ticket_id}"
  53. if title
  54. taskbar_tab = find(".tasks .task[data-key='#{tab_data_key}']")
  55. expect(taskbar_tab).to have_css('.nav-tab-name', text: title)
  56. end
  57. if modified
  58. expect(page).to have_css(".tasks .task[data-key='#{tab_data_key}'].is-modified")
  59. else
  60. expect(page).to have_no_css(".tasks .task[data-key='#{tab_data_key}'].is-modified")
  61. end
  62. end
  63. context 'when two different users are simultaneously in one ticket' do
  64. before do
  65. visit "#ticket/zoom/#{ticket.id}"
  66. using_session(:second_browser) do
  67. login(
  68. username: agent.login,
  69. password: 'test',
  70. )
  71. visit "#ticket/zoom/#{ticket.id}"
  72. end
  73. end
  74. it 'avatar from other user should be visible in ticket zoom' do
  75. expect(page).to have_avatar('AT')
  76. using_session(:second_browser) do
  77. expect(page).to have_avatar('TA')
  78. end
  79. end
  80. it 'check changes from the first user and added changes from the second user' do
  81. within(:active_content) do
  82. find('.js-textarea').send_keys('some note')
  83. expect(page).to have_css('.js-reset')
  84. end
  85. expect(page).to have_avatar('AT')
  86. using_session(:second_browser) do
  87. expect(page).to have_avatar('TA').changed!
  88. within(:active_content) do
  89. find('.js-textarea').send_keys('some other note')
  90. expect(page).to have_css('.js-reset')
  91. end
  92. end
  93. expect(page).to have_avatar('AT').changed!
  94. using_session(:second_browser) do
  95. within(:active_content) do
  96. click '.js-attributeBar .js-submit'
  97. expect(page).to have_no_css('.js-reset')
  98. expect(page).to have_css('.article-content', text: 'some other note')
  99. end
  100. expect(page).to have_avatar('TA').changed!
  101. end
  102. expect(page).to have_avatar('AT')
  103. check_taskbar_tab(ticket.id, title: ticket.title, modified: true)
  104. within(:active_content) do
  105. expect(page).to have_css('.article-content', text: 'some other note')
  106. click '.js-attributeBar .js-submit'
  107. expect(page).to have_no_css('.js-reset')
  108. expect(page).to have_css('.article-content', text: 'some note')
  109. end
  110. using_session(:second_browser) do
  111. expect(page).to have_avatar('TA')
  112. expect(page).to have_css('.article-content', text: 'some note')
  113. check_taskbar_tab(ticket.id, title: ticket.title, modified: true)
  114. end
  115. # Reload browsers and check if state is correct.
  116. refresh
  117. using_session(:second_browser) do
  118. refresh
  119. expect(page).to have_avatar('TA')
  120. expect(page).to have_no_css('.js-reset')
  121. end
  122. expect(page).to have_avatar('AT')
  123. expect(page).to have_no_css('.js-reset')
  124. end
  125. it 'check refresh for unsaved changes and reset after refresh' do
  126. using_session(:second_browser) do
  127. within(:active_content) do
  128. find('.js-textarea').send_keys('some other note')
  129. expect(page).to have_css('.js-reset')
  130. end
  131. expect(page).to have_avatar('TA')
  132. # We need to wait for the auto save feature.
  133. wait.until do
  134. Taskbar.find_by(key: "Ticket-#{ticket.id}", user_id: agent.id).state_changed?
  135. end
  136. refresh
  137. end
  138. expect(page).to have_avatar('AT').changed!
  139. using_session(:second_browser) do
  140. refresh
  141. within(:active_content) do
  142. click '.js-reset'
  143. expect(page).to have_css('.js-textarea', text: '')
  144. end
  145. end
  146. expect(page).to have_avatar('AT')
  147. end
  148. it 'change title with second user' do
  149. find('.js-textarea').send_keys('some note')
  150. using_session(:second_browser) do
  151. find('.js-textarea').send_keys('some other note')
  152. find('.ticketZoom-header .js-objectTitle').set('TTTsome level 2 <b>subject</b> 123äöü')
  153. # Click in the body field, to trigger the title update.
  154. find('.js-textarea').send_keys('trigger title')
  155. expect(page).to have_css('.js-objectTitle', text: 'TTTsome level 2 <b>subject</b> 123äöü')
  156. check_taskbar_tab(ticket.id, title: 'TTTsome level 2 <b>subject</b> 123äöü')
  157. expect(page).to have_css('.js-textarea', text: 'some other note')
  158. end
  159. expect(page).to have_css('.js-objectTitle', text: 'TTTsome level 2 <b>subject</b> 123äöü')
  160. expect(page).to have_css('.js-textarea', text: 'some note')
  161. check_taskbar_tab(ticket.id, title: 'TTTsome level 2 <b>subject</b> 123äöü', modified: true)
  162. # Refresh and check that modified flag is gone
  163. refresh
  164. check_taskbar_tab(ticket.id, title: 'TTTsome level 2 <b>subject</b> 123äöü', modified: false)
  165. end
  166. end
  167. context 'when working on multiple platforms', authenticated_as: :user do
  168. let(:ticket) { create(:ticket) }
  169. let(:user) { create(:agent, groups: [ticket.group]) }
  170. let(:another_user) { create(:agent, groups: [ticket.group]) }
  171. let(:key) { "Ticket-#{ticket.id}" }
  172. let(:path) { "ticket/zoom/#{ticket.id}" }
  173. let(:taskbar_mobile) { create(:taskbar, user: user, app: :mobile, key: key) }
  174. let(:taskbar_desktop) { create(:taskbar, user: user, app: :desktop, key: key) }
  175. let(:another_taskbar_mobile) { create(:taskbar, user: another_user, app: :mobile, key: key) }
  176. let(:another_taskbar_desktop) { create(:taskbar, user: another_user, app: :desktop, key: key) }
  177. context 'when looking on a ticket' do
  178. before do
  179. taskbar_desktop
  180. visit path
  181. end
  182. it 'does not show current user' do
  183. expect(page).not_to have_avatar(user)
  184. end
  185. end
  186. context 'when another user is looking on desktop' do
  187. before do
  188. another_taskbar_desktop
  189. taskbar_desktop
  190. visit path
  191. end
  192. it 'shows another user' do
  193. expect(page).to have_avatar(another_user).with_no_icon!
  194. end
  195. end
  196. context 'when another user is looking on mobile' do
  197. before do
  198. another_taskbar_mobile
  199. taskbar_desktop
  200. visit path
  201. end
  202. it 'shows another user' do
  203. expect(page).to have_avatar(another_user).with_icon(:mobile)
  204. end
  205. end
  206. context 'when another user is looking on mobile and desktop' do
  207. before do
  208. another_taskbar_mobile
  209. another_taskbar_desktop
  210. taskbar_desktop
  211. visit path
  212. end
  213. it 'shows another user' do
  214. expect(page).to have_avatar(another_user).with_no_icon!
  215. end
  216. end
  217. context 'when another user is editing on desktop' do
  218. before do
  219. another_taskbar_desktop.update!(state: { a: 1 })
  220. taskbar_desktop
  221. visit path
  222. end
  223. it 'shows another user' do
  224. expect(page).to have_avatar(another_user).with_icon(:pen).changed!
  225. end
  226. end
  227. context 'when another user is editing on mobile' do
  228. before do
  229. another_taskbar_mobile.update!(state: { a: 1 })
  230. taskbar_desktop
  231. visit path
  232. end
  233. it 'shows another user' do
  234. expect(page).to have_avatar(another_user).with_icon(:pen).changed!
  235. end
  236. end
  237. context 'when same user is looking on mobile too' do
  238. before do
  239. taskbar_mobile
  240. taskbar_desktop
  241. visit path
  242. end
  243. it 'shows same user' do
  244. expect(page).not_to have_avatar(user)
  245. end
  246. end
  247. context 'when same user is editing' do
  248. before do
  249. taskbar_desktop.update!(state: { a: 1 })
  250. visit path
  251. end
  252. it 'do not show same user' do
  253. expect(page).not_to have_avatar(user)
  254. end
  255. end
  256. context 'when same user is editing on mobile' do
  257. before do
  258. taskbar_mobile.update!(state: { a: 1 })
  259. taskbar_desktop
  260. visit path
  261. end
  262. it 'shows same user' do
  263. expect(page).to have_avatar(user).with_icon(:'mobile-edit').changed!
  264. end
  265. end
  266. end
  267. end