def test_models_subject_courses_copied_when_publishing(self): """ When publishing an subject, the links courses on the draft version of the subject should be copied. """ # Create draft courses course1, course2 = CourseFactory.create_batch(2) # Create a draft subject draft_subject = SubjectFactory(with_courses=[course1, course2]) # Publish course1 course1.extended_object.publish("en") course1.refresh_from_db() # The draft subject should see all courses self.assertEqual(set(draft_subject.courses.all()), {course1, course2}) self.assertEqual(set(draft_subject.courses.drafts()), {course1, course2}) # Publish the subject and check that the courses are copied draft_subject.extended_object.publish("en") published_subject = Subject.objects.get( extended_object__publisher_is_draft=False) self.assertEqual(set(published_subject.courses.all()), {course1.public_extension})
def test_cms_plugins_subject_render_on_draft_page(self): """ The subject 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 Subject subject = SubjectFactory(page_title="public title") subject_page = subject.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, SubjectPlugin, "en", **{"page": subject_page}) subject_page.publish("en") subject_page.unpublish("en") url = "{:s}?edit".format(page.get_absolute_url(language="en")) # The subject plugin should still be visible on the draft page response = self.client.get(url) self.assertContains(response, "public title") # Now modify the subject to have a draft different from the public version title_obj = subject_page.get_title_obj(language="en") title_obj.title = "draft title" title_obj.save() # The draft version of the subject plugin should now be visible response = self.client.get(url) self.assertContains(response, "draft title") self.assertNotContains(response, "public title")
def test_models_subject_get_courses_snapshots(self): """ Snapshot courses should be excluded from the list of courses returned. The new filter query we added to exclude snapshots should not create duplicates. Indeed, we had to add a "distinct" clause to the query so this test enforces it. """ # We create a root page because it was responsible for duplicate results when the # distinct clause is not applied. # This is because of the clause "extended_object__node__parent__cms_pages__..." # which is there to exclude snapshots but also acts on the main course page and # checks its parent (so the root page) and the duplicate comes from the fact that # the parent has a draft and a public page... so "cms_pages" has a cardinality of 2 root_page = create_i18n_page(published=True) subject = SubjectFactory(should_publish=True) course = CourseFactory(page_parent=root_page, fill_subjects=[subject], should_publish=True) CourseFactory( page_parent=course.extended_object, fill_subjects=[subject], should_publish=True, ) self.assertEqual(Course.objects.count(), 4) self.assertEqual(subject.get_courses().count(), 1) self.assertEqual(subject.public_extension.get_courses().count(), 1)
def test_models_subject_str(self): """ The string representation should be built with the title of the related page. Only 1 query to the associated page should be generated. """ page = create_page("Art", "courses/cms/subject_detail.html", "en") subject = SubjectFactory(extended_object=page) with self.assertNumQueries(1): self.assertEqual(str(subject), "Subject: Art")
def test_subject_cms_draft_content(self): """ A staff user should see a draft subject including its draft elements with an annotation """ user = UserFactory(is_staff=True, is_superuser=True) self.client.login(username=user.username, password="******") courses = CourseFactory.create_batch(4) subject = SubjectFactory(title="Very interesting subject", with_courses=courses) page = subject.extended_object # Publish only 2 out of 4 courses courses[0].extended_object.publish("en") courses[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. courses[2].extended_object.publish("en") courses[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.assertContains( response, "<title>Very interesting subject</title>", status_code=200, html=True, ) self.assertContains( response, '<h1 class="subject-detail__title">Very interesting subject</h1>', html=True, ) # The published courses should be present on the page for course in courses[:2]: self.assertContains( response, '<li class="subject-detail__content__courses__item">{:s}</li>'. format(course.extended_object.get_title()), html=True, ) # The draft course should also be present on the page with an annotation for styling for course in courses[-2:]: self.assertContains( response, '<li class="{element:s} {element:s}--draft">{title:s}</li>'. format( element="subject-detail__content__courses__item", title=course.extended_object.get_title(), ), html=True, )
def test_course_change_view_get(self): """ The admin change view should include the editable and readonly fields as expected. In particular, the relation fields should only include options for related objects in their draft version. """ user = UserFactory(is_staff=True, is_superuser=True) self.client.login(username=user.username, password="******") # Create a course course = CourseFactory() # Create an organization and publish it organization = OrganizationFactory() organization.extended_object.publish("en") organization.refresh_from_db() # Create a subject and publish it subject = SubjectFactory() subject.extended_object.publish("en") subject.refresh_from_db() # Get the admin change view url = reverse("admin:courses_course_change", args=[course.id]) response = self.client.get(url) # Check that the page includes all our fields self.assertContains( response, course.extended_object.get_title(), status_code=200 ) self.assertContains(response, course.active_session) self.assertContains( response, course.organization_main.extended_object.get_title() ) # Only the draft organization and subject should be proposed as options in select boxes self.assertContains( response, '<option value="{:d}">{!s}'.format(subject.id, subject) ) self.assertNotContains( response, '<option value="{:d}">{!s}'.format( subject.public_extension.id, subject.public_extension ), ) self.assertContains( response, '<option value="{:d}">{!s}'.format(organization.id, organization) ) self.assertNotContains( response, '<option value="{:d}">{!s}'.format( organization.public_extension.id, organization.public_extension ), )
def test_templates_subject_detail_cms_published_content(self): """ Validate that the important elements are displayed on a published subject page """ courses = CourseFactory.create_batch(4) subject = SubjectFactory(page_title="Very interesting subject", fill_courses=courses) page = subject.extended_object # Publish only 2 out of 4 courses courses[0].extended_object.publish("en") courses[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. courses[2].extended_object.publish("en") courses[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 the subject and ensure the content is correct page.publish("en") response = self.client.get(url) self.assertContains( response, "<title>Very interesting subject</title>", status_code=200, html=True, ) self.assertContains( response, '<h1 class="subject-detail__title">Very interesting subject</h1>', html=True, ) # Only published courses should be present on the page for course in courses[:2]: self.assertContains( response, '<p class="course-glimpse__content__title">{:s}</p>'.format( course.extended_object.get_title()), html=True, ) for course in courses[-2:]: self.assertNotContains(response, course.extended_object.get_title())
def test_models_subject_get_courses_several_languages(self): """ The courses should not be duplicated if they exist in several languages. """ subject = SubjectFactory(should_publish=True) CourseFactory( page_title={ "en": "my title", "fr": "mon titre" }, fill_subjects=[subject], should_publish=True, ) self.assertEqual(Course.objects.count(), 2) self.assertEqual(subject.get_courses().count(), 1)
def test_subject_factory_description(self): """ The SubjectFactory should be able to generate a plugin with a realistic fake description. """ subject = SubjectFactory(fill_description=True) # Check that the description plugin was created as expected description = subject.extended_object.placeholders.get( slot="description") self.assertEqual(description.cmsplugin_set.count(), 1) # The description plugin should contain paragraphs description_plugin = description.cmsplugin_set.get( plugin_type="CKEditorPlugin") self.assertIn("<p>", description_plugin.simple_text_ckeditor_simpletext.body)
def test_subject_factory_banner(self): """ The SubjectFactory should be able to generate a plugin with a realistic fake banner. """ subject = SubjectFactory(fill_banner=True) # Check that the logo plugin was created as expected banner = subject.extended_object.placeholders.get(slot="banner") self.assertEqual(banner.cmsplugin_set.count(), 1) # The banner plugin should point to one of our fixtures images banner_plugin = banner.cmsplugin_set.get(plugin_type="PicturePlugin") self.assertIn( "banner", os.path.basename( banner_plugin.djangocms_picture_picture.picture.file.name), )
def test_cms_plugins_subject_form_page_choices(self): """ The form to create a subject plugin should only list subject pages in the select box. """ class SubjectPluginModelForm(forms.ModelForm): """A form for testing the choices in the select box""" class Meta: model = SubjectPluginModel fields = ["page"] subject = SubjectFactory() other_page_title = "other page" create_page(other_page_title, "richie/fullwidth.html", settings.LANGUAGE_CODE) plugin_form = SubjectPluginModelForm() self.assertIn(subject.extended_object.get_title(), plugin_form.as_table()) self.assertNotIn(other_page_title, plugin_form.as_table())
def test_models_course_subjects_copied_when_publishing(self): """ When publishing a course, the links to draft subjects on the draft version of the course should be copied (clear then add) to set equivalent links between the corresponding published course and published subjects. """ # Create subjects: 2 published and 1 draft subject1, subject2 = SubjectFactory.create_batch(2, should_publish=True) subject3 = SubjectFactory() # Create a draft course draft_course = CourseFactory( with_subjects=[subject1, subject2, subject3]) # The draft course should see all subjects self.assertEqual(set(draft_course.subjects.all()), {subject1, subject2, subject3}) # Publish the course and check that the subjects are copied draft_course.extended_object.publish("en") published_course = Course.objects.get( extended_object__publisher_is_draft=False) self.assertEqual( set(published_course.subjects.all()), {subject1.public_extension, subject2.public_extension}, ) # A published subject should see the published course self.assertEqual(subject1.public_extension.courses.first(), published_course) # The subjects that are removed from the draft course should only be cleared from the # published course upon publishing draft_course.subjects.remove(subject1) self.assertEqual( set(published_course.subjects.all()), {subject1.public_extension, subject2.public_extension}, ) draft_course.extended_object.publish("en") self.assertEqual(set(published_course.subjects.all()), {subject2.public_extension}) # The published subject that was removed should not see the published course any more self.assertIsNone(subject1.public_extension.courses.first())
def test_models_subject_get_courses(self): """ It should be possible to retrieve the list of related courses on the subject instance. The number of queries should be minimal. """ subject = SubjectFactory(should_publish=True) courses = CourseFactory.create_batch(3, page_title="my title", fill_subjects=[subject], should_publish=True) retrieved_courses = subject.get_courses() with self.assertNumQueries(2): self.assertEqual(set(retrieved_courses), set(courses)) with self.assertNumQueries(0): for course in retrieved_courses: self.assertEqual( course.extended_object.prefetched_titles[0].title, "my title")
def test_subject_courses_copied_when_publishing(self): """ When publishing a subject, the links to draft courses on the draft version of the subject should be copied (clear then add) to the published version. Links to published courses should not be copied as they are redundant and not up-to-date. """ # Create draft courses course1, course2 = CourseFactory.create_batch(2) # Create a draft subject draft_subject = SubjectFactory(with_courses=[course1, course2]) # Publish course1 course1.extended_object.publish("en") course1.refresh_from_db() # The draft subject should see all courses and propose a custom filter to easily access # the draft versions self.assertEqual( set(draft_subject.courses.all()), {course1, course1.public_extension, course2}, ) self.assertEqual(set(draft_subject.courses.drafts()), {course1, course2}) # Publish the subject and check that the courses are copied draft_subject.extended_object.publish("en") published_subject = Subject.objects.get( extended_object__publisher_is_draft=False) self.assertEqual(set(published_subject.courses.all()), {course1, course2}) # When publishing, the courses that are obsolete should be cleared draft_subject.courses.remove(course2) self.assertEqual(set(published_subject.courses.all()), {course1, course2}) # courses on the published subject are only cleared after publishing the draft page draft_subject.extended_object.publish("en") self.assertEqual(set(published_subject.courses.all()), {course1})
def test_cms_plugins_subject_render_on_public_page(self): """ The subject plugin should render as expected on a public page. """ # Create a filer fake image image = FilerImageFactory() # Create a Subject subject = SubjectFactory(page_title={ "en": "public title", "fr": "titre publique" }) subject_page = subject.extended_object # Add logo to related placeholder logo_placeholder = subject_page.placeholders.get(slot="logo") add_plugin( logo_placeholder, PicturePlugin, "en", **{ "picture": image, "attributes": { "alt": "logo description" } }) add_plugin( logo_placeholder, PicturePlugin, "fr", **{ "picture": image, "attributes": { "alt": "description du logo" } }) # 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, SubjectPlugin, "en", **{"page": subject_page}) add_plugin(placeholder, SubjectPlugin, "fr", **{"page": subject_page}) subject_page.publish("en") subject_page.publish("fr") subject.refresh_from_db() page.publish("en") page.publish("fr") url = page.get_absolute_url(language="en") # The subject plugin should not be visible on the public page before it is published subject_page.unpublish("en") response = self.client.get(url) self.assertNotContains(response, "public title") # Republish the plugin subject_page.publish("en") # Now modify the subject to have a draft different from the public version title_obj = subject_page.get_title_obj(language="en") title_obj.tile = "draft title" title_obj.save() # Publishing the page again should make the plugin public page.publish("en") # Check the page content in English response = self.client.get(url) # Subject's title should be present as a link to the cms page # And CMS page title should be in title attribute of the link self.assertContains( response, '<a class="subject-plugin__body" href="/en/public-title/" title="{title:s}"' .format( title=subject.public_extension.extended_object.get_title()), status_code=200, ) # The subject's title should be wrapped in a div self.assertContains( response, '<div class="subject-plugin__title">{:s}</div>'.format( subject.public_extension.extended_object.get_title()), html=True, ) self.assertNotContains(response, "draft title") # Subject's logo should be present # pylint: disable=no-member self.assertContains(response, image.file.name) # Same checks in French url = page.get_absolute_url(language="fr") response = self.client.get(url) self.assertContains( response, '<a class="subject-plugin__body" href="/fr/titre-publique/" title="{title:s}"' .format( title=subject.public_extension.extended_object.get_title()), status_code=200, ) # pylint: disable=no-member self.assertContains(response, image.file.name)