Commit 72c2d98b by E. Kolpakov

Integration tests for custom data (rendering + interaction)

parent 06c53a4f
{
"zones": [
{
"index": 1,
"width": 200,
"title": "Zone A",
"height": 100,
"y": "200",
"x": "100",
"id": "zone-1"
},
{
"index": 2,
"width": 200,
"title": "Zone B",
"height": 100,
"y": 0,
"x": 0,
"id": "zone-2"
}
],
"items": [
{
"displayName": "A here",
"feedback": {
"incorrect": "No A",
"correct": "Yes A"
},
"zone": "Zone A",
"backgroundImage": "",
"id": 0,
"size": {
"width": "190px",
"height": "auto"
}
},
{
"displayName": "B here",
"feedback": {
"incorrect": "No B",
"correct": "Yes B"
},
"zone": "Zone B",
"backgroundImage": "",
"id": 1,
"size": {
"width": "190px",
"height": "auto"
}
},
{
"displayName": "X",
"feedback": {
"incorrect": "No Zone for this",
"correct": ""
},
"zone": "none",
"backgroundImage": "",
"id": 2,
"size": {
"width": "100px",
"height": "100px"
}
}
],
"state": {
"items": {},
"finished": true
},
"feedback": {
"start": "Other Intro Feed",
"finish": "Other Final Feed"
},
"targetImg": "http://i0.kym-cdn.com/photos/images/newsfeed/000/030/404/1260585284155.png"
}
{
"zones": [
{
"index": 1,
"width": 200,
"title": "Zone <i>A</i>",
"height": 100,
"y": "200",
"x": "100",
"id": "zone-1"
},
{
"index": 2,
"width": 200,
"title": "Zone <b>B</b>",
"height": 100,
"y": 0,
"x": 0,
"id": "zone-2"
}
],
"items": [
{
"displayName": "<b>A</b>",
"feedback": {
"incorrect": "No <b>A</b>",
"correct": "Yes <b>A</b>"
},
"zone": "Zone <i>A</i>",
"backgroundImage": "",
"id": 0,
"size": {
"width": "190px",
"height": "auto"
}
},
{
"displayName": "<i>B</i>",
"feedback": {
"incorrect": "No <i>B</i>",
"correct": "Yes <i>B</i>"
},
"zone": "Zone <b>B</b>",
"backgroundImage": "",
"id": 1,
"size": {
"width": "190px",
"height": "auto"
}
},
{
"displayName": "<span style='color:red'>X</span>",
"feedback": {
"incorrect": "No Zone for <i>X</i>",
"correct": ""
},
"zone": "none",
"backgroundImage": "",
"id": 2,
"size": {
"width": "100px",
"height": "100px"
}
}
],
"state": {
"items": {},
"finished": true
},
"feedback": {
"start": "Intro <i>Feed</i>",
"finish": "Final <b>Feed</b>"
},
"targetImg": "http://i0.kym-cdn.com/photos/images/newsfeed/000/030/404/1260585284155.png"
}
# Imports ###########################################################
from xml.sax.saxutils import escape
from selenium.webdriver.support.wait import WebDriverWait
from tests.utils import load_resource
from workbench import scenarios
from workbench.test.selenium_test import SeleniumTest
......@@ -9,6 +11,12 @@ from workbench.test.selenium_test import SeleniumTest
class BaseIntegrationTest(SeleniumTest):
_additional_escapes = {
'"': "&quot;",
"'": "&apos;"
}
def setUp(self):
super(BaseIntegrationTest, self).setUp()
......@@ -30,10 +38,23 @@ class BaseIntegrationTest(SeleniumTest):
</vertical_demo>
""".format(display_name=escape(display_name), question_text=escape(question_text), completed=completed)
def _get_custom_scenario_xml(self, filename):
data = load_resource(filename)
return "<vertical_demo><drag-and-drop-v2 data='{data}'/></vertical_demo>".format(
data=escape(data, self._additional_escapes)
)
def _add_scenario(self, identifier, title, xml):
scenarios.add_xml_scenario(identifier, title, xml)
self.addCleanup(scenarios.remove_scenario, identifier)
def _get_items(self):
items_container = self._page.find_element_by_css_selector('ul.items')
return items_container.find_elements_by_css_selector('li.option')
def _get_zones(self):
return self._page.find_elements_by_css_selector(".drag-container .zone")
def _get_feedback_message(self):
return self._page.find_element_by_css_selector(".feedback .message")
......@@ -50,4 +71,7 @@ class BaseIntegrationTest(SeleniumTest):
return element.get_attribute('innerHTML').strip()
def get_element_classes(self, element):
return element.get_attribute('class').split()
\ No newline at end of file
return element.get_attribute('class').split()
def scroll_by(self, y):
self.browser.execute_script('window.scrollTo(0, {0})'.format(y))
\ No newline at end of file
from tests.integration.test_base import BaseIntegrationTest
class TestCustomDataDragAndDropRendering(BaseIntegrationTest):
PAGE_TITLE = 'Drag and Drop v2'
PAGE_ID = 'drag_and_drop_v2'
def setUp(self):
super(TestCustomDataDragAndDropRendering, self).setUp()
scenario_xml = self._get_custom_scenario_xml("integration/data/test_html_data.json")
self._add_scenario(self.PAGE_ID, self.PAGE_TITLE, scenario_xml)
self.browser.get(self.live_server_url)
self._page = self.go_to_page(self.PAGE_TITLE)
header1 = self.browser.find_element_by_css_selector('h1')
self.assertEqual(header1.text, 'XBlock: ' + self.PAGE_TITLE)
def test_items_rendering(self):
items = self._get_items()
self.assertEqual(len(items), 3)
self.assertEqual(self.get_element_html(items[0]), "<b>A</b>")
self.assertEqual(self.get_element_html(items[1]), "<i>B</i>")
self.assertEqual(self.get_element_html(items[2]), '<span style="color:red">X</span>')
\ No newline at end of file
......@@ -3,19 +3,51 @@ from selenium.webdriver import ActionChains
from selenium.webdriver.firefox import webdriver
from tests.integration.test_base import BaseIntegrationTest
from tests.utils import load_resource
class TestInteraction(BaseIntegrationTest):
class ItemDefinition(object):
def __init__(self, item_id, zone_id, feedback_positive, feedback_negative):
self.feedback_negative = feedback_negative
self.feedback_positive = feedback_positive
self.zone_id = zone_id
self.item_id = item_id
class InteractionTestFixture(BaseIntegrationTest):
"""
Verifying Drag and Drop XBlock rendering against default data - if default data changes this would probably broke
"""
PAGE_TITLE = 'Drag and Drop v2'
PAGE_ID = 'drag_and_drop_v2'
items_map = {
0: ItemDefinition(0, 'Zone A', "Yes, it's an A", "No, A does not belong here"),
1: ItemDefinition(1, 'Zone B', "Yes, it's a B", "No, B does not belong here"),
2: ItemDefinition(2, None, "", "You silly, there are no zones for X")
}
all_zones = ['Zone A', 'Zone B']
feedback = {
"intro": "Intro Feed",
"final": "Final Feed"
}
def _get_scenario_xml(self):
return "<vertical_demo><drag-and-drop-v2/></vertical_demo>"
@classmethod
def _get_correct_item_to_zone(cls):
return {
item_key: definition for item_key, definition in cls.items_map.items()
if definition.zone_id is not None
}
def setUp(self):
super(TestInteraction, self).setUp()
super(InteractionTestFixture, self).setUp()
scenario_xml = "<vertical_demo><drag-and-drop-v2/></vertical_demo>"
scenario_xml = self._get_scenario_xml()
self._add_scenario(self.PAGE_ID, self.PAGE_TITLE, scenario_xml)
self.browser.get(self.live_server_url)
......@@ -37,44 +69,81 @@ class TestInteraction(BaseIntegrationTest):
action_chains.drag_and_drop(element, target).perform()
@parameterized.expand([
("Item A", 0, 'Zone A', "Yes, it's an A"),
("Item B", 1, 'Zone B', "Yes, it's a B"),
])
def test_correct_item_positive_feedback(self, _, item_value, zone_id, expected_feedback):
self.drag_item_to_zone(item_value, zone_id)
def test_correct_item_positive_feedback(self):
feedback_popup = self._page.find_element_by_css_selector(".popup-content")
for definition in self._get_correct_item_to_zone().values():
self.drag_item_to_zone(definition.item_id, definition.zone_id)
self.assertEqual(self.get_element_html(feedback_popup), definition.feedback_positive)
self.assertEqual(self._page.find_element_by_css_selector(".popup-content").text, expected_feedback)
def test_incorrect_item_negative_feedback(self):
feedback_popup = self._page.find_element_by_css_selector(".popup-content")
@parameterized.expand([
("Item A", 0, 'Zone B', "No, A does not belong here"),
("Item B", 1, 'Zone A', "No, B does not belong here"),
("Item X", 2, 'Zone A', "You silly, there are no zones for X"),
("Item X", 2, 'Zone B', "You silly, there are no zones for X"),
])
def test_incorrect_item_negative_feedback(self, _, item_value, zone_id, expected_feedback):
self.drag_item_to_zone(item_value, zone_id)
self.assertEqual(self._page.find_element_by_css_selector(".popup-content").text, expected_feedback)
for definition in self.items_map.values():
for zone in self.all_zones:
if zone == definition.zone_id:
continue
self.drag_item_to_zone(definition.item_id, zone)
self.assertEqual(self.get_element_html(feedback_popup), definition.feedback_negative)
def test_final_feedback_and_reset(self):
feedback_message = self._get_feedback_message()
self.assertEqual(feedback_message.text, "Intro Feed") # precondition check
self.assertEqual(self.get_element_html(feedback_message), self.feedback['intro']) # precondition check
items = self._get_correct_item_to_zone()
get_locations = lambda: {item_id: self._get_item_by_value(item_id).location for item_id in items.keys()}
item0_initial_position = self._get_item_by_value(0).location
item1_initial_position = self._get_item_by_value(1).location
initial_locations = get_locations()
self.drag_item_to_zone(0, 'Zone A')
self.drag_item_to_zone(1, 'Zone B')
for item_key, definition in items.items():
self.drag_item_to_zone(item_key, definition.zone_id)
self.assertEqual(feedback_message.text, "Final Feed")
self.assertEqual(self.get_element_html(feedback_message), self.feedback['final'])
# scrolling to have `reset` visible, otherwise it does not receive a click
# this is due to xblock workbench header that consumes top 40px - selenium scrolls so page so that target
# element is a the very top.
self.scroll_by(100)
reset = self._page.find_element_by_css_selector(".reset-button")
reset.click()
self.assertEqual(feedback_message.text, "Intro Feed")
self.assertEqual(self.get_element_html(feedback_message), self.feedback['intro'])
locations_after_reset = get_locations()
for item_key in items.keys():
self.assertDictEqual(locations_after_reset[item_key], initial_locations[item_key])
class CustomDataInteractionTest(InteractionTestFixture):
items_map = {
0: ItemDefinition(0, 'Zone A', "Yes A", "No A"),
1: ItemDefinition(1, 'Zone B', "Yes B", "No B"),
2: ItemDefinition(2, None, "", "No Zone for this")
}
all_zones = ['Zone A', 'Zone B']
feedback = {
"intro": "Other Intro Feed",
"final": "Other Final Feed"
}
def _get_scenario_xml(self):
return self._get_custom_scenario_xml("integration/data/test_data.json")
class CustomHtmlDataInteractionTest(InteractionTestFixture):
items_map = {
0: ItemDefinition(0, 'Zone <i>A</i>', "Yes <b>A</b>", "No <b>A</b>"),
1: ItemDefinition(1, 'Zone <b>B</b>', "Yes <i>B</i>", "No <i>B</i>"),
2: ItemDefinition(2, None, "", "No Zone for <i>X</i>")
}
self.assertDictEqual(self._get_item_by_value(0).location, item0_initial_position)
self.assertDictEqual(self._get_item_by_value(1).location, item1_initial_position)
all_zones = ['Zone <i>A</i>', 'Zone <b>B</b>']
feedback = {
"intro": "Intro <i>Feed</i>",
"final": "Final <b>Feed</b>"
}
def _get_scenario_xml(self):
return self._get_custom_scenario_xml("integration/data/test_html_data.json")
\ No newline at end of file
......@@ -17,15 +17,6 @@ class TestDragAndDropRender(BaseIntegrationTest):
self.browser.get(self.live_server_url)
self._page = self.go_to_page(self.PAGE_TITLE)
def _get_items(self):
items_container = self._page.find_element_by_css_selector('ul.items')
return items_container.find_elements_by_css_selector('li.option')
def _get_zones(self):
return self._page.find_elements_by_css_selector(".drag-container .zone")
def test_items(self):
items = self._get_items()
......
from xml.sax.saxutils import escape
from nose_parameterized import parameterized
from tests.integration.test_base import BaseIntegrationTest
from workbench import scenarios
......
import os
import json
import pkg_resources
from xml.sax.saxutils import escape
def load_resource(resource_path):
"""
Gets the content of a resource
"""
resource_content = pkg_resources.resource_string(__name__, resource_path)
return unicode(resource_content)
\ No newline at end of file
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