Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion openassessment/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
Initialization Information for Open Assessment Module
"""

__version__ = '6.16.4'
__version__ = '6.16.5'
24 changes: 12 additions & 12 deletions openassessment/xblock/static/dist/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,18 @@
"openassessment-editor-textarea.js.map": "/openassessment-editor-textarea.cbb31e4372be87d437fb.js.map",
"openassessment-editor-tinymce.js": "/openassessment-editor-tinymce.2a1e66e98a2a1132f633.js",
"openassessment-editor-tinymce.js.map": "/openassessment-editor-tinymce.2a1e66e98a2a1132f633.js.map",
"openassessment-lms.css": "/openassessment-lms.42bdf01af5d117224bc9.css",
"openassessment-lms.js": "/openassessment-lms.42bdf01af5d117224bc9.js",
"openassessment-lms.css.map": "/openassessment-lms.42bdf01af5d117224bc9.css.map",
"openassessment-lms.js.map": "/openassessment-lms.42bdf01af5d117224bc9.js.map",
"openassessment-ltr.css": "/openassessment-ltr.0c6318822d5661f0f9ea.css",
"openassessment-ltr.js": "/openassessment-ltr.0c6318822d5661f0f9ea.js",
"openassessment-ltr.css.map": "/openassessment-ltr.0c6318822d5661f0f9ea.css.map",
"openassessment-ltr.js.map": "/openassessment-ltr.0c6318822d5661f0f9ea.js.map",
"openassessment-rtl.css": "/openassessment-rtl.c938e1614108d8feb891.css",
"openassessment-rtl.js": "/openassessment-rtl.c938e1614108d8feb891.js",
"openassessment-rtl.css.map": "/openassessment-rtl.c938e1614108d8feb891.css.map",
"openassessment-rtl.js.map": "/openassessment-rtl.c938e1614108d8feb891.js.map",
"openassessment-lms.css": "/openassessment-lms.1d1152611b06737ce755.css",
"openassessment-lms.js": "/openassessment-lms.1d1152611b06737ce755.js",
"openassessment-lms.css.map": "/openassessment-lms.1d1152611b06737ce755.css.map",
"openassessment-lms.js.map": "/openassessment-lms.1d1152611b06737ce755.js.map",
"openassessment-ltr.css": "/openassessment-ltr.97990de9668aaa4cd1d5.css",
"openassessment-ltr.js": "/openassessment-ltr.97990de9668aaa4cd1d5.js",
"openassessment-ltr.css.map": "/openassessment-ltr.97990de9668aaa4cd1d5.css.map",
"openassessment-ltr.js.map": "/openassessment-ltr.97990de9668aaa4cd1d5.js.map",
"openassessment-rtl.css": "/openassessment-rtl.19576ab9f81f644cb7fd.css",
"openassessment-rtl.js": "/openassessment-rtl.19576ab9f81f644cb7fd.js",
"openassessment-rtl.css.map": "/openassessment-rtl.19576ab9f81f644cb7fd.css.map",
"openassessment-rtl.js.map": "/openassessment-rtl.19576ab9f81f644cb7fd.js.map",
"openassessment-studio.js": "/openassessment-studio.b58bb4e55f63dadac6de.js",
"openassessment-studio.js.map": "/openassessment-studio.b58bb4e55f63dadac6de.js.map",
"fallback-default.png": "/4620b30a966533ace489dcc7afb151b9.png",
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Large diffs are not rendered by default.

This file was deleted.

Large diffs are not rendered by default.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Large diffs are not rendered by default.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

159 changes: 144 additions & 15 deletions openassessment/xblock/static/js/spec/lms/oa_response.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
/* eslint-disable */
import { waitFor } from '@testing-library/react';

import BaseView from 'lms/oa_base';
import ResponseView from 'lms/oa_response';

Expand Down Expand Up @@ -152,13 +154,29 @@ describe("OpenAssessment.ResponseView", function() {
stubConfirm = didConfirm;
};

/**
Helper function to assert response buttons are all consistent and in an expected state
(disabled or enabled).
**/
var expectResponseFieldsEnabled = function(view, expectedEnabled) {
expect(view.baseView.buttonEnabled('.step--response__submit')).toBe(expectedEnabled);
expect(view.baseView.buttonEnabled('.step--response .file__upload')).toBe(expectedEnabled);
expect(view.baseView.buttonEnabled('.step--response .submission__answer__upload')).toBe(expectedEnabled);

// only check enabled status of delete image buttons if there are any present
if ($('.step--response .delete__uploaded__file').length > 0) {
expect(view.baseView.buttonEnabled('.step--response .delete__uploaded__file')).toBe(expectedEnabled);
}
};

// Store window URL object for replacing after tests;
const windowURL = window.URL;
const mockURL = {
createObjectURL: () => 'url',
};

beforeEach(function(done) {
jasmine.DEFAULT_TIMEOUT_INTERVAL = 2147483646;
// Load the DOM fixture
loadFixtures('oa_response.html');

Expand Down Expand Up @@ -239,21 +257,21 @@ describe("OpenAssessment.ResponseView", function() {
// response is blank
view.response(['', '']);
view.handleResponseChanged();
expect(view.submitEnabled()).toBe(true);
expectResponseFieldsEnabled(view, true);
expect(view.isValidForSubmit()).toBe(false);
expect(view.saveStatus()).toContain('This response has not been saved');

// response is whitespace
view.response([' \n \n ', ' ']);
view.handleResponseChanged();
expect(view.submitEnabled()).toBe(true);
expectResponseFieldsEnabled(view, true);
expect(view.isValidForSubmit()).toBe(false);
expect(view.saveStatus()).toContain('This response has not been saved');

// response is not blank
view.response(['Test response 1', ' ']);
view.handleResponseChanged();
expect(view.submitEnabled()).toBe(true);
expectResponseFieldsEnabled(view, true);
expect(view.isValidForSubmit()).toBe(true);
expect(view.saveStatus()).toContain('Saving draft');
});
Expand All @@ -262,14 +280,14 @@ describe("OpenAssessment.ResponseView", function() {
view.textResponse = 'optional';
view.fileUploadResponse = 'required';

expect(view.submitEnabled()).toBe(true);
expectResponseFieldsEnabled(view, true);
expect(view.isValidForSubmit()).toBe(false);

var files = [{type: 'application/pdf', size: 1024, name: 'application.pdf', data: ''}];
view.prepareUpload(files, 'pdf-and-image', ['test description']);
view.uploadFiles();

expect(view.submitEnabled()).toBe(true);
expectResponseFieldsEnabled(view, true);
expect(view.isValidForSubmit()).toBe(true);
});

Expand Down Expand Up @@ -336,7 +354,7 @@ describe("OpenAssessment.ResponseView", function() {
expect(server.submit).not.toHaveBeenCalled();
});

it("disables the submit button on submission", function() {
it("disables the response buttons on submission", function() {
// Prevent the server's response from resolving,
// so we can see what happens before view gets re-rendered.
spyOn(server, 'submit').and.callFake(function() {
Expand All @@ -345,10 +363,10 @@ describe("OpenAssessment.ResponseView", function() {

view.response(['Test response 1', 'Test response 2']);
view.handleSubmitClicked();
expect(view.submitEnabled()).toBe(false);
expectResponseFieldsEnabled(view, false);
});

it("re-enables the submit button on submission error", function() {
it("re-enables the response buttons on submission error", function() {
// Simulate a server error
spyOn(server, 'submit').and.callFake(function() {
return $.Deferred(function(defer) {
Expand All @@ -359,11 +377,11 @@ describe("OpenAssessment.ResponseView", function() {
view.response(['Test response 1', 'Test response 2']);
view.handleSubmitClicked();

// Expect the submit button to have been re-enabled
expect(view.submitEnabled()).toBe(true);
// Expect the response buttons to have been re-enabled
expectResponseFieldsEnabled(view, true);
});

it("re-enables the submit button after cancelling", function() {
it("re-enables the response buttons after cancelling", function() {
// Simulate the user cancelling the submission
setStubConfirm(false);
spyOn(server, 'submit').and.callThrough();
Expand All @@ -372,8 +390,8 @@ describe("OpenAssessment.ResponseView", function() {
view.response(['Test response 1', 'Test response 2']);
view.handleSubmitClicked();

// Expect the submit button to be re-enabled
expect(view.submitEnabled()).toBe(true);
// Expect the response buttons to be re-enabled
expectResponseFieldsEnabled(view, true);
});

it("moves to the next step on duplicate submission error", function() {
Expand Down Expand Up @@ -620,7 +638,7 @@ describe("OpenAssessment.ResponseView", function() {
view.prepareUpload(files, 'image', ['i1', 'i2']);
view.uploadFiles()
expect(view.isValidForSubmit()).toBe(true);
expect(view.submitEnabled()).toBe(true);
expectResponseFieldsEnabled(view, true);
expect(view.files.length).toEqual(2);
view.prepareUpload(files, 'image', ['i1', 'i2']);
view.uploadFiles()
Expand All @@ -641,7 +659,7 @@ describe("OpenAssessment.ResponseView", function() {
view.prepareUpload(files, 'image', ['i1', 'i2','i1', 'i2','i1', 'i2','i1', 'i2','i1', 'i2','i1', 'i2',
'i1', 'i2','i1', 'i2','i1', 'i2','i1', 'i2',]);
expect(view.isValidForSubmit()).toBe(false);
expect(view.submitEnabled()).toBe(true);
expectResponseFieldsEnabled(view, true);
expect(view.getSavedFileCount()).toEqual(20);
var files2 = [];
for(i=0; i<2;i++) {
Expand All @@ -652,6 +670,117 @@ describe("OpenAssessment.ResponseView", function() {
'upload', 'The maximum number files that can be saved is ' + view.data.MAXIMUM_FILE_UPLOAD_COUNT);
});

it("tests that form fields are disabled while file is uploading", async function() {
spyOn(view, 'enableFields').and.callThrough();

view.textResponse = 'optional';
view.fileUploadResponse = 'required';

// Simulate the user accepting confirmation dialogs
setStubConfirm(true);

expect(view.isValidForSubmit()).toBe(false);
expectResponseFieldsEnabled(view, true);

// Upload files
var files = [
{ type: 'image/jpeg', size: 1024, name: 'picture1.jpg', data: '' },
{ type: 'image/jpeg', size: 1024, name: 'picture2.jpg', data: '' },
];
view.prepareUpload(files, 'image', ['i1', 'i2']);
$('.file__description').text('custom description text');
expect(view.enableFields).not.toHaveBeenCalled();
$('.file__upload.action--upload').first().click();
// wait for the files to be uploaded and the fields to be enabled again
await waitFor(() => {
// assert fields were disabled then enabled again
expect(view.enableFields.calls.argsFor(0)).toEqual([false]);
expect(view.enableFields.calls.argsFor(1)).toEqual([true]);
expect(view.enableFields).toHaveBeenCalledTimes(2);
// assert fields are really enabled again
expectResponseFieldsEnabled(view, true);
});

// now that the required files are uploaded, the form should be valid for submitting
expect(view.isValidForSubmit()).toBe(true);
});

it("tests that form fields are disabled while a file is being deleted", async function() {
view.textResponse = 'optional';
view.fileUploadResponse = 'required';

// Simulate the user accepting confirmation dialogs
setStubConfirm(true);

// Upload files
var files = [
{ type: 'image/jpeg', size: 1024, name: 'picture1.jpg', data: '' },
{ type: 'image/jpeg', size: 1024, name: 'picture2.jpg', data: '' },
];
view.prepareUpload(files, 'image', ['i1', 'i2']);
$('.file__description').text('custom description text');
$('.file__upload.action--upload').first().click();

// wait for the files to be uploaded and the delete button present
await waitFor(() => {
expect($('.delete__uploaded__file').length).toBeGreaterThan(0);
});

// Verify the fields are disabled during deletion, and re-enabled after.
// It's tricky here to verify the fields are disabled and enabled at the right time,
// but we can verify they were disabled and then re-enabled at some point.
expectResponseFieldsEnabled(view, true);
spyOn(view, 'enableFields').and.callThrough();
$('.delete__uploaded__file').first().click();
await waitFor(() => {
// assert fields were disabled then enabled again
expect(view.enableFields.calls.argsFor(0)).toEqual([false]);
expect(view.enableFields.calls.argsFor(1)).toEqual([true]);
expect(view.enableFields).toHaveBeenCalledTimes(2);
// assert fields are really enabled again
expectResponseFieldsEnabled(view, true);
});
});

[true, false].forEach((acceptConfirmation) => {
it(`tests that form fields are disabled while a file is being deleted when the confirmation is {acceptConfirmation ? "accepted" : "rejected"}`, async function() {
view.textResponse = 'optional';
view.fileUploadResponse = 'required';

// Simulate the user either accepting or cancelling confirmation dialogs
setStubConfirm(acceptConfirmation);

// Upload files
var files = [
{ type: 'image/jpeg', size: 1024, name: 'picture1.jpg', data: '' },
{ type: 'image/jpeg', size: 1024, name: 'picture2.jpg', data: '' },
];
view.prepareUpload(files, 'image', ['i1', 'i2']);
$('.file__description').text('custom description text');
$('.file__upload.action--upload').first().click();

// wait for the files to be uploaded and the delete button present
await waitFor(() => {
expect($('.delete__uploaded__file').length).toBeGreaterThan(0);
});

// Verify the fields are disabled during deletion, and re-enabled after.
// It's tricky here to verify the fields are disabled and enabled at the right time,
// but we can verify they were disabled and then re-enabled at some point.
expectResponseFieldsEnabled(view, true);
spyOn(view, 'enableFields').and.callThrough();
$('.delete__uploaded__file').first().click();
await waitFor(() => {
// assert fields were disabled then enabled again
expect(view.enableFields.calls.argsFor(0)).toEqual([false]);
expect(view.enableFields.calls.argsFor(1)).toEqual([true]);
expect(view.enableFields).toHaveBeenCalledTimes(2);
// assert fields are really enabled again
expectResponseFieldsEnabled(view, true);
});
});
});

it("tests that file upload works after file delete", function() {
view.textResponse = 'optional';
view.fileUploadResponse = 'required';
Expand Down
Loading