예제 #1
0
    def test_templates_program_detail_open_graph_description_program_excerpt_exceeds_max_length(
        self, ):
        """
        The open graph description should be cut if it exceeds more than 200 caracters
        """
        program = ProgramFactory()
        page = program.extended_object
        placeholder_value = (
            "Long description that describes the page with a summary. " * 8)

        # Add a excerpt to a program
        placeholder = program.extended_object.placeholders.get(
            slot="program_excerpt")
        add_plugin(
            language="en",
            placeholder=placeholder,
            plugin_type="PlainTextPlugin",
            body=placeholder_value,
        )
        page.publish("en")

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

        cut = placeholder_value[0:200]
        self.assertContains(
            response,
            f'<meta property="og:description" content="{cut}" />',
        )
예제 #2
0
    def test_templates_program_detail_meta_description_program_excerpt_exceeds_max_length(
        self, ):
        """
        The program meta description should show the program_excerpt if no meta_description is
        specified
        """
        program = ProgramFactory()
        page = program.extended_object
        placeholder_value = (
            "Long description that describes the page with a summary. "
            "Long description that describes the page with a summary. "
            "Long description that describes the page with a summary. ")

        # Add a excerpt to a program
        placeholder = program.extended_object.placeholders.get(
            slot="program_excerpt")
        add_plugin(
            language="en",
            placeholder=placeholder,
            plugin_type="PlainTextPlugin",
            body=placeholder_value,
        )
        page.publish("en")

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

        cut = placeholder_value[0:160]
        self.assertContains(
            response,
            f'<meta name="description" content="{cut}" />',
        )
    def test_cms_plugins_program_fallback_when_published_unpublished(self):
        """
        The program plugin should not render when the program was voluntarily
        unpublished in the current language.
        """
        # Create a program
        program = ProgramFactory(
            page_title={
                "en": "public title",
                "fr": "titre public"
            },
            fill_cover={
                "original_filename": "cover.jpg",
                "default_alt_text": "my cover",
            },
        )
        program_page = program.extended_object

        # Create a page to add the plugin to
        page = create_i18n_page({"en": "A page", "fr": "Une page"})
        placeholder = page.placeholders.get(slot="maincontent")
        add_plugin(placeholder, ProgramPlugin, "en", **{"page": program_page})
        add_plugin(placeholder, ProgramPlugin, "fr", **{"page": program_page})

        # Publish only the French version of the program
        program_page.publish("fr")
        program_page.publish("en")
        program_page.unpublish("en")

        # Check the page content in English
        page.publish("en")
        url = page.get_absolute_url(language="en")
        response = self.client.get(url)

        self.assertNotContains(response, "glimpse")
    def test_cms_wizards_program_submit_form_slug_duplicate(self):
        """
        Trying to create a program with a slug that would lead to a duplicate path should
        raise a validation error.
        """
        # A parent page should pre-exist
        parent_page = create_page(
            "News",
            "richie/single_column.html",
            "en",
            reverse_id=Program.PAGE["reverse_id"],
        )
        # Create an existing page with a known slug
        ProgramFactory(page_parent=parent_page, page_title="My title")

        # Submit a title that will lead to the same slug
        data = {"title": "my title"}
        user = UserFactory(is_staff=True, is_superuser=True)
        form = ProgramWizardForm(data=data,
                                 wizard_language="en",
                                 wizard_user=user)

        self.assertFalse(form.is_valid())
        self.assertEqual(form.errors,
                         {"slug": ["This slug is already in use"]})
예제 #5
0
    def test_templates_program_detail_cms_no_course(self):
        """
        Validate that a program without course doesn't show the course section
        on a published program page but does on the draft program page
        """
        program = ProgramFactory(
            page_title="Preums",
            fill_cover=True,
            fill_excerpt=True,
            fill_body=True,
        )
        page = program.extended_object

        # Publish the program and ensure the content is absent
        page.publish("en")
        url = page.get_absolute_url()
        response = self.client.get(url)
        self.assertNotContains(
            response,
            '<div class="program-detail__courses program-detail__block">')

        # The content should be visible as draft to the staff user
        user = UserFactory(is_staff=True, is_superuser=True)
        self.client.login(username=user.username, password="******")
        response = self.client.get(url)
        self.assertContains(
            response,
            '<div class="program-detail__courses program-detail__block">')
예제 #6
0
    def test_templates_program_detail_cms_published_content_opengraph(self):
        """The program logo should be used as opengraph image."""
        program = ProgramFactory(
            fill_cover={
                "original_filename": "cover.jpg",
                "default_alt_text": "my cover",
            },
            should_publish=True,
        )
        url = program.extended_object.get_absolute_url()
        response = self.client.get(url)
        self.assertEqual(response.status_code, 200)

        self.assertContains(response,
                            '<meta property="og:type" content="website" />')
        self.assertContains(
            response,
            f'<meta property="og:url" content="http://example.com{url:s}" />')
        pattern = (
            r'<meta property="og:image" content="http://example.com'
            r"/media/filer_public_thumbnails/filer_public/.*cover\.jpg__1200x630"
        )
        self.assertIsNotNone(re.search(pattern, str(response.content)))
        self.assertContains(
            response, '<meta property="og:image:width" content="1200" />')
        self.assertContains(
            response, '<meta property="og:image:height" content="630" />')
예제 #7
0
    def test_templates_program_detail_open_graph_description_program_excerpt(
            self):
        """
        An opengraph description meta should be present if the program_excerpt placeholder is set.
        """
        program = ProgramFactory()
        page = program.extended_object

        # Add a excerpt to a program
        placeholder = program.extended_object.placeholders.get(
            slot="program_excerpt")
        add_plugin(
            language="en",
            placeholder=placeholder,
            plugin_type="PlainTextPlugin",
            body="A program excerpt description",
        )
        page.publish("en")

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

        self.assertContains(
            response,
            '<meta property="og:description" content="A program excerpt description" />',
        )
예제 #8
0
    def test_cms_plugins_program_render_on_draft_page(self):
        """
        The program 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 Program
        program = ProgramFactory(page_title="public title")
        program_page = program.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, ProgramPlugin, "en", **{"page": program_page})

        program_page.publish("en")
        program_page.unpublish("en")
        program_page.refresh_from_db()

        url = "{:s}?edit".format(page.get_absolute_url(language="en"))

        # The program plugin should still be visible on the draft page
        response = self.client.get(url)
        self.assertContains(response, "public title")

        # Now modify the program to have a draft different from the public version
        title_obj = program_page.get_title_obj(language="en")
        title_obj.title = "draft title"
        title_obj.save()

        # The draft version of the program plugin should now be visible
        response = self.client.get(url)
        self.assertContains(response, "draft title")
        self.assertNotContains(response, "public title")
예제 #9
0
    def test_templates_program_detail_meta_description_program_excerpt(self):
        """
        The program meta description should show the program_excerpt if no meta_description is
        specified
        """
        program = ProgramFactory()
        page = program.extended_object

        # Add a excerpt to a program
        placeholder = program.extended_object.placeholders.get(
            slot="program_excerpt")
        add_plugin(
            language="en",
            placeholder=placeholder,
            plugin_type="PlainTextPlugin",
            body="A program excerpt description",
        )
        page.publish("en")

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

        self.assertContains(
            response,
            '<meta name="description" content="A program excerpt description" />',
        )
    def test_templates_program_detail_cms_draft_content(self):
        """
        A staff user should see a draft program including only its public elements.
        """
        user = UserFactory(is_staff=True, is_superuser=True)
        self.client.login(username=user.username, password="******")

        courses = CourseFactory.create_batch(4)
        program = ProgramFactory(
            page_title="Preums",
            fill_cover=True,
            fill_excerpt=True,
            fill_body=True,
            fill_courses=courses,
        )
        page = program.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>Preums</title>",
                            html=True,
                            status_code=200)
        self.assertContains(response,
                            '<h1 class="subheader__title">Preums</h1>',
                            html=True)

        # The published courses should be present on the page
        for course in courses[:2]:
            self.assertContains(
                response,
                '<p class="course-glimpse__title">{:s}</p>'.format(
                    course.extended_object.get_title()),
                html=True,
            )
        # Draft courses should not be present on the page
        for course in courses[-2:]:
            self.assertNotIn(
                course.extended_object.get_absolute_url(),
                re.sub(" +", " ",
                       str(response.content).replace("\\n", "")),
            )
            self.assertNotContains(
                response,
                course.extended_object.get_title(),
                html=True,
            )
예제 #11
0
    def test_templates_program_detail_cms_published_content(self):
        """
        Validate that the important elements are displayed on a published program page
        """
        courses = CourseFactory.create_batch(4)

        program = ProgramFactory(
            page_title="Preums",
            fill_cover=True,
            fill_excerpt=True,
            fill_body=True,
            fill_courses=courses,
        )
        page = program.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 program and ensure the content is correct
        page.publish("en")
        response = self.client.get(url)
        self.assertContains(
            response,
            "<title>Preums - Program - example.com</title>",
            html=True,
            status_code=200,
        )
        self.assertContains(response,
                            '<h1 class="subheader__title">Preums</h1>',
                            html=True)

        # Only published courses should be present on the page
        for course in courses[:2]:
            self.assertContains(
                response,
                '<span class="course-glimpse__title-text">{0:s}</span>'.format(  # noqa pylint: disable=consider-using-f-string,line-too-long
                    course.extended_object.get_title()),
                html=True,
            )
        for course in courses[-2:]:
            self.assertNotContains(response,
                                   course.extended_object.get_title())
예제 #12
0
    def test_templates_program_list_cms_content(self):
        """
        Validate that the public website only displays programs that are currently published,
        while staff users can see draft and unpublished programs.
        """
        page = PageFactory(
            template="courses/cms/program_list.html",
            title__language="en",
            should_publish=True,
        )

        ProgramFactory(page_parent=page, page_title="First program")
        ProgramFactory(
            page_parent=page, page_title="Second program", should_publish=True
        )

        # Publish with a publication date in the future
        future = timezone.now() + timedelta(hours=1)
        with mock.patch("cms.models.pagemodel.now", return_value=future):
            ProgramFactory(
                page_parent=page, page_title="Third program", should_publish=True
            )

        # Anonymous users should only see published programs
        response = self.client.get(page.get_absolute_url())

        self.assertEqual(response.status_code, 200)
        self.assertNotContains(response, "First")
        self.assertContains(response, "Second")
        self.assertNotContains(response, "Third")

        # Staff users can see draft and unpublished programs
        user = UserFactory(is_staff=True, is_superuser=True)
        self.client.login(username=user.username, password="******")

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

        for title in ["First", "Second", "Third"]:
            self.assertContains(response, title)
    def test_cms_plugins_program_fallback_when_never_published(self):
        """
        The program plugin should render in the fallback language when the program
        page has never been published in the current language.
        """
        # Create a program
        program = ProgramFactory(
            page_title={
                "en": "public program",
                "fr": "programme publique"
            },
            fill_cover={
                "original_filename": "cover.jpg",
                "default_alt_text": "my cover",
            },
        )
        program_page = program.extended_object

        # Create a page to add the plugin to
        page = create_i18n_page({"en": "A page", "fr": "Une page"})
        placeholder = page.placeholders.get(slot="maincontent")
        add_plugin(placeholder, ProgramPlugin, "en", **{"page": program_page})
        add_plugin(placeholder, ProgramPlugin, "fr", **{"page": program_page})

        # Publish only the French version of the program
        program_page.publish("fr")

        # Check the page content in English
        page.publish("en")
        url = page.get_absolute_url(language="en")
        response = self.client.get(url)

        # Program's name should be present as a link to the cms page
        self.assertContains(
            response,
            '<a href="/en/programme-publique/" class="program-glimpse program-glimpse--link" >',
            status_code=200,
        )
        # The program's full name should be wrapped in a h2
        self.assertContains(
            response,
            '<p class="program-glimpse__title">programme publique</p>',
            html=True,
        )
        self.assertNotContains(response, "public program")

        # Program's cover should be present
        pattern = (
            r'<div class="program-glimpse__media">'
            r'<img src="/media/filer_public_thumbnails/filer_public/.*cover\.jpg__300x170'
            r'.*alt=""')
        self.assertIsNotNone(re.search(pattern, str(response.content)))
예제 #14
0
    def test_cms_plugins_program_fallback_when_never_published(self):
        """
        The program plugin should render in the fallback language when the program
        page has never been published in the current language.
        """
        # Create a program
        program = ProgramFactory(
            page_title={
                "en": "public program",
                "fr": "programme publique"
            },
            fill_cover={
                "original_filename": "cover.jpg",
                "default_alt_text": "my cover",
            },
        )
        program_page = program.extended_object

        # Create a page to add the plugin to
        page = create_i18n_page({"en": "A page", "fr": "Une page"})
        placeholder = page.placeholders.get(slot="maincontent")
        add_plugin(placeholder, ProgramPlugin, "en", **{"page": program_page})
        add_plugin(placeholder, ProgramPlugin, "fr", **{"page": program_page})

        # Publish only the French version of the program
        program_page.publish("fr")

        # Check the page content in English
        page.publish("en")
        url = page.get_absolute_url(language="en")
        response = self.client.get(url)

        html = lxml.html.fromstring(response.content)

        # The program's full name should be wrapped in a link within an h2
        title = html.cssselect(".program-glimpse__title")[0]
        link = title.cssselect(".program-glimpse__link")[0]
        self.assertEqual(link.text_content().strip(), "programme publique")
        self.assertNotContains(response, "public program")

        # Program's cover should be present
        cover = html.cssselect(".program-glimpse__media")[0]
        self.assertEqual(cover.get("aria-hidden"), "true")
        img = cover.cssselect("img")[0]
        self.assertIsNotNone(
            re.search(
                r"/media/filer_public_thumbnails/filer_public/.*cover\.jpg__300x170",
                img.get("src"),
            ))
예제 #15
0
    def test_templates_program_detail_meta_description_empty(self):
        """
        The program meta description should not be present if neither the meta_description field
        on the page, nor the `program_excerpt` placeholder are filled
        """
        program = ProgramFactory()
        page = program.extended_object
        page.publish("en")

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

        self.assertNotContains(
            response,
            '<meta name="description"',
        )
예제 #16
0
    def test_factories_program_body(self):
        """
        ProgramFactory should be able to generate plugins with a realistic body for
        several languages.
        """
        program = ProgramFactory(page_languages=["fr", "en"], fill_body=True)

        # Check that the body plugins were created as expected
        body = program.extended_object.placeholders.get(slot="program_body")
        self.assertEqual(body.cmsplugin_set.count(), 2)

        # The body plugins should contain paragraphs
        for language in ["fr", "en"]:
            description_plugin = body.cmsplugin_set.get(
                plugin_type="TextPlugin", language=language)
            self.assertIn("<p>",
                          description_plugin.djangocms_text_ckeditor_text.body)
예제 #17
0
    def test_factories_program_excerpt(self):
        """
        ProgramFactory should be able to generate plugins with a realistic excerpt for
        several languages.
        """
        program = ProgramFactory(page_languages=["fr", "en"],
                                 fill_excerpt=True)

        # Check that the excerpt plugins were created as expected
        excerpt = program.extended_object.placeholders.get(
            slot="program_excerpt")
        self.assertEqual(excerpt.cmsplugin_set.count(), 2)

        # The excerpt plugins should contain paragraphs
        for language in ["fr", "en"]:
            excerpt_plugin = excerpt.cmsplugin_set.get(
                plugin_type="PlainTextPlugin", language=language)
            self.assertTrue(len(excerpt_plugin.plain_text_plaintext.body) > 0)
예제 #18
0
    def test_templates_program_detail_meta_description_empty_program_excerpt(
            self):
        """
        The opengraph description meta should be missing if the program_excerpt placeholder is not
        set.
        """
        program = ProgramFactory()
        page = program.extended_object
        page.publish("en")

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

        self.assertNotContains(
            response,
            "og:description",
        )
예제 #19
0
    def test_factories_program_cover(self):
        """
        ProgramFactory should be able to generate plugins with a realistic cover for several
        languages.
        """
        program = ProgramFactory(page_languages=["fr", "en"], fill_cover=True)

        # Check that the cover plugins were created as expected
        cover = program.extended_object.placeholders.get(slot="program_cover")
        self.assertEqual(cover.cmsplugin_set.count(), 2)

        # The cover plugins should point to one of our fixtures images
        for language in ["fr", "en"]:
            cover_plugin = cover.cmsplugin_set.get(
                plugin_type="SimplePicturePlugin", language=language)
            self.assertIn(
                "cover",
                os.path.basename(
                    cover_plugin.djangocms_picture_picture.picture.file.name),
            )
예제 #20
0
    def test_cms_plugins_program_form_page_choices(self):
        """
        The form to create a program plugin should only list program pages
        in the select box.
        """
        class ProgramPluginModelForm(forms.ModelForm):
            """A form for testing the choices in the select box"""
            class Meta:
                model = ProgramPluginModel
                fields = ["page"]

        program_page = create_i18n_page("my title", published=True)
        program = ProgramFactory(page_parent=program_page)
        other_page_title = "other page"
        create_page(other_page_title, "richie/single_column.html",
                    settings.LANGUAGE_CODE)
        plugin_form = ProgramPluginModelForm()
        rendered_form = plugin_form.as_table()
        self.assertEqual(
            rendered_form.count(program.extended_object.get_title()), 1)
        self.assertNotIn(other_page_title, plugin_form.as_table())
예제 #21
0
    def test_templates_program_detail_meta_description(self):
        """
        The program meta description should show meta_description placeholder if defined
        """
        program = ProgramFactory()
        page = program.extended_object

        title_obj = page.get_title_obj(language="en")
        title_obj.meta_description = "A custom description of the program"
        title_obj.save()

        page.publish("en")

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

        self.assertContains(
            response,
            '<meta name="description" content="A custom description of the program" />',
        )
예제 #22
0
    def test_cms_plugins_program_render_on_public_page(self):
        """
        The program plugin should render as expected on a public page.
        """
        # Create an program
        program = ProgramFactory(
            page_title={
                "en": "public title",
                "fr": "titre public"
            },
            fill_cover={
                "original_filename": "cover.jpg",
                "default_alt_text": "my cover",
            },
        )
        program_page = program.extended_object

        # Create a page to add the plugin to
        page = create_i18n_page({"en": "A page", "fr": "Une page"})
        placeholder = page.placeholders.get(slot="maincontent")
        add_plugin(placeholder, ProgramPlugin, "en", **{"page": program_page})
        add_plugin(placeholder, ProgramPlugin, "fr", **{"page": program_page})

        program_page.publish("en")
        program_page.publish("fr")
        program.refresh_from_db()

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

        # Check the page content in English
        url = page.get_absolute_url(language="en")

        # The program plugin should not be visible on the public page before it is published
        program_page.unpublish("en")
        response = self.client.get(url)
        self.assertNotContains(response, "public title")

        # # Republish the plugin
        program_page.publish("en")

        # Now modify the program to have a draft different from the public version
        title_obj = program_page.get_title_obj(language="en")
        title_obj.title = "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)
        # The program's name should be present as a link to the cms page
        self.assertContains(
            response,
            '<a href="/en/public-title/" class="program-glimpse program-glimpse--link',
            status_code=200,
        )
        # The program's title should be wrapped in a p
        self.assertContains(
            response,
            '<p class="program-glimpse__title">{:s}</p>'.format(
                program.public_extension.extended_object.get_title()),
            html=True,
        )
        self.assertNotContains(response, "draft title")

        # Program's cover should be present
        pattern = (
            r'<div class="program-glimpse__media">'
            r'<img src="/media/filer_public_thumbnails/filer_public/.*cover\.jpg__300x170'
            r'.*alt=""')
        self.assertIsNotNone(re.search(pattern, str(response.content)))

        # Same checks in French
        url = page.get_absolute_url(language="fr")
        response = self.client.get(url)
        self.assertContains(
            response,
            '<a href="/fr/titre-public/" class="program-glimpse program-glimpse--link',
            status_code=200,
        )
        # pylint: disable=no-member
        pattern = (
            r'<div class="program-glimpse__media">'
            r'<img src="/media/filer_public_thumbnails/filer_public/.*cover\.jpg__300x170'
            r'.*alt=""')
        self.assertIsNotNone(re.search(pattern, str(response.content)))
예제 #23
0
    def test_templates_program_detail_cms_draft_content(self):
        """
        A staff user should see a draft program including draft elements.
        """
        user = UserFactory(is_staff=True, is_superuser=True)
        self.client.login(username=user.username, password="******")

        courses = CourseFactory.create_batch(4)
        program = ProgramFactory(
            page_title="Preums",
            fill_cover=True,
            fill_excerpt=True,
            fill_body=True,
            fill_courses=courses,
        )
        page = program.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[3].extended_object.publish("en")
        courses[3].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>Preums - Program - example.com</title>",
            html=True,
            status_code=200,
        )
        self.assertContains(response,
                            '<h1 class="subheader__title">Preums</h1>',
                            html=True)

        # Draft and published courses should be present on the page
        for course in courses[:2]:
            self.assertContains(
                response,
                '<a class="course-glimpse__link" '
                f'href="{course.extended_object.get_absolute_url():s}"',
            )
            self.assertContains(
                response,
                '<span class="course-glimpse__title-text">{0:s}</span>'.format(  # noqa pylint: disable=consider-using-f-string,line-too-long
                    course.extended_object.get_title()),
                html=True,
            )
        self.assertContains(
            response,
            '<div class="course-glimpse course-glimpse--draft">'
            '<div aria-hidden="true" class="course-glimpse__media">'
            f'<a tabindex="-1" href="{courses[2].extended_object.get_absolute_url():s}"',
        )

        self.assertContains(
            response,
            '<span class="course-glimpse__title-text">{0:s}</span>'.format(  # noqa pylint: disable=consider-using-f-string,line-too-long
                courses[2].extended_object.get_title()),
            html=True,
        )
        # The unpublished course should not be present on the page
        self.assertNotContains(response,
                               courses[3].extended_object.get_title())
    def test_templates_program_detail_cms_draft_content(self):
        """
        A staff user should see a draft program 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)
        program = ProgramFactory(
            page_title="Preums",
            fill_cover=True,
            fill_excerpt=True,
            fill_body=True,
            fill_courses=courses,
        )
        page = program.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>Preums</title>", html=True, status_code=200
        )
        self.assertContains(
            response, '<h1 class="program-detail__title">Preums</h1>', html=True
        )

        # The 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,
            )
        # Draft courses should also be present on the page with an annotation for styling
        for course in courses[-2:]:
            self.assertContains(
                response,
                '<a href="{link:s}" class="{name:s} {name:s}--link {name:s}--draft">'.format(
                    name="course-glimpse",
                    link=course.extended_object.get_absolute_url(),
                ),
            )
            self.assertContains(
                response,
                '<p class="course-glimpse__content__title">{title:s}</p>'.format(
                    title=course.extended_object.get_title()
                ),
                html=True,
            )