def test_templates_blogpost_detail_author_empty(self): """ The empty message for blogpost author should be present in edition but not in published mode if its placeholder is empty. """ blogpost = BlogPostFactory() page = blogpost.extended_object page.publish("en") url = page.get_absolute_url() response = self.client.get(url) # Published view does not have empty author message self.assertEqual(response.status_code, 200) self.assertNotContains( response, ('<span class="blogpost-detail__empty">No author yet</span>'), html=True, ) user = UserFactory(is_staff=True, is_superuser=True) self.client.login(username=user.username, password="******") response = self.client.get(url, {"edit": "true"}) # Edition view does have empty author message self.assertEqual(response.status_code, 200) self.assertContains( response, ('<span class="blogpost-detail__empty">No author yet</span>'), html=True, )
def test_templates_blogpost_detail_cms_published_content_opengraph(self): """The blogpost logo should be used as opengraph image.""" blogpost = BlogPostFactory( fill_cover={ "original_filename": "cover.jpg", "default_alt_text": "my cover", }, should_publish=True, ) url = blogpost.extended_object.get_absolute_url() response = self.client.get(url) self.assertEqual(response.status_code, 200) self.assertContains(response, '<meta property="og:type" content="article" />') self.assertContains( response, f'<meta property="og:url" content="http://example.com{url:s}" />') pattern = ( r'<meta property="og:image" content="http://example.com' r"/media/filer_public_thumbnails/filer_public/.*cover\.jpg__845x500" ) self.assertIsNotNone(re.search(pattern, str(response.content))) self.assertContains( response, '<meta property="og:image:width" content="845" />') self.assertContains( response, '<meta property="og:image:height" content="500" />')
def test_cms_plugins_blogpost_render_on_draft_page(self): """ The blogpost plugin should render as expected on a draft page. """ staff = UserFactory(is_staff=True, is_superuser=True) self.client.login(username=staff.username, password="******") # Create a BlogPost blogpost = BlogPostFactory(page_title="public title") blogpost_page = blogpost.extended_object # Create a page to add the plugin to page = create_i18n_page("A page") placeholder = page.placeholders.get(slot="maincontent") add_plugin(placeholder, BlogPostPlugin, "en", **{"page": blogpost_page}) url = "{:s}?edit".format(page.get_absolute_url(language="en")) # The blogpost plugin should still be visible on the draft page response = self.client.get(url) self.assertContains(response, "public title") # Now modify the blogpost to have a draft different from the public version title_obj = blogpost_page.get_title_obj(language="en") title_obj.title = "draft title" title_obj.save() # The draft version of the blogpost plugin should now be visible response = self.client.get(url) self.assertContains(response, "draft title") self.assertNotContains(response, "public title") # Publication date block should be absent self.assertNotContains(response, "__date")
def test_templates_blogpost_detail_cms_draft_content(self): """ A staff user should see a draft blogpost including only its published linked objects. """ user = UserFactory(is_staff=True, is_superuser=True) self.client.login(username=user.username, password="******") category = CategoryFactory() published_category = CategoryFactory(should_publish=True) author = PersonFactory( page_title={"en": "Comte de Saint-Germain"}, should_publish=True ) blogpost = BlogPostFactory( page_title="Preums", fill_cover=True, fill_body=True, fill_categories=[category, published_category], fill_author=[author], ) page = blogpost.extended_object # The page should be visible as draft to the staff user url = page.get_absolute_url() response = self.client.get(url) self.assertContains( response, "<title>Preums</title>", html=True, status_code=200 ) self.assertContains( response, '<h1 class="blogpost-detail__title">Preums</h1>', html=True ) self.assertContains(response, "Comte de Saint-Germain", html=True) self.assertContains( response, ( '<a class="category-tag" ' 'href="{:s}"><span class="category-tag__title">{:s}</span></a>' ).format( published_category.extended_object.get_absolute_url(), published_category.extended_object.get_title(), ), html=True, ) self.assertContains( response, ( '<a class="category-tag category-tag--draft" ' 'href="{:s}"><span class="category-tag__title">{:s}</span></a>' ).format( category.extended_object.get_absolute_url(), category.extended_object.get_title(), ), html=True, ) self.assertContains( response, '<p class="blogpost-detail__pubdate">Not published yet</p>', html=True, )
def test_templates_blogpost_detail_open_graph_description_excerpt_max_length( self): """ An opengraph description should be cut if it exceeds more than 200 caracters """ blogpost = BlogPostFactory() page = blogpost.extended_object placeholder_value = ( "Long description that describes the page with a summary. " * 5) # Add an excerpt to the blogpost placeholder = blogpost.extended_object.placeholders.get(slot="excerpt") add_plugin( language="en", placeholder=placeholder, plugin_type="PlainTextPlugin", body=placeholder_value, ) page.publish("en") url = blogpost.extended_object.get_absolute_url() response = self.client.get(url) self.assertEqual(response.status_code, 200) cut = placeholder_value[0:200] self.assertContains( response, f'<meta property="og:description" content="{cut}" />', )
def test_models_person_get_blogposts_public_person_page(self): """ When a person is added on a draft blog post, the blog post should not be visible on the public person page until the blog post is published. """ person = PersonFactory(should_publish=True) person_page = person.extended_object blog_post = BlogPostFactory(page_title="my title", should_publish=True) blog_post_page = blog_post.extended_object # Add a person to the blog post but don't publish the modification placeholder = blog_post_page.placeholders.get(slot="author") add_plugin(placeholder, PersonPlugin, "en", page=person_page) self.assertEqual(list(person.get_blogposts()), [blog_post]) self.assertEqual(list(person.public_extension.get_blogposts()), []) # Now publish the modification and check that the blog post is displayed # on the public person page blog_post.extended_object.publish("en") self.assertEqual(list(person.public_extension.get_blogposts()), [blog_post.public_extension]) # If the blog post is unpublished, it should not be displayed on the public # page anymore blog_post_page.unpublish("en") self.assertEqual(list(person.get_blogposts()), [blog_post]) self.assertEqual(list(person.public_extension.get_blogposts()), [])
def test_cms_wizards_blogpost_submit_form_slug_duplicate(self): """ Trying to create a blog post with a slug that would lead to a duplicate path should raise a validation error. """ # A parent page should pre-exist parent_page = create_page( "News", "richie/single_column.html", "en", reverse_id=BlogPost.PAGE["reverse_id"], ) # Create an existing page with a known slug BlogPostFactory(page_parent=parent_page, page_title="My title") # Submit a title that will lead to the same slug data = {"title": "my title"} user = UserFactory(is_staff=True, is_superuser=True) form = BlogPostWizardForm(data=data, wizard_language="en", wizard_user=user) self.assertFalse(form.is_valid()) self.assertEqual(form.errors, {"slug": ["This slug is already in use"]})
def test_cms_plugins_blogpost_cascade_variant(self): """ If the variant is not specified on the blogpost plugin, it should render according to variant variable eventually present in the context of its container. """ # Create an blogpost blogpost = BlogPostFactory(page_title="public title", should_publish=True) blogpost_page = blogpost.extended_object # Create a page to add the plugin to page = create_i18n_page("A page") placeholder = page.placeholders.get(slot="maincontent") # Add blogpost plugin with default template model_instance = add_plugin(placeholder, BlogPostPlugin, "en", page=blogpost_page) # Get generated html request = RequestFactory() request.current_page = page request.path_info = "/en/my-path/" request.user = AnonymousUser() context = { "current_page": page, "blogpost_variant": "xxl", "request": request } renderer = ContentRenderer(request=request) html = renderer.render_plugin(model_instance, context) self.assertIn("blogpost-xxl", html)
def test_cms_plugins_blogpost_render_template(self): """ The blogpost plugin should render according to variant choice. """ staff = UserFactory(is_staff=True, is_superuser=True) self.client.login(username=staff.username, password="******") # Create an blogpost blogpost = BlogPostFactory(page_title="public title", should_publish=True) blogpost_page = blogpost.extended_object # Create a page to add the plugin to page = create_i18n_page("A page") placeholder = page.placeholders.get(slot="maincontent") # Add blogpost plugin with default template add_plugin(placeholder, BlogPostPlugin, "en", page=blogpost_page) url = "{:s}?edit".format(page.get_absolute_url(language="en")) # The blogpost-glimpse default variant should be glimpse response = self.client.get(url) self.assertContains(response, "blogpost-glimpse") # Add blogpost plugin with small variant add_plugin(placeholder, BlogPostPlugin, "en", page=blogpost_page, variant="small") # The blogpost-glimpse default template should not have the small attribute response = self.client.get(url) self.assertContains(response, "blogpost-small")
def test_templates_blogpost_detail_cms_published_content(self): """ Validate that the important elements are displayed on a published blogpost page """ author = PersonFactory(page_title={"en": "Comte de Saint-Germain"}, should_publish=True) blogpost = BlogPostFactory(page_title="Preums", fill_cover=True, fill_body=True, fill_author=[author]) page = blogpost.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 blogpost and ensure the content is correct page.publish("en") response = self.client.get(url) self.assertContains(response, "<title>Preums</title>", html=True, status_code=200) self.assertContains(response, '<h1 class="blogpost-detail__title">Preums</h1>', html=True) self.assertContains(response, "Comte de Saint-Germain", html=True)
def test_templates_blogpost_detail_open_graph_description_excerpt(self): """ An opengraph description meta should be present if the excerpt placeholder is set. """ blogpost = BlogPostFactory() page = blogpost.extended_object # Add an excerpt to the blogpost placeholder = blogpost.extended_object.placeholders.get(slot="excerpt") add_plugin( language="en", placeholder=placeholder, plugin_type="PlainTextPlugin", body="A further sub title of the blog post", ) page.publish("en") url = blogpost.extended_object.get_absolute_url() response = self.client.get(url) self.assertEqual(response.status_code, 200) self.assertContains( response, '<meta property="og:description" content="A further sub title of the blog post" />', )
def test_templates_blogpost_detail_meta_description_excerpt(self): """ The blogpost meta description should show the excerpt placeholder if no meta_description placeholoder exists """ blogpost = BlogPostFactory() page = blogpost.extended_object # Add an excerpt to the blogpost placeholder = blogpost.extended_object.placeholders.get(slot="excerpt") add_plugin( language="en", placeholder=placeholder, plugin_type="PlainTextPlugin", body="A further sub title of the blog post", ) page.publish("en") url = blogpost.extended_object.get_absolute_url() response = self.client.get(url) self.assertEqual(response.status_code, 200) self.assertContains( response, '<meta name="description" content="A further sub title of the blog post" />', )
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_category_get_blogposts_several_languages(self): """ The blogposts should not be duplicated if they exist in several languages. """ category = CategoryFactory(should_publish=True) BlogPostFactory( page_title={"en": "my title", "fr": "mon titre"}, fill_categories=[category], should_publish=True, ) self.assertEqual(BlogPost.objects.count(), 2) self.assertEqual(category.get_blogposts().count(), 1)
def test_templates_blogpost_list_cms_content(self): """ Validate that the public website only displays blogposts that are currently published, while staff users can see draft and unpublished blogposts. """ page = PageFactory( template="courses/cms/blogpost_list.html", title__language="en", should_publish=True, ) BlogPostFactory(page_parent=page, page_title="First post") BlogPostFactory(page_parent=page, page_title="Second post", should_publish=True) # Publish with a publication date in the future future = timezone.now() + timedelta(hours=1) with mock.patch("cms.models.pagemodel.now", return_value=future): BlogPostFactory(page_parent=page, page_title="Third post", should_publish=True) # Anonymous users should only see published blogposts response = self.client.get(page.get_absolute_url()) self.assertEqual(response.status_code, 200) self.assertNotContains(response, "First") self.assertContains(response, "Second") self.assertNotContains(response, "Third") # Staff users can see draft and unpublished blogposts user = UserFactory(is_staff=True, is_superuser=True) self.client.login(username=user.username, password="******") response = self.client.get(page.get_absolute_url()) self.assertEqual(response.status_code, 200) for title in ["First", "Second", "Third"]: self.assertContains(response, title)
def test_models_person_get_blogposts_several_languages(self): """ The blogposts should not be duplicated if they exist in several languages. """ person = PersonFactory(should_publish=True) BlogPostFactory( page_title={ "en": "my title", "fr": "mon titre" }, fill_author=[person], should_publish=True, ) self.assertEqual(BlogPost.objects.count(), 2) self.assertEqual(person.get_blogposts().count(), 1)
def test_templates_blogpost_detail_open_graph_description_excerpt_empty(self): """ The opengraph description meta should be missing if the excerpt placeholder is not set. """ blogpost = BlogPostFactory() page = blogpost.extended_object page.publish("en") url = blogpost.extended_object.get_absolute_url() response = self.client.get(url) self.assertEqual(response.status_code, 200) self.assertNotContains( response, "og:description", )
def test_models_blogpost_get_related_blogposts(self): """ Blogposts related via a plugin on the draft page should appear in their draft version on the draft page. Blogposts related via a plugin on the public page should appear in their public version on the public page. """ blogpost1, blogpost2, blogpost3 = BlogPostFactory.create_batch( 3, should_publish=True ) category = CategoryFactory(should_publish=True) # Attach categories self._attach_category(blogpost1, category) self._attach_category(blogpost2, category) self._attach_category(blogpost1.public_extension, category) self._attach_category(blogpost3.public_extension, category) # Draft blogposts with self.assertNumQueries(1): self.assertEqual(list(blogpost1.get_related_blogposts()), [blogpost2]) with self.assertNumQueries(1): self.assertEqual(list(blogpost2.get_related_blogposts()), [blogpost1]) # 2 queries because blogpost3 page fields were not retrieved when attaching categories with self.assertNumQueries(2): self.assertFalse(blogpost3.get_related_blogposts().exists()) blogpost1_public = blogpost1.public_extension with self.assertNumQueries(1): self.assertEqual( list(blogpost1_public.get_related_blogposts()), [blogpost3.public_extension], ) # Public blogposts blogpost2_public = blogpost2.public_extension # 2 queries because blogpost2.public_extension page fields were not retrieved when # attaching categories with self.assertNumQueries(2): self.assertFalse(blogpost2_public.get_related_blogposts().exists()) blogpost3_public = blogpost3.public_extension with self.assertNumQueries(1): self.assertEqual( list(blogpost3_public.get_related_blogposts()), [blogpost1.public_extension], )
def test_factories_blogpost_body(self): """ BlogPostFactory should be able to generate plugins with a realistic body for several languages. """ blogpost = BlogPostFactory(page_languages=["fr", "en"], fill_body=True) # Check that the body plugins were created as expected body = blogpost.extended_object.placeholders.get(slot="body") self.assertEqual(body.cmsplugin_set.count(), 2) # The body plugins should contain paragraphs for language in ["fr", "en"]: description_plugin = body.cmsplugin_set.get( plugin_type="TextPlugin", language=language) self.assertIn("<p>", description_plugin.djangocms_text_ckeditor_text.body)
def test_templates_blogpost_detail_meta_description_empty(self): """ The blogpost meta description should not be present if neither the meta_description field on the page, nor the excerpt placeholder are filled """ blogpost = BlogPostFactory() page = blogpost.extended_object page.publish("en") url = blogpost.extended_object.get_absolute_url() response = self.client.get(url) self.assertEqual(response.status_code, 200) self.assertNotContains( response, '<meta name="description"', )
def test_templates_person_detail_related_blog_posts(self): """ The blog posts written by a person should appear on this person's detail page. """ user = UserFactory(is_staff=True, is_superuser=True) self.client.login(username=user.username, password="******") person = PersonFactory() blog_post = BlogPostFactory(fill_author=[person]) url = person.extended_object.get_absolute_url() response = self.client.get(url) html = lxml.html.fromstring(response.content) # The blog post should be present on the page title = html.cssselect("h3.blogpost-glimpse__title")[0] self.assertEqual(title.text_content().strip(), blog_post.extended_object.get_title())
def test_models_category_get_blogposts_ordering(self): """The related blogposts should be sorted by their position in the pages tree.""" category = CategoryFactory(should_publish=True) blogpost1, blogpost2, blogpost3 = BlogPostFactory.create_batch( 3, fill_categories=[category], should_publish=True) self.assertEqual(list(category.get_blogposts()), [blogpost1, blogpost2, blogpost3]) # Move pages in the tree and check that they are returned in the new order blogpost3.extended_object.move_page(blogpost1.extended_object.node, position="left") self.assertEqual(list(category.get_blogposts()), [blogpost3, blogpost1, blogpost2]) blogpost1.extended_object.move_page(blogpost3.extended_object.node, position="left") self.assertEqual(list(category.get_blogposts()), [blogpost1, blogpost3, blogpost2])
def test_models_category_get_blogposts(self): """ It should be possible to retrieve the list of related blogposts on the category instance. The number of queries should be minimal. """ category = CategoryFactory(should_publish=True) blogposts = BlogPostFactory.create_batch( 2, page_title="my title", fill_categories=[category], should_publish=True ) retrieved_blogposts = category.get_blogposts() with self.assertNumQueries(2): self.assertEqual(set(retrieved_blogposts), set(blogposts)) with self.assertNumQueries(0): for blogpost in retrieved_blogposts: self.assertEqual( blogpost.extended_object.prefetched_titles[0].title, "my title" )
def test_templates_person_detail_related_blog_posts(self): """ The blog posts written by a person should appear on this person's detail page. """ user = UserFactory(is_staff=True, is_superuser=True) self.client.login(username=user.username, password="******") person = PersonFactory() blog_post = BlogPostFactory(fill_author=[person]) url = person.extended_object.get_absolute_url() response = self.client.get(url) # The blog post should be present on the page self.assertContains( response, f'<p class="blogpost-glimpse__title">{blog_post.extended_object.get_title():s}</p>', html=True, )
def test_cms_plugins_blogpost_fallback_when_published_unpublished(self): """ The blogpost plugin should not render when the blogpost was voluntarily unpublished in the current language. """ # Create a blogpost blogpost = BlogPostFactory( page_title={ "en": "public title", "fr": "titre public" }, fill_cover={ "original_filename": "cover.jpg", "default_alt_text": "my cover", }, ) blogpost_page = blogpost.extended_object # Create a page to add the plugin to page = create_i18n_page({"en": "A page", "fr": "Une page"}) placeholder = page.placeholders.get(slot="maincontent") add_plugin(placeholder, BlogPostPlugin, "en", **{"page": blogpost_page}) add_plugin(placeholder, BlogPostPlugin, "fr", **{"page": blogpost_page}) # Publish only the French version of the blog post with mock.patch( "cms.models.pagemodel.now", return_value=datetime(2019, 11, 30, tzinfo=timezone.utc), ): blogpost_page.publish("fr") blogpost_page.publish("en") blogpost_page.unpublish("en") # Check the page content in English page.publish("en") url = page.get_absolute_url(language="en") response = self.client.get(url) self.assertNotContains(response, "glimpse")
def test_cms_plugins_blogpost_form_page_choices(self): """ The form to create a blogpost plugin should only list blogpost pages in the select box. """ class BlogPostPluginModelForm(forms.ModelForm): """A form for testing the choices in the select box""" class Meta: model = BlogPostPluginModel fields = ["page"] blog_page = create_i18n_page("my title", published=True) blogpost = BlogPostFactory(page_parent=blog_page) other_page_title = "other page" create_page(other_page_title, "richie/single_column.html", settings.LANGUAGE_CODE) plugin_form = BlogPostPluginModelForm() rendered_form = plugin_form.as_table() self.assertEqual( rendered_form.count(blogpost.extended_object.get_title()), 1) self.assertNotIn(other_page_title, plugin_form.as_table())
def test_factories_blogpost_cover(self): """ BlogPostFactory should be able to generate plugins with a realistic cover for several languages. """ blogpost = BlogPostFactory(page_languages=["fr", "en"], fill_cover=True) # Check that the cover plugins were created as expected cover = blogpost.extended_object.placeholders.get(slot="cover") self.assertEqual(cover.cmsplugin_set.count(), 2) # The cover plugins should point to one of our fixtures images for language in ["fr", "en"]: cover_plugin = cover.cmsplugin_set.get( plugin_type="SimplePicturePlugin", language=language) self.assertIn( "cover", os.path.basename( cover_plugin.djangocms_picture_picture.picture.file.name), )
def test_templates_blogpost_detail_meta_description(self): """ The blogpost meta description should show meta_description placeholder if defined """ blogpost = BlogPostFactory() page = blogpost.extended_object title_obj = page.get_title_obj(language="en") title_obj.meta_description = "A custom description of the blog post" title_obj.save() page.publish("en") url = blogpost.extended_object.get_absolute_url() response = self.client.get(url) self.assertEqual(response.status_code, 200) self.assertContains( response, '<meta name="description" content="A custom description of the blog post" />', )
def test_templates_blogpost_detail_cms_published_content(self): """ Validate that the important elements are displayed on a published blogpost page """ author = PersonFactory(page_title={"en": "Comte de Saint-Germain"}, should_publish=True) blogpost = BlogPostFactory(page_title="Preums", fill_cover=True, fill_body=True, fill_author=[author]) page = blogpost.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 blogpost with a past date and ensure the content is correct with mock.patch( "cms.models.pagemodel.now", return_value=datetime(2019, 11, 27, tzinfo=timezone.utc), ): page.publish("en") response = self.client.get(url) self.assertContains(response, "<title>Preums</title>", html=True, status_code=200) self.assertContains(response, '<h1 class="blogpost-detail__title">Preums</h1>', html=True) self.assertNotContains(response, "Comte de Saint-Germain", html=True) self.assertContains( response, '<p class="blogpost-detail__pubdate">11/27/2019</p>', html=True, )
def test_models_category_get_blogposts_public_category_page(self): """ When a category is added on a draft blog post, the blog post should not be visible on the public category page until the blog post is published. """ category = CategoryFactory(should_publish=True) category_page = category.extended_object blog_post = BlogPostFactory(page_title="my title", should_publish=True) blog_post_page = blog_post.extended_object # Add a category to the blog post but don't publish the modification placeholder = blog_post_page.placeholders.get(slot="categories") add_plugin(placeholder, CategoryPlugin, "en", page=category_page) self.assertEqual(list(category.get_blogposts()), [blog_post]) self.assertEqual(list(category.public_extension.get_blogposts()), []) # Now publish the modification and check that the blog post is displayed # on the public category page blog_post.extended_object.publish("en") self.assertEqual( list(category.public_extension.get_blogposts()), [blog_post.public_extension], )