Source code for nested_admin.tests.admin_widgets.tests

from contextlib import contextmanager
import time
from unittest import SkipTest

import django
from django.conf import settings
from django.utils.text import slugify

try:
    from html import unescape
except ImportError:
    from django.utils.text import unescape_entities as unescape

from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as ec
from selenium.webdriver.support.ui import Select
import six

from nested_admin.tests.base import (
    expected_failure_if_suit, skip_if_not_grappelli, BaseNestedAdminTestCase)
from .models import (
    TestAdminWidgetsRoot, TestAdminWidgetsA, TestAdminWidgetsB,
    TestAdminWidgetsC0, TestAdminWidgetsC1,
    TestWidgetMediaOrderRoot, TestWidgetMediaOrderA, TestWidgetMediaOrderB,
    TestWidgetMediaOrderC0, TestWidgetMediaOrderC1)
from .admin import (
    TestAdminWidgetsAInline, TestAdminWidgetsBInline, TestAdminWidgetsM2M,
    TestAdminWidgetsC0Inline, TestAdminWidgetsC1Inline,
    TestWidgetMediaOrderAInline, TestWidgetMediaOrderBInline,
    TestWidgetMediaOrderC0Inline, TestWidgetMediaOrderC1Inline)


[docs]class BaseWidgetTestCase(BaseNestedAdminTestCase): admin_classes = [] root_model = None nested_models = [] fixtures = ['admin-widgets.xml']
[docs] @classmethod def setUpClass(cls): super(BaseWidgetTestCase, cls).setUpClass() if cls.nested_models: cls.a_model, cls.b_model, (cls.c0_model, cls.c1_model) = cls.nested_models
[docs] @contextmanager def enable_inline_collapsing(self): """A context manager that configures the inline classes to be collapsible.""" if 'grappelli' in settings.INSTALLED_APPS: class_attr = "inline_classes" class_val = ("grp-collapse", "grp-closed") reset_val = ("grp-collapse", "grp-open") else: class_attr = "classes" class_val = ("collapse", ) reset_val = None for admin in self.admin_classes: setattr(admin, class_attr, class_val) try: yield finally: for admin in self.admin_classes: setattr(admin, class_attr, reset_val)
[docs] def get_name_for_indexes(self, indexes): name = "Item %s" % (" ABC"[len(indexes)]) if name == 'Item C': name += "%d%d" % (indexes[-1][0], indexes[-1][1]) else: name += "%d" % indexes[-1] name += " (%s)" % " > ".join(["%s" % i[1] for i in self._normalize_indexes(indexes)]) return name
[docs] def check_prepopulated(self, indexes): name = self.get_name_for_indexes(indexes) expected_slug = slugify(six.text_type(name)) slug_sel = self.get_form_field_selector('slug', indexes) self.set_field('name', name, indexes) time.sleep(0.2) slug_val = self.selenium.execute_script( 'return $("%s").val()' % slug_sel) self.assertEqual(slug_val, expected_slug, "prepopulated slug field did not sync")
[docs] def check_datetime(self, indexes): date_el = self.get_field('date_0', indexes) time_el = self.get_field('date_1', indexes) if self.has_grappelli: now_link_xpath = "following-sibling::*[1]" else: now_link_xpath = "following-sibling::*[1]/a[1]" date_el.clear() time_el.clear() self.click(date_el.find_element_by_xpath(now_link_xpath)) if self.has_grappelli: selector = '#ui-datepicker-div .ui-state-highlight' with self.clickable_selector(selector, timeout=1) as el: self.selenium.execute_script('arguments[0].scrollIntoView()', date_el) self.click(el) self.wait_until_element_is('#ui-datepicker-div', ':not(:visible)', 'Datepicker widget did not close') time.sleep(0.1) self.click(time_el.find_element_by_xpath(now_link_xpath)) if self.has_grappelli: selector = '#ui-timepicker .ui-state-active' with self.clickable_selector(selector, timeout=1) as el: self.selenium.execute_script('arguments[0].scrollIntoView()', time_el) self.click(el) self.wait_until_element_is('#ui-timepicker', ':not(:visible)', 'Timepicker widget did not close') time.sleep(0.10) self.assertNotEqual(date_el.get_attribute('value'), '', 'Date was not set') self.assertNotEqual(time_el.get_attribute('value'), '', 'Time was not set')
[docs] def check_m2m(self, indexes): add_all_link = self.get_field('m2m_add_all_link', indexes) remove_all_link = self.get_field('m2m_remove_all_link', indexes) self.click(remove_all_link) self.click(add_all_link) m2m_to_sel = self.get_form_field_selector('m2m_to', indexes) time.sleep(0.2) selected = self.selenium.execute_script(( 'return $("%s").find("option").toArray().map(' ' function(el) { return parseInt(el.value, 10); })') % m2m_to_sel) self.assertEqual(selected, [1, 2, 3])
[docs] def check_fk(self, indexes): field = self.get_field('fk1', indexes) parent = field.get_property('parentNode').get_property('parentNode') add_related = parent.find_element_by_css_selector('.add-related') if self.has_grappelli: # Grappelli can be very slow to initialize fk bindings, particularly # when run on travis-ci time.sleep(1) self.click(add_related) name = self.get_name_for_indexes(indexes) with self.switch_to_popup_window(): self.set_field('name', name) self.save_form() time.sleep(0.1) field_id = field.get_attribute('id') current_val = self.selenium.execute_script( 'return $("#%s").find("option:selected").html()' % field_id) self.assertEqual(unescape(current_val), name)
[docs]class TestAdminWidgets(BaseWidgetTestCase): admin_classes = [ TestAdminWidgetsAInline, TestAdminWidgetsBInline, TestAdminWidgetsC0Inline, TestAdminWidgetsC1Inline, ] root_model = TestAdminWidgetsRoot nested_models = (TestAdminWidgetsA, TestAdminWidgetsB, (TestAdminWidgetsC0, TestAdminWidgetsC1))
[docs] def test_collapsible_inlines(self): if not self.has_grappelli and django.VERSION < (1, 10): raise SkipTest("Collapsible inlines not supported") with self.enable_inline_collapsing(): self.load_admin() name_field = self.get_field('name', [0]) self.assertFalse(name_field.is_displayed(), "Inline did not load collapsed") if self.has_grappelli: collapse_handler = self.selenium.execute_script( 'return $(arguments[0]).find("> .djn-collapse-handler")[0]', self.get_item([0])) else: collapse_handler = self.selenium.execute_script( 'return $(arguments[0]).find("> fieldset > h2 > .collapse-toggle")[0]', self.get_group()) self.click(collapse_handler) self.assertTrue(name_field.is_displayed(), "Inline did not expand") self.click(collapse_handler) self.assertFalse(name_field.is_displayed(), "Inline did not collapse")
[docs] def test_initial_extra_prepopulated(self): self.load_admin() self.check_prepopulated([0]) self.check_prepopulated([0, 0])
[docs] def test_initial_extra_m2m(self): self.load_admin() self.check_m2m([0]) self.check_m2m([0, 0])
[docs] def test_initial_extra_fk_one_deep(self): self.load_admin() self.check_fk([0])
[docs] def test_initial_extra_fk_two_deep(self): self.load_admin() if self.has_grappelli: time.sleep(0.3) self.check_fk([0, 0])
[docs] def test_initial_extra_datetime(self): self.load_admin() self.check_datetime([0]) self.check_datetime([0, 0])
[docs] @expected_failure_if_suit # Known bug with prepopulated fields and django-suit def test_add_prepopulated(self): self.load_admin() self.add_inline() self.check_prepopulated([1])
[docs] @expected_failure_if_suit # Known bug with prepopulated fields and django-suit def test_add_initial_extra_prepopulated(self): self.load_admin() self.add_inline() self.check_prepopulated([1, 0])
[docs] def test_add_m2m(self): self.load_admin() self.add_inline() self.check_m2m([1])
[docs] @expected_failure_if_suit # Known bug with this test, django-suit, and phantomjs def test_add_fk(self): self.load_admin() if self.has_grappelli: time.sleep(0.3) self.add_inline() if self.has_grappelli: time.sleep(0.3) time.sleep(0.1) self.check_fk([1])
[docs] def test_add_initial_extra_m2m(self): self.load_admin() time.sleep(0.2) self.add_inline() time.sleep(0.2) self.check_m2m([1, 0])
[docs] def test_add_initial_extra_fk(self): self.load_admin() if self.has_grappelli: time.sleep(0.3) self.add_inline() if self.has_grappelli: time.sleep(0.3) self.check_fk([1, 0])
[docs] def test_add_datetime(self): self.load_admin() self.add_inline() self.check_datetime([1])
[docs] def test_add_initial_extra_datetime(self): self.load_admin() self.add_inline() self.check_datetime([1, 0])
[docs] def test_add_two_deep_m2m(self): self.load_admin() self.add_inline() self.add_inline([1]) self.check_m2m([1, 1])
[docs] def test_add_two_deep_fk(self): self.load_admin() self.add_inline() self.add_inline([1]) self.check_fk([1, 1])
[docs] @expected_failure_if_suit # Known bug with prepopulated fields and django-suit def test_add_two_deep_prepopulated(self): self.load_admin() self.add_inline() self.add_inline([1]) self.check_prepopulated([1, 1])
[docs] def test_add_two_deep_datetime(self): self.load_admin() self.add_inline() self.add_inline([1]) self.check_datetime([1, 1])
[docs] def test_add_three_deep_m2m(self): self.load_admin() self.add_inline() self.add_inline([1]) self.add_inline([1, 0, [1]]) self.check_m2m([1, 0, [1, 0]])
[docs] def test_add_three_deep_fk(self): self.load_admin() self.add_inline() self.add_inline([1]) self.add_inline([1, 0, [1]]) self.check_fk([1, 0, [1, 0]])
[docs] @expected_failure_if_suit # Known bug with prepopulated fields and django-suit def test_add_three_deep_prepopulated(self): self.load_admin() self.add_inline() self.add_inline([1]) self.add_inline([1, 0, [1]]) self.check_prepopulated([1, 0, [1, 0]])
[docs] def test_add_three_deep_datetime(self): self.load_admin() self.add_inline() self.add_inline([1]) self.add_inline([1, 0, [1]]) self.check_datetime([1, 0, [1, 0]])
[docs] @skip_if_not_grappelli def test_autocomplete_single_init(self): self.load_admin() self.add_inline() self.add_inline([1]) autocomplete_elements = self.selenium.find_elements_by_xpath( '//*[@id="id_testadminwidgetsa_set-1-testadminwidgetsb_set-0-fk2-autocomplete"]') self.assertNotEqual(len(autocomplete_elements), 0, "Zero autocomplete fields initialized") self.assertEqual(len(autocomplete_elements), 1, "Too many autocomplete fields initialized")
[docs] def test_nested_autocomplete_extra(self): if self.has_grappelli: raise SkipTest("Not testing autocomplete on grappelli") if django.VERSION < (2, 0): raise SkipTest("autocomplete_fields not available in Django before 2.0") self.load_admin() self.add_inline([0, [0]]) self.add_inline([0, 1, [0]]) select_field = self.get_field('fk3', indexes=[0, 1, [0, 0]]) select_parent = select_field.find_element_by_xpath('parent::*') select_parent.click() select2_is_active = self.selenium.execute_script( 'return $(".select2-search__field").length > 0') self.assertTrue(select2_is_active) select2_input = self.selenium.execute_script('return $(".select2-search__field")[0]') self.assertIsNotNone(select2_input) select2_input.send_keys('l') select2_input.send_keys(Keys.ENTER) self.assertEqual(select_field.get_attribute('value'), '2')
[docs]class TestWidgetMediaOrder(BaseWidgetTestCase): admin_classes = [ TestWidgetMediaOrderAInline, TestWidgetMediaOrderBInline, TestWidgetMediaOrderC0Inline, TestWidgetMediaOrderC1Inline, ] root_model = TestWidgetMediaOrderRoot nested_models = (TestWidgetMediaOrderA, TestWidgetMediaOrderB, (TestWidgetMediaOrderC0, TestWidgetMediaOrderC1))
[docs] def test_add_three_deep_m2m(self): self.load_admin() self.add_inline() self.add_inline([1]) self.add_inline([1, 0, [1]]) self.check_m2m([1, 0, [1, 0]])
[docs] def test_add_three_deep_fk(self): self.load_admin() self.add_inline() self.add_inline([1]) self.add_inline([1, 0, [1]]) self.check_fk([1, 0, [1, 0]])
[docs] @expected_failure_if_suit # Known bug with prepopulated fields and django-suit def test_add_three_deep_prepopulated(self): self.load_admin() self.add_inline() self.add_inline([1]) self.add_inline([1, 0, [1]]) self.check_prepopulated([1, 0, [1, 0]])
[docs] def test_add_three_deep_datetime(self): self.load_admin() self.add_inline() self.add_inline([1]) self.add_inline([1, 0, [1]]) self.check_datetime([1, 0, [1, 0]])