diff --git a/app/models/vote.rb b/app/models/vote.rb index 64ab74de..cdd1e4cb 100644 --- a/app/models/vote.rb +++ b/app/models/vote.rb @@ -2,7 +2,7 @@ class Vote < ApplicationRecord belongs_to :user - belongs_to :event + belongs_to :event, touch: true validates :user_id, uniqueness: { scope: :event_id } diff --git a/app/views/admin/events/_datatable_row.haml b/app/views/admin/events/_datatable_row.haml index 7de3f3f1..17f7b264 100644 --- a/app/views/admin/events/_datatable_row.haml +++ b/app/views/admin/events/_datatable_row.haml @@ -1,4 +1,4 @@ -- cache ['admin', conference_id, program, event] do +- cache ['admin', conference_id, program, event, current_user] do %tr{ id: "event-#{event.id}" } %td = event.id diff --git a/app/views/admin/events/_proposal.html.haml b/app/views/admin/events/_proposal.html.haml index 12f0efb6..17ed991d 100644 --- a/app/views/admin/events/_proposal.html.haml +++ b/app/views/admin/events/_proposal.html.haml @@ -1,4 +1,4 @@ -- cache ['admin/event', @conference, @program, @event] do +- cache ['admin/event', @conference, @program, @event, current_user] do .row .col-md-12 %h3 diff --git a/spec/factories/users.rb b/spec/factories/users.rb index 24affba4..b21dd88b 100644 --- a/spec/factories/users.rb +++ b/spec/factories/users.rb @@ -50,6 +50,16 @@ FactoryBot.define do end end + factory :cfp_user do + transient do + resource { create(:resource) } + end + + after :create do |user, evaluator| + user.add_role :cfp, evaluator.resource + end + end + trait :disabled do is_disabled { true } end diff --git a/spec/features/voting_spec.rb b/spec/features/voting_spec.rb new file mode 100644 index 00000000..1758de0b --- /dev/null +++ b/spec/features/voting_spec.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +require 'spec_helper' + +def have_rating(rating, max) + have_selector('.rating.bright', count: rating) + .and have_selector('.rating:not(.bright)', count: max - rating) +end + +def cast_vote(rating, before:, after:) + # Index shows existing rating but not user’s vote + visit admin_conference_program_events_path(conference.short_title) + within("#event-#{event.id}") do + expect(page).to have_rating(before, 5) + expect(page).to have_text('Not rated') + end + + # Event page shows existing rating but not user’s vote + click_on event.title + within('tr', text: 'Rating') { expect(page).to have_rating(before, 5) } + within('tr', text: 'Your vote') { expect(page).to have_rating(0, 5) } + + # Voting dynamically updates the page + within('tr', text: 'Your vote') { page.find(".rating:nth-of-type(#{rating})").click } + within('tr', text: 'Rating') { expect(page).to have_rating(after, 5) } + within('tr', text: 'Your vote') { expect(page).to have_rating(rating, 5) } + + # Index shows updated rating and vote + visit admin_conference_program_events_path(conference.short_title) + within("#event-#{event.id}") do + expect(page).to have_rating(after, 5) + expect(page).to have_text("You voted: #{rating}/5") + end + + # Re-rendered event page shows updated rating and vote + click_on event.title + within('tr', text: 'Rating') { expect(page).to have_rating(after, 5) } + within('tr', text: 'Your vote') { expect(page).to have_rating(rating, 5) } +end + +feature 'Voting' do + let(:conference) { create(:conference) } + let!(:event) { create(:event, program: conference.program) } + let(:voter1) { create(:cfp_user, resource: conference) } + let(:voter2) { create(:cfp_user, resource: conference) } + + before :each do + conference.program.update_attribute :rating, 5 + end + + scenario 'multiple users casting votes', feature: true, js: true do + sign_in voter1 + cast_vote 3, before: 0, after: 3 + sign_out + + sign_in voter2 + cast_vote 5, before: 3, after: 4 + end +end