# Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/ require 'rails_helper' require 'system/examples/text_modules_examples' require 'system/examples/macros_examples' RSpec.describe 'Ticket Update', type: :system do let(:group) { Group.find_by(name: 'Users') } let(:ticket) { create(:ticket, group: group) } # Regression test for issue #2242 - mandatory fields can be empty (or "-") on ticket update context 'when updating a ticket without its required select attributes' do it 'frontend checks reject the update', db_strategy: :reset do # setup and migrate a required select attribute attribute = create_attribute(:object_manager_attribute_select, :required_screen, data_option: { options: { 'name 1': 'name 1', 'name 2': 'name 2', }, default: '', null: false, relation: '', maxlength: 255, nulloption: true, }) # create a new ticket and attempt to update its state without the required select attribute visit "#ticket/zoom/#{ticket.id}" within(:active_content) do expect(page).to have_css('.js-objectNumber', text: ticket.number) select('closed', from: 'state_id') click('.js-attributeBar .js-submit') expect(page).to have_no_css('.js-submitDropdown .js-submit[disabled]') end # the update should have failed and thus the ticket is still in the new state expect(ticket.reload.state.name).to eq('new') within(:active_content) do # update should work now find(".edit [name=#{attribute.name}]").select('name 2') click('.js-attributeBar .js-submit') expect(page).to have_no_css('.js-submitDropdown .js-submit[disabled]') end ticket.reload expect(ticket[attribute.name]).to eq('name 2') expect(ticket.state.name).to eq('closed') end end context 'when updating a ticket date attribute', db_strategy: :reset do let!(:date_attribute) do create_attribute( :object_manager_attribute_date, name: 'example_date', screens: { create: { 'ticket.agent' => { shown: true }, }, edit: { 'ticket.agent' => { shown: true } }, view: { 'ticket.agent' => { shown: true }, } }, data_option: { 'future' => true, 'past' => false, 'diff' => 0, 'null' => true, } ) end let(:ticket) { create(:ticket, group: group, "#{date_attribute.name}": '2018-02-28') } it 'set date attribute to empty' do visit "#ticket/zoom/#{ticket.id}" within(:active_content) do check_date_field_value(date_attribute.name, '02/28/2018') set_date_field_value(date_attribute.name, '') click('.js-attributeBar .js-submit') expect(page).to have_no_css('.js-submitDropdown .js-submit[disabled]') ticket.reload expect(ticket[date_attribute.name]).to be_nil end end end context 'when updating a ticket with macro' do context 'when required tree_select field is present' do it 'performs no validation (#2492)', db_strategy: :reset do # setup and migrate a required select attribute attribute = create_attribute(:object_manager_attribute_tree_select, :required_screen, data_option: { options: [ { name: 'name 1', value: 'name 1', }, { name: 'name 2', value: 'name 2', }, ], default: '', null: false, relation: '', maxlength: 255, nulloption: true, }) attribute_value = 'name 2' state = Ticket::State.by_category(:closed).first macro = create(:macro, perform: { 'ticket.state_id' => { value: state.id, }, "ticket.#{attribute.name}" => { value: attribute_value, }, }, ux_flow_next_up: 'none',) # refresh browser to get macro accessible refresh # create a new ticket and attempt to update its state without the required select attribute visit "#ticket/zoom/#{ticket.id}" within(:active_content) do expect(page).to have_css('.js-objectNumber', text: ticket.number) expect(page).to have_field(attribute.name, with: '', visible: :hidden) expect(page).to have_select('state_id', selected: 'new', options: ['new', 'closed', 'open', 'pending close', 'pending reminder']) click('.js-openDropdownMacro') click(".js-dropdownActionMacro[data-id=\"#{macro.id}\"]") expect(page).to have_no_css('.js-submitDropdown .js-submit[disabled]') end expect(page).to have_field(attribute.name, with: attribute_value, visible: :hidden) expect(page).to have_select('state_id', selected: 'closed', options: ['closed', 'open', 'pending close', 'pending reminder']) # the update should not have failed and thus the ticket is in closed state ticket.reload expect(ticket[attribute.name]).to eq(attribute_value) expect(ticket.state.name).to eq(state.name) end end context 'when macro has article configured' do it 'creates an article with the configured attributes' do state = Ticket::State.find_by(name: 'closed') macro = create(:macro, perform: { 'ticket.state_id' => { value: state.id, }, 'article.note' => { 'body' => 'test body', 'internal' => 'true', 'subject' => 'test sub' }, }, ux_flow_next_up: 'none',) # refresh browser to get macro accessible refresh # create a new ticket and attempt to update its state without the required select attribute visit "#ticket/zoom/#{ticket.id}" within(:active_content) do expect(page).to have_css('.js-objectNumber', text: ticket.number) expect(page).to have_select('state_id', selected: 'new', options: ['new', 'closed', 'open', 'pending close', 'pending reminder']) click('.js-openDropdownMacro') click(".js-dropdownActionMacro[data-id=\"#{macro.id}\"]") expect(page).to have_no_css('.js-submitDropdown .js-submit[disabled]') end expect(page).to have_css('.content.active .article-content', text: 'test body') expect(page).to have_select('state_id', selected: 'closed', options: ['closed', 'open', 'pending close', 'pending reminder']) # the update should not have failed and thus the ticket is in closed state ticket.reload expect(ticket.state.name).to eq(state.name) article = ticket.articles.last expect(article).to be_present expect(article.body).to eq('test body') expect(article.subject).to eq('test sub') expect(article.internal).to be(true) end end end context 'when merging tickets' do let!(:user) { create(:user) } let!(:origin_ticket) { create(:ticket, group: group) } let!(:target_ticket) { create(:ticket, group: group) } before do origin_ticket.merge_to(ticket_id: target_ticket.id, user_id: user.id) end # Issue #2469 - Add information "Ticket merged" to History it 'tickets history of both tickets should show the merge event' do visit "#ticket/zoom/#{origin_ticket.id}" within(:active_content) do expect(page).to have_css('.js-actions .dropdown-toggle') click '.js-actions .dropdown-toggle' click '.js-actions .dropdown-menu [data-type="ticket-history"]' in_modal do expect(page).to have_content "this ticket was merged into ticket ##{target_ticket.number}" expect(page).to have_link "##{target_ticket.number}", href: "#ticket/zoom/#{target_ticket.id}" end visit "#ticket/zoom/#{target_ticket.id}" expect(page).to have_css('.js-actions .dropdown-toggle') click '.js-actions .dropdown-toggle' click '.js-actions .dropdown-menu [data-type="ticket-history"]' in_modal do expect(page).to have_content("ticket ##{origin_ticket.number} was merged into this ticket") expect(page).to have_link "##{origin_ticket.number}", href: "#ticket/zoom/#{origin_ticket.id}" end end end # Issue #2960 - Ticket removal of merged / linked tickets doesn't remove references context 'when the merged origin ticket is deleted' do before do origin_ticket.destroy end it 'shows the target ticket history' do visit "#ticket/zoom/#{target_ticket.id}" within(:active_content) do expect(page).to have_css('.js-actions .dropdown-toggle') click '.js-actions .dropdown-toggle' click '.js-actions .dropdown-menu [data-type="ticket-history"]' end in_modal do expect(page).to have_text "##{origin_ticket.number} #{origin_ticket.title}" end end end # Issue #2960 - Ticket removal of merged / linked tickets doesn't remove references context 'when the merged target ticket is deleted' do before do target_ticket.destroy end it 'shows the origin history' do visit "#ticket/zoom/#{origin_ticket.id}" within(:active_content) do expect(page).to have_css('.js-actions .dropdown-toggle') click '.js-actions .dropdown-toggle' click '.js-actions .dropdown-menu [data-type="ticket-history"]' end in_modal do expect(page).to have_text "##{target_ticket.number} #{target_ticket.title}" end end end end context 'when closing taskbar tab for ticket' do it 'close task bar entry after some changes in ticket update form' do visit "#ticket/zoom/#{ticket.id}" within(:active_content) do find('.js-textarea').send_keys('some note') end taskbar_tab_close("Ticket-#{ticket.id}") end end context 'when using text modules' do include_examples 'text modules', path: "#ticket/zoom/#{Ticket.first.id}", ticket: Ticket.first context 'when owner is used in a text module and was updated in the ticket', authenticated_as: :authenticate do let(:user) { User.find_by(email: 'agent1@example.com') } let(:another_user) { create(:agent, groups: [Group.find_by(name: 'Users')]) } let(:ticket) { create(:ticket, group: group, owner: user) } let(:text_module) { create(:text_module, name: 'firstlast', keywords: 'firstlast', content: '#{ticket.owner.firstname} #{ticket.owner.lastname}') } # rubocop:disable Lint/InterpolationCheck def authenticate ticket && text_module && another_user true end def select_text_module find(:richtext).send_keys(':') find(:richtext).send_keys(':') find(:richtext).send_keys('firstlast') expect(page).to have_selector(:text_module, text_module.id) find(:richtext).send_keys(:enter) end it 'updates used data' do visit "#ticket/zoom/#{ticket.id}" expect(page).to have_field('owner_id', with: user.id) within(:active_content) do select_text_module expect(find(:richtext).text).to include("#{user.firstname} #{user.lastname}") select another_user.fullname, from: 'Owner' find('.js-submit').click expect(ticket.reload.owner_id).to eq(another_user.id) select_text_module expect(find(:richtext).text).to include("#{another_user.firstname} #{another_user.lastname}") end end end end context 'when using macros' do include_examples 'macros', path: "#ticket/zoom/#{Ticket.first.id}" end context 'when group will be changed' do let(:user) { User.find_by(email: 'agent1@example.com') } let(:ticket) { create(:ticket, group: group, owner: user) } it 'check that owner resets after group change' do visit "#ticket/zoom/#{ticket.id}" expect(page).to have_field('owner_id', with: user.id) set_tree_select_value('group_id', '') # empty selection expect(page).to have_field('owner_id', with: '') end end end