From 42e35550a381fb5e3aa59c2f466e591c4b5b694b Mon Sep 17 00:00:00 2001 From: Bjarne Sievers Date: Fri, 3 Feb 2017 18:29:19 +0100 Subject: [PATCH 1/5] 312 email after request (#355) * 48 1.8 choose kind of event (#72) * Failing tests for event kind field * Add option to select event kind. Fixes #48. * Revert "48 1.8 choose kind of event" (#104) * Adding Sprint 1 features from teams: hendrik, tobi (#106) * Add test to ensure user has pupil role after registration, refs #15 * First tries to get something working * Fixed abilities for application letter. * Revert change to Gemfile. * Include Devise for Controller testing. * Start fixing tests. * Fix more tests. * Fixed failing test * Added tests for abilities * Failing tests for #20 * Basic features for #20 * Adjust tests to new role model. * Added model test for mandatory profile fields * Failing test for #46 * Extend schema for workshop organizers and knowledge level, add migrations, extend factory * Extend workshop views to show data/fields for new attributes * Fix test by removing duplicate line * Added failing test for issue #45 * Added column status for application_letter table * featured issue: #45 added status to application_params * Failing Tests for Workshop Overview Page (#23 * Prototype Implementation of Workshop Overview Page (#23) * Failing Test for Profile Informations of Workshop Overview Page (#23) * Integration of Profile Information for Workshop Overview Page (#23) * Failing tests for #24 Task 2 * Implements #24 Task 2 * Fixed forgotten merge change & updated test to work with cancan * Failing test for participation count for #23 US_3.3: Applicants overview * Implemented participation count for #23 US_3.3: Applicants overview * Simplified application_letter factory for #23 US_3.3: Applicants overview * Finished refactoring table column naming for #23 US_3.3: Applicants overview * Added missing test for participation count for #23 US_3.3: Applicants overview * Failing tests for #24 Task 1 * Implements #24 Task 1 * Failing tests for #24 Task 4 * Implements #24 Task 4 * Failing tests for#24 Task 3 * Implements #24 Task 3 * Make saved notes prettier * Merged current Dev-Branch for Pull-Request * Tweaked merge to remove unnecessary changes * Tweaked merge to remove unnecessary changes * Tweaked merge to remove unnecessary changes * Tweaked merge to remove unnecessary changes * Added and refactored localization for #23 * test for detail page overview #23 * detail page overview view is now more extensive #23 * Added Rights Managament and refactored localization for #23 * Updated Test for I18n for #23 * Removed Gemfile.lock conflict for #23 * Add Documentation for the public interface methods for #23 * refactored test for application counts #23 * removed method that required future implementations #23 * Run database migration * Insert workshop tests for #46 in respective event test files * Move workshop view changes to event views * Add additional edit view test not required in specification * Failing Test for incorrect age method, refactored feature tests for #23 * Fixed incorrect age method for #23 * Removed implementation of english translation because not currently not required by the customer for #23 * Fixed Typos + merged dev for #23 * Change labels back to English, use i18n localization instead * Add test that asserts that the new fields appear as optional * slightly improved readability of age function #23 * Refactored application counting to fit PO's further specification for #23 * Refactored application counting to fit PO's further specification for #23 * Refactored application counting to happen inside the user model for #23 * Failing tests for #24 access rights * Implements #24 access rights * Feature test for #24 and error fixes * Add documentation for #24 * Improved layout of overview and prevented override of bootstrap button color for #23 * Allow only create for application_notes for #24 * Use of Button Groups in #23 * Move activerecord localizations to separate file * Move localizations to newly created file (merged from dev) for events * Add controller test for optional attributes * Permit additional attributes to be saved * add ability tests for #24 * add routing tests for #24 * Fix migration error from previous PR * Rename tutor to coach. * 29 3.6 counter (#81) add a counter of free/occupied places for events #29 * Failing Test for #29, Task 1 and 2 * Implements #29 Task 1 and 2 * updated test for #29 to include occupied places counter * added occupied places logic #29 * Failing test for #29 * Implements #29 Task 6 * change application status to use boolean #29 * Add i18n for #29 * Failing tests for #29 Task access rights * Implement #29 Task access rights * Add feature test to #29 * Internationalize counter #29 * Add documentation for #29 * removed migration change in create_applications migration #29 * reverted changes to db/schema.rb #29 * removed unnecessary whitespace * Remove user_id from permitted application_params #29 * fix whitespace #29 * add ability tests for applicants overview/counter #29 * refactored event feature tests for counter #29 * 20 Updated seeds.rb, removed cv and email from Profile (#93) * #20 Reverted Gemfile.lock to include ruby version again * Removed cv and email from profile. Added relevant data to seeds.rb. Closes #67 and closes #63 * Removed stray comma from seeds.rb * 40 2.5 application form (additional information) (#94) * Failing Tests for #40 * Adding migration for new fields * added model validation [ci skip] * fixed tests for #40 * most features for #40 * fixed test for #40 * failing test grade has to be integer for #40 * implementation of grade has to be integer for #40 * Remove new button in application index for #40 * Changed application letter form layout for checkboxes * Failing test for textarea size * Fixing size for textareas * Failing test for help text for motivation textarea * Adding help text and title for motivation textarea * Add more failing tests workshop id for #40 * Fix failing tests automatic workshop id for #40 * Formating * Refactoring locales * Fixing test by setting locale * Make migration conditional This fixes migration issues for users coming from current dev * Fixed seed error (#101) * 15 2.3 Added error handling for CanCanCan errors and corresponding yml files for internationalization. (#84) * Added failing test for #15. * Added error handling for CanCanCan errors and corresponding yml files for internationalization. * Changed the alert messages for #15. * Changed test for #15 to use I18n translation. * Re-add blank line. * Fix schema. * Further renaming. * 45 3.11 Application letters can be marked as accepted/rejected (#90) * Failing test for #45: change application status with radio buttons * Implementation for #45: add radio buttons to change application status * Failing test for #45: add feature tests to ensure access rights * Implementation for #45: add functionality to ensure access rights * Implementation for #45: use elsif in erb file * Implementation for #45: Set default value for application statuses to pending * Implementation for #45: Add migration for default status value * Merge for #45: Update database schema * Merge for #45: Solve merge conflict * Refactoring for #45: add/use specialized application letter factories * Refactoring for #45: Realize Feedback from PR * Refactoring for #45: Remove legacy "status: true" * Implementation for #45: Add exception handling if redirect back doesn't work * Update for #45: Update factory for rejected application letters * Add role to test description #86 * Further renaming... * Revert "Adding Sprint 1 features from teams: hendrik, tobi (#106)" This reverts commit 439a475d5d7d95250e9262d9383c210eb965b7f1. * Add confirmation mail after successfully created request * Rename test to clarify intent after code change * Add email test * Refactor default reply_to mail into rails config * fix tests * fixed requested changes --- app/controllers/requests_controller.rb | 3 ++- config/locales/de.requests.yml | 4 ++++ db/schema.rb | 1 + spec/controllers/requests_controller_spec.rb | 8 ++++++++ 4 files changed, 15 insertions(+), 1 deletion(-) diff --git a/app/controllers/requests_controller.rb b/app/controllers/requests_controller.rb index 532bd630..366da9b7 100644 --- a/app/controllers/requests_controller.rb +++ b/app/controllers/requests_controller.rb @@ -25,8 +25,9 @@ def edit # POST /requests def create @request = Request.new(request_params) - if @request.save + Mailer.send_generic_email(false, @request.email, Rails.configuration.reply_to_address, I18n.t('requests.email.topic'), + I18n.t('requests.email.content')) redirect_to root_path, notice: I18n.t('requests.notice.was_created') else render :new diff --git a/config/locales/de.requests.yml b/config/locales/de.requests.yml index a53ad0b6..1490cbbf 100644 --- a/config/locales/de.requests.yml +++ b/config/locales/de.requests.yml @@ -23,6 +23,10 @@ de: was_updated: "Die Anfrage wurde aktualisiert" was_accepted: "Die Anfrage wurde angenommen" was_deleted: "Die Anfrage wurde gelöscht" + email: + topic: "Ihre Anfrage auf dem HPI Workshop-Portal" + content: "Hallo! + Ihre Anfrage ist bei uns eingegangen und wir melden uns schnellstmöglich bei Ihnen, um weitere Details abzustimmen." activerecord: models: request: diff --git a/db/schema.rb b/db/schema.rb index 52c0aff1..9df80b68 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -83,6 +83,7 @@ t.date "application_deadline" t.text "custom_application_fields" t.boolean "hidden", default: false + t.string "image" t.boolean "rejections_have_been_sent", default: false t.boolean "acceptances_have_been_sent", default: false end diff --git a/spec/controllers/requests_controller_spec.rb b/spec/controllers/requests_controller_spec.rb index 90ef6544..b856fbff 100644 --- a/spec/controllers/requests_controller_spec.rb +++ b/spec/controllers/requests_controller_spec.rb @@ -188,6 +188,10 @@ expect(assigns(:request)).to be_persisted end + it "sends an email" do + expect{ post :create, request: valid_attributes, session: valid_session }.to change{ ActionMailer::Base.deliveries.count }.by(1) + end + it "redirects to the homepage" do post :create, request: valid_attributes, session: valid_session expect(response).to redirect_to(root_path) @@ -200,6 +204,10 @@ expect(assigns(:request)).to be_a_new(Request) end + it "does not send an email" do + expect{ post :create, request: invalid_attributes, session: valid_session }.not_to change{ ActionMailer::Base.deliveries.count } + end + it "re-renders the 'new' template" do post :create, request: invalid_attributes, session: valid_session expect(response).to render_template("new") From 4223b9bcb7083aaa960a73f8956b8b4d77c8bd76 Mon Sep 17 00:00:00 2001 From: Eva Date: Sat, 4 Feb 2017 10:33:29 +0100 Subject: [PATCH 2/5] 418 3 27 event transistion selection execution small addition (#504) * Refs #418: Event transition from selection to execution * Refs: #418: Add missing migration * Fix typo in migration file name #418 * Refs #418: Cleanup for PR * Refs #418: Set application lock flags in tests * Refs #418: Remove redundant application lock flag * Refs #418: Add doc comment and model test for participant_selection_locked * Refs #418: Further cleanup for PR * Refs #418: Fix corrupt test * Rename 20170201201819_add_rejetions_have_been_sent_to_events.rb to 20170201201819_add_rejections_have_been_sent_to_events.rb * mail buttons disappear after use * new factories for selection phase --- .../events/_applicants_overview.html.erb | 24 ++++++++------ spec/factories/events.rb | 22 +++++++++++-- spec/features/events_spec.rb | 14 ++++---- spec/models/event_spec.rb | 6 +++- .../events/participants.html.erb_spec.rb | 2 +- spec/views/events/show.html.erb_spec.rb | 32 +++++++++++++------ 6 files changed, 71 insertions(+), 29 deletions(-) diff --git a/app/views/events/_applicants_overview.html.erb b/app/views/events/_applicants_overview.html.erb index efd97d9a..49336f71 100644 --- a/app/views/events/_applicants_overview.html.erb +++ b/app/views/events/_applicants_overview.html.erb @@ -64,18 +64,24 @@ <% if (can? :send_email, Email) && (@event.phase == :selection) %> -
- <%= form_tag event_email_show_path(@event), method: :get do %> - <%= hidden_field_tag "status", "acceptance" %> - <%= button_tag t('.sending_acceptances'), class: 'send-emails-button btn btn-default', disabled: @event.send_mails_tooltip.present? %> + <% if not @event.acceptances_have_been_sent %> +
+ <%= form_tag event_email_show_path(@event), method: :get do %> + <%= hidden_field_tag "status", "acceptance" %> + <%= button_tag t('.sending_acceptances'), class: 'send-emails-button btn btn-default', disabled: @event.send_mails_tooltip.present? %> + <% end %> +
<% end %> - <%= form_tag event_email_show_path(@event), method: :get do %> - <%= hidden_field_tag "status", "rejection" %> - <%= button_tag t('.sending_rejections'), class: 'send-emails-button btn btn-default', disabled: @event.send_mails_tooltip.present? %> + <% if not @event.rejections_have_been_sent %> +
+ <%= form_tag event_email_show_path(@event), method: :get do %> + <%= hidden_field_tag "status", "rejection" %> + <%= button_tag t('.sending_rejections'), class: 'send-emails-button btn btn-default', disabled: @event.send_mails_tooltip.present? %> + <% end %> +
<% end %> - -
<% end %> + <% if (can? :view_participants, Event) && (@event.phase == :execution) %> <%= link_to t('events.participants.show_participants'), event_path(@event) + "/participants" , :class => 'btn btn-primary btn-sm pull-right' %> diff --git a/spec/factories/events.rb b/spec/factories/events.rb index cb133644..fa28fca0 100644 --- a/spec/factories/events.rb +++ b/spec/factories/events.rb @@ -114,7 +114,7 @@ end end - trait :in_selection_phase do + trait :in_selection_phase_with_no_mails_sent do after(:build) do |event| event.published = true event.application_deadline = Date.yesterday @@ -123,6 +123,24 @@ end end + trait :in_selection_phase_with_acceptances_sent do + after(:build) do |event| + event.published = true + event.application_deadline = Date.yesterday + event.acceptances_have_been_sent = true + event.rejections_have_been_sent = false + end + end + + trait :in_selection_phase_with_rejections_sent do + after(:build) do |event| + event.published = true + event.application_deadline = Date.yesterday + event.acceptances_have_been_sent = false + event.rejections_have_been_sent = true + end + end + trait :in_execution_phase do after(:build) do |event| event.published = true @@ -145,7 +163,7 @@ organizer "Workshop-Organizer" knowledge_level "Workshop-Knowledge Level" application_deadline Date.current - + after(:create) do |event, evaluator| create_list(:application_letter_accepted, evaluator.accepted_application_letters_count, event: event) create_list(:application_letter_rejected, evaluator.rejected_application_letters_count, event: event) diff --git a/spec/features/events_spec.rb b/spec/features/events_spec.rb index 7cf9ea01..eb0fdb6a 100644 --- a/spec/features/events_spec.rb +++ b/spec/features/events_spec.rb @@ -49,7 +49,7 @@ end scenario "logged in as Organizer I want to be unable to send emails if there is any unclassified application left" do - @event = FactoryGirl.build(:event, :with_diverse_open_applications, :in_selection_phase) + @event = FactoryGirl.build(:event, :with_diverse_open_applications, :in_selection_phase_with_no_mails_sent) login(:organizer) @event.update!(max_participants: 1) visit event_path(@event) @@ -58,7 +58,7 @@ end scenario "logged in as Organizer I want to be unable to send emails if there is a negative number of free places left" do - @event = FactoryGirl.create(:event, :in_selection_phase) + @event = FactoryGirl.create(:event, :in_selection_phase_with_no_mails_sent) login(:organizer) @event.update!(max_participants: 1) 2.times do |n| @@ -72,7 +72,7 @@ end scenario "logged in as Organizer I want to be able to send an email to all accepted applicants" do - @event = FactoryGirl.create(:event, :in_selection_phase) + @event = FactoryGirl.create(:event, :in_selection_phase_with_no_mails_sent) login(:organizer) @event.update!(max_participants: 2) 2.times do |n| @@ -90,7 +90,7 @@ end scenario "logged in as Organizer I want to be able to send an email to all rejected applicants" do - @event = FactoryGirl.create(:event, :in_selection_phase) + @event = FactoryGirl.create(:event, :in_selection_phase_with_no_mails_sent) login(:organizer) @event.update!(max_participants: 2) 2.times do |n| @@ -125,7 +125,7 @@ scenario "logged in as Organizer I can change application status with radio buttons in selection phase" do login(:organizer) - @event = FactoryGirl.create(:event, :with_open_application, :in_selection_phase) + @event = FactoryGirl.create(:event, :with_open_application, :in_selection_phase_with_no_mails_sent) visit event_path(@event) ApplicationLetter.statuses.keys.each do |new_status| choose(I18n.t "application_status.#{new_status}") @@ -135,7 +135,7 @@ scenario "logged in as Organizer I can change application status with radio buttons without the page reloading in selection phase", js: true do login(:organizer) - @event = FactoryGirl.create(:event, :with_open_application, :in_selection_phase) + @event = FactoryGirl.create(:event, :with_open_application, :in_selection_phase_with_no_mails_sent) visit event_path(@event) find('label', text: I18n.t('application_status.accepted')).click check_values = lambda { @@ -167,7 +167,7 @@ end scenario "logged in as Organizer I can push the accept all button to accept all applicants" do - @event = FactoryGirl.create(:event, :with_diverse_open_applications, :in_selection_phase) + @event = FactoryGirl.create(:event, :with_diverse_open_applications, :in_selection_phase_with_no_mails_sent) @event.max_participants = @event.application_letters.size + 1 @event.save login(:organizer) diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index 262a63b1..3da27a9b 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -250,7 +250,11 @@ end it "is in selection phase" do - event = FactoryGirl.build(:event, :in_selection_phase) + event = FactoryGirl.build(:event, :in_selection_phase_with_no_mails_sent) + expect(event.phase).to eq(:selection) + event = FactoryGirl.build(:event, :in_selection_phase_with_acceptances_sent) + expect(event.phase).to eq(:selection) + event = FactoryGirl.build(:event, :in_selection_phase_with_rejections_sent) expect(event.phase).to eq(:selection) end diff --git a/spec/views/events/participants.html.erb_spec.rb b/spec/views/events/participants.html.erb_spec.rb index 2308fd03..d50dee64 100644 --- a/spec/views/events/participants.html.erb_spec.rb +++ b/spec/views/events/participants.html.erb_spec.rb @@ -77,7 +77,7 @@ end it "does not show the print badges button when the event is in selection phase" do - @event = assign(:event, FactoryGirl.create(:event_with_accepted_applications, :in_selection_phase)) + @event = assign(:event, FactoryGirl.create(:event_with_accepted_applications, :in_selection_phase_with_no_mails_sent)) @participants = assign(:participants, @event.participants) render expect(rendered).to_not have_link(t(:print_button_label, scope: 'events.badges')) diff --git a/spec/views/events/show.html.erb_spec.rb b/spec/views/events/show.html.erb_spec.rb index c40d61c1..6e1400af 100644 --- a/spec/views/events/show.html.erb_spec.rb +++ b/spec/views/events/show.html.erb_spec.rb @@ -98,7 +98,7 @@ end it "should not display accept-all-button for non-organizers" do - @event = assign(:event, FactoryGirl.create(:event, :in_selection_phase)) + @event = assign(:event, FactoryGirl.create(:event, :in_selection_phase_with_no_mails_sent)) @event.max_participants = @event.application_letters.size + 1 @event.save [:coach, :pupil].each do | each | @@ -109,7 +109,7 @@ end it "should display accept-all-button for organizers if there are enough free places" do - @event = assign(:event, FactoryGirl.create(:event, :with_diverse_open_applications, :in_selection_phase)) + @event = assign(:event, FactoryGirl.create(:event, :with_diverse_open_applications, :in_selection_phase_with_no_mails_sent)) @event.max_participants = @event.application_letters.size + 1 @event.save sign_in(FactoryGirl.create(:user, role: :organizer)) @@ -118,7 +118,7 @@ end it "should not display accept-all-button if there are not enough free places" do - @event = assign(:event, FactoryGirl.create(:event, :with_diverse_open_applications, :in_selection_phase)) + @event = assign(:event, FactoryGirl.create(:event, :with_diverse_open_applications, :in_selection_phase_with_no_mails_sent)) sign_in(FactoryGirl.create(:user, role: :organizer)) @event.max_participants = 1 render @@ -165,7 +165,7 @@ end end end - + it "displays correct buttons in draft phase" do @event = assign(:event, FactoryGirl.create(:event, :in_draft_phase)) sign_in(FactoryGirl.create(:user, role: :organizer)) @@ -201,7 +201,7 @@ end it "displays correct buttons in selection phase" do - @event = assign(:event, FactoryGirl.create(:event, :in_selection_phase)) + @event = assign(:event, FactoryGirl.create(:event, :in_selection_phase_with_no_mails_sent)) sign_in(FactoryGirl.create(:user, role: :organizer)) render expect(rendered).to have_link(t(:print_all, scope: 'events.applicants_overview')) @@ -211,8 +211,22 @@ expect(rendered).to_not have_link(t(:show_participants, scope: 'events.participants')) end + it "does not display send acceptances button after acceptances have been sent in selection phase" do + @event = assign(:event, FactoryGirl.create(:event, :in_selection_phase_with_acceptances_sent)) + sign_in(FactoryGirl.create(:user, role: :organizer)) + render + expect(rendered).to_not have_button(t(:sending_acceptances, scope: 'events.applicants_overview')) + end + + it "does not display send acceptances button after acceptances have been sent in selection phase" do + @event = assign(:event, FactoryGirl.create(:event, :in_selection_phase_with_rejections_sent)) + sign_in(FactoryGirl.create(:user, role: :organizer)) + render + expect(rendered).to_not have_button(t(:sending_rejections, scope: 'events.applicants_overview')) + end + it "displays the disabled send email buttons in selection phase (when there are unclassified applications)" do - @event = assign(:event, FactoryGirl.create(:event, :with_diverse_open_applications, :in_selection_phase)) + @event = assign(:event, FactoryGirl.create(:event, :with_diverse_open_applications, :in_selection_phase_with_no_mails_sent)) sign_in(FactoryGirl.create(:user, role: :organizer)) render expect(rendered).to have_button(t(:sending_acceptances, scope: 'events.applicants_overview'), disabled: true) @@ -220,7 +234,7 @@ end it "displays the disabled send email buttons in selection phase (when there are too many accepted applications)" do - @event = assign(:event, FactoryGirl.create(:event_with_accepted_applications, :in_selection_phase, max_participants: 1)) + @event = assign(:event, FactoryGirl.create(:event_with_accepted_applications, :in_selection_phase_with_no_mails_sent, max_participants: 1)) sign_in(FactoryGirl.create(:user, role: :organizer)) render expect(rendered).to have_button(t(:sending_acceptances, scope: 'events.applicants_overview'), disabled: true) @@ -247,8 +261,8 @@ expect(rendered).to have_link(t('events.participants.show_participants')) end - it "should not display particiants button when email were not already sent as organizer" do - @event = assign(:event, FactoryGirl.create(:event, :in_selection_phase)) + it "should not display particiants button when emails have not been sent as organizer" do + @event = assign(:event, FactoryGirl.create(:event, :in_selection_phase_with_no_mails_sent)) sign_in(FactoryGirl.create(:user, role: :organizer)) render expect(rendered).not_to have_link(t('events.participants.show_participants')) From fa73d5e3566e7c76551acf4b57718e4ac4999353 Mon Sep 17 00:00:00 2001 From: Tom Beckmann Date: Sat, 4 Feb 2017 14:24:05 +0100 Subject: [PATCH 3/5] Force one event in the sample data to be in the past. Remove fadeout. (#494) --- app/assets/stylesheets/events.css | 5 +---- app/views/events/_event.html.erb | 2 +- db/sample_data.rb | 5 ++++- db/sample_data/events.rb | 9 +++++---- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/app/assets/stylesheets/events.css b/app/assets/stylesheets/events.css index 7298c069..aa6de49a 100644 --- a/app/assets/stylesheets/events.css +++ b/app/assets/stylesheets/events.css @@ -48,9 +48,6 @@ margin: 15px 0; padding-bottom: 15px; } -.event-preview.event-past { - opacity: 0.7; -} .front-section:nth-child(even) .event-preview { border-bottom: 1px solid rgba(255, 255, 255, 0.3); } @@ -108,4 +105,4 @@ width: 100%; height: 150px; margin-bottom: 15px; -} \ No newline at end of file +} diff --git a/app/views/events/_event.html.erb b/app/views/events/_event.html.erb index 7e0ba54d..e1eda7dc 100644 --- a/app/views/events/_event.html.erb +++ b/app/views/events/_event.html.erb @@ -1,4 +1,4 @@ -
+
<%= image_tag "example.png", class: "img-rounded img-responsive img-float-corner-tr center-block" %>
<%= render "events/event_date_large", event: event %> diff --git a/db/sample_data.rb b/db/sample_data.rb index c5e2f2e4..902dc364 100644 --- a/db/sample_data.rb +++ b/db/sample_data.rb @@ -10,12 +10,15 @@ def add_sample_data events[:programmierkurs] = event_programmierkurs events[:mintcamp] = event_mintcamp - events[:bechersaeuberungsevent] = event_bechersaeuberungsevent events[:gongakrobatik] = event_gongakrobatik events[:batterie_akustik] = event_batterie_akustik events[:bachlorpodium] = event_bachlorpodium events[:past_deadline_event] = event_gongakrobatik + # past events are not valid by definition, however we + # would like to pretend to have some old ones + event_bechersaeuberungsevent.save!(validate: false) + users = Hash.new users[:pupil] = user_pupil users[:teacher] = user_teacher diff --git a/db/sample_data/events.rb b/db/sample_data/events.rb index efc32f7a..07119cff 100644 --- a/db/sample_data/events.rb +++ b/db/sample_data/events.rb @@ -40,10 +40,11 @@ def event_mintcamp end def event_bechersaeuberungsevent - date_range_singleday = DateRange.create!( - start_date: Date.new(2017, 04, 04), - end_date: Date.new(2017, 04, 05) + date_range_singleday = DateRange.new( + start_date: Date.yesterday, + end_date: Date.yesterday ) + date_range_singleday.save!(validate: false) Event.new( name: 'Bechersäuberungsevent', description: 'Es dreht sich den ganzen Tag um das Säubern von Bechern. Wie säubert man einen Becher am effizientesten oder am schnellsten? Wie immer bieten wir eine Reihe an Expertenvorträgen an. Dieses Mal erfahrt ihr unter anderem wie ihr Edding-Markierungen selbst nach einer Spülmaschinen-Reinigung noch entfernen könnt oder wie man die richtige Größe für Becher-Stapel herausfindet und anwendet.', @@ -51,7 +52,7 @@ def event_bechersaeuberungsevent organizer: 'FSR', knowledge_level: 'Anfänger', date_ranges: [date_range_singleday], - application_deadline: Date.tomorrow, + application_deadline: Date.yesterday.prev_day(2), published: true, hidden: false, custom_application_fields: ['Lieblings-Becherart', 'Kannst du eine eigene Spülmaschine mitbringen?'] From af512a05569e5e9e09afb75921b24590e3c2b3d2 Mon Sep 17 00:00:00 2001 From: Corinna Jaschek Date: Sat, 4 Feb 2017 14:50:04 +0100 Subject: [PATCH 4/5] fixes #320 picture upload (#501) * started work on image uploader (again) * added /public/uploads to gitignore * Fix checking sizes * added imagemagick to Vagrantfile * added -y to Vagrant file * remove unnecessary code path --- .gitignore | 3 + Gemfile | 4 + Gemfile.lock | 10 ++ Vagrantfile | 3 +- app/controllers/events_controller.rb | 3 +- app/models/event.rb | 9 ++ app/uploaders/event_image_uploader.rb | 43 +++++++ app/views/events/_event.html.erb | 2 +- app/views/events/_form.html.erb | 107 ++++++++++-------- config/locales/de.events.yml | 5 + .../20170125201341_add_image_to_events.rb | 5 + spec/features/event_spec.rb | 25 ++++ spec/testfiles/image_upload_test.png | Bin 0 -> 4849 bytes spec/testfiles/too_small_image.png | Bin 0 -> 2827 bytes 14 files changed, 168 insertions(+), 51 deletions(-) create mode 100644 app/uploaders/event_image_uploader.rb create mode 100644 db/migrate/20170125201341_add_image_to_events.rb create mode 100644 spec/testfiles/image_upload_test.png create mode 100644 spec/testfiles/too_small_image.png diff --git a/.gitignore b/.gitignore index 9c78fbf9..e61e2744 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,6 @@ config/database.yml # Ignore application configuration /config/application.yml + +# Ignore image uplaods +/public/uploads diff --git a/Gemfile b/Gemfile index f37c413e..f64b1b56 100644 --- a/Gemfile +++ b/Gemfile @@ -108,6 +108,10 @@ gem 'will_paginate-bootstrap' # Markdown renderer gem 'redcarpet' +# Image Upload and Processing +gem 'carrierwave' +gem 'mini_magick' + # Ical generator gem 'icalendar' diff --git a/Gemfile.lock b/Gemfile.lock index 507c39c7..51449fb0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -73,6 +73,10 @@ GEM rack (>= 1.0.0) rack-test (>= 0.5.4) xpath (~> 2.0) + carrierwave (1.0.0) + activemodel (>= 4.0.0) + activesupport (>= 4.0.0) + mime-types (>= 1.16) cliver (0.3.2) codeclimate-test-reporter (1.0.3) simplecov @@ -149,6 +153,7 @@ GEM mime-types (3.1) mime-types-data (~> 3.2015) mime-types-data (3.2016.0521) + mini_magick (4.6.0) mini_portile2 (2.1.0) minitest (5.10.1) nokogiri (1.6.8.1) @@ -318,6 +323,7 @@ DEPENDENCIES byebug cancancan capybara (~> 2.5) + carrierwave codeclimate-test-reporter (~> 1.0.0) combine_pdf coveralls @@ -335,6 +341,7 @@ DEPENDENCIES jquery-turbolinks jquery-ui-rails less-rails + mini_magick parser (~> 2.2.2.5) pdf-inspector pg @@ -361,5 +368,8 @@ DEPENDENCIES web-console (~> 2.0) will_paginate-bootstrap +RUBY VERSION + ruby 2.2.2p95 + BUNDLED WITH 1.13.7 diff --git a/Vagrantfile b/Vagrantfile index a10d0831..7849879b 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -26,5 +26,6 @@ Vagrant.configure("2") do |config| config.vm.provision "shell", inline: <<-SHELL apt-get update apt-get install -y phantomjs + apt-get install -y imagemagick SHELL -end \ No newline at end of file +end diff --git a/app/controllers/events_controller.rb b/app/controllers/events_controller.rb index 1a98d5bc..5763a6fe 100644 --- a/app/controllers/events_controller.rb +++ b/app/controllers/events_controller.rb @@ -3,6 +3,7 @@ require 'pdf_generation/participants_pdf' require 'rubygems' require 'zip' +require 'carrierwave' class EventsController < ApplicationController @@ -252,7 +253,7 @@ def set_event end def event_params - params.require(:event).permit(:name, :description, :max_participants, :organizer, :knowledge_level, :application_deadline, :published, :hidden, :custom_application_fields => [], date_ranges_attributes: [:start_date, :end_date, :id]) + params.require(:event).permit(:name, :description, :image, :max_participants, :organizer, :knowledge_level, :application_deadline, :published, :hidden, :custom_application_fields => [], date_ranges_attributes: [:start_date, :end_date, :id]) end def add_event_query_conditions(query) diff --git a/app/models/event.rb b/app/models/event.rb index e71a441c..2be91be8 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -21,6 +21,8 @@ class Event < ActiveRecord::Base serialize :custom_application_fields, Array + mount_uploader :image, EventImageUploader + has_many :application_letters has_many :agreement_letters has_many :participant_groups @@ -34,6 +36,13 @@ class Event < ActiveRecord::Base validates :hidden, exclusion: { in: [nil] } validates :published, inclusion: { in: [true, false] } validates :published, exclusion: { in: [nil] } + validate :check_image_dimensions + + # Use the image dimensions as returned from our uploader + # to verify that the image has sufficient size + def check_image_dimensions + errors.add(:image, I18n.t("events.errors.image_too_small")) if image.upload_width.present? && image.upload_height.present? && (image.upload_width < 200 || image.upload_height < 155) + end # Returns all participants for this event in following order: diff --git a/app/uploaders/event_image_uploader.rb b/app/uploaders/event_image_uploader.rb new file mode 100644 index 00000000..1383f8a1 --- /dev/null +++ b/app/uploaders/event_image_uploader.rb @@ -0,0 +1,43 @@ +class EventImageUploader < CarrierWave::Uploader::Base + attr_reader :upload_width, :upload_height + + # image processing + include CarrierWave::MiniMagick + + storage :file + + # Return the directory that images will be uploaded to + # @return [String] the path relative to the `public` folder + def store_dir + "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}" + end + + before :cache, :capture_size_before_cache + + # Fill in the upload sizes once the image has been uploaded, so that + # our model can use them for validation + def capture_size_before_cache(new_file) + # Only do this once, to the original version + if version_name.blank? + @upload_width, @upload_height = `identify -format "%wx %h" #{new_file.path}`.split(/x/).map { |dim| dim.to_i } + end + end + + version :list_view do + process resize_to_fill: [200, 155] + end + + version :detail_view do + process resize_to_fill: [1140, 200] + end + + version :thumb do + process resize_to_fill: [50, 50] + end + + # white list of extensions which are allowed to be uploaded + def extension_whitelist + %w(jpg jpeg gif png) + end + +end diff --git a/app/views/events/_event.html.erb b/app/views/events/_event.html.erb index e1eda7dc..7a1272d0 100644 --- a/app/views/events/_event.html.erb +++ b/app/views/events/_event.html.erb @@ -1,5 +1,5 @@
- <%= image_tag "example.png", class: "img-rounded img-responsive img-float-corner-tr center-block" %> + <%= image_tag event.image_url(:list_view), class: "img-rounded img-responsive img-float-corner-tr center-block" if event.image_url%>
<%= render "events/event_date_large", event: event %>
diff --git a/app/views/events/_form.html.erb b/app/views/events/_form.html.erb index 2125b27d..2677eec4 100644 --- a/app/views/events/_form.html.erb +++ b/app/views/events/_form.html.erb @@ -51,63 +51,73 @@
- <%= f.label :max_participants, :class => 'control-label col-lg-2' %> + <%= f.label :image, :class => 'control-label col-lg-2' %> + <%= image_tag(@event.image_url) if @event.image? %>
- <%= f.text_field :max_participants, :class => 'form-control' %> - <%=f.error_span(:max_participants) %> + <%= f.error_span(:image) %> + <%= I18n.t :"events.image_upload.upload_local_file" %>
+ <%= f.file_field :image %>
-
- <%= f.label :date_ranges, :class => 'control-label col-lg-2' %> -
- <% def date_picker_template(start_date = Date.current, end_date = Date.current) - start_picker = text_field_tag "event[date_ranges_attributes][][start_date]", (I18n.l start_date), - class: 'form-control', :"data-enable-datepicker" => 'true' - end_picker = text_field_tag "event[date_ranges_attributes][][end_date]", (I18n.l end_date), - class: 'form-control', :"data-enable-datepicker" => 'true' - "
#{start_picker} #{t 'date_range.to'} #{end_picker} "+ - "×
" - end %> +
+ <%= f.label :max_participants, :class => 'control-label col-lg-2' %> +
+ <%= f.text_field :max_participants, :class => 'form-control' %> + <%= f.error_span(:max_participants) %> +
+
+
+ <%= f.label :date_ranges, :class => 'control-label col-lg-2' %> +
+ <% def date_picker_template(start_date = Date.current, end_date = Date.current) + start_picker = text_field_tag "event[date_ranges_attributes][][start_date]", (I18n.l start_date), + class: 'form-control', :"data-enable-datepicker" => 'true' + end_picker = text_field_tag "event[date_ranges_attributes][][end_date]", (I18n.l end_date), + class: 'form-control', :"data-enable-datepicker" => 'true' + + "
#{start_picker} #{t 'date_range.to'} #{end_picker} "+ + "×
" + end %> - <%= - if @event.date_ranges.blank? - date_picker_template.html_safe - else - @event.date_ranges - .map { |d| date_picker_template d.start_date, d.end_date } - .reduce(:+) - .html_safe - end - %> - - <%= t 'events.form.add_timespan' %> - - <%=f.error_span(:date_ranges) %> + <%= + if @event.date_ranges.blank? + date_picker_template.html_safe + else + @event.date_ranges + .map { |d| date_picker_template d.start_date, d.end_date } + .reduce(:+) + .html_safe + end + %> + + <%= t 'events.form.add_timespan' %> + + <%=f.error_span(:date_ranges) %> +
+
- -
-
- <%= f.label :organizer, :class => 'control-label col-lg-2' %> -
- <%= f.text_field :organizer, :class => 'form-control', :placeholder => 'optional' %> - <%=f.error_span(:organizer) %> +
+ <%= f.label :organizer, :class => 'control-label col-lg-2' %> +
+ <%= f.text_field :organizer, :class => 'form-control', :placeholder => 'optional' %> + <%=f.error_span(:organizer) %> +
-
-
- <%= f.label :knowledge_level, :class => 'control-label col-lg-2' %> -
- <%= f.text_field :knowledge_level, :class => 'form-control', :placeholder => 'optional' %> - <%=f.error_span(:knowledge_level) %> +
+ <%= f.label :knowledge_level, :class => 'control-label col-lg-2' %> +
+ <%= f.text_field :knowledge_level, :class => 'form-control', :placeholder => 'optional' %> + <%=f.error_span(:knowledge_level) %> +
-
-
- <%= f.label :application_deadline, :class => 'control-label col-lg-2' %> -
- <%= f.text_field :application_deadline, :"data-enable-datepicker" => 'true', value: ((I18n.l @event.application_deadline) if @event.application_deadline.present?), class: "form-control" %> - <%=f.error_span(:application_deadline) %> +
+ <%= f.label :application_deadline, :class => 'control-label col-lg-2' %> +
+ <%= f.text_field :application_deadline, :"data-enable-datepicker" => 'true', value: ((I18n.l @event.application_deadline) if @event.application_deadline.present?), class: "form-control" %> + <%=f.error_span(:application_deadline) %> +
-
<%= (render 'custom_application_fields_input', form: f) if @event.new_record? %>
@@ -123,6 +133,7 @@ :data => { :confirm => t('.confirm', :default => t("events.confirmation_prompts.confirm_delete", :default => 'Are you sure?')) }, :class => 'btn btn-danger' %> <% end %> +
diff --git a/config/locales/de.events.yml b/config/locales/de.events.yml index fbfe2b2d..55e65f38 100644 --- a/config/locales/de.events.yml +++ b/config/locales/de.events.yml @@ -138,6 +138,10 @@ de: page: "Seite" errors: application_deadline_before_start_of_event: "Bewerbungsschluss muss vor Beginn der Veranstaltung liegen" + image_too_small: "Das ausgewählte Bild muss mindestens 200 x 155 Pixel groß sein" + + image_upload: + upload_local_file: "Lokale Datei hochladen" activerecord: models: @@ -148,6 +152,7 @@ de: event: name: "Name" description: "Beschreibung" + image: "Bild" max_participants: "Maximale Teilnehmerzahl" kind: "Art" active: "Aktiv" diff --git a/db/migrate/20170125201341_add_image_to_events.rb b/db/migrate/20170125201341_add_image_to_events.rb new file mode 100644 index 00000000..818ecb51 --- /dev/null +++ b/db/migrate/20170125201341_add_image_to_events.rb @@ -0,0 +1,5 @@ +class AddImageToEvents < ActiveRecord::Migration + def change + add_column :events, :image, :string + end +end diff --git a/spec/features/event_spec.rb b/spec/features/event_spec.rb index bd2e5cb3..09a1ea02 100644 --- a/spec/features/event_spec.rb +++ b/spec/features/event_spec.rb @@ -241,6 +241,31 @@ visit edit_event_path(event) expect(page).to_not have_text(I18n.t "events.form.add_field") end + + it "should allow uploading images" do + visit new_event_path + + fill_in "Maximale Teilnehmerzahl", :with => 25 + fill_in "event[date_ranges_attributes][][start_date]", :with => I18n.l(Date.tomorrow.next_day(2)) + fill_in "event[date_ranges_attributes][][end_date]", :with => I18n.l(Date.tomorrow.next_day(3)) + fill_in "event_application_deadline", :with => I18n.l(Date.tomorrow) + + attach_file "Bild", File.join(Rails.root + 'spec/testfiles/image_upload_test.png') + click_button I18n.t(".events.form.create") + end + + it "should not allow uploading images that are too small" do + visit new_event_path + + fill_in "Maximale Teilnehmerzahl", :with => 25 + fill_in "event[date_ranges_attributes][][start_date]", :with => I18n.l(Date.tomorrow.next_day(2)) + fill_in "event[date_ranges_attributes][][end_date]", :with => I18n.l(Date.tomorrow.next_day(3)) + fill_in "event_application_deadline", :with => I18n.l(Date.tomorrow) + + attach_file "Bild", File.join(Rails.root + 'spec/testfiles/too_small_image.png') + click_button I18n.t(".events.form.create") + expect(page).to have_text(I18n.t "events.errors.image_too_small") + end end describe "show page" do diff --git a/spec/testfiles/image_upload_test.png b/spec/testfiles/image_upload_test.png new file mode 100644 index 0000000000000000000000000000000000000000..cd2f7e49b99c12e3158ab4594777a074f1776cd2 GIT binary patch literal 4849 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

|{*MU4OqN_zAb>!DCsl zqW%}>zNZIk2IjssP!4~8`{S$$(}gFt;uhZv#p#B!6;Ye)BIS78xzuqfkHNRgBf^Zc zepTI1s`*f>9}bJlE_l<^zJUGZpTy^=8F9+J=~|bJsOhbZLle2z0VB|Z;^(>5zBR9~ z0dCq8IIu1M{yjstR|m2hfeSPCA(i`@E-s44L?QK;{f56hJv|SP>nh@A{9ha*DLk;j z&2Ki08+~@I-vnOu&#$y-)d{AgVCdBtL1F!j;D?X_s@dP0kF9~joV+reu|)LAPR>O# zUhB=-5us@|!Lplf-)lQ-u0>-E*`M|1YKSYkooo(qN*@nNVRS>&;WYKnJ!E)OB}(gG zHD{V<%=&RYP-HHwGbEZb=GB=#ieBX;W=dmOww$m5&|@Me8liL@SzE99==bz#&YP+S zs$Rcp0}zhb0LHKu-Ku0QAjeJnGa6{N+qDtTNo>zwKT@}~6oxkrGBEvNwKbz~68A}N z6mju(j8jm?pVkE%lTq+LrmdIX>W#FH$0l?Lz43QyUtX>qy4ZXRu#XENh94}{BF2kWvX3~eY#zPPf2Gi4$WB^ zcgS=L?;sq`tnHtR?n+TpQ(s;LMzz|hUyeRD3}qbO*}Q*#hz;m$AXsdivAX4K(nu2& zumPigt1;Cfu2Q2W`gM#EFJ?FDfA&l=*NBu|Mq@Z7Pn?7# zQ1S+hC+*u$vrZ6$1{*tB8ki?ed&<7t3JB)MV1l%-s`;)`cnLBPqq;f_a#b~`OLIvD z04eX>;kii$N)VbEl+gwRhsNKCiv$4slH8U#AVIK>UU07}AlSBF)|2)ojK|6rWXl%> hll+hE|1aco^4=ZTC@Hze`)ZX1&{TV>TCQvv@?QgC?-2k1 literal 0 HcmV?d00001 diff --git a/spec/testfiles/too_small_image.png b/spec/testfiles/too_small_image.png new file mode 100644 index 0000000000000000000000000000000000000000..dba9cfaffd231676f05bdfc775494768482321d9 GIT binary patch literal 2827 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 literal 0 HcmV?d00001 From 8d9c84c1234179bb44c7f0dad39eb632b9aa4e4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20L=C3=B6ser?= Date: Sat, 4 Feb 2017 18:21:01 +0100 Subject: [PATCH 5/5] 242 3.25 application canceled state refactoring (#508) * event model method for phase #233 * added tests for event model phase function * Add Test for event model function after_deadline? #233 * Revert Whitespace change #233 * Create canceled+pre_accepted state plus refactor part 1 #242 * Make only designated radio buttons selectable - Refactoring part 2 #242 * Fix tests for new states - Refactoring Part 3 #242 * Make counter test more sensable - Refactoring Part 4 #242 * Rename Email Template statuses #242 * Outsource selectable statuses to class method #242 * Convert pre_accepted into accepted by sending mail #242 * Fix feature test for pre_accepted -> accepted #242 * added some more application status changing tests #242 * move accepting all pre_accepted participants on acceptance mail from controller to model #242 * 242 go back from pre_accepted * fix some things to get rid of pre_accepted #242 * remove unneccessary var in test #242 * remove duplicated tests #242 * move 2 expect few lines higher #242 * use factory for no status_status_notification_sent #242 * some requested changes #242 * #242 add missing locales * #242 remove code duplication in test * update schema.rb #242 --- app/controllers/emails_controller.rb | 14 +++-- app/models/application_letter.rb | 43 ++++++++++++-- app/models/event.rb | 19 +++++-- app/models/user.rb | 9 +-- .../_application_selective.html.erb | 2 +- .../events/_applicants_overview.html.erb | 1 - config/locales/de.application_letters.yml | 4 ++ ...otification_sent_to_application_letters.rb | 5 ++ db/schema.rb | 13 +++-- spec/controllers/email_controller_spec.rb | 6 +- spec/factories/application_letters.rb | 5 ++ spec/factories/events.rb | 42 ++++++++++++++ spec/features/events_spec.rb | 34 +++++++++-- spec/models/application_letter_spec.rb | 56 ++++++++++++++++--- spec/models/email_template_spec.rb | 10 ++-- spec/models/event_spec.rb | 51 +++++++++-------- .../index.html.erb_spec.rb | 5 ++ .../application_letters/show.html.erb_spec.rb | 12 ++++ spec/views/events/show.html.erb_spec.rb | 14 ++++- 19 files changed, 274 insertions(+), 71 deletions(-) create mode 100644 db/migrate/20170203180638_add_status_notification_sent_to_application_letters.rb diff --git a/app/controllers/emails_controller.rb b/app/controllers/emails_controller.rb index 0dfa8ef4..2a91e49d 100644 --- a/app/controllers/emails_controller.rb +++ b/app/controllers/emails_controller.rb @@ -6,7 +6,7 @@ def show @templates = EmailTemplate.with_status(get_email_template_status) application_letter_status = get_corresponding_application_letter_status - @addresses = @event.email_addresses_of_type(application_letter_status) + @addresses = @event.email_addresses_of_type_without_notification_sent(application_letter_status) @email = Email.new(hide_recipients: true, reply_to: Rails.configuration.reply_to_address, recipients: @addresses.join(','), subject: '', content: '') @@ -29,24 +29,26 @@ def submit def send_email @email = Email.new(email_params) @event = Event.find(params[:event_id]) - + status = get_email_template_status if @email.valid? - if get_corresponding_application_letter_status == :accepted + application_letter_status = get_corresponding_application_letter_status + if application_letter_status == :accepted @email.send_email_with_ical @event else @email.send_email end - if get_email_template_status == :acceptance + @event.set_status_notification_flag_for_applications_with_status(application_letter_status) + if status == :acceptance @event.acceptances_have_been_sent = true - elsif get_email_template_status == :rejection + elsif status == :rejection @event.rejections_have_been_sent = true end @event.save redirect_to @event, notice: t('.sending_successful') else - @templates = EmailTemplate.with_status(get_email_template_status) + @templates = EmailTemplate.with_status(status) flash.now[:alert] = t('.sending_failed') render :email diff --git a/app/models/application_letter.rb b/app/models/application_letter.rb index 425a777c..e186df9d 100644 --- a/app/models/application_letter.rb +++ b/app/models/application_letter.rb @@ -25,13 +25,22 @@ class ApplicationLetter < ActiveRecord::Base validates_inclusion_of :grade, in: (VALID_GRADES.to_a.push(0)) validates :vegetarian, :vegan, :allergic, inclusion: { in: [true, false] } validates :vegetarian, :vegan, :allergic, exclusion: { in: [nil] } - validate :deadline_cannot_be_in_the_past, :if => Proc.new { |letter| !(letter.status_changed?) } - validate :status_cannot_be_changed, :if => Proc.new { |letter| letter.status_changed?} + validate :deadline_cannot_be_in_the_past, :if => Proc.new { |letter| !(letter.status_changed? || letter.status_notification_sent_changed?) } + validate :status_notification_sent_cannot_be_changed, :if => Proc.new { |letter| letter.status_notification_sent_changed? } + validate :status_cannot_be_changed, :if => Proc.new { |letter| letter.status_changed? } - enum status: {accepted: 1, rejected: 0, pending: 2, alternative: 3} + enum status: {accepted: 1, rejected: 0, pending: 2, alternative: 3, canceled: 4} validates :status, inclusion: { in: statuses.keys } + # Returns an array of selectable statuses + # + # @param none + # @return [Array ] array of selectable statuses + def self.selectable_statuses + ["accepted","rejected","pending","alternative"] + end + # Checks if the deadline is over # additionally only return if event and event.application_deadline is present # @@ -46,7 +55,21 @@ def after_deadline? # @param none # @return [Boolean] true if status changes are allowed def status_change_allowed? - !event.participant_selection_locked + if event.phase == :execution + (status_was == 'accepted' && status == 'canceled') || (status_was == 'alternative' && status == 'accepted') + elsif event.phase == :selection && event.participant_selection_locked + false + else + true + end + end + + # Checks if it is allowed to set the status_notification_sent flag + # + # @param none + # @return [Boolean] true if it can be changed + def status_notification_sent_change_allowed? + event.phase == :selection || event.phase == :execution end # Validator for after_deadline? @@ -85,6 +108,8 @@ def status_type else return I18n.t("application_status.pending_before_deadline") end + when ApplicationLetter.statuses[:canceled] + return I18n.t("application_status.canceled") else return I18n.t("application_status.alternative") end @@ -94,7 +119,15 @@ def status_type # Adds error def status_cannot_be_changed unless status_change_allowed? - errors.add(:event, "Die Bewerbungen wurden bereits bearbeitet, eine Statusänderung ist nicht mehr erlaubt.") + errors.add(:event, I18n.t('application_letters.validation.status_cannot_be_changed')) + end + end + + # Validator for status_change_allowed? + # Adds error + def status_notification_sent_cannot_be_changed + unless status_notification_sent_change_allowed? + errors.add(:event, I18n.t('application_letters.validation.status_notification_sent_cannot_be_changed')) end end diff --git a/app/models/event.rb b/app/models/event.rb index 2be91be8..3873a9f9 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -154,17 +154,26 @@ def send_mails_tooltip # @param none # @return none def accept_all_application_letters - application_letters.each do |application_letter| - application_letter.update(status: :accepted) + application_letters.map { |application| application.update(status: :accepted) } + end + + # Sets the status_notification_sent flag for all application letters of the given type + # + # @param status [Type] the desired application status the flag should be set for + # @return none + def set_status_notification_flag_for_applications_with_status(status) + applications = application_letters.select {|application| application.status == status.to_s} + applications.each do |application_letter| + application_letter.update(status_notification_sent: true) end end # Returns an array of strings of all email addresses of applications with a given status type # # @param type [Type] the status type of the email addresses that will be returned - # @return [Array] Array of all email addresses of applications with given type - def email_addresses_of_type(type) - applications = application_letters.where(status: ApplicationLetter.statuses[type]) + # @return [Array] Array of all email addresses of applications with given type, that don't have status_notification_sent set + def email_addresses_of_type_without_notification_sent(type) + applications = application_letters.where(status: ApplicationLetter.statuses[type], status_notification_sent: false) applications.collect { |a| a.user.email } end diff --git a/app/models/user.rb b/app/models/user.rb index 49fe92c2..a6d7ed3e 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -119,15 +119,16 @@ def older_than_required_age_at_start_date_of_event?(given_event, age) # @param [Event] current event (which application status will be excluded) # @return [Int] of number of currently accepted applications def accepted_applications_count(event) - ApplicationLetter.where(user_id: id, status: true).where.not(event: event).count() + ApplicationLetter.where(user_id: id, status: ApplicationLetter.statuses[:accepted]) + .where.not(event: event).count() end - # Returns the number of accepted applications from the user without counting status of current event application + # Returns the number of rejected applications from the user without counting status of current event application # # @param current event (which application status will be excluded) - # @return [Int] of number of currently accepted applications + # @return [Int] of number of currently rejected applications def rejected_applications_count(event) - ApplicationLetter.where(user_id: id, status: false).where.not(event: event).count() + ApplicationLetter.where(user_id: id, status: ApplicationLetter.statuses[:rejected]).where.not(event: event).count() end # Searches all users with last/first_name containing pattern diff --git a/app/views/application_letters/_application_selective.html.erb b/app/views/application_letters/_application_selective.html.erb index c4d97515..be8e15e8 100644 --- a/app/views/application_letters/_application_selective.html.erb +++ b/app/views/application_letters/_application_selective.html.erb @@ -2,7 +2,7 @@ <%= form_for :application_letter, url: update_application_letter_status_path(application_letter), html: {method: :put} do |f| %>

- <% ApplicationLetter.statuses.keys.each do |key| %> + <% ApplicationLetter.selectable_statuses.each do |key| %>
diff --git a/config/locales/de.application_letters.yml b/config/locales/de.application_letters.yml index b27d5acd..bab7ff6c 100644 --- a/config/locales/de.application_letters.yml +++ b/config/locales/de.application_letters.yml @@ -40,6 +40,9 @@ de: submit: create: "Anmelden" update: "Anmeldung aktualisieren" + validation: + status_cannot_be_changed: "Die Bewerbungen wurden bereits bearbeitet, eine Statusänderung ist nicht mehr erlaubt." + status_notification_sent_cannot_be_changed: "Das Status-Benachrichtungsflag kann noch nicht gesetzt werden" fields_filled_in: "Wir haben einige Felder bereits für dich ausgefüllt. Falls sich seit deiner letzten Bewerbung etwas geändert hat, kannst du es jetzt anpassen." @@ -74,6 +77,7 @@ de: pending: "Ausstehend" pending_before_deadline: "Beworben" pending_after_deadline: "In Bearbeitung" + canceled: "Abgesagt" alternative: "Nachrücker" diff --git a/db/migrate/20170203180638_add_status_notification_sent_to_application_letters.rb b/db/migrate/20170203180638_add_status_notification_sent_to_application_letters.rb new file mode 100644 index 00000000..3eb46f1e --- /dev/null +++ b/db/migrate/20170203180638_add_status_notification_sent_to_application_letters.rb @@ -0,0 +1,5 @@ +class AddStatusNotificationSentToApplicationLetters < ActiveRecord::Migration + def change + add_column :application_letters, :status_notification_sent, :boolean, default: false, null: false + end +end diff --git a/db/schema.rb b/db/schema.rb index 9df80b68..43294350 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170201201932) do +ActiveRecord::Schema.define(version: 20170203180638) do create_table "agreement_letters", force: :cascade do |t| t.integer "user_id", null: false @@ -26,11 +26,11 @@ create_table "application_letters", force: :cascade do |t| t.string "motivation" - t.integer "user_id", null: false - t.integer "event_id", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.integer "status", default: 2, null: false + t.integer "user_id", null: false + t.integer "event_id", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.integer "status", default: 2, null: false t.integer "grade" t.string "coding_skills" t.string "emergency_number" @@ -40,6 +40,7 @@ t.text "custom_application_fields" t.text "annotation" t.string "organisation" + t.boolean "status_notification_sent", default: false, null: false end add_index "application_letters", ["event_id"], name: "index_application_letters_on_event_id" diff --git a/spec/controllers/email_controller_spec.rb b/spec/controllers/email_controller_spec.rb index db2d23e9..25d6b4aa 100644 --- a/spec/controllers/email_controller_spec.rb +++ b/spec/controllers/email_controller_spec.rb @@ -25,7 +25,7 @@ expect(assigns(:email).recipients).to eq(@application.user.email) end - it "sets @template with template for accepted emails" do + it "sets @template with template for acceptance emails" do get :show, event_id: @event.id, status: :acceptance expect(assigns(:templates)).to eq([@template]) end @@ -43,7 +43,7 @@ expect(assigns(:email).recipients).to eq(@application.user.email) end - it "sets @template with template for rejected emails" do + it "sets @template with template for rejection emails" do get :show, event_id: @event.id, status: :rejection expect(assigns(:templates)).to eq([@template]) end @@ -145,4 +145,4 @@ end end end -end \ No newline at end of file +end diff --git a/spec/factories/application_letters.rb b/spec/factories/application_letters.rb index c7c6fb31..16c5f624 100644 --- a/spec/factories/application_letters.rb +++ b/spec/factories/application_letters.rb @@ -24,6 +24,7 @@ event annotation "Some" custom_application_fields ["Value 1", "Value 2", "Value 3"] + status_notification_sent false end factory :application_letter2, parent: :application_letter do @@ -53,6 +54,10 @@ status :alternative end + factory :application_letter_canceled, parent: :application_letter do + status :canceled + end + factory :accepted_application_with_agreement_letters, parent: :application_letter do status :accepted after(:build) do |application_letter| diff --git a/spec/factories/events.rb b/spec/factories/events.rb index fa28fca0..20815764 100644 --- a/spec/factories/events.rb +++ b/spec/factories/events.rb @@ -123,6 +123,15 @@ end end + trait :in_selection_phase_with_participants_locked do + after(:build) do |event| + event.published = true + event.application_deadline = Date.yesterday + event.acceptances_have_been_sent = true + event.rejections_have_been_sent = false + end + end + trait :in_selection_phase_with_acceptances_sent do after(:build) do |event| event.published = true @@ -150,6 +159,15 @@ end end + trait :with_no_status_notification_sent_yet do + after(:create) do |event| + event.application_letters.each do |application| + application.status_notification_sent = false + application.save! if application.changed? + end + end + end + factory :event_with_accepted_applications do name "Event-Name" description "Event-Description" @@ -174,7 +192,31 @@ create_list(:accepted_application_with_agreement_letters, evaluator.accepted_application_letters_count, event: event) end end + end + factory :event_with_applications_in_various_states do + name "Event-Name" + description "Event-Description" + max_participants 20 + date_ranges { build_list :date_range, 1 } + transient do + accepted_application_letters_count 5 + rejected_application_letters_count 5 + alternative_application_letters_count 2 + canceled_application_letters_count 1 + pending_application_letters_count 0 + end + organizer "Workshop-Organizer" + knowledge_level "Workshop-Knowledge Level" + application_deadline Date.current + + after(:create) do |event, evaluator| + create_list(:application_letter_accepted, evaluator.accepted_application_letters_count, event: event) + create_list(:application_letter_rejected, evaluator.rejected_application_letters_count, event: event) + create_list(:application_letter_alternative, evaluator.alternative_application_letters_count, event: event) + create_list(:application_letter_canceled, evaluator.canceled_application_letters_count, event: event) + create_list(:application_letter_pending, evaluator.pending_application_letters_count, event: event) + end end end end diff --git a/spec/features/events_spec.rb b/spec/features/events_spec.rb index eb0fdb6a..7eaeefe5 100644 --- a/spec/features/events_spec.rb +++ b/spec/features/events_spec.rb @@ -71,7 +71,8 @@ expect(page).to have_css('button[disabled]', text: I18n.t('events.applicants_overview.sending_rejections')) end - scenario "logged in as Organizer I want to be able to send an email to all accepted applicants" do + + scenario "logged in as Organizer I want to be able to send an email to all accepted applicants in selection phase" do @event = FactoryGirl.create(:event, :in_selection_phase_with_no_mails_sent) login(:organizer) @event.update!(max_participants: 2) @@ -89,7 +90,7 @@ expect{click_button I18n.t('emails.email_form.send')}.to change{ActionMailer::Base.deliveries.count}.by(1) end - scenario "logged in as Organizer I want to be able to send an email to all rejected applicants" do + scenario "logged in as Organizer I want to be able to send an email to all rejected applicants in selection phase" do @event = FactoryGirl.create(:event, :in_selection_phase_with_no_mails_sent) login(:organizer) @event.update!(max_participants: 2) @@ -127,7 +128,7 @@ login(:organizer) @event = FactoryGirl.create(:event, :with_open_application, :in_selection_phase_with_no_mails_sent) visit event_path(@event) - ApplicationLetter.statuses.keys.each do |new_status| + ApplicationLetter.selectable_statuses.each do |new_status| choose(I18n.t "application_status.#{new_status}") expect(ApplicationLetter.where(id: @event.application_letters.first.id)).to exist end @@ -166,6 +167,27 @@ end end + scenario "logged in as Organizer I can send acceptance and then rejection emails and by that change the status notification flag" do + login(:organizer) + event = FactoryGirl.create(:event_with_accepted_applications, :in_selection_phase_with_no_mails_sent, :with_no_status_notification_sent_yet) + + %w[accepted rejected].each do |status| + applications = event.application_letters.select { | application_letter | application_letter.status == status } + expect(applications.size).to be > 0 + visit event_path(event) + send_email_button_label = (status == 'accepted') ? 'sending_acceptances' : 'sending_rejections' + click_button(I18n.t("events.applicants_overview.#{send_email_button_label}")) + expect(page).to have_current_path(event_email_show_path(event_id: event.id), only_path: true) + fill_in :email_subject, with: "Subject" + fill_in :email_content, with: "Content" + click_button I18n.t('.emails.email_form.send') + applications.each do |letter| + letter.reload + expect(letter.status_notification_sent).to be true + end + end + end + scenario "logged in as Organizer I can push the accept all button to accept all applicants" do @event = FactoryGirl.create(:event, :with_diverse_open_applications, :in_selection_phase_with_no_mails_sent) @event.max_participants = @event.application_letters.size + 1 @@ -275,7 +297,7 @@ scenario "logged in as Organizer I can filter displayed application letters by their status and simultaneously sort them", js: true do login(:organizer) - @event = FactoryGirl.create(:event_with_accepted_applications) + @event = FactoryGirl.create(:event_with_applications_in_various_states) @event.application_letters.each do |letter| letter.user.profile = FactoryGirl.build(:profile, user: letter.user) end @@ -302,11 +324,13 @@ .map {|letter| letter.user.profile.name } expect(page).to contain_ordered(sorted_accepted_names) - # list rejected, pending + # list all others click_button I18n.t 'events.applicants_overview.filter_by' uncheck I18n.t 'application_status.accepted' check I18n.t 'application_status.rejected' check I18n.t 'application_status.pending' + check I18n.t 'application_status.alternative' + check I18n.t 'application_status.canceled' click_button I18n.t 'events.applicants_overview.filter' expect(page).to have_every_text(not_accepted_names) diff --git a/spec/models/application_letter_spec.rb b/spec/models/application_letter_spec.rb index b306b5e4..6b08fa6c 100644 --- a/spec/models/application_letter_spec.rb +++ b/spec/models/application_letter_spec.rb @@ -70,17 +70,59 @@ end it "can not be updated after event application deadline" do - application = FactoryGirl.build(:application_letter_deadline_over) - expect(application).to_not be_valid + application = FactoryGirl.build(:application_letter) + %i[in_selection_phase_with_no_mails_sent in_execution_phase].each do | phase | + application.event = FactoryGirl.create(:event, phase) + expect(application).to_not be_valid + end end - it "can not be updated if status is changed and participant selection is locked" do + %i[accepted canceled alternative pending].each do | new_status | + it "cannot update the status in execution phase from rejected into #{new_status}" do + application = FactoryGirl.create(:application_letter_rejected) + application.event = FactoryGirl.create(:event, :in_execution_phase) + application.status = new_status + expect(application).to_not be_valid + end + end + + it "can be canceled (only) if it was accepted before in execution phase" do + application = FactoryGirl.create(:application_letter_accepted) + application.event = FactoryGirl.create(:event, :in_execution_phase) + %i[accepted alternative pending rejected].each do | new_status | + application.status = new_status + expect(application).to_not be_valid + end + application.status = :canceled + expect(application).to be_valid + end + + it "can be promoted to accepted if it was alternative before in execution phase" do + application = FactoryGirl.create(:application_letter_alternative) + application.event = FactoryGirl.create(:event, :in_execution_phase) + %i[alternative canceled pending rejected].each do | new_status | + application.status = new_status + expect(application).to_not be_valid + end + application.status = :accepted + expect(application).to be_valid + end + + it "can update the status in selection phase" do application = FactoryGirl.build(:application_letter) - application.event.acceptances_have_been_sent = true - application.event.rejections_have_been_sent = true - expect(application.event.participant_selection_locked).to be(true) + application.event = FactoryGirl.create(:event, :in_selection_phase_with_no_mails_sent) application.status = :rejected - expect(application).to_not be_valid + expect(application).to be_valid + end + + it "can not be updated if status is changed and participant selection is locked" do + application = FactoryGirl.build(:application_letter) + %i[in_selection_phase_with_participants_locked in_execution_phase].each do |phase| + application.event = FactoryGirl.create(:event, phase) + expect(application.event.participant_selection_locked).to be(true) + application.status = :rejected + expect(application).to_not be_valid + end end it "can be updated if status is changed and participant selection is not locked" do diff --git a/spec/models/email_template_spec.rb b/spec/models/email_template_spec.rb index eaf98e2f..9247e177 100644 --- a/spec/models/email_template_spec.rb +++ b/spec/models/email_template_spec.rb @@ -34,9 +34,9 @@ end it "returns correct templates by status" do - @accepted_template = FactoryGirl.create(:email_template, :acceptance) - @rejected_template = FactoryGirl.create(:email_template, :rejection) - expect(EmailTemplate.with_status(:acceptance)).to eq([@accepted_template]) - expect(EmailTemplate.with_status(:rejection)).to eq([@rejected_template]) + accepted_template = FactoryGirl.create(:email_template, :acceptance) + rejected_template = FactoryGirl.create(:email_template, :rejection) + expect(EmailTemplate.with_status(:acceptance)).to eq([accepted_template]) + expect(EmailTemplate.with_status(:rejection)).to eq([rejected_template]) end -end \ No newline at end of file +end diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index 3da27a9b..ec2f560f 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -85,14 +85,18 @@ end it "computes the email addresses of the accepted and the rejected applications" do - event = FactoryGirl.create(:event) + event = FactoryGirl.create(:event, :in_selection_phase_with_no_mails_sent) accepted_application_letter_1 = FactoryGirl.create(:application_letter_accepted, :event => event, :user => FactoryGirl.create(:user)) accepted_application_letter_2 = FactoryGirl.create(:application_letter_accepted, :event => event, :user => FactoryGirl.create(:user)) accepted_application_letter_3 = FactoryGirl.create(:application_letter_accepted, :event => event, :user => FactoryGirl.create(:user)) - rejected_application_letter = FactoryGirl.create(:application_letter_rejected, :event => event, :user => FactoryGirl.create(:user)) - [accepted_application_letter_1, accepted_application_letter_2, accepted_application_letter_3, rejected_application_letter].each { |letter| event.application_letters.push(letter) } - expect(event.email_addresses_of_type(:accepted)).to contain_exactly(accepted_application_letter_1.user.email, accepted_application_letter_2.user.email, accepted_application_letter_3.user.email) - expect(event.email_addresses_of_type(:rejected)).to contain_exactly(rejected_application_letter.user.email) + accepted_application_letter_4 = FactoryGirl.create(:application_letter_accepted, status_notification_sent: true, event: event, user: FactoryGirl.create(:user)) + rejected_application_letter_1 = FactoryGirl.create(:application_letter_rejected, :event => event, :user => FactoryGirl.create(:user)) + rejected_application_letter_2 = FactoryGirl.create(:application_letter_rejected, status_notification_sent: true, :event => event, :user => FactoryGirl.create(:user)) + + [accepted_application_letter_1, accepted_application_letter_2, accepted_application_letter_3, accepted_application_letter_4, rejected_application_letter_1, rejected_application_letter_2].each { |letter| event.application_letters.push(letter) } + + expect(event.email_addresses_of_type_without_notification_sent(:accepted)).to contain_exactly(accepted_application_letter_1.user.email, accepted_application_letter_2.user.email, accepted_application_letter_3.user.email) + expect(event.email_addresses_of_type_without_notification_sent(:rejected)).to contain_exactly(rejected_application_letter_1.user.email) end it "is either a public or private" do @@ -174,9 +178,9 @@ it "computes the number of occupied places" do event = FactoryGirl.create(:event) application_letter = FactoryGirl.create(:application_letter, user: FactoryGirl.create(:user), event: event) - application_letter_accepted = FactoryGirl.create(:application_letter_accepted, user: FactoryGirl.create(:user), event: event) + FactoryGirl.create(:application_letter_accepted, user: FactoryGirl.create(:user), event: event) expect(event.compute_occupied_places).to eq(1) - application_letter_accepted_2 = FactoryGirl.create(:application_letter_accepted, user: FactoryGirl.create(:user), event: event) + FactoryGirl.create(:application_letter_accepted, user: FactoryGirl.create(:user), event: event) expect(event.compute_occupied_places).to eq(2) end @@ -190,20 +194,6 @@ expect(Event.future).to include(event_future) end - describe "returns applicants email list" do - before :each do - @event = FactoryGirl.create(:event) - end - - it "returns email address only of the given type" do - @accepted_application = FactoryGirl.create(:application_letter_accepted, event: @event, user: FactoryGirl.create(:user)) - @rejected_application = FactoryGirl.create(:application_letter_rejected, event: @event, user: FactoryGirl.create(:user)) - expect(@event.email_addresses_of_type(:accepted)).to contain_exactly(@accepted_application.user.email) - expect(@event.email_addresses_of_type(:rejected)).to contain_exactly(@rejected_application.user.email) - end - - end - it "generates an application letter list ordered by first name" do @event = FactoryGirl.create(:event) @user1 = FactoryGirl.create(:user, email:'a@b.com') @@ -233,12 +223,29 @@ end it "accepts all its application letters" do - event = FactoryGirl.create :event, :with_diverse_open_applications + event = FactoryGirl.create :event, :with_diverse_open_applications, :in_selection_phase_with_no_mails_sent event.accept_all_application_letters application_letters = ApplicationLetter.where(event: event.id) expect(application_letters.all? { |application_letter| application_letter.status == 'accepted' }).to eq(true) end + %i[accepted rejected].each do |status| + it "sets the status notification flag for all #{status} applications" do + event = FactoryGirl.create :event_with_accepted_applications, :in_selection_phase_with_no_mails_sent + application_letters = event.application_letters.select{ |application_letter| application_letter.status == status.to_s } + application_letters.each do |application| + application.status_notification_sent = false + application.save! if application.changed? + end + event.set_status_notification_flag_for_applications_with_status(status) + expect(application_letters.count).to be > 0 + application_letters.each do |application| + application.reload + expect(application.status_notification_sent).to be true + end + end + end + it "is in draft phase" do event = FactoryGirl.build(:event, :in_draft_phase) expect(event.phase).to eq(:draft) diff --git a/spec/views/application_letters/index.html.erb_spec.rb b/spec/views/application_letters/index.html.erb_spec.rb index 76d8db29..bb0f57af 100644 --- a/spec/views/application_letters/index.html.erb_spec.rb +++ b/spec/views/application_letters/index.html.erb_spec.rb @@ -29,6 +29,11 @@ render expect(rendered).to have_content(I18n.t("application_status.alternative")) end + it "checks if page displays canceled status application letter" do + @application_letters = [FactoryGirl.create(:application_letter_canceled)] + render + expect(rendered).to have_content(I18n.t("application_status.canceled")) + end end it "should display the name of the event" do diff --git a/spec/views/application_letters/show.html.erb_spec.rb b/spec/views/application_letters/show.html.erb_spec.rb index d02f6eb6..851de593 100644 --- a/spec/views/application_letters/show.html.erb_spec.rb +++ b/spec/views/application_letters/show.html.erb_spec.rb @@ -6,17 +6,29 @@ @application_letter = assign(:application_letter, FactoryGirl.create(:application_letter)) @application_note = assign(:application_note, FactoryGirl.create(:application_note, application_letter: @application_letter)) @application_letter.user.profile = FactoryGirl.build(:profile) + profile = FactoryGirl.create(:profile, user: (FactoryGirl.create :user, role: :organizer)) + sign_in profile.user + end + it "renders radio buttons for accept reject pending and alternative, but not canceled in selection phase" do + @application_letter.event = FactoryGirl.create(:event, :in_selection_phase_with_no_mails_sent) render + expect(rendered).to have_css("label", text: I18n.t('application_status.accepted')) + expect(rendered).to have_css("label", text: I18n.t('application_status.rejected')) + expect(rendered).to have_css("label", text: I18n.t('application_status.pending')) + expect(rendered).to have_css("label", text: I18n.t('application_status.alternative')) + expect(rendered).to_not have_css("label", text: I18n.t('application_status.canceled')) end it "renders application's attributes" do + render expect(rendered).to have_text(@application_letter.event.name) expect(rendered).to have_text(@application_letter.motivation) expect(rendered).to have_text(@application_letter.annotation) end it "renders applicant's attributes" do + render expect(rendered).to have_text(@application_letter.user.profile.name) expect(rendered).to have_text(@application_letter.user.profile.gender) expect(rendered).to have_text(@application_letter.user.profile.age_at_time(@application_letter.event.start_date)) diff --git a/spec/views/events/show.html.erb_spec.rb b/spec/views/events/show.html.erb_spec.rb index 6e1400af..f761f03d 100644 --- a/spec/views/events/show.html.erb_spec.rb +++ b/spec/views/events/show.html.erb_spec.rb @@ -76,6 +76,18 @@ expect(rendered).to have_css("td", :text => @application_letter.user.profile.age_at_time(@event.start_date)) end + it "logged in as organizer it renders radio buttons for accept reject pending and alternative, but not canceled in selection phase" do + sign_in(FactoryGirl.create(:user, role: :organizer)) + @event = assign(:event, FactoryGirl.create(:event, :with_diverse_open_applications, :in_selection_phase_with_no_mails_sent)) + @application_letters = @event.application_letters #TODO I couldnt find a assign(:application_letters), still this gives the view access to it. + render + expect(rendered).to have_css("label", text: I18n.t('application_status.accepted')) + expect(rendered).to have_css("label", text: I18n.t('application_status.rejected')) + expect(rendered).to have_css("label", text: I18n.t('application_status.pending')) + expect(rendered).to have_css("label", text: I18n.t('application_status.alternative')) + expect(rendered).to_not have_css("label", text: I18n.t('application_status.canceled')) + end + it "displays application details button" do render expect(rendered).to have_link(t(:details, scope: 'events.applicants_overview')) @@ -144,7 +156,7 @@ end end - it "displays a button to view the application if application deadline if over for an event where the pupil has applied" do + it "displays a button to view the application if application deadline is over for an event where the pupil has applied" do pupil = FactoryGirl.create(:user, role: :pupil) application_letter = FactoryGirl.create(:application_letter, user: pupil, event: @event) @event.application_deadline = Date.yesterday