diff --git a/app/controllers/admin/content_modules/games_controller.rb b/app/controllers/admin/content_modules/games_controller.rb new file mode 100644 index 0000000..82f917c --- /dev/null +++ b/app/controllers/admin/content_modules/games_controller.rb @@ -0,0 +1,33 @@ +class Admin::ContentModules::GamesController < AdminController + before_action :set_content_module + + def new + @content = @content_module.contents.build + load_game_options + end + + def create + @content = @content_module.contents.build(contentable: Game.find_by(id: content_params[:contentable_id])) + + if @content.save + redirect_to edit_admin_content_module_path(@content_module), notice: "Game was successfully added." + else + load_game_options + render :new, status: :unprocessable_entity + end + end + + private + def set_content_module + @content_module = ContentModule.find(params.expect(:content_module_id)) + end + + def load_game_options + @available_games = Game.available_for(@content_module) + @any_games = Game.exists? + end + + def content_params + params.expect(content: [ :contentable_id ]) + end +end diff --git a/app/views/admin/content_modules/edit.html.erb b/app/views/admin/content_modules/edit.html.erb index cfe4a6f..6f47207 100644 --- a/app/views/admin/content_modules/edit.html.erb +++ b/app/views/admin/content_modules/edit.html.erb @@ -23,6 +23,7 @@

Contents

+ <%= link_to "Add game", new_admin_content_module_game_path(@content_module), class: "btn btn-sm btn-primary" %> <%= link_to "Add link", new_admin_content_module_link_path(@content_module), class: "btn btn-sm btn-primary" %>
diff --git a/app/views/admin/content_modules/games/new.html.erb b/app/views/admin/content_modules/games/new.html.erb new file mode 100644 index 0000000..92be4f4 --- /dev/null +++ b/app/views/admin/content_modules/games/new.html.erb @@ -0,0 +1,45 @@ +<% content_for :title, "Add Game" %> + + +
+
+
+

Add Game

+ <% if @available_games.any? %> + <%= form_with model: @content, url: admin_content_module_games_path(@content_module) do |form| %> +
+ <% if @content.errors.any? %> +
+

<%= pluralize(@content.errors.count, "error") %> prohibited this game from being added:

+
    + <% @content.errors.each do |error| %> +
  • <%= error.full_message %>
  • + <% end %> +
+
+ <% end %> + + <%= form.label :contentable_id, "Game", class: "label" %> + <%= form.collection_select :contentable_id, @available_games, :id, :title, + { include_blank: "Select a game" }, class: "select w-full" %> + <%= form.submit "Add game", class: "btn btn-primary" %> +
+ <% end %> + <% elsif @any_games %> +

All games are in this module.

+ <% else %> +

+ No games yet. + <%= link_to "Create a game", new_admin_game_path, class: "link" %> +

+ <% end %> + <%= link_to "Back to module", edit_admin_content_module_path(@content_module), class: "link text-sm" %> +
+
+
diff --git a/config/locales/en.yml b/config/locales/en.yml index 6c349ae..ed89ef2 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -29,3 +29,8 @@ en: hello: "Hello world" + activerecord: + attributes: + content: + contentable: "Game" + contentable_id: "Game" diff --git a/config/routes.rb b/config/routes.rb index b5269d3..923338e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -24,6 +24,7 @@ end resources :content_modules, except: [ :show ] do resources :links, shallow: true, except: %i[index show destroy] + resources :games, only: %i[new create], module: :content_modules resources :contents, shallow: true, only: :destroy end resources :classroom_modules, only: %i[update] diff --git a/test/controllers/admin/content_modules/games_controller_test.rb b/test/controllers/admin/content_modules/games_controller_test.rb new file mode 100644 index 0000000..6a3daf0 --- /dev/null +++ b/test/controllers/admin/content_modules/games_controller_test.rb @@ -0,0 +1,107 @@ +require "test_helper" + +class Admin::ContentModules::GamesControllerTest < ActionDispatch::IntegrationTest + setup do + @content_module = ContentModule.create! name: "Module", program: programs(:one), level: :basic + sign_in_as users(:one) + end + + test "new offers only games not yet in the module" do + @content_module.contents.create! contentable: games(:one) + expected_games = Game.all - [ games(:one) ] + + get new_admin_content_module_game_url(@content_module) + + assert_response :success + assert_select "form[action='#{admin_content_module_games_path(@content_module)}']" do + expected_games.each do |game| + assert_select "select[name='content[contentable_id]'] option", text: game.title, count: 1 + end + assert_select "select[name='content[contentable_id]'] option", text: games(:one).title, count: 0 + end + end + + test "new offers a blank select a game option" do + get new_admin_content_module_game_url(@content_module) + + assert_select "select[name='content[contentable_id]'] option[value='']", text: "Select a game" + end + + test "new explains when every game is attached" do + Game.all.each { |game| @content_module.contents.create! contentable: game } + + get new_admin_content_module_game_url(@content_module) + + assert_response :success + assert_select "select[name='content[contentable_id]']", count: 0 + assert_select "p", text: /All games are in this module\./ + assert_select "a[href='#{edit_admin_content_module_path(@content_module)}']" + end + + test "new links to creating a game when no games exist" do + Game.destroy_all + + get new_admin_content_module_game_url(@content_module) + + assert_response :success + assert_select "select[name='content[contentable_id]']", count: 0 + assert_select "p", text: /No games yet/ + assert_select "a.link[href='#{new_admin_game_path}']", text: "Create a game" + end + + test "new requires admin sign in" do + sign_out + + get new_admin_content_module_game_url(@content_module) + + assert_redirected_to new_admin_session_url + end + + test "create attaches an existing game to the module" do + game = games(:one) + + assert_difference -> { @content_module.contents.count }, 1 do + post admin_content_module_games_url(@content_module), params: { content: { contentable_id: game.id } } + end + + assert_redirected_to edit_admin_content_module_url(@content_module) + assert_equal game, @content_module.contents.last.contentable + end + + test "create does not attach the same game twice" do + @content_module.contents.create! contentable: games(:one) + assert_no_difference "Content.count" do + post admin_content_module_games_url(@content_module), params: { content: { contentable_id: games(:one).id } } + end + + assert_response :unprocessable_entity + assert_select "li", text: "Game is already in this module" + end + + test "create asks for a game when none is selected" do + assert_no_difference "Content.count" do + post admin_content_module_games_url(@content_module), params: { content: { contentable_id: "" } } + end + + assert_response :unprocessable_entity + assert_select "li", text: "Game must exist" + end + + test "create asks for a game when the game is unknown" do + assert_no_difference "Content.count" do + post admin_content_module_games_url(@content_module), params: { content: { contentable_id: 0 } } + end + + assert_response :unprocessable_entity + assert_select "li", text: "Game must exist" + end + + test "create requires admin sign in" do + sign_out + + assert_no_difference "Content.count" do + post admin_content_module_games_url(@content_module), params: { content: { contentable_id: games(:two).id } } + end + assert_redirected_to new_admin_session_url + end +end diff --git a/test/models/content_module_test.rb b/test/models/content_module_test.rb index 7d8f697..ba04dde 100644 --- a/test/models/content_module_test.rb +++ b/test/models/content_module_test.rb @@ -39,9 +39,10 @@ class ContentModuleTest < ActiveSupport::TestCase test "returns contents ordered by position" do content_module = create_module("With Contents") first, second = [ - content_module.contents.create!(contentable: games(:one), position: 1), - content_module.contents.create!(contentable: games(:two), position: 2) + content_module.contents.create!(contentable: games(:one)), + content_module.contents.create!(contentable: games(:two)) ] + second.move_up assert_equal [ second, first ], content_module.contents.to_a end