Commit dbbd3afd by Xavier Antoviaque

Merge pull request #23 from open-craft/eugeny/custom-data-integration-tests

Integration tests with custom data
parents 035fd327 95a914db
{
"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"
}
}
{
"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>"
}
}
# Imports ########################################################### # Imports ###########################################################
from xml.sax.saxutils import escape from xml.sax.saxutils import escape
from tests.utils import load_resource
from workbench import scenarios from workbench import scenarios
from workbench.test.selenium_test import SeleniumTest from workbench.test.selenium_test import SeleniumTest
...@@ -9,6 +10,11 @@ from workbench.test.selenium_test import SeleniumTest ...@@ -9,6 +10,11 @@ from workbench.test.selenium_test import SeleniumTest
class BaseIntegrationTest(SeleniumTest): class BaseIntegrationTest(SeleniumTest):
_additional_escapes = {
'"': "&quot;",
"'": "&apos;"
}
def setUp(self): def setUp(self):
super(BaseIntegrationTest, self).setUp() super(BaseIntegrationTest, self).setUp()
...@@ -30,10 +36,23 @@ class BaseIntegrationTest(SeleniumTest): ...@@ -30,10 +36,23 @@ class BaseIntegrationTest(SeleniumTest):
</vertical_demo> </vertical_demo>
""".format(display_name=escape(display_name), question_text=escape(question_text), completed=completed) """.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): def _add_scenario(self, identifier, title, xml):
scenarios.add_xml_scenario(identifier, title, xml) scenarios.add_xml_scenario(identifier, title, xml)
self.addCleanup(scenarios.remove_scenario, identifier) 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): def _get_feedback_message(self):
return self._page.find_element_by_css_selector(".feedback .message") return self._page.find_element_by_css_selector(".feedback .message")
...@@ -50,4 +69,7 @@ class BaseIntegrationTest(SeleniumTest): ...@@ -50,4 +69,7 @@ class BaseIntegrationTest(SeleniumTest):
return element.get_attribute('innerHTML').strip() return element.get_attribute('innerHTML').strip()
def get_element_classes(self, element): def get_element_classes(self, element):
return element.get_attribute('class').split() return element.get_attribute('class').split()
\ No newline at end of file
def scroll_to(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._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
from nose_parameterized import parameterized
from selenium.webdriver import ActionChains from selenium.webdriver import ActionChains
from selenium.webdriver.firefox import webdriver
from tests.integration.test_base import BaseIntegrationTest from tests.integration.test_base import BaseIntegrationTest
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 Verifying Drag and Drop XBlock rendering against default data - if default data changes this would probably broke
""" """
PAGE_TITLE = 'Drag and Drop v2' PAGE_TITLE = 'Drag and Drop v2'
PAGE_ID = '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_for_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): 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._add_scenario(self.PAGE_ID, self.PAGE_TITLE, scenario_xml)
self.browser.get(self.live_server_url) self.browser.get(self.live_server_url)
...@@ -37,44 +66,81 @@ class TestInteraction(BaseIntegrationTest): ...@@ -37,44 +66,81 @@ class TestInteraction(BaseIntegrationTest):
action_chains.drag_and_drop(element, target).perform() action_chains.drag_and_drop(element, target).perform()
@parameterized.expand([ def test_correct_item_positive_feedback(self):
("Item A", 0, 'Zone A', "Yes, it's an A"), feedback_popup = self._page.find_element_by_css_selector(".popup-content")
("Item B", 1, 'Zone B', "Yes, it's a B"), for definition in self._get_correct_item_for_zone().values():
]) self.drag_item_to_zone(definition.item_id, definition.zone_id)
def test_correct_item_positive_feedback(self, _, item_value, zone_id, expected_feedback): self.assertEqual(self.get_element_html(feedback_popup), definition.feedback_positive)
self.drag_item_to_zone(item_value, zone_id)
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([ for definition in self.items_map.values():
("Item A", 0, 'Zone B', "No, A does not belong here"), for zone in self.all_zones:
("Item B", 1, 'Zone A', "No, B does not belong here"), if zone == definition.zone_id:
("Item X", 2, 'Zone A', "You silly, there are no zones for X"), continue
("Item X", 2, 'Zone B', "You silly, there are no zones for X"), self.drag_item_to_zone(definition.item_id, zone)
]) self.assertEqual(self.get_element_html(feedback_popup), definition.feedback_negative)
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)
def test_final_feedback_and_reset(self): def test_final_feedback_and_reset(self):
feedback_message = self._get_feedback_message() 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_for_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 initial_locations = get_locations()
item1_initial_position = self._get_item_by_value(1).location
self.drag_item_to_zone(0, 'Zone A') for item_key, definition in items.items():
self.drag_item_to_zone(1, 'Zone B') 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_to(100)
reset = self._page.find_element_by_css_selector(".reset-button") reset = self._page.find_element_by_css_selector(".reset-button")
reset.click() 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) all_zones = ['Zone <i>A</i>', 'Zone <b>B</b>']
self.assertDictEqual(self._get_item_by_value(1).location, item1_initial_position)
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,14 +17,11 @@ class TestDragAndDropRender(BaseIntegrationTest): ...@@ -17,14 +17,11 @@ class TestDragAndDropRender(BaseIntegrationTest):
self.browser.get(self.live_server_url) self.browser.get(self.live_server_url)
self._page = self.go_to_page(self.PAGE_TITLE) self._page = self.go_to_page(self.PAGE_TITLE)
def _get_items(self): def _test_style(self, item, style_settings):
items_container = self._page.find_element_by_css_selector('ul.items') style = item.get_attribute('style')
return items_container.find_elements_by_css_selector('li.option') for style_prop, expected_value in style_settings.items():
expected = u"{0}: {1}".format(style_prop, expected_value)
def _get_zones(self): self.assertIn(expected, style)
return self._page.find_elements_by_css_selector(".drag-container .zone")
def test_items(self): def test_items(self):
items = self._get_items() items = self._get_items()
...@@ -33,18 +30,18 @@ class TestDragAndDropRender(BaseIntegrationTest): ...@@ -33,18 +30,18 @@ class TestDragAndDropRender(BaseIntegrationTest):
self.assertEqual(items[0].get_attribute('data-value'), '0') self.assertEqual(items[0].get_attribute('data-value'), '0')
self.assertEqual(items[0].text, 'A') self.assertEqual(items[0].text, 'A')
self.assertEqual(items[0].get_attribute('style'), u"width: 190px; height: auto;")
self.assertIn('ui-draggable', self.get_element_classes(items[0])) self.assertIn('ui-draggable', self.get_element_classes(items[0]))
self._test_style(items[0], {'width': '190px', 'height': 'auto'})
self.assertEqual(items[1].get_attribute('data-value'), '1') self.assertEqual(items[1].get_attribute('data-value'), '1')
self.assertEqual(items[1].text, 'B') self.assertEqual(items[1].text, 'B')
self.assertEqual(items[1].get_attribute('style'), u"width: 190px; height: auto;")
self.assertIn('ui-draggable', self.get_element_classes(items[1])) self.assertIn('ui-draggable', self.get_element_classes(items[1]))
self._test_style(items[1], {'width': '190px', 'height': 'auto'})
self.assertEqual(items[2].get_attribute('data-value'), '2') self.assertEqual(items[2].get_attribute('data-value'), '2')
self.assertEqual(items[2].text, 'X') self.assertEqual(items[2].text, 'X')
self.assertEqual(items[2].get_attribute('style'), u"width: 100px; height: 100px;")
self.assertIn('ui-draggable', self.get_element_classes(items[2])) self.assertIn('ui-draggable', self.get_element_classes(items[2]))
self._test_style(items[2], {'width': '100px', 'height': '100px'})
def test_zones(self): def test_zones(self):
zones = self._get_zones() zones = self._get_zones()
...@@ -52,12 +49,12 @@ class TestDragAndDropRender(BaseIntegrationTest): ...@@ -52,12 +49,12 @@ class TestDragAndDropRender(BaseIntegrationTest):
self.assertEqual(len(zones), 2) self.assertEqual(len(zones), 2)
self.assertEqual(zones[0].get_attribute('data-zone'), 'Zone A') self.assertEqual(zones[0].get_attribute('data-zone'), 'Zone A')
self.assertEqual(zones[0].get_attribute('style'), u"top: 200px; left: 120px; width: 200px; height: 100px;")
self.assertIn('ui-droppable', self.get_element_classes(zones[0])) self.assertIn('ui-droppable', self.get_element_classes(zones[0]))
self._test_style(zones[0], {'top': '200px', 'left': '120px', 'width': '200px', 'height': '100px'})
self.assertEqual(zones[1].get_attribute('data-zone'), 'Zone B') self.assertEqual(zones[1].get_attribute('data-zone'), 'Zone B')
self.assertEqual(zones[1].get_attribute('style'), u"top: 360px; left: 120px; width: 200px; height: 100px;")
self.assertIn('ui-droppable', self.get_element_classes(zones[1])) self.assertIn('ui-droppable', self.get_element_classes(zones[1]))
self._test_style(zones[1], {'top': '360px', 'left': '120px', 'width': '200px', 'height': '100px'})
def test_feedback(self): def test_feedback(self):
feedback_message = self._get_feedback_message() feedback_message = self._get_feedback_message()
......
from xml.sax.saxutils import escape
from nose_parameterized import parameterized from nose_parameterized import parameterized
from tests.integration.test_base import BaseIntegrationTest from tests.integration.test_base import BaseIntegrationTest
from workbench import scenarios from workbench import scenarios
__author__ = 'john'
class TestDragAndDropTitleAndQuestion(BaseIntegrationTest): class TestDragAndDropTitleAndQuestion(BaseIntegrationTest):
@parameterized.expand([ @parameterized.expand([
......
import pkg_resources
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