chore: init commit
in worker.../repo/GITFOLDER.zip is the .git folder.
This commit is contained in:
@@ -0,0 +1,202 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "test_helper"
|
||||
|
||||
module Admin
|
||||
class AnnouncementsControllerTest < ActionDispatch::IntegrationTest
|
||||
setup do
|
||||
@admin = create(:admin, admin: true)
|
||||
sign_in(@admin)
|
||||
|
||||
@announcement = Announcement.create!(
|
||||
title: "Test Announcement",
|
||||
content: "Test content"
|
||||
)
|
||||
end
|
||||
|
||||
# Authorization tests
|
||||
|
||||
test "non-admin cannot access index" do
|
||||
sign_out(@admin)
|
||||
sign_in(create(:teacher))
|
||||
|
||||
get admin_announcements_path
|
||||
|
||||
assert_redirected_to root_path
|
||||
assert_equal "Access denied. Admin privileges required.", flash[:alert]
|
||||
end
|
||||
|
||||
test "unauthenticated user is redirected to sign in" do
|
||||
sign_out(@admin)
|
||||
|
||||
get admin_announcements_path
|
||||
|
||||
assert_redirected_to new_user_session_path
|
||||
end
|
||||
|
||||
# Index tests
|
||||
|
||||
test "should get index" do
|
||||
get admin_announcements_path
|
||||
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "index lists all announcements" do
|
||||
announcement2 = Announcement.create!(title: "Second Announcement", content: "More content")
|
||||
|
||||
get admin_announcements_path
|
||||
|
||||
assert_response :success
|
||||
assert_match @announcement.title, response.body
|
||||
assert_match announcement2.title, response.body
|
||||
end
|
||||
|
||||
test "index sorts by created_at ascending by default" do
|
||||
get admin_announcements_path
|
||||
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "index sorts by column when sort param is provided" do
|
||||
get admin_announcements_path, params: { sort: "title", direction: "asc" }
|
||||
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "index sorts descending when direction is desc" do
|
||||
get admin_announcements_path, params: { sort: "title", direction: "desc" }
|
||||
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
# Show tests
|
||||
|
||||
test "should show announcement" do
|
||||
get admin_announcement_path(@announcement)
|
||||
|
||||
assert_response :success
|
||||
assert_match @announcement.title, response.body
|
||||
end
|
||||
|
||||
# New tests
|
||||
|
||||
test "should get new" do
|
||||
get new_admin_announcement_path
|
||||
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
# Create tests
|
||||
|
||||
test "should create announcement" do
|
||||
assert_difference("Announcement.count") do
|
||||
post admin_announcements_path, params: {
|
||||
announcement: {
|
||||
title: "New Announcement",
|
||||
content: "New content",
|
||||
featured: false
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
assert_redirected_to admin_announcement_path(Announcement.last)
|
||||
assert_equal "Announcement created successfully.", flash[:notice]
|
||||
end
|
||||
|
||||
test "should create featured announcement" do
|
||||
assert_difference("Announcement.count") do
|
||||
post admin_announcements_path, params: {
|
||||
announcement: {
|
||||
title: "Featured Announcement",
|
||||
content: "Important content",
|
||||
featured: true
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
assert Announcement.last.featured?
|
||||
end
|
||||
|
||||
test "should not create announcement without title" do
|
||||
assert_no_difference("Announcement.count") do
|
||||
post admin_announcements_path, params: {
|
||||
announcement: {
|
||||
title: "",
|
||||
content: "Some content"
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
assert_response :unprocessable_content
|
||||
end
|
||||
|
||||
test "should not create announcement without content" do
|
||||
assert_no_difference("Announcement.count") do
|
||||
post admin_announcements_path, params: {
|
||||
announcement: {
|
||||
title: "A title",
|
||||
content: ""
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
assert_response :unprocessable_content
|
||||
end
|
||||
|
||||
# Edit tests
|
||||
|
||||
test "should get edit" do
|
||||
get edit_admin_announcement_path(@announcement)
|
||||
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
# Update tests
|
||||
|
||||
test "should update announcement" do
|
||||
patch admin_announcement_path(@announcement), params: {
|
||||
announcement: {
|
||||
title: "Updated Title"
|
||||
}
|
||||
}
|
||||
|
||||
assert_redirected_to admin_announcement_path(@announcement)
|
||||
assert_equal "Announcement updated successfully.", flash[:notice]
|
||||
assert_equal "Updated Title", @announcement.reload.title
|
||||
end
|
||||
|
||||
test "should not update announcement with invalid params" do
|
||||
patch admin_announcement_path(@announcement), params: {
|
||||
announcement: {
|
||||
title: ""
|
||||
}
|
||||
}
|
||||
|
||||
assert_response :unprocessable_content
|
||||
assert_equal "Test Announcement", @announcement.reload.title
|
||||
end
|
||||
|
||||
# Destroy tests
|
||||
|
||||
test "should destroy announcement" do
|
||||
assert_difference("Announcement.count", -1) do
|
||||
delete admin_announcement_path(@announcement)
|
||||
end
|
||||
|
||||
assert_redirected_to admin_announcements_path
|
||||
assert_equal "Announcement deleted successfully.", flash[:notice]
|
||||
end
|
||||
|
||||
test "non-admin cannot destroy announcement" do
|
||||
sign_out(@admin)
|
||||
sign_in(create(:teacher))
|
||||
|
||||
assert_no_difference("Announcement.count") do
|
||||
delete admin_announcement_path(@announcement)
|
||||
end
|
||||
|
||||
assert_redirected_to root_path
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,32 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "test_helper"
|
||||
|
||||
module Admin
|
||||
class BaseControllerTest < ActionDispatch::IntegrationTest
|
||||
test "admin can access admin v2 dashboard" do
|
||||
admin = create(:admin, admin: true)
|
||||
sign_in(admin)
|
||||
|
||||
get admin_root_path
|
||||
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "non-admin cannot access admin v2 dashboard" do
|
||||
teacher = create(:teacher)
|
||||
sign_in(teacher)
|
||||
|
||||
get admin_root_path
|
||||
|
||||
assert_redirected_to root_path
|
||||
assert_equal "Access denied. Admin privileges required.", flash[:alert]
|
||||
end
|
||||
|
||||
test "unauthenticated user redirected to sign in" do
|
||||
get admin_root_path
|
||||
|
||||
assert_redirected_to new_user_session_path
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,165 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "test_helper"
|
||||
|
||||
module Admin
|
||||
class ClassroomsControllerTest < ActionDispatch::IntegrationTest
|
||||
test "index" do
|
||||
classroom1 = create(:classroom, name: "Bravo")
|
||||
classroom2 = create(:classroom, name: "Charlie")
|
||||
classroom3 = create(:classroom, name: "Alpha")
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
get admin_classrooms_path
|
||||
rows = css_select("tbody tr[id^='classroom_']")
|
||||
ordered_row_ids = rows.pluck("id")
|
||||
|
||||
assert_response :success
|
||||
assert_select "h3", "Classrooms"
|
||||
assert_equal(
|
||||
[dom_id(classroom3), dom_id(classroom1), dom_id(classroom2)],
|
||||
ordered_row_ids
|
||||
)
|
||||
end
|
||||
|
||||
test "show" do
|
||||
grade9 = create(:grade, level: 9)
|
||||
grade10 = create(:grade, level: 10)
|
||||
classroom = create(:classroom, grades: [grade9, grade10])
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
get admin_classroom_path(classroom)
|
||||
|
||||
assert_response :success
|
||||
assert_select "h2", classroom.name
|
||||
assert_select "[data-testid='grades_display'] dd", text: "9th-10th"
|
||||
end
|
||||
|
||||
test "should get new" do
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
get new_admin_classroom_path
|
||||
|
||||
assert_response :success
|
||||
assert_select "h1", "New Classroom"
|
||||
end
|
||||
|
||||
test "create" do
|
||||
grade = create(:grade, level: 7)
|
||||
school_year = create(:school_year)
|
||||
params = {
|
||||
classroom: {
|
||||
name: "Abc123",
|
||||
grade_ids: [grade.id],
|
||||
school_year_id: school_year.id
|
||||
}
|
||||
}
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
assert_difference("Classroom.count") do
|
||||
post(admin_classrooms_path, params:)
|
||||
end
|
||||
|
||||
assert_redirected_to admin_classroom_path(Classroom.last)
|
||||
assert_equal "Classroom created successfully.", flash[:notice]
|
||||
end
|
||||
|
||||
test "create with invalid params" do
|
||||
params = { classroom: { name: "" } }
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
assert_no_difference("Classroom.count") do
|
||||
post(admin_classrooms_path, params:)
|
||||
end
|
||||
|
||||
assert_response :unprocessable_entity
|
||||
end
|
||||
|
||||
test "edit" do
|
||||
classroom = create(:classroom)
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
get edit_admin_classroom_path(classroom)
|
||||
|
||||
assert_response :success
|
||||
assert_select "h1", "Edit Classroom"
|
||||
end
|
||||
|
||||
test "update" do
|
||||
name = "Abc123"
|
||||
classroom = create(:classroom)
|
||||
params = { classroom: { name: } }
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
patch(admin_classroom_path(classroom), params:)
|
||||
|
||||
assert_redirected_to admin_classroom_path(classroom)
|
||||
assert_equal "Classroom updated successfully.", flash[:notice]
|
||||
assert_equal name, classroom.reload.name
|
||||
end
|
||||
|
||||
test "update with invalid params" do
|
||||
classroom = create(:classroom)
|
||||
params = { classroom: { name: "" } }
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
patch(admin_classroom_path(classroom), params:)
|
||||
|
||||
assert_response :unprocessable_entity
|
||||
end
|
||||
|
||||
test "toggle_archive" do
|
||||
classroom = create(:classroom, archived: false)
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
patch toggle_archive_admin_classroom_path(classroom)
|
||||
classroom.reload
|
||||
|
||||
assert_redirected_to admin_classroom_path(classroom)
|
||||
assert_equal "Classroom has been archived.", flash[:notice]
|
||||
assert classroom.archived?
|
||||
end
|
||||
|
||||
test "activate via toggle_archive" do
|
||||
classroom = create(:classroom, archived: true)
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
patch toggle_archive_admin_classroom_path(classroom)
|
||||
classroom.reload
|
||||
|
||||
assert_redirected_to admin_classroom_path(classroom)
|
||||
assert_equal "Classroom has been activated.", flash[:notice]
|
||||
assert_not classroom.archived?
|
||||
end
|
||||
|
||||
test "index when teacher" do
|
||||
teacher = create(:teacher)
|
||||
sign_in(teacher)
|
||||
|
||||
get admin_classrooms_path
|
||||
|
||||
assert_redirected_to root_path
|
||||
assert_equal "Access denied. Admin privileges required.", flash[:alert]
|
||||
end
|
||||
|
||||
test "toggle archive when teacher" do
|
||||
classroom = create(:classroom)
|
||||
teacher = create(:teacher)
|
||||
sign_in(teacher)
|
||||
|
||||
patch toggle_archive_admin_classroom_path(classroom)
|
||||
|
||||
assert_redirected_to root_path
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,33 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "test_helper"
|
||||
|
||||
module Admin
|
||||
class DashboardControllerTest < ActionDispatch::IntegrationTest
|
||||
test "admin can access admin dashboard" do
|
||||
admin = create(:admin, admin: true)
|
||||
sign_in(admin)
|
||||
|
||||
get admin_root_path
|
||||
|
||||
assert_response :success
|
||||
assert_select "h1", "Admin Dashboard"
|
||||
end
|
||||
|
||||
test "non-admin cannot access admin dashboard" do
|
||||
teacher = create(:teacher)
|
||||
sign_in(teacher)
|
||||
|
||||
get admin_root_path
|
||||
|
||||
assert_redirected_to root_path
|
||||
assert_equal "Access denied. Admin privileges required.", flash[:alert]
|
||||
end
|
||||
|
||||
test "unauthenticated user redirected to sign in" do
|
||||
get admin_root_path
|
||||
|
||||
assert_redirected_to new_user_session_path
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,133 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "test_helper"
|
||||
|
||||
module Admin
|
||||
class PortfolioTransactionsControllerTest < ActionDispatch::IntegrationTest
|
||||
test "show" do
|
||||
portfolio = build(:portfolio)
|
||||
create(:student, portfolio:)
|
||||
portfolio_transaction = create(:portfolio_transaction, portfolio:)
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
get admin_portfolio_transaction_path(portfolio_transaction)
|
||||
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "new" do
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
get new_admin_portfolio_transaction_path
|
||||
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "create" do
|
||||
portfolio = build(:portfolio)
|
||||
create(:student, portfolio:)
|
||||
params = {
|
||||
portfolio_transaction: {
|
||||
portfolio_id: portfolio.id,
|
||||
transaction_type: "deposit",
|
||||
reason: "awards",
|
||||
description: "LSP's royal allowance",
|
||||
amount_cents: 5000
|
||||
}
|
||||
}
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
assert_difference("PortfolioTransaction.count") do
|
||||
post(admin_portfolio_transactions_path, params:)
|
||||
end
|
||||
portfolio_transaction = PortfolioTransaction.last
|
||||
|
||||
assert_redirected_to(
|
||||
admin_portfolio_transaction_path(portfolio_transaction)
|
||||
)
|
||||
assert_equal(
|
||||
"Portfolio transaction created successfully.",
|
||||
flash[:notice]
|
||||
)
|
||||
end
|
||||
|
||||
test "create with invalid params" do
|
||||
params = { portfolio_transaction: { portfolio_id: "" } }
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
assert_no_difference("PortfolioTransaction.count") do
|
||||
post(admin_portfolio_transactions_path, params:)
|
||||
end
|
||||
|
||||
assert_response :unprocessable_content
|
||||
end
|
||||
|
||||
test "edit" do
|
||||
portfolio = build(:portfolio)
|
||||
create(:student, portfolio:)
|
||||
portfolio_transaction = create(:portfolio_transaction, portfolio:)
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
get edit_admin_portfolio_transaction_path(portfolio_transaction)
|
||||
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "update" do
|
||||
description = "Finn's dungeon loot"
|
||||
amount_cents = 7_500
|
||||
portfolio = build(:portfolio)
|
||||
create(:student, portfolio:)
|
||||
portfolio_transaction = create(:portfolio_transaction, portfolio:)
|
||||
params = { portfolio_transaction: { description:, amount_cents: } }
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
patch(admin_portfolio_transaction_path(portfolio_transaction), params:)
|
||||
portfolio_transaction.reload
|
||||
|
||||
assert_redirected_to(
|
||||
admin_portfolio_transaction_path(portfolio_transaction)
|
||||
)
|
||||
assert_equal(
|
||||
"Portfolio transaction updated successfully.",
|
||||
flash[:notice]
|
||||
)
|
||||
assert_equal description, portfolio_transaction.description
|
||||
assert_equal amount_cents, portfolio_transaction.amount_cents
|
||||
end
|
||||
|
||||
test "update with invalid params" do
|
||||
portfolio = build(:portfolio)
|
||||
create(:student, portfolio:)
|
||||
portfolio_transaction = create(:portfolio_transaction, portfolio:)
|
||||
params = { portfolio_transaction: { portfolio_id: "" } }
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
patch(admin_portfolio_transaction_path(portfolio_transaction), params:)
|
||||
|
||||
assert_response :unprocessable_content
|
||||
end
|
||||
|
||||
test "destroy" do
|
||||
portfolio = build(:portfolio)
|
||||
create(:student, portfolio:)
|
||||
portfolio_transaction = create(:portfolio_transaction, portfolio:)
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
assert_difference("PortfolioTransaction.count", -1) do
|
||||
delete admin_portfolio_transaction_path(portfolio_transaction)
|
||||
end
|
||||
|
||||
assert_redirected_to admin_root_path
|
||||
assert_equal "Portfolio transaction deleted successfully.", flash[:notice]
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,158 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "test_helper"
|
||||
|
||||
module Admin
|
||||
class SchoolYearsControllerTest < ActionDispatch::IntegrationTest
|
||||
test "index" do
|
||||
create(:school_year)
|
||||
create(:school_year)
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
get admin_school_years_path
|
||||
|
||||
assert_response :success
|
||||
assert_select "h3", "School Years"
|
||||
assert_select "tbody tr", count: 2
|
||||
end
|
||||
|
||||
test "show" do
|
||||
school_name = "Candy Kingdom"
|
||||
year_name = "1000"
|
||||
school = create(:school, name: school_name)
|
||||
year = create(:year, name: year_name)
|
||||
school_year = create(:school_year, school:, year:)
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
get admin_school_year_path(school_year)
|
||||
|
||||
assert_response :success
|
||||
assert_select "h2", "#{school_name} (#{year_name})"
|
||||
assert_select "h3", "Quarters"
|
||||
end
|
||||
|
||||
test "new" do
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
get new_admin_school_year_path
|
||||
|
||||
assert_response :success
|
||||
assert_select "h1", "New School Year"
|
||||
end
|
||||
|
||||
test "create" do
|
||||
school = create(:school)
|
||||
year = create(:year)
|
||||
params = { school_year: { school_id: school.id, year_id: year.id } }
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
assert_difference("SchoolYear.count") do
|
||||
post(admin_school_years_path, params:)
|
||||
end
|
||||
school_year = SchoolYear.last
|
||||
|
||||
assert_redirected_to admin_school_year_path(school_year)
|
||||
assert_equal "School year created successfully.", flash[:notice]
|
||||
assert_equal(
|
||||
["Quarter 1", "Quarter 2", "Quarter 3", "Quarter 4"],
|
||||
school_year.quarters.order(:number).pluck(:name)
|
||||
)
|
||||
end
|
||||
|
||||
test "create with invalid params" do
|
||||
params = { school_year: { school_id: "" } }
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
assert_no_difference("SchoolYear.count") do
|
||||
post(admin_school_years_path, params:)
|
||||
end
|
||||
|
||||
assert_response :unprocessable_content
|
||||
end
|
||||
|
||||
test "create with duplicate school year shows error" do
|
||||
school = create(:school)
|
||||
year = create(:year)
|
||||
create(:school_year, school:, year:)
|
||||
params = { school_year: { school_id: school.id, year_id: year.id } }
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
assert_no_difference("SchoolYear.count") do
|
||||
post(admin_school_years_path, params:)
|
||||
end
|
||||
|
||||
assert_response :unprocessable_content
|
||||
assert_select "p.text-red-600", /already exists/
|
||||
end
|
||||
|
||||
test "edit" do
|
||||
school_year = create(:school_year)
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
get edit_admin_school_year_path(school_year)
|
||||
|
||||
assert_response :success
|
||||
assert_select "h1", "Edit School Year"
|
||||
end
|
||||
|
||||
test "update" do
|
||||
school_year = create(:school_year)
|
||||
school = create(:school)
|
||||
params = { school_year: { school_id: school.id } }
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
patch(admin_school_year_path(school_year), params:)
|
||||
school_year.reload
|
||||
|
||||
assert_redirected_to admin_school_year_path(school_year)
|
||||
assert_equal "School year updated successfully.", flash[:notice]
|
||||
assert_equal school.id, school_year.school_id
|
||||
end
|
||||
|
||||
test "update with invalid params" do
|
||||
school_year = create(:school_year)
|
||||
params = { school_year: { school_id: nil } }
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
patch(admin_school_year_path(school_year), params:)
|
||||
|
||||
assert_response :unprocessable_content
|
||||
end
|
||||
|
||||
test "destroy" do
|
||||
school_year = create(:school_year)
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
assert_difference("SchoolYear.count", -1) do
|
||||
delete admin_school_year_path(school_year)
|
||||
end
|
||||
|
||||
assert_redirected_to admin_school_years_path
|
||||
assert_equal "School year deleted successfully.", flash[:notice]
|
||||
end
|
||||
|
||||
test "destroy with classrooms" do
|
||||
school_year = create(:school_year)
|
||||
create(:classroom, school_year:)
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
assert_no_difference("SchoolYear.count") do
|
||||
delete admin_school_year_path(school_year)
|
||||
end
|
||||
|
||||
assert_redirected_to admin_school_years_path
|
||||
assert_match(/Cannot delete school/, flash[:alert])
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,203 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "test_helper"
|
||||
|
||||
module Admin
|
||||
class SchoolsControllerTest < ActionDispatch::IntegrationTest
|
||||
setup do
|
||||
@admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(@admin)
|
||||
end
|
||||
|
||||
test "index" do
|
||||
create(:school, name: "Alpha School")
|
||||
create(:school, name: "Beta School")
|
||||
|
||||
get admin_schools_path
|
||||
|
||||
assert_response :success
|
||||
assert_select "h3", "Schools"
|
||||
assert_select "tbody tr", count: 2
|
||||
end
|
||||
|
||||
test "show" do
|
||||
school = create(:school, name: "Test School")
|
||||
|
||||
get admin_school_path(school)
|
||||
|
||||
assert_response :success
|
||||
assert_select "h2", school.name
|
||||
end
|
||||
|
||||
test "show displays associated years" do
|
||||
school = create(:school, name: "Test School")
|
||||
year1 = create(:year, name: "2024 - 2025")
|
||||
year2 = create(:year, name: "2025 - 2026")
|
||||
school.years << [year1, year2]
|
||||
|
||||
get admin_school_path(school)
|
||||
|
||||
assert_response :success
|
||||
assert_select "li", text: year1.name
|
||||
assert_select "li", text: year2.name
|
||||
end
|
||||
|
||||
test "new" do
|
||||
get new_admin_school_path
|
||||
|
||||
assert_response :success
|
||||
assert_select "h1", "New School"
|
||||
end
|
||||
|
||||
test "new shows year checkboxes" do
|
||||
year1 = create(:year, name: "2024 - 2025")
|
||||
year2 = create(:year, name: "2025 - 2026")
|
||||
|
||||
get new_admin_school_path
|
||||
|
||||
assert_response :success
|
||||
assert_select "input[type='checkbox'][name='school[year_ids][]']", count: 2
|
||||
assert_select "label", text: year1.name
|
||||
assert_select "label", text: year2.name
|
||||
end
|
||||
|
||||
test "create" do
|
||||
year1 = create(:year, name: "2024 - 2025")
|
||||
year2 = create(:year, name: "2025 - 2026")
|
||||
params = {
|
||||
school: {
|
||||
name: "New School",
|
||||
year_ids: [year1.id, year2.id]
|
||||
}
|
||||
}
|
||||
|
||||
assert_difference("School.count") do
|
||||
post(admin_schools_path, params:)
|
||||
end
|
||||
|
||||
school = School.last
|
||||
assert_redirected_to admin_school_path(school)
|
||||
assert_equal "School created successfully.", flash[:notice]
|
||||
assert_equal [year1, year2].sort_by(&:id), school.years.sort_by(&:id)
|
||||
end
|
||||
|
||||
test "create without years" do
|
||||
params = {
|
||||
school: {
|
||||
name: "School Without Years"
|
||||
}
|
||||
}
|
||||
|
||||
assert_difference("School.count") do
|
||||
post(admin_schools_path, params:)
|
||||
end
|
||||
|
||||
school = School.last
|
||||
assert_redirected_to admin_school_path(school)
|
||||
assert_empty school.years
|
||||
end
|
||||
|
||||
test "create with invalid params" do
|
||||
params = {
|
||||
school: {
|
||||
name: ""
|
||||
}
|
||||
}
|
||||
|
||||
assert_no_difference("School.count") do
|
||||
post(admin_schools_path, params:)
|
||||
end
|
||||
|
||||
assert_response :unprocessable_content
|
||||
end
|
||||
|
||||
test "edit" do
|
||||
school = create(:school, name: "Test School")
|
||||
|
||||
get edit_admin_school_path(school)
|
||||
|
||||
assert_response :success
|
||||
assert_select "h1", "Edit School"
|
||||
end
|
||||
|
||||
test "edit shows current year selections" do
|
||||
year1 = create(:year, name: "2024 - 2025")
|
||||
year2 = create(:year, name: "2025 - 2026")
|
||||
school = create(:school, name: "Test School")
|
||||
school.years << year1
|
||||
|
||||
get edit_admin_school_path(school)
|
||||
|
||||
assert_response :success
|
||||
assert_select "input[type='checkbox'][name='school[year_ids][]'][value='#{year1.id}'][checked='checked']"
|
||||
assert_select "input[type='checkbox'][name='school[year_ids][]'][value='#{year2.id}']:not([checked])"
|
||||
end
|
||||
|
||||
test "update" do
|
||||
year1 = create(:year, name: "2024 - 2025")
|
||||
year2 = create(:year, name: "2025 - 2026")
|
||||
school = create(:school, name: "Original Name")
|
||||
school.years << year1
|
||||
params = {
|
||||
school: {
|
||||
name: "Updated Name",
|
||||
year_ids: [year2.id]
|
||||
}
|
||||
}
|
||||
|
||||
patch(admin_school_path(school), params:)
|
||||
school.reload
|
||||
|
||||
assert_redirected_to admin_school_path(school)
|
||||
assert_equal "School updated successfully.", flash[:notice]
|
||||
assert_equal "Updated Name", school.name
|
||||
assert_equal [year2], school.years
|
||||
end
|
||||
|
||||
test "update can remove all years" do
|
||||
year1 = create(:year, name: "2024 - 2025")
|
||||
school = create(:school, name: "Test School")
|
||||
school.years << year1
|
||||
params = {
|
||||
school: {
|
||||
name: school.name,
|
||||
year_ids: []
|
||||
}
|
||||
}
|
||||
|
||||
patch(admin_school_path(school), params:)
|
||||
school.reload
|
||||
|
||||
assert_redirected_to admin_school_path(school)
|
||||
assert_empty school.years
|
||||
end
|
||||
|
||||
test "update with invalid params" do
|
||||
school = create(:school, name: "Test School")
|
||||
params = { school: { name: "" } }
|
||||
|
||||
patch(admin_school_path(school), params:)
|
||||
|
||||
assert_response :unprocessable_content
|
||||
end
|
||||
|
||||
test "destroy" do
|
||||
school = create(:school, name: "Test School")
|
||||
|
||||
assert_difference("School.count", -1) do
|
||||
delete admin_school_path(school)
|
||||
end
|
||||
|
||||
assert_redirected_to admin_schools_path
|
||||
end
|
||||
|
||||
test "cannot destroy school with associated years" do
|
||||
school = create(:school, name: "Test School")
|
||||
school.years << create(:year, name: "2025 - 2026")
|
||||
|
||||
assert_no_difference("School.count") do
|
||||
delete admin_school_path(school)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,170 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "test_helper"
|
||||
|
||||
module Admin
|
||||
class StocksControllerTest < ActionDispatch::IntegrationTest
|
||||
setup do
|
||||
@admin = create(:admin, admin: true)
|
||||
sign_in(@admin)
|
||||
|
||||
@stock1 = Stock.create!(ticker: "AAPL", company_name: "Apple Inc.", price_cents: 15_000, archived: false)
|
||||
@stock2 = Stock.create!(ticker: "GOOGL", company_name: "Alphabet Inc.", price_cents: 14_000, archived: false)
|
||||
@stock3 = Stock.create!(ticker: "MSFT", company_name: "Microsoft Corp.", price_cents: 38_000, archived: true)
|
||||
end
|
||||
|
||||
# Index tests
|
||||
test "should get index" do
|
||||
get admin_stocks_path
|
||||
|
||||
assert_response :success
|
||||
assert_select "h3", "Stocks"
|
||||
end
|
||||
|
||||
test "index shows all stocks" do
|
||||
get admin_stocks_path
|
||||
|
||||
assert_response :success
|
||||
assert_select "tbody tr", count: 3
|
||||
end
|
||||
|
||||
# Show tests
|
||||
test "should show stock" do
|
||||
get admin_stock_path(@stock1)
|
||||
|
||||
assert_response :success
|
||||
assert_select "h2", @stock1.ticker
|
||||
end
|
||||
|
||||
test "should show stock price information" do
|
||||
get admin_stock_path(@stock1)
|
||||
|
||||
assert_response :success
|
||||
assert_select "h3", "Price Information"
|
||||
assert_select "dt", text: "Current Price"
|
||||
end
|
||||
|
||||
# New tests
|
||||
test "should get new" do
|
||||
get new_admin_stock_path
|
||||
|
||||
assert_response :success
|
||||
assert_select "h1", "New Stock"
|
||||
end
|
||||
|
||||
# Create tests
|
||||
test "should create stock" do
|
||||
assert_difference("Stock.count") do
|
||||
post admin_stocks_path, params: {
|
||||
stock: {
|
||||
ticker: "TSLA",
|
||||
company_name: "Tesla Inc.",
|
||||
company_website: "https://www.tesla.com",
|
||||
price_cents: 25_000,
|
||||
yesterday_price_cents: 24_000,
|
||||
archived: false
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
assert_redirected_to admin_stock_path(Stock.last)
|
||||
assert_equal "Stock created successfully.", flash[:notice]
|
||||
end
|
||||
|
||||
test "should not create stock with invalid params" do
|
||||
assert_no_difference("Stock.count") do
|
||||
post admin_stocks_path, params: {
|
||||
stock: {
|
||||
ticker: "",
|
||||
company_name: ""
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
assert_response :unprocessable_content
|
||||
end
|
||||
|
||||
test "should not create stock with invalid website URL" do
|
||||
assert_no_difference("Stock.count") do
|
||||
post admin_stocks_path, params: {
|
||||
stock: {
|
||||
ticker: "TEST",
|
||||
company_name: "Test Company",
|
||||
company_website: "not-a-valid-url"
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
assert_response :unprocessable_content
|
||||
end
|
||||
|
||||
# Edit tests
|
||||
test "should get edit" do
|
||||
get edit_admin_stock_path(@stock1)
|
||||
|
||||
assert_response :success
|
||||
assert_select "h1", "Edit Stock"
|
||||
end
|
||||
|
||||
# Update tests
|
||||
test "should update stock" do
|
||||
patch admin_stock_path(@stock1), params: {
|
||||
stock: {
|
||||
company_name: "Apple Incorporated"
|
||||
}
|
||||
}
|
||||
|
||||
assert_redirected_to admin_stock_path(@stock1)
|
||||
assert_equal "Stock updated successfully.", flash[:notice]
|
||||
assert_equal "Apple Incorporated", @stock1.reload.company_name
|
||||
end
|
||||
|
||||
test "should not update stock with invalid params" do
|
||||
patch admin_stock_path(@stock1), params: {
|
||||
stock: {
|
||||
ticker: ""
|
||||
}
|
||||
}
|
||||
|
||||
assert_response :unprocessable_content
|
||||
end
|
||||
|
||||
# Destroy tests
|
||||
test "should destroy stock" do
|
||||
assert_difference("Stock.count", -1) do
|
||||
delete admin_stock_path(@stock1)
|
||||
end
|
||||
|
||||
assert_redirected_to admin_stocks_path
|
||||
assert_equal "Stock deleted successfully.", flash[:notice]
|
||||
end
|
||||
|
||||
# Authorization tests
|
||||
test "non-admin cannot access index" do
|
||||
sign_out(@admin)
|
||||
classroom = create(:classroom)
|
||||
student = User.create!(username: "student", type: "Student", password: "password", classroom: classroom)
|
||||
sign_in(student)
|
||||
|
||||
get admin_stocks_path
|
||||
|
||||
assert_redirected_to root_path
|
||||
assert_equal "Access denied. Admin privileges required.", flash[:alert]
|
||||
end
|
||||
|
||||
test "non-admin cannot create stock" do
|
||||
sign_out(@admin)
|
||||
teacher = create(:teacher)
|
||||
sign_in(teacher)
|
||||
|
||||
post admin_stocks_path, params: {
|
||||
stock: {
|
||||
ticker: "TEST",
|
||||
company_name: "Test"
|
||||
}
|
||||
}
|
||||
|
||||
assert_redirected_to root_path
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,482 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "test_helper"
|
||||
|
||||
module Admin
|
||||
class StudentsControllerTest < ActionDispatch::IntegrationTest
|
||||
setup do
|
||||
@classroom1 = create(:classroom)
|
||||
@classroom2 = create(:classroom)
|
||||
@admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(@admin)
|
||||
end
|
||||
|
||||
test "index" do
|
||||
create(:student)
|
||||
create(:student)
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
get admin_students_path
|
||||
|
||||
assert_response :success
|
||||
assert_select "h3", "Students"
|
||||
assert_select "tbody tr", count: 2
|
||||
end
|
||||
|
||||
test "index with discarded filter" do
|
||||
student = create(:student, :discarded)
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
get admin_students_path(discarded: true)
|
||||
|
||||
assert_response :success
|
||||
assert_select "tbody tr", count: 1
|
||||
assert_select "form[action=?]", restore_admin_student_path(student) do
|
||||
assert_select "button", text: "Restore"
|
||||
end
|
||||
end
|
||||
|
||||
test "index with all filter" do
|
||||
create(:student)
|
||||
create(:student)
|
||||
create(:student, :discarded)
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
get admin_students_path(all: true)
|
||||
|
||||
assert_response :success
|
||||
assert_select "tbody tr", count: 3
|
||||
assert_select "a[href*='edit']", count: 2
|
||||
end
|
||||
|
||||
test "show" do
|
||||
username = "finn"
|
||||
student = create(:student, username:)
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
get admin_student_path(student)
|
||||
|
||||
assert_response :success
|
||||
assert_select "h2", username
|
||||
assert_select "h3", text: "Portfolio Details"
|
||||
assert_select(
|
||||
"[data-testid='cash_balance_label']",
|
||||
text: "Cash Balance"
|
||||
)
|
||||
assert_select(
|
||||
"[data-testid='total_portfolio_worth_label']",
|
||||
text: "Total Portfolio Worth"
|
||||
)
|
||||
end
|
||||
|
||||
test "show displays attendance records" do
|
||||
student = create(:student)
|
||||
quarter = student.classroom.school_year.quarters.find_by!(number: 1)
|
||||
grade_book = student.classroom.grade_books.find_by!(quarter: quarter)
|
||||
create(:grade_entry, grade_book: grade_book, user: student, attendance_days: 42, is_perfect_attendance: true)
|
||||
|
||||
get admin_student_path(student)
|
||||
|
||||
assert_response :success
|
||||
assert_select "h3", text: "Attendance"
|
||||
assert_select "tbody tr", minimum: 1
|
||||
assert_select "td", text: "Q1"
|
||||
assert_select "td", text: "42"
|
||||
end
|
||||
|
||||
test "show displays empty attendance message when no records" do
|
||||
student = create(:student)
|
||||
|
||||
get admin_student_path(student)
|
||||
|
||||
assert_response :success
|
||||
assert_select "p", text: "No attendance records found."
|
||||
end
|
||||
|
||||
test "show displays earnings summary" do
|
||||
student = create(:student)
|
||||
portfolio = student.portfolio
|
||||
portfolio.portfolio_transactions.create!(
|
||||
amount_cents: 500, transaction_type: :deposit,
|
||||
reason: :attendance_earnings
|
||||
)
|
||||
portfolio.portfolio_transactions.create!(amount_cents: 300, transaction_type: :deposit, reason: :math_earnings)
|
||||
portfolio.portfolio_transactions.create!(amount_cents: 200, transaction_type: :deposit, reason: :awards)
|
||||
|
||||
get admin_student_path(student)
|
||||
|
||||
assert_response :success
|
||||
assert_select "h4", text: "Earnings Summary"
|
||||
assert_select "td", text: "Attendance Earnings"
|
||||
assert_select "td", text: "Math Earnings"
|
||||
assert_select "td", text: "Rewards"
|
||||
assert_select "td", text: "Total Earnings"
|
||||
assert_select "td", text: "Transaction Fees"
|
||||
end
|
||||
|
||||
test "new" do
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
get new_admin_student_path
|
||||
|
||||
assert_response :success
|
||||
assert_select "h1", "New Student"
|
||||
end
|
||||
|
||||
test "create" do
|
||||
username = "jake"
|
||||
classroom = create(:classroom, name: "Ice Kingdom")
|
||||
params = {
|
||||
student: {
|
||||
username:,
|
||||
classroom_id: classroom.id,
|
||||
password: "password123",
|
||||
password_confirmation: "password123"
|
||||
}
|
||||
}
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
assert_difference(["Student.count", "Portfolio.count"]) do
|
||||
post(admin_students_path, params:)
|
||||
end
|
||||
student = Student.last
|
||||
|
||||
assert_redirected_to admin_student_path(student)
|
||||
assert_equal(
|
||||
"Student #{username} created successfully. Password: password123",
|
||||
flash[:notice]
|
||||
)
|
||||
assert_not_nil student.portfolio
|
||||
end
|
||||
|
||||
test "create with invalid params" do
|
||||
params = { student: { username: "", classroom_id: nil } }
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
assert_no_difference("Student.count") do
|
||||
post(admin_students_path, params:)
|
||||
end
|
||||
|
||||
assert_response :unprocessable_content
|
||||
end
|
||||
|
||||
test "edit" do
|
||||
student = create(:student)
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
get edit_admin_student_path(student)
|
||||
|
||||
assert_response :success
|
||||
assert_select "h1", "Edit Student"
|
||||
end
|
||||
|
||||
test "update" do
|
||||
username = "marceline"
|
||||
student = create(:student)
|
||||
params = { student: { username: } }
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
patch(admin_student_path(student), params:)
|
||||
student.reload
|
||||
|
||||
assert_redirected_to admin_student_path(student)
|
||||
assert_equal "Student updated successfully.", flash[:notice]
|
||||
assert_equal username, student.username
|
||||
end
|
||||
|
||||
test "update with invalid params" do
|
||||
student = create(:student)
|
||||
params = { student: { username: "" } }
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
patch(admin_student_path(student), params:)
|
||||
|
||||
assert_response :unprocessable_content
|
||||
end
|
||||
|
||||
test "destroy" do
|
||||
username = "gunter"
|
||||
student = create(:student, username:)
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
assert_no_difference("Student.count") do
|
||||
delete admin_student_path(student)
|
||||
end
|
||||
student.reload
|
||||
|
||||
assert_redirected_to admin_students_path
|
||||
assert_equal "Student #{username} archived successfully.", flash[:notice]
|
||||
assert student.discarded?
|
||||
end
|
||||
|
||||
test "restore" do
|
||||
username = "lsp"
|
||||
student = create(:student, :discarded, username:)
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
patch restore_admin_student_path(student)
|
||||
student.reload
|
||||
|
||||
assert_redirected_to admin_students_path(discarded: true)
|
||||
assert_equal "Student #{username} restored successfully.", flash[:notice]
|
||||
assert_not student.discarded?
|
||||
end
|
||||
|
||||
test "add_transaction" do
|
||||
portfolio = build(:portfolio, user: nil)
|
||||
student = create(:student, portfolio:)
|
||||
create(:portfolio_transaction, :deposit, portfolio:, amount_cents: 10_000)
|
||||
params = {
|
||||
transaction_type: "deposit",
|
||||
add_fund_amount: "100.50",
|
||||
transaction_reason: "awards",
|
||||
transaction_description: "Test deposit"
|
||||
}
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
post(add_transaction_admin_student_path(student), params:)
|
||||
portfolio.reload
|
||||
|
||||
assert_redirected_to admin_student_path(student)
|
||||
assert_equal "Transaction added successfully.", flash[:notice]
|
||||
assert_equal 20_050, portfolio.cash_balance * 100
|
||||
end
|
||||
|
||||
test "add_transaction debit" do
|
||||
student = create(:student)
|
||||
params = {
|
||||
transaction_type: "debit",
|
||||
add_fund_amount: "50.25",
|
||||
transaction_reason: "administrative_adjustments",
|
||||
transaction_description: "Test debit"
|
||||
}
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
post(add_transaction_admin_student_path(student), params:)
|
||||
transaction = student.portfolio.portfolio_transactions.last
|
||||
|
||||
assert_redirected_to admin_student_path(student)
|
||||
assert_equal "debit", transaction.transaction_type
|
||||
assert_equal 5_025, transaction.amount_cents
|
||||
end
|
||||
|
||||
test "add_transaction invalid params" do
|
||||
student = create(:student)
|
||||
params = { transaction_type: "" }
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
post(add_transaction_admin_student_path(student), params:)
|
||||
expected_error_message =
|
||||
"Transaction Type must be present, Amount must be present, " \
|
||||
"Reason must be present"
|
||||
|
||||
assert_redirected_to edit_admin_student_path(student)
|
||||
assert_equal expected_error_message, flash[:alert]
|
||||
end
|
||||
|
||||
test "add_transaction params are not nested under student key" do
|
||||
student = create(:student)
|
||||
params = {
|
||||
student: {
|
||||
transaction_type: "deposit",
|
||||
add_fund_amount: "50.00",
|
||||
transaction_reason: "awards",
|
||||
transaction_description: "Nested params should be ignored"
|
||||
}
|
||||
}
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
assert_no_difference("PortfolioTransaction.count") do
|
||||
post(add_transaction_admin_student_path(student), params:)
|
||||
end
|
||||
|
||||
assert_redirected_to edit_admin_student_path(student)
|
||||
end
|
||||
|
||||
# Import/Template tests
|
||||
test "template should download CSV template" do
|
||||
get template_admin_students_path
|
||||
|
||||
assert_response :success
|
||||
assert_equal "text/csv", response.media_type
|
||||
assert_includes response.headers["Content-Disposition"], "attachment; filename=\"student_import_template.csv\""
|
||||
assert_match(/classroom_id,username/, response.body)
|
||||
end
|
||||
|
||||
test "import should create students from valid CSV" do
|
||||
csv_content = "classroom_id,username\n#{@classroom1.id},import_student1\n#{@classroom2.id},import_student2"
|
||||
csv_file = Tempfile.new(["test_import", ".csv"])
|
||||
csv_file.write(csv_content)
|
||||
csv_file.rewind
|
||||
|
||||
assert_difference("Student.count", 2) do
|
||||
post import_admin_students_path, params: {
|
||||
csv_file: fixture_file_upload(csv_file.path, "text/csv")
|
||||
}
|
||||
end
|
||||
|
||||
csv_file.close
|
||||
csv_file.unlink
|
||||
|
||||
assert_redirected_to admin_students_path
|
||||
assert_match(/Successfully created 2 students/, flash[:notice])
|
||||
assert_match(/import_student1/, flash[:notice])
|
||||
assert_match(/import_student2/, flash[:notice])
|
||||
end
|
||||
|
||||
test "import should skip existing students" do
|
||||
create(:student, username: "student1", classroom: @classroom1)
|
||||
csv_content = "classroom_id,username\n#{@classroom1.id},student1\n#{@classroom2.id},new_student"
|
||||
csv_file = Tempfile.new(["test_import", ".csv"])
|
||||
csv_file.write(csv_content)
|
||||
csv_file.rewind
|
||||
|
||||
assert_difference("Student.count", 1) do
|
||||
post import_admin_students_path, params: {
|
||||
csv_file: fixture_file_upload(csv_file.path, "text/csv")
|
||||
}
|
||||
end
|
||||
|
||||
csv_file.close
|
||||
csv_file.unlink
|
||||
|
||||
assert_redirected_to admin_students_path
|
||||
assert_match(/Successfully created 1 students/, flash[:notice])
|
||||
assert_match(/Skipped 1 existing usernames/, flash[:notice])
|
||||
end
|
||||
|
||||
test "import should handle errors and show line numbers" do
|
||||
csv_content = "classroom_id,username\n999,invalid_student\n#{@classroom1.id},valid_student"
|
||||
csv_file = Tempfile.new(["test_import", ".csv"])
|
||||
csv_file.write(csv_content)
|
||||
csv_file.rewind
|
||||
|
||||
post import_admin_students_path, params: {
|
||||
csv_file: fixture_file_upload(csv_file.path, "text/csv")
|
||||
}
|
||||
|
||||
csv_file.close
|
||||
csv_file.unlink
|
||||
|
||||
assert_redirected_to admin_students_path
|
||||
assert_match(/errors occurred/, flash[:alert])
|
||||
assert_match(/Row 2:/, flash[:alert])
|
||||
end
|
||||
|
||||
test "import should reject missing file" do
|
||||
assert_no_difference("Student.count") do
|
||||
post import_admin_students_path
|
||||
end
|
||||
|
||||
assert_redirected_to admin_students_path
|
||||
assert_equal I18n.t("admin.students.import.errors.no_file"), flash[:alert]
|
||||
end
|
||||
|
||||
test "import should handle malformed CSV" do
|
||||
csv_content = "classroom_id,username\n1,\"unclosed quote\n2,another_row"
|
||||
csv_file = Tempfile.new(["test_import", ".csv"])
|
||||
csv_file.write(csv_content)
|
||||
csv_file.rewind
|
||||
|
||||
assert_no_difference("Student.count") do
|
||||
post import_admin_students_path, params: {
|
||||
csv_file: fixture_file_upload(csv_file.path, "text/csv")
|
||||
}
|
||||
end
|
||||
|
||||
csv_file.close
|
||||
csv_file.unlink
|
||||
|
||||
assert_redirected_to admin_students_path
|
||||
assert_match(/Invalid CSV format/, flash[:alert])
|
||||
end
|
||||
|
||||
test "import should handle empty CSV" do
|
||||
csv_content = "classroom_id,username\n"
|
||||
csv_file = Tempfile.new(["test_import", ".csv"])
|
||||
csv_file.write(csv_content)
|
||||
csv_file.rewind
|
||||
|
||||
assert_no_difference("Student.count") do
|
||||
post import_admin_students_path, params: {
|
||||
csv_file: fixture_file_upload(csv_file.path, "text/csv")
|
||||
}
|
||||
end
|
||||
|
||||
csv_file.close
|
||||
csv_file.unlink
|
||||
|
||||
assert_redirected_to admin_students_path
|
||||
assert_equal I18n.t("admin.students.import.errors.no_students"), flash[:alert]
|
||||
end
|
||||
|
||||
test "import should show both success and error messages" do
|
||||
csv_content = "classroom_id,username\n" \
|
||||
"#{@classroom1.id},import_success1\n999,import_fail\n#{@classroom2.id},import_success2"
|
||||
csv_file = Tempfile.new(["test_import", ".csv"])
|
||||
csv_file.write(csv_content)
|
||||
csv_file.rewind
|
||||
|
||||
post import_admin_students_path, params: {
|
||||
csv_file: fixture_file_upload(csv_file.path, "text/csv")
|
||||
}
|
||||
|
||||
csv_file.close
|
||||
csv_file.unlink
|
||||
|
||||
assert_redirected_to admin_students_path
|
||||
assert_match(/Successfully created 2 students/, flash[:notice])
|
||||
assert_match(/1 errors occurred/, flash[:alert])
|
||||
end
|
||||
|
||||
test "non-admin cannot access template" do
|
||||
sign_out(@admin)
|
||||
teacher = create(:teacher)
|
||||
sign_in(teacher)
|
||||
|
||||
get template_admin_students_path
|
||||
|
||||
assert_redirected_to root_path
|
||||
assert_equal "Access denied. Admin privileges required.", flash[:alert]
|
||||
end
|
||||
|
||||
test "non-admin cannot import students" do
|
||||
sign_out(@admin)
|
||||
teacher = create(:teacher)
|
||||
sign_in(teacher)
|
||||
|
||||
csv_content = "classroom_id,username\n#{@classroom1.id},import_student"
|
||||
csv_file = Tempfile.new(["test_import", ".csv"])
|
||||
csv_file.write(csv_content)
|
||||
csv_file.rewind
|
||||
|
||||
assert_no_difference("Student.count") do
|
||||
post import_admin_students_path, params: {
|
||||
csv_file: fixture_file_upload(csv_file.path, "text/csv")
|
||||
}
|
||||
end
|
||||
|
||||
csv_file.close
|
||||
csv_file.unlink
|
||||
|
||||
assert_redirected_to root_path
|
||||
assert_equal "Access denied. Admin privileges required.", flash[:alert]
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,42 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "test_helper"
|
||||
|
||||
module Admin
|
||||
module Teachers
|
||||
class DeactivationsControllerTest < ActionDispatch::IntegrationTest
|
||||
setup do
|
||||
@admin = create(:admin, admin: true)
|
||||
sign_in(@admin)
|
||||
|
||||
@classroom1 = create(:classroom, name: "Math 101")
|
||||
@teacher1 = create(:teacher, username: "teacher1", email: "teacher1@example.com")
|
||||
@teacher1.classrooms << @classroom1
|
||||
end
|
||||
|
||||
test "should deactivate teacher" do
|
||||
assert_no_difference("Teacher.count") do
|
||||
post admin_teacher_deactivation_path(@teacher1)
|
||||
end
|
||||
|
||||
assert_redirected_to admin_teachers_path
|
||||
assert_equal "Teacher teacher1@example.com deactivated successfully.", flash[:notice]
|
||||
assert @teacher1.reload.discarded?
|
||||
end
|
||||
|
||||
test "deactivate should soft delete teacher and remove from active list" do
|
||||
post admin_teacher_deactivation_path(@teacher1)
|
||||
|
||||
# Teacher should still exist in database
|
||||
assert_not_nil Teacher.with_discarded.find_by(id: @teacher1.id)
|
||||
# But should not appear in kept scope
|
||||
assert_nil Teacher.kept.find_by(id: @teacher1.id)
|
||||
|
||||
follow_redirect!
|
||||
|
||||
# Should not appear on the default (active) index
|
||||
assert_select "##{dom_id(@teacher1)}", count: 0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,45 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "test_helper"
|
||||
|
||||
module Admin
|
||||
module Teachers
|
||||
class ReactivationsControllerTest < ActionDispatch::IntegrationTest
|
||||
setup do
|
||||
@admin = create(:admin, admin: true)
|
||||
sign_in(@admin)
|
||||
|
||||
@classroom1 = create(:classroom, name: "Math 101")
|
||||
@teacher1 = create(:teacher, username: "teacher1", email: "teacher1@example.com")
|
||||
@teacher1.classrooms << @classroom1
|
||||
end
|
||||
|
||||
test "should reactivate deactivated teacher" do
|
||||
@teacher1.discard
|
||||
|
||||
assert @teacher1.discarded?
|
||||
|
||||
post admin_teacher_reactivation_path(@teacher1)
|
||||
|
||||
assert_redirected_to admin_teachers_path
|
||||
assert_equal "Teacher teacher1@example.com reactivated successfully.", flash[:notice]
|
||||
assert_not @teacher1.reload.discarded?
|
||||
end
|
||||
|
||||
test "reactivate should restore teacher to active status and appear in active list" do
|
||||
@teacher1.discard
|
||||
assert_not_nil @teacher1.reload.discarded_at
|
||||
|
||||
post admin_teacher_reactivation_path(@teacher1)
|
||||
|
||||
# Should clear discarded_at timestamp
|
||||
assert_nil @teacher1.reload.discarded_at
|
||||
|
||||
follow_redirect!
|
||||
|
||||
# Should appear on the default (active) index
|
||||
assert_select "##{dom_id(@teacher1)}", count: 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,294 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "test_helper"
|
||||
|
||||
module Admin
|
||||
class TeachersControllerTest < ActionDispatch::IntegrationTest
|
||||
test "index" do
|
||||
teacher1 = create(:teacher, email: "z@example.com")
|
||||
teacher2 = create(:teacher, email: "a@example.com")
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
get admin_teachers_path
|
||||
rows = css_select("tbody tr[id^='teacher_']")
|
||||
row_ids = rows.pluck("id")
|
||||
|
||||
assert_response :success
|
||||
assert_select "h3", "Teachers"
|
||||
assert_equal [dom_id(teacher2), dom_id(teacher1)], row_ids
|
||||
end
|
||||
|
||||
test "index sorts by username by default" do
|
||||
create(:teacher, email: "teacher1@example.com")
|
||||
create(:teacher, email: "teacher2@example.com")
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
get admin_teachers_path
|
||||
|
||||
assert_response :success
|
||||
# Default sort should be by username ascending (username == email for teachers)
|
||||
assert_select "tbody tr:nth-child(1)", text: /teacher1/
|
||||
assert_select "tbody tr:nth-child(2)", text: /teacher2/
|
||||
end
|
||||
|
||||
test "index shows only active teachers by default" do
|
||||
teacher1 = create(:teacher, username: "teacher1")
|
||||
create(:teacher, username: "teacher2")
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
teacher1.discard
|
||||
|
||||
get admin_teachers_path
|
||||
|
||||
assert_response :success
|
||||
assert_select "tbody tr", count: 1
|
||||
assert_select "span.bg-green-50", text: "Active"
|
||||
end
|
||||
|
||||
test "index shows both active and deactivated teachers with all filter" do
|
||||
teacher1 = create(:teacher, username: "teacher1")
|
||||
create(:teacher, username: "teacher2")
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
teacher1.discard
|
||||
|
||||
get admin_teachers_path(all: true)
|
||||
|
||||
assert_response :success
|
||||
assert_select "tbody tr", count: 2
|
||||
assert_select "span.bg-red-50", text: "Deactivated"
|
||||
assert_select "span.bg-green-50", text: "Active"
|
||||
end
|
||||
|
||||
test "index shows only deactivated teachers with discarded filter" do
|
||||
teacher1 = create(:teacher, username: "teacher1")
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
teacher1.discard
|
||||
|
||||
get admin_teachers_path(discarded: true)
|
||||
|
||||
assert_response :success
|
||||
assert_select "tbody tr", count: 1
|
||||
assert_select "span.bg-red-50", text: "Deactivated"
|
||||
end
|
||||
|
||||
# Show tests
|
||||
test "should show teacher" do
|
||||
teacher = create(:teacher)
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
get admin_teacher_path(teacher)
|
||||
|
||||
assert_response :success
|
||||
assert_select "h2", teacher.username
|
||||
end
|
||||
|
||||
test "should show teacher classrooms" do
|
||||
classroom = create(:classroom, name: "Math 101")
|
||||
teacher = create(:teacher)
|
||||
teacher.classrooms << classroom
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
get admin_teacher_path(teacher)
|
||||
|
||||
assert_response :success
|
||||
assert_select "h2", teacher.username
|
||||
assert_select "h3", "Classrooms"
|
||||
assert_select "li", text: classroom.name
|
||||
end
|
||||
|
||||
test "show when teacher has no classrooms" do
|
||||
teacher = create(:teacher)
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
get admin_teacher_path(teacher)
|
||||
|
||||
assert_response :success
|
||||
assert_select "p", text: "No classrooms assigned to this teacher yet."
|
||||
end
|
||||
|
||||
test "new" do
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
get new_admin_teacher_path
|
||||
|
||||
assert_response :success
|
||||
assert_select "h1", "New Teacher"
|
||||
end
|
||||
|
||||
test "new shows warning message when no classrooms available" do
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
get new_admin_teacher_path
|
||||
|
||||
assert_response :success
|
||||
assert_select "h1", "New Teacher"
|
||||
|
||||
assert_select "h3", text: "No Classrooms Available"
|
||||
assert_select "p", text: /No Classrooms associated with this school and active year/
|
||||
assert_select "a[href='#{admin_classrooms_path}']", text: "update classrooms"
|
||||
end
|
||||
|
||||
test "new shows classrooms when available" do
|
||||
current_year = Year.find_or_create_by(name: "2025 - 2026")
|
||||
school = create(:school, name: "Test School")
|
||||
school_year = create(:school_year, school: school, year: current_year)
|
||||
|
||||
create(:classroom, name: "Test Classroom", archived: false, school_year: school_year)
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
get new_admin_teacher_path
|
||||
|
||||
assert_response :success
|
||||
|
||||
assert_select "input[type='checkbox'][name='teacher[classroom_ids][]']"
|
||||
assert_select "label", text: "Test Classroom"
|
||||
|
||||
assert_select "h3", text: "No Classrooms Available", count: 0
|
||||
end
|
||||
|
||||
test "create" do
|
||||
classroom1 = create(:classroom, name: "Ice Kingdom")
|
||||
classroom2 = create(:classroom, name: "Candy Kingdom")
|
||||
params = {
|
||||
teacher: {
|
||||
username: "lsp",
|
||||
email: "lsp@lumpyspace.com",
|
||||
name: "Lumpy Space Princess",
|
||||
classroom_ids: [classroom1.id, classroom2.id]
|
||||
}
|
||||
}
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
assert_difference("Teacher.count") do
|
||||
post(admin_teachers_path, params:)
|
||||
end
|
||||
|
||||
teacher = Teacher.last
|
||||
assert_redirected_to admin_teacher_path(teacher)
|
||||
assert_equal(
|
||||
"Teacher created successfully. Password reset email has been sent.",
|
||||
flash[:notice]
|
||||
)
|
||||
assert_equal [classroom1, classroom2], teacher.classrooms
|
||||
assert_not_nil teacher.reset_password_token
|
||||
end
|
||||
|
||||
test "create with invalid params" do
|
||||
params = {
|
||||
teacher: {
|
||||
username: "",
|
||||
email: "invalid"
|
||||
}
|
||||
}
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
assert_no_difference("Teacher.count") do
|
||||
post(admin_teachers_path, params:)
|
||||
end
|
||||
|
||||
assert_response :unprocessable_content
|
||||
end
|
||||
|
||||
test "edit" do
|
||||
teacher = create(:teacher)
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
get edit_admin_teacher_path(teacher)
|
||||
|
||||
assert_response :success
|
||||
assert_select "h1", "Edit Teacher"
|
||||
end
|
||||
|
||||
test "update" do
|
||||
name = "Lumpy Space Princess"
|
||||
classroom1 = create(:classroom, name: "Ice Kingdom")
|
||||
classroom2 = create(:classroom, name: "Candy Kingdom")
|
||||
teacher = create(:teacher, classrooms: [classroom1])
|
||||
params = { teacher: { name:, classroom_ids: [classroom2.id] } }
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
patch(admin_teacher_path(teacher), params:)
|
||||
teacher.reload
|
||||
|
||||
assert_redirected_to admin_teacher_path(teacher)
|
||||
assert_equal "Teacher updated successfully.", flash[:notice]
|
||||
assert_equal name, teacher.name
|
||||
assert_equal [classroom2], teacher.classrooms
|
||||
end
|
||||
|
||||
test "update with invalid params" do
|
||||
teacher = create(:teacher)
|
||||
params = { teacher: { email: "" } }
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
patch(admin_teacher_path(teacher), params:)
|
||||
|
||||
assert_response :unprocessable_content
|
||||
end
|
||||
|
||||
# Hard delete (destroy) tests
|
||||
test "should permanently delete deactivated teacher" do
|
||||
teacher = create(:teacher, email: "teacher1@example.com")
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
teacher.discard
|
||||
|
||||
assert_difference("Teacher.with_discarded.count", -1) do
|
||||
delete admin_teacher_path(teacher)
|
||||
end
|
||||
|
||||
assert_redirected_to admin_teachers_path
|
||||
assert_equal "Teacher teacher1@example.com permanently deleted.", flash[:notice]
|
||||
assert_nil Teacher.with_discarded.find_by(id: teacher.id)
|
||||
end
|
||||
|
||||
test "should not permanently delete active teacher" do
|
||||
teacher = create(:teacher, username: "teacher1")
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
assert_no_difference("Teacher.with_discarded.count") do
|
||||
delete admin_teacher_path(teacher)
|
||||
end
|
||||
|
||||
assert_redirected_to edit_admin_teacher_path(teacher)
|
||||
assert_equal "Teacher must be deactivated before permanent deletion.", flash[:alert]
|
||||
assert_not teacher.reload.discarded?
|
||||
end
|
||||
|
||||
test "permanent delete should remove teacher from database and DOM" do
|
||||
teacher = create(:teacher, username: "teacher1")
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
teacher.discard
|
||||
|
||||
assert_difference("Teacher.with_discarded.count", -1) do
|
||||
delete admin_teacher_path(teacher)
|
||||
end
|
||||
|
||||
follow_redirect!
|
||||
|
||||
assert_select "##{dom_id(teacher)}", count: 0
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,144 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "test_helper"
|
||||
|
||||
module Admin
|
||||
class UsersControllerTest < ActionDispatch::IntegrationTest
|
||||
test "index" do
|
||||
create(:admin, username: "marceline")
|
||||
create(:teacher, username: "bonnibel")
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
get admin_users_path
|
||||
|
||||
assert_response :success
|
||||
assert_select "h3", "Users"
|
||||
assert_select "tbody tr", count: User.count
|
||||
end
|
||||
|
||||
test "index sorts by username by default" do
|
||||
create(:admin, username: "zzz_last")
|
||||
create(:admin, username: "aaa_first")
|
||||
admin = create(:admin, username: "mmm_admin", admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
get admin_users_path
|
||||
rows = css_select("tbody tr td:nth-child(2)").map(&:text).map(&:strip)
|
||||
|
||||
assert_response :success
|
||||
assert_equal rows, rows.sort
|
||||
end
|
||||
|
||||
test "index sorts descending when direction is desc" do
|
||||
create(:admin, username: "zzz_last")
|
||||
create(:admin, username: "aaa_first")
|
||||
admin = create(:admin, username: "mmm_admin", admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
get admin_users_path, params: { sort: "username", direction: "desc" }
|
||||
rows = css_select("tbody tr td:nth-child(2)").map(&:text).map(&:strip)
|
||||
|
||||
assert_response :success
|
||||
assert_equal rows, rows.sort.reverse
|
||||
end
|
||||
|
||||
test "show" do
|
||||
user = create(:admin, username: "finn")
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
get admin_user_path(user)
|
||||
|
||||
assert_response :success
|
||||
assert_select "h2", "finn"
|
||||
end
|
||||
|
||||
test "new" do
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
get new_admin_user_path
|
||||
|
||||
assert_response :success
|
||||
assert_select "h1", "New User"
|
||||
end
|
||||
|
||||
test "edit" do
|
||||
user = create(:admin)
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
get edit_admin_user_path(user)
|
||||
|
||||
assert_response :success
|
||||
assert_select "h1", "Edit User"
|
||||
end
|
||||
|
||||
test "create" do
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
params = {
|
||||
user: {
|
||||
username: "bmo",
|
||||
email: "bmo@example.com",
|
||||
type: "User",
|
||||
admin: false,
|
||||
password: "Passw0rd",
|
||||
password_confirmation: "Passw0rd"
|
||||
}
|
||||
}
|
||||
|
||||
assert_difference("User.count") do
|
||||
post admin_users_path, params: params
|
||||
end
|
||||
|
||||
assert_redirected_to admin_user_path(User.last)
|
||||
assert_equal "User created successfully.", flash[:notice]
|
||||
end
|
||||
|
||||
test "create with invalid params" do
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
params = { user: { username: "", email: "bad@example.com" } }
|
||||
|
||||
assert_no_difference("User.count") do
|
||||
post admin_users_path, params: params
|
||||
end
|
||||
|
||||
assert_response :unprocessable_content
|
||||
end
|
||||
|
||||
test "update" do
|
||||
user = create(:admin, username: "original")
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
patch admin_user_path(user), params: { user: { username: "updated" } }
|
||||
user.reload
|
||||
|
||||
assert_redirected_to admin_user_path(user)
|
||||
assert_equal "User updated successfully.", flash[:notice]
|
||||
assert_equal "updated", user.username
|
||||
end
|
||||
|
||||
test "update with invalid params" do
|
||||
user = create(:admin)
|
||||
admin = create(:admin, admin: true, classroom: nil)
|
||||
sign_in(admin)
|
||||
|
||||
patch admin_user_path(user), params: { user: { username: "" } }
|
||||
|
||||
assert_response :unprocessable_content
|
||||
end
|
||||
|
||||
test "non-admin cannot access index" do
|
||||
sign_in(create(:teacher))
|
||||
|
||||
get admin_users_path
|
||||
|
||||
assert_redirected_to root_path
|
||||
assert_equal "Access denied. Admin privileges required.", flash[:alert]
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,34 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "test_helper"
|
||||
|
||||
class AnnouncementsControllerTest < ActionDispatch::IntegrationTest
|
||||
setup do
|
||||
@user = create(:student)
|
||||
sign_in @user
|
||||
@announcement = Announcement.create!(
|
||||
title: "Test Announcement",
|
||||
content: "Test content"
|
||||
)
|
||||
end
|
||||
|
||||
test "should show announcement" do
|
||||
get announcement_url(@announcement)
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "students can view announcements" do
|
||||
get announcement_url(@announcement)
|
||||
assert_response :success
|
||||
assert_match @announcement.title, response.body
|
||||
end
|
||||
|
||||
test "teachers can view announcements" do
|
||||
sign_out @user
|
||||
teacher = create(:teacher)
|
||||
sign_in teacher
|
||||
|
||||
get announcement_url(@announcement)
|
||||
assert_response :success
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,59 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "test_helper"
|
||||
|
||||
class ApplicationControllerTest < ActionDispatch::IntegrationTest
|
||||
test "after_sign_in_path_for redirects students to root" do
|
||||
create(:student, username: "teststudent", password: "password123")
|
||||
|
||||
post user_session_path, params: {
|
||||
user: { username: "teststudent", password: "password123" }
|
||||
}
|
||||
|
||||
assert_redirected_to root_path
|
||||
end
|
||||
|
||||
test "after_sign_in_path_for redirects teachers to root" do
|
||||
create(:teacher, email: "testteacher@example.com", password: "password123")
|
||||
|
||||
post user_session_path, params: {
|
||||
user: { username: "testteacher@example.com", password: "password123" }
|
||||
}
|
||||
|
||||
assert_redirected_to root_path
|
||||
end
|
||||
|
||||
test "after_sign_in_path_for redirects admins to root" do
|
||||
create(:admin, username: "testadmin", password: "password123")
|
||||
|
||||
post user_session_path, params: {
|
||||
user: { username: "testadmin", password: "password123" }
|
||||
}
|
||||
|
||||
assert_redirected_to root_path
|
||||
end
|
||||
|
||||
test "ensure_teacher_or_admin allows teachers" do
|
||||
teacher = create(:teacher)
|
||||
sign_in teacher
|
||||
|
||||
get classrooms_path
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "ensure_teacher_or_admin allows admins" do
|
||||
admin = create(:admin)
|
||||
sign_in admin
|
||||
|
||||
get classrooms_path
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "ensure_teacher_or_admin blocks students" do
|
||||
student = create(:student)
|
||||
sign_in student
|
||||
|
||||
get new_classroom_path
|
||||
assert_redirected_to student.portfolio_path
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,87 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "test_helper"
|
||||
|
||||
class ClassroomEnrollmentsControllerTest < ActionDispatch::IntegrationTest
|
||||
setup do
|
||||
@classroom = create(:classroom)
|
||||
@student = create(:student, classroom: @classroom)
|
||||
@teacher = create(:teacher)
|
||||
@teacher.classrooms << @classroom
|
||||
end
|
||||
|
||||
# create
|
||||
|
||||
test "create enrolls student in classroom" do
|
||||
other_classroom = create(:classroom)
|
||||
sign_in(@teacher)
|
||||
|
||||
assert_difference("ClassroomEnrollment.count") do
|
||||
post classroom_classroom_enrollments_path(other_classroom),
|
||||
params: { classroom_enrollment: { student_id: @student.id, primary: "false" } }
|
||||
end
|
||||
|
||||
assert_redirected_to classroom_path(other_classroom)
|
||||
assert_match @student.username, flash[:notice]
|
||||
assert_match other_classroom.name, flash[:notice]
|
||||
end
|
||||
|
||||
test "create enrolls student as primary" do
|
||||
other_classroom = create(:classroom)
|
||||
sign_in(@teacher)
|
||||
|
||||
post classroom_classroom_enrollments_path(other_classroom),
|
||||
params: { classroom_enrollment: { student_id: @student.id, primary: "true" } }
|
||||
|
||||
enrollment = ClassroomEnrollment.last
|
||||
assert enrollment.primary?
|
||||
assert_redirected_to classroom_path(other_classroom)
|
||||
end
|
||||
|
||||
# destroy
|
||||
|
||||
test "destroy removes enrollment" do
|
||||
enrollment = create(:classroom_enrollment, classroom: @classroom, student: @student)
|
||||
sign_in(@teacher)
|
||||
|
||||
assert_difference("ClassroomEnrollment.count", -1) do
|
||||
delete classroom_classroom_enrollment_path(@classroom, enrollment)
|
||||
end
|
||||
|
||||
assert_redirected_to classroom_path(@classroom)
|
||||
assert_match @student.username, flash[:notice]
|
||||
assert_match @classroom.name, flash[:notice]
|
||||
end
|
||||
|
||||
# unenroll
|
||||
|
||||
test "unenroll sets unenrolled_at on enrollment" do
|
||||
enrollment = create(:classroom_enrollment, classroom: @classroom, student: @student)
|
||||
sign_in(@teacher)
|
||||
|
||||
patch unenroll_classroom_classroom_enrollment_path(@classroom, enrollment)
|
||||
|
||||
assert enrollment.reload.historical?
|
||||
assert_redirected_to classroom_path(@classroom)
|
||||
assert_match @student.username, flash[:notice]
|
||||
end
|
||||
|
||||
# authorization
|
||||
|
||||
test "student cannot create enrollment" do
|
||||
other_classroom = create(:classroom)
|
||||
sign_in(@student)
|
||||
|
||||
post classroom_classroom_enrollments_path(other_classroom),
|
||||
params: { classroom_enrollment: { student_id: @student.id, primary: "false" } }
|
||||
|
||||
assert_equal "You do not have access to this page.", flash[:alert]
|
||||
end
|
||||
|
||||
test "unauthenticated user is redirected to sign in" do
|
||||
post classroom_classroom_enrollments_path(@classroom),
|
||||
params: { classroom_enrollment: { student_id: @student.id, primary: "false" } }
|
||||
|
||||
assert_redirected_to new_user_session_path
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,405 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "test_helper"
|
||||
|
||||
class ClassroomsControllerTest < ActionDispatch::IntegrationTest
|
||||
def setup
|
||||
@admin = create(:admin)
|
||||
@classroom = create(:classroom)
|
||||
@teacher = create(:teacher)
|
||||
@teacher.classrooms << @classroom
|
||||
@student = create(:student, classroom: @classroom)
|
||||
end
|
||||
|
||||
test "index" do
|
||||
sign_in(@admin)
|
||||
get classrooms_path
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "new" do
|
||||
sign_in(@admin)
|
||||
get new_classroom_path
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "create" do
|
||||
sign_in(@admin)
|
||||
school = create(:school)
|
||||
year = create(:year)
|
||||
grade = create(:grade, level: 7)
|
||||
|
||||
params = {
|
||||
classroom: {
|
||||
name: "Test Class",
|
||||
grade_ids: [grade.id],
|
||||
school_id: school.id,
|
||||
year_id: year.id
|
||||
}
|
||||
}
|
||||
|
||||
assert_difference("Classroom.count") do
|
||||
post(classrooms_path, params:)
|
||||
end
|
||||
|
||||
classroom = Classroom.last
|
||||
|
||||
assert_redirected_to classroom_path(classroom)
|
||||
assert_includes classroom.grades, grade
|
||||
assert_equal t("classrooms.create.notice"), flash[:notice]
|
||||
end
|
||||
|
||||
test "create with multiple grades" do
|
||||
sign_in(@admin)
|
||||
school = create(:school)
|
||||
year = create(:year)
|
||||
|
||||
grade1 = create(:grade)
|
||||
grade2 = create(:grade)
|
||||
grade3 = create(:grade)
|
||||
|
||||
params = {
|
||||
classroom: {
|
||||
name: "Combined Class",
|
||||
grade_ids: [grade1.id, grade2.id, grade3.id],
|
||||
school_id: school.id,
|
||||
year_id: year.id
|
||||
}
|
||||
}
|
||||
|
||||
assert_difference("Classroom.count", 1) do
|
||||
assert_difference("ClassroomGrade.count", 3) do
|
||||
post classrooms_path, params: params
|
||||
end
|
||||
end
|
||||
|
||||
classroom = Classroom.last
|
||||
assert_redirected_to classroom_path(classroom)
|
||||
assert_equal [grade1, grade2, grade3].sort, classroom.grades.sort
|
||||
end
|
||||
|
||||
test "show" do
|
||||
sign_in(@admin)
|
||||
get classroom_path(@classroom)
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "edit" do
|
||||
sign_in(@admin)
|
||||
get edit_classroom_path(@classroom)
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "update" do
|
||||
school = create(:school)
|
||||
year = create(:year)
|
||||
grade = create(:grade, level: 6)
|
||||
params = { classroom: { name: "Abc123", grade_ids: [grade.id], school_id: school.id, year_id: year.id } }
|
||||
sign_in(@admin)
|
||||
|
||||
assert_changes "@classroom.reload.updated_at" do
|
||||
patch(classroom_path(@classroom), params:)
|
||||
end
|
||||
|
||||
assert_redirected_to classroom_path(@classroom)
|
||||
assert_equal t("classrooms.update.notice"), flash[:notice]
|
||||
|
||||
@classroom.reload
|
||||
assert_includes @classroom.grades, grade
|
||||
end
|
||||
|
||||
test "update replaces grades correctly" do
|
||||
old_grade = create(:grade)
|
||||
create(:classroom_grade, classroom: @classroom, grade: old_grade)
|
||||
|
||||
new_grade1 = create(:grade)
|
||||
new_grade2 = create(:grade)
|
||||
|
||||
sign_in(@admin)
|
||||
|
||||
params = {
|
||||
classroom: {
|
||||
name: "Updated Name",
|
||||
grade_ids: [new_grade1.id, new_grade2.id]
|
||||
}
|
||||
}
|
||||
|
||||
patch classroom_path(@classroom), params: params
|
||||
@classroom.reload
|
||||
|
||||
assert_equal [new_grade1, new_grade2].sort, @classroom.grades.sort
|
||||
assert_not_includes @classroom.grades, old_grade
|
||||
end
|
||||
|
||||
test "archive classroom" do
|
||||
sign_in(@admin)
|
||||
assert_no_difference("Classroom.count") do
|
||||
patch toggle_archive_admin_classroom_path(@classroom)
|
||||
end
|
||||
assert_redirected_to admin_classroom_path(@classroom)
|
||||
assert @classroom.reload.archived?
|
||||
assert_equal "Classroom has been archived.", flash[:notice]
|
||||
end
|
||||
|
||||
test "activate classroom" do
|
||||
sign_in(@admin)
|
||||
@classroom.update!(archived: true)
|
||||
patch toggle_archive_admin_classroom_path(@classroom)
|
||||
assert_redirected_to admin_classroom_path(@classroom)
|
||||
assert_not @classroom.reload.archived?
|
||||
assert_equal "Classroom has been activated.", flash[:notice]
|
||||
end
|
||||
|
||||
test "admins can see all classrooms in index" do
|
||||
classroom1 = create(:classroom, name: "Class 1")
|
||||
classroom2 = create(:classroom, name: "Class 2")
|
||||
sign_in(@admin)
|
||||
get classrooms_path
|
||||
assert_response :success
|
||||
assert_includes response.body, classroom1.name
|
||||
assert_includes response.body, classroom2.name
|
||||
end
|
||||
|
||||
test "show includes student management for teachers" do
|
||||
create(:portfolio, user: @student)
|
||||
# @teacher.classrooms << @classroom
|
||||
sign_in @teacher
|
||||
get classroom_path(@classroom)
|
||||
assert_response :success
|
||||
assert_includes response.body, @student.username
|
||||
end
|
||||
|
||||
test "teachers can only see their own classroom in index" do
|
||||
classroom1 = create(:classroom, name: "Teacher 1 Class")
|
||||
classroom2 = create(:classroom, name: "Teacher 2 Class")
|
||||
teacher = create(:teacher, classroom: classroom1)
|
||||
teacher.classrooms << classroom1
|
||||
sign_in teacher
|
||||
get classrooms_path
|
||||
assert_response :success
|
||||
assert_includes response.body, classroom1.name
|
||||
assert_not_includes response.body, classroom2.name
|
||||
end
|
||||
|
||||
test "teachers cannot see archived classrooms in index" do
|
||||
classroom1 = create(:classroom, name: "Active Class")
|
||||
classroom2 = create(:classroom, name: "Archived Class", archived: true)
|
||||
teacher = create(:teacher)
|
||||
teacher.classrooms << classroom1
|
||||
teacher.classrooms << classroom2
|
||||
sign_in teacher
|
||||
get classrooms_path
|
||||
assert_response :success
|
||||
assert_includes response.body, classroom1.name
|
||||
assert_not_includes response.body, classroom2.name
|
||||
end
|
||||
|
||||
test "teachers can only access details of their own classroom" do
|
||||
classroom1 = create(:classroom, name: "Teacher 1 Class")
|
||||
classroom2 = create(:classroom, name: "Teacher 2 Class")
|
||||
teacher = create(:teacher)
|
||||
teacher.classrooms << classroom1
|
||||
sign_in teacher
|
||||
|
||||
get classroom_path(classroom1)
|
||||
assert_response :success
|
||||
assert_includes response.body, classroom1.name
|
||||
|
||||
get classroom_path(classroom2)
|
||||
assert_redirected_to root_path
|
||||
end
|
||||
|
||||
test "students cannot see classrooms" do
|
||||
sign_in @student
|
||||
get classrooms_path
|
||||
assert_redirected_to @student.portfolio_path
|
||||
end
|
||||
|
||||
test "students cannot see details of a classroom" do
|
||||
sign_in @student
|
||||
classroom1 = create(:classroom, name: "Teacher 1 Class")
|
||||
get classroom_path(id: classroom1.id)
|
||||
assert_redirected_to @student.portfolio_path
|
||||
end
|
||||
|
||||
test "students cannot create or edit classrooms" do
|
||||
sign_in @student
|
||||
|
||||
get new_classroom_path
|
||||
assert_redirected_to @student.portfolio_path
|
||||
|
||||
get edit_classroom_path(@classroom)
|
||||
assert_redirected_to @student.portfolio_path
|
||||
end
|
||||
|
||||
test "teachers cannot create classrooms" do
|
||||
sign_in @teacher
|
||||
get new_classroom_path
|
||||
assert_redirected_to root_path
|
||||
end
|
||||
|
||||
test "teachers can toggle trading for their own classrooms" do
|
||||
sign_in @teacher
|
||||
assert_not @classroom.trading_enabled
|
||||
patch toggle_trading_classroom_path(@classroom)
|
||||
assert_redirected_to classroom_path(@classroom)
|
||||
assert @classroom.reload.trading_enabled
|
||||
end
|
||||
|
||||
test "teachers cannot edit classrooms" do
|
||||
sign_in @teacher
|
||||
get edit_classroom_path(@classroom)
|
||||
assert_redirected_to root_path
|
||||
end
|
||||
|
||||
test "teachers cannot toggle trading for other teachers' classrooms" do
|
||||
other_classroom = create(:classroom, name: "Other Class")
|
||||
sign_in @teacher
|
||||
patch toggle_trading_classroom_path(other_classroom)
|
||||
assert_redirected_to root_path
|
||||
end
|
||||
|
||||
test "teachers cannot archive classrooms" do
|
||||
sign_in @teacher
|
||||
# Teachers shouldn't be able to archive
|
||||
patch toggle_archive_admin_classroom_path(@classroom)
|
||||
assert_redirected_to root_path
|
||||
end
|
||||
|
||||
# Tests for dropdown functionality
|
||||
test "new action renders form with school and year dropdowns" do
|
||||
school1 = create(:school, name: "Elementary School")
|
||||
school2 = create(:school, name: "High School")
|
||||
year1 = create(:year, name: "2023-2024")
|
||||
year2 = create(:year, name: "2024-2025")
|
||||
|
||||
sign_in(@admin)
|
||||
get new_classroom_path
|
||||
|
||||
assert_response :success
|
||||
assert_select "select[name='classroom[school_id]']" do
|
||||
assert_select "option[value='#{school1.id}']", text: school1.name
|
||||
assert_select "option[value='#{school2.id}']", text: school2.name
|
||||
end
|
||||
assert_select "select[name='classroom[year_id]']" do
|
||||
assert_select "option[value='#{year1.id}']", text: year1.name
|
||||
assert_select "option[value='#{year2.id}']", text: year2.name
|
||||
end
|
||||
end
|
||||
|
||||
test "edit action renders form with school and year dropdowns" do
|
||||
school1 = create(:school, name: "Elementary School")
|
||||
school2 = create(:school, name: "High School")
|
||||
year1 = create(:year, name: "2023-2024")
|
||||
year2 = create(:year, name: "2024-2025")
|
||||
|
||||
sign_in(@admin)
|
||||
get edit_classroom_path(@classroom)
|
||||
|
||||
assert_response :success
|
||||
assert_select "select[name='classroom[school_id]']" do
|
||||
assert_select "option[value='#{school1.id}']", text: school1.name
|
||||
assert_select "option[value='#{school2.id}']", text: school2.name
|
||||
end
|
||||
assert_select "select[name='classroom[year_id]']" do
|
||||
assert_select "option[value='#{year1.id}']", text: year1.name
|
||||
assert_select "option[value='#{year2.id}']", text: year2.name
|
||||
end
|
||||
end
|
||||
|
||||
test "create with new school_year creates quarters and gradebooks" do
|
||||
school = create(:school)
|
||||
year = create(:year)
|
||||
grade = create(:grade)
|
||||
|
||||
sign_in(@admin)
|
||||
params = { classroom: { name: "New Class", grade_ids: [grade.id], school_id: school.id, year_id: year.id } }
|
||||
|
||||
assert_difference("Quarter.count", 4) do
|
||||
assert_difference("GradeBook.count", 4) do
|
||||
post(classrooms_path, params:)
|
||||
end
|
||||
end
|
||||
|
||||
classroom = Classroom.last
|
||||
assert_equal 4, classroom.grade_books.count
|
||||
end
|
||||
|
||||
test "create with existing school_year uses existing quarters for gradebooks" do
|
||||
school = create(:school)
|
||||
year = create(:year)
|
||||
grade = create(:grade)
|
||||
school_year = SchoolYear.create!(school: school, year: year)
|
||||
|
||||
sign_in(@admin)
|
||||
params = { classroom: { name: "New Class", grade_ids: [grade.id], school_id: school.id, year_id: year.id } }
|
||||
|
||||
assert_no_difference("Quarter.count") do
|
||||
assert_difference("GradeBook.count", 4) do
|
||||
post(classrooms_path, params:)
|
||||
end
|
||||
end
|
||||
|
||||
classroom = Classroom.last
|
||||
assert_equal school_year, classroom.school_year
|
||||
assert_equal 4, classroom.grade_books.count
|
||||
end
|
||||
|
||||
test "create with valid school_id and year_id creates classroom with correct school_year" do
|
||||
school = create(:school, name: "Test School")
|
||||
year = create(:year, name: "2024-2025")
|
||||
grade = create(:grade)
|
||||
|
||||
sign_in(@admin)
|
||||
params = { classroom: { name: "Test Class", grade_ids: [grade.id], school_id: school.id, year_id: year.id } }
|
||||
|
||||
assert_difference("Classroom.count") do
|
||||
post(classrooms_path, params:)
|
||||
end
|
||||
|
||||
classroom = Classroom.last
|
||||
assert_equal school, classroom.school
|
||||
assert_equal year, classroom.year
|
||||
end
|
||||
|
||||
test "update with valid school_id and year_id updates classroom school_year" do
|
||||
new_school = create(:school, name: "New School")
|
||||
new_year = create(:year, name: "2025-2026")
|
||||
|
||||
sign_in(@admin)
|
||||
params = { classroom: { name: "Updated Class", school_id: new_school.id, year_id: new_year.id } }
|
||||
|
||||
patch(classroom_path(@classroom), params:)
|
||||
|
||||
@classroom.reload
|
||||
assert_equal new_school, @classroom.school
|
||||
assert_equal new_year, @classroom.year
|
||||
end
|
||||
|
||||
test "edit form shows current school and year selected" do
|
||||
school = create(:school, name: "Current School")
|
||||
year = create(:year, name: "Current Year")
|
||||
school_year = create(:school_year, school: school, year: year)
|
||||
classroom = create(:classroom, school_year: school_year)
|
||||
|
||||
sign_in(@admin)
|
||||
get edit_classroom_path(classroom)
|
||||
|
||||
assert_response :success
|
||||
assert_select "select[name='classroom[school_id]']" do
|
||||
assert_select "option[value='#{school.id}'][selected='selected']", text: school.name
|
||||
end
|
||||
assert_select "select[name='classroom[year_id]']" do
|
||||
assert_select "option[value='#{year.id}'][selected='selected']", text: year.name
|
||||
end
|
||||
end
|
||||
|
||||
test "index page includes link to classroom show page" do
|
||||
sign_in(@admin)
|
||||
classroom = create(:classroom, name: "Linked Class")
|
||||
|
||||
get classrooms_path
|
||||
assert_response :success
|
||||
assert_select "a[href='#{classroom_path(classroom)}']", text: "Linked Class"
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,209 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "test_helper"
|
||||
|
||||
class GradeBooksControllerTest < ActionDispatch::IntegrationTest
|
||||
def setup
|
||||
@classroom = create(:classroom)
|
||||
@teacher = create(:teacher)
|
||||
@teacher.classrooms << @classroom
|
||||
@student = create(:student, classroom: @classroom)
|
||||
@first_quarter = @classroom.school_year.quarters.find_by!(number: 1)
|
||||
@second_quarter = @classroom.school_year.quarters.find_by!(number: 2)
|
||||
@grade_book = @classroom.grade_books.find_by!(quarter: @first_quarter)
|
||||
create(:grade_entry, grade_book: @grade_book, user: @student)
|
||||
end
|
||||
|
||||
test "show" do
|
||||
sign_in(@teacher)
|
||||
get classroom_grade_book_path(@classroom, @grade_book)
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "update" do
|
||||
sign_in(@teacher)
|
||||
entry = @grade_book.grade_entries.first
|
||||
params = {
|
||||
grade_entries: {
|
||||
entry.id => {
|
||||
math_grade: "A",
|
||||
reading_grade: "B",
|
||||
attendance_days: 30
|
||||
}
|
||||
}
|
||||
}
|
||||
patch classroom_grade_book_path(@classroom, @grade_book), params: params
|
||||
assert_redirected_to classroom_grade_book_path(@classroom, @grade_book)
|
||||
|
||||
entry.reload
|
||||
assert_equal "A", entry.math_grade
|
||||
assert_equal "B", entry.reading_grade
|
||||
assert_equal 30, entry.attendance_days
|
||||
end
|
||||
|
||||
test "students cannot access grade book" do
|
||||
sign_in(@student)
|
||||
get classroom_grade_book_path(@classroom, @grade_book)
|
||||
assert_redirected_to @student.portfolio_path
|
||||
end
|
||||
|
||||
test "teachers cannot finalize a grade book" do
|
||||
DistributeEarnings.expects(:execute).never
|
||||
sign_in(@teacher)
|
||||
|
||||
post finalize_classroom_grade_book_path(@classroom, @grade_book)
|
||||
assert_redirected_to root_path
|
||||
@grade_book.reload
|
||||
assert_not @grade_book.verified?
|
||||
end
|
||||
|
||||
test "finalize runs the DistributeFunds service" do
|
||||
DistributeEarnings.expects(:execute).with(@grade_book).once
|
||||
|
||||
sign_in(create(:admin))
|
||||
# Fill out all entries to make the grade book finalizable
|
||||
@grade_book.grade_entries.each do |entry|
|
||||
entry.update!(math_grade: "A", reading_grade: "B", attendance_days: 30)
|
||||
end
|
||||
|
||||
post finalize_classroom_grade_book_path(@classroom, @grade_book)
|
||||
|
||||
assert_redirected_to classroom_grade_book_path(@classroom, @grade_book)
|
||||
@grade_book.reload
|
||||
assert @grade_book.verified?
|
||||
end
|
||||
|
||||
test "finalize grade book with incomplete grade entry and no previous quarter" do
|
||||
sign_in(create(:admin))
|
||||
|
||||
@grade_book.grade_entries.first.update!(
|
||||
math_grade: nil, reading_grade: nil, attendance_days: 30
|
||||
)
|
||||
|
||||
post finalize_classroom_grade_book_path(@classroom, @grade_book)
|
||||
|
||||
assert_redirected_to classroom_grade_book_path(@classroom, @grade_book)
|
||||
assert_equal "Grade book finalized. Funds have been distributed.", flash[:notice]
|
||||
|
||||
@grade_book.reload
|
||||
assert @grade_book.completed?
|
||||
end
|
||||
|
||||
test "finalize grade book with incomplete grade entry (empty string) and no previous quarter" do
|
||||
sign_in(create(:admin))
|
||||
|
||||
@grade_book.grade_entries.first.update!(
|
||||
math_grade: "", reading_grade: "", attendance_days: 30
|
||||
)
|
||||
|
||||
post finalize_classroom_grade_book_path(@classroom, @grade_book)
|
||||
|
||||
assert_redirected_to classroom_grade_book_path(@classroom, @grade_book)
|
||||
assert_equal "Grade book finalized. Funds have been distributed.", flash[:notice]
|
||||
|
||||
@grade_book.reload
|
||||
assert @grade_book.completed?
|
||||
end
|
||||
|
||||
test "finalize grade book with complete grade entry and no previous quarter" do
|
||||
sign_in(create(:admin))
|
||||
|
||||
@grade_book.grade_entries.first.update!(
|
||||
math_grade: "A", reading_grade: "B", attendance_days: 30
|
||||
)
|
||||
|
||||
post finalize_classroom_grade_book_path(@classroom, @grade_book)
|
||||
|
||||
assert_redirected_to classroom_grade_book_path(@classroom, @grade_book)
|
||||
assert_equal "Grade book finalized. Funds have been distributed.", flash[:notice]
|
||||
|
||||
@grade_book.reload
|
||||
assert @grade_book.completed?
|
||||
end
|
||||
|
||||
# Scenario 2: Finalize grade book with previous quarter grade book
|
||||
test "finalize grade book with incomplete grade entry and previous quarter exists" do
|
||||
sign_in(create(:admin))
|
||||
|
||||
# Mark first quarter as completed (acts as previous quarter)
|
||||
@grade_book.update!(status: :completed)
|
||||
@grade_book.grade_entries.first.update!(
|
||||
math_grade: "A", reading_grade: "B", attendance_days: 30
|
||||
)
|
||||
|
||||
# Create new grade book for second quarter with incomplete grades
|
||||
new_grade_book = @classroom.grade_books.find_by!(quarter: @second_quarter)
|
||||
create(
|
||||
:grade_entry, grade_book: new_grade_book, user: @student,
|
||||
math_grade: nil, reading_grade: nil, attendance_days: 30
|
||||
)
|
||||
|
||||
post finalize_classroom_grade_book_path(@classroom, new_grade_book)
|
||||
|
||||
assert_redirected_to classroom_grade_book_path(@classroom, new_grade_book)
|
||||
assert_equal "Grade book finalized. Funds have been distributed.", flash[:notice]
|
||||
|
||||
new_grade_book.reload
|
||||
assert new_grade_book.completed?
|
||||
end
|
||||
|
||||
test "finalize grade book with complete grade entry and previous quarter exists" do
|
||||
sign_in(create(:admin))
|
||||
|
||||
# Mark first quarter as completed (acts as previous quarter)
|
||||
@grade_book.update!(status: :completed)
|
||||
@grade_book.grade_entries.first.update!(
|
||||
math_grade: "B", reading_grade: nil, attendance_days: 30
|
||||
)
|
||||
|
||||
# Create new grade book for second quarter with complete grades
|
||||
new_grade_book = @classroom.grade_books.find_by!(quarter: @second_quarter)
|
||||
create(
|
||||
:grade_entry, grade_book: new_grade_book, user: @student,
|
||||
math_grade: "A", reading_grade: "A", attendance_days: 30
|
||||
)
|
||||
|
||||
post finalize_classroom_grade_book_path(@classroom, new_grade_book)
|
||||
|
||||
assert_redirected_to classroom_grade_book_path(@classroom, new_grade_book)
|
||||
assert_equal "Grade book finalized. Funds have been distributed.", flash[:notice]
|
||||
|
||||
new_grade_book.reload
|
||||
assert new_grade_book.completed?
|
||||
end
|
||||
|
||||
test "finalize grade book with complete grade entry and previous quarter exists (empty string)" do
|
||||
sign_in(create(:admin))
|
||||
|
||||
# Mark first quarter as completed (acts as previous quarter)
|
||||
@grade_book.update!(status: :completed)
|
||||
@grade_book.grade_entries.first.update!(
|
||||
math_grade: "", reading_grade: "", attendance_days: 30
|
||||
)
|
||||
|
||||
# Create new grade book for second quarter with complete grades
|
||||
new_grade_book = @classroom.grade_books.find_by!(quarter: @second_quarter)
|
||||
create(
|
||||
:grade_entry, grade_book: new_grade_book, user: @student,
|
||||
math_grade: "A", reading_grade: "A", attendance_days: 30
|
||||
)
|
||||
|
||||
post finalize_classroom_grade_book_path(@classroom, new_grade_book)
|
||||
|
||||
assert_redirected_to classroom_grade_book_path(@classroom, new_grade_book)
|
||||
assert_equal "Grade book finalized. Funds have been distributed.", flash[:notice]
|
||||
|
||||
new_grade_book.reload
|
||||
assert new_grade_book.completed?
|
||||
end
|
||||
|
||||
test "does not finalize already completed grade book" do
|
||||
sign_in(create(:admin))
|
||||
@grade_book.update!(status: :completed)
|
||||
|
||||
post finalize_classroom_grade_book_path(@classroom, @grade_book)
|
||||
|
||||
assert_redirected_to classroom_grade_book_path(@classroom, @grade_book)
|
||||
assert_equal "Cannot finalize because it's already completed.", flash[:alert]
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,130 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "test_helper"
|
||||
|
||||
class HomeControllerTest < ActionDispatch::IntegrationTest
|
||||
test "should redirect to sign in when not authenticated" do
|
||||
get root_url
|
||||
assert_redirected_to new_user_session_url
|
||||
end
|
||||
|
||||
test "should get index when authenticated" do
|
||||
sign_in create(:student)
|
||||
get root_url
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "index page has expected content when authenticated" do
|
||||
sign_in create(:student)
|
||||
|
||||
get root_url
|
||||
|
||||
assert_response :success
|
||||
assert_select "title", /StocksInTheFuture/i
|
||||
end
|
||||
|
||||
test "index responds with HTML when authenticated" do
|
||||
sign_in create(:student)
|
||||
|
||||
get root_url
|
||||
assert_response :success
|
||||
assert_equal "text/html; charset=utf-8", response.content_type
|
||||
end
|
||||
|
||||
test "should show current announcement on home page" do
|
||||
sign_in create(:student)
|
||||
|
||||
announcement = Announcement.create!(
|
||||
title: "Welcome Students!",
|
||||
content: "This is an important announcement for all students.",
|
||||
featured: true
|
||||
)
|
||||
|
||||
get root_url
|
||||
assert_response :success
|
||||
assert_select "h3", text: "Welcome Students!"
|
||||
assert_select "a[href=?]", announcement_path(announcement), text: "Read More", count: 0
|
||||
end
|
||||
|
||||
test "should show featured announcement when multiple exist" do
|
||||
sign_in create(:student)
|
||||
|
||||
Announcement.create!(
|
||||
title: "Old Announcement",
|
||||
content: "This is old news.",
|
||||
created_at: 2.days.ago,
|
||||
featured: false
|
||||
)
|
||||
|
||||
Announcement.create!(
|
||||
title: "Featured News",
|
||||
content: "This is the featured announcement.",
|
||||
featured: true
|
||||
)
|
||||
|
||||
get root_url
|
||||
assert_response :success
|
||||
assert_select "h3", text: "Featured News"
|
||||
assert_no_match(/Old Announcement/, response.body)
|
||||
end
|
||||
|
||||
test "should show no announcements message when none exist" do
|
||||
sign_in create(:student)
|
||||
|
||||
get root_url
|
||||
assert_response :success
|
||||
assert_select "p", text: "No announcements yet."
|
||||
end
|
||||
|
||||
test "should show full announcement content in scrollable box" do
|
||||
sign_in create(:student)
|
||||
|
||||
long_content = "This is a long announcement content that is displayed in full in a scrollable box. " * 5
|
||||
Announcement.create!(
|
||||
title: "Long Announcement",
|
||||
content: long_content,
|
||||
featured: true
|
||||
)
|
||||
|
||||
get root_url
|
||||
assert_response :success
|
||||
assert_select "h3", text: "Long Announcement"
|
||||
assert_select "a", text: "Read More", count: 0
|
||||
end
|
||||
|
||||
test "should show positive balance message when student has money" do
|
||||
student = create(:student)
|
||||
portfolio = student.portfolio
|
||||
|
||||
create(:portfolio_transaction, :deposit, portfolio: portfolio, amount_cents: 5000) # $50.00
|
||||
|
||||
sign_in student
|
||||
get root_url
|
||||
|
||||
assert_response :success
|
||||
assert_select "span", text: /You have.*\$50\.00.*to invest! Lets Get Trading!/
|
||||
assert_select "img[alt='Party popper celebration']"
|
||||
end
|
||||
|
||||
test "should show no earnings message when student has zero balance" do
|
||||
student = create(:student)
|
||||
sign_in student
|
||||
|
||||
get root_url
|
||||
|
||||
assert_response :success
|
||||
assert_select "span", text: "Sorry, you don't have any earnings to invest yet"
|
||||
assert_no_match(/to invest! Lets Get Trading!/, response.body)
|
||||
end
|
||||
|
||||
test "should not show balance message for non-student users" do
|
||||
teacher = create(:teacher)
|
||||
sign_in teacher
|
||||
|
||||
get root_url
|
||||
|
||||
assert_response :success
|
||||
assert_no_match(/You have.*to invest/, response.body)
|
||||
assert_no_match(/Sorry, you don't have any earnings/, response.body)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,169 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "test_helper"
|
||||
|
||||
class OrdersControllerTest < ActionDispatch::IntegrationTest
|
||||
test "index" do
|
||||
user = create(:teacher)
|
||||
sign_in(user)
|
||||
|
||||
get orders_path
|
||||
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "index as guest" do
|
||||
get orders_path
|
||||
|
||||
assert_redirected_to new_user_session_path
|
||||
end
|
||||
|
||||
test "new" do
|
||||
user = create(:student)
|
||||
stock = create(:stock)
|
||||
sign_in(user)
|
||||
|
||||
get new_order_path(stock_id: stock.id, transaction_type: "buy")
|
||||
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "create" do
|
||||
student = create(:student)
|
||||
student.portfolio.portfolio_transactions.create!(amount_cents: 10_000, transaction_type: :deposit) # $100
|
||||
stock = create(:stock, price_cents: 1_000) # $10 per share
|
||||
params = { order: { user_id: student.id, stock_id: stock.id, shares: 1, action: "buy" } }
|
||||
sign_in(student)
|
||||
|
||||
assert_difference("Order.count", 1) do
|
||||
post(orders_path, params:)
|
||||
end
|
||||
|
||||
assert_redirected_to orders_path
|
||||
end
|
||||
|
||||
test "create with invalid shares handles validation errors" do
|
||||
student = create(:student)
|
||||
student.portfolio.portfolio_transactions.create!(amount_cents: 10_000, transaction_type: :deposit)
|
||||
stock = create(:stock, price_cents: 1_000)
|
||||
sign_in(student)
|
||||
|
||||
[nil, "", "not_a_number"].each do |invalid_shares|
|
||||
buy_params = { order: { stock_id: stock.id, shares: invalid_shares, action: :buy } }
|
||||
sell_params = { order: { stock_id: stock.id, shares: invalid_shares, action: :sell } }
|
||||
|
||||
assert_no_difference("Order.count") do
|
||||
assert_nothing_raised do
|
||||
post(orders_path, params: buy_params)
|
||||
assert_response :unprocessable_content
|
||||
|
||||
post(orders_path, params: sell_params)
|
||||
assert_response :unprocessable_content
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
test "edit" do
|
||||
user = create(:student)
|
||||
stock = create(:stock)
|
||||
create(:portfolio_stock, portfolio: user.portfolio, stock: stock, shares: 10)
|
||||
order = create(:order, action: :sell, user: user, stock: stock, shares: 1)
|
||||
sign_in(order.user)
|
||||
|
||||
get edit_order_path(order)
|
||||
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "update" do
|
||||
user = create(:student)
|
||||
stock = create(:stock)
|
||||
create(:portfolio_stock, portfolio: user.portfolio, stock: stock, shares: 10)
|
||||
params = { order: { shares: 3 } }
|
||||
order = create(:order, :pending, action: :sell, user: user, stock: stock, shares: 1)
|
||||
sign_in(user)
|
||||
|
||||
assert_changes "order.reload.updated_at" do
|
||||
patch(order_path(order), params:)
|
||||
end
|
||||
|
||||
assert_redirected_to orders_path
|
||||
assert order.shares, 3
|
||||
end
|
||||
|
||||
test "update with invalid shares handles validation errors" do
|
||||
user = create(:student)
|
||||
stock = create(:stock, price_cents: 1_000)
|
||||
create(:portfolio_stock, portfolio: user.portfolio, stock: stock, shares: 10)
|
||||
order = create(:order, :pending, action: :sell, user: user, stock: stock, shares: 5)
|
||||
sign_in(user)
|
||||
|
||||
[nil, "", "not_a_number", -1, 0].each do |invalid_shares|
|
||||
params = { order: { shares: invalid_shares } }
|
||||
|
||||
assert_no_changes "order.reload.shares" do
|
||||
assert_nothing_raised do
|
||||
patch(order_path(order), params: params)
|
||||
assert_response :unprocessable_content
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
test "cancel" do
|
||||
user = create(:student)
|
||||
stock = create(:stock)
|
||||
create(:portfolio_stock, portfolio: user.portfolio, stock: stock, shares: 10)
|
||||
order = create(:order, :pending, action: :sell, user: user, stock: stock, shares: 1)
|
||||
sign_in(order.user)
|
||||
|
||||
assert_difference("Order.pending.count", -1) do
|
||||
assert_difference("Order.canceled.count", 1) do
|
||||
patch(cancel_order_path(order))
|
||||
end
|
||||
end
|
||||
|
||||
assert_redirected_to orders_path
|
||||
assert_equal "Order was successfully canceled", flash[:notice]
|
||||
end
|
||||
|
||||
test "cancel route exists" do
|
||||
assert_routing(
|
||||
{ path: "orders/1/cancel", method: "patch" },
|
||||
{ controller: "orders", action: "cancel", id: "1" }
|
||||
)
|
||||
end
|
||||
|
||||
test "cancel with unauthorized user" do
|
||||
user = create(:student)
|
||||
stock = create(:stock)
|
||||
create(:portfolio_stock, portfolio: user.portfolio, stock: stock, shares: 10)
|
||||
order = create(:order, :pending, action: :sell, user: user, stock: stock, shares: 1)
|
||||
unauthorized_user = create(:student)
|
||||
|
||||
sign_in(unauthorized_user)
|
||||
|
||||
assert_no_difference("Order.count") do
|
||||
patch(cancel_order_path(order))
|
||||
end
|
||||
|
||||
assert_response :redirect
|
||||
assert_equal "You do not have access to this page.", flash[:alert]
|
||||
end
|
||||
|
||||
test "cancel with non-pending order" do
|
||||
user = create(:student)
|
||||
stock = create(:stock)
|
||||
create(:portfolio_stock, portfolio: user.portfolio, stock: stock, shares: 10)
|
||||
order = create(:order, :completed, action: :sell, user: user, stock: stock, shares: 1)
|
||||
sign_in(order.user)
|
||||
|
||||
assert_no_difference("Order.count") do
|
||||
patch(cancel_order_path(order))
|
||||
end
|
||||
|
||||
assert_response :redirect
|
||||
assert_equal "Only pending orders can be canceled", flash[:alert]
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,64 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "test_helper"
|
||||
|
||||
class PortfoliosControllerTest < ActionDispatch::IntegrationTest
|
||||
test "show with empty portfolio" do
|
||||
portfolio = create(:portfolio)
|
||||
sign_in(portfolio.user)
|
||||
|
||||
get portfolio_path(portfolio)
|
||||
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "show with portfolio positions renders" do
|
||||
portfolio = create(:portfolio)
|
||||
stock = create(:stock, price_cents: 15_000, yesterday_price_cents: 14_500)
|
||||
create(:portfolio_stock, portfolio: portfolio, stock: stock, shares: 10)
|
||||
|
||||
sign_in(portfolio.user)
|
||||
|
||||
get portfolio_path(portfolio)
|
||||
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "trade button links to trading floor" do
|
||||
portfolio = create(:portfolio)
|
||||
stock = create(:stock)
|
||||
create(:portfolio_stock, portfolio: portfolio, stock: stock, shares: 5)
|
||||
|
||||
sign_in(portfolio.user)
|
||||
|
||||
get portfolio_path(portfolio)
|
||||
|
||||
assert_response :success
|
||||
assert_select "a[href='#{stocks_path}']", text: "Trade"
|
||||
end
|
||||
|
||||
test "show displays empty state when less than 2 snapshots" do
|
||||
portfolio = create(:portfolio)
|
||||
create(:portfolio_snapshot, portfolio: portfolio, date: Date.current, worth_cents: 10_000)
|
||||
|
||||
sign_in(portfolio.user)
|
||||
|
||||
get portfolio_path(portfolio)
|
||||
|
||||
assert_response :success
|
||||
assert_select ".text-gray-600", text: "Not Enough Data"
|
||||
end
|
||||
|
||||
test "show displays chart when 2 or more snapshots exist" do
|
||||
portfolio = create(:portfolio)
|
||||
create(:portfolio_snapshot, portfolio: portfolio, date: 1.month.ago.to_date, worth_cents: 10_000)
|
||||
create(:portfolio_snapshot, portfolio: portfolio, date: Date.current, worth_cents: 15_000)
|
||||
|
||||
sign_in(portfolio.user)
|
||||
|
||||
get portfolio_path(portfolio)
|
||||
|
||||
assert_response :success
|
||||
assert_select "canvas[data-controller='portfolio-chart']"
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,63 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "test_helper"
|
||||
|
||||
class SchoolsControllerTest < ActionDispatch::IntegrationTest
|
||||
setup do
|
||||
@school = create(:school)
|
||||
@admin_user = create(:admin)
|
||||
sign_in @admin_user
|
||||
end
|
||||
|
||||
test "should get index" do
|
||||
get admin_schools_url
|
||||
assert_response :success
|
||||
assert_select "title", /Admin/i
|
||||
end
|
||||
|
||||
test "should get new" do
|
||||
get new_admin_school_url
|
||||
assert_response :success
|
||||
assert_select "form"
|
||||
end
|
||||
|
||||
test "should create school" do
|
||||
assert_difference("School.count") do
|
||||
post admin_schools_url, params: { school: { name: "New Test School" } }
|
||||
end
|
||||
assert_redirected_to admin_school_url(School.last)
|
||||
assert_equal "New Test School", School.last.name
|
||||
end
|
||||
|
||||
test "should show school" do
|
||||
get admin_school_url(@school)
|
||||
assert_response :success
|
||||
assert_select "h2", /#{@school.name}/
|
||||
end
|
||||
|
||||
test "should get edit" do
|
||||
get edit_admin_school_url(@school)
|
||||
assert_response :success
|
||||
assert_select "form"
|
||||
end
|
||||
|
||||
test "should update school" do
|
||||
patch admin_school_url(@school), params: { school: { name: "Updated School Name" } }
|
||||
assert_redirected_to admin_school_url(@school)
|
||||
@school.reload
|
||||
assert_equal "Updated School Name", @school.name
|
||||
end
|
||||
|
||||
test "should destroy school" do
|
||||
assert_difference("School.count", -1) do
|
||||
delete admin_school_url(@school)
|
||||
end
|
||||
assert_redirected_to admin_schools_url
|
||||
end
|
||||
|
||||
test "requires authentication" do
|
||||
sign_out @admin_user
|
||||
get admin_schools_url
|
||||
assert_redirected_to new_user_session_url
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,35 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "test_helper"
|
||||
|
||||
class StocksControllerTest < ActionDispatch::IntegrationTest
|
||||
test "should get index" do
|
||||
admin = create(:admin)
|
||||
sign_in admin
|
||||
|
||||
get stocks_url
|
||||
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "should get show" do
|
||||
stock = create(:stock)
|
||||
admin = create(:admin)
|
||||
sign_in admin
|
||||
|
||||
get stock_url(stock)
|
||||
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "trade stock button links to trading floor for students" do
|
||||
stock = create(:stock)
|
||||
student = create(:student)
|
||||
create(:portfolio, user: student)
|
||||
sign_in student
|
||||
|
||||
get stock_url(stock)
|
||||
|
||||
assert_select "a[href='#{stocks_path}']", text: "Trade Stock"
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,131 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "test_helper"
|
||||
|
||||
class StudentsControllerTest < ActionDispatch::IntegrationTest
|
||||
setup do
|
||||
@classroom = create(:classroom)
|
||||
@teacher = create(:teacher, classroom: @classroom)
|
||||
@student = create(:student, classroom: @classroom)
|
||||
@other_classroom = create(:classroom)
|
||||
@other_student = create(:student, classroom: @other_classroom)
|
||||
end
|
||||
|
||||
test "students cannot access student management actions" do
|
||||
sign_in @student
|
||||
|
||||
get new_classroom_student_path(@classroom)
|
||||
assert_redirected_to @student.portfolio_path
|
||||
|
||||
get edit_classroom_student_path(@classroom, @student)
|
||||
assert_redirected_to @student.portfolio_path
|
||||
|
||||
patch classroom_student_path(@classroom, @student), params: { student: { username: "hacked" } }
|
||||
assert_redirected_to @student.portfolio_path
|
||||
|
||||
assert_no_difference("User.kept.count") do
|
||||
delete classroom_student_path(@classroom, @student)
|
||||
end
|
||||
assert_redirected_to @student.portfolio_path
|
||||
end
|
||||
|
||||
test "teacher can create student in their classroom" do
|
||||
sign_in @teacher
|
||||
|
||||
assert_difference("User.count") do
|
||||
assert_difference("Student.count") do
|
||||
post classroom_students_path(@classroom), params: {
|
||||
student: {
|
||||
username: "newstudent",
|
||||
email: "newstudent@example.com"
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
student = Student.last
|
||||
assert_equal @classroom, student.classroom
|
||||
assert_not_nil student.encrypted_password
|
||||
assert_redirected_to classroom_path(@classroom)
|
||||
assert_match(/created successfully/, flash[:notice])
|
||||
end
|
||||
|
||||
test "student creation creates portfolio automatically" do
|
||||
sign_in @teacher
|
||||
|
||||
assert_difference("Portfolio.count") do
|
||||
post classroom_students_path(@classroom), params: {
|
||||
student: {
|
||||
username: "newstudent",
|
||||
email: "newstudent@example.com"
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
student = Student.last
|
||||
assert_not_nil student.portfolio
|
||||
assert_equal 0, student.portfolio.calculate_total_value
|
||||
end
|
||||
|
||||
test "student creation generates memorable password" do
|
||||
sign_in @teacher
|
||||
|
||||
post classroom_students_path(@classroom), params: {
|
||||
student: {
|
||||
username: "newstudent",
|
||||
email: "newstudent@example.com"
|
||||
}
|
||||
}
|
||||
|
||||
assert_match(/password:/, flash[:notice].downcase)
|
||||
end
|
||||
|
||||
test "teacher can update student in their classroom" do
|
||||
sign_in @teacher
|
||||
|
||||
patch classroom_student_path(@classroom, @student), params: {
|
||||
student: {
|
||||
username: "updatedname",
|
||||
email: "updated@example.com"
|
||||
}
|
||||
}
|
||||
|
||||
@student.reload
|
||||
assert_equal "updatedname", @student.username
|
||||
assert_equal "updated@example.com", @student.email
|
||||
assert_redirected_to classroom_path(@classroom)
|
||||
end
|
||||
|
||||
test "teacher can delete student from their classroom" do
|
||||
sign_in @teacher
|
||||
|
||||
assert_difference("User.kept.count", -1) do
|
||||
delete classroom_student_path(@classroom, @student)
|
||||
end
|
||||
|
||||
assert_redirected_to classroom_path(@classroom)
|
||||
assert_match(/deleted successfully/, flash[:notice])
|
||||
assert @student.reload.discarded?
|
||||
end
|
||||
|
||||
test "teacher can reset student password" do
|
||||
sign_in @teacher
|
||||
original_password = @student.encrypted_password
|
||||
|
||||
patch reset_password_classroom_student_path(@classroom, @student)
|
||||
|
||||
@student.reload
|
||||
assert_not_equal original_password, @student.encrypted_password
|
||||
assert_redirected_to classroom_path(@classroom)
|
||||
assert_match(/password reset/i, flash[:notice])
|
||||
assert_match(/new password:/i, flash[:notice])
|
||||
end
|
||||
|
||||
test "password reset generates memorable password" do
|
||||
sign_in @teacher
|
||||
|
||||
patch reset_password_classroom_student_path(@classroom, @student)
|
||||
|
||||
assert_match(/new password: \w+\d+/i, flash[:notice])
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,176 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "test_helper"
|
||||
|
||||
class TeachersControllerTest < ActionDispatch::IntegrationTest
|
||||
test "index" do
|
||||
admin = create(:admin)
|
||||
sign_in(admin)
|
||||
|
||||
get admin_teachers_path
|
||||
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "new" do
|
||||
admin = create(:admin)
|
||||
sign_in(admin)
|
||||
|
||||
get new_admin_teacher_path
|
||||
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "show" do
|
||||
teacher = create(:teacher)
|
||||
admin = create(:admin)
|
||||
sign_in(admin)
|
||||
|
||||
get admin_teacher_path(teacher)
|
||||
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "edit" do
|
||||
teacher = create(:teacher)
|
||||
admin = create(:admin)
|
||||
sign_in(admin)
|
||||
|
||||
get edit_admin_teacher_path(teacher)
|
||||
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "update" do
|
||||
teacher = create(:teacher)
|
||||
admin = create(:admin)
|
||||
sign_in(admin)
|
||||
|
||||
params = { teacher: { name: "Jane Smith" } }
|
||||
assert_changes "teacher.reload.updated_at" do
|
||||
patch admin_teacher_path(teacher), params: params
|
||||
end
|
||||
|
||||
assert_redirected_to admin_teacher_path(teacher)
|
||||
end
|
||||
|
||||
test "create creates a teacher, associates classroom, sends reset email, and redirects with notice" do
|
||||
admin = create(:admin)
|
||||
classroom = create(:classroom)
|
||||
sign_in(admin)
|
||||
ActionMailer::Base.deliveries.clear
|
||||
|
||||
params = {
|
||||
teacher: {
|
||||
email: "t1@example.com",
|
||||
username: "teacher1",
|
||||
classroom_ids: [classroom.id]
|
||||
}
|
||||
}
|
||||
|
||||
assert_difference %w[Teacher.count ActionMailer::Base.deliveries.size], 1 do
|
||||
post admin_teachers_path, params: params
|
||||
end
|
||||
|
||||
teacher = Teacher.find_by!(email: "t1@example.com")
|
||||
assert_redirected_to admin_teacher_path(teacher)
|
||||
assert_not_nil flash[:notice]
|
||||
assert_includes teacher.classrooms, classroom
|
||||
|
||||
teacher.reload
|
||||
|
||||
assert_not_nil teacher.reset_password_sent_at, "reset_password_instructions should be sent"
|
||||
assert teacher.encrypted_password.present?, "temp password should be set"
|
||||
end
|
||||
|
||||
test "create with no classroom id still creates teacher, and does not associate classroom" do
|
||||
admin = create(:admin)
|
||||
sign_in(admin)
|
||||
ActionMailer::Base.deliveries.clear
|
||||
|
||||
params = {
|
||||
teacher: {
|
||||
email: "t2@example.com",
|
||||
username: "teacher2"
|
||||
}
|
||||
}
|
||||
|
||||
assert_difference %w[Teacher.count ActionMailer::Base.deliveries.size], 1 do
|
||||
post admin_teachers_path, params: params
|
||||
end
|
||||
|
||||
teacher = Teacher.find_by!(email: "t2@example.com")
|
||||
assert_redirected_to admin_teacher_path(teacher)
|
||||
assert_equal 0, teacher.classrooms.count, "no classroom should be associated"
|
||||
end
|
||||
|
||||
test "create sends email with password reset link" do
|
||||
admin = create(:admin)
|
||||
sign_in(admin)
|
||||
|
||||
params = {
|
||||
teacher: {
|
||||
email: "newteacher@example.com",
|
||||
username: "newteacher"
|
||||
}
|
||||
}
|
||||
|
||||
emails = capture_emails do
|
||||
post admin_teachers_path, params: params
|
||||
end
|
||||
|
||||
assert_equal 1, emails.size
|
||||
email = emails.first
|
||||
assert_equal ["newteacher@example.com"], email.to
|
||||
assert_match(/reset_password_token=/, email.body.encoded)
|
||||
assert_match(/Change my password/, email.body.encoded)
|
||||
end
|
||||
|
||||
test "teacher can set password via reset link from creation email" do
|
||||
admin = create(:admin)
|
||||
sign_in(admin)
|
||||
ActionMailer::Base.deliveries.clear
|
||||
|
||||
post admin_teachers_path, params: {
|
||||
teacher: { email: "resettest@example.com", username: "resettest" }
|
||||
}
|
||||
|
||||
email = ActionMailer::Base.deliveries.last
|
||||
token = email.body.encoded.match(/reset_password_token=([^"]+)/)[1]
|
||||
|
||||
sign_out(admin)
|
||||
|
||||
# Teacher follows the link from the email
|
||||
get edit_user_password_path(reset_password_token: token)
|
||||
assert_response :success
|
||||
|
||||
# Teacher sets a new password
|
||||
put user_password_path, params: {
|
||||
user: {
|
||||
reset_password_token: token,
|
||||
password: "newpassword123",
|
||||
password_confirmation: "newpassword123"
|
||||
}
|
||||
}
|
||||
|
||||
teacher = Teacher.find_by!(email: "resettest@example.com")
|
||||
assert teacher.valid_password?("newpassword123")
|
||||
end
|
||||
|
||||
test "create with invalid params renders unprocessable_entity and does not create teacher" do
|
||||
admin = create(:admin)
|
||||
sign_in(admin)
|
||||
|
||||
params = {
|
||||
teacher: {
|
||||
email: ""
|
||||
}
|
||||
}
|
||||
|
||||
assert_no_difference "Teacher.count" do
|
||||
post admin_teachers_path, params: params
|
||||
end
|
||||
|
||||
assert_response :unprocessable_entity
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user