diff --git a/app/assets/javascripts/components/game.js.jsx b/app/assets/javascripts/components/game.js.jsx
index 80ca117..2eafada 100644
--- a/app/assets/javascripts/components/game.js.jsx
+++ b/app/assets/javascripts/components/game.js.jsx
@@ -39,7 +39,6 @@ var Game = React.createClass({
},
handlePlayHandSubmit: function(card_ids) {
- console.log("Hello from handlePlayHandSubmit()");
$.ajax({
url: this.url() + '/play_hand',
type: 'PATCH',
@@ -55,6 +54,21 @@ var Game = React.createClass({
});
},
+ handleSeatSubmit: function(seat_id, action) {
+ $.ajax({
+ url: window.location.pathname + '/seats/' + seat_id + '/' + action,
+ type: 'PATCH',
+ dataType: 'json',
+ success: function (data, textStatus, jqXHR) {
+ // success callback
+ this.loadGameFromServer();
+ }.bind(this),
+ error: function (jqXHR, textStatus, errorThrown) {
+ // error callback
+ }
+ });
+ },
+
handleStart: function() {
$.ajax({
url: this.url() + '/start',
@@ -114,7 +128,7 @@ var Game = React.createClass({
{this.state.data.winner_player_id ? "Winner: " + this.state.data.winner_player_id : null}
diff --git a/app/assets/javascripts/components/hand_to_beat.js.jsx b/app/assets/javascripts/components/hand_to_beat.js.jsx
new file mode 100644
index 0000000..ab4c505
--- /dev/null
+++ b/app/assets/javascripts/components/hand_to_beat.js.jsx
@@ -0,0 +1,30 @@
+var HandToBeat = React.createClass({
+
+ render: function() {
+ if (this.props.cards == null || this.props.cards == "") {
+ return (
+
+
Hand to beat:
+ None
+
+ );
+ } else {
+
+ return (
+
+
Hand to beat:
+
+ {this.props.cards.map(function(card) {
+ return -
+ {/* TODO: Don't hard code face cards here */}
+ {/* {card.rank.replace('11','J').replace('12','Q').replace('13','K').replace('14','A').replace('15','2')} {card.suit} */}
+
+ ;})}
+
+
+ );
+ }
+ }
+});
diff --git a/app/assets/javascripts/components/table.js.jsx b/app/assets/javascripts/components/table.js.jsx
index 6b28004..483e797 100644
--- a/app/assets/javascripts/components/table.js.jsx
+++ b/app/assets/javascripts/components/table.js.jsx
@@ -28,19 +28,16 @@ var Table = React.createClass({
},
handleSeatSubmit: function(seat_id, action) {
- console.log("handleSeatSubmit");
$.ajax({
url: window.location.pathname + '/seats/' + seat_id + '/' + action,
type: 'PATCH',
dataType: 'json',
success: function (data, textStatus, jqXHR) {
// success callback
- console.log(data.message);
this.loadTableFromServer();
}.bind(this),
error: function (jqXHR, textStatus, errorThrown) {
// error callback
- console.log(data.message);
}
});
},
@@ -52,7 +49,6 @@ var Table = React.createClass({
return (
{this.state.data.title}
- {/* TODO: DEPRECATED seats are now rendered within games */}
);
diff --git a/app/models/game.rb b/app/models/game.rb
index 13c0568..9627ab8 100644
--- a/app/models/game.rb
+++ b/app/models/game.rb
@@ -100,8 +100,12 @@ class Game < ActiveRecord::Base
end
def has_user? user
- return true if self.players.exists?(user_id: user.id)
- return true if self.players.exists?(soft_token: user.soft_token)
+ if user.id
+ return true if self.players.exists?(user_id: user.id)
+ end
+ if user.soft_token
+ return true if self.players.exists?(soft_token: user.soft_token)
+ end
return false
end
@@ -118,11 +122,16 @@ class Game < ActiveRecord::Base
end
def joinable? user
- !self.already_has?(user) && !self.full?
+ !self.has_user?(user) && !self.full?
+ end
+
+ def player_ids_by_seat_order
+ player_ids = self.seats.order(:position).select {|s| s.player_id}.map {|s| s.player_id}
end
def next_player_id
- player_ids[(player_ids.index(controlling_player_id) + 1 ) % player_ids.length]
+ @controlling_player_index = self.player_ids_by_seat_order.index(controlling_player_id)
+ player_ids_by_seat_order[(@controlling_player_index + 1) % self.player_ids.length]
end
# The player whose turn it is
diff --git a/app/models/play.rb b/app/models/play.rb
index fa137bf..eab57c1 100644
--- a/app/models/play.rb
+++ b/app/models/play.rb
@@ -40,7 +40,7 @@ class Play < ActiveRecord::Base
# Validations
# Is there a hand_to_beat ?
- if game.try(:hand_to_beat)
+ if !game.try(:hand_to_beat).blank?
# current player is not the player to beat
if (game.player_to_beat != player)
# valid bomb?
diff --git a/app/models/seat.rb b/app/models/seat.rb
index 06b20c4..e00008f 100644
--- a/app/models/seat.rb
+++ b/app/models/seat.rb
@@ -2,6 +2,7 @@ class Seat < ActiveRecord::Base
belongs_to :table
belongs_to :game
belongs_to :user
+ belongs_to :player
def sit(current_user)
if self.occupied?
@@ -10,11 +11,12 @@ class Seat < ActiveRecord::Base
# Stand up from any other seats at this table
current_user.stand_from(self.table)
+ self.reload
+ self.player_id = self.table.current_game.add_player_from_user(current_user).id
self.user_id = current_user.id
self.user_soft_token = current_user.soft_token
self.save
- self.table.current_game.add_player_from_user(current_user)
return "Successfully sat."
end
end
@@ -23,11 +25,11 @@ class Seat < ActiveRecord::Base
unless self.occupied_by?(current_user)
return "Error. Seat not occupied by you."
else
+ self.table.current_game.remove_player_from_user(current_user)
self.user_id = nil
self.user_soft_token = nil
+ self.player_id = nil
self.save
-
- self.table.current_game.remove_player_from_user(current_user)
return "Successfully stood up."
end
end
diff --git a/app/models/user.rb b/app/models/user.rb
index 285f330..1fb78bd 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -29,7 +29,9 @@ class User < ActiveRecord::Base
@seats << table.seats.where(user_soft_token: self.soft_token)
end
@seats.flatten.each do |seat|
+ seat.player.destroy
seat.user_id = nil
+ seat.player_id = nil
seat.user_soft_token = nil
seat.save
end
diff --git a/config/routes.rb b/config/routes.rb
index 969cfff..62d517b 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -17,6 +17,12 @@ Rails.application.routes.draw do
post 'join'
patch 'play_hand'
end
+ resources :seats do
+ member do
+ patch 'sit'
+ patch 'stand'
+ end
+ end
end
mount Upmin::Engine => '/admin'
diff --git a/db/migrate/20160222131306_add_player_id_to_seat.rb b/db/migrate/20160222131306_add_player_id_to_seat.rb
new file mode 100644
index 0000000..9fc587a
--- /dev/null
+++ b/db/migrate/20160222131306_add_player_id_to_seat.rb
@@ -0,0 +1,5 @@
+class AddPlayerIdToSeat < ActiveRecord::Migration
+ def change
+ add_column :seats, :player_id, :integer
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index d77beb4..dfbe485 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: 20160219012611) do
+ActiveRecord::Schema.define(version: 20160222131306) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -79,6 +79,7 @@ ActiveRecord::Schema.define(version: 20160219012611) do
t.integer "position"
t.string "user_soft_token"
t.integer "game_id"
+ t.integer "player_id"
end
add_index "seats", ["table_id"], name: "index_seats_on_table_id", using: :btree
diff --git a/spec/features/plays_spec.rb b/spec/features/plays_spec.rb
index 846600d..070f965 100644
--- a/spec/features/plays_spec.rb
+++ b/spec/features/plays_spec.rb
@@ -8,7 +8,6 @@ feature 'Play a hand', type: :feature, js: true do
@game.reload
@player = @game.controlling_player
signin(@player.user.email,'password')
- #login_as(@player.user, scope: :user)
visit game_path @game
end
@@ -32,7 +31,7 @@ feature 'Play a hand', type: :feature, js: true do
# Play the highest card
@card_to_play = @player.inventory.last
- find("[data-card-name='#{@card_to_play.to_s}'] ~ img").click
+ find_card(@card_to_play).click
#click_button 'Play Hand'
click_button "play_hand_button_#{@player.id}"
@@ -50,11 +49,10 @@ feature 'Play a hand', type: :feature, js: true do
# Then I see the hand to beat is this play
scenario 'first play contains lowest card' do
@card_to_play = @game.lowest_card
- find("[data-card-name='#{@card_to_play.to_s}'] ~ img").click
+ find_card(@card_to_play).click
#click_button 'Play Hand'
click_button "play_hand_button_#{@player.id}"
- #expect(page).to have_content(/Hand to beat.*#{@game.lowest_card.to_s}/)
- expect(page).to have_content("Hand to beat: #{@game.lowest_card.to_s}")
+ expect(cards_to_beat).to eq @game.lowest_card.to_s
end
# Scenario: Played cards leave the players inventory
@@ -65,13 +63,15 @@ feature 'Play a hand', type: :feature, js: true do
# Store initial state of players inventory
@player_inventory = @player.inventory.join(' ')
@card_to_play = @player.inventory.first
- find("[data-card-name='#{@card_to_play.to_s}'] ~ img").click
+ find_card(@card_to_play).click
#click_button 'Play Hand'
click_button "play_hand_button_#{@player.id}"
#wait_for_ajax
+ expect(page).to have_content " "
# Played hand is rendered
- expect(page).to have_content(/Hand to beat.*#{@game.lowest_card.to_s}/)
+ expect(cards_to_beat).to eq @game.lowest_card.to_s
# Player inventory is rendered without played cards
+ #TODO: Deprecated have_content for inventory. Write new expectation.
expect(page.find("#player_controls_#{@player.id}")).to_not have_content(@card_to_play.to_s)
end
@@ -114,27 +114,19 @@ feature 'Play a hand', type: :feature, js: true do
# Then the next player becomes the current player
scenario 'active player rotates after a valid hand' do
@game.reload
+ @player = @game.controlling_player
@next_player_id = @game.next_player_id
- @controlling_player_id = @player.id
- @controlling_player_index = @game.player_ids.index(@game.controlling_player_id)
+ @controlling_player_id = @game.controlling_player.id
+ @controlling_player_index = @game.player_ids_by_seat_order.index(@game.controlling_player_id)
@card_to_play = @player.inventory.first
- find("[data-card-name='#{@card_to_play.to_s}'] ~ img").click
+ find_card(@card_to_play).click
#click_button 'Play Hand'
click_button "play_hand_button_#{@player.id}"
-
- # reload the game object instance
- @game.reload
- expect(page).to have_content(/Hand to beat.*#{@card_to_play.to_s}/)
-
- # render active player style
- #expect(page).to have_content(@game.players.find(@next_player_id).user.email + " My Turn")
-
- # next player is now the active player
- #expect(@game.controlling_player_id).to_not eq(@controlling_player_id)
- #expect(@game.controlling_player_id).to eq(@next_player_id)
+ expect(page).to have_content "Hand to beat"
+ expect(cards_to_beat).to eq @card_to_play.to_s
expect(page).to have_selector("#player-#{@next_player_id}.has-control")
- end
+ end
# Scenario: Player can not play an invalid hand on their turn
# Given I am the active player
@@ -211,7 +203,10 @@ feature 'Play a hand', type: :feature, js: true do
#click_button 'Play Hand'
click_button "play_hand_button_#{@player.id}"
- expect(page).to have_content "Hand to beat: #{@card_to_play.to_s}"
+ #TODO: Fix race condition
+ sleep 4
+ expect(page).to have_content "Hand to beat"
+ expect(cards_to_beat).to eq @card_to_play.to_s
@game.reload
expect(@game.player_to_beat).to eq @player
diff --git a/spec/features/seats/seats_sit_spec.rb b/spec/features/seats/seats_sit_spec.rb
index ceca71c..534a187 100644
--- a/spec/features/seats/seats_sit_spec.rb
+++ b/spec/features/seats/seats_sit_spec.rb
@@ -31,6 +31,10 @@ feature 'Seats', :devise, js: true do
scenario 'table seats match up with game seats' do
click_button 'Sit', match: :first
+ expect(page).to have_content "Guest"
+ expect(page.find(:css, ".game-seat.position-1")).to have_content "Guest"
+ click_button 'Sit', match: :first
+ expect(page).to have_content "Guest"
expect(page.find(:css, ".game-seat.position-2")).to have_content "Guest"
end
@@ -38,30 +42,31 @@ feature 'Seats', :devise, js: true do
scenario 'visitor changes seats' do
click_button 'Sit', match: :first
expect(page).to have_content "Guest"
- #TODO: Fix race condition
- sleep 3
+ expect(page.find(:css, ".game-seat.position-1")).to have_content "Guest"
click_button 'Sit', match: :first
expect(page).to have_content "Guest"
- expect(page.text.scan('Guest').count).to eq 2
+ expect(page.find(:css, ".game-seat.position-2")).to have_content "Guest"
+ expect(page.text.scan('Guest').count).to eq 1
end
scenario 'visitor can join the current game' do
click_button 'Sit', match: :first
- #TODO: Fix race condition. Trigger game react component reload when player joins a table
- sleep 4
+ sleep 3
expect(page).to have_content "Guest"
- expect(page.text.scan('Guest').count).to eq 2
+ expect(page.text.scan('Guest').count).to eq 1
+ end
+
+ def sit_buttons
+ find_all(:button, "Sit")
end
scenario 'visitor can leave the current game' do
click_button 'Sit', match: :first
- #TODO: Fix race condition
- sleep 4
- expect(page.text.scan('Guest').count).to eq 2
+ expect(page).to have_content 'Guest'
+ expect(page.text.scan('Guest').count).to eq 1
click_button 'Stand', match: :first
- expect(page).to have_content "Sit"
- #TODO: Fix race condition
- sleep 4
+ expect(page.find('.game-seat.position-1')).to have_content 'Sit'
+ expect(sit_buttons.length).to eq 4
expect(page.text.scan('Guest').count).to eq 0
end
@@ -85,22 +90,18 @@ feature 'Seats', :devise, js: true do
scenario 'user can join the current game' do
expect(page).to have_content "Sit"
click_button 'Sit', match: :first
- #TODO: Fix race condition. Trigger game react component reload when player joins a table
- sleep 4
expect(page).to have_content @user.email
- expect(page.text.scan(@user.email).count).to eq 2
+ expect(page.text.scan(@user.email).count).to eq 1
end
scenario 'user can leave the current game' do
expect(page).to have_content "Sit"
click_button 'Sit', match: :first
- #TODO: Fix race condition
- sleep 4
- expect(page.text.scan(@user.email).count).to eq 2
+ expect(page).to have_content @user.email
+ expect(page.text.scan(@user.email).count).to eq 1
click_button 'Stand', match: :first
expect(page).to have_content "Sit"
- #TODO: Fix race condition
- sleep 4
+ sleep 1
expect(page.text.scan(@user.email).count).to eq 0
end
diff --git a/spec/features/tables/table_join_spec.rb b/spec/features/tables/table_join_spec.rb
index d7057e6..3ec9e20 100644
--- a/spec/features/tables/table_join_spec.rb
+++ b/spec/features/tables/table_join_spec.rb
@@ -17,21 +17,16 @@ feature 'Table show', :devise do
scenario 'visitor can join the current game', js: true do
click_button 'Sit', match: :first
- #TODO: Fix race condition. Trigger game react component reload when player joins a table
- sleep 4
expect(page).to have_content "Guest"
- expect(page.text.scan('Guest').count).to eq 2
+ expect(page.text.scan('Guest').count).to eq 1
end
scenario 'visitor can leave the current game', js: true do
click_button 'Sit', match: :first
- #TODO: Fix race condition
- sleep 4
- expect(page.text.scan('Guest').count).to eq 2
+ expect(page).to have_content "Guest"
+ expect(page.text.scan('Guest').count).to eq 1
click_button 'Stand', match: :first
- expect(page).to have_content "Sit"
- #TODO: Fix race condition
- sleep 4
+ expect(page.find('.game-seat.position-1')).to have_content "Sit"
expect(page.text.scan('Guest').count).to eq 0
end
@@ -55,22 +50,17 @@ feature 'Table show', :devise do
scenario 'user can join the current game', js: true do
expect(page).to have_content "Sit"
click_button 'Sit', match: :first
- #TODO: Fix race condition. Trigger game react component reload when player joins a table
- sleep 4
expect(page).to have_content @user.email
- expect(page.text.scan(@user.email).count).to eq 2
+ expect(page.text.scan(@user.email).count).to eq 1
end
scenario 'user can leave the current game', js: true do
expect(page).to have_content "Sit"
click_button 'Sit', match: :first
- #TODO: Fix race condition
- sleep 4
- expect(page.text.scan(@user.email).count).to eq 2
+ expect(page).to have_content @user.email
+ expect(page.text.scan(@user.email).count).to eq 1
click_button 'Stand', match: :first
- expect(page).to have_content "Sit"
- #TODO: Fix race condition
- sleep 4
+ expect(page.find('.game-seat.position-1')).to have_content "Sit"
expect(page.text.scan(@user.email).count).to eq 0
end
diff --git a/spec/features/tables/table_show_spec.rb b/spec/features/tables/table_show_spec.rb
index 15bf470..6f0f6d8 100644
--- a/spec/features/tables/table_show_spec.rb
+++ b/spec/features/tables/table_show_spec.rb
@@ -35,6 +35,8 @@ feature 'Table show', :devise do
@table = FactoryGirl.create :table
@user = FactoryGirl.create :user
signin(@user.email, @user.password)
+ visit tables_path
+ click_link @table.title
end
# Scenario: User can view a table
@@ -42,18 +44,13 @@ feature 'Table show', :devise do
# When I click a table
# Then I see the table attributes
scenario 'user can see the table', js: true do
- visit tables_path
- click_link @table.title
expect(page).to have_content "Sign out"
expect(page).to have_selector "#table-#{@table.id} > h1", @table.title
expect(current_path).to eq table_path(@table)
end
scenario 'user can see the current game', js: true do
- #TODO: Fix race condition
- visit table_path @table
- #TODO: Write a better expectation
- expect(page).to have_content "Inventory"
+ expect(page).to have_content "Sit"
end
end
diff --git a/spec/features/users/user_join_game_spec.rb b/spec/features/users/user_join_game_spec.rb
index 78d8906..86af26b 100644
--- a/spec/features/users/user_join_game_spec.rb
+++ b/spec/features/users/user_join_game_spec.rb
@@ -25,10 +25,8 @@ feature 'Join a game', type: :feature, js: true do
@user = FactoryGirl.create :user
login_as(@user, scope: :user)
visit game_path @game
- click_button "Join"
- #TODO: Fix this asynchronous race condition
- visit current_path
- expect(page).to have_content @user.email
+ click_button "Sit", match: :first
+ expect(page).to have_content @user.display_name
end
# Scenario: The user is unable to join a game if they are already joined
@@ -40,12 +38,10 @@ feature 'Join a game', type: :feature, js: true do
@user = FactoryGirl.create :user
login_as(@user, scope: :user)
visit game_path @game
- click_button "Join"
- #TODO: Fix this asynchronous race condition
- visit current_path
- expect(page).to_not have_content "Join"
+ click_button "Sit", match: :first
+ expect(page).to have_content @user.display_name
+ click_button "Sit", match: :first
+ expect(page).to have_content @user.display_name
+ expect(page.text.scan(@user.display_name).count).to eq 1
end
-
-
-
end
diff --git a/spec/features/visitor_plays_spec.rb b/spec/features/visitor_plays_spec.rb
index 1bfdab4..d34c3af 100644
--- a/spec/features/visitor_plays_spec.rb
+++ b/spec/features/visitor_plays_spec.rb
@@ -6,8 +6,9 @@ feature 'Play a hand', type: :feature, js: true do
before(:each) do
@game = setup_visitor_game
visit game_path @game
- click_button "Join"
- visit game_path @game
+ click_button "Sit", match: :first
+ #TODO: Fix race condition
+ sleep 2
click_button "Start"
@game.reload
end
@@ -17,15 +18,14 @@ feature 'Play a hand', type: :feature, js: true do
end
scenario 'player can see their own 13 card faces' do
- #TODO: Remove race condition
- sleep 3
- @player = @game.players.last
- expect(page.find_all("#player-#{@player.id} #inventory img").length).to eq(13)
+ #TODO: Fix race condition
+ sleep 2
+ expect(page.find_all("#inventory img").length).to eq(13)
end
scenario 'player can see other players 13 card backs' do
#TODO: Remove race condition
- sleep 3
+ sleep 2
inventories = page.find_all(".inventory")
inventories.each do |inventory|
#TODO: This test should verify that other players card fronts are NOT seen
diff --git a/spec/features/visitors/game_join_spec.rb b/spec/features/visitors/game_join_spec.rb
index c630517..ba92cb5 100644
--- a/spec/features/visitors/game_join_spec.rb
+++ b/spec/features/visitors/game_join_spec.rb
@@ -11,8 +11,8 @@ feature 'Join game', :devise do
visit new_game_path
fill_in 'game_title', with: 'Test Game'
click_button 'Create Game'
- expect(page).to have_button("Join")
- click_button 'Join'
+ expect(page).to have_button("Sit")
+ click_button 'Sit', match: :first
#TODO: Fix this asynchronous race condition
visit current_path
@game = Game.find(current_path.split('/').last)
diff --git a/spec/features/visitors/sign_up_spec.rb b/spec/features/visitors/sign_up_spec.rb
index 3d5a825..98e3484 100644
--- a/spec/features/visitors/sign_up_spec.rb
+++ b/spec/features/visitors/sign_up_spec.rb
@@ -22,7 +22,7 @@ feature 'Sign Up', :devise do
visit new_game_path
fill_in 'game_title', with: 'Test Game'
click_button 'Create Game'
- click_button 'Join'
+ click_button 'Sit', match: :first
sign_up_with('test@example.com', 'please123', 'please123')
visit games_path
click_link 'Test Game'
diff --git a/spec/models/play_spec.rb b/spec/models/play_spec.rb
index 2ec4143..556e238 100644
--- a/spec/models/play_spec.rb
+++ b/spec/models/play_spec.rb
@@ -335,6 +335,7 @@ RSpec.describe Play, type: :model do
@play_to_beat = Play.new(game_id: @game.id, player_id: @game.next_player_id, player_cards: @cards_to_beat)
@play_to_beat.save
@game.play_to_beat_id = @play_to_beat.id
+ @play.reload
# Setup the play
@spade3 = PlayerCard.new(rank: "3", suit: "Spade", value: 1)
diff --git a/spec/support/helpers/game_helpers.rb b/spec/support/helpers/game_helpers.rb
index 1862c10..a4d4712 100644
--- a/spec/support/helpers/game_helpers.rb
+++ b/spec/support/helpers/game_helpers.rb
@@ -18,11 +18,32 @@ module Features
def setup_visitor_game
# Default game has 3 players
game = FactoryGirl.create :game
- player1 = FactoryGirl.create :player, game: game
- player2 = FactoryGirl.create :player, game: game
- player3 = FactoryGirl.create :player, game: game
+
+ u = FactoryGirl.build :user
+ game.seats[0].sit u
+
+ u = FactoryGirl.build :user
+ game.seats[1].sit u
+
+ u = FactoryGirl.build :user
+ game.seats[2].sit u
+
return game
end
+
+ def find_card card
+ find("[data-card-name='#{card.to_s}'] ~ img")
+ end
+
+ def cards_to_beat
+ @cards = find("#cards-to-beat")
+ if @cards
+ @cards = @cards.find_all('img')
+ end
+ @array = []
+ @cards.map {|c| @array << c[:alt]}
+ return @array.join " "
+ end
end
end
@@ -36,10 +57,10 @@ module Models
user2 = FactoryGirl.create :user
user3 = FactoryGirl.create :user
user4 = FactoryGirl.create :user
- game.add_player_from_user user1
- game.add_player_from_user user2
- game.add_player_from_user user3
- game.add_player_from_user user4
+ game.seats[0].sit user1
+ game.seats[1].sit user2
+ game.seats[2].sit user3
+ game.seats[3].sit user4
game.start
return game
end