diff --git a/runestone/dragndrop/README.md b/runestone/dragndrop/README.md
index 8235c9071..a244fa414 100644
--- a/runestone/dragndrop/README.md
+++ b/runestone/dragndrop/README.md
@@ -2,17 +2,17 @@
```html
- The Question goes here.
- This is feedback that is displayed when this is answered incorrectly.
+ The Question goes here.
+ This is feedback that is displayed when this is answered incorrectly.
- - Drag to Answer A
- - Answer A
+ - Drag to Answer A
+ - Answer A
- - Drag to Answer B
- - Answer B
+ - Drag to Answer B
+ - Answer B
- - Drag to Answer C
- - Answer C
+ - Drag to Answer C
+ - Answer C
@@ -28,15 +28,15 @@ Option spec:
data-component="dragndrop" Identifies this as a drag n drop component
id Must be unique in the document
- data-component="question" Optional--Identifies a span that contains the question
- data-component="feedback" Optional--Identifies a span that contains the feedback for incorrect answers
+ data-subcomponent="question" Optional--Identifies a span that contains the question
+ data-subcomponent="feedback" Optional--Identifies a span that contains the feedback for incorrect answers
Option spec for the li tags:
- data-component="draggable" Identifies a draggable element that will be dropped into a dropzone block
+ data-subcomponent="draggable" Identifies a draggable element that will be dropped into a dropzone block
id For the draggable elements--must be unique in the component
- data-component="dropzone" Identifies a dropzone component that will receive a draggable element
+ data-subcomponent="dropzone" Identifies a dropzone component that will receive a draggable element
for For the dropzone components--identifies the correct draggable element (via id) that when dropped into this dropzone, will be registered as correct
diff --git a/runestone/dragndrop/dragndrop.py b/runestone/dragndrop/dragndrop.py
index a2ae6a8a2..27ca38718 100644
--- a/runestone/dragndrop/dragndrop.py
+++ b/runestone/dragndrop/dragndrop.py
@@ -18,7 +18,6 @@
from docutils import nodes
from docutils.parsers.rst import directives
-from docutils.parsers.rst import Directive
from runestone.server.componentdb import (
addQuestionToDB,
addHTMLToDB,
@@ -27,7 +26,6 @@
from runestone.common.runestonedirective import (
RunestoneIdDirective,
RunestoneIdNode,
- add_i18n_js,
)
@@ -42,13 +40,13 @@ def setup(app):
TEMPLATE_START = """
- %(qnumber)s: %(question)s
+ %(qnumber)s: %(question)s
%(feedback)s
"""
TEMPLATE_OPTION = """
- - %(dragText)s
- - %(dropText)s
+ - %(dragText)s
+ - %(dropText)s
"""
TEMPLATE_END = """
"""
@@ -75,7 +73,7 @@ def visit_dnd_node(self, node):
if "feedback" in node.runestone_options:
node.runestone_options["feedback"] = (
- ""
+ ""
+ node.runestone_options["feedback"]
+ ""
)
diff --git a/runestone/dragndrop/js/dragndrop.js b/runestone/dragndrop/js/dragndrop.js
index 699027e76..54fdd4ed4 100644
--- a/runestone/dragndrop/js/dragndrop.js
+++ b/runestone/dragndrop/js/dragndrop.js
@@ -44,7 +44,7 @@ export default class DragNDrop extends RunestoneBase {
populate() {
for (var i = 0; i < this.origElem.childNodes.length; i++) {
if (
- $(this.origElem.childNodes[i]).data("component") === "dropzone"
+ $(this.origElem.childNodes[i]).data("subcomponent") === "dropzone"
) {
var tmp = $(this.origElem).find(
`#${$(this.origElem.childNodes[i]).attr("for")}`
@@ -65,11 +65,11 @@ export default class DragNDrop extends RunestoneBase {
tmpArr.push(otherReplaceSpan);
this.dragPairArray.push(tmpArr);
} else if (
- $(this.origElem.childNodes[i]).data("component") === "question"
+ $(this.origElem.childNodes[i]).data("subcomponent") === "question"
) {
this.question = this.origElem.childNodes[i].innerHTML;
} else if (
- $(this.origElem.childNodes[i]).data("component") === "feedback"
+ $(this.origElem.childNodes[i]).data("subcomponent") === "feedback"
) {
this.feedback = this.origElem.childNodes[i].innerHTML;
}
@@ -81,6 +81,7 @@ export default class DragNDrop extends RunestoneBase {
========================================*/
createNewElements() {
this.containerDiv = document.createElement("div");
+ this.containerDiv.id = this.divid;
$(this.containerDiv).addClass(
"alert alert-warning draggable-container"
);
@@ -97,7 +98,7 @@ export default class DragNDrop extends RunestoneBase {
this.dragDropWrapDiv.appendChild(this.draggableDiv);
this.dragDropWrapDiv.appendChild(this.dropZoneDiv);
this.createButtons();
- this.checkServer("dragNdrop");
+ this.checkServer("dragNdrop", true);
}
finishSettingUp() {
this.appendReplacementSpans();
diff --git a/runestone/dragndrop/test/_sources/index.rst b/runestone/dragndrop/test/_sources/index.rst
index d1f802695..4dc0b18e9 100644
--- a/runestone/dragndrop/test/_sources/index.rst
+++ b/runestone/dragndrop/test/_sources/index.rst
@@ -18,11 +18,10 @@ Testing: Drag and Drop Questions
Drag and Drop
-------------
-.. dragndrop:: question3
+.. dragndrop:: test_dnd_1
:feedback: Review your choice
:match_1: C++|||cpp
:match_2: Java|||java
:match_3: Python|||py
Match the language and the file extension.
-
diff --git a/runestone/dragndrop/test/test_dragndrop.py b/runestone/dragndrop/test/test_dragndrop.py
index ada38c2b4..d5b81504e 100644
--- a/runestone/dragndrop/test/test_dragndrop.py
+++ b/runestone/dragndrop/test/test_dragndrop.py
@@ -10,94 +10,121 @@
__author__ = "yasinovskyy"
-from selenium.webdriver.support import expected_conditions as EC
-from selenium.webdriver.support.ui import WebDriverWait
-from selenium.webdriver.common.by import By
-from runestone.unittest_base import module_fixture_maker, RunestoneTestCase
-
-setUpModule, tearDownModule = module_fixture_maker(__file__)
-
-
-class DragAndDropQuestion_Tests(RunestoneTestCase):
- def setUp(self):
- super(DragAndDropQuestion_Tests, self).setUp()
- self.driver.set_script_timeout(5)
- with open("drag_and_drop_helper.js") as f:
- self.js = f.read()
-
- def test_dnd1(self):
- """No selection. Button clicked"""
- self.driver.get(self.host + "/index.html")
- wait = WebDriverWait(self.driver, 10)
- try:
- wait.until(EC.presence_of_element_located((By.ID, "drag-and-drop")))
- except:
- text = self.driver.page_source
- print(text[:300])
-
- t1 = self.driver.find_element_by_id("drag-and-drop")
-
- btn_check = t1.find_element_by_class_name("btn-success")
- btn_check.click()
-
- fb = t1.find_element_by_id("question3_feedback")
- self.assertIsNotNone(fb)
- cnamestr = fb.get_attribute("class")
- self.assertIn("alert-danger", cnamestr)
-
- targets = t1.find_elements_by_class_name("draggable-drop")
- for item in targets:
- self.assertIn("drop-incorrect", item.get_attribute("class"))
-
- def test_dnd2(self):
- """Terms matched correctly"""
- self.driver.get(self.host + "/index.html")
- t1 = self.driver.find_element_by_id("drag-and-drop")
-
- targets = self.driver.find_elements_by_class_name("draggable-drop")
-
- for target in targets:
- if target.text == "cpp":
- element_id = "question3question3_drag1"
- elif target.text == "java":
- element_id = "question3question3_drag2"
- elif target.text == "py":
- element_id = "question3question3_drag3"
-
- self.driver.execute_script(
- self.js
- + "$('#"
- + element_id
- + "').simulateDragDrop({ dropTarget: 'span:contains(\""
- + target.text
- + "\")' });"
- )
- btn_check = t1.find_element_by_class_name("btn-success")
- btn_check.click()
+from pathlib import Path
+
+
+def dnd_helper():
+ dnd_helper_path = Path(__file__).parent / "drag_and_drop_helper.js"
+ with open(dnd_helper_path, encoding="utf-8") as f:
+ return f.read()
+
+
+def get_dnd_div(selenium_utils):
+ selenium_utils.wait_until_ready("test_dnd_1")
+ div = selenium_utils.driver.find_element_by_id("test_dnd_1")
+ return div, div.find_elements_by_class_name("draggable-drop")
+
- fb = t1.find_element_by_id("question3_feedback")
- self.assertIsNotNone(fb)
- cnamestr = fb.get_attribute("class")
- self.assertIn("alert-info", cnamestr)
+def find_feedback(dnd_element):
+ div_id = dnd_element.get_attribute("id")
+ fb = dnd_element.find_element_by_id(f"{div_id}_feedback")
+ return fb.get_attribute("class")
- def test_dnd3(self):
- """Reset button clicked"""
- self.driver.get(self.host + "/index.html")
- t1 = self.driver.find_element_by_id("drag-and-drop")
- targets = self.driver.find_elements_by_class_name("draggable-drop")
+def click_button(dnd_element):
+ btn_check = dnd_element.find_element_by_class_name("btn-success")
+ btn_check.click()
- for target in targets:
- if target.text == "cpp":
- element_id = "question3question3_drag1"
- elif target.text == "java":
- element_id = "question3question3_drag2"
- elif target.text == "py":
- element_id = "question3question3_drag3"
- self.driver.execute_script(
- self.js
+def test_dnd1(selenium_utils_get):
+ """No selection. Button clicked"""
+ t1, targets = get_dnd_div(selenium_utils_get)
+
+ click_button(t1)
+ assert "alert-danger" in find_feedback(t1)
+
+ for item in targets:
+ assert "drop-incorrect" in item.get_attribute("class")
+
+
+def test_dnd2(selenium_utils_get):
+ """Terms matched correctly"""
+ t1, targets = get_dnd_div(selenium_utils_get)
+ div_id = t1.get_attribute("id")
+
+ for target in targets:
+ div_id = t1.get_attribute("id")
+ if target.text == "cpp":
+ element_id = f"{div_id}{div_id}_drag1"
+ elif target.text == "java":
+ element_id = f"{div_id}{div_id}_drag2"
+ elif target.text == "py":
+ element_id = f"{div_id}{div_id}_drag3"
+
+ selenium_utils_get.driver.execute_script(
+ dnd_helper()
+ + "$('#"
+ + element_id
+ + "').simulateDragDrop({ dropTarget: 'span:contains(\""
+ + target.text
+ + "\")' });"
+ )
+
+ click_button(t1)
+ assert "alert-info" in find_feedback(t1)
+
+
+def test_dnd3(selenium_utils_get):
+ """Reset button clicked"""
+ t1, targets = get_dnd_div(selenium_utils_get)
+
+ for target in targets:
+ div_id = t1.get_attribute("id")
+ if target.text == "cpp":
+ element_id = f"{div_id}{div_id}_drag1"
+ elif target.text == "java":
+ element_id = f"{div_id}{div_id}_drag2"
+ elif target.text == "py":
+ element_id = f"{div_id}{div_id}_drag3"
+
+ selenium_utils_get.driver.execute_script(
+ dnd_helper()
+ + "$('#"
+ + element_id
+ + "').simulateDragDrop({ dropTarget: 'span:contains(\""
+ + target.text
+ + "\")' });"
+ )
+
+ for target in targets:
+ element = target.find_element_by_class_name("draggable-drag")
+ # Expected: draggable-drag inside a draggable-drop
+ assert element
+
+ t1.find_element_by_class_name("drag-reset").click()
+ for target in targets:
+ element = target.find_elements_by_class_name("draggable-drag")
+ # Expected: empty list of elements
+ assert not element
+
+
+def test_dnd4(selenium_utils_get):
+ """Incorrect answer changed to correct"""
+ t1, targets = get_dnd_div(selenium_utils_get)
+
+ for target in targets:
+ div_id = t1.get_attribute("id")
+ if target.text == "cpp":
+ element_id = f"{div_id}{div_id}_drag1"
+ elif target.text == "java":
+ element_id = f"{div_id}{div_id}_drag2"
+ elif target.text == "py":
+ element_id = ""
+
+ if element_id:
+ selenium_utils_get.driver.execute_script(
+ dnd_helper()
+ "$('#"
+ element_id
+ "').simulateDragDrop({ dropTarget: 'span:contains(\""
@@ -105,59 +132,12 @@ def test_dnd3(self):
+ "\")' });"
)
- for target in targets:
- element = target.find_element_by_class_name("draggable-drag")
- # Expected: draggable-drag inside a draggable-drop
- self.assertIsNotNone(element)
-
- btn_reset = t1.find_element_by_class_name("drag-reset")
- btn_reset.click()
-
- for target in targets:
- element = target.find_elements_by_class_name("draggable-drag")
- # Expected: empty list of elements
- self.assertFalse(element)
-
- def test_dnd4(self):
- """Incorrect answer changed to correct"""
- self.driver.get(self.host + "/index.html")
- t1 = self.driver.find_element_by_id("drag-and-drop")
-
- targets = self.driver.find_elements_by_class_name("draggable-drop")
-
- for target in targets:
- if target.text == "cpp":
- element_id = "question3question3_drag1"
- elif target.text == "java":
- element_id = "question3question3_drag2"
- elif target.text == "py":
- element_id = ""
-
- if element_id:
- self.driver.execute_script(
- self.js
- + "$('#"
- + element_id
- + "').simulateDragDrop({ dropTarget: 'span:contains(\""
- + target.text
- + "\")' });"
- )
-
- btn_check = t1.find_element_by_class_name("btn-success")
- btn_check.click()
-
- fb = t1.find_element_by_id("question3_feedback")
- self.assertIsNotNone(fb)
- cnamestr = fb.get_attribute("class")
- self.assertIn("alert-danger", cnamestr)
-
- self.driver.execute_script(
- self.js
- + "$('#question3question3_drag3').simulateDragDrop({ dropTarget: 'span:contains(\"py\")' });"
- )
- btn_check.click()
+ click_button(t1)
+ assert "alert-danger" in find_feedback(t1)
- fb = t1.find_element_by_id("question3_feedback")
- self.assertIsNotNone(fb)
- cnamestr = fb.get_attribute("class")
- self.assertIn("alert-info", cnamestr)
+ selenium_utils_get.driver.execute_script(
+ dnd_helper()
+ + f"$('#{div_id}{div_id}_drag3').simulateDragDrop({{ dropTarget: 'span:contains(\"py\")' }});"
+ )
+ click_button(t1)
+ assert "alert-info" in find_feedback(t1)