reuse quizzes launch session in speedgrader
We used to support reusing the same launch session when the teacher switches students in SpeedGrader, but this functionality was accidentally removed. This PS re-enables this functionality. closes QUIZ-10207 closes EVAL-2673 flag=none Test plan: 1. Create a Quizzes.Next quiz assigned to at least three students 2. As two of the students, take the quiz. As the third student, don't take the quiz. 3. As the teacher, go to SpeedGrader for the quiz. Notice that Quizzes.Next launches when you navigate to the first student that has taken the quiz. Then navigate to the other student that has taken the quiz and verify that we're not launching a new Quizzes.Next session (you can verify this via the Network tab) — instead, the content in the existing session is updated to show the new student's information. 4. When navigating from a student that has taken the quiz to a student that has not taken the quiz, verify the "No submission" text is shown in SpeedGrader. 5. If a Quizzes.Next session has already been launched (from previously viewing a student that has taken the quiz), when navigating from a student that has not taken the quiz to a student that has taken the quiz, verify we're not launching a new Quizzes.Next session. Change-Id: If0346e205c14d65815fe1fc872534f185a3b73d7 Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/303326 Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com> QA-Review: Spencer Olson <solson@instructure.com> Product-Review: Marissa Pio Roda <marissa.pioroda@instructure.com> Reviewed-by: Ricardo Oliveira <ricardo.oliveira@instructure.com> Reviewed-by: Spencer Olson <solson@instructure.com>
This commit is contained in:
parent
77338f8fdc
commit
9db7a4dcab
|
@ -895,6 +895,7 @@ class GradebooksController < ApplicationController
|
|||
@outer_frame = true
|
||||
log_asset_access(["speed_grader", @context], "grades", "other")
|
||||
env = {
|
||||
SINGLE_NQ_SESSION_ENABLED: Account.site_admin.feature_enabled?(:single_new_quiz_session_in_speedgrader),
|
||||
EMOJIS_ENABLED: @context.feature_enabled?(:submission_comment_emojis),
|
||||
EMOJI_DENY_LIST: @context.root_account.settings[:emoji_deny_list],
|
||||
MANAGE_GRADES: @context.grants_right?(@current_user, session, :manage_grades),
|
||||
|
|
|
@ -132,3 +132,15 @@ originality_reports_for_a2:
|
|||
state: on # enable for local development
|
||||
test:
|
||||
state: on # enable for the deployed 'test' environment
|
||||
single_new_quiz_session_in_speedgrader:
|
||||
state: hidden
|
||||
applies_to: SiteAdmin
|
||||
display_name: Single Quizzes.Next Session in SpeedGrader
|
||||
description: If set, Quizzes.Next will operate using a single launch session in SpeedGrader
|
||||
environments:
|
||||
ci:
|
||||
state: allowed_on # enable for automated testing builds and local testing
|
||||
development:
|
||||
state: allowed_on # enable for local development
|
||||
test:
|
||||
state: allowed_on # enable for the deployed 'test' environment
|
||||
|
|
|
@ -93,10 +93,12 @@ QUnit.module('SpeedGrader', rootHooks => {
|
|||
sandbox.stub(SpeedGraderHelpers, 'reloadPage')
|
||||
|
||||
setupFixtures()
|
||||
fakeENV.setup({SINGLE_NQ_SESSION_ENABLED: true})
|
||||
})
|
||||
|
||||
rootHooks.afterEach(() => {
|
||||
teardownFixtures()
|
||||
fakeENV.teardown()
|
||||
})
|
||||
|
||||
QUnit.module('SpeedGrader#showDiscussion', {
|
||||
|
@ -3272,6 +3274,7 @@ QUnit.module('SpeedGrader', rootHooks => {
|
|||
help_url: 'example.com/foo',
|
||||
settings_url: 'example.com/settings',
|
||||
show_help_menu_item: false,
|
||||
SINGLE_NQ_SESSION_ENABLED: true,
|
||||
})
|
||||
sinon.stub($, 'getJSON')
|
||||
sinon.stub($, 'ajaxJSON')
|
||||
|
@ -6417,10 +6420,21 @@ QUnit.module('SpeedGrader', rootHooks => {
|
|||
})
|
||||
})
|
||||
|
||||
QUnit.module('#loadSubmissionPreview', hooks => {
|
||||
QUnit.module('#loadSubmissionPreview', contextHooks => {
|
||||
const EG = SpeedGrader.EG
|
||||
async function postMessage(message, targetOrigin) {
|
||||
await new Promise(resolve => {
|
||||
const listen = () => {
|
||||
window.removeEventListener('message', listen, false)
|
||||
resolve()
|
||||
}
|
||||
|
||||
hooks.beforeEach(() => {
|
||||
window.addEventListener('message', listen, false)
|
||||
window.postMessage(message, targetOrigin)
|
||||
})
|
||||
}
|
||||
|
||||
contextHooks.beforeEach(() => {
|
||||
setupFixtures(`
|
||||
<div id='this_student_does_not_have_a_submission'></div>
|
||||
<div id='iframe_holder'>
|
||||
|
@ -6433,11 +6447,53 @@ QUnit.module('SpeedGrader', rootHooks => {
|
|||
}
|
||||
})
|
||||
|
||||
hooks.afterEach(() => {
|
||||
contextHooks.afterEach(() => {
|
||||
SpeedGrader.teardown()
|
||||
})
|
||||
|
||||
QUnit.module('when a submission is unsubmitted', () => {
|
||||
QUnit.module('when the student has submitted for a quizzesNext quiz', hooks => {
|
||||
let submission
|
||||
hooks.beforeEach(() => {
|
||||
EG.currentStudent.submission = {
|
||||
submission_type: 'basic_lti_launch',
|
||||
workflow_state: 'pending_review',
|
||||
}
|
||||
submission = {submission_type: 'basic_lti_launch'}
|
||||
})
|
||||
|
||||
test('launches a quizzesNext session if it has not yet been launched', async () => {
|
||||
await postMessage({subject: 'quizzesNext.register'}, '*')
|
||||
const ltiLaunchStub = sandbox.stub(EG, 'renderLtiLaunch')
|
||||
EG.loadSubmissionPreview(null, submission)
|
||||
ok(ltiLaunchStub.calledOnce)
|
||||
})
|
||||
|
||||
QUnit.module('when a quizzesNext session has already been launched', () => {
|
||||
test('does not launch a new quizzesNext session when in "single session" mode', async () => {
|
||||
await postMessage({subject: 'quizzesNext.register'}, '*')
|
||||
EG.loadSubmissionPreview(null, submission)
|
||||
const ltiLaunchStub = sandbox.stub(EG, 'renderLtiLaunch')
|
||||
EG.loadSubmissionPreview(null, submission)
|
||||
ok(ltiLaunchStub.notCalled)
|
||||
})
|
||||
|
||||
test('launches a new quizzesNext session when in "session per student" mode', async () => {
|
||||
await postMessage(
|
||||
{
|
||||
subject: 'quizzesNext.register',
|
||||
payload: {singleLtiLaunch: false},
|
||||
},
|
||||
'*'
|
||||
)
|
||||
EG.loadSubmissionPreview(null, submission)
|
||||
const ltiLaunchStub = sandbox.stub(EG, 'renderLtiLaunch')
|
||||
EG.loadSubmissionPreview(null, submission)
|
||||
ok(ltiLaunchStub.calledOnce)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
QUnit.module('when the student has not submitted', () => {
|
||||
test('shows the "this student does not have a submission" div', () => {
|
||||
const $noSubmission = $('#this_student_does_not_have_a_submission')
|
||||
$noSubmission.hide()
|
||||
|
@ -6451,6 +6507,32 @@ QUnit.module('SpeedGrader', rootHooks => {
|
|||
|
||||
strictEqual($('#iframe_holder').html(), '')
|
||||
})
|
||||
|
||||
QUnit.module('when quizzesNext is loaded', () => {
|
||||
test('does not empty the iframe when operating in "single session" mode', async () => {
|
||||
await postMessage(
|
||||
{subject: 'quizzesNext.register', payload: {singleLtiLaunch: true}},
|
||||
'*'
|
||||
)
|
||||
EG.loadSubmissionPreview()
|
||||
ok($('#iframe_holder').text().includes('I am an iframe holder!'))
|
||||
})
|
||||
|
||||
test('operates in "single session" mode by default for quizzesNext', async () => {
|
||||
await postMessage({subject: 'quizzesNext.register'}, '*')
|
||||
EG.loadSubmissionPreview()
|
||||
ok($('#iframe_holder').text().includes('I am an iframe holder!'))
|
||||
})
|
||||
|
||||
test('empties the iframe when operating in "session per student" mode', async () => {
|
||||
await postMessage(
|
||||
{subject: 'quizzesNext.register', payload: {singleLtiLaunch: false}},
|
||||
'*'
|
||||
)
|
||||
EG.loadSubmissionPreview()
|
||||
notOk($('#iframe_holder').text().includes('I am an iframe holder!'))
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -227,6 +227,8 @@ let gradeeLabel
|
|||
let sessionTimer
|
||||
let isAdmin
|
||||
let showSubmissionOverride
|
||||
let externalToolLaunchOptions = {singleLtiLaunch: false}
|
||||
let externalToolLoaded = false
|
||||
let provisionalGraderDisplayNames
|
||||
let EG
|
||||
const customProvisionalGraderLabel = I18n.t('Custom')
|
||||
|
@ -257,6 +259,8 @@ function setupBeforeLeavingSpeedgrader() {
|
|||
}
|
||||
|
||||
function teardownBeforeLeavingSpeedgrader() {
|
||||
externalToolLaunchOptions = {singleLtiLaunch: false}
|
||||
externalToolLoaded = false
|
||||
window.removeEventListener('beforeunload', EG.beforeLeavingSpeedgrader)
|
||||
}
|
||||
|
||||
|
@ -2726,7 +2730,9 @@ EG = {
|
|||
this.currentStudent.submission.workflow_state === 'unsubmitted'
|
||||
) {
|
||||
$this_student_does_not_have_a_submission.show()
|
||||
this.emptyIframeHolder()
|
||||
if (!ENV.SINGLE_NQ_SESSION_ENABLED || !externalToolLaunchOptions.singleLtiLaunch) {
|
||||
this.emptyIframeHolder()
|
||||
}
|
||||
} else if (
|
||||
this.currentStudent.submission &&
|
||||
this.currentStudent.submission.submitted_at &&
|
||||
|
@ -2737,7 +2743,12 @@ EG = {
|
|||
} else if (attachment) {
|
||||
this.renderAttachment(attachment)
|
||||
} else if (submission && submission.submission_type === 'basic_lti_launch') {
|
||||
this.renderLtiLaunch($iframe_holder, ENV.lti_retrieve_url, submission)
|
||||
if (!ENV.SINGLE_NQ_SESSION_ENABLED || !externalToolLoaded || !externalToolLaunchOptions.singleLtiLaunch) {
|
||||
this.renderLtiLaunch($iframe_holder, ENV.lti_retrieve_url, submission)
|
||||
externalToolLoaded = true
|
||||
} else {
|
||||
$iframe_holder.show()
|
||||
}
|
||||
} else {
|
||||
this.renderSubmissionPreview()
|
||||
}
|
||||
|
@ -4257,8 +4268,11 @@ export default {
|
|||
EG.setUpAssessmentAuditTray()
|
||||
}
|
||||
|
||||
function registerQuizzesNext(overriddenShowSubmission) {
|
||||
function registerQuizzesNext(overriddenShowSubmission, launchOptions) {
|
||||
showSubmissionOverride = overriddenShowSubmission
|
||||
if (launchOptions) {
|
||||
externalToolLaunchOptions = launchOptions
|
||||
}
|
||||
}
|
||||
quizzesNextSpeedGrading(EG, $iframe_holder, registerQuizzesNext, refreshGrades, window)
|
||||
|
||||
|
|
|
@ -90,7 +90,7 @@ function quizzesNextSpeedGrading(
|
|||
switch (message.subject) {
|
||||
case 'quizzesNext.register':
|
||||
EG.setGradeReadOnly(true)
|
||||
return registerCb(postChangeSubmissionMessage)
|
||||
return registerCb(postChangeSubmissionMessage, message.payload || {singleLtiLaunch: true})
|
||||
case 'quizzesNext.submissionUpdate':
|
||||
return refreshGradesCb(quizzesNextChange, retryRefreshGrades, 1000)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue