chore: init commit
in worker.../repo/GITFOLDER.zip is the .git folder.
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "application_system_test_case"
|
||||
|
||||
module Admin
|
||||
class AdminNavigationTest < ApplicationSystemTestCase
|
||||
setup do
|
||||
@admin = create(:admin)
|
||||
sign_in @admin
|
||||
end
|
||||
|
||||
test "admin navigation shows links" do
|
||||
visit admin_root_path
|
||||
|
||||
assert_link "School Years"
|
||||
assert_link "Announcements"
|
||||
assert_link "Classrooms"
|
||||
assert_link "Schools"
|
||||
assert_link "Stocks"
|
||||
assert_link "Students"
|
||||
assert_link "Teachers"
|
||||
assert_link "Users"
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,79 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "application_system_test_case"
|
||||
|
||||
module Admin
|
||||
class AnnouncementsTest < ApplicationSystemTestCase
|
||||
setup do
|
||||
@admin = create(:admin)
|
||||
sign_in @admin
|
||||
@announcement = Announcement.create!(
|
||||
title: "Test Announcement",
|
||||
content: "Test content"
|
||||
)
|
||||
end
|
||||
|
||||
# Skipping - Trix/ActionText editor not working in system tests, will fix in follow-up PR
|
||||
# test "admin can create announcements through Administrate" do
|
||||
# visit admin_announcements_url
|
||||
# assert_text "Announcements"
|
||||
|
||||
# click_on "New Announcement"
|
||||
|
||||
# fill_in "Title", with: "New Admin Announcement"
|
||||
# # Fill in the rich text area by finding the hidden input
|
||||
# fill_in "announcement_content", with: "Admin created this announcement"
|
||||
|
||||
# assert_difference("Announcement.count", 1) do
|
||||
# click_on "Create Announcement"
|
||||
|
||||
# assert_text "Announcement was successfully created"
|
||||
# end
|
||||
|
||||
# assert_text "New Admin Announcement"
|
||||
# end
|
||||
|
||||
# Skipping - Trix/ActionText editor not working in system tests, will fix in follow-up PR
|
||||
# test "admin can edit announcements" do
|
||||
# visit admin_announcement_url(@announcement)
|
||||
# click_on "Edit"
|
||||
|
||||
# fill_in "Title", with: "Updated by Admin"
|
||||
# # Fill in the rich text area by finding the hidden input
|
||||
# fill_in "announcement_content", with: "Updated content by admin"
|
||||
|
||||
# assert_no_difference("Announcement.count") do
|
||||
# click_on "Update Announcement"
|
||||
|
||||
# assert_text "Announcement was successfully updated"
|
||||
# end
|
||||
|
||||
# assert_text "Updated by Admin"
|
||||
# end
|
||||
|
||||
test "admin can delete announcements" do
|
||||
visit admin_announcements_url
|
||||
assert_text @announcement.title
|
||||
|
||||
visit admin_announcement_url(@announcement)
|
||||
|
||||
assert_difference("Announcement.count", -1) do
|
||||
accept_confirm { click_on "Delete", match: :first }
|
||||
|
||||
assert_text "Announcement deleted successfully."
|
||||
end
|
||||
end
|
||||
|
||||
test "non-admin cannot access admin announcements" do
|
||||
sign_out @admin
|
||||
teacher = create(:teacher)
|
||||
sign_in teacher
|
||||
|
||||
visit admin_announcements_url
|
||||
# Should be redirected away from admin area
|
||||
assert_current_path root_path
|
||||
# Verify we're on the home page, not the admin interface
|
||||
assert_text "WELCOME TO YOUR FINANCIAL JOURNEY!"
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,45 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "application_system_test_case"
|
||||
|
||||
module Admin
|
||||
class ClassroomsTest < ApplicationSystemTestCase
|
||||
setup do
|
||||
@admin = create(:admin)
|
||||
sign_in @admin
|
||||
@classroom = create(:classroom)
|
||||
end
|
||||
|
||||
test "admin can archive classroom" do
|
||||
visit admin_classroom_url(@classroom)
|
||||
assert_text @classroom.name
|
||||
|
||||
accept_confirm do
|
||||
click_on "Archive"
|
||||
end
|
||||
|
||||
assert_text "Classroom has been archived"
|
||||
assert_current_path admin_classroom_path(@classroom)
|
||||
end
|
||||
|
||||
test "admin can activate archived classroom" do
|
||||
@classroom.update!(archived: true)
|
||||
|
||||
visit admin_classroom_url(@classroom)
|
||||
assert_text @classroom.name
|
||||
|
||||
accept_confirm do
|
||||
click_on "Activate"
|
||||
end
|
||||
|
||||
assert_text "Classroom has been activated"
|
||||
assert_current_path admin_classroom_path(@classroom)
|
||||
end
|
||||
|
||||
test "destroy button does not appear" do
|
||||
visit admin_classroom_url(@classroom)
|
||||
|
||||
assert_no_button "Destroy"
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,699 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "application_system_test_case"
|
||||
|
||||
class AdminEarningsDistributionTest < ApplicationSystemTestCase
|
||||
test "admin finalizes grade book and distributes earnings" do
|
||||
admin = create(:admin)
|
||||
school_year = create(:school_year)
|
||||
quarter = school_year.quarters.find_by!(number: 1)
|
||||
classroom = create(:classroom)
|
||||
student = create(:student, :with_portfolio, classroom: classroom)
|
||||
student.reload
|
||||
portfolio = student.portfolio
|
||||
|
||||
initial_balance = (portfolio.cash_balance * 100).to_i
|
||||
|
||||
grade_book = create(
|
||||
:grade_book,
|
||||
quarter: quarter,
|
||||
classroom: classroom,
|
||||
status: :verified
|
||||
)
|
||||
|
||||
grade_entry = create(
|
||||
:grade_entry,
|
||||
grade_book: grade_book,
|
||||
user: student,
|
||||
attendance_days: 20,
|
||||
is_perfect_attendance: false,
|
||||
math_grade: "A",
|
||||
reading_grade: "B"
|
||||
)
|
||||
|
||||
assert grade_book.verified?, "Grade book should be verified before finalization"
|
||||
assert_equal 0, portfolio.portfolio_transactions.count, "Student should have no transactions initially"
|
||||
|
||||
sign_in(admin)
|
||||
visit classroom_grade_book_path(classroom, grade_book)
|
||||
|
||||
assert_difference -> { PortfolioTransaction.count } => 3,
|
||||
-> { GradeBook.completed.count } => 1 do
|
||||
accept_confirm do
|
||||
click_on "Finalize Grades"
|
||||
end
|
||||
|
||||
assert_text "Grade book finalized. Funds have been distributed."
|
||||
end
|
||||
|
||||
grade_book.reload
|
||||
assert grade_book.completed?, "Grade book status should be completed"
|
||||
|
||||
portfolio.reload
|
||||
transactions = portfolio.portfolio_transactions.reload
|
||||
assert_equal 3, transactions.count, "Should have 3 transactions (attendance, math, reading)"
|
||||
|
||||
expected_attendance = grade_entry.earnings_for_attendance
|
||||
expected_math = GradeEntry::EARNINGS_FOR_A_GRADE
|
||||
expected_reading = GradeEntry::EARNINGS_FOR_B_GRADE
|
||||
expected_total = expected_attendance + expected_math + expected_reading
|
||||
|
||||
current_balance = (portfolio.cash_balance * 100).to_i
|
||||
assert current_balance > initial_balance, "Cash balance should increase"
|
||||
assert_equal initial_balance + expected_total, current_balance
|
||||
|
||||
assert transactions.find(&:attendance_earnings?).present?
|
||||
assert transactions.find(&:math_earnings?).present?
|
||||
assert transactions.find(&:reading_earnings?).present?
|
||||
|
||||
sign_out(admin)
|
||||
end
|
||||
|
||||
test "attendance earnings calculated correctly" do
|
||||
admin = create(:admin)
|
||||
school_year = create(:school_year)
|
||||
quarter = school_year.quarters.find_by!(number: 1)
|
||||
classroom = create(:classroom)
|
||||
student = create(:student, :with_portfolio, classroom: classroom)
|
||||
student.reload
|
||||
portfolio = student.portfolio
|
||||
|
||||
attendance_days = 25
|
||||
grade_book = create(
|
||||
:grade_book,
|
||||
quarter: quarter,
|
||||
classroom: classroom,
|
||||
status: :verified
|
||||
)
|
||||
|
||||
grade_entry = create(
|
||||
:grade_entry,
|
||||
grade_book: grade_book,
|
||||
user: student,
|
||||
attendance_days: attendance_days,
|
||||
is_perfect_attendance: false,
|
||||
math_grade: nil,
|
||||
reading_grade: nil
|
||||
)
|
||||
|
||||
expected_attendance_earnings = attendance_days * GradeEntry::EARNINGS_PER_DAY_ATTENDANCE
|
||||
|
||||
assert_equal expected_attendance_earnings, grade_entry.earnings_for_attendance
|
||||
assert_equal 0, grade_entry.attendance_perfect_earnings, "Should have no perfect attendance bonus"
|
||||
|
||||
sign_in(admin)
|
||||
visit classroom_grade_book_path(classroom, grade_book)
|
||||
|
||||
assert_difference("PortfolioTransaction.count", 1) do
|
||||
accept_confirm do
|
||||
click_on "Finalize Grades"
|
||||
end
|
||||
|
||||
assert_text "Grade book finalized. Funds have been distributed."
|
||||
end
|
||||
|
||||
portfolio.reload
|
||||
transaction = portfolio.portfolio_transactions.last
|
||||
|
||||
assert transaction.attendance_earnings?, "Transaction should be for attendance earnings"
|
||||
assert_equal expected_attendance_earnings, transaction.amount_cents
|
||||
assert transaction.deposit?
|
||||
assert_equal expected_attendance_earnings, (portfolio.cash_balance * 100).to_i
|
||||
|
||||
sign_out(admin)
|
||||
end
|
||||
|
||||
test "perfect attendance bonus awarded" do
|
||||
admin = create(:admin)
|
||||
school_year = create(:school_year)
|
||||
quarter = school_year.quarters.find_by!(number: 1)
|
||||
classroom = create(:classroom)
|
||||
student = create(:student, :with_portfolio, classroom: classroom)
|
||||
student.reload
|
||||
portfolio = student.portfolio
|
||||
|
||||
attendance_days = 30
|
||||
grade_book = create(
|
||||
:grade_book,
|
||||
quarter: quarter,
|
||||
classroom: classroom,
|
||||
status: :verified
|
||||
)
|
||||
|
||||
grade_entry = create(
|
||||
:grade_entry,
|
||||
grade_book: grade_book,
|
||||
user: student,
|
||||
attendance_days: attendance_days,
|
||||
is_perfect_attendance: true,
|
||||
math_grade: nil,
|
||||
reading_grade: nil
|
||||
)
|
||||
|
||||
base_attendance_earnings = attendance_days * GradeEntry::EARNINGS_PER_DAY_ATTENDANCE
|
||||
perfect_bonus = GradeEntry::EARNINGS_FOR_PERFECT_ATTENDANCE
|
||||
expected_total = base_attendance_earnings + perfect_bonus
|
||||
|
||||
assert grade_entry.is_perfect_attendance?, "Student should have perfect attendance"
|
||||
assert_equal perfect_bonus, grade_entry.attendance_perfect_earnings
|
||||
|
||||
sign_in(admin)
|
||||
visit classroom_grade_book_path(classroom, grade_book)
|
||||
|
||||
assert_difference("PortfolioTransaction.count", 1) do
|
||||
accept_confirm do
|
||||
click_on "Finalize Grades"
|
||||
end
|
||||
|
||||
assert_text "Grade book finalized. Funds have been distributed."
|
||||
end
|
||||
|
||||
portfolio.reload
|
||||
transaction = portfolio.portfolio_transactions.last
|
||||
|
||||
assert transaction.attendance_earnings?
|
||||
assert_equal expected_total, transaction.amount_cents,
|
||||
"Transaction should include base attendance ($#{base_attendance_earnings / 100.0}) " \
|
||||
"and perfect bonus ($#{perfect_bonus / 100.0})"
|
||||
assert_equal expected_total, (portfolio.cash_balance * 100).to_i
|
||||
|
||||
sign_out(admin)
|
||||
end
|
||||
|
||||
test "A grade earnings calculated correctly" do
|
||||
admin = create(:admin)
|
||||
school_year = create(:school_year)
|
||||
quarter = school_year.quarters.find_by!(number: 1)
|
||||
classroom = create(:classroom)
|
||||
student = create(:student, :with_portfolio, classroom: classroom)
|
||||
student.reload
|
||||
portfolio = student.portfolio
|
||||
|
||||
grade_book = create(
|
||||
:grade_book,
|
||||
quarter: quarter,
|
||||
classroom: classroom,
|
||||
status: :verified
|
||||
)
|
||||
|
||||
grade_entry = create(
|
||||
:grade_entry,
|
||||
grade_book: grade_book,
|
||||
user: student,
|
||||
attendance_days: 0,
|
||||
math_grade: "A",
|
||||
reading_grade: "A-"
|
||||
)
|
||||
|
||||
expected_math_earnings = GradeEntry::EARNINGS_FOR_A_GRADE
|
||||
expected_reading_earnings = GradeEntry::EARNINGS_FOR_A_GRADE
|
||||
|
||||
assert_equal expected_math_earnings, grade_entry.earnings_for_math
|
||||
assert_equal expected_reading_earnings, grade_entry.earnings_for_reading
|
||||
|
||||
sign_in(admin)
|
||||
visit classroom_grade_book_path(classroom, grade_book)
|
||||
|
||||
assert_difference("PortfolioTransaction.count", 2) do
|
||||
accept_confirm do
|
||||
click_on "Finalize Grades"
|
||||
end
|
||||
|
||||
assert_text "Grade book finalized. Funds have been distributed."
|
||||
end
|
||||
|
||||
portfolio.reload
|
||||
transactions = portfolio.portfolio_transactions.reload
|
||||
|
||||
math_transaction = transactions.find(&:math_earnings?)
|
||||
reading_transaction = transactions.find(&:reading_earnings?)
|
||||
|
||||
assert_not_nil math_transaction, "Should have math earnings transaction"
|
||||
assert_equal expected_math_earnings, math_transaction.amount_cents
|
||||
assert math_transaction.deposit?
|
||||
|
||||
assert_not_nil reading_transaction, "Should have reading earnings transaction"
|
||||
assert_equal expected_reading_earnings, reading_transaction.amount_cents
|
||||
assert reading_transaction.deposit?
|
||||
|
||||
assert_equal expected_math_earnings + expected_reading_earnings, (portfolio.cash_balance * 100).to_i
|
||||
|
||||
sign_out(admin)
|
||||
end
|
||||
|
||||
test "B grade earnings calculated correctly" do
|
||||
admin = create(:admin)
|
||||
school_year = create(:school_year)
|
||||
quarter = school_year.quarters.find_by!(number: 1)
|
||||
classroom = create(:classroom)
|
||||
student = create(:student, :with_portfolio, classroom: classroom)
|
||||
student.reload
|
||||
portfolio = student.portfolio
|
||||
|
||||
grade_book = create(
|
||||
:grade_book,
|
||||
quarter: quarter,
|
||||
classroom: classroom,
|
||||
status: :verified
|
||||
)
|
||||
|
||||
grade_entry = create(
|
||||
:grade_entry,
|
||||
grade_book: grade_book,
|
||||
user: student,
|
||||
attendance_days: 0,
|
||||
math_grade: "B+",
|
||||
reading_grade: "B"
|
||||
)
|
||||
|
||||
expected_math_earnings = GradeEntry::EARNINGS_FOR_B_GRADE
|
||||
expected_reading_earnings = GradeEntry::EARNINGS_FOR_B_GRADE
|
||||
|
||||
assert_equal expected_math_earnings, grade_entry.earnings_for_math
|
||||
assert_equal expected_reading_earnings, grade_entry.earnings_for_reading
|
||||
|
||||
sign_in(admin)
|
||||
visit classroom_grade_book_path(classroom, grade_book)
|
||||
|
||||
assert_difference("PortfolioTransaction.count", 2) do
|
||||
accept_confirm do
|
||||
click_on "Finalize Grades"
|
||||
end
|
||||
|
||||
assert_text "Grade book finalized. Funds have been distributed."
|
||||
end
|
||||
|
||||
portfolio.reload
|
||||
transactions = portfolio.portfolio_transactions.reload
|
||||
|
||||
math_transaction = transactions.find(&:math_earnings?)
|
||||
reading_transaction = transactions.find(&:reading_earnings?)
|
||||
|
||||
assert_not_nil math_transaction
|
||||
assert_equal expected_math_earnings, math_transaction.amount_cents
|
||||
|
||||
assert_not_nil reading_transaction
|
||||
assert_equal expected_reading_earnings, reading_transaction.amount_cents
|
||||
|
||||
assert_equal expected_math_earnings + expected_reading_earnings, (portfolio.cash_balance * 100).to_i
|
||||
|
||||
sign_out(admin)
|
||||
end
|
||||
|
||||
test "no earnings for grades below B" do
|
||||
admin = create(:admin)
|
||||
school_year = create(:school_year)
|
||||
quarter = school_year.quarters.find_by!(number: 1)
|
||||
classroom = create(:classroom)
|
||||
student = create(:student, :with_portfolio, classroom: classroom)
|
||||
student.reload
|
||||
portfolio = student.portfolio
|
||||
|
||||
attendance_days = 10
|
||||
grade_book = create(
|
||||
:grade_book,
|
||||
quarter: quarter,
|
||||
classroom: classroom,
|
||||
status: :verified
|
||||
)
|
||||
|
||||
grade_entry = create(
|
||||
:grade_entry,
|
||||
grade_book: grade_book,
|
||||
user: student,
|
||||
attendance_days: attendance_days,
|
||||
math_grade: "C",
|
||||
reading_grade: "F"
|
||||
)
|
||||
|
||||
expected_attendance_only = attendance_days * GradeEntry::EARNINGS_PER_DAY_ATTENDANCE
|
||||
|
||||
assert_equal 0, grade_entry.earnings_for_math, "C grade should earn $0"
|
||||
assert_equal 0, grade_entry.earnings_for_reading, "F grade should earn $0"
|
||||
assert_equal expected_attendance_only, grade_entry.earnings_for_attendance
|
||||
|
||||
sign_in(admin)
|
||||
visit classroom_grade_book_path(classroom, grade_book)
|
||||
|
||||
assert_difference("PortfolioTransaction.count", 1) do
|
||||
accept_confirm do
|
||||
click_on "Finalize Grades"
|
||||
end
|
||||
|
||||
assert_text "Grade book finalized. Funds have been distributed."
|
||||
end
|
||||
|
||||
portfolio.reload
|
||||
transactions = portfolio.portfolio_transactions.reload
|
||||
|
||||
assert_equal 1, transactions.count, "Should only have attendance transaction"
|
||||
assert transactions.find(&:attendance_earnings?).present?
|
||||
assert_nil transactions.find(&:math_earnings?), "Should have no math earnings"
|
||||
assert_nil transactions.find(&:reading_earnings?), "Should have no reading earnings"
|
||||
|
||||
assert_equal expected_attendance_only, (portfolio.cash_balance * 100).to_i
|
||||
|
||||
sign_out(admin)
|
||||
end
|
||||
|
||||
test "grade improvement bonus awarded" do
|
||||
# Setup
|
||||
admin = create(:admin)
|
||||
school_year = create(:school_year)
|
||||
quarter1 = school_year.quarters.find_by!(number: 1)
|
||||
quarter2 = school_year.quarters.find_by!(number: 2)
|
||||
classroom = create(:classroom)
|
||||
student = create(:student, :with_portfolio, classroom: classroom)
|
||||
student.reload
|
||||
portfolio = student.portfolio
|
||||
|
||||
# Previous quarter with lower grades
|
||||
previous_grade_book = create(
|
||||
:grade_book,
|
||||
quarter: quarter1,
|
||||
classroom: classroom,
|
||||
status: :completed
|
||||
)
|
||||
|
||||
previous_entry = create(
|
||||
:grade_entry,
|
||||
grade_book: previous_grade_book,
|
||||
user: student,
|
||||
math_grade: "C",
|
||||
reading_grade: "B"
|
||||
)
|
||||
|
||||
# Current quarter with improved grades
|
||||
current_grade_book = create(
|
||||
:grade_book,
|
||||
quarter: quarter2,
|
||||
classroom: classroom,
|
||||
status: :verified
|
||||
)
|
||||
|
||||
current_entry = create(
|
||||
:grade_entry,
|
||||
grade_book: current_grade_book,
|
||||
user: student,
|
||||
attendance_days: 0,
|
||||
math_grade: "B",
|
||||
reading_grade: "A"
|
||||
)
|
||||
|
||||
improvement_bonus = GradeEntry::EARNINGS_FOR_IMPROVED_GRADE
|
||||
expected_math = GradeEntry::EARNINGS_FOR_B_GRADE + improvement_bonus
|
||||
expected_reading = GradeEntry::EARNINGS_FOR_A_GRADE + improvement_bonus
|
||||
|
||||
assert_equal improvement_bonus, current_entry.math_improvement_earnings(previous_entry),
|
||||
"Math improved from C to B, should get bonus"
|
||||
assert_equal improvement_bonus, current_entry.reading_improvement_earnings(previous_entry),
|
||||
"Reading improved from B to A, should get bonus"
|
||||
|
||||
sign_in(admin)
|
||||
visit classroom_grade_book_path(classroom, current_grade_book)
|
||||
|
||||
assert_difference("PortfolioTransaction.count", 2) do
|
||||
accept_confirm do
|
||||
click_on "Finalize Grades"
|
||||
end
|
||||
|
||||
assert_text "Grade book finalized. Funds have been distributed."
|
||||
end
|
||||
|
||||
portfolio.reload
|
||||
transactions = portfolio.portfolio_transactions.reload
|
||||
|
||||
math_transaction = transactions.find(&:math_earnings?)
|
||||
reading_transaction = transactions.find(&:reading_earnings?)
|
||||
|
||||
assert_not_nil math_transaction
|
||||
assert_equal expected_math, math_transaction.amount_cents,
|
||||
"Math should be base B grade ($2.00) + improvement bonus ($2.00) = $4.00"
|
||||
|
||||
assert_not_nil reading_transaction
|
||||
assert_equal expected_reading, reading_transaction.amount_cents,
|
||||
"Reading should be base A grade ($3.00) + improvement bonus ($2.00) = $5.00"
|
||||
|
||||
sign_out(admin)
|
||||
end
|
||||
|
||||
test "no improvement bonus for first quarter" do
|
||||
admin = create(:admin)
|
||||
school_year = create(:school_year)
|
||||
quarter = school_year.quarters.find_by!(number: 1)
|
||||
classroom = create(:classroom)
|
||||
student = create(:student, :with_portfolio, classroom: classroom)
|
||||
student.reload
|
||||
portfolio = student.portfolio
|
||||
|
||||
# First quarter - no previous quarter exists
|
||||
grade_book = create(
|
||||
:grade_book,
|
||||
quarter: quarter,
|
||||
classroom: classroom,
|
||||
status: :verified
|
||||
)
|
||||
|
||||
grade_entry = create(
|
||||
:grade_entry,
|
||||
grade_book: grade_book,
|
||||
user: student,
|
||||
attendance_days: 0,
|
||||
math_grade: "A",
|
||||
reading_grade: "A"
|
||||
)
|
||||
|
||||
expected_math = GradeEntry::EARNINGS_FOR_A_GRADE
|
||||
expected_reading = GradeEntry::EARNINGS_FOR_A_GRADE
|
||||
|
||||
assert_equal 1, quarter.number, "This is first quarter"
|
||||
assert_nil quarter.previous, "No previous quarter should exist"
|
||||
assert_equal 0, grade_entry.math_improvement_earnings(nil), "No improvement bonus without previous entry"
|
||||
assert_equal 0, grade_entry.reading_improvement_earnings(nil), "No improvement bonus without previous entry"
|
||||
|
||||
sign_in(admin)
|
||||
visit classroom_grade_book_path(classroom, grade_book)
|
||||
|
||||
assert_difference("PortfolioTransaction.count", 2) do
|
||||
accept_confirm do
|
||||
click_on "Finalize Grades"
|
||||
end
|
||||
|
||||
assert_text "Grade book finalized. Funds have been distributed."
|
||||
end
|
||||
|
||||
portfolio.reload
|
||||
transactions = portfolio.portfolio_transactions.reload
|
||||
|
||||
math_transaction = transactions.find(&:math_earnings?)
|
||||
reading_transaction = transactions.find(&:reading_earnings?)
|
||||
|
||||
# Should only receive base A grade earnings, no improvement bonus
|
||||
assert_equal expected_math, math_transaction.amount_cents,
|
||||
"Math should only be base A grade ($3.00) with no improvement bonus"
|
||||
assert_equal expected_reading, reading_transaction.amount_cents,
|
||||
"Reading should only be base A grade ($3.00) with no improvement bonus"
|
||||
|
||||
sign_out(admin)
|
||||
end
|
||||
|
||||
test "multiple students receive individual earnings" do
|
||||
admin = create(:admin)
|
||||
school_year = create(:school_year)
|
||||
quarter = school_year.quarters.find_by!(number: 1)
|
||||
classroom = create(:classroom)
|
||||
|
||||
# Student 1: High grades and perfect attendance
|
||||
student1 = create(:student, :with_portfolio, classroom: classroom)
|
||||
student1.reload
|
||||
portfolio1 = student1.portfolio
|
||||
|
||||
# Student 2: Average grades and some attendance
|
||||
student2 = create(:student, :with_portfolio, classroom: classroom)
|
||||
student2.reload
|
||||
portfolio2 = student2.portfolio
|
||||
|
||||
# Student 3: Low grades and poor attendance
|
||||
student3 = create(:student, :with_portfolio, classroom: classroom)
|
||||
student3.reload
|
||||
portfolio3 = student3.portfolio
|
||||
|
||||
grade_book = create(
|
||||
:grade_book,
|
||||
quarter: quarter,
|
||||
classroom: classroom,
|
||||
status: :verified
|
||||
)
|
||||
|
||||
entry1 = create(
|
||||
:grade_entry,
|
||||
grade_book: grade_book,
|
||||
user: student1,
|
||||
attendance_days: 30,
|
||||
is_perfect_attendance: true,
|
||||
math_grade: "A",
|
||||
reading_grade: "A"
|
||||
)
|
||||
|
||||
entry2 = create(
|
||||
:grade_entry,
|
||||
grade_book: grade_book,
|
||||
user: student2,
|
||||
attendance_days: 20,
|
||||
is_perfect_attendance: false,
|
||||
math_grade: "B",
|
||||
reading_grade: "B"
|
||||
)
|
||||
|
||||
entry3 = create(
|
||||
:grade_entry,
|
||||
grade_book: grade_book,
|
||||
user: student3,
|
||||
attendance_days: 5,
|
||||
is_perfect_attendance: false,
|
||||
math_grade: "D",
|
||||
reading_grade: "F"
|
||||
)
|
||||
|
||||
student1_expected = entry1.earnings_for_attendance +
|
||||
entry1.attendance_perfect_earnings +
|
||||
entry1.earnings_for_math +
|
||||
entry1.earnings_for_reading
|
||||
|
||||
student2_expected = entry2.earnings_for_attendance +
|
||||
entry2.earnings_for_math +
|
||||
entry2.earnings_for_reading
|
||||
|
||||
student3_expected = entry3.earnings_for_attendance
|
||||
|
||||
assert student1_expected > student2_expected, "Student 1 should earn more than Student 2"
|
||||
assert student2_expected > student3_expected, "Student 2 should earn more than Student 3"
|
||||
|
||||
sign_in(admin)
|
||||
visit classroom_grade_book_path(classroom, grade_book)
|
||||
|
||||
expected_transaction_count = 3 + 3 + 1 # student1 (3) + student2 (3) + student3 (1)
|
||||
assert_difference("PortfolioTransaction.count", expected_transaction_count) do
|
||||
accept_confirm do
|
||||
click_on "Finalize Grades"
|
||||
end
|
||||
|
||||
assert_text "Grade book finalized. Funds have been distributed."
|
||||
end
|
||||
|
||||
portfolio1.reload
|
||||
portfolio2.reload
|
||||
portfolio3.reload
|
||||
|
||||
assert_equal student1_expected, (portfolio1.cash_balance * 100).to_i,
|
||||
"Student 1 balance should match expected earnings"
|
||||
assert_equal student2_expected, (portfolio2.cash_balance * 100).to_i,
|
||||
"Student 2 balance should match expected earnings"
|
||||
assert_equal student3_expected, (portfolio3.cash_balance * 100).to_i,
|
||||
"Student 3 balance should match expected earnings"
|
||||
|
||||
assert_equal 3, portfolio1.portfolio_transactions.count, "Student 1 should have 3 transactions"
|
||||
assert_equal 3, portfolio2.portfolio_transactions.count, "Student 2 should have 3 transactions"
|
||||
assert_equal 1, portfolio3.portfolio_transactions.count, "Student 3 should have 1 transaction (attendance only)"
|
||||
|
||||
sign_out(admin)
|
||||
end
|
||||
|
||||
test "earnings distribution creates separate transactions per category" do
|
||||
# Setup
|
||||
admin = create(:admin)
|
||||
school_year = create(:school_year)
|
||||
quarter = school_year.quarters.find_by!(number: 1)
|
||||
classroom = create(:classroom)
|
||||
student = create(:student, :with_portfolio, classroom: classroom)
|
||||
student.reload
|
||||
portfolio = student.portfolio
|
||||
|
||||
grade_book = create(
|
||||
:grade_book,
|
||||
quarter: quarter,
|
||||
classroom: classroom,
|
||||
status: :verified
|
||||
)
|
||||
|
||||
create(
|
||||
:grade_entry,
|
||||
grade_book: grade_book,
|
||||
user: student,
|
||||
attendance_days: 15,
|
||||
math_grade: "A",
|
||||
reading_grade: "B"
|
||||
)
|
||||
|
||||
sign_in(admin)
|
||||
visit classroom_grade_book_path(classroom, grade_book)
|
||||
|
||||
assert_difference("PortfolioTransaction.count", 3) do
|
||||
accept_confirm do
|
||||
click_on "Finalize Grades"
|
||||
end
|
||||
|
||||
assert_text "Grade book finalized. Funds have been distributed."
|
||||
end
|
||||
|
||||
portfolio.reload
|
||||
transactions = portfolio.portfolio_transactions.reload
|
||||
|
||||
assert_equal 3, transactions.count, "Should have exactly 3 separate transactions"
|
||||
|
||||
# Verify each transaction type exists and is distinct
|
||||
attendance_tx = transactions.find(&:attendance_earnings?)
|
||||
math_tx = transactions.find(&:math_earnings?)
|
||||
reading_tx = transactions.find(&:reading_earnings?)
|
||||
|
||||
assert_not_nil attendance_tx, "Should have attendance earnings transaction"
|
||||
assert_not_nil math_tx, "Should have math earnings transaction"
|
||||
assert_not_nil reading_tx, "Should have reading earnings transaction"
|
||||
|
||||
[attendance_tx, math_tx, reading_tx].each do |tx|
|
||||
assert tx.deposit?, "all tx should be deposit: #{tx}"
|
||||
end
|
||||
|
||||
# Verify they have different reasons
|
||||
assert_equal :attendance_earnings, attendance_tx.reason.to_sym
|
||||
assert_equal :math_earnings, math_tx.reason.to_sym
|
||||
assert_equal :reading_earnings, reading_tx.reason.to_sym
|
||||
|
||||
sign_out(admin)
|
||||
end
|
||||
|
||||
test "grade book cannot be finalized twice" do
|
||||
admin = create(:admin)
|
||||
school_year = create(:school_year)
|
||||
quarter = school_year.quarters.find_by!(number: 1)
|
||||
classroom = create(:classroom)
|
||||
student = create(:student, :with_portfolio, classroom: classroom)
|
||||
student.reload
|
||||
|
||||
grade_book = create(
|
||||
:grade_book,
|
||||
quarter: quarter,
|
||||
classroom: classroom,
|
||||
status: :completed
|
||||
)
|
||||
|
||||
create(
|
||||
:grade_entry,
|
||||
grade_book: grade_book,
|
||||
user: student,
|
||||
attendance_days: 20,
|
||||
math_grade: "A",
|
||||
reading_grade: "A"
|
||||
)
|
||||
|
||||
assert grade_book.completed?, "Grade book should already be completed"
|
||||
|
||||
sign_in(admin)
|
||||
visit classroom_grade_book_path(classroom, grade_book)
|
||||
|
||||
# Verification - Finalize button should not be visible
|
||||
assert_no_button "Finalize Grades"
|
||||
|
||||
sign_out(admin)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,38 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "application_system_test_case"
|
||||
|
||||
class AnnouncementsTest < ApplicationSystemTestCase
|
||||
setup do
|
||||
@announcement = Announcement.create!(
|
||||
title: "Test Announcement",
|
||||
content: "Test content"
|
||||
)
|
||||
end
|
||||
|
||||
test "students can view announcements" do
|
||||
student = create(:student)
|
||||
sign_in student
|
||||
|
||||
visit announcement_url(@announcement)
|
||||
assert_text @announcement.title
|
||||
end
|
||||
|
||||
test "teachers can view announcements" do
|
||||
teacher = create(:teacher)
|
||||
sign_in teacher
|
||||
|
||||
visit announcement_url(@announcement)
|
||||
assert_text @announcement.title
|
||||
end
|
||||
|
||||
test "should display announcement content properly" do
|
||||
student = create(:student)
|
||||
sign_in student
|
||||
|
||||
visit announcement_url(@announcement)
|
||||
assert_text @announcement.title
|
||||
# ActionText content should be displayed
|
||||
assert_selector ".trix-content"
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,204 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "application_system_test_case"
|
||||
|
||||
class GradeBooksTest < ApplicationSystemTestCase
|
||||
test "teacher updates grade book entries" do
|
||||
classroom = create(:classroom)
|
||||
grade_book = create(:grade_book, classroom:)
|
||||
student1 = create(:student, classroom:)
|
||||
student2 = create(:student, classroom:)
|
||||
student1_entry = create(:grade_entry, grade_book:, user: student1)
|
||||
student2_entry = create(:grade_entry, grade_book:, user: student2)
|
||||
teacher = create(:teacher)
|
||||
create(:teacher_classroom, teacher:, classroom:)
|
||||
sign_in(teacher)
|
||||
visit classroom_grade_book_path(classroom, grade_book)
|
||||
|
||||
within("##{dom_id(student1_entry)}") do
|
||||
find("[data-testid='math-grade-select']").select("A")
|
||||
find("[data-testid='reading-grade-select']").select("B+")
|
||||
find("[data-testid='attendance-days-input']").set(95)
|
||||
end
|
||||
within("##{dom_id(student2_entry)}") do
|
||||
find("[data-testid='math-grade-select']").select("B")
|
||||
find("[data-testid='reading-grade-select']").select("A")
|
||||
find("[data-testid='attendance-days-input']").set(87)
|
||||
end
|
||||
click_on "Save Grades"
|
||||
|
||||
within("##{dom_id(student1_entry)}") do
|
||||
assert_equal "A", find("[data-testid='math-grade-select']").value
|
||||
assert_equal "B+", find("[data-testid='reading-grade-select']").value
|
||||
assert_equal "95", find("[data-testid='attendance-days-input']").value
|
||||
end
|
||||
within("##{dom_id(student2_entry)}") do
|
||||
assert_equal "B", find("[data-testid='math-grade-select']").value
|
||||
assert_equal "A", find("[data-testid='reading-grade-select']").value
|
||||
assert_equal "87", find("[data-testid='attendance-days-input']").value
|
||||
end
|
||||
end
|
||||
test "admin sees success message when finalizing grade book" do
|
||||
DistributeEarnings.stubs(:execute)
|
||||
classroom = create(:classroom)
|
||||
grade_book = create(:grade_book, classroom:)
|
||||
student1 = create(:student, classroom:)
|
||||
student2 = create(:student, classroom:)
|
||||
create(:grade_entry, grade_book:, user: student1)
|
||||
create(:grade_entry, grade_book:, user: student2)
|
||||
admin = create(:admin)
|
||||
sign_in(admin)
|
||||
visit classroom_grade_book_path(classroom, grade_book)
|
||||
|
||||
assert_button "Finalize Grades"
|
||||
accept_confirm do
|
||||
click_on "Finalize Grades"
|
||||
end
|
||||
|
||||
assert_selector(
|
||||
"#notice",
|
||||
text: "Grade book finalized. Funds have been distributed."
|
||||
)
|
||||
end
|
||||
|
||||
test "teacher enters grades for multiple students" do
|
||||
classroom = create(:classroom)
|
||||
grade_book = create(:grade_book, classroom:)
|
||||
student1 = create(:student, classroom:)
|
||||
student2 = create(:student, classroom:)
|
||||
student1_entry = create(:grade_entry, grade_book:, user: student1)
|
||||
student2_entry = create(:grade_entry, grade_book:, user: student2)
|
||||
teacher = create(:teacher)
|
||||
create(:teacher_classroom, teacher:, classroom:)
|
||||
sign_in(teacher)
|
||||
visit classroom_grade_book_path(classroom, grade_book)
|
||||
|
||||
within("##{dom_id(student1_entry)}") do
|
||||
find("[data-testid='math-grade-select']").select("A")
|
||||
find("[data-testid='reading-grade-select']").select("B+")
|
||||
find("[data-testid='attendance-days-input']").set(95)
|
||||
end
|
||||
within("##{dom_id(student2_entry)}") do
|
||||
find("[data-testid='math-grade-select']").select("B")
|
||||
find("[data-testid='reading-grade-select']").select("A")
|
||||
find("[data-testid='attendance-days-input']").set(87)
|
||||
end
|
||||
click_on "Save Grades"
|
||||
|
||||
within("##{dom_id(student1_entry)}") do
|
||||
assert_equal "A", find("[data-testid='math-grade-select']").value
|
||||
assert_equal "B+", find("[data-testid='reading-grade-select']").value
|
||||
end
|
||||
within("##{dom_id(student2_entry)}") do
|
||||
assert_equal "B", find("[data-testid='math-grade-select']").value
|
||||
assert_equal "A", find("[data-testid='reading-grade-select']").value
|
||||
end
|
||||
end
|
||||
|
||||
test "teacher updates previously entered grades" do
|
||||
classroom = create(:classroom)
|
||||
grade_book = create(:grade_book, classroom:)
|
||||
student_entry = create(:grade_entry, grade_book:)
|
||||
teacher = create(:teacher)
|
||||
create(:teacher_classroom, teacher:, classroom:)
|
||||
sign_in(teacher)
|
||||
visit classroom_grade_book_path(classroom, grade_book)
|
||||
|
||||
within("##{dom_id(student_entry)}") do
|
||||
find("[data-testid='math-grade-select']").select("A")
|
||||
find("[data-testid='reading-grade-select']").select("B+")
|
||||
find("[data-testid='attendance-days-input']").set(25)
|
||||
end
|
||||
click_on "Save Grades"
|
||||
|
||||
within("##{dom_id(student_entry)}") do
|
||||
assert_equal "A", find("[data-testid='math-grade-select']").value
|
||||
assert_equal "B+", find("[data-testid='reading-grade-select']").value
|
||||
end
|
||||
end
|
||||
|
||||
test "teacher marks student with perfect attendance" do
|
||||
classroom = create(:classroom)
|
||||
grade_book = create(:grade_book, classroom:)
|
||||
student = create(:student, classroom:)
|
||||
student_entry = create(:grade_entry, grade_book:, user: student)
|
||||
teacher = create(:teacher)
|
||||
create(:teacher_classroom, teacher:, classroom:)
|
||||
sign_in(teacher)
|
||||
visit classroom_grade_book_path(classroom, grade_book)
|
||||
|
||||
within("##{dom_id(student_entry)}") do
|
||||
find("[data-testid='math-grade-select']").select("A")
|
||||
find("[data-testid='reading-grade-select']").select("A")
|
||||
find("[data-testid='attendance-days-input']").set(90)
|
||||
find("[data-testid='perfect-attendance-checkbox']").check
|
||||
end
|
||||
click_on "Save Grades"
|
||||
|
||||
within("##{dom_id(student_entry)}") do
|
||||
assert find("[data-testid='perfect-attendance-checkbox']").checked?
|
||||
end
|
||||
end
|
||||
|
||||
test "teacher can view but cannot finalize grade book" do
|
||||
classroom = create(:classroom)
|
||||
grade_book = create(:grade_book, classroom:)
|
||||
student1 = create(:student, classroom:)
|
||||
student2 = create(:student, classroom:)
|
||||
student1_entry = create(:grade_entry, grade_book:, user: student1)
|
||||
student2_entry = create(:grade_entry, grade_book:, user: student2)
|
||||
teacher = create(:teacher)
|
||||
create(:teacher_classroom, teacher:, classroom:)
|
||||
sign_in(teacher)
|
||||
visit classroom_grade_book_path(classroom, grade_book)
|
||||
|
||||
assert_selector "##{dom_id(student1_entry)}"
|
||||
assert_selector "##{dom_id(student2_entry)}"
|
||||
assert_no_text "Finalize Grades"
|
||||
end
|
||||
|
||||
test "teacher cannot view grade books from other classrooms" do
|
||||
classroom = create(:classroom)
|
||||
teacher = create(:teacher)
|
||||
create(:teacher_classroom, teacher:, classroom:)
|
||||
other_classroom = create(:classroom)
|
||||
other_grade_book = create(:grade_book, classroom: other_classroom)
|
||||
sign_in(teacher)
|
||||
|
||||
visit classroom_grade_book_path(other_classroom, other_grade_book)
|
||||
|
||||
assert_current_path root_path
|
||||
end
|
||||
|
||||
test "grade book displays validation for attendance input" do
|
||||
classroom = create(:classroom)
|
||||
grade_book = create(:grade_book, classroom:)
|
||||
student = create(:student, classroom:)
|
||||
student_entry = create(:grade_entry, grade_book:, user: student)
|
||||
teacher = create(:teacher)
|
||||
create(:teacher_classroom, teacher:, classroom:)
|
||||
sign_in(teacher)
|
||||
|
||||
visit classroom_grade_book_path(classroom, grade_book)
|
||||
|
||||
within("##{dom_id(student_entry)}") do
|
||||
assert_selector "input[type='number'][min='0']"
|
||||
end
|
||||
end
|
||||
|
||||
test "teacher cannot edit finalized grade book" do
|
||||
classroom = create(:classroom)
|
||||
grade_book = create(:grade_book, classroom:, status: :completed)
|
||||
create(:grade_entry, grade_book:)
|
||||
create(:grade_entry, grade_book:)
|
||||
teacher = create(:teacher)
|
||||
create(:teacher_classroom, teacher:, classroom:)
|
||||
sign_in(teacher)
|
||||
visit classroom_grade_book_path(classroom, grade_book)
|
||||
|
||||
assert_selector "select[disabled]", count: 4
|
||||
assert_selector "input[type='number'][disabled]", count: 2
|
||||
assert_selector "input[type='checkbox'][disabled]", count: 2
|
||||
assert_no_button "Save Grades"
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,224 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "application_system_test_case"
|
||||
|
||||
class StudentPortfolioViewTest < ApplicationSystemTestCase
|
||||
test "student views portfolio with cash and stock holdings" do
|
||||
classroom = create(:classroom)
|
||||
student = create(:student, :with_portfolio, classroom: classroom)
|
||||
student.reload
|
||||
portfolio = student.portfolio
|
||||
|
||||
# Give student some cash
|
||||
create(:portfolio_transaction, :deposit, portfolio: portfolio, amount_cents: 100_000)
|
||||
|
||||
# Give student some stocks
|
||||
stock_aapl = create(:stock, ticker: "AAPL", company_name: "Apple Inc.", price_cents: 15_000)
|
||||
stock_googl = create(:stock, ticker: "GOOGL", company_name: "Google LLC", price_cents: 20_000)
|
||||
create(:portfolio_stock, portfolio: portfolio, stock: stock_aapl, shares: 5)
|
||||
create(:portfolio_stock, portfolio: portfolio, stock: stock_googl, shares: 3)
|
||||
|
||||
sign_in(student)
|
||||
visit portfolio_path(portfolio)
|
||||
|
||||
# Verify page heading
|
||||
assert_text "PORTFOLIO"
|
||||
assert_text student.username.upcase
|
||||
|
||||
# Verify cash balance is displayed
|
||||
within "[data-testid='cash-balance']" do
|
||||
assert_text "$1,000.00"
|
||||
end
|
||||
|
||||
# Verify stock holdings are displayed
|
||||
within "[data-testid='holdings-table']" do
|
||||
# AAPL holdings
|
||||
assert_text "AAPL"
|
||||
assert_text "5"
|
||||
assert_text "$150.00"
|
||||
|
||||
# GOOGL holdings
|
||||
assert_text "GOOGL"
|
||||
assert_text "3"
|
||||
assert_text "$200.00"
|
||||
end
|
||||
|
||||
# Verify total stocks count
|
||||
within "[data-testid='total-stocks']" do
|
||||
assert_text "8"
|
||||
end
|
||||
|
||||
sign_out(student)
|
||||
end
|
||||
|
||||
test "student views portfolio with only cash (no stocks)" do
|
||||
classroom = create(:classroom)
|
||||
student = create(:student, :with_portfolio, classroom: classroom)
|
||||
student.reload
|
||||
portfolio = student.portfolio
|
||||
|
||||
# Give student cash but no stocks
|
||||
create(:portfolio_transaction, :deposit, portfolio: portfolio, amount_cents: 50_000)
|
||||
|
||||
sign_in(student)
|
||||
visit portfolio_path(portfolio)
|
||||
|
||||
# Verify page heading
|
||||
assert_text "PORTFOLIO"
|
||||
|
||||
# Verify cash balance is displayed
|
||||
within "[data-testid='cash-balance']" do
|
||||
assert_text "$500.00"
|
||||
end
|
||||
|
||||
# Verify no stock holdings
|
||||
# The view may show an empty table but no "Trade" buttons
|
||||
within "[data-testid='holdings-table']" do
|
||||
# Table should be empty (no holdings rows with Trade buttons)
|
||||
assert_no_text "Trade"
|
||||
end
|
||||
|
||||
# Total stocks should be 0
|
||||
within "[data-testid='total-stocks']" do
|
||||
assert_text "0"
|
||||
end
|
||||
|
||||
sign_out(student)
|
||||
end
|
||||
|
||||
test "student views portfolio with no cash (only stocks)" do
|
||||
classroom = create(:classroom)
|
||||
student = create(:student, :with_portfolio, classroom: classroom)
|
||||
student.reload
|
||||
portfolio = student.portfolio
|
||||
|
||||
# Give student stocks but no cash (portfolio starts with $0)
|
||||
stock_tsla = create(:stock, ticker: "TSLA", company_name: "Tesla Inc.", price_cents: 25_000)
|
||||
create(:portfolio_stock, portfolio: portfolio, stock: stock_tsla, shares: 4)
|
||||
|
||||
sign_in(student)
|
||||
visit portfolio_path(portfolio)
|
||||
|
||||
# Verify page heading
|
||||
assert_text "PORTFOLIO"
|
||||
|
||||
# Verify cash balance is $0
|
||||
within "[data-testid='cash-balance']" do
|
||||
assert_text "$0.00"
|
||||
end
|
||||
|
||||
# Verify stock holdings are displayed
|
||||
within "[data-testid='holdings-table']" do
|
||||
assert_text "TSLA"
|
||||
assert_text "4"
|
||||
assert_text "$250.00"
|
||||
end
|
||||
|
||||
# Verify total stocks count
|
||||
within "[data-testid='total-stocks']" do
|
||||
assert_text "4"
|
||||
end
|
||||
|
||||
sign_out(student)
|
||||
end
|
||||
|
||||
test "student views empty portfolio (new account)" do
|
||||
classroom = create(:classroom)
|
||||
student = create(:student, :with_portfolio, classroom: classroom)
|
||||
student.reload
|
||||
portfolio = student.portfolio
|
||||
|
||||
# Portfolio was just created with no transactions or stocks
|
||||
sign_in(student)
|
||||
visit portfolio_path(portfolio)
|
||||
|
||||
# Verify page heading
|
||||
assert_text "PORTFOLIO"
|
||||
|
||||
# Verify cash balance is $0
|
||||
within "[data-testid='cash-balance']" do
|
||||
assert_text "$0.00"
|
||||
end
|
||||
|
||||
# Verify no stock holdings
|
||||
within "[data-testid='holdings-table']" do
|
||||
assert_no_text "Trade"
|
||||
end
|
||||
|
||||
# Total stocks should be 0
|
||||
within "[data-testid='total-stocks']" do
|
||||
assert_text "0"
|
||||
end
|
||||
|
||||
sign_out(student)
|
||||
end
|
||||
|
||||
test "portfolio reflects real-time stock prices" do
|
||||
classroom = create(:classroom)
|
||||
student = create(:student, :with_portfolio, classroom: classroom)
|
||||
student.reload
|
||||
portfolio = student.portfolio
|
||||
|
||||
# Create stock with initial price
|
||||
stock = create(:stock, ticker: "NFLX", company_name: "Netflix", price_cents: 30_000)
|
||||
create(:portfolio_stock, portfolio: portfolio, stock: stock, shares: 2)
|
||||
|
||||
sign_in(student)
|
||||
visit portfolio_path(portfolio)
|
||||
|
||||
# Verify initial price is displayed
|
||||
within "[data-testid='holdings-table']" do
|
||||
assert_text "$300.00" # Last Price column
|
||||
end
|
||||
|
||||
# Update stock price in database (simulating price update)
|
||||
stock.update!(price_cents: 35_000)
|
||||
|
||||
# Refresh page to see updated price
|
||||
visit portfolio_path(portfolio)
|
||||
|
||||
# Verify updated price is displayed
|
||||
within "[data-testid='holdings-table']" do
|
||||
assert_text "$350.00" # Updated Last Price
|
||||
end
|
||||
|
||||
sign_out(student)
|
||||
end
|
||||
|
||||
test "student cannot view another student's portfolio" do
|
||||
classroom = create(:classroom)
|
||||
student_a = create(:student, :with_portfolio, classroom: classroom)
|
||||
student_b = create(:student, :with_portfolio, classroom: classroom)
|
||||
student_a.reload
|
||||
student_b.reload
|
||||
portfolio_b = student_b.portfolio
|
||||
|
||||
# Give student B some cash and stocks
|
||||
create(:portfolio_transaction, :deposit, portfolio: portfolio_b, amount_cents: 20_000)
|
||||
stock = create(:stock, ticker: "AMZN", company_name: "Amazon", price_cents: 40_000)
|
||||
create(:portfolio_stock, portfolio: portfolio_b, stock: stock, shares: 1)
|
||||
|
||||
# Student A attempts to access Student B's portfolio
|
||||
sign_in(student_a)
|
||||
|
||||
# Attempt to visit Student B's portfolio URL directly
|
||||
visit portfolio_path(portfolio_b)
|
||||
|
||||
# Should be denied access (Pundit policy blocks) and redirected to own portfolio
|
||||
# Verify Student A is viewing their own portfolio (not Student B's)
|
||||
assert_text student_a.username.upcase
|
||||
|
||||
# Verify this is NOT Student B's portfolio by checking stock count
|
||||
# Student B has 1 share of AMZN, Student A has 0
|
||||
within "[data-testid='total-stocks']" do
|
||||
assert_text "0"
|
||||
end
|
||||
|
||||
# Verify Student B's stock is NOT shown
|
||||
within "[data-testid='holdings-table']" do
|
||||
assert_no_text "AMZN"
|
||||
end
|
||||
|
||||
sign_out(student_a)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,226 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "application_system_test_case"
|
||||
|
||||
class StudentTradingFlowTest < ApplicationSystemTestCase
|
||||
test "student buys a stock" do
|
||||
classroom = create(:classroom, :with_trading)
|
||||
student = create(:student, :with_portfolio, classroom: classroom)
|
||||
student.reload
|
||||
portfolio = student.portfolio
|
||||
create(:portfolio_transaction, :deposit, portfolio: portfolio, amount_cents: 100_000)
|
||||
stock = create(:stock, ticker: "AAPL", company_name: "Apple Inc.", price_cents: 15_000)
|
||||
shares_to_buy = 2
|
||||
|
||||
sign_in(student)
|
||||
|
||||
visit stocks_path
|
||||
|
||||
assert_text "Trading Floor"
|
||||
assert_text stock.ticker
|
||||
assert_text stock.company_name
|
||||
|
||||
within "tr", text: stock.company_name do
|
||||
click_on "Buy"
|
||||
end
|
||||
|
||||
assert_text "Buy #{stock.ticker}"
|
||||
|
||||
fill_in "Number of shares", with: shares_to_buy
|
||||
|
||||
assert_difference("Order.buy.pending.count", +1) do
|
||||
click_button "Buy Shares"
|
||||
|
||||
assert_text "Order was successfully created"
|
||||
end
|
||||
|
||||
order = Order.buy.pending.find_by(user: student, stock: stock)
|
||||
assert_equal shares_to_buy, order.shares
|
||||
assert_equal stock.id, order.stock_id
|
||||
assert order.buy?
|
||||
assert order.pending?
|
||||
|
||||
sign_out(student)
|
||||
end
|
||||
|
||||
test "student sells a stock" do
|
||||
classroom = create(:classroom, :with_trading)
|
||||
student = create(:student, :with_portfolio, classroom: classroom)
|
||||
student.reload
|
||||
portfolio = student.portfolio
|
||||
stock = create(:stock, ticker: "GOOGL", company_name: "Google LLC", price_cents: 10_000)
|
||||
shares_owned = 5
|
||||
create(:portfolio_stock, portfolio: portfolio, stock: stock, shares: shares_owned)
|
||||
shares_to_sell = 2
|
||||
|
||||
sign_in(student)
|
||||
|
||||
visit stocks_path
|
||||
|
||||
assert_text "Trading Floor"
|
||||
assert_text stock.ticker
|
||||
assert_text stock.company_name
|
||||
|
||||
within "tr", text: stock.company_name do
|
||||
click_on "Sell"
|
||||
end
|
||||
|
||||
assert_text "Sell #{stock.ticker}"
|
||||
|
||||
within "[data-testid='shares-owned-value']" do
|
||||
assert_text shares_owned.to_s
|
||||
end
|
||||
|
||||
fill_in "Number of shares", with: shares_to_sell
|
||||
|
||||
assert_difference("Order.sell.pending.count", +1) do
|
||||
click_button "Sell Shares"
|
||||
|
||||
assert_text "Order was successfully created"
|
||||
end
|
||||
|
||||
order = Order.sell.pending.find_by(user: student, stock: stock)
|
||||
assert_equal shares_to_sell, order.shares
|
||||
assert_equal stock.id, order.stock_id
|
||||
assert order.sell?
|
||||
assert order.pending?
|
||||
|
||||
sign_out(student)
|
||||
end
|
||||
|
||||
test "student updates pending order" do
|
||||
classroom = create(:classroom, :with_trading)
|
||||
student = create(:student, :with_portfolio, classroom: classroom)
|
||||
student.reload
|
||||
portfolio = student.portfolio
|
||||
stock = create(:stock, ticker: "TSLA", company_name: "Tesla Inc.", price_cents: 25_000)
|
||||
create(:portfolio_transaction, :deposit, portfolio: portfolio, amount_cents: 200_000)
|
||||
|
||||
# create buy order beforehand
|
||||
initial_shares = 4
|
||||
updated_shares = 2
|
||||
order = create(:order, :pending, action: :buy, user: student, stock: stock, shares: initial_shares)
|
||||
|
||||
sign_in(student)
|
||||
|
||||
visit orders_path
|
||||
|
||||
within "tr", text: stock.ticker do
|
||||
find("[data-testid='edit-order-button']").click
|
||||
end
|
||||
|
||||
fill_in "Number of shares", with: updated_shares
|
||||
|
||||
assert_no_difference("Order.buy.pending.count") do
|
||||
click_button "Buy Shares"
|
||||
|
||||
assert_text "Order was successfully updated"
|
||||
end
|
||||
|
||||
order.reload
|
||||
assert_equal updated_shares, order.shares
|
||||
assert order.pending?
|
||||
|
||||
sign_out(student)
|
||||
end
|
||||
|
||||
test "student cancels pending order" do
|
||||
classroom = create(:classroom, :with_trading)
|
||||
student = create(:student, :with_portfolio, classroom: classroom)
|
||||
student.reload
|
||||
portfolio = student.portfolio
|
||||
stock = create(:stock, ticker: "NFLX", company_name: "Netflix", price_cents: 30_000)
|
||||
create(:portfolio_transaction, :deposit, portfolio: portfolio, amount_cents: 150_000)
|
||||
|
||||
# create pending order beforehand
|
||||
shares = 3
|
||||
order = create(:order, :pending, action: :buy, user: student, stock: stock, shares: shares)
|
||||
|
||||
sign_in(student)
|
||||
|
||||
visit orders_path
|
||||
|
||||
assert_difference -> { Order.pending.count } => -1, -> { Order.canceled.count } => +1 do
|
||||
accept_confirm do
|
||||
within "tr", text: stock.ticker do
|
||||
find("[data-testid='cancel-order-button']").click
|
||||
end
|
||||
end
|
||||
|
||||
assert_text "Order was successfully canceled"
|
||||
end
|
||||
|
||||
order.reload
|
||||
assert order.canceled?
|
||||
assert_equal shares, order.shares
|
||||
|
||||
sign_out(student)
|
||||
end
|
||||
|
||||
test "student cannot buy with insufficient funds" do
|
||||
classroom = create(:classroom, :with_trading)
|
||||
student = create(:student, :with_portfolio, classroom: classroom)
|
||||
student.reload
|
||||
portfolio = student.portfolio
|
||||
fund = 10_000
|
||||
stock_price = 25_000
|
||||
shares_to_buy = 1
|
||||
create(:portfolio_transaction, :deposit, portfolio: portfolio, amount_cents: fund)
|
||||
stock = create(:stock, ticker: "TSLA", company_name: "Tesla Inc.", price_cents: stock_price)
|
||||
|
||||
assert stock_price > fund, "cannot buy with insufficient funds"
|
||||
|
||||
sign_in(student)
|
||||
|
||||
visit stocks_path
|
||||
|
||||
within "tr", text: stock.company_name do
|
||||
click_on "Buy"
|
||||
end
|
||||
|
||||
fill_in "Number of shares", with: shares_to_buy
|
||||
|
||||
assert_no_difference("Order.buy.pending.count") do
|
||||
click_button "Buy Shares"
|
||||
|
||||
assert_text "Insufficient funds"
|
||||
end
|
||||
|
||||
sign_out(student)
|
||||
end
|
||||
|
||||
test "student cannot sell more shares than owned" do
|
||||
classroom = create(:classroom, :with_trading)
|
||||
student = create(:student, :with_portfolio, classroom: classroom)
|
||||
student.reload
|
||||
portfolio = student.portfolio
|
||||
stock = create(:stock, ticker: "AMZN", company_name: "Amazon", price_cents: 20_000)
|
||||
shares_owned = 3
|
||||
shares_to_sell = 5
|
||||
create(:portfolio_stock, portfolio: portfolio, stock: stock, shares: shares_owned)
|
||||
|
||||
assert shares_to_sell > shares_owned, "cannot sell more shares than owned"
|
||||
|
||||
sign_in(student)
|
||||
|
||||
visit stocks_path
|
||||
|
||||
within "tr", text: stock.company_name do
|
||||
click_on "Sell"
|
||||
end
|
||||
|
||||
within "[data-testid='shares-owned-value']" do
|
||||
assert_text shares_owned.to_s
|
||||
end
|
||||
|
||||
fill_in "Number of shares", with: shares_to_sell
|
||||
|
||||
assert_no_difference("Order.sell.pending.count") do
|
||||
click_button "Sell Shares"
|
||||
|
||||
assert_text "Cannot sell more shares than you own"
|
||||
end
|
||||
|
||||
sign_out(student)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,126 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "application_system_test_case"
|
||||
|
||||
class TeacherCreatesStudentTest < ApplicationSystemTestCase
|
||||
test "teacher can create a student" do
|
||||
username = "student_one"
|
||||
classroom = create(:classroom)
|
||||
teacher = create(:teacher)
|
||||
create(:teacher_classroom, teacher:, classroom:)
|
||||
sign_in(teacher)
|
||||
visit classroom_path(classroom)
|
||||
|
||||
click_on "Add Student"
|
||||
fill_in "Username", with: username
|
||||
click_button "Create Student"
|
||||
|
||||
assert_selector "#notice", text: "Student #{username} created successfully"
|
||||
assert_selector "[data-testid='student-username']", text: username
|
||||
end
|
||||
|
||||
test "teacher can view and manage student list" do
|
||||
classroom = create(:classroom)
|
||||
student1 = create(:student, :with_portfolio, classroom:)
|
||||
student2 = create(:student, :with_portfolio, classroom:)
|
||||
student3 = create(:student, :with_portfolio, classroom:)
|
||||
teacher = create(:teacher)
|
||||
create(:teacher_classroom, teacher:, classroom:)
|
||||
sign_in(teacher)
|
||||
|
||||
visit classroom_path(classroom)
|
||||
|
||||
within "##{dom_id(student1)}" do
|
||||
assert_selector "[data-testid='student-actions']"
|
||||
end
|
||||
within "##{dom_id(student2)}" do
|
||||
assert_selector "[data-testid='student-actions']"
|
||||
end
|
||||
within "##{dom_id(student3)}" do
|
||||
assert_selector "[data-testid='student-actions']"
|
||||
end
|
||||
end
|
||||
|
||||
test "teacher can reset student password" do
|
||||
skip "Flaky modal test - to be fixed in future PR"
|
||||
username = "student_one"
|
||||
classroom = create(:classroom)
|
||||
student = create(:student, :with_portfolio, classroom:, username:)
|
||||
teacher = create(:teacher)
|
||||
create(:teacher_classroom, teacher:, classroom:)
|
||||
sign_in(teacher)
|
||||
visit classroom_path(classroom)
|
||||
|
||||
assert_selector "##{dom_id(student)} [data-testid='reset-password']"
|
||||
accept_confirm do
|
||||
find("##{dom_id(student)} [data-testid='reset-password']").click
|
||||
end
|
||||
|
||||
assert_selector "#notice", text: "Password reset for #{username}"
|
||||
notice_text = find("p#notice").text
|
||||
new_password = notice_text.match(/New password: (.+)/)[1]
|
||||
|
||||
sign_out(teacher)
|
||||
visit new_user_session_path
|
||||
|
||||
fill_in "Username", with: username
|
||||
fill_in "Password", with: new_password
|
||||
click_button "Sign in"
|
||||
|
||||
assert_selector "h1", text: "WELCOME TO YOUR FINANCIAL JOURNEY!"
|
||||
end
|
||||
|
||||
test "teacher can edit student information" do
|
||||
old_username = "student_one"
|
||||
new_username = "student_two"
|
||||
classroom = create(:classroom)
|
||||
student = create(:student, classroom:, username: old_username)
|
||||
teacher = create(:teacher)
|
||||
create(:teacher_classroom, teacher:, classroom:)
|
||||
sign_in(teacher)
|
||||
visit classroom_path(classroom)
|
||||
|
||||
find("##{dom_id(student)} [data-testid='edit-student']").click
|
||||
fill_in "Username", with: new_username
|
||||
click_button "Update Student"
|
||||
|
||||
assert_selector "#notice", text: "Student updated successfully"
|
||||
assert_selector "[data-testid='student-username']", text: new_username
|
||||
assert_no_selector "[data-testid='student-username']", text: old_username
|
||||
end
|
||||
|
||||
test "teacher can delete a student" do
|
||||
skip "Flaky modal test - to be fixed in future PR"
|
||||
username = "student_one"
|
||||
classroom = create(:classroom)
|
||||
student = create(:student, :with_portfolio, classroom:, username:)
|
||||
teacher = create(:teacher)
|
||||
create(:teacher_classroom, teacher:, classroom:)
|
||||
sign_in(teacher)
|
||||
visit classroom_path(classroom)
|
||||
|
||||
assert_selector "##{dom_id(student)} [data-testid='delete-student']"
|
||||
accept_confirm do
|
||||
find("##{dom_id(student)} [data-testid='delete-student']").click
|
||||
end
|
||||
|
||||
assert_selector "#notice", text: "Student #{username} deleted successfully"
|
||||
assert_no_selector "[data-testid='student-username']", text: username
|
||||
end
|
||||
|
||||
test "teacher cannot create student with duplicate username" do
|
||||
username = "student_one"
|
||||
classroom = create(:classroom)
|
||||
create(:student, classroom:, username:)
|
||||
teacher = create(:teacher)
|
||||
create(:teacher_classroom, teacher:, classroom:)
|
||||
sign_in(teacher)
|
||||
visit classroom_path(classroom)
|
||||
|
||||
click_on "Add Student"
|
||||
fill_in "Username", with: username
|
||||
click_button "Create Student"
|
||||
|
||||
assert_selector ".field_with_errors", text: "Username"
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,60 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "application_system_test_case"
|
||||
|
||||
class UserManagesClassroomsTest < ApplicationSystemTestCase
|
||||
test "admin can create a new classroom" do
|
||||
school1 = create(:school, name: "Elementary School")
|
||||
create(:school, name: "High School")
|
||||
year1 = create(:year, name: "2023-2024")
|
||||
create(:year, name: "2024-2025")
|
||||
create(:grade, level: 5, name: "5th Grade")
|
||||
admin = create(:admin)
|
||||
sign_in(admin)
|
||||
visit new_classroom_path
|
||||
|
||||
fill_in "Name", with: "Test Classroom"
|
||||
check "5th Grade"
|
||||
select school1.name, from: "classroom_school_id"
|
||||
select year1.name, from: "classroom_year_id"
|
||||
click_on "Create Classroom"
|
||||
|
||||
assert_selector "#notice", text: "Classroom was successfully created"
|
||||
assert_selector "h1", text: "Test Classroom"
|
||||
assert_selector "h1", text: "2023-2024"
|
||||
end
|
||||
|
||||
test "admin can update a classroom" do
|
||||
school1 = create(:school, name: "Original School")
|
||||
school2 = create(:school, name: "New School")
|
||||
year1 = create(:year, name: "2023-2024")
|
||||
year2 = create(:year, name: "2024-2025")
|
||||
grade5 = create(:grade, level: 5, name: "5th Grade")
|
||||
school_year = create(:school_year, school: school1, year: year1)
|
||||
classroom = create(:classroom, name: "Original Name", school_year: school_year, grades: [grade5])
|
||||
admin = create(:admin)
|
||||
sign_in(admin)
|
||||
visit edit_classroom_path(classroom)
|
||||
|
||||
fill_in "Name", with: "Updated Classroom"
|
||||
select school2.name, from: "classroom_school_id"
|
||||
select year2.name, from: "classroom_year_id"
|
||||
click_on "Update Classroom"
|
||||
|
||||
assert_selector "#notice", text: "Classroom was successfully updated"
|
||||
assert_selector "h1", text: "Updated Classroom"
|
||||
assert_selector "h1", text: "2024-2025"
|
||||
end
|
||||
|
||||
test "admin can update trading for a classroom" do
|
||||
classroom = create(:classroom, trading_enabled: false)
|
||||
admin = create(:admin)
|
||||
sign_in(admin)
|
||||
visit classroom_path(classroom)
|
||||
|
||||
check "trading", allow_label_click: true
|
||||
assert_selector "#notice", text: "Trading has been enabled for this classroom."
|
||||
uncheck "trading", allow_label_click: true
|
||||
assert_selector "#notice", text: "Trading has been disabled for this classroom."
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,102 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "application_system_test_case"
|
||||
|
||||
class UserManagesOrdersTest < ApplicationSystemTestCase
|
||||
test "creating a new order" do
|
||||
student = create(:student)
|
||||
student.reload
|
||||
portfolio = student.portfolio
|
||||
create(:portfolio_transaction, :deposit, portfolio: portfolio, amount_cents: 100_000)
|
||||
stock = create(:stock)
|
||||
shares_to_buy = 1
|
||||
|
||||
sign_in(student)
|
||||
|
||||
visit stocks_path
|
||||
|
||||
assert_text "Trading Floor"
|
||||
assert_text stock.ticker
|
||||
|
||||
within "tr", text: stock.company_name do
|
||||
click_on "Buy"
|
||||
end
|
||||
|
||||
fill_in "Number of shares", with: shares_to_buy
|
||||
assert_difference("Order.buy.pending.count", +1) do
|
||||
click_button "Buy Shares"
|
||||
assert_text "Order was successfully created"
|
||||
end
|
||||
|
||||
skip "Number of shares field not visible - needs modal JavaScript fix"
|
||||
|
||||
order = Order.buy.pending.find_by(user: student, stock: stock)
|
||||
assert_equal shares_to_buy, order.shares
|
||||
assert_equal stock.id, order.stock_id
|
||||
|
||||
sign_out(student)
|
||||
end
|
||||
|
||||
test "updating an order" do
|
||||
student = create(:student)
|
||||
student.reload
|
||||
portfolio = student.portfolio
|
||||
create(:portfolio_transaction, :deposit, portfolio: portfolio, amount_cents: 100_000)
|
||||
initial_shares = 4
|
||||
updated_shares = 2
|
||||
order = create(:order, :pending, action: :buy, user: student, shares: initial_shares)
|
||||
|
||||
assert_not_equal initial_shares, updated_shares, "test requires shares to change"
|
||||
|
||||
sign_in(student)
|
||||
|
||||
visit orders_path
|
||||
|
||||
within "tr", text: order.stock.ticker do
|
||||
find("[data-testid='edit-order-button']").click
|
||||
end
|
||||
|
||||
fill_in "Number of shares", with: updated_shares
|
||||
|
||||
assert_no_difference("Order.buy.pending.count") do
|
||||
click_button "Buy Shares"
|
||||
|
||||
assert_text "Order was successfully updated"
|
||||
end
|
||||
|
||||
order.reload
|
||||
assert_equal updated_shares, order.shares
|
||||
|
||||
sign_out(student)
|
||||
end
|
||||
|
||||
test "canceling an order" do
|
||||
student = create(:student)
|
||||
student.reload
|
||||
portfolio = student.portfolio
|
||||
create(:portfolio_transaction, :deposit, portfolio: portfolio, amount_cents: 50_000)
|
||||
order = create(:order, :pending, action: :buy, user: student)
|
||||
|
||||
sign_in(student)
|
||||
|
||||
visit orders_path
|
||||
|
||||
assert_difference -> { Order.pending.count } => -1, -> { Order.canceled.count } => +1 do
|
||||
accept_confirm do
|
||||
within "tr", text: order.stock.company_name do
|
||||
find("[data-testid='cancel-order-button']").click
|
||||
end
|
||||
end
|
||||
|
||||
assert_text "Order was successfully canceled"
|
||||
end
|
||||
|
||||
order.reload
|
||||
assert order.canceled?
|
||||
within "tr", text: order.stock.company_name do
|
||||
assert_text "Canceled"
|
||||
end
|
||||
|
||||
sign_out(student)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,43 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "application_system_test_case"
|
||||
|
||||
class UserManagesSchoolsTest < ApplicationSystemTestCase
|
||||
test "admin can create a school" do
|
||||
admin = create(:admin)
|
||||
sign_in(admin)
|
||||
visit admin_schools_path
|
||||
|
||||
click_on "New School"
|
||||
fill_in "Name", with: "Test School"
|
||||
click_on "Create School"
|
||||
|
||||
assert_selector "#notice", text: "School created successfully"
|
||||
end
|
||||
|
||||
test "admin can update a school" do
|
||||
school = create(:school, name: "Original School")
|
||||
admin = create(:admin)
|
||||
sign_in(admin)
|
||||
visit admin_school_url(school)
|
||||
|
||||
click_on "Edit"
|
||||
fill_in "Name", with: "Updated School"
|
||||
click_on "Update School"
|
||||
|
||||
assert_selector "#notice", text: "School updated successfully"
|
||||
end
|
||||
|
||||
test "admin can delete a school" do
|
||||
school = create(:school, name: "School To Delete")
|
||||
admin = create(:admin)
|
||||
sign_in(admin)
|
||||
visit admin_school_url(school)
|
||||
|
||||
accept_confirm do
|
||||
click_on "Delete"
|
||||
end
|
||||
|
||||
assert_selector "#notice", text: "School deleted successfully"
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user