From 31111e07ebe097ea424819bd7608cb0565b8f728 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Radan=20Skori=C4=87?= Date: Mon, 26 Aug 2024 10:16:27 +0200 Subject: [PATCH] Implement replaying the same game For logged in users (still shadow shipped), when a game is finished they can chose to replay the same board from the start. --- app/controllers/games_controller.rb | 14 ++++++ app/models/game.rb | 5 +++ app/views/games/_game.html.erb | 4 ++ config/routes.rb | 4 ++ spec/models/game_spec.rb | 14 ++++++ spec/requests/games_controller_spec.rb | 36 +++++++++++++++ spec/system/play_private_game_spec.rb | 62 ++++++++++++++++++++------ 7 files changed, 125 insertions(+), 14 deletions(-) create mode 100644 spec/requests/games_controller_spec.rb diff --git a/app/controllers/games_controller.rb b/app/controllers/games_controller.rb index e93f58e..388218c 100644 --- a/app/controllers/games_controller.rb +++ b/app/controllers/games_controller.rb @@ -20,6 +20,10 @@ def my def show @game = Game.find(params[:id]) + if @game.private? + rodauth.require_account + head :not_found unless @game.owner == current_account + end end def new @@ -48,6 +52,16 @@ def update end end + def replay + rodauth.require_account + game = Game.find(params[:id]) + if game.finished? + redirect_to game.replay_for(current_account) + else + head :forbidden + end + end + private def game_params diff --git a/app/models/game.rb b/app/models/game.rb index 649b315..ec89ae9 100644 --- a/app/models/game.rb +++ b/app/models/game.rb @@ -63,4 +63,9 @@ def click!(x:, y:, mark_as_mine: false) update!(status: new_game_object.status) end end + + # @param owner [Account] the account to replay the game for as a new private game. + def replay_for(owner) + Game.create!(board: board, owner:) + end end diff --git a/app/views/games/_game.html.erb b/app/views/games/_game.html.erb index 8d316ed..ba3319f 100644 --- a/app/views/games/_game.html.erb +++ b/app/views/games/_game.html.erb @@ -39,3 +39,7 @@ <% end %> <% end %> + +<% if game.finished? && rodauth.logged_in? %> + <%= link_to "Replay This Game", replay_game_path(game), class: "btn bg-gray-300 hover:bg-gray-400", data: {turbo_method: :post, turbo_frame: "_top"} %> +<% end %> diff --git a/config/routes.rb b/config/routes.rb index 141b24a..2018b1f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -6,6 +6,10 @@ collection do get :my end + + member do + post :replay + end end # Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500. diff --git a/spec/models/game_spec.rb b/spec/models/game_spec.rb index 76b7e89..26dde80 100644 --- a/spec/models/game_spec.rb +++ b/spec/models/game_spec.rb @@ -169,4 +169,18 @@ expect(result).to be_a(Minesweeper::Game) end end + + describe "#replay_for" do + before { game } # make sure it's created + + it "creates a game" do + expect { game.replay_for(accounts(:freddie)) }.to change(Game, :count).by(1) + end + + it "keeps the same board but sets the new owner" do + new_game = game.replay_for(accounts(:freddie)) + expect(new_game.board).to eq game.board + expect(new_game.owner).to eq accounts(:freddie) + end + end end diff --git a/spec/requests/games_controller_spec.rb b/spec/requests/games_controller_spec.rb new file mode 100644 index 0000000..cb030f8 --- /dev/null +++ b/spec/requests/games_controller_spec.rb @@ -0,0 +1,36 @@ +require 'rails_helper' + +RSpec.describe "Games Controller" do + fixtures :accounts + + let(:account) { accounts(:freddie) } + let(:public_game) { Game.create!(board:) } + let(:private_game) { Game.create!(board:, owner: account) } + let(:board) do + Board.create!( + width: 10, + height: 10, + mines: [Mine.new(x: 2, y: 2)], + ) + end + + describe "show" do + it "doesn't allow viewing other's private game" do + post "/login", params: { email: accounts(:brian).email, password: "password"} + + get "/games/#{private_game.id}" + + expect(response).to be_not_found + end + end + + describe '#replay' do + it "doesn't allow replaying an unfinished game" do + post "/login", params: { email: account.email, password: "password" } + + post "/games/#{public_game.id}/replay" + + expect(response).to be_forbidden + end + end +end diff --git a/spec/system/play_private_game_spec.rb b/spec/system/play_private_game_spec.rb index a6b169f..ca5c34f 100644 --- a/spec/system/play_private_game_spec.rb +++ b/spec/system/play_private_game_spec.rb @@ -59,7 +59,25 @@ def lose_the_game!(game) expect(page).to have_no_content('Reloading to new game in') end - context "when logged in with existing private games" do + it "allows to replay a communal game privately, after it's finished" do + Game.create!(board: Board.create!( width: 10, height: 10, mines: [Mine.new(x: 2, y: 2)])) + visit "/login" + login_with(email: accounts(:freddie).email, password: "password") + + visit "/" + expect(page).not_to have_content("Replay this game") + + click_cell(5, 5) + expect(page).to have_content("Humanity won!") + expect(page).to have_content("Refresh Now") + + click_on "Replay This Game" + expect(page).not_to have_content("Refresh Now") + click_cell(5, 5) + expect(page).to have_content("You won!") + end + + context "with an existing private games" do let!(:private_game) do Game.create!( board: Board.create!( @@ -72,23 +90,39 @@ def lose_the_game!(game) end let(:owner) { accounts(:freddie) } - before do - visit "/login" - login_with(email: owner.email, password: "password") - expect(page).to have_content("You have been logged in") - end + context "when logged in as owner" do + before do + visit "/login" + login_with(email: owner.email, password: "password") + expect(page).to have_content("You have been logged in") + end + + it "allows browsing existing private games" do + visit "/games/my" + + expect(page).to have_content("My Games") + expect(page).to have_content("Game ##{private_game.id}: In progress") + + click_on "Game ##{private_game.id}" - it "allows browsing existing private games" do - visit "/games/my" + expect(page).to have_content("Mines left: 1") + click_cell(4, 4) + expect(page).to have_content("You won!") + end - expect(page).to have_content("My Games") - expect(page).to have_content("Game ##{private_game.id}: In progress") + it "allows replaying it after its finished" do + visit "/games/my" + click_on "Game ##{private_game.id}" - click_on "Game ##{private_game.id}" + expect(page).to have_content("Mines left: 1") + click_cell(2, 2) + expect(page).to have_content("You lost.") - expect(page).to have_content("Mines left: 1") - click_cell(4, 4) - expect(page).to have_content("You won!") + click_on "Replay This Game" + expect(page).to have_content("Mines left: 1") + click_cell(4, 4) + expect(page).to have_content("You won!") + end end end end