Esempio n. 1
0
 def test_list_course_runs_by_anonymous_user(self):
     """
     The course run API does not support list requests.
     """
     CourseRunFactory.create_batch(2)
     response = self.client.get("/api/v1.0/course-runs/")
     self.assertEqual(response.status_code, 403)
Esempio n. 2
0
 def test_list_course_runs_by_admin_user(self):
     """
     The course run API does not support list requests.
     """
     CourseRunFactory.create_batch(2)
     self.client.force_login(UserFactory(is_staff=True, is_superuser=True))
     response = self.client.get("/api/v1.0/course-runs/")
     self.assertEqual(response.status_code, 403)
Esempio n. 3
0
    def test_models_course_attribute_course_runs(self):
        """
        The "course_runs" property should return all descendants ranked by path, not only children
        and should respect publication status for the active language.
        """
        # Create a course with draft and published course runs
        course = CourseFactory(should_publish=True)

        # Create draft and published course runs for this course
        # We want to test 3 situations:
        # - a draft course run
        # - a published course run
        # - a course run that was published and unpublished
        course_runs = CourseRunFactory.create_batch(
            3, page_parent=course.extended_object)
        course_runs[0].extended_object.publish("en")
        course_runs[1].extended_object.publish("en")
        course_runs[1].extended_object.unpublish("en")

        # Create a child course with draft and published course runs (what results from
        # snapshotting a course)
        child_course = CourseFactory(page_parent=course.extended_object,
                                     should_publish=True)
        child_course_runs = CourseRunFactory.create_batch(
            3, page_parent=child_course.extended_object)
        child_course_runs[0].extended_object.publish("en")
        child_course_runs[1].extended_object.publish("en")
        child_course_runs[1].extended_object.unpublish("en")

        # Create another course, not related to the first one, with draft and published course runs
        other_course = CourseFactory(should_publish=True)
        other_course_runs = CourseRunFactory.create_batch(
            3, page_parent=other_course.extended_object)
        other_course_runs[0].extended_object.publish("en")
        other_course_runs[1].extended_object.publish("en")
        other_course_runs[1].extended_object.unpublish("en")

        # Check that the draft course retrieves all its descendant course runs
        # 3 draft course runs and 2 published course runs per course
        self.assertEqual(CourseRun.objects.count(), 3 * (3 + 2))
        with self.assertNumQueries(2):
            self.assertEqual(list(course.course_runs),
                             course_runs + child_course_runs)

        # Check that the published course retrieves only the published descendant course runs
        course_runs[0].refresh_from_db()
        child_course_runs[0].refresh_from_db()
        public_course = course.public_extension
        with self.assertNumQueries(3):
            result = list(public_course.course_runs)
        self.assertEqual(
            result,
            [
                course_runs[0].public_extension,
                child_course_runs[0].public_extension
            ],
        )
Esempio n. 4
0
    def test_models_course_run_fields_course_runs(self):
        """
        The "course_runs" field should always return the course runs linked to the draft version
        of the course.
        """
        # Create a draft course with course runs
        course = CourseFactory()
        course_runs = set(CourseRunFactory.create_batch(2, course=course))

        # The course runs should be accessible from the course
        self.assertEqual(set(course.course_runs.all()), course_runs)

        # The published course should point to the same course runs
        course.extended_object.publish("en")
        course.refresh_from_db()
        self.assertEqual(set(course.public_extension.course_runs.all()), course_runs)
Esempio n. 5
0
    def test_models_course_get_course_runs(self):
        """
        The `get_course_runs` and `get_course_runs_for_language` methods should return all
        descendants ranked by path, not only children and should respect publication status.
        """
        course = CourseFactory(page_languages=["en", "fr"],
                               should_publish=True)

        # Create draft and published course runs for this course
        # We want to test 4 situations:
        # - a draft course run
        # - a course run published in the current language
        # - a course run published in another language
        # - a course run published in the current language that was then unpublished
        course_runs = CourseRunFactory.create_batch(
            3, page_parent=course.extended_object, page_languages=["en"])
        self.assertTrue(course_runs[0].extended_object.publish("en"))
        self.assertTrue(course_runs[1].extended_object.publish("en"))
        self.assertTrue(course_runs[1].extended_object.unpublish("en"))

        course_run_fr = CourseRunFactory(
            page_parent=course.extended_object,
            page_languages=["fr"],
            should_publish=True,
        )

        # Create a child course with draft and published course runs (what results from
        # snapshotting a course)
        child_course = CourseFactory(
            page_languages=["en", "fr"],
            page_parent=course.extended_object,
            should_publish=True,
        )
        child_course_runs = CourseRunFactory.create_batch(
            3, page_parent=child_course.extended_object, page_languages=["en"])
        self.assertTrue(child_course_runs[0].extended_object.publish("en"))
        self.assertTrue(child_course_runs[1].extended_object.publish("en"))
        self.assertTrue(child_course_runs[1].extended_object.unpublish("en"))

        child_course_run_fr = CourseRunFactory(
            page_parent=child_course.extended_object,
            page_languages=["fr"],
            should_publish=True,
        )

        # Create another course, not related to the first one, with draft and published course runs
        other_course = CourseFactory(page_languages=["en", "fr"],
                                     should_publish=True)
        other_course_runs = CourseRunFactory.create_batch(
            3, page_parent=other_course.extended_object, page_languages=["en"])
        self.assertTrue(other_course_runs[0].extended_object.publish("en"))
        self.assertTrue(other_course_runs[1].extended_object.publish("en"))
        self.assertTrue(other_course_runs[1].extended_object.unpublish("en"))

        CourseRunFactory(
            page_parent=other_course.extended_object,
            page_languages=["fr"],
            should_publish=True,
        )

        # Check that the draft course retrieves all its descendant course runs
        # 3 draft course runs and 2 published course runs per course
        self.assertEqual(CourseRun.objects.count(), 3 * (4 + 3))

        with self.assertNumQueries(2):
            self.assertEqual(
                list(course.get_course_runs()),
                course_runs + [course_run_fr] + child_course_runs +
                [child_course_run_fr],
            )

        with self.assertNumQueries(1):
            self.assertEqual(
                list(course.get_course_runs_for_language(language="en")),
                course_runs + child_course_runs,
            )

        # Check that the published course retrieves only the published descendant course runs
        course_runs[0].refresh_from_db()
        child_course_runs[0].refresh_from_db()
        public_course = course.public_extension

        with self.assertNumQueries(3):
            result = list(public_course.get_course_runs())
        self.assertEqual(
            result,
            [
                course_runs[0].public_extension,
                course_run_fr.public_extension,
                child_course_runs[0].public_extension,
                child_course_run_fr.public_extension,
            ],
        )

        with self.assertNumQueries(1):
            result = list(
                public_course.get_course_runs_for_language(language="en"))
        self.assertEqual(
            result,
            [
                course_runs[0].public_extension,
                child_course_runs[0].public_extension
            ],
        )
Esempio n. 6
0
    def test_indexers_courses_get_es_documents_from_models(self, _mock_picture):
        """
        Happy path: the data is retrieved from the models properly formatted
        """
        # Create a course with a page in both english and french
        published_categories = [
            CategoryFactory(  # L-0001
                fill_icon=True,
                page_title={"en": "Title L-0001", "fr": "Titre L-0001"},
                should_publish=True,
            ),
            CategoryFactory(  # L-0002
                fill_icon=True,
                page_title={"en": "Title L-0002", "fr": "Titre L-0002"},
                should_publish=True,
            ),
        ]
        draft_category = CategoryFactory(fill_icon=True)  # L-0003

        main_organization = OrganizationFactory(  # L-0004
            page_title={
                "en": "english main organization title",
                "fr": "titre organisation principale français",
            },
            should_publish=True,
        )
        other_draft_organization = OrganizationFactory(  # L-0005
            page_title={
                "en": "english other organization title",
                "fr": "titre autre organisation français",
            }
        )
        other_published_organization = OrganizationFactory(  # L-0006
            page_title={
                "en": "english other organization title",
                "fr": "titre autre organisation français",
            },
            should_publish=True,
        )

        person1 = PersonFactory(
            page_title={"en": "Eugène Delacroix", "fr": "Eugène Delacroix"},
            should_publish=True,
        )
        person2 = PersonFactory(
            page_title={"en": "Comte de Saint-Germain", "fr": "Earl of Saint-Germain"},
            should_publish=True,
        )
        person_draft = PersonFactory(
            page_title={"en": "Jules de Polignac", "fr": "Jules de Polignac"}
        )

        course = CourseFactory(
            duration=[3, WEEK],
            effort=[2, HOUR, WEEK],
            fill_categories=published_categories + [draft_category],
            fill_cover=True,
            fill_icons=published_categories + [draft_category],
            fill_organizations=[
                main_organization,
                other_draft_organization,
                other_published_organization,
            ],
            fill_team=[person1, person_draft, person2],
            page_title={
                "en": "an english course title",
                "fr": "un titre cours français",
            },
        )
        CourseRunFactory.create_batch(2, direct_course=course)
        course.extended_object.publish("en")
        course.extended_object.publish("fr")
        course.refresh_from_db()

        # Add a description in several languages
        placeholder = course.public_extension.extended_object.placeholders.get(
            slot="course_description"
        )
        plugin_params = {"placeholder": placeholder, "plugin_type": "CKEditorPlugin"}
        add_plugin(body="english description line 1.", language="en", **plugin_params)
        add_plugin(body="english description line 2.", language="en", **plugin_params)
        add_plugin(body="a propos français ligne 1.", language="fr", **plugin_params)
        add_plugin(body="a propos français ligne 2.", language="fr", **plugin_params)

        # The results were properly formatted and passed to the consumer
        expected_course = {
            "_id": str(course.public_extension.extended_object_id),
            "_index": "some_index",
            "_op_type": "some_action",
            "_type": "course",
            "absolute_url": {
                "en": "/en/an-english-course-title/",
                "fr": "/fr/un-titre-cours-francais/",
            },
            "categories": ["L-0001", "L-0002"],
            "categories_names": {
                "en": ["Title L-0001", "Title L-0002"],
                "fr": ["Titre L-0001", "Titre L-0002"],
            },
            "complete": {
                "en": [
                    "an english course title",
                    "english course title",
                    "course title",
                    "title",
                ],
                "fr": [
                    "un titre cours français",
                    "titre cours français",
                    "cours français",
                    "français",
                ],
            },
            "course_runs": [
                {
                    "start": course_run.public_course_run.start,
                    "end": course_run.public_course_run.end,
                    "enrollment_start": course_run.public_course_run.enrollment_start,
                    "enrollment_end": course_run.public_course_run.enrollment_end,
                    "languages": course_run.public_course_run.languages,
                }
                for course_run in course.get_course_runs().order_by("-end")
            ],
            "cover_image": {
                "en": {"info": "picture info"},
                "fr": {"info": "picture info"},
            },
            "description": {
                "en": "english description line 1. english description line 2.",
                "fr": "a propos français ligne 1. a propos français ligne 2.",
            },
            "duration": {"en": "3 weeks", "fr": "3 semaines"},
            "effort": {"en": "2 hours/week", "fr": "2 heures/semaine"},
            "icon": {
                "en": {
                    "color": published_categories[0].color,
                    "info": "picture info",
                    "title": "Title L-0001",
                },
                "fr": {
                    "color": published_categories[0].color,
                    "info": "picture info",
                    "title": "Titre L-0001",
                },
            },
            "is_new": False,
            "is_listed": True,
            "organization_highlighted": {
                "en": "english main organization title",
                "fr": "titre organisation principale français",
            },
            "organizations": ["L-0004", "L-0006"],
            "organizations_names": {
                "en": [
                    "english main organization title",
                    "english other organization title",
                ],
                "fr": [
                    "titre organisation principale français",
                    "titre autre organisation français",
                ],
            },
            "persons": [
                str(person1.public_extension.extended_object_id),
                str(person2.public_extension.extended_object_id),
            ],
            "persons_names": {
                "en": ["Eugène Delacroix", "Comte de Saint-Germain"],
                "fr": ["Eugène Delacroix", "Earl of Saint-Germain"],
            },
            "title": {"fr": "un titre cours français", "en": "an english course title"},
        }
        indexed_courses = list(
            CoursesIndexer.get_es_documents(index="some_index", action="some_action")
        )
        self.assertEqual(len(indexed_courses), 1)
        self.assertEqual(indexed_courses[0], expected_course)
    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(
            organization_main=organizations[0],
            title="Very interesting course",
            with_organizations=organizations,
            with_subjects=subjects,
        )
        page = course.extended_object
        CourseRunFactory.create_batch(2, course=course)

        # 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)

        # 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 en</title>",
                            html=True)
        self.assertContains(
            response,
            '<h1 class="course-detail__title">Very interesting course en</h1>',
            html=True,
        )

        # Only published subjects should be present on the page
        for subject in subjects[:2]:
            self.assertContains(
                response,
                '<a class="course-detail__content__subjects__item" 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())

        # 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)

        # Course runs should be in the page
        self.assertContains(response, "Enroll now", count=2)
    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(
            organization_main=organizations[0],
            title="Very interesting course",
            with_organizations=organizations,
            with_subjects=subjects,
        )
        page = course.extended_object
        CourseRunFactory.create_batch(2, course=course)

        # 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 en</title>",
                            html=True)
        self.assertContains(
            response,
            '<h1 class="course-detail__title">Very interesting course en</h1>',
            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,
                '<a class="course-detail__content__subjects__item" 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="course-detail__content__subjects__item",
                    title=subject.extended_object.get_title(),
                ),
                html=True,
            )
        # Course runs should be in the page
        self.assertContains(response, "Enroll now", count=2)
Esempio n. 9
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)
    def test_templates_course_detail_cms_published_content(self):
        """
        Validate that the important elements are displayed on a published course page
        """
        categories = CategoryFactory.create_batch(4)
        icons = CategoryFactory.create_batch(4, fill_icon=True)
        organizations = OrganizationFactory.create_batch(4)

        course = CourseFactory(
            page_title="Very interesting course",
            fill_organizations=organizations,
            fill_categories=categories,
            fill_icons=icons,
        )
        page = course.extended_object
        # Create 2 ongoing open course runs
        now = timezone.now()
        course_run1, _course_run2 = CourseRunFactory.create_batch(
            2,
            page_parent=course.extended_object,
            start=now - timedelta(hours=1),
            end=now + timedelta(hours=2),
            enrollment_end=now + timedelta(hours=1),
            languages=["en", "fr"],
        )
        self.assertFalse(course_run1.extended_object.publish("en"))

        # Publish only 2 out of 4 categories, icons and organizations
        categories[0].extended_object.publish("en")
        categories[1].extended_object.publish("en")
        icons[0].extended_object.publish("en")
        icons[1].extended_object.publish("en")
        organizations[0].extended_object.publish("en")
        organizations[1].extended_object.publish("en")

        # The unpublished objects may have been published and unpublished which puts them in a
        # status different from objects that have never been published.
        # We want to test both cases.
        categories[2].extended_object.publish("en")
        categories[2].extended_object.unpublish("en")
        icons[2].extended_object.publish("en")
        icons[2].extended_object.unpublish("en")
        organizations[2].extended_object.publish("en")
        organizations[2].extended_object.unpublish("en")

        # The page should not be visible before it is published
        url = page.get_absolute_url()
        response = self.client.get(url)
        self.assertEqual(response.status_code, 404)

        # Publish and ensure content is correct
        page.publish("en")

        # Now we can publish children course runs: publish only 1 of the 2
        course_run1.extended_object.parent_page.refresh_from_db()
        self.assertTrue(course_run1.extended_object.publish("en"))

        response = self.client.get(url)
        self.assertEqual(response.status_code, 200)

        self.assertContains(
            response, "<title>Very interesting course</title>", html=True
        )
        self.assertContains(
            response,
            '<h1 class="subheader__title">Very interesting course</h1>',
            html=True,
        )

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

        # Only published icons should be present on the page
        pattern = (
            r'<a.*class="category-badge".*href="{link:s}".*>'
            r'<img src="/media/filer_public_thumbnails/filer_public/.*icon\.jpg.*alt="{title:s}">'
            r'<span class="category-badge__title">'
            r".*{title:s}.*</span>"
        )

        for icon in icons[:2]:
            self.assertIsNotNone(
                re.search(
                    pattern.format(
                        link=icon.extended_object.get_absolute_url(),
                        title=icon.extended_object.get_title(),
                    ),
                    str(response.content),
                )
            )
        for icon in icons[-2:]:
            self.assertNotContains(response, icon.extended_object.get_title())

        # Public organizations should be in response content
        for organization in organizations[:2]:
            self.assertContains(
                response,
                '<div class="organization-glimpse__title">{title:s}</div>'.format(
                    title=organization.extended_object.get_title()
                ),
                html=True,
            )

        # Draft organizations should not be in response content
        for organization in organizations[-2:]:
            self.assertNotContains(
                response, organization.extended_object.get_title(), html=True
            )

        # Only the published course run should be in response content
        self.assertContains(response, "<dd>English and french</dd>", html=True, count=1)
    def test_templates_course_detail_cms_draft_content(self):
        """
        A staff user should see a draft course including its draft elements with
        an annotation
        """
        user = UserFactory(is_staff=True, is_superuser=True)
        self.client.login(username=user.username, password="******")

        categories = CategoryFactory.create_batch(4)
        organizations = OrganizationFactory.create_batch(4)

        course = CourseFactory(
            page_title="Very interesting course",
            fill_organizations=organizations,
            fill_categories=categories,
        )
        page = course.extended_object
        now = timezone.now()
        course_run1, _course_run2 = CourseRunFactory.create_batch(
            2,
            page_parent=course.extended_object,
            start=now - timedelta(hours=1),
            end=now + timedelta(hours=2),
            enrollment_end=now + timedelta(hours=1),
            languages=["en", "fr"],
        )

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

        # Publish only 2 out of 4 categories and 2 out of 4 organizations
        categories[0].extended_object.publish("en")
        categories[1].extended_object.publish("en")
        organizations[0].extended_object.publish("en")
        organizations[1].extended_object.publish("en")

        # The unpublished objects may have been published and unpublished which puts them in a
        # status different from objects that have never been published.
        # We want to test both cases.
        categories[2].extended_object.publish("en")
        categories[2].extended_object.unpublish("en")
        organizations[2].extended_object.publish("en")
        organizations[2].extended_object.unpublish("en")

        # The page should be visible as draft to the staff user
        url = page.get_absolute_url()
        response = self.client.get(url)

        self.assertEqual(response.status_code, 200)

        self.assertContains(
            response, "<title>Very interesting course</title>", html=True
        )
        self.assertContains(
            response,
            '<h1 class="subheader__title">Very interesting course</h1>',
            html=True,
        )

        # Draft and public organizations should all be present on the page
        for organization in organizations:
            self.assertContains(
                response,
                '<div class="organization-glimpse__title">{title:s}</div>'.format(
                    title=organization.extended_object.get_title()
                ),
                html=True,
            )

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

        # The published categories should be present on the page
        for category in categories[:2]:
            self.assertContains(
                response,
                (
                    '<a class="category-badge" href="{:s}">'
                    '<span class="category-badge__title">{:s}</span></a>'
                ).format(
                    category.extended_object.get_absolute_url(),
                    category.extended_object.get_title(),
                ),
                html=True,
            )
        # Draft categories should also be present on the page with an annotation for styling
        for category in categories[-2:]:
            self.assertContains(
                response,
                (
                    '<a class="{element:s} {element:s}--draft" href="{url:s}">'
                    '<span class="category-badge__title">{title:s}</span></a>'
                ).format(
                    url=category.extended_object.get_absolute_url(),
                    element="category-badge",
                    title=category.extended_object.get_title(),
                ),
                html=True,
            )
        # The draft and the published course runs should both be in the page
        self.assertContains(response, "<dd>English and french</dd>", html=True, count=2)
    def test_templates_course_detail_cms_published_content(self):
        """
        Validate that the important elements are displayed on a published course page
        """
        categories = CategoryFactory.create_batch(4)
        organizations = OrganizationFactory.create_batch(4)

        course = CourseFactory(
            page_title="Very interesting course",
            fill_organizations=organizations,
            fill_categories=categories,
        )
        page = course.extended_object
        course_run1, _course_run2 = CourseRunFactory.create_batch(
            2, page_parent=course.extended_object, languages=["en", "fr"]
        )
        self.assertFalse(course_run1.extended_object.publish("en"))

        # Publish only 2 out of 4 categories and 2 out of 4 organizations
        categories[0].extended_object.publish("en")
        categories[1].extended_object.publish("en")
        organizations[0].extended_object.publish("en")
        organizations[1].extended_object.publish("en")

        # The unpublished objects may have been published and unpublished which puts them in a
        # status different from objects that have never been published.
        # We want to test both cases.
        categories[2].extended_object.publish("en")
        categories[2].extended_object.unpublish("en")
        organizations[2].extended_object.publish("en")
        organizations[2].extended_object.unpublish("en")

        # The page should not be visible before it is published
        url = page.get_absolute_url()
        response = self.client.get(url)
        self.assertEqual(response.status_code, 404)

        # Publish and ensure content is correct
        page.publish("en")

        # Now we can publish children course runs: publish only 1 of the 2
        course_run1.extended_object.parent_page.refresh_from_db()
        self.assertTrue(course_run1.extended_object.publish("en"))

        response = self.client.get(url)
        self.assertEqual(response.status_code, 200)

        self.assertContains(
            response, "<title>Very interesting course</title>", html=True
        )
        self.assertContains(
            response,
            '<h1 class="course-detail__content__title">Very interesting course</h1>',
            html=True,
        )

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

        # Public organizations should be in response content
        for organization in organizations[:2]:
            self.assertContains(
                response,
                '<div class="organization-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, "<dd>English and french</dd>", html=True, count=1)
Esempio n. 13
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)