def test_models_course_state_future_enrollment_open(self):
        """
        Confirm course state when there is a future course run open for enrollment.
        """
        course = CourseFactory()
        course_run = self.create_run_future_open(course)
        with self.assertNumQueries(3):
            state = course.state
        expected_state = CourseState(1, course_run.start)
        self.assertEqual(state, expected_state)

        # Adding course runs of lower priority states should not change the result and require
        # only 1 additional database query
        self.create_run_ongoing_closed(course)
        self.create_run_future_closed(course)
        with self.assertNumQueries(1):
            state = course.state
        self.assertEqual(state, expected_state)
    def test_indexers_courses_get_es_documents_no_enrollment_end(self):
        """
        Course runs with no end of enrollment date should get their end date as date of end
        of enrollment.
        """
        course = CourseFactory(should_publish=True)
        course_run = CourseRunFactory(page_parent=course.extended_object,
                                      enrollment_end=None,
                                      should_publish=True)

        indexed_courses = list(
            CoursesIndexer.get_es_documents(index="some_index",
                                            action="some_action"))
        self.assertEqual(len(indexed_courses), 1)
        self.assertEqual(len(indexed_courses[0]["course_runs"]), 1)
        self.assertEqual(
            indexed_courses[0]["course_runs"][0]["enrollment_end"],
            course_run.end)
    def test_models_course_state_future_enrollment_closed(self):
        """
        Confirm course state when there is a future course run but closed for
        enrollment.
        """
        course = CourseFactory()
        self.create_run_future_closed(course)
        with self.assertNumQueries(3):
            state = course.state
        expected_state = CourseState(4)
        self.assertEqual(state, expected_state)

        # Adding an on-going but closed course run should not change the result and require
        # only 1 additional database query
        self.create_run_ongoing_closed(course)
        with self.assertNumQueries(1):
            state = course.state
        self.assertEqual(state, expected_state)
    def test_models_course_run_delete_draft(self):
        """
        Deleting a draft course run that is not published should delete all its
        related translations.
        """
        course = CourseFactory(page_languages=["en", "fr"])
        course_run = CourseRunFactory(direct_course=course)
        CourseRunTranslation.objects.create(
            master=course_run, language_code="fr", title="mon titre"
        )

        self.assertEqual(CourseRun.objects.count(), 1)
        self.assertEqual(CourseRunTranslation.objects.count(), 2)

        course_run.delete()

        self.assertFalse(CourseRun.objects.exists())
        self.assertFalse(CourseRunTranslation.objects.exists())
Beispiel #5
0
    def test_models_course_state_future_enrollment_open(self):
        """
        Confirm course state when there is a future course run open for enrollment.
        """
        course = CourseFactory()
        course_run = self.create_run_future_open(course)
        with self.assertNumQueries(2):
            state = course.state
        expected_state = CourseState(1, "enroll now", "starting on",
                                     course_run.start)
        self.assertEqual(state, expected_state)

        # Adding courses in less priorietary states should not change the result
        self.create_run_ongoing_closed(course)
        self.create_run_future_closed(course)
        with self.assertNumQueries(2):
            state = course.state
        self.assertEqual(state, expected_state)
Beispiel #6
0
    def test_models_course_state_future_enrollment_not_yet_open(self):
        """
        Confirm course state when there is a future course run but not yet open for
        enrollment.
        """
        course = CourseFactory()
        course_run = self.create_run_future_not_yet_open(course)
        with self.assertNumQueries(2):
            state = course.state
        expected_state = CourseState(2, "see details", "starting on",
                                     course_run.start)
        self.assertEqual(state, expected_state)

        # Adding an on-going but closed course run should not change the result
        self.create_run_ongoing_closed(course)
        with self.assertNumQueries(2):
            state = course.state
        self.assertEqual(state, expected_state)
    def test_admin_course_list_view(self):
        """
        The admin list view of courses should display the title of the related page.
        """
        user = UserFactory(is_staff=True, is_superuser=True)
        self.client.login(username=user.username, password="******")

        # Create a course linked to a page
        course = CourseFactory()

        # Get the admin list view
        url = reverse("admin:courses_course_changelist")
        response = self.client.get(url, follow=True)

        # Check that the page includes the title field
        self.assertContains(response,
                            course.extended_object.get_title(),
                            status_code=200)
Beispiel #8
0
    def test_api_course_run_sync_create_partial_not_required(self, mock_signal):
        """
        If the submitted data is not related to an existing course run and some optional fields
        are missing, it should create the course run.
        """
        course = CourseFactory(code="DemoX")
        Title.objects.update(publisher_state=PUBLISHER_STATE_DEFAULT)
        data = {
            "resource_link": "http://example.edx:8073/courses/course-v1:edX+DemoX+01/course/",
            "enrollment_end": "2020-12-24T09:31:59.417972Z",
            "languages": ["en", "fr"],
        }

        self.assertEqual(
            course.extended_object.title_set.first().publisher_state,
            PUBLISHER_STATE_DEFAULT,
        )
        mock_signal.reset_mock()

        response = self.client.post(
            "/api/v1.0/course-runs-sync",
            data,
            content_type="application/json",
            HTTP_AUTHORIZATION=(
                "SIG-HMAC-SHA256 313cefea7a14f26ed7dc249719bc5a86bce36b0c63a9d27b2e30e3a616e108d6"
            ),
        )

        self.assertEqual(response.status_code, 200)
        self.assertEqual(json.loads(response.content), {"success": True})
        self.assertEqual(CourseRun.objects.count(), 1)

        # Check the new draft course run
        draft_course_run = CourseRun.objects.get(direct_course=course)
        draft_serializer = SyncCourseRunSerializer(instance=draft_course_run)
        data.update({"start": None, "end": None, "enrollment_start": None})
        self.assertEqual(draft_serializer.data, data)

        # The page is not marked dirty because the course run is to be scheduled
        self.assertEqual(
            course.extended_object.title_set.first().publisher_state,
            PUBLISHER_STATE_DEFAULT,
        )
        self.assertFalse(mock_signal.called)
    def test_templates_course_detail_placeholder(self):
        """
        Draft editing course page should contain all key placeholders when empty.
        """
        user = UserFactory(is_staff=True, is_superuser=True)
        self.client.login(username=user.username, password="******")

        course = CourseFactory(page_title="Very interesting course")
        page = course.extended_object
        url = "{:s}?edit".format(page.get_absolute_url(language="en"))
        response = self.client.get(url)

        pattern = (
            r'<div class="course-detail__content__row course-detail__content__introduction">'
            r'<div class="cms-placeholder')
        self.assertIsNotNone(re.search(pattern, str(response.content)))
        pattern = (
            r'<div class="course-detail__content__row course-detail__content__categories">'
            r'<div class="cms-placeholder')
        self.assertIsNotNone(re.search(pattern, str(response.content)))
        pattern = (
            r'<div class="course-detail__content__row course-detail__content__teaser">'
            r'<div class="cms-placeholder')
        self.assertIsNotNone(re.search(pattern, str(response.content)))
        pattern = (
            r'<h2 class="course-detail__content__row__title">About the course</h2>'
            r'<div class="cms-placeholder')
        self.assertIsNotNone(re.search(pattern, str(response.content)))
        pattern = (
            r'<div class="section__items course-detail__content__organizations__items">'
            r'<div class="cms-placeholder')
        self.assertIsNotNone(re.search(pattern, str(response.content)))
        pattern = (
            r'<div class="section__items course-detail__content__team__items">'
            r'<div class="cms-placeholder')
        self.assertIsNotNone(re.search(pattern, str(response.content)))
        pattern = (
            r'<div class="course-detail__content__row course-detail__content__information">'
            r'<div class="cms-placeholder')
        self.assertIsNotNone(re.search(pattern, str(response.content)))
        pattern = (
            r'<h3 class="course-detail__content__license__item__title">'
            r'License for the course content</h3><div class="cms-placeholder')
        self.assertIsNotNone(re.search(pattern, str(response.content)))
Beispiel #10
0
    def test_api_course_run_sync_create_sync_to_draft(self, mock_signal):
        """
        If the submitted data is not related to an existing course run, a new course run should
        be created. In "sync_to_draft" mode, the synchronization should be limited to the draft
        course run, even if the related course is published.
        """
        course = CourseFactory(code="DemoX", should_publish=True)
        data = {
            "resource_link": "http://example.edx:8073/courses/course-v1:edX+DemoX+01/course/",
            "start": "2020-12-09T09:31:59.417817Z",
            "end": "2021-03-14T09:31:59.417895Z",
            "enrollment_start": "2020-11-09T09:31:59.417936Z",
            "enrollment_end": "2020-12-24T09:31:59.417972Z",
            "languages": ["en", "fr"],
        }
        self.assertEqual(
            course.extended_object.title_set.first().publisher_state,
            PUBLISHER_STATE_DEFAULT,
        )
        mock_signal.reset_mock()

        response = self.client.post(
            "/api/v1.0/course-runs-sync",
            data,
            content_type="application/json",
            HTTP_AUTHORIZATION=(
                "SIG-HMAC-SHA256 338f7c262254e8220fea54467526f8f1f4562ee3adf1e3a71abaf23a20b739e4"
            ),
        )

        self.assertEqual(response.status_code, 200)
        self.assertEqual(json.loads(response.content), {"success": True})
        self.assertEqual(CourseRun.objects.count(), 1)

        # Check the new draft course run
        draft_course_run = CourseRun.objects.get(direct_course=course)
        draft_serializer = SyncCourseRunSerializer(instance=draft_course_run)
        self.assertEqual(draft_serializer.data, data)

        self.assertEqual(
            course.extended_object.title_set.first().publisher_state,
            PUBLISHER_STATE_DIRTY,
        )
        self.assertFalse(mock_signal.called)
Beispiel #11
0
    def test_cms_wizards_course_run_submit_form_success(self, mock_snapshot):
        """
        A user with the required permissions submitting a valid CourseRunWizardForm should be able
        to create a course run and its related page.
        """
        course = CourseFactory()

        # Create a user with just the required permissions
        user = UserFactory(
            is_staff=True,
            permissions=[
                "courses.add_courserun", "cms.add_page", "cms.change_page"
            ],
        )

        # Submit a valid form
        form = CourseRunWizardForm(
            data={"title": "My title"},
            wizard_language="en",
            wizard_user=user,
            wizard_page=course.extended_object,
        )

        self.assertTrue(form.is_valid())
        page = form.save()
        course_run = page.courserun

        # The course run and its related page should have been created as draft
        Page.objects.drafts().get(id=page.id)
        CourseRun.objects.get(id=course_run.id, extended_object=page)

        self.assertEqual(page.get_title(), "My title")
        # The slug should have been automatically set
        self.assertEqual(page.get_slug(), "my-title")

        # The course run should be a child of the course page
        self.assertEqual(course_run.extended_object.parent_page,
                         course.extended_object)

        # The languages field should have been set
        self.assertEqual(course_run.languages, ["en"])

        # Snapshot was not request and should not have been triggered
        self.assertFalse(mock_snapshot.called)
Beispiel #12
0
    def test_models_course_copy_relations_publish(self):
        """
        When publishing a draft course, the draft course run should be copied to a newly created
        course run with its parler translations.

        In a second part of the test, we check that when publishing a course that was already
        published, the draft course run should be copied to the existing public course run with its
        parler translations.

        """
        # 1- Publishing a draft course

        course = CourseFactory(page_title="my course title")
        TitleFactory(page=course.extended_object,
                     language="fr",
                     title="mon titre de cours")
        course_run = CourseRunFactory(direct_course=course, title="my run")
        CourseRunTranslation.objects.create(master=course_run,
                                            language_code="fr",
                                            title="ma session")
        self.assertEqual(Course.objects.count(), 1)
        self.assertEqual(CourseRun.objects.count(), 1)
        self.assertEqual(CourseRunTranslation.objects.count(), 2)
        self.assertEqual(Title.objects.count(), 2)

        self.assertTrue(course.extended_object.publish("fr"))

        self.assertEqual(Course.objects.count(), 2)
        self.assertEqual(CourseRun.objects.count(), 2)
        self.assertEqual(CourseRunTranslation.objects.count(), 3)
        self.assertEqual(Title.objects.count(), 3)

        # 2- Publishing a course that was already published
        self.assertTrue(course.extended_object.publish("en"))

        self.assertEqual(CourseRunTranslation.objects.count(), 4)
        self.assertEqual(Title.objects.count(), 4)

        course_run.refresh_from_db()

        public_course_run = course_run.public_course_run
        self.assertEqual(public_course_run.title, "my run")
        with switch_language(public_course_run, "fr"):
            self.assertEqual(public_course_run.title, "ma session")
Beispiel #13
0
    def test_templates_course_detail_teaser_empty_cover_empty_edit(self):
        """
        Without video in `course_teaser` placeholder and no image in `course_cover` placeholder,
        no component should be present on the `course_teaser` placeholder.
        """
        user = UserFactory(is_staff=True, is_superuser=True)
        self.client.login(username=user.username, password="******")
        course = CourseFactory()

        response = self.client.get(course.extended_object.get_absolute_url())

        self.assertEqual(response.status_code, 200)
        pattern = (r'<div class="subheader__teaser">'
                   r'<p class="empty">'
                   r"Add a teaser video or add a cover image below"
                   r" and it will be used as teaser image as well."
                   r"</p>"
                   r"</div>")
        self.assertIsNotNone(re.search(pattern, str(response.content)))
Beispiel #14
0
    def test_templates_course_detail_placeholder(self):
        """
        Draft editing course page should contain all key placeholders when empty.
        """
        user = UserFactory(is_staff=True, is_superuser=True)
        self.client.login(username=user.username, password="******")

        course = CourseFactory(page_title="Very interesting course")
        page = course.extended_object
        url = "{:s}?edit".format(page.get_absolute_url(language="en"))
        response = self.client.get(url)

        pattern = (
            r'<div class="course-detail__row course-detail__description">'
            r'<h2 class="course-detail__title">Description</h2>'
            r'<div class="cms-placeholder')
        self.assertIsNotNone(re.search(pattern, str(response.content)))
        pattern = (r'<div class="category-badge-list__container">'
                   r'<div class="cms-placeholder')
        self.assertIsNotNone(re.search(pattern, str(response.content)))
        pattern = r'<div class="subheader__teaser"><div class="cms-placeholder'
        self.assertIsNotNone(re.search(pattern, str(response.content)))
        pattern = (r'<div class="subheader__content">'
                   r'<div class="characteristics">'
                   r"<ul.*</ul>"
                   r"</div>"
                   r'<div class="cms-placeholder')
        self.assertIsNotNone(re.search(pattern, str(response.content)))
        pattern = (
            r'<div class="section__items section__items--organizations">'
            r'<div class="cms-placeholder')
        self.assertIsNotNone(re.search(pattern, str(response.content)))
        pattern = (r'<div class="section__items section__items--team">'
                   r'<div class="cms-placeholder')
        self.assertIsNotNone(re.search(pattern, str(response.content)))
        pattern = (
            r'<div class="course-detail__row course-detail__information">'
            r'<div class="cms-placeholder')
        self.assertIsNotNone(re.search(pattern, str(response.content)))
        pattern = (
            r'<h3 class="course-detail__label">'
            r'License for the course content</h3><div class="cms-placeholder')
        self.assertIsNotNone(re.search(pattern, str(response.content)))
Beispiel #15
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_templates_person_detail_related_courses(self):
        """
        The courses to which a person has participated should appear on this person's detail page.
        """
        user = UserFactory(is_staff=True, is_superuser=True)
        self.client.login(username=user.username, password="******")

        person = PersonFactory()
        course = CourseFactory(fill_team=[person])

        url = person.extended_object.get_absolute_url()
        response = self.client.get(url)

        # The course should be present on the page
        self.assertContains(
            response,
            f'<p class="course-glimpse__title">{course.extended_object.get_title():s}</p>',
            html=True,
        )
    def test_open_graph_course_no_cover_image(self):
        """
        Test if a course without a cover image has the default logo as og:image meta
        """
        course = CourseFactory(
            page_title="Introduction to Programming",
            code="IntroProg",
            page_languages=["en", "fr"],
        )
        course_page = course.extended_object
        course_page.publish("en")

        url = course_page.get_absolute_url(language="en")
        response = self.client.get(url)
        response_content = response.content.decode("UTF-8")

        match = re.search("<meta[^>]+og:image[^>]+>", response_content)
        html_meta_og_image = match.group(0)
        self.assertIn("richie/images/logo.png", html_meta_og_image)
Beispiel #18
0
    def test_models_course_copy_relations_publish_recursive_loop(self):
        """
        In a previous version of the the CourseRun method "copy_translations" in which we
        used instances instead of update queries, this test was generating an infinite
        recursive loop.
        """
        course = CourseFactory(page_title="my course title")
        TitleFactory(page=course.extended_object,
                     language="fr",
                     title="mon titre de cours")
        course_run = CourseRunFactory(direct_course=course, title="my run")
        course_run_translation_fr = CourseRunTranslation.objects.create(
            master=course_run, language_code="fr", title="ma session")
        self.assertTrue(course.extended_object.publish("fr"))

        course_run_translation_fr.title = "ma session modifiée"
        course_run_translation_fr.save()

        self.assertTrue(course.extended_object.publish("fr"))
    def test_templates_course_detail_runs_future_open(self):
        """
        Priority 1: an upcoming open course run should show in a separate section.
        """
        course = CourseFactory(page_title="my course", should_publish=True)
        self.create_run_future_open(course)

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

        self.assertNotContains(response, "No open course runs")
        self.assertContains(
            response,
            '<a href="/en/my-course/my-course-run/">Enroll now</a>',
            html=True,
        )
        self.assertNotContains(
            response, '<h3 class="course-detail__aside__runs__block__title">')
Beispiel #20
0
    def test_admin_form_course_run_staff_all_permissions(self):
        """A staff user with all permissions can submit a form."""
        course = CourseFactory()
        user = UserFactory(is_staff=True)

        self.add_permission(user, "change_page")
        PagePermission.objects.create(
            page=course.extended_object,
            user=user,
            can_add=False,
            can_change=True,
            can_delete=False,
            can_publish=False,
            can_move_page=False,
        )

        form = self._get_admin_form(course, user)

        self.assertTrue(form.is_valid())
Beispiel #21
0
    def test_course_organization_main_always_included_in_organizations(self):
        """
        The main organization should always be in the organizations linked via many-to-many
        """
        organization1, organization2 = OrganizationFactory.create_batch(2)
        course = CourseFactory(organization_main=organization1)
        self.assertEqual(list(course.organizations.all()), [organization1])

        # Now set the second organization as the main
        course.organization_main = organization2
        course.save()
        self.assertEqual(course.organization_main, organization2)
        self.assertEqual(list(course.organizations.all()),
                         [organization1, organization2])

        # Setting an organization that is already included as many-to-many should not fail
        course.organization_main = organization1
        course.save()
        self.assertEqual(course.organization_main, organization1)
        self.assertEqual(list(course.organizations.all()),
                         [organization1, organization2])
Beispiel #22
0
    def test_views_redirect_edx_courses_success_with_old_course_uri(self):
        """Old OpenEdX course urls are redirected to the corresponding page in richie."""
        course = CourseFactory(code="abc",
                               page_title="Physique 101",
                               should_publish=True)
        TitleFactory(page=course.extended_object,
                     language="en",
                     title="Physics 101")
        course.extended_object.publish("en")

        response = self.client.get("/courses/sorbonne/abc/001/about/")

        self.assertRedirects(
            response,
            "/fr/physique-101/",
            status_code=301,
            target_status_code=200,
            fetch_redirect_response=True,
        )
Beispiel #23
0
    def test_context_processors_queries_are_cached(self):
        """
        Once the page is cached, no db queries should be made again
        """
        organizations = OrganizationFactory.create_batch(2,
                                                         should_publish=True,
                                                         page_languages=["fr"])
        course = CourseFactory(should_publish=True,
                               fill_organizations=organizations,
                               page_languages=["fr"])
        page = course.extended_object

        # Get the page a first time to cache it
        self.client.get(page.get_public_url())

        # Check that db queries are well cached
        # The one remaining is related to django-cms
        with self.assertNumQueries(1):
            self.client.get(page.get_public_url())
Beispiel #24
0
    def test_models_category_get_courses(self):
        """
        It should be possible to retrieve the list of related courses on the category instance.
        The number of queries should be minimal.
        """
        category = CategoryFactory(should_publish=True)
        courses = CourseFactory.create_batch(
            3, page_title="my title", fill_categories=[category], should_publish=True
        )
        retrieved_courses = category.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"
                )
Beispiel #25
0
    def test_indexers_courses_get_es_document_for_course_not_published(self):
        """
        A course indexed with no published title shoud not be listed.
        """
        course = CourseFactory(
            page_title={"en": "a course", "fr": "un cours"}, should_publish=True
        )

        course_document = CoursesIndexer.get_es_document_for_course(course)
        self.assertTrue(course_document["is_listed"])

        # Only after unpublishing all languages, the course stops being listed
        course.extended_object.unpublish("en")
        course_document = CoursesIndexer.get_es_document_for_course(course)
        self.assertTrue(course_document["is_listed"])

        course.extended_object.unpublish("fr")
        course_document = CoursesIndexer.get_es_document_for_course(course)
        self.assertFalse(course_document["is_listed"])
    def test_organization_courses_copied_when_publishing(self):
        """
        When publishing a organization, the links to draft courses on the draft version of the
        organization should be copied (clear then add) to the published version.
        Links to published courses should not be copied as they are redundant and not
        up-to-date.
        """
        # Create draft courses
        course1, course2 = CourseFactory.create_batch(2)

        # Create a draft organization
        draft_organization = OrganizationFactory(
            with_courses=[course1, course2])

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

        # The draft organization should see all courses and propose a custom filter to easily
        # access the draft versions
        self.assertEqual(
            set(draft_organization.courses.all()),
            {course1, course1.public_extension, course2},
        )
        self.assertEqual(set(draft_organization.courses.drafts()),
                         {course1, course2})

        # Publish the organization and check that the courses are copied
        draft_organization.extended_object.publish("en")
        published_organization = Organization.objects.get(
            extended_object__publisher_is_draft=False)
        self.assertEqual(set(published_organization.courses.all()),
                         {course1, course2})

        # When publishing, the courses that are obsolete should be cleared
        draft_organization.courses.remove(course2)
        self.assertEqual(set(published_organization.courses.all()),
                         {course1, course2})

        # courses on the published organization are only cleared after publishing the draft page
        draft_organization.extended_object.publish("en")
        self.assertEqual(set(published_organization.courses.all()), {course1})
Beispiel #27
0
    def test_indexers_courses_get_es_document_no_image_icon_picture(self):
        """
        ES document is created without errors when a icon image for the course is
        actually a Picture instance without an image on it.
        """
        # Create the example course to index and get hold of its course_icons placeholder
        course = CourseFactory(should_publish=True)
        course_icons_placeholder = course.extended_object.placeholders.filter(
            slot="course_icons").first()
        # Create a category and add it to the course on the icons placeholder
        category = CategoryFactory(should_publish=True, color="#654321")
        add_plugin(course_icons_placeholder, CategoryPlugin, "en",
                   **{"page": category.extended_object})
        course.extended_object.publish("en")
        # Make sure we associate an image-less picture with the category through
        # the icon placeholder
        category_icon_placeholder = category.extended_object.placeholders.filter(
            slot="icon").first()
        add_plugin(category_icon_placeholder,
                   SimplePicturePlugin,
                   "en",
                   picture=None)
        category.extended_object.publish("en")

        indexed_courses = list(
            CoursesIndexer.get_es_documents(index="some_index",
                                            action="some_action"))

        self.assertEqual(len(indexed_courses), 1)
        self.assertEqual(
            indexed_courses[0]["_id"],
            str(course.extended_object.get_public_object().id),
        )
        self.assertEqual(
            indexed_courses[0]["icon"],
            {
                "en": {
                    "color": "#654321",
                    "title": category.extended_object.get_title()
                }
            },
        )
Beispiel #28
0
    def test_templates_course_detail_cms_draft_content_no_code(self):
        """
        Validate that the code is replaced by "..." on the draft page when the "code"
        field is not set.
        """
        user = UserFactory(is_staff=True, is_superuser=True)
        self.client.login(username=user.username, password="******")

        course = CourseFactory(page_title="Very interesting course", code=None)

        # The page should be visible as draft to the staff user
        url = course.extended_object.get_absolute_url()
        response = self.client.get(url)
        self.assertEqual(response.status_code, 200)

        self.assertContains(
            response,
            '<div class="subheader__code">Ref. ...</div>',
            html=True,
        )
    def test_models_course_run_delete_published_cascade(self):
        """
        Deleting a draft course run that is published should delete its public
        counterpart and all its translations by cascade.
        """
        course = CourseFactory(page_languages=["en", "fr"])
        course_run = CourseRunFactory(direct_course=course)
        CourseRunTranslation.objects.create(master=course_run,
                                            language_code="fr",
                                            title="mon titre")
        self.assertTrue(course_run.direct_course.extended_object.publish("en"))
        self.assertTrue(course_run.direct_course.extended_object.publish("fr"))

        self.assertEqual(CourseRun.objects.count(), 2)
        self.assertEqual(CourseRunTranslation.objects.count(), 4)

        course_run.delete()

        self.assertFalse(CourseRun.objects.exists())
        self.assertFalse(CourseRunTranslation.objects.exists())
    def prepare_to_test_state(self, state):
        """
        Not a test.
        Create objects and mock to help testing the impact of the state on template rendering.
        """
        course = CourseFactory(page_title="my course", should_publish=True)
        CourseRunFactory(
            page_parent=course.extended_object,
            page_title="my course run",
            should_publish=True,
        )

        url = course.extended_object.get_absolute_url()
        with mock.patch.object(
            CourseRun, "state", new_callable=mock.PropertyMock, return_value=state
        ):
            response = self.client.get(url)

        self.assertEqual(response.status_code, 200)
        return response