Merge pull request #29996 from eduNEXT/MJG/course-about-render-filter

[BD-32] feat: add filter before course about rendering process starts
This commit is contained in:
Maria Grimaldi
2022-05-11 14:07:02 -04:00
committed by GitHub
2 changed files with 272 additions and 2 deletions

View File

@@ -0,0 +1,253 @@
"""
Test that various filters are fired for courseware views.
"""
from django.http import HttpResponse
from django.test import override_settings
from django.urls import reverse
from openedx_filters import PipelineStep
from openedx_filters.learning.filters import CourseAboutRenderStarted
from rest_framework import status
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
from openedx.core.djangolib.testing.utils import skip_unless_lms
class TestRenderInvalidCourseAbout(PipelineStep):
"""
Utility class used when getting steps for pipeline.
"""
def run_filter(self, context, template_name): # pylint: disable=arguments-differ
"""
Pipeline step that stops the course about render process.
When raising PreventCourseAboutRender, this filter overrides the course about
template name so the view renders a module-error instead.
"""
raise CourseAboutRenderStarted.RenderInvalidCourseAbout(
"You can't access the courses about page.",
course_about_template="module-error.html",
template_context=context,
)
class TestRedirectToPage(PipelineStep):
"""
Utility class used when getting steps for pipeline.
"""
def run_filter(self, context, template_name): # pylint: disable=arguments-differ
"""
Pipeline step that redirects to the course survey.
When raising RedirectToPage, this filter uses a redirect_to field handled by
the course about view that redirects to that URL.
"""
course_key = str(context.get("course").id)
raise CourseAboutRenderStarted.RedirectToPage(
"You can't access this courses about page, redirecting to the correct location.",
redirect_to=f"courses/{course_key}/survey",
)
class TestRedirectToDefaultPage(PipelineStep):
"""
Utility class used when getting steps for pipeline.
"""
def run_filter(self, context, template_name): # pylint: disable=arguments-differ
"""
Pipeline step that redirects to the default page when redirect_to is not specified.
When raising RedirectToPage, this filter uses a redirect_to field handled by
the course about view that redirects to that URL.
"""
course_key = str(context.get("course").id)
raise CourseAboutRenderStarted.RedirectToPage(
"You can't access this courses about page, redirecting to the correct location.",
)
class TestRenderCustomResponse(PipelineStep):
"""
Utility class used when getting steps for pipeline.
"""
def run_filter(self, context, template_name): # pylint: disable=arguments-differ
"""
Pipeline step that redirects to the course survey.
When raising RenderCustomResponse, this filter uses a redirect_to field handled by
the course about view that redirects to that URL.
"""
response = HttpResponse("Here's the text of the web page.")
raise CourseAboutRenderStarted.RenderCustomResponse(
"You can't access this courses home page.",
response=response,
)
class TestCourseAboutRender(PipelineStep):
"""
Utility class used when getting steps for pipeline.
"""
def run_filter(self, context, template_name): # pylint: disable=arguments-differ
"""Pipeline that gives staff view to the current user."""
context["staff_access"] = True
context["studio_url"] = "http://madeup-studio.com"
return {
"context": context, template_name: template_name,
}
@skip_unless_lms
class CourseAboutFiltersTest(ModuleStoreTestCase):
"""
Tests for the Open edX Filters associated with the course about rendering process.
This class guarantees that the following filters are triggered during the course about rendering:
- CourseAboutRenderStarted
"""
def setUp(self): # pylint: disable=arguments-differ
super().setUp()
self.course = CourseFactory.create()
self.course_about_url = reverse(
"about_course",
kwargs={
"course_id": self.course.id,
}
)
@override_settings(
OPEN_EDX_FILTERS_CONFIG={
"org.openedx.learning.course_about.render.started.v1": {
"pipeline": [
"lms.djangoapps.courseware.tests.test_filters.TestCourseAboutRender",
],
"fail_silently": False,
},
},
)
def test_course_about_render_filter_executed(self):
"""
Test whether the course about filter is triggered before the course about view
renders.
Expected result:
- CourseAboutRenderStarted is triggered and executes TestCourseAboutRender.
- The course about renders with View About Page in studio.
"""
response = self.client.get(self.course_about_url)
self.assertContains(response, "View About Page in studio", status_code=200)
@override_settings(
OPEN_EDX_FILTERS_CONFIG={
"org.openedx.learning.course_about.render.started.v1": {
"pipeline": [
"lms.djangoapps.courseware.tests.test_filters.TestRenderInvalidCourseAbout",
],
"fail_silently": False,
},
},
PLATFORM_NAME="My site",
)
def test_course_about_render_alternative(self):
"""
Test rendering an error template after catching PreventCourseAboutRender exception.
Expected result:
- CourseAboutRenderStarted is triggered and executes TestRenderInvalidCourseAbout.
- The module-error template is rendered instead of the usual course about.
"""
response = self.client.get(self.course_about_url)
self.assertContains(response, "There has been an error on the <em>My site</em> servers")
@override_settings(
OPEN_EDX_FILTERS_CONFIG={
"org.openedx.learning.course_about.render.started.v1": {
"pipeline": [
"lms.djangoapps.courseware.tests.test_filters.TestRedirectToPage",
],
"fail_silently": False,
},
},
)
def test_course_about_redirect(self):
"""
Test redirecting to a new page after catching RedirectCourseAboutPage exception.
Expected result:
- CourseAboutRenderStarted is triggered and executes TestRedirectToPage.
- The view response is a redirection.
"""
response = self.client.get(self.course_about_url)
self.assertEqual(status.HTTP_302_FOUND, response.status_code)
self.assertEqual(f"courses/{self.course.id}/survey", response.url)
@override_settings(
OPEN_EDX_FILTERS_CONFIG={
"org.openedx.learning.course_about.render.started.v1": {
"pipeline": [
"lms.djangoapps.courseware.tests.test_filters.TestRedirectToDefaultPage",
],
"fail_silently": False,
},
},
)
def test_course_about_redirect_default(self):
"""
Test redirecting to the default page after catching RedirectCourseAboutPage exception.
Expected result:
- CourseAboutRenderStarted is triggered and executes TestRedirectToPage.
- The view response is a redirection.
"""
response = self.client.get(self.course_about_url)
self.assertEqual(status.HTTP_302_FOUND, response.status_code)
self.assertEqual(f"{reverse('dashboard')}", response.url)
@override_settings(
OPEN_EDX_FILTERS_CONFIG={
"org.openedx.learning.course_about.render.started.v1": {
"pipeline": [
"lms.djangoapps.courseware.tests.test_filters.TestRenderCustomResponse",
],
"fail_silently": False,
},
},
)
def test_course_about_custom_response(self):
"""
Test redirecting to a new page after catching RenderCustomResponse exception.
Expected result:
- CourseAboutRenderStarted is triggered and executes TestRenderCustomResponse.
- The view response is a redirection.
"""
response = self.client.get(self.course_about_url)
self.assertContains(response, "Here's the text of the web page.")
@override_settings(OPEN_EDX_FILTERS_CONFIG={})
def test_course_about_render_without_filter_config(self):
"""
Test whether the course about filter is triggered before the course about
render without affecting its execution flow.
Expected result:
- CourseAboutRenderStarted executes a noop (empty pipeline). Without any
modification comparing it with the effects of TestCourseAboutRender.
- The view response is HTTP_200_OK.
"""
response = self.client.get(self.course_about_url)
self.assertNotContains(response, "View About Page in studio", status_code=200)

View File

@@ -37,6 +37,7 @@ from ipware.ip import get_client_ip
from markupsafe import escape
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey, UsageKey
from openedx_filters.learning.filters import CourseAboutRenderStarted
from pytz import UTC
from requests.exceptions import ConnectionError, Timeout # pylint: disable=redefined-builtin
from rest_framework import status
@@ -890,7 +891,7 @@ class EnrollStaffView(View):
@ensure_csrf_cookie
@ensure_valid_course_key
@cache_if_anonymous()
def course_about(request, course_id):
def course_about(request, course_id): # pylint: disable=too-many-statements
"""
Display the course's about page.
"""
@@ -999,7 +1000,23 @@ def course_about(request, course_id):
'allow_anonymous': allow_anonymous,
}
return render_to_response('courseware/course_about.html', context)
course_about_template = 'courseware/course_about.html'
try:
# .. filter_implemented_name: CourseAboutRenderStarted
# .. filter_type: org.openedx.learning.course_about.render.started.v1
context, course_about_template = CourseAboutRenderStarted.run_filter(
context=context, template_name=course_about_template,
)
except CourseAboutRenderStarted.RenderInvalidCourseAbout as exc:
response = render_to_response(exc.course_about_template, exc.template_context)
except CourseAboutRenderStarted.RedirectToPage as exc:
raise CourseAccessRedirect(exc.redirect_to or reverse('dashboard')) from exc
except CourseAboutRenderStarted.RenderCustomResponse as exc:
response = exc.response or render_to_response(course_about_template, context)
else:
response = render_to_response(course_about_template, context)
return response
@ensure_csrf_cookie