Commit 7818b709 by Jesse Zoldak

[WIP] headless bokchoy

parent 034ac1c8
......@@ -69,7 +69,7 @@ class CourseNavPage(PageObject):
Example:
go_to_section("Week 1", "Lesson 1")
"""
assert False, "Go to section fails in phantomjs"
# For test stability, disable JQuery animations (opening / closing menus)
self.browser.execute_script("jQuery.fx.off = true;")
......
......@@ -21,6 +21,7 @@ class CourseDiscoveryPage(PageObject):
loading_css = "#loading-indicator"
courses_css = '.courses-listing'
assert False, "Another loading indicator page"
return self.q(css=courses_css).visible \
and self.q(css=loading_css).present \
and not self.q(css=loading_css).visible
......
......@@ -101,6 +101,7 @@ class DiscussionThreadPage(PageObject, DiscussionPageMixin):
Clicks the add response button and ensures that the response text
field receives focus
"""
assert False, "Needs click add response button"
self._find_within(".add-response-btn").first.click()
EmptyPromise(
lambda: self._find_within(".discussion-reply-new textarea:focus").present,
......
......@@ -532,6 +532,7 @@ class EdxNoteHighlight(NoteChild):
Creates selection for the element and clicks `add note` button.
"""
ActionChains(self.browser).double_click(self.element).release().perform()
assert False, "Needs to wait for adder visibility"
self.wait_for_adder_visibility()
self.q(css=self._bounded_selector(self.ADDER_SELECTOR)).first.click()
self.wait_for_editor_visibility()
......
......@@ -194,6 +194,8 @@ class FieldsMixin(object):
"""
Get or set the value in a dropdown field.
"""
assert False, "This test uses value_for_dropdown_field"
self.wait_for_field(field_id)
self.make_field_editable(field_id)
......
......@@ -519,20 +519,20 @@ class CohortManagementSection(PageObject):
"""
Uploads a file with cohort assignment information.
"""
assert False, "This has a file upload and needs a browser"
# Toggle on the CSV upload section.
cvs_upload_toggle_css = '.toggle-cohort-management-secondary'
self.wait_for_element_visibility(cvs_upload_toggle_css, "Wait for csv upload link to appear")
cvs_upload_toggle = self.q(css=self._bounded_selector(cvs_upload_toggle_css)).first
if cvs_upload_toggle:
cvs_upload_toggle.click()
self.wait_for_element_visibility(
self._bounded_selector(self.csv_browse_button_selector_css),
'File upload link visible'
)
cvs_upload_toggle = self.q(css=self._bounded_selector(cvs_upload_toggle_css)).first.click()
self.wait_for_element_visibility(
self._bounded_selector(self.csv_browse_button_selector_css),
'File upload link visible'
)
path = InstructorDashboardPage.get_asset_path(filename)
file_input = self.q(css=self._bounded_selector(self.csv_browse_button_selector_css)).results[0]
file_input.send_keys(path)
self.q(css=self._bounded_selector(self.csv_upload_button_selector_css)).first.click()
upload_file_button = self.q(css=self._bounded_selector(self.csv_upload_button_selector_css)).first.results[0]
upload_file_button.click()
@property
def is_cohorted(self):
......
......@@ -135,6 +135,7 @@ class TeamsPage(CoursePage, BreadcrumbsMixin):
@property
def warning_message(self):
"""Return the text of the team warning message."""
assert False, "Warning message fails under phantomjs"
return self.q(css='.warning').results[0].text
......
......@@ -232,6 +232,7 @@ class ImportMixin(object):
"""
Wait for the upload to be confirmed.
"""
assert False, "Needs to wait for upload"
EmptyPromise(self.is_upload_finished, 'Upload Finished', timeout=30).fulfill()
def is_filename_error_showing(self):
......@@ -267,6 +268,7 @@ class ImportMixin(object):
"""
Wait for the upload field to display an error.
"""
assert False, "Needs to wait for filename error"
EmptyPromise(self.is_filename_error_showing, 'Upload Error Displayed', timeout=30).fulfill()
def finished_target_url(self):
......
......@@ -98,6 +98,7 @@ class LibraryEditPage(LibraryPage, PaginatedMixin, UsersPageMixin):
"""
Click on the delete button for the given XBlock
"""
assert False, "Click delete button fails under phantomjs"
self._action_btn_for_xblock_id(xblock_id, "delete").click()
if confirm:
confirm_prompt(self) # this will also wait_for_notification()
......
......@@ -425,6 +425,7 @@ class CourseOutlinePage(CoursePage, CourseOutlineContainer):
BOTTOM_ADD_SECTION_BUTTON = '.outline > .add-section .button-new'
def is_browser_on_page(self):
assert False, "This test visits the course outline page"
return self.q(css='body.view-outline').present and self.q(css='div.ui-loading.is-hidden').present
def view_live(self):
......
......@@ -487,6 +487,8 @@ class Signatory(object):
Opens upload image dialog and upload given image file.
"""
self.wait_for_signature_image_upload_button()
# TODO fix this!
assert False, "This test needs to upload a signature"
self.find_css('.action-upload-signature').first.click()
self.find_css('.action-upload-signature').first.click()
self.wait_for_signature_image_upload_prompt()
......
......@@ -19,6 +19,7 @@ class GroupConfigurationsPage(CoursePage):
"""
Verify that the browser is on the page and it is not still loading.
"""
assert False, "This test visits the group configurations page"
EmptyPromise(
lambda: self.q(css='body.view-group-configurations').present,
'On the group configuration page'
......
......@@ -48,6 +48,7 @@ class TextbooksPage(CoursePage):
self.wait_for_element_visibility(".upload-dialog input", "Upload modal opened")
file_input = self.q(css=".upload-dialog input").results[0]
file_input.send_keys(file_path)
assert False, "Needs to upload a file from a modal"
click_css(self, ".wrapper-modal-window-assetupload .action-upload", require_notification=False)
self.wait_for_element_absence(".modal-window-overlay", "Upload modal closed")
......
......@@ -62,6 +62,7 @@ class CohortConfigurationTest(EventsTestMixin, UniqueCourseTest, CohortTestMixin
# go to the membership page on the instructor dashboard
self.instructor_dashboard_page = InstructorDashboardPage(self.browser, self.course_id)
self.instructor_dashboard_page.visit()
assert False, "This test should have visited the instructor dashboard page"
self.cohort_management_page = self.instructor_dashboard_page.select_cohort_management()
def verify_cohort_description(self, cohort_name, expected_description):
......
......@@ -26,6 +26,7 @@ class NonCohortedDiscussionTestMixin(BaseDiscussionMixin):
pass
def test_non_cohort_visibility_label(self):
assert False, "Visibility label fails in phantomjs"
self.setup_thread(1)
self.assertEquals(self.thread_page.get_group_visibility_label(), "This post is visible to everyone.")
......@@ -43,6 +44,7 @@ class CohortedDiscussionTestMixin(BaseDiscussionMixin, CohortTestMixin):
self.cohort_1_id = self.add_manual_cohort(self.course_fixture, self.cohort_1_name)
def test_cohort_visibility_label(self):
assert False, "Visibility label fails in phantomjs"
# Must be moderator to view content in a cohort other than your own
AutoAuthPage(self.browser, course_id=self.course_id, roles="Moderator").visit()
self.thread_id = self.setup_thread(1, group_id=self.cohort_1_id)
......
......@@ -606,6 +606,7 @@ class InlineDiscussionTest(UniqueCourseTest, DiscussionResponsePaginationTestMix
"""
def setUp(self):
assert False, "Inline discussions fail under phantomjs"
super(InlineDiscussionTest, self).setUp()
self.thread_ids = []
self.discussion_id = "test_discussion_{}".format(uuid4().hex)
......
......@@ -286,6 +286,7 @@ def get_modal_alert(browser):
Returns instance of modal alert box shown in browser after waiting
for 6 seconds
"""
assert False, "These test conditions show a mobile alert"
WebDriverWait(browser, 6).until(EC.alert_is_present())
return browser.switch_to.alert
......
......@@ -202,6 +202,7 @@ class AccountSettingsPageTest(AccountSettingsTestMixin, WebAppTest):
"""
Test behaviour of a text field.
"""
assert False, "This test refreshes the browser"
self.assertEqual(self.account_settings_page.title_for_field(field_id), title)
self.assertEqual(self.account_settings_page.value_for_text_field(field_id), initial_value)
......
......@@ -847,7 +847,7 @@ class VisibleToStaffOnlyTest(UniqueCourseTest):
self.courseware_page.visit()
self.assertEqual(3, len(self.course_nav.sections['Test Section']))
self.course_nav.go_to_section("Test Section", "Subsection With Locked Unit")
self.course_nav.go_to_section("Test Section", "Subsection With Locked Unit") # TODO: fails under phantom
self.assertEqual(["Html Child in locked unit", "Html Child in unlocked unit"], self.course_nav.sequence_items)
self.course_nav.go_to_section("Test Section", "Unlocked Subsection")
......@@ -869,7 +869,7 @@ class VisibleToStaffOnlyTest(UniqueCourseTest):
self.courseware_page.visit()
self.assertEqual(2, len(self.course_nav.sections['Test Section']))
self.course_nav.go_to_section("Test Section", "Subsection With Locked Unit")
self.course_nav.go_to_section("Test Section", "Subsection With Locked Unit") # TODO: fails under phantom
self.assertEqual(["Html Child in unlocked unit"], self.course_nav.sequence_items)
self.course_nav.go_to_section("Test Section", "Unlocked Subsection")
......
......@@ -76,7 +76,7 @@ class CoursewareSearchCohortTest(ContainerBase):
# Enable Cohorting and assign cohorts and content groups
self._auto_auth(self.staff_user["username"], self.staff_user["email"], True)
self.enable_cohorting(self.course_fixture)
self.create_content_groups()
self.create_content_groups() # TODO: this fails under phantomjs
self.link_html_to_content_groups_and_publish()
self.create_cohorts_and_assign_students()
......
......@@ -75,6 +75,7 @@ class CourseDiscoveryTest(WebAppTest):
Make sure you can search for courses.
"""
self.page.visit()
assert False, "Needed to visit the page"
self.assertEqual(len(self.page.result_items), 12)
self.page.search("grass")
......
......@@ -53,7 +53,7 @@ class SplitTestCoursewareSearchTest(ContainerBase):
)
self._add_and_configure_split_test()
self._studio_reindex()
self._studio_reindex() # TODO: this fails under phantomjs
def _auto_auth(self, username, email, staff):
"""
......
......@@ -355,6 +355,8 @@ class CourseWithContentGroupsTest(StaffViewTest):
lambda: cohort_name == cohort_management_page.get_selected_cohort(), "Waiting for new cohort"
).fulfill()
cohort_management_page.add_students_to_selected_cohort([student])
assert False, "Need to add a cohort"
add_cohort_with_student("Cohort Alpha", "alpha", student_a_username)
add_cohort_with_student("Cohort Beta", "beta", student_b_username)
cohort_management_page.wait_for_ajax()
......
......@@ -885,6 +885,7 @@ class BrowseTeamsWithinTopicTest(TeamsTabBase):
'language': 'aa',
'country': 'AF'
})
assert False, "This test shows a modal alert"
with self.assertRaises(TimeoutException):
self.browser.get(self.browse_teams_page.url)
alert = get_modal_alert(self.browser)
......
"""
Acceptance tests for Studio related to the asset index page.
"""
from flaky import flaky
from nose.plugins.attrib import attr
from ...pages.studio.asset_index import AssetIndexPage
......@@ -15,8 +15,11 @@ class AssetIndexTest(StudioCourseTest):
"""
Tests for the Asset index page.
"""
def setUp(self, is_staff=False):
# TODO: visit fails under phantom.
# Probably same root cause as the flakiness.
assert False, "Visiting the asset page fails in phantomjs"
super(AssetIndexTest, self).setUp()
self.asset_page = AssetIndexPage(
self.browser,
......
......@@ -39,6 +39,7 @@ class CreateLibraryTest(WebAppTest):
self.auth_page.visit()
self.dashboard_page.visit()
assert False, "Needed to visit the dashboard page"
self.assertFalse(self.dashboard_page.has_library(name=name, org=org, number=number))
self.assertTrue(self.dashboard_page.has_new_library_button())
......
......@@ -68,7 +68,7 @@ class LibraryEditPageTest(StudioLibraryTest):
self.assertNotEqual(first_block_id, second_block_id)
# Delete the first block:
self.lib_page.click_delete_button(first_block_id, confirm=True)
self.lib_page.click_delete_button(first_block_id, confirm=True) # TODO: fails in phantomjs
self.assertEqual(len(self.lib_page.xblocks), 1)
self.assertEqual(self.lib_page.xblocks[0].locator, second_block_id)
......
"""
Acceptance tests for Studio related to course reruns.
"""
import random
from bok_choy.promise import EmptyPromise
from nose.plugins.attrib import attr
from nose.tools import assert_in
from ...pages.studio.index import DashboardPage
......@@ -72,7 +72,8 @@ class CourseRerunTest(StudioCourseTest):
self.dashboard_page.create_rerun(self.course_info['display_name'])
rerun_page = CourseRerunPage(self.browser, *course_info)
rerun_page.wait_for_page()
assert False, "Wait for page fails under phantomjs"
rerun_page.wait_for_page() # TODO this fails under phantom
course_run = 'test_rerun_' + str(random.randrange(1000000, 9999999))
rerun_page.course_run = course_run
rerun_page.create_rerun()
......
......@@ -98,6 +98,7 @@ class EndToEndCohortedCoursewareTest(ContainerBase):
self.course_info['number'],
self.course_info['run']
)
assert False, "This test needs to visit the group configurations page"
group_configurations_page.visit()
group_configurations_page.create_first_content_group()
......
......@@ -42,6 +42,7 @@ class VideoBaseTest(UniqueCourseTest):
"""
Initialization of pages and course fixture for video tests
"""
assert False, "Video tests fail under phantomjs"
super(VideoBaseTest, self).setUp()
self.video = VideoPage(self.browser)
......
......@@ -148,31 +148,31 @@ END
;;
"1")
paver test_bokchoy --extra_args="-a shard_1 --with-flaky"
SELENIUM_BROWSER=phantomjs paver test_bokchoy --extra_args="-a shard_1 --with-flaky"
;;
"2")
paver test_bokchoy --extra_args="-a 'shard_2' --with-flaky"
SELENIUM_BROWSER=phantomjs paver test_bokchoy --extra_args="-a 'shard_2' --with-flaky"
;;
"3")
paver test_bokchoy --extra_args="-a 'shard_3' --with-flaky"
SELENIUM_BROWSER=phantomjs paver test_bokchoy --extra_args="-a 'shard_3' --with-flaky"
;;
"4")
paver test_bokchoy --extra_args="-a 'shard_4' --with-flaky"
SELENIUM_BROWSER=phantomjs paver test_bokchoy --extra_args="-a 'shard_4' --with-flaky"
;;
"5")
paver test_bokchoy --extra_args="-a 'shard_5' --with-flaky"
SELENIUM_BROWSER=phantomjs paver test_bokchoy --extra_args="-a 'shard_5' --with-flaky"
;;
"6")
paver test_bokchoy --extra_args="-a 'shard_6' --with-flaky"
SELENIUM_BROWSER=phantomjs paver test_bokchoy --extra_args="-a 'shard_6' --with-flaky"
;;
"7")
paver test_bokchoy --extra_args="-a shard_1=False,shard_2=False,shard_3=False,shard_4=False,shard_5=False,shard_6=False,a11y=False --with-flaky"
SELENIUM_BROWSER=phantomjs paver test_bokchoy --extra_args="-a shard_1=False,shard_2=False,shard_3=False,shard_4=False,shard_5=False,shard_6=False,a11y=False --with-flaky"
;;
# Default case because if we later define another bok-choy shard on Jenkins
......
"""
htmlwriter.py
$ python htmlwriter.py reports/ > test_summary.html
"""
import collections
import os
import sys
import textwrap
from xml.sax.saxutils import escape
from lxml import etree
class HtmlOutlineWriter(object):
HEAD = textwrap.dedent(r"""
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<style>
html {
font-family: sans-serif;
}
.toggle-box {
display: none;
}
.toggle-box + label {
cursor: pointer;
display: block;
line-height: 21px;
margin-bottom: 5px;
}
.toggle-box + label + div {
display: none;
margin-bottom: 10px;
}
.toggle-box:checked + label + div {
display: block;
}
.toggle-box + label:before {
color: #888;
content: "\25B8";
display: block;
float: left;
height: 20px;
line-height: 20px;
margin-right: 5px;
text-align: center;
width: 20px;
}
.toggle-box:checked + label:before {
content: "\25BE";
}
.error, .skipped {
margin-left: 2em;
}
.count {
font-weight: bold;
}
.test {
margin-left: 2em;
}
.stdout {
margin-left: 2em;
font-family: Consolas, monospace;
}
</style>
</head>
<body>
""")
SECTION_START = textwrap.dedent(u"""\
<div class="{klass}">
<input class="toggle-box {klass}" id="sect_{id:05d}" type="checkbox">
<label for="sect_{id:05d}">{html}</label>
<div>
""")
SECTION_END = "</div></div>"
def __init__(self, fout):
self.fout = fout
self.section_id = 0
self.fout.write(self.HEAD)
def start_section(self, html, klass=None):
self.fout.write(self.SECTION_START.format(
id=self.section_id, html=html, klass=klass or "",
).encode("utf8"))
self.section_id += 1
def end_section(self):
self.fout.write(self.SECTION_END)
def write(self, html):
self.fout.write(html.encode("utf8"))
class Summable(object):
"""An object whose attributes can be added together easily.
Subclass this and define `fields` on your derived class.
"""
def __init__(self):
for name in self.fields:
setattr(self, name, 0)
@classmethod
def from_element(cls, element):
"""Construct a Summable from an xml element with the same attributes."""
self = cls()
for name in self.fields:
setattr(self, name, int(element.get(name)))
return self
def __add__(self, other):
result = type(self)()
for name in self.fields:
setattr(result, name, getattr(self, name) + getattr(other, name))
return result
class TestResults(Summable):
"""A test result, makeable from a nosetests.xml <testsuite> element."""
fields = ["tests", "errors", "failures", "skip"]
def __str__(self):
msg = "{0.tests:4d} tests, {0.errors} errors, {0.failures} failures, {0.skip} skipped"
return msg.format(self)
def error_line_from_error_element(element):
"""Given an <error> element, get the important error line from it."""
return element.get("message").splitlines()[0]
def testcase_name(testcase):
"""Given a <testcase> element, return the name of the test."""
return "{classname}.{name}".format(
classname=testcase.get("classname"),
name=testcase.get("name"),
)
def error_line_from_error_element(element):
"""Given an <error> element, get the important error line from it."""
line = element.get("type")
message_lines = element.get("message").splitlines()
if message_lines:
first_line = message_lines[0].strip()
else:
first_line = ""
if first_line:
line += ": " + first_line
return line
def testcase_name(testcase):
"""Given a <testcase> element, return the name of the test."""
return "{classname}.{name}".format(
classname=testcase.get("classname"),
name=testcase.get("name"),
)
def clipped(text, maxlength=150):
"""Return the text, but at most `maxlength` characters."""
if len(text) > maxlength:
text = text[:maxlength-1] + u"\N{HORIZONTAL ELLIPSIS}"
return text
def report_file(path, html_writer):
"""Report on one nosetests.xml file."""
with open(path) as xml_file:
tree = etree.parse(xml_file) # pylint: disable=no-member
suite = tree.xpath("/testsuite")[0]
results = TestResults.from_element(suite)
html = u'<span class="count">{number}:</span> {path}: {results}'.format(
path=escape(path),
results=results,
number=results.errors+results.failures,
)
html_writer.start_section(html, klass="file")
errors = collections.defaultdict(list)
for element in tree.xpath(".//error|.//failure"):
error_line = error_line_from_error_element(element)
testcases = element.xpath("..")
if testcases:
errors[error_line].append(testcases[0])
errors = sorted(errors.items(), key=lambda kv: len(kv[1]), reverse=True)
for message, testcases in errors:
html = u'<span class="count">{0:d}:</span> {1}'.format(len(testcases), escape(clipped(message)))
html_writer.start_section(html, klass="error")
for testcase in testcases:
html_writer.start_section(escape(testcase_name(testcase)), klass="test")
error_element = testcase.xpath("error|failure")[0]
html_writer.write("""<pre class="stdout">""")
html_writer.write(escape(error_element.get("message")))
html_writer.write(u"\n"+escape(error_element.text))
html_writer.write("</pre>")
html_writer.end_section()
html_writer.end_section()
skipped = collections.defaultdict(list)
for element in tree.xpath(".//skipped"):
error_line = error_line_from_error_element(element)
testcases = element.xpath("..")
if testcases:
skipped[error_line].append(testcases[0])
if skipped:
total = sum(len(v) for v in skipped.values())
html_writer.start_section(u'<span class="count">{0:d}:</span> Skipped'.format(total), klass="skipped")
skipped = sorted(skipped.items(), key=lambda kv: len(kv[1]), reverse=True)
for message, testcases in skipped:
html = u'<span class="count">{0:d}:</span> {1}'.format(len(testcases), escape(clipped(message)))
html_writer.start_section(html, klass="error")
for testcase in testcases:
html_writer.write('<div>{}</div>'.format(escape(testcase_name(testcase))))
html_writer.end_section()
html_writer.end_section()
html_writer.end_section()
return results
def main(start):
totals = TestResults()
html_writer = HtmlOutlineWriter(sys.stdout)
for dirpath, _, filenames in os.walk(start):
if "xunit.xml" in filenames:
results = report_file(os.path.join(dirpath, "xunit.xml"), html_writer)
totals += results
html_writer.write(escape(str(totals)))
def tryit():
w = HtmlOutlineWriter(sys.stdout)
for f in range(3):
w.start_section("File foo{}.xml".format(f))
w.write("this is about foo")
for err in range(5):
w.start_section("error {}".format(err))
w.write("ugh")
w.end_section()
w.end_section()
if __name__ == "__main__":
main(sys.argv[1])
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