Exemple #1
0
    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_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
            ),
        )
Exemple #3
0
 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)
Exemple #4
0
    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")
Exemple #6
0
 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")
Exemple #7
0
    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_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,
            )
Exemple #9
0
    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_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())
Exemple #11
0
    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)
Exemple #12
0
    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_course_change_view_post(self):
        """
        Validate that the course can be updated via the admin.
        In particular, make sure that when a course is updated from the admin, the main
        organization is automatically added to the many-to-many field "organizations".
        See http://stackoverflow.com/a/1925784/469575 for details on the issue.
        """
        user = UserFactory(is_staff=True, is_superuser=True)
        self.client.login(username=user.username, password="******")

        # Create a course, some organizations and some subjects
        organization1, organization2, organization3 = OrganizationFactory.create_batch(
            3
        )
        subject1, subject2 = SubjectFactory.create_batch(2)
        course = CourseFactory(
            with_organizations=[organization1], with_subjects=[subject1]
        )
        self.assertEqual(
            set(course.organizations.all()), {organization1, course.organization_main}
        )
        self.assertEqual(set(course.subjects.all()), {subject1})

        # Get the admin change view
        url = reverse("admin:courses_course_change", args=[course.id])
        data = {
            "active_session": "xyz",
            "organization_main": organization2.id,
            "organizations": [organization3.id],
            "subjects": [subject2.id],
        }
        response = self.client.post(url, data)
        self.assertEqual(response.status_code, 302)

        # Check that the course was updated as expected
        course.refresh_from_db()
        self.assertEqual(course.active_session, "xyz")
        self.assertEqual(course.organization_main, organization2)
        self.assertEqual(set(course.subjects.all()), {subject2})
        # Check that the main organization was added and the old organization cleared
        self.assertEqual(
            set(course.organizations.all()), {organization2, organization3}
        )
Exemple #15
0
    def test_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 the published version.
        Links to published subjects should not be copied as they are redundant and not
        up-to-date.
        """
        # Create draft subjects
        subject1, subject2 = SubjectFactory.create_batch(2)

        # Create a draft course
        draft_course = CourseFactory(with_subjects=[subject1, subject2])

        # Publish subject1
        subject1.extended_object.publish("en")
        subject1.refresh_from_db()

        # The draft course should see all subjects and propose a custom filter to easily access
        # the draft versions
        self.assertEqual(
            set(draft_course.subjects.all()),
            {subject1, subject1.public_extension, subject2},
        )
        self.assertEqual(set(draft_course.subjects.drafts()),
                         {subject1, subject2})

        # 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, subject2})

        # When publishing, the subjects that are obsolete should be cleared
        draft_course.subjects.remove(subject2)
        self.assertEqual(set(published_course.subjects.all()),
                         {subject1, subject2})

        # Subjects on the published course are only cleared after publishing the draft page
        draft_course.extended_object.publish("en")
        self.assertEqual(set(published_course.subjects.all()), {subject1})
Exemple #16
0
    def test_course_cms_published_content(self):
        """
        Validate that the important elements are displayed on a published course page
        """
        subjects = SubjectFactory.create_batch(4)
        organizations = OrganizationFactory.create_batch(4)

        course = CourseFactory(
            organization_main=organizations[0],
            title="Very interesting course",
            with_organizations=organizations,
            with_subjects=subjects,
        )
        page = course.extended_object

        # Publish only 2 out of 4 subjects and 2 out of 4 organizations
        subjects[0].extended_object.publish("en")
        subjects[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.
        subjects[2].extended_object.publish("en")
        subjects[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")
        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__title">Very interesting course</h1>',
            html=True,
        )
        self.assertContains(
            response,
            '<div class="course-detail__aside__active-session">{:s}</div>'.
            format(course.active_session),
            html=True,
        )

        # Only published subjects should be present on the page
        for subject in subjects[:2]:
            self.assertContains(
                response,
                '<li class="course-detail__content__subjects__item">{:s}</li>'.
                format(subject.extended_object.get_title()),
                html=True,
            )
        for subject in subjects[-2:]:
            self.assertNotContains(response,
                                   subject.extended_object.get_title())

        # organization 1 is marked as main organization
        self.assertContains(
            response,
            '<li class="{element:s} {element:s}--main">{title:s}</li>'.format(
                element="course-detail__content__organizations__item",
                title=organizations[0].extended_object.get_title(),
            ),
            html=True,
        )

        # organization 2 is the only "common" org in listing
        self.assertContains(
            response,
            '<li class="course-detail__content__organizations__item">{:s}</li>'
            .format(organizations[1].extended_object.get_title()),
            html=True,
        )

        # Draft organization should not be in response content
        for organization in organizations[-2:]:
            self.assertNotContains(response,
                                   organization.extended_object.get_title(),
                                   html=True)
Exemple #17
0
    def test_templates_course_run_detail_cms_published_content(self, _):
        """
        Validate that the important elements are displayed on a published course run page
        """
        subjects = SubjectFactory.create_batch(4)
        organizations = OrganizationFactory.create_batch(4)

        course = CourseFactory(
            page_title="Very interesting course",
            fill_organizations=organizations,
            fill_subjects=subjects,
            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),
            enrollment_end=datetime(2019, 1, 18),
            start=datetime(2018, 12, 10),
            end=datetime(2019, 2, 14),
            languages=["en", "fr"],
        )
        page = course_run.extended_object

        # Publish only 2 out of 4 subjects and 2 out of 4 organizations
        subjects[0].extended_object.publish("en")
        subjects[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.
        subjects[2].extended_object.publish("en")
        subjects[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="course-detail__content__title">'
            "Very interesting course<br>first session</h1>",
            html=True,
        )

        # Only published subjects should be present on the page
        for subject in subjects[:2]:
            self.assertContains(
                response,
                '<a class="subject-plugin-tag" href="{:s}">{:s}</a>'.format(
                    subject.extended_object.get_absolute_url(),
                    subject.extended_object.get_title(),
                ),
                html=True,
            )
        for subject in subjects[-2:]:
            self.assertNotContains(response, subject.extended_object.get_title())

        # Public organizations should be in response content
        for organization in organizations[:2]:
            self.assertContains(
                response,
                '<div class="organization-plugin__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, "<dt>Enrollment starts</dt><dd>Oct. 21, 2018</dd>"
        )
        self.assertContains(response, "<dt>Enrollment ends</dt><dd>Jan. 18, 2019</dd>")
        self.assertContains(response, "<dt>Course starts</dt><dd>Dec. 10, 2018</dd>")
        self.assertContains(response, "<dt>Course ends</dt><dd>Feb. 14, 2019</dd>")
        self.assertContains(
            response,
            '<a class="course-detail__content__run__block__cta" '
            'href="https://www.example.com/enroll">Enroll now</a>',
            html=True,
        )
        self.assertContains(response, "<dt>Languages</dt><dd>English, French</dd>")
    def test_cms_plugins_course_render_on_public_page(self):
        """
        Test that an CoursePlugin correctly renders course's page specific information
        """
        # Create an Course with a page in both english and french
        subjects = SubjectFactory.create_batch(4)
        organization = OrganizationFactory(page_title="public title")

        course = CourseFactory(
            page_title={
                "en": "public title",
                "fr": "titre public"
            },
            fill_organizations=[organization],
            fill_subjects=subjects,
        )
        course_page = course.extended_object

        course_page.publish("en")
        course_page.publish("fr")

        # 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, CoursePlugin, "en", **{"page": course_page})
        add_plugin(placeholder, CoursePlugin, "fr", **{"page": course_page})

        page.publish("en")
        page.publish("fr")

        # Check the page content in English
        url = page.get_absolute_url(language="en")
        response = self.client.get(url)
        self.assertContains(response, "public title")
        self.assertNotContains(response, "titre public")

        # The course's url should be present
        self.assertContains(
            response,
            '<a class="course-plugin" href="{url}"'.format(
                url=course_page.get_absolute_url()),
            status_code=200,
        )

        # The course's name should be present
        self.assertContains(
            response,
            '<p class="course-glimpse__content__title">{title}</p>'.format(
                title=course_page.get_title()),
            status_code=200,
        )
        # The course's main organization should be present
        self.assertContains(
            response,
            "<p>{title}</p>".format(
                title=organization.extended_object.get_title()),
            status_code=200,
        )

        # The draft course plugin should not be present
        # Check if draft is shown after unpublish
        course_page.unpublish("en")
        page.publish("en")
        response = self.client.get(url)
        self.assertNotContains(response, "public title")
        self.assertNotContains(response, course_page.get_absolute_url())

        # Check the page content in french
        url = page.get_absolute_url(language="fr")
        response = self.client.get(url)
        self.assertContains(response, "titre public")

        # The course's url should be present
        self.assertContains(
            response,
            '<a class="course-plugin" href="{url}"'.format(
                url=course_page.get_absolute_url()),
            status_code=200,
        )

        # The course's name should be present
        self.assertContains(
            response,
            '<p class="course-glimpse__content__title">{title}</p>'.format(
                title=course_page.get_title()),
            status_code=200,
        )

        # The course's main organization should be present
        self.assertContains(
            response,
            "<p>{title}</p>".format(
                title=organization.extended_object.get_title()),
            status_code=200,
        )

        # The draft course plugin should not be present
        # Check if draft is shown after unpublish
        course_page.unpublish("fr")
        page.publish("fr")
        response = self.client.get(url)
        self.assertNotContains(response, "titre public")
        self.assertNotContains(response, course_page.get_absolute_url())
Exemple #19
0
    def test_course_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="******")

        subjects = SubjectFactory.create_batch(4)
        organizations = OrganizationFactory.create_batch(4)

        course = CourseFactory(
            organization_main=organizations[0],
            title="Very interesting course",
            with_organizations=organizations,
            with_subjects=subjects,
        )
        page = course.extended_object

        # Publish only 2 out of 4 subjects and 2 out of 4 organizations
        subjects[0].extended_object.publish("en")
        subjects[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.
        subjects[2].extended_object.publish("en")
        subjects[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>Very interesting course</title>",
                            html=True)
        self.assertContains(
            response,
            '<h1 class="course-detail__title">Very interesting course</h1>',
            html=True,
        )
        self.assertContains(
            response,
            '<div class="course-detail__aside__active-session">{:s}</div>'.
            format(course.active_session),
            html=True,
        )

        # organization 1 is marked as main and not duplicated
        self.assertContains(
            response,
            '<li class="{element:s} {element:s}--main">{title:s}</li>'.format(
                element="course-detail__content__organizations__item",
                title=organizations[0].extended_object.get_title(),
            ),
            html=True,
        )
        self.assertNotContains(
            response,
            ('<li class="course-detail__content__organizations__item">{:s}</li>'
             ).format(organizations[0].extended_object.get_title()),
            html=True,
        )
        # organization 2 is not marked as a draft since it has been published
        self.assertContains(
            response,
            '<li class="course-detail__content__organizations__item">{:s}</li>'
            .format(organizations[1].extended_object.get_title()),
            html=True,
        )
        # Draft organizations should be present on the page with an annotation for styling
        for organization in organizations[:2]:
            self.assertNotContains(
                response,
                '<li class="{element:s} {element:s}--draft">{title:s}</li>'.
                format(
                    element="course - detail__content__organizations__item",
                    title=organization.extended_object.get_title(),
                ),
                html=True,
            )

        # The published subjects should be present on the page
        for subject in subjects[:2]:
            self.assertContains(
                response,
                '<li class="course-detail__content__subjects__item">{:s}</li>'.
                format(subject.extended_object.get_title()),
                html=True,
            )
        # Draft subjects should also be present on the page with an annotation for styling
        for subject in subjects[-2:]:
            self.assertContains(
                response,
                '<li class="{element:s} {element:s}--draft">{title:s}</li>'.
                format(
                    element="course-detail__content__subjects__item",
                    title=subject.extended_object.get_title(),
                ),
                html=True,
            )
Exemple #20
0
def create_demo_site():
    """
    Create a simple site tree structure for developpers to work in realistic environment.

    We create multilingual pages, add organizations under the related page and add
    plugins to each page.
    """
    site = Site.objects.get(id=1)

    # Create pages as described in PAGES_INFOS
    pages_created = recursive_page_creation(site, PAGE_INFOS)

    # Create some licences
    licences = LicenceFactory.create_batch(NB_LICENCES)

    # Create organizations under the `Organizations` page
    organizations = OrganizationFactory.create_batch(
        NB_ORGANIZATIONS,
        languages=[l[0] for l in settings.LANGUAGES],
        parent=pages_created["organizations"],
        fill_banner=True,
        fill_description=True,
        fill_logo=True,
        should_publish=True,
        in_navigation=True,
    )

    # Create subjects under the `Subjects` page
    subjects = SubjectFactory.create_batch(
        NB_SUBJECTS,
        languages=[l[0] for l in settings.LANGUAGES],
        parent=pages_created["subjects"],
        fill_banner=True,
        fill_description=True,
        fill_logo=True,
        should_publish=True,
        in_navigation=True,
    )

    # Django parler require a language to be manually set when working out of
    # request/response flow and PersonTitle use 'parler'
    translation.activate(settings.LANGUAGE_CODE)

    # Create persons under the `persons` page
    persons = PersonFactory.create_batch(
        NB_PERSONS,
        languages=[l[0] for l in settings.LANGUAGES],
        parent=pages_created["persons"],
        fill_portrait=True,
        fill_resume=True,
        should_publish=True,
        in_navigation=True,
    )

    # Create courses under the `Course` page with subjects and organizations
    # relations
    for _ in range(NB_COURSES):
        course_organizations = random.sample(
            organizations, NB_COURSES_ORGANIZATION_RELATIONS)
        video_sample = random.choice(VIDEO_SAMPLE_LINKS)

        course = CourseFactory(
            languages=[l[0] for l in settings.LANGUAGES],
            parent=pages_created["courses"],
            organization_main=random.choice(course_organizations),
            fill_licences=[
                ("course_license_content", random.choice(licences)),
                ("course_license_participation", random.choice(licences)),
            ],
            fill_team=random.sample(persons, NB_COURSES_PERSONS_PLUGINS),
            fill_teaser=video_sample,
            fill_cover=video_sample.image,
            fill_texts=[
                "course_syllabus",
                "course_format",
                "course_prerequisites",
                "course_plan",
                # "course_license_content",
                # "course_license_participation",
            ],
            with_organizations=course_organizations,
            with_subjects=random.sample(subjects,
                                        NB_COURSES_SUBJECT_RELATIONS),
            should_publish=True,
            in_navigation=True,
        )
        # Add a random number of course runs to the course
        nb_course_runs = get_number_of_course_runs()
        if nb_course_runs > 0:
            CourseRunFactory.create_batch(nb_course_runs, course=course)
Exemple #21
0
    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="******")

        subjects = SubjectFactory.create_batch(4)
        organizations = OrganizationFactory.create_batch(4)

        course = CourseFactory(
            page_title="Very interesting course",
            fill_organizations=organizations,
            fill_subjects=subjects,
        )
        page = course.extended_object
        course_run1, _course_run2 = CourseRunFactory.create_batch(
            2, page_parent=course.extended_object, languages=["en", "fr"]
        )

        # Publish only 1 of the course runs
        course_run1.extended_object.publish("en")

        # Publish only 2 out of 4 subjects and 2 out of 4 organizations
        subjects[0].extended_object.publish("en")
        subjects[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.
        subjects[2].extended_object.publish("en")
        subjects[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>Very interesting course</title>", html=True
        )
        self.assertContains(
            response,
            '<h1 class="course-detail__content__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-plugin__title">{title:s}</div>'.format(
                    title=organization.extended_object.get_title()
                ),
                html=True,
            )

        # Draft organizations should be annotated for styling
        self.assertContains(response, "organization-plugin-container--draft", count=2)

        # The published subjects should be present on the page
        for subject in subjects[:2]:
            self.assertContains(
                response,
                '<a class="subject-plugin-tag" href="{:s}">{:s}</a>'.format(
                    subject.extended_object.get_absolute_url(),
                    subject.extended_object.get_title(),
                ),
                html=True,
            )
        # Draft subjects should also be present on the page with an annotation for styling
        for subject in subjects[-2:]:
            self.assertContains(
                response,
                '<a class="{element:s} {element:s}--draft" href="{url:s}">{title:s}</a>'.format(
                    url=subject.extended_object.get_absolute_url(),
                    element="subject-plugin-tag",
                    title=subject.extended_object.get_title(),
                ),
                html=True,
            )
        # The draft and the published course runs should both be in the page
        self.assertContains(response, "Enroll now", count=2)
        self.assertContains(response, "<dd>English, French</dd>", html=True, count=2)
Exemple #22
0
    def test_templates_course_detail_cms_published_content(self, _):
        """
        Validate that the important elements are displayed on a published course page
        """
        subjects = SubjectFactory.create_batch(4)
        organizations = OrganizationFactory.create_batch(4)

        course = CourseFactory(
            page_title="Very interesting course",
            fill_organizations=organizations,
            fill_subjects=subjects,
        )
        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 subjects and 2 out of 4 organizations
        subjects[0].extended_object.publish("en")
        subjects[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.
        subjects[2].extended_object.publish("en")
        subjects[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 subjects should be present on the page
        for subject in subjects[:2]:
            self.assertContains(
                response,
                '<a class="subject-plugin-tag" href="{:s}">{:s}</a>'.format(
                    subject.extended_object.get_absolute_url(),
                    subject.extended_object.get_title(),
                ),
                html=True,
            )
        for subject in subjects[-2:]:
            self.assertNotContains(response, subject.extended_object.get_title())

        # Public organizations should be in response content
        for organization in organizations[:2]:
            self.assertContains(
                response,
                '<div class="organization-plugin__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, "Enroll now", count=1)
        self.assertContains(response, "<dd>English, French</dd>", html=True, count=1)
    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)
Exemple #24
0
    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="******")

        subjects = SubjectFactory.create_batch(4)
        organizations = OrganizationFactory.create_batch(4)

        course = CourseFactory(
            page_title="Very interesting course",
            fill_organizations=organizations,
            fill_subjects=subjects,
            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),
            enrollment_end=datetime(2019, 1, 18),
            start=datetime(2018, 12, 10),
            end=datetime(2019, 2, 14),
            languages=["en", "fr"],
        )
        page = course_run.extended_object

        # Publish only 2 out of 4 subjects and 2 out of 4 organizations
        subjects[0].extended_object.publish("en")
        subjects[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.
        subjects[2].extended_object.publish("en")
        subjects[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="course-detail__content__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-plugin__title">{title:s}</div>'.format(
                    title=organization.extended_object.get_title()
                ),
                html=True,
            )

        # Draft organizations should be annotated for styling
        self.assertContains(response, "organization-plugin-container--draft", count=2)

        # The published subjects should be present on the page
        for subject in subjects[:2]:
            self.assertContains(
                response,
                '<a class="subject-plugin-tag" href="{:s}">{:s}</a>'.format(
                    subject.extended_object.get_absolute_url(),
                    subject.extended_object.get_title(),
                ),
                html=True,
            )
        # Draft subjects should also be present on the page with an annotation for styling
        for subject in subjects[-2:]:
            self.assertContains(
                response,
                '<a class="{element:s} {element:s}--draft" href="{url:s}">{title:s}</a>'.format(
                    url=subject.extended_object.get_absolute_url(),
                    element="subject-plugin-tag",
                    title=subject.extended_object.get_title(),
                ),
                html=True,
            )

        # The course run details should be on the page
        self.assertContains(
            response, "<dt>Enrollment starts</dt><dd>Oct. 21, 2018</dd>"
        )
        self.assertContains(response, "<dt>Enrollment ends</dt><dd>Jan. 18, 2019</dd>")
        self.assertContains(response, "<dt>Course starts</dt><dd>Dec. 10, 2018</dd>")
        self.assertContains(response, "<dt>Course ends</dt><dd>Feb. 14, 2019</dd>")
        self.assertContains(
            response,
            '<a class="course-detail__content__run__block__cta" '
            'href="https://www.example.com/enroll">Enroll now</a>',
            html=True,
        )
        self.assertContains(response, "<dt>Languages</dt><dd>English, French</dd>")