def test_admin_person_change_view_post(self): """ Validate that the person can be updated via the admin. """ user = UserFactory(is_staff=True, is_superuser=True) self.client.login(username=user.username, password="******") # Create a person, title will automaticaly be created person = PersonFactory() # create a new title new_title = PersonTitleFactory() # Get the admin change view url = reverse("admin:persons_person_change", args=[person.id]) data = { "person_title": new_title.id, "first_name": "New First Name", "last_name": "New Last Name", } response = self.client.post(url, data) self.assertEqual(response.status_code, 302) # Check that the person was updated as expected person.refresh_from_db() self.assertEqual(person.person_title, new_title) self.assertEqual(person.first_name, "New First Name") self.assertEqual(person.last_name, "New Last Name")
def test_models_person_get_full_name(self): """ The get_full_name method should return title, first name and last name separated by space. No SQL query should be generated. """ person_title = PersonTitleFactory(title="Madam", abbreviation="Mme") person = PersonFactory( first_name="Louise", last_name="Dupont", person_title=person_title ) with self.assertNumQueries(0): self.assertEqual(person.get_full_name(), "Madam Louise Dupont")
def test_models_person_fields_last_name_required(self): """ The `last_name` field should be required """ with self.assertRaises(ValidationError) as context: PersonFactory(last_name=None) self.assertEqual(context.exception.messages[0], "This field cannot be null.")
def test_person_factory(self): """ PersonFactoryTestCase should be able to generate plugins with realistic fake data: portrait and resume. """ person = PersonFactory(with_content=True) # The portrait plugin should point to one of our fixtures images portrait_placeholder = person.extended_object.placeholders.get( slot="portrait") portrait_plugin = portrait_placeholder.cmsplugin_set.get( plugin_type="PicturePlugin") self.assertIn( "portrait", os.path.basename( portrait_plugin.djangocms_picture_picture.picture.file.name), ) # The resume plugin should contain paragraphs resume_placeholder = person.extended_object.placeholders.get( slot="resume") resume_plugin = resume_placeholder.cmsplugin_set.get( plugin_type="CKEditorPlugin") self.assertIn("<p>", resume_plugin.simple_text_ckeditor_simpletext.body)
def test_cms_toolbars_person_has_page_extension_settings_item(self): """ Validate that a new item to edit the person is available only when visiting the page in edit mode and for users with permission to edit the page. """ person = PersonFactory() self.check_toolbar_item(person, "Person settings...")
def test_cms_plugins_person_form_page_choices(self): """ The form to create a person plugin should only list person pages in the select box. """ class PersonPluginModelForm(forms.ModelForm): """A form for testing the choices in the select box""" class Meta: model = PersonPluginModel exclude = () person = PersonFactory() other_page_title = "other page" create_page(other_page_title, "richie/fullwidth.html", settings.LANGUAGE_CODE) plugin_form = PersonPluginModelForm() self.assertIn(person.get_full_name(), plugin_form.as_table()) self.assertNotIn(other_page_title, plugin_form.as_table())
def test_cms_plugins_person_render_on_draft_page(self): """ The person 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 Person person = PersonFactory(first_name="Meimei") person_page = person.extended_object # Add resume to related placeholder resume_placeholder = person_page.placeholders.get(slot="resume") resume_en = add_plugin( resume_placeholder, PlainTextPlugin, "en", **{"body": "public resume"} ) # Create a page to add the plugin to page = create_i18n_page("A page") placeholder = page.placeholders.get(slot="maincontent") add_plugin(placeholder, PersonPlugin, "en", **{"page": person.extended_object}) person_page.publish("en") person_page.unpublish("en") url = "{:s}?edit".format(page.get_absolute_url(language="en")) # The person plugin should still be visible on the draft page response = self.client.get(url) self.assertContains(response, "Meimei") self.assertContains(response, "public resume") # Now modify the person to have a draft different from the public version person.first_name = "Jiji" person.save() resume_en.body = "draft resume" resume_en.save() # The draft version of the person plugin should now be visible response = self.client.get(url) self.assertContains(response, "Jiji") self.assertContains(response, "draft resume") self.assertNotContains(response, "Meimei") self.assertNotContains(response, "public resume")
def test_models_person_fields_person_title_required(self): """ The `person_title` field should be required """ # Create a page and pass it to the factory, because it can't create a CMS page # without a valid person_title page = create_page("A page", Person.TEMPLATE_DETAIL, "en") with self.assertRaises(ValidationError) as context: PersonFactory(person_title=None, extended_object=page) self.assertEqual(context.exception.messages[0], "This field cannot be null.")
def test_cms_toolbars_person_has_page_extension_settings_item(self): """ Validate that a new item to edit the person is available only when visiting the page in edit mode and for users with permission to edit the page. """ person = PersonFactory() # Create different users for each possible level of access # pylint: disable=too-many-locals superuser = UserFactory(is_staff=True, is_superuser=True) staff_with_permission = UserFactory(is_staff=True) user_with_permission = UserFactory() staff = UserFactory(is_staff=True) user = UserFactory() anonymous = AnonymousUser() # Add global permission to change page for users concerned can_change_page = Permission.objects.get(codename="change_page") staff_with_permission.user_permissions.add(can_change_page) user_with_permission.user_permissions.add(can_change_page) cases = [ ([superuser, False, False], self.check_disabled), ([superuser, True, False], self.check_active), ([superuser, False, True], self.check_disabled), ([staff_with_permission, False, False], self.check_disabled), ([staff_with_permission, True, False], self.check_active), ([staff_with_permission, False, True], self.check_disabled), ([staff, False, False], self.check_missing), ([staff, True, False], self.check_missing), ([staff, False, True], self.check_missing), ([user_with_permission, False, False], self.check_absent), ([user_with_permission, True, False], self.check_absent), ([user_with_permission, False, True], self.check_absent), ([user, False, False], self.check_absent), ([user, True, False], self.check_absent), ([user, False, True], self.check_absent), ([anonymous, False, False], self.check_absent), ([anonymous, True, False], self.check_absent), ([anonymous, False, True], self.check_absent), ] url = "/en/admin/persons/person/{id:d}/change/".format(id=person.id) for args, method in cases: toolbar = self.get_toolbar_for_page(person.extended_object, *args) item = method(toolbar, "Person settings...") if item: self.assertEqual(item.url, url)
def test_factories_person_resume(self): """ PersonFactory should be able to generate plugins with a realistic resume for several languages. """ person = PersonFactory(page_languages=["fr", "en"], fill_resume=True) # Check that the resume plugins were created as expected resume = person.extended_object.placeholders.get(slot="resume") self.assertEqual(resume.cmsplugin_set.count(), 2) # The resume plugins should contain paragraphs for language in ["fr", "en"]: resume_plugin = resume.cmsplugin_set.get( plugin_type="CKEditorPlugin", language=language) self.assertIn("<p>", resume_plugin.simple_text_ckeditor_simpletext.body)
def test_admin_person_change_view_get(self): """ The admin change view should include the editable and readonly fields as expected. """ user = UserFactory(is_staff=True, is_superuser=True) self.client.login(username=user.username, password="******") # Create a person person = PersonFactory() # Get the admin change view url = reverse("admin:persons_person_change", args=[person.id]) response = self.client.get(url) # Check that the page includes all our fields self.assertContains(response, person.person_title) self.assertContains(response, person.first_name) self.assertContains(response, person.last_name)
def test_models_person_str(self): """ The string representation should be built with the page `title` and all person fields. Only 1 query to the associated page should be generated. """ page = create_page("Page of Lady Louise Dupont", "persons/cms/person_detail.html", "en") person = PersonFactory( extended_object=page, first_name="Louise", last_name="Dupont", person_title__translation__title="Madam", person_title__translation__abbreviation="Mme", ) with self.assertNumQueries(3): self.assertEqual( str(person), "Person: Page of Lady Louise Dupont (Madam Louise Dupont)")
def test_factories_person_portrait(self): """ PersonFactory should be able to generate plugins with a realistic portrait for several languages. """ person = PersonFactory(page_languages=["fr", "en"], fill_portrait=True) # Check that the portrait plugins were created as expected portrait = person.extended_object.placeholders.get(slot="portrait") self.assertEqual(portrait.cmsplugin_set.count(), 2) # The portrait plugins should point to one of our fixtures images for language in ["fr", "en"]: portrait_plugin = portrait.cmsplugin_set.get( plugin_type="PicturePlugin", language=language) self.assertIn( "portrait", os.path.basename(portrait_plugin.djangocms_picture_picture. picture.file.name), )
def test_admin_person_list_view(self): """ The admin list view of persons should display page title, person's title first_name and last_name """ user = UserFactory(is_staff=True, is_superuser=True) self.client.login(username=user.username, password="******") # Create a person linked to a page person = PersonFactory() # Get the admin list view url = reverse("admin:persons_person_changelist") response = self.client.get(url) # Check that the page includes all our fields self.assertContains(response, person.extended_object.get_title(), status_code=200) self.assertContains(response, person.first_name) self.assertContains(response, person.last_name) self.assertContains(response, person.person_title)
def test_models_person_get_full_name(self): """ The get_full_name method should return title, first name and last name separated by space. No SQL query should be generated. """ person1 = PersonFactory( first_name="Louise", last_name="Dupont", person_title__translation__title="Madam", person_title__translation__abbreviation="Mme", ) person2 = PersonFactory(first_name="Jacques", last_name="Martin", person_title=None) with self.assertNumQueries(1): self.assertEqual(person1.get_full_name(), "Madam Louise Dupont") self.assertEqual(person2.get_full_name(), "Jacques Martin")
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, page_in_navigation=True, page_languages=["en", "fr"], page_parent=pages_created["organizations"], fill_banner=True, fill_description=True, fill_logo=True, should_publish=True, ) # Generate each category tree and return a list of the leaf categories levels = list(create_categories(LEVELS_INFO, pages_created["categories"])) subjects = list( create_categories(SUBJECTS_INFO, pages_created["categories"])) title = PersonTitleFactory(translation=None) PersonTitleTranslationFactory(master=title, language_code="en", title="Doctor", abbreviation="Dr.") PersonTitleTranslationFactory(master=title, language_code="fr", title="Docteur", abbreviation="Dr.") # Create persons under the `persons` page persons = PersonFactory.create_batch( NB_PERSONS, page_in_navigation=True, page_languages=["en", "fr"], page_parent=pages_created["persons"], person_title=random.choice([title, None]), fill_portrait=True, fill_resume=True, should_publish=True, ) # Create courses under the `Course` page with categories and organizations # relations courses = [] for _ in range(NB_COURSES): video_sample = random.choice(VIDEO_SAMPLE_LINKS) course = CourseFactory( page_in_navigation=True, page_languages=["en", "fr"], page_parent=pages_created["courses"], 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_categories=[ *random.sample( subjects, random.randint(1, NB_COURSES_SUBJECT_RELATIONS)), random.choice(levels), ], fill_organizations=random.sample( organizations, NB_COURSES_ORGANIZATION_RELATIONS), fill_texts=[ "course_syllabus", "course_format", "course_prerequisites", "course_plan", # "course_license_content", # "course_license_participation", ], should_publish=True, ) courses.append(course) # Add a random number of course runs to the course nb_course_runs = get_number_of_course_runs() # 1) Make sure we have one course run open for enrollment now = timezone.now() CourseRunFactory( __sequence=nb_course_runs, page_in_navigation=False, page_parent=course.extended_object, start=now + timedelta(days=1), enrollment_start=now - timedelta(days=5), enrollment_end=now + timedelta(days=5), should_publish=True, ) # 2) Add more random course runs for i in range(nb_course_runs - 1, 0, -1): CourseRunFactory( __sequence=i, page_in_navigation=False, page_languages=["en", "fr"], page_parent=course.extended_object, should_publish=True, ) # Once everything has been created, use some content to create a homepage placeholder = pages_created["home"].placeholders.get(slot="maincontent") # - Get a banner image banner_file = file_getter("banner")() wrapped_banner = File(banner_file, banner_file.name) banner = Image.objects.create(file=wrapped_banner) # - Get a logo image logo_file = file_getter("logo")() wrapped_logo = File(logo_file, logo_file.name) logo = Image.objects.create(file=wrapped_logo) # - Create the home page in each language for language, content in HOMEPAGE_CONTENT.items(): # Add a banner add_plugin( language=language, placeholder=placeholder, plugin_type="LargeBannerPlugin", title=content["banner_title"], background_image=banner, logo=logo, logo_alt_text="logo", content=content["banner_content"], template=content["banner_template"], ) # Add highlighted courses courses_section = add_plugin( language=language, placeholder=placeholder, plugin_type="SectionPlugin", title=content["courses_title"], template=content["section_template"], ) for course in random.sample(courses, NB_HOME_HIGHLIGHTED_COURSES): add_plugin( language=language, placeholder=placeholder, plugin_type="CoursePlugin", target=courses_section, page=course.extended_object, ) # Add highlighted organizations organizations_section = add_plugin( language=language, placeholder=placeholder, plugin_type="SectionPlugin", title=content["organizations_title"], template=content["section_template"], ) for organization in random.sample(organizations, NB_HOME_HIGHLIGHTED_ORGANIZATIONS): add_plugin( language=language, placeholder=placeholder, plugin_type="OrganizationPlugin", target=organizations_section, page=organization.extended_object, ) # Add highlighted subjects subjects_section = add_plugin( language=language, placeholder=placeholder, plugin_type="SectionPlugin", title=content["subjects_title"], template=content["section_template"], ) for subject in random.sample(subjects, NB_HOME_HIGHLIGHTED_SUBJECTS): add_plugin( language=language, placeholder=placeholder, plugin_type="CategoryPlugin", target=subjects_section, page=subject.extended_object, ) # Add highlighted persons persons_section = add_plugin( language=language, placeholder=placeholder, plugin_type="SectionPlugin", title=content["persons_title"], template=content["section_template"], ) for person in random.sample(persons, NB_HOME_HIGHLIGHTED_PERSONS): add_plugin( language=language, placeholder=placeholder, plugin_type="PersonPlugin", target=persons_section, page=person.extended_object, ) # Once content has been added we must publish again homepage in every # edited Languages pages_created["home"].publish("en") pages_created["home"].publish("fr")
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_cms_plugins_person_render(self): """ Test that a PersonPlugin correctly renders person's page specific information """ # Create a filer fake image staff = UserFactory(is_staff=True, is_superuser=True) image = FilerImageFactory(owner=staff) # Create a Person person = PersonFactory() person_page = person.extended_object # Add portrait to related placeholder portrait_placeholder = person_page.placeholders.get(slot="portrait") add_plugin( portrait_placeholder, PicturePlugin, "en", **{ "picture": image, "attributes": { "alt": "portrait description" } }) add_plugin( portrait_placeholder, PicturePlugin, "fr", **{ "picture": image, "attributes": { "alt": "description du portrait" } }) # A resume to related placeholder resume_placeholder = person_page.placeholders.get(slot="resume") add_plugin(resume_placeholder, PlaintextPlugin, "en", **{"body": "A short resume"}) add_plugin(resume_placeholder, PlaintextPlugin, "fr", **{"body": "Un résumé court"}) # 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, PersonPlugin, "en", **{"person": person}) add_plugin(placeholder, PersonPlugin, "fr", **{"person": person}) page.publish("en") page.publish("fr") # Check the page content in English url = page.get_absolute_url(language="en") response = self.client.get(url) # Person's name should be present as a link to the cms page # And CMS page title should be in title attribute of the link self.assertContains( response, '<a href="{url}" title="{page_title}">'.format( url=person_page.get_absolute_url(), page_title=person_page.get_title()), status_code=200, ) self.assertContains(response, person.get_full_name(), html=True) # Person's portrait and its properties should be present # pylint: disable=no-member self.assertContains(response, image.file.name) # Short resume should be present self.assertContains( response, '<div class="person-plugin__content__text">A short resume</div>', html=True, ) # The person's full name should be wrapped in a h2 self.assertContains( response, '<h2 class="person-plugin__content__title">{:s}</h2>'.format( person.get_full_name()), html=True, ) # Same checks in French url = page.get_absolute_url(language="fr") response = self.client.get(url) self.assertContains( response, '<a href="{url}" title="{page_title}">'.format( url=person_page.get_absolute_url(), page_title=person_page.get_title()), status_code=200, ) # pylint: disable=no-member self.assertContains(response, image.file.name) self.assertContains( response, '<div class="person-plugin__content__text">Un résumé court</div>', html=True, )
def test_cms_plugins_person_render_on_public_page(self): """ The person plugin should render as expected on a public page. """ # Create a filer fake image image = FilerImageFactory() # Create a Person person = PersonFactory( page_title={"en": "person title", "fr": "titre personne"}, first_name="Meimei", ) person_page = person.extended_object # Add portrait to related placeholder portrait_placeholder = person_page.placeholders.get(slot="portrait") add_plugin( portrait_placeholder, PicturePlugin, "en", **{"picture": image, "attributes": {"alt": "portrait description"}} ) add_plugin( portrait_placeholder, PicturePlugin, "fr", **{"picture": image, "attributes": {"alt": "description du portrait"}} ) # Add resume to related placeholder resume_placeholder = person_page.placeholders.get(slot="resume") resume_en = add_plugin( resume_placeholder, PlainTextPlugin, "en", **{"body": "public resume"} ) add_plugin( resume_placeholder, PlainTextPlugin, "fr", **{"body": "résumé public"} ) # 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, PersonPlugin, "en", **{"page": person.extended_object}) add_plugin(placeholder, PersonPlugin, "fr", **{"page": person.extended_object}) person_page.publish("en") person_page.publish("fr") person.refresh_from_db() page.publish("en") page.publish("fr") url = page.get_absolute_url(language="en") # The person plugin should not be visible on the public page before it is published person_page.unpublish("en") response = self.client.get(url) self.assertNotContains(response, "Meimei") # Republishing the plugin should not make it public person_page.publish("en") response = self.client.get(url) self.assertNotContains(response, "Meimei") # Now modify the person to have a draft different from the public version person.first_name = "Jiji" person.save() resume_en.body = "draft resume" resume_en.save() # Publishing the page again should make the plugin public page.publish("en") # Check the page content in English response = self.client.get(url) # Person's name should be present as a link to the cms page # And CMS page title should be in title attribute of the link self.assertContains( response, '<a href="/en/person-title/" title="{name:s}">'.format( name=person.public_extension.get_full_name() ), status_code=200, ) # The person's full name should be wrapped in a h2 self.assertContains( response, '<h2 class="person-plugin__content__title">{:s}</h2>'.format( person.public_extension.get_full_name() ), html=True, ) self.assertContains(response, "Meimei") self.assertNotContains(response, "Jiji") # Person's portrait and its properties should be present # pylint: disable=no-member self.assertContains(response, image.file.name) # Short resume should be present self.assertContains( response, '<div class="person-plugin__content__text">public resume</div>', html=True, ) self.assertNotContains(response, "draft resume") # Same checks in French url = page.get_absolute_url(language="fr") response = self.client.get(url) self.assertContains( response, '<a href="/fr/titre-personne/" title="{name:s}">'.format( name=person.public_extension.get_full_name() ), status_code=200, ) # pylint: disable=no-member self.assertContains(response, image.file.name) self.assertContains( response, '<div class="person-plugin__content__text">résumé public</div>', html=True, )
def test_models_person_fields_person_title_not_required(self): """ The `person_title` field should not be required """ person = PersonFactory(person_title=None) self.assertIsNone(person.person_title)