From d9faffc96a04dacdac8472f80c9e3f106e69a2e2 Mon Sep 17 00:00:00 2001 From: AEtherC0r3 Date: Fri, 14 Jul 2017 15:57:34 +0300 Subject: [PATCH] Implement track request acceptance Allow track submitter to request specific dates Redirect to Tracks#edit if a track doesn't have a room or start/end date before accepting it Don't allow the submitter or the track organizers to edit the request after it has been accepted or confirmed Restrict track selection in proposals and move track selection from Proposals form to events helper Mark cfp_active of the tracks table as not null and fill in true if nil --- .haml-lint_todo.yml | 2 + app/controllers/admin/tracks_controller.rb | 46 +++ app/controllers/tracks_controller.rb | 28 +- app/helpers/application_helper.rb | 2 +- app/helpers/events_helper.rb | 8 + app/models/ability.rb | 6 +- app/models/admin_ability.rb | 4 + app/models/event.rb | 8 + app/models/track.rb | 52 ++- .../tracks/_change_state_dropdown.html.haml | 34 ++ app/views/admin/tracks/_form.html.haml | 3 +- app/views/admin/tracks/index.html.haml | 32 +- app/views/proposals/_proposal_form.html.haml | 5 +- app/views/tracks/_form.html.haml | 4 +- app/views/tracks/index.html.haml | 29 +- app/views/tracks/show.html.haml | 17 +- config/routes.rb | 15 +- ...20134353_make_track_cfp_active_not_null.rb | 14 + db/schema.rb | 2 +- .../admin/tracks_controller_spec.rb | 203 ++++++++++- spec/controllers/tracks_controller_spec.rb | 66 +++- spec/factories/tracks.rb | 4 + spec/models/ability_spec.rb | 30 ++ spec/models/admin_ability_spec.rb | 4 +- spec/models/conference_spec.rb | 4 +- spec/models/event_spec.rb | 34 ++ spec/models/track_spec.rb | 315 +++++++++++++++++- 27 files changed, 909 insertions(+), 62 deletions(-) create mode 100644 app/views/admin/tracks/_change_state_dropdown.html.haml create mode 100644 db/migrate/20170720134353_make_track_cfp_active_not_null.rb diff --git a/.haml-lint_todo.yml b/.haml-lint_todo.yml index 11397297..89d18c3f 100644 --- a/.haml-lint_todo.yml +++ b/.haml-lint_todo.yml @@ -181,6 +181,7 @@ linters: - "app/views/tracks/index.html.haml" - "app/views/tracks/show.html.haml" - "app/views/conferences/_call_for_tracks.html.haml" + - "app/views/admin/tracks/_change_state_dropdown.html.haml" # Offense count: 223 InstanceVariables: @@ -245,6 +246,7 @@ linters: - "app/views/tracks/_form.html.haml" - "app/views/admin/cfps/_tracks_cfp.html.haml" - "app/views/conferences/_call_for_tracks.html.haml" + - "app/views/admin/tracks/_change_state_dropdown.html.haml" # Offense count: 32 IdNames: diff --git a/app/controllers/admin/tracks_controller.rb b/app/controllers/admin/tracks_controller.rb index 2cb6ec2b..3d51a677 100644 --- a/app/controllers/admin/tracks_controller.rb +++ b/app/controllers/admin/tracks_controller.rb @@ -20,6 +20,7 @@ module Admin def create @track = @program.tracks.new(track_params) @track.state = 'confirmed' + @track.cfp_active = true if @track.save redirect_to admin_conference_program_tracks_path(conference_id: @conference.short_title), notice: 'Track successfully created.' @@ -60,10 +61,55 @@ module Admin end end + def restart + update_state(:restart, "Review for #{@track.name} started!") + end + + def to_accept + update_state(:to_accept, "Track #{@track.name} marked as a possible acceptance!") + end + + def accept + if @track.room && @track.start_date && @track.end_date + update_state(:accept, "Track #{@track.name} accepted!") + else + flash[:alert] = 'Please make sure that the track has a room and start/end dates before accepting it' + redirect_to edit_admin_conference_program_track_path(@conference.short_title, @track) + end + end + + def confirm + update_state(:confirm, "Track #{@track.name} confirmed!") + end + + def to_reject + update_state(:to_reject, "Track #{@track.name} marked as a possible rejection!") + end + + def reject + update_state(:reject, "Track #{@track.name} rejected!") + end + + def cancel + update_state(:cancel, "Track #{@track.name} canceled!") + end + private def track_params params.require(:track).permit(:name, :description, :color, :short_name, :cfp_active, :start_date, :end_date, :room_id) end + + def update_state(transition, notice) + errors = @track.update_state(transition) + + if errors.blank? + flash[:notice] = notice + else + flash[:error] = errors + end + + redirect_back_or_to(admin_conference_program_tracks_path(conference_id: @conference.short_title)) + end end end diff --git a/app/controllers/tracks_controller.rb b/app/controllers/tracks_controller.rb index 59deae3c..9305950e 100644 --- a/app/controllers/tracks_controller.rb +++ b/app/controllers/tracks_controller.rb @@ -4,7 +4,7 @@ class TracksController < ApplicationController load_and_authorize_resource through: :program, find_by: :short_name def index - @tracks = current_user.tracks.where(program: @program) + @tracks = @tracks.where(submitter: current_user) end def show; end @@ -38,9 +38,33 @@ class TracksController < ApplicationController end end + def restart + update_state(:restart, "Track #{@track.name} re-submitted.") + end + + def confirm + update_state(:confirm, "Track #{@track.name} confirmed.") + end + + def withdraw + update_state(:withdraw, "Track #{@track.name} withdrawn.") + end + private def track_params - params.require(:track).permit(:name, :description, :color, :short_name) + params.require(:track).permit(:name, :description, :color, :short_name, :start_date, :end_date) + end + + def update_state(transition, notice) + errors = @track.update_state(transition) + + if errors.blank? + flash[:notice] = notice + else + flash[:error] = errors + end + + redirect_back_or_to(conference_program_tracks_path(conference_id: @conference.short_title)) end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index fdef7b4a..040927c1 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -56,7 +56,7 @@ module ApplicationHelper end def tracks(conference) - all = conference.program.tracks.map { |t| t.name if !t.self_organized? || t.confirmed? && t.cfp_active }.compact + all = conference.program.tracks.where(state: 'confirmed', cfp_active: true).pluck(:name) first = all[0...-1] last = all[-1] ts = '' diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb index 012bd930..dbc609ac 100644 --- a/app/helpers/events_helper.rb +++ b/app/helpers/events_helper.rb @@ -39,4 +39,12 @@ module EventsHelper content_tag :span, 'REPLACEMENT', class: (['label', 'label-info'] + label_classes) end end + + def track_selector_input(form) + if @program.tracks.any? + form.input :track_id, as: :select, + collection: @program.tracks.where(state: 'confirmed', cfp_active: true).pluck(:name, :id), + include_blank: true + end + end end diff --git a/app/models/ability.rb b/app/models/ability.rb index e5dbd7e4..63ec68dc 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -100,8 +100,10 @@ class Ability track.new_record? && track.program.cfps.for_tracks.try(:open?) end - can [:index, :show, :edit, :update], Track do |track| - user == track.submitter + can [:index, :show, :restart, :confirm, :withdraw], Track, submitter_id: user.id + + can [:edit, :update], Track do |track| + user == track.submitter && !(track.accepted? || track.confirmed?) end end diff --git a/app/models/admin_ability.rb b/app/models/admin_ability.rb index 716aecb0..b0c9d4c9 100644 --- a/app/models/admin_ability.rb +++ b/app/models/admin_ability.rb @@ -275,6 +275,10 @@ class AdminAbility can :manage, Track, id: track_ids_for_track_organizer + cannot [:edit, :update], Track do |track| + track.self_organized_and_accepted_or_confirmed? + end + # Show Roles in the admin sidebar and allow authorization of the index action can [:index, :show], Role do |role| role.resource_type == 'Conference' || role.resource_type == 'Track' diff --git a/app/models/event.rb b/app/models/event.rb index 8436aac9..682eb6c8 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -45,6 +45,7 @@ class Event < ActiveRecord::Base validates :max_attendees, numericality: { only_integer: true, greater_than_or_equal_to: 1, allow_nil: true } validate :max_attendees_no_more_than_room_size + validate :acceptable_track scope :confirmed, -> { where(state: 'confirmed') } scope :canceled, -> { where(state: 'canceled') } @@ -303,4 +304,11 @@ class Event < ActiveRecord::Base def conference_id program.conference_id end + + ## + # Allow only confirmed tracks that belong to the same program and are included in the cfp + def acceptable_track + return unless track && track.program && program + errors.add(:track, 'is invalid') unless track.confirmed? && track.cfp_active && track.program == program + end end diff --git a/app/models/track.rb b/app/models/track.rb index a53471bc..3743cd79 100644 --- a/app/models/track.rb +++ b/app/models/track.rb @@ -22,13 +22,13 @@ class Track < ActiveRecord::Base } validates :state, presence: true, - inclusion: { in: %w(new to_accept accepted confirmed to_reject rejected canceled withdrawn) }, - if: :self_organized? - validates :cfp_active, inclusion: { in: [true, false] }, if: :self_organized? + inclusion: { in: %w(new to_accept accepted confirmed to_reject rejected canceled withdrawn) } + validates :cfp_active, inclusion: { in: [true, false] } validates :start_date, presence: true, if: :self_organized_and_accepted_or_confirmed? validates :end_date, presence: true, if: :self_organized_and_accepted_or_confirmed? validates :room, presence: true, if: :self_organized_and_accepted_or_confirmed? - validate :valid_dates, if: :self_organized_and_accepted_or_confirmed? + validate :valid_dates + validate :valid_room, if: :self_organized_and_accepted_or_confirmed? before_validation :capitalize_color @@ -45,7 +45,7 @@ class Track < ActiveRecord::Base event :restart do transitions to: :new, from: [:rejected, :withdrawn, :canceled] end - event :readiness_to_accept do + event :to_accept do transitions to: :to_accept, from: [:new] end event :accept do @@ -54,7 +54,7 @@ class Track < ActiveRecord::Base event :confirm do transitions to: :confirmed, from: [:accepted], on_transition: :assign_role_to_submitter end - event :readiness_to_reject do + event :to_reject do transitions to: :to_reject, from: [:new] end event :reject do @@ -107,6 +107,7 @@ class Track < ActiveRecord::Base events.each do |event| event.track = nil + event.save! end end @@ -137,6 +138,19 @@ class Track < ActiveRecord::Base self_organized? && (accepted? || confirmed?) end + def update_state(transition) + error = '' + + begin + send(transition) + rescue Transitions::InvalidTransition => e + error += "State update failed. #{e.message} " + end + + error += errors.full_messages.join(', ') unless save + error + end + private def generate_guid @@ -162,26 +176,32 @@ class Track < ActiveRecord::Base end def valid_dates - return unless start_date && end_date - - if program && program.conference && program.conference.start_date && (start_date < program.conference.start_date) - errors.add(:start_date, "can't be before the conference start date (#{program.conference.end_date})") + if start_date && program && program.conference && program.conference.start_date && (start_date < program.conference.start_date) + errors.add(:start_date, "can't be before the conference start date (#{program.conference.start_date})") end - if program && program.conference && program.conference.start_date && (end_date < program.conference.start_date) - errors.add(:end_date, "can't be before the conference start date (#{program.conference.end_date})") + if end_date && program && program.conference && program.conference.start_date && (end_date < program.conference.start_date) + errors.add(:end_date, "can't be before the conference start date (#{program.conference.start_date})") end - if program && program.conference && program.conference.end_date && (start_date > program.conference.end_date) + if start_date && program && program.conference && program.conference.end_date && (start_date > program.conference.end_date) errors.add(:start_date, "can't be after the conference end date (#{program.conference.end_date})") end - if program && program.conference && program.conference.end_date && (end_date > program.conference.end_date) + if end_date && program && program.conference && program.conference.end_date && (end_date > program.conference.end_date) errors.add(:end_date, "can't be after the conference end date (#{program.conference.end_date})") end - if start_date > end_date - errors.add(:start_date, 'can\'t be after the end_date') + if start_date && end_date && (start_date > end_date) + errors.add(:start_date, 'can\'t be after the end date') + end + end + + ## + # Verify that the room is a room of the conference + def valid_room + if room && room.venue && room.venue.conference && program && program.conference && (program.conference != room.venue.conference) + errors.add(:room, "must be a room of #{program.conference.venue.name}") end end end diff --git a/app/views/admin/tracks/_change_state_dropdown.html.haml b/app/views/admin/tracks/_change_state_dropdown.html.haml new file mode 100644 index 00000000..0bbbcd7f --- /dev/null +++ b/app/views/admin/tracks/_change_state_dropdown.html.haml @@ -0,0 +1,34 @@ +- if track.transition_possible? :restart + %li= link_to 'Start review', + restart_admin_conference_program_track_path(@conference.short_title, track), + method: :patch, id: "restart_track_#{track.id}" + +- if track.transition_possible? :to_accept + %li= link_to 'Mark as possible acceptance', + to_accept_admin_conference_program_track_path(@conference.short_title, track), + method: :patch, id: "to_accept_track_#{track.id}" + +- if track.transition_possible? :accept + %li= link_to 'Accept track request', + accept_admin_conference_program_track_path(@conference.short_title, track), + method: :patch, id: "accept_track_#{track.id}" + +- if track.transition_possible? :confirm + %li= link_to 'Confirm track', + confirm_admin_conference_program_track_path(@conference.short_title, track), + method: :patch, id: "confirm_track_#{track.id}" + +- if track.transition_possible? :to_reject + %li= link_to 'Mark as possible rejection', + to_reject_admin_conference_program_track_path(@conference.short_title, track), + method: :patch, id: "to_reject_track_#{track.id}" + +- if track.transition_possible? :reject + %li= link_to 'Reject track request', + reject_admin_conference_program_track_path(@conference.short_title, track), + method: :patch, confirm: 'Are you sure?', id: "reject_track_#{track.id}" + +- if track.transition_possible? :cancel + %li= link_to 'Cancel track request', + cancel_admin_conference_program_track_path(@conference.short_title, track), + method: :patch, id: "cancel_track_#{track.id}" diff --git a/app/views/admin/tracks/_form.html.haml b/app/views/admin/tracks/_form.html.haml index cb2e8b01..eb12561b 100644 --- a/app/views/admin/tracks/_form.html.haml +++ b/app/views/admin/tracks/_form.html.haml @@ -19,6 +19,5 @@ - else Please add a venue with rooms, if you want to select a room for the track. = f.input :description, input_html: {rows: 2, data: { provide: 'markdown-editable' } }, hint: markdown_hint - - if @track.self_organized? - = f.input :cfp_active, label: 'Allow event submitters to select this track for their proposal' + = f.input :cfp_active, label: 'Allow event submitters to select this track for their proposal' = f.action :submit, as: :button, button_html: { class: 'btn btn-primary' } diff --git a/app/views/admin/tracks/index.html.haml b/app/views/admin/tracks/index.html.haml index 4da97a18..0cfe0ff2 100644 --- a/app/views/admin/tracks/index.html.haml +++ b/app/views/admin/tracks/index.html.haml @@ -40,21 +40,24 @@ = track.color %td - if track.self_organized? - = track.state + .btn-group + %button{ type: 'button', class: 'btn btn-link dropdown-toggle', 'data-toggle' => 'dropdown' } + = track.state.humanize + %span.caret + %ul.dropdown-menu{ role: 'menu' } + = render 'change_state_dropdown', track: track - else = track.state.humanize %td - - if track.self_organized? - = check_box_tag "#{@conference.short_title}_#{track.short_name}", track.id, track.cfp_active, - class: 'switch-checkbox', method: :patch, - url: toggle_cfp_inclusion_admin_conference_program_track_path(@conference.short_title, id: track.short_name)+"?included=", - data: { size: 'small', - on_color: 'success', - off_color: 'warning', - on_text: 'Yes', - off_text: 'No' } - - else - %i.fa.fa-check + = check_box_tag "#{@conference.short_title}_#{track.short_name}", track.id, track.cfp_active, + class: 'switch-checkbox', method: :patch, + url: toggle_cfp_inclusion_admin_conference_program_track_path(@conference.short_title, id: track.short_name)+"?included=", + data: { size: 'small', + on_color: 'success', + off_color: 'warning', + on_text: 'Yes', + off_text: 'No' } + %td - if track.room = link_to track.room.name, admin_conference_venue_room_path(@conference.short_title, track.room.id) @@ -72,8 +75,9 @@ N/A %td .btn-group{role: "group"} - = link_to 'Edit', edit_admin_conference_program_track_path(@conference.short_title, track), - method: :get, class: 'btn btn-primary' + - if can? :edit, track + = link_to 'Edit', edit_admin_conference_program_track_path(@conference.short_title, track), + method: :get, class: 'btn btn-primary' - if can? :destroy, track = link_to 'Delete', admin_conference_program_track_path(@conference.short_title, track), method: :delete, class: 'btn btn-danger', diff --git a/app/views/proposals/_proposal_form.html.haml b/app/views/proposals/_proposal_form.html.haml index f31651a1..4b66240c 100644 --- a/app/views/proposals/_proposal_form.html.haml +++ b/app/views/proposals/_proposal_form.html.haml @@ -6,10 +6,7 @@ = speaker_selector_input f - - if @program.tracks.any? - = f.input :track_id, as: :select, - collection: @program.tracks.map {|track| ["#{track.name}", track.id] }, - include_blank: true + = track_selector_input f = f.input :event_type_id, as: :select, collection: @conference.program.event_types.map {|type| ["#{type.title} - #{show_time(type.length)}", type.id, diff --git a/app/views/tracks/_form.html.haml b/app/views/tracks/_form.html.haml index 0e48a851..2a55df5a 100644 --- a/app/views/tracks/_form.html.haml +++ b/app/views/tracks/_form.html.haml @@ -9,9 +9,11 @@ Track .row .col-md-12 - = semantic_form_for(@track, url: (@track.new_record? ? conference_program_tracks_path : conference_program_track_path(@conference.short_title, @track))) do |f| + = semantic_form_for(@track, url: (@track.new_record? ? conference_program_tracks_path(@conference.short_title) : conference_program_track_path(@conference.short_title, @track))) do |f| = f.input :name = f.input :short_name, hint: "A short and unique handle for the track, using only letters, numbers, underscores, and dashes. This will be used to identify the track in URLs etc. Example: 'my_awesome_track'", input_html: { required: 'required', pattern: '[a-zA-Z0-9_-]+', title: 'Only letters, numbers, underscores, and dashes.' } = f.input :color, input_html: {size: 6, type: 'color'}, required: true + = f.input :start_date, as: :string, input_html: { id: 'registration-period-start-datepicker', start_date: @conference.start_date, end_date: @conference.end_date, readonly: 'readonly' } + = f.input :end_date, as: :string, input_html: { id: 'registration-period-end-datepicker', readonly: 'readonly' } = f.input :description, input_html: {rows: 2, data: { provide: 'markdown-editable' } }, required: true, hint: markdown_hint = f.action :submit, as: :button, button_html: { class: 'btn btn-primary' } diff --git a/app/views/tracks/index.html.haml b/app/views/tracks/index.html.haml index 7e16304f..fd1235d4 100644 --- a/app/views/tracks/index.html.haml +++ b/app/views/tracks/index.html.haml @@ -16,6 +16,8 @@ %th Description %th Color %th State + %th Start Date + %th End Date %th Actions %tbody - @tracks.each do |track| @@ -34,10 +36,31 @@ %td = track.state.humanize %td - = link_to 'Edit', edit_conference_program_track_path(@conference.short_title, track), - method: :get, class: 'btn btn-primary' + - if track.start_date + = track.start_date.strftime('%A, %B %-d. %Y') + - else + N/A + %td + - if track.end_date + = track.end_date.strftime('%A, %B %-d. %Y') + - else + N/A + %td + - if track.transition_possible? :confirm + = link_to 'Confirm', confirm_conference_program_track_path(@conference.short_title, track), + method: :patch, class: 'btn btn-mini btn-success', id: "confirm_track_#{track.id}" + - if track.transition_possible? :withdraw + = link_to 'Withdraw', withdraw_conference_program_track_path(@conference.short_title, track), + method: :patch, data: { confirm: 'Are you sure you want to withdraw this track request?' }, + class: 'btn btn-mini btn-warning', id: "withdraw_track_request_#{track.id}" + - if track.transition_possible? :restart + = link_to 'Re-Submit', restart_conference_program_track_path(@conference.short_title, track), + method: :patch, class: 'btn btn-mini btn-success', id: "resubmit_track_request_#{track.id}" + - if can? :edit, track + = link_to 'Edit', edit_conference_program_track_path(@conference.short_title, track), + method: :get, class: 'btn btn-primary' .row .col-md-12 - - if can? :create, @track + - if can? :new, @program.tracks.new = link_to "New Track request", new_conference_program_track_path(@conference.short_title), class: 'btn btn-success pull-right' diff --git a/app/views/tracks/show.html.haml b/app/views/tracks/show.html.haml index 37c180ae..c5bc27e5 100644 --- a/app/views/tracks/show.html.haml +++ b/app/views/tracks/show.html.haml @@ -17,10 +17,25 @@ State: %dd = @track.state.humanize + %dt + Start date: + %dd + - if @track.start_date + = @track.start_date.strftime('%A, %B %-d. %Y') + - else + N/A + %dt + End date: + %dd + - if @track.end_date + = @track.end_date.strftime('%A, %B %-d. %Y') + - else + N/A %dt Description %dd = @track.description .row .col-md-12.text-right - = link_to 'Edit Track request', edit_conference_program_track_path(@conference.short_title, @track), class: 'btn btn-primary' + - if can? :edit, @track + = link_to 'Edit Track request', edit_conference_program_track_path(@conference.short_title, @track), class: 'btn btn-primary' diff --git a/config/routes.rb b/config/routes.rb index be507d35..d4d6d0ee 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -69,6 +69,13 @@ Osem::Application.routes.draw do resources :tracks do member do patch :toggle_cfp_inclusion + patch :restart + patch :to_accept + patch :accept + patch :confirm + patch :to_reject + patch :reject + patch :cancel end end resources :event_types @@ -149,7 +156,13 @@ Osem::Application.routes.draw do patch '/restart' => 'proposals#restart' end end - resources :tracks, except: :destroy + resources :tracks, except: :destroy do + member do + patch :restart + patch :confirm + patch :withdraw + end + end end # TODO: change conference_registrations to singular resource diff --git a/db/migrate/20170720134353_make_track_cfp_active_not_null.rb b/db/migrate/20170720134353_make_track_cfp_active_not_null.rb new file mode 100644 index 00000000..0f75d046 --- /dev/null +++ b/db/migrate/20170720134353_make_track_cfp_active_not_null.rb @@ -0,0 +1,14 @@ +class MakeTrackCfpActiveNotNull < ActiveRecord::Migration + class TmpTrack < ActiveRecord::Base + self.table_name = 'tracks' + end + + def change + TmpTrack.where(cfp_active: nil).each do |track| + track.cfp_active = true + track.save! + end + + change_column_null :tracks, :cfp_active, false + end +end diff --git a/db/schema.rb b/db/schema.rb index aa2f160d..f8cc3ddc 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -520,7 +520,7 @@ ActiveRecord::Schema.define(version: 20170807092805) do t.integer "program_id" t.string "short_name", null: false t.string "state", default: "new", null: false - t.boolean "cfp_active" + t.boolean "cfp_active", null: false t.integer "submitter_id" t.integer "room_id" t.date "start_date" diff --git a/spec/controllers/admin/tracks_controller_spec.rb b/spec/controllers/admin/tracks_controller_spec.rb index ed76fe68..70ca48a1 100644 --- a/spec/controllers/admin/tracks_controller_spec.rb +++ b/spec/controllers/admin/tracks_controller_spec.rb @@ -5,7 +5,7 @@ describe Admin::TracksController do let(:conference) { create(:conference) } let!(:track) { create(:track, program: conference.program, color: '#800080') } - let!(:self_organized_track) { create(:track, :self_organized, program: conference.program) } + let!(:self_organized_track) { create(:track, :self_organized, program: conference.program, name: 'My awesome track') } before :each do sign_in(admin) @@ -83,6 +83,7 @@ describe Admin::TracksController do it 'the new tracks has the correct attributes' do expect(assigns(:track).state).to eq 'confirmed' + expect(assigns(:track).cfp_active).to eq true end end @@ -257,4 +258,204 @@ describe Admin::TracksController do end end end + + describe 'PATCH #restart' do + before :each do + self_organized_track.state = 'canceled' + self_organized_track.save! + patch :restart, conference_id: conference.short_title, id: self_organized_track.short_name + self_organized_track.reload + end + + it 'assigns the correct track' do + expect(assigns(:track)).to eq self_organized_track + end + + it 'shows message in flash notice' do + expect(flash[:notice]).to eq 'Review for My awesome track started!' + end + + it 'changes the track\'s state to new' do + expect(self_organized_track.state).to eq 'new' + end + end + + describe 'PATCH #to_accept' do + before :each do + patch :to_accept, conference_id: conference.short_title, id: self_organized_track.short_name + self_organized_track.reload + end + + it 'assigns the correct track' do + expect(assigns(:track)).to eq self_organized_track + end + + it 'shows message in flash notice' do + expect(flash[:notice]).to eq 'Track My awesome track marked as a possible acceptance!' + end + + it 'changes the track\'s state to to_accept' do + expect(self_organized_track.state).to eq 'to_accept' + end + end + + describe 'PATCH #accept' do + shared_examples 'fails to accept' do |start_date, end_date, room| + before :each do + self_organized_track.start_date = start_date ? Date.today : nil + self_organized_track.end_date = end_date ? Date.today : nil + if room + conference.venue = create(:venue) + self_organized_track.room = create(:room, venue: conference.venue) + else + self_organized_track.room = nil + end + self_organized_track.save! + + patch :accept, conference_id: conference.short_title, id: self_organized_track.short_name + self_organized_track.reload + end + + it 'redirects to Tracks#edit' do + expect(response).to redirect_to edit_admin_conference_program_track_path(conference.short_title, self_organized_track) + end + + it 'shows message in flash alert' do + expect(flash[:alert]).to eq 'Please make sure that the track has a room and start/end dates before accepting it' + end + end + + context 'has start_date, end_date and room' do + before :each do + self_organized_track.start_date = Date.today + self_organized_track.end_date = Date.today + conference.venue = create(:venue) + self_organized_track.room = create(:room, venue: conference.venue) + self_organized_track.save! + + patch :accept, conference_id: conference.short_title, id: self_organized_track.short_name + self_organized_track.reload + end + + it 'assigns the correct track' do + expect(assigns(:track)).to eq self_organized_track + end + + it 'shows message in flash notice' do + expect(flash[:notice]).to eq 'Track My awesome track accepted!' + end + + it 'changes the track\'s state to accepted' do + expect(self_organized_track.state).to eq 'accepted' + end + end + + context 'has start_date and end_date' do + it_behaves_like 'fails to accept', true, true, false + end + + context 'has start_date and room' do + it_behaves_like 'fails to accept', true, false, true + end + + context 'has start_date' do + it_behaves_like 'fails to accept', true, false, false + end + + context 'has end_date and room' do + it_behaves_like 'fails to accept', false, true, true + end + + context 'has end_date' do + it_behaves_like 'fails to accept', false, true, false + end + + context 'has room' do + it_behaves_like 'fails to accept', false, false, true + end + + context 'has non of start_date, end_date, room' do + it_behaves_like 'fails to accept', false, false, false + end + end + + describe 'PATCH #confirm' do + before :each do + self_organized_track.state = 'accepted' + self_organized_track.save! + patch :confirm, conference_id: conference.short_title, id: self_organized_track.short_name + self_organized_track.reload + end + + it 'assigns the correct track' do + expect(assigns(:track)).to eq self_organized_track + end + + it 'shows message in flash notice' do + expect(flash[:notice]).to eq 'Track My awesome track confirmed!' + end + + it 'changes the track\'s state to confirmed' do + expect(self_organized_track.state).to eq 'confirmed' + end + end + + describe 'PATCH #to_reject' do + before :each do + patch :to_reject, conference_id: conference.short_title, id: self_organized_track.short_name + self_organized_track.reload + end + + it 'assigns the correct track' do + expect(assigns(:track)).to eq self_organized_track + end + + it 'shows message in flash notice' do + expect(flash[:notice]).to eq 'Track My awesome track marked as a possible rejection!' + end + + it 'changes the track\'s state to to_reject' do + expect(self_organized_track.state).to eq 'to_reject' + end + end + + describe 'PATCH #reject' do + before :each do + patch :reject, conference_id: conference.short_title, id: self_organized_track.short_name + self_organized_track.reload + end + + it 'assigns the correct track' do + expect(assigns(:track)).to eq self_organized_track + end + + it 'shows message in flash notice' do + expect(flash[:notice]).to eq 'Track My awesome track rejected!' + end + + it 'changes the track\'s state to rejected' do + expect(self_organized_track.state).to eq 'rejected' + end + end + + describe 'PATCH #cancel' do + before :each do + self_organized_track.state = 'confirmed' + self_organized_track.save! + patch :cancel, conference_id: conference.short_title, id: self_organized_track.short_name + self_organized_track.reload + end + + it 'assigns the correct track' do + expect(assigns(:track)).to eq self_organized_track + end + + it 'shows message in flash notice' do + expect(flash[:notice]).to eq 'Track My awesome track canceled!' + end + + it 'changes the track\'s state to canceled' do + expect(self_organized_track.state).to eq 'canceled' + end + end end diff --git a/spec/controllers/tracks_controller_spec.rb b/spec/controllers/tracks_controller_spec.rb index 373435c2..0e3a0553 100644 --- a/spec/controllers/tracks_controller_spec.rb +++ b/spec/controllers/tracks_controller_spec.rb @@ -1,12 +1,11 @@ require 'spec_helper' describe TracksController do - # A regular user should be used when the track requests have been enabled let(:user) { create(:admin) } let(:conference) { create(:conference) } let!(:regular_track) { create(:track, program: conference.program) } - let!(:self_organized_track) { create(:track, :self_organized, program: conference.program, submitter: user, color: '#800080') } + let!(:self_organized_track) { create(:track, :self_organized, program: conference.program, submitter: user, name: 'My awesome track', color: '#800080') } before :each do sign_in(user) @@ -176,4 +175,67 @@ describe TracksController do end end end + + describe 'PATCH #restart' do + before :each do + self_organized_track.state = 'withdrawn' + self_organized_track.save! + patch :restart, conference_id: conference.short_title, id: self_organized_track.short_name + self_organized_track.reload + end + + it 'assigns the correct track' do + expect(assigns(:track)).to eq self_organized_track + end + + it 'shows message in flash notice' do + expect(flash[:notice]).to eq 'Track My awesome track re-submitted.' + end + + it 'changes the track\'s state to new' do + expect(self_organized_track.state).to eq 'new' + end + end + + describe 'PATCH #confirm' do + before :each do + self_organized_track.state = 'accepted' + self_organized_track.save! + patch :confirm, conference_id: conference.short_title, id: self_organized_track.short_name + self_organized_track.reload + end + + it 'assigns the correct track' do + expect(assigns(:track)).to eq self_organized_track + end + + it 'shows message in flash notice' do + expect(flash[:notice]).to eq 'Track My awesome track confirmed.' + end + + it 'changes the track\'s state to confirmed' do + expect(self_organized_track.state).to eq 'confirmed' + end + end + + describe 'PATCH #withdraw' do + before :each do + self_organized_track.state = 'confirmed' + self_organized_track.save! + patch :withdraw, conference_id: conference.short_title, id: self_organized_track.short_name + self_organized_track.reload + end + + it 'assigns the correct track' do + expect(assigns(:track)).to eq self_organized_track + end + + it 'shows message in flash notice' do + expect(flash[:notice]).to eq 'Track My awesome track withdrawn.' + end + + it 'changes the track\'s state to withdrawn' do + expect(self_organized_track.state).to eq 'withdrawn' + end + end end diff --git a/spec/factories/tracks.rb b/spec/factories/tracks.rb index d30a686e..d61c9a1b 100644 --- a/spec/factories/tracks.rb +++ b/spec/factories/tracks.rb @@ -5,12 +5,16 @@ FactoryGirl.define do color { Faker::Color.hex_color } short_name { SecureRandom.urlsafe_base64(5) } state 'confirmed' + cfp_active true program trait :self_organized do association :submitter, factory: :user state 'new' cfp_active false + start_date { Date.today } + end_date { Date.today } + room end end end diff --git a/spec/models/ability_spec.rb b/spec/models/ability_spec.rb index 2144cec6..ff3f14cb 100644 --- a/spec/models/ability_spec.rb +++ b/spec/models/ability_spec.rb @@ -26,6 +26,7 @@ describe 'User' do let(:program_with_cfp) { create(:program, :with_cfp) } let(:program_without_cfp) { create(:program) } + let(:program_with_call_for_tracks) { create(:cfp, cfp_type: 'tracks').program } let(:conference_with_open_registration) { create(:conference) } let!(:open_registration_period) { create(:registration_period, conference: conference_with_open_registration, start_date: Date.current - 6.days) } let(:conference_with_closed_registration) { create(:conference) } @@ -82,6 +83,10 @@ describe 'User' do let(:user_event_with_cfp) { create(:event, users: [user], program: program_with_cfp) } let(:user_commercial) { create(:commercial, commercialable: user_event_with_cfp) } + let(:user_self_organized_track) { create(:track, :self_organized, submitter: user) } + let(:accepted_user_self_organized_track) { create(:track, :self_organized, submitter: user, state: 'accepted') } + let(:confirmed_user_self_organized_track) { create(:track, :self_organized, submitter: user, state: 'confirmed') } + let(:other_self_organized_track) { create(:track, :self_organized) } it{ should be_able_to(:manage, user) } @@ -114,6 +119,31 @@ describe 'User' do it{ should be_able_to(:create, user_event_with_cfp.commercials.new) } it{ should be_able_to(:manage, user_commercial) } it{ should_not be_able_to(:manage, commercial_event_unconfirmed) } + + it{ should be_able_to(:new, Track.new(program: program_with_call_for_tracks)) } + it{ should be_able_to(:create, Track.new(program: program_with_call_for_tracks)) } + it{ should_not be_able_to(:new, Track.new(program: program_without_cfp)) } + it{ should_not be_able_to(:create, Track.new(program: program_without_cfp)) } + + it{ should be_able_to(:index, user_self_organized_track) } + it{ should be_able_to(:show, user_self_organized_track) } + it{ should be_able_to(:restart, user_self_organized_track) } + it{ should be_able_to(:confirm, user_self_organized_track) } + it{ should be_able_to(:withdraw, user_self_organized_track) } + it{ should_not be_able_to(:index, other_self_organized_track) } + it{ should_not be_able_to(:show, other_self_organized_track) } + it{ should_not be_able_to(:restart, other_self_organized_track) } + it{ should_not be_able_to(:confirm, other_self_organized_track) } + it{ should_not be_able_to(:withdraw, other_self_organized_track) } + + it{ should be_able_to(:edit, user_self_organized_track) } + it{ should be_able_to(:update, user_self_organized_track) } + it{ should_not be_able_to(:edit, accepted_user_self_organized_track) } + it{ should_not be_able_to(:update, accepted_user_self_organized_track) } + it{ should_not be_able_to(:edit, confirmed_user_self_organized_track) } + it{ should_not be_able_to(:update, confirmed_user_self_organized_track) } + it{ should_not be_able_to(:edit, other_self_organized_track) } + it{ should_not be_able_to(:update, other_self_organized_track) } end end end diff --git a/spec/models/admin_ability_spec.rb b/spec/models/admin_ability_spec.rb index 16456a19..126d32de 100644 --- a/spec/models/admin_ability_spec.rb +++ b/spec/models/admin_ability_spec.rb @@ -44,7 +44,7 @@ describe 'User with admin role' do let!(:my_event_schedule) { create(:event_schedule, schedule: my_schedule) } let!(:other_event_schedule) { create(:event_schedule, schedule: other_schedule) } - let!(:my_self_organized_track) { create(:track, :self_organized, program: my_conference.program) } + let!(:my_self_organized_track) { create(:track, :self_organized, program: my_conference.program, state: 'confirmed') } context 'user #is_admin?' do let(:venue) { my_conference.venue } @@ -505,6 +505,8 @@ describe 'User with admin role' do it{ should be_able_to(:show, my_conference.program) } it{ should be_able_to(:update, new_track) } it{ should be_able_to(:manage, my_self_organized_track) } + it{ should_not be_able_to(:edit, my_self_organized_track) } + it{ should_not be_able_to(:update, my_self_organized_track) } it_behaves_like 'user with any role' it_behaves_like 'user with non-organizer role', 'track_organizer' diff --git a/spec/models/conference_spec.rb b/spec/models/conference_spec.rb index 91c06140..26070134 100755 --- a/spec/models/conference_spec.rb +++ b/spec/models/conference_spec.rb @@ -606,8 +606,8 @@ describe Conference do describe 'tracks_distribution' do before do subject.email_settings = create(:email_settings) - @track_one = create(:track, name: 'Track One', color: '#000000') - @track_two = create(:track, name: 'Track Two', color: '#ffffff') + @track_one = create(:track, name: 'Track One', color: '#000000', program: subject.program) + @track_two = create(:track, name: 'Track Two', color: '#ffffff', program: subject.program) end describe '#tracks_distribution' do diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index b723d151..3ada61ca 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -96,6 +96,40 @@ describe Event do end end end + + describe '#acceptable_track' do + context 'is valid' do + it 'when the track belong to the same program, is confirmed and is included in the cfp' do + track = create(:track, state: 'confirmed', cfp_active: true, program: conference.program) + event = build(:event, program: conference.program, track: track) + expect(event.valid?).to eq true + end + end + + context 'is invalid' do + it 'when the track doesn\'t have the same program' do + track = create(:track, state: 'confirmed', cfp_active: true) + event = build(:event, program: conference.program, track: track) + expect(event.valid?).to eq false + expect(event.errors[:track]).to eq ['is invalid'] + end + + it 'when the track is unconfirmed' do + track = create(:track, cfp_active: true, program: conference.program) + allow(track).to receive(:confirmed?).and_return(false) + event = build(:event, program: conference.program, track: track) + expect(event.valid?).to eq false + expect(event.errors[:track]).to eq ['is invalid'] + end + + it 'when the track isn\'t included in the cfp' do + track = create(:track, state: 'confirmed', cfp_active: false, program: conference.program) + event = build(:event, program: conference.program, track: track) + expect(event.valid?).to eq false + expect(event.errors[:track]).to eq ['is invalid'] + end + end + end end describe '#comments_count' do diff --git a/spec/models/track_spec.rb b/spec/models/track_spec.rb index 840641cb..4853f31d 100644 --- a/spec/models/track_spec.rb +++ b/spec/models/track_spec.rb @@ -8,6 +8,7 @@ describe Track do describe 'association' do it { is_expected.to belong_to(:program) } it { is_expected.to belong_to(:submitter).class_name('User') } + it { is_expected.to belong_to(:room) } it { is_expected.to have_many(:events) } end @@ -23,23 +24,99 @@ describe Track do it { is_expected.to allow_value('My_track_name').for(:short_name) } it { is_expected.to_not allow_value('My track name').for(:short_name) } it { is_expected.to validate_uniqueness_of(:short_name).scoped_to(:program_id) } + it { is_expected.to validate_presence_of(:state) } + it { is_expected.to validate_inclusion_of(:state).in_array(%w[new to_accept accepted confirmed to_reject rejected canceled withdrawn]) } + it { is_expected.to validate_inclusion_of(:cfp_active).in_array([true, false]) } - context 'when self-organized' do + context 'when self_organized_and_accepted_or_confirmed? returns true' do before :each do - allow(subject).to receive(:self_organized?).and_return(true) + allow(subject).to receive(:self_organized_and_accepted_or_confirmed?).and_return(true) end - it { is_expected.to validate_presence_of(:state) } - it { is_expected.to validate_inclusion_of(:cfp_active).in_array([true, false]) } + it { is_expected.to validate_presence_of(:start_date) } + it { is_expected.to validate_presence_of(:end_date) } + it { is_expected.to validate_presence_of(:room) } end - context 'when regular' do + context 'when self_organized_and_accepted_or_confirmed? returns false' do before :each do - allow(subject).to receive(:self_organized?).and_return(false) + allow(subject).to receive(:self_organized_and_accepted_or_confirmed?).and_return(false) end - it { is_expected.to_not validate_presence_of(:state) } - it { is_expected.to_not validate_inclusion_of(:cfp_active) } + it { is_expected.to_not validate_presence_of(:start_date) } + it { is_expected.to_not validate_presence_of(:end_date) } + it { is_expected.to_not validate_presence_of(:room) } + end + + describe '#valid_dates' do + before :each do + @conference = create(:conference, start_date: 1.day.ago, end_date: 2.days.from_now) + end + + context 'is valid' do + it 'when the track\'s start date is before it\'s end date and between the conference start/end dates' do + track = build(:track, start_date: Date.today, end_date: Date.tomorrow, program: @conference.program) + expect(track.valid?).to eq true + end + end + + context 'is invalid' do + it 'when the track\'s start date is before the conference\'s start date' do + track = build(:track, start_date: 2.days.ago, end_date: Date.tomorrow, program: @conference.program) + expect(track.valid?).to eq false + expect(track.errors[:start_date]).to eq ["can't be before the conference start date (#{1.day.ago.to_date})"] + end + + it 'when the track\'s end date is before the conference\'s start date' do + track = build(:track, start_date: 3.days.ago, end_date: 2.days.ago, program: @conference.program) + expect(track.valid?).to eq false + expect(track.errors[:end_date]).to eq ["can't be before the conference start date (#{1.day.ago.to_date})"] + end + + it 'when the track\'s start date is after the conference\'s end date' do + track = build(:track, start_date: 3.days.from_now, end_date: 4.days.from_now, program: @conference.program) + expect(track.valid?).to eq false + expect(track.errors[:start_date]).to eq ["can't be after the conference end date (#{2.days.from_now.to_date})"] + end + + it 'when the track\'s end date is after the conference\'s end date' do + track = build(:track, start_date: Date.today, end_date: 3.days.from_now, program: @conference.program) + expect(track.valid?).to eq false + expect(track.errors[:end_date]).to eq ["can't be after the conference end date (#{2.days.from_now.to_date})"] + end + + it 'when the track\'s start date is after it\'s end date' do + track = build(:track, start_date: 1.day.from_now, end_date: 1.day.ago) + expect(track.valid?).to eq false + expect(track.errors[:start_date]).to eq ['can\'t be after the end date'] + end + end + end + + describe '#valid_room' do + before :each do + @conference = create(:conference) + @conference.venue = create(:venue, name: 'The venue') + end + + context 'is valid' do + it 'when the track\'s room belongs to the venue of the conference' do + room = create(:room, venue: @conference.venue) + track = build(:track, :self_organized, state: 'accepted', program: @conference.program, room: room) + expect(track.valid?).to eq true + end + end + + context 'is invalid' do + it 'when the track\'s room doesn\'t belong to the venue of the track\'s conference' do + other_conference = create(:conference) + other_conference.venue = create(:venue) + room = create(:room, venue: other_conference.venue) + track = build(:track, :self_organized, state: 'accepted', program: @conference.program, room: room) + expect(track.valid?).to eq false + expect(track.errors[:room]).to eq ['must be a room of The venue'] + end + end end end @@ -54,4 +131,226 @@ describe Track do expect(track.self_organized?).to eq false end end + + describe '#transition_possible?' do + shared_examples 'transition_possible?' do |state, transition, expected| + it "returns #{expected} for #{transition} event, when the track's state is #{state}}" do + my_self_organized_track = create(:track, :self_organized, state: state) + expect(my_self_organized_track.transition_possible?(transition.to_sym)).to eq expected + end + end + + states = [:new, :to_accept, :accepted, :confirmed, :to_reject, :rejected, :canceled, :withdrawn] + transitions = [:restart, :to_accept, :accept, :confirm, :to_reject, :reject, :cancel, :withdraw] + + states_transitions = { new: { restart: false, to_accept: true, accept: true, confirm: false, to_reject: true, reject: true, cancel: false, withdraw: true }, + to_accept: { restart: false, to_accept: false, accept: true, confirm: false, to_reject: false, reject: false, cancel: true, withdraw: true }, + accepted: { restart: false, to_accept: false, accept: false, confirm: true, to_reject: false, reject: false, cancel: true, withdraw: true }, + confirmed: { restart: false, to_accept: false, accept: false, confirm: false, to_reject: false, reject: false, cancel: true, withdraw: true }, + to_reject: { restart: false, to_accept: false, accept: false, confirm: false, to_reject: false, reject: true, cancel: true, withdraw: true }, + rejected: { restart: true, to_accept: false, accept: false, confirm: false, to_reject: false, reject: false, cancel: false, withdraw: false }, + canceled: { restart: true, to_accept: false, accept: false, confirm: false, to_reject: false, reject: false, cancel: false, withdraw: false }, + withdrawn: { restart: true, to_accept: false, accept: false, confirm: false, to_reject: false, reject: false, cancel: false, withdraw: false } } + + states.each do |state| + transitions.each do |transition| + it_behaves_like 'transition_possible?', state, transition, states_transitions[state.to_sym][transition.to_sym] + end + end + end + + describe '#assign_role_to_submitter' do + before :each do + Role.where(name: 'track_organizer', resource: self_organized_track).first_or_create + @submitter = self_organized_track.submitter + end + + it 'gives the role of the track organizer to the submitter of the track' do + expect(@submitter.has_role?(:track_organizer, self_organized_track)).to eq false + self_organized_track.assign_role_to_submitter + expect(@submitter.has_role?(:track_organizer, self_organized_track)).to eq true + end + + it 'is executed when the track is confirmed' do + self_organized_track.state = 'accepted' + self_organized_track.save! + expect(@submitter.has_role?(:track_organizer, self_organized_track)).to eq false + self_organized_track.confirm + expect(@submitter.has_role?(:track_organizer, self_organized_track)).to eq true + end + end + + describe '#revoke_role_and_cleanup' do + before :each do + Role.where(name: 'track_organizer', resource: self_organized_track).first_or_create + @a_track_organizer = create(:user) + self_organized_track.state = 'confirmed' + self_organized_track.cfp_active = true + self_organized_track.save! + @a_track_organizer.add_role 'track_organizer', self_organized_track + @an_event_of_the_track = create(:event, program: self_organized_track.program, track: self_organized_track) + end + + it 'revokes the role of the track organizer' do + expect(@a_track_organizer.has_role?(:track_organizer, self_organized_track)).to eq true + self_organized_track.revoke_role_and_cleanup + expect(@a_track_organizer.has_role?(:track_organizer, self_organized_track)).to eq false + end + + it 'removes the track from the events that have it set' do + expect(@an_event_of_the_track.track).to eq self_organized_track + self_organized_track.revoke_role_and_cleanup + @an_event_of_the_track.reload + expect(@an_event_of_the_track.track).to eq nil + end + + it 'is executed when the track is canceled' do + self_organized_track.state = 'confirmed' + self_organized_track.save! + self_organized_track.cancel + expect(@a_track_organizer.has_role?(:track_organizer, self_organized_track)).to eq false + @an_event_of_the_track.reload + expect(@an_event_of_the_track.track).to eq nil + end + + it 'is executed when the track is withdrawn' do + self_organized_track.withdraw + expect(@a_track_organizer.has_role?(:track_organizer, self_organized_track)).to eq false + @an_event_of_the_track.reload + expect(@an_event_of_the_track.track).to eq nil + end + end + + describe '#accepted?' do + context 'returns true' do + it 'when the state is "accepted"' do + self_organized_track.state = 'accepted' + self_organized_track.save! + expect(self_organized_track.accepted?).to eq true + end + end + + context 'returns false' do + %w[new to_accept confirmed to_reject rejected canceled withdrawn].each do |state| + it "when the state is \"#{state}\"" do + self_organized_track.state = state + self_organized_track.save! + expect(self_organized_track.accepted?).to eq false + end + end + end + end + + describe '#confirmed?' do + context 'returns true' do + it 'when the state is "confirmed"' do + self_organized_track.state = 'confirmed' + self_organized_track.save! + expect(self_organized_track.confirmed?).to eq true + end + end + + context 'returns false' do + %w[new to_accept accepted to_reject rejected canceled withdrawn].each do |state| + it "when the state is \"#{state}\"" do + self_organized_track.state = state + self_organized_track.save! + expect(self_organized_track.confirmed?).to eq false + end + end + end + end + + # accepted? and confirmed? are mutually exclusive (they can't be both true) + describe '#self_organized_and_accepted_or_confirmed?' do + context 'returns true' do + context 'when self_organized? returns true' do + before :each do + allow(track).to receive(:self_organized?).and_return(true) + end + + context 'accepted? returns true and confirmed? returns false' do + before :each do + allow(track).to receive(:accepted?).and_return(true) + allow(track).to receive(:confirmed?).and_return(false) + end + + it { expect(track.self_organized_and_accepted_or_confirmed?).to eq true } + end + + context 'accepted? returns false and confirmed? returns true' do + before :each do + allow(track).to receive(:accepted?).and_return(false) + allow(track).to receive(:confirmed?).and_return(true) + end + + it { expect(track.self_organized_and_accepted_or_confirmed?).to eq true } + end + end + end + + context 'returns false' do + context 'when self_organized? returns true' do + before :each do + allow(track).to receive(:self_organized?).and_return(true) + end + + context 'accepted? returns false and confirmed? returns false' do + before :each do + allow(track).to receive(:accepted?).and_return(false) + allow(track).to receive(:confirmed?).and_return(false) + end + + it { expect(track.self_organized_and_accepted_or_confirmed?).to eq false } + end + end + + context 'when self_organized? returns false' do + before :each do + allow(track).to receive(:self_organized?).and_return(false) + end + + context 'accepted? returns false and confirmed? returns false' do + before :each do + allow(track).to receive(:accepted?).and_return(false) + allow(track).to receive(:confirmed?).and_return(false) + end + + it { expect(track.self_organized_and_accepted_or_confirmed?).to eq false } + end + + context 'accepted? returns true and confirmed? returns false' do + before :each do + allow(track).to receive(:accepted?).and_return(true) + allow(track).to receive(:confirmed?).and_return(false) + end + + it { expect(track.self_organized_and_accepted_or_confirmed?).to eq false } + end + + context 'accepted? returns false and confirmed? returns true' do + before :each do + allow(track).to receive(:accepted?).and_return(false) + allow(track).to receive(:confirmed?).and_return(true) + end + + it { expect(track.self_organized_and_accepted_or_confirmed?).to eq false } + end + end + end + end + + describe '#create_organizer_role' do + it 'creates the role of the track organizer' do + expect(Role.find_by(name: 'track_organizer', resource: self_organized_track)).to eq nil + self_organized_track.send(:create_organizer_role) + expect(Role.find_by(name: 'track_organizer', resource: self_organized_track).description).to eq 'For the organizers of the Track' + end + + it 'is executed when the track is accepted' do + expect(Role.find_by(name: 'track_organizer', resource: self_organized_track)).to eq nil + self_organized_track.accept + expect(Role.find_by(name: 'track_organizer', resource: self_organized_track).description).to eq 'For the organizers of the Track' + end + end end