allow submissions with comments to be posted
Submissions without grades, but with comments from instructors, will now be postable. closes APG-102 Test Plan - Enable the Allow Postable Submission Comments feature flag. - Create a manually posted assignment (NORMAL). - Create a peer review, manually posted assignment (PEER). - Create a group, manually posted assignment (GROUP). NORMAL - Verify that the option to post grades for the assignment is disabled, in both Gradebook and SpeedGrader. - As a teacher, leave a comment on a student's submission. - Reload the Gradebook. - Verify that the option to post grades is available for both Gradebook and SpeedGrader. - Verify posting grades works. - Verify hiding grades works. - After hiding grades, post grades for Graded only. - Verify that the submission with a comment is posted. - Verify that submissions with no comments and no grades remain unposted. - Hide grades. - Change the manually posted assignment to be automatically posted. - Verify posting grades works. - Verify hiding grades works. - Disable the Allow Postable Submission Comments feature flag. - Verify that submission comments on a manual posting assignment are not enough to allow posting of the assignment. PEER - Set up peer reviews between two students. - Submit to the assignment for each student. - Comment as students for self, and for the peer review. - Verify that the option to post grades is not available for both Gradebook and SpeedGrader. - Comment as a teacher on one of the submissions. - Verify that the option to post grades is available for both Gradebook and SpeedGrader. GROUP - Set up a group with at least 2 students. - Set up a group assignment. - As one student, submit to the assignment and leave a comment. - As the other student in the group, leave a comment. - Verify that the option to post grades is not available for both Gradebook and SpeedGrader. - Comment as a teacher on one of the submissions. - Verify that the option to post grades is available for both Gradebook and SpeedGrader. Change-Id: Ia33b757d6c0acdb8cb915f980e81b3b485abc61a Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/208747 Tested-by: Jenkins Reviewed-by: Adrian Packel <apackel@instructure.com> Reviewed-by: Spencer Olson <solson@instructure.com> QA-Review: Adrian Packel <apackel@instructure.com> Product-Review: Jonathan Fenton <jfenton@instructure.com>
This commit is contained in:
parent
fd70765313
commit
6f3e62a013
|
@ -75,7 +75,7 @@ import assignmentHelper from 'jsx/gradezilla/shared/helpers/assignmentHelper'
|
|||
import TextMeasure from 'jsx/gradezilla/shared/helpers/TextMeasure'
|
||||
import * as GradeInputHelper from 'jsx/grading/helpers/GradeInputHelper'
|
||||
import OutlierScoreHelper from 'jsx/grading/helpers/OutlierScoreHelper'
|
||||
import {isHidden} from 'jsx/grading/helpers/SubmissionHelper'
|
||||
import {isPostable} from 'jsx/grading/helpers/SubmissionHelper'
|
||||
import LatePolicyApplicator from 'jsx/grading/LatePolicyApplicator'
|
||||
import {Button} from '@instructure/ui-buttons'
|
||||
import {IconSettingsSolid} from '@instructure/ui-icons'
|
||||
|
@ -2088,7 +2088,7 @@ export default do ->
|
|||
# Ignore anonymous assignments when deciding whether to show the
|
||||
# "hidden" icon, as including them could reveal which students have
|
||||
# and have not been graded
|
||||
submission? && isHidden(submission) && !assignment.anonymize_students
|
||||
submission? && isPostable(submission) && !assignment.anonymize_students
|
||||
)
|
||||
else
|
||||
@filteredContentInfo.mutedAssignments
|
||||
|
|
|
@ -472,6 +472,10 @@ class SubmissionsApiController < ApplicationController
|
|||
end
|
||||
|
||||
if params[:grouped].present?
|
||||
if @context.root_account.feature_enabled?(:allow_postable_submission_comments) && @context.post_policies_enabled?
|
||||
includes << "has_postable_comments"
|
||||
end
|
||||
|
||||
scope = (@section || @context).all_student_enrollments.
|
||||
preload(:root_account, :sis_pseudonym, :user => :pseudonyms).
|
||||
where(:user_id => student_ids).order(:user_id)
|
||||
|
@ -483,8 +487,17 @@ class SubmissionsApiController < ApplicationController
|
|||
if params[:workflow_state].present?
|
||||
submissions_scope = submissions_scope.where(:workflow_state => params[:workflow_state])
|
||||
end
|
||||
submissions_scope = submissions_scope.preload(:attachment) unless params[:exclude_response_fields]&.include?('attachments')
|
||||
submissions = submissions_scope.preload(:originality_reports, :quiz_submission).to_a
|
||||
|
||||
submission_preloads = [:originality_reports, :quiz_submission]
|
||||
submission_preloads << :attachment unless params[:exclude_response_fields]&.include?("attachments")
|
||||
submissions = submissions_scope.preload(submission_preloads).to_a
|
||||
|
||||
ActiveRecord::Associations::Preloader.new.preload(
|
||||
submissions,
|
||||
:submission_comments,
|
||||
{select: [:hidden, :submission_id]}
|
||||
)
|
||||
|
||||
bulk_load_attachments_and_previews(submissions)
|
||||
submissions_for_user = submissions.group_by(&:user_id)
|
||||
|
||||
|
|
|
@ -58,9 +58,15 @@ class Mutations::PostAssignmentGrades < Mutations::BaseMutation
|
|||
visible_enrollments = visible_enrollments.where(user_id: input[:only_student_ids]) if input[:only_student_ids]
|
||||
visible_enrollments = visible_enrollments.where.not(user_id: input[:skip_student_ids]) if input[:skip_student_ids]
|
||||
|
||||
submissions_scope = input[:graded_only] ? assignment.submissions.graded : assignment.submissions
|
||||
submissions_scope = submissions_scope.joins(user: :enrollments).merge(visible_enrollments)
|
||||
submissions_scope = if input[:graded_only] && course.root_account.feature_enabled?(:allow_postable_submission_comments)
|
||||
assignment.submissions.postable
|
||||
elsif input[:graded_only]
|
||||
assignment.submissions.graded
|
||||
else
|
||||
assignment.submissions
|
||||
end
|
||||
|
||||
submissions_scope = submissions_scope.joins(user: :enrollments).merge(visible_enrollments)
|
||||
submission_ids = submissions_scope.pluck(:id)
|
||||
progress = course.progresses.new(tag: "post_assignment_grades")
|
||||
|
||||
|
|
|
@ -50,7 +50,14 @@ class Mutations::PostAssignmentGradesForSections < Mutations::BaseMutation
|
|||
|
||||
visible_enrollments = course.apply_enrollment_visibility(course.student_enrollments, current_user, sections)
|
||||
|
||||
submissions_scope = input[:graded_only] ? assignment.submissions.graded : assignment.submissions
|
||||
submissions_scope = if input[:graded_only] && course.root_account.feature_enabled?(:allow_postable_submission_comments)
|
||||
assignment.submissions.postable
|
||||
elsif input[:graded_only]
|
||||
assignment.submissions.graded
|
||||
else
|
||||
assignment.submissions
|
||||
end
|
||||
|
||||
submissions_scope = submissions_scope.joins(user: :enrollments).merge(visible_enrollments)
|
||||
|
||||
progress = course.progresses.new(tag: "post_assignment_grades_for_sections")
|
||||
|
|
|
@ -22,7 +22,7 @@ import I18n from 'i18n!gradezilla'
|
|||
import htmlEscape from 'str/htmlEscape'
|
||||
import {extractDataTurnitin} from 'compiled/gradezilla/Turnitin'
|
||||
import GradeFormatHelper from '../../../../gradebook/shared/helpers/GradeFormatHelper'
|
||||
import {extractSimilarityInfo, isHidden} from '../../../../grading/helpers/SubmissionHelper'
|
||||
import {extractSimilarityInfo, isPostable} from '../../../../grading/helpers/SubmissionHelper'
|
||||
import {classNamesForAssignmentCell} from './CellStyles'
|
||||
|
||||
function getTurnitinState(submission) {
|
||||
|
@ -214,7 +214,7 @@ export default class AssignmentCellFormatter {
|
|||
}
|
||||
|
||||
const showUnpostedIndicator =
|
||||
columnDef.postAssignmentGradesTrayOpenForAssignmentId && isHidden(submission)
|
||||
columnDef.postAssignmentGradesTrayOpenForAssignmentId && isPostable(submission)
|
||||
|
||||
const options = {
|
||||
classNames: classNamesForAssignmentCell(assignmentData, submissionData),
|
||||
|
|
|
@ -26,7 +26,7 @@ import {Text} from '@instructure/ui-elements'
|
|||
import 'message_students'
|
||||
import I18n from 'i18n!gradezilla'
|
||||
import {ScreenReaderContent} from '@instructure/ui-a11y'
|
||||
import {isHidden} from '../../../../grading/helpers/SubmissionHelper'
|
||||
import {isPostable} from '../../../../grading/helpers/SubmissionHelper'
|
||||
import MessageStudentsWhoHelper from '../../../shared/helpers/messageStudentsWhoHelper'
|
||||
import ColumnHeader from './ColumnHeader'
|
||||
|
||||
|
@ -89,23 +89,23 @@ SecondaryDetailLine.propTypes = {
|
|||
}
|
||||
|
||||
function labelForPostGradesAction(postGradesAction) {
|
||||
if (!postGradesAction.hasGrades) {
|
||||
return I18n.t('No grades to post')
|
||||
} else if (postGradesAction.hasGradesToPost) {
|
||||
if (postGradesAction.hasGradesOrCommentsToPost) {
|
||||
return I18n.t('Post grades')
|
||||
} else if (postGradesAction.hasGradesOrPostableComments) {
|
||||
return I18n.t('All grades posted')
|
||||
}
|
||||
|
||||
return I18n.t('All grades posted')
|
||||
return I18n.t('No grades to post')
|
||||
}
|
||||
|
||||
function labelForHideGradesAction(hideGradesAction) {
|
||||
if (!hideGradesAction.hasGrades) {
|
||||
return I18n.t('No grades to hide')
|
||||
} else if (hideGradesAction.hasGradesToHide) {
|
||||
if (hideGradesAction.hasGradesOrCommentsToHide) {
|
||||
return I18n.t('Hide grades')
|
||||
} else if (hideGradesAction.hasGradesOrPostableComments) {
|
||||
return I18n.t('All grades hidden')
|
||||
}
|
||||
|
||||
return I18n.t('All grades hidden')
|
||||
return I18n.t('No grades to hide')
|
||||
}
|
||||
|
||||
export default class AssignmentColumnHeader extends ColumnHeader {
|
||||
|
@ -131,13 +131,14 @@ export default class AssignmentColumnHeader extends ColumnHeader {
|
|||
}).isRequired,
|
||||
|
||||
hideGradesAction: shape({
|
||||
hasGradesToHide: bool.isRequired,
|
||||
hasGradesOrCommentsToHide: bool.isRequired,
|
||||
onSelect: func.isRequired
|
||||
}).isRequired,
|
||||
|
||||
postGradesAction: shape({
|
||||
featureEnabled: bool.isRequired,
|
||||
hasGradesToPost: bool.isRequired,
|
||||
hasGradesOrPostableComments: bool.isRequired,
|
||||
hasGradesOrCommentsToPost: bool.isRequired,
|
||||
newIconsEnabled: bool.isRequired,
|
||||
onSelect: func.isRequired
|
||||
}).isRequired,
|
||||
|
@ -423,9 +424,7 @@ export default class AssignmentColumnHeader extends ColumnHeader {
|
|||
|
||||
{this.props.postGradesAction.featureEnabled ? (
|
||||
<Menu.Item
|
||||
disabled={
|
||||
!this.props.postGradesAction.hasGradesToPost || !this.props.postGradesAction.hasGrades
|
||||
}
|
||||
disabled={!this.props.postGradesAction.hasGradesOrCommentsToPost}
|
||||
onSelect={this.postGrades}
|
||||
>
|
||||
{labelForPostGradesAction(this.props.postGradesAction)}
|
||||
|
@ -445,9 +444,7 @@ export default class AssignmentColumnHeader extends ColumnHeader {
|
|||
|
||||
{this.props.postGradesAction.featureEnabled && (
|
||||
<Menu.Item
|
||||
disabled={
|
||||
!this.props.hideGradesAction.hasGradesToHide || !this.props.hideGradesAction.hasGrades
|
||||
}
|
||||
disabled={!this.props.hideGradesAction.hasGradesOrCommentsToHide}
|
||||
onSelect={this.hideGrades}
|
||||
>
|
||||
{labelForHideGradesAction(this.props.hideGradesAction)}
|
||||
|
@ -507,7 +504,7 @@ export default class AssignmentColumnHeader extends ColumnHeader {
|
|||
}
|
||||
|
||||
const submissions = this.props.students.map(student => student.submission)
|
||||
const postableSubmissionsPresent = submissions.some(isHidden)
|
||||
const postableSubmissionsPresent = submissions.some(isPostable)
|
||||
|
||||
if (newIconsEnabled) {
|
||||
// Assignment has at least one hidden submission that can be posted
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import {isGraded, isHidden} from '../../../../grading/helpers/SubmissionHelper'
|
||||
import {isGraded, isPostable} from '../../../../grading/helpers/SubmissionHelper'
|
||||
import {optionsForGradingType} from '../../../shared/EnterGradesAsSetting'
|
||||
import AssignmentColumnHeader from './AssignmentColumnHeader'
|
||||
|
||||
|
@ -26,11 +26,19 @@ function getSubmission(student, assignmentId) {
|
|||
const submission = student[`assignment_${assignmentId}`]
|
||||
|
||||
if (!submission) {
|
||||
return {excused: false, latePolicyStatus: null, postedAt: null, score: null, submittedAt: null}
|
||||
return {
|
||||
excused: false,
|
||||
hasPostableComments: false,
|
||||
latePolicyStatus: null,
|
||||
postedAt: null,
|
||||
score: null,
|
||||
submittedAt: null
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
excused: submission.excused,
|
||||
hasPostableComments: submission.has_postable_comments,
|
||||
latePolicyStatus: submission.late_policy_status,
|
||||
postedAt: submission.posted_at,
|
||||
score: submission.score,
|
||||
|
@ -62,7 +70,9 @@ function getProps(column, gradebook, options) {
|
|||
submission: getSubmission(student, assignmentId)
|
||||
}))
|
||||
|
||||
const hasGrades = students.some(student => isGraded(student.submission))
|
||||
const hasGradesOrPostableComments = students.some(
|
||||
student => isGraded(student.submission) || student.submission.hasPostableComments
|
||||
)
|
||||
|
||||
return {
|
||||
ref: options.ref,
|
||||
|
@ -104,8 +114,8 @@ function getProps(column, gradebook, options) {
|
|||
},
|
||||
|
||||
hideGradesAction: {
|
||||
hasGrades,
|
||||
hasGradesToHide: students.some(student => student.submission.postedAt != null),
|
||||
hasGradesOrPostableComments,
|
||||
hasGradesOrCommentsToHide: students.some(student => student.submission.postedAt != null),
|
||||
onSelect(onExited) {
|
||||
if (gradebook.postPolicies) {
|
||||
gradebook.postPolicies.showHideAssignmentGradesTray({assignmentId, onExited})
|
||||
|
@ -115,8 +125,8 @@ function getProps(column, gradebook, options) {
|
|||
|
||||
postGradesAction: {
|
||||
featureEnabled: gradebook.postPolicies != null,
|
||||
hasGrades,
|
||||
hasGradesToPost: students.some(student => isHidden(student.submission)),
|
||||
hasGradesOrPostableComments,
|
||||
hasGradesOrCommentsToPost: students.some(student => isPostable(student.submission)),
|
||||
newIconsEnabled: !!gradebook.options.new_post_policy_icons_enabled,
|
||||
onSelect(onExited) {
|
||||
if (gradebook.postPolicies) {
|
||||
|
|
|
@ -26,12 +26,14 @@ import PostAssignmentGradesTray from '../../../grading/PostAssignmentGradesTray'
|
|||
|
||||
function getSubmission(student, assignmentId) {
|
||||
const submission = student[`assignment_${assignmentId}`] || {
|
||||
has_postable_comments: false,
|
||||
posted_at: null,
|
||||
score: null,
|
||||
workflow_state: null
|
||||
}
|
||||
|
||||
return {
|
||||
hasPostableComments: !!submission.has_postable_comments,
|
||||
postedAt: submission.posted_at,
|
||||
score: submission.score,
|
||||
workflowState: submission.workflow_state
|
||||
|
|
|
@ -22,7 +22,7 @@ import I18n from 'i18n!gradezilla'
|
|||
import {View} from '@instructure/ui-layout'
|
||||
import {Pill} from '@instructure/ui-elements'
|
||||
import Message from './SubmissionStatus/Message'
|
||||
import {isHidden} from '../../../grading/helpers/SubmissionHelper'
|
||||
import {isPostable} from '../../../grading/helpers/SubmissionHelper'
|
||||
|
||||
export default class SubmissionStatus extends React.Component {
|
||||
static defaultProps = {
|
||||
|
@ -46,6 +46,7 @@ export default class SubmissionStatus extends React.Component {
|
|||
submission: shape({
|
||||
drop: bool,
|
||||
excused: bool,
|
||||
hasPostableComments: bool,
|
||||
postedAt: instanceOf(Date),
|
||||
score: number,
|
||||
workflowState: string.isRequired
|
||||
|
@ -57,7 +58,7 @@ export default class SubmissionStatus extends React.Component {
|
|||
const statusPillComponents = []
|
||||
|
||||
if (postPoliciesEnabled) {
|
||||
if (isHidden(submission)) {
|
||||
if (isPostable(submission)) {
|
||||
statusPillComponents.push(
|
||||
<Pill
|
||||
key="hidden-submission"
|
||||
|
|
|
@ -111,7 +111,8 @@ export default class SubmissionTray extends React.Component {
|
|||
pointsDeducted: number,
|
||||
postedAt: string.isRequired,
|
||||
secondsLate: number.isRequired,
|
||||
assignmentId: string.isRequired
|
||||
assignmentId: string.isRequired,
|
||||
hasPostableComments: bool.isRequired
|
||||
}),
|
||||
isFirstAssignment: bool.isRequired,
|
||||
isLastAssignment: bool.isRequired,
|
||||
|
|
|
@ -44,7 +44,9 @@ export default function PostTypes({anonymousGrading, defaultValue, disabled, pos
|
|||
<>
|
||||
<Text>{I18n.t('Everyone')}</Text>
|
||||
<br />
|
||||
<Text size="small">{I18n.t('Grades will be made visible to all students')}</Text>
|
||||
<Text size="small">
|
||||
{I18n.t('All students will be able to see their grade and/or submission comments.')}
|
||||
</Text>
|
||||
</>
|
||||
}
|
||||
value={EVERYONE}
|
||||
|
@ -56,7 +58,9 @@ export default function PostTypes({anonymousGrading, defaultValue, disabled, pos
|
|||
<Text>{I18n.t('Graded')}</Text>
|
||||
<br />
|
||||
<Text size="small">
|
||||
{I18n.t('Grades will be made visible to students with graded submissions')}
|
||||
{I18n.t(
|
||||
'Students who have received a grade or a submission comment will be able to see their grade and/or submission comments.'
|
||||
)}
|
||||
</Text>
|
||||
</>
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ import {
|
|||
postAssignmentGradesForSections,
|
||||
resolvePostAssignmentGradesStatus
|
||||
} from './Api'
|
||||
import {isHidden} from '../helpers/SubmissionHelper'
|
||||
import {isPostable} from '../helpers/SubmissionHelper'
|
||||
import {showFlashAlert} from '../../shared/FlashAlert'
|
||||
|
||||
function initialShowState() {
|
||||
|
@ -175,7 +175,7 @@ export default class PostAssignmentGradesTray extends PureComponent {
|
|||
return null
|
||||
}
|
||||
|
||||
const unpostedCount = submissions.filter(submission => isHidden(submission)).length
|
||||
const unpostedCount = submissions.filter(submission => isPostable(submission)).length
|
||||
|
||||
return (
|
||||
<Tray
|
||||
|
|
|
@ -24,9 +24,9 @@ export function isGraded(submission) {
|
|||
return (sub.score != null && sub.workflowState === 'graded') || sub.excused
|
||||
}
|
||||
|
||||
export function isHidden(submission) {
|
||||
export function isPostable(submission) {
|
||||
const sub = camelize(submission)
|
||||
return isGraded(sub) && !sub.postedAt
|
||||
return !sub.postedAt && (isGraded(sub) || !!sub.hasPostableComments)
|
||||
}
|
||||
|
||||
// This function returns an object containing plagiarism/originality-related
|
||||
|
|
|
@ -114,6 +114,7 @@ export default class PostPolicies {
|
|||
onPosted,
|
||||
sections: this._sections,
|
||||
submissions: submissions.map(submission => ({
|
||||
hasPostableComments: !!submission.has_postable_comments,
|
||||
postedAt: submission.posted_at,
|
||||
score: submission.score,
|
||||
workflowState: submission.workflow_state
|
||||
|
|
|
@ -25,7 +25,8 @@ import {Text} from '@instructure/ui-elements'
|
|||
import I18n from 'i18n!SpeedGraderPostGradesMenu'
|
||||
|
||||
export default function SpeedGraderPostGradesMenu(props) {
|
||||
const Icon = props.allowPostingGrades ? IconOffLine : IconEyeLine
|
||||
const {allowHidingGradesOrComments, allowPostingGradesOrComments} = props
|
||||
const Icon = allowPostingGradesOrComments ? IconOffLine : IconEyeLine
|
||||
const menuTrigger = (
|
||||
<Button
|
||||
icon={<Icon className="speedgrader-postgradesmenu-icon" />}
|
||||
|
@ -36,23 +37,31 @@ export default function SpeedGraderPostGradesMenu(props) {
|
|||
|
||||
return (
|
||||
<Menu placement="bottom end" trigger={menuTrigger}>
|
||||
{props.allowPostingGrades && props.hasGrades ? (
|
||||
{allowPostingGradesOrComments ? (
|
||||
<Menu.Item name="postGrades" onSelect={props.onPostGrades}>
|
||||
<Text>{I18n.t('Post Grades')}</Text>
|
||||
</Menu.Item>
|
||||
) : (
|
||||
<Menu.Item name="postGrades" disabled>
|
||||
<Text>{props.hasGrades ? I18n.t('All Grades Posted') : I18n.t('No Grades to Post')}</Text>
|
||||
<Text>
|
||||
{props.hasGradesOrPostableComments
|
||||
? I18n.t('All Grades Posted')
|
||||
: I18n.t('No Grades to Post')}
|
||||
</Text>
|
||||
</Menu.Item>
|
||||
)}
|
||||
|
||||
{props.allowHidingGrades && props.hasGrades ? (
|
||||
{allowHidingGradesOrComments ? (
|
||||
<Menu.Item name="hideGrades" onSelect={props.onHideGrades}>
|
||||
<Text>{I18n.t('Hide Grades')}</Text>
|
||||
</Menu.Item>
|
||||
) : (
|
||||
<Menu.Item name="hideGrades" disabled>
|
||||
<Text>{props.hasGrades ? I18n.t('All Grades Hidden') : I18n.t('No Grades to Hide')}</Text>
|
||||
<Text>
|
||||
{props.hasGradesOrPostableComments
|
||||
? I18n.t('All Grades Hidden')
|
||||
: I18n.t('No Grades to Hide')}
|
||||
</Text>
|
||||
</Menu.Item>
|
||||
)}
|
||||
</Menu>
|
||||
|
@ -60,9 +69,9 @@ export default function SpeedGraderPostGradesMenu(props) {
|
|||
}
|
||||
|
||||
SpeedGraderPostGradesMenu.propTypes = {
|
||||
allowHidingGrades: bool.isRequired,
|
||||
allowPostingGrades: bool.isRequired,
|
||||
hasGrades: bool.isRequired,
|
||||
allowHidingGradesOrComments: bool.isRequired,
|
||||
allowPostingGradesOrComments: bool.isRequired,
|
||||
hasGradesOrPostableComments: bool.isRequired,
|
||||
onHideGrades: func.isRequired,
|
||||
onPostGrades: func.isRequired
|
||||
}
|
||||
|
|
|
@ -177,6 +177,10 @@ module SpeedGrader
|
|||
json.merge! provisional_grade_to_json(provisional_grade)
|
||||
end
|
||||
|
||||
if course.root_account.feature_enabled?(:allow_postable_submission_comments) && course.post_policies_enabled?
|
||||
json[:has_postable_comments] = sub.submission_comments.select(&:hidden?).present?
|
||||
end
|
||||
|
||||
json[:submission_comments] = anonymous_moderated_submission_comments_json(
|
||||
assignment: assignment,
|
||||
course: course,
|
||||
|
|
|
@ -143,6 +143,11 @@ class Submission < ActiveRecord::Base
|
|||
|
||||
scope :for_context_codes, lambda { |context_codes| where(:context_code => context_codes) }
|
||||
|
||||
scope :postable, -> { graded.union(with_hidden_comments) }
|
||||
scope :with_hidden_comments, -> {
|
||||
where("EXISTS (?)", SubmissionComment.where("submission_id = submissions.id AND hidden = true"))
|
||||
}
|
||||
|
||||
# This should only be used in the course drop down to show assignments recently graded.
|
||||
scope :recently_graded_assignments, lambda { |user_id, date, limit|
|
||||
select("assignments.id, assignments.title, assignments.points_possible, assignments.due_at,
|
||||
|
|
|
@ -17,3 +17,10 @@ add_grading_scheme_to_admin_grade_reports:
|
|||
display_name: Add Grading Scheme to Admin Grade Reports
|
||||
description: Includes grade values in the admin grade reports.
|
||||
applies_to: RootAccount
|
||||
allow_postable_submission_comments:
|
||||
state: hidden
|
||||
display_name: Allow Postable Submission Comments
|
||||
description: For manually posted assignments, the presence of a submission comment not by the submission's own
|
||||
user will allow posting the assignment. Previously, this would have required a grade before posting could
|
||||
occur.
|
||||
applies_to: RootAccount
|
||||
|
|
|
@ -70,6 +70,10 @@ module Api::V1::Submission
|
|||
)
|
||||
end
|
||||
|
||||
if includes.include?("has_postable_comments")
|
||||
hash["has_postable_comments"] = submission.submission_comments.select(&:hidden?).present?
|
||||
end
|
||||
|
||||
if includes.include?("submission_comments")
|
||||
published_comments = submission.comments_for(@current_user).published
|
||||
hash['submission_comments'] = submission_comments_json(published_comments, current_user)
|
||||
|
|
|
@ -36,7 +36,7 @@ import PostPolicies from 'jsx/speed_grader/PostPolicies'
|
|||
import SpeedGraderProvisionalGradeSelector from 'jsx/speed_grader/SpeedGraderProvisionalGradeSelector'
|
||||
import SpeedGraderPostGradesMenu from 'jsx/speed_grader/SpeedGraderPostGradesMenu'
|
||||
import SpeedGraderSettingsMenu from 'jsx/speed_grader/SpeedGraderSettingsMenu'
|
||||
import {isGraded, isHidden} from 'jsx/grading/helpers/SubmissionHelper'
|
||||
import {isGraded, isPostable} from 'jsx/grading/helpers/SubmissionHelper'
|
||||
import studentViewedAtTemplate from 'jst/speed_grader/student_viewed_at'
|
||||
import submissionsDropdownTemplate from 'jst/speed_grader/submissions_dropdown'
|
||||
import speechRecognitionTemplate from 'jst/speed_grader/speech_recognition'
|
||||
|
@ -758,7 +758,7 @@ function renderProgressIcon(attachment) {
|
|||
function renderHiddenSubmissionPill(submission) {
|
||||
const mountPoint = document.getElementById(SPEED_GRADER_HIDDEN_SUBMISSION_PILL_MOUNT_POINT)
|
||||
|
||||
if (isHidden(submission)) {
|
||||
if (isPostable(submission)) {
|
||||
ReactDOM.render(
|
||||
<Pill variant="warning" text={I18n.t('Hidden')} margin="0 0 small" />,
|
||||
mountPoint
|
||||
|
@ -3752,11 +3752,15 @@ function renderPostGradesMenu() {
|
|||
const {submissionsMap} = window.jsonData
|
||||
const submissions = window.jsonData.studentsWithSubmissions.map(student => student.submission)
|
||||
|
||||
const hasGrades = submissions.some(isGraded)
|
||||
const allowHidingGrades = submissions.some(
|
||||
const hasGradesOrPostableComments = submissions.some(
|
||||
submission => isGraded(submission) || submission.has_postable_comments
|
||||
)
|
||||
const allowHidingGradesOrComments = submissions.some(
|
||||
submission => submission && submission.posted_at != null
|
||||
)
|
||||
const allowPostingGrades = submissions.some(submission => submission && isHidden(submission))
|
||||
const allowPostingGradesOrComments = submissions.some(
|
||||
submission => submission && isPostable(submission)
|
||||
)
|
||||
|
||||
function onHideGrades() {
|
||||
EG.postPolicies.showHideAssignmentGradesTray({submissionsMap})
|
||||
|
@ -3767,9 +3771,9 @@ function renderPostGradesMenu() {
|
|||
}
|
||||
|
||||
const props = {
|
||||
allowHidingGrades,
|
||||
allowPostingGrades,
|
||||
hasGrades,
|
||||
allowHidingGradesOrComments,
|
||||
allowPostingGradesOrComments,
|
||||
hasGradesOrPostableComments,
|
||||
onHideGrades,
|
||||
onPostGrades
|
||||
}
|
||||
|
|
|
@ -1689,6 +1689,59 @@ describe 'Submissions API', type: :request do
|
|||
expect(response).to be_forbidden
|
||||
end
|
||||
|
||||
describe "has_postable_comments" do
|
||||
let(:assignment) { @course.assignments.create! }
|
||||
let(:student1_sub) { assignment.submissions.find_by(user: @student1) }
|
||||
|
||||
before(:each) do
|
||||
@course.root_account.enable_feature!(:allow_postable_submission_comments)
|
||||
PostPolicy.enable_feature!
|
||||
@course.enable_feature!(:new_gradebook)
|
||||
assignment.ensure_post_policy(post_manually: true)
|
||||
end
|
||||
|
||||
def student_json(params = {grouped: true, student_ids: [@student1.to_param]})
|
||||
api_call(
|
||||
:get,
|
||||
"/api/v1/courses/#{@course.id}/students/submissions.json",
|
||||
{
|
||||
controller: "submissions_api",
|
||||
action: "for_students",
|
||||
format: "json",
|
||||
course_id: @course.to_param
|
||||
},
|
||||
params
|
||||
).first
|
||||
end
|
||||
|
||||
it "is not included when allow_postable_submission_comments feature is not enabled" do
|
||||
@course.root_account.disable_feature!(:allow_postable_submission_comments)
|
||||
expect(student_json.fetch("submissions").first).not_to have_key "has_postable_comments"
|
||||
end
|
||||
|
||||
it "is not included when Post Policies are not enabled" do
|
||||
@course.disable_feature!(:new_gradebook)
|
||||
expect(student_json.fetch("submissions").first).not_to have_key "has_postable_comments"
|
||||
end
|
||||
|
||||
it "is not included when params[:grouped] is not present" do
|
||||
submission_json = student_json({student_ids: [@student1.to_param]})
|
||||
expect(submission_json).not_to have_key "has_postable_comments"
|
||||
end
|
||||
|
||||
it "is true when unposted and hidden comments exist" do
|
||||
student1_sub.add_comment(author: @teacher, comment: "good job!", hidden: true)
|
||||
submission_json = student_json.fetch("submissions").find { |s| s.fetch("id") == student1_sub.id }
|
||||
expect(submission_json.fetch("has_postable_comments")).to be true
|
||||
end
|
||||
|
||||
it "is false when unposted and only non-hidden comments exist" do
|
||||
student1_sub.add_comment(author: @student, comment: "fun assignment!", hidden: false)
|
||||
submission_json = student_json.fetch("submissions").find { |s| s.fetch("id") == student1_sub.id }
|
||||
expect(submission_json.fetch("has_postable_comments")).to be false
|
||||
end
|
||||
end
|
||||
|
||||
context 'OriginalityReport' do
|
||||
it 'includes has_originality_report if the submission has an originality_report' do
|
||||
attachment_model
|
||||
|
|
|
@ -206,41 +206,62 @@ describe Mutations::PostAssignmentGradesForSections do
|
|||
end
|
||||
|
||||
describe "graded_only" do
|
||||
let(:post_submissions_job) { Delayed::Job.where(tag: "Assignment#post_submissions").order(:id).last }
|
||||
let(:section1_user_ids) { section1.enrollments.pluck(:user_id) }
|
||||
let(:section1_submissions) { assignment.submissions.where(user_id: section1_user_ids) }
|
||||
|
||||
before(:each) do
|
||||
section1_student2 = User.create!
|
||||
section1.enroll_user(section1_student2, "StudentEnrollment", "active")
|
||||
@section1_student2 = User.create!
|
||||
section1.enroll_user(@section1_student2, "StudentEnrollment", "active")
|
||||
@student1_submission = assignment.submissions.find_by(user: @section1_student)
|
||||
@student2_submission = assignment.submissions.find_by(user: section1_student2)
|
||||
@student2_submission = assignment.submissions.find_by(user: @section1_student2)
|
||||
assignment.ensure_post_policy(post_manually: true)
|
||||
assignment.grade_student(@section1_student, grader: teacher, score: 100)
|
||||
end
|
||||
|
||||
it "posts the graded submissions if graded_only is true" do
|
||||
execute_query(mutation_str(assignment_id: assignment.id, section_ids:[section1.id], graded_only: true), context)
|
||||
post_submissions_job = Delayed::Job.where(tag: "Assignment#post_submissions").order(:id).last
|
||||
post_submissions_job.invoke_job
|
||||
expect(@student1_submission.reload).to be_posted
|
||||
end
|
||||
|
||||
it "posts submissions with hidden comments if graded_only is true and post comments feature is enabled" do
|
||||
course.root_account.enable_feature!(:allow_postable_submission_comments)
|
||||
@student2_submission.add_comment(author: teacher, comment: "good work!", hidden: true)
|
||||
execute_query(mutation_str(assignment_id: assignment.id, section_ids:[section1.id], graded_only: true), context)
|
||||
post_submissions_job.invoke_job
|
||||
expect(@student2_submission.reload).to be_posted
|
||||
end
|
||||
|
||||
it "does not post submissions with hidden comments if graded_only is true and post comments feature is not enabled" do
|
||||
@student2_submission.add_comment(author: teacher, comment: "good work!", hidden: true)
|
||||
execute_query(mutation_str(assignment_id: assignment.id, section_ids:[section1.id], graded_only: true), context)
|
||||
post_submissions_job.invoke_job
|
||||
expect(@student2_submission.reload).not_to be_posted
|
||||
end
|
||||
|
||||
it "does not post submissions with no hidden comments if graded_only is true and post comments feature is enabled" do
|
||||
course.root_account.enable_feature!(:allow_postable_submission_comments)
|
||||
@student2_submission.add_comment(author: @section1_student2, comment: "good work!", hidden: false)
|
||||
execute_query(mutation_str(assignment_id: assignment.id, section_ids:[section1.id], graded_only: true), context)
|
||||
post_submissions_job.invoke_job
|
||||
expect(@student2_submission.reload).not_to be_posted
|
||||
end
|
||||
|
||||
it "does not post the ungraded submissions if graded_only is true" do
|
||||
execute_query(mutation_str(assignment_id: assignment.id, section_ids:[section1.id], graded_only: true), context)
|
||||
post_submissions_job = Delayed::Job.where(tag: "Assignment#post_submissions").order(:id).last
|
||||
post_submissions_job.invoke_job
|
||||
expect(@student2_submission.reload).not_to be_posted
|
||||
end
|
||||
|
||||
it "posts all the submissions if graded_only is false" do
|
||||
execute_query(mutation_str(assignment_id: assignment.id, section_ids:[section1.id], graded_only: false), context)
|
||||
post_submissions_job = Delayed::Job.where(tag: "Assignment#post_submissions").order(:id).last
|
||||
post_submissions_job.invoke_job
|
||||
expect(section1_submissions).to all(be_posted)
|
||||
end
|
||||
|
||||
it "posts all the sections' submissions if graded_only is not present" do
|
||||
execute_query(mutation_str(assignment_id: assignment.id, section_ids:[section1.id]), context)
|
||||
post_submissions_job = Delayed::Job.where(tag: "Assignment#post_submissions").order(:id).last
|
||||
post_submissions_job.invoke_job
|
||||
expect(section1_submissions).to all(be_posted)
|
||||
end
|
||||
|
|
|
@ -277,6 +277,7 @@ describe Mutations::PostAssignmentGrades do
|
|||
before(:each) do
|
||||
@student1_submission = assignment.submissions.find_by(user: student)
|
||||
@student2_submission = assignment.submissions.find_by(user: student2)
|
||||
assignment.ensure_post_policy(post_manually: true)
|
||||
assignment.grade_student(student, grader: teacher, score: 100)
|
||||
end
|
||||
|
||||
|
@ -293,6 +294,29 @@ describe Mutations::PostAssignmentGrades do
|
|||
expect(@student1_submission.reload).to be_posted
|
||||
end
|
||||
|
||||
it "posts submissions with hidden comments if graded_only is true and post comments feature is enabled" do
|
||||
course.root_account.enable_feature!(:allow_postable_submission_comments)
|
||||
@student2_submission.add_comment(author: teacher, comment: "good work!", hidden: true)
|
||||
execute_query(mutation_str(assignment_id: assignment.id, graded_only: true), context)
|
||||
post_submissions_job.invoke_job
|
||||
expect(@student2_submission.reload).to be_posted
|
||||
end
|
||||
|
||||
it "does not post submissions with hidden comments if graded_only is true and post comments feature is not enabled" do
|
||||
@student2_submission.add_comment(author: teacher, comment: "good work!", hidden: true)
|
||||
execute_query(mutation_str(assignment_id: assignment.id, graded_only: true), context)
|
||||
post_submissions_job.invoke_job
|
||||
expect(@student2_submission.reload).not_to be_posted
|
||||
end
|
||||
|
||||
it "does not post submissions with no hidden comments if graded_only is true and post comments feature is enabled" do
|
||||
course.root_account.enable_feature!(:allow_postable_submission_comments)
|
||||
@student2_submission.add_comment(author: student, comment: "good work!", hidden: false)
|
||||
execute_query(mutation_str(assignment_id: assignment.id, graded_only: true), context)
|
||||
post_submissions_job.invoke_job
|
||||
expect(@student2_submission.reload).not_to be_posted
|
||||
end
|
||||
|
||||
it "does not post the ungraded submissions if graded_only is true" do
|
||||
execute_query(mutation_str(assignment_id: assignment.id, graded_only: true), context)
|
||||
post_submissions_job.invoke_job
|
||||
|
|
|
@ -375,10 +375,9 @@ QUnit.module('GradebookGrid AssignmentCellFormatter', suiteHooks => {
|
|||
strictEqual(renderCell().querySelectorAll('.Grid__GradeCell__UnpostedGrade').length, 1)
|
||||
})
|
||||
|
||||
test('does not display an unposted grade indicator when submission is graded and posted', () => {
|
||||
submission.workflow_state = 'graded'
|
||||
submission.posted_at = new Date()
|
||||
strictEqual(renderCell().querySelectorAll('.Grid__GradeCell__UnpostedGrade').length, 0)
|
||||
test('displays an unposted grade indicator when a submission comment exists and is unposted', () => {
|
||||
submission.hasPostableComments = true
|
||||
strictEqual(renderCell().querySelectorAll('.Grid__GradeCell__UnpostedGrade').length, 1)
|
||||
})
|
||||
|
||||
test('does not display an unposted grade indicator when grade is posted', () => {
|
||||
|
@ -386,14 +385,15 @@ QUnit.module('GradebookGrid AssignmentCellFormatter', suiteHooks => {
|
|||
strictEqual(renderCell().querySelectorAll('.Grid__GradeCell__UnpostedGrade').length, 0)
|
||||
})
|
||||
|
||||
test('does not display an unposted grade indicator when submission not graded', () => {
|
||||
test('does not display an unposted grade indicator when submission has no grade nor comment', () => {
|
||||
submission.workflow_state = 'unsubmitted'
|
||||
strictEqual(renderCell().querySelectorAll('.Grid__GradeCell__UnpostedGrade').length, 0)
|
||||
})
|
||||
|
||||
test('does not display an unposted grade indicator when submission does not have a score', () => {
|
||||
test('does not display an unposted grade indicator when submission does not have a score nor postable comment', () => {
|
||||
submission.workflow_state = 'graded'
|
||||
submission.score = null
|
||||
submission.hasPostableComments = false
|
||||
strictEqual(renderCell().querySelectorAll('.Grid__GradeCell__UnpostedGrade').length, 0)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -83,6 +83,7 @@ QUnit.module('GradebookGrid AssignmentColumnHeaderRenderer', suiteHooks => {
|
|||
id: '93',
|
||||
assignment_id: '2301',
|
||||
excused: false,
|
||||
hasPostableComments: false,
|
||||
late_policy_status: null,
|
||||
posted_at: null,
|
||||
score: null,
|
||||
|
@ -362,34 +363,41 @@ QUnit.module('GradebookGrid AssignmentColumnHeaderRenderer', suiteHooks => {
|
|||
strictEqual(component.props.postGradesAction.featureEnabled, true)
|
||||
})
|
||||
|
||||
test('sets hasGrades to true if at least one graded submission is graded', () => {
|
||||
test('sets hasGradesOrPostableComments to true if at least one submission is graded', () => {
|
||||
submission.workflow_state = 'graded'
|
||||
submission.score = 1
|
||||
gradebook.gotChunkOfStudents([student])
|
||||
render()
|
||||
strictEqual(component.props.postGradesAction.hasGrades, true)
|
||||
strictEqual(component.props.postGradesAction.hasGradesOrPostableComments, true)
|
||||
})
|
||||
|
||||
test('sets hasGrades to false if no submissions are graded', () => {
|
||||
test('sets hasGradesOrPostableComments to false if no submissions are graded', () => {
|
||||
gradebook.gotChunkOfStudents([student])
|
||||
render()
|
||||
strictEqual(component.props.postGradesAction.hasGrades, false)
|
||||
strictEqual(component.props.postGradesAction.hasGradesOrPostableComments, false)
|
||||
})
|
||||
|
||||
test('sets hasGradesToPost to true if at least one graded submission has no posted_at date', () => {
|
||||
test('sets hasGradesOrCommentsToPost to true if at least one submission is graded and unposted', () => {
|
||||
submission.workflow_state = 'graded'
|
||||
submission.score = 1
|
||||
gradebook.gotChunkOfStudents([student])
|
||||
render()
|
||||
strictEqual(component.props.postGradesAction.hasGradesToPost, true)
|
||||
strictEqual(component.props.postGradesAction.hasGradesOrCommentsToPost, true)
|
||||
})
|
||||
|
||||
test('sets hasGradesToPost to false if all submissions have a posted_at date', () => {
|
||||
test('sets hasGradesOrCommentsToPost to true if at least one submission has a postable comment and unposted', () => {
|
||||
submission.has_postable_comments = true
|
||||
gradebook.gotChunkOfStudents([student])
|
||||
render()
|
||||
strictEqual(component.props.postGradesAction.hasGradesOrCommentsToPost, true)
|
||||
})
|
||||
|
||||
test('sets hasGradesOrCommentsToPost to false if all submissions have a posted_at date', () => {
|
||||
submission.posted_at = new Date('Wed Oct 1 1997')
|
||||
|
||||
gradebook.gotChunkOfStudents([student])
|
||||
render()
|
||||
strictEqual(component.props.postGradesAction.hasGradesToPost, false)
|
||||
strictEqual(component.props.postGradesAction.hasGradesOrCommentsToPost, false)
|
||||
})
|
||||
|
||||
test('sets newIconsEnabled to true if Gradebook has new_post_policy_icons_enabled set to true', () => {
|
||||
|
@ -447,31 +455,38 @@ QUnit.module('GradebookGrid AssignmentColumnHeaderRenderer', suiteHooks => {
|
|||
sinon.stub(gradebook.postPolicies, 'showHideAssignmentGradesTray')
|
||||
})
|
||||
|
||||
test('sets hasGrades to true if at least one graded submission is graded', () => {
|
||||
test('sets hasGradesOrPostableComments to true if at least one submission is graded', () => {
|
||||
submission.workflow_state = 'graded'
|
||||
submission.score = 1
|
||||
gradebook.gotChunkOfStudents([student])
|
||||
render()
|
||||
strictEqual(component.props.hideGradesAction.hasGrades, true)
|
||||
strictEqual(component.props.hideGradesAction.hasGradesOrPostableComments, true)
|
||||
})
|
||||
|
||||
test('sets hasGrades to false if no submissions are graded', () => {
|
||||
test('sets hasGradesOrPostableComments to true if at least one submission has postable comments', () => {
|
||||
submission.has_postable_comments = true
|
||||
gradebook.gotChunkOfStudents([student])
|
||||
render()
|
||||
strictEqual(component.props.hideGradesAction.hasGrades, false)
|
||||
strictEqual(component.props.hideGradesAction.hasGradesOrPostableComments, true)
|
||||
})
|
||||
|
||||
test('sets hasGradesToHide to true if at least one submission has a posted_at date', () => {
|
||||
test('sets hasGradesOrPostableComments to false if no submissions are graded or have comments', () => {
|
||||
gradebook.gotChunkOfStudents([student])
|
||||
render()
|
||||
strictEqual(component.props.hideGradesAction.hasGradesToHide, true)
|
||||
strictEqual(component.props.hideGradesAction.hasGradesOrPostableComments, false)
|
||||
})
|
||||
|
||||
test('sets hasGradesToHide to false if no submission has a posted_at date', () => {
|
||||
test('sets hasGradesOrCommentsToHide to true if at least one submission has a posted_at date', () => {
|
||||
gradebook.gotChunkOfStudents([student])
|
||||
render()
|
||||
strictEqual(component.props.hideGradesAction.hasGradesOrCommentsToHide, true)
|
||||
})
|
||||
|
||||
test('sets hasGradesOrCommentsToHide to false if no submission has a posted_at date', () => {
|
||||
submission.posted_at = null
|
||||
gradebook.gotChunkOfStudents([student])
|
||||
render()
|
||||
strictEqual(component.props.hideGradesAction.hasGradesToHide, false)
|
||||
strictEqual(component.props.hideGradesAction.hasGradesOrCommentsToHide, false)
|
||||
})
|
||||
|
||||
test('includes a callback to show the "Hide Assignment Grades" tray', () => {
|
||||
|
|
|
@ -77,8 +77,8 @@ QUnit.module('GradebookGrid AssignmentColumnHeader', suiteHooks => {
|
|||
},
|
||||
|
||||
hideGradesAction: {
|
||||
hasGrades: true,
|
||||
hasGradesToHide: true,
|
||||
hasGradesOrPostableComments: true,
|
||||
hasGradesOrCommentsToHide: true,
|
||||
onSelect() {}
|
||||
},
|
||||
|
||||
|
@ -90,8 +90,8 @@ QUnit.module('GradebookGrid AssignmentColumnHeader', suiteHooks => {
|
|||
postGradesAction: {
|
||||
enabled: false,
|
||||
featureEnabled: false,
|
||||
hasGrades: true,
|
||||
hasGradesToPost: true,
|
||||
hasGradesOrPostableComments: true,
|
||||
hasGradesOrCommentsToPost: true,
|
||||
onSelect() {}
|
||||
},
|
||||
|
||||
|
@ -1100,7 +1100,7 @@ QUnit.module('GradebookGrid AssignmentColumnHeader', suiteHooks => {
|
|||
QUnit.module('"Options" > "Post grades" action', hooks => {
|
||||
hooks.beforeEach(() => {
|
||||
props.postGradesAction.featureEnabled = true
|
||||
props.postGradesAction.hasGradesToPost = true
|
||||
props.postGradesAction.hasGradesOrCommentsToPost = true
|
||||
})
|
||||
|
||||
QUnit.module('when post policies is enabled', () => {
|
||||
|
@ -1115,34 +1115,26 @@ QUnit.module('GradebookGrid AssignmentColumnHeader', suiteHooks => {
|
|||
})
|
||||
|
||||
test('has the text "All grades posted" when no submissions can be posted', () => {
|
||||
props.postGradesAction.hasGradesToPost = false
|
||||
props.postGradesAction.hasGradesOrCommentsToPost = false
|
||||
mountAndOpenOptionsMenu()
|
||||
ok(getMenuItem($menuContent, 'All grades posted'))
|
||||
})
|
||||
|
||||
test('has the text "No grades to post" when no submissions are graded', () => {
|
||||
props.postGradesAction.hasGrades = false
|
||||
test('has the text "No grades to post" when no submissions are graded or have comments', () => {
|
||||
props.postGradesAction.hasGradesOrCommentsToPost = false
|
||||
props.postGradesAction.hasGradesOrPostableComments = false
|
||||
mountAndOpenOptionsMenu()
|
||||
ok(getMenuItem($menuContent, 'No grades to post'))
|
||||
})
|
||||
|
||||
test('is disabled when no submissions can be posted', () => {
|
||||
props.postGradesAction.hasGradesToPost = false
|
||||
props.postGradesAction.hasGradesOrCommentsToPost = false
|
||||
mountAndOpenOptionsMenu()
|
||||
strictEqual(
|
||||
getMenuItem($menuContent, 'All grades posted').getAttribute('aria-disabled'),
|
||||
'true'
|
||||
)
|
||||
})
|
||||
|
||||
test('is disabled when no submissions are graded', () => {
|
||||
props.postGradesAction.hasGrades = false
|
||||
mountAndOpenOptionsMenu()
|
||||
strictEqual(
|
||||
getMenuItem($menuContent, 'No grades to post').getAttribute('aria-disabled'),
|
||||
'true'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
test('is not present when post policies is not enabled', () => {
|
||||
|
@ -1181,7 +1173,7 @@ QUnit.module('GradebookGrid AssignmentColumnHeader', suiteHooks => {
|
|||
QUnit.module('"Options" > "Hide grades" action', hooks => {
|
||||
hooks.beforeEach(() => {
|
||||
props.postGradesAction.featureEnabled = true
|
||||
props.hideGradesAction.hasGradesToHide = true
|
||||
props.hideGradesAction.hasGradesOrCommentsToHide = true
|
||||
})
|
||||
|
||||
QUnit.module('when post policies is enabled', () => {
|
||||
|
@ -1196,34 +1188,26 @@ QUnit.module('GradebookGrid AssignmentColumnHeader', suiteHooks => {
|
|||
})
|
||||
|
||||
test('has the text "All grades hidden" when no submissions can be hidden', () => {
|
||||
props.hideGradesAction.hasGradesToHide = false
|
||||
props.hideGradesAction.hasGradesOrCommentsToHide = false
|
||||
mountAndOpenOptionsMenu()
|
||||
ok(getMenuItem($menuContent, 'All grades hidden'))
|
||||
})
|
||||
|
||||
test('has the text "No grades to hide" when no submissions are graded', () => {
|
||||
props.hideGradesAction.hasGrades = false
|
||||
test('has the text "No grades to hide" when no submissions are graded or have comments', () => {
|
||||
props.hideGradesAction.hasGradesOrCommentsToHide = false
|
||||
props.hideGradesAction.hasGradesOrPostableComments = false
|
||||
mountAndOpenOptionsMenu()
|
||||
ok(getMenuItem($menuContent, 'No grades to hide'))
|
||||
})
|
||||
|
||||
test('is disabled when no submissions can be hidden', () => {
|
||||
props.hideGradesAction.hasGradesToHide = false
|
||||
props.hideGradesAction.hasGradesOrCommentsToHide = false
|
||||
mountAndOpenOptionsMenu()
|
||||
strictEqual(
|
||||
getMenuItem($menuContent, 'All grades hidden').getAttribute('aria-disabled'),
|
||||
'true'
|
||||
)
|
||||
})
|
||||
|
||||
test('is disabled when no submissions are graded', () => {
|
||||
props.hideGradesAction.hasGrades = false
|
||||
mountAndOpenOptionsMenu()
|
||||
strictEqual(
|
||||
getMenuItem($menuContent, 'No grades to hide').getAttribute('aria-disabled'),
|
||||
'true'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
test('is present when post policies is enabled', () => {
|
||||
|
|
|
@ -253,6 +253,7 @@ QUnit.module('Gradebook PostPolicies', suiteHooks => {
|
|||
}
|
||||
submission = {
|
||||
assignment_id: '2301',
|
||||
has_postable_comments: true,
|
||||
posted_at: new Date().toISOString(),
|
||||
score: 1.0,
|
||||
workflow_state: 'graded'
|
||||
|
@ -309,7 +310,12 @@ QUnit.module('Gradebook PostPolicies', suiteHooks => {
|
|||
postPolicies.showPostAssignmentGradesTray({assignmentId: '2301'})
|
||||
const [{submissions}] = postPolicies._postAssignmentGradesTray.show.lastCall.args
|
||||
deepEqual(submissions, [
|
||||
{postedAt: submission.posted_at, score: 1.0, workflowState: 'graded'}
|
||||
{
|
||||
hasPostableComments: true,
|
||||
postedAt: submission.posted_at,
|
||||
score: 1.0,
|
||||
workflowState: 'graded'
|
||||
}
|
||||
])
|
||||
})
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@ QUnit.module('SubmissionStatus - Pills', hooks => {
|
|||
submission: {
|
||||
assignmentId: '1',
|
||||
excused: false,
|
||||
hasPostableComments: false,
|
||||
late: false,
|
||||
missing: false,
|
||||
postedAt: null,
|
||||
|
@ -176,6 +177,13 @@ QUnit.module('SubmissionStatus - Pills', hooks => {
|
|||
strictEqual(hiddenPills.length, 1)
|
||||
})
|
||||
|
||||
test('shows the "Hidden" pill when the submission has comments and not posted', () => {
|
||||
props.submission.hasPostableComments = true
|
||||
wrapper = mountComponent()
|
||||
const hiddenPills = getHiddenPills()
|
||||
strictEqual(hiddenPills.length, 1)
|
||||
})
|
||||
|
||||
test('does not show the "Hidden" pill when the submission is not graded', () => {
|
||||
props.submission.workflowState = 'unsubmitted'
|
||||
wrapper = mountComponent()
|
||||
|
@ -218,6 +226,7 @@ QUnit.module('SubmissionStatus - Grading Period not in any grading period warnin
|
|||
postPoliciesEnabled: false,
|
||||
submission: {
|
||||
excused: false,
|
||||
hasPostableComments: false,
|
||||
late: false,
|
||||
missing: false,
|
||||
secondsLate: 0,
|
||||
|
@ -278,6 +287,7 @@ QUnit.module('SubmissionStatus - Grading Period is a closed warning', hooks => {
|
|||
postPoliciesEnabled: false,
|
||||
submission: {
|
||||
excused: false,
|
||||
hasPostableComments: false,
|
||||
late: false,
|
||||
missing: false,
|
||||
secondsLate: 0,
|
||||
|
@ -338,6 +348,7 @@ QUnit.module('SubmissionStatus - Grading Period is in another period warning', h
|
|||
postPoliciesEnabled: false,
|
||||
submission: {
|
||||
excused: false,
|
||||
hasPostableComments: false,
|
||||
late: false,
|
||||
missing: false,
|
||||
secondsLate: 0,
|
||||
|
@ -398,6 +409,7 @@ QUnit.module('SubmissionStatus - Concluded Enrollment Warning', hooks => {
|
|||
postPoliciesEnabled: false,
|
||||
submission: {
|
||||
excused: false,
|
||||
hasPostableComments: false,
|
||||
late: false,
|
||||
missing: false,
|
||||
secondsLate: 0,
|
||||
|
@ -458,6 +470,7 @@ QUnit.module('SubmissionStatus - Not calculated in final grade', hooks => {
|
|||
postPoliciesEnabled: false,
|
||||
submission: {
|
||||
excused: false,
|
||||
hasPostableComments: false,
|
||||
late: false,
|
||||
missing: false,
|
||||
secondsLate: 0,
|
||||
|
|
|
@ -74,7 +74,8 @@ QUnit.module('SubmissionTray', hooks => {
|
|||
enteredScore: 10,
|
||||
excused: false,
|
||||
grade: '7',
|
||||
gradedAt: null,
|
||||
gradedAt: new Date().toISOString(),
|
||||
hasPostableComments: false,
|
||||
id: '2501',
|
||||
late: false,
|
||||
missing: false,
|
||||
|
@ -83,7 +84,8 @@ QUnit.module('SubmissionTray', hooks => {
|
|||
score: 7,
|
||||
secondsLate: 0,
|
||||
submissionType: 'online_text_entry',
|
||||
userId: '27'
|
||||
userId: '27',
|
||||
workflowState: 'graded'
|
||||
},
|
||||
updateSubmission() {},
|
||||
updateSubmissionComment() {},
|
||||
|
@ -94,6 +96,7 @@ QUnit.module('SubmissionTray', hooks => {
|
|||
gradingType: 'points',
|
||||
htmlUrl: 'http://htmlUrl/',
|
||||
id: '30',
|
||||
moderatedGrading: false,
|
||||
muted: false,
|
||||
pointsPossible: 10,
|
||||
postManually: false,
|
||||
|
@ -294,6 +297,24 @@ QUnit.module('SubmissionTray', hooks => {
|
|||
})
|
||||
})
|
||||
|
||||
QUnit.module('when passing true for postPoliciesEnabled', contextHooks => {
|
||||
contextHooks.beforeEach(() => {
|
||||
defaultProps.postPoliciesEnabled = true
|
||||
})
|
||||
|
||||
test('"Hidden" is displayed when a submission is graded and unposted', () => {
|
||||
defaultProps.submission.workflowState = 'graded'
|
||||
mountComponent()
|
||||
ok(content.textContent.includes('Hidden'))
|
||||
})
|
||||
|
||||
test('"Hidden" is displayed when a submission has comments and is unposted', () => {
|
||||
defaultProps.submission.hasPostableComments = true
|
||||
mountComponent()
|
||||
ok(content.textContent.includes('Hidden'))
|
||||
})
|
||||
})
|
||||
|
||||
test('shows avatar if avatar is not null', () => {
|
||||
const avatarUrl = 'http://bob_is_not_a_domain/me.jpg?filter=make_me_pretty'
|
||||
const gradesUrl = 'http://gradesUrl/'
|
||||
|
@ -334,13 +355,6 @@ QUnit.module('SubmissionTray', hooks => {
|
|||
ok(content.textContent.includes('This submission is not in any grading period'))
|
||||
})
|
||||
|
||||
test('passes along postPoliciesEnabled prop to SubmissionStatus', () => {
|
||||
defaultProps.postPoliciesEnabled = true
|
||||
defaultProps.submission.workflowState = 'graded'
|
||||
mountComponent()
|
||||
ok(content.textContent.includes('Hidden'))
|
||||
})
|
||||
|
||||
test('shows student name', () => {
|
||||
mountComponent({
|
||||
student: {id: '27', name: 'Sara', gradesUrl: 'http://gradeUrl/', isConcluded: false}
|
||||
|
|
|
@ -243,6 +243,16 @@ QUnit.module('PostAssignmentGradesTray', suiteHooks => {
|
|||
await show()
|
||||
strictEqual(getUnpostedCount().textContent, '1')
|
||||
})
|
||||
|
||||
test('submissions with postable comments and without a postedAt are counted', async () => {
|
||||
context.submissions = [
|
||||
{postedAt: new Date().toISOString(), hasPostableComments: true},
|
||||
{postedAt: null, score: 1, workflowState: 'graded'},
|
||||
{postedAt: null, score: null, workflowState: 'unsubmitted'}
|
||||
]
|
||||
await show()
|
||||
strictEqual(getUnpostedCount().textContent, '1')
|
||||
})
|
||||
})
|
||||
|
||||
QUnit.module('with no unposted submissions', unpostedSubmissionsHooks => {
|
||||
|
|
|
@ -30,12 +30,14 @@ QUnit.module('PostAssignmentGradesTray PostTypes', suiteHooks => {
|
|||
}
|
||||
|
||||
function getGradedPostType() {
|
||||
const labelText = 'GradedGrades will be made visible to students with graded submissions'
|
||||
const labelText =
|
||||
'GradedStudents who have received a grade or a submission comment will be able to see their grade and/or submission comments.'
|
||||
return document.getElementById(getLabel(labelText).htmlFor)
|
||||
}
|
||||
|
||||
function getEveryonePostType() {
|
||||
const labelText = 'EveryoneGrades will be made visible to all students'
|
||||
const labelText =
|
||||
'EveryoneAll students will be able to see their grade and/or submission comments.'
|
||||
return document.getElementById(getLabel(labelText).htmlFor)
|
||||
}
|
||||
|
||||
|
@ -66,13 +68,15 @@ QUnit.module('PostAssignmentGradesTray PostTypes', suiteHooks => {
|
|||
|
||||
test('"Everyone" type includes description"', () => {
|
||||
mountComponent()
|
||||
const labelText = 'EveryoneGrades will be made visible to all students'
|
||||
const labelText =
|
||||
'EveryoneAll students will be able to see their grade and/or submission comments.'
|
||||
ok(getLabel(labelText))
|
||||
})
|
||||
|
||||
test('"Graded" type includes description"', () => {
|
||||
mountComponent()
|
||||
const labelText = 'GradedGrades will be made visible to students with graded submissions'
|
||||
const labelText =
|
||||
'GradedStudents who have received a grade or a submission comment will be able to see their grade and/or submission comments.'
|
||||
ok(getLabel(labelText))
|
||||
})
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {isHidden, extractSimilarityInfo} from 'jsx/grading/helpers/SubmissionHelper'
|
||||
import {isPostable, extractSimilarityInfo} from 'jsx/grading/helpers/SubmissionHelper'
|
||||
|
||||
QUnit.module('SubmissionHelper', suiteHooks => {
|
||||
let submission
|
||||
|
@ -24,19 +24,21 @@ QUnit.module('SubmissionHelper', suiteHooks => {
|
|||
suiteHooks.beforeEach(() => {
|
||||
submission = {
|
||||
excused: false,
|
||||
hasPostableComments: false,
|
||||
score: null,
|
||||
submissionComments: [],
|
||||
workflowState: 'unsubmitted'
|
||||
}
|
||||
})
|
||||
|
||||
QUnit.module('.isHidden', () => {
|
||||
QUnit.module('.isPostable', () => {
|
||||
QUnit.module('when submission is excused', excusedHooks => {
|
||||
excusedHooks.beforeEach(() => {
|
||||
submission.excused = true
|
||||
})
|
||||
|
||||
test('returns true', () => {
|
||||
strictEqual(isHidden(submission), true)
|
||||
strictEqual(isPostable(submission), true)
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -44,17 +46,22 @@ QUnit.module('SubmissionHelper', suiteHooks => {
|
|||
test('is true when submission workflow state is graded and score is present', () => {
|
||||
submission.score = 1
|
||||
submission.workflowState = 'graded'
|
||||
strictEqual(isHidden(submission), true)
|
||||
strictEqual(isPostable(submission), true)
|
||||
})
|
||||
|
||||
test('is false when workflow state is not graded', () => {
|
||||
test('is true when submission hasPostableComments is true', () => {
|
||||
submission.hasPostableComments = true
|
||||
strictEqual(isPostable(submission), true)
|
||||
})
|
||||
|
||||
test('is false when workflow state is not graded and hasPostableComments is not true', () => {
|
||||
submission.score = 1
|
||||
strictEqual(isHidden(submission), false)
|
||||
strictEqual(isPostable(submission), false)
|
||||
})
|
||||
|
||||
test('is false when score is not present', () => {
|
||||
test('is false when score is not present and hasPostableComments is not true', () => {
|
||||
submission.workflowState = 'graded'
|
||||
strictEqual(isHidden(submission), false)
|
||||
strictEqual(isPostable(submission), false)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -212,6 +212,7 @@ QUnit.module('SpeedGrader PostPolicies', suiteHooks => {
|
|||
const submission = {
|
||||
id: '93',
|
||||
assignment_id: '2301',
|
||||
has_postable_comments: true,
|
||||
posted_at: new Date().toISOString(),
|
||||
score: 1.0,
|
||||
user_id: '441',
|
||||
|
@ -220,7 +221,12 @@ QUnit.module('SpeedGrader PostPolicies', suiteHooks => {
|
|||
postPolicies.showPostAssignmentGradesTray({submissions: [submission]})
|
||||
const {submissions} = postGradesShowArgs()
|
||||
deepEqual(submissions, [
|
||||
{postedAt: submission.posted_at, score: 1.0, workflowState: submission.workflow_state}
|
||||
{
|
||||
hasPostableComments: true,
|
||||
postedAt: submission.posted_at,
|
||||
score: 1.0,
|
||||
workflowState: submission.workflow_state
|
||||
}
|
||||
])
|
||||
})
|
||||
|
||||
|
|
|
@ -35,9 +35,9 @@ QUnit.module('SpeedGraderPostGradesMenu', hooks => {
|
|||
|
||||
function renderAndOpenMenu(customProps) {
|
||||
const props = {
|
||||
allowHidingGrades: true,
|
||||
allowPostingGrades: true,
|
||||
hasGrades: true,
|
||||
allowHidingGradesOrComments: true,
|
||||
allowPostingGradesOrComments: true,
|
||||
hasGradesOrPostableComments: true,
|
||||
onHideGrades: () => {},
|
||||
onPostGrades: () => {},
|
||||
...customProps
|
||||
|
@ -65,76 +65,86 @@ QUnit.module('SpeedGraderPostGradesMenu', hooks => {
|
|||
}
|
||||
|
||||
QUnit.module('menu trigger', () => {
|
||||
test('is rendered as an "off" icon when allowPostingGrades is true', () => {
|
||||
renderAndOpenMenu({allowPostingGrades: true})
|
||||
test('is rendered as an "off" icon when allowPostingGradesOrComments is true', () => {
|
||||
renderAndOpenMenu({allowPostingGradesOrComments: true})
|
||||
ok(getMenuTrigger().querySelector('svg[name="IconOff"]'))
|
||||
})
|
||||
|
||||
test('is rendered as an "eye" icon when allowPostingGrades is false', () => {
|
||||
renderAndOpenMenu({allowPostingGrades: false})
|
||||
test('is rendered as an "eye" icon when allowPostingGradesOrComments is false', () => {
|
||||
renderAndOpenMenu({allowPostingGradesOrComments: false})
|
||||
ok(getMenuTrigger().querySelector('svg[name="IconEye"]'))
|
||||
})
|
||||
})
|
||||
|
||||
QUnit.module('"Post Grades" menu item', () => {
|
||||
QUnit.module('when allowPostingGrades is true', itemHooks => {
|
||||
QUnit.module('when allowPostingGradesOrComments is true', itemHooks => {
|
||||
let postGradesSpy
|
||||
|
||||
itemHooks.beforeEach(() => {
|
||||
postGradesSpy = sinon.spy()
|
||||
renderAndOpenMenu({allowPostingGrades: true, onPostGrades: postGradesSpy})
|
||||
})
|
||||
|
||||
test('enables the "Post Grades" menu item', () => {
|
||||
notOk(getPostGradesMenuItem().getAttribute('aria-disabled'))
|
||||
renderAndOpenMenu({allowPostingGradesOrComments: true, onPostGrades: postGradesSpy})
|
||||
})
|
||||
|
||||
test('retains the text "Post Grades"', () => {
|
||||
strictEqual(getPostGradesMenuItem().textContent, 'Post Grades')
|
||||
})
|
||||
|
||||
test('enables the "Post Grades" menu item', () => {
|
||||
notOk(getPostGradesMenuItem().getAttribute('aria-disabled'))
|
||||
})
|
||||
|
||||
test('fires the onPostGrades event when clicked', () => {
|
||||
getPostGradesMenuItem().click()
|
||||
strictEqual(postGradesSpy.callCount, 1)
|
||||
})
|
||||
})
|
||||
|
||||
QUnit.module('when allowPostingGrades is false', itemHooks => {
|
||||
itemHooks.beforeEach(() => {
|
||||
renderAndOpenMenu({allowPostingGrades: false})
|
||||
QUnit.module('when allowPostingGradesOrComments is false', contextHooks => {
|
||||
let context
|
||||
|
||||
contextHooks.beforeEach(() => {
|
||||
context = {allowPostingGradesOrComments: false}
|
||||
})
|
||||
|
||||
test('disables the "Post Grades" menu item', () => {
|
||||
strictEqual(getPostGradesMenuItem().getAttribute('aria-disabled'), 'true')
|
||||
QUnit.module('when hasGradesOrPostableComments is false', itemHooks => {
|
||||
itemHooks.beforeEach(() => {
|
||||
context.hasGradesOrPostableComments = false
|
||||
renderAndOpenMenu(context)
|
||||
})
|
||||
|
||||
test('sets the text to "No Grades to Post"', () => {
|
||||
strictEqual(getPostGradesMenuItem().textContent, 'No Grades to Post')
|
||||
})
|
||||
|
||||
test('disables the "No Grades to Post" menu item', () => {
|
||||
strictEqual(getPostGradesMenuItem().getAttribute('aria-disabled'), 'true')
|
||||
})
|
||||
})
|
||||
|
||||
test('sets the text to "All Grades Posted"', () => {
|
||||
strictEqual(getPostGradesMenuItem().textContent, 'All Grades Posted')
|
||||
})
|
||||
})
|
||||
QUnit.module('when hasGradesOrPostableComments is true', itemHooks => {
|
||||
itemHooks.beforeEach(() => {
|
||||
context.hasGradesOrPostableComments = true
|
||||
renderAndOpenMenu(context)
|
||||
})
|
||||
|
||||
QUnit.module('when hasGrades is false', itemHooks => {
|
||||
itemHooks.beforeEach(() => {
|
||||
renderAndOpenMenu({hasGrades: false})
|
||||
})
|
||||
test('sets the text to "All Grades Posted"', () => {
|
||||
strictEqual(getPostGradesMenuItem().textContent, 'All Grades Posted')
|
||||
})
|
||||
|
||||
test('disables the "Post Grades" menu item', () => {
|
||||
strictEqual(getPostGradesMenuItem().getAttribute('aria-disabled'), 'true')
|
||||
})
|
||||
|
||||
test('sets the text to "No Grades to Post"', () => {
|
||||
strictEqual(getPostGradesMenuItem().textContent, 'No Grades to Post')
|
||||
test('disables the "All Grades Posted" menu item', () => {
|
||||
strictEqual(getPostGradesMenuItem().getAttribute('aria-disabled'), 'true')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
QUnit.module('"Hide Grades" menu item', () => {
|
||||
QUnit.module('when allowHidingGrades is true', itemHooks => {
|
||||
QUnit.module('when allowHidingGradesOrComments is true', itemHooks => {
|
||||
let hideGradesSpy
|
||||
|
||||
itemHooks.beforeEach(() => {
|
||||
hideGradesSpy = sinon.spy()
|
||||
renderAndOpenMenu({allowHidingGrades: true, onHideGrades: hideGradesSpy})
|
||||
renderAndOpenMenu({allowHidingGradesOrComments: true, onHideGrades: hideGradesSpy})
|
||||
})
|
||||
|
||||
test('enables the "Hide Grades" menu item', () => {
|
||||
|
@ -151,31 +161,41 @@ QUnit.module('SpeedGraderPostGradesMenu', hooks => {
|
|||
})
|
||||
})
|
||||
|
||||
QUnit.module('when allowHidingGrades is false', itemHooks => {
|
||||
itemHooks.beforeEach(() => {
|
||||
renderAndOpenMenu({allowHidingGrades: false})
|
||||
QUnit.module('when allowHidingGradesOrComments is false', contextHooks => {
|
||||
let context
|
||||
|
||||
contextHooks.beforeEach(() => {
|
||||
context = {allowHidingGradesOrComments: false}
|
||||
})
|
||||
|
||||
test('disables the "Hide Grades" menu item', () => {
|
||||
strictEqual(getHideGradesMenuItem().getAttribute('aria-disabled'), 'true')
|
||||
QUnit.module('when hasGradesOrPostableComments is false', itemHooks => {
|
||||
itemHooks.beforeEach(() => {
|
||||
context.hasGradesOrPostableComments = false
|
||||
renderAndOpenMenu(context)
|
||||
})
|
||||
|
||||
test('sets the text to "No Grades to Hide"', () => {
|
||||
strictEqual(getHideGradesMenuItem().textContent, 'No Grades to Hide')
|
||||
})
|
||||
|
||||
test('disables the "No Grades to Hide" menu item', () => {
|
||||
strictEqual(getHideGradesMenuItem().getAttribute('aria-disabled'), 'true')
|
||||
})
|
||||
})
|
||||
|
||||
test('sets the text to "All Grades Hidden"', () => {
|
||||
strictEqual(getHideGradesMenuItem().textContent, 'All Grades Hidden')
|
||||
})
|
||||
})
|
||||
QUnit.module('when hasGradesOrPostableComments is true', itemHooks => {
|
||||
itemHooks.beforeEach(() => {
|
||||
context.hasGradesOrPostableComments = true
|
||||
renderAndOpenMenu(context)
|
||||
})
|
||||
|
||||
QUnit.module('when hasGrades is false', itemHooks => {
|
||||
itemHooks.beforeEach(() => {
|
||||
renderAndOpenMenu({hasGrades: false})
|
||||
})
|
||||
test('sets the text to "All Grades Hidden"', () => {
|
||||
strictEqual(getHideGradesMenuItem().textContent, 'All Grades Hidden')
|
||||
})
|
||||
|
||||
test('disables the "Hide Grades" menu item', () => {
|
||||
strictEqual(getHideGradesMenuItem().getAttribute('aria-disabled'), 'true')
|
||||
})
|
||||
|
||||
test('sets the text to "No Grades to Hide"', () => {
|
||||
strictEqual(getHideGradesMenuItem().textContent, 'No Grades to Hide')
|
||||
test('disables the "All Grades Hidden" menu item', () => {
|
||||
strictEqual(getHideGradesMenuItem().getAttribute('aria-disabled'), 'true')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -3789,50 +3789,51 @@ QUnit.module('SpeedGrader', rootHooks => {
|
|||
})
|
||||
})
|
||||
|
||||
test('passes the allowHidingGrades prop as true if any submissions are posted', () => {
|
||||
test('passes the allowHidingGradesOrComments prop as true if any submissions are posted', () => {
|
||||
SpeedGrader.EG.jsonReady()
|
||||
|
||||
const [SpeedGraderPostGradesMenu] = findRenderCall()
|
||||
strictEqual(SpeedGraderPostGradesMenu.props.allowHidingGrades, true)
|
||||
strictEqual(SpeedGraderPostGradesMenu.props.allowHidingGradesOrComments, true)
|
||||
})
|
||||
|
||||
test('passes the allowHidingGrades prop as false if no submissions are posted', () => {
|
||||
test('passes the allowHidingGradesOrComments prop as false if no submissions are posted', () => {
|
||||
alphaSubmission.posted_at = null
|
||||
omegaSubmission.posted_at = null
|
||||
|
||||
SpeedGrader.EG.jsonReady()
|
||||
|
||||
const [SpeedGraderPostGradesMenu] = findRenderCall()
|
||||
strictEqual(SpeedGraderPostGradesMenu.props.allowHidingGrades, false)
|
||||
strictEqual(SpeedGraderPostGradesMenu.props.allowHidingGradesOrComments, false)
|
||||
})
|
||||
|
||||
test('passes the allowPostingGrades prop as true if any submissions are unposted', () => {
|
||||
test('passes the allowPostingGradesOrComments prop as true if any submissions are postable', () => {
|
||||
alphaSubmission.posted_at = null
|
||||
alphaSubmission.has_postable_comments = true
|
||||
|
||||
SpeedGrader.EG.jsonReady()
|
||||
|
||||
const [SpeedGraderPostGradesMenu] = findRenderCall()
|
||||
strictEqual(SpeedGraderPostGradesMenu.props.allowPostingGrades, true)
|
||||
strictEqual(SpeedGraderPostGradesMenu.props.allowPostingGradesOrComments, true)
|
||||
})
|
||||
|
||||
test('passes the allowPostingGrades prop as false if all submissions are posted', () => {
|
||||
test('passes the allowPostingGradesOrComments prop as false if all submissions are posted', () => {
|
||||
SpeedGrader.EG.jsonReady()
|
||||
|
||||
const [SpeedGraderPostGradesMenu] = findRenderCall()
|
||||
strictEqual(SpeedGraderPostGradesMenu.props.allowPostingGrades, false)
|
||||
strictEqual(SpeedGraderPostGradesMenu.props.allowPostingGradesOrComments, false)
|
||||
})
|
||||
|
||||
test('passes the hasGrades prop as true if any submissions are graded', () => {
|
||||
test('passes the hasGradesOrPostableComments prop as true if any submissions are graded', () => {
|
||||
SpeedGrader.EG.jsonReady()
|
||||
const [SpeedGraderPostGradesMenu] = findRenderCall()
|
||||
strictEqual(SpeedGraderPostGradesMenu.props.hasGrades, true)
|
||||
strictEqual(SpeedGraderPostGradesMenu.props.hasGradesOrPostableComments, true)
|
||||
})
|
||||
|
||||
test('passes the hasGrades prop as false if no submissions are graded', () => {
|
||||
test('passes the hasGradesOrPostableComments prop as false if no submissions are graded', () => {
|
||||
alphaSubmission.score = null
|
||||
SpeedGrader.EG.jsonReady()
|
||||
const [SpeedGraderPostGradesMenu] = findRenderCall()
|
||||
strictEqual(SpeedGraderPostGradesMenu.props.hasGrades, false)
|
||||
strictEqual(SpeedGraderPostGradesMenu.props.hasGradesOrPostableComments, false)
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -280,6 +280,52 @@ describe SpeedGrader::Assignment do
|
|||
allow(Canvadoc).to receive(:mime_types).and_return("image/png")
|
||||
end
|
||||
|
||||
describe "has_postable_comments" do
|
||||
before(:each) do
|
||||
PostPolicy.enable_feature!
|
||||
@course.root_account.enable_feature!(:allow_postable_submission_comments)
|
||||
@course.enable_feature!(:new_gradebook)
|
||||
@assignment.ensure_post_policy(post_manually: true)
|
||||
end
|
||||
|
||||
it "is not included when allow_postable_submission_comments feature is not enabled" do
|
||||
@course.root_account.disable_feature!(:allow_postable_submission_comments)
|
||||
json = SpeedGrader::Assignment.new(@assignment, @teacher).json
|
||||
expect(json[:submissions].first).not_to have_key "has_postable_comments"
|
||||
end
|
||||
|
||||
it "is not included when Post Policies are not enabled" do
|
||||
@course.disable_feature!(:new_gradebook)
|
||||
json = SpeedGrader::Assignment.new(@assignment, @teacher).json
|
||||
expect(json[:submissions].first).not_to have_key "has_postable_comments"
|
||||
end
|
||||
|
||||
it "is true when unposted, hidden comments exist, and postable comments feature is enabled" do
|
||||
student1_sub = @assignment.submissions.find_by!(user: @student_1)
|
||||
student1_sub.add_comment(author: @teacher, comment: "good job!", hidden: true)
|
||||
json = SpeedGrader::Assignment.new(@assignment, @teacher).json
|
||||
submission_json = json[:submissions].find { |sub| sub["user_id"] == student1_sub.user_id.to_s }
|
||||
expect(submission_json["has_postable_comments"]).to be true
|
||||
end
|
||||
|
||||
it "is not present when unposted, hidden comments exist, and postable comments feature is not enabled" do
|
||||
@course.root_account.disable_feature!(:allow_postable_submission_comments)
|
||||
student1_sub = @assignment.submissions.find_by!(user: @student_1)
|
||||
student1_sub.add_comment(author: @teacher, comment: "good job!", hidden: true)
|
||||
json = SpeedGrader::Assignment.new(@assignment, @teacher).json
|
||||
submission_json = json[:submissions].find { |sub| sub["user_id"] == student1_sub.user_id.to_s }
|
||||
expect(submission_json).not_to have_key "has_postable_comments"
|
||||
end
|
||||
|
||||
it "is false when unposted and only non-hidden comments exist" do
|
||||
student1_sub = @assignment.submissions.find_by!(user: @student_1)
|
||||
student1_sub.add_comment(author: @student1, comment: "good job!", hidden: false)
|
||||
json = SpeedGrader::Assignment.new(@assignment, @teacher).json
|
||||
submission_json = json[:submissions].find { |sub| sub["user_id"] == student1_sub.user_id.to_s }
|
||||
expect(submission_json["has_postable_comments"]).to be false
|
||||
end
|
||||
end
|
||||
|
||||
it "returns submission lateness" do
|
||||
json = SpeedGrader::Assignment.new(@assignment, @teacher).json
|
||||
json[:submissions].each do |submission|
|
||||
|
|
|
@ -3256,6 +3256,50 @@ describe Submission do
|
|||
end
|
||||
end
|
||||
|
||||
describe "scope: postable" do
|
||||
subject(:submissions) { assignment.submissions.postable }
|
||||
|
||||
let(:assignment) { @course.assignments.create! }
|
||||
let(:submission) { assignment.submissions.find_by(user: @student) }
|
||||
|
||||
it "does not include submissions that neither have grades nor hidden comments" do
|
||||
submission.add_comment(author: @teacher, comment: "good job!", hidden: false)
|
||||
is_expected.not_to include(submission)
|
||||
end
|
||||
|
||||
it "includes submissions with hidden comments" do
|
||||
submission.add_comment(author: @teacher, comment: "good job!", hidden: true)
|
||||
is_expected.to include(submission)
|
||||
end
|
||||
|
||||
it "includes submissions with a grade" do
|
||||
assignment.grade_student(@student, grader: @teacher, grade: 10)
|
||||
is_expected.to include(submission)
|
||||
end
|
||||
|
||||
it "includes submissions that are excused" do
|
||||
assignment.grade_student(@student, grader: @teacher, excused: true)
|
||||
is_expected.to include(submission)
|
||||
end
|
||||
end
|
||||
|
||||
describe "scope: with_hidden_comments" do
|
||||
subject(:submissions) { assignment.submissions.with_hidden_comments }
|
||||
|
||||
let(:assignment) { @course.assignments.create! }
|
||||
let(:submission) { assignment.submissions.find_by(user: @student) }
|
||||
|
||||
it "does not include submissions without a hidden comment" do
|
||||
submission.add_comment(author: @teacher, comment: "good job!", hidden: false)
|
||||
is_expected.not_to include(submission)
|
||||
end
|
||||
|
||||
it "includes submissions with hidden comments" do
|
||||
submission.add_comment(author: @teacher, comment: "good job!", hidden: true)
|
||||
is_expected.to include(submission)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'scope: anonymized' do
|
||||
subject(:submissions) { assignment.all_submissions.anonymized }
|
||||
|
||||
|
|
Loading…
Reference in New Issue