From 098c1d59cd05f067d9395d79856f27880e5b0ad9 Mon Sep 17 00:00:00 2001 From: Ben McMorran Date: Fri, 22 Aug 2014 11:12:54 -0400 Subject: [PATCH] Add bokchoy tests for course reruns --- cms/envs/aws.py | 1 + cms/envs/bok_choy.env.json | 4 +- .../acceptance/pages/studio/course_rerun.py | 41 +++++++ common/test/acceptance/pages/studio/index.py | 17 +++ .../test/acceptance/pages/studio/overview.py | 13 +++ .../test/acceptance/tests/base_studio_test.py | 4 +- .../acceptance/tests/test_studio_rerun.py | 103 ++++++++++++++++++ 7 files changed, 180 insertions(+), 3 deletions(-) create mode 100644 common/test/acceptance/pages/studio/course_rerun.py create mode 100644 common/test/acceptance/tests/test_studio_rerun.py diff --git a/cms/envs/aws.py b/cms/envs/aws.py index e3b0924c8d..1027612035 100644 --- a/cms/envs/aws.py +++ b/cms/envs/aws.py @@ -243,6 +243,7 @@ if 'DATADOG_API' in AUTH_TOKENS: DATADOG['api_key'] = AUTH_TOKENS['DATADOG_API'] # Celery Broker +CELERY_ALWAYS_EAGER = ENV_TOKENS.get("CELERY_ALWAYS_EAGER", False) CELERY_BROKER_TRANSPORT = ENV_TOKENS.get("CELERY_BROKER_TRANSPORT", "") CELERY_BROKER_HOSTNAME = ENV_TOKENS.get("CELERY_BROKER_HOSTNAME", "") CELERY_BROKER_VHOST = ENV_TOKENS.get("CELERY_BROKER_VHOST", "") diff --git a/cms/envs/bok_choy.env.json b/cms/envs/bok_choy.env.json index 7580de1f47..204515802c 100644 --- a/cms/envs/bok_choy.env.json +++ b/cms/envs/bok_choy.env.json @@ -45,6 +45,7 @@ ] } }, + "CELERY_ALWAYS_EAGER": true, "CELERY_BROKER_HOSTNAME": "localhost", "CELERY_BROKER_TRANSPORT": "amqp", "CERT_QUEUE": "certificates", @@ -70,7 +71,8 @@ "PREVIEW_LMS_BASE": "localhost:8003", "SUBDOMAIN_BRANDING": false, "SUBDOMAIN_COURSE_LISTINGS": false, - "ALLOW_ALL_ADVANCED_COMPONENTS": true + "ALLOW_ALL_ADVANCED_COMPONENTS": true, + "ALLOW_COURSE_RERUNS": true }, "FEEDBACK_SUBMISSION_EMAIL": "", "GITHUB_REPO_ROOT": "** OVERRIDDEN **", diff --git a/common/test/acceptance/pages/studio/course_rerun.py b/common/test/acceptance/pages/studio/course_rerun.py new file mode 100644 index 0000000000..abd4ee9e78 --- /dev/null +++ b/common/test/acceptance/pages/studio/course_rerun.py @@ -0,0 +1,41 @@ +""" +Course rerun page in Studio +""" + +from .course_page import CoursePage +from .utils import set_input_value + + +class CourseRerunPage(CoursePage): + """ + Course rerun page in Studio + """ + + url_path = "course_rerun" + COURSE_RUN_INPUT = '.rerun-course-run' + + def is_browser_on_page(self): + """ + Returns True iff the browser has loaded the course rerun page. + """ + return self.q(css='body.view-course-create-rerun').present + + @property + def course_run(self): + """ + Returns the value of the course run field. + """ + return self.q(css=self.COURSE_RUN_INPUT).text[0] + + @course_run.setter + def course_run(self, value): + """ + Sets the value of the course run field. + """ + set_input_value(self, self.COURSE_RUN_INPUT, value) + + def create_rerun(self): + """ + Clicks the create rerun button. + """ + self.q(css='.rerun-course-save')[0].click() diff --git a/common/test/acceptance/pages/studio/index.py b/common/test/acceptance/pages/studio/index.py index 9735979844..491850a977 100644 --- a/common/test/acceptance/pages/studio/index.py +++ b/common/test/acceptance/pages/studio/index.py @@ -15,3 +15,20 @@ class DashboardPage(PageObject): def is_browser_on_page(self): return self.q(css='body.view-dashboard').present + + @property + def has_processing_courses(self): + return self.q(css='.courses-processing').present + + def create_rerun(self, display_name): + """ + Clicks the create rerun link of the course specified by display_name. + """ + name = self.q(css='.course-title').filter(lambda el: el.text == display_name)[0] + name.find_elements_by_xpath('../..')[0].find_elements_by_class_name('rerun-button')[0].click() + + def click_course_run(self, run): + """ + Clicks on the course with run given by run. + """ + self.q(css='.course-run .value').filter(lambda el: el.text == run)[0].click() diff --git a/common/test/acceptance/pages/studio/overview.py b/common/test/acceptance/pages/studio/overview.py index fc57322727..11e0b2b69f 100644 --- a/common/test/acceptance/pages/studio/overview.py +++ b/common/test/acceptance/pages/studio/overview.py @@ -473,6 +473,19 @@ class CourseOutlinePage(CoursePage, CourseOutlineContainer): """ return self.q(css='.outline .no-content').is_present() + @property + def has_rerun_notification(self): + """ + Returns true iff the rerun notification is present on the page. + """ + return self.q(css='.wrapper-alert.is-shown').is_present() + + def dismiss_rerun_notification(self): + """ + Clicks the dismiss button in the rerun notification. + """ + self.q(css='.dismiss-button').click() + @property def expand_collapse_link_state(self): """ diff --git a/common/test/acceptance/tests/base_studio_test.py b/common/test/acceptance/tests/base_studio_test.py index 2f37de9a3a..deea40c5ff 100644 --- a/common/test/acceptance/tests/base_studio_test.py +++ b/common/test/acceptance/tests/base_studio_test.py @@ -8,7 +8,7 @@ class StudioCourseTest(UniqueCourseTest): Base class for all Studio course tests. """ - def setUp(self): + def setUp(self, is_staff=False): """ Install a course with no content using a fixture. """ @@ -22,7 +22,7 @@ class StudioCourseTest(UniqueCourseTest): self.populate_course_fixture(self.course_fixture) self.course_fixture.install() self.user = self.course_fixture.user - self.log_in(self.user) + self.log_in(self.user, is_staff) def populate_course_fixture(self, course_fixture): """ diff --git a/common/test/acceptance/tests/test_studio_rerun.py b/common/test/acceptance/tests/test_studio_rerun.py new file mode 100644 index 0000000000..e1f14bef35 --- /dev/null +++ b/common/test/acceptance/tests/test_studio_rerun.py @@ -0,0 +1,103 @@ +""" +Acceptance tests for Studio related to course reruns. +""" + +import random +from nose.plugins.attrib import attr +from bok_choy.promise import EmptyPromise + +from ..pages.studio.index import DashboardPage +from ..pages.studio.course_rerun import CourseRerunPage +from ..pages.studio.overview import CourseOutlinePage +from ..pages.lms.courseware import CoursewarePage +from ..fixtures.course import XBlockFixtureDesc + +from .base_studio_test import StudioCourseTest + + +@attr('shard_2') +class CourseRerunTest(StudioCourseTest): + """ + Feature: Courses can be rerun + """ + + __test__ = True + + SECTION_NAME = 'Rerun Section' + SUBSECITON_NAME = 'Rerun Subsection' + UNIT_NAME = 'Rerun Unit' + COMPONENT_NAME = 'Rerun Component' + COMPONENT_CONTENT = 'Test Content' + + def setUp(self): + """ + Login as global staff because that's the only way to rerun a course. + """ + super(CourseRerunTest, self).setUp(is_staff=True) + self.dashboard_page = DashboardPage(self.browser) + + def populate_course_fixture(self, course_fixture): + """ + Create a sample course with one section, one subsection, one unit, and one component. + """ + course_fixture.add_children( + XBlockFixtureDesc('chapter', self.SECTION_NAME).add_children( + XBlockFixtureDesc('sequential', self.SUBSECITON_NAME).add_children( + XBlockFixtureDesc('vertical', self.UNIT_NAME).add_children( + XBlockFixtureDesc('html', self.COMPONENT_NAME, self.COMPONENT_CONTENT) + ) + ) + ) + ) + + def test_course_rerun(self): + """ + Scenario: Courses can be rurun + Given I have a course with a section, subsesction, vertical, and html component with content 'Test Content' + When I visit the course rerun page + And I type 'test_rerun' in the course run field + And I click Create Rerun + And I visit the course listing page + And I wait for all courses to finish processing + And I click on the course with run 'test_rerun' + Then I see a rerun notification on the course outline page + And when I click 'Dismiss' on the notification + Then I do not see a rerun notification + And when I expand the subsection and click on the unit + And I click 'View Live Version' + Then I see one html component with the content 'Test Content' + """ + course_info = (self.course_info['org'], self.course_info['number'], self.course_info['run']) + + self.dashboard_page.visit() + self.dashboard_page.create_rerun(self.course_info['display_name']) + + rerun_page = CourseRerunPage(self.browser, *course_info) + rerun_page.wait_for_page() + course_run = 'test_rerun_' + str(random.randrange(1000000, 9999999)) + rerun_page.course_run = course_run + rerun_page.create_rerun() + + def finished_processing(): + self.dashboard_page.visit() + return not self.dashboard_page.has_processing_courses + + EmptyPromise(finished_processing, "Rerun finished processing", try_interval=5, timeout=60).fulfill() + self.dashboard_page.click_course_run(course_run) + + outline_page = CourseOutlinePage(self.browser, *course_info) + outline_page.wait_for_page() + self.assertTrue(outline_page.has_rerun_notification) + + outline_page.dismiss_rerun_notification() + EmptyPromise(lambda: not outline_page.has_rerun_notification, "Rerun notification dismissed").fulfill() + + subsection = outline_page.section(self.SECTION_NAME).subsection(self.SUBSECITON_NAME) + subsection.toggle_expand() + unit_page = subsection.unit(self.UNIT_NAME).go_to() + + unit_page.view_published_version() + courseware = CoursewarePage(self.browser, self.course_id) + courseware.wait_for_page() + self.assertEqual(courseware.num_xblock_components, 1) + self.assertEqual(courseware.xblock_component_html_content(), self.COMPONENT_CONTENT)