Merge pull request #14462 from edx/renzo/programs-from-catalog

Load all programs from the catalog
This commit is contained in:
Renzo Lucioni
2017-02-07 10:42:17 -05:00
committed by GitHub
34 changed files with 978 additions and 1742 deletions

View File

@@ -19,8 +19,5 @@ COMMENTS_STUB_URL = os.environ.get('comments_url', 'http://localhost:4567')
# Get the URL of the EdxNotes service stub used in the test
EDXNOTES_STUB_URL = os.environ.get('edxnotes_url', 'http://localhost:8042')
# Get the URL of the Programs service stub used in the test
PROGRAMS_STUB_URL = os.environ.get('programs_url', 'http://localhost:8090')
# Get the URL of the Catalog service stub used in the test
CATALOG_STUB_URL = os.environ.get('catalog_url', 'http://localhost:8091')

View File

@@ -13,31 +13,31 @@ class CatalogFixture(object):
"""
Interface to set up mock responses from the Catalog stub server.
"""
def install_programs(self, programs):
def install_programs(self, data):
"""Set response data for the catalog's course run API."""
key = 'catalog.programs'
requests.put(
'{}/set_config'.format(CATALOG_STUB_URL),
data={key: json.dumps(programs)},
)
if isinstance(data, dict):
key += '.' + data['uuid']
def install_course_run(self, course_run):
"""Set response data for the catalog's course run API."""
key = 'catalog.{}'.format(course_run['key'])
requests.put(
'{}/set_config'.format(CATALOG_STUB_URL),
data={key: json.dumps(course_run)},
)
requests.put(
'{}/set_config'.format(CATALOG_STUB_URL),
data={key: json.dumps(data)},
)
else:
requests.put(
'{}/set_config'.format(CATALOG_STUB_URL),
data={key: json.dumps({'results': data})},
)
class CatalogConfigMixin(object):
class CatalogIntegrationMixin(object):
"""Mixin providing a method used to configure the catalog integration."""
def set_catalog_configuration(self, is_enabled=False, service_url=CATALOG_STUB_URL):
"""Dynamically adjusts the catalog config model during tests."""
def set_catalog_integration(self, is_enabled=False, service_username=None):
"""Use this to change the catalog integration config model during tests."""
ConfigModelFixture('/config/catalog', {
'enabled': is_enabled,
'internal_api_url': '{}/api/v1/'.format(service_url),
'internal_api_url': '{}/api/v1/'.format(CATALOG_STUB_URL),
'cache_ttl': 0,
'service_username': service_username,
}).install()

View File

@@ -1,46 +1,19 @@
"""
Tools to create programs-related data for use in bok choy tests.
"""
import json
import requests
from common.test.acceptance.fixtures import PROGRAMS_STUB_URL
from common.test.acceptance.fixtures.config import ConfigModelFixture
class ProgramsFixture(object):
"""
Interface to set up mock responses from the Programs stub server.
"""
def install_programs(self, programs, is_list=True):
"""Sets the response data for Programs API endpoints."""
if is_list:
key = 'programs'
api_result = {'results': programs}
else:
program = programs[0]
key = 'programs.{}'.format(program['id'])
api_result = program
requests.put(
'{}/set_config'.format(PROGRAMS_STUB_URL),
data={key: json.dumps(api_result)},
)
class ProgramsConfigMixin(object):
"""Mixin providing a method used to configure the programs feature."""
def set_programs_api_configuration(self, is_enabled=False, api_version=1, api_url=PROGRAMS_STUB_URL):
def set_programs_api_configuration(self, is_enabled=False, api_version=1):
"""Dynamically adjusts the Programs config model during tests."""
ConfigModelFixture('/config/programs', {
'enabled': is_enabled,
'api_version_number': api_version,
'internal_service_url': api_url,
'public_service_url': api_url,
'cache_ttl': 0,
'marketing_path': '/foo',
'enable_student_dashboard': is_enabled,
'enable_studio_tab': is_enabled,
'enable_certification': is_enabled,
'program_listing_enabled': is_enabled,
'program_details_enabled': is_enabled,

View File

@@ -1,4 +1,6 @@
"""LMS-hosted Programs pages"""
from uuid import uuid4
from bok_choy.page_object import PageObject
from common.test.acceptance.pages.lms import BASE_URL
@@ -24,8 +26,8 @@ class ProgramListingPage(PageObject):
class ProgramDetailsPage(PageObject):
"""Program details page."""
program_id = 123
url = BASE_URL + '/dashboard/programs/{}/program-name/'.format(program_id)
program_uuid = str(uuid4())
url = '{base}/dashboard/programs/{program_uuid}/'.format(base=BASE_URL, program_uuid=program_uuid)
def is_browser_on_page(self):
return self.q(css='.js-program-details-wrapper').present

View File

@@ -1,67 +1,48 @@
"""Acceptance tests for LMS-hosted Programs pages"""
from nose.plugins.attrib import attr
from common.test.acceptance.fixtures.catalog import CatalogFixture, CatalogConfigMixin
from common.test.acceptance.fixtures.programs import ProgramsFixture, ProgramsConfigMixin
from common.test.acceptance.fixtures.catalog import CatalogFixture, CatalogIntegrationMixin
from common.test.acceptance.fixtures.programs import ProgramsConfigMixin
from common.test.acceptance.fixtures.course import CourseFixture
from common.test.acceptance.tests.helpers import UniqueCourseTest
from common.test.acceptance.pages.lms.auto_auth import AutoAuthPage
from common.test.acceptance.pages.lms.programs import ProgramListingPage, ProgramDetailsPage
from openedx.core.djangoapps.catalog.tests import factories as catalog_factories
from openedx.core.djangoapps.programs.tests import factories as program_factories
from openedx.core.djangoapps.catalog.tests.factories import (
ProgramFactory, CourseFactory, CourseRunFactory
)
class ProgramPageBase(ProgramsConfigMixin, CatalogConfigMixin, UniqueCourseTest):
class ProgramPageBase(ProgramsConfigMixin, CatalogIntegrationMixin, UniqueCourseTest):
"""Base class used for program listing page tests."""
def setUp(self):
super(ProgramPageBase, self).setUp()
self.set_programs_api_configuration(is_enabled=True)
self.programs = [catalog_factories.Program() for __ in range(3)]
self.course_run = catalog_factories.CourseRun(key=self.course_id)
self.stub_catalog_api()
def create_program(self, program_id=None, course_id=None):
"""DRY helper for creating test program data."""
course_id = course_id if course_id else self.course_id
run_mode = program_factories.RunMode(course_key=course_id)
course_code = program_factories.CourseCode(run_modes=[run_mode])
org = program_factories.Organization(key=self.course_info['org'])
if program_id:
program = program_factories.Program(
id=program_id,
status='active',
organizations=[org],
course_codes=[course_code]
)
else:
program = program_factories.Program(
status='active',
organizations=[org],
course_codes=[course_code]
)
return program
def stub_programs_api(self, programs, is_list=True):
"""Stub out the programs API with fake data."""
ProgramsFixture().install_programs(programs, is_list=is_list)
def stub_catalog_api(self):
"""Stub out the catalog API's program and course run endpoints."""
self.set_catalog_configuration(is_enabled=True)
CatalogFixture().install_programs(self.programs)
CatalogFixture().install_course_run(self.course_run)
self.programs = ProgramFactory.create_batch(3)
self.username = None
def auth(self, enroll=True):
"""Authenticate, enrolling the user in the configured course if requested."""
CourseFixture(**self.course_info).install()
course_id = self.course_id if enroll else None
AutoAuthPage(self.browser, course_id=course_id).visit()
auth_page = AutoAuthPage(self.browser, course_id=course_id)
auth_page.visit()
self.username = auth_page.user_info['username']
def create_program(self):
"""DRY helper for creating test program data."""
course_run = CourseRunFactory(key=self.course_id)
course = CourseFactory(course_runs=[course_run])
return ProgramFactory(courses=[course])
def stub_catalog_api(self, data=None):
"""Stub out the catalog API's program and course run endpoints."""
self.set_catalog_integration(is_enabled=True, service_username=self.username)
CatalogFixture().install_programs(data or self.programs)
class ProgramListingPageTest(ProgramPageBase):
@@ -73,9 +54,8 @@ class ProgramListingPageTest(ProgramPageBase):
def test_no_enrollments(self):
"""Verify that no cards appear when the user has no enrollments."""
program = self.create_program()
self.stub_programs_api([program])
self.auth(enroll=False)
self.stub_catalog_api()
self.listing_page.visit()
@@ -87,14 +67,8 @@ class ProgramListingPageTest(ProgramPageBase):
Verify that no cards appear when the user has enrollments
but none are included in an active program.
"""
course_id = self.course_id.replace(
self.course_info['run'],
'other_run'
)
program = self.create_program(course_id=course_id)
self.stub_programs_api([program])
self.auth()
self.stub_catalog_api()
self.listing_page.visit()
@@ -106,10 +80,11 @@ class ProgramListingPageTest(ProgramPageBase):
Verify that cards appear when the user has enrollments
which are included in at least one active program.
"""
program = self.create_program()
self.stub_programs_api([program])
self.auth()
program = self.create_program()
self.stub_catalog_api(data=[program])
self.listing_page.visit()
self.assertTrue(self.listing_page.is_sidebar_present)
@@ -124,12 +99,13 @@ class ProgramListingPageA11yTest(ProgramPageBase):
self.listing_page = ProgramListingPage(self.browser)
program = self.create_program()
self.stub_programs_api([program])
self.program = self.create_program()
def test_empty_a11y(self):
"""Test a11y of the page's empty state."""
self.auth(enroll=False)
self.stub_catalog_api(data=[self.program])
self.listing_page.visit()
self.assertTrue(self.listing_page.is_sidebar_present)
@@ -139,6 +115,8 @@ class ProgramListingPageA11yTest(ProgramPageBase):
def test_cards_a11y(self):
"""Test a11y when program cards are present."""
self.auth()
self.stub_catalog_api(data=[self.program])
self.listing_page.visit()
self.assertTrue(self.listing_page.is_sidebar_present)
@@ -154,11 +132,14 @@ class ProgramDetailsPageA11yTest(ProgramPageBase):
self.details_page = ProgramDetailsPage(self.browser)
program = self.create_program(program_id=self.details_page.program_id)
self.stub_programs_api([program], is_list=False)
self.program = self.create_program()
self.program['uuid'] = self.details_page.program_uuid
def test_a11y(self):
"""Test the page's a11y compliance."""
self.auth()
self.stub_catalog_api(data=self.program)
self.details_page.visit()
self.details_page.a11y_audit.check_for_accessibility_errors()