From 30e28e71f63b605fa05d3f495152c58aa5ff38e9 Mon Sep 17 00:00:00 2001 From: Martin Edenhofer Date: Sun, 20 Mar 2016 20:09:52 +0100 Subject: [PATCH 01/11] Added asset support for jobs, overviews and slas. --- app/controllers/application_controller.rb | 15 ++ app/controllers/calendars_controller.rb | 3 +- app/models/application_model.rb | 42 ++++ app/models/calendar.rb | 2 + app/models/job.rb | 3 + app/models/job/assets.rb | 47 ++++ app/models/overview.rb | 5 +- app/models/overview/assets.rb | 56 +++++ app/models/sla.rb | 6 + app/models/sla/assets.rb | 52 ++++ db/migrate/20120101000010_create_ticket.rb | 4 +- test/unit/assets_test.rb | 261 ++++++++++++++++++++- 12 files changed, 490 insertions(+), 6 deletions(-) create mode 100644 app/models/job/assets.rb create mode 100644 app/models/overview/assets.rb create mode 100644 app/models/sla/assets.rb diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 97595bfd6..9c0d31434 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -378,6 +378,21 @@ class ApplicationController < ActionController::Base def model_index_render (object, _params) generic_objects = object.all + + if params[:full] + assets = {} + item_ids = [] + generic_objects.each {|item| + item_ids.push item.id + assets = item.assets(assets) + } + render json: { + record_ids: item_ids, + assets: assets, + }, status: :ok + return + end + generic_objects_with_associations = [] generic_objects.each {|item| generic_objects_with_associations.push item.attributes_with_associations diff --git a/app/controllers/calendars_controller.rb b/app/controllers/calendars_controller.rb index 7ecc83614..5a429d0bd 100644 --- a/app/controllers/calendars_controller.rb +++ b/app/controllers/calendars_controller.rb @@ -6,9 +6,8 @@ class CalendarsController < ApplicationController def index return if deny_if_not_role(Z_ROLENAME_ADMIN) - assets = {} - # calendars + assets = {} calendar_ids = [] Calendar.all.order(:name, :created_at).each {|calendar| calendar_ids.push calendar.id diff --git a/app/models/application_model.rb b/app/models/application_model.rb index 28e0c17a8..63dcb5b30 100644 --- a/app/models/application_model.rb +++ b/app/models/application_model.rb @@ -1131,6 +1131,48 @@ get assets of object list assets end +=begin + +get assets and record_ids of selector + + model = Model.find(123) + + assets = model.assets_of_selector('attribute_name_of_selector', assets) + +=end + + def assets_of_selector(selector, assets = {}) + + # get assets of condition + models = Models.all + send(selector).each {|item, content| + attribute = item.split(/\./) + next if !attribute[1] + attribute_class = attribute[0].to_classname.constantize + reflection = attribute[1].sub(/_id$/, '') + #reflection = reflection.to_sym + next if !models[attribute_class] + next if !models[attribute_class][:reflections] + next if !models[attribute_class][:reflections][reflection] + next if !models[attribute_class][:reflections][reflection].klass + attribute_ref_class = models[attribute_class][:reflections][reflection].klass + if content['value'].class == Array + content['value'].each {|item_id| + attribute_object = attribute_ref_class.find_by(id: item_id) + if attribute_object + assets = attribute_object.assets(assets) + end + } + else + attribute_object = attribute_ref_class.find_by(id: content['value']) + if attribute_object + assets = attribute_object.assets(assets) + end + end + } + assets + end + private def attachments_buffer diff --git a/app/models/calendar.rb b/app/models/calendar.rb index a91fa8bf5..d22f6bcbc 100644 --- a/app/models/calendar.rb +++ b/app/models/calendar.rb @@ -10,6 +10,8 @@ class Calendar < ApplicationModel after_update :sync_default, :min_one_check after_destroy :min_one_check + notify_clients_support + =begin set inital default calendar diff --git a/app/models/job.rb b/app/models/job.rb index 7d4a66bf7..70bb39b7b 100644 --- a/app/models/job.rb +++ b/app/models/job.rb @@ -1,6 +1,9 @@ # Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/ class Job < ApplicationModel + load 'job/assets.rb' + include Job::Assets + store :timeplan store :condition store :perform diff --git a/app/models/job/assets.rb b/app/models/job/assets.rb new file mode 100644 index 000000000..620fb3914 --- /dev/null +++ b/app/models/job/assets.rb @@ -0,0 +1,47 @@ +# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/ + +class Job + module Assets + +=begin + +get all assets / related models for this job + + job = Job.find(123) + result = job.assets(assets_if_exists) + +returns + + result = { + :jobs => { + 123 => job_model_123, + 1234 => job_model_1234, + } + } + +=end + + def assets (data) + + if !data[ Job.to_app_model ] + data[ Job.to_app_model ] = {} + end + if !data[ User.to_app_model ] + data[ User.to_app_model ] = {} + end + if !data[ Job.to_app_model ][ id ] + data[ Job.to_app_model ][ id ] = attributes_with_associations + data = assets_of_selector('condition', data) + data = assets_of_selector('perform', data) + end + %w(created_by_id updated_by_id).each {|local_user_id| + next if !self[ local_user_id ] + next if data[ User.to_app_model ][ self[ local_user_id ] ] + user = User.lookup(id: self[ local_user_id ]) + next if !user + data = user.assets(data) + } + data + end + end +end diff --git a/app/models/overview.rb b/app/models/overview.rb index a56b585c9..abfedfd66 100644 --- a/app/models/overview.rb +++ b/app/models/overview.rb @@ -1,7 +1,10 @@ # Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/ class Overview < ApplicationModel - has_and_belongs_to_many :users, after_add: :cache_update, after_remove: :cache_update + load 'overview/assets.rb' + include Overview::Assets + + has_and_belongs_to_many :users, after_add: :cache_update, after_remove: :cache_update store :condition store :order store :view diff --git a/app/models/overview/assets.rb b/app/models/overview/assets.rb new file mode 100644 index 000000000..48d9da93e --- /dev/null +++ b/app/models/overview/assets.rb @@ -0,0 +1,56 @@ +# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/ + +class Overview + module Assets + +=begin + +get all assets / related models for this overview + + overview = Overview.find(123) + result = overview.assets(assets_if_exists) + +returns + + result = { + :overviews => { + 123 => overview_model_123, + 1234 => overview_model_1234, + } + } + +=end + + def assets (data) + + if !data[ Overview.to_app_model ] + data[ Overview.to_app_model ] = {} + end + if !data[ User.to_app_model ] + data[ User.to_app_model ] = {} + end + if !data[ Overview.to_app_model ][ id ] + data[ Overview.to_app_model ][ id ] = attributes_with_associations + if user_ids + user_ids.each {|local_user_id| + next if data[ User.to_app_model ][ local_user_id ] + user = User.lookup(id: local_user_id) + next if !user + data = user.assets(data) + } + end + + data = assets_of_selector('condition', data) + + end + %w(created_by_id updated_by_id).each {|local_user_id| + next if !self[ local_user_id ] + next if data[ User.to_app_model ][ self[ local_user_id ] ] + user = User.lookup(id: self[ local_user_id ]) + next if !user + data = user.assets(data) + } + data + end + end +end diff --git a/app/models/sla.rb b/app/models/sla.rb index 950fe21e7..af0d2cb66 100644 --- a/app/models/sla.rb +++ b/app/models/sla.rb @@ -1,8 +1,14 @@ # Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/ class Sla < ApplicationModel + load 'sla/assets.rb' + include Sla::Assets + store :condition store :data validates :name, presence: true belongs_to :calendar + + notify_clients_support + end diff --git a/app/models/sla/assets.rb b/app/models/sla/assets.rb new file mode 100644 index 000000000..18756e399 --- /dev/null +++ b/app/models/sla/assets.rb @@ -0,0 +1,52 @@ +# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/ + +class Sla + module Assets + +=begin + +get all assets / related models for this sla + + sla = Sla.find(123) + result = sla.assets(assets_if_exists) + +returns + + result = { + :slas => { + 123 => sla_model_123, + 1234 => sla_model_1234, + } + } + +=end + + def assets (data) + + if !data[ Sla.to_app_model ] + data[ Sla.to_app_model ] = {} + end + if !data[ User.to_app_model ] + data[ User.to_app_model ] = {} + end + if !data[ Sla.to_app_model ][ id ] + data[ Sla.to_app_model ][ id ] = attributes_with_associations + data = assets_of_selector('condition', data) + if calendar_id + calendar = Calendar.lookup(id: calendar_id) + if calendar + data = calendar.assets(data) + end + end + end + %w(created_by_id updated_by_id).each {|local_user_id| + next if !self[ local_user_id ] + next if data[ User.to_app_model ][ self[ local_user_id ] ] + user = User.lookup(id: self[ local_user_id ]) + next if !user + data = user.assets(data) + } + data + end + end +end diff --git a/db/migrate/20120101000010_create_ticket.rb b/db/migrate/20120101000010_create_ticket.rb index b2359d099..100e4f049 100644 --- a/db/migrate/20120101000010_create_ticket.rb +++ b/db/migrate/20120101000010_create_ticket.rb @@ -357,8 +357,8 @@ class CreateTicket < ActiveRecord::Migration t.column :update_time, :integer, null: true t.column :close_time, :integer, null: true t.column :condition, :string, limit: 5000, null: true - t.column :data, :string, limit: 5000, null: true - t.column :timezone, :string, limit: 50, null: true + t.column :data, :string, limit: 5000, null: true + t.column :timezone, :string, limit: 50, null: true t.column :active, :boolean, null: false, default: true t.column :updated_by_id, :integer, null: false t.column :created_by_id, :integer, null: false diff --git a/test/unit/assets_test.rb b/test/unit/assets_test.rb index 014057f81..134e9c416 100644 --- a/test/unit/assets_test.rb +++ b/test/unit/assets_test.rb @@ -4,7 +4,7 @@ require 'test_helper' class AssetsTest < ActiveSupport::TestCase test 'user' do - roles = Role.where( name: %w(Agent Admin) ) + roles = Role.where(name: %w(Agent Admin)) groups = Group.all org = Organization.create_or_update( name: 'some user org', @@ -278,4 +278,263 @@ class AssetsTest < ActiveSupport::TestCase #puts "ERROR: difference \n1: #{o1.inspect}\n2: #{o2.inspect}\ndiff: #{(o1.to_a - o2.to_a).inspect}" false end + + test 'overview' do + + UserInfo.current_user_id = 1 + roles = Role.where(name: %w(Customer)) + + user1 = User.create_or_update( + login: 'assets_overview1@example.org', + firstname: 'assets_overview1', + lastname: 'assets_overview1', + email: 'assets_overview1@example.org', + password: 'some_pass', + active: true, + roles: roles, + ) + user2 = User.create_or_update( + login: 'assets_overview2@example.org', + firstname: 'assets_overview2', + lastname: 'assets_overview2', + email: 'assets_overview2@example.org', + password: 'some_pass', + active: true, + roles: roles, + ) + user3 = User.create_or_update( + login: 'assets_overview3@example.org', + firstname: 'assets_overview3', + lastname: 'assets_overview3', + email: 'assets_overview3@example.org', + password: 'some_pass', + active: true, + roles: roles, + ) + user4 = User.create_or_update( + login: 'assets_overview4@example.org', + firstname: 'assets_overview4', + lastname: 'assets_overview4', + email: 'assets_overview4@example.org', + password: 'some_pass', + active: true, + roles: roles, + ) + user5 = User.create_or_update( + login: 'assets_overview5@example.org', + firstname: 'assets_overview5', + lastname: 'assets_overview5', + email: 'assets_overview5@example.org', + password: 'some_pass', + active: true, + roles: roles, + ) + + ticket_state1 = Ticket::State.find_by(name: 'new') + ticket_state2 = Ticket::State.find_by(name: 'open') + overview_role = Role.find_by(name: 'Agent') + overview = Overview.create_or_update( + name: 'my asset test', + link: 'my_asset_test', + prio: 1000, + role_id: overview_role.id, + user_ids: [ user4.id, user5.id ], + condition: { + 'ticket.state_id' => { + operator: 'is', + value: [ ticket_state1.id, ticket_state2.id ], + }, + 'ticket.owner_id' => { + operator: 'is', + pre_condition: 'specific', + value: user1.id, + value_completion: 'John Smith ' + }, + }, + order: { + by: 'created_at', + direction: 'ASC', + }, + view: { + d: %w(title customer group created_at), + s: %w(title customer group created_at), + m: %w(number title customer group created_at), + view_mode_default: 's', + }, + ) + assets = overview.assets({}) + assert(assets[:User][user1.id], 'check assets') + assert_not(assets[:User][user2.id], 'check assets') + assert_not(assets[:User][user3.id], 'check assets') + assert(assets[:User][user4.id], 'check assets') + assert(assets[:User][user5.id], 'check assets') + assert(assets[:TicketState][ticket_state1.id], 'check assets') + assert(assets[:TicketState][ticket_state2.id], 'check assets') + + overview = Overview.create_or_update( + name: 'my asset test', + link: 'my_asset_test', + prio: 1000, + role_id: overview_role.id, + user_ids: [ user4.id ], + condition: { + 'ticket.state_id' => { + operator: 'is', + value: ticket_state1.id, + }, + 'ticket.owner_id' => { + operator: 'is', + pre_condition: 'specific', + value: [user1.id, user2.id], + }, + }, + order: { + by: 'created_at', + direction: 'ASC', + }, + view: { + d: %w(title customer group created_at), + s: %w(title customer group created_at), + m: %w(number title customer group created_at), + view_mode_default: 's', + }, + ) + assets = overview.assets({}) + assert(assets[:User][user1.id], 'check assets') + assert(assets[:User][user2.id], 'check assets') + assert_not(assets[:User][user3.id], 'check assets') + assert(assets[:User][user4.id], 'check assets') + assert_not(assets[:User][user5.id], 'check assets') + assert(assets[:TicketState][ticket_state1.id], 'check assets') + assert_not(assets[:TicketState][ticket_state2.id], 'check assets') + + end + + test 'sla' do + + UserInfo.current_user_id = 1 + roles = Role.where(name: %w(Customer)) + + user1 = User.create_or_update( + login: 'assets_sla1@example.org', + firstname: 'assets_sla1', + lastname: 'assets_sla1', + email: 'assets_sla1@example.org', + password: 'some_pass', + active: true, + roles: roles, + ) + user2 = User.create_or_update( + login: 'assets_sla2@example.org', + firstname: 'assets_sla2', + lastname: 'assets_sla2', + email: 'assets_sla2@example.org', + password: 'some_pass', + active: true, + roles: roles, + ) + + calendar1 = Calendar.first + ticket_state1 = Ticket::State.find_by(name: 'new') + ticket_state2 = Ticket::State.find_by(name: 'open') + sla = Sla.create_or_update( + name: 'my asset test', + calendar_id: calendar1.id, + condition: { + 'ticket.state_id' => { + operator: 'is', + value: [ ticket_state1.id, ticket_state2.id ], + }, + 'ticket.owner_id' => { + operator: 'is', + pre_condition: 'specific', + value: user1.id, + value_completion: 'John Smith ' + }, + }, + ) + assets = sla.assets({}) + assert(assets[:User][user1.id], 'check assets') + assert_not(assets[:User][user2.id], 'check assets') + assert(assets[:TicketState][ticket_state1.id], 'check assets') + assert(assets[:TicketState][ticket_state2.id], 'check assets') + assert(assets[:Calendar][calendar1.id], 'check assets') + + end + + test 'job' do + + UserInfo.current_user_id = 1 + roles = Role.where(name: %w(Customer)) + + user1 = User.create_or_update( + login: 'assets_job1@example.org', + firstname: 'assets_job1', + lastname: 'assets_job1', + email: 'assets_job1@example.org', + password: 'some_pass', + active: true, + roles: roles, + ) + user2 = User.create_or_update( + login: 'assets_job2@example.org', + firstname: 'assets_job2', + lastname: 'assets_job2', + email: 'assets_job2@example.org', + password: 'some_pass', + active: true, + roles: roles, + ) + user3 = User.create_or_update( + login: 'assets_job3@example.org', + firstname: 'assets_job3', + lastname: 'assets_job3', + email: 'assets_job3@example.org', + password: 'some_pass', + active: true, + roles: roles, + ) + + ticket_state1 = Ticket::State.find_by(name: 'new') + ticket_state2 = Ticket::State.find_by(name: 'open') + ticket_priority2 = Ticket::Priority.find_by(name: '2 normal') + job = Job.create_or_update( + name: 'my job', + timeplan: { + mon: true, + }, + condition: { + 'ticket.state_id' => { + operator: 'is', + value: [ ticket_state1.id, ticket_state2.id ], + }, + 'ticket.owner_id' => { + operator: 'is', + pre_condition: 'specific', + value: user1.id, + value_completion: 'John Smith ' + }, + }, + perform: { + 'ticket.priority_id' => { + value: ticket_priority2.id, + }, + 'ticket.owner_id' => { + pre_condition: 'specific', + value: user2.id, + value_completion: 'metest123@znuny.com ' + }, + }, + disable_notification: true, + ) + assets = job.assets({}) + assert(assets[:User][user1.id], 'check assets') + assert(assets[:User][user2.id], 'check assets') + assert_not(assets[:User][user3.id], 'check assets') + assert(assets[:TicketState][ticket_state1.id], 'check assets') + assert(assets[:TicketState][ticket_state2.id], 'check assets') + assert(assets[:TicketPriority][ticket_priority2.id], 'check assets') + + end + end From d4ec678238fbc7ce310a1df6ef028a1924291469 Mon Sep 17 00:00:00 2001 From: Martin Edenhofer Date: Sun, 20 Mar 2016 21:04:58 +0100 Subject: [PATCH 02/11] Moved to assets for sla, overview and jobs. --- .../_application_controller.coffee | 2 +- .../_application_controller_generic.coffee | 5 +- .../app/controllers/calendar.coffee | 27 ++--- .../app/controllers/navigation.coffee | 26 ++--- .../javascripts/app/controllers/sla.coffee | 26 ++--- .../widget/online_notification.coffee | 4 +- .../app/models/_application_model.coffee | 103 ++++++++++-------- app/controllers/slas_controller.rb | 62 ++++------- 8 files changed, 121 insertions(+), 134 deletions(-) diff --git a/app/assets/javascripts/app/controllers/_application_controller.coffee b/app/assets/javascripts/app/controllers/_application_controller.coffee index 1f32af140..07645cbc2 100644 --- a/app/assets/javascripts/app/controllers/_application_controller.coffee +++ b/app/assets/javascripts/app/controllers/_application_controller.coffee @@ -542,7 +542,7 @@ class App.Controller extends Spine.Controller el.html App.view('generic/page_loading')() else @html App.view('generic/page_loading')() - @initLoadingDoneDelay = @delay(later, 2800) + @initLoadingDoneDelay = @delay(later, 1800) stopLoading: => return if !@initLoadingDoneDelay diff --git a/app/assets/javascripts/app/controllers/_application_controller_generic.coffee b/app/assets/javascripts/app/controllers/_application_controller_generic.coffee index 86f22598e..e48dbcb69 100644 --- a/app/assets/javascripts/app/controllers/_application_controller_generic.coffee +++ b/app/assets/javascripts/app/controllers/_application_controller_generic.coffee @@ -124,7 +124,10 @@ class App.ControllerGenericIndex extends App.Controller # fetch all if !@disableInitFetch - App[ @genericObject ].fetch() + App[ @genericObject ].fetchFull( + -> + clear: true + ) release: => if @subscribeId diff --git a/app/assets/javascripts/app/controllers/calendar.coffee b/app/assets/javascripts/app/controllers/calendar.coffee index ba1e94fec..46e979380 100644 --- a/app/assets/javascripts/app/controllers/calendar.coffee +++ b/app/assets/javascripts/app/controllers/calendar.coffee @@ -12,26 +12,17 @@ class Index extends App.ControllerContent # check authentication return if !@authenticate(false, 'Admin') - @interval(@load, 60000) - #@load() + @subscribeId = App.Calendar.subscribe(@render) - load: => + callback = (data) => + App.Config.set('ical_feeds', data.ical_feeds) + App.Config.set('timezones', data.timezones) + @stopLoading() + @render() @startLoading() - @ajax( - id: 'calendar_index' - type: 'GET' - url: @apiPath + '/calendars' - processData: true - success: (data, status, xhr) => - - App.Config.set('ical_feeds', data.ical_feeds) - App.Config.set('timezones', data.timezones) - - # load assets - App.Collection.loadAssets(data.assets) - - @stopLoading() - @render(data) + App.Calendar.fetchFull( + callback + clear: true ) render: => diff --git a/app/assets/javascripts/app/controllers/navigation.coffee b/app/assets/javascripts/app/controllers/navigation.coffee index e2c0e0cbd..6aaaa95b3 100644 --- a/app/assets/javascripts/app/controllers/navigation.coffee +++ b/app/assets/javascripts/app/controllers/navigation.coffee @@ -327,7 +327,7 @@ class App.Navigation extends App.ControllerWidgetPermanent level1 = [] dropdown = {} - roles = App.Session.get( 'roles' ) + roles = App.Session.get('roles') for item in navbar if typeof item.callback is 'function' @@ -339,11 +339,11 @@ class App.Navigation extends App.ControllerWidgetPermanent if !item.role match = 1 if !roles && item.role - match = _.include( item.role, 'Anybody' ) + match = _.include(item.role, 'Anybody') if roles for role in roles if !match - match = _.include( item.role, role.name ) + match = _.include(item.role, role.name) if match level1.push item @@ -359,11 +359,11 @@ class App.Navigation extends App.ControllerWidgetPermanent if !itemSub.role match = 1 if !roles - match = _.include( itemSub.role, 'Anybody' ) + match = _.include(itemSub.role, 'Anybody') if roles for role in roles if !match - match = _.include( itemSub.role, role.name ) + match = _.include(itemSub.role, role.name) if match dropdown[ item.parent ].push itemSub @@ -371,13 +371,13 @@ class App.Navigation extends App.ControllerWidgetPermanent # find parent for itemLevel1 in level1 if itemLevel1.target is item.parent - sub = @getOrder( dropdown[ item.parent ] ) + sub = @getOrder(dropdown[ item.parent ]) itemLevel1.child = sub # clean up, only show navbar items with existing childrens clean_list = [] for item in level1 - if !item.child || item.child && !_.isEmpty( item.child ) + if !item.child || item.child && !_.isEmpty(item.child) clean_list.push item nav = @getOrder(clean_list) return nav @@ -435,11 +435,11 @@ class App.Navigation extends App.ControllerWidgetPermanent delete NavBarRight[key] if !@Session.get() - @Config.set( 'NavBarRight', NavBarRight ) + @Config.set('NavBarRight', NavBarRight) return # add new views - items = App.RecentView.search(sortBy: 'created_at', order: 'DESC' ) + items = App.RecentView.search(sortBy: 'created_at', order: 'DESC') items = @prepareForObjectList(items) prio = 80 for item in items @@ -460,12 +460,12 @@ class App.Navigation extends App.ControllerWidgetPermanent type: 'recentViewed' } - @Config.set( 'NavBarRight', NavBarRight ) + @Config.set('NavBarRight', NavBarRight) fetchRecentView: => - load = (items) => - App.RecentView.refresh( items, { clear: true } ) + load = (data) => + App.RecentView.refresh(data.stream, clear: true) @renderPersonal() App.RecentView.fetchFull(load) -App.Config.set( 'navigation', App.Navigation, 'Navigations' ) +App.Config.set('navigation', App.Navigation, 'Navigations') diff --git a/app/assets/javascripts/app/controllers/sla.coffee b/app/assets/javascripts/app/controllers/sla.coffee index 75f815912..8c2e105e0 100644 --- a/app/assets/javascripts/app/controllers/sla.coffee +++ b/app/assets/javascripts/app/controllers/sla.coffee @@ -11,20 +11,16 @@ class Index extends App.ControllerContent # check authentication return if !@authenticate(false, 'Admin') - @interval(@load, 60000) - #@load() + @subscribeCalendarId = App.Calendar.subscribe(@render) + @subscribeSlaId = App.Sla.subscribe(@render) - load: => + callback = => + @stopLoading() + @render() @startLoading() - @ajax( - id: 'sla_index' - type: 'GET' - url: @apiPath + '/slas' - processData: true - success: (data, status, xhr) => - App.Collection.loadAssets(data.assets) - @stopLoading() - @render(data) + App.Sla.fetchFull( + callback + clear: true ) render: => @@ -56,8 +52,10 @@ class Index extends App.ControllerContent ) release: => - if @subscribeId - App.Calendar.unsubscribe(@subscribeId) + if @subscribeCalendarId + App.Calendar.unsubscribe(@subscribeCalendarId) + if @subscribeSlaId + App.Sla.unsubscribe(@subscribeSlaId) new: (e) -> e.preventDefault() diff --git a/app/assets/javascripts/app/controllers/widget/online_notification.coffee b/app/assets/javascripts/app/controllers/widget/online_notification.coffee index 147d13bf2..a09bbcdc2 100644 --- a/app/assets/javascripts/app/controllers/widget/online_notification.coffee +++ b/app/assets/javascripts/app/controllers/widget/online_notification.coffee @@ -153,9 +153,9 @@ class App.OnlineNotificationWidget extends App.Controller @toggle.popover('hide') fetch: => - load = (items) => + load = (data) => @fetchedData = true - App.OnlineNotification.refresh(items, { clear: true }) + App.OnlineNotification.refresh(data.stream, clear: true) @updateContent() App.OnlineNotification.fetchFull(load) diff --git a/app/assets/javascripts/app/models/_application_model.coffee b/app/assets/javascripts/app/models/_application_model.coffee index a16907879..c91dc5c86 100644 --- a/app/assets/javascripts/app/models/_application_model.coffee +++ b/app/assets/javascripts/app/models/_application_model.coffee @@ -63,15 +63,15 @@ class App.Model extends Spine.Model iconActivity: (user) -> '' - @validate: ( data = {} ) -> + @validate: (data = {}) -> # based on model attrbutes if App[ data['model'] ] && App[ data['model'] ].attributesGet - attributes = App[ data['model'] ].attributesGet( data['screen'] ) + attributes = App[ data['model'] ].attributesGet(data['screen']) # based on custom attributes else if data['model'].configure_attributes - attributes = App.Model.attributesGet( data['screen'], data['model'].configure_attributes ) + attributes = App.Model.attributesGet(data['screen'], data['model'].configure_attributes) # check required_if attributes for attributeName, attribute of attributes @@ -186,9 +186,9 @@ class App.Model extends Spine.Model @attributesGet: (screen = undefined, attributes = false) -> if !attributes - attributes = clone( App[ @.className ].configure_attributes, true ) + attributes = clone(App[ @.className ].configure_attributes, true) else - attributes = clone( attributes, true ) + attributes = clone(attributes, true) # in case if no configure_attributes exist return {} if !attributes @@ -198,13 +198,13 @@ class App.Model extends Spine.Model if screen for attribute in attributes if attribute.screen - if attribute && attribute.screen && attribute.screen[ screen ] && !_.isEmpty( attribute.screen[ screen ] ) + if attribute && attribute.screen && attribute.screen[ screen ] && !_.isEmpty(attribute.screen[ screen ]) for item, value of attribute.screen[ screen ] attribute[item] = value attributesNew[ attribute.name ] = attribute # if no screen is given or no attribute has this screen - use default attributes - if !screen || _.isEmpty( attributesNew ) + if !screen || _.isEmpty(attributesNew) for attribute in attributes attributesNew[ attribute.name ] = attribute @@ -238,11 +238,11 @@ class App.Model extends Spine.Model subscribeId = App[ @className ].subscribe_item(id, callback) # execute if object already exists - if !force && App[ @className ].exists( id ) - data = App[ @className ].find( id ) - data = @_fillUp( data ) + if !force && App[ @className ].exists(id) + data = App[ @className ].find(id) + data = @_fillUp(data) if callback - callback( data, 'full' ) + callback(data, 'full') return subscribeId # store callback and requested id @@ -270,16 +270,16 @@ class App.Model extends Spine.Model # full / load assets if data.assets - App.Collection.loadAssets( data.assets ) + App.Collection.loadAssets(data.assets) # find / load object else - App[ @className ].refresh( data ) + App[ @className ].refresh(data) # execute callbacks if @FULL_CALLBACK[ data.id ] for key, callback of @FULL_CALLBACK[ data.id ] - callback( @_fillUp( App[ @className ].find( data.id ) ) ) + callback( @_fillUp( App[ @className ].find(data.id) ) ) delete @FULL_CALLBACK[ data.id ][ key ] if _.isEmpty @FULL_CALLBACK[ data.id ] delete @FULL_CALLBACK[ data.id ] @@ -297,11 +297,13 @@ class App.Model extends Spine.Model params = initFetch: true # fetch initial collection - @subscribeId = App.Model.subscribe( methodWhichIsCalledAtLocalOrServerSiteChange ) + @subscribeId = App.Model.subscribe(methodWhichIsCalledAtLocalOrServerSiteChange) ### @subscribe: (callback, param = {}) -> + + # global bind to changes if !@SUBSCRIPTION_COLLECTION @SUBSCRIPTION_COLLECTION = {} @@ -320,7 +322,10 @@ class App.Model extends Spine.Model events => App.Log.debug('Model', "server notify collection change #{@className}") - @fetch( {}, { clear: true } ) + @fetchFull( + -> + clear: true + ) 'Collection::Subscribe::' + @className ) @@ -334,9 +339,12 @@ class App.Model extends Spine.Model @one 'refresh', (collection) => @initFetchActive = true callback(collection) - @fetch( {}, { clear: true } ) + @fetchFull( + -> + clear: true + ) else - callback( @all() ) + callback(@all()) # return key key @@ -347,7 +355,7 @@ class App.Model extends Spine.Model console.log("Item has changed", changedItem, localOrServer) model = App.Model.find(1) - @subscribeId = model.subscribe( methodWhichIsCalledAtLocalOrServerSiteChange ) + @subscribeId = model.subscribe(methodWhichIsCalledAtLocalOrServerSiteChange) ### @@ -373,7 +381,7 @@ class App.Model extends Spine.Model App.Log.debug('Model', "local change #{@className}", items) for item in items for key, callback of App[ @className ].SUBSCRIPTION_ITEM[ item.id ] - item = App[ @className ]._fillUp( item ) + item = App[ @className ]._fillUp(item) callback(item, 'change') ) @bind( @@ -386,7 +394,7 @@ class App.Model extends Spine.Model App.Log.debug('Model', "local remove #{@className}", items) for item in items for key, callback of App[ @className ].SUBSCRIPTION_ITEM[ item.id ] - item = App[ @className ]._fillUp( item ) + item = App[ @className ]._fillUp(item) callback(item, 'remove') ) @@ -405,7 +413,7 @@ class App.Model extends Spine.Model # only trigger callbacks if object has changed if !@changeTable[key] || @changeTable[key] isnt item.updated_at @changeTable[key] = item.updated_at - item = App[ @className ]._fillUp( item ) + item = App[ @className ]._fillUp(item) callback(item, 'refresh') ) @@ -416,14 +424,13 @@ class App.Model extends Spine.Model (item) => if @SUBSCRIPTION_ITEM && @SUBSCRIPTION_ITEM[ item.id ] genericObject = undefined - if App[ @className ].exists( item.id ) - genericObject = App[ @className ].find( item.id ) + if App[ @className ].exists(item.id) + genericObject = App[ @className ].find(item.id) App.Log.debug('Model', "server change on #{@className}.find(#{item.id}) #{item.updated_at}") callback = => if !genericObject || new Date(item.updated_at) >= new Date(genericObject.updated_at) App.Log.debug('Model', "request #{@className}.find(#{item.id}) from server") - @full( item.id, false, true ) - + @full(item.id, false, true) App.Delay.set(callback, 500, item.id, "full-#{@className}") 'Item::Subscribe::' + @className @@ -460,10 +467,16 @@ class App.Model extends Spine.Model fetch full collection (with assets) - App.Model.fetchFull( @callback ) + App.Model.fetchFull(@callback) + + App.Model.fetchFull( + @callback + clear: true + ) + ### - @fetchFull: (callback) -> + @fetchFull: (callback, params = {}) -> url = "#{@url}/?full=true" App.Log.debug('Model', "fetchFull collection #{@className}", url) App.Ajax.request( @@ -474,16 +487,20 @@ class App.Model extends Spine.Model App.Log.debug('Model', "got fetchFull collection #{@className}", data) + # clear collection + if params.clear + App[@className].deleteAll() + # full / load assets if data.assets - App.Collection.loadAssets( data.assets ) + App.Collection.loadAssets(data.assets) # find / load object else - App[ @className ].refresh( data ) + App[@className].refresh(data) - # execute callbacks - callback(data.stream) + if callback + callback(data) error: (xhr, statusText, error) -> App.Log.error('Model', statusText, error, url) @@ -494,7 +511,7 @@ class App.Model extends Spine.Model for id, keys of @SUBSCRIPTION_ITEM return false if !_.isEmpty(keys) - if @SUBSCRIPTION_COLLECTION && !_.isEmpty( @SUBSCRIPTION_COLLECTION ) + if @SUBSCRIPTION_COLLECTION && !_.isEmpty(@SUBSCRIPTION_COLLECTION) return false return true @@ -502,8 +519,8 @@ class App.Model extends Spine.Model @_fillUp: (data, classNames = []) -> # fill up via relations - return data if !App[ @className ].configure_attributes - for attribute in App[ @className ].configure_attributes + return data if !App[@className].configure_attributes + for attribute in App[@className].configure_attributes # lookup relations if attribute.relation @@ -512,19 +529,19 @@ class App.Model extends Spine.Model if !_.contains(classNames, @className) # only if relation model exists - if App[ attribute.relation ] - withoutId = attribute.name.substr( 0, attribute.name.length - 3 ) - if attribute.name.substr( attribute.name.length - 3, attribute.name.length ) is '_id' + if App[attribute.relation] + withoutId = attribute.name.substr(0, attribute.name.length - 3) + if attribute.name.substr(attribute.name.length - 3, attribute.name.length) is '_id' if data[attribute.name] # only if relation record exists in collection - if App[ attribute.relation ].exists( data[attribute.name] ) - item = App[ attribute.relation ].find( data[attribute.name] ) - item = App[ attribute.relation ]._fillUp(item, classNames.concat(@className)) - data[ withoutId ] = item + if App[ attribute.relation ].exists(data[attribute.name]) + item = App[attribute.relation].find(data[attribute.name]) + item = App[attribute.relation]._fillUp(item, classNames.concat(@className)) + data[withoutId] = item else if !attribute.do_not_log - console.log("ERROR, cant find #{ attribute.name } App.#{ attribute.relation }.find(#{ data[attribute.name] }) for '#{ data.constructor.className }' #{ data.displayName() }") + console.log("ERROR, cant find #{attribute.name} App.#{attribute.relation}.find(#{data[attribute.name]}) for '#{data.constructor.className}' #{data.displayName()}") data ### diff --git a/app/controllers/slas_controller.rb b/app/controllers/slas_controller.rb index d94c6c447..45f213e7d 100644 --- a/app/controllers/slas_controller.rb +++ b/app/controllers/slas_controller.rb @@ -49,51 +49,29 @@ curl http://localhost/api/v1/slas.json -v -u #{login}:#{password} def index return if deny_if_not_role(Z_ROLENAME_ADMIN) - assets = {} + if params[:full] - # calendars - calendar_ids = [] - Calendar.all.order(:name, :created_at).each {|calendar| - calendar_ids.push calendar.id - assets = calendar.assets(assets) - } - - # slas - sla_ids = [] - models = Models.all - Sla.all.order(:name, :created_at).each {|sla| - sla_ids.push sla.id - assets = sla.assets(assets) - - # get assets of condition - sla.condition.each {|item, content| - attribute = item.split(/\./) - next if !attribute[1] - attribute_class = attribute[0].to_classname.constantize - reflection = attribute[1].sub(/_id$/, '') - reflection = reflection.to_sym - next if !models[attribute_class] - next if !models[attribute_class][:reflections] - next if !models[attribute_class][:reflections][reflection] - next if !models[attribute_class][:reflections][reflection].klass - attribute_ref_class = models[attribute_class][:reflections][reflection].klass - if content['value'].class == Array - content['value'].each {|item_id| - attribute_object = attribute_ref_class.find_by(id: item_id) - assets = attribute_object.assets(assets) - } - else - attribute_object = attribute_ref_class.find_by(id: content['value']) - assets = attribute_object.assets(assets) - end + # calendars + assets = {} + calendar_ids = [] + Calendar.all.each {|calendar| + assets = calendar.assets(assets) } - } - render json: { - calendar_ids: calendar_ids, - sla_ids: sla_ids, - assets: assets, - }, status: :ok + # slas + sla_ids = [] + Sla.all.each {|item| + sla_ids.push item.id + assets = item.assets(assets) + } + render json: { + record_ids: sla_ids, + assets: assets, + }, status: :ok + return + end + + model_index_render(Sla, params) end =begin From 6953ae52afda1790875a2cf9590d9c91762787e4 Mon Sep 17 00:00:00 2001 From: Martin Edenhofer Date: Sun, 20 Mar 2016 21:41:19 +0100 Subject: [PATCH 03/11] Fixed not removable items. Automatically shown online notification popover on new messages. Defined missing @header element. --- .../widget/online_notification.coffee | 27 ++++++++++++------- .../views/widget/online_notification.jst.eco | 2 +- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/app/assets/javascripts/app/controllers/widget/online_notification.coffee b/app/assets/javascripts/app/controllers/widget/online_notification.coffee index 63ecb774a..500c95de3 100644 --- a/app/assets/javascripts/app/controllers/widget/online_notification.coffee +++ b/app/assets/javascripts/app/controllers/widget/online_notification.coffee @@ -17,6 +17,7 @@ class App.OnlineNotificationWidget extends App.Controller '.js-mark': 'mark' '.js-item': 'item' '.js-content': 'content' + '.js-header': 'header' constructor: -> super @@ -50,7 +51,7 @@ class App.OnlineNotificationWidget extends App.Controller return if @access() - @subscribeId = App.OnlineNotification.subscribe(@show) + @subscribeId = App.OnlineNotification.subscribe(@updateContent) @bind('ui:reshow', => @show() @@ -59,6 +60,8 @@ class App.OnlineNotificationWidget extends App.Controller $(window).on 'click.notifications', @hide + @updateContent() + release: -> $(window).off 'click.notifications' App.OnlineNotification.unsubscribe(@subscribeId) @@ -131,6 +134,7 @@ class App.OnlineNotificationWidget extends App.Controller ) updateHeight: -> + # set height of notification popover heightApp = $('#app').height() heightPopoverSpacer = 22 @@ -150,7 +154,7 @@ class App.OnlineNotificationWidget extends App.Controller load = (data) => @fetchedData = true App.OnlineNotification.refresh(data.stream, clear: true) - @show() + @updateContent() App.OnlineNotification.fetchFull(load) toggle: => @@ -159,8 +163,7 @@ class App.OnlineNotificationWidget extends App.Controller else @show() - show: => - @shown = true + updateContent: => if !@Session.get() @content.html('') return @@ -197,19 +200,25 @@ class App.OnlineNotificationWidget extends App.Controller count: @count ) + return if !@shown + @show() + + show: => + @shown = true @el.show() + @updateHeight() hide: => @shown = false @el.hide() - onItemClick: (event) -> - @locationVerify(event) + onItemClick: (e) -> + @locationVerify(e) @hide() - removeItem: (event) -> - event.preventDefault() - event.stopPropagation() + removeItem: (e) => + e.preventDefault() + e.stopPropagation() row = $(e.target).closest('.js-item') id = row.data('id') App.OnlineNotification.destroy(id) diff --git a/app/assets/javascripts/app/views/widget/online_notification.jst.eco b/app/assets/javascripts/app/views/widget/online_notification.jst.eco index 540c5950f..da34fb6d3 100644 --- a/app/assets/javascripts/app/views/widget/online_notification.jst.eco +++ b/app/assets/javascripts/app/views/widget/online_notification.jst.eco @@ -1,6 +1,6 @@
-
<%- @T('Notifications') %><%- @count %>
+
<%- @T('Notifications') %> <%- @count %>
<%- @T( 'Mark all as read' ) %>
From abb049ececc899f72a0015c5101718598773f220 Mon Sep 17 00:00:00 2001 From: Martin Edenhofer Date: Mon, 21 Mar 2016 08:49:36 +0100 Subject: [PATCH 04/11] Reduced amount of refresh (-> rerender) events. --- .../app/controllers/_channel/form.coffee | 2 +- .../app/controllers/_settings/area.coffee | 15 +-------------- .../app/lib/app_post/collection.coffee | 13 ++++++++++--- .../app/models/_application_model.coffee | 5 ++++- app/controllers/settings_controller.rb | 8 -------- 5 files changed, 16 insertions(+), 27 deletions(-) diff --git a/app/assets/javascripts/app/controllers/_channel/form.coffee b/app/assets/javascripts/app/controllers/_channel/form.coffee index bc4517345..73ffd4274 100644 --- a/app/assets/javascripts/app/controllers/_channel/form.coffee +++ b/app/assets/javascripts/app/controllers/_channel/form.coffee @@ -12,7 +12,7 @@ class App.ChannelForm extends App.Controller constructor: -> super @title 'Form' - @subscribeId = App.Setting.subscribe(@render, initFetch: true) + @subscribeId = App.Setting.subscribe(@render, initFetch: true, clear: false) render: => App.Setting.unsubscribe(@subscribeId) diff --git a/app/assets/javascripts/app/controllers/_settings/area.coffee b/app/assets/javascripts/app/controllers/_settings/area.coffee index d8bf7c90c..36a718730 100644 --- a/app/assets/javascripts/app/controllers/_settings/area.coffee +++ b/app/assets/javascripts/app/controllers/_settings/area.coffee @@ -5,20 +5,7 @@ class App.SettingsArea extends App.Controller # check authentication return if !@authenticate() - @load() - - load: -> - @startLoading() - @ajax( - id: "setting_area_#{@area}" - type: 'GET' - url: "#{@apiPath}/settings/area/#{@area}" - processData: true - success: (data, status, xhr) => - @stopLoading() - App.Collection.load( sessionStorage: false, type: 'Setting', data: data ) - @render() - ) + @subscribeId = App.Setting.subscribe(@render, initFetch: true, clear: false) render: => diff --git a/app/assets/javascripts/app/lib/app_post/collection.coffee b/app/assets/javascripts/app/lib/app_post/collection.coffee index bd9867e73..7c7fb635b 100644 --- a/app/assets/javascripts/app/lib/app_post/collection.coffee +++ b/app/assets/javascripts/app/lib/app_post/collection.coffee @@ -85,16 +85,23 @@ class _collectionSingleton extends Spine.Module return # load data from object + listToRefresh = [] for key, object of params.data if !params.refresh && appObject + @log 'debug', 'refrest try', params.type, key # check if new object is newer, just load newer objects if object.updated_at && appObject.exists(key) exists = appObject.find(key) if exists.updated_at if exists.updated_at < object.updated_at - appObject.refresh(object) + listToRefresh.push object + @log 'debug', 'refrest newser', params.type, key else - appObject.refresh(object) + listToRefresh.push object + @log 'debug', 'refrest try no updated_at', params.type, key else - appObject.refresh(object) + listToRefresh.push object + @log 'debug', 'refrest new', params.type, key + return if _.isEmpty(listToRefresh) + appObject.refresh(listToRefresh) diff --git a/app/assets/javascripts/app/models/_application_model.coffee b/app/assets/javascripts/app/models/_application_model.coffee index c91dc5c86..1dda13a5e 100644 --- a/app/assets/javascripts/app/models/_application_model.coffee +++ b/app/assets/javascripts/app/models/_application_model.coffee @@ -335,13 +335,16 @@ class App.Model extends Spine.Model # fetch init collection if param.initFetch is true + clear = true + if param.clear is true || param.clear is false + clear = param.clear if !@initFetchActive @one 'refresh', (collection) => @initFetchActive = true callback(collection) @fetchFull( -> - clear: true + clear: clear ) else callback(@all()) diff --git a/app/controllers/settings_controller.rb b/app/controllers/settings_controller.rb index 5d0aa802f..a98b1faa0 100644 --- a/app/controllers/settings_controller.rb +++ b/app/controllers/settings_controller.rb @@ -6,14 +6,6 @@ class SettingsController < ApplicationController # GET /settings def index return if deny_if_not_role(Z_ROLENAME_ADMIN) - - # only serve requested items - if params[:area] - model_index_render_result( Setting.where(area: params[:area]) ) - return - end - - # serve all items model_index_render(Setting, params) end From 5e11344fb96798694c1e45cef2d0331516d05dfb Mon Sep 17 00:00:00 2001 From: Martin Edenhofer Date: Mon, 21 Mar 2016 13:51:55 +0100 Subject: [PATCH 05/11] Improved helper (use internal select and set). --- test/browser_test_helper.rb | 193 +++++++++++++++++++++++------------- 1 file changed, 123 insertions(+), 70 deletions(-) diff --git a/test/browser_test_helper.rb b/test/browser_test_helper.rb index c9386f327..b4ae1e54a 100644 --- a/test/browser_test_helper.rb +++ b/test/browser_test_helper.rb @@ -504,9 +504,10 @@ class TestCase < Test::Unit::TestCase =begin select( - browser: browser1, - css: '.some_class', - value: 'Some Value', + browser: browser1, + css: '.some_class', + value: 'Some Value', + deselect_all: false, # default false ) =end @@ -535,12 +536,18 @@ class TestCase < Test::Unit::TestCase begin element = instance.find_elements(css: params[:css])[0] dropdown = Selenium::WebDriver::Support::Select.new(element) + if params[:deselect_all] + dropdown.deselect_all + end dropdown.select_by(:text, params[:value]) puts "select - #{params.inspect}" rescue # just try again element = instance.find_elements(css: params[:css])[0] dropdown = Selenium::WebDriver::Support::Select.new(element) + if params[:deselect_all] + dropdown.deselect_all + end dropdown.select_by(:text, params[:value]) puts "select2 - #{params.inspect}" end @@ -1298,33 +1305,48 @@ wait untill text in selector disabppears sleep 2 if data[:name] - element = instance.find_elements(css: '.modal input[name=name]')[0] - element.clear - element.send_keys(data[:name]) + set( + browser: instance, + css: '.modal input[name=name]', + value: data[:name], + mute_log: true, + ) end if data[:role] - element = instance.find_elements(css: '.modal select[name="role_id"]')[0] - dropdown = Selenium::WebDriver::Support::Select.new(element) - dropdown.select_by(:text, data[:role]) + select( + browser: instance, + css: '.modal select[name="role_id"]', + value: data[:role], + mute_log: true, + ) end if data[:selector] data[:selector].each {|key, value| - element = instance.find_elements(css: '.modal .ticket_selector .js-attributeSelector select')[0] - dropdown = Selenium::WebDriver::Support::Select.new(element) - dropdown.select_by(:text, key) + select( + browser: instance, + css: '.modal .ticket_selector .js-attributeSelector select', + value: key, + mute_log: true, + ) sleep 0.5 - element = instance.find_elements(css: '.modal .ticket_selector .js-value select')[0] - dropdown = Selenium::WebDriver::Support::Select.new(element) - dropdown.deselect_all - dropdown.select_by(:text, value) + select( + browser: instance, + css: '.modal .ticket_selector .js-value select', + value: value, + deselect_all: true, + mute_log: true, + ) } end if data['order::direction'] - element = instance.find_elements(css: '.modal select[name="order::direction"]')[0] - dropdown = Selenium::WebDriver::Support::Select.new(element) - dropdown.select_by(:text, data['order::direction']) + select( + browser: instance, + css: '.modal select[name="order::direction"]', + value: data['order::direction'], + mute_log: true, + ) end instance.find_elements(css: '.modal button.js-submit')[0].click @@ -1379,33 +1401,48 @@ wait untill text in selector disabppears sleep 2 if data[:name] - element = instance.find_elements(css: '.modal input[name=name]')[0] - element.clear - element.send_keys(data[:name]) + set( + browser: instance, + css: '.modal input[name=name]', + value: data[:name], + mute_log: true, + ) end if data[:role] - element = instance.find_elements(css: '.modal select[name="role_id"]')[0] - dropdown = Selenium::WebDriver::Support::Select.new(element) - dropdown.select_by(:text, data[:role]) + select( + browser: instance, + css: '.modal select[name="role_id"]', + value: data[:role], + mute_log: true, + ) end if data[:selector] data[:selector].each {|key, value| - element = instance.find_elements(css: '.modal .ticket_selector .js-attributeSelector select')[0] - dropdown = Selenium::WebDriver::Support::Select.new(element) - dropdown.select_by(:text, key) - instance.execute_script("$('#content .modal .ticket_selector .js-attributeSelector select').first().trigger('change')") - element = instance.find_elements(css: '.modal .ticket_selector .js-value select')[0] - dropdown = Selenium::WebDriver::Support::Select.new(element) - dropdown.deselect_all - dropdown.select_by(:text, value) + select( + browser: instance, + css: '.modal .ticket_selector .js-attributeSelector select', + value: key, + mute_log: true, + ) + sleep 0.5 + select( + browser: instance, + css: '.modal .ticket_selector .js-value select', + value: value, + deselect_all: true, + mute_log: true, + ) } end if data['order::direction'] - element = instance.find_elements(css: '.modal select[name="order::direction"]')[0] - dropdown = Selenium::WebDriver::Support::Select.new(element) - dropdown.select_by(:text, data['order::direction']) + select( + browser: instance, + css: '.modal select[name="order::direction"]', + value: data['order::direction'], + mute_log: true, + ) end instance.find_elements(css: '.modal button.js-submit')[0].click @@ -1481,31 +1518,40 @@ wait untill text in selector disabppears # check count of agents, should be only 1 / - selection on init screen count = instance.find_elements(css: '.active .newTicket select[name="owner_id"] option').count assert_equal(1, count, 'check if owner selection is empty per default') - - element = instance.find_elements(css: '.active .newTicket select[name="group_id"]')[0] - dropdown = Selenium::WebDriver::Support::Select.new(element) - dropdown.select_by(:text, data[:group]) + select( + browser: instance, + css: '.active .newTicket select[name="group_id"]', + value: data[:group], + mute_log: true, + ) sleep 0.2 end end if data[:priority] - element = instance.find_elements(css: '.active .newTicket select[name="priority_id"]')[0] - dropdown = Selenium::WebDriver::Support::Select.new(element) - dropdown.select_by(:text, data[:priority]) - sleep 0.2 + select( + browser: instance, + css: '.active .newTicket select[name="priority_id"]', + value: data[:priority], + mute_log: true, + ) end if data[:title] - element = instance.find_elements(css: '.active .newTicket input[name="title"]')[0] - element.clear - element.send_keys(data[:title]) - sleep 0.2 + set( + browser: instance, + css: '.active .newTicket input[name="title"]', + value: data[:title], + clear: true, + mute_log: true, + ) end if data[:body] - #instance.execute_script('$(".active .newTicket div[data-name=body]").focus()') - sleep 0.5 - element = instance.find_elements(css: '.active .newTicket div[data-name=body]')[0] - element.clear - element.send_keys(data[:body]) + set( + browser: instance, + css: '.active .newTicket div[data-name=body]', + value: data[:body], + clear: true, + mute_log: true, + ) # it's not working stable via selenium, use js value = instance.find_elements(css: '.content .newTicket div[data-name=body]')[0].text @@ -1675,11 +1721,12 @@ wait untill text in selector disabppears end if data[:body] - #instance.execute_script('$(".content.active div[data-name=body]").focus()') - sleep 0.5 - element = instance.find_elements(css: '.content.active div[data-name=body]')[0] - element.clear - element.send_keys(data[:body]) + set( + browser: instance, + css: '.content.active div[data-name=body]', + value: data[:body], + mute_log: true, + ) # it's not working stable via selenium, use js value = instance.find_elements(css: '.content.active div[data-name=body]')[0].text @@ -1702,26 +1749,32 @@ wait untill text in selector disabppears assert_equal(3, count, 'check if owner selection is - selection + master + agent per default') else - - element = instance.find_elements(css: '.active .sidebar select[name="group_id"]')[0] - dropdown = Selenium::WebDriver::Support::Select.new(element) - dropdown.select_by(:text, data[:group]) + select( + browser: instance, + css: '.active .sidebar select[name="group_id"]', + value: data[:group], + mute_log: true, + ) sleep 0.2 end end if data[:priority] - element = instance.find_elements(css: '.active .sidebar select[name="priority_id"]')[0] - dropdown = Selenium::WebDriver::Support::Select.new(element) - dropdown.select_by(:text, data[:priority]) - sleep 0.2 + select( + browser: instance, + css: '.active .sidebar select[name="priority_id"]', + value: data[:priority], + mute_log: true, + ) end if data[:state] - element = instance.find_elements(css: '.active .sidebar select[name="state_id"]')[0] - dropdown = Selenium::WebDriver::Support::Select.new(element) - dropdown.select_by(:text, data[:state]) - sleep 0.2 + select( + browser: instance, + css: '.active .sidebar select[name="state_id"]', + value: data[:state], + mute_log: true, + ) end if data[:state] || data[:group] || data[:body] From 21817abab61fed252c520a619faac71c5186202c Mon Sep 17 00:00:00 2001 From: Martin Edenhofer Date: Mon, 21 Mar 2016 14:39:18 +0100 Subject: [PATCH 06/11] Fixed ff click on js-textarea issue on ticket update. --- test/browser_test_helper.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/browser_test_helper.rb b/test/browser_test_helper.rb index b4ae1e54a..157774743 100644 --- a/test/browser_test_helper.rb +++ b/test/browser_test_helper.rb @@ -542,6 +542,8 @@ class TestCase < Test::Unit::TestCase dropdown.select_by(:text, params[:value]) puts "select - #{params.inspect}" rescue + sleep 0.5 + # just try again element = instance.find_elements(css: params[:css])[0] dropdown = Selenium::WebDriver::Support::Select.new(element) @@ -1725,6 +1727,7 @@ wait untill text in selector disabppears browser: instance, css: '.content.active div[data-name=body]', value: data[:body], + no_click: true, mute_log: true, ) From fd93057d4581b93ee70362d5f6858f031d29a86a Mon Sep 17 00:00:00 2001 From: Martin Edenhofer Date: Mon, 21 Mar 2016 16:11:49 +0100 Subject: [PATCH 07/11] Removed not longer needed @container, added js-notificationsContainer for browser testing. --- .../app/controllers/widget/online_notification.coffee | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/assets/javascripts/app/controllers/widget/online_notification.coffee b/app/assets/javascripts/app/controllers/widget/online_notification.coffee index 500c95de3..bcd83bda5 100644 --- a/app/assets/javascripts/app/controllers/widget/online_notification.coffee +++ b/app/assets/javascripts/app/controllers/widget/online_notification.coffee @@ -1,7 +1,7 @@ class App.OnlineNotificationWidget extends App.Controller alreadyShown: {} shown: false - className: 'popover popover--notifications right' + className: 'popover popover--notifications right js-notificationsContainer' attributes: role: 'tooltip' @@ -13,7 +13,6 @@ class App.OnlineNotificationWidget extends App.Controller 'keydown': 'listNavigate' elements: - '.js-notificationsContainer': 'container' '.js-mark': 'mark' '.js-item': 'item' '.js-content': 'content' @@ -144,9 +143,6 @@ class App.OnlineNotificationWidget extends App.Controller if (heightPopoverHeader + heightPopoverContent + heightPopoverSpacer) > heightApp heightPopoverContent = heightApp - heightPopoverHeader - heightPopoverSpacer - @container.addClass('is-overflowing') - else - @container.removeClass('is-overflowing') @content.css('height', heightPopoverContent) From cc12b9196107884ed776a1eb439e0a48d93b1951 Mon Sep 17 00:00:00 2001 From: Martin Edenhofer Date: Mon, 21 Mar 2016 16:15:06 +0100 Subject: [PATCH 08/11] Upgrade to latest gems. --- Gemfile.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index d51b0aba6..b81169417 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -43,7 +43,7 @@ GEM addressable (2.4.0) arel (6.0.3) ast (2.2.0) - autoprefixer-rails (6.3.3.1) + autoprefixer-rails (6.3.4) execjs biz (1.4.0) clavius (~> 1.0) @@ -76,7 +76,7 @@ GEM diffy (3.1.0) dnsruby (1.59.2) docile (1.1.5) - domain_name (0.5.20160309) + domain_name (0.5.20160310) unf (>= 0.0.5, < 1.0.0) eco (1.0.0) coffee-script @@ -115,7 +115,7 @@ GEM multi_json (~> 1.8) hashie (3.4.3) htmlentities (4.3.4) - http (1.0.2) + http (1.0.4) addressable (~> 2.3) http-cookie (~> 1.0) http-form_data (~> 1.0.1) @@ -199,7 +199,7 @@ GEM coderay (~> 1.1.0) method_source (~> 0.8.1) slop (~> 3.4) - puma (3.1.0) + puma (3.2.0) rack (1.6.4) rack-livereload (0.3.16) rack @@ -277,7 +277,7 @@ GEM activesupport (>= 4.0) sprockets (>= 3.0.0) sqlite3 (1.3.11) - test-unit (3.1.7) + test-unit (3.1.8) power_assert therubyracer (0.12.2) libv8 (~> 3.16.14.0) From 121bf3c2715cf0363766df6d8ee3d9a575fe56fc Mon Sep 17 00:00:00 2001 From: Martin Edenhofer Date: Mon, 21 Mar 2016 16:48:43 +0100 Subject: [PATCH 09/11] Fixed keyboard navigation and min. height with empty online notification. --- .../app/controllers/widget/online_notification.coffee | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/app/controllers/widget/online_notification.coffee b/app/assets/javascripts/app/controllers/widget/online_notification.coffee index bcd83bda5..b2b420661 100644 --- a/app/assets/javascripts/app/controllers/widget/online_notification.coffee +++ b/app/assets/javascripts/app/controllers/widget/online_notification.coffee @@ -10,7 +10,6 @@ class App.OnlineNotificationWidget extends App.Controller 'click .js-item': 'hide' 'click .js-remove': 'removeItem' 'click .js-locationVerify': 'onItemClick' - 'keydown': 'listNavigate' elements: '.js-mark': 'mark' @@ -58,11 +57,13 @@ class App.OnlineNotificationWidget extends App.Controller ) $(window).on 'click.notifications', @hide + $(window).on 'keydown.notifications', @listNavigate @updateContent() release: -> $(window).off 'click.notifications' + $(window).off 'keydown.notifications' App.OnlineNotification.unsubscribe(@subscribeId) super @@ -99,7 +100,7 @@ class App.OnlineNotificationWidget extends App.Controller current.removeClass('is-hover') next.addClass('is-hover') else - prev = current.prev('.is-item') + prev = current.prev('.js-item') if prev.size() current.removeClass('is-hover') prev.addClass('is-hover') @@ -138,7 +139,7 @@ class App.OnlineNotificationWidget extends App.Controller heightApp = $('#app').height() heightPopoverSpacer = 22 heightPopoverHeader = @header.outerHeight(true) - heightPopoverContent = 0 + heightPopoverContent = 30 @item.each -> heightPopoverContent += @clientHeight if (heightPopoverHeader + heightPopoverContent + heightPopoverSpacer) > heightApp From 205ab421bbc0083b7ffce04e7141125d36e11737 Mon Sep 17 00:00:00 2001 From: Martin Edenhofer Date: Mon, 21 Mar 2016 20:10:01 +0100 Subject: [PATCH 10/11] Fixed ticket creation. --- .../user_organization_autocompletion.coffee | 1 + test/browser_test_helper.rb | 223 +++++++++++++----- 2 files changed, 166 insertions(+), 58 deletions(-) diff --git a/app/assets/javascripts/app/lib/app_post/user_organization_autocompletion.coffee b/app/assets/javascripts/app/lib/app_post/user_organization_autocompletion.coffee index a4c41dd4e..83797c5ea 100644 --- a/app/assets/javascripts/app/lib/app_post/user_organization_autocompletion.coffee +++ b/app/assets/javascripts/app/lib/app_post/user_organization_autocompletion.coffee @@ -155,6 +155,7 @@ class App.UserOrganizationAutocompletion extends App.Controller # enter / take item if e.keyCode is 13 e.preventDefault() + e.stopPropagation() userId = @$('.recipientList').find('li.is-active').data('user-id') if !userId organizationId = @$('.recipientList').find('li.is-active').data('organization-id') diff --git a/test/browser_test_helper.rb b/test/browser_test_helper.rb index 157774743..c6f8bfcd7 100644 --- a/test/browser_test_helper.rb +++ b/test/browser_test_helper.rb @@ -70,6 +70,7 @@ class TestCase < Test::Unit::TestCase rescue # just try again sleep 10 + log('browser_instance', { rescure: true }) browser_instance_preferences(local_browser) end @@ -148,7 +149,7 @@ class TestCase < Test::Unit::TestCase value: 'auto wizard is enabled', timeout: 10, ) - location( url: "#{browser_url}/#getting_started/auto_wizard" ) + location(url: "#{browser_url}/#getting_started/auto_wizard") sleep 10 login = instance.find_elements(css: '.user-menu .user a')[0].attribute('title') if login != params[:username] @@ -213,9 +214,17 @@ class TestCase < Test::Unit::TestCase instance = params[:browser] || @browser - instance.find_elements(css: 'a[href="#current_user"]')[0].click - sleep 0.1 - instance.find_elements(css: 'a[href="#logout"]')[0].click + click( + browser: instance, + css: 'a[href="#current_user"]', + mute_log: true, + ) + click( + browser: instance, + css: 'a[href="#logout"]', + mute_log: true, + ) + (1..6).each { sleep 1 login = instance.find_elements(css: '#login')[0] @@ -349,16 +358,26 @@ class TestCase < Test::Unit::TestCase instance = params[:browser] || @browser if params[:css] - element = instance.find_elements(css: params[:css])[0] - #instance.mouse.move_to(element) - #sleep 0.2 - element.click + begin + element = instance.find_elements(css: params[:css])[0] + #if element + # instance.mouse.move_to(element) + #end + sleep 0.2 + element.click + rescue => e + sleep 0.5 + + # just try again + log('click', { rescure: true }) + element = instance.find_elements(css: params[:css])[0] + #if element + # instance.mouse.move_to(element) + #end + sleep 0.2 + element.click + end - # trigger also focus on input/select and textarea fields - #if params[:css] =~ /(input|select|textarea)/ - # instance.execute_script("$('#{params[:css]}').trigger('focus')") - # sleep 0.2 - #end else sleep 1 instance.find_elements(partial_link_text: params[:text])[0].click @@ -540,19 +559,21 @@ class TestCase < Test::Unit::TestCase dropdown.deselect_all end dropdown.select_by(:text, params[:value]) - puts "select - #{params.inspect}" + #puts "select - #{params.inspect}" rescue sleep 0.5 # just try again + log('select', { rescure: true }) element = instance.find_elements(css: params[:css])[0] dropdown = Selenium::WebDriver::Support::Select.new(element) if params[:deselect_all] dropdown.deselect_all end dropdown.select_by(:text, params[:value]) - puts "select2 - #{params.inspect}" + #puts "select2 - #{params.inspect}" end + sleep 0.8 end =begin @@ -1299,12 +1320,21 @@ wait untill text in selector disabppears instance = params[:browser] || @browser data = params[:data] - instance.find_elements(css: 'a[href="#manage"]')[0].click - sleep 0.2 - instance.find_elements(css: 'a[href="#manage/overviews"]')[0].click - sleep 0.2 - instance.find_elements(css: '#content a[data-type="new"]')[0].click - sleep 2 + click( + browser: instance, + css: 'a[href="#manage"]', + mute_log: true, + ) + click( + browser: instance, + css: 'a[href="#manage/overviews"]', + mute_log: true, + ) + click( + browser: instance, + css: '#content a[data-type="new"]', + mute_log: true, + ) if data[:name] set( @@ -1392,12 +1422,16 @@ wait untill text in selector disabppears instance = params[:browser] || @browser data = params[:data] - instance.find_elements(css: 'a[href="#manage"]')[0].click - sleep 0.2 - instance.find_elements(css: 'a[href="#manage/overviews"]')[0].click - sleep 1 - #instance.find_elements(css: '#content a[data-type="new"]')[0].click - #sleep 2 + click( + browser: instance, + css: 'a[href="#manage"]', + mute_log: true, + ) + click( + browser: instance, + css: 'a[href="#manage/overviews"]', + mute_log: true, + ) instance.execute_script("$(\"#content td:contains('#{data[:name]}')\").first().click()") sleep 2 @@ -1495,8 +1529,17 @@ wait untill text in selector disabppears instance = params[:browser] || @browser data = params[:data] - instance.find_elements(css: 'a[href="#new"]')[0].click - instance.find_elements(css: 'a[href="#ticket/create"]')[0].click + click( + browser: instance, + css: 'a[href="#new"]', + mute_log: true, + ) + click( + browser: instance, + css: 'a[href="#ticket/create"]', + mute_log: true, + ) + element = instance.find_elements(css: '.active .newTicket')[0] if !element screenshot(browser: instance, comment: 'ticket_create_failed') @@ -1594,9 +1637,14 @@ wait untill text in selector disabppears assert(true, 'ticket created without submit') return end - sleep 0.8 + sleep 0.5 #instance.execute_script('$(".content.active .newTicket form").submit();') - instance.find_elements(css: '.active .newTicket button.js-submit')[0].click + click( + browser: instance, + css: '.active .newTicket button.js-submit', + mute_log: true, + ) + sleep 1 (1..10).each { if instance.current_url =~ /#{Regexp.quote('#ticket/zoom/')}/ @@ -2136,11 +2184,22 @@ wait untill text in selector disabppears instance = params[:browser] || @browser data = params[:data] - instance.find_elements(css: 'a[href="#manage"]')[0].click - sleep 1 - instance.find_elements(css: 'a[href="#manage/users"]')[0].click - sleep 2 - instance.find_elements(css: 'a[data-type="new"]')[0].click + click( + browser: instance, + css: 'a[href="#manage"]', + mute_log: true, + ) + click( + browser: instance, + css: 'a[href="#manage/users"]', + mute_log: true, + ) + click( + browser: instance, + css: 'a[data-type="new"]', + mute_log: true, + ) + sleep 2 element = instance.find_elements(css: '.modal input[name=firstname]')[0] element.clear @@ -2196,11 +2255,22 @@ wait untill text in selector disabppears instance = params[:browser] || @browser data = params[:data] - instance.find_elements(css: 'a[href="#manage"]')[0].click - sleep 1 - instance.find_elements(css: 'a[href="#manage/slas"]')[0].click - sleep 2 - instance.find_elements(css: 'a.js-new')[0].click + click( + browser: instance, + css: 'a[href="#manage"]', + mute_log: true, + ) + click( + browser: instance, + css: 'a[href="#manage/slas"]', + mute_log: true, + ) + click( + browser: instance, + css: 'a.js-new', + mute_log: true, + ) + sleep 2 element = instance.find_elements(css: '.modal input[name=name]')[0] element.clear @@ -2243,11 +2313,22 @@ wait untill text in selector disabppears instance = params[:browser] || @browser data = params[:data] - instance.find_elements(css: 'a[href="#manage"]')[0].click - sleep 1 - instance.find_elements(css: 'a[href="#manage/text_modules"]')[0].click - sleep 2 - instance.find_elements(css: 'a[data-type="new"]')[0].click + click( + browser: instance, + css: 'a[href="#manage"]', + mute_log: true, + ) + click( + browser: instance, + css: 'a[href="#manage/text_modules"]', + mute_log: true, + ) + click( + browser: instance, + css: 'a[data-type="new"]', + mute_log: true, + ) + sleep 2 element = instance.find_elements(css: '.modal input[name=name]')[0] element.clear @@ -2292,13 +2373,28 @@ wait untill text in selector disabppears instance = params[:browser] || @browser data = params[:data] - instance.find_elements(css: 'a[href="#manage"]')[0].click - sleep 1 - instance.find_elements(css: 'a[href="#channels/email"]')[0].click - sleep 1 - instance.find_elements(css: 'a[href="#c-signature"]')[0].click - sleep 8 - instance.find_elements(css: '#content #c-signature a[data-type="new"]')[0].click + click( + browser: instance, + css: 'a[href="#manage"]', + mute_log: true, + ) + click( + browser: instance, + css: 'a[href="#channels/email"]', + mute_log: true, + ) + click( + browser: instance, + css: 'a[href="#c-signature"]', + mute_log: true, + ) + sleep 4 + click( + browser: instance, + css: '#content #c-signature a[data-type="new"]', + mute_log: true, + ) + sleep 2 element = instance.find_elements(css: '.modal input[name=name]')[0] element.clear @@ -2343,11 +2439,22 @@ wait untill text in selector disabppears instance = params[:browser] || @browser data = params[:data] - instance.find_elements(css: 'a[href="#manage"]')[0].click - sleep 0.5 - instance.find_elements(css: 'a[href="#manage/groups"]')[0].click - sleep 2 - instance.find_elements(css: 'a[data-type="new"]')[0].click + click( + browser: instance, + css: 'a[href="#manage"]', + mute_log: true, + ) + click( + browser: instance, + css: 'a[href="#channels/groups"]', + mute_log: true, + ) + click( + browser: instance, + css: 'a[data-type="new"]', + mute_log: true, + ) + sleep 2 element = instance.find_elements(css: '.modal input[name=name]')[0] element.clear From 715ed8e6dabf4eaff4a845bac7ef6a819a9d6674 Mon Sep 17 00:00:00 2001 From: Martin Edenhofer Date: Mon, 21 Mar 2016 21:51:05 +0100 Subject: [PATCH 11/11] Fixed group_create helper. --- test/browser_test_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/browser_test_helper.rb b/test/browser_test_helper.rb index c6f8bfcd7..d33dff2ae 100644 --- a/test/browser_test_helper.rb +++ b/test/browser_test_helper.rb @@ -2446,7 +2446,7 @@ wait untill text in selector disabppears ) click( browser: instance, - css: 'a[href="#channels/groups"]', + css: 'a[href="#manage/groups"]', mute_log: true, ) click(