diff --git a/app/assets/javascripts/app/controllers/_application_controller_form.js.coffee b/app/assets/javascripts/app/controllers/_application_controller_form.js.coffee index 35397afa9..5ff075c71 100644 --- a/app/assets/javascripts/app/controllers/_application_controller_form.js.coffee +++ b/app/assets/javascripts/app/controllers/_application_controller_form.js.coffee @@ -95,7 +95,7 @@ class App.ControllerForm extends App.Controller for eventSelector, callback of @events do (eventSelector, callback) => evs = eventSelector.split(' ') - fieldset.find( evs[1] ).bind(evs[0], (e) => callback(e) ) + fieldset.find( evs[1] ).bind( evs[0], (e) => callback(e) ) # return form return fieldset @@ -264,13 +264,13 @@ class App.ControllerForm extends App.Controller { name: 'inactive', value: false } ] - # update boolean types - for record in attribute.options - record.value = '{boolean}::' + record.value + # set data type + if attribute.name + attribute.name = '{boolean}' + attribute.name # finde selected item of list for record in attribute.options - if record.value is '{boolean}::' + attribute.value + if record.value is attribute.value record.selected = 'selected' # return item @@ -283,7 +283,6 @@ class App.ControllerForm extends App.Controller # date else if attribute.tag is 'date' attribute.type = 'text' - item = $( App.view('generic/date')( attribute: attribute ) ) #item.datetimepicker({ # format: 'Y.m.d' #}); @@ -291,10 +290,163 @@ class App.ControllerForm extends App.Controller # date else if attribute.tag is 'datetime' attribute.type = 'text' - item = $( App.view('generic/date')( attribute: attribute ) ) - #item.datetimepicker({ - # format: 'Y.m.d H:i' - #}); + + # set data type + if attribute.name + attribute.nameRaw = attribute.name + attribute.name = '{datetime}' + attribute.name + if attribute.value + if typeof( attribute.value ) is 'string' + unixtime = new Date( Date.parse( attribute.value ) ) + else + unixtime = new Date( attribute.value ) + year = unixtime.getYear() + 1900 + month = unixtime.getMonth() + 1 + day = unixtime.getDate() + hour = unixtime.getHours() + minute = unixtime.getMinutes() + item = $( App.view('generic/datetime')( + attribute: attribute + year: year + month: month + day: day + hour: hour + minute: minute + ) ) + item.find('.js-today').bind('click', (e) -> + e.preventDefault() + name = $(@).closest('.form-group').find('[data-name]').attr('data-name') + today = new Date(); + item.closest('.form-group').find("[name=\"{datetime}#{name}___day\"]").val( today.getDate() ) + item.closest('.form-group').find("[name=\"{datetime}#{name}___month\"]").val( today.getMonth()+1 ) + item.closest('.form-group').find("[name=\"{datetime}#{name}___year\"]").val( today.getFullYear() ) + item.closest('.form-group').find("[name=\"{datetime}#{name}___hour\"]").val( today.getHours() ) + item.closest('.form-group').find("[name=\"{datetime}#{name}___minute\"]").val( today.getMinutes() ) + ) + + setNewTime = (diff, el) -> + name = $(el).closest('.form-group').find('[data-name]').attr('data-name') + + day = item.closest('.form-group').find("[name=\"{datetime}#{name}___day\"]").val() + month = item.closest('.form-group').find("[name=\"{datetime}#{name}___month\"]").val() + year = item.closest('.form-group').find("[name=\"{datetime}#{name}___year\"]").val() + hour = item.closest('.form-group').find("[name=\"{datetime}#{name}___hour\"]").val() + minute = item.closest('.form-group').find("[name=\"{datetime}#{name}___minute\"]").val() + format = (number) -> + if parseInt(number) < 10 + number = "0#{number}" + number + #console.log('ph', diff, "#{year}-#{format(month)}-#{format(day)}T#{format(hour)}:#{format(minute)}:00Z") + time = new Date( Date.parse( "#{year}-#{format(month)}-#{format(day)}T#{format(hour)}:#{format(minute)}:00Z" ) ) + time.setMinutes( time.getMinutes() + diff + time.getTimezoneOffset() ) + #console.log('T', time, time.getHours(), time.getMinutes()) + item.closest('.form-group').find("[name=\"{datetime}#{name}___day\"]").val( time.getDate() ) + item.closest('.form-group').find("[name=\"{datetime}#{name}___month\"]").val( time.getMonth()+1 ) + item.closest('.form-group').find("[name=\"{datetime}#{name}___year\"]").val( time.getFullYear() ) + item.closest('.form-group').find("[name=\"{datetime}#{name}___hour\"]").val( time.getHours() ) + item.closest('.form-group').find("[name=\"{datetime}#{name}___minute\"]").val( time.getMinutes() ) + + item.find('.js-plus-hour').bind('click', (e) -> + e.preventDefault() + setNewTime(60, @) + ) + item.find('.js-minus-hour').bind('click', (e) -> + e.preventDefault() + setNewTime(-60, @) + ) + item.find('.js-plus-day').bind('click', (e) -> + e.preventDefault() + setNewTime(60 * 24, @) + ) + item.find('.js-minus-day').bind('click', (e) -> + e.preventDefault() + setNewTime(-60 * 24, @) + ) + item.find('.js-plus-week').bind('click', (e) -> + e.preventDefault() + setNewTime(60 * 24 * 7, @) + ) + item.find('.js-minus-week').bind('click', (e) -> + e.preventDefault() + setNewTime(-60 * 24 * 7, @) + ) + + item.find('input').bind('keyup blur focus', (e) -> + + # do validation + name = $(@).attr('name') + if name + fieldPrefix = name.split('___')[0] + + # remove old validation + item.find('.has-error').removeClass('has-error') + item.closest('.form-group').find('.help-inline').html('') + + day = item.closest('.form-group').find("[name=\"#{fieldPrefix}___day\"]").val() + month = item.closest('.form-group').find("[name=\"#{fieldPrefix}___month\"]").val() + year = item.closest('.form-group').find("[name=\"#{fieldPrefix}___year\"]").val() + hour = item.closest('.form-group').find("[name=\"#{fieldPrefix}___hour\"]").val() + minute = item.closest('.form-group').find("[name=\"#{fieldPrefix}___minute\"]").val() + + # validate exists + errors = {} + if !day + errors.day = 'missing' + if !month + errors.month = 'missing' + if !year + errors.year = 'missing' + if !hour + errors.hour = 'missing' + if !minute + errors.minute = 'missing' + + # ranges + if day + daysInMonth = 31 + if month && year + daysInMonth = new Date(year, month, 0).getDate(); + console.log('222', month, year, daysInMonth) + + if parseInt(day).toString() is 'NaN' + errors.day = 'invalid' + else if parseInt(day) > daysInMonth || parseInt(day) < 1 + errors.day = 'invalid' + + if month + if parseInt(month).toString() is 'NaN' + errors.month = 'invalid' + else if parseInt(month) > 12 || parseInt(month) < 1 + errors.month = 'invalid' + + if year + if parseInt(year).toString() is 'NaN' + errors.year = 'invalid' + else if parseInt(year) > 2100 || parseInt(year) < 2001 + errors.year = 'invalid' + + if hour + if parseInt(hour).toString() is 'NaN' + errors.hour = 'invalid' + else if parseInt(hour) > 23 || parseInt(hour) < 0 + errors.hour = 'invalid' + + if minute + if parseInt(minute).toString() is 'NaN' + errors.minute = 'invalid' + else if parseInt(minute) > 59 + errors.minute = 'invalid' + + if !_.isEmpty(errors) + for key, value of errors + item.closest('.form-group').addClass('has-error') + item.closest('.form-group').find("[name=\"#{fieldPrefix}___#{key}\"]").addClass('has-error') + #item.closest('.form-group').find('.help-inline').text( value ) + + e.preventDefault() + e.stopPropagation() + return + ) # timezone else if attribute.tag is 'timezone' @@ -615,95 +767,6 @@ class App.ControllerForm extends App.Controller if listItem.value is "#{ attribute.name }::#{key}" addItem( "#{ attribute.name }::#{key}", listItem.name, item.find('.add a'), value ) - # select - else if attribute.tag is 'input_select' - item = $('
') - - # select shown attributes - loopData = {} - if @params && @params[ attribute.name ] - loopData = @params[ attribute.name ] - loopData[''] = '' - - # show each attribote - counter = 0 - for key of loopData - counter =+ 1 - - # clone to keep it untouched for next loop - select = _.clone( attribute ) - input = _.clone( attribute ) - - # set field ids - not needed in this case - select.id = '' - input.id = '' - - # rename to be able to identify this option later - select.name = '{input_select}::' + select.name - input.name = '{input_select}::' + input.name - - # set sub attributes - for keysub of attribute.select - select[keysub] = attribute.select[keysub] - for keysub of attribute.input - input[keysub] = attribute.input[keysub] - - # set hide for + options - itemClass = '' - if key is '' - itemClass = 'hide' - select['nulloption'] = true - - # set selected value - select.value = key - input.value = loopData[ key ] - - # build options list based on config - @_getConfigOptionList( select ) - - # build options list based on relation - @_getRelationOptionList( select ) - - # add null selection if needed - @_addNullOption( select ) - - # sort attribute.options - @_sortOptions( select ) - - # finde selected/checked item of list - @_selectedOptions( select ) - - pearItem = $("
") - pearItem.append $( App.view('generic/select')( attribute: select ) ) - pearItem.append $( App.view('generic/input')( attribute: input ) ) - itemRemote = $('') - itemRemote.bind('click', (e) -> - e.preventDefault() - $(@).parent().remove() - ) - pearItem.append( itemRemote ) - item.append( pearItem ) - - if key is '' - itemAdd = $('
') - itemAdd.bind('click', (e) -> - e.preventDefault() - - # copy - newElement = $(@).prev().clone() - newElement.removeClass('hide') - - # bind on remove - newElement.find('.input_select_remove').bind('click', (e) -> - e.preventDefault() - $(@).parent().remove() - ) - - # prepend - $(@).parent().find('.add').before( newElement ) - ) - item.append( itemAdd ) - # checkbox else if attribute.tag is 'checkbox' item = $( App.view('generic/checkbox')( attribute: attribute ) ) @@ -1792,6 +1855,8 @@ class App.ControllerForm extends App.Controller for key in name el.find('[name="' + key + '"]').closest('.form-group').removeClass('hide') el.find('[name="' + key + '"]').removeClass('is-hidden') + el.find('[data-name="' + key + '"]').closest('.form-group').removeClass('hide') + el.find('[data-name="' + key + '"]').removeClass('is-hidden') _hide: (name, el = @el) -> if !_.isArray(name) @@ -1799,6 +1864,8 @@ class App.ControllerForm extends App.Controller for key in name el.find('[name="' + key + '"]').closest('.form-group').addClass('hide') el.find('[name="' + key + '"]').addClass('is-hidden') + el.find('[data-name="' + key + '"]').closest('.form-group').addClass('hide') + el.find('[data-name="' + key + '"]').addClass('is-hidden') _mandantory: (name, el = @el) -> if !_.isArray(name) @@ -1819,8 +1886,13 @@ class App.ControllerForm extends App.Controller if attribute.shown_if hit = false for refAttribute, refValue of attribute.shown_if - if params[refAttribute] && params[refAttribute].toString() is refValue.toString() - hit = true + if params[refAttribute] + if _.isArray( refValue ) + for item in refValue + if params[refAttribute].toString() is item.toString() + hit = true + else if params[refAttribute].toString() is refValue.toString() + hit = true if hit ui._show(attribute.name) else @@ -1831,8 +1903,13 @@ class App.ControllerForm extends App.Controller if attribute.required_if hit = false for refAttribute, refValue of attribute.required_if - if params[refAttribute] && params[refAttribute].toString() is refValue.toString() - hit = true + if params[refAttribute] + if _.isArray( refValue ) + for item in refValue + if params[refAttribute].toString() is item.toString() + hit = true + else if params[refAttribute].toString() is refValue.toString() + hit = true if hit ui._mandantory(attribute.name) else @@ -2066,33 +2143,70 @@ class App.ControllerForm extends App.Controller # get form elements array = lookupForm.serializeArray() - # 1:1 and boolean params + # array to names for key in array # check if item is-hidden and should not be used if lookupForm.find('[name="' + key.name + '"]').hasClass('is-hidden') + param[key.name] = undefined continue - # collect all other params + # collect all params, push it to an array if already exists if param[key.name] if typeof param[key.name] is 'string' param[key.name] = [ param[key.name], key.value] else param[key.name].push key.value else - - # check boolean - attributeType = key.value.split '::' - if attributeType[0] is '{boolean}' - if attributeType[1] is 'true' - key.value = true - else - key.value = false -# else if attributeType[0] is '{boolean}' - param[key.name] = key.value - # check :: fields + # data type conversion + for key of param + + # get boolean + if key.substr(0,9) is '{boolean}' + newKey = key.substr( 9, key.length ) + param[ newKey ] = param[ key ] + delete param[ key ] + if param[ newKey ] && param[ newKey ].toString() is 'true' + param[ newKey ] = true + else + param[ newKey ] = false + + # get {datetime} + else if key.substr(0,10) is '{datetime}' + newKey = key.substr( 10, key.length ) + namespace = newKey.split '___' + + if !param[ namespace[0] ] + datetimeKey = "{datetime}#{namespace[0]}___" + year = param[ "#{datetimeKey}year" ] + month = param[ "#{datetimeKey}month" ] + day = param[ "#{datetimeKey}day" ] + hour = param[ "#{datetimeKey}hour" ] + minute = param[ "#{datetimeKey}minute" ] + timezone = (new Date()).getTimezoneOffset()/60 + if year && month && day && hour && minute + format = (number) -> + if parseInt(number) < 10 + number = "0#{number}" + number + try + time = new Date( Date.parse( "#{year}-#{format(month)}-#{format(day)}T#{format(hour)}:#{format(minute)}:00Z" ) ) + time.setMinutes( time.getMinutes() + time.getTimezoneOffset() ) + param[ namespace[0] ] = time.toISOString() + catch err + console.log('ERR', err) + + #console.log('T', time, time.getHours(), time.getMinutes()) + + delete param[ "#{datetimeKey}year" ] + delete param[ "#{datetimeKey}month" ] + delete param[ "#{datetimeKey}day" ] + delete param[ "#{datetimeKey}hour" ] + delete param[ "#{datetimeKey}minute" ] + + # split :: fields, build objects inputSelectObject = {} for key of param parts = key.split '::' @@ -2109,25 +2223,7 @@ class App.ControllerForm extends App.Controller inputSelectObject[ parts[0] ][ parts[1] ][ parts[2] ] = param[ key ] delete param[ key ] - # check {input_select} - for key of param - attributeType = key.split '::' - name = attributeType[1] -# console.log 'split', key, attributeType, param[ name ] - if attributeType[0] is '{input_select}' && !param[ name ] - - # array need to be converted - inputSelectData = param[ key ] - inputSelectObject[ name ] = {} - for x in [0..inputSelectData.length] by 2 -# console.log 'for by 111', x, inputSelectData, inputSelectData[x], inputSelectData[ x + 1 ] - if inputSelectData[ x ] - inputSelectObject[ name ][ inputSelectData[x] ] = inputSelectData[ x + 1 ] - - # remove {input_select} items - delete param[ key ] - - # set new {input_select} items + # set new object params for key of inputSelectObject param[ key ] = inputSelectObject[ key ] @@ -2203,8 +2299,13 @@ class App.ControllerForm extends App.Controller # show new errors for key, msg of data.errors - lookupForm.find('[name="' + key + '"]').parents('div .form-group').addClass('has-error') - lookupForm.find('[name="' + key + '"]').parent().find('.help-inline').html(msg) + item = lookupForm.find('[name="' + key + '"]').closest('.form-group') + item.addClass('has-error') + item.find('.help-inline').html(msg) + + item = lookupForm.find('[data-name="' + key + '"]').closest('.form-group') + item.addClass('has-error') + item.find('.help-inline').html(msg) # set autofocus - lookupForm.find('.has-error').find('input, textarea').first().focus() + lookupForm.find('.has-error').find('input, textarea, select').first().focus() diff --git a/app/assets/javascripts/app/views/generic/datetime.jst.eco b/app/assets/javascripts/app/views/generic/datetime.jst.eco new file mode 100644 index 000000000..666d9e828 --- /dev/null +++ b/app/assets/javascripts/app/views/generic/datetime.jst.eco @@ -0,0 +1,19 @@ +
+ + . + + . + + + : + +
+
+<%- @T('now') %> +| +<%- @T('-1') %> <%- @T('+1') %> <%- @T('hour') %> +| +<%- @T('-1') %> <%- @T('+1') %> <%- @T('day') %> +| +<%- @T('-7') %> <%- @T('+7') %> <%- @T('days') %> +
\ No newline at end of file diff --git a/app/assets/stylesheets/zammad.css.scss b/app/assets/stylesheets/zammad.css.scss index 9d18ab2a7..cae1fd802 100644 --- a/app/assets/stylesheets/zammad.css.scss +++ b/app/assets/stylesheets/zammad.css.scss @@ -757,6 +757,11 @@ textarea, border-color: red !important; } + input.has-error { + box-shadow: none; + border-color: red !important; + } + .help-inline:not(:empty) { color: red; padding: 2px; diff --git a/app/controllers/tests_controller.rb b/app/controllers/tests_controller.rb index 0d17c6e06..81bf09477 100644 --- a/app/controllers/tests_controller.rb +++ b/app/controllers/tests_controller.rb @@ -2,48 +2,6 @@ class TestsController < ApplicationController - # GET /tests/core - def core - respond_to do |format| - format.html # index.html.erb - end - end - - # GET /tests/ui - def ui - respond_to do |format| - format.html # index.html.erb - end - end - - # GET /tests/from - def form - respond_to do |format| - format.html # index.html.erb - end - end - - # GET /tests/from_extended - def form - respond_to do |format| - format.html # index.html.erb - end - end - - # GET /tests/table - def table - respond_to do |format| - format.html # index.html.erb - end - end - - # GET /tests/html_utils - def html_utils - respond_to do |format| - format.html # index.html.erb - end - end - # GET /test/wait def wait sleep params[:sec].to_i @@ -51,4 +9,4 @@ class TestsController < ApplicationController render :json => result end -end +end \ No newline at end of file diff --git a/config/routes/test.rb b/config/routes/test.rb index 8faf95f01..bc85e8126 100644 --- a/config/routes/test.rb +++ b/config/routes/test.rb @@ -1,12 +1,13 @@ Zammad::Application.routes.draw do - match '/tests-core', :to => 'tests#core', :via => :get - match '/tests-ui', :to => 'tests#ui', :via => :get - match '/tests-model', :to => 'tests#model', :via => :get - match '/tests-form', :to => 'tests#form', :via => :get - match '/tests-form-extended', :to => 'tests#form_extended', :via => :get - match '/tests-table', :to => 'tests#table', :via => :get - match '/tests-html-utils', :to => 'tests#html_utils', :via => :get - match '/tests/wait/:sec', :to => 'tests#wait', :via => :get + match '/tests-core', :to => 'tests#core', :via => :get + match '/tests-ui', :to => 'tests#ui', :via => :get + match '/tests-model', :to => 'tests#model', :via => :get + match '/tests-form', :to => 'tests#form', :via => :get + match '/tests-form-extended', :to => 'tests#form_extended', :via => :get + match '/tests-form-validation', :to => 'tests#form_validation', :via => :get + match '/tests-table', :to => 'tests#table', :via => :get + match '/tests-html-utils', :to => 'tests#html_utils', :via => :get + match '/tests/wait/:sec', :to => 'tests#wait', :via => :get end \ No newline at end of file diff --git a/db/seeds.rb b/db/seeds.rb index 09cbe4a22..8ceb17f73 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -1345,10 +1345,11 @@ Ticket::StateType.create_if_not_exists( :id => 7, :name => 'removed', :updated_b Ticket::State.create_if_not_exists( :id => 1, :name => 'new', :state_type_id => Ticket::StateType.where(:name => 'new').first.id ) Ticket::State.create_if_not_exists( :id => 2, :name => 'open', :state_type_id => Ticket::StateType.where(:name => 'open').first.id ) -Ticket::State.create_if_not_exists( :id => 3, :name => 'pending', :state_type_id => Ticket::StateType.where(:name => 'pending reminder').first.id ) +Ticket::State.create_if_not_exists( :id => 3, :name => 'pending reminder', :state_type_id => Ticket::StateType.where(:name => 'pending reminder').first.id ) Ticket::State.create_if_not_exists( :id => 4, :name => 'closed', :state_type_id => Ticket::StateType.where(:name => 'closed').first.id ) Ticket::State.create_if_not_exists( :id => 5, :name => 'merged', :state_type_id => Ticket::StateType.where(:name => 'merged').first.id ) -Ticket::State.create_if_not_exists( :id => 6, :name => 'removed', :state_type_id => Ticket::StateType.where(:name => 'removed').first.id ) +Ticket::State.create_if_not_exists( :id => 6, :name => 'removed', :state_type_id => Ticket::StateType.where(:name => 'removed').first.id, :active => false ) +Ticket::State.create_if_not_exists( :id => 7, :name => 'pending close', :state_type_id => Ticket::StateType.where(:name => 'pending action').first.id, :next_state_id => 5 ) Ticket::Priority.create_if_not_exists( :name => '1 low' ) Ticket::Priority.create_if_not_exists( :name => '2 normal' ) @@ -1427,8 +1428,9 @@ Overview.create_if_not_exists( :prio => 1010, :role_id => overview_role.id, :condition => { - 'tickets.state_id' => [3], - 'tickets.owner_id' => 'current_user.id', + 'tickets.state_id' => [3], + 'tickets.owner_id' => 'current_user.id', + 'tickets.pending_time' => { 'direction' => 'before', 'count'=> 1, 'area' => 'minute' }, }, :order => { :by => 'created_at', @@ -1483,13 +1485,34 @@ Overview.create_if_not_exists( }, ) +Overview.create_if_not_exists( + :name => 'All pending reached Tickets', + :link => 'all_pending_reached', + :prio => 1035, + :role_id => overview_role.id, + :condition => { + 'tickets.state_id' => [3], + 'tickets.pending_time' => { 'direction' => 'before', 'count'=> 1, 'area' => 'minute' }, + }, + :order => { + :by => 'created_at', + :direction => 'ASC', + }, + :view => { + :d => [ 'title', 'customer', 'group', 'created_at' ], + :s => [ 'title', 'customer', 'group', 'created_at' ], + :m => [ 'number', 'title', 'customer', 'group', 'created_at' ], + :view_mode_default => 's', + }, +) + Overview.create_if_not_exists( :name => 'Escalated Tickets', :link => 'all_escalated', :prio => 1040, :role_id => overview_role.id, :condition => { - 'tickets.escalation_time' =>{ 'direction' => 'before', 'count'=> 5, 'area' => 'minute' }, + 'tickets.escalation_time' => { 'direction' => 'before', 'count'=> 5, 'area' => 'minute' }, }, :order => { :by => 'escalation_time', @@ -1510,8 +1533,8 @@ Overview.create_if_not_exists( :prio => 1000, :role_id => overview_role.id, :condition => { - 'tickets.state_id' => [ 1,2,3,4,6 ], - 'tickets.customer_id' => 'current_user.id', + 'tickets.state_id' => [ 1,2,3,4,6 ], + 'tickets.customer_id' => 'current_user.id', }, :order => { :by => 'created_at', diff --git a/public/assets/tests/form-validation.js b/public/assets/tests/form-validation.js new file mode 100644 index 000000000..bca5d6f2c --- /dev/null +++ b/public/assets/tests/form-validation.js @@ -0,0 +1,149 @@ +test( "form validation check", function() { + + $('#forms').append('

form params check

') + + var el = $('#form1') + var defaults = {} + var form = new App.ControllerForm({ + el: el, + model: { + configure_attributes: [ + { name: 'input1', display: 'Input1', tag: 'input', type: 'text', limit: 100, null: false }, + { name: 'password1', display: 'Password1', tag: 'input', type: 'password', limit: 100, null: false }, + { name: 'textarea1', display: 'Textarea1', tag: 'textarea', rows: 6, limit: 100, null: false, upload: true }, + { name: 'select1', display: 'Select1', tag: 'select', null: false, nulloption: true, options: { true: 'internal', false: 'public' } }, + { name: 'selectmulti1', display: 'SelectMulti1', tag: 'select', null: false, nulloption: true, multiple: true, options: { true: 'internal', false: 'public' } }, + { name: 'autocompletion1', display: 'AutoCompletion1', tag: 'autocompletion', null: false, options: { true: 'internal', false: 'public' }, source: [ { label: "Choice1", value: "value1", id: "id1" }, { label: "Choice2", value: "value2", id: "id2" }, ], minLength: 1 }, + { name: 'richtext1', display: 'Richtext1', tag: 'richtext', maxlength: 100, null: false, type: 'richtext', multiline: true, upload: true, default: defaults['richtext1'] }, + { name: 'datetime1', display: 'Datetime1', tag: 'datetime', null: false, default: defaults['datetime1'] }, + { name: 'active1', display: 'Active1', tag: 'boolean', type: 'boolean', default: defaults['active1'], null: false }, + ], + }, + params: defaults, + }); + equal( el.find('[name="input1"]').val(), '', 'check input1 value') + equal( el.find('[name="input1"]').prop('required'), true, 'check input1 required') +// equal( el.find('[name="input1"]').is(":focus"), true, 'check input1 focus') + + equal( el.find('[name="password1"]').val(), '', 'check password1 value') + equal( el.find('[name="password1_confirm"]').val(), '', 'check password1 value') + equal( el.find('[name="password1"]').prop('required'), true, 'check password1 required') + + equal( el.find('[name="textarea1"]').val(), '', 'check textarea1 value') + equal( el.find('[name="textarea1"]').prop('required'), true, 'check textarea1 required') + + equal( el.find('[name="select1"]').val(), '', 'check select1 value') + equal( el.find('[name="select1"]').prop('required'), true, 'check select1 required') + + equal( el.find('[name="selectmulti1"]').val(), null, 'check selectmulti1 value') + equal( el.find('[name="selectmulti1"]').prop('required'), true, 'check selectmulti1 required') + + equal( el.find('[name="autocompletion1"]').val(), '', 'check autocompletion1 value') + equal( el.find('[name="autocompletion1"]').prop('required'), true, 'check autocompletion1 required') + + equal( el.find('[data-name="richtext1"]').val(), '', 'check richtext1 value') + //equal( el.find('[data-name="richtext1"]').prop('required'), true, 'check richtext1 required') + + + + params = App.ControllerForm.params( el ) + errors = form.validate(params) + + test_errors = { + input1: "is required", + password1: "is required", + textarea1: "is required", + select1: "is required", + selectmulti1: "is required", + autocompletion1: "is required", + richtext1: "is required", + datetime1: "is required", + } + deepEqual( errors, test_errors, 'validation errors check' ) + + App.ControllerForm.validate( { errors: errors, form: el } ) + + equal( el.find('[name="input1"]').closest('.form-group').hasClass('has-error'), true, 'check input1 has-error') + equal( el.find('[name="input1"]').closest('.form-group').find('.help-inline').text(), 'is required', 'check input1 error message') + + equal( el.find('[name="password1"]').closest('.form-group').hasClass('has-error'), true, 'check password1 has-error') + equal( el.find('[name="password1"]').closest('.form-group').find('.help-inline').text(), 'is required', 'check password1 error message') + + equal( el.find('[name="textarea1"]').closest('.form-group').hasClass('has-error'), true, 'check textarea1 has-error') + equal( el.find('[name="textarea1"]').closest('.form-group').find('.help-inline').text(), 'is required', 'check textarea1 error message') + + equal( el.find('[name="select1"]').closest('.form-group').hasClass('has-error'), true, 'check select1 has-error') + equal( el.find('[name="select1"]').closest('.form-group').find('.help-inline').text(), 'is required', 'check select1 error message') + + equal( el.find('[name="selectmulti1"]').closest('.form-group').hasClass('has-error'), true, 'check selectmulti1 has-error') + equal( el.find('[name="selectmulti1"]').closest('.form-group').find('.help-inline').text(), 'is required', 'check selectmulti1 error message') + + equal( el.find('[name="autocompletion1"]').closest('.form-group').hasClass('has-error'), true, 'check autocompletion1 has-error') + equal( el.find('[name="autocompletion1"]').closest('.form-group').find('.help-inline').text(), 'is required', 'check autocompletion1 error message') + + equal( el.find('[data-name="richtext1"]').closest('.form-group').hasClass('has-error'), true, 'check richtext1 has-error') + equal( el.find('[data-name="richtext1"]').closest('.form-group').find('.help-inline').text(), 'is required', 'check richtext1 error message') + + equal( el.find('[data-name="datetime1"]').closest('.form-group').hasClass('has-error'), true, 'check datetime1 has-error') + equal( el.find('[data-name="datetime1"]').closest('.form-group').find('.help-inline').text(), 'is required', 'check datetime1 error message') + +}); + +test( "datetime validation check", function() { + + $('#forms').append('

datetime validation check

') + + var el = $('#form2') + var defaults = {} + var form = new App.ControllerForm({ + el: el, + model: { + configure_attributes: [ + { name: 'datetime1', display: 'Datetime1', tag: 'datetime', null: false, default: defaults['datetime1'] }, + ], + }, + params: defaults, + }); + + params = App.ControllerForm.params( el ) + errors = form.validate(params) + test_errors = { + datetime1: "is required", + } + deepEqual( errors, test_errors, 'validation errors check' ) + App.ControllerForm.validate( { errors: errors, form: el } ) + + equal( el.find('[data-name="datetime1"]').closest('.form-group').hasClass('has-error'), true, 'check datetime1 has-error') + equal( el.find('[data-name="datetime1"]').closest('.form-group').find('.help-inline').text(), 'is required', 'check datetime1 error message') + + el.find('[name="{datetime}datetime1___day"]').val('1') + el.find('[name="{datetime}datetime1___month"]').val('1') + el.find('[name="{datetime}datetime1___year"]').val('2015') + el.find('[name="{datetime}datetime1___hour"]').val('12') + el.find('[name="{datetime}datetime1___minute"]').val('42') + params = App.ControllerForm.params( el ) + errors = form.validate(params) + test_errors = undefined +// datetime1: "invalid", +// } + deepEqual( errors, test_errors, 'validation errors check' ) + App.ControllerForm.validate( { errors: errors, form: el } ) + equal( el.find('[data-name="datetime1"]').closest('.form-group').hasClass('has-error'), false, 'check datetime1 has-error') + equal( el.find('[data-name="datetime1"]').closest('.form-group').find('.help-inline').text(), '', 'check datetime1 error message') + + el.find('[name="{datetime}datetime1___day"]').val('47') + el.find('[name="{datetime}datetime1___month"]').val('1') + el.find('[name="{datetime}datetime1___year"]').val('2015') + el.find('[name="{datetime}datetime1___hour"]').val('12') + el.find('[name="{datetime}datetime1___minute"]').val('42') + params = App.ControllerForm.params( el ) + errors = form.validate(params) + test_errors = { + datetime1: "is required", + } + deepEqual( errors, test_errors, 'validation errors check' ) + App.ControllerForm.validate( { errors: errors, form: el } ) + equal( el.find('[data-name="datetime1"]').closest('.form-group').hasClass('has-error'), true, 'check datetime1 has-error') + equal( el.find('[data-name="datetime1"]').closest('.form-group').find('.help-inline').text(), '', 'check datetime1 error message') + +}); \ No newline at end of file diff --git a/public/assets/tests/form.js b/public/assets/tests/form.js index b53592a03..270d35c2b 100644 --- a/public/assets/tests/form.js +++ b/public/assets/tests/form.js @@ -15,6 +15,7 @@ test( "form elements check", function() { selectmultioption1: false, selectmultioption2: [ false, true ], richtext2: 'lalu lalu', + datetime1: Date.parse('2015-01-11T12:40:00Z'), } new App.ControllerForm({ el: el, @@ -34,6 +35,8 @@ test( "form elements check", function() { { name: 'selectmultioption2', display: 'SelectMultiOption2', tag: 'select', null: false, multiple: true, options: [{ value: true, name: 'A' }, { value: 1, name: 'B'}, { value: false, name: 'C' }], default: defaults['selectmultioption2'] }, { name: 'richtext1', display: 'Richtext1', tag: 'richtext', limit: 100, null: true, upload: true, default: defaults['richtext1'] }, { name: 'richtext2', display: 'Richtext2', tag: 'richtext', limit: 100, null: true, upload: true, default: defaults['richtext2'] }, + { name: 'datetime1', display: 'Datetime1', tag: 'datetime', null: true, default: defaults['datetime1'] }, + { name: 'datetime2', display: 'Datetime2', tag: 'datetime', null: true, default: defaults['datetime2'] }, ] }, autofocus: true @@ -115,6 +118,9 @@ test( "form params check", function() { richtext6: '
lalu b lalu
', richtext7: "
 
 \n
\n
", richtext8: '
lalu b lalu
', + datetime1: new Date( Date.parse('2015-01-11T12:40:00Z') ), + active1: true, + active2: false, } new App.ControllerForm({ el: el, @@ -142,6 +148,10 @@ test( "form params check", function() { { name: 'richtext6', display: 'Richtext6', tag: 'richtext', maxlength: 100, null: true, type: 'textonly', multiline: true, upload: true, default: defaults['richtext6'] }, { name: 'richtext7', display: 'Richtext7', tag: 'richtext', maxlength: 100, null: true, type: 'textonly', multiline: false, default: defaults['richtext7'] }, { name: 'richtext8', display: 'Richtext8', tag: 'richtext', maxlength: 100, null: true, type: 'textonly', multiline: false, default: defaults['richtext8'] }, + { name: 'datetime1', display: 'Datetime1', tag: 'datetime', null: true, default: defaults['datetime1'] }, + { name: 'datetime2', display: 'Datetime2', tag: 'datetime', null: true, default: defaults['datetime2'] }, + { name: 'active1', display: 'Active1', tag: 'boolean', type: 'boolean', default: defaults['active1'], null: false }, + { name: 'active2', display: 'Active2', tag: 'boolean', type: 'boolean', default: defaults['active2'], null: false }, ], }, params: defaults, @@ -220,6 +230,9 @@ test( "form params check", function() { richtext6: '
lalu b lalu
', richtext7: '', richtext8: '
lalu b lalu
', + datetime1: '2015-01-11T12:40:00.000Z', + active1: true, + active2: false, } deepEqual( params, test_params, 'form param check' ); @@ -405,6 +418,7 @@ test( "form dependend fields check", function() { var test_params = { input1: "", input2: "some used default", + input3: undefined, select1: "false", select2: "false", selectmulti2: [ "true", "false" ], @@ -416,6 +430,7 @@ test( "form dependend fields check", function() { params = App.ControllerForm.params( el ) test_params = { input1: "", + input2: undefined, input3: "some used default", select1: "true", select2: "false", @@ -425,6 +440,143 @@ test( "form dependend fields check", function() { deepEqual( params, test_params, 'form param check' ); }); +test( "form handler check with and without fieldset", function() { +// deepEqual( item, test.value, 'group set/get tests' ); + +// mix default and params -> check it -> add note +// test auto completion +// show/hide fields base on field values -> bind changed event +// form validation +// form params check + +// add signature only if form_state is empty + $('#forms').append('

form handler check with and without fieldset

') + var el = $('#form5') + var defaults = { + select1: 'a', + select2: '', + } + + var formChanges = function(params, attribute, attributes, classname, form, ui) { + console.log('FROM', form) + if (params['select1'] === 'b') { + console.log('lala', params) + var item = { + name: 'select2', + display: 'Select2', + tag: 'select', + null: true, + options: { 1:'1', 2:'2', 3:'3' }, + default: 3, + }; + var newElement = ui.formGenItem( item, classname, form ) + form.find('[name="select2"]').closest('.form-group').replaceWith( newElement ) + } + if (params['select1'] === 'a') { + console.log('lala', params) + var item = { + name: 'select2', + display: 'Select2', + tag: 'select', + null: true, + options: { 1:'1', 2:'2', 3:'3' }, + default: 1, + }; + var newElement = ui.formGenItem( item, classname, form ) + form.find('[name="select2"]').closest('.form-group').replaceWith( newElement ) + } + } + + new App.ControllerForm({ + el: el, + model: { + configure_attributes: [ + { name: 'select1', display: 'Select1', tag: 'select', null: true, options: { a: 'a', b: 'b' }, default: 'b'}, + { name: 'select2', display: 'Select2', tag: 'select', null: true, options: { 1:'1', 2:'2', 3:'3' }, default: 2 }, + ], + }, + params: defaults, + handlers: [ + formChanges, + ], + //noFieldset: true, + }); + equal( el.find('[name="select1"]').val(), 'a', 'check select1 value') + equal( el.find('[name="select1"]').prop('required'), false, 'check select1 required') + + equal( el.find('[name="select2"]').val(), '1', 'check select2 value') + equal( el.find('[name="select2"]').prop('required'), false, 'check select2 required') + + var params = App.ControllerForm.params( el ) + var test_params = { + select1: 'a', + select2: '1', + } + deepEqual( params, test_params, 'form param check' ); + el.find('[name="select1"]').val('b') + el.find('[name="select1"]').trigger('change') + params = App.ControllerForm.params( el ) + test_params = { + select1: 'b', + select2: '3', + } + deepEqual( params, test_params, 'form param check' ); + el.find('[name="select1"]').val('a') + el.find('[name="select1"]').trigger('change') + params = App.ControllerForm.params( el ) + test_params = { + select1: 'a', + select2: '1', + } + deepEqual( params, test_params, 'form param check' ); + + // test with noFieldset + el.empty() + new App.ControllerForm({ + el: el, + model: { + configure_attributes: [ + { name: 'select1', display: 'Select1', tag: 'select', null: true, options: { a: 'a', b: 'b' }, default: 'b'}, + { name: 'select2', display: 'Select2', tag: 'select', null: true, options: { 1:'1', 2:'2', 3:'3' }, default: 2 }, + ], + }, + params: defaults, + handlers: [ + formChanges, + ], + noFieldset: true, + }); + equal( el.find('[name="select1"]').val(), 'a', 'check select1 value') + equal( el.find('[name="select1"]').prop('required'), false, 'check select1 required') + + equal( el.find('[name="select2"]').val(), '1', 'check select2 value') + equal( el.find('[name="select2"]').prop('required'), false, 'check select2 required') + + var params = App.ControllerForm.params( el ) + var test_params = { + select1: 'a', + select2: '1', + } + deepEqual( params, test_params, 'form param check' ); + el.find('[name="select1"]').val('b') + el.find('[name="select1"]').trigger('change') + params = App.ControllerForm.params( el ) + test_params = { + select1: 'b', + select2: '3', + } + deepEqual( params, test_params, 'form param check' ); + el.find('[name="select1"]').val('a') + el.find('[name="select1"]').trigger('change') + params = App.ControllerForm.params( el ) + test_params = { + select1: 'a', + select2: '1', + } + deepEqual( params, test_params, 'form param check' ); + +}); + test( "form postmaster filter", function() { // check match area @@ -455,8 +607,8 @@ test( "form postmaster filter", function() { }, ] ) - $('#forms').append('

form postmaster filter

') - var el = $('#form5') + $('#forms').append('

form postmaster filter

') + var el = $('#form6') var defaults = { input2: 'some name', match: { @@ -493,8 +645,8 @@ test( "form postmaster filter", function() { set: { 'x-zammad-ticket-owner': 'owner', 'x-zammad-ticket-customer': 'customer', - 'x-zammad-ticket-priority_id': "2", - 'x-zammad-ticket-group_id': "1", + 'x-zammad-ticket-priority_id': '2', + 'x-zammad-ticket-group_id': '1', }, }; deepEqual( params, test_params, 'form param check' ); @@ -512,7 +664,7 @@ test( "form postmaster filter", function() { }, set: { 'x-zammad-ticket-owner': 'owner', - 'x-zammad-ticket-group_id': "1", + 'x-zammad-ticket-group_id': '1', }, }; deepEqual( params, test_params, 'form param check' ); @@ -524,8 +676,8 @@ test( "form postmaster filter", function() { }); test( "form selector", function() { - $('#forms').append('

form selector

') - var el = $('#form6') + $('#forms').append('

form selector

') + var el = $('#form7') var defaults = { input2: 'some name66', } @@ -555,10 +707,12 @@ test( "form selector", function() { }); test( "form required_if + shown_if", function() { - $('#forms').append('

form required_if + shown_if

') - var el = $('#form7') + $('#forms').append('

form required_if + shown_if

') + var el = $('#form8') var defaults = { input2: 'some name66', + input3: 'some name77', + input4: 'some name88', } new App.ControllerForm({ el: el, @@ -566,6 +720,8 @@ test( "form required_if + shown_if", function() { configure_attributes: [ { name: 'input1', display: 'Input1', tag: 'input', type: 'text', limit: 100, null: true, default: 'some not used default33' }, { name: 'input2', display: 'Input2', tag: 'input', type: 'text', limit: 100, null: true, default: 'some used default', required_if: { active: true }, shown_if: { active: true } }, + { name: 'input3', display: 'Input3', tag: 'input', type: 'text', limit: 100, null: true, default: 'some used default', required_if: { active: [true,false] }, shown_if: { active: [true,false] } }, + { name: 'input4', display: 'Input4', tag: 'input', type: 'text', limit: 100, null: true, default: 'some used default', required_if: { active: [55,66] }, shown_if: { active: [55,66] } }, { name: 'active', display: 'Active', tag: 'boolean', type: 'boolean', 'default': true, null: false }, ], }, @@ -574,33 +730,49 @@ test( "form required_if + shown_if", function() { test_params = { input1: "some not used default33", input2: "some name66", - active: true + input3: "some name77", + input4: undefined, + active: true, }; params = App.ControllerForm.params( el ) deepEqual( params, test_params, 'form param check via $("#form")' ); equal( el.find('[name="input2"]').attr('required'), 'required', 'check required attribute of input2 ') equal( el.find('[name="input2"]').is(":visible"), true, 'check visible attribute of input2 ') + equal( el.find('[name="input3"]').attr('required'), 'required', 'check required attribute of input3 ') + equal( el.find('[name="input3"]').is(":visible"), true, 'check visible attribute of input3 ') + equal( el.find('[name="input4"]').is(":visible"), false, 'check visible attribute of input4 ') - el.find('[name="active"]').val('{boolean}::false').trigger('change') + + el.find('[name="{boolean}active"]').val('false').trigger('change') test_params = { input1: "some not used default33", - active: false + input2: undefined, + input3: undefined, + input4: undefined, + active: false, }; params = App.ControllerForm.params( el ) deepEqual( params, test_params, 'form param check via $("#form")' ); equal( el.find('[name="input2"]').attr('required'), undefined, 'check required attribute of input2 ') equal( el.find('[name="input2"]').is(":visible"), false, 'check visible attribute of input2 ') + equal( el.find('[name="input3"]').is(":visible"), false, 'check visible attribute of input3 ') + equal( el.find('[name="input4"]').is(":visible"), false, 'check visible attribute of input4 ') - el.find('[name="active"]').val('{boolean}::true').trigger('change') + el.find('[name="{boolean}active"]').val('true').trigger('change') test_params = { input1: "some not used default33", input2: "some name66", - active: true + input3: "some name77", + input4: undefined, + active: true, }; params = App.ControllerForm.params( el ) deepEqual( params, test_params, 'form param check via $("#form")' ); equal( el.find('[name="input2"]').attr('required'), 'required', 'check required attribute of input2 ') equal( el.find('[name="input2"]').is(":visible"), true, 'check visible attribute of input2 ') + equal( el.find('[name="input3"]').attr('required'), 'required', 'check required attribute of input3 ') + equal( el.find('[name="input3"]').is(":visible"), true, 'check visible attribute of input3 ') + equal( el.find('[name="input4"]').is(":visible"), false, 'check visible attribute of input4 ') }); \ No newline at end of file diff --git a/test/browser/aab_unit_test.rb b/test/browser/aab_unit_test.rb index a702c571a..cf8609337 100644 --- a/test/browser/aab_unit_test.rb +++ b/test/browser/aab_unit_test.rb @@ -112,6 +112,28 @@ class AAbUnitTest < TestCase ] browser_single_test(tests) end + def test_form_validation + tests = [ + { + :name => 'start', + :instance => browser_instance, + :url => browser_url + '/tests-form-validation', + :action => [ + { + :execute => 'wait', + :value => 8, + }, + { + :execute => 'match', + :css => '.result .failed', + :value => '0', + :match_result => true, + }, + ], + }, + ] + browser_single_test(tests) + end def test_table tests = [ {