Commit dd94251a by E. Kolpakov Committed by Jesse Shapiro

Submission answer UI

[#SOL-1944] Review notes: responsiveness, ARIA attributes, focus after reset
parent 2afee49e
......@@ -127,6 +127,12 @@ class DragAndDropBlock(XBlock, XBlockWithSettingsMixin, ThemableXBlockMixin):
default={},
)
num_attempts = Integer(
help=_("Number of attempts learner used"),
scope=Scope.user_state,
default=0
)
completed = Boolean(
help=_("Indicates whether a learner has completed the problem at least once"),
scope=Scope.user_state,
......@@ -191,6 +197,7 @@ class DragAndDropBlock(XBlock, XBlockWithSettingsMixin, ThemableXBlockMixin):
return {
"mode": self.mode,
"max_attempts": self.max_attempts,
"zones": self._get_zones(),
# SDK doesn't supply url_name.
"url_name": getattr(self, 'url_name', ''),
......@@ -433,6 +440,7 @@ class DragAndDropBlock(XBlock, XBlockWithSettingsMixin, ThemableXBlockMixin):
return {
'items': item_state,
'finished': is_finished,
'num_attempts': self.num_attempts,
'overall_feedback': self.data['feedback']['finish' if is_finished else 'start'],
}
......
......@@ -326,13 +326,174 @@
outline: 2px solid white;
}
/*** KEYBOARD HELP ***/
/*** edX pattern library components ***/
/* reset stock edx-platform button styles */
.xblock--drag-and-drop button,
.xblock--drag-and-drop button:hover,
.xblock--drag-and-drop button.is-hovered,
.xblock--drag-and-drop button:focus,
.xblock--drag-and-drop button.is-focused,
.xblock--drag-and-drop button:active,
.xblock--drag-and-drop button.is-active {
box-shadow: none;
text-shadow: none;
background-image: none;
}
.xblock--drag-and-drop .btn-default,
.xblock--drag-and-drop .btn-brand {
display: inline-block;
font-weight: normal;
background: #0079bc;
color: #fcfcfc;
-webkit-transition: color 0.125s ease-in-out 0s, border-color 0.125s ease-in-out 0s, background 0.125s ease-in-out 0s, box-shadow 0.125s ease-in-out 0s;
-moz-transition: color 0.125s ease-in-out 0s, border-color 0.125s ease-in-out 0s, background 0.125s ease-in-out 0s, box-shadow 0.125s ease-in-out 0s;
transition: color 0.125s ease-in-out 0s, border-color 0.125s ease-in-out 0s, background 0.125s ease-in-out 0s, box-shadow 0.125s ease-in-out 0s;
border-radius: 3px;
border: 1px solid #0079bc;
padding: 0.625em 1.25em;
font-size: 1em;
}
.xblock--drag-and-drop .btn-default {
border-color: transparent;
background: transparent;
color: #0074b4
}
.xblock--drag-and-drop .btn-brand {
border-color: #0075b4;
background: white;
color: #0075b4;
}
.xblock--drag-and-drop .btn-small {
padding: 0.625em 0.625em;
font-size: 0.875em;
}
.xblock--drag-and-drop .btn-default:hover,
.xblock--drag-and-drop .btn-default.is-hovered,
.xblock--drag-and-drop .btn-default:focus,
.xblock--drag-and-drop .btn-default.is-focused {
background-color: transparent;
border: 1px solid #0074b4;
color: #0074b4
}
.xblock--drag-and-drop .btn-default:active,
.xblock--drag-and-drop .btn-default.is-pressed,
.xblock--drag-and-drop .btn-default.is-active {
border-color: #0074b4;
color: #0074b4
}
.xblock--drag-and-drop .btn-default:disabled,
.xblock--drag-and-drop .btn-default.is-disabled {
color: #6b6969
}
.xblock--drag-and-drop .btn-brand {
border-color: #0074b4;
background: #0074b4;
color: #fcfcfc
}
.xblock--drag-and-drop .btn-brand:hover,
.xblock--drag-and-drop .btn-brand.is-hovered,
.xblock--drag-and-drop .btn-brand:focus,
.xblock--drag-and-drop .btn-brand.is-focused {
border-color: #008bd8;
background-color: #008bd8;
color: #fcfcfc
}
.xblock--drag-and-drop .btn-brand:active,
.xblock--drag-and-drop .btn-brand.is-pressed,
.xblock--drag-and-drop .btn-brand.is-active {
border-color: #0074b4;
background: #0074b4
}
.xblock--drag-and-drop .btn-brand:disabled,
.xblock--drag-and-drop .btn-brand.is-disabled {
border-color: #d2d0d0;
background: #f2f3f3;
color: #676666
}
/*** ACTIONS TOOLBAR ***/
.xblock--drag-and-drop .actions-toolbar {
min-height: 3.75em;
width: auto;
position: relative;
}
.xblock--drag-and-drop .actions-toolbar .action-toolbar-item {
display: inline-block;
margin: 10px 0;
}
.xblock--drag-and-drop .attempts-used {
margin-left: 0.675em;
font-size: 0.875em;
color: #666;
}
.xblock--drag-and-drop .actions-toolbar .action-toolbar-item.sidebar-buttons {
text-align: left;
display: block;
}
@media (min-width: 768px) {
.xblock--drag-and-drop .actions-toolbar .action-toolbar-item.sidebar-buttons {
float: right;
margin: 0;
padding-right: -5px;
}
}
.xblock--drag-and-drop .sidebar-buttons .sidebar-button-wrapper {
border-right: 1px solid #ddd;
border-collapse: collapse;
padding: 0 5px;
display: inline-block;
height: 100%;
}
.xblock--drag-and-drop .sidebar-buttons .sidebar-button-wrapper:first-child {
padding-left: 0;
}
.xblock--drag-and-drop .sidebar-buttons .sidebar-button-wrapper:last-child {
border-right: none;
padding-right: 0;
}
.xblock--drag-and-drop .sidebar-buttons .btn-brand {
display: inline-block;
padding: 3px 5px;
}
.xblock--drag-and-drop .keyboard-help {
margin-top: 3px;
margin-bottom: 6px;
}
.xblock--drag-and-drop .btn-icon {
display: block;
}
.xblock--drag-and-drop .reset-button {
margin-top: 3px;
}
/*** ACTIONS TOOLBAR END ***/
/*** KEYBOARD HELP ***/
.xblock--drag-and-drop .keyboard-help-dialog {
position: fixed;
left: 50%;
......@@ -382,26 +543,7 @@
margin-left: 2%;
}
.xblock--drag-and-drop .link-button {
padding: 0;
margin: 0;
cursor: pointer;
color: #2d74b3;
font-weight: normal;
font-size: 12pt;
background: none;
box-shadow: none;
border: none;
}
.xblock--drag-and-drop .reset-button {
float: right;
margin-top: 3px;
}
.xblock--drag-and-drop .link-button:focus {
outline: 2px solid #2d74b3;
}
/*** KEYBOARD HELP END ***/
/* Make sure screen-reader content is hidden in the workbench: */
.xblock--drag-and-drop .sr {
......
......@@ -228,32 +228,23 @@ function DragAndDropTemplates(configuration) {
var feedbackTemplate = function(ctx) {
var feedback_display = ctx.feedback_html ? 'block' : 'none';
var reset_button_display = ctx.display_reset_button ? 'block' : 'none';
var properties = { attributes: { 'aria-live': 'polite' } };
return (
h('section.feedback', properties, [
h(
'button.reset-button.unbutton.link-button',
{ style: { display: reset_button_display }, attributes: { tabindex: 0 }, 'aria-live': 'off'},
gettext('Reset problem')
),
h('h3.title1', { style: { display: feedback_display } }, gettext('Feedback')),
h('p.message', { style: { display: feedback_display }, innerHTML: ctx.feedback_html })
])
);
};
var keyboardHelpTemplate = function(ctx) {
var dialog_attributes = { role: 'dialog', 'aria-labelledby': 'modal-window-title' };
var dialog_style = {};
var keyboardHelpPopupTemplate = function(ctx) {
var labelledby_id = 'modal-window-title-'+configuration.url_name;
return (
h('section.keyboard-help', [
h('button.keyboard-help-button.unbutton.link-button', { attributes: { tabindex: 0 } }, gettext('Keyboard Help')),
h('div.keyboard-help-dialog', [
h('div.modal-window-overlay'),
h('div.modal-window', { attributes: dialog_attributes, style: dialog_style }, [
h('div.modal-window', {attributes: {role: 'dialog', 'aria-labelledby': labelledby_id}}, [
h('div.modal-header', [
h('h2.modal-window-title', gettext('Keyboard Help'))
h('h2.modal-window-title#'+labelledby_id, gettext('Keyboard Help'))
]),
h('div.modal-content', [
h('p', gettext('You can complete this problem using only your keyboard.')),
......@@ -269,10 +260,55 @@ function DragAndDropTemplates(configuration) {
])
])
])
);
};
var submitAnswerTemplate = function(ctx) {
var attemptsUsedId = "attempts-used-"+configuration.url_name;
var attemptsUsedDisplay = (ctx.max_attempts && ctx.max_attempts > 0) ? 'inline': 'none';
var button_enabled = ctx.items.some(function(item) {return item.is_placed;}) &&
(ctx.max_attempts === null || ctx.max_attempts > ctx.num_attempts);
return (
h("section.action-toolbar-item.submit-answer", {}, [
h(
"button.btn-brand.submit-answer-button",
{disabled: !button_enabled, attributes: {"aria-describedby": attemptsUsedId}},
gettext("Submit")
),
h(
"span.attempts-used#"+attemptsUsedId, {style: {display: attemptsUsedDisplay}},
gettext("You have used {used} of {total} attempts.")
.replace("{used}", ctx.num_attempts).replace("{total}", ctx.max_attempts)
)
])
);
};
var sidebarButtonTemplate = function(buttonClass, iconClass, buttonText, disabled) {
return (
h('span.sidebar-button-wrapper', {}, [
h(
'button.unbutton.btn-default.btn-small.'+buttonClass,
{disabled: disabled || false, attributes: {tabindex: 0}},
[
h("span.btn-icon.fa."+iconClass, {attributes: {"aria-hidden": true}}, []),
buttonText
]
)
])
);
};
var sidebarTemplate = function(ctx) {
return(
h("section.action-toolbar-item.sidebar-buttons", {}, [
sidebarButtonTemplate("keyboard-help-button", "fa-question", gettext('Keyboard Help')),
sidebarButtonTemplate("reset-button", "fa-refresh", gettext('Reset'), ctx.disable_reset_button),
])
)
};
var mainTemplate = function(ctx) {
var problemTitle = ctx.show_title ? h('h2.problem-title', {innerHTML: ctx.title_html}) : null;
var problemHeader = ctx.show_problem_header ? h('h3.title1', gettext('Problem')) : null;
......@@ -336,7 +372,11 @@ function DragAndDropTemplates(configuration) {
renderCollection(zoneTemplate, ctx.zones, ctx)
]),
]),
keyboardHelpTemplate(ctx),
h("section.actions-toolbar", {}, [
sidebarTemplate(ctx),
(ctx.show_submit_answer ? submitAnswerTemplate(ctx) : null),
]),
keyboardHelpPopupTemplate(ctx),
feedbackTemplate(ctx),
])
);
......@@ -655,6 +695,10 @@ function DragAndDropBlock(runtime, element, configuration) {
zones[idx].focus();
};
var focusFirstDraggable = function() {
$root.find('.item-bank .option').first().focus();
};
var placeItem = function($zone, $item) {
var item_id;
var $anchor;
......@@ -918,13 +962,11 @@ function DragAndDropBlock(runtime, element, configuration) {
type: 'POST',
url: runtime.handlerUrl(element, 'reset'),
data: '{}',
});
state = {
'items': [],
'finished': false,
'overall_feedback': configuration.initial_feedback,
};
}).done(function(data) {
state = data;
applyState();
focusFirstDraggable();
});
};
var render = function() {
......@@ -984,8 +1026,12 @@ function DragAndDropBlock(runtime, element, configuration) {
bg_image_width: bgImgNaturalWidth, // Not stored in configuration since it's unknown on the server side
title_html: configuration.title,
show_title: configuration.show_title,
mode: configuration.mode,
max_attempts: configuration.max_attempts,
num_attempts: state.num_attempts,
problem_html: configuration.problem_text,
show_problem_header: configuration.show_problem_header,
show_submit_answer: configuration.mode == DragAndDropBlock.ASSESSMENT_MODE,
target_img_src: configuration.target_img_expanded_url,
target_img_description: configuration.target_img_description,
display_zone_labels: configuration.display_zone_labels,
......@@ -997,7 +1043,7 @@ function DragAndDropBlock(runtime, element, configuration) {
item_bank_focusable: item_bank_focusable,
popup_html: state.feedback || '',
feedback_html: $.trim(state.overall_feedback),
display_reset_button: Object.keys(state.items).length > 0,
disable_reset_button: Object.keys(state.items).length == 0,
};
return renderView(context);
......@@ -1035,8 +1081,8 @@ function DragAndDropBlock(runtime, element, configuration) {
if (configuration.items[i].id === +item_id) {
var size = configuration.items[i].size;
// size is an object like '{width: "50px", height: "auto"}'
if (parseInt(size.width ) > 0) { width = parseInt(size.width); }
if (parseInt(size.height) > 0) { height = parseInt(size.height); }
if (parseInt(size.width ) > 0) {width = parseInt(size.width);}
if (parseInt(size.height) > 0) {height = parseInt(size.height);}
break;
}
}
......
......@@ -74,14 +74,20 @@ class BaseIntegrationTest(SeleniumBaseTest):
return self._page.find_element_by_css_selector(".keyboard-help")
def _get_keyboard_help_button(self):
return self._page.find_element_by_css_selector(".keyboard-help .keyboard-help-button")
return self._page.find_element_by_css_selector(".keyboard-help-button")
def _get_keyboard_help_dialog(self):
return self._page.find_element_by_css_selector(".keyboard-help .keyboard-help-dialog")
return self._page.find_element_by_css_selector(".keyboard-help-dialog")
def _get_reset_button(self):
return self._page.find_element_by_css_selector('.reset-button')
def _get_submit_button(self):
return self._page.find_element_by_css_selector('.submit-answer-button')
def _get_attempts_info(self):
return self._page.find_element_by_css_selector('.attempts-used')
def _get_feedback(self):
return self._page.find_element_by_css_selector(".feedback")
......
......@@ -473,8 +473,12 @@ class DefaultAssessmentDataTestMixin(DefaultDataTestMixin):
"""
Provides a test scenario with default options in assessment mode.
"""
MAX_ATTEMPTS = 5
def _get_scenario_xml(self): # pylint: disable=no-self-use
return "<vertical_demo><drag-and-drop-v2 mode='assessment'/></vertical_demo>"
return """
<vertical_demo><drag-and-drop-v2 mode='assessment' max_attempts='{max_attempts}'/></vertical_demo>
""".format(max_attempts=self.MAX_ATTEMPTS)
@ddt
......@@ -542,6 +546,22 @@ class AssessmentInteractionTest(DefaultAssessmentDataTestMixin, InteractionTestB
def test_keyboard_help(self, use_keyboard):
self.interact_with_keyboard_help(use_keyboard=use_keyboard)
def test_submit_button_shown(self):
first_item_definition = self._get_items_with_zone(self.items_map).values()[0]
submit_button = self._get_submit_button()
self.assertTrue(submit_button.is_displayed())
self.assertEqual(submit_button.get_attribute('disabled'), 'true') # no items are placed
attempts_info = self._get_attempts_info()
expected_text = "You have used {num} of {max} attempts.".format(num=0, max=self.MAX_ATTEMPTS)
self.assertEqual(attempts_info.text, expected_text)
self.assertEqual(attempts_info.is_displayed(), self.MAX_ATTEMPTS > 0)
self.place_item(first_item_definition.item_id, first_item_definition.zone_ids[0], None)
self.assertEqual(submit_button.get_attribute('disabled'), None)
class MultipleValidOptionsInteractionTest(DefaultDataTestMixin, InteractionTestBase, BaseIntegrationTest):
......
......@@ -213,7 +213,6 @@ class TestDragAndDropRender(BaseIntegrationTest):
def test_keyboard_help(self):
self.load_scenario()
self._get_keyboard_help()
keyboard_help_button = self._get_keyboard_help_button()
keyboard_help_dialog = self._get_keyboard_help_dialog()
dialog_modal_overlay = keyboard_help_dialog.find_element_by_css_selector('.modal-window-overlay')
......@@ -223,7 +222,7 @@ class TestDragAndDropRender(BaseIntegrationTest):
self.assertFalse(dialog_modal_overlay.is_displayed())
self.assertFalse(dialog_modal.is_displayed())
self.assertEqual(dialog_modal.get_attribute('role'), 'dialog')
self.assertEqual(dialog_modal.get_attribute('aria-labelledby'), 'modal-window-title')
self.assertEqual(dialog_modal.get_attribute('aria-labelledby'), 'modal-window-title-')
def test_feedback(self):
self.load_scenario()
......
{
"title": "DnDv2 XBlock with plain text instructions",
"mode": "assessment",
"max_attempts": 10,
"show_title": true,
"problem_text": "Can you solve this drag-and-drop problem?",
"show_problem_header": true,
"target_img_expanded_url": "http://placehold.it/800x600",
"target_img_description": "This describes the target image",
"item_background_color": null,
"item_text_color": null,
"initial_feedback": "This is the initial feedback.",
"display_zone_borders": false,
"display_zone_labels": false,
"url_name": "test",
"zones": [
{
"title": "Zone 1",
"y": 123,
"x": 234,
"width": 345,
"height": 456,
"uid": "zone-1"
},
{
"title": "Zone 2",
"y": 20,
"x": 10,
"width": 30,
"height": 40,
"uid": "zone-2"
}
],
"items": [
{
"displayName": "1",
"imageURL": "",
"expandedImageURL": "",
"id": 0
},
{
"displayName": "2",
"imageURL": "",
"expandedImageURL": "",
"id": 1
},
{
"displayName": "X",
"imageURL": "/static/test_url_expansion",
"expandedImageURL": "/course/test-course/assets/test_url_expansion",
"id": 2
},
{
"displayName": "",
"imageURL": "http://placehold.it/200x100",
"expandedImageURL": "http://placehold.it/200x100",
"id": 3
}
]
}
{
"zones": [
{
"title": "Zone 1",
"y": 123,
"x": 234,
"width": 345,
"height": 456,
"uid": "zone-1"
},
{
"title": "Zone 2",
"y": 20,
"x": 10,
"width": 30,
"height": 40,
"uid": "zone-2"
}
],
"items": [
{
"displayName": "1",
"feedback": {
"incorrect": "No 1",
"correct": "Yes 1"
},
"zone": "zone-1",
"imageURL": "",
"id": 0
},
{
"displayName": "2",
"feedback": {
"incorrect": "No 2",
"correct": "Yes 2"
},
"zone": "zone-2",
"imageURL": "",
"id": 1
},
{
"displayName": "X",
"feedback": {
"incorrect": "",
"correct": ""
},
"zone": "none",
"imageURL": "/static/test_url_expansion",
"id": 2
},
{
"displayName": "",
"feedback": {
"incorrect": "",
"correct": ""
},
"zone": "none",
"imageURL": "http://placehold.it/200x100",
"id": 3
}
],
"feedback": {
"start": "This is the initial feedback.",
"finish": "This is the final feedback."
},
"targetImg": "http://placehold.it/800x600",
"targetImgDescription": "This describes the target image",
"displayLabels": false
}
{
"display_name": "DnDv2 XBlock with plain text instructions",
"mode": "assessment",
"max_attempts": 10,
"show_title": true,
"question_text": "Can you solve this drag-and-drop problem?",
"show_question_header": true,
"weight": 1,
"item_background_color": "",
"item_text_color": "",
"url_name": "test"
}
{
"title": "DnDv2 XBlock with HTML instructions",
"mode": "standard",
"max_attempts": 0,
"show_title": false,
"problem_text": "Solve this <strong>drag-and-drop</strong> problem.",
"show_problem_header": false,
......
{
"display_name": "DnDv2 XBlock with HTML instructions",
"max_attempts": 0,
"show_title": false,
"question_text": "Solve this <strong>drag-and-drop</strong> problem.",
"show_question_header": false,
......
{
"title": "Drag and Drop",
"mode": "standard",
"max_attempts": null,
"show_title": true,
"problem_text": "",
"show_problem_header": true,
......
{
"title": "DnDv2 XBlock with plain text instructions",
"mode": "standard",
"max_attempts": 0,
"show_title": true,
"problem_text": "Can you solve this drag-and-drop problem?",
"show_problem_header": true,
......
{
"display_name": "DnDv2 XBlock with plain text instructions",
"max_attempts": 0,
"show_title": true,
"question_text": "Can you solve this drag-and-drop problem?",
"show_question_header": true,
......
......@@ -5,8 +5,6 @@ import unittest
from xblockutils.resources import ResourceLoader
from drag_and_drop_v2.drag_and_drop_v2 import DragAndDropBlock
from ..utils import make_block, TestCaseMixin
......@@ -57,8 +55,13 @@ class BaseDragAndDropAjaxFixture(TestCaseMixin):
return cls.expected_configuration()["initial_feedback"]
def test_get_configuration(self):
self.assertEqual(self.expected_configuration(), self.block.get_configuration())
self.assertEqual(self.block.get_configuration(), self.expected_configuration())
class StandardModeFixture(BaseDragAndDropAjaxFixture):
"""
Common tests for drag and drop in standard mode
"""
def test_do_attempt_wrong_with_feedback(self):
item_id, zone_id = 0, self.ZONE_2
data = {"val": item_id, "zone": zone_id, "x_percent": "33%", "y_percent": "11%"}
......@@ -92,14 +95,6 @@ class BaseDragAndDropAjaxFixture(TestCaseMixin):
"feedback": self.FEEDBACK[item_id]["correct"]
})
def test_do_attempt_in_assessment_mode(self):
self.block.mode = DragAndDropBlock.ASSESSMENT_MODE
item_id, zone_id = 0, self.ZONE_1
data = {"val": item_id, "zone": zone_id, "x_percent": "33%", "y_percent": "11%"}
res = self.call_handler('do_attempt', data)
# In assessment mode, the do_attempt doesn't return any data.
self.assertEqual(res, {})
def test_grading(self):
published_grades = []
......@@ -131,6 +126,7 @@ class BaseDragAndDropAjaxFixture(TestCaseMixin):
"0": {"x_percent": "33%", "y_percent": "11%", "correct": True, "zone": self.ZONE_1}
},
"finished": False,
"num_attempts": 0,
'overall_feedback': self.initial_feedback(),
}
self.assertEqual(expected_state, self.call_handler('get_user_state', method="GET"))
......@@ -154,12 +150,25 @@ class BaseDragAndDropAjaxFixture(TestCaseMixin):
}
},
"finished": True,
"num_attempts": 0,
'overall_feedback': self.FINAL_FEEDBACK,
}
self.assertEqual(expected_state, self.call_handler('get_user_state', method="GET"))
class TestDragAndDropHtmlData(BaseDragAndDropAjaxFixture, unittest.TestCase):
class AssessmentModeFixture(BaseDragAndDropAjaxFixture):
"""
Common tests for drag and drop in assessment mode
"""
def test_do_attempt_in_assessment_mode(self):
item_id, zone_id = 0, self.ZONE_1
data = {"val": item_id, "zone": zone_id, "x_percent": "33%", "y_percent": "11%"}
res = self.call_handler('do_attempt', data)
# In assessment mode, the do_attempt doesn't return any data.
self.assertEqual(res, {})
class TestDragAndDropHtmlData(StandardModeFixture, unittest.TestCase):
FOLDER = "html"
ZONE_1 = "Zone <i>1</i>"
......@@ -174,7 +183,7 @@ class TestDragAndDropHtmlData(BaseDragAndDropAjaxFixture, unittest.TestCase):
FINAL_FEEDBACK = "Final <strong>feedback</strong>!"
class TestDragAndDropPlainData(BaseDragAndDropAjaxFixture, unittest.TestCase):
class TestDragAndDropPlainData(StandardModeFixture, unittest.TestCase):
FOLDER = "plain"
ZONE_1 = "zone-1"
......@@ -198,3 +207,18 @@ class TestOldDataFormat(TestDragAndDropPlainData):
ZONE_1 = "Zone 1"
ZONE_2 = "Zone 2"
class TestDragAndDropAssessmentData(AssessmentModeFixture, unittest.TestCase):
FOLDER = "assessment"
ZONE_1 = "zone-1"
ZONE_2 = "zone-2"
FEEDBACK = {
0: {"correct": "Yes 1", "incorrect": "No 1"},
1: {"correct": "Yes 2", "incorrect": "No 2"},
2: {"correct": "", "incorrect": ""}
}
FINAL_FEEDBACK = "This is the final feedback."
......@@ -31,6 +31,7 @@ class BasicTests(TestCaseMixin, unittest.TestCase):
items = config.pop("items")
self.assertEqual(config, {
"mode": DragAndDropBlock.STANDARD_MODE,
"max_attempts": None,
"display_zone_borders": False,
"display_zone_labels": False,
"title": "Drag and Drop",
......@@ -68,6 +69,7 @@ class BasicTests(TestCaseMixin, unittest.TestCase):
self.assertEqual(self.call_handler("get_user_state"), {
'items': {},
'finished': False,
"num_attempts": 0,
'overall_feedback': START_FEEDBACK,
})
assert_user_state_empty()
......@@ -98,6 +100,7 @@ class BasicTests(TestCaseMixin, unittest.TestCase):
'3': {'x_percent': '67%', 'y_percent': '80%', 'correct': True, "zone": MIDDLE_ZONE_ID},
},
'finished': True,
"num_attempts": 0,
'overall_feedback': FINISH_FEEDBACK,
})
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment