<%= game.description %>
diff --git a/app/views/admin/games/new.html.erb b/app/views/admin/games/new.html.erb
index 1cfe2e0..99983d5 100644
--- a/app/views/admin/games/new.html.erb
+++ b/app/views/admin/games/new.html.erb
@@ -3,9 +3,6 @@
diff --git a/app/views/admin/links/_form.html.erb b/app/views/admin/links/_form.html.erb
index 4709ee3..a897152 100644
--- a/app/views/admin/links/_form.html.erb
+++ b/app/views/admin/links/_form.html.erb
@@ -1,4 +1,4 @@
-<%= form_with(model: link.persisted? ? [:admin, link] : [:admin, link.content_module, link]) do |form| %>
+<%= form_with(model: link.persisted? ? [:admin, link] : [:admin, content_module, link]) do |form| %>
<% if link.errors.any? %>
@@ -21,9 +21,6 @@
<%= form.select :link_type, Link.link_types.keys.map { |t| [ t.humanize, t ] },
{ include_blank: "Select a type" }, class: "select w-full" %>
- <%= form.label :position, class: "label" %>
- <%= form.number_field :position, class: "input w-full" %>
-
<%= form.submit class: "btn btn-primary" %>
<% end %>
diff --git a/app/views/admin/links/edit.html.erb b/app/views/admin/links/edit.html.erb
index b9db752..bba1e74 100644
--- a/app/views/admin/links/edit.html.erb
+++ b/app/views/admin/links/edit.html.erb
@@ -11,7 +11,7 @@
Edit Link
- <%= render "form", link: @link %>
+ <%= render "form", link: @link, content_module: @link.content_module %>
diff --git a/app/views/admin/links/new.html.erb b/app/views/admin/links/new.html.erb
index 35513db..05b0d88 100644
--- a/app/views/admin/links/new.html.erb
+++ b/app/views/admin/links/new.html.erb
@@ -11,7 +11,7 @@
New Link
- <%= render "form", link: @link %>
+ <%= render "form", link: @link, content_module: @content_module %>
diff --git a/db/migrate/20261031181034_add_position_to_contents.rb b/db/migrate/20261031181034_add_position_to_contents.rb
new file mode 100644
index 0000000..71e34ab
--- /dev/null
+++ b/db/migrate/20261031181034_add_position_to_contents.rb
@@ -0,0 +1,7 @@
+class AddPositionToContents < ActiveRecord::Migration[8.1]
+ def change
+ add_column :contents, :position, :integer
+ add_index :contents, [ :content_module_id, :position ]
+ add_index :contents, [ :content_module_id, :contentable_type, :contentable_id ], unique: true, name: "index_contents_on_module_and_contentable"
+ end
+end
diff --git a/db/migrate/20261031181544_remove_content_module_from_links_and_games.rb b/db/migrate/20261031181544_remove_content_module_from_links_and_games.rb
new file mode 100644
index 0000000..95ae340
--- /dev/null
+++ b/db/migrate/20261031181544_remove_content_module_from_links_and_games.rb
@@ -0,0 +1,11 @@
+class RemoveContentModuleFromLinksAndGames < ActiveRecord::Migration[8.1]
+ def up
+ remove_reference :links, :content_module, foreign_key: true, index: true
+ remove_column :links, :position
+ remove_reference :games, :content_module, foreign_key: true, index: true
+ end
+
+ def down
+ raise ActiveRecord::IrreversibleMigration
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index a58913e..fc75edd 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[8.1].define(version: 2026_08_29_190944) do
+ActiveRecord::Schema[8.1].define(version: 2026_10_31_181544) do
create_table "classroom_modules", force: :cascade do |t|
t.integer "classroom_program_id", null: false
t.integer "content_module_id", null: false
@@ -60,7 +60,10 @@
t.integer "contentable_id", null: false
t.string "contentable_type", null: false
t.datetime "created_at", null: false
+ t.integer "position"
t.datetime "updated_at", null: false
+ t.index ["content_module_id", "contentable_type", "contentable_id"], name: "index_contents_on_module_and_contentable", unique: true
+ t.index ["content_module_id", "position"], name: "index_contents_on_content_module_id_and_position"
t.index ["content_module_id"], name: "index_contents_on_content_module_id"
t.index ["contentable_type", "contentable_id"], name: "index_contents_on_contentable"
end
@@ -81,25 +84,20 @@
end
create_table "games", force: :cascade do |t|
- t.integer "content_module_id"
t.datetime "created_at", null: false
t.text "description"
t.string "slug", null: false
t.string "title", null: false
t.datetime "updated_at", null: false
- t.index ["content_module_id"], name: "index_games_on_content_module_id"
t.index ["slug"], name: "index_games_on_slug", unique: true
end
create_table "links", force: :cascade do |t|
- t.integer "content_module_id", null: false
t.datetime "created_at", null: false
t.string "link_type", null: false
- t.integer "position"
t.string "title", null: false
t.datetime "updated_at", null: false
t.string "url", null: false
- t.index ["content_module_id"], name: "index_links_on_content_module_id"
end
create_table "programs", force: :cascade do |t|
@@ -172,8 +170,6 @@
add_foreign_key "contents", "content_modules"
add_foreign_key "game_attempts", "games"
add_foreign_key "game_attempts", "students"
- add_foreign_key "games", "content_modules"
- add_foreign_key "links", "content_modules"
add_foreign_key "sessions", "users"
add_foreign_key "student_sessions", "students"
add_foreign_key "students", "classrooms"
diff --git a/db/seeds.rb b/db/seeds.rb
index d8385d2..1c9ac2f 100644
--- a/db/seeds.rb
+++ b/db/seeds.rb
@@ -16,13 +16,39 @@ def maybe(&block)
# Production should not have any seeded data for the content or schools
return if Rails.env.production?
+fruit_veggie_quiz = Game.find_or_create_by!(slug: "fruit-veggie-quiz") do |game|
+ game.title = "Fruit and Veggie Quiz"
+ game.description = "Sort fruits and vegetables into the right food groups."
+end
+sleep_habits = Game.find_or_create_by!(slug: "sleep-habits") do |game|
+ game.title = "Sleep Habits"
+ game.description = "Pick the habits that help you get a good night's sleep."
+end
+# Intentionally not attached to any module so the "Add game" dropdown has an option.
+Game.find_or_create_by!(slug: "hydration-heroes") do |game|
+ game.title = "Hydration Heroes"
+ game.description = "Learn how much water your body needs each day."
+end
+games_by_program = { programs.first => fruit_veggie_quiz, programs.last => sleep_habits }
+
+# The first module of each program and level has a survey then a game.
+# The second module of the basic level has a game then a survey.
+# Other modules have only a survey.
+# TODO: This is getting complex, we should extract this into a partial and make it more readable
ContentModule.levels.keys.each do |level|
programs.each do |program|
+ game = games_by_program.fetch(program)
2.times do |i|
- mod = program.content_modules.create!(name: "#{program.name} #{level.humanize} Module #{i + 1}", level: level, position: i + 1)
- # TODO: Update this when the delegated types migration is completed
- link = Link.new(title: "Survey #{i + 1}", url: "https://example.com/survey/#{program.id}-#{level}-#{i}", link_type: "survey", position: 1, content_module: mod)
- mod.contents.create! contentable: link
+ mod = program.content_modules.create!(name: "#{program.name} #{level.humanize} Module #{i + 1}", level: level)
+ link = Link.new(title: "Survey #{i + 1}", url: "https://example.com/survey/#{program.id}-#{level}-#{i}", link_type: "survey")
+ contentables = if i.zero?
+ [ link, game ]
+ elsif level == "basic"
+ [ game, link ]
+ else
+ [ link ]
+ end
+ contentables.each { |contentable| mod.contents.create!(contentable: contentable) }
end
end
end
diff --git a/skills/rails-test-fixtures-conventions.md b/skills/rails-test-fixtures-conventions.md
index e2d874f..eaed12d 100644
--- a/skills/rails-test-fixtures-conventions.md
+++ b/skills/rails-test-fixtures-conventions.md
@@ -4,21 +4,39 @@ Goal: keep fixtures few. Too many fixtures = nobody understands the dependency g
## Rules
-1. **1-2 fixtures per model max**
+1. 1-2 fixtures per model max
- Name them `:one` and `:two`
- Plain, valid, boring attributes
- Minimal dependencies
- Add `:two` only to compare two records
-2. **Customize in the test, not the fixture**
+2. Customize in the test, not the fixture
- `developers(:one).update!(field: value)`
- Only set the field the test cares about
- Extra DB write is fine, readability wins
-3. **Use a custom helper method for when needed**
+3. Use a custom helper method for when needed
- Create a method like `create_thing` that allows customizing attributes
-4. **New named fixture = last resort**
+4. New named fixture = last resort
- Only for deep/complex required associations
- Give it a real name, not `:three` (e.g. `:paying_customer`)
- - Max 1-2 per model
\ No newline at end of file
+ - Max 1-2 per model
+
+5. Don't use a fixture when creating data inline would be more explicit
+ - A reader should not need to reference multiple fixtures to understand implicit relationships in the test
+ - Examples
+ - Bad: The reader doesn't know that games(:one) is already in modules(:one)
+ ```ruby
+ test "you cannot add a game more than once to the same module" do
+ refute modules(:one).contents.build(contentable: games(:one)).valid?
+ end
+ ```
+ - Good: The build the module explicitly rather than using the fixture
+ ```ruby
+ test "you cannot add a game more than once to the same module" do
+ module_with_game = create_module
+ module_with_game.contents.create!(contentable: games(:one))
+ refute module_with_game.contents.build(contentable: games(:one)).valid?
+ end
+ ```
\ No newline at end of file
diff --git a/test/controllers/admin/content_modules_controller_test.rb b/test/controllers/admin/content_modules_controller_test.rb
index 1336eb3..62e74e5 100644
--- a/test/controllers/admin/content_modules_controller_test.rb
+++ b/test/controllers/admin/content_modules_controller_test.rb
@@ -25,12 +25,22 @@ class Admin::ContentModulesControllerTest < ActionDispatch::IntegrationTest
test "should create content module" do
assert_difference "ContentModule.count" do
post admin_content_modules_url, params: {
- content_module: { name: "New Module", program_id: programs(:one).id, level: "moderate", position: 1 }
+ content_module: { name: "New Module", program_id: programs(:one).id, level: "moderate" }
}
end
assert_redirected_to admin_content_modules_url
end
+ test "create appends the module and ignores a submitted position" do
+ ContentModule.create!(program: programs(:one), level: "moderate", name: "Existing")
+
+ post admin_content_modules_url, params: {
+ content_module: { name: "New Module", program_id: programs(:one).id, level: "moderate", position: 99 }
+ }
+
+ assert_equal 2, ContentModule.find_by!(name: "New Module").position
+ end
+
test "should not create with missing name" do
assert_no_difference "ContentModule.count" do
post admin_content_modules_url, params: {
@@ -46,16 +56,16 @@ class Admin::ContentModulesControllerTest < ActionDispatch::IntegrationTest
end
test "edit lists links ordered by position" do
- @content_module.links.destroy_all
- last_link = Link.create!(content_module: @content_module, title: "Last Link", url: "https://example.com/2", link_type: "survey", position: 2)
- first_link = Link.create!(content_module: @content_module, title: "First Link", url: "https://example.com/1", link_type: "survey", position: 1)
- Content.create!(content_module: @content_module, contentable: last_link)
- Content.create!(content_module: @content_module, contentable: first_link)
+ content_module = ContentModule.create!(program: programs(:one), level: "basic", name: "Ordered Module")
+ last, first = [
+ content_module.contents.create!(contentable: Link.new(title: "Last Link", url: "https://example.com/2", link_type: "survey",), position: 2),
+ content_module.contents.create!(contentable: Link.new(title: "First Link", url: "https://example.com/1", link_type: "survey"), position: 1)
+ ]
- get edit_admin_content_module_url(@content_module)
+ get edit_admin_content_module_url(content_module)
hrefs = css_select("table tbody tr td:first-child a").map { |link| link["href"] }
- assert_equal [ "https://example.com/1", "https://example.com/2" ], hrefs
+ assert_equal [ last.contentable.url, first.contentable.url ], hrefs
end
test "should update content module" do
diff --git a/test/controllers/admin/games_controller_test.rb b/test/controllers/admin/games_controller_test.rb
index 6fae0f6..d3f4b9e 100644
--- a/test/controllers/admin/games_controller_test.rb
+++ b/test/controllers/admin/games_controller_test.rb
@@ -3,7 +3,6 @@
class Admin::GamesControllerTest < ActionDispatch::IntegrationTest
setup do
@game = games(:one)
- @content_module = content_modules(:one)
sign_in_as users(:one)
end
@@ -13,13 +12,15 @@ class Admin::GamesControllerTest < ActionDispatch::IntegrationTest
end
test "should get new" do
- get new_admin_game_url(content_module_id: @content_module.id)
+ get new_admin_game_url
assert_response :success
end
- test "should create game" do
- assert_difference [ "Game.count", "Content.count" ] do
- post admin_games_url, params: { game: { slug: "new-game", title: "New Game", content_module_id: @content_module.id } }
+ test "creating a game does not add it to a module" do
+ assert_difference "Game.count" do
+ assert_no_difference "Content.count" do
+ post admin_games_url, params: { game: { slug: "new-game", title: "New Game" } }
+ end
end
assert_redirected_to admin_games_url
diff --git a/test/controllers/admin/links_controller_test.rb b/test/controllers/admin/links_controller_test.rb
index 8e9a7a5..72ed1b4 100644
--- a/test/controllers/admin/links_controller_test.rb
+++ b/test/controllers/admin/links_controller_test.rb
@@ -12,17 +12,18 @@ class Admin::LinksControllerTest < ActionDispatch::IntegrationTest
assert_response :success
end
- test "should create link" do
- assert_difference "Link.count" do
+ test "create adds the new link to the end of the module" do
+ assert_difference [ "Link.count", "@mod.contents.count" ], 1 do
post admin_content_module_links_url(@mod), params: {
- link: { title: "New Survey", url: "https://example.com/new", link_type: "survey", position: 3 }
+ link: { title: "New Survey", url: "https://example.com/new", link_type: "survey" }
}
end
assert_redirected_to edit_admin_content_module_url(@mod)
+ assert_equal "New Survey", @mod.contents.last.contentable.title
end
test "should not create with missing title" do
- assert_no_difference "Link.count" do
+ assert_no_difference [ "Link.count", "Content.count" ] do
post admin_content_module_links_url(@mod), params: {
link: { title: "", url: "https://example.com", link_type: "survey" }
}
diff --git a/test/fixtures/contents.yml b/test/fixtures/contents.yml
index 1d64a6d..d60d7f6 100644
--- a/test/fixtures/contents.yml
+++ b/test/fixtures/contents.yml
@@ -2,10 +2,10 @@
one:
content_module: one
- contentable_id: <%= ActiveRecord::FixtureSet.identify(:one) %>
- contentable_type: Game
+ contentable: one (Game)
+ position: 1
two:
content_module: one
- contentable_id: <%= ActiveRecord::FixtureSet.identify(:one) %>
- contentable_type: Link
+ contentable: one (Link)
+ position: 2
diff --git a/test/fixtures/games.yml b/test/fixtures/games.yml
index bd0ba0c..14779e4 100644
--- a/test/fixtures/games.yml
+++ b/test/fixtures/games.yml
@@ -3,11 +3,9 @@
one:
title: Game 1
slug: game-1
- content_module: one
description: MyText
two:
title: Game 2
slug: game-2
- content_module: one
description: MyText
diff --git a/test/fixtures/links.yml b/test/fixtures/links.yml
index c6fce72..e6b2526 100644
--- a/test/fixtures/links.yml
+++ b/test/fixtures/links.yml
@@ -1,6 +1,4 @@
one:
- content_module: one
title: Health Survey
url: https://example.com/survey/1
link_type: survey
- position: 1
diff --git a/test/models/content_module_test.rb b/test/models/content_module_test.rb
index c8df5bd..04ebd7d 100644
--- a/test/models/content_module_test.rb
+++ b/test/models/content_module_test.rb
@@ -28,17 +28,64 @@ class ContentModuleTest < ActiveSupport::TestCase
test "destroys associated links" do
content_module = ContentModule.create!(program: @program, level: "basic", name: "With Links")
- link = Link.create!(content_module: content_module, title: "A Link", url: "https://example.com", link_type: "survey")
- Content.create!(content_module: content_module, contentable: link)
+ link = Link.new(title: "A Link", url: "https://example.com", link_type: "survey")
+ content_module.contents.create!(contentable: link)
assert_difference "Link.count", -1 do
content_module.destroy!
end
end
- test "exposes delegated contentables" do
- assert_equal [ links(:one) ], content_modules(:one).links.to_a
- assert_equal [ games(:one) ], content_modules(:one).games.to_a
+ 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)
+ ]
+ assert_equal [ second, first ], content_module.contents.to_a
+ end
+
+ test "appends new modules after the highest position in their program and level" do
+ first = create_module("First")
+ second = create_module("Second")
+
+ assert_equal [ 1, 2 ], [ first.position, second.position ]
+ end
+
+ test "positions are separate for each level and program" do
+ create_module("Advanced")
+ moderate = create_module("Moderate", level: "moderate")
+ other_program = create_module("Other", program: programs(:two))
+
+ assert_equal 1, moderate.position
+ assert_equal 1, other_program.position
+ end
+
+ test "moves to the end of its new level when the level changes" do
+ mod = create_module("Advanced")
+ create_module("Moderate", level: "moderate")
+
+ mod.update!(level: "moderate")
+
+ assert_equal 2, mod.position
+ end
+
+ test "moves to the end of its new program when the program changes" do
+ mod = create_module("Advanced")
+ create_module("Other", program: programs(:two))
+
+ mod.update!(program: programs(:two))
+
+ assert_equal 2, mod.position
+ end
+
+ test "keeps its position when other fields change" do
+ first = create_module("First")
+ create_module("Second")
+
+ first.update!(name: "Renamed")
+
+ assert_equal 1, first.reload.position
end
test "cannot be destroyed when classroom modules exist" do
@@ -49,4 +96,12 @@ class ContentModuleTest < ActiveSupport::TestCase
assert_not content_module.destroy
assert content_module.persisted?
end
+
+ private
+
+ # Defaults to a level no fixture uses, so positions start empty.
+ # TODO: This can drift based on the fixture file and cause unexpected behavior. We should consider a better way to handle this.
+ def create_module(name, program: @program, level: "advanced", **attributes)
+ ContentModule.create!(program: program, level: level, name: name, **attributes)
+ end
end
diff --git a/test/models/content_test.rb b/test/models/content_test.rb
index e006b41..795c55e 100644
--- a/test/models/content_test.rb
+++ b/test/models/content_test.rb
@@ -2,15 +2,78 @@
class ContentTest < ActiveSupport::TestCase
test "delegates to its contentable" do
- content = contents(:one)
+ game = games(:one)
+ content = create_module.contents.create!(contentable: game)
- assert_equal games(:one), content.contentable
+ assert_equal game, content.contentable
assert_equal "Game", content.contentable_type
end
test "belongs to a content module" do
- content = contents(:one)
+ content_module = create_module
+ content = content_module.contents.create!(contentable: games(:one))
- assert_equal content_modules(:one), content.content_module
+ assert_equal content_module, content.content_module
+ end
+
+ test "appends new content after the highest position in its module" do
+ content_module = create_module
+ existing = content_module.contents.create!(contentable: games(:one))
+
+ content = content_module.contents.create!(contentable: games(:two))
+
+ assert_equal existing.position + 1, content.position
+ end
+
+ test "starts positions at 1 in an empty module" do
+ content = create_module.contents.create!(contentable: games(:one))
+
+ assert_equal 1, content.position
+ end
+
+ test "cannot add the same game to a module twice" do
+ content_module = create_module
+ content_module.contents.create!(contentable: games(:one))
+
+ duplicate = content_module.contents.build(contentable: games(:one))
+
+ assert_not duplicate.valid?
+ assert_includes duplicate.errors[:contentable_id], "is already in this module"
+ end
+
+ test "can add the same game to different modules" do
+ create_module.contents.create!(contentable: games(:one))
+
+ content = create_module.contents.build(contentable: games(:one))
+
+ assert content.valid?
+ end
+
+ test "destroying a link's content destroys the link" do
+ content = create_module.contents.create!(contentable: build_link)
+
+ assert_difference [ "Link.count", "Content.count" ], -1 do
+ content.destroy!
+ end
+ end
+
+ test "destroying a game's content keeps the game" do
+ content = create_module.contents.create!(contentable: games(:one))
+
+ assert_difference "Content.count", -1 do
+ assert_no_difference "Game.count" do
+ content.destroy!
+ end
+ end
+ end
+
+ private
+
+ def create_module
+ ContentModule.create!(program: programs(:one), level: "basic", name: "Module #{ContentModule.count}")
+ end
+
+ def build_link
+ Link.new(title: "A Link", url: "https://example.com", link_type: "survey")
end
end
diff --git a/test/models/game_test.rb b/test/models/game_test.rb
index 3622a6f..3fd2fad 100644
--- a/test/models/game_test.rb
+++ b/test/models/game_test.rb
@@ -2,26 +2,61 @@
class GameTest < ActiveSupport::TestCase
test "should not save game without title" do
- game = Game.new(slug: "test-slug", content_module: ContentModule.new)
+ game = Game.new(slug: "test-slug")
assert_not game.save, "Saved the game without a title"
end
test "should not save game without slug" do
- game = Game.new(title: "Test Game", content_module: ContentModule.new)
+ game = Game.new(title: "Test Game")
assert_not game.save, "Saved the game without a slug"
end
test "should not save game with duplicate title" do
- content_module = ContentModule.create!(program: programs(:one), level: "basic", name: "Test Module")
- Game.create!(title: "Test Game", slug: "test-slug", content_module: content_module)
- duplicate_game = Game.new(title: "Test Game", slug: "another-slug", content_module: content_module)
+ Game.create!(title: "Test Game", slug: "test-slug")
+ duplicate_game = Game.new(title: "Test Game", slug: "another-slug")
assert_not duplicate_game.save, "Saved the game with a duplicate title"
end
+ test "destroying a game destroys all its contents" do
+ game = Game.create!(title: "Shared Game", slug: "shared-game")
+ 2.times do |i|
+ ContentModule.create!(program: programs(:one), level: "basic", name: "Module #{i}").contents.create!(contentable: game)
+ end
+
+ assert_difference "Content.count", -2 do
+ game.destroy!
+ end
+ assert_empty Content.where(contentable: game)
+ end
+
+ test "available_for returns games not in the module, ordered by title" do
+ content_module = ContentModule.create!(program: programs(:one), level: "basic", name: "Module")
+ other_module = ContentModule.create!(program: programs(:one), level: "basic", name: "Other Module")
+ in_module = Game.create!(title: "In Module", slug: "in-module").tap do
+ content_module.contents.create!(contentable: it)
+ end
+ in_other_module = Game.create!(title: "B In Other Module", slug: "in-other-module").tap do
+ other_module.contents.create!(contentable: it)
+ end
+ in_no_module = Game.create!(title: "A In No Module", slug: "in-no-module")
+
+
+ available = Game.available_for(content_module)
+
+ assert_not_includes available, in_module
+ assert_equal [ in_no_module, in_other_module ], available.where(id: [ in_module, in_other_module, in_no_module ]).to_a
+ end
+
+ test "destroying a game deletes its student attempts" do
+ assert_difference "GameAttempt.count", -games(:one).game_attempts.count do
+ games(:one).destroy!
+ end
+ assert_empty GameAttempt.where(game: games(:one))
+ end
+
test "should not save game with duplicate slug" do
- content_module = ContentModule.create!(program: programs(:one), level: "basic", name: "Test Module")
- Game.create!(title: "Test Game", slug: "test-slug", content_module: content_module)
- duplicate_game = Game.new(title: "Another Game", slug: "test-slug", content_module: content_module)
+ Game.create!(title: "Test Game", slug: "test-slug")
+ duplicate_game = Game.new(title: "Another Game", slug: "test-slug")
assert_not duplicate_game.save, "Saved the game with a duplicate slug"
end
end
diff --git a/test/models/link_test.rb b/test/models/link_test.rb
index c1889be..3a4995e 100644
--- a/test/models/link_test.rb
+++ b/test/models/link_test.rb
@@ -2,24 +2,41 @@
class LinkTest < ActiveSupport::TestCase
test "is valid with required fields" do
- link = Link.new(content_module: content_modules(:one), title: "My Link", url: "https://example.com", link_type: "survey")
+ link = Link.new(title: "My Link", url: "https://example.com", link_type: "survey")
assert link.valid?
end
test "is invalid without a title" do
- link = Link.new(content_module: content_modules(:one), url: "https://example.com", link_type: "survey")
+ link = Link.new(url: "https://example.com", link_type: "survey")
assert_not link.valid?
assert_includes link.errors[:title], "can't be blank"
end
test "is invalid without a url" do
- link = Link.new(content_module: content_modules(:one), title: "My Link", link_type: "survey")
+ link = Link.new(title: "My Link", link_type: "survey")
assert_not link.valid?
assert_includes link.errors[:url], "can't be blank"
end
+ test "destroying a link destroys its content" do
+ link = Link.new(title: "My Link", url: "https://example.com", link_type: "survey")
+ content = content_modules(:one).contents.create!(contentable: link)
+
+ assert_difference [ "Link.count", "Content.count" ], -1 do
+ link.destroy!
+ end
+ assert_nil Content.find_by(id: content.id)
+ end
+
+ test "finds its content module through its content" do
+ link = Link.new(title: "My Link", url: "https://example.com", link_type: "survey")
+ content_modules(:one).contents.create!(contentable: link)
+
+ assert_equal content_modules(:one), link.content_module
+ end
+
test "is invalid without a link_type" do
- link = Link.new(content_module: content_modules(:one), title: "My Link", url: "https://example.com")
+ link = Link.new(title: "My Link", url: "https://example.com")
assert_not link.valid?
end
end