2020-10-27 00:51:19 +08:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2011-11-22 07:19:04 +08:00
|
|
|
#
|
2017-04-28 12:02:05 +08:00
|
|
|
# Copyright (C) 2011 - present Instructure, Inc.
|
2011-11-22 07:19:04 +08:00
|
|
|
#
|
|
|
|
# This file is part of Canvas.
|
|
|
|
#
|
|
|
|
# Canvas is free software: you can redistribute it and/or modify it under
|
|
|
|
# the terms of the GNU Affero General Public License as published by the Free
|
|
|
|
# Software Foundation, version 3 of the License.
|
|
|
|
#
|
|
|
|
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
|
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
|
|
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
|
|
# details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU Affero General Public License along
|
|
|
|
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
#
|
|
|
|
|
2011-10-18 06:41:32 +08:00
|
|
|
describe CoursesHelper do
|
|
|
|
include ApplicationHelper
|
2017-02-10 04:03:11 +08:00
|
|
|
include AssignmentsHelper
|
2011-10-18 06:41:32 +08:00
|
|
|
include CoursesHelper
|
2012-10-10 00:41:11 +08:00
|
|
|
include QuizzesHelper
|
2011-10-18 06:41:32 +08:00
|
|
|
|
|
|
|
context "a view with a 'Coming Up' sidebar" do
|
2014-07-07 11:03:53 +08:00
|
|
|
before(:once) do
|
2015-05-08 04:58:50 +08:00
|
|
|
course_with_teacher(active_all: true)
|
2015-10-23 06:39:52 +08:00
|
|
|
@assignment = factory_with_protected_attributes(@course.assignments,
|
|
|
|
assignment_valid_attributes.merge({ points_possible: 10,
|
|
|
|
submission_types: "online_text_entry" }))
|
|
|
|
@assignment2 = factory_with_protected_attributes(@course.assignments,
|
|
|
|
assignment_valid_attributes.merge({ points_possible: 10,
|
|
|
|
submission_types: "none" }))
|
2011-10-18 06:41:32 +08:00
|
|
|
end
|
|
|
|
|
2021-11-10 08:07:22 +08:00
|
|
|
before do
|
2014-07-07 11:03:53 +08:00
|
|
|
user_session(@user)
|
|
|
|
end
|
|
|
|
|
2011-10-18 06:41:32 +08:00
|
|
|
describe "an assignment with no submissions" do
|
2015-10-23 06:39:52 +08:00
|
|
|
before(:once) do
|
|
|
|
@student_one = factory_with_protected_attributes(User, valid_user_attributes)
|
|
|
|
@student_two = factory_with_protected_attributes(User, valid_user_attributes)
|
|
|
|
[@student_one, @student_two].each do |student|
|
|
|
|
e = @course.enroll_student(student)
|
|
|
|
e.invite
|
|
|
|
e.accept
|
|
|
|
end
|
|
|
|
@assignment.reload
|
|
|
|
end
|
|
|
|
|
2021-09-23 22:17:23 +08:00
|
|
|
it "returns a no submission tooltip if there are no submissions" do
|
2017-07-26 06:06:35 +08:00
|
|
|
expect(self).to receive(:t).with("#courses.recent_event.no_submissions", "no submissions").and_return("no submissions")
|
2015-10-23 06:39:52 +08:00
|
|
|
check_icon_data("no submissions", "Assignment", "icon-assignment")
|
|
|
|
end
|
|
|
|
|
2021-09-23 22:17:23 +08:00
|
|
|
it "returns a not submitted tooltip for a student if they have not made a submission" do
|
2017-07-26 06:06:35 +08:00
|
|
|
expect(self).to receive(:t).with("#courses.recent_event.not_submitted", "not submitted").and_return("not submitted")
|
2015-10-23 06:39:52 +08:00
|
|
|
check_icon_data("not submitted", "Assignment", "icon-assignment", current_user: @student_one)
|
|
|
|
end
|
|
|
|
|
2021-09-23 22:17:23 +08:00
|
|
|
it "returns a nil tooltip for a student if the assignment does not expect a submission" do
|
2017-07-26 06:06:35 +08:00
|
|
|
expect(self).to receive(:t).with("#courses.recent_event.not_submitted", "not submitted").and_return("not submitted")
|
2015-10-23 06:39:52 +08:00
|
|
|
check_icon_data(nil, "Assignment", "icon-assignment", current_user: @student_one, recent_event: @assignment2)
|
2011-10-18 06:41:32 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "an assignment with submissions" do
|
2014-07-07 11:03:53 +08:00
|
|
|
before(:once) do
|
2011-10-18 06:41:32 +08:00
|
|
|
@student_one = factory_with_protected_attributes(User, valid_user_attributes)
|
|
|
|
@student_two = factory_with_protected_attributes(User, valid_user_attributes)
|
|
|
|
[@student_one, @student_two].each do |student|
|
|
|
|
e = @course.enroll_student(student)
|
|
|
|
e.invite
|
|
|
|
e.accept
|
|
|
|
end
|
|
|
|
@assignment.reload
|
|
|
|
end
|
|
|
|
|
2021-09-23 22:17:23 +08:00
|
|
|
it "returns a needs grading tooltip if assignments have been submitted that aren't graded" do
|
2017-07-26 06:06:35 +08:00
|
|
|
expect(self).to receive(:t).with("#courses.recent_event.needs_grading", "needs grading").and_return("needs grading")
|
2012-01-25 10:31:17 +08:00
|
|
|
@assignment.submit_homework(@student_one, { submission_type: "online_text_entry", body: "..." })
|
2015-10-23 06:39:52 +08:00
|
|
|
check_icon_data("needs grading", "Assignment", "icon-assignment")
|
|
|
|
end
|
|
|
|
|
2021-09-23 22:17:23 +08:00
|
|
|
it "returns the submission's readable_state as the tooltip for a student" do
|
2015-10-23 06:39:52 +08:00
|
|
|
submission = @assignment.submit_homework(@student_one, { submission_type: "online_text_entry", body: "..." })
|
2023-06-02 06:06:09 +08:00
|
|
|
check_icon_data(submission.readable_state, "", "icon-check", current_user: @student_one, submission:)
|
2015-10-23 06:39:52 +08:00
|
|
|
end
|
|
|
|
|
2021-09-23 22:17:23 +08:00
|
|
|
it "returns an assignment icon instead of a check icon if show_assignment_type_icon is set" do
|
2015-10-23 06:39:52 +08:00
|
|
|
submission = @assignment.submit_homework(@student_two, { submission_type: "online_text_entry", body: "..." })
|
|
|
|
check_icon_data(submission.readable_state,
|
|
|
|
"Assignment",
|
|
|
|
"icon-assignment",
|
|
|
|
current_user: @student_two,
|
2023-06-02 06:06:09 +08:00
|
|
|
submission:,
|
2015-10-23 06:39:52 +08:00
|
|
|
show_assignment_type_icon: true)
|
2011-10-18 06:41:32 +08:00
|
|
|
end
|
|
|
|
|
2021-09-23 22:17:23 +08:00
|
|
|
it "returns a no new submissions tooltip if some assignments have been submitted and graded" do
|
2017-07-26 06:06:35 +08:00
|
|
|
expect(self).to receive(:t).with("#courses.recent_event.no_new_submissions", "no new submissions").and_return("no new submissions")
|
2012-01-25 10:31:17 +08:00
|
|
|
@assignment.submit_homework(@student_one, { submission_type: "online_text_entry", body: "xyz" })
|
2016-11-15 05:36:56 +08:00
|
|
|
@assignment.grade_student(@student_one, grade: 5, grader: @teacher)
|
2015-10-23 06:39:52 +08:00
|
|
|
check_icon_data("no new submissions", "Assignment", "icon-assignment")
|
2011-10-18 06:41:32 +08:00
|
|
|
end
|
|
|
|
|
2021-09-23 22:17:23 +08:00
|
|
|
it "returns an all graded tooltip if all assignments are submitted and graded" do
|
2017-07-26 06:06:35 +08:00
|
|
|
expect(self).to receive(:t).with("#courses.recent_event.all_graded", "all graded").and_return("all graded")
|
2011-10-18 06:41:32 +08:00
|
|
|
[@student_one, @student_two].each do |student|
|
2012-01-25 10:31:17 +08:00
|
|
|
@assignment.submit_homework(student, { submission_type: "online_text_entry", body: "bod" })
|
2016-11-15 05:36:56 +08:00
|
|
|
@assignment.grade_student(student, grade: 5, grader: @teacher)
|
2011-10-18 06:41:32 +08:00
|
|
|
end
|
2015-10-23 06:39:52 +08:00
|
|
|
check_icon_data("all graded", "Assignment", "icon-assignment")
|
2011-10-18 06:41:32 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-10-23 06:39:52 +08:00
|
|
|
def check_icon_data(msg, aria_label, icon, options = {})
|
|
|
|
base_options = {
|
|
|
|
context: @course,
|
|
|
|
contexts: [@course],
|
|
|
|
current_user: @teacher,
|
|
|
|
recent_event: @assignment,
|
|
|
|
submission: nil
|
|
|
|
}.merge(options)
|
|
|
|
@icon_explanation, @icon_aria_label, @icon_class = icon_data(base_options)
|
2014-10-14 01:48:10 +08:00
|
|
|
expect(@icon_explanation).to eql msg
|
2015-10-23 06:39:52 +08:00
|
|
|
expect(@icon_aria_label).to eql aria_label
|
2014-10-14 01:48:10 +08:00
|
|
|
expect(@icon_class).to eql icon
|
2011-10-18 06:41:32 +08:00
|
|
|
end
|
|
|
|
end
|
2012-10-10 00:41:11 +08:00
|
|
|
|
|
|
|
context "readable_grade" do
|
2021-09-23 22:17:23 +08:00
|
|
|
it "returns nil if not graded" do
|
2012-10-10 00:41:11 +08:00
|
|
|
submission = Submission.new
|
2014-10-14 01:48:10 +08:00
|
|
|
expect(readable_grade(submission)).to be_nil
|
2012-10-10 00:41:11 +08:00
|
|
|
end
|
|
|
|
|
2021-09-23 22:17:23 +08:00
|
|
|
it "returns the score if graded" do
|
2018-04-17 23:07:21 +08:00
|
|
|
assignment = Assignment.new(points_possible: 5, grading_type: "points")
|
2023-06-02 06:06:09 +08:00
|
|
|
submission = Submission.new(grade: 1.33333333, workflow_state: "graded", assignment:)
|
2014-10-14 01:48:10 +08:00
|
|
|
expect(readable_grade(submission)).to eq "1.33 out of 5"
|
2012-10-10 00:41:11 +08:00
|
|
|
end
|
|
|
|
|
2021-09-23 22:17:23 +08:00
|
|
|
it "does not raise an error when passing a numeric type but grading_type is not 'points'" do
|
2018-04-17 23:07:21 +08:00
|
|
|
assignment = Assignment.new(points_possible: 5, grading_type: "percent")
|
2023-06-02 06:06:09 +08:00
|
|
|
submission = Submission.new(grade: 1.33333333, workflow_state: "graded", assignment:)
|
2017-11-08 14:36:17 +08:00
|
|
|
expect(readable_grade(submission)).to eq "1.33333%"
|
2012-10-10 00:41:11 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
pass user-related params to docviewer
When talking to DocViewer, include relevant data for the current user as
well as the filter of users whose comments should be shown. This commit
enables functionality for anonymous annotations.
closes GRADE-1427
closes GRADE-1456
Test Plan 1: Moderated Assignments
1. Create a moderated assignment and allow for at least two provisional
graders in addition to the final grader. Then, leave at least one
annotation and one comment per provisional grader, final grader, and
the student.
2. When "Graders cannot view student names" is checked, verify that
no instructor or admin can see the students identity on annotaions.
Instead, the student's name should show up simply as 'Student'.
3. When "Graders cannot view each other's names" is checked, verify that
non-admin, non-final-grader provisional graders cannot see each
other's names on annotations. Instead, they should see a generic
grader name such as "Grader 1".
4. When "Final grader can view other grader names" is unchecked, verify
the final grader cannot view the other graders' names on annotations.
Instead, they should see a generic grader name such as "Grader 1".
5. Smoke test the settings listed in steps 2, 3, and 4 in various
combinations of being on or off.
6. While the assignment is still in moderation, verify the student can
only see their own annotations.
7. When grades are published for the assignment, verify the assignment
no longer shows any anonymous annotations.
Test Plan 2: Anonymous, Not Moderated Assignments
1. Create an anonymous assignment. Submit to the assignment as a student
and leave some annotations as the student and as an instructor.
2. Verify the student can only see their own annotations while the
assignment is still muted.
3. An instructor *should* be able to see any annotations made by an
instructor, but DocViewer has not implemented this functionality
on their side yet. As a result, just verify that an instructor
can see the student's annotations but they are anonymized while
the assignment is muted.
4. Unmute the assignment and verify the annotations are no longer
anonymized, and the student can now see annotations from instructors.
Test Plan 3: Normal, Not Anonymous Assignments
1. Do a general smoke test of not anonymous, not moderated assignments
to verify annotations still show up as expected.
Change-Id: I181a6ace3c00ca93ab8e6c7608a034b521ed78b7
Reviewed-on: https://gerrit.instructure.com/161486
Reviewed-by: Derek Bender <djbender@instructure.com>
Tested-by: Jenkins
Reviewed-by: Keith T. Garner <kgarner@instructure.com>
QA-Review: Derek Bender <djbender@instructure.com>
Product-Review: Keith T. Garner <kgarner@instructure.com>
2018-08-11 02:52:14 +08:00
|
|
|
describe "#user_type" do
|
2018-07-12 05:05:59 +08:00
|
|
|
let(:admin) { account_admin_user(account: Account.default, active_user: true) }
|
|
|
|
let(:course) { Account.default.courses.create! }
|
2023-06-02 06:06:09 +08:00
|
|
|
let(:teacher) { teacher_in_course(course:, active_all: true).user }
|
|
|
|
let(:ta) { ta_in_course(course:, active_all: true).user }
|
|
|
|
let(:student) { student_in_course(course:, active_all: true).user }
|
2018-07-12 05:05:59 +08:00
|
|
|
let(:test_student) { course.student_view_student }
|
|
|
|
let(:rando) { User.create! }
|
|
|
|
let(:observer) do
|
|
|
|
observer_user = User.create!
|
|
|
|
enrollment = course.enroll_user(observer_user, "ObserverEnrollment")
|
|
|
|
enrollment.update!(workflow_state: "active", associated_user: student)
|
|
|
|
observer_user
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns nil for random users with no course association" do
|
|
|
|
expect(user_type(course, rando)).to be_nil
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns 'teacher' for TeacherEnrollments" do
|
|
|
|
expect(user_type(course, teacher)).to eq "teacher"
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns 'ta' for TaEnrollments" do
|
|
|
|
expect(user_type(course, ta)).to eq "ta"
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns 'student' for StudentEnrollments" do
|
|
|
|
expect(user_type(course, student)).to eq "student"
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns 'student' for StudentViewEnrollments" do
|
|
|
|
expect(user_type(course, test_student)).to eq "student"
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns 'student' for ObserverEnrollments" do
|
|
|
|
expect(user_type(course, observer)).to eq "student"
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns 'admin' for admin enrollments" do
|
|
|
|
expect(user_type(course, admin)).to eq "admin"
|
|
|
|
end
|
|
|
|
|
pass user-related params to docviewer
When talking to DocViewer, include relevant data for the current user as
well as the filter of users whose comments should be shown. This commit
enables functionality for anonymous annotations.
closes GRADE-1427
closes GRADE-1456
Test Plan 1: Moderated Assignments
1. Create a moderated assignment and allow for at least two provisional
graders in addition to the final grader. Then, leave at least one
annotation and one comment per provisional grader, final grader, and
the student.
2. When "Graders cannot view student names" is checked, verify that
no instructor or admin can see the students identity on annotaions.
Instead, the student's name should show up simply as 'Student'.
3. When "Graders cannot view each other's names" is checked, verify that
non-admin, non-final-grader provisional graders cannot see each
other's names on annotations. Instead, they should see a generic
grader name such as "Grader 1".
4. When "Final grader can view other grader names" is unchecked, verify
the final grader cannot view the other graders' names on annotations.
Instead, they should see a generic grader name such as "Grader 1".
5. Smoke test the settings listed in steps 2, 3, and 4 in various
combinations of being on or off.
6. While the assignment is still in moderation, verify the student can
only see their own annotations.
7. When grades are published for the assignment, verify the assignment
no longer shows any anonymous annotations.
Test Plan 2: Anonymous, Not Moderated Assignments
1. Create an anonymous assignment. Submit to the assignment as a student
and leave some annotations as the student and as an instructor.
2. Verify the student can only see their own annotations while the
assignment is still muted.
3. An instructor *should* be able to see any annotations made by an
instructor, but DocViewer has not implemented this functionality
on their side yet. As a result, just verify that an instructor
can see the student's annotations but they are anonymized while
the assignment is muted.
4. Unmute the assignment and verify the annotations are no longer
anonymized, and the student can now see annotations from instructors.
Test Plan 3: Normal, Not Anonymous Assignments
1. Do a general smoke test of not anonymous, not moderated assignments
to verify annotations still show up as expected.
Change-Id: I181a6ace3c00ca93ab8e6c7608a034b521ed78b7
Reviewed-on: https://gerrit.instructure.com/161486
Reviewed-by: Derek Bender <djbender@instructure.com>
Tested-by: Jenkins
Reviewed-by: Keith T. Garner <kgarner@instructure.com>
QA-Review: Derek Bender <djbender@instructure.com>
Product-Review: Keith T. Garner <kgarner@instructure.com>
2018-08-11 02:52:14 +08:00
|
|
|
it "can optionally be passed preloaded enrollments" do
|
|
|
|
enrollments = course.enrollments.index_by(&:user_id)
|
|
|
|
expect(course).not_to receive(:enrollments)
|
|
|
|
user_type(course, teacher, enrollments)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns the correct user type when passed preloaded enrollments" do
|
|
|
|
enrollments = teacher && course.enrollments.index_by(&:user_id)
|
|
|
|
expect(user_type(course, teacher, enrollments)).to eq "teacher"
|
|
|
|
end
|
|
|
|
end
|
2022-10-05 00:38:06 +08:00
|
|
|
|
|
|
|
describe "#sortable_tabs" do
|
|
|
|
it "returns tool tabs" do
|
|
|
|
tool = external_tool_model(context: course_model)
|
|
|
|
tool.course_navigation = { enabled: true }
|
|
|
|
tool.save
|
|
|
|
controller = CoursesController.new
|
|
|
|
controller.instance_variable_set(:@context, tool.context)
|
|
|
|
tabs = controller.sortable_tabs
|
|
|
|
|
|
|
|
tool_tab = tabs.find { |t| Lti::ExternalToolTab.tool_for_tab(t) == tool }
|
|
|
|
expect(tool_tab[:args][1]).to eq(tool.id)
|
|
|
|
end
|
|
|
|
|
|
|
|
context "when given a quizzes tool tab" do
|
|
|
|
before do
|
|
|
|
allow_any_instance_of(ContextExternalTool).to receive(:quiz_lti?).and_return(true)
|
|
|
|
end
|
|
|
|
|
|
|
|
context "quizzes is enabled for the course" do
|
|
|
|
it "includes the tab" do
|
|
|
|
tool = external_tool_model(context: course_model)
|
|
|
|
tool.course_navigation = { enabled: true }
|
|
|
|
tool.save
|
|
|
|
controller = CoursesController.new
|
|
|
|
controller.instance_variable_set(:@context, tool.context)
|
|
|
|
|
|
|
|
Account.site_admin.enable_feature! :assignments_2_teacher
|
|
|
|
allow(controller).to receive(:new_quizzes_navigation_placements_enabled?).with(tool.context).and_return(true)
|
|
|
|
|
|
|
|
tabs = controller.sortable_tabs
|
|
|
|
tool_tab = tabs.find { |t| Lti::ExternalToolTab.tool_for_tab(t) == tool }
|
|
|
|
expect(tool_tab[:args][1]).to eq(tool.id)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context "quizzes is disabled for the account/course" do
|
|
|
|
it "doesn't include the tab" do
|
|
|
|
tool = external_tool_model(context: course_model)
|
|
|
|
tool.course_navigation = { enabled: true }
|
|
|
|
tool.save
|
|
|
|
controller = CoursesController.new
|
|
|
|
controller.instance_variable_set(:@context, tool.context)
|
|
|
|
|
|
|
|
Account.site_admin.disable_feature! :assignments_2_teacher
|
|
|
|
|
|
|
|
tabs = controller.sortable_tabs
|
|
|
|
tool_tab = tabs.find { |t| Lti::ExternalToolTab.tool_for_tab(t) == tool }
|
|
|
|
expect(tool_tab).to be_nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2024-05-01 02:16:30 +08:00
|
|
|
|
|
|
|
describe "#format_course_section_date" do
|
|
|
|
it "returns formatted date when date provided" do
|
|
|
|
date = Time.zone.parse("January 14, 2019")
|
|
|
|
expect(format_course_section_date(date)).to eq "Jan 14, 2019"
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns string (no date) when date not provided" do
|
|
|
|
expect(self).to receive(:t).with("#courses.sections.no_date", "(no date)").and_return("(no date)")
|
|
|
|
expect(format_course_section_date).to eq "(no date)"
|
|
|
|
end
|
|
|
|
end
|
2011-10-18 06:41:32 +08:00
|
|
|
end
|