def test_signals_organizations_no_parent(self, mock_bulk, *_): """ Publishing an organization should update its document in the Elasticsearch organizations index, and the documents for published courses to which it is related, excluding snapshots. """ organization = OrganizationFactory() published_course, _unpublished_course = CourseFactory.create_batch( 2, fill_organizations=[organization]) published_course.extended_object.publish("en") published_course.refresh_from_db() self.run_commit_hooks() mock_bulk.reset_mock() organization.extended_object.publish("en") organization.refresh_from_db() # Elasticsearch should not be called before the db transaction is successful self.assertFalse(mock_bulk.called) self.run_commit_hooks() self.assertEqual(mock_bulk.call_count, 1) self.assertEqual(len(mock_bulk.call_args[1]["actions"]), 2) actions = list(mock_bulk.call_args[1]["actions"]) self.assertEqual( actions[0]["_id"], str(published_course.public_extension.extended_object_id)) self.assertEqual(actions[0]["_type"], "course") self.assertEqual(actions[1]["_id"], "L-0001") self.assertEqual(actions[1]["_type"], "organization")
def test_signals_organizations_publish(self, mock_bulk, *_): """ Publishing an organization should update its document in the Elasticsearch organizations index, and the documents for published courses to which it is related, excluding snapshots. """ parent = OrganizationFactory(should_publish=True) organization = OrganizationFactory(page_parent=parent.extended_object) published_course, _unpublished_course = CourseFactory.create_batch( 2, fill_organizations=[organization]) self.assertTrue(published_course.extended_object.publish("en")) published_course.refresh_from_db() self.run_commit_hooks() mock_bulk.reset_mock() self.assertTrue(organization.extended_object.publish("en")) organization.refresh_from_db() # Elasticsearch should not be called before the db transaction is successful self.assertFalse(mock_bulk.called) self.run_commit_hooks() self.assertEqual(mock_bulk.call_count, 1) self.assertEqual(len(mock_bulk.call_args[1]["actions"]), 3) actions = list(mock_bulk.call_args[1]["actions"]) self.assertEqual(actions[0]["_id"], published_course.get_es_id()) self.assertEqual(actions[0]["_op_type"], "index") self.assertEqual(actions[0]["_index"], "test_courses") self.assertEqual(actions[1]["_id"], organization.get_es_id()) self.assertEqual(actions[1]["_op_type"], "index") self.assertEqual(actions[1]["_index"], "richie_organizations") self.assertEqual(actions[2]["_id"], parent.get_es_id()) self.assertEqual(actions[2]["_op_type"], "index") self.assertEqual(actions[2]["_index"], "richie_organizations")
def test_signals_organizations_unpublish(self, mock_bulk, *_): """ Unpublishing an organization in a language should update its document in the Elasticsearch organizations index or delete it if there is no language published anymore. It should also reindex the documents for published courses to which it is related, excluding snapshots. """ organization = OrganizationFactory(page_languages=["en", "fr"], should_publish=True) published_course, _unpublished_course = CourseFactory.create_batch( 2, fill_organizations=[organization]) self.assertTrue(published_course.extended_object.publish("en")) published_course.refresh_from_db() self.run_commit_hooks() mock_bulk.reset_mock() # - Unpublish the first language self.assertTrue(organization.extended_object.unpublish("en")) organization.refresh_from_db() # Elasticsearch should not be called before the db transaction is successful self.assertFalse(mock_bulk.called) self.run_commit_hooks() self.assertEqual(mock_bulk.call_count, 1) self.assertEqual(len(mock_bulk.call_args[1]["actions"]), 2) actions = list(mock_bulk.call_args[1]["actions"]) self.assertEqual(actions[0]["_id"], published_course.get_es_id()) self.assertEqual(actions[0]["_op_type"], "index") self.assertEqual(actions[0]["_index"], "test_courses") self.assertEqual(actions[1]["_id"], organization.get_es_id()) self.assertEqual(actions[1]["_op_type"], "index") self.assertEqual(actions[1]["_index"], "richie_organizations") mock_bulk.reset_mock() # - Unpublish the second language self.assertTrue(organization.extended_object.unpublish("fr")) organization.refresh_from_db() # Elasticsearch should not be called before the db transaction is successful self.assertFalse(mock_bulk.called) self.run_commit_hooks() self.assertEqual(mock_bulk.call_count, 1) self.assertEqual(len(mock_bulk.call_args[1]["actions"]), 2) actions = list(mock_bulk.call_args[1]["actions"]) self.assertEqual(actions[0]["_id"], published_course.get_es_id()) self.assertEqual(actions[0]["_op_type"], "index") self.assertEqual(actions[0]["_index"], "test_courses") self.assertEqual(actions[1]["_id"], organization.get_es_id()) self.assertEqual(actions[1]["_op_type"], "delete") self.assertEqual(actions[1]["_index"], "richie_organizations")
def test_course_change_view_get(self): """ The admin change view should include the editable and readonly fields as expected. In particular, the relation fields should only include options for related objects in their draft version. """ user = UserFactory(is_staff=True, is_superuser=True) self.client.login(username=user.username, password="******") # Create a course course = CourseFactory() # Create an organization and publish it organization = OrganizationFactory() organization.extended_object.publish("en") organization.refresh_from_db() # Create a subject and publish it subject = SubjectFactory() subject.extended_object.publish("en") subject.refresh_from_db() # Get the admin change view url = reverse("admin:courses_course_change", args=[course.id]) response = self.client.get(url) # Check that the page includes all our fields self.assertContains( response, course.extended_object.get_title(), status_code=200 ) self.assertContains(response, course.active_session) self.assertContains( response, course.organization_main.extended_object.get_title() ) # Only the draft organization and subject should be proposed as options in select boxes self.assertContains( response, '<option value="{:d}">{!s}'.format(subject.id, subject) ) self.assertNotContains( response, '<option value="{:d}">{!s}'.format( subject.public_extension.id, subject.public_extension ), ) self.assertContains( response, '<option value="{:d}">{!s}'.format(organization.id, organization) ) self.assertNotContains( response, '<option value="{:d}">{!s}'.format( organization.public_extension.id, organization.public_extension ), )
def test_admin_organization_change_view_post(self): """ Validate that the organization can be updated via the admin. """ user = UserFactory(is_staff=True, is_superuser=True) self.client.login(username=user.username, password="******") # Create an organization organization = OrganizationFactory() # Get the admin change view url = reverse("admin:courses_organization_change", args=[organization.id]) data = {"code": " r5G yç👷å¦pm 44 "} response = self.client.post(url, data) self.assertEqual(response.status_code, 302) # Check that the organization was updated as expected organization.refresh_from_db() self.assertEqual(organization.code, "R5G-YÇå¦PM-44")
def test_cms_plugins_organization_render_on_public_page(self): """ The organization plugin should render as expected on a public page. """ # Create a filer fake image image = FilerImageFactory() # Create an organization organization = OrganizationFactory(page_title={ "en": "public title", "fr": "titre publique" }) organization_page = organization.extended_object # Add logo to related placeholder logo_placeholder = organization_page.placeholders.get(slot="logo") add_plugin( logo_placeholder, PicturePlugin, "en", **{ "picture": image, "attributes": { "alt": "logo description" } }) add_plugin( logo_placeholder, PicturePlugin, "fr", **{ "picture": image, "attributes": { "alt": "description du logo" } }) # Create a page to add the plugin to page = create_i18n_page({"en": "A page", "fr": "Une page"}) placeholder = page.placeholders.get(slot="maincontent") add_plugin(placeholder, OrganizationPlugin, "en", **{"page": organization_page}) add_plugin(placeholder, OrganizationPlugin, "fr", **{"page": organization_page}) organization_page.publish("en") organization_page.publish("fr") organization.refresh_from_db() page.publish("en") page.publish("fr") # Check the page content in English url = page.get_absolute_url(language="en") # The organization plugin should not be visible on the public page before it is published organization_page.unpublish("en") response = self.client.get(url) self.assertNotContains(response, "public title") # # Republish the plugin organization_page.publish("en") # Now modify the organization to have a draft different from the public version title_obj = organization_page.get_title_obj(language="en") title_obj.tile = "draft title" title_obj.save() # Publishing the page again should make the plugin public page.publish("en") # Check the page content in English response = self.client.get(url) # The organization'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 class="organization-plugin__body" href="/en/public-title/" title="{:s}"' .format(organization.public_extension.extended_object.get_title()), status_code=200, ) # The organization's title should be wrapped in a div self.assertContains( response, '<div class="organization-plugin__title">{:s}</div>'.format( organization.public_extension.extended_object.get_title()), html=True, ) self.assertNotContains(response, "draft title") # Organziation's logo should be present # pylint: disable=no-member self.assertContains(response, image.file.name) # Same checks in French url = page.get_absolute_url(language="fr") response = self.client.get(url) self.assertContains( response, '<a class="organization-plugin__body" href="/fr/titre-publique/" title="{:s}"' .format(organization.public_extension.extended_object.get_title()), status_code=200, ) # pylint: disable=no-member self.assertContains(response, image.file.name)
def test_cms_plugins_organization_render_on_public_page(self): """ The organization plugin should render as expected on a public page. """ # Create an organization organization = OrganizationFactory( page_title={ "en": "public title", "fr": "titre public" }, fill_logo={ "original_filename": "logo.jpg", "default_alt_text": "my logo" }, ) organization_page = organization.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, OrganizationPlugin, "en", **{"page": organization_page}) add_plugin(placeholder, OrganizationPlugin, "fr", **{"page": organization_page}) organization_page.publish("en") organization_page.publish("fr") organization.refresh_from_db() page.publish("en") page.publish("fr") # Check the page content in English url = page.get_absolute_url(language="en") # The organization plugin should not be visible on the public page before it is published organization_page.unpublish("en") response = self.client.get(url) self.assertNotContains(response, "public title") # # Republish the plugin organization_page.publish("en") # Now modify the organization to have a draft different from the public version title_obj = organization_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 organization'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.assertIn( '<a class=" organization-glimpse organization-glimpse--link " ' 'href="/en/public-title/"', re.sub(" +", " ", str(response.content).replace("\\n", "")), ) # The organization's title should be wrapped in a div self.assertContains( response, '<div class="organization-glimpse__title">{:s}</div>'.format( organization.public_extension.extended_object.get_title()), html=True, ) self.assertNotContains(response, "draft title") # Organziation's logo should be present pattern = ( r'<div class="organization-glimpse__logo">' r'<img src="/media/filer_public_thumbnails/filer_public/.*logo\.jpg__200x113' 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.assertIn( '<a class=" organization-glimpse organization-glimpse--link " ' 'href="/fr/titre-public/"', re.sub(" +", " ", str(response.content).replace("\\n", "")), ) pattern = ( r'<div class="organization-glimpse__logo">' r'<img src="/media/filer_public_thumbnails/filer_public/.*logo\.jpg__200x113' r'.*alt=""') self.assertIsNotNone(re.search(pattern, str(response.content)))