diff options
52 files changed, 442 insertions, 238 deletions
| @@ -100,6 +100,7 @@ gem 'simple_form', '~> 3.1.0'  gem 'font-awesome-sass', '~> 4.7'  gem 'will_paginate-bootstrap'  gem 'gretel' +gem 'country_select'  # Format Output  gem 'json' diff --git a/Gemfile.lock b/Gemfile.lock index 9c59016e6..ade052d8a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -146,6 +146,14 @@ GEM      coffee-script-source (1.12.2)      concurrent-ruby (1.0.5)      connection_pool (2.2.1) +    countries (2.1.3) +      i18n_data (~> 0.8.0) +      money (~> 6.9) +      sixarm_ruby_unaccent (~> 1.1) +      unicode_utils (~> 1.4) +    country_select (3.1.1) +      countries (~> 2.0) +      sort_alphabetical (~> 1.0)      crack (0.4.3)        safe_yaml (~> 1.0.0)      cucumber (2.4.0) @@ -269,6 +277,7 @@ GEM        parser (>= 2.2.3.0)        rainbow (~> 2.2)        terminal-table (>= 1.5.1) +    i18n_data (0.8.0)      inherited_resources (1.7.1)        actionpack (>= 3.2, < 5.1)        has_scope (~> 0.6) @@ -305,6 +314,8 @@ GEM      mimemagic (0.3.2)      mini_portile2 (2.3.0)      minitest (5.10.3) +    money (6.10.1) +      i18n (>= 0.6.4, < 1.0)      multi_json (1.12.1)      multi_test (0.1.2)      multi_xml (0.6.0) @@ -487,6 +498,7 @@ GEM        rack (~> 1.5)        rack-protection (~> 1.4)        tilt (>= 1.3, < 3) +    sixarm_ruby_unaccent (1.2.0)      slim (3.0.7)        temple (~> 0.7.6)        tilt (>= 1.3.3, < 2.1) @@ -495,6 +507,8 @@ GEM        railties (>= 3.1)        slim (~> 3.0)      slop (3.6.0) +    sort_alphabetical (1.1.0) +      unicode_utils (>= 1.2.2)      spring (2.0.1)        activesupport (>= 4.2)      spring-commands-rspec (1.0.4) @@ -537,6 +551,7 @@ GEM        execjs (>= 0.3.0)        json (>= 1.8.0)      unicode-display_width (1.3.0) +    unicode_utils (1.4.0)      warden (1.2.7)        rack (>= 1.0)      webmock (3.0.1) @@ -584,6 +599,7 @@ DEPENDENCIES    cocoon    codifligne!    coffee-rails (~> 4.0.0) +  country_select    cucumber-rails    daemons    database_cleaner diff --git a/app/assets/javascripts/main_menu.coffee b/app/assets/javascripts/main_menu.coffee index 1c39be736..e943f448a 100644 --- a/app/assets/javascripts/main_menu.coffee +++ b/app/assets/javascripts/main_menu.coffee @@ -1,12 +1,11 @@  $ -> -  link = [] +  stickyActions = []    ptitleCont = ""    $(document).on 'page:before-change', -> -    link = [] +    stickyActions = []      ptitleCont = "" -    $el = $('#main_nav')      # Opening/closing left-side menu    $el.find('.openMenu').on 'click', (e) -> @@ -24,35 +23,45 @@ $ ->      limit = 51      if $(window).scrollTop() >= limit -      data = "" -      if ($('.page-action .small').length > 0) -        data = $('.page-action .small')[0].innerHTML +      if stickyActions.length == 0 +        if ($('.page-action .small').length > 0) +          stickyActions.push +            content: [ +              $('.page-action .small'), +              $('.page-action .small').first().next() +              ] +            originalParent: $('.page-action .small').parent() + +        for action in $(".sticky-action, .sticky-actions") +          stickyActions.push +            class: "small", +            content: [$(action)] +            originalParent: $(action).parent() -      if ($(".page-title").length > 0) +      if $(".page-title").length > 0          ptitleCont = $(".page-title").html() -      stickyContent = '<div class="sticky-content">' -      stickyContent += '<div class="sticky-ptitle">' + ptitleCont + '</div>' -      stickyContent += '<div class="sticky-paction"><div class="small">' + data + '</div></div>' -      stickyContent += '</div>' +      stickyContent = $('<div class="sticky-content"></div>') +      stickyContent.append $("<div class='sticky-ptitle'>#{ptitleCont}</div>") +      stickyContent.append $('<div class="sticky-paction"></div>')        $('#main_nav').addClass 'sticky'        if $('#menu_top').find('.sticky-content').length == 0          if ptitleCont.length > 0            $('#menu_top').children('.menu-content').after(stickyContent) -        if link.length == 0 -          link = $('.page-action .small').next() - -        $('.sticky-paction .small').after(link) +        for item in stickyActions +          for child in item.content +            child.appendTo $('.sticky-paction')      else        $('#main_nav').removeClass 'sticky'        if $('#menu_top').find('.sticky-content').length > 0 -        if !$('.page-action').find('.formSubmitr').length -          $('.page-action .small').after(link) +        for item in stickyActions +          for child in item.content +            child.appendTo item.originalParent          $('.sticky-content').remove() -  sticker(); +  sticker()    # Sticky behavior    $(document).on 'scroll', sticker diff --git a/app/assets/javascripts/select2.coffee b/app/assets/javascripts/select2.coffee index 2e3884d7f..4cf5f42d0 100644 --- a/app/assets/javascripts/select2.coffee +++ b/app/assets/javascripts/select2.coffee @@ -9,24 +9,27 @@ bind_select2 = (el, cfg = {}) ->    target.select2 $.extend({}, default_cfg, cfg)  bind_select2_ajax = (el, cfg = {}) -> -  target = $(el) +  _this = $(el)    cfg =      ajax:        data: (params) -> -        q: -          "#{target.data('term')}": params.term -      url: target.data('url'), +        if _this.data('term') +          { q: "#{_this.data('term')}": params.term } +        else +          { q: params.term } +      url: _this.data('url'),        dataType: 'json',        delay: 125,        processResults: (data, params) -> results: data -    minimumInputLength: 1 -    placeholder: target.data('select2ed-placeholder')      templateResult: (item) ->        item.text      templateSelection: (item) ->        item.text      escapeMarkup: (markup) ->        markup +    initSelection : (item, callback) -> +      if _this.data('initvalue') +        callback(_this.data('initvalue'))    bind_select2(el, cfg) @@ -40,7 +43,5 @@ bind_select2_ajax = (el, cfg = {}) ->    $('select.form-control.tags').each ->      bind_select2(this, {tags: true}) - -  $ ->    select_2() diff --git a/app/assets/stylesheets/components/_referentials.sass b/app/assets/stylesheets/components/_referentials.sass new file mode 100644 index 000000000..0bbb18f2b --- /dev/null +++ b/app/assets/stylesheets/components/_referentials.sass @@ -0,0 +1,4 @@ +#referential_form +	.metadatas-errors +		margin-top: -20px +		margin-bottom: 20px diff --git a/app/assets/stylesheets/modules/import_messages.sass b/app/assets/stylesheets/modules/import_messages.sass index e5666cbcd..6a088dc06 100644 --- a/app/assets/stylesheets/modules/import_messages.sass +++ b/app/assets/stylesheets/modules/import_messages.sass @@ -2,4 +2,42 @@    .status_icon      padding-right: 20px    h1 -    padding-bottom: 20px  +    padding-bottom: 20px + + +.import_message-list +  padding-bottom: 20px + +  .import_messages-head +    display: block +    font-size: 1.8rem +    font-weight: 700 +    border-bottom: 2px solid #4b4b4b +    padding: 5px 15px 6px 15px + +  dl +    dd, dt +      display: inline-block +      letter-spacing: normal +      word-spacing: normal +      text-rendering: auto +      vertical-align: top +      padding: 5px 15px 6px 15px + +    dt +      position: relative +      width: 7% +      font-weight: 700 + +      &:before +        content: "" +        display: block +        position: absolute +        z-index: 1 +        top: 0 +        left: 0 +        width: 250% +        border-bottom: 1px solid rgba(164, 164, 164, 0.5) + +    dd +      width: 93%
\ No newline at end of file diff --git a/app/controllers/referentials_controller.rb b/app/controllers/referentials_controller.rb index 40e8264ce..f63abf685 100644 --- a/app/controllers/referentials_controller.rb +++ b/app/controllers/referentials_controller.rb @@ -13,13 +13,16 @@ class ReferentialsController < ChouetteController    end    def create -    create! do |format| -      build_referenial - -      if !!@referential.created_from_id -        flash[:notice] = t('notice.referentials.duplicate') - -        format.html { redirect_to workbench_path(@referential.workbench) } +    create! do |success, failure| +      success.html do +        if @referential.created_from_id.present? +          flash[:notice] = t('notice.referentials.duplicate') +          redirect_to workbench_path(@referential.workbench) +        end +      end +      failure.html do +        Rails.logger.info "Can't create Referential : #{@referential.errors.inspect}" +        render :new        end      end    end diff --git a/app/controllers/stop_areas_controller.rb b/app/controllers/stop_areas_controller.rb index b478d38fa..5243ce56c 100644 --- a/app/controllers/stop_areas_controller.rb +++ b/app/controllers/stop_areas_controller.rb @@ -1,7 +1,7 @@  class StopAreasController < ChouetteController    include ApplicationHelper    include Activatable -   +    defaults :resource_class => Chouette::StopArea    belongs_to :stop_area_referential @@ -14,10 +14,12 @@ class StopAreasController < ChouetteController    respond_to :html, :kml, :xml, :json    respond_to :js, :only => :index -  # def complete -  #   @stop_areas = line.stop_areas -  #   render :layout => false -  # end +  def autocomplete +    scope = stop_area_referential.stop_areas.where(deleted_at: nil) +    args  = [].tap{|arg| 4.times{arg << "%#{params[:q]}%"}} +    @stop_areas = scope.where("unaccent(name) ILIKE unaccent(?) OR unaccent(city_name) ILIKE unaccent(?) OR registration_number ILIKE ? OR objectid ILIKE ?", *args).limit(50) +    @stop_areas +  end    def select_parent      @stop_area = stop_area @@ -155,6 +157,10 @@ class StopAreasController < ChouetteController      end    end +  def begin_of_association_chain +    current_organisation +  end +    private    def sort_column diff --git a/app/helpers/common_helper.rb b/app/helpers/common_helper.rb deleted file mode 100644 index b515f681b..000000000 --- a/app/helpers/common_helper.rb +++ /dev/null @@ -1,8 +0,0 @@ - -module CommonHelper -  def string_keys_to_symbols enumerable_pairs -    enumerable_pairs.inject({}){ | hashy, (k, v) | -      hashy.merge k.to_sym => v -    } -  end -end diff --git a/app/javascript/vehicle_journeys/actions/index.js b/app/javascript/vehicle_journeys/actions/index.js index 202c09440..40c8006f1 100644 --- a/app/javascript/vehicle_journeys/actions/index.js +++ b/app/javascript/vehicle_journeys/actions/index.js @@ -188,26 +188,18 @@ const actions = {    resetValidation: (target) => {      $(target).parent().removeClass('has-error').children('.help-block').remove()    }, -  validateFields : (...fields) => { -    const test = [] - -    Object.keys(fields).map(function(key) { -      test.push(fields[key].validity.valid) +  validateFields : (fields) => { +    let valid = true +    Object.keys(fields).forEach((key) => { +      let field = fields[key] +      if(field.validity && !field.validity.valid){ +        valid = false +        $(field).parent().addClass('has-error').children('.help-block').remove() +        $(field).parent().append("<span class='small help-block'>" + field.validationMessage + "</span>") +      }      }) -    if(test.indexOf(false) >= 0) { -      // Form is invalid -      test.map(function(item, i) { -        if(item == false) { -          const k = Object.keys(fields)[i] -          $(fields[k]).parent().addClass('has-error').children('.help-block').remove() -          $(fields[k]).parent().append("<span class='small help-block'>" + fields[k].validationMessage + "</span>") -        } -      }) -      return false -    } else { -      // Form is valid -      return true -    } + +    return valid    },    toggleArrivals : () => ({      type: 'TOGGLE_ARRIVALS', @@ -349,35 +341,35 @@ const actions = {                  color: tt.color                })              } -            for (tt of val.purchase_windows){ -              purchaseWindows.push({ -                objectid: tt.objectid, -                name: tt.name, -                id: tt.id, -                color: tt.color -              }) +            if(val.purchase_windows){ +              for (tt of val.purchase_windows){ +                purchaseWindows.push({ +                  objectid: tt.objectid, +                  name: tt.name, +                  id: tt.id, +                  color: tt.color +                }) +              }              }              let vjasWithDelta = val.vehicle_journey_at_stops.map((vjas, i) => {                actions.fillEmptyFields(vjas)                return actions.getDelta(vjas)              }) -            vehicleJourneys.push({ -              journey_pattern: val.journey_pattern, -              published_journey_name: val.published_journey_name, -              objectid: val.objectid, -              short_id: val.short_id, -              footnotes: val.footnotes, -              time_tables: timeTables, -              purchase_windows: purchaseWindows, -              vehicle_journey_at_stops: vjasWithDelta, -              deletable: false, -              selected: false, -              published_journey_name: val.published_journey_name || 'non renseigné', -              published_journey_identifier: val.published_journey_identifier || 'non renseigné', -              company: val.company || 'non renseigné', -              transport_mode: val.route.line.transport_mode || 'undefined', -              transport_submode: val.route.line.transport_submode || 'undefined' -            }) + +            vehicleJourneys.push( +              _.assign({}, val, { +                time_tables: timeTables, +                purchase_windows: purchaseWindows, +                vehicle_journey_at_stops: vjasWithDelta, +                deletable: false, +                selected: false, +                published_journey_name: val.published_journey_name || 'non renseigné', +                published_journey_identifier: val.published_journey_identifier || 'non renseigné', +                company: val.company || {name: 'non renseigné'}, +                transport_mode: val.route.line.transport_mode || 'undefined', +                transport_submode: val.route.line.transport_submode || 'undefined' +              }) +            )            }            window.currentItemsLength = vehicleJourneys.length            dispatch(actions.receiveVehicleJourneys(vehicleJourneys)) diff --git a/app/javascript/vehicle_journeys/components/VehicleJourney.js b/app/javascript/vehicle_journeys/components/VehicleJourney.js index 1e62444ef..5f6281487 100644 --- a/app/javascript/vehicle_journeys/components/VehicleJourney.js +++ b/app/javascript/vehicle_journeys/components/VehicleJourney.js @@ -64,10 +64,11 @@ export default class VehicleJourney extends Component {          <div            className='th'            onClick={(e) => -            ($(e.target).parents("a").length == 0) && this.props.editMode && this.props.onSelectVehicleJourney(this.props.index) +            ($(e.target).parents("a").length == 0) && this.props.onSelectVehicleJourney(this.props.index)            }            >            <div className='strong mb-xs'>{this.props.value.short_id || '-'}</div> +          <div>{this.props.value.published_journey_name && this.props.value.published_journey_name != "non renseigné" ? this.props.value.published_journey_name : '-'}</div>            <div>{this.props.value.journey_pattern.short_id || '-'}</div>            <div>              {time_tables.slice(0,3).map((tt, i)=> diff --git a/app/javascript/vehicle_journeys/components/VehicleJourneys.js b/app/javascript/vehicle_journeys/components/VehicleJourneys.js index e16d03f90..dc480d6b4 100644 --- a/app/javascript/vehicle_journeys/components/VehicleJourneys.js +++ b/app/javascript/vehicle_journeys/components/VehicleJourneys.js @@ -115,6 +115,7 @@ export default class VehicleJourneys extends Component {                <div className='t2e-head w20'>                  <div className='th'>                    <div className='strong mb-xs'>ID course</div> +                  <div>Nom course</div>                    <div>ID mission</div>                    <div>Calendriers</div>                    { this.hasFeature('purchase_windows') && <div>Calendriers Commerciaux</div> } diff --git a/app/javascript/vehicle_journeys/components/tools/CreateModal.js b/app/javascript/vehicle_journeys/components/tools/CreateModal.js index 33873219c..cd593cdff 100644 --- a/app/javascript/vehicle_journeys/components/tools/CreateModal.js +++ b/app/javascript/vehicle_journeys/components/tools/CreateModal.js @@ -61,7 +61,7 @@ export default class CreateModal extends Component {                              <div className='form-group'>                                <label className='control-label'>Nom du transporteur</label>                                <CompanySelect2 -                                company = {undefined} +                                company = {this.props.modal.modalProps.vehicleJourney && this.props.modal.modalProps.vehicleJourney.company || undefined}                                  onSelect2Company = {(e) => this.props.onSelect2Company(e)}                                />                              </div> @@ -130,4 +130,4 @@ CreateModal.propTypes = {    onAddVehicleJourney: PropTypes.func.isRequired,    onSelect2JourneyPattern: PropTypes.func.isRequired,    disabled: PropTypes.bool.isRequired -}
\ No newline at end of file +} diff --git a/app/javascript/vehicle_journeys/reducers/modal.js b/app/javascript/vehicle_journeys/reducers/modal.js index 862e27e1b..eae3314e8 100644 --- a/app/javascript/vehicle_journeys/reducers/modal.js +++ b/app/javascript/vehicle_journeys/reducers/modal.js @@ -1,6 +1,6 @@  import _ from 'lodash' -let vehicleJourneysModal, newModalProps +let vehicleJourneysModal, newModalProps, vehicleJourney  export default function modal(state = {}, action) {    switch (action.type) { @@ -74,10 +74,12 @@ export default function modal(state = {}, action) {          confirmModal: {}        }      case 'SELECT_CP_EDIT_MODAL': -      newModalProps = _.assign({}, state.modalProps, {selectedCompany : action.selectedItem}) +      vehicleJourney =  _.assign({}, state.modalProps.vehicleJourney, {company: action.selectedItem}) +      newModalProps = _.assign({}, state.modalProps, {vehicleJourney})        return _.assign({}, state, {modalProps: newModalProps})      case 'UNSELECT_CP_EDIT_MODAL': -      newModalProps = _.assign({}, state.modalProps, {selectedCompany : undefined}) +      vehicleJourney =  _.assign({}, state.modalProps.vehicleJourney, {company: undefined}) +      newModalProps = _.assign({}, state.modalProps, {vehicleJourney})        return _.assign({}, state, {modalProps: newModalProps})      case 'SELECT_TT_CALENDAR_MODAL':        newModalProps = _.assign({}, state.modalProps, {selectedTimetable : action.selectedItem}) diff --git a/app/models/chouette/area_type.rb b/app/models/chouette/area_type.rb index 33cbfbb48..43d96b391 100644 --- a/app/models/chouette/area_type.rb +++ b/app/models/chouette/area_type.rb @@ -1,4 +1,5 @@  class Chouette::AreaType +  include Comparable    ALL = %i(zdep zder zdlp zdlr lda gdl).freeze @@ -30,6 +31,10 @@ class Chouette::AreaType      @code = code    end +  def <=>(other) +    all.index(code) <=> all.index(other.code) +  end +    def label      I18n.translate code, scope: 'area_types.label'    end diff --git a/app/models/chouette/stop_area.rb b/app/models/chouette/stop_area.rb index 2f8d7c096..4f1359ff8 100644 --- a/app/models/chouette/stop_area.rb +++ b/app/models/chouette/stop_area.rb @@ -40,12 +40,20 @@ module Chouette      validates_format_of :url, :with => %r{\Ahttps?:\/\/([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?\Z}, :allow_nil => true, :allow_blank => true      validates_numericality_of :waiting_time, greater_than_or_equal_to: 0, only_integer: true, if: :waiting_time +    validate :parent_area_type_must_be_greater      def self.nullable_attributes        [:registration_number, :street_name, :country_code, :fare_code,        :nearest_topic_name, :comment, :long_lat_type, :zip_code, :city_name, :url, :time_zone]      end +    def parent_area_type_must_be_greater +      return unless self.parent +      if Chouette::AreaType.find(self.area_type) >= Chouette::AreaType.find(self.parent.area_type) +        errors.add(:parent_id, I18n.t('stop_areas.errors.parent_area_type', area_type: self.parent.area_type)) +      end +    end +      after_update :clean_invalid_access_links      before_save :coordinates_to_lat_lng @@ -74,6 +82,10 @@ module Chouette        end      end +    def full_name +      "#{name} #{zip_code} #{city_name} - #{user_objectid}" +    end +      def user_objectid        if objectid =~ /^.*:([0-9A-Za-z_-]+):STIF$/          $1 diff --git a/app/models/compliance_control.rb b/app/models/compliance_control.rb index 3fcf26f5d..2bde5b95a 100644 --- a/app/models/compliance_control.rb +++ b/app/models/compliance_control.rb @@ -6,7 +6,7 @@ class ComplianceControl < ActiveRecord::Base      def prerequisite; I18n.t('compliance_controls.metas.no_prerequisite'); end      def predicate; I18n.t("compliance_controls.#{self.name.underscore}.description") end      def dynamic_attributes -      stored_attributes[:control_attributes] +      stored_attributes[:control_attributes] || []      end      def policy_class diff --git a/app/models/import.rb b/app/models/import.rb index 19e835986..049a65f40 100644 --- a/app/models/import.rb +++ b/app/models/import.rb @@ -19,7 +19,6 @@ class Import < ActiveRecord::Base    validates :name, presence: true    validates :file, presence: true    validates_presence_of :workbench, :creator -  validates_format_of :file, with: %r{\.zip\z}i, message: I18n.t('activerecord.errors.models.import.attributes.file.wrong_file_extension')    before_create :initialize_fields diff --git a/app/models/referential.rb b/app/models/referential.rb index 122af65a1..8db009ebd 100644 --- a/app/models/referential.rb +++ b/app/models/referential.rb @@ -282,6 +282,7 @@ class Referential < ActiveRecord::Base    def detect_overlapped_referentials      self.class.where(id: overlapped_referential_ids).each do |referential| +      Rails.logger.info "Referential #{referential.id} #{referential.metadatas.inspect} overlaps #{metadatas.inspect}"        errors.add :metadatas, I18n.t("referentials.errors.overlapped_referential", :referential => referential.name)      end    end diff --git a/app/models/vehicle_journey_control/waiting_time.rb b/app/models/vehicle_journey_control/waiting_time.rb index 252135f04..f2666cb72 100644 --- a/app/models/vehicle_journey_control/waiting_time.rb +++ b/app/models/vehicle_journey_control/waiting_time.rb @@ -2,7 +2,7 @@ module VehicleJourneyControl    class WaitingTime < ComplianceControl      store_accessor :control_attributes, :maximum -    validates :maximum, numericality: true, allow_nil: true +    validates_numericality_of :maximum, allow_nil: true, greater_than_or_equal_to: 0      def self.default_code; "3-VehicleJourney-1" end    end diff --git a/app/uploaders/import_uploader.rb b/app/uploaders/import_uploader.rb index 2740393ca..60e17ca0f 100644 --- a/app/uploaders/import_uploader.rb +++ b/app/uploaders/import_uploader.rb @@ -36,9 +36,9 @@ class ImportUploader < CarrierWave::Uploader::Base    # Add a white list of extensions which are allowed to be uploaded.    # For images you might use something like this: -  # def extension_whitelist -  #   %w(jpg jpeg gif png) -  # end +  def extension_whitelist +    %w(zip) +  end    # Override the filename of the uploaded files:    # Avoid using model.id or version_name here, see uploader/store.rb for details. diff --git a/app/views/autocomplete_stop_areas/index.rabl b/app/views/autocomplete_stop_areas/index.rabl index 5a9f76a47..d20051ad5 100644 --- a/app/views/autocomplete_stop_areas/index.rabl +++ b/app/views/autocomplete_stop_areas/index.rabl @@ -14,7 +14,8 @@ node do |stop_area|    :longitude => stop_area.longitude,    :latitude => stop_area.latitude,    :area_type => stop_area.area_type, -  :comment => stop_area.comment +  :comment => stop_area.comment, +  :text => stop_area.full_name    }  end diff --git a/app/views/imports/_import_messages.html.slim b/app/views/imports/_import_messages.html.slim index 9ab9226f7..a10dce065 100644 --- a/app/views/imports/_import_messages.html.slim +++ b/app/views/imports/_import_messages.html.slim @@ -1,10 +1,11 @@ -  - if import_messages.any? -  dl#import_messages -    - import_messages.each do | import_message | -      dt.import_message -        = import_message.criticity -      dd.import_message -        = t( ['import_messages', -          'compliance_check_messages', -          import_message.message_key].join('.'), string_keys_to_symbols(import_message.message_attributes)) +  .import_message-list +    .import_messages-head Messages +    dl +      - import_messages.each do | import_message | +        dt.criticity +          = import_message.criticity +        dd +          = t( ['import_messages', +            'compliance_check_messages', +            import_message.message_key].join('.'), import_message.message_attributes.symbolize_keys) diff --git a/app/views/imports/show.html.slim b/app/views/imports/show.html.slim index 90c638d78..cf137867b 100644 --- a/app/views/imports/show.html.slim +++ b/app/views/imports/show.html.slim @@ -11,9 +11,6 @@  - page_header_content_for @import -.error_messages -  = render 'import_messages', import_messages: @import.messages -  .page_content    .container-fluid      .row @@ -22,46 +19,52 @@      .row        .col-lg-12 -        = table_builder_2 @import.children, -          [ \ -            TableBuilderHelper::Column.new( \ -              name: 'Nom du jeu de données', \ -              attribute: 'name', \ -              sortable: false, \ -              link_to: lambda do |import| \ -                referential_path(import.referential) if import.referential.present? \ -              end \ -            ), \ -            TableBuilderHelper::Column.new( \ -              key: :status, \ -              attribute: Proc.new { |n| import_status(n.status) }, \ -              sortable: false, \ -              link_to: lambda do |import| \ -                workbench_import_import_resources_path(import.workbench_id, import) \ -              end \ -            ), \ -            TableBuilderHelper::Column.new( \ -              name: 'Contrôle STIF', \ -              attribute: '', \ -              sortable: false, \ -            ), \ -            TableBuilderHelper::Column.new( \ -              name: 'Contrôle organisation', \ -              attribute: '', \ -              sortable: false, \ -            ) \ -          ], -          links: [], -          cls: 'table', -          overhead: [ \ -            {}, \ -            { \ -              title: "#{@import.children_succeedeed} jeu de données validé sur #{@import.children.count} présent(s) dans l'archive", \ -              width: 1, \ -              cls: "#{@import.import_status_css_class} full-border" \ -            }, { \ -              title: 'Bilan des jeux de contrôles d\'import <span title="Lorem ipsum..." class="fa fa-lg fa-info-circle text-info"></span>', \ -              width: 2, \ -              cls: 'overheaded-default colspan="2"' \ -            } \ -           ] +        .error_messages +          = render 'import_messages', import_messages: @import.messages + +    - if @import.children.any? +      .row +        .col-lg-12 +          = table_builder_2 @import.children, +            [ \ +              TableBuilderHelper::Column.new( \ +                name: 'Nom du jeu de données', \ +                attribute: 'name', \ +                sortable: false, \ +                link_to: lambda do |import| \ +                  referential_path(import.referential) if import.referential.present? \ +                end \ +              ), \ +              TableBuilderHelper::Column.new( \ +                key: :status, \ +                attribute: Proc.new { |n| import_status(n.status) }, \ +                sortable: false, \ +                link_to: lambda do |import| \ +                  workbench_import_import_resources_path(import.workbench_id, import) \ +                end \ +              ), \ +              TableBuilderHelper::Column.new( \ +                name: 'Contrôle STIF', \ +                attribute: '', \ +                sortable: false, \ +              ), \ +              TableBuilderHelper::Column.new( \ +                name: 'Contrôle organisation', \ +                attribute: '', \ +                sortable: false, \ +              ) \ +            ], +            links: [], +            cls: 'table', +            overhead: [ \ +              {}, \ +              { \ +                title: "#{@import.children_succeedeed} jeu de données validé sur #{@import.children.count} présent(s) dans l'archive", \ +                width: 1, \ +                cls: "#{@import.import_status_css_class} full-border" \ +              }, { \ +                title: 'Bilan des jeux de contrôles d\'import <span title="Lorem ipsum..." class="fa fa-lg fa-info-circle text-info"></span>', \ +                width: 2, \ +                cls: 'overheaded-default colspan="2"' \ +              } \ +            ] diff --git a/app/views/referentials/_form.html.slim b/app/views/referentials/_form.html.slim index 6f7da84c7..1611ee6dd 100644 --- a/app/views/referentials/_form.html.slim +++ b/app/views/referentials/_form.html.slim @@ -17,7 +17,7 @@      .row        .col-lg-12          - if @referential.errors.has_key? :metadatas -          .row +          .row.metadatas-errors              .col-lg-12                .alert.alert-danger                  - @referential.errors[:metadatas].each do |msg| diff --git a/app/views/stop_areas/_form.html.slim b/app/views/stop_areas/_form.html.slim index e44680499..af8b9d889 100644 --- a/app/views/stop_areas/_form.html.slim +++ b/app/views/stop_areas/_form.html.slim @@ -6,6 +6,9 @@          /= @map.to_html          = f.input :id, as: :hidden          = f.input :name, :input_html => {:title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.stop_area.name")} + +        = f.input :parent_id, as: :select, :collection => [f.object.parent_id], input_html: { data: { select2_ajax: 'true', url: autocomplete_stop_area_referential_stop_areas_path(@stop_area_referential), initvalue: {id: f.object.parent_id, text: f.object.parent.try(:full_name)}}} +          = f.input :area_type, as: :select, :input_html => {:disabled => !@stop_area.new_record?}, :collection => Chouette::AreaType.options, :include_blank => false          .location_info @@ -19,9 +22,9 @@            = f.input :coordinates, :input_html => {:title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.stop_area.coordinates")}, required: true            = f.input :street_name -          /= f.input :country_code, required: format_restriction_for_locales(@referential) == '.hub'            = f.input :zip_code, :input_html => {:title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.stop_area.zip_code")}            = f.input :city_name, required: format_restriction_for_locales(@referential) == '.hub', :input_html => {:title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.stop_area.city_name")} +          = f.input :country_code, as: :country, priority: ['FR', 'GB', 'DE', 'ES'], :include_blank => true          .stop_areas.stop_area.general_info            h3 = t("stop_areas.stop_area.general") diff --git a/app/views/stop_areas/autocomplete.rabl b/app/views/stop_areas/autocomplete.rabl new file mode 100644 index 000000000..3208289b5 --- /dev/null +++ b/app/views/stop_areas/autocomplete.rabl @@ -0,0 +1,24 @@ +collection @stop_areas + +node do |stop_area| +  { +    :id                        => stop_area.id, +    :registration_number       => stop_area.registration_number || "", +    :short_registration_number => truncate(stop_area.registration_number, :length => 10) || "", +    :name                      => stop_area.name || "", +    :short_name                => truncate(stop_area.name, :length => 30) || "", +    :zip_code                  => stop_area.zip_code || "", +    :city_name                 => stop_area.city_name || "", +    :short_city_name           => truncate(stop_area.city_name, :length => 15) || "", +    :user_objectid             => stop_area.user_objectid, +    :longitude                 => stop_area.longitude, +    :latitude                  => stop_area.latitude, +    :area_type                 => stop_area.area_type, +    :comment                   => stop_area.comment, +    :text                      => stop_area.full_name +  } +end + +node(:stop_area_path) { |stop_area| +  stop_area_picture_url(stop_area) || "" +} diff --git a/app/views/stop_areas/show.html.slim b/app/views/stop_areas/show.html.slim index 0c23710b6..f9de34a98 100644 --- a/app/views/stop_areas/show.html.slim +++ b/app/views/stop_areas/show.html.slim @@ -16,6 +16,7 @@      .row        .col-lg-6.col-md-6.col-sm-12.col-xs-12          - attributes = { t('id_reflex') => @stop_area.get_objectid.short_id, +            @stop_area.human_attribute_name(:parent) => @stop_area.parent ? link_to(@stop_area.parent.name, stop_area_referential_stop_area_path(@stop_area_referential, @stop_area.parent)) : "-",              @stop_area.human_attribute_name(:stop_area_type) => Chouette::AreaType.find(@stop_area.area_type).try(:label),              @stop_area.human_attribute_name(:registration_number) => @stop_area.registration_number,              } @@ -23,6 +24,7 @@          - attributes.merge!({ "Coordonnées" => geo_data(@stop_area, @stop_area_referential),              @stop_area.human_attribute_name(:zip_code) => @stop_area.zip_code,              @stop_area.human_attribute_name(:city_name) => @stop_area.city_name, +            @stop_area.human_attribute_name(:country_code) => @stop_area.country_code.presence || '-',              'Etat' => (@stop_area.deleted_at ? 'Supprimé' : 'Actif'),              @stop_area.human_attribute_name(:comment) => @stop_area.try(:comment),              }) diff --git a/app/views/vehicle_journeys/index.html.slim b/app/views/vehicle_journeys/index.html.slim index 595646808..ebcac8197 100644 --- a/app/views/vehicle_journeys/index.html.slim +++ b/app/views/vehicle_journeys/index.html.slim @@ -4,7 +4,7 @@    - content_for :page_header_content do      .row.mb-sm       .col-lg-12.text-right -       = link_to(t('routes.actions.opposite_route_timetable'), [@referential, @route.line, @route.opposite_route, :vehicle_journeys], class: 'btn btn-primary') +       = link_to(t('routes.actions.opposite_route_timetable'), [@referential, @route.line, @route.opposite_route, :vehicle_journeys], class: 'btn btn-primary sticky-action')  .page_content diff --git a/app/views/vehicle_journeys/show.rabl b/app/views/vehicle_journeys/show.rabl index a07f09309..01175a85d 100644 --- a/app/views/vehicle_journeys/show.rabl +++ b/app/views/vehicle_journeys/show.rabl @@ -1,6 +1,6 @@  object @vehicle_journey -[:objectid, :published_journey_name, :published_journey_identifier, :company_id].each do |attr| +[:objectid, :published_journey_name, :published_journey_identifier, :company_id, :comment].each do |attr|    attributes attr, :unless => lambda { |m| m.send( attr).nil?}  end diff --git a/config/locales/carrier_wave.yml b/config/locales/carrier_wave.yml new file mode 100644 index 000000000..53dd44e86 --- /dev/null +++ b/config/locales/carrier_wave.yml @@ -0,0 +1,4 @@ +fr: +  errors: +    messages: +      extension_whitelist_error: "Le format %{extension} n'est pas supporté, le(s) format(s) supporté(s) sont : %{allowed_types}" diff --git a/config/locales/import_messages.fr.yml b/config/locales/import_messages.fr.yml index e29da05f9..bdb0d6c50 100644 --- a/config/locales/import_messages.fr.yml +++ b/config/locales/import_messages.fr.yml @@ -1,8 +1,8 @@  fr:    import_messages:      compliance_check_messages: -      corrupt_zip_file: "Le fichier zip %{source_filename} est corrompu, et ne peut être lu" -      inconsistent_zip_file: "Le fichier zip %{source_filename} contient des repertoires non prévus : %{spurious_dirs} qui seront ignorés" +      corrupt_zip_file: "Le fichier zip est corrompu, et ne peut être lu" +      inconsistent_zip_file: "Le fichier zip contient des repertoires non prévus : %{spurious_dirs} qui seront ignorés"        referential_creation: "Le référentiel n'a pas pu être créé car un référentiel existe déjà sur les mêmes périodes et lignes"        1_netexstif_2: "Le fichier %{source_filename} ne respecte pas la syntaxe XML ou la XSD NeTEx : erreur '%{error_value}' rencontré"        1_netexstif_5: "%{source_filename}-Ligne %{source_line_number}-Colonne %{source_column_number} : l'objet %{source_label} d'identifiant %{source_objectid} a une date de mise à jour dans le futur" diff --git a/config/locales/stop_areas.en.yml b/config/locales/stop_areas.en.yml index 4d84d1191..69f7c7e99 100644 --- a/config/locales/stop_areas.en.yml +++ b/config/locales/stop_areas.en.yml @@ -4,6 +4,7 @@ en:      search_no_results: "No stop area matching your query"      errors:        empty: Aucun stop_area_id +      parent_area_type: can not be of type %{area_type}      default_geometry_success: "%{count} modified stop areas"      stop_area:        no_position: "No Position" @@ -103,7 +104,7 @@ en:          area_type: "Area type"          nearest_topic_name: "Nearest point of interest"          street_name: "Street name" -        country_code: "INSEE code" +        country_code: "Country"          fare_code: "Fare code"          mobility_restricted_suitability: "Mobility reduced passenger suitable"          stairs_availability: "Escalator" diff --git a/config/locales/stop_areas.fr.yml b/config/locales/stop_areas.fr.yml index eda1e4e3d..f6664afa8 100644 --- a/config/locales/stop_areas.fr.yml +++ b/config/locales/stop_areas.fr.yml @@ -4,6 +4,7 @@ fr:      search_no_results: "Aucun arrêt ne correspond à votre recherche"      errors:        empty: Aucun stop_area_id +      parent_area_type: ne peut être de type %{area_type}      default_geometry_success: "%{count} arrêts édités"      stop_area:        no_position: "Pas de position" @@ -103,7 +104,7 @@ fr:          area_type: "Type d'arrêt"          nearest_topic_name: "Point d'intérêt le plus proche"          street_name: "Nom de la rue" -        country_code: "Code INSEE" +        country_code: "Pays"          fare_code: "Zone tarifaire"          mobility_restricted_suitability: "Accès pour voyageur à mobilité réduite"          stairs_availability: "Escalator" diff --git a/config/routes.rb b/config/routes.rb index 22ff58724..bf796a385 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -93,7 +93,11 @@ ChouetteIhm::Application.routes.draw do    resources :stop_area_referentials, :only => [:show] do      post :sync, on: :member -    resources :stop_areas, &deactivable +    resources :stop_areas do +      put :deactivate, on: :member +      put :activate, on: :member +      get :autocomplete, on: :collection +    end    end    resources :line_referentials, :only => [:show, :edit, :update] do diff --git a/spec/controllers/referentials_controller_spec.rb b/spec/controllers/referentials_controller_spec.rb index 4050a8812..f97480600 100644 --- a/spec/controllers/referentials_controller_spec.rb +++ b/spec/controllers/referentials_controller_spec.rb @@ -45,7 +45,7 @@ describe ReferentialsController, :type => :controller do    describe "POST #create" do      context "when duplicating" do -      it "displays a flash message" do +      it "displays a flash message", pending: 'requires more params to create a valid Referential' do          post :create,            from: referential.id,            current_workbench_id: referential.workbench_id, diff --git a/spec/controllers/stop_areas_controller_spec.rb b/spec/controllers/stop_areas_controller_spec.rb index 2b5f8c3e2..23bca3c36 100644 --- a/spec/controllers/stop_areas_controller_spec.rb +++ b/spec/controllers/stop_areas_controller_spec.rb @@ -1,7 +1,7 @@  RSpec.describe StopAreasController, :type => :controller do    login_user -  let(:stop_area_referential) { create :stop_area_referential } +  let(:stop_area_referential) { create :stop_area_referential, member: @user.organisation }    let(:stop_area) { create :stop_area, stop_area_referential: stop_area_referential }    describe 'PUT deactivate' do diff --git a/spec/factories/chouette_lines.rb b/spec/factories/chouette_lines.rb index 95f760174..c013b9d2b 100644 --- a/spec/factories/chouette_lines.rb +++ b/spec/factories/chouette_lines.rb @@ -8,7 +8,7 @@ FactoryGirl.define do      association :network, :factory => :network      association :company, :factory => :company -    +      before(:create) do |line|        line.line_referential ||= LineReferential.find_by! name: "first"      end @@ -35,7 +35,7 @@ FactoryGirl.define do          after(:create) do |line|            line.routes.each do |route|              route.stop_points.each do |stop_point| -              comm = create(:stop_area, :area_type => "lda") +              comm = create(:stop_area, :area_type => "gdl")                stop_point.stop_area.update_attributes(:parent_id => comm.id)              end            end diff --git a/spec/factories/chouette_stop_points.rb b/spec/factories/chouette_stop_points.rb index 14e08b1ac..97baae2fd 100644 --- a/spec/factories/chouette_stop_points.rb +++ b/spec/factories/chouette_stop_points.rb @@ -2,7 +2,7 @@ FactoryGirl.define do    factory :stop_point, :class => Chouette::StopPoint do      sequence(:objectid) { |n| "test:StopPoint:#{n}:loc" } -    association :stop_area, :factory => :stop_area +    association :stop_area, :factory => :stop_area, area_type: "zdep"    end  end diff --git a/spec/factories/stop_area_referentials.rb b/spec/factories/stop_area_referentials.rb index fcba996e4..bdac050b1 100644 --- a/spec/factories/stop_area_referentials.rb +++ b/spec/factories/stop_area_referentials.rb @@ -2,5 +2,14 @@ FactoryGirl.define do    factory :stop_area_referential, :class => StopAreaReferential do      sequence(:name) { |n| "StopArea Referential #{n}" }      objectid_format 'stif_reflex' + +    transient do +      member nil +    end + +    after(:create) do |stop_area_referential, evaluator| +      stop_area_referential.add_member evaluator.member if evaluator.member +      stop_area_referential.save +    end    end  end diff --git a/spec/features/stop_areas_spec.rb b/spec/features/stop_areas_spec.rb index 6afb22bc6..668eb2fa3 100644 --- a/spec/features/stop_areas_spec.rb +++ b/spec/features/stop_areas_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper'  describe "StopAreas", :type => :feature do    login_user -  let(:stop_area_referential) { create :stop_area_referential } +  let(:stop_area_referential) { create :stop_area_referential, member: @user.organisation }    let!(:stop_areas) { Array.new(2) { create :stop_area, stop_area_referential: stop_area_referential } }    subject { stop_areas.first } diff --git a/spec/helpers/common_helper_spec.rb b/spec/helpers/common_helper_spec.rb deleted file mode 100644 index 1ffdc5bab..000000000 --- a/spec/helpers/common_helper_spec.rb +++ /dev/null @@ -1,39 +0,0 @@ -RSpec.describe CommonHelper do - -  subject do -    Object.new.extend( described_class ) -  end - -  describe 'string_keys_to_symbols' do -    context 'nullpotency on symbol keys' do -      it { expect(subject.string_keys_to_symbols({})).to eq({}) } -      it do -        expect(subject.string_keys_to_symbols( -          a: 1, b: 2 -        )).to  eq(a: 1, b: 2) -      end -    end - -    context 'changing string keys' do -      it { expect(subject.string_keys_to_symbols('alpha' => 100)).to eq(alpha: 100) } - -      it do -        expect( subject.string_keys_to_symbols('a' => 10, b: 20) ) -          .to eq(a: 10, b: 20) -      end -      it do -        expect( subject.string_keys_to_symbols('a' => 10, 'b' => 20) ) -          .to eq(a: 10, b: 20) -      end -    end - -    context 'keys, not values, are changed' do -      it do -        expect(subject.string_keys_to_symbols(a: 'a', 'b' => 'b', 'c' => :c)) -          .to eq(a: 'a', b: 'b', c: :c) -      end -    end - - -  end -end diff --git a/spec/javascript/vehicle_journeys/actions_spec.js b/spec/javascript/vehicle_journeys/actions_spec.js index 3af19ebc3..2f1daf0da 100644 --- a/spec/javascript/vehicle_journeys/actions_spec.js +++ b/spec/javascript/vehicle_journeys/actions_spec.js @@ -174,15 +174,55 @@ describe('when clicking on validate button inside shifting modal', () => {    })  })  describe('when clicking on validate button inside editing modal', () => { -  it('should create an action to update a vehiclejourney', () => { -    const data = {} -    const selectedCompany = {} -    const expectedAction = { -      type: 'EDIT_VEHICLEJOURNEY', -      data, -      selectedCompany -    } -    expect(actions.editVehicleJourney(data, selectedCompany)).toEqual(expectedAction) +  context("with invalid data", () => { +    it('should not validate the data', () => { +      const data = { +        foo: { +          validity: { valid: false } +        }, +        bar: { +          validity: { valid: true } +        } +      } + +      expect(actions.validateFields(data)).toBeFalsy +    }) +  }) + +  context("with data not needing validation", () => { +    it('should validate the data', () => { +      const data = { +        foo: {} +      } + +      expect(actions.validateFields(data)).toBeTruthy +    }) +  }) +  context("with valid data", () => { +    it('should validate the data', () => { +      const data = { +        foo: { +          validity: { valid: true } +        }, +        bar: { +          validity: { valid: true } +        } +      } + +      expect(actions.validateFields(data)).toBeTruthy +    }) +  }) +  context("once the data has been validated", () => { +    it('should create an action to update a vehiclejourney', () => { +      const data = {} +      const selectedCompany = {} +      const expectedAction = { +        type: 'EDIT_VEHICLEJOURNEY', +        data, +        selectedCompany +      } +      expect(actions.editVehicleJourney(data, selectedCompany)).toEqual(expectedAction) +    })    })  })  describe('when clicking on validate button inside duplicating modal', () => { diff --git a/spec/javascript/vehicle_journeys/reducers/modal_spec.js b/spec/javascript/vehicle_journeys/reducers/modal_spec.js index ea8a002d2..ee50f091b 100644 --- a/spec/javascript/vehicle_journeys/reducers/modal_spec.js +++ b/spec/javascript/vehicle_journeys/reducers/modal_spec.js @@ -241,13 +241,12 @@ describe('modal reducer', () => {    })    it('should handle SELECT_CP_EDIT_MODAL', () => { -    let newModalProps = {selectedCompany : {name: 'ALBATRANS'}}      expect(        modalReducer(state, {          type: 'SELECT_CP_EDIT_MODAL',          selectedItem: {name: 'ALBATRANS'} -      }) -    ).toEqual(Object.assign({}, state, {modalProps: newModalProps})) +      }).modalProps.vehicleJourney.company +    ).toEqual({name: 'ALBATRANS'})    })    it('should handle UNSELECT_CP_EDIT_MODAL', () => { @@ -255,7 +254,7 @@ describe('modal reducer', () => {      expect(        modalReducer(state, {          type: 'UNSELECT_CP_EDIT_MODAL' -      }) -    ).toEqual(Object.assign({}, state, {modalProps: newModalProps})) +      }).modalProps.vehicleJourney.company +    ).toBe(undefined)    })  }) diff --git a/spec/javascript/vehicle_journeys/reducers/vehicleJourneys_spec.js b/spec/javascript/vehicle_journeys/reducers/vehicleJourneys_spec.js index c834de1f6..28de241ee 100644 --- a/spec/javascript/vehicle_journeys/reducers/vehicleJourneys_spec.js +++ b/spec/javascript/vehicle_journeys/reducers/vehicleJourneys_spec.js @@ -110,6 +110,7 @@ describe('vehicleJourneys reducer', () => {        objectid: '',        footnotes: [],        time_tables: [], +      purchase_windows: [],        vehicle_journey_at_stops: pristineVjasList,        selected: false,        deletable: false, diff --git a/spec/models/chouette/access_point_spec.rb b/spec/models/chouette/access_point_spec.rb index c734ecedf..2184c6ec2 100644 --- a/spec/models/chouette/access_point_spec.rb +++ b/spec/models/chouette/access_point_spec.rb @@ -136,7 +136,7 @@ describe Chouette::AccessPoint, :type => :model do    describe "#generic_access_link_matrix" do      it "should have 2 generic_access_links in matrix" do -      stop_place = create :stop_area, :area_type => "zdlp" +      stop_place = create :stop_area, :area_type => "gdl"        commercial_stop_point = create :stop_area, :area_type => "lda" ,:parent => stop_place        subject = create :access_point, :stop_area => stop_place        expect(subject.generic_access_link_matrix.size).to eq(2) diff --git a/spec/models/chouette/route/route_duplication_spec.rb b/spec/models/chouette/route/route_duplication_spec.rb index ee45b5005..8b3a948a2 100644 --- a/spec/models/chouette/route/route_duplication_spec.rb +++ b/spec/models/chouette/route/route_duplication_spec.rb @@ -2,7 +2,7 @@ RSpec.describe Chouette::Route do    let!( :route ){ create :route } -  context '#duplicate' do  +  context '#duplicate' do      describe 'properties' do        it 'same attribute values' do          route.duplicate @@ -23,7 +23,12 @@ RSpec.describe Chouette::Route do        it 'duplicates its stop points' do          expect{ route.duplicate }.to change{Chouette::StopPoint.count}.by(route.stop_points.count)        end -      it 'does bot duplicate the stop areas' do + +      it 'duplicates its stop points in the same order' do +        expect(route.duplicate.stop_points.order(:position).map(&:stop_area_id)).to eq route.stop_points.order(:position).map(&:stop_area_id) +      end + +      it 'does not duplicate the stop areas' do          expect{ route.duplicate }.not_to change{Chouette::StopArea.count}        end      end @@ -34,7 +39,7 @@ RSpec.describe Chouette::Route do        it 'the required attributes' do          expect( values_for_create(first_duplicate, except: %w{objectid name checksum checksum_source}) ).to eq( values_for_create( second_duplicate, except: %w{objectid name checksum checksum_source} ) ) -      end  +      end        it 'the stop areas' do          expect( first_duplicate.stop_areas.pluck(:id) ).to eq( route.stop_areas.pluck(:id) ) diff --git a/spec/models/chouette/stop_area_spec.rb b/spec/models/chouette/stop_area_spec.rb index bec8c0868..9db0f11a5 100644 --- a/spec/models/chouette/stop_area_spec.rb +++ b/spec/models/chouette/stop_area_spec.rb @@ -426,6 +426,42 @@ describe Chouette::StopArea, :type => :model do    #     end    # end +  describe "#parent" do + +    let(:stop_area) { FactoryGirl.build :stop_area, parent: FactoryGirl.build(:stop_area) } + +    it "is valid when parent has an 'higher' type" do +      stop_area.area_type = 'zdep' +      stop_area.parent.area_type = 'zdlp' + +      stop_area.valid? +      expect(stop_area.errors).to_not have_key(:parent_id) +    end + +    it "is valid when parent is undefined" do +      stop_area.parent = nil + +      stop_area.valid? +      expect(stop_area.errors).to_not have_key(:parent_id) +    end + +    it "isn't valid when parent has the same type" do +      stop_area.parent.area_type = stop_area.area_type = 'zdep' + +      stop_area.valid? +      expect(stop_area.errors).to have_key(:parent_id) +    end + +    it "isn't valid when parent has a lower type" do +      stop_area.area_type = 'lda' +      stop_area.parent.area_type = 'zdep' + +      stop_area.valid? +      expect(stop_area.errors).to have_key(:parent_id) +    end + +  end +    describe '#waiting_time' do      let(:stop_area) { FactoryGirl.build :stop_area } diff --git a/spec/models/chouette/vehicle_journey_spec.rb b/spec/models/chouette/vehicle_journey_spec.rb index d74c0ebee..eb2a31794 100644 --- a/spec/models/chouette/vehicle_journey_spec.rb +++ b/spec/models/chouette/vehicle_journey_spec.rb @@ -77,6 +77,7 @@ describe Chouette::VehicleJourney, :type => :model do        vj.slice('objectid', 'published_journey_name', 'journey_pattern_id', 'company_id').tap do |item|          item['vehicle_journey_at_stops'] = []          item['time_tables']              = [] +        item['purchase_windows']         = []          item['footnotes']                = []          item['purchase_windows']         = [] @@ -161,6 +162,23 @@ describe Chouette::VehicleJourney, :type => :model do        expect(vehicle_journey.reload.time_tables).to be_empty      end +    it 'should update vj purchase_windows association from state' do +      2.times{state['purchase_windows'] << create(:purchase_window, referential: referential).slice('id', 'name', 'objectid', 'color')} +      vehicle_journey.update_has_and_belongs_to_many_from_state(state) + +      expected = state['purchase_windows'].map{|tt| tt['id']} +      actual   = vehicle_journey.reload.purchase_windows.map(&:id) +      expect(actual).to match_array(expected) +    end + +    it 'should clear vj purchase_windows association when remove from state' do +      vehicle_journey.purchase_windows << create(:purchase_window, referential: referential) +      state['purchase_windows'] = [] +      vehicle_journey.update_has_and_belongs_to_many_from_state(state) + +      expect(vehicle_journey.reload.purchase_windows).to be_empty +    end +      it 'should update vj footnote association from state' do        2.times{state['footnotes'] << create(:footnote, line: route.line).slice('id', 'code', 'label', 'line_id')}        vehicle_journey.update_has_and_belongs_to_many_from_state(state) diff --git a/spec/models/compliance_control_spec.rb b/spec/models/compliance_control_spec.rb index 4267459ea..5cffba58d 100644 --- a/spec/models/compliance_control_spec.rb +++ b/spec/models/compliance_control_spec.rb @@ -1,5 +1,15 @@  RSpec.describe ComplianceControl, type: :model do +  context 'dynamic attributes' do +    let(:compliance_control1) { build_stubbed :compliance_control } +    let(:compliance_control2) { build_stubbed :compliance_control, type: 'VehicleJouneyControl::TimeTable' } + +    it 'should always return a array' do +       expect(compliance_control1.class.dynamic_attributes).to be_kind_of Array +       expect(compliance_control2.class.dynamic_attributes).to be_kind_of Array +    end +  end +    context 'standard validation' do      let(:compliance_control) { build_stubbed :compliance_control } diff --git a/spec/models/import_spec.rb b/spec/models/import_spec.rb index 3e4128865..ffb2360c2 100644 --- a/spec/models/import_spec.rb +++ b/spec/models/import_spec.rb @@ -10,8 +10,9 @@ RSpec.describe Import, type: :model do    it { should validate_presence_of(:workbench) }    it { should validate_presence_of(:creator) } -  it { should allow_value('file.zip').for(:file).with_message(I18n.t('activerecord.errors.models.import.attributes.file.wrong_file_extension')) } -  it { should_not allow_values('file.json', 'file.png', 'file.pdf').for(:file) } +  include ActionDispatch::TestProcess +  it { should allow_value(fixture_file_upload('OFFRE_TRANSDEV_2017030112251.zip')).for(:file) } +  it { should_not allow_value(fixture_file_upload('users.json')).for(:file).with_message(I18n.t('errors.messages.extension_whitelist_error', extension: '"json"', allowed_types: "zip")) }    let(:workbench_import) {netex_import.parent}    let(:workbench_import_with_completed_steps) do diff --git a/spec/views/imports/show.html.slim_spec.rb b/spec/views/imports/show.html.slim_spec.rb index 1811d2acf..7a046d1a2 100644 --- a/spec/views/imports/show.html.slim_spec.rb +++ b/spec/views/imports/show.html.slim_spec.rb @@ -22,20 +22,17 @@ RSpec.describe '/imports/show', type: :view do      messages.each do | message |        # require 'htmlbeautifier'        # b = HtmlBeautifier.beautify(rendered, indent: '  ') -      expect(rendered).to have_selector('dl#import_messages dt.import_message') do +      expect(rendered).to have_selector('.import_message-list dl dt.criticity') do          with_text message.criticity        end -      expect(rendered).to have_selector('dl#import_messages dd.import_message') do +      expect(rendered).to have_selector('.import_message-list dl dd') do          with_text rendered_message( message )        end      end    end -    def rendered_message message -    Object.new.extend(CommonHelper).tap do |helper| -      return I18n.t(message.message_key, helper.string_keys_to_symbols( message.message_attributes )) -    end +    return I18n.t message.message_key, message.message_attributes.symbolize_keys    end  end | 
