diff --git a/app/jsx/grading/HideAssignmentGradesTray/index.js b/app/jsx/grading/HideAssignmentGradesTray/index.js index 4f915770f2a..9d63033e705 100644 --- a/app/jsx/grading/HideAssignmentGradesTray/index.js +++ b/app/jsx/grading/HideAssignmentGradesTray/index.js @@ -55,6 +55,7 @@ export default class HideAssignmentGradesTray extends PureComponent { this.state = { hideBySections: false, hidingGrades: false, + onExited() {}, open: false, selectedSectionIds: [] } @@ -113,7 +114,7 @@ export default class HideAssignmentGradesTray extends PureComponent { message: successMessage, type: 'success' }) - } catch (error) { + } catch (_error) { showFlashAlert({ message: I18n.t('There was a problem hiding assignment grades.'), type: 'error' diff --git a/app/jsx/grading/PostAssignmentGradesTray/index.js b/app/jsx/grading/PostAssignmentGradesTray/index.js index 2095cf573af..7469556dbd1 100644 --- a/app/jsx/grading/PostAssignmentGradesTray/index.js +++ b/app/jsx/grading/PostAssignmentGradesTray/index.js @@ -60,6 +60,7 @@ export default class PostAssignmentGradesTray extends PureComponent { postBySections: false, postType: EVERYONE, postingGrades: false, + onExited() {}, open: false, selectedSectionIds: [], submissions: [] diff --git a/app/jsx/speed_grader/PostPolicies/index.js b/app/jsx/speed_grader/PostPolicies/index.js index 5f500967013..40154c49b23 100644 --- a/app/jsx/speed_grader/PostPolicies/index.js +++ b/app/jsx/speed_grader/PostPolicies/index.js @@ -22,14 +22,24 @@ import ReactDOM from 'react-dom' import HideAssignmentGradesTray from '../../grading/HideAssignmentGradesTray' import PostAssignmentGradesTray from '../../grading/PostAssignmentGradesTray' +function submissionsPostedAtUpdater({submissionsMap, updateSubmission, afterUpdateSubmission}) { + return function({postedAt, userIds}) { + userIds.forEach(userId => { + const submission = submissionsMap[userId] + submission.posted_at = postedAt + updateSubmission(submission) + }) + afterUpdateSubmission() + } +} + export default class PostPolicies { - constructor({assignment, sections}) { + constructor({assignment, sections, updateSubmission, afterUpdateSubmission}) { this._assignment = assignment this._sections = sections - this.initialize() - } + this._updateSubmission = updateSubmission + this._afterUpdateSubmission = afterUpdateSubmission - initialize() { const $hideContainer = document.getElementById('hide-assignment-grades-tray') const bindHideTray = ref => { this._hideAssignmentGradesTray = ref @@ -56,18 +66,28 @@ export default class PostPolicies { } } - showHideAssignmentGradesTray({onExited}) { + showHideAssignmentGradesTray({submissionsMap}) { + const onHidden = submissionsPostedAtUpdater({ + afterUpdateSubmission: this._afterUpdateSubmission, + submissionsMap, + updateSubmission: this._updateSubmission + }) this._hideAssignmentGradesTray.show({ assignment: this._assignment, - onExited, + onHidden, sections: this._sections }) } - showPostAssignmentGradesTray({onExited, submissions}) { + showPostAssignmentGradesTray({submissionsMap, submissions}) { + const onPosted = submissionsPostedAtUpdater({ + afterUpdateSubmission: this._afterUpdateSubmission, + submissionsMap, + updateSubmission: this._updateSubmission + }) this._postAssignmentGradesTray.show({ assignment: this._assignment, - onExited, + onPosted, sections: this._sections, submissions }) diff --git a/app/jsx/speed_grader/SpeedGraderPostGradesMenu.js b/app/jsx/speed_grader/SpeedGraderPostGradesMenu.js index 51cf43bc7b1..0960834dd3c 100644 --- a/app/jsx/speed_grader/SpeedGraderPostGradesMenu.js +++ b/app/jsx/speed_grader/SpeedGraderPostGradesMenu.js @@ -29,7 +29,7 @@ import I18n from 'i18n!SpeedGraderPostGradesMenu' export default function SpeedGraderPostGradesMenu(props) { const menuTrigger = ( ) diff --git a/public/javascripts/speed_grader.js b/public/javascripts/speed_grader.js index 2d3313a5f86..0e538bb67b8 100644 --- a/public/javascripts/speed_grader.js +++ b/public/javascripts/speed_grader.js @@ -491,6 +491,29 @@ function initDropdown() { } } +function setupPostPolicies() { + if (!ENV.post_policies_enabled) return + const {jsonData} = window + const gradesPublished = !jsonData.moderated_grading || jsonData.grades_published_at != null + + EG.postPolicies = new PostPolicies({ + assignment: { + anonymousGrading: jsonData.anonymous_grading, + gradesPublished, + id: jsonData.id, + name: jsonData.title + }, + sections: jsonData.context.active_course_sections, + updateSubmission: EG.setOrUpdateSubmission, + afterUpdateSubmission() { + renderPostGradesMenu() + EG.showGrade() + } + }) + + renderPostGradesMenu() +} + function setupHeader({showMuteButton = true}) { const elements = { nav: $gradebook_header.find('#prev-student-button, #next-student-button'), @@ -1344,21 +1367,7 @@ EG = { initDropdown() initGroupAssignmentMode() setupHandleStatePopped() - - if (ENV.post_policies_enabled) { - const {jsonData} = window - - EG.postPolicies = new PostPolicies({ - assignment: { - anonymizeStudents: jsonData.anonymize_students, - gradesPublished: !jsonData.moderated_grading || jsonData.grades_published_at != null, - id: jsonData.id, - name: jsonData.title - }, - sections: jsonData.context.active_course_sections - }) - renderPostGradesMenu() - } + setupPostPolicies() } }, @@ -3636,29 +3645,38 @@ function renderSettingsMenu() { showHelpMenuItem: ENV.show_help_menu_item } - const settingsMenu = - ReactDOM.render(settingsMenu, document.getElementById(SPEED_GRADER_SETTINGS_MOUNT_POINT)) + const mountPoint = document.getElementById(SPEED_GRADER_SETTINGS_MOUNT_POINT) + ReactDOM.render(, mountPoint) +} + +function teardownSettingsMenu() { + const mountPoint = document.getElementById(SPEED_GRADER_SETTINGS_MOUNT_POINT) + ReactDOM.unmountComponentAtNode(mountPoint) } function renderPostGradesMenu() { + const {submissionsMap} = window.jsonData const submissions = window.jsonData.studentsWithSubmissions.map(student => student.submission) const allowHidingGrades = submissions.some(submission => submission.posted_at != null) const allowPostingGrades = submissions.some(submission => submission.posted_at == null) + function onHideGrades() { + EG.postPolicies.showHideAssignmentGradesTray({submissionsMap}) + } + + function onPostGrades() { + EG.postPolicies.showPostAssignmentGradesTray({submissionsMap, submissions}) + } + const props = { allowHidingGrades, allowPostingGrades, - onHideGrades: () => { - EG.postPolicies.showHideAssignmentGradesTray({onExited: () => {}}) - }, - onPostGrades: () => { - EG.postPolicies.showPostAssignmentGradesTray({onExited: () => {}, submissions}) - } + onHideGrades, + onPostGrades } - const postGradesMenu = ReactDOM.render( - postGradesMenu, + , document.getElementById(SPEED_GRADER_POST_GRADES_MENU_MOUNT_POINT) ) } @@ -3727,6 +3745,7 @@ export default { EG.postPolicies.destroy() } + teardownSettingsMenu() teardownHandleStatePopped() teardownBeforeLeavingSpeedgrader() }, diff --git a/spec/javascripts/jsx/grading/HideAssignmentGradesTray/HideAssignmentGradesTraySpec.js b/spec/javascripts/jsx/grading/HideAssignmentGradesTray/HideAssignmentGradesTraySpec.js index 76ec140fc1c..39968925cd7 100644 --- a/spec/javascripts/jsx/grading/HideAssignmentGradesTray/HideAssignmentGradesTraySpec.js +++ b/spec/javascripts/jsx/grading/HideAssignmentGradesTray/HideAssignmentGradesTraySpec.js @@ -164,6 +164,15 @@ QUnit.module('HideAssignmentGradesTray', suiteHooks => { await waitForTrayClosed() notOk(getTrayElement()) }) + + test('calls optional onExited', async () => { + await show() + await waitForElement(getTrayElement) + getCloseIconButton().click() + await waitForTrayClosed() + const {callCount} = context.onExited + strictEqual(callCount, 1) + }) }) QUnit.module('"Specific Sections" toggle', hooks => { @@ -200,6 +209,15 @@ QUnit.module('HideAssignmentGradesTray', suiteHooks => { await waitForTrayClosed() notOk(getTrayElement()) }) + + test('calls optional onExited', async () => { + await show() + await waitForElement(getTrayElement) + getCloseButton().click() + await waitForTrayClosed() + const {callCount} = context.onExited + strictEqual(callCount, 1) + }) }) QUnit.module('"Hide" Button', hooks => { diff --git a/spec/javascripts/jsx/grading/PostAssignmentGradesTray/PostAssignmentGradesTraySpec.js b/spec/javascripts/jsx/grading/PostAssignmentGradesTray/PostAssignmentGradesTraySpec.js index 70b69fc0540..713e1f98fea 100644 --- a/spec/javascripts/jsx/grading/PostAssignmentGradesTray/PostAssignmentGradesTraySpec.js +++ b/spec/javascripts/jsx/grading/PostAssignmentGradesTray/PostAssignmentGradesTraySpec.js @@ -177,6 +177,15 @@ QUnit.module('PostAssignmentGradesTray', suiteHooks => { await waitForTrayClosed() notOk(getTrayElement()) }) + + test('calls optional onExited', async () => { + await show() + await waitForElement(getTrayElement) + getCloseIconButton().click() + await waitForTrayClosed() + const {callCount} = context.onExited + strictEqual(callCount, 1) + }) }) QUnit.module('"Specific Sections" toggle', hooks => { @@ -213,6 +222,15 @@ QUnit.module('PostAssignmentGradesTray', suiteHooks => { await waitForTrayClosed() notOk(getTrayElement()) }) + + test('calls optional onExited', async () => { + await show() + await waitForElement(getTrayElement) + getCloseIconButton().click() + await waitForTrayClosed() + const {callCount} = context.onExited + strictEqual(callCount, 1) + }) }) QUnit.module('unposted summary', () => { diff --git a/spec/javascripts/jsx/speed_grader/PostPolicies/PostPoliciesSpec.js b/spec/javascripts/jsx/speed_grader/PostPolicies/PostPoliciesSpec.js index 8fccfe9b8c2..e487a11c90a 100644 --- a/spec/javascripts/jsx/speed_grader/PostPolicies/PostPoliciesSpec.js +++ b/spec/javascripts/jsx/speed_grader/PostPolicies/PostPoliciesSpec.js @@ -16,13 +16,24 @@ * with this program. If not, see . */ -import ReactDOM from 'react-dom' +import {unmountComponentAtNode} from 'react-dom' import PostPolicies from '../../../../../app/jsx/speed_grader/PostPolicies' QUnit.module('SpeedGrader PostPolicies', suiteHooks => { let $hideTrayMountPoint let $postTrayMountPoint + let afterUpdateSubmission let postPolicies + let updateSubmission + + function expectedAssignment() { + return { + anonymizeStudents: false, + gradesPublished: true, + id: '2301', + name: 'Math 1.1' + } + } suiteHooks.beforeEach(() => { $hideTrayMountPoint = document.createElement('div') @@ -33,14 +44,15 @@ QUnit.module('SpeedGrader PostPolicies', suiteHooks => { document.body.appendChild($hideTrayMountPoint) document.body.appendChild($postTrayMountPoint) - const assignment = { - anonymizeStudents: false, - gradesPublished: true, - id: '2301', - name: 'Math 1.1' - } const sections = [{id: '2001', name: 'Hogwarts'}, {id: '2002', name: 'Freshmen'}] - postPolicies = new PostPolicies({assignment, sections}) + afterUpdateSubmission = sinon.stub() + updateSubmission = sinon.stub() + postPolicies = new PostPolicies({ + afterUpdateSubmission, + assignment: expectedAssignment(), + sections, + updateSubmission + }) }) suiteHooks.afterEach(() => { @@ -51,121 +63,116 @@ QUnit.module('SpeedGrader PostPolicies', suiteHooks => { test('renders the "Hide Assignment Grades" tray', () => { const $trayContainer = document.getElementById('hide-assignment-grades-tray') - const unmounted = ReactDOM.unmountComponentAtNode($trayContainer) + const unmounted = unmountComponentAtNode($trayContainer) strictEqual(unmounted, true) }) test('renders the "Post Assignment Grades" tray', () => { const $trayContainer = document.getElementById('post-assignment-grades-tray') - const unmounted = ReactDOM.unmountComponentAtNode($trayContainer) + const unmounted = unmountComponentAtNode($trayContainer) strictEqual(unmounted, true) }) - QUnit.module('#destroy()', () => { + QUnit.module('#destroy', () => { test('unmounts the "Hide Assignment Grades" tray', () => { postPolicies.destroy() const $trayContainer = document.getElementById('hide-assignment-grades-tray') - const unmounted = ReactDOM.unmountComponentAtNode($trayContainer) + const unmounted = unmountComponentAtNode($trayContainer) strictEqual(unmounted, false) }) test('unmounts the "Post Assignment Grades" tray', () => { postPolicies.destroy() const $trayContainer = document.getElementById('post-assignment-grades-tray') - const unmounted = ReactDOM.unmountComponentAtNode($trayContainer) + const unmounted = unmountComponentAtNode($trayContainer) strictEqual(unmounted, false) }) }) - QUnit.module('#showHideAssignmentGradesTray()', hooks => { + QUnit.module('#showHideAssignmentGradesTray', hooks => { + function hideGradesShowArgs() { + return postPolicies._hideAssignmentGradesTray.show.firstCall.args[0] + } + hooks.beforeEach(() => { sinon.stub(postPolicies._hideAssignmentGradesTray, 'show') }) - test('shows the "Hide Assignment Grades" tray', () => { + test('calls "show" for the "Hide Assignment Grades" tray', () => { postPolicies.showHideAssignmentGradesTray({}) strictEqual(postPolicies._hideAssignmentGradesTray.show.callCount, 1) }) - test('includes the assignment id when showing the "Hide Assignment Grades" tray', () => { + test('passes the assignment to "show"', () => { postPolicies.showHideAssignmentGradesTray({}) - const [{assignment}] = postPolicies._hideAssignmentGradesTray.show.lastCall.args - strictEqual(assignment.id, '2301') + const {assignment} = hideGradesShowArgs() + deepEqual(assignment, expectedAssignment()) }) - test('includes the assignment name when showing the "Hide Assignment Grades" tray', () => { + test('passes the sections to "show"', () => { postPolicies.showHideAssignmentGradesTray({}) - const [{assignment}] = postPolicies._hideAssignmentGradesTray.show.lastCall.args - strictEqual(assignment.name, 'Math 1.1') - }) - - test('includes the assignment anonymizeStudents', () => { - postPolicies.showHideAssignmentGradesTray({}) - const [{assignment}] = postPolicies._hideAssignmentGradesTray.show.lastCall.args - strictEqual(assignment.anonymizeStudents, false) - }) - - test('includes the assignment gradesPublished', () => { - postPolicies.showHideAssignmentGradesTray({}) - const [{assignment}] = postPolicies._hideAssignmentGradesTray.show.lastCall.args - strictEqual(assignment.gradesPublished, true) - }) - - test('includes the sections', () => { - postPolicies.showHideAssignmentGradesTray({}) - const [{sections}] = postPolicies._hideAssignmentGradesTray.show.lastCall.args + const {sections} = hideGradesShowArgs() deepEqual(sections, [{id: '2001', name: 'Hogwarts'}, {id: '2002', name: 'Freshmen'}]) }) - test('includes the `onExited` callback when showing the "Hide Assignment Grades" tray', () => { - const callback = sinon.stub() - postPolicies.showHideAssignmentGradesTray({onExited: callback}) - const [{onExited}] = postPolicies._hideAssignmentGradesTray.show.lastCall.args - strictEqual(onExited, callback) + test('passes updateSubmission to "show"', () => { + postPolicies.showHideAssignmentGradesTray({ + submissionsMap: { + '1': {posted_at: new Date().toISOString()} + } + }) + const {onHidden} = hideGradesShowArgs() + onHidden({userIds: ['1']}) + strictEqual(updateSubmission.callCount, 1) + }) + + test('passes afterUpdateSubmission to "show"', () => { + postPolicies.showHideAssignmentGradesTray({ + submissionsMap: {'1': {posted_at: new Date().toISOString()}} + }) + const {onHidden} = hideGradesShowArgs() + onHidden({userIds: ['1']}) + strictEqual(afterUpdateSubmission.callCount, 1) + }) + + test('onHidden updates posted_at', () => { + const submissionsMap = { + '1': {posted_at: new Date().toISOString()} + } + postPolicies.showHideAssignmentGradesTray({submissionsMap}) + const {onHidden} = hideGradesShowArgs() + onHidden({postedAt: null, userIds: ['1']}) + strictEqual(submissionsMap['1'].posted_at, null) }) }) - QUnit.module('#showPostAssignmentGradesTray()', hooks => { + QUnit.module('#showPostAssignmentGradesTray', hooks => { + function postGradesShowArgs() { + return postPolicies._postAssignmentGradesTray.show.firstCall.args[0] + } + hooks.beforeEach(() => { sinon.stub(postPolicies._postAssignmentGradesTray, 'show') }) - test('shows the "Post Assignment Grades" tray', () => { + test('calls "show" for the "Post Assignment Grades" tray', () => { postPolicies.showPostAssignmentGradesTray({}) strictEqual(postPolicies._postAssignmentGradesTray.show.callCount, 1) }) - test('includes the assignment id when showing the "Post Assignment Grades" tray', () => { + test('passes the assignment to "show"', () => { postPolicies.showPostAssignmentGradesTray({}) - const [{assignment}] = postPolicies._postAssignmentGradesTray.show.lastCall.args - strictEqual(assignment.id, '2301') + const {assignment} = postGradesShowArgs() + deepEqual(assignment, expectedAssignment()) }) - test('includes the assignment name when showing the "Post Assignment Grades" tray', () => { + test('passes sections to "show"', () => { postPolicies.showPostAssignmentGradesTray({}) - const [{assignment}] = postPolicies._postAssignmentGradesTray.show.lastCall.args - strictEqual(assignment.name, 'Math 1.1') - }) - - test('includes the assignment anonymizeStudents', () => { - postPolicies.showPostAssignmentGradesTray({}) - const [{assignment}] = postPolicies._postAssignmentGradesTray.show.lastCall.args - strictEqual(assignment.anonymizeStudents, false) - }) - - test('includes the assignment gradesPublished', () => { - postPolicies.showPostAssignmentGradesTray({}) - const [{assignment}] = postPolicies._postAssignmentGradesTray.show.lastCall.args - strictEqual(assignment.gradesPublished, true) - }) - - test('includes the sections', () => { - postPolicies.showPostAssignmentGradesTray({}) - const [{sections}] = postPolicies._postAssignmentGradesTray.show.lastCall.args + const {sections} = postGradesShowArgs() deepEqual(sections, [{id: '2001', name: 'Hogwarts'}, {id: '2002', name: 'Freshmen'}]) }) - test('includes the submissions', () => { + test('passes submissions to "show"', () => { const submission = { id: '93', assignment_id: '2301', @@ -173,15 +180,33 @@ QUnit.module('SpeedGrader PostPolicies', suiteHooks => { user_id: '441' } postPolicies.showPostAssignmentGradesTray({submissions: [submission]}) - const [{submissions}] = postPolicies._postAssignmentGradesTray.show.lastCall.args + const {submissions} = postGradesShowArgs() deepEqual(submissions, [submission]) }) - test('includes the `onExited` callback when showing the "Post Assignment Grades" tray', () => { - const callback = sinon.stub() - postPolicies.showPostAssignmentGradesTray({onExited: callback}) - const [{onExited}] = postPolicies._postAssignmentGradesTray.show.lastCall.args - strictEqual(onExited, callback) + test('passes updateSubmission to "show"', () => { + postPolicies.showPostAssignmentGradesTray({submissionsMap: {'1': {posted_at: null}}}) + const {onPosted} = postGradesShowArgs() + onPosted({userIds: ['1']}) + strictEqual(updateSubmission.callCount, 1) + }) + + test('passes afterUpdateSubmission to "show"', () => { + postPolicies.showPostAssignmentGradesTray({submissionsMap: {'1': {posted_at: null}}}) + const {onPosted} = postGradesShowArgs() + onPosted({userIds: ['1']}) + strictEqual(afterUpdateSubmission.callCount, 1) + }) + + test('onPosted updates posted_at', () => { + const submissionsMap = { + '1': {posted_at: null} + } + postPolicies.showPostAssignmentGradesTray({submissionsMap}) + const postedAt = new Date().toISOString() + const {onPosted} = postGradesShowArgs() + onPosted({postedAt, userIds: ['1']}) + strictEqual(submissionsMap['1'].posted_at, postedAt) }) }) }) diff --git a/spec/javascripts/jsx/speed_graderSpec.js b/spec/javascripts/jsx/speed_graderSpec.js index be879b1c0e8..c5332ff4710 100644 --- a/spec/javascripts/jsx/speed_graderSpec.js +++ b/spec/javascripts/jsx/speed_graderSpec.js @@ -17,12 +17,12 @@ */ import $ from 'jquery' +import React from 'react' import ReactDOM from 'react-dom' +import _ from 'underscore' import SpeedGrader from 'speed_grader' import SpeedGraderHelpers from 'speed_grader_helpers' - -import _ from 'underscore' import JQuerySelectorCache from 'jsx/shared/helpers/JQuerySelectorCache' import fakeENV from 'helpers/fakeENV' import numberHelper from 'jsx/shared/helpers/numberHelper' @@ -141,6 +141,7 @@ QUnit.module('SpeedGrader#showDiscussion', { }, teardown() { + SpeedGrader.teardown() SpeedGrader.EG.domReady.restore() $.getJSON.restore() SpeedGrader.EG.currentStudent = this.originalStudent @@ -242,6 +243,7 @@ test('can handle non-nested submission history', () => { SpeedGrader.setup() SpeedGrader.EG.refreshSubmissionsToView() ok(true, 'should not throw an exception') + SpeedGrader.teardown() }) test('includes submission time for submissions when not anonymizing', () => { @@ -250,6 +252,7 @@ test('includes submission time for submissions when not anonymizing', () => { const submissionDropdown = document.getElementById('multiple_submissions') ok(submissionDropdown.innerHTML.includes('Jan 1, 2010')) + SpeedGrader.teardown() }) test('includes submission time for submissions when the user is an admin', () => { @@ -261,6 +264,7 @@ test('includes submission time for submissions when the user is an admin', () => const submissionDropdown = document.getElementById('multiple_submissions') ok(submissionDropdown.innerHTML.includes('Jan 1, 2010')) + SpeedGrader.teardown() }) test('omits submission time for submissions when anonymizing and not an admin', () => { @@ -272,6 +276,7 @@ test('omits submission time for submissions when anonymizing and not an admin', const submissionDropdown = document.getElementById('multiple_submissions') notOk(submissionDropdown.innerHTML.includes('Jan 1, 2010')) + SpeedGrader.teardown() }) test('sets submission history container content to empty when submission history is blank', () => { @@ -281,6 +286,7 @@ test('sets submission history container content to empty when submission history SpeedGrader.EG.refreshSubmissionsToView() const submissionDropdown = document.getElementById('multiple_submissions') strictEqual(submissionDropdown.innerHTML, '') + SpeedGrader.teardown() }) QUnit.module('#showSubmissionDetails', function(hooks) { @@ -316,6 +322,7 @@ QUnit.module('#showSubmissionDetails', function(hooks) { }) hooks.afterEach(function() { + SpeedGrader.teardown() SpeedGrader.EG.domReady.restore() $.ajaxJSON.restore() $.getJSON.restore() @@ -725,9 +732,9 @@ QUnit.module('SpeedGrader#handleGradeSubmit', { teardown() { SpeedGrader.EG.currentStudent = this.originalStudent - teardownFixtures() window.jsonData = this.originalWindowJSONData SpeedGrader.teardown() + teardownFixtures() fakeENV.teardown() SpeedGraderHelpers.reloadPage.restore() } @@ -862,6 +869,7 @@ QUnit.module('renderLtiLaunch', { }, teardown() { + SpeedGrader.teardown() SpeedGrader.EG.domReady.restore() $.ajaxJSON.restore() $.getJSON.restore() @@ -1792,78 +1800,80 @@ QUnit.module('SpeedGrader', suiteHooks => { }) QUnit.module('"Post Policies"', hooks => { - function getPostAndHideGradesButton() { + function postAndHideGradesButton() { return document.querySelector('span#speed_grader_post_grades_menu_mount_point button') } - function getHideGradesMenuItem() { + function hideGradesMenuItem() { return document.querySelector('[name="hideGrades"]') } - function getPostGradesMenuItem() { + function postGradesMenuItem() { return document.querySelector('[name="postGrades"]') } - let showHideAssignmentGradesTrayStub - let showPostAssignmentGradesTrayStub - - const postedSubmission = { - id: '3001', - posted_at: new Date().toISOString(), - user_id: '1101' - } - - const unpostedSubmission = { - id: '3002', - posted_at: null, - user_id: '1102' - } - - const windowJsonData = { - anonymize_students: false, - grades_published_at: null, - moderated_grading: false, - id: '2301', - title: 'Assignment 1', - context: { - students: [{id: '1101'}, {id: '1102'}], - enrollments: [{user_id: '1101', course_section_id: '2001'}, {user_id: '1102', course_section_id: '2001'}], - active_course_sections: [] - }, - submissions: [postedSubmission, unpostedSubmission] - } + let showHideAssignmentGradesTray + let showPostAssignmentGradesTray + let setOrUpdateSubmission + let showGrade + let postedSubmission + let unpostedSubmission hooks.beforeEach(() => { - ENV.post_policies_enabled = true - window.jsonData = windowJsonData + fakeENV.setup({ + assignment_id: '17', + course_id: '29', + grading_role: 'moderator', + help_url: 'example.com/support', + post_policies_enabled: true, + show_help_menu_item: false + }) + postedSubmission = {posted_at: new Date().toISOString(), user_id: '1101'} + unpostedSubmission = {posted_at: null, user_id: '1102'} + + SpeedGrader.setup() + window.jsonData = { + context: { + students: [{id: '1101'}, {id: '1102'}], + enrollments: [{user_id: '1101'}, {user_id: '1102'}], + active_course_sections: [] + }, + submissions: [postedSubmission, unpostedSubmission] + } SpeedGrader.EG.jsonReady() - showHideAssignmentGradesTrayStub = sinon.stub(SpeedGrader.EG.postPolicies, 'showHideAssignmentGradesTray') - showPostAssignmentGradesTrayStub = sinon.stub(SpeedGrader.EG.postPolicies, 'showPostAssignmentGradesTray') - getPostAndHideGradesButton().click() + showHideAssignmentGradesTray = sinon.stub(SpeedGrader.EG.postPolicies, 'showHideAssignmentGradesTray') + showPostAssignmentGradesTray = sinon.stub(SpeedGrader.EG.postPolicies, 'showPostAssignmentGradesTray') + setOrUpdateSubmission = sinon.stub(SpeedGrader.EG, 'setOrUpdateSubmission') + showGrade = sinon.stub(SpeedGrader.EG, 'showGrade') + postAndHideGradesButton().click() }) hooks.afterEach(() => { - showPostAssignmentGradesTrayStub.restore() - showHideAssignmentGradesTrayStub.restore() + showGrade.restore() + setOrUpdateSubmission.restore() + showPostAssignmentGradesTray.restore() + showHideAssignmentGradesTray.restore() + delete window.jsonData + fakeENV.teardown() SpeedGrader.teardown() }) QUnit.module('Post Grades', () => { test('shows the Post Assignment Grades Tray', () => { - getPostGradesMenuItem().click() - strictEqual(showPostAssignmentGradesTrayStub.callCount, 1) + postGradesMenuItem().click() + strictEqual(showPostAssignmentGradesTray.callCount, 1) }) test('passes the submissions to showPostAssignmentGradesTray', () => { - getPostGradesMenuItem().click() - deepEqual(showPostAssignmentGradesTrayStub.firstCall.args[0].submissions, window.jsonData.submissions) + postGradesMenuItem().click() + deepEqual(showPostAssignmentGradesTray.firstCall.args[0].submissions, window.jsonData.submissions) }) }) QUnit.module('Hide Grades', () => { test('shows the Hide Assignment Grades Tray', () => { - getHideGradesMenuItem().click() - strictEqual(showHideAssignmentGradesTrayStub.callCount, 1) + hideGradesMenuItem().click() + strictEqual(showHideAssignmentGradesTray.callCount, 1) }) }) }) @@ -1900,6 +1910,7 @@ test('shows an error when the gateway times out', function() { const message = 'Something went wrong. Please try refreshing the page. If the problem persists, there may be too many records on "Assignment Title" to load SpeedGrader.' strictEqual($('#speed_grader_timeout_alert').text(), message) + SpeedGrader.teardown() }) QUnit.module('SpeedGrader - clicking save rubric button', function(hooks) { @@ -1965,10 +1976,11 @@ QUnit.module('SpeedGrader - clicking save rubric button', function(hooks) { }) hooks.afterEach(function() { + delete window.jsonData + SpeedGrader.teardown() teardownFixtures() fakeENV.teardown() disableWhileLoadingStub.restore() - SpeedGrader.teardown() $.ajaxJSON.restore() $.fn.ready.restore() }) @@ -2068,12 +2080,12 @@ QUnit.module('SpeedGrader - clicking save rubric button for an anonymous assignm hooks.afterEach(() => { window.jsonData = originalWindowJsonData SpeedGrader.EG.currentStudent = originalSpeedGraderEGCurrentStudent + SpeedGrader.teardown() teardownFixtures() - rubricAssessmentDataStub.restore() fakeENV.teardown() + rubricAssessmentDataStub.restore() disableWhileLoadingStub.restore() $.ajaxJSON.restore() - SpeedGrader.teardown() }) test('sends the anonymous submission ID in rubric_assessment[anonymous_id] if the assignment is anonymous', () => { @@ -2138,6 +2150,7 @@ test('does not show an error when the gateway times out', function() { SpeedGrader.setup() strictEqual($('#speed_grader_timeout_alert').text(), '') domReadyStub.restore() + SpeedGrader.teardown() }) QUnit.module('SpeedGrader', function(suiteHooks) { @@ -2332,15 +2345,13 @@ QUnit.module('SpeedGrader', function(suiteHooks) { }) hooks.afterEach(() => { - teardownFixtures() delete SpeedGrader.EG.currentStudent window.jsonData = jsonData SpeedGrader.teardown() - document.querySelector('.ui-selectmenu-menu').remove() + teardownFixtures() }) - - test('mounts the progressIcon when attachment uplod_status is pending', function() { + test('mounts the progressIcon when attachment upload_status is pending', function() { const attachment = {content_type: 'application/rtf', upload_status: 'pending'} SpeedGrader.EG.renderAttachment(attachment) @@ -2403,8 +2414,55 @@ QUnit.module('SpeedGrader', function(suiteHooks) { }) }) - QUnit.module('#setup', function(hooks) { - hooks.beforeEach(function() { + QUnit.module('#setup', hooks => { + let assignment + let student + let enrollment + let submissionComment + let submission + let windowJsonData + + hooks.beforeEach(() => { + assignment = { + anonymous_grading: false, + title: 'An Assigment' + } + student = { + id: '1', + submission_history: [] + } + enrollment = {user_id: student.id, course_section_id: '1'} + submissionComment = { + created_at: new Date().toISOString(), + publishable: false, + comment: 'a comment', + author_id: 1, + author_name: 'an author' + } + submission = { + id: '3', + user_id: '1', + grade_matches_current_submission: true, + workflow_state: 'active', + submitted_at: new Date().toISOString(), + grade: 'A', + assignment_id: '456', + submission_history: [], + submission_comments: [submissionComment] + } + windowJsonData = { + ...assignment, + context_id: '123', + context: { + students: [student], + enrollments: [enrollment], + active_course_sections: [], + rep_for_student: {} + }, + submissions: [submission], + gradingPeriods: [] + } + fakeENV.setup({ ...window.ENV, assignment_id: '17', @@ -2427,6 +2485,57 @@ QUnit.module('SpeedGrader', function(suiteHooks) { $('.ui-dialog').remove() }) + QUnit.module('PostPolicy setup', ({beforeEach, afterEach}) => { + let setOrUpdateSubmission + let showGrade + let show + let render + + beforeEach(() => { + fakeENV.setup({ + ...ENV, + grading_role: undefined, + post_policies_enabled: true, + RUBRIC_ASSESSMENT: {} + }) + setOrUpdateSubmission = sinon.spy(SpeedGrader.EG, 'setOrUpdateSubmission') + SpeedGrader.setup() + window.jsonData = windowJsonData + SpeedGrader.EG.jsonReady() + setupCurrentStudent() + show = sinon.spy(SpeedGrader.EG.postPolicies._postAssignmentGradesTray, 'show') + const {jsonData: {submissionsMap, submissions}} = window + SpeedGrader.EG.postPolicies.showPostAssignmentGradesTray({submissionsMap, submissions}) + const {firstCall: {args: [{onPosted}]}} = show + showGrade = sinon.spy(SpeedGrader.EG, 'showGrade') + render = sinon.spy(ReactDOM, 'render') + onPosted({postedAt: new Date().toISOString(), userIds: [Object.keys(submissionsMap)]}) + }) + + afterEach(() => { + render.restore() + showGrade.restore() + show.restore() + setOrUpdateSubmission.restore() + fakeENV.teardown() + }) + + test('updateSubmissions calls setOrUpdateSubmission', () => { + strictEqual(setOrUpdateSubmission.callCount, 1) + }) + + test('afterUpdateSubmissions calls showGrade', () => { + strictEqual(showGrade.callCount, 1) + }) + + test('afterUpdateSubmissions calls renders SpeedGraderPostGradesMenu', () => { + const callCount = render.getCalls().filter(call => + call.args[0].type.name === 'SpeedGraderPostGradesMenu' + ).length + strictEqual(callCount, 1) + }) + }) + test('populates the settings mount point', () => { SpeedGrader.setup() const mountPoint = document.getElementById('speed_grader_settings_mount_point') @@ -2562,10 +2671,10 @@ QUnit.module('SpeedGrader', function(suiteHooks) { }) hooks.afterEach(() => { - teardownFixtures() delete SpeedGrader.EG.currentStudent window.jsonData = jsonData SpeedGrader.teardown() + teardownFixtures() document.querySelector('.ui-selectmenu-menu').remove() }) @@ -2824,8 +2933,8 @@ QUnit.module('SpeedGrader', function(suiteHooks) { }) }) - QUnit.module('Anonymous Enabled', () => { - hooks.beforeEach(() => { + QUnit.module('Anonymous Enabled', anonymousEnabledHooks => { + anonymousEnabledHooks.beforeEach(() => { fakeENV.setup({ ...ENV, assignment_id: '17', @@ -2850,7 +2959,7 @@ QUnit.module('SpeedGrader', function(suiteHooks) { commentElement = $('#comment_fixture') }) - hooks.afterEach(() => { + anonymousEnabledHooks.afterEach(() => { delete SpeedGrader.EG.currentStudent window.jsonData = originalJsonData SpeedGrader.teardown() @@ -3002,11 +3111,11 @@ QUnit.module('SpeedGrader', function(suiteHooks) { }) hooks.afterEach(() => { - teardownFixtures() - fakeENV.teardown() window.jsonData = originalJsonData delete SpeedGrader.EG.currentStudent SpeedGrader.teardown() + teardownFixtures() + fakeENV.teardown() }) test('when students are anonymized no link is shown', () => { @@ -3071,11 +3180,11 @@ QUnit.module('SpeedGrader', function(suiteHooks) { }) hooks.afterEach(() => { - teardownFixtures() - fakeENV.teardown() window.jsonData = originalJsonData delete SpeedGrader.EG.currentStudent SpeedGrader.teardown() + teardownFixtures() + fakeENV.teardown() }) test('renderComment adds the comment text to the submit button for draft comments', () => { @@ -3318,7 +3427,7 @@ QUnit.module('SpeedGrader', function(suiteHooks) { }) }) - QUnit.module('#renderPostGradesMenu', hooks => { + QUnit.module('Post Grades Menu', hooks => { const findRenderCall = () => ReactDOM.render.args.find( argsForCall => argsForCall[1].id === 'speed_grader_post_grades_menu_mount_point' ) @@ -3341,10 +3450,77 @@ QUnit.module('SpeedGrader', function(suiteHooks) { ReactDOM.render.restore() }) - test('renders the "Post Grades" menu', () => { + test('renders the Post Grades" menu once', () => { SpeedGrader.EG.jsonReady() + const renderCalls = ReactDOM.render.args.filter(argsForCall => + argsForCall[1].id === 'speed_grader_post_grades_menu_mount_point' + ) + strictEqual(renderCalls.length, 1) + }) - ok(findRenderCall()) + QUnit.module('Posting Grades', ({beforeEach, afterEach}) => { + let createElementSpy + let showPostAssignmentGradesTrayStub + let onPostGrades + + beforeEach(() => { + createElementSpy = sinon.spy(React, 'createElement') + SpeedGrader.EG.jsonReady() + onPostGrades = createElementSpy.args.find(argsForCall => + argsForCall[0].name === 'SpeedGraderPostGradesMenu' + )[1].onPostGrades + showPostAssignmentGradesTrayStub = sinon.stub(SpeedGrader.EG.postPolicies, 'showPostAssignmentGradesTray') + onPostGrades() + }) + + afterEach(() => { + showPostAssignmentGradesTrayStub.restore() + createElementSpy.restore() + }) + + test('onPostGrades calls showPostAssignmentGradesTray', () => { + strictEqual(showPostAssignmentGradesTrayStub.callCount, 1) + }) + + test('onPostGrades calls showPostAssignmentGradesTray with submissionsMap', () => { + const {firstCall: {args: [{submissionsMap}]}} = showPostAssignmentGradesTrayStub + deepEqual(submissionsMap, window.jsonData.submissionsMap) + }) + + test('onPostGrades calls showPostAssignmentGradesTray with submissions', () => { + const {firstCall: {args: [{submissions}]}} = showPostAssignmentGradesTrayStub + deepEqual(submissions, window.jsonData.studentsWithSubmissions.map(student => student.submission)) + }) + }) + + QUnit.module('Hiding Grades', ({beforeEach, afterEach}) => { + let createElementSpy + let showHideAssignmentGradesTrayStub + let onHideGrades + + beforeEach(() => { + createElementSpy = sinon.spy(React, 'createElement') + SpeedGrader.EG.jsonReady() + onHideGrades = createElementSpy.args.find(argsForCall => + argsForCall[0].name === 'SpeedGraderPostGradesMenu' + )[1].onHideGrades + showHideAssignmentGradesTrayStub = sinon.stub(SpeedGrader.EG.postPolicies, 'showHideAssignmentGradesTray') + onHideGrades() + }) + + afterEach(() => { + showHideAssignmentGradesTrayStub.restore() + createElementSpy.restore() + }) + + test('onHideGrades calls showHideAssignmentGradesTray', () => { + strictEqual(showHideAssignmentGradesTrayStub.callCount, 1) + }) + + test('onHideGrades calls showHideAssignmentGradesTray with submissionsMap', () => { + const {firstCall: {args: [{submissionsMap}]}} = showHideAssignmentGradesTrayStub + deepEqual(submissionsMap, window.jsonData.submissionsMap) + }) }) test('passes the allowHidingGrades prop as true if any submissions are posted', () => { @@ -3420,10 +3596,10 @@ QUnit.module('SpeedGrader', function(suiteHooks) { }) hooks.afterEach(function() { - teardownFixtures() + window.jsonData = originalJsonData SpeedGrader.teardown() SpeedGrader.EG.goToStudent.restore() - window.jsonData = originalJsonData + teardownFixtures() }) test('goToStudent is called with next student anonymous_id', () => { @@ -3461,9 +3637,9 @@ QUnit.module('SpeedGrader', function(suiteHooks) { hooks.afterEach(function() { SpeedGrader.EG.goToStudent.restore() - teardownFixtures() - SpeedGrader.teardown() window.jsonData = originalJsonData + SpeedGrader.teardown() + teardownFixtures() }) test('goToStudent is called with student anonymous_id', () => { @@ -3567,11 +3743,11 @@ QUnit.module('SpeedGrader', function(suiteHooks) { }) hooks.afterEach(() => { - teardownFixtures() SpeedGrader.EG.updateHistoryForCurrentStudent.restore() window.jsonData = originalJsonData delete SpeedGrader.EG.currentStudent SpeedGrader.teardown() + teardownFixtures() }) test('pushes the current student onto the browser history if "push" is specified as the behavior', () => { @@ -3632,10 +3808,10 @@ QUnit.module('SpeedGrader', function(suiteHooks) { }) hooks.afterEach(() => { - teardownFixtures() window.jsonData = originalJsonData delete SpeedGrader.EG.currentStudent SpeedGrader.teardown() + teardownFixtures() }) QUnit.module('when a behavior of "push" is specified', pushHooks => { @@ -3929,10 +4105,10 @@ QUnit.module('SpeedGrader', function(suiteHooks) { }) hooks.afterEach(() => { - teardownFixtures() window.jsonData = originalJsonData delete SpeedGrader.EG.currentStudent SpeedGrader.teardown() + teardownFixtures() }) test('assessment_user_id is set via anonymous id', () => { @@ -3981,10 +4157,10 @@ QUnit.module('SpeedGrader', function(suiteHooks) { }) hooks.afterEach(() => { - teardownFixtures() window.jsonData = originalJsonData delete SpeedGrader.EG.currentStudent SpeedGrader.teardown() + teardownFixtures() }) test('attachmentElement has submitter_id set to anonymous id', () => { @@ -4013,10 +4189,10 @@ QUnit.module('SpeedGrader', function(suiteHooks) { }) hooks.afterEach(() => { - teardownFixtures() window.jsonData = originalJsonData delete SpeedGrader.EG.currentStudent SpeedGrader.teardown() + teardownFixtures() }) test('calls isStudentConcluded with student looked up by anonymous id', () => { @@ -4055,10 +4231,10 @@ QUnit.module('SpeedGrader', function(suiteHooks) { }) hooks.afterEach(() => { - teardownFixtures() window.jsonData = originalJsonData delete SpeedGrader.EG.currentStudent SpeedGrader.teardown() + teardownFixtures() }) test('calls ajaxJSON with anonymous submission url with anonymous id', () => { @@ -4137,10 +4313,10 @@ QUnit.module('SpeedGrader', function(suiteHooks) { }) hooks.afterEach(() => { - teardownFixtures() window.jsonData = originalJsonData delete SpeedGrader.EG.currentStudent SpeedGrader.teardown() + teardownFixtures() }) test('calls isStudentConcluded with student looked up by anonymous id', () => { @@ -4259,10 +4435,10 @@ QUnit.module('SpeedGrader', function(suiteHooks) { }) hooks.afterEach(() => { - teardownFixtures() window.jsonData = originalJsonData delete SpeedGrader.EG.currentStudent SpeedGrader.teardown() + teardownFixtures() document.querySelector('.ui-selectmenu-menu').remove() }) @@ -4303,10 +4479,10 @@ QUnit.module('SpeedGrader', function(suiteHooks) { }) hooks.afterEach(() => { - teardownFixtures() window.jsonData = originalJsonData delete SpeedGrader.EG.currentStudent SpeedGrader.teardown() + teardownFixtures() }) test("the iframe src points to a user's submission by anonymous_id", () => { @@ -4350,10 +4526,10 @@ QUnit.module('SpeedGrader', function(suiteHooks) { }) hooks.afterEach(() => { - teardownFixtures() window.jsonData = originalJsonData delete SpeedGrader.EG.currentStudent SpeedGrader.teardown() + teardownFixtures() }) test('attachment src points to the submission download url', () => { @@ -4934,8 +5110,8 @@ QUnit.module('SpeedGrader', function(suiteHooks) { }) hooks.afterEach(() => { - teardownFixtures() SpeedGrader.teardown() + teardownFixtures() }) QUnit.module('when a submission is unsubmitted', () => { @@ -5098,10 +5274,10 @@ QUnit.module('SpeedGrader', function(suiteHooks) { test('does not attempt to select a representative', () => { queryParamsStub.returns({anonymous_id: 'fffff'}) - window.jsonData.context.rep_for_student['fffff'] = 'rrrrr' + window.jsonData.context.rep_for_student.fffff = 'rrrrr' SpeedGrader.EG.setInitiallyLoadedStudent() strictEqual(SpeedGrader.EG.currentStudent.anonymous_id, 'fffff') - delete window.jsonData.context.rep_for_student['fffff'] + delete window.jsonData.context.rep_for_student.fffff }) test('defaults to the first ungraded student if no student is specified', () => { @@ -5222,8 +5398,8 @@ QUnit.module('SpeedGrader', function(suiteHooks) { element.remove() }) - teardownFixtures() SpeedGrader.teardown() + teardownFixtures() window.jsonData = originalWindowJSONData }) diff --git a/spec/selenium/grades/speedgrader/speedgrader_post_policy_spec.rb b/spec/selenium/grades/speedgrader/speedgrader_post_policy_spec.rb index 5a4cd3ce3b1..9e6321c2376 100644 --- a/spec/selenium/grades/speedgrader/speedgrader_post_policy_spec.rb +++ b/spec/selenium/grades/speedgrader/speedgrader_post_policy_spec.rb @@ -22,8 +22,9 @@ require_relative '../pages/student_grades_page' describe 'SpeedGrader Post Policy' do include_context "in-process server selenium tests" + before(:all) { skip('GRADE-2192') } + before :once do - skip('Unskip in GRADE-1943') # course course_with_teacher( course_name: "Post Policy Course", @@ -70,13 +71,12 @@ describe 'SpeedGrader Post Policy' do context 'when post everyone' do before :each do - skip('Unskip in GRADE-1943') manually_post_grades('Everyone') end it 'post grades option disabled' do - skip('Unskip in GRADE-1943') # TODO: expect post option to be disabled for Speedgrader.post_grades + expect(Speedgrader.post_grades).to be_disabled end it 'students see grade', priority: '1', test_id: 3757534 do @@ -88,7 +88,6 @@ describe 'SpeedGrader Post Policy' do context 'when post everyone for section' do before :each do - skip('Unskip in GRADE-1943') manually_post_grades('Everyone', @section2) end @@ -99,7 +98,6 @@ describe 'SpeedGrader Post Policy' do end it 'does not post for other section', priority: '1', test_id: 3757535 do - skip('Unskip in GRADE-1943') expect(Speedgrader.hidden_pill).to be_displayed # TODO: verify students not in section have eyeball icon @@ -110,7 +108,6 @@ describe 'SpeedGrader Post Policy' do end it 'Post tray shows unposted count', priority: '1', test_id: 3757535 do - skip('Unskip in GRADE-1943') expect(Speedgrader.PostGradesTray.unposted_count).to eq '2' # TODO: expect unposted indicator to be displayed and show count 2 end @@ -118,7 +115,6 @@ describe 'SpeedGrader Post Policy' do context 'when hide posted grades for everyone' do before :each do - skip('Unskip in GRADE-1943') manually_post_grades('Everyone') wait_for_ajaximations Speedgrader.visit(@course.id, @assignment.id) @@ -127,7 +123,6 @@ describe 'SpeedGrader Post Policy' do end it 'header has HIDDEN icon', priority: '1', test_id: 3757537 do - skip('Unskip in GRADE-1943') # TODO: expect header to have HIDDEN icon Speedgrader.grades_not_posted_icon end @@ -139,14 +134,12 @@ describe 'SpeedGrader Post Policy' do end it 'hidden pill displayed in side panel', priority: '1', test_id: 3757537 do - skip('Unskip in GRADE-1943') expect(Speedgrader.hidden_pill).to be_displayed end end context 'when hide posted grades for section' do before :each do - skip('Unskip in GRADE-1943') manually_post_grades('Everyone') wait_for_ajaximations manually_hide_grades(@section2) @@ -154,7 +147,6 @@ describe 'SpeedGrader Post Policy' do end it 'students in section have hidden grades', priority: '1', test_id: 3756683 do - skip('Unskip in GRADE-1943') @students1.each do |student| verify_student_grade_comments_displayed(student, '') # TODO: expect hidden icon to be displayed @@ -178,7 +170,6 @@ describe 'SpeedGrader Post Policy' do end before :each do - skip('Unskip in GRADE-1943') manually_post_grades('Graded') end @@ -189,7 +180,6 @@ describe 'SpeedGrader Post Policy' do end it 'does not post for ungraded', priority: '1', test_id: 3756680 do - skip('Unskip in GRADE-1943') # TODO: verify ungraded students still have eyeball icon verify_student_grade_comments_displayed(@students[3], '') # TODO: expect icon to be displayed @@ -205,7 +195,6 @@ describe 'SpeedGrader Post Policy' do end before :each do - skip('Unskip in GRADE-1943') manually_post_grades('Graded', @section2) end @@ -214,7 +203,6 @@ describe 'SpeedGrader Post Policy' do end it 'does not post ungraded for section', priority: '1', test_id: 3757539 do - skip('Unskip in GRADE-1943') # TODO: verify students in section without grades have eyeball icon verify_student_grade_comments_displayed(@students2.second, '') # TODO: expect eyeball icon to be displayed