diff options
| author | Luc Donnet | 2017-07-04 10:21:27 +0200 |
|---|---|---|
| committer | Luc Donnet | 2017-07-04 10:21:27 +0200 |
| commit | b336585e7a6dec90848b522e7a595d69cb59f2e6 (patch) | |
| tree | 34fc82f0a876f6e109b7f018a04c692f78bdf59c | |
| parent | 4305f4ea9563a25ce29a55e7077cb86bed2bf325 (diff) | |
| parent | 433c498ac44aac684f0165265e3da437cafa3243 (diff) | |
| download | chouette-core-b336585e7a6dec90848b522e7a595d69cb59f2e6.tar.bz2 | |
Merge branch 'master' into staging
49 files changed, 605 insertions, 716 deletions
diff --git a/.gitignore b/.gitignore index 1bca870cb..0cdaa7c9f 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,8 @@ bin/ # Ignore node modules /node_modules + +# Every machine shall create its binstubs +/bin/rake +/bin/rails +/bin/rspec 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 43c40a4d5..18ed5f889 100644 --- a/app/assets/javascripts/es6_browserified/journey_patterns/components/JourneyPattern.js +++ b/app/assets/javascripts/es6_browserified/journey_patterns/components/JourneyPattern.js @@ -43,75 +43,79 @@ class JourneyPattern extends Component{ ) } + getErrors(errors) { + let err = Object.keys(errors).map((key, index) => { + return ( + <li key={index} style={{listStyleType: 'disc'}}> + <strong>{key}</strong> { errors[key] } + </li> + ) + }) + + return ( + <ul className="alert alert-danger">{err}</ul> + ) + } + render() { this.previousCity = undefined return ( - <div className={'t2e-item' + (this.props.value.deletable ? ' disabled' : '') + (this.props.value.object_id ? '' : ' to_record')}> + <div className={'t2e-item' + (this.props.value.deletable ? ' disabled' : '') + (this.props.value.object_id ? '' : ' to_record') + (this.props.value.errors ? ' has-error': '')}> {/* Errors */} - {(this.props.value.errors) && ( - <ul className='alert alert-danger small' style={{paddingLeft: 30}}> - {Object.keys(this.props.value.errors).map(function(key, i) { - return ( - <li key={i} style={{listStyleType: 'disc'}}> - <strong>'{key}'</strong> {this.props.value.errors[key]} - </li> - ) - })} - </ul> - )} + {/* this.props.value.errors ? this.getErrors(this.props.value.errors) : '' */} - <div className='th'> - <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> + <div className='th'> + <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> - <div className={this.props.value.deletable ? 'btn-group disabled' : 'btn-group'}> - <div - className={this.props.value.deletable ? 'btn dropdown-toggle disabled' : 'btn dropdown-toggle'} - data-toggle='dropdown' - > - <span className='fa fa-cog'></span> - </div> - <ul className='dropdown-menu'> - <li className={(this.props.value.deletable || this.props.status.policy['journey_patterns.edit'] == false) ? 'disabled' : ''}> - <button - type='button' - onClick={this.props.onOpenEditModal} - data-toggle='modal' - data-target='#JourneyPatternModal' - > - Editer + <div className={this.props.value.deletable ? 'btn-group disabled' : 'btn-group'}> + <div + className={this.props.value.deletable ? 'btn dropdown-toggle disabled' : 'btn dropdown-toggle'} + data-toggle='dropdown' + > + <span className='fa fa-cog'></span> + </div> + <ul className='dropdown-menu'> + <li className={(this.props.value.deletable || this.props.status.policy['journey_patterns.edit'] == false) ? 'disabled' : ''}> + <button + type='button' + onClick={this.props.onOpenEditModal} + data-toggle='modal' + data-target='#JourneyPatternModal' + > + Editer + </button> + </li> + <li className={this.props.value.object_id ? '' : 'disabled'}> + {this.vehicleJourneyURL(this.props.value.object_id)} + </li> + <li className={'delete-action' + ((this.props.status.policy['journey_patterns.edit'] == false)? ' disabled' : '')}> + <button + type='button' + disabled={(this.props.status.policy['journey_patterns.edit'] == false)? 'disabled' : ''} + onClick={(e) => { + e.preventDefault() + this.props.onDeleteJourneyPattern(this.props.index)} + } + > + <span className='fa fa-trash'></span>Supprimer </button> </li> - <li className={this.props.value.object_id ? '' : 'disabled'}> - {this.vehicleJourneyURL(this.props.value.object_id)} - </li> - <li className={'delete-action' + ((this.props.status.policy['journey_patterns.edit'] == false)? ' disabled' : '')}> - <button - type='button' - disabled={(this.props.status.policy['journey_patterns.edit'] == false)? 'disabled' : ''} - onClick={(e) => { - e.preventDefault() - this.props.onDeleteJourneyPattern(this.props.index)} - } - > - <span className='fa fa-trash'></span>Supprimer - </button> - </li> - </ul> - </div> + </ul> </div> - - {this.props.value.stop_points.map((stopPoint, i) =>{ - return ( - <div key={i} className='td'> - {this.cityNameChecker(stopPoint)} - </div> - ) - })} </div> - ) + + {this.props.value.stop_points.map((stopPoint, i) =>{ + return ( + <div key={i} className='td'> + {this.cityNameChecker(stopPoint)} + </div> + ) + })} + </div> + ) } } diff --git a/app/assets/javascripts/es6_browserified/journey_patterns/reducers/status.js b/app/assets/javascripts/es6_browserified/journey_patterns/reducers/status.js index 6241777da..d7ef12d0b 100644 --- a/app/assets/javascripts/es6_browserified/journey_patterns/reducers/status.js +++ b/app/assets/javascripts/es6_browserified/journey_patterns/reducers/status.js @@ -9,6 +9,8 @@ const status = (state = {}, action) => { return _.assign({}, state, {isFetching: true}) case 'RECEIVE_JOURNEY_PATTERNS': return _.assign({}, state, {fetchSuccess: true, isFetching: false}) + case 'RECEIVE_ERRORS': + return _.assign({}, state, {isFetching: false}) default: return state } diff --git a/app/assets/javascripts/es6_browserified/time_tables/components/PeriodForm.js b/app/assets/javascripts/es6_browserified/time_tables/components/PeriodForm.js index a8a92c522..028974fc8 100644 --- a/app/assets/javascripts/es6_browserified/time_tables/components/PeriodForm.js +++ b/app/assets/javascripts/es6_browserified/time_tables/components/PeriodForm.js @@ -61,30 +61,30 @@ const PeriodForm = ({modal, timetable, metas, onOpenAddPeriodForm, onClosePeriod <div className="nested-fields"> <div className="wrapper"> <div> - <div className={'form-group date smart_date' + (modal.modalProps.error ? ' has-error' : '')}> + <div className={'form-group date ' + (modal.modalProps.error ? ' has-error' : '')}> <div className="form-inline"> - <select value={formatNumber(modal.modalProps.begin.day)} onChange={(e) => onUpdatePeriodForm(e, 'begin', 'day')} id="q_validity_period_begin_gteq_3i" className="date required form-control"> + <select value={formatNumber(modal.modalProps.begin.day)} onChange={(e) => onUpdatePeriodForm(e, 'begin', 'day', modal.modalProps)} id="q_validity_period_begin_gteq_3i" className="date required form-control"> {makeDaysOptions(modal.modalProps.begin.day)} </select> - <select value={formatNumber(modal.modalProps.begin.month)} onChange={(e) => onUpdatePeriodForm(e, 'begin', 'month')} id="q_validity_period_begin_gteq_2i" className="date required form-control"> + <select value={formatNumber(modal.modalProps.begin.month)} onChange={(e) => onUpdatePeriodForm(e, 'begin', 'month', modal.modalProps)} id="q_validity_period_begin_gteq_2i" className="date required form-control"> {makeMonthsOptions(modal.modalProps.begin.month)} </select> - <select value={modal.modalProps.begin.year} onChange={(e) => onUpdatePeriodForm(e, 'begin', 'year')} id="q_validity_period_begin_gteq_1i" className="date required form-control"> + <select value={modal.modalProps.begin.year} onChange={(e) => onUpdatePeriodForm(e, 'begin', 'year', modal.modalProps)} id="q_validity_period_begin_gteq_1i" className="date required form-control"> {makeYearsOptions(modal.modalProps.begin.year)} </select> </div> </div> </div> <div> - <div className={'form-group date smart_date' + (modal.modalProps.error ? ' has-error' : '')}> + <div className={'form-group date ' + (modal.modalProps.error ? ' has-error' : '')}> <div className="form-inline"> - <select value={formatNumber(modal.modalProps.end.day)} onChange={(e) => onUpdatePeriodForm(e, 'end', 'day')} id="q_validity_period_end_gteq_3i" className="date required form-control"> + <select value={formatNumber(modal.modalProps.end.day)} onChange={(e) => onUpdatePeriodForm(e, 'end', 'day', modal.modalProps)} id="q_validity_period_end_gteq_3i" className="date required form-control"> {makeDaysOptions(modal.modalProps.end.day)} </select> - <select value={formatNumber(modal.modalProps.end.month)} onChange={(e) => onUpdatePeriodForm(e, 'end', 'month')} id="q_validity_period_end_gteq_2i" className="date required form-control"> + <select value={formatNumber(modal.modalProps.end.month)} onChange={(e) => onUpdatePeriodForm(e, 'end', 'month', modal.modalProps)} id="q_validity_period_end_gteq_2i" className="date required form-control"> {makeMonthsOptions(modal.modalProps.end.month)} </select> - <select value={modal.modalProps.end.year} onChange={(e) => onUpdatePeriodForm(e, 'end', 'year')} id="q_validity_period_end_gteq_1i" className="date required form-control"> + <select value={modal.modalProps.end.year} onChange={(e) => onUpdatePeriodForm(e, 'end', 'year', modal.modalProps)} id="q_validity_period_end_gteq_1i" className="date required form-control"> {makeYearsOptions(modal.modalProps.end.year)} </select> </div> diff --git a/app/assets/javascripts/es6_browserified/time_tables/containers/PeriodForm.js b/app/assets/javascripts/es6_browserified/time_tables/containers/PeriodForm.js index a7edbc328..7f2db785a 100644 --- a/app/assets/javascripts/es6_browserified/time_tables/containers/PeriodForm.js +++ b/app/assets/javascripts/es6_browserified/time_tables/containers/PeriodForm.js @@ -1,6 +1,7 @@ var connect = require('react-redux').connect var PeriodFormComponent = require('../components/PeriodForm') var actions = require('../actions') +var _ = require('lodash') const mapStateToProps = (state) => { return { @@ -18,10 +19,13 @@ const mapDispatchToProps = (dispatch) => { onClosePeriodForm: () => { dispatch(actions.closePeriodForm()) }, - onUpdatePeriodForm: (e, group, selectType) => { + onUpdatePeriodForm: (e, group, selectType, modalProps) => { dispatch(actions.updatePeriodForm(e.currentTarget.value, group, selectType)) - let selector = '#q_validity_period_' + group + '_gteq_3i' - dispatch(actions.updatePeriodForm($(selector).val(), group, 'day')) + let mProps = _.assign({}, modalProps) + mProps[group][selectType] = e.currentTarget.value + let val = window.correctDay([parseInt(mProps[group]['day']), parseInt(mProps[group]['month']), parseInt(mProps[group]['year'])]) + val = (val < 10) ? '0' + String(val) : String(val) + dispatch(actions.updatePeriodForm(val, group, 'day')) }, onValidatePeriodForm: (modalProps, timeTablePeriods, metas) => { dispatch(actions.validatePeriodForm(modalProps, timeTablePeriods, metas)) 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 e90d2d307..c30f460d8 100644 --- a/app/assets/javascripts/es6_browserified/vehicle_journeys/actions/index.js +++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/actions/index.js @@ -449,12 +449,18 @@ const actions = { if (parseInt(schedule.departure_time.minute) < 0){ hours = Math.floor(parseInt(schedule.departure_time.minute) / 60) minutes = (parseInt(schedule.departure_time.minute) % 60) + 60 + if(minutes == 60){ + minutes = 0 + } schedule.departure_time.minute = actions.simplePad(minutes, 'minute') schedule.departure_time.hour = parseInt(schedule.departure_time.hour) + hours } if (parseInt(schedule.arrival_time.minute) < 0){ hours = Math.floor(parseInt(schedule.arrival_time.minute) / 60) minutes = (parseInt(schedule.arrival_time.minute) % 60) + 60 + if(minutes == 60){ + minutes = 0 + } schedule.arrival_time.minute = actions.simplePad(minutes, 'minute') schedule.arrival_time.hour = parseInt(schedule.arrival_time.hour) + hours } diff --git a/app/assets/javascripts/routing_constraint_zones.coffee b/app/assets/javascripts/routing_constraint_zones.coffee index 8721dfb38..2fd83fa28 100644 --- a/app/assets/javascripts/routing_constraint_zones.coffee +++ b/app/assets/javascripts/routing_constraint_zones.coffee @@ -1,73 +1,17 @@ -@ITL_stoppoints = -> - routeID = $('#routing_constraint_zone_route_id').val() - - if (routeID) - origin = window.location.origin - path = window.location.pathname.split('/', 5).join('/') - reqURL = origin + path + '/routes/' + routeID + '/stop_points.json' +$ -> + update_stop_points = () -> + url = $('#routing_constraint_zone_route_id').attr("data-url") + route_id = $('#routing_constraint_zone_route_id').val() $.ajax - url: reqURL - dataType: 'json' + url: url + dataType: 'script' + data: { route_id: route_id } error: (jqXHR, textStatus, errorThrown) -> - console.log(errorThrown) - success: (collection, textStatus, jqXHR) -> - html = '' - stopAreaBaseURL = origin + window.location.pathname.split('/', 3).join('/') + '/stop_areas/' - - collection.forEach (item, index) -> - html += "<div class='nested-fields'><div class='wrapper'> - <div><a href='" + stopAreaBaseURL + item.stop_area_id + "' class='navlink' title='Voir l'arrêt'><span>" + item.name + "</span></a></div> - <div><span>" + item.city_name + " (" + item.zip_code + ")</span></div> - <div> - <span class='has_radio'> - <input type='checkbox' name='routing_constraint_zone[stop_point_ids][" + index + "]' value='" + item.id + "'> - <span class='radio-label'></span> - </span> - </div> - </div></div>" - - $('#ITL_stoppoints').find('.nested-fields').remove() - $('#ITL_stoppoints').find('.nested-head').after(html) - - # VALIDATION - selection = [] - - totalItems = collection.length - - $('#ITL_stoppoints').on 'click', "input[type='checkbox']", (e) -> - v = $(e.target).val() + console.log("ERROR") + success: (data, textStatus, jqXHR) -> + console.log("SUCCESS") - if ( $.inArray(v, selection) != -1 ) - selection.splice(selection.indexOf(v), 1) - else - selection.push(v) - - alertMsg1 = "<div class='alert alert-danger' style='margin-bottom:15px;'> - <p class='small'>Un ITL doit comporter au moins deux arrêts</p> - </div>" - alertMsg2 = "<div class='alert alert-danger' style='margin-bottom:15px;'> - <p class='small'>Un ITL ne peut recouvrir tous les arrêts d'un itinéraire</p> - </div>" - - $(document).on 'click', "input[type='submit']", (e)-> - inputName = $('#routing_constraint_zone_name').val() - - $('.alert-danger').remove() - - if ( selection.length < 2 && inputName != "" ) - e.preventDefault() - $('#routing_constraint_zone_name').closest('.form-group').removeClass('has-error').find('.help-block').remove() - $('#ITL_stoppoints').prepend(alertMsg1) - - if ( selection.length == totalItems && inputName != "" ) - e.preventDefault() - $('#routing_constraint_zone_name').closest('.form-group').removeClass('has-error').find('.help-block').remove() - $('#ITL_stoppoints').prepend(alertMsg2) - -$ -> - ITL_stoppoints() + $("#itl_form #routing_constraint_zone_route_id").on 'change', -> update_stop_points() - $('#routing_constraint_zone_route_id').on 'change', -> - $('.alert-danger').remove() - ITL_stoppoints() + update_stop_points() diff --git a/app/assets/javascripts/smart_date.coffee b/app/assets/javascripts/smart_date.coffee index afc0c7ddf..48aa1c2f9 100644 --- a/app/assets/javascripts/smart_date.coffee +++ b/app/assets/javascripts/smart_date.coffee @@ -2,8 +2,6 @@ window.legalDaysPerMonth = false: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], true: [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] - -window.legal window.correctDay = (dateValues) -> [day, month, year] = dateValues return day unless day > 0 && month > 0 && year > 0 diff --git a/app/assets/stylesheets/components/_tables.sass b/app/assets/stylesheets/components/_tables.sass index 20679a3ba..b991e7b8d 100644 --- a/app/assets/stylesheets/components/_tables.sass +++ b/app/assets/stylesheets/components/_tables.sass @@ -296,7 +296,7 @@ border-right: 1px solid rgba($grey, 0.5) .th - > div + > div:not(.btn-group) min-height: 19px > *:first-child diff --git a/app/assets/stylesheets/modules/_jp_collection.sass b/app/assets/stylesheets/modules/_jp_collection.sass index c109fc71a..f579cf87b 100644 --- a/app/assets/stylesheets/modules/_jp_collection.sass +++ b/app/assets/stylesheets/modules/_jp_collection.sass @@ -98,3 +98,33 @@ left: -23px top: 50% margin-top: -8px + + // Errors + .table-2entries .t2e-item-list + .t2e-item + position: relative + + .th .vj_tt + display: inline-block + vertical-align: top + + + .vj_tt + margin-left: 5px + + &.has-error + &:before + content: '' + position: absolute + top: 0 + left: 0 + right: 0 + bottom: 0 + border: 2px solid $red + + > .th + > div:first-child, > div:first-child + div + color: $red + + // Reset default behaviour + .form-control + border-color: #ccc diff --git a/app/controllers/routing_constraint_zones_controller.rb b/app/controllers/routing_constraint_zones_controller.rb index 9d2fd712c..1eb2ddf28 100644 --- a/app/controllers/routing_constraint_zones_controller.rb +++ b/app/controllers/routing_constraint_zones_controller.rb @@ -4,35 +4,44 @@ class RoutingConstraintZonesController < ChouetteController defaults resource_class: Chouette::RoutingConstraintZone respond_to :html, :xml, :json - before_action :check_stoppoint_param, only: [:create, :update] + # before_action :check_stoppoint_param, only: [:create, :update] belongs_to :referential do belongs_to :line, parent_class: Chouette::Line end - def index - @routing_constraint_zones = collection + def show + show! do |format| + @routing_constraint_zone = @routing_constraint_zone.decorate(context: { + referential: current_referential, + line: parent.id + }) + end end - def show - @routing_constraint_zone = collection.find(params[:id]) - @routing_constraint_zone = @routing_constraint_zone.decorate(context: { - referential: @referential, - line: @line - }) + def new + new! do |format| + format.html + @route = @line.routes.find params[:route_id] if params[:route_id] + format.js + end end protected + alias_method :routing_constraint_zone, :resource + def collection - @q = resource.routing_constraint_zones.search(params[:q]) + @q = current_referential.routing_constraint_zones.search(params[:q]) - if sort_column && sort_direction - @routing_constraint_zones ||= @q.result(distinct: true).order(sort_column + ' ' + sort_direction) - else - @routing_constraint_zones ||= @q.result(distinct: true).order(:name) + @routing_constraint_zones ||= begin + if sort_column && sort_direction + routing_constraint_zones = @q.result(distinct: true).order(sort_column + ' ' + sort_direction) + else + routing_constraint_zones = @q.result(distinct: true).order(:name) + end + routing_constraint_zones = routing_constraint_zones.paginate(page: params[:page], per_page: 10) end - @routing_constraint_zones = @routing_constraint_zones.paginate(page: params[:page], per_page: 10) end private @@ -43,11 +52,6 @@ class RoutingConstraintZonesController < ChouetteController %w[asc desc].include?(params[:direction]) ? params[:direction] : 'asc' end - def resource - @referential = Referential.find params[:referential_id] - @line = @referential.lines.find params[:line_id] - end - def routing_constraint_zone_params params.require(:routing_constraint_zone).permit( :name, diff --git a/app/controllers/time_tables_controller.rb b/app/controllers/time_tables_controller.rb index 3704f2885..6d2639981 100644 --- a/app/controllers/time_tables_controller.rb +++ b/app/controllers/time_tables_controller.rb @@ -49,8 +49,8 @@ class TimeTablesController < ChouetteController calendar.dates.each_with_index do |date, i| @time_table.dates << Chouette::TimeTableDate.new(date: date, position: i, in_out: true) end - calendar.date_ranges.each_with_index do |date_range, i| - @time_table.periods << Chouette::TimeTablePeriod.new(period_start: date_range.begin, period_end: date_range.end, position: i) + calendar.periods.each_with_index do |period, i| + @time_table.periods << Chouette::TimeTablePeriod.new(period_start: period.begin, period_end: period.end, position: i) end end diff --git a/app/controllers/workbenches_controller.rb b/app/controllers/workbenches_controller.rb index d03841356..30b98eec0 100644 --- a/app/controllers/workbenches_controller.rb +++ b/app/controllers/workbenches_controller.rb @@ -10,18 +10,10 @@ class WorkbenchesController < BreadcrumbController scope = ransack_periode(scope) scope = ransack_status(scope) - # Ignore archived_at_not_null/archived_at_null managed by ransack_status scope - # We clone params[:q] so we can delete fake ransack filter arguments before calling search method, - # which will allow us to preserve params[:q] for sorting - ransack_params = params[:q].merge(archived_at_not_null: nil, archived_at_null: nil).clone - ransack_params.delete('associated_lines_id_eq') - - @q = scope.ransack(ransack_params) - @wbench_refs = sort_result(@q.result).paginate(page: params[:page], per_page: 30) - @wbench_refs = ModelDecorator.decorate( - @wbench_refs, - with: ReferentialDecorator - ) + @q_for_form = scope.ransack(params[:q]) + @q_for_result = scope.ransack(ransack_params) + @wbench_refs = sort_result(@q_for_result.result).paginate(page: params[:page], per_page: 30) + @wbench_refs = ModelDecorator.decorate(@wbench_refs, with: ReferentialDecorator) show! do build_breadcrumb :show @@ -55,16 +47,13 @@ class WorkbenchesController < BreadcrumbController end def query_params - if params[:q].present? - params[:q].delete_if { |query, value| value.blank? } - else - params[:q] = { "archived_at_not_null"=>"1", "archived_at_null"=>"1" } - end + params[:q] ||= {} + params[:q].delete_if { |query, value| value.blank? } end # Fake ransack filter def ransack_associated_lines scope - if params[:q] && params[:q]['associated_lines_id_eq'] + if params[:q]['associated_lines_id_eq'] scope = scope.include_metadatas_lines([params[:q]['associated_lines_id_eq']]) end scope @@ -89,29 +78,28 @@ class WorkbenchesController < BreadcrumbController scope end - # Fake (again) ransack filter + # Fake ransack filter def ransack_status scope - return scope unless params[:q] - - archived_at_not_null = params[:q]['archived_at_not_null'] == '1' - archived_at_null = params[:q]['archived_at_null'] == '1' + archived = !params[:q]['archived_at_not_null'].to_i.zero? + unarchived = !params[:q]['archived_at_null'].to_i.zero? - if !archived_at_not_null and !archived_at_null - return scope.none - end - - if archived_at_not_null and archived_at_null - return scope - end - - if archived_at_null - return scope.where(archived_at: nil) - end - - if archived_at_not_null - return scope.where("archived_at is not null") - end + # Both status checked, means no filter + return scope unless archived || unarchived + return scope if archived && unarchived + scope = scope.where(archived_at: nil) if unarchived + scope = scope.where("archived_at is not null") if archived scope end + + # Ignore archived_at_not_null/archived_at_null managed by ransack_status scope + # We clone params[:q] so we can delete fake ransack filter arguments before calling search method, + # which will allow us to preserve params[:q] for sorting + def ransack_params + copy_params = params[:q].clone + copy_params.delete('associated_lines_id_eq') + copy_params.delete('archived_at_not_null') + copy_params.delete('archived_at_null') + copy_params + end end diff --git a/app/helpers/links_helper.rb b/app/helpers/links_helper.rb index 683b66a52..4fb7a797d 100644 --- a/app/helpers/links_helper.rb +++ b/app/helpers/links_helper.rb @@ -1,5 +1,5 @@ module LinksHelper def destroy_link_content(translation_key = 'actions.destroy') - content_tag(:span, nil, class: 'fa fa-trash') + t(translation_key) + content_tag(:span, nil, class: 'fa fa-trash mr-xs') + t(translation_key) end end diff --git a/app/models/calendar.rb b/app/models/calendar.rb index 70fea9619..fb575515a 100644 --- a/app/models/calendar.rb +++ b/app/models/calendar.rb @@ -28,7 +28,7 @@ class Calendar < ActiveRecord::Base self.dates.each do |d| tt.dates << Chouette::TimeTableDate.new(date: d, in_out: true) end - self.date_ranges.each do |p| + self.periods.each do |p| tt.periods << Chouette::TimeTablePeriod.new(period_start: p.begin, period_end: p.end) end end diff --git a/app/models/chouette/journey_pattern.rb b/app/models/chouette/journey_pattern.rb index a146dcff1..868d8121e 100644 --- a/app/models/chouette/journey_pattern.rb +++ b/app/models/chouette/journey_pattern.rb @@ -27,7 +27,7 @@ class Chouette::JourneyPattern < Chouette::TridentActiveRecord jp = find_by(objectid: item['object_id']) || state_create_instance(route, item) next if item['deletable'] && jp.persisted? && jp.destroy # Update attributes and stop_points associations - jp.update_attributes(state_permited_attributes(item)) + jp.update_attributes(state_permited_attributes(item)) unless item['new_record'] jp.state_stop_points_update(item) if !jp.errors.any? && jp.persisted? item['errors'] = jp.errors if jp.errors.any? end @@ -53,6 +53,13 @@ class Chouette::JourneyPattern < Chouette::TridentActiveRecord def self.state_create_instance route, item # Flag new record, so we can unset object_id if transaction rollback jp = route.journey_patterns.create(state_permited_attributes(item)) + + # FIXME + # DefaultAttributesSupport will trigger some weird validation on after save + # wich will call to valid?, wich will populate errors + # In this case, we mark jp to be valid if persisted? return true + jp.errors.clear if jp.persisted? + item['object_id'] = jp.objectid item['new_record'] = true jp diff --git a/app/models/chouette/time_table.rb b/app/models/chouette/time_table.rb index c566452f4..d907d797e 100644 --- a/app/models/chouette/time_table.rb +++ b/app/models/chouette/time_table.rb @@ -496,33 +496,39 @@ class Chouette::TimeTable < Chouette::TridentActiveRecord self.convert_continuous_dates_to_periods end + def included_days_in_dates_and_periods + in_day = self.dates.select {|d| d.in_out }.map(&:date) + out_day = self.dates.select {|d| !d.in_out }.map(&:date) + + in_periods = self.periods.map{|p| (p.period_start..p.period_end).to_a }.flatten + days = in_periods + in_day + days -= out_day + days + end + # remove dates form tt which aren't in another_tt def intersect!(another_tt) transaction do - - # transform tt as effective dates and get common ones - days = another_tt.intersects(self.effective_days) & self.intersects(another_tt.effective_days) + days = self.included_days_in_dates_and_periods & another_tt.included_days_in_dates_and_periods self.dates.clear - days.each {|d| self.dates << Chouette::TimeTableDate.new( :date =>d, :in_out => true)} self.periods.clear - self.dates.to_a.sort! { |a,b| a.date <=> b.date} + days.sort.each do |d| + self.dates << Chouette::TimeTableDate.new(:date => d, :in_out => true) + end self.save! end self.convert_continuous_dates_to_periods end - + # remove days from another calendar def disjoin!(another_tt) transaction do - # remove days from another calendar - days_to_exclude = self.intersects(another_tt.effective_days) - days = self.effective_days - days_to_exclude + days = self.included_days_in_dates_and_periods - another_tt.included_days_in_dates_and_periods self.dates.clear self.periods.clear - days.each {|d| self.dates << Chouette::TimeTableDate.new( :date =>d, :in_out => true)} - - self.dates.to_a.sort! { |a,b| a.date <=> b.date} - self.periods.to_a.sort! { |a,b| a.period_start <=> b.period_start} + days.sort.each do |d| + self.dates << Chouette::TimeTableDate.new(:date => d, :in_out => true) + end self.save! end self.convert_continuous_dates_to_periods diff --git a/app/models/chouette/vehicle_journey.rb b/app/models/chouette/vehicle_journey.rb index 3a5851310..71c339780 100644 --- a/app/models/chouette/vehicle_journey.rb +++ b/app/models/chouette/vehicle_journey.rb @@ -23,15 +23,14 @@ module Chouette validates_presence_of :route validates_presence_of :journey_pattern + validates :vehicle_journey_at_stops, :vjas_departure_time_must_be_before_next_stop_arrival_time, + vehicle_journey_at_stops_are_in_increasing_time_order: true + validates_presence_of :number has_many :vehicle_journey_at_stops, -> { includes(:stop_point).order("stop_points.position") }, :dependent => :destroy has_and_belongs_to_many :time_tables, :class_name => 'Chouette::TimeTable', :foreign_key => "vehicle_journey_id", :association_foreign_key => "time_table_id" has_many :stop_points, -> { order("stop_points.position") }, :through => :vehicle_journey_at_stops - validates :vehicle_journey_at_stops, :vjas_departure_time_must_be_before_next_stop_arrival_time, - vehicle_journey_at_stops_are_in_increasing_time_order: true - validates_presence_of :number - before_validation :set_default_values, :calculate_vehicle_journey_at_stop_day_offset diff --git a/app/models/clean_up.rb b/app/models/clean_up.rb index e39928a17..cbcde72f5 100644 --- a/app/models/clean_up.rb +++ b/app/models/clean_up.rb @@ -16,10 +16,13 @@ class CleanUp < ActiveRecord::Base def clean {}.tap do |result| - result['time_table'] = send("destroy_time_tables_#{self.date_type}").try(:count) + processed = send("destroy_time_tables_#{self.date_type}") + if processed + result['time_table'] = processed[:time_tables].try(:count) + result['vehicle_journey'] = processed[:vehicle_journeys].try(:count) + end result['time_table_date'] = send("destroy_time_tables_dates_#{self.date_type}").try(:count) result['time_table_period'] = send("destroy_time_tables_periods_#{self.date_type}").try(:count) - result['vehicle_journey'] = destroy_vehicle_journey_without_time_table.try(:count) self.overlapping_periods.each do |period| exclude_dates_in_overlapping_period(period) end @@ -106,13 +109,16 @@ class CleanUp < ActiveRecord::Base end def destroy_time_tables(time_tables) + results = { :time_tables => [], :vehicle_journeys => [] } # Delete vehicle_journey time_table association time_tables.each do |time_table| time_table.vehicle_journeys.each do |vj| vj.time_tables.delete(time_table) + results[:vehicle_journeys] << vj.destroy if vj.time_tables.empty? end end - time_tables.destroy_all + results[:time_tables] = time_tables.destroy_all + results end aasm column: :status do diff --git a/app/models/referential.rb b/app/models/referential.rb index 0ce325bd6..ed23e2e51 100644 --- a/app/models/referential.rb +++ b/app/models/referential.rb @@ -114,6 +114,10 @@ class Referential < ActiveRecord::Base Chouette::RouteSection.all end + def routing_constraint_zones + Chouette::RoutingConstraintZone.all + end + after_initialize :define_default_attributes def define_default_attributes diff --git a/app/views/calendars/show.html.slim b/app/views/calendars/show.html.slim index d7849005b..4ce5de57f 100644 --- a/app/views/calendars/show.html.slim +++ b/app/views/calendars/show.html.slim @@ -25,4 +25,4 @@ Calendar.human_attribute_name(:shared) => t("#{@calendar.shared}"), 'Organisation' => @calendar.organisation.name, Calendar.human_attribute_name(:dates) => @calendar.dates.collect{|d| l(d, format: :short)}.join(', ').html_safe, - Calendar.human_attribute_name(:date_ranges) => @calendar.date_ranges.collect{|d| t('validity_range', debut: l(d.begin, format: :short), end: l(d.end - (d.exclude_end? ? 1.day : 0), format: :short))}.join('<br>').html_safe } + Calendar.human_attribute_name(:date_ranges) => @calendar.periods.map{|d| t('validity_range', debut: l(d.begin, format: :short), end: l(d.end, format: :short))}.join('<br>').html_safe } diff --git a/app/views/routing_constraint_zones/_form.html.slim b/app/views/routing_constraint_zones/_form.html.slim deleted file mode 100644 index 3d4764ef7..000000000 --- a/app/views/routing_constraint_zones/_form.html.slim +++ /dev/null @@ -1,25 +0,0 @@ -= simple_form_for [@referential, @line, @routing_constraint_zone], html: {class: 'form-horizontal', id: 'itl_form'}, wrapper: :horizontal_form do |form| - - .row - .col-lg-12 - = form.input :name - = form.input :route_id, collection: @line.routes.select { |route| route.stop_points.count > 2 }, include_blank: false - - .separator - - #ITL_stoppoints - .subform - .nested-head - .wrapper - div - .form-group - label.control-label Arrêt - div - .form-group - label.control-label Commune - div - - = hidden_field_tag 'stop_point_ids', @routing_constraint_zone.stop_point_ids.to_s, id: 'stop_point_ids' - - - = form.button :submit, t('actions.submit'), class: 'btn btn-default formSubmitr', form: 'itl_form' diff --git a/app/views/routing_constraint_zones/_stop_points.html.slim b/app/views/routing_constraint_zones/_stop_points.html.slim new file mode 100644 index 000000000..10c1d4900 --- /dev/null +++ b/app/views/routing_constraint_zones/_stop_points.html.slim @@ -0,0 +1,13 @@ +- stop_points.each do |stop_point| + .wrapper + div + = link_to referential_stop_area_path(@referential, stop_point.stop_area.id), class: 'navlink', title: "Voir l'arrêt" do + span = stop_point.stop_area.name + + div + span + = "#{stop_point.stop_area.city_name} (#{stop_point.stop_area.zip_code})" + div + span.has_radio + = check_box_tag( "routing_constraint_zone[stop_point_ids][]", "#{stop_point.id}", @routing_constraint_zone.stop_point_ids && @routing_constraint_zone.stop_point_ids.include?(stop_point.id) ) + span.radio-label diff --git a/app/views/routing_constraint_zones/edit.html.slim b/app/views/routing_constraint_zones/edit.html.slim index d81a347e0..4fe2786a3 100644 --- a/app/views/routing_constraint_zones/edit.html.slim +++ b/app/views/routing_constraint_zones/edit.html.slim @@ -1,12 +1,39 @@ / PageHeader = pageheader 'map-marker', - t('.title'), + t('.title', routing_constraint_zone: @routing_constraint_zone.name), '', t('last_update', time: l(@routing_constraint_zone.updated_at, format: :short)) / PageContent .page_content - .container-fluid - .row - .col-lg-8.col-lg-offset-2.col-md-8.col-md-offset-2.col-sm-10.col-sm-offset-1 - == render 'form' + .container-fluid + .row + .col-lg-8.col-lg-offset-2.col-md-8.col-md-offset-2.col-sm-10.col-sm-offset-1 + = simple_form_for [@referential, @line, @routing_constraint_zone], html: {class: 'form-horizontal', id: 'itl_form'}, wrapper: :horizontal_form do |form| + + .row + .col-lg-12 + = form.input :name + = form.association :route, collection: @line.routes, disabled: true + + .separator + + - @routing_constraint_zone.errors[:stop_point_ids].each do |error_message| + .alert.alert-danger = error_message + + #ITL_stoppoints + .subform + .nested-head + .wrapper + div + .form-group + label.control-label Arrêt + div + .form-group + label.control-label Commune + div + + .nested-fields + = render( partial: 'routing_constraint_zones/stop_points', object: @routing_constraint_zone.route.stop_points) + + = form.button :submit, t('actions.submit'), class: 'btn btn-default formSubmitr', form: 'itl_form' diff --git a/app/views/routing_constraint_zones/new.html.slim b/app/views/routing_constraint_zones/new.html.slim index f6ae62825..ab7143a76 100644 --- a/app/views/routing_constraint_zones/new.html.slim +++ b/app/views/routing_constraint_zones/new.html.slim @@ -7,4 +7,29 @@ .container-fluid .row .col-lg-8.col-lg-offset-2.col-md-8.col-md-offset-2.col-sm-10.col-sm-offset-1 - == render 'form' + = simple_form_for [@referential, @line, @routing_constraint_zone], html: {class: 'form-horizontal', id: 'itl_form'}, wrapper: :horizontal_form do |form| + + .row + .col-lg-12 + = form.input :name + = form.input :route_id, collection: @line.routes, include_blank: false, input_html: {data: {url: new_referential_line_routing_constraint_zone_path(@referential, @line) }} + + .separator + + - @routing_constraint_zone.errors[:stop_point_ids].each do |error_message| + .alert.alert-danger = error_message + + #ITL_stoppoints + .subform + .nested-head + .wrapper + div + .form-group + label.control-label Arrêt + div + .form-group + label.control-label Commune + div + .nested-fields#stop_points + + = form.button :submit, t('actions.submit'), class: 'btn btn-default formSubmitr', form: 'itl_form' diff --git a/app/views/routing_constraint_zones/new.js.erb b/app/views/routing_constraint_zones/new.js.erb new file mode 100644 index 000000000..406845a4d --- /dev/null +++ b/app/views/routing_constraint_zones/new.js.erb @@ -0,0 +1 @@ +$("#stop_points").html("<%= escape_javascript(render partial: 'routing_constraint_zones/stop_points', object: @route.stop_points) %>"); diff --git a/app/views/routing_constraint_zones/show.html.slim b/app/views/routing_constraint_zones/show.html.slim index 1dad4f561..3decc086f 100644 --- a/app/views/routing_constraint_zones/show.html.slim +++ b/app/views/routing_constraint_zones/show.html.slim @@ -28,7 +28,7 @@ .col-lg-12 = table_builder @routing_constraint_zone.route.stop_points, { "Arrêts de l'itinéraire" => 'name', - "Arrêts inclus dans l'ITL" => Proc.new{ |rsp| (@routing_constraint_zone.stop_points.collect{|c| c.name}.include? rsp.name) ? 'Oui' : 'Non' } }, + "Arrêts inclus dans l'ITL" => Proc.new{ |rsp| (@routing_constraint_zone.stop_point_ids.include? rsp.id) ? 'Oui' : 'Non' } }, [], [], 'table has-stoppoints' diff --git a/app/views/workbenches/_filters.html.slim b/app/views/workbenches/_filters.html.slim index 4eaf910c0..d7ac79577 100644 --- a/app/views/workbenches/_filters.html.slim +++ b/app/views/workbenches/_filters.html.slim @@ -1,4 +1,4 @@ -= search_form_for @q, url: workbench_path(@workbench.id), builder: SimpleForm::FormBuilder, class: 'form form-filter' do |f| += search_form_for @q_for_form, url: workbench_path(@workbench.id), builder: SimpleForm::FormBuilder, class: 'form form-filter' do |f| .ffg-row .input-group.search_bar = f.search_field :name_cont, class: 'form-control', placeholder: 'Indiquez un nom de référentiel...' diff --git a/bin/bundle b/bin/bundle deleted file mode 100755 index 66e9889e8..000000000 --- a/bin/bundle +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env ruby -ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) -load Gem.bin_path('bundler', 'bundle') diff --git a/bin/rails b/bin/rails deleted file mode 100755 index f2b0313dd..000000000 --- a/bin/rails +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env ruby -begin - load File.expand_path('../spring', __FILE__) -rescue LoadError => e - raise unless e.message.include?('spring') -end -APP_PATH = File.expand_path('../../config/application', __FILE__) -require_relative '../config/boot' -require 'rails/commands' diff --git a/bin/rake b/bin/rake deleted file mode 100755 index d87d5f578..000000000 --- a/bin/rake +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env ruby -begin - load File.expand_path('../spring', __FILE__) -rescue LoadError => e - raise unless e.message.include?('spring') -end -require_relative '../config/boot' -require 'rake' -Rake.application.run diff --git a/bin/spring b/bin/spring deleted file mode 100755 index fb2ec2ebb..000000000 --- a/bin/spring +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env ruby - -# This file loads spring without using Bundler, in order to be fast. -# It gets overwritten when you run the `spring binstub` command. - -unless defined?(Spring) - require 'rubygems' - require 'bundler' - - lockfile = Bundler::LockfileParser.new(Bundler.default_lockfile.read) - spring = lockfile.specs.detect { |spec| spec.name == "spring" } - if spring - Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path - gem 'spring', spring.version - require 'spring/binstub' - end -end diff --git a/config/deploy.rb b/config/deploy.rb index 0445ec480..4ab888e92 100644 --- a/config/deploy.rb +++ b/config/deploy.rb @@ -51,7 +51,7 @@ namespace :deploy do end task :bundle_link do - run "ln -fs #{bundle_cmd} #{release_path}/bin/bundle" + run "mkdir -p #{release_path}/bin && ln -fs #{bundle_cmd} #{release_path}/bin/bundle" end after "bundle:install", "deploy:bundle_link" diff --git a/lib/tasks/referential.rake b/lib/tasks/referential.rake index ce1ded4fc..d27354a40 100644 --- a/lib/tasks/referential.rake +++ b/lib/tasks/referential.rake @@ -39,6 +39,8 @@ namespace :referential do print " ✓ Created Route ".green, route.name, "(#{route.id}), ".blue, "Line (#{line.id}) has #{line.routes.count} routes\n" journey_pattern = Chouette::JourneyPattern.create!(route: route, name: "Journey Pattern #{Faker::Name.unique.name}") + print "✓ Created JourneyPattern ".green, journey_pattern.name, "(#{journey_pattern.id})".blue, "\n" + journey_pattern.stop_points = stop_areas.inject([]) { |stop_points, stop_area| stop_points += stop_area.stop_points } time_tables = [] @@ -46,11 +48,14 @@ namespace :referential do name = "Test #{Faker::Name.unique.name}" time_table = Chouette::TimeTable.create!(comment: name, start_date: Date.parse(args[:start_date]) + j.days, end_date: Date.parse(args[:end_date]) - j.days) + print "✓ Created TimeTable ".green, time_table.comment, "(#{time_table.id})".blue, "\n" time_tables << time_table end 25.times do |j| vehicle_journey = Chouette::VehicleJourney.create!(journey_pattern: journey_pattern, route: route, number: Faker::Number.unique.number(4), time_tables: time_tables) + print "✓ Created VehicleJourney ".green, vehicle_journey.number, "(#{vehicle_journey.id})".blue, "\n" + time = Time.current.at_noon + j.minutes journey_pattern.stop_points.each_with_index do |stop_point, k| vehicle_journey.vehicle_journey_at_stops.create!(stop_point: stop_point, arrival_time: time + k.minutes, departure_time: time + k.minutes + 30.seconds) diff --git a/spec/factories/referentials.rb b/spec/factories/referentials.rb index dd5bf1c2e..5348ad96b 100644 --- a/spec/factories/referentials.rb +++ b/spec/factories/referentials.rb @@ -8,5 +8,6 @@ FactoryGirl.define do association :line_referential association :stop_area_referential time_zone "Europe/Paris" + ready { true } end end diff --git a/spec/features/workbenches_spec.rb b/spec/features/workbenches_spec.rb index 953eb2bf5..2f453389d 100644 --- a/spec/features/workbenches_spec.rb +++ b/spec/features/workbenches_spec.rb @@ -3,50 +3,137 @@ describe 'Workbenches', type: :feature do login_user - #let!(:organisations) { Array.new(2) { create :organisation } } - #let!(:referentials) { Array.new(2) { create :referential, ready: true } } - let(:line_referential) { create :line_referential } - let(:workbenches) { Array.new(2) { create :workbench, line_referential: line_referential, organisation: @user.organisation } } - let(:workbench) { workbenches.first } - let!(:line) { create :line, line_referential: line_referential } + let(:line_ref) { create :line_referential } + let(:line) { create :line, line_referential: line_ref } + let(:ref_metadata) { create(:referential_metadata, lines: [line]) } - let(:referential_metadatas) { Array.new(2) { |i| create :referential_metadata, lines: [line] } } + let!(:workbench) { create(:workbench, line_referential: line_ref, organisation: @user.organisation) } + let!(:referential) { create :referential, workbench: workbench, metadatas: [ref_metadata], organisation: @user.organisation } describe 'show' do + context 'ready' do + it 'should show ready referentials' do + visit workbench_path(workbench) + expect(page).to have_content(referential.name) + end - let!(:ready_referential) { create :referential, workbench: workbench, metadatas: referential_metadatas, ready: true, organisation: @user.organisation } - let!(:unready_referential) { create :referential, workbench: workbench } + it 'should not show unready referentials' do + referential.update_attribute(:ready, false) + visit workbench_path(workbench) + expect(page).to_not have_content(referential.name) + end + end - before(:each) { visit workbench_path(workbench) } + context 'filtering' do + let(:another_organisation) { create :organisation } + let(:another_line) { create :line, line_referential: line_ref } + let(:another_ref_metadata) { create(:referential_metadata, lines: [another_line]) } + let!(:other_referential) { create :referential, workbench: workbench, metadatas: [another_ref_metadata], organisation: another_organisation} - it 'shows ready referentials belonging to that workbench by default' do - expect(page).to have_content(ready_referential.name) - expect(page).not_to have_content(unready_referential.name) - end + before(:each) do + visit workbench_path(workbench) + end + + context 'without any filter' do + it 'should have results' do + click_button 'Filtrer' + expect(page).to have_content(referential.name) + expect(page).to have_content(other_referential.name) + end + end + + context 'filter by organisation' do + it 'should be possible to filter by organisation' do + find("#q_organisation_name_eq_any_#{@user.organisation.name.parameterize.underscore}").set(true) + click_button 'Filtrer' + + expect(page).to have_content(referential.name) + expect(page).not_to have_content(other_referential.name) + end + + it 'should be possible to filter by multiple organisation' do + find("#q_organisation_name_eq_any_#{@user.organisation.name.parameterize.underscore}").set(true) + find("#q_organisation_name_eq_any_#{another_organisation.name.parameterize.underscore}").set(true) + click_button 'Filtrer' + + expect(page).to have_content(referential.name) + expect(page).to have_content(other_referential.name) + end + + it 'should keep filter value on submit' do + box = "#q_organisation_name_eq_any_#{another_organisation.name.parameterize.underscore}" + find(box).set(true) + click_button 'Filtrer' + expect(find(box)).to be_checked + end + end - context 'user has the permission to create referentials' do - it 'shows the link for a new referetnial' do - expect(page).to have_link(I18n.t('actions.add'), href: new_referential_path(workbench_id: workbenches.first)) + context 'filter by status' do + it 'should display archived referentials' do + other_referential.update_attribute(:archived_at, Date.today) + find("#q_archived_at_not_null").set(true) + + click_button 'Filtrer' + expect(page).to have_content(other_referential.name) + expect(page).to_not have_content(referential.name) + end + + it 'should display both archived and unarchived referentials' do + other_referential.update_attribute(:archived_at, Date.today) + find("#q_archived_at_not_null").set(true) + find("#q_archived_at_null").set(true) + + click_button 'Filtrer' + expect(page).to have_content(referential.name) + expect(page).to have_content(other_referential.name) + end + + it 'should display unarchived referentials' do + other_referential.update_attribute(:archived_at, Date.today) + find("#q_archived_at_null").set(true) + + click_button 'Filtrer' + expect(page).to have_content(referential.name) + expect(page).to_not have_content(other_referential.name) + end + + it 'should keep filter value on submit' do + find("#q_archived_at_null").set(true) + click_button 'Filtrer' + expect(find("#q_archived_at_null")).to be_checked + end end end - context 'user does not have the permission to create referentials' do - it 'does not show the clone link for referetnial' do - @user.update_attribute(:permissions, []) - visit referential_path(referential) - expect(page).not_to have_link(I18n.t('actions.add'), href: new_referential_path(workbench_id: workbenches.first)) + context 'permissions' do + before(:each) do + visit workbench_path(workbench) + end + + context 'user has the permission to create referentials' do + it 'shows the link for a new referetnial' do + expect(page).to have_link(I18n.t('actions.add'), href: new_referential_path(workbench_id: workbench.id)) + end + end + + context 'user does not have the permission to create referentials' do + it 'does not show the clone link for referential' do + @user.update_attribute(:permissions, []) + visit referential_path(referential) + expect(page).not_to have_link(I18n.t('actions.add'), href: new_referential_path(workbench_id: workbench.id)) + end end end end describe 'create new Referential' do it "create a new Referential with a specifed line and period" do - visit workbench_path(workbench) + referential.destroy + visit workbench_path(workbench) click_link I18n.t('actions.add') - - fill_in "referential[name]", with: "Referential to test creation" # Nom du JDD - select workbench.lines.first.id, from: 'referential[metadatas_attributes][0][lines][]' # Lignes + fill_in "referential[name]", with: "Referential to test creation" + select workbench.lines.first.id, from: 'referential[metadatas_attributes][0][lines][]' click_button "Valider" expect(page).to have_css("h1", text: "Referential to test creation") diff --git a/spec/helpers/table_builder_helper_spec.rb b/spec/helpers/table_builder_helper_spec.rb index 8f4d98c88..67980fc2c 100644 --- a/spec/helpers/table_builder_helper_spec.rb +++ b/spec/helpers/table_builder_helper_spec.rb @@ -81,7 +81,7 @@ describe TableBuilderHelper, type: :helper do <li><a href="/referentials/#{referential.id}/time_tables">Calendriers</a></li> <li><a href="/referentials/new?from=#{referential.id}">Dupliquer</a></li> <li><a rel="nofollow" data-method="put" href="/referentials/#{referential.id}/archive">Conserver</a></li> - <li class="delete-action"><a data-confirm="Etes vous sûr de vouloir supprimer ce jeu de données ?" rel="nofollow" data-method="delete" href="/referentials/#{referential.id}"><span class="fa fa-trash"></span>Supprimer</a></li> + <li class="delete-action"><a data-confirm="Etes vous sûr de vouloir supprimer ce jeu de données ?" rel="nofollow" data-method="delete" href="/referentials/#{referential.id}"><span class="fa fa-trash mr-xs"></span>Supprimer</a></li> </ul> </div> </td> diff --git a/spec/models/calendar_spec.rb b/spec/models/calendar_spec.rb index cf7e4aa27..f5020ebf8 100644 --- a/spec/models/calendar_spec.rb +++ b/spec/models/calendar_spec.rb @@ -8,13 +8,13 @@ RSpec.describe Calendar, :type => :model do it { is_expected.to validate_uniqueness_of(:short_name) } describe '#to_time_table' do - let(:calendar) { create(:calendar, date_ranges: [Date.today..(Date.today + 1.month)]) } + let(:calendar) { create(:calendar, date_ranges: [Date.today...(Date.today + 1.month)]) } it 'should convert calendar to an instance of Chouette::TimeTable' do time_table = calendar.convert_to_time_table expect(time_table).to be_an_instance_of(Chouette::TimeTable) - expect(time_table.periods[0].period_start).to eq(calendar.date_ranges[0].begin) - expect(time_table.periods[0].period_end).to eq(calendar.date_ranges[0].end) + expect(time_table.periods[0].period_start).to eq(calendar.periods[0].begin) + expect(time_table.periods[0].period_end).to eq(calendar.periods[0].end) expect(time_table.dates.map(&:date)).to match_array(calendar.dates) end end diff --git a/spec/models/chouette/journey_pattern_spec.rb b/spec/models/chouette/journey_pattern_spec.rb index aaf9a694f..6601ed5f4 100644 --- a/spec/models/chouette/journey_pattern_spec.rb +++ b/spec/models/chouette/journey_pattern_spec.rb @@ -20,6 +20,11 @@ describe Chouette::JourneyPattern, :type => :model do expect(journey_pattern).to_not be_valid expect(journey_pattern.errors).to have_key(:stop_points) end + + it 'should only validate on update' do + jp = build(:journey_pattern_common) + expect(jp).to be_valid + end end describe "state_update" do diff --git a/spec/models/chouette/time_table_spec.rb b/spec/models/chouette/time_table_spec.rb index 76c5def5c..bd74a2d4c 100644 --- a/spec/models/chouette/time_table_spec.rb +++ b/spec/models/chouette/time_table_spec.rb @@ -7,6 +7,10 @@ describe Chouette::TimeTable, :type => :model do it { is_expected.to validate_presence_of :comment } it { is_expected.to validate_uniqueness_of :objectid } + def create_time_table_periode time_table, start_date, end_date + create(:time_table_period, time_table: time_table, :period_start => start_date, :period_end => end_date) + end + describe "#merge! with time_table" do let(:another_tt) { create(:time_table) } let(:another_tt_periods_to_range) { another_tt.periods.map{|p| p.period_start..p.period_end } } @@ -72,6 +76,134 @@ describe Chouette::TimeTable, :type => :model do end end + describe "#disjoin!" do + let(:another_tt) { create(:time_table) } + + context 'dates' do + before do + subject.periods.clear + another_tt.periods.clear + end + + it 'should remove common dates' do + subject.disjoin!(another_tt) + expect(subject.reload.dates).to be_empty + end + + it 'should remove common dates with mixed none common dates' do + another_tt.dates.clear + another_tt.dates << create(:time_table_date, time_table: another_tt, date: subject.dates[0].date) + + subject.disjoin!(another_tt) + expect(subject.reload.dates.map(&:date)).to_not include(another_tt.dates[0].date) + end + end + + context 'periods' do + let(:another_tt_periods_to_range) { another_tt.periods.map{|p| p.period_start..p.period_end } } + # Clear dates as we are testing periods + before do + subject.dates.clear + another_tt.dates.clear + end + + it 'should remove common dates in periods' do + subject.disjoin!(another_tt) + expect(subject_periods_to_range).to_not include(*another_tt_periods_to_range) + end + + it 'should build new period without common dates in periods' do + subject.periods.clear + another_tt.periods.clear + + subject.periods << create_time_table_periode(subject, Date.today, Date.today + 10.day) + another_tt.periods << create_time_table_periode(another_tt, Date.tomorrow, Date.today + 3.day) + + subject.disjoin!(another_tt) + expected_range = Date.tomorrow..Date.today + 3.day + + expect(subject_periods_to_range).to_not include(expected_range) + expect(subject.periods.count).to eq 1 + end + end + end + + describe '#intersect! with time_table' do + let(:another_tt) { create(:time_table) } + + context 'dates' do + # Clear periods as we are testing dates + before do + subject.periods.clear + another_tt.periods.clear + end + + it 'should keep common dates' do + days = subject.dates.map(&:date) + subject.intersect!(another_tt) + expect(subject.included_days_in_dates_and_periods).to include(*days) + end + + it 'should not keep dates who are not in common' do + # Add 1 year interval, to make sur we have not dates in common + another_tt.dates.map{|d| d.date = d.date + 1.year } + subject.intersect!(another_tt) + + expect(subject.reload.dates).to be_empty + end + end + + context 'periods' do + let(:another_tt_periods_to_range) { another_tt.periods.map{|p| p.period_start..p.period_end } } + # Clear dates as we are testing periods + before do + subject.dates.clear + another_tt.dates.clear + end + + it 'should keep common dates in periods' do + subject.intersect!(another_tt) + expect(subject_periods_to_range).to include(*another_tt_periods_to_range) + end + + it 'should build new period with common dates in periods' do + subject.periods.clear + another_tt.periods.clear + + subject.periods << create_time_table_periode(subject, Date.today, Date.today + 10.day) + another_tt.periods << create_time_table_periode(another_tt, Date.tomorrow, Date.today + 3.day) + + subject.intersect!(another_tt) + expected_range = Date.tomorrow..Date.today + 3.day + + expect(subject_periods_to_range).to include(expected_range) + expect(subject.periods.count).to eq 1 + end + + it 'should not keep dates in periods who are not in common' do + another_tt.periods.map do |p| + p.period_start = p.period_start + 1.year + p.period_end = p.period_end + 1.year + end + + subject.intersect!(another_tt) + expect(subject.periods).to be_empty + end + + context 'with calendar' do + let(:period_start) { subject.periods[0].period_start } + let(:period_end) { subject.periods[0].period_end } + let(:another_tt) { create(:calendar, date_ranges: [period_start..period_end]).convert_to_time_table } + + it 'should keep common dates in periods' do + subject.intersect!(another_tt) + expect(subject.reload.periods.count).to eq 1 + expect(subject_periods_to_range).to include(*another_tt_periods_to_range) + end + end + end + end + describe "actualize" do let(:calendar) { create(:calendar) } let(:int_day_types) { 508 } @@ -1037,378 +1169,6 @@ end end end - describe "#intersect!" do - context "timetables have periods with common day_types " do - before do - subject.periods.clear - subject.dates.clear - subject.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,8,1), :period_end => Date.new(2014,8,6)) - subject.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,6,30), :period_end => Date.new(2014,7,20)) - subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,16), :in_out => true) - subject.int_day_types = 4|16|32|128 - another_tt = create(:time_table , :int_day_types => (4|16|64|128) ) - another_tt.periods.clear - another_tt.dates.clear - another_tt.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,8,6), :period_end => Date.new(2014,8,12)) - another_tt.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,7,15), :period_end => Date.new(2014,7,25)) - subject.intersect! another_tt - subject.reload - end - it "should have no period" do - expect(subject.periods.size).to eq(0) - end - it "should have date all common days" do - expect(subject.dates.size).to eq(3) - expect(subject.dates[0].date).to eq(Date.new(2014,7,16)) - expect(subject.dates[1].date).to eq(Date.new(2014,7,19)) - expect(subject.dates[2].date).to eq(Date.new(2014,8,6)) - end - end - context "timetables have periods or dates " do - before do - subject.periods.clear - subject.dates.clear - subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,16), :in_out => true) - subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,17), :in_out => true) - subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,18), :in_out => true) - subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,19), :in_out => true) - subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,20), :in_out => true) - subject.int_day_types = 0 - another_tt = create(:time_table , :int_day_types => (4|16|64|128) ) - another_tt.periods.clear - another_tt.dates.clear - another_tt.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,8,6), :period_end => Date.new(2014,8,12)) - another_tt.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,7,17), :period_end => Date.new(2014,7,25)) - subject.intersect! another_tt - subject.reload - end - it "should have 0 period" do - expect(subject.periods.size).to eq(0) - end - it "should not modify day_types" do - expect(subject.int_day_types).to eq(0) - end - it "should have date reduced for period" do - expect(subject.dates.size).to eq(2) - expect(subject.dates[0].date).to eq(Date.new(2014,7,18)) - expect(subject.dates[1].date).to eq(Date.new(2014,7,19)) - end - end - context "with only periods : intersect timetable have no one day period" do - before do - subject.periods.clear - subject.dates.clear - subject.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,8,1), :period_end => Date.new(2014,8,6)) - subject.int_day_types = 4|8|16 - another_tt = create(:time_table , :int_day_types => (4|8|16) ) - another_tt.periods.clear - another_tt.dates.clear - another_tt.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,8,6), :period_end => Date.new(2014,8,12)) - subject.intersect! another_tt - subject.reload - end - it "should have 0 result periods" do - expect(subject.periods.size).to eq(0) - end - it "should not modify day_types" do - expect(subject.int_day_types).to eq(4|8|16) - end - it "should have 1 date " do - expect(subject.dates.size).to eq(1) - expect(subject.dates[0].date).to eq(Date.new(2014,8,6)) - end - end - - end - - describe "#disjoin!" do - context "timetables have periods with common day_types " do - before do - subject.periods.clear - subject.dates.clear - subject.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,8,1), :period_end => Date.new(2014,8,6)) - subject.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,6,30), :period_end => Date.new(2014,7,20)) - subject.int_day_types = 4|16|32|128 - another_tt = create(:time_table , :int_day_types => (4|16|64|128) ) - another_tt.periods.clear - another_tt.dates.clear - another_tt.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,8,6), :period_end => Date.new(2014,8,12)) - another_tt.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,7,15), :period_end => Date.new(2014,8,2)) - subject.disjoin! another_tt - subject.reload - end - it "should have 0 periods" do - expect(subject.periods.size).to eq(0) - end - it "should have only dates " do - expect(subject.dates.size).to eq(11) - expect(subject.dates[0].date).to eq(Date.new(2014,6,30)) - expect(subject.dates[1].date).to eq(Date.new(2014,7,2)) - expect(subject.dates[2].date).to eq(Date.new(2014,7,3)) - expect(subject.dates[3].date).to eq(Date.new(2014,7,5)) - expect(subject.dates[4].date).to eq(Date.new(2014,7,7)) - expect(subject.dates[5].date).to eq(Date.new(2014,7,9)) - expect(subject.dates[6].date).to eq(Date.new(2014,7,10)) - expect(subject.dates[7].date).to eq(Date.new(2014,7,12)) - expect(subject.dates[8].date).to eq(Date.new(2014,7,14)) - expect(subject.dates[9].date).to eq(Date.new(2014,7,17)) - expect(subject.dates[10].date).to eq(Date.new(2014,8,4)) - end - end - context "timetables have periods or dates " do - before do - subject.periods.clear - subject.dates.clear - subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,16), :in_out => true) - subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,17), :in_out => true) - subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,18), :in_out => true) - subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,19), :in_out => true) - subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,20), :in_out => true) - subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,8,6), :in_out => true) - subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,8,7), :in_out => true) - subject.int_day_types = 0 - another_tt = create(:time_table , :int_day_types => (4|16|64|128) ) - another_tt.periods.clear - another_tt.dates.clear - another_tt.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,8,6), :period_end => Date.new(2014,8,12)) - another_tt.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,7,17), :period_end => Date.new(2014,7,25)) - subject.disjoin! another_tt - subject.reload - end - it "should have 0 period" do - expect(subject.periods.size).to eq(0) - end - it "should not modify day_types" do - expect(subject.int_day_types).to eq(0) - end - it "should have date reduced for period" do - expect(subject.dates.size).to eq(4) - expect(subject.dates[0].date).to eq(Date.new(2014,7,16)) - expect(subject.dates[1].date).to eq(Date.new(2014,7,17)) - expect(subject.dates[2].date).to eq(Date.new(2014,7,20)) - expect(subject.dates[3].date).to eq(Date.new(2014,8,7)) - end - end - context "disjoined timetable have all periods in removed ones " do - before do - subject.periods.clear - subject.dates.clear - subject.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,8,1), :period_end => Date.new(2014,8,8)) - subject.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,6,30), :period_end => Date.new(2014,7,20)) - subject.int_day_types = 4|16|32|128 - another_tt = create(:time_table , :int_day_types => (4|16|64|128) ) - another_tt.periods.clear - another_tt.dates.clear - another_tt.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,7,31), :period_end => Date.new(2014,8,12)) - another_tt.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,6,30), :period_end => Date.new(2014,7,20)) - subject.disjoin! another_tt - subject.reload - end - it "should have 0 result periods" do - expect(subject.periods.size).to eq(0) - end - it "should have dates for period reduced" do - expect(subject.dates.size).to eq(4) - expect(subject.dates[0].date).to eq(Date.new(2014,7,3)) - expect(subject.dates[1].date).to eq(Date.new(2014,7,10)) - expect(subject.dates[2].date).to eq(Date.new(2014,7,17)) - expect(subject.dates[3].date).to eq(Date.new(2014,8,7)) - end - end - - context "timetable with dates against timetable with dates and periods" do - before do - subject.periods.clear - subject.dates.clear - subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,16), :in_out => true) - subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,17), :in_out => true) - subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,18), :in_out => true) - subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,19), :in_out => true) - subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,20), :in_out => true) - subject.int_day_types = 0 - another_tt = create(:time_table , :int_day_types => (4|16|64|128) ) - another_tt.periods.clear - another_tt.dates.clear - another_tt.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,7,31), :period_end => Date.new(2014,8,12)) - another_tt.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,17), :in_out => true) - another_tt.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,18), :in_out => true) - subject.disjoin! another_tt - subject.reload - end - it "should have 0 result periods" do - expect(subject.periods.size).to eq(0) - end - it "should have 3 dates left" do - expect(subject.dates.size).to eq(3) - expect(subject.dates[0].date).to eq(Date.new(2014,7,16)) - expect(subject.dates[1].date).to eq(Date.new(2014,7,19)) - expect(subject.dates[2].date).to eq(Date.new(2014,7,20)) - end - end - - context "timetable with dates against timetable with dates and periods all covered" do - before do - subject.periods.clear - subject.dates.clear - subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,1), :in_out => true) - subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,2), :in_out => true) - subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,5), :in_out => true) - subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,6), :in_out => true) - subject.int_day_types = 512 - another_tt = create(:time_table , :int_day_types => (32|64|512) ) - another_tt.periods.clear - another_tt.dates.clear - another_tt.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,6,30), :period_end => Date.new(2014,7,11)) - another_tt.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,1), :in_out => true) - another_tt.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,2), :in_out => true) - another_tt.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,5), :in_out => true) - another_tt.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,6), :in_out => true) - subject.disjoin! another_tt - subject.reload - end - - it "should have 0 result periods" do - expect(subject.periods.size).to eq(0) - end - - it "should have 0 dates left" do - expect(subject.dates.size).to eq(0) - end - end - - context "with only periods : disjoined timetable have no empty period" do - before do - subject.periods.clear - subject.dates.clear - subject.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,8,1), :period_end => Date.new(2014,8,8)) - subject.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,8,10), :period_end => Date.new(2014,8,31)) - subject.int_day_types = 4|8 - another_tt = create(:time_table , :int_day_types => (4|8) ) - another_tt.periods.clear - another_tt.dates.clear - another_tt.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,8,4), :period_end => Date.new(2014,8,7)) - subject.disjoin! another_tt - subject.reload - end - - it "should have 0 result periods" do - expect(subject.periods.size).to eq(0) - end - - it "should have 6 dates " do - expect(subject.dates.size).to eq(6) - expect(subject.dates[0].date).to eq(Date.new(2014,8,11)) - expect(subject.dates[1].date).to eq(Date.new(2014,8,12)) - expect(subject.dates[2].date).to eq(Date.new(2014,8,18)) - expect(subject.dates[3].date).to eq(Date.new(2014,8,19)) - expect(subject.dates[4].date).to eq(Date.new(2014,8,25)) - expect(subject.dates[5].date).to eq(Date.new(2014,8,26)) - end - end - - context "with only periods : disjoined timetable have no one day period" do - before do - subject.periods.clear - subject.dates.clear - subject.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,8,1), :period_end => Date.new(2014,8,6)) - subject.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,8,10), :period_end => Date.new(2014,8,31)) - subject.int_day_types = 4|8|16 - another_tt = create(:time_table , :int_day_types => (4|8) ) - another_tt.periods.clear - another_tt.dates.clear - another_tt.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,8,4), :period_end => Date.new(2014,8,5)) - subject.disjoin! another_tt - subject.reload - end - - it "should have 3 result periods" do - expect(subject.periods.size).to eq(3) - [ - ['2014-08-11', '2014-08-13'], - ['2014-08-18', '2014-08-20'], - ['2014-08-25', '2014-08-27'] - ].each_with_index do |period, index| - expect(subject.periods[index].period_start.to_s).to eq(period[0]) - expect(subject.periods[index].period_end.to_s).to eq(period[1]) - end - end - - it "should not modify day_types" do - expect(subject.int_day_types).to eq(4|8|16) - end - - it "should have 1 dates " do - expect(subject.dates.size).to eq(1) - expect(subject.dates.first.date.to_s).to eq('2014-08-06') - end - end - - context "with periods against dates: disjoined timetable have no unused excluded date" do - before do - subject.periods.clear - subject.dates.clear - subject.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,8,1), :period_end => Date.new(2014,8,8)) - subject.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,8,10), :period_end => Date.new(2014,8,31)) - subject.int_day_types = 4|8|16 - another_tt = create(:time_table , :int_day_types => (0) ) - another_tt.periods.clear - another_tt.dates.clear - another_tt.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,8,4), :in_out => true) - another_tt.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,8,5), :in_out => true) - another_tt.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,8,7), :in_out => true) - subject.disjoin! another_tt - subject.reload - end - - it "should have same 3 result periods" do - expect(subject.periods.size).to eq(3) - [ - ['2014-08-11', '2014-08-13'], - ['2014-08-18', '2014-08-20'], - ['2014-08-25', '2014-08-27'] - ].each_with_index do |period, index| - expect(subject.periods[index].period_start.to_s).to eq(period[0]) - expect(subject.periods[index].period_end.to_s).to eq(period[1]) - end - end - - it "should not modify day_types" do - expect(subject.int_day_types).to eq(4|8|16) - end - - it "should have only 1 dates " do - expect(subject.dates.size).to eq(1) - expect(subject.dates.first.date.to_s).to eq('2014-08-06') - end - end - - - context "with same definition : dsjointed timetable should be empty" do - before do - subject.periods.clear - subject.dates.clear - subject.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2015,6,1), :period_end => Date.new(2015,6,30)) - subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2015,6,16), :in_out => true) - subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2015,6,22), :in_out => false) - subject.int_day_types = 4|8|16|32|64|128|256 - another_tt = create(:time_table , :int_day_types => ( 4|8|16|32|64|128|256) ) - another_tt.periods.clear - another_tt.dates.clear - another_tt.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2015,6,1), :period_end => Date.new(2015,6,30)) - another_tt.dates << Chouette::TimeTableDate.new( :date => Date.new(2015,6,16), :in_out => true) - another_tt.dates << Chouette::TimeTableDate.new( :date => Date.new(2015,6,22), :in_out => false) - subject.disjoin! another_tt - subject.reload - end - it "should have same 0 result periods" do - expect(subject.periods.size).to eq(0) - end - it "should have 0 dates " do - expect(subject.dates.size).to eq(0) - end - end - end - describe "#duplicate" do it 'should also copy tags' do subject.tag_list.add('tag1', 'tag2') diff --git a/spec/models/clean_up_spec.rb b/spec/models/clean_up_spec.rb index ee88ca773..4dc692ab2 100644 --- a/spec/models/clean_up_spec.rb +++ b/spec/models/clean_up_spec.rb @@ -200,9 +200,17 @@ RSpec.describe CleanUp, :type => :model do end it 'should destroy time_table vehicle_journey association' do + vj = create(:vehicle_journey, time_tables: [time_table, create(:time_table)]) + cleaner.destroy_time_tables(Chouette::TimeTable.where(id: time_table.id)) + + expect(vj.reload.time_tables.map(&:id)).to_not include(time_table.id) + end + + it 'should also destroy associated vehicle_journey if it belongs to any other time_table' do vj = create(:vehicle_journey, time_tables: [time_table]) - cleaner.destroy_time_tables(Chouette::TimeTable.all) - expect(vj.reload.time_tables).to be_empty + expect{cleaner.destroy_time_tables(Chouette::TimeTable.all)}.to change { + Chouette::VehicleJourney.count + }.by(-1) end end diff --git a/spec/policies/application_policy_spec.rb b/spec/policies/application_policy_spec.rb index a7234461e..3ec177209 100644 --- a/spec/policies/application_policy_spec.rb +++ b/spec/policies/application_policy_spec.rb @@ -7,7 +7,7 @@ RSpec.describe ApplicationPolicy, type: :policy do end it "allows a user with a different organisation" do - user.update_attribute :organisation, referential.organisation + user.organisation = referential.organisation expect_it.to permit(user_context, referential) end end diff --git a/spec/policies/boiv_policy_spec.rb b/spec/policies/boiv_policy_spec.rb index 514534adc..6787ab2ac 100644 --- a/spec/policies/boiv_policy_spec.rb +++ b/spec/policies/boiv_policy_spec.rb @@ -1,5 +1,7 @@ RSpec.describe BoivPolicy, type: :policy do + let( :record ){ nil } + permissions :index? do it_behaves_like 'permitted policy and same organisation', 'boiv:read-offer' end diff --git a/spec/policies/line_policy_spec.rb b/spec/policies/line_policy_spec.rb index ead5918aa..e720b2bc7 100644 --- a/spec/policies/line_policy_spec.rb +++ b/spec/policies/line_policy_spec.rb @@ -1,5 +1,8 @@ RSpec.describe LinePolicy, type: :policy do + let( :record ){ build_stubbed :line } + + %w{create destroy edit}.each do | permission | footnote_permission = "#{permission}_footnote" permissions "#{footnote_permission}?".to_sym do diff --git a/spec/policies/route_policy_spec.rb b/spec/policies/route_policy_spec.rb index baf14c9fc..cc949ff45 100644 --- a/spec/policies/route_policy_spec.rb +++ b/spec/policies/route_policy_spec.rb @@ -1,5 +1,7 @@ RSpec.describe RoutePolicy, type: :policy do + let( :record ){ build_stubbed :route } + permissions :create? do it_behaves_like 'permitted policy', 'routes.create', archived: true end diff --git a/spec/policies/routing_constraint_zone_policy_spec.rb b/spec/policies/routing_constraint_zone_policy_spec.rb index 4b0f2cafe..2508b49f9 100644 --- a/spec/policies/routing_constraint_zone_policy_spec.rb +++ b/spec/policies/routing_constraint_zone_policy_spec.rb @@ -1,5 +1,8 @@ RSpec.describe RoutingConstraintZonePolicy, type: :policy do + let( :record ){ build_stubbed :routing_constraint_zone } + + permissions :create? do it_behaves_like 'permitted policy', 'routing_constraint_zones.create', archived: true end diff --git a/spec/policies/time_table_policy_spec.rb b/spec/policies/time_table_policy_spec.rb index 1283a9fcf..90e6600ea 100644 --- a/spec/policies/time_table_policy_spec.rb +++ b/spec/policies/time_table_policy_spec.rb @@ -1,5 +1,8 @@ RSpec.describe TimeTablePolicy, type: :policy do + let( :record ){ build_stubbed :time_table } + + permissions :duplicate? do it_behaves_like 'permitted policy and same organisation', 'time_tables.create', archived: true end diff --git a/spec/support/pundit/policies.rb b/spec/support/pundit/policies.rb index 56433b2ee..02fea2944 100644 --- a/spec/support/pundit/policies.rb +++ b/spec/support/pundit/policies.rb @@ -24,8 +24,8 @@ module Support into.module_eval do subject { described_class } let( :user_context ) { create_user_context(user: user, referential: referential) } - let( :referentail ) { create :referential } - let( :user ) { create :user } + let( :referential ) { build_stubbed :referential } + let( :user ) { build_stubbed :user } end end def with_user_permission(permission, &blk) diff --git a/spec/support/pundit/shared_examples.rb b/spec/support/pundit/shared_examples.rb index 4d14c46da..33ed1ffae 100644 --- a/spec/support/pundit/shared_examples.rb +++ b/spec/support/pundit/shared_examples.rb @@ -3,11 +3,11 @@ RSpec.shared_examples 'permitted policy and same organisation' do context 'permission absent → ' do it "denies a user with a different organisation" do - expect_it.not_to permit(user_context, referential) + expect_it.not_to permit(user_context, record) end it 'and also a user with the same organisation' do - user.update_attribute :organisation, referential.organisation - expect_it.not_to permit(user_context, referential) + user.organisation = referential.organisation + expect_it.not_to permit(user_context, record) end end @@ -17,19 +17,19 @@ RSpec.shared_examples 'permitted policy and same organisation' do end it 'denies a user with a different organisation' do - expect_it.not_to permit(user_context, referential) + expect_it.not_to permit(user_context, record) end it 'but allows it for a user with the same organisation' do - user.update_attribute :organisation, referential.organisation - expect_it.to permit(user_context, referential) + user.organisation = referential.organisation + expect_it.to permit(user_context, record) end if archived it 'removes the permission for archived referentials' do - user.update_attribute :organisation, referential.organisation - referential.update_attribute :archived_at, 42.seconds.ago - expect_it.not_to permit(user_context, referential) + user.organisation = referential.organisation + referential.archived_at = 42.seconds.ago + expect_it.not_to permit(user_context, record) end end end @@ -39,7 +39,7 @@ RSpec.shared_examples 'permitted policy' do | permission, archived: false| context 'permission absent → ' do it "denies a user with a different organisation" do - expect_it.not_to permit(user_context, referential) + expect_it.not_to permit(user_context, record) end end context 'permission present → ' do @@ -47,13 +47,13 @@ RSpec.shared_examples 'permitted policy' do add_permissions(permission, for_user: user) end it 'allows a user with a different organisation' do - expect_it.to permit(user_context, referential) + expect_it.to permit(user_context, record) end if archived it 'removes the permission for archived referentials' do - referential.update_attribute :archived_at, 42.seconds.ago - expect_it.not_to permit(user_context, referential) + referential.archived_at = 42.seconds.ago + expect_it.not_to permit(user_context, record) end end end |
