diff --git a/Gemfile b/Gemfile index 75a7ee347..c45a4abec 100644 --- a/Gemfile +++ b/Gemfile @@ -13,8 +13,6 @@ gem 'bcrypt-ruby', '=3.1.0' group :development do gem 'sqlite3' - gem 'pry' - gem 'pry-nav' gem 'thin' gem 'better_errors' gem 'binding_of_caller' @@ -24,11 +22,15 @@ group :development, :test do gem 'rspec-rails', '2.99' gem 'fabrication' gem 'faker' + gem 'pry' + gem 'pry-nav' end group :test do gem 'database_cleaner', '1.2.0' gem 'shoulda-matchers' + gem 'capybara' + gem 'launchy' end group :production do diff --git a/Gemfile.lock b/Gemfile.lock index 507063341..216610a0c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -27,6 +27,7 @@ GEM minitest (~> 5.1) thread_safe (~> 0.1) tzinfo (~> 1.1) + addressable (2.3.6) arel (5.0.1.20140414130214) bcrypt-ruby (3.1.0) better_errors (1.0.1) @@ -38,6 +39,12 @@ GEM sass (~> 3.2) bootstrap_form (2.1.1) builder (3.2.2) + capybara (2.4.1) + mime-types (>= 1.16) + nokogiri (>= 1.3.3) + rack (>= 1.0.0) + rack-test (>= 0.5.4) + xpath (~> 2.0) coderay (1.1.0) coffee-rails (4.0.1) coffee-script (>= 2.2.0) @@ -69,13 +76,18 @@ GEM railties (>= 3.0, < 5.0) thor (>= 0.14, < 2.0) json (1.8.1) + launchy (2.4.2) + addressable (~> 2.3) mail (2.5.4) mime-types (~> 1.16) treetop (~> 1.4.8) method_source (0.8.2) mime-types (1.25.1) + mini_portile (0.6.0) minitest (5.3.4) multi_json (1.10.1) + nokogiri (1.6.2.1) + mini_portile (= 0.6.0) pg (0.17.0) polyglot (0.3.5) pry (0.9.12.3) @@ -157,6 +169,8 @@ GEM uglifier (2.3.1) execjs (>= 0.3.0) json (>= 1.8.0) + xpath (2.0.0) + nokogiri (~> 1.3) PLATFORMS ruby @@ -167,12 +181,14 @@ DEPENDENCIES binding_of_caller bootstrap-sass bootstrap_form + capybara coffee-rails database_cleaner (= 1.2.0) fabrication faker haml-rails jquery-rails + launchy pg pry pry-nav diff --git a/app/models/user.rb b/app/models/user.rb index 8368c580a..68a0b36ae 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -13,4 +13,8 @@ def normalize_queue_item_positions end end + def queued_video?(video) + queue_items.map(&:video).include?(video) + end + end \ No newline at end of file diff --git a/app/views/queue_items/index.html.haml b/app/views/queue_items/index.html.haml index 07f3ca775..f66abb778 100644 --- a/app/views/queue_items/index.html.haml +++ b/app/views/queue_items/index.html.haml @@ -18,7 +18,7 @@ - @queue_items.each do |queue_item| %tr = hidden_field_tag("queue_items[][id]", queue_item.id) - %td= text_field_tag("queue_items[][position]", queue_item.position) + %td= text_field_tag("queue_items[][position]", queue_item.position, data: {video_id: queue_item.video.id}) %td = link_to queue_item.video_title, queue_item.video %td diff --git a/app/views/videos/show.html.haml b/app/views/videos/show.html.haml index c7b16dc32..e4a85f7c3 100644 --- a/app/views/videos/show.html.haml +++ b/app/views/videos/show.html.haml @@ -9,7 +9,7 @@ %span Rating: 4.5/5.0 %p= @video.description .actions - = link_to "+ My Queue", queue_items_path(video_id: @video.id), method: :post, class: 'btn' + = link_to "+ My Queue", queue_items_path(video_id: @video.id), method: :post, class: 'btn' unless current_user.queued_video?(@video) %section.reviews.container .row .col-sm-10.col-sm-offset-1 diff --git a/spec/controllers/queue_items_controller_spec.rb b/spec/controllers/queue_items_controller_spec.rb index b97db6a62..70a749b32 100644 --- a/spec/controllers/queue_items_controller_spec.rb +++ b/spec/controllers/queue_items_controller_spec.rb @@ -4,48 +4,47 @@ describe "GET index" do it "sets @queue_items to the queue items of the logged in user" do alice = Fabricate(:user) - session[:user_id] = alice.id + set_current_user(alice) queue_item1 = Fabricate(:queue_item, user: alice) queue_item2 = Fabricate(:queue_item, user: alice) get :index expect(assigns(:queue_items)).to match_array([queue_item1, queue_item2]) end - it "redirects to the sign in page for unauthenticated users" do - get :index - expect(response).to redirect_to sign_in_path + it_behaves_like "requires sign in" do + let(:action) { get :index } end end describe "POST create" do it "redirects to the my queue page" do - session[:user_id] = Fabricate(:user).id + set_current_user video = Fabricate(:video) post :create, video_id: video.id expect(response).to redirect_to my_queue_path end it "creates a queue item" do - session[:user_id] = Fabricate(:user).id + set_current_user video = Fabricate(:video) post :create, video_id: video.id expect(QueueItem.count).to eq(1) end it "creates the queue item that is associated with the video" do - session[:user_id] = Fabricate(:user).id + set_current_user video = Fabricate(:video) post :create, video_id: video.id expect(QueueItem.first.video).to eq(video) end it "creates the queue item that is associated with the signed in user" do alice = Fabricate(:user) - session[:user_id] = alice.id + set_current_user(alice) video = Fabricate(:video) post :create, video_id: video.id expect(QueueItem.first.user).to eq(alice) end it "puts the video as the last one in the queue" do alice = Fabricate(:user) - session[:user_id] = alice.id + set_current_user(alice) monk = Fabricate(:video) Fabricate(:queue_item, video: monk, user: alice) south_park = Fabricate(:video) @@ -55,7 +54,7 @@ end it "does not add the video to the queue if the video is already in the queue" do alice = Fabricate(:user) - session[:user_id] = alice.id + set_current_user(alice) monk = Fabricate(:video) Fabricate(:queue_item, video: monk, user: alice) post :create, video_id: monk.id @@ -65,18 +64,22 @@ post :create, video_id: 3 expect(response).to redirect_to sign_in_path end + + it_behaves_like "requires sign in" do + let(:action) { post :create, video_id: 3 } + end end describe "DELETE destroy" do it "redirects to the my queue page" do - session[:user_id] = Fabricate(:user).id + set_current_user queue_item = Fabricate(:queue_item) delete :destroy, id: queue_item.id expect(response).to redirect_to my_queue_path end it "deletes the queue item" do alice = Fabricate(:user) - session[:user_id] = alice.id + set_current_user(alice) queue_item = Fabricate(:queue_item, user: alice) delete :destroy, id: queue_item.id expect(QueueItem.count).to eq(0) @@ -84,7 +87,7 @@ it "normalizes the remaining queue_items" do alice = Fabricate(:user) - session[:user_id] = alice.id + set_current_user(alice) queue_item1 = Fabricate(:queue_item, user: alice, position: 1) queue_item2 = Fabricate(:queue_item, user: alice, position: 2) delete :destroy, id: queue_item1.id @@ -94,26 +97,30 @@ it "does not delete the queue item if the queue item is not in the current user's queue" do alice = Fabricate(:user) bob = Fabricate(:user) - session[:user_id] = alice.id + set_current_user(alice) queue_item = Fabricate(:queue_item, user: bob) delete :destroy, id: queue_item.id expect(QueueItem.count).to eq(1) end - it "redirects to the sign in page for unauthenticated users" do - delete :destroy, id: 3 - expect(response).to redirect_to sign_in_path + + it_behaves_like "requires sign in" do + let(:action) { delete :destroy, id: 3 } end end describe "POST update_queue" do + it_behaves_like "requires sign in" do + let(:action) do + post :update_queue, queue_items: [{id: 2, position: 3}, {id: 5, position: 2}] + end + end + context "with valid inputs" do let(:alice) { Fabricate(:user) } let(:video) { Fabricate(:video) } let(:queue_item1) { Fabricate(:queue_item, user: alice, position: 1, video: video) } let(:queue_item2) { Fabricate(:queue_item, user: alice, position: 2, video: video) } - before do - session[:user_id] = alice.id - end + before { set_current_user(alice) } it "redirects to the my queue page" do post :update_queue, queue_items: [{id: queue_item1.id, position: 2}, {id: queue_item2.id, position: 1}] expect(response).to redirect_to my_queue_path @@ -135,7 +142,7 @@ let(:queue_item1) { Fabricate(:queue_item, user: alice, position: 1, video: video) } let(:queue_item2) { Fabricate(:queue_item, user: alice, position: 2, video: video) } before do - session[:user_id] = alice.id + set_current_user(alice) end it "redirects to the my queue page" do @@ -151,16 +158,11 @@ expect(queue_item1.reload.position).to eq(1) end end - context "with unauthenticated users" do - it "redirects to the sign in path" do - post :update_queue, queue_items: [{id: 2, position: 3}, {id: 5, position: 2}] - expect(response).to redirect_to sign_in_path - end - end + context "with queue items that do not belong to the current user" do it "does not change the queue items" do alice = Fabricate(:user) - session[:user_id] = alice.id + set_current_user(alice) bob = Fabricate(:user) video = Fabricate(:video) queue_item1 = Fabricate(:queue_item, user: bob, position: 1, video: video) diff --git a/spec/fabricators/category_fabricator.rb b/spec/fabricators/category_fabricator.rb index fb3ef3696..33a77acd0 100644 --- a/spec/fabricators/category_fabricator.rb +++ b/spec/fabricators/category_fabricator.rb @@ -1,3 +1,3 @@ Fabricator(:category) do - name { Faker::Lorem.words(1) } + name { Faker::Lorem.word } end \ No newline at end of file diff --git a/spec/features/user_interacts_with_queue_spec.rb b/spec/features/user_interacts_with_queue_spec.rb new file mode 100644 index 000000000..c7a980b1f --- /dev/null +++ b/spec/features/user_interacts_with_queue_spec.rb @@ -0,0 +1,61 @@ +require 'spec_helper' + +feature "User interacts with the queue" do + scenario "user adds and reorders videos in the queue" do + comedies = Fabricate(:category) + monk = Fabricate(:video, title: "Monk", category: comedies) + south_park = Fabricate(:video, title: "South Park", category: comedies) + futurama = Fabricate(:video, title: "Futurama", category: comedies) + sign_in + + add_video_to_queue(monk) + expect_video_to_be_in_queue(monk) + + visit video_path(monk) + expect_link_not_to_be_seen("+ My Queue") + + + add_video_to_queue(south_park) + add_video_to_queue(futurama) + + + set_video_position(monk, 3) + set_video_position(south_park, 1) + set_video_position(futurama, 2) + + update_queue + + expect_video_position(south_park, 1) + expect_video_position(futurama, 2) + expect_video_position(monk, 3) + + end + + def update_queue + click_button "Update Instant Queue" + end + + def expect_video_to_be_in_queue(video) + page.should have_content(video.title) + end + + def expect_link_not_to_be_seen(link_text) + page.should_not have_content(link_text) + end + + def add_video_to_queue(video) + visit home_path + find("a[href='/videos/#{video.id}']").click + click_link "+ My Queue" + end + + def set_video_position(video, position) + within(:xpath, "//tr[contains(.,'#{video.title}')]") do + fill_in "queue_items[][position]", with: position + end + end + + def expect_video_position(video, position) + expect(find(:xpath, "//tr[contains(.,'#{video.title}')]//input[@type='text']").value).to eq(position.to_s) + end +end \ No newline at end of file diff --git a/spec/features/user_sign_in_spec.rb b/spec/features/user_sign_in_spec.rb new file mode 100644 index 000000000..3e1e816da --- /dev/null +++ b/spec/features/user_sign_in_spec.rb @@ -0,0 +1,9 @@ +require 'spec_helper' + +feature "user signs in" do + scenario "with valid user email and password" do + alice = Fabricate(:user) + sign_in(alice) + page.should have_content alice.full_name + end +end \ No newline at end of file diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 33f9a5153..a44fd047d 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -6,4 +6,18 @@ it { should validate_presence_of(:full_name) } it { should validate_uniqueness_of(:email) } it { should have_many(:queue_items).order(:position)} + + describe "#queued_video?" do + it "returns true when the user queued the video" do + user = Fabricate(:user) + video = Fabricate(:video) + Fabricate(:queue_item, user: user, video: video) + user.queued_video?(video).should be true + end + it "returns false when the user has not queued the video" do + user = Fabricate(:user) + video = Fabricate(:video) + user.queued_video?(video).should be false + end + end end \ No newline at end of file diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 73d0e5638..fd6eae775 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -2,6 +2,7 @@ ENV["RAILS_ENV"] ||= 'test' require File.expand_path("../../config/environment", __FILE__) require 'rspec/rails' +require 'capybara/rails' # Requires supporting ruby files with custom matchers and macros, etc, in # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are diff --git a/spec/support/macros.rb b/spec/support/macros.rb new file mode 100644 index 000000000..b9253b64d --- /dev/null +++ b/spec/support/macros.rb @@ -0,0 +1,12 @@ +def set_current_user(user=nil) + session[:user_id] = (user || Fabricate(:user)).id +end + +def sign_in(a_user=nil) + user = a_user || Fabricate(:user) + visit sign_in_path + fill_in "Email Address", with: user.email + fill_in "Password", with: user.password + click_button "Sign In" + +end \ No newline at end of file diff --git a/spec/support/shared_examples.rb b/spec/support/shared_examples.rb new file mode 100644 index 000000000..f4c6fe751 --- /dev/null +++ b/spec/support/shared_examples.rb @@ -0,0 +1,7 @@ +shared_examples "requires sign in" do + it "redirects to the sign in page" do + session[:user_id] = nil + action + expect(response).to redirect_to sign_in_path + end +end \ No newline at end of file