diff options
50 files changed, 1307 insertions, 167 deletions
| diff --git a/INSTALL.md b/INSTALL.md index 4e052a0db..1d5badb79 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -9,7 +9,7 @@ and install that version.  Example with [rvm](https://rvm.io/): -        rvm install 2.3.0 +        rvm install 2.3.1  Add the bundler gem @@ -92,6 +92,43 @@ To create Referential with some data (Route, JourneyPattern, VehicleJourney, etc  If PG complains about illegal type `hstore` in your tests that is probably because the shared extension is not installed, here is what to do: +#### Check installation + +* Run tests + +      bundle exec rake spec +      bundle exec rake teaspoon + +* Start local server + +      bundle exec rails server + + +#### Synchronize With STIF + +If you have access to STIF CodifLigne and Reflex : + +* Launch Sidekiq + +      bundle exec sidekiq + +* Execute the Synchronization Tasks + +      bundle exec rake codifligne:sync +      bundle exec rake reflex:sync + +**N.B.** These are asynchronious tasks, you can observe the launched jobs in your [Sidekiq Console](http://localhost:3000/sidekiq) + +#### Data in various Apartments (Referentials) + +To create `Referential` objects with some data (`Route`, `JourneyPattern`, `VehicleJourney`, etc) : + +      bundle exec rake referential:create + +# Troubleshooting + +If Postgres complains about illegal type `hstore` in your tests that is probably because the shared extension is not installed, here is what to do: +        bundle exec rake db:test:purge  Thanks to `lib/tasks/extensions.rake`. diff --git a/app/assets/javascripts/es6_browserified/journey_patterns/actions/index.js b/app/assets/javascripts/es6_browserified/journey_patterns/actions/index.js index 54d62f999..0ed961f44 100644 --- a/app/assets/javascripts/es6_browserified/journey_patterns/actions/index.js +++ b/app/assets/javascripts/es6_browserified/journey_patterns/actions/index.js @@ -84,6 +84,10 @@ const actions = {    resetValidation: (target) => {      $(target).parent().removeClass('has-error').children('.help-block').remove()    }, +  humanOID : (oid) => { +    var a = oid.split(':') +    return a[a.length - 1] +  },    validateFields : (fields) => {      const test = [] diff --git a/app/assets/javascripts/es6_browserified/journey_patterns/components/JourneyPattern.js b/app/assets/javascripts/es6_browserified/journey_patterns/components/JourneyPattern.js index d9f6d5550..14ddf2b99 100644 --- a/app/assets/javascripts/es6_browserified/journey_patterns/components/JourneyPattern.js +++ b/app/assets/javascripts/es6_browserified/journey_patterns/components/JourneyPattern.js @@ -14,7 +14,7 @@ class JourneyPattern extends Component{      let vjURL = routeURL + '/vehicle_journeys?jp=' + jpOid      return ( -      <a data-no-turbolink="true" href={vjURL}>Horaires des courses</a> +      <a data-turbolinks="false" href={vjURL}>Horaires des courses</a>      )    } @@ -62,7 +62,7 @@ class JourneyPattern extends Component{            )}            <div className='th'> -            <div className='strong mb-xs'>{this.props.value.object_id ? this.props.value.object_id : '-'}</div> +            <div className='strong mb-xs'>{this.props.value.object_id ? actions.humanOID(this.props.value.object_id) : '-'}</div>              <div>{this.props.value.registration_number}</div>              <div>{actions.getChecked(this.props.value.stop_points).length} arrêt(s)</div> diff --git a/app/assets/javascripts/es6_browserified/time_tables/actions/index.js b/app/assets/javascripts/es6_browserified/time_tables/actions/index.js index cef9bc75d..3f15b7f01 100644 --- a/app/assets/javascripts/es6_browserified/time_tables/actions/index.js +++ b/app/assets/javascripts/es6_browserified/time_tables/actions/index.js @@ -45,7 +45,7 @@ const actions = {      pagination,      nextPage : true    }), -  changePage : (dispatch, pagination, val) => ({ +  changePage : (dispatch, val) => ({      type: 'CHANGE_PAGE',      dispatch,      page: val @@ -104,16 +104,14 @@ const actions = {      timeTablePeriods,      metas    }), -  includeDateInPeriod: (index, day, dayTypes) => ({ +  includeDateInPeriod: (index, dayTypes) => ({      type: 'INCLUDE_DATE_IN_PERIOD',      index, -    day,      dayTypes    }), -  excludeDateFromPeriod: (index, day, dayTypes) => ({ +  excludeDateFromPeriod: (index, dayTypes) => ({      type: 'EXCLUDE_DATE_FROM_PERIOD',      index, -    day,      dayTypes    }),    openConfirmModal : (callback) => ({ @@ -140,6 +138,10 @@ const actions = {      return (D + ' ' + M + ' ' + Y)    }, +  getLocaleDate(strDate) { +    let date = new Date(strDate) +    return date.toLocaleDateString() +  },    updateSynthesis: (state, daytypes) => {      let periods = state.time_table_periods diff --git a/app/assets/javascripts/es6_browserified/time_tables/components/ExceptionsInDay.js b/app/assets/javascripts/es6_browserified/time_tables/components/ExceptionsInDay.js index 13615a6ef..e90099283 100644 --- a/app/assets/javascripts/es6_browserified/time_tables/components/ExceptionsInDay.js +++ b/app/assets/javascripts/es6_browserified/time_tables/components/ExceptionsInDay.js @@ -21,7 +21,7 @@ class ExceptionsInDay extends Component {                data-actiontype='remove'                onClick={(e) => {                  $(e.currentTarget).toggleClass('active') -                this.props.onExcludeDateFromPeriod(this.props.index, this.props.value.current_month[this.props.index], this.props.metas.day_types) +                this.props.onExcludeDateFromPeriod(this.props.index, this.props.metas.day_types)                }}              >                <span className='fa fa-times'></span> @@ -37,7 +37,7 @@ class ExceptionsInDay extends Component {                data-actiontype='add'                onClick={(e) => {                  $(e.currentTarget).toggleClass('active') -                this.props.onIncludeDateInPeriod(this.props.index, this.props.value.current_month[this.props.index], this.props.metas.day_types) +                this.props.onIncludeDateInPeriod(this.props.index, this.props.metas.day_types)                }}              >                <span className='fa fa-plus'></span> diff --git a/app/assets/javascripts/es6_browserified/time_tables/components/Navigate.js b/app/assets/javascripts/es6_browserified/time_tables/components/Navigate.js index c43cd025a..74ca36ea6 100644 --- a/app/assets/javascripts/es6_browserified/time_tables/components/Navigate.js +++ b/app/assets/javascripts/es6_browserified/time_tables/components/Navigate.js @@ -39,7 +39,7 @@ let Navigate = ({ dispatch, metas, timetable, pagination, status, filters}) => {                        value={month}                        onClick={e => {                          e.preventDefault() -                        dispatch(actions.checkConfirmModal(e, actions.changePage(dispatch, pagination, e.currentTarget.value), pagination.stateChanged, dispatch)) +                        dispatch(actions.checkConfirmModal(e, actions.changePage(dispatch, e.currentTarget.value), pagination.stateChanged, dispatch))                        }}                      >                        {actions.monthName(month) + ' ' + new Date(month).getFullYear()} diff --git a/app/assets/javascripts/es6_browserified/time_tables/components/PeriodManager.js b/app/assets/javascripts/es6_browserified/time_tables/components/PeriodManager.js index de3f31ee0..cf4cbfb32 100644 --- a/app/assets/javascripts/es6_browserified/time_tables/components/PeriodManager.js +++ b/app/assets/javascripts/es6_browserified/time_tables/components/PeriodManager.js @@ -8,14 +8,33 @@ class PeriodManager extends Component {      super(props)    } +  toEndPeriod(curr, end) { +    let diff + +    let startCurrM = curr.split('-')[1] +    let endPeriodM = end.split('-')[1] + +    let lastDayInM = new Date(curr.split('-')[2], startCurrM + 1, 0) +    lastDayInM = lastDayInM.toJSON().substr(0, 10).split('-')[2] + +    if(startCurrM === endPeriodM) { +      diff = (end.split('-')[2] - curr.split('-')[2]) +    } else { +      diff = (lastDayInM - curr.split('-')[2]) +    } + +    return diff +  } +    render() {      return (        <div          className='period_manager'          id={this.props.value.id} +        data-toendperiod={this.toEndPeriod(this.props.currentDate.toJSON().substr(0, 10),  this.props.value.period_end)}        >          <p className='strong'> -          {actions.getHumanDate(this.props.value.period_start, 3).substr(0, 7) + ' > ' + actions.getHumanDate(this.props.value.period_end, 3)} +          {actions.getLocaleDate(this.props.value.period_start) + ' > ' + actions.getLocaleDate(this.props.value.period_end)}          </p>          <div className='dropdown'> @@ -58,6 +77,7 @@ class PeriodManager extends Component {  PeriodManager.propTypes = {    value: PropTypes.object.isRequired, +  currentDate: PropTypes.object.isRequired,    onDeletePeriod: PropTypes.func.isRequired,    onOpenEditPeriodForm: PropTypes.func.isRequired  } diff --git a/app/assets/javascripts/es6_browserified/time_tables/components/PeriodsInDay.js b/app/assets/javascripts/es6_browserified/time_tables/components/PeriodsInDay.js index 93a8fe433..ca44d3a07 100644 --- a/app/assets/javascripts/es6_browserified/time_tables/components/PeriodsInDay.js +++ b/app/assets/javascripts/es6_browserified/time_tables/components/PeriodsInDay.js @@ -50,9 +50,10 @@ class PeriodsInDay extends Component {                      key={i}                      index={i}                      value={p} +                    metas={this.props.metas} +                    currentDate={this.props.currentDate}                      onDeletePeriod={this.props.onDeletePeriod}                      onOpenEditPeriodForm={this.props.onOpenEditPeriodForm} -                    metas={this.props.metas}                    />                  )                } else { diff --git a/app/assets/javascripts/es6_browserified/time_tables/containers/Timetable.js b/app/assets/javascripts/es6_browserified/time_tables/containers/Timetable.js index 2a17d3dea..c6b5fcc6b 100644 --- a/app/assets/javascripts/es6_browserified/time_tables/containers/Timetable.js +++ b/app/assets/javascripts/es6_browserified/time_tables/containers/Timetable.js @@ -15,11 +15,11 @@ const mapDispatchToProps = (dispatch) => {      onDeletePeriod: (index, dayTypes) =>{        dispatch(actions.deletePeriod(index, dayTypes))      }, -    onExcludeDateFromPeriod: (index, day, dayTypes) => { -      dispatch(actions.excludeDateFromPeriod(index, day, dayTypes)) +    onExcludeDateFromPeriod: (index, dayTypes) => { +      dispatch(actions.excludeDateFromPeriod(index, dayTypes))      }, -    onIncludeDateInPeriod: (index, day, dayTypes) => { -      dispatch(actions.includeDateInPeriod(index, day, dayTypes)) +    onIncludeDateInPeriod: (index, dayTypes) => { +      dispatch(actions.includeDateInPeriod(index, dayTypes))      },      onOpenEditPeriodForm: (period, index) => {        dispatch(actions.openEditPeriodForm(period, index)) diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/actions/index.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/actions/index.js index ea03694bd..0e6f5ed12 100644 --- a/app/assets/javascripts/es6_browserified/vehicle_journeys/actions/index.js +++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/actions/index.js @@ -248,6 +248,10 @@ const actions = {      type: 'RECEIVE_TOTAL_COUNT',      total    }), +  humanOID : (oid) => { +    var a = oid.split(':') +    return a[a.length - 1] +  },    fetchVehicleJourneys : (dispatch, currentPage, nextPage, queryString) => {      if(currentPage == undefined){        currentPage = 1 diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/VehicleJourney.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/VehicleJourney.js index d795d76e3..6f338f747 100644 --- a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/VehicleJourney.js +++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/VehicleJourney.js @@ -1,6 +1,7 @@  var React = require('react')  var Component = require('react').Component  var PropTypes = require('react').PropTypes +var actions = require('../actions')  class VehicleJourney extends Component {    constructor(props) { @@ -48,8 +49,8 @@ class VehicleJourney extends Component {      return (        <div className={'t2e-item' + (this.props.value.deletable ? ' disabled' : '') + (this.props.value.errors ? ' has-error': '')}>          <div className='th'> -          <div className='strong mb-xs'>{this.props.value.objectid ? this.props.value.objectid : '-'}</div> -          <div>{this.props.value.journey_pattern.objectid}</div> +          <div className='strong mb-xs'>{this.props.value.objectid ? actions.humanOID(this.props.value.objectid) : '-'}</div> +          <div>{actions.humanOID(this.props.value.journey_pattern.objectid)}</div>            {this.props.value.time_tables.map((tt, i)=>              <div key={i}>{this.timeTableURL(tt)}</div>            )} diff --git a/app/assets/stylesheets/modules/_jp_collection.sass b/app/assets/stylesheets/modules/_jp_collection.sass index d1f864e5c..c109fc71a 100644 --- a/app/assets/stylesheets/modules/_jp_collection.sass +++ b/app/assets/stylesheets/modules/_jp_collection.sass @@ -82,3 +82,19 @@        .t2e-head > .td:last-child > div > span          &:after            bottom: 50% + +    .t2e-head > .td:nth-child(2) > div, +    .t2e-head > .td:last-child > div +      > span:before +        content: '•' +        color: $blue +        text-align: center +        font-size: 28px +        letter-spacing: 0 +        text-indent: -0.01em +        line-height: 12px +        width: 15px +        height: 15px +        left: -23px +        top: 50% +        margin-top: -8px diff --git a/app/assets/stylesheets/modules/_routes_stopoints.sass b/app/assets/stylesheets/modules/_routes_stopoints.sass index 735e91df7..88e662849 100644 --- a/app/assets/stylesheets/modules/_routes_stopoints.sass +++ b/app/assets/stylesheets/modules/_routes_stopoints.sass @@ -12,7 +12,7 @@        height: 100%        > div:first-child          position: relative -        padding-left: 20px /* intial value is 10 */ +        padding-left: 25px /* intial value is 10 */          overflow: hidden          span @@ -70,3 +70,19 @@    .nested-head + .nested-fields > .wrapper > div:first-child      span:after        top: 5px + +  .nested-head + .nested-fields > .wrapper > div:first-child, +  .nested-fields:last-child > .wrapper:last-child > div:first-child +    span:before +      content: '•' +      color: $blue +      text-align: center +      font-size: 28px +      letter-spacing: 0 +      text-indent: -0.01em +      line-height: 12px +      width: 15px +      height: 15px +      left: -23px +      top: 50% +      margin-top: -8px diff --git a/app/assets/stylesheets/modules/_timetables.sass b/app/assets/stylesheets/modules/_timetables.sass index 03dba3e23..84f1af043 100644 --- a/app/assets/stylesheets/modules/_timetables.sass +++ b/app/assets/stylesheets/modules/_timetables.sass @@ -125,21 +125,35 @@      display: block      height: auto      word-wrap: normal -    white-space: nowrap +    white-space: normal      position: absolute -    left: 8px +    left: 0      top: 50%      transform: translateY(-50%)      z-index: 5 +    padding: 0 8px + +    @for $i from 0 through 31 +      &[data-toendperiod='#{$i}'] +        width: 40px * ($i + 1)      > *        display: inline-block        vertical-align: middle        margin: 0 +      max-width: calc(100% - 30px)        &.dropdown          margin-left: 5px +    &[data-toendperiod='0'], &[data-toendperiod='1'], &[data-toendperiod='2'] +      max-width: none +      > * +        display: none + +        &.dropdown +          display: inline-block +      .btn.dropdown-toggle        color: $blue        background-color: rgba(#fff, 0) diff --git a/app/assets/stylesheets/modules/_vj_collection.sass b/app/assets/stylesheets/modules/_vj_collection.sass index 50ad1cd54..8ff983310 100644 --- a/app/assets/stylesheets/modules/_vj_collection.sass +++ b/app/assets/stylesheets/modules/_vj_collection.sass @@ -79,6 +79,27 @@          &:after            bottom: -6px +      .t2e-head > .td:last-child > div > span +        &:after +          bottom: 50% + +  .table-2entries .t2e-head > .td:nth-child(2) > div, +  .table-2entries .t2e-head > .td:last-child > div, +  .table-2entries.no_result .t2e-head > .td:last-child > div +    > span:before +      content: '•' +      color: $blue +      text-align: center +      font-size: 28px +      letter-spacing: 0 +      text-indent: -0.01em +      line-height: 12px +      width: 15px +      height: 15px +      left: -23px +      top: 50% +      margin-top: -8px +    // Errors    .table-2entries .t2e-item-list      .t2e-item @@ -92,7 +113,6 @@            left: 0            right: 0            bottom: 0 -          z-index: 5            border: 2px solid $red          > .th diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index f2c9b4c6f..42b7c2a25 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -12,7 +12,10 @@ class ApplicationController < ActionController::Base    helper LanguageEngine::Engine.helpers    def set_locale -    I18n.locale = session[:language] || I18n.default_locale +    # I18n.locale = session[:language] || I18n.default_locale +    # For testing different locales w/o restarting the server +    I18n.locale = (params['lang'] || session[:language] || I18n.default_locale).to_sym +    logger.info "locale set to #{I18n.locale.inspect}"    end    def pundit_user diff --git a/app/controllers/calendars_controller.rb b/app/controllers/calendars_controller.rb index d18e165d2..5370d9cbb 100644 --- a/app/controllers/calendars_controller.rb +++ b/app/controllers/calendars_controller.rb @@ -45,14 +45,13 @@ class CalendarsController < BreadcrumbController    end    def ransack_contains_date -    # 3 parts to date object, in order to use in ransackable_scopes +    date =[]      if params[:q] && !params[:q]['contains_date(1i)'].empty? -      date =[]        ['contains_date(1i)', 'contains_date(2i)', 'contains_date(3i)'].each do |key| -        date << params[:q][key] +        date << params[:q][key].to_i          params[:q].delete(key)        end -      params[:q]['contains_date'] = Date.parse(date.join('-')) +      params[:q]['contains_date'] = Date.new(*date)      end    end diff --git a/app/controllers/routes_controller.rb b/app/controllers/routes_controller.rb index a1aadf883..73febc4b9 100644 --- a/app/controllers/routes_controller.rb +++ b/app/controllers/routes_controller.rb @@ -46,12 +46,6 @@ class RoutesController < ChouetteController      end    end -  # overwrite inherited resources to use delete instead of destroy -  # foreign keys will propagate deletion) -  def destroy_resource(object) -    object.delete -  end -    def destroy      destroy! do |success, failure|        success.html { redirect_to referential_line_path(@referential,@line) } diff --git a/app/controllers/vehicle_journeys_controller.rb b/app/controllers/vehicle_journeys_controller.rb index c084b592a..316652ca2 100644 --- a/app/controllers/vehicle_journeys_controller.rb +++ b/app/controllers/vehicle_journeys_controller.rb @@ -77,14 +77,14 @@ class VehicleJourneysController < ChouetteController    protected    def collection -    scope = route.vehicle_journeys.joins(:journey_pattern).joins('LEFT JOIN "vehicle_journey_at_stops" ON "vehicle_journey_at_stops"."vehicle_journey_id" = "vehicle_journeys"."id"') +    scope = route.vehicle_journeys.with_stops      @q = scope.search filtered_ransack_params      grouping = ransack_periode_filter      @q.build_grouping(grouping) if grouping      @ppage = 20 -    @vehicle_journeys = @q.result(distinct: true).paginate(:page => params[:page], :per_page => @ppage) +    @vehicle_journeys = @q.result.paginate(:page => params[:page], :per_page => @ppage)      @footnotes = route.line.footnotes.to_json      @matrix    = resource_class.matrix(@vehicle_journeys)      @vehicle_journeys diff --git a/app/models/calendar.rb b/app/models/calendar.rb index cd945a67f..91a17e853 100644 --- a/app/models/calendar.rb +++ b/app/models/calendar.rb @@ -118,9 +118,18 @@ class Calendar < ActiveRecord::Base      end    end +  def flatten_date_array attributes, key +    date_int = %w(1 2 3).map {|e| attributes["#{key}(#{e}i)"].to_i } +    Date.new(*date_int) +  end +    def periods_attributes=(attributes = {})      @periods = []      attributes.each do |index, period_attribute| +      # Convert date_select to date +      ['begin', 'end'].map do |attr| +        period_attribute[attr] = flatten_date_array(period_attribute, attr) +      end        period = Period.new(period_attribute.merge(id: index))        @periods << period unless period.marked_for_destruction?      end @@ -223,6 +232,7 @@ class Calendar < ActiveRecord::Base    def date_values_attributes=(attributes = {})      @date_values = []      attributes.each do |index, date_value_attribute| +      date_value_attribute['value'] = flatten_date_array(date_value_attribute, 'value')        date_value = DateValue.new(date_value_attribute.merge(id: index))        @date_values << date_value unless date_value.marked_for_destruction?      end diff --git a/app/models/chouette/route.rb b/app/models/chouette/route.rb index 429189ff5..9f6344055 100644 --- a/app/models/chouette/route.rb +++ b/app/models/chouette/route.rb @@ -105,7 +105,7 @@ class Chouette::Route < Chouette::TridentActiveRecord    end    def time_tables -    self.vehicle_journeys.joins(:time_tables).map(&:"time_tables").flatten.uniq +    vehicle_journeys.joins(:time_tables).map(&:"time_tables").flatten.uniq    end    def sorted_vehicle_journeys(journey_category_model) diff --git a/app/models/chouette/time_table.rb b/app/models/chouette/time_table.rb index 798fa81b4..37f609163 100644 --- a/app/models/chouette/time_table.rb +++ b/app/models/chouette/time_table.rb @@ -33,6 +33,31 @@ class Chouette::TimeTable < Chouette::TridentActiveRecord    validates_associated :dates    validates_associated :periods +  def continuous_dates +    chunk = {} +    group = nil +    self.dates.where(in_out: true).each_with_index do |date, index| +      group ||= index +      group = (date.date == dates[index - 1].date + 1.day) ? group : group + 1 +      chunk[group] ||= [] +      chunk[group] << date +    end +    chunk.values +  end + +  def convert_continuous_dates_to_periods +    chunks = self.continuous_dates +    # Remove less than 3 continuous day chunk +    chunks.delete_if {|chunk| chunk.count < 3} + +    transaction do +      chunks.each do |chunk| +        self.periods.create!(period_start: chunk.first.date, period_end: chunk.last.date) +        chunk.map(&:destroy) +      end +    end +  end +    def state_update state      update_attributes(self.class.state_permited_attributes(state))      self.tag_list = state['tags'].collect{|t| t['name']}.join(', ') diff --git a/app/models/chouette/vehicle_journey.rb b/app/models/chouette/vehicle_journey.rb index 4d7d596d8..297e462f0 100644 --- a/app/models/chouette/vehicle_journey.rb +++ b/app/models/chouette/vehicle_journey.rb @@ -209,5 +209,18 @@ module Chouette        end      end +    def self.with_stops +      self +        .joins(:journey_pattern) +        .joins(' +          LEFT JOIN "vehicle_journey_at_stops" +            ON "vehicle_journey_at_stops"."vehicle_journey_id" = +              "vehicle_journeys"."id" +            AND "vehicle_journey_at_stops"."stop_point_id" = +              "journey_patterns"."departure_stop_point_id" +        ') +        .order("vehicle_journey_at_stops.departure_time") +    end +    end  end diff --git a/app/models/route_observer.rb b/app/models/route_observer.rb index 71578c6da..1848bbc85 100644 --- a/app/models/route_observer.rb +++ b/app/models/route_observer.rb @@ -14,8 +14,8 @@ class RouteObserver < ActiveRecord::Observer      end    end -  def after_destroy(route) +  def before_destroy(route)      Rails.logger.debug "after_destroy(#{route.inspect})" -    line.routes.where(opposite_route: route).update_all(opposite_route: nil) +    route.line.routes.where(opposite_route: route).update_all(opposite_route_id: nil)    end  end diff --git a/app/models/time_table_combination.rb b/app/models/time_table_combination.rb index 783ef53ef..519c02f2b 100644 --- a/app/models/time_table_combination.rb +++ b/app/models/time_table_combination.rb @@ -1,33 +1,33 @@  class TimeTableCombination -  include ActiveModel::Validations   -  include ActiveModel::Conversion   +  include ActiveModel::Validations +  include ActiveModel::Conversion    extend ActiveModel::Naming -   -  attr_accessor :source_id, :combined_id, :operation  -   -  validates_presence_of :source_id, :combined_id, :operation  + +  attr_accessor :source_id, :combined_id, :operation + +  validates_presence_of :source_id, :combined_id, :operation    validates_inclusion_of :operation, :in =>  %w( union intersection disjunction), :allow_nil => true -     +    def clean      self.source_id = nil      self.combined_id = nil      self.operation = nil      self.errors.clear -  end   -    +  end +    def self.operations      %w( union intersection disjunction)    end -  def initialize(attributes = {})   -    attributes.each do |name, value|   -      send("#{name}=", value)   -    end   -  end   -     -  def persisted?   -    false   -  end  +  def initialize(attributes = {}) +    attributes.each do |name, value| +      send("#{name}=", value) +    end +  end + +  def persisted? +    false +  end    def combine      source = Chouette::TimeTable.find( source_id) @@ -38,10 +38,10 @@ class TimeTableCombination        source.intersect! combined      elsif operation == "disjunction"        source.disjoin! combined -    else  +    else        raise "unknown operation"      end      source    end -   +  end diff --git a/app/views/line_referentials/show.html.slim b/app/views/line_referentials/show.html.slim index 5c0df1a71..95c2c02b0 100644 --- a/app/views/line_referentials/show.html.slim +++ b/app/views/line_referentials/show.html.slim @@ -27,9 +27,9 @@            table.table              thead                tr -                th Synchronisé -                th Statut -                th Message +                th = t('.synchronized') +                th = t('.status') +                th = t('.message')              tbody                - @line_referential.line_referential_syncs.each_with_index do |sync, i| @@ -39,8 +39,9 @@                      - sync.line_referential_sync_messages.last.tap do |log|                        - if log.criticity = log.criticity                          tr -                          td = l(log.created_at, format: :short) -                          td +                          td style='width: 150px' +                            = l(log.created_at, format: :short_with_time) +                          td.text-center                              .fa.fa-circle class="text-#{criticity_class(log.criticity)}"                            td                              - data = log.message_attributs.symbolize_keys! diff --git a/app/views/referentials/show.html.slim b/app/views/referentials/show.html.slim index 45fffe6a1..befa851ab 100644 --- a/app/views/referentials/show.html.slim +++ b/app/views/referentials/show.html.slim @@ -50,8 +50,8 @@              { 'ID Codif' => Proc.new { |n| n.objectid.local_id },              :number => 'number',              :name => 'name', -            :deactivated => Proc.new{|n| n.deactivated? ? t('false') : t('true')}, -            :transport_mode => 'transport_mode', +            :deactivated => Proc.new{ |n| n.deactivated? ? t('false') : t('true') }, +            :transport_mode => Proc.new{ |n| n.transport_mode ? t("enumerize.line.transport_mode.#{n.transport_mode}") : '' },              'networks.name' => Proc.new { |n| n.try(:network).try(:name) },              'companies.name' => Proc.new { |n| n.try(:company).try(:name) } },              [:show], diff --git a/app/views/stop_area_referentials/show.html.slim b/app/views/stop_area_referentials/show.html.slim index 24428eea4..56ecbf6da 100644 --- a/app/views/stop_area_referentials/show.html.slim +++ b/app/views/stop_area_referentials/show.html.slim @@ -32,8 +32,9 @@                      - sync.stop_area_referential_sync_messages.last.tap do |log|                        - if log.criticity = log.criticity                          tr -                          td = l(log.created_at, format: :short) -                          td +                          td style='width:150px' +                            = l(log.created_at, format: :short_with_time) +                          td.text-center                              .fa.fa-circle class="text-#{criticity_class(log.criticity)}"                            td                              - data = log.message_attributs.symbolize_keys! diff --git a/app/views/time_tables/index.html.slim b/app/views/time_tables/index.html.slim index 01b65653c..c17f96c85 100644 --- a/app/views/time_tables/index.html.slim +++ b/app/views/time_tables/index.html.slim @@ -15,7 +15,7 @@        .row          .col-lg-12            = table_builder @time_tables, -            { :color => Proc.new{|tt| tt.color ? content_tag(:span, '', class: 'fa fa-circle', style: "color:#{tt.color}") : '-' }, :comment => 'comment',  +            { 'OiD' => Proc.new { |n| n.objectid.local_id }, :color => Proc.new{|tt| tt.color ? content_tag(:span, '', class: 'fa fa-circle', style: "color:#{tt.color}") : '-' }, :comment => 'comment',                 "Période englobante" => Proc.new{ |tt| tt.bounding_dates.empty? ? '-' : t('bounding_dates', debut: l(tt.bounding_dates.min), end: l(tt.bounding_dates.max)) },                "Nombre de courses associées" => Proc.new{ |tt| tt.vehicle_journeys.count },                "Journées d'application" => Proc.new{ |tt| (%w(monday tuesday wednesday thursday friday saturday sunday).collect{|d| tt.send(d) ? t("calendars.days.#{d}") : '' }).reject{|a| a.empty?}.join(', ').html_safe }, diff --git a/config/application.rb b/config/application.rb index 8606a3156..6c8de781d 100644 --- a/config/application.rb +++ b/config/application.rb @@ -25,7 +25,7 @@ module ChouetteIhm      # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.      # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] -    config.i18n.default_locale = :fr +    config.i18n.default_locale = ENV.fetch('RAILS_LOCALE', 'fr').to_sym      # Configure Browserify to use babelify to compile ES6      config.browserify_rails.commandline_options = "-t [ babelify --presets [ react es2015 ] ]" diff --git a/config/locales/en.yml b/config/locales/en.yml index 5ed0b9ec5..3f6a68f8d 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -8,10 +8,12 @@ en:        hour: "%Hh%M"        minute: "%M min"        short: "%Y/%m/%d" +      short_with_time: "%Y/%m/%d at %Hh%M"    date:      formats:        short: "%Y/%m/%d" +      short_with_time: "%Y/%m/%d at %Hh%M"    last_update: 'Last update on<br>%{time}'    last_sync: 'Last sync on %{time}' diff --git a/config/locales/fr.yml b/config/locales/fr.yml index db8a3608d..49d1c5bb7 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -8,10 +8,12 @@ fr:        hour: "%Hh%M"        minute: "%M min"        short: "%d/%m/%Y" +      short_with_time: "%d/%m/%Y à %Hh%M"    date:      formats:        short: "%d/%m/%Y" +      short_with_time: "%d/%m/%Y à %Hh%M"    last_update: 'Dernière mise à jour<br>le %{time}'    last_sync: 'Dernière mise à jour le %{time}' diff --git a/config/locales/line_referentials.en.yml b/config/locales/line_referentials.en.yml index 7d84a24f8..78083912d 100644 --- a/config/locales/line_referentials.en.yml +++ b/config/locales/line_referentials.en.yml @@ -8,6 +8,9 @@ en:        title: "Edit %{line_referential} referential"      show:        title: "iLICO synchronization" +      synchronized: Synchronized +      status: Status +      message: Message    activerecord:      models:        line_referential: diff --git a/config/locales/line_referentials.fr.yml b/config/locales/line_referentials.fr.yml index fe8496d99..c8dfbd640 100644 --- a/config/locales/line_referentials.fr.yml +++ b/config/locales/line_referentials.fr.yml @@ -8,6 +8,9 @@ fr:        title: "Editer le référentiel %{line_referential}"      show:        title: 'Synchronisation iLICO' +      synchronized: Synchronisé +      status: Statut +      message: Message    activerecord:      models:        line_referential: diff --git a/db/schema.rb b/db/schema.rb index 17df3c34f..a45181687 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -283,7 +283,7 @@ ActiveRecord::Schema.define(version: 20170502130327) do      t.datetime "started_at"      t.datetime "ended_at"      t.string   "token_download" -    t.string   "type",                  limit: 255 +    t.string   "type"    end    add_index "imports", ["referential_id"], name: "index_imports_on_referential_id", using: :btree @@ -601,7 +601,7 @@ ActiveRecord::Schema.define(version: 20170502130327) do    create_table "stop_areas", id: :bigserial, force: :cascade do |t|      t.integer  "parent_id",                       limit: 8 -    t.string   "objectid",                                                              null: false +    t.string   "objectid",                                                            null: false      t.integer  "object_version",                  limit: 8      t.string   "creator_id"      t.string   "name" @@ -610,8 +610,8 @@ ActiveRecord::Schema.define(version: 20170502130327) do      t.string   "registration_number"      t.string   "nearest_topic_name"      t.integer  "fare_code" -    t.decimal  "longitude",                                   precision: 19, scale: 16 -    t.decimal  "latitude",                                    precision: 19, scale: 16 +    t.decimal  "longitude",                                 precision: 19, scale: 16 +    t.decimal  "latitude",                                  precision: 19, scale: 16      t.string   "long_lat_type"      t.string   "country_code"      t.string   "street_name" @@ -629,7 +629,7 @@ ActiveRecord::Schema.define(version: 20170502130327) do      t.datetime "deleted_at"      t.datetime "created_at"      t.datetime "updated_at" -    t.string   "stif_type",                       limit: 255 +    t.string   "stif_type"    end    add_index "stop_areas", ["name"], name: "index_stop_areas_on_name", using: :btree @@ -696,18 +696,18 @@ ActiveRecord::Schema.define(version: 20170502130327) do    add_index "time_table_periods", ["time_table_id"], name: "index_time_table_periods_on_time_table_id", using: :btree    create_table "time_tables", id: :bigserial, force: :cascade do |t| -    t.string   "objectid",                                null: false -    t.integer  "object_version",  limit: 8,   default: 1 +    t.string   "objectid",                              null: false +    t.integer  "object_version",  limit: 8, default: 1      t.string   "creator_id"      t.string   "version"      t.string   "comment" -    t.integer  "int_day_types",               default: 0 +    t.integer  "int_day_types",             default: 0      t.date     "start_date"      t.date     "end_date"      t.integer  "calendar_id",     limit: 8      t.datetime "created_at"      t.datetime "updated_at" -    t.string   "color",           limit: 255 +    t.string   "color"      t.integer  "created_from_id"    end diff --git a/lib/stif/codif_line_synchronization.rb b/lib/stif/codif_line_synchronization.rb index feb313f45..2bb4d8778 100644 --- a/lib/stif/codif_line_synchronization.rb +++ b/lib/stif/codif_line_synchronization.rb @@ -11,18 +11,18 @@ module Stif        def processed_counts          { -          imported: self.imported_count, -          updated: self.updated_count, -          deleted: self.deleted_count +          imported: imported_count, +          updated: updated_count, +          deleted: deleted_count          }        end        def increment_counts prop_name, value -        self.send("#{prop_name}=", self.send(prop_name) + value) +        send("#{prop_name}=", self.send(prop_name) + value)        end        def synchronize -        self.reset_counts +        reset_counts          start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC, :second)          # Fetch Codifline data          client = Codifligne::API.new diff --git a/lib/stif/reflex_synchronization.rb b/lib/stif/reflex_synchronization.rb index 6d8497bce..675486265 100644 --- a/lib/stif/reflex_synchronization.rb +++ b/lib/stif/reflex_synchronization.rb @@ -35,7 +35,7 @@ module Stif        end        def synchronize -        self.reset_counts +        reset_counts          ['getOR', 'getOP'].each do |method|            start   = Time.now            results = Reflex::API.new().process(method) diff --git a/spec/factories/chouette_routes.rb b/spec/factories/chouette_routes.rb index e872d24f5..c1a9423c5 100644 --- a/spec/factories/chouette_routes.rb +++ b/spec/factories/chouette_routes.rb @@ -20,7 +20,18 @@ FactoryGirl.define do          create_list(:stop_point, evaluator.stop_points_count, route: route)        end +      factory :route_with_journey_patterns do +        transient do +          journey_patterns_count 2 +        end + +        after(:create) do |route, evaluator| +          create_list(:journey_pattern, evaluator.journey_patterns_count, route: route) +        end + +      end      end +    end  end diff --git a/spec/factories/chouette_vehicle_journey.rb b/spec/factories/chouette_vehicle_journey.rb index b7c5e37d5..9ba660800 100644 --- a/spec/factories/chouette_vehicle_journey.rb +++ b/spec/factories/chouette_vehicle_journey.rb @@ -3,29 +3,31 @@ FactoryGirl.define do    factory :vehicle_journey_common, :class => Chouette::VehicleJourney do      sequence(:objectid) { |n| "test:VehicleJourney:#{n}" } -    factory :vehicle_journey do +    factory :vehicle_journey_empty do        association :journey_pattern, :factory => :journey_pattern        after(:build) do |vehicle_journey|          vehicle_journey.route = vehicle_journey.journey_pattern.route        end -      after(:create) do |vehicle_journey| -        vehicle_journey.journey_pattern.stop_points.each_with_index do |stop_point, index| -          vehicle_journey.vehicle_journey_at_stops << create(:vehicle_journey_at_stop, -                 :vehicle_journey => vehicle_journey, -                 :stop_point      => stop_point, -                 :arrival_time    => '2000-01-01 01:00:00 UTC', -                 :departure_time  => '2000-01-01 03:00:00 UTC') +      factory :vehicle_journey do +        after(:create) do |vehicle_journey| +          vehicle_journey.journey_pattern.stop_points.each_with_index do |stop_point, index| +            vehicle_journey.vehicle_journey_at_stops << create(:vehicle_journey_at_stop, +                   :vehicle_journey => vehicle_journey, +                   :stop_point      => stop_point, +                   :arrival_time    => '2000-01-01 01:00:00 UTC', +                   :departure_time  => '2000-01-01 03:00:00 UTC') +          end          end -      end -      factory :vehicle_journey_odd do -        association :journey_pattern, :factory => :journey_pattern_odd -      end +        factory :vehicle_journey_odd do +          association :journey_pattern, :factory => :journey_pattern_odd +        end -      factory :vehicle_journey_even do -        association :journey_pattern, :factory => :journey_pattern_even +        factory :vehicle_journey_even do +          association :journey_pattern, :factory => :journey_pattern_even +        end        end      end    end diff --git a/spec/javascripts/time_table/actions_spec.js b/spec/javascripts/time_table/actions_spec.js index eac2f86bb..1ab5635a0 100644 --- a/spec/javascripts/time_table/actions_spec.js +++ b/spec/javascripts/time_table/actions_spec.js @@ -1,5 +1,16 @@  var actions = require('es6_browserified/time_tables/actions') - +const dispatch = function(){} +const dayTypes = [true, true, true, true, true, true, true] +const day = { +  date : "2017-05-01", +  day : "lundi", +  excluded_date : false, +  in_periods : true, +  include_date : false, +  mday : 1, +  wday : 1, +  wnumber : "18" +}  describe('actions', () => {    it('should create an action to update dayTypes', () => {      const expectedAction = { @@ -48,4 +59,146 @@ describe('actions', () => {      }      expect(actions.unselect2Tags(selectedItem)).toEqual(expectedAction)    }) + +  it('should create an action to go to previous page', () => { +    let pagination = { +      currentPage: '2017-01-01', +      periode_range: [], +      stateChanged: false +    } +    const expectedAction = { +      type: 'GO_TO_PREVIOUS_PAGE', +      dispatch, +      pagination, +      nextPage: false +    } +    expect(actions.goToPreviousPage(dispatch, pagination)).toEqual(expectedAction) +  }) + +  it('should create an action to go to next page', () => { +    let pagination = { +      currentPage: '2017-01-01', +      periode_range: [], +      stateChanged: false +    } +    const expectedAction = { +      type: 'GO_TO_NEXT_PAGE', +      dispatch, +      pagination, +      nextPage: true +    } +    expect(actions.goToNextPage(dispatch, pagination)).toEqual(expectedAction) +  }) + +  it('should create an action to change page', () => { +    let page = '2017-05-04' +    const expectedAction = { +      type: 'CHANGE_PAGE', +      dispatch, +      page: page +    } +    expect(actions.changePage(dispatch, page)).toEqual(expectedAction) +  }) + +  it('should create an action to delete period', () => { +    let index = 1 +    const expectedAction = { +      type: 'DELETE_PERIOD', +      index, +      dayTypes +    } +    expect(actions.deletePeriod(index, dayTypes)).toEqual(expectedAction) +  }) + +  it('should create an action to open add period form', () => { +    const expectedAction = { +      type: 'OPEN_ADD_PERIOD_FORM', +    } +    expect(actions.openAddPeriodForm()).toEqual(expectedAction) +  }) + +  it('should create an action to open edit period form', () => { +    let period = { +      id : 1, +      period_end : "2017-03-05", +      period_start : "2017-02-23" +    } +    let index = 1 +    const expectedAction = { +      type: 'OPEN_EDIT_PERIOD_FORM', +      period, +      index +    } +    expect(actions.openEditPeriodForm(period, index)).toEqual(expectedAction) +  }) + +  it('should create an action to close period form', () => { +    const expectedAction = { +      type: 'CLOSE_PERIOD_FORM', +    } +    expect(actions.closePeriodForm()).toEqual(expectedAction) +  }) + +  it('should create an action to update period form', () => { +    let val = "11" +    let group = "start" +    let selectType = "day" +    const expectedAction = { +      type: 'UPDATE_PERIOD_FORM', +      val, +      group, +      selectType +    } +    expect(actions.updatePeriodForm(val, group, selectType)).toEqual(expectedAction) +  }) + +  it('should create an action to validate period form', () => { +    let modalProps = {} +    let timeTablePeriods = [] +    let metas = {} +    const expectedAction = { +      type: 'VALIDATE_PERIOD_FORM', +      modalProps, +      timeTablePeriods, +      metas +    } +    expect(actions.validatePeriodForm(modalProps, timeTablePeriods, metas)).toEqual(expectedAction) +  }) + +  it('should create an action to include date in period', () => { +    let index = 1 +    const expectedAction = { +      type: 'INCLUDE_DATE_IN_PERIOD', +      index, +      dayTypes +    } +    expect(actions.includeDateInPeriod(index, dayTypes)).toEqual(expectedAction) +  }) + +  it('should create an action to exclude date from period', () => { +    let index = 1 +    const expectedAction = { +      type: 'EXCLUDE_DATE_FROM_PERIOD', +      index, +      dayTypes +    } +    expect(actions.excludeDateFromPeriod(index, dayTypes)).toEqual(expectedAction) +  }) + +  it('should create an action to open confirm modal', () => { +    let callback = function(){} +    const expectedAction = { +      type: 'OPEN_CONFIRM_MODAL', +      callback +    } +    expect(actions.openConfirmModal(callback)).toEqual(expectedAction) +  }) + +  it('should create an action to close modal', () => { +    const expectedAction = { +      type: 'CLOSE_MODAL', +    } +    expect(actions.closeModal()).toEqual(expectedAction) +  }) +  }) diff --git a/spec/javascripts/time_table/reducers/metas_spec.js b/spec/javascripts/time_table/reducers/metas_spec.js index adc6a9d05..61e3048db 100644 --- a/spec/javascripts/time_table/reducers/metas_spec.js +++ b/spec/javascripts/time_table/reducers/metas_spec.js @@ -2,7 +2,7 @@ var metasReducer = require('es6_browserified/time_tables/reducers/metas')  let state = {} -describe('status reducer', () => { +describe('metas reducer', () => {    beforeEach(() => {      let tag = {        id: 0, diff --git a/spec/javascripts/time_table/reducers/modal_spec.js b/spec/javascripts/time_table/reducers/modal_spec.js new file mode 100644 index 000000000..4246027b8 --- /dev/null +++ b/spec/javascripts/time_table/reducers/modal_spec.js @@ -0,0 +1,249 @@ +var modalReducer = require('es6_browserified/time_tables/reducers/modal') + +let state = {} + +describe('modal reducer', () => { +  beforeEach(() => { +    state = { +      confirmModal: {}, +      modalProps: { +        active: false, +        begin: { +          day: '01', +          month: '01', +          year: String(new Date().getFullYear()) +        }, +        end: { +          day: '01', +          month: '01', +          year: String(new Date().getFullYear()) +        }, +        index: false, +        error: '' +      }, +      type: "" +    } +  }) + +  it('should return the initial state', () => { +    expect( +      modalReducer(undefined, {}) +    ).toEqual({}) +  }) + +  it('should handle OPEN_CONFIRM_MODAL', () => { +    let callback = function(){} +    expect( +      modalReducer(state, { +        type: 'OPEN_CONFIRM_MODAL', +        callback +      }) +    ).toEqual(Object.assign({}, state, {type: "confirm", confirmModal: { callback: callback }})) +  }) + +  it('should handle CLOSE_PERIOD_FORM', () => { +    let newModalProps = Object.assign({}, state.modalProps, {active: false}) +    expect( +      modalReducer(state, { +        type: 'CLOSE_PERIOD_FORM' +      }) +    ).toEqual(Object.assign({}, state, {modalProps: newModalProps})) +  }) + +  it('should handle OPEN_EDIT_PERIOD_FORM', () => { +    let period = { +      id : 1, +      period_end : "2017-03-05", +      period_start : "2017-02-23" +    } +    let period_start = period.period_start.split('-') +    let period_end = period.period_end.split('-') + +    let index = 1 + +    let newModalProps = { +      active: true, +      begin: { +        day: period_start[2], +        month: period_start[1], +        year: period_start[0] +      }, +      end: { +        day: period_end[2], +        month: period_end[1], +        year: period_end[0] +      }, +      index: index, +      error: '' +    } +    expect( +      modalReducer(state, { +        type: 'OPEN_EDIT_PERIOD_FORM', +        period, +        index +      }) +    ).toEqual(Object.assign({}, state, {modalProps: newModalProps})) +  }) + +  it('should handle OPEN_ADD_PERIOD_FORM', () => { +    let emptyDate = { +      day: '01', +      month: '01', +      year: String(new Date().getFullYear()) +    } +    let newModalProps = Object.assign({}, state.modalProps, { +      active: true, +      begin: emptyDate, +      end: emptyDate, +      index: false, +      error: "" +    }) + +    expect( +      modalReducer(state, { +        type: 'OPEN_ADD_PERIOD_FORM' +      }) +    ).toEqual(Object.assign({}, state, {modalProps: newModalProps})) +  }) + +  it('should handle UPDATE_PERIOD_FORM', () => { +    let val = "11" +    let group = "begin" +    let selectType = "day" + +    let newModalProps = { +      active: false, +      begin: { +        day: val, +        month: '01', +        year: String(new Date().getFullYear()) +      }, +      end: { +        day: '01', +        month: '01', +        year: String(new Date().getFullYear()) +      }, +      index: false, +      error: '' +    } + +    expect( +      modalReducer(state, { +        type: 'UPDATE_PERIOD_FORM', +        val, +        group, +        selectType +      }) +    ).toEqual(Object.assign({}, state, {modalProps: newModalProps})) +  }) + +  it('should handle VALIDATE_PERIOD_FORM and throw error if period starts after the end', () => { +    let modProps = { +      active: false, +      begin: { +        day: '13', +        month: '01', +        year: String(new Date().getFullYear()) +      }, +      end: { +        day: '01', +        month: '01', +        year: String(new Date().getFullYear()) +      }, +      index: false, +      error: '' +    } +    let newModalProps = { +      active: false, +      begin: { +        day: '01', +        month: '01', +        year: String(new Date().getFullYear()) +      }, +      end: { +        day: '01', +        month: '01', +        year: String(new Date().getFullYear()) +      }, +      index: false, +      error: 'La date de départ doit être antérieure à la date de fin' +    } + +    let ttperiods = [] + +    expect( +      modalReducer(state, { +        type: 'VALIDATE_PERIOD_FORM', +        modalProps : modProps, +        timeTablePeriods: ttperiods +      }) +    ).toEqual(Object.assign({}, state, {modalProps: newModalProps})) +  }) + +  it('should handle VALIDATE_PERIOD_FORM and throw error if periods overlap', () => { +    let state2 = { +      confirmModal: {}, +      modalProps: { +        active: false, +        begin: { +          day: '03', +          month: '05', +          year: '2017' +        }, +        end: { +          day: '09', +          month: '05', +          year: '2017' +        }, +        index: false, +        error: '' +      }, +      type: '' +    } +    let modProps2 = { +      active: false, +      begin: { +        day: '03', +        month: '05', +        year: '2017' +      }, +      end: { +        day: '09', +        month: '05', +        year: '2017' +      }, +      index: false, +      error: '' +    } +    let ttperiods2 = [ +      {id: 261, period_start: '2017-02-23', period_end: '2017-03-05'}, +      {id: 262, period_start: '2017-03-15', period_end: '2017-03-25'}, +      {id: 264, period_start: '2017-04-24', period_end: '2017-05-04'}, +      {id: 265, period_start: '2017-05-14', period_end: '2017-05-24'} +    ] + +    let newModalProps2 = { +      active: true, +      begin: { +        day: '03', +        month: '05', +        year: '2017' +      }, +      end: { +        day: '09', +        month: '05', +        year: '2017' +      }, +      index: false, +      error: "Les périodes ne peuvent pas se chevaucher" +    } + +    expect( +      modalReducer(state2, { +        type: 'VALIDATE_PERIOD_FORM', +        modalProps : modProps2, +        timeTablePeriods: ttperiods2 +      }) +    ).toEqual(Object.assign({}, state2, {modalProps: newModalProps2})) +  }) +}) diff --git a/spec/javascripts/time_table/reducers/pagination_spec.js b/spec/javascripts/time_table/reducers/pagination_spec.js new file mode 100644 index 000000000..740ded3ac --- /dev/null +++ b/spec/javascripts/time_table/reducers/pagination_spec.js @@ -0,0 +1,128 @@ +var paginationReducer = require('es6_browserified/time_tables/reducers/pagination') + +const dispatch = function(){} + +let pagination = { +  currentPage: "1982-02-15", +  periode_range: ["1982-02-01", "1982-02-02", "1982-02-03"], +  stateChanged: false +} + +let state = {} + +describe('pagination reducer', () => { +  beforeEach(() => { +    state = { +      currentPage: "", +      periode_range: [], +      stateChanged: false +    } +  }) + +  it('should return the initial state', () => { +    expect( +      paginationReducer(undefined, {}) +    ).toEqual({}) +  }) + +  it('should handle RECEIVE_TIME_TABLES', () => { +    let json = [{ +      current_periode_range: "1982-02-15", +      periode_range: ["1982-02-01", "1982-02-02", "1982-02-03"] +    }] +    expect( +      paginationReducer(state, { +        type: 'RECEIVE_TIME_TABLES', +        json +      }) +    ).toEqual(Object.assign({}, state, {currentPage: json.current_periode_range, periode_range: json.periode_range})) +  }) + +  it('should handle GO_TO_PREVIOUS_PAGE', () => { +    let nextPage = nextPage ? 1 : -1 +    let newPage = pagination.periode_range[pagination.periode_range.indexOf(pagination.currentPage) + nextPage] + +    expect( +      paginationReducer(state, { +        type: 'GO_TO_PREVIOUS_PAGE', +        dispatch, +        pagination, +        nextPage: false +      }) +    ).toEqual(Object.assign({}, state,  {currentPage : newPage, stateChanged: false})) +  }) +  it('should handle GO_TO_NEXT_PAGE', () => { +    let nextPage = nextPage ? 1 : -1 +    let newPage = pagination.periode_range[pagination.periode_range.indexOf(pagination.currentPage) + nextPage] + +    expect( +      paginationReducer(state, { +        type: 'GO_TO_NEXT_PAGE', +        dispatch, +        pagination, +        nextPage: false +      }) +    ).toEqual(Object.assign({}, state,  {currentPage : newPage, stateChanged: false})) +  }) + +  it('should handle CHANGE_PAGE', () => { +    let page = "1982-02-15" +    expect( +      paginationReducer(state, { +        type: 'CHANGE_PAGE', +        dispatch, +        page +      }) +    ).toEqual(Object.assign({}, state, {currentPage : page, stateChanged: false})) +  }) + +  it('should handle INCLUDE_DATE_IN_PERIOD', () => { +    expect( +      paginationReducer(state, { +        type: 'INCLUDE_DATE_IN_PERIOD' +      }) +    ).toEqual(Object.assign({}, state, {stateChanged: true})) +  }) +  it('should handle EXCLUDE_DATE_FROM_PERIOD', () => { +    expect( +      paginationReducer(state, { +        type: 'EXCLUDE_DATE_FROM_PERIOD' +      }) +    ).toEqual(Object.assign({}, state, {stateChanged: true})) +  }) +  it('should handle DELETE_PERIOD', () => { +    expect( +      paginationReducer(state, { +        type: 'DELETE_PERIOD' +      }) +    ).toEqual(Object.assign({}, state, {stateChanged: true})) +  }) +  it('should handle VALIDATE_PERIOD_FORM', () => { +    expect( +      paginationReducer(state, { +        type: 'VALIDATE_PERIOD_FORM' +      }) +    ).toEqual(Object.assign({}, state, {stateChanged: true})) +  }) +  it('should handle UPDATE_COMMENT', () => { +    expect( +      paginationReducer(state, { +        type: 'UPDATE_COMMENT' +      }) +    ).toEqual(Object.assign({}, state, {stateChanged: true})) +  }) +  it('should handle UPDATE_COLOR', () => { +    expect( +      paginationReducer(state, { +        type: 'UPDATE_COLOR' +      }) +    ).toEqual(Object.assign({}, state, {stateChanged: true})) +  }) +  it('should handle UPDATE_DAY_TYPES', () => { +    expect( +      paginationReducer(state, { +        type: 'UPDATE_DAY_TYPES' +      }) +    ).toEqual(Object.assign({}, state, {stateChanged: true})) +  }) +}) diff --git a/spec/javascripts/time_table/reducers/status_spec.js b/spec/javascripts/time_table/reducers/status_spec.js new file mode 100644 index 000000000..f000324cc --- /dev/null +++ b/spec/javascripts/time_table/reducers/status_spec.js @@ -0,0 +1,50 @@ +var statusReducer = require('es6_browserified/time_tables/reducers/status') + +let state = {} + +describe('status reducer', () => { +  beforeEach(() => { +    state = { +      actionType: "edit", +      fetchSuccess: true, +      isFetching: false +    } +  }) + +  it('should return the initial state', () => { +    expect( +      statusReducer(undefined, {}) +    ).toEqual({}) +  }) + +  it('should handle UNAVAILABLE_SERVER', () => { +    expect( +      statusReducer(state, { +        type: 'UNAVAILABLE_SERVER' +      }) +    ).toEqual(Object.assign({}, state, {fetchSuccess: false})) +  }) + +  it('should handle FETCH_API', () => { +    expect( +      statusReducer(state, { +        type: 'FETCH_API' +      }) +    ).toEqual(Object.assign({}, state, {isFetching: true})) +  }) + +  it('should handle RECEIVE_TIME_TABLES', () => { +    expect( +      statusReducer(state, { +        type: 'RECEIVE_TIME_TABLES' +      }) +    ).toEqual(Object.assign({}, state, {fetchSuccess: true, isFetching: false})) +  }) +  it('should handle RECEIVE_MONTH', () => { +    expect( +      statusReducer(state, { +        type: 'RECEIVE_MONTH' +      }) +    ).toEqual(Object.assign({}, state, {fetchSuccess: true, isFetching: false})) +  }) +}) diff --git a/spec/javascripts/time_table/reducers/timetable_spec.js b/spec/javascripts/time_table/reducers/timetable_spec.js new file mode 100644 index 000000000..0b418a52e --- /dev/null +++ b/spec/javascripts/time_table/reducers/timetable_spec.js @@ -0,0 +1,184 @@ +require('whatwg-fetch') +var timetableReducer = require('es6_browserified/time_tables/reducers/timetable') + +let state = {} +const dispatch = function(){} +let arrDayTypes = [true, true, true, true, true, true, true] +let strDayTypes = 'LuMaMeJeVeSaDi' +let time_table_periods = [{"id":261,"period_start":"2017-02-23","period_end":"2017-03-05"},{"id":262,"period_start":"2017-03-15","period_end":"2017-03-25"},{"id":263,"period_start":"2017-04-04","period_end":"2017-04-14"},{"id":264,"period_start":"2017-04-24","period_end":"2017-05-04"},{"id":265,"period_start":"2017-05-14","period_end":"2017-05-24"}] +let current_periode_range = "2017-05-01" +let periode_range = ["2014-05-01","2014-06-01","2014-07-01","2014-08-01","2014-09-01","2014-10-01","2014-11-01","2014-12-01","2015-01-01","2015-02-01","2015-03-01","2015-04-01","2015-05-01","2015-06-01","2015-07-01","2015-08-01","2015-09-01","2015-10-01","2015-11-01","2015-12-01","2016-01-01","2016-02-01","2016-03-01","2016-04-01","2016-05-01","2016-06-01","2016-07-01","2016-08-01","2016-09-01","2016-10-01","2016-11-01","2016-12-01","2017-01-01","2017-02-01","2017-03-01","2017-04-01","2017-05-01","2017-06-01","2017-07-01","2017-08-01","2017-09-01","2017-10-01","2017-11-01","2017-12-01","2018-01-01","2018-02-01","2018-03-01","2018-04-01","2018-05-01","2018-06-01","2018-07-01","2018-08-01","2018-09-01","2018-10-01","2018-11-01","2018-12-01","2019-01-01","2019-02-01","2019-03-01","2019-04-01","2019-05-01","2019-06-01","2019-07-01","2019-08-01","2019-09-01","2019-10-01","2019-11-01","2019-12-01","2020-01-01","2020-02-01","2020-03-01","2020-04-01","2020-05-01"] +let current_month = [{"day":"lundi","date":"2017-05-01","wday":1,"wnumber":"18","mday":1,"include_date":false,"excluded_date":false},{"day":"mardi","date":"2017-05-02","wday":2,"wnumber":"18","mday":2,"include_date":false,"excluded_date":false},{"day":"mercredi","date":"2017-05-03","wday":3,"wnumber":"18","mday":3,"include_date":false,"excluded_date":false},{"day":"jeudi","date":"2017-05-04","wday":4,"wnumber":"18","mday":4,"include_date":false,"excluded_date":false},{"day":"vendredi","date":"2017-05-05","wday":5,"wnumber":"18","mday":5,"include_date":false,"excluded_date":false},{"day":"samedi","date":"2017-05-06","wday":6,"wnumber":"18","mday":6,"include_date":false,"excluded_date":false},{"day":"dimanche","date":"2017-05-07","wday":0,"wnumber":"18","mday":7,"include_date":false,"excluded_date":false},{"day":"lundi","date":"2017-05-08","wday":1,"wnumber":"19","mday":8,"include_date":false,"excluded_date":false},{"day":"mardi","date":"2017-05-09","wday":2,"wnumber":"19","mday":9,"include_date":false,"excluded_date":false},{"day":"mercredi","date":"2017-05-10","wday":3,"wnumber":"19","mday":10,"include_date":false,"excluded_date":false},{"day":"jeudi","date":"2017-05-11","wday":4,"wnumber":"19","mday":11,"include_date":false,"excluded_date":false},{"day":"vendredi","date":"2017-05-12","wday":5,"wnumber":"19","mday":12,"include_date":false,"excluded_date":false},{"day":"samedi","date":"2017-05-13","wday":6,"wnumber":"19","mday":13,"include_date":false,"excluded_date":false},{"day":"dimanche","date":"2017-05-14","wday":0,"wnumber":"19","mday":14,"include_date":false,"excluded_date":false},{"day":"lundi","date":"2017-05-15","wday":1,"wnumber":"20","mday":15,"include_date":false,"excluded_date":false},{"day":"mardi","date":"2017-05-16","wday":2,"wnumber":"20","mday":16,"include_date":false,"excluded_date":false},{"day":"mercredi","date":"2017-05-17","wday":3,"wnumber":"20","mday":17,"include_date":false,"excluded_date":false},{"day":"jeudi","date":"2017-05-18","wday":4,"wnumber":"20","mday":18,"include_date":false,"excluded_date":false},{"day":"vendredi","date":"2017-05-19","wday":5,"wnumber":"20","mday":19,"include_date":false,"excluded_date":false},{"day":"samedi","date":"2017-05-20","wday":6,"wnumber":"20","mday":20,"include_date":false,"excluded_date":false},{"day":"dimanche","date":"2017-05-21","wday":0,"wnumber":"20","mday":21,"include_date":false,"excluded_date":false},{"day":"lundi","date":"2017-05-22","wday":1,"wnumber":"21","mday":22,"include_date":false,"excluded_date":false},{"day":"mardi","date":"2017-05-23","wday":2,"wnumber":"21","mday":23,"include_date":false,"excluded_date":false},{"day":"mercredi","date":"2017-05-24","wday":3,"wnumber":"21","mday":24,"include_date":false,"excluded_date":false},{"day":"jeudi","date":"2017-05-25","wday":4,"wnumber":"21","mday":25,"include_date":false,"excluded_date":false},{"day":"vendredi","date":"2017-05-26","wday":5,"wnumber":"21","mday":26,"include_date":false,"excluded_date":false},{"day":"samedi","date":"2017-05-27","wday":6,"wnumber":"21","mday":27,"include_date":false,"excluded_date":false},{"day":"dimanche","date":"2017-05-28","wday":0,"wnumber":"21","mday":28,"include_date":false,"excluded_date":false},{"day":"lundi","date":"2017-05-29","wday":1,"wnumber":"22","mday":29,"include_date":false,"excluded_date":false},{"day":"mardi","date":"2017-05-30","wday":2,"wnumber":"22","mday":30,"include_date":false,"excluded_date":false},{"day":"mercredi","date":"2017-05-31","wday":3,"wnumber":"22","mday":31,"include_date":false,"excluded_date":false}] + +let newCurrentMonth = [{"day":"lundi","date":"2017-05-01","wday":1,"wnumber":"18","mday":1,"include_date":false,"excluded_date":false,"in_periods":true},{"day":"mardi","date":"2017-05-02","wday":2,"wnumber":"18","mday":2,"include_date":false,"excluded_date":false,"in_periods":true},{"day":"mercredi","date":"2017-05-03","wday":3,"wnumber":"18","mday":3,"include_date":false,"excluded_date":false,"in_periods":true},{"day":"jeudi","date":"2017-05-04","wday":4,"wnumber":"18","mday":4,"include_date":false,"excluded_date":false,"in_periods":true},{"day":"vendredi","date":"2017-05-05","wday":5,"wnumber":"18","mday":5,"include_date":false,"excluded_date":false,"in_periods":false},{"day":"samedi","date":"2017-05-06","wday":6,"wnumber":"18","mday":6,"include_date":false,"excluded_date":false,"in_periods":false},{"day":"dimanche","date":"2017-05-07","wday":0,"wnumber":"18","mday":7,"include_date":false,"excluded_date":false,"in_periods":false},{"day":"lundi","date":"2017-05-08","wday":1,"wnumber":"19","mday":8,"include_date":false,"excluded_date":false,"in_periods":false},{"day":"mardi","date":"2017-05-09","wday":2,"wnumber":"19","mday":9,"include_date":false,"excluded_date":false,"in_periods":false},{"day":"mercredi","date":"2017-05-10","wday":3,"wnumber":"19","mday":10,"include_date":false,"excluded_date":false,"in_periods":false},{"day":"jeudi","date":"2017-05-11","wday":4,"wnumber":"19","mday":11,"include_date":false,"excluded_date":false,"in_periods":false},{"day":"vendredi","date":"2017-05-12","wday":5,"wnumber":"19","mday":12,"include_date":false,"excluded_date":false,"in_periods":false},{"day":"samedi","date":"2017-05-13","wday":6,"wnumber":"19","mday":13,"include_date":false,"excluded_date":false,"in_periods":false},{"day":"dimanche","date":"2017-05-14","wday":0,"wnumber":"19","mday":14,"include_date":false,"excluded_date":false,"in_periods":true},{"day":"lundi","date":"2017-05-15","wday":1,"wnumber":"20","mday":15,"include_date":false,"excluded_date":false,"in_periods":true},{"day":"mardi","date":"2017-05-16","wday":2,"wnumber":"20","mday":16,"include_date":false,"excluded_date":false,"in_periods":true},{"day":"mercredi","date":"2017-05-17","wday":3,"wnumber":"20","mday":17,"include_date":false,"excluded_date":false,"in_periods":true},{"day":"jeudi","date":"2017-05-18","wday":4,"wnumber":"20","mday":18,"include_date":false,"excluded_date":false,"in_periods":true},{"day":"vendredi","date":"2017-05-19","wday":5,"wnumber":"20","mday":19,"include_date":false,"excluded_date":false,"in_periods":true},{"day":"samedi","date":"2017-05-20","wday":6,"wnumber":"20","mday":20,"include_date":false,"excluded_date":false,"in_periods":true},{"day":"dimanche","date":"2017-05-21","wday":0,"wnumber":"20","mday":21,"include_date":false,"excluded_date":false,"in_periods":true},{"day":"lundi","date":"2017-05-22","wday":1,"wnumber":"21","mday":22,"include_date":false,"excluded_date":false,"in_periods":true},{"day":"mardi","date":"2017-05-23","wday":2,"wnumber":"21","mday":23,"include_date":false,"excluded_date":false,"in_periods":true},{"day":"mercredi","date":"2017-05-24","wday":3,"wnumber":"21","mday":24,"include_date":false,"excluded_date":false,"in_periods":true},{"day":"jeudi","date":"2017-05-25","wday":4,"wnumber":"21","mday":25,"include_date":false,"excluded_date":false,"in_periods":false},{"day":"vendredi","date":"2017-05-26","wday":5,"wnumber":"21","mday":26,"include_date":false,"excluded_date":false,"in_periods":false},{"day":"samedi","date":"2017-05-27","wday":6,"wnumber":"21","mday":27,"include_date":false,"excluded_date":false,"in_periods":false},{"day":"dimanche","date":"2017-05-28","wday":0,"wnumber":"21","mday":28,"include_date":false,"excluded_date":false,"in_periods":false},{"day":"lundi","date":"2017-05-29","wday":1,"wnumber":"22","mday":29,"include_date":false,"excluded_date":false,"in_periods":false},{"day":"mardi","date":"2017-05-30","wday":2,"wnumber":"22","mday":30,"include_date":false,"excluded_date":false,"in_periods":false},{"day":"mercredi","date":"2017-05-31","wday":3,"wnumber":"22","mday":31,"include_date":false,"excluded_date":false,"in_periods":false}] + +let json = { +  current_month: current_month, +  current_periode_range: current_periode_range, +  periode_range: periode_range, +  time_table_periods: time_table_periods, +  day_types: strDayTypes +} + +describe('timetable reducer with empty state', () => { +  beforeEach(() => { +    state = { +      current_month: [], +      current_periode_range: "", +      periode_range: [], +      time_table_periods: [] +    } +  }) + +  it('should return the initial state', () => { +    expect( +      timetableReducer(undefined, {}) +    ).toEqual({}) +  }) + +  it('should handle RECEIVE_TIME_TABLES', () => { +    let newState = { +      current_month: newCurrentMonth, +      current_periode_range: current_periode_range, +      periode_range: periode_range, +      time_table_periods: time_table_periods, +    } +    expect( +      timetableReducer(state, { +        type: 'RECEIVE_TIME_TABLES', +        json +      }) +    ).toEqual(newState) +  }) +}) + +describe('timetable reducer with filled state', () => { +  beforeEach(() => { +    state = { +      current_month: newCurrentMonth, +      current_periode_range: current_periode_range, +      periode_range: periode_range, +      time_table_periods: time_table_periods, +    } +  }) + +  it('should handle RECEIVE_MONTH', () => { +    expect( +      timetableReducer(state, { +        type: 'RECEIVE_MONTH', +        json: { +          days: current_month, +          day_types: strDayTypes +        } +      }) +    ).toEqual(state) +  }) + + +  it('should handle GO_TO_PREVIOUS_PAGE', () => { +    let pagination = { +      periode_range: periode_range, +      currentPage: current_periode_range +    } +    expect( +      timetableReducer(state, { +        type: 'GO_TO_PREVIOUS_PAGE', +        dispatch, +        pagination, +        nextPage: false +      }) +    ).toEqual(Object.assign({}, state, {current_periode_range: '2017-04-01'})) +  }) + +  it('should handle GO_TO_NEXT_PAGE', () => { +    let pagination = { +      periode_range: periode_range, +      currentPage: current_periode_range +    } +    expect( +      timetableReducer(state, { +        type: 'GO_TO_NEXT_PAGE', +        dispatch, +        pagination, +        nextPage: true +      }) +    ).toEqual(Object.assign({}, state, {current_periode_range: '2017-06-01'})) +  }) + +  it('should handle CHANGE_PAGE', () => { +    const actions = { +      fetchTimeTables: function(){} +    } +    let newPage = '2017-05-01' +    expect( +      timetableReducer(state, { +        type: 'CHANGE_PAGE', +        dispatch, +        page: newPage +      }) +    ).toEqual(Object.assign({}, state, {current_periode_range: newPage})) +  }) + +  it('should handle DELETE_PERIOD', () => { +    state.time_table_periods[0].deleted = true +    expect( +      timetableReducer(state, { +        type: 'DELETE_PERIOD', +        index: 0, +        dayTypes: arrDayTypes +      }) +    ).toEqual(state) +  }) + +  it('should handle INCLUDE_DATE_IN_PERIOD', () => { +    state.current_month[4].include_date = true +    expect( +      timetableReducer(state, { +        type: 'INCLUDE_DATE_IN_PERIOD', +        index: 4, +        dayTypes: arrDayTypes +      }) +    ).toEqual(state) +  }) + +  it('should handle EXCLUDE_DATE_FROM_PERIOD', () => { +    state.current_month[0].excluded_date = true +    expect( +      timetableReducer(state, { +        type: 'EXCLUDE_DATE_FROM_PERIOD', +        index: 0, +        dayTypes: arrDayTypes +      }) +    ).toEqual(state) +  }) + +  it('should handle VALIDATE_PERIOD_FORM', () => { +    state.current_month[13].in_periods = false +    state.time_table_periods[4].period_start = '2017-05-15' +    let modalProps = { +      active: false, +      begin: { +        day: '15', +        month: '05', +        year: '2017' +      }, +      end: { +        day: '24', +        month: '05', +        year: '2017' +      }, +      error: '', +      index: 4 +    } +    expect( +      timetableReducer(state, { +        type: 'VALIDATE_PERIOD_FORM', +        modalProps: modalProps, +        timeTablePeriods: state.time_table_periods, +        metas: { +          day_types: arrDayTypes +        } +      }) +    ).toEqual(state) +  }) +}) diff --git a/spec/models/chouette/route/route_base_spec.rb b/spec/models/chouette/route/route_base_spec.rb new file mode 100644 index 000000000..08f201022 --- /dev/null +++ b/spec/models/chouette/route/route_base_spec.rb @@ -0,0 +1,69 @@ +RSpec.describe Chouette::Route, :type => :model do + +  subject { create(:route) } + +  describe '#objectid' do +    subject { super().objectid } +    it { is_expected.to be_kind_of(Chouette::ObjectId) } +  end + +  it { is_expected.to enumerize(:direction).in(:straight_forward, :backward, :clockwise, :counter_clockwise, :north, :north_west, :west, :south_west, :south, :south_east, :east, :north_east) } +  it { is_expected.to enumerize(:wayback).in(:straight_forward, :backward) } + +  #it { is_expected.to validate_presence_of :name } +  it { is_expected.to validate_presence_of :line } +  it { is_expected.to validate_uniqueness_of :objectid } +  #it { is_expected.to validate_presence_of :wayback_code } +  #it { is_expected.to validate_presence_of :direction_code } +  it { is_expected.to validate_inclusion_of(:direction).in_array(%i(straight_forward backward clockwise counter_clockwise north north_west west south_west south south_east east north_east)) } +  it { is_expected.to validate_inclusion_of(:wayback).in_array(%i(straight_forward backward)) } + +  context "reordering methods" do +    let(:bad_stop_point_ids){subject.stop_points.map { |sp| sp.id + 1}} +    let(:ident){subject.stop_points.map(&:id)} +    let(:first_last_swap){ [ident.last] + ident[1..-2] + [ident.first]} + +    describe "#reorder!" do +      context "invalid stop_point_ids" do +        let(:new_stop_point_ids) { bad_stop_point_ids} +        it { expect(subject.reorder!( new_stop_point_ids)).to be_falsey} +      end + +      context "swaped last and first stop_point_ids" do +        let!(:new_stop_point_ids) { first_last_swap} +        let!(:old_stop_point_ids) { subject.stop_points.map(&:id) } +        let!(:old_stop_area_ids) { subject.stop_areas.map(&:id) } + +        it "should keep stop_point_ids order unchanged" do +          expect(subject.reorder!( new_stop_point_ids)).to be_truthy +          expect(subject.stop_points.map(&:id)).to eq( old_stop_point_ids) +        end +        # This test is no longer relevant, as reordering is done with Reactux +        # it "should have changed stop_area_ids order" do +        #   expect(subject.reorder!( new_stop_point_ids)).to be_truthy +        #   subject.reload +        #   expect(subject.stop_areas.map(&:id)).to eq( [old_stop_area_ids.last] + old_stop_area_ids[1..-2] + [old_stop_area_ids.first]) +        # end +      end +    end + +    describe "#stop_point_permutation?" do +      context "invalid stop_point_ids" do +        let( :new_stop_point_ids ) { bad_stop_point_ids} +        it { is_expected.not_to be_stop_point_permutation( new_stop_point_ids)} +      end +      context "unchanged stop_point_ids" do +        let(:new_stop_point_ids) { ident} +        it { is_expected.to be_stop_point_permutation( new_stop_point_ids)} +      end +      context "swaped last and first stop_point_ids" do +        let(:new_stop_point_ids) { first_last_swap} +        it { is_expected.to be_stop_point_permutation( new_stop_point_ids)} +      end +    end +  end + +end + + + diff --git a/spec/models/chouette/route/route_destroy_spec.rb b/spec/models/chouette/route/route_destroy_spec.rb new file mode 100644 index 000000000..930ce521a --- /dev/null +++ b/spec/models/chouette/route/route_destroy_spec.rb @@ -0,0 +1,45 @@ +RSpec.describe Chouette::Route, :type => :model do + +  subject { create( :route_with_journey_patterns ) } + + +  context "delete a route" do +    let( :vehicle_journey ){ create :vehicle_journey } + +    it "deletes the associated journey_patterns" do +      expected_delta = subject.journey_patterns.count +      expect( expected_delta > 0 ).to eq(true) +      expect{ subject.destroy }.to change{Chouette::JourneyPattern.count}.by -expected_delta +    end + +    it "deletes the associated stop_points" do +      expected_delta = subject.stop_points.count +      expect( expected_delta > 0 ).to eq(true) +      expect{ subject.destroy }.to change{Chouette::StopPoint.count}.by -expected_delta +    end + +    it "does not delete the associated stop_areas" do +      count = subject.stop_points.count +      expect( count > 0 ).to eq(true) +      expect{ subject.destroy }.not_to change{Chouette::StopArea.count} +    end + +    it "deletes the associated vehicle_journeys" do +      vehicle_journey +      expect{ vehicle_journey.route.destroy}.to change{Chouette::VehicleJourney.count}.by -1 +    end + +    it "does not delete the corresponding time_tables" do +      tt = create :time_table +      tt.vehicle_journeys << vehicle_journey +      tables = vehicle_journey.route.time_tables +      expect( tables ).not_to be_empty +      expect{ vehicle_journey.route.destroy }.not_to change{Chouette::TimeTable.count} +    end + +    it "does not delete the associated line" do +      expect( subject.line ).not_to be_nil +      expect{ subject.destroy }.not_to change{Chouette::Line.count} +    end +  end +end diff --git a/spec/models/chouette/route_spec.rb b/spec/models/chouette/route/route_stop_points_spec.rb index 6138f28b9..03c53b4cf 100644 --- a/spec/models/chouette/route_spec.rb +++ b/spec/models/chouette/route/route_stop_points_spec.rb @@ -1,72 +1,10 @@ -require 'spec_helper' +RSpec.describe Chouette::Route, :type => :model do -describe Chouette::Route, :type => :model do    subject { create(:route) } -  describe '#objectid' do -    subject { super().objectid } -    it { is_expected.to be_kind_of(Chouette::ObjectId) } -  end - -  it { is_expected.to enumerize(:direction).in(:straight_forward, :backward, :clockwise, :counter_clockwise, :north, :north_west, :west, :south_west, :south, :south_east, :east, :north_east) } -  it { is_expected.to enumerize(:wayback).in(:straight_forward, :backward) } - -  #it { is_expected.to validate_presence_of :name } -  it { is_expected.to validate_presence_of :line } -  it { is_expected.to validate_uniqueness_of :objectid } -  #it { is_expected.to validate_presence_of :wayback_code } -  #it { is_expected.to validate_presence_of :direction_code } -  it { is_expected.to validate_inclusion_of(:direction).in_array(%i(straight_forward backward clockwise counter_clockwise north north_west west south_west south south_east east north_east)) } -  it { is_expected.to validate_inclusion_of(:wayback).in_array(%i(straight_forward backward)) } - -  context "reordering methods" do -    let( :bad_stop_point_ids){subject.stop_points.map { |sp| sp.id + 1}} -    let( :ident){subject.stop_points.map(&:id)} -    let( :first_last_swap){ [ident.last] + ident[1..-2] + [ident.first]} - -    describe "#reorder!" do -      context "invalid stop_point_ids" do -        let( :new_stop_point_ids) { bad_stop_point_ids} -        it { expect(subject.reorder!( new_stop_point_ids)).to be_falsey} -      end - -      context "swaped last and first stop_point_ids" do -        let!( :new_stop_point_ids) { first_last_swap} -        let!( :old_stop_point_ids) { subject.stop_points.map(&:id) } -        let!( :old_stop_area_ids) { subject.stop_areas.map(&:id) } - -        it "should keep stop_point_ids order unchanged" do -          expect(subject.reorder!( new_stop_point_ids)).to be_truthy -          expect(subject.stop_points.map(&:id)).to eq( old_stop_point_ids) -        end -        # This test is no longer relevant, as reordering is done with Reactux -        # it "should have changed stop_area_ids order" do -        #   expect(subject.reorder!( new_stop_point_ids)).to be_truthy -        #   subject.reload -        #   expect(subject.stop_areas.map(&:id)).to eq( [old_stop_area_ids.last] + old_stop_area_ids[1..-2] + [old_stop_area_ids.first]) -        # end -      end -    end - -    describe "#stop_point_permutation?" do -      context "invalid stop_point_ids" do -        let( :new_stop_point_ids) { bad_stop_point_ids} -        it { is_expected.not_to be_stop_point_permutation( new_stop_point_ids)} -      end -      context "unchanged stop_point_ids" do -        let( :new_stop_point_ids) { ident} -        it { is_expected.to be_stop_point_permutation( new_stop_point_ids)} -      end -      context "swaped last and first stop_point_ids" do -        let( :new_stop_point_ids) { first_last_swap} -        it { is_expected.to be_stop_point_permutation( new_stop_point_ids)} -      end -    end -  end -    describe "#stop_points_attributes=" do -      let( :journey_pattern) { create( :journey_pattern, :route => subject )} -      let( :vehicle_journey) { create( :vehicle_journey, :journey_pattern => journey_pattern)} +      let(:journey_pattern) { create( :journey_pattern, :route => subject )} +      let(:vehicle_journey) { create( :vehicle_journey, :journey_pattern => journey_pattern)}        def subject_stop_points_attributes            {}.tap do |hash|                subject.stop_points.each_with_index { |sp,index| hash[ index.to_s ] = sp.attributes } @@ -171,3 +109,4 @@ describe Chouette::Route, :type => :model do      end    end  end + diff --git a/spec/models/chouette/vehicle_journey_spec.rb b/spec/models/chouette/vehicle_journey_spec.rb index 3d3a948bc..b22183ab6 100644 --- a/spec/models/chouette/vehicle_journey_spec.rb +++ b/spec/models/chouette/vehicle_journey_spec.rb @@ -221,6 +221,95 @@ describe Chouette::VehicleJourney, :type => :model do      end    end +  describe ".with_stops" do +    def initialize_stop_times(vehicle_journey, &block) +      vehicle_journey +        .vehicle_journey_at_stops +        .each_with_index do |at_stop, index| +          at_stop.update( +            departure_time: at_stop.departure_time + block.call(index), +            arrival_time: at_stop.arrival_time + block.call(index) +          ) +        end +    end + +    it "selects vehicle journeys including stops in order or earliest departure time" do +      # Create later vehicle journey to give it a later id, such that it should +      # appear last if the order in the query isn't right. +      journey_late = create(:vehicle_journey) +      journey_early = create( +        :vehicle_journey, +        route: journey_late.route, +        journey_pattern: journey_late.journey_pattern +      ) + +      initialize_stop_times(journey_early) do |index| +        (index + 5).minutes +      end +      initialize_stop_times(journey_late) do |index| +        (index + 65).minutes +      end + +      expected_journey_order = [journey_early, journey_late] + +      expect( +        journey_late +          .route +          .vehicle_journeys +          .with_stops +          .to_a +      ).to eq(expected_journey_order) +    end + +    it "orders journeys with nil times at the end" do +      journey_nil = create(:vehicle_journey_empty) +      journey = create( +        :vehicle_journey, +        route: journey_nil.route, +        journey_pattern: journey_nil.journey_pattern +      ) + +      expected_journey_order = [journey, journey_nil] + +      expect( +        journey +          .route +          .vehicle_journeys +          .with_stops +          .to_a +      ).to eq(expected_journey_order) +    end + +    it "journeys that skip the first stop(s) get ordered by the time of the \ +        first stop that they make" do +      journey_missing_stop = create(:vehicle_journey) +      journey_early = create( +      :vehicle_journey, +        route: journey_missing_stop.route, +        journey_pattern: journey_missing_stop.journey_pattern +      ) + +      initialize_stop_times(journey_early) do |index| +        (index + 5).minutes +      end +      initialize_stop_times(journey_missing_stop) do |index| +        (index + 65).minutes +      end + +      journey_missing_stop.vehicle_journey_at_stops.first.destroy + +      expected_journey_order = [journey_early, journey_missing_stop] + +      expect( +        journey_missing_stop +          .route +          .vehicle_journeys +          .with_stops +          .to_a +      ).to eq(expected_journey_order) +    end +  end +    subject { create(:vehicle_journey_odd) }    describe "in_relation_to_a_journey_pattern methods" do      let!(:route) { create(:route)} diff --git a/spec/models/time_table_combination_spec.rb b/spec/models/time_table_combination_spec.rb index 46d5f8504..4c99a14fa 100644 --- a/spec/models/time_table_combination_spec.rb +++ b/spec/models/time_table_combination_spec.rb @@ -4,7 +4,37 @@ describe TimeTableCombination, :type => :model do    let!(:source){ create(:time_table)}    let!(:combined){create(:time_table)}    subject {build(:time_table_combination)} -   + +  describe '#continuous_dates' do +    it 'should group continuous dates' do +      dates = source.dates.where(in_out: true) +      expect(source.continuous_dates[0].count).to eq(dates.count) + +      # 6 more continuous date, 1 isolated date +      (10..15).each do |n| +        source.dates.create(date: Date.today + n.day, in_out: true) +      end +      source.dates.create(date: Date.today + 1.year, in_out: true) +      expect(source.reload.continuous_dates[1].count).to eq(6) +      expect(source.reload.continuous_dates[2].count).to eq(1) +    end +  end + +  describe '#convert_continuous_dates_to_periods' do +    it 'should convert continuous dates to periods' do +      (10..12).each do |n| +        source.dates.create(date: Date.today + n.day, in_out: true) +      end +      source.dates.create(date: Date.today + 1.year, in_out: true) + +      expect { +        source.reload.convert_continuous_dates_to_periods +      }.to change {source.periods.count}.by(2) + +      expect(source.reload.dates.where(in_out: true).count).to eq(1) +    end +  end +    describe "#combine" do      context "when operation is union" do        before(:each) do | 
