def test_models_course_get_categories_other_placeholders(self): """ The `get_categories` method should return all categories linked to a course via a plugin on whichever placeholder. """ category1, category2 = CategoryFactory.create_batch(2) course = CourseFactory(should_publish=True) placeholder1 = course.extended_object.placeholders.get( slot="course_description") placeholder2 = course.extended_object.placeholders.get( slot="course_format") add_plugin( language="en", placeholder=placeholder1, plugin_type="CategoryPlugin", page=category1.extended_object, ) add_plugin( language="en", placeholder=placeholder2, plugin_type="CategoryPlugin", page=category2.extended_object, ) self.assertEqual(list(course.get_categories()), [category1, category2])
def test_templates_blogpost_list_related_categories(self): """ The top of the page should list all categories related to at least one of the blog posts on the blog posts list page. """ page = PageFactory( template="courses/cms/blogpost_list.html", title__language="en", should_publish=True, ) post1, post2 = BlogPostFactory.create_batch(2, page_parent=page, should_publish=True) category1, category2, category12, category_alone = CategoryFactory.create_batch( 4, should_publish=True) # Attach categories to post1 placeholder = post1.extended_object.get_public_object( ).placeholders.all()[0] add_plugin(placeholder, "CategoryPlugin", "en", page=category1.extended_object) add_plugin(placeholder, "CategoryPlugin", "en", page=category12.extended_object) # Attach categories to post2 placeholder = post2.extended_object.get_public_object( ).placeholders.all()[0] add_plugin(placeholder, "CategoryPlugin", "en", page=category2.extended_object) add_plugin(placeholder, "CategoryPlugin", "en", page=category12.extended_object) response = self.client.get(page.get_absolute_url()) self.assertEqual(response.status_code, 200) for category in [category1, category2, category12]: self.assertContains( response, ('<a class="category-tag" href="{slug:s}">' '<span class="category-tag__title">{title:s}</span>' "</a>").format( slug=category.extended_object.get_absolute_url(), title=category.extended_object.get_title(), ), html=True, ) self.assertNotContains( response, category_alone.extended_object.get_absolute_url())
def test_models_course_get_categories(self): """ The `get_categories` method should return all categories linked to a course and should respect publication status. """ # The 2 first categories are grouped in one variable name and will be linked to the # course in the following, the third category will not be linked so we can check that # only the categories linked to the course are retrieved (its name starts with `_` # because it is not used and only here for unpacking purposes) *draft_categories, _other_draft = CategoryFactory.create_batch(3) *published_categories, _other_public = CategoryFactory.create_batch( 3, should_publish=True) course = CourseFactory(fill_categories=draft_categories + published_categories, should_publish=True) self.assertEqual(list(course.get_categories()), draft_categories + published_categories) self.assertEqual(list(course.public_extension.get_categories()), published_categories)
def test_models_category_get_children_categories(self): """ It should be possible to retrieve the list of direct children page which are Category extensions and not any other type. """ empty_category = CategoryFactory(should_publish=True) parent_category = CategoryFactory(should_publish=True) child_categories = CategoryFactory.create_batch( 2, page_parent=parent_category.extended_object, should_publish=True ) with self.assertNumQueries(2): self.assertFalse(empty_category.get_children_categories().exists()) with self.assertNumQueries(2): self.assertEqual( set(parent_category.get_children_categories()), set(child_categories) )
def test_templates_course_run_detail_cms_published_content(self): """ Validate that the important elements are displayed on a published course run page """ categories = CategoryFactory.create_batch(4) organizations = OrganizationFactory.create_batch(4) course = CourseFactory( page_title="Very interesting course", fill_organizations=organizations, fill_categories=categories, should_publish=True, ) course_run = CourseRunFactory( page_title="first session", page_parent=course.extended_object, resource_link="https://www.example.com/enroll", enrollment_start=datetime(2018, 10, 21, tzinfo=pytz.utc), enrollment_end=datetime(2019, 1, 18, tzinfo=pytz.utc), start=datetime(2018, 12, 10, tzinfo=pytz.utc), end=datetime(2019, 2, 14, tzinfo=pytz.utc), languages=["en", "fr"], ) page = course_run.extended_object # Publish only 2 out of 4 categories and 2 out of 4 organizations categories[0].extended_object.publish("en") categories[1].extended_object.publish("en") organizations[0].extended_object.publish("en") organizations[1].extended_object.publish("en") # The unpublished objects may have been published and unpublished which puts them in a # status different from objects that have never been published. # We want to test both cases. categories[2].extended_object.publish("en") categories[2].extended_object.unpublish("en") organizations[2].extended_object.publish("en") organizations[2].extended_object.unpublish("en") # The page should not be visible before it is published url = page.get_absolute_url() response = self.client.get(url) self.assertEqual(response.status_code, 404) # Now publish the page and check its content page.publish("en") response = self.client.get(url) self.assertEqual(response.status_code, 200) self.assertContains( response, "<title>First session - Very interesting course</title>", html=True, ) self.assertContains( response, '<h1 class="subheader__title">' "Very interesting course<br>first session</h1>", html=True, ) # Only published categories should be present on the page for category in categories[:2]: self.assertContains( response, ('<a class="category-badge" href="{:s}">' '<span class="category-badge__title">{:s}</span></a>').format( category.extended_object.get_absolute_url(), category.extended_object.get_title(), ), html=True, ) for category in categories[-2:]: self.assertNotContains(response, category.extended_object.get_title()) # Public organizations should be in response content for organization in organizations[:2]: self.assertContains( response, '<div class="organization-glimpse__title">{title:s}</div>'. format(title=organization.extended_object.get_title()), html=True, ) # Draft organizations should not be in response content for organization in organizations[-2:]: self.assertNotContains(response, organization.extended_object.get_title(), html=True) # The course run details should be on the page self.assertContains( response, "<strong>Enrollment starts</strong><span>Oct. 21, 2018</span>") self.assertContains( response, "<strong>Enrollment ends</strong><span>Jan. 18, 2019</span>") self.assertContains( response, "<strong>Course starts</strong><span>Dec. 10, 2018</span>") self.assertContains( response, "<strong>Course ends</strong><span>Feb. 14, 2019</span>") self.assertContains( response, "<strong>Languages</strong><span>English and french</span>")
def test_templates_course_run_detail_cms_draft_content(self): """ A staff user should see a draft course run including its draft elements with an annotation """ user = UserFactory(is_staff=True, is_superuser=True) self.client.login(username=user.username, password="******") categories = CategoryFactory.create_batch(4) organizations = OrganizationFactory.create_batch(4) course = CourseFactory( page_title="Very interesting course", fill_organizations=organizations, fill_categories=categories, should_publish=True, ) course_run = CourseRunFactory( page_title="first session", page_parent=course.extended_object, resource_link="https://www.example.com/enroll", enrollment_start=datetime(2018, 10, 21, tzinfo=pytz.utc), enrollment_end=datetime(2019, 1, 18, tzinfo=pytz.utc), start=datetime(2018, 12, 10, tzinfo=pytz.utc), end=datetime(2019, 2, 14, tzinfo=pytz.utc), languages=["en", "fr"], ) page = course_run.extended_object # Publish only 2 out of 4 categories and 2 out of 4 organizations categories[0].extended_object.publish("en") categories[1].extended_object.publish("en") organizations[0].extended_object.publish("en") organizations[1].extended_object.publish("en") # The unpublished objects may have been published and unpublished which puts them in a # status different from objects that have never been published. # We want to test both cases. categories[2].extended_object.publish("en") categories[2].extended_object.unpublish("en") organizations[2].extended_object.publish("en") organizations[2].extended_object.unpublish("en") # The page should be visible as draft to the staff user url = page.get_absolute_url() response = self.client.get(url) self.assertEqual(response.status_code, 200) self.assertContains( response, "<title>First session - Very interesting course</title>", html=True, ) self.assertContains( response, '<h1 class="subheader__title">' "Very interesting course<br>first session</h1>", html=True, ) # Draft and public organizations should all be present on the page for organization in organizations: self.assertContains( response, '<div class="organization-glimpse__title">{title:s}</div>'. format(title=organization.extended_object.get_title()), html=True, ) # Draft organizations should be annotated for styling self.assertContains(response, "organization-glimpse--draft", count=2) # The published categories should be present on the page for category in categories[:2]: self.assertContains( response, ('<a class="category-badge" href="{:s}">' '<span class="category-badge__title">{:s}</span></a>').format( category.extended_object.get_absolute_url(), category.extended_object.get_title(), ), html=True, ) # Draft categories should also be present on the page with an annotation for styling for category in categories[-2:]: self.assertContains( response, ('<a class="{element:s} {element:s}--draft" href="{url:s}">' '<span class="category-badge__title">{title:s}</span></a>' ).format( url=category.extended_object.get_absolute_url(), element="category-badge", title=category.extended_object.get_title(), ), html=True, ) # The course run details should be on the page self.assertContains( response, "<strong>Enrollment starts</strong><span>Oct. 21, 2018</span>") self.assertContains( response, "<strong>Enrollment ends</strong><span>Jan. 18, 2019</span>") self.assertContains( response, "<strong>Course starts</strong><span>Dec. 10, 2018</span>") self.assertContains( response, "<strong>Course ends</strong><span>Feb. 14, 2019</span>") self.assertContains( response, "<strong>Languages</strong><span>English and french</span>")
def test_templates_course_detail_cms_published_content(self): """ Validate that the important elements are displayed on a published course page """ categories = CategoryFactory.create_batch(4) icons = CategoryFactory.create_batch(4, fill_icon=True) organizations = OrganizationFactory.create_batch(4) course = CourseFactory( page_title="Very interesting course", fill_organizations=organizations, fill_categories=categories, fill_icons=icons, ) page = course.extended_object # Create an ongoing open course run that will be published (created before # publishing the page) now = timezone.now() CourseRunFactory( direct_course=course, start=now - timedelta(hours=1), end=now + timedelta(hours=2), enrollment_end=now + timedelta(hours=1), languages=["en", "fr"], ) # Publish only 2 out of 4 categories, icons and organizations self.assertTrue(categories[0].extended_object.publish("en")) self.assertTrue(categories[1].extended_object.publish("en")) self.assertTrue(icons[0].extended_object.publish("en")) self.assertTrue(icons[1].extended_object.publish("en")) self.assertTrue(organizations[0].extended_object.publish("en")) self.assertTrue(organizations[1].extended_object.publish("en")) # The unpublished objects may have been published and unpublished which puts them in a # status different from objects that have never been published. # We want to test both cases. self.assertTrue(categories[2].extended_object.publish("en")) self.assertTrue(categories[2].extended_object.unpublish("en")) self.assertTrue(icons[2].extended_object.publish("en")) self.assertTrue(icons[2].extended_object.unpublish("en")) self.assertTrue(organizations[2].extended_object.publish("en")) self.assertTrue(organizations[2].extended_object.unpublish("en")) # The page should not be visible before it is published url = page.get_absolute_url() response = self.client.get(url) self.assertEqual(response.status_code, 404) # Publish and ensure content is correct self.assertTrue(page.publish("en")) # Create an unpublished ongoing open course run (created after # publishing the page) CourseRunFactory( direct_course=course, start=now - timedelta(hours=1), end=now + timedelta(hours=2), enrollment_end=now + timedelta(hours=1), languages=["en", "fr"], ) response = self.client.get(url) self.assertEqual(response.status_code, 200) self.assertContains(response, "<title>Very interesting course</title>", html=True) self.assertContains( response, '<h1 class="subheader__title">Very interesting course</h1>', html=True, ) # Only published categories should be present on the page for category in categories[:2]: self.assertContains( response, ('<a class="category-badge" href="{:s}">' '<span class="category-badge__title">{:s}</span></a>').format( category.extended_object.get_absolute_url(), category.extended_object.get_title(), ), html=True, ) for category in categories[-2:]: self.assertNotContains(response, category.extended_object.get_title()) # Only published icons should be present on the page pattern = ( r'<a.*class="category-badge".*href="{link:s}".*>' r'<img src="/media/filer_public_thumbnails/filer_public/.*icon\.jpg.*alt="{title:s}">' r'<span class="category-badge__title">' r".*{title:s}.*</span>") for icon in icons[:2]: self.assertIsNotNone( re.search( pattern.format( link=icon.extended_object.get_absolute_url(), title=icon.extended_object.get_title(), ), str(response.content), )) for icon in icons[-2:]: self.assertNotContains(response, icon.extended_object.get_title()) # Public organizations should be in response content for organization in organizations[:2]: self.assertContains( response, '<div class="organization-glimpse__title">{title:s}</div>'. format(title=organization.extended_object.get_title()), html=True, ) # Draft organizations should not be in response content for organization in organizations[-2:]: self.assertNotContains(response, organization.extended_object.get_title(), html=True) # Only the published course run should be in response content self.assertEqual(CourseRun.objects.count(), 3) self.assertContains(response, "<dd>English and french</dd>", html=True, count=1)
def test_templates_course_detail_cms_draft_content(self): """ A staff user should see a draft course including its draft elements with an annotation """ user = UserFactory(is_staff=True, is_superuser=True) self.client.login(username=user.username, password="******") categories = CategoryFactory.create_batch(4) organizations = OrganizationFactory.create_batch(4) course = CourseFactory( page_title="Very interesting course", fill_organizations=organizations, fill_categories=categories, ) page = course.extended_object now = timezone.now() CourseRunFactory( direct_course=course, start=now - timedelta(hours=1), end=now + timedelta(hours=2), enrollment_end=now + timedelta(hours=1), languages=["en", "fr"], ) # Publish only 2 out of 4 categories and 2 out of 4 organizations self.assertTrue(categories[0].extended_object.publish("en")) self.assertTrue(categories[1].extended_object.publish("en")) self.assertTrue(organizations[0].extended_object.publish("en")) self.assertTrue(organizations[1].extended_object.publish("en")) # The unpublished objects may have been published and unpublished which puts them in a # status different from objects that have never been published. # We want to test both cases. self.assertTrue(categories[2].extended_object.publish("en")) self.assertTrue(categories[2].extended_object.unpublish("en")) self.assertTrue(organizations[2].extended_object.publish("en")) self.assertTrue(organizations[2].extended_object.unpublish("en")) # The page should be visible as draft to the staff user url = page.get_absolute_url() response = self.client.get(url) self.assertEqual(response.status_code, 200) self.assertContains(response, "<title>Very interesting course</title>", html=True) self.assertContains( response, '<h1 class="subheader__title">Very interesting course</h1>', html=True, ) # Draft and public organizations should all be present on the page for organization in organizations: self.assertContains( response, '<div class="organization-glimpse__title">{title:s}</div>'. format(title=organization.extended_object.get_title()), html=True, ) # Draft organizations should be annotated for styling self.assertContains(response, "organization-glimpse--draft", count=2) # The published categories should be present on the page for category in categories[:2]: self.assertContains( response, ('<a class="category-badge" href="{:s}">' '<span class="category-badge__title">{:s}</span></a>').format( category.extended_object.get_absolute_url(), category.extended_object.get_title(), ), html=True, ) # Draft categories should also be present on the page with an annotation for styling for category in categories[-2:]: self.assertContains( response, ('<a class="{element:s} {element:s}--draft" href="{url:s}">' '<span class="category-badge__title">{title:s}</span></a>' ).format( url=category.extended_object.get_absolute_url(), element="category-badge", title=category.extended_object.get_title(), ), html=True, ) # The course run should be in the page self.assertContains(response, "<dd>English and french</dd>", html=True, count=1)
def test_templates_course_detail_cms_published_content(self): """ Validate that the important elements are displayed on a published course page """ categories = CategoryFactory.create_batch(4) icons = CategoryFactory.create_batch(4, fill_icon=True) organizations = OrganizationFactory.create_batch(4) course = CourseFactory( page_title="Very interesting course", fill_organizations=organizations, fill_categories=categories, fill_icons=icons, ) page = course.extended_object course_run1, _course_run2 = CourseRunFactory.create_batch( 2, page_parent=course.extended_object, languages=["en", "fr"] ) self.assertFalse(course_run1.extended_object.publish("en")) # Publish only 2 out of 4 categories, icons and organizations categories[0].extended_object.publish("en") categories[1].extended_object.publish("en") icons[0].extended_object.publish("en") icons[1].extended_object.publish("en") organizations[0].extended_object.publish("en") organizations[1].extended_object.publish("en") # The unpublished objects may have been published and unpublished which puts them in a # status different from objects that have never been published. # We want to test both cases. categories[2].extended_object.publish("en") categories[2].extended_object.unpublish("en") icons[2].extended_object.publish("en") icons[2].extended_object.unpublish("en") organizations[2].extended_object.publish("en") organizations[2].extended_object.unpublish("en") # The page should not be visible before it is published url = page.get_absolute_url() response = self.client.get(url) self.assertEqual(response.status_code, 404) # Publish and ensure content is correct page.publish("en") # Now we can publish children course runs: publish only 1 of the 2 course_run1.extended_object.parent_page.refresh_from_db() self.assertTrue(course_run1.extended_object.publish("en")) response = self.client.get(url) self.assertEqual(response.status_code, 200) self.assertContains( response, "<title>Very interesting course</title>", html=True ) self.assertContains( response, '<h1 class="course-detail__content__title">Very interesting course</h1>', html=True, ) # Only published categories should be present on the page for category in categories[:2]: self.assertContains( response, ( '<a class="category-plugin-tag" href="{:s}">' '<div class="category-plugin-tag__title">{:s}</div></a>' ).format( category.extended_object.get_absolute_url(), category.extended_object.get_title(), ), html=True, ) for category in categories[-2:]: self.assertNotContains(response, category.extended_object.get_title()) # Only published icons should be present on the page pattern = ( r'<a.*class="category-plugin-tag".*href="{link:s}".*>' r'<figure class="category-plugin-tag__figure">' r'<figcaption.*class="category-plugin-tag__figure__caption".*>' r".*{title:s}.*</figcaption>" r'<img src="/media/filer_public_thumbnails/filer_public/.*icon\.jpg.*alt=""/>' ) for icon in icons[:2]: self.assertIsNotNone( re.search( pattern.format( link=icon.extended_object.get_absolute_url(), title=icon.extended_object.get_title(), ), str(response.content), ) ) for icon in icons[-2:]: self.assertNotContains(response, icon.extended_object.get_title()) # Public organizations should be in response content for organization in organizations[:2]: self.assertContains( response, '<div class="organization-glimpse__title">{title:s}</div>'.format( title=organization.extended_object.get_title() ), html=True, ) # Draft organizations should not be in response content for organization in organizations[-2:]: self.assertNotContains( response, organization.extended_object.get_title(), html=True ) # Only the published course run should be in response content self.assertContains(response, "<dd>English and french</dd>", html=True, count=1)
def test_templatetags_get_related_category_pages_draft(self): """ On a draft page, the "get_related_category_pages" template tag should inject in the context, all categories related to a queryset of pages via a CategoryPlugin. """ page_main = PageFactory(template="richie/single_column.html", should_publish=True) page1, page2 = PageFactory.create_batch( 2, template="richie/single_column.html", should_publish=True) ( category_draft_page1_draft, category_draft_page1_published, category_draft_page2_draft, category_draft_page2_published, ) = [c.extended_object for c in CategoryFactory.create_batch(4)] ( category_published_page1_draft, category_published_page1_published, category_published_page2_draft, category_published_page2_published, ) = [ c.extended_object for c in CategoryFactory.create_batch(4, should_publish=True) ] self._attach_categories(page1, category_draft_page1_draft, category_published_page1_draft) self._attach_categories( page1.get_public_object(), category_draft_page1_published, category_published_page1_published, ) self._attach_categories(page2, category_draft_page2_draft, category_published_page2_draft) self._attach_categories( page2.get_public_object(), category_draft_page2_published, category_published_page2_published, ) request = RequestFactory().get("/") template = ( "{% load cms_tags category_tags %}" "{% get_related_category_pages pages as categories %}" "{% for category in categories %}{{ category.extended_object.id }}{% endfor %}" ) # 1. Test categories present on the draft page # - Linked with one of the pages with self.assertNumQueries(1): output = self.render_template_obj(template, { "current_page": page_main, "pages": [page1] }, request) expected = [category_draft_page1_draft, category_published_page1_draft] self.assertEqual(output, "".join([str(c.id) for c in expected])) # - Linked with either of the 2 pages with self.assertNumQueries(1): output = self.render_template_obj(template, { "current_page": page_main, "pages": [page1, page2] }, request) expected = [ category_draft_page1_draft, category_draft_page2_draft, category_published_page1_draft, category_published_page2_draft, ] self.assertEqual(output, "".join([str(c.id) for c in expected])) # - Linked with a page in a different publication status with self.assertNumQueries(1): output = self.render_template_obj( template, { "current_page": page_main, "pages": [page1.get_public_object()] }, request, ) expected = [category_draft_page1_draft, category_published_page1_draft] self.assertEqual(output, "") # 2. Test categories on the public page current_page = page_main.get_public_object() # - Linked with one of the pages with self.assertNumQueries(1): output = self.render_template_obj( template, { "current_page": current_page, "pages": [page1.get_public_object()] }, request, ) self.assertEqual( output, str(category_published_page1_published.get_public_object().id)) # - Linked with either of the 2 pages with self.assertNumQueries(1): output = self.render_template_obj( template, { "current_page": current_page, "pages": [page1.get_public_object(), page2.get_public_object()], }, request, ) expected = [ category_published_page1_published.get_public_object(), category_published_page2_published.get_public_object(), ] self.assertEqual(output, "".join([str(c.id) for c in expected])) # - Linked with a page in a different publication status with self.assertNumQueries(1): output = self.render_template_obj( template, { "current_page": current_page, "pages": [page1] }, request, ) self.assertEqual(output, "")
def test_templates_person_detail_cms_published_content(self): """ Validate that the important elements are displayed on a published person page """ # Categories published_category, extra_published_category = CategoryFactory.create_batch( 2, should_publish=True) unpublished_category = CategoryFactory() # Modify the draft version of the published category title_obj = published_category.extended_object.title_set.get( language="en") title_obj.title = "modified title" title_obj.save() # Organizations published_organization, extra_published_organization = OrganizationFactory.create_batch( 2, should_publish=True) unpublished_organization = OrganizationFactory() # Modify the draft version of the published organization title_obj = published_organization.extended_object.title_set.get( language="en") title_obj.title = "modified title" title_obj.save() person = PersonFactory( page_title="My page title", fill_portrait=True, fill_bio=True, fill_categories=[published_category, unpublished_category], fill_organizations=[ published_organization, unpublished_organization ], ) page = person.extended_object # The page should not be visible before it is published url = page.get_absolute_url() response = self.client.get(url) self.assertEqual(response.status_code, 404) # Publish the person page.publish("en") # Add a new category to the draft person page but don't publish the modification placeholder = page.placeholders.get(slot="categories") add_plugin( placeholder, CategoryPlugin, "en", page=extra_published_category.extended_object, ) # Add a new organization to the draft person page but don't publish the modification placeholder = page.placeholders.get(slot="organizations") add_plugin( placeholder, OrganizationPlugin, "en", page=extra_published_organization.extended_object, ) # Ensure the published page content is correct response = self.client.get(url) self.assertContains(response, "<title>My page title</title>", html=True, status_code=200) title = person.extended_object.get_title() self.assertContains( response, f'<h1 class="person-detail__card__content__title">{title:s}</h1>', html=True, ) # The published category should be on the page in its published version self.assertContains( response, ('<a class="category-plugin-tag" href="{:s}">' '<div class="category-plugin-tag__title">{:s}</div></a>').format( published_category.public_extension.extended_object. get_absolute_url(), published_category.public_extension.extended_object.get_title( ), ), html=True, ) # The other categories should not be leaked: # - new_category linked only on the draft person page self.assertNotContains( response, extra_published_category.extended_object.get_title(), html=True) # - unpublished category self.assertNotContains( response, unpublished_category.extended_object.get_title(), html=True) # - modified draft version of the published category self.assertNotContains(response, "modified title") # The published organization should be on the page in its published version self.assertContains( response, '<div class="organization-glimpse__title">{:s}</div>'.format( published_organization.public_extension.extended_object. get_title()), html=True, ) # The other categories should not be leaked: # - new_organization linked only on the draft person page self.assertNotContains( response, extra_published_organization.extended_object.get_title(), html=True, ) # - unpublished organization self.assertNotContains( response, unpublished_organization.extended_object.get_title(), html=True) # - modified draft version of the published organization self.assertNotContains(response, "modified title")