Commit 7818b709 by Jesse Zoldak

[WIP] headless bokchoy

parent 034ac1c8
...@@ -69,7 +69,7 @@ class CourseNavPage(PageObject): ...@@ -69,7 +69,7 @@ class CourseNavPage(PageObject):
Example: Example:
go_to_section("Week 1", "Lesson 1") go_to_section("Week 1", "Lesson 1")
""" """
assert False, "Go to section fails in phantomjs"
# For test stability, disable JQuery animations (opening / closing menus) # For test stability, disable JQuery animations (opening / closing menus)
self.browser.execute_script("jQuery.fx.off = true;") self.browser.execute_script("jQuery.fx.off = true;")
......
...@@ -21,6 +21,7 @@ class CourseDiscoveryPage(PageObject): ...@@ -21,6 +21,7 @@ class CourseDiscoveryPage(PageObject):
loading_css = "#loading-indicator" loading_css = "#loading-indicator"
courses_css = '.courses-listing' courses_css = '.courses-listing'
assert False, "Another loading indicator page"
return self.q(css=courses_css).visible \ return self.q(css=courses_css).visible \
and self.q(css=loading_css).present \ and self.q(css=loading_css).present \
and not self.q(css=loading_css).visible and not self.q(css=loading_css).visible
......
...@@ -101,6 +101,7 @@ class DiscussionThreadPage(PageObject, DiscussionPageMixin): ...@@ -101,6 +101,7 @@ class DiscussionThreadPage(PageObject, DiscussionPageMixin):
Clicks the add response button and ensures that the response text Clicks the add response button and ensures that the response text
field receives focus field receives focus
""" """
assert False, "Needs click add response button"
self._find_within(".add-response-btn").first.click() self._find_within(".add-response-btn").first.click()
EmptyPromise( EmptyPromise(
lambda: self._find_within(".discussion-reply-new textarea:focus").present, lambda: self._find_within(".discussion-reply-new textarea:focus").present,
......
...@@ -532,6 +532,7 @@ class EdxNoteHighlight(NoteChild): ...@@ -532,6 +532,7 @@ class EdxNoteHighlight(NoteChild):
Creates selection for the element and clicks `add note` button. Creates selection for the element and clicks `add note` button.
""" """
ActionChains(self.browser).double_click(self.element).release().perform() ActionChains(self.browser).double_click(self.element).release().perform()
assert False, "Needs to wait for adder visibility"
self.wait_for_adder_visibility() self.wait_for_adder_visibility()
self.q(css=self._bounded_selector(self.ADDER_SELECTOR)).first.click() self.q(css=self._bounded_selector(self.ADDER_SELECTOR)).first.click()
self.wait_for_editor_visibility() self.wait_for_editor_visibility()
......
...@@ -194,6 +194,8 @@ class FieldsMixin(object): ...@@ -194,6 +194,8 @@ class FieldsMixin(object):
""" """
Get or set the value in a dropdown field. 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.wait_for_field(field_id)
self.make_field_editable(field_id) self.make_field_editable(field_id)
......
...@@ -519,20 +519,20 @@ class CohortManagementSection(PageObject): ...@@ -519,20 +519,20 @@ class CohortManagementSection(PageObject):
""" """
Uploads a file with cohort assignment information. Uploads a file with cohort assignment information.
""" """
assert False, "This has a file upload and needs a browser"
# Toggle on the CSV upload section. # Toggle on the CSV upload section.
cvs_upload_toggle_css = '.toggle-cohort-management-secondary' cvs_upload_toggle_css = '.toggle-cohort-management-secondary'
self.wait_for_element_visibility(cvs_upload_toggle_css, "Wait for csv upload link to appear") 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 cvs_upload_toggle = self.q(css=self._bounded_selector(cvs_upload_toggle_css)).first.click()
if cvs_upload_toggle: self.wait_for_element_visibility(
cvs_upload_toggle.click() self._bounded_selector(self.csv_browse_button_selector_css),
self.wait_for_element_visibility( 'File upload link visible'
self._bounded_selector(self.csv_browse_button_selector_css), )
'File upload link visible'
)
path = InstructorDashboardPage.get_asset_path(filename) path = InstructorDashboardPage.get_asset_path(filename)
file_input = self.q(css=self._bounded_selector(self.csv_browse_button_selector_css)).results[0] file_input = self.q(css=self._bounded_selector(self.csv_browse_button_selector_css)).results[0]
file_input.send_keys(path) 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 @property
def is_cohorted(self): def is_cohorted(self):
......
...@@ -135,6 +135,7 @@ class TeamsPage(CoursePage, BreadcrumbsMixin): ...@@ -135,6 +135,7 @@ class TeamsPage(CoursePage, BreadcrumbsMixin):
@property @property
def warning_message(self): def warning_message(self):
"""Return the text of the team warning message.""" """Return the text of the team warning message."""
assert False, "Warning message fails under phantomjs"
return self.q(css='.warning').results[0].text return self.q(css='.warning').results[0].text
......
...@@ -232,6 +232,7 @@ class ImportMixin(object): ...@@ -232,6 +232,7 @@ class ImportMixin(object):
""" """
Wait for the upload to be confirmed. Wait for the upload to be confirmed.
""" """
assert False, "Needs to wait for upload"
EmptyPromise(self.is_upload_finished, 'Upload Finished', timeout=30).fulfill() EmptyPromise(self.is_upload_finished, 'Upload Finished', timeout=30).fulfill()
def is_filename_error_showing(self): def is_filename_error_showing(self):
...@@ -267,6 +268,7 @@ class ImportMixin(object): ...@@ -267,6 +268,7 @@ class ImportMixin(object):
""" """
Wait for the upload field to display an error. 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() EmptyPromise(self.is_filename_error_showing, 'Upload Error Displayed', timeout=30).fulfill()
def finished_target_url(self): def finished_target_url(self):
......
...@@ -98,6 +98,7 @@ class LibraryEditPage(LibraryPage, PaginatedMixin, UsersPageMixin): ...@@ -98,6 +98,7 @@ class LibraryEditPage(LibraryPage, PaginatedMixin, UsersPageMixin):
""" """
Click on the delete button for the given XBlock 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() self._action_btn_for_xblock_id(xblock_id, "delete").click()
if confirm: if confirm:
confirm_prompt(self) # this will also wait_for_notification() confirm_prompt(self) # this will also wait_for_notification()
......
...@@ -425,6 +425,7 @@ class CourseOutlinePage(CoursePage, CourseOutlineContainer): ...@@ -425,6 +425,7 @@ class CourseOutlinePage(CoursePage, CourseOutlineContainer):
BOTTOM_ADD_SECTION_BUTTON = '.outline > .add-section .button-new' BOTTOM_ADD_SECTION_BUTTON = '.outline > .add-section .button-new'
def is_browser_on_page(self): 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 return self.q(css='body.view-outline').present and self.q(css='div.ui-loading.is-hidden').present
def view_live(self): def view_live(self):
......
...@@ -487,6 +487,8 @@ class Signatory(object): ...@@ -487,6 +487,8 @@ class Signatory(object):
Opens upload image dialog and upload given image file. Opens upload image dialog and upload given image file.
""" """
self.wait_for_signature_image_upload_button() 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.find_css('.action-upload-signature').first.click() self.find_css('.action-upload-signature').first.click()
self.wait_for_signature_image_upload_prompt() self.wait_for_signature_image_upload_prompt()
......
...@@ -19,6 +19,7 @@ class GroupConfigurationsPage(CoursePage): ...@@ -19,6 +19,7 @@ class GroupConfigurationsPage(CoursePage):
""" """
Verify that the browser is on the page and it is not still loading. Verify that the browser is on the page and it is not still loading.
""" """
assert False, "This test visits the group configurations page"
EmptyPromise( EmptyPromise(
lambda: self.q(css='body.view-group-configurations').present, lambda: self.q(css='body.view-group-configurations').present,
'On the group configuration page' 'On the group configuration page'
......
...@@ -48,6 +48,7 @@ class TextbooksPage(CoursePage): ...@@ -48,6 +48,7 @@ class TextbooksPage(CoursePage):
self.wait_for_element_visibility(".upload-dialog input", "Upload modal opened") self.wait_for_element_visibility(".upload-dialog input", "Upload modal opened")
file_input = self.q(css=".upload-dialog input").results[0] file_input = self.q(css=".upload-dialog input").results[0]
file_input.send_keys(file_path) 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) click_css(self, ".wrapper-modal-window-assetupload .action-upload", require_notification=False)
self.wait_for_element_absence(".modal-window-overlay", "Upload modal closed") self.wait_for_element_absence(".modal-window-overlay", "Upload modal closed")
......
...@@ -62,6 +62,7 @@ class CohortConfigurationTest(EventsTestMixin, UniqueCourseTest, CohortTestMixin ...@@ -62,6 +62,7 @@ class CohortConfigurationTest(EventsTestMixin, UniqueCourseTest, CohortTestMixin
# go to the membership page on the instructor dashboard # go to the membership page on the instructor dashboard
self.instructor_dashboard_page = InstructorDashboardPage(self.browser, self.course_id) self.instructor_dashboard_page = InstructorDashboardPage(self.browser, self.course_id)
self.instructor_dashboard_page.visit() 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() self.cohort_management_page = self.instructor_dashboard_page.select_cohort_management()
def verify_cohort_description(self, cohort_name, expected_description): def verify_cohort_description(self, cohort_name, expected_description):
......
...@@ -26,6 +26,7 @@ class NonCohortedDiscussionTestMixin(BaseDiscussionMixin): ...@@ -26,6 +26,7 @@ class NonCohortedDiscussionTestMixin(BaseDiscussionMixin):
pass pass
def test_non_cohort_visibility_label(self): def test_non_cohort_visibility_label(self):
assert False, "Visibility label fails in phantomjs"
self.setup_thread(1) self.setup_thread(1)
self.assertEquals(self.thread_page.get_group_visibility_label(), "This post is visible to everyone.") self.assertEquals(self.thread_page.get_group_visibility_label(), "This post is visible to everyone.")
...@@ -43,6 +44,7 @@ class CohortedDiscussionTestMixin(BaseDiscussionMixin, CohortTestMixin): ...@@ -43,6 +44,7 @@ class CohortedDiscussionTestMixin(BaseDiscussionMixin, CohortTestMixin):
self.cohort_1_id = self.add_manual_cohort(self.course_fixture, self.cohort_1_name) self.cohort_1_id = self.add_manual_cohort(self.course_fixture, self.cohort_1_name)
def test_cohort_visibility_label(self): 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 # Must be moderator to view content in a cohort other than your own
AutoAuthPage(self.browser, course_id=self.course_id, roles="Moderator").visit() AutoAuthPage(self.browser, course_id=self.course_id, roles="Moderator").visit()
self.thread_id = self.setup_thread(1, group_id=self.cohort_1_id) self.thread_id = self.setup_thread(1, group_id=self.cohort_1_id)
......
...@@ -606,6 +606,7 @@ class InlineDiscussionTest(UniqueCourseTest, DiscussionResponsePaginationTestMix ...@@ -606,6 +606,7 @@ class InlineDiscussionTest(UniqueCourseTest, DiscussionResponsePaginationTestMix
""" """
def setUp(self): def setUp(self):
assert False, "Inline discussions fail under phantomjs"
super(InlineDiscussionTest, self).setUp() super(InlineDiscussionTest, self).setUp()
self.thread_ids = [] self.thread_ids = []
self.discussion_id = "test_discussion_{}".format(uuid4().hex) self.discussion_id = "test_discussion_{}".format(uuid4().hex)
......
...@@ -286,6 +286,7 @@ def get_modal_alert(browser): ...@@ -286,6 +286,7 @@ def get_modal_alert(browser):
Returns instance of modal alert box shown in browser after waiting Returns instance of modal alert box shown in browser after waiting
for 6 seconds for 6 seconds
""" """
assert False, "These test conditions show a mobile alert"
WebDriverWait(browser, 6).until(EC.alert_is_present()) WebDriverWait(browser, 6).until(EC.alert_is_present())
return browser.switch_to.alert return browser.switch_to.alert
......
...@@ -202,6 +202,7 @@ class AccountSettingsPageTest(AccountSettingsTestMixin, WebAppTest): ...@@ -202,6 +202,7 @@ class AccountSettingsPageTest(AccountSettingsTestMixin, WebAppTest):
""" """
Test behaviour of a text field. 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.title_for_field(field_id), title)
self.assertEqual(self.account_settings_page.value_for_text_field(field_id), initial_value) self.assertEqual(self.account_settings_page.value_for_text_field(field_id), initial_value)
......
...@@ -847,7 +847,7 @@ class VisibleToStaffOnlyTest(UniqueCourseTest): ...@@ -847,7 +847,7 @@ class VisibleToStaffOnlyTest(UniqueCourseTest):
self.courseware_page.visit() self.courseware_page.visit()
self.assertEqual(3, len(self.course_nav.sections['Test Section'])) 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.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") self.course_nav.go_to_section("Test Section", "Unlocked Subsection")
...@@ -869,7 +869,7 @@ class VisibleToStaffOnlyTest(UniqueCourseTest): ...@@ -869,7 +869,7 @@ class VisibleToStaffOnlyTest(UniqueCourseTest):
self.courseware_page.visit() self.courseware_page.visit()
self.assertEqual(2, len(self.course_nav.sections['Test Section'])) 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.assertEqual(["Html Child in unlocked unit"], self.course_nav.sequence_items)
self.course_nav.go_to_section("Test Section", "Unlocked Subsection") self.course_nav.go_to_section("Test Section", "Unlocked Subsection")
......
...@@ -76,7 +76,7 @@ class CoursewareSearchCohortTest(ContainerBase): ...@@ -76,7 +76,7 @@ class CoursewareSearchCohortTest(ContainerBase):
# Enable Cohorting and assign cohorts and content groups # Enable Cohorting and assign cohorts and content groups
self._auto_auth(self.staff_user["username"], self.staff_user["email"], True) self._auto_auth(self.staff_user["username"], self.staff_user["email"], True)
self.enable_cohorting(self.course_fixture) 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.link_html_to_content_groups_and_publish()
self.create_cohorts_and_assign_students() self.create_cohorts_and_assign_students()
......
...@@ -75,6 +75,7 @@ class CourseDiscoveryTest(WebAppTest): ...@@ -75,6 +75,7 @@ class CourseDiscoveryTest(WebAppTest):
Make sure you can search for courses. Make sure you can search for courses.
""" """
self.page.visit() self.page.visit()
assert False, "Needed to visit the page"
self.assertEqual(len(self.page.result_items), 12) self.assertEqual(len(self.page.result_items), 12)
self.page.search("grass") self.page.search("grass")
......
...@@ -53,7 +53,7 @@ class SplitTestCoursewareSearchTest(ContainerBase): ...@@ -53,7 +53,7 @@ class SplitTestCoursewareSearchTest(ContainerBase):
) )
self._add_and_configure_split_test() self._add_and_configure_split_test()
self._studio_reindex() self._studio_reindex() # TODO: this fails under phantomjs
def _auto_auth(self, username, email, staff): def _auto_auth(self, username, email, staff):
""" """
......
...@@ -355,6 +355,8 @@ class CourseWithContentGroupsTest(StaffViewTest): ...@@ -355,6 +355,8 @@ class CourseWithContentGroupsTest(StaffViewTest):
lambda: cohort_name == cohort_management_page.get_selected_cohort(), "Waiting for new cohort" lambda: cohort_name == cohort_management_page.get_selected_cohort(), "Waiting for new cohort"
).fulfill() ).fulfill()
cohort_management_page.add_students_to_selected_cohort([student]) 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 Alpha", "alpha", student_a_username)
add_cohort_with_student("Cohort Beta", "beta", student_b_username) add_cohort_with_student("Cohort Beta", "beta", student_b_username)
cohort_management_page.wait_for_ajax() cohort_management_page.wait_for_ajax()
......
...@@ -885,6 +885,7 @@ class BrowseTeamsWithinTopicTest(TeamsTabBase): ...@@ -885,6 +885,7 @@ class BrowseTeamsWithinTopicTest(TeamsTabBase):
'language': 'aa', 'language': 'aa',
'country': 'AF' 'country': 'AF'
}) })
assert False, "This test shows a modal alert"
with self.assertRaises(TimeoutException): with self.assertRaises(TimeoutException):
self.browser.get(self.browse_teams_page.url) self.browser.get(self.browse_teams_page.url)
alert = get_modal_alert(self.browser) alert = get_modal_alert(self.browser)
......
""" """
Acceptance tests for Studio related to the asset index page. Acceptance tests for Studio related to the asset index page.
""" """
from flaky import flaky from flaky import flaky
from nose.plugins.attrib import attr
from ...pages.studio.asset_index import AssetIndexPage from ...pages.studio.asset_index import AssetIndexPage
...@@ -15,8 +15,11 @@ class AssetIndexTest(StudioCourseTest): ...@@ -15,8 +15,11 @@ class AssetIndexTest(StudioCourseTest):
""" """
Tests for the Asset index page. Tests for the Asset index page.
""" """
def setUp(self, is_staff=False): 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() super(AssetIndexTest, self).setUp()
self.asset_page = AssetIndexPage( self.asset_page = AssetIndexPage(
self.browser, self.browser,
......
...@@ -39,6 +39,7 @@ class CreateLibraryTest(WebAppTest): ...@@ -39,6 +39,7 @@ class CreateLibraryTest(WebAppTest):
self.auth_page.visit() self.auth_page.visit()
self.dashboard_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.assertFalse(self.dashboard_page.has_library(name=name, org=org, number=number))
self.assertTrue(self.dashboard_page.has_new_library_button()) self.assertTrue(self.dashboard_page.has_new_library_button())
......
...@@ -68,7 +68,7 @@ class LibraryEditPageTest(StudioLibraryTest): ...@@ -68,7 +68,7 @@ class LibraryEditPageTest(StudioLibraryTest):
self.assertNotEqual(first_block_id, second_block_id) self.assertNotEqual(first_block_id, second_block_id)
# Delete the first block: # 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(len(self.lib_page.xblocks), 1)
self.assertEqual(self.lib_page.xblocks[0].locator, second_block_id) self.assertEqual(self.lib_page.xblocks[0].locator, second_block_id)
......
""" """
Acceptance tests for Studio related to course reruns. Acceptance tests for Studio related to course reruns.
""" """
import random import random
from bok_choy.promise import EmptyPromise from bok_choy.promise import EmptyPromise
from nose.plugins.attrib import attr
from nose.tools import assert_in from nose.tools import assert_in
from ...pages.studio.index import DashboardPage from ...pages.studio.index import DashboardPage
...@@ -72,7 +72,8 @@ class CourseRerunTest(StudioCourseTest): ...@@ -72,7 +72,8 @@ class CourseRerunTest(StudioCourseTest):
self.dashboard_page.create_rerun(self.course_info['display_name']) self.dashboard_page.create_rerun(self.course_info['display_name'])
rerun_page = CourseRerunPage(self.browser, *course_info) 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)) course_run = 'test_rerun_' + str(random.randrange(1000000, 9999999))
rerun_page.course_run = course_run rerun_page.course_run = course_run
rerun_page.create_rerun() rerun_page.create_rerun()
......
...@@ -98,6 +98,7 @@ class EndToEndCohortedCoursewareTest(ContainerBase): ...@@ -98,6 +98,7 @@ class EndToEndCohortedCoursewareTest(ContainerBase):
self.course_info['number'], self.course_info['number'],
self.course_info['run'] self.course_info['run']
) )
assert False, "This test needs to visit the group configurations page"
group_configurations_page.visit() group_configurations_page.visit()
group_configurations_page.create_first_content_group() group_configurations_page.create_first_content_group()
......
...@@ -42,6 +42,7 @@ class VideoBaseTest(UniqueCourseTest): ...@@ -42,6 +42,7 @@ class VideoBaseTest(UniqueCourseTest):
""" """
Initialization of pages and course fixture for video tests Initialization of pages and course fixture for video tests
""" """
assert False, "Video tests fail under phantomjs"
super(VideoBaseTest, self).setUp() super(VideoBaseTest, self).setUp()
self.video = VideoPage(self.browser) self.video = VideoPage(self.browser)
......
...@@ -148,31 +148,31 @@ END ...@@ -148,31 +148,31 @@ END
;; ;;
"1") "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") "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") "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") "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") "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") "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") "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 # 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