Skip to content

Commit 623c5ba

Browse files
authored
chore: CI updates, fix deprecation issues (theatlantic#252)
* fix(ci): bump version of nanasess/setup-chromedriver to v2.2.2 * fix(ci): use latest selenium and update deprecated methods * fix(ci): increase speed of move_to_element actions * fix(tests): finesse element offset targets for drag-and-drop operations * fix: flake8 and black failures * chore(ci): update tox envlist and gha matrix * add setuptools to tox until jazzband/django-polymorphic#599 is released
1 parent 0b2bc4f commit 623c5ba

File tree

11 files changed

+130
-72
lines changed

11 files changed

+130
-72
lines changed

.github/workflows/test.yml

+19-17
Original file line numberDiff line numberDiff line change
@@ -7,27 +7,28 @@ jobs:
77
strategy:
88
fail-fast: false
99
matrix:
10-
grappelli: ["0"]
11-
python-version: ["3.8"]
12-
django-version: ["3.2"]
10+
grappelli: ["0", "1"]
11+
python-version: ["3.11"]
12+
django-version: ["4.2"]
13+
exclude:
14+
- python-version: "3.11"
15+
grappelli: "1"
1316
include:
14-
- grappelli: "0"
15-
name-suffix: ""
1617
- python-version: "3.9"
17-
django-version: "4.0"
18+
django-version: "4.2"
19+
grappelli: "1"
20+
- python-version: "3.9"
21+
django-version: "5.0"
22+
grappelli: "0"
1823
- python-version: "3.10"
19-
django-version: "4.1"
20-
- grappelli: "1"
21-
name-suffix: " + grappelli"
22-
python-version: "3.7"
23-
django-version: "3.2"
24-
- grappelli: "1"
25-
name-suffix: " + grappelli"
26-
python-version: "3.8"
27-
django-version: "4.0"
24+
django-version: "5.0"
25+
grappelli: "1"
26+
- python-version: "3.12"
27+
django-version: "5.1"
28+
grappelli: "0"
2829

2930
runs-on: ubuntu-latest
30-
name: Django ${{ matrix.django-version }} (Python ${{ matrix.python-version }})${{ matrix.name-suffix }}
31+
name: Django ${{ matrix.django-version }} (Python ${{ matrix.python-version }})${{ matrix.grappelli == '1' && ' + grappelli' || '' }}
3132

3233
env:
3334
DJANGO: ${{ matrix.django-version }}
@@ -45,7 +46,8 @@ jobs:
4546
python-version: ${{ matrix.python-version }}
4647

4748
- name: Setup chromedriver
48-
uses: nanasess/[email protected]
49+
# uses: nanasess/[email protected]
50+
uses: nanasess/setup-chromedriver@42cc2998329f041de87dc3cfa33a930eacd57eaa
4951

5052
- name: Install tox
5153
run: |

nested_admin/nested.py

+8-4
Original file line numberDiff line numberDiff line change
@@ -193,9 +193,11 @@ def inline_formset_data(self):
193193
"lookupAutocomplete": getattr(
194194
self.opts, "autocomplete_lookup_fields", {}
195195
),
196-
"formsetFkName": self.formset.fk.name
197-
if getattr(self.formset, "fk", None)
198-
else "",
196+
"formsetFkName": (
197+
self.formset.fk.name
198+
if getattr(self.formset, "fk", None)
199+
else ""
200+
),
199201
"formsetFkModel": formset_fk_model,
200202
"nestingLevel": getattr(self.formset, "nesting_depth", 0),
201203
"fieldNames": {
@@ -538,13 +540,15 @@ def __init__(self, *args, **kwargs):
538540
_get_formsets = ModelAdmin._get_formsets
539541

540542
def get_formset(self, request, obj=None, **kwargs):
541-
FormSet = BaseFormSet = kwargs.pop("formset", self.formset)
543+
BaseFormSet = kwargs.pop("formset", self.formset)
542544

543545
if self.sortable_field_name:
544546

545547
class FormSet(BaseFormSet):
546548
sortable_field_name = self.sortable_field_name
547549

550+
else:
551+
FormSet = BaseFormSet
548552
kwargs["formset"] = FormSet
549553
return super().get_formset(request, obj, **kwargs)
550554

nested_admin/polymorphic.py

+7-2
Original file line numberDiff line numberDiff line change
@@ -148,13 +148,15 @@ class Child(
148148
formset = NestedBasePolymorphicInlineFormSet
149149

150150
def get_formset(self, request, obj=None, **kwargs):
151-
FormSet = BaseFormSet = kwargs.pop("formset", self.formset)
151+
BaseFormSet = kwargs.pop("formset", self.formset)
152152

153153
if self.sortable_field_name:
154154

155155
class FormSet(BaseFormSet):
156156
sortable_field_name = self.sortable_field_name
157157

158+
else:
159+
FormSet = BaseFormSet
158160
kwargs["formset"] = FormSet
159161
return super(PolymorphicInlineModelAdmin.Child, self).get_formset(
160162
request, obj, **kwargs
@@ -186,13 +188,16 @@ class Child(
186188
formset = NestedBaseGenericPolymorphicInlineFormSet
187189

188190
def get_formset(self, request, obj=None, **kwargs):
189-
FormSet = BaseFormSet = kwargs.pop("formset", self.formset)
191+
BaseFormSet = kwargs.pop("formset", self.formset)
190192

191193
if self.sortable_field_name:
192194

193195
class FormSet(BaseFormSet):
194196
sortable_field_name = self.sortable_field_name
195197

198+
else:
199+
FormSet = BaseFormSet
200+
196201
kwargs["formset"] = FormSet
197202
return super(GenericPolymorphicInlineModelAdmin.Child, self).get_formset(
198203
request, obj, **kwargs

nested_admin/tests/admin_widgets/tests.py

+8-8
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ def check_datetime(self, indexes):
107107
now_link_xpath = "following-sibling::*[1]/a[1]"
108108
date_el.clear()
109109
time_el.clear()
110-
self.click(date_el.find_element_by_xpath(now_link_xpath))
110+
self.click(date_el.find_element(By.XPATH, now_link_xpath))
111111
if self.has_grappelli:
112112
selector = "#ui-datepicker-div .ui-state-highlight"
113113
with self.clickable_selector(selector, timeout=1) as el:
@@ -119,7 +119,7 @@ def check_datetime(self, indexes):
119119
"Datepicker widget did not close",
120120
)
121121
time.sleep(0.2)
122-
self.click(time_el.find_element_by_xpath(now_link_xpath))
122+
self.click(time_el.find_element(By.XPATH, now_link_xpath))
123123
if self.has_grappelli:
124124
selector = "#ui-timepicker .ui-state-active"
125125
with self.clickable_selector(selector, timeout=1) as el:
@@ -153,7 +153,7 @@ def check_m2m(self, indexes):
153153
def check_fk(self, indexes):
154154
field = self.get_field("fk1", indexes)
155155
parent = field.get_property("parentNode").get_property("parentNode")
156-
add_related = parent.find_element_by_css_selector(".add-related")
156+
add_related = parent.find_element(By.CSS_SELECTOR, ".add-related")
157157
if self.has_grappelli:
158158
# Grappelli can be very slow to initialize fk bindings, particularly
159159
# when run on travis-ci
@@ -185,7 +185,7 @@ def check_gfk_related_lookup(self, indexes):
185185
% object_id_field_id,
186186
)
187187

188-
lookup_el = self.selenium.find_element_by_css_selector(related_lookup_selector)
188+
lookup_el = self.selenium.find_element(By.CSS_SELECTOR, related_lookup_selector)
189189
lookup_el.click()
190190
with self.switch_to_popup_window():
191191
with self.clickable_xpath('//tr//a[text()="Zither"]') as el:
@@ -195,7 +195,7 @@ def check_gfk_related_lookup(self, indexes):
195195
z_pk = "%s" % WidgetsM2M.objects.get(name="Zither").pk
196196

197197
def element_value_populated(d):
198-
el = d.find_element_by_css_selector("#%s" % object_id_field_id)
198+
el = d.find_element(By.CSS_SELECTOR, "#%s" % object_id_field_id)
199199
return el.get_attribute("value")
200200

201201
self.wait_until(
@@ -376,8 +376,8 @@ def test_autocomplete_single_init(self):
376376
self.load_admin()
377377
self.add_inline()
378378
self.add_inline([1])
379-
autocomplete_elements = self.selenium.find_elements_by_xpath(
380-
'//*[@id="id_widgetsa_set-1-widgetsb_set-0-fk2-autocomplete"]'
379+
autocomplete_elements = self.selenium.find_elements(
380+
By.XPATH, '//*[@id="id_widgetsa_set-1-widgetsb_set-0-fk2-autocomplete"]'
381381
)
382382
self.assertNotEqual(
383383
len(autocomplete_elements), 0, "Zero autocomplete fields initialized"
@@ -405,7 +405,7 @@ def test_nested_autocomplete_extra(self):
405405
self.add_inline([0, [0]])
406406
self.add_inline([0, 1, [0]])
407407
select_field = self.get_field("fk3", indexes=[0, 1, [0, 0]])
408-
select_parent = select_field.find_element_by_xpath("parent::*")
408+
select_parent = select_field.find_element(By.XPATH, "parent::*")
409409
select_parent.click()
410410
select2_is_active = self.selenium.execute_script(
411411
'return $(".select2-search__field").length > 0'

nested_admin/tests/base.py

+21-5
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
from django.contrib.admin.sites import site as admin_site
1313

1414
from selenosis import AdminSelenosisTestCase
15+
from selenium.webdriver.common.by import By
16+
from selenium.webdriver.common.actions.pointer_input import PointerInput
1517
from .drag_drop import DragAndDropAction
1618
from .utils import xpath_item, is_sequence, is_integer, is_str, ElementRect
1719

@@ -34,6 +36,20 @@ class BaseNestedAdminTestCase(AdminSelenosisTestCase):
3436
def setUpClass(cls):
3537
super().setUpClass()
3638

39+
# Increase speed of move_to_element action
40+
PointerInput.DEFAULT_MOVE_DURATION = 1
41+
42+
if not hasattr(PointerInput.create_pointer_move, "_patched"):
43+
orig_create_pointer_move = PointerInput.create_pointer_move
44+
45+
def create_pointer_move(self, *args, **kwargs):
46+
kwargs["duration"] = 1
47+
return orig_create_pointer_move(self, *args, **kwargs)
48+
49+
create_pointer_move._patched = True
50+
51+
PointerInput.create_pointer_move = create_pointer_move
52+
3753
root_admin = admin_site._registry[cls.root_model]
3854

3955
def descend_admin_inlines(admin):
@@ -180,7 +196,7 @@ def save_form(self):
180196
)
181197
)
182198
name_attr = "_continue" if has_continue else "_save"
183-
self.click(self.selenium.find_element_by_xpath('//*[@name="%s"]' % name_attr))
199+
self.click(self.selenium.find_element(By.XPATH, '//*[@name="%s"]' % name_attr))
184200
if has_continue:
185201
self.wait_page_loaded()
186202
self.initialize_page()
@@ -362,15 +378,15 @@ def get_group(self, indexes=None):
362378
]
363379
expr_parts += ["/*[@data-inline-model='%s']" % model_name]
364380
expr = "/%s" % ("/".join(expr_parts))
365-
return self.selenium.find_element_by_xpath(expr)
381+
return self.selenium.find_element(By.XPATH, expr)
366382

367383
def get_item(self, indexes):
368384
indexes = self._normalize_indexes(indexes)
369385
model_name, item_index = indexes.pop()
370386
indexes.append(model_name)
371387
group = self.get_group(indexes=indexes)
372-
return group.find_element_by_xpath(
373-
".//*[%s][%d]" % (xpath_item(model_name), item_index + 1)
388+
return group.find_element(
389+
By.XPATH, ".//*[%s][%d]" % (xpath_item(model_name), item_index + 1)
374390
)
375391

376392
def add_inline(self, indexes=None, name=None, slug=None):
@@ -442,7 +458,7 @@ def get_form_field_selector(self, attname, indexes=None):
442458
def get_field(self, attname, indexes=None):
443459
indexes = self._normalize_indexes(indexes)
444460
field_selector = self.get_form_field_selector(attname, indexes=indexes)
445-
return self.selenium.find_element_by_css_selector(field_selector)
461+
return self.selenium.find_element(By.CSS_SELECTOR, field_selector)
446462

447463
def set_field(self, attname, value, indexes=None):
448464
indexes = self._normalize_indexes(indexes)

nested_admin/tests/drag_drop.py

+19-14
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import time
33

44
from selenium.webdriver.common.action_chains import ActionChains
5+
from selenium.webdriver.common.by import By
56

67
from .utils import xpath_cls, xpath_item, is_integer, Position, Size, ElementRect
78

@@ -107,7 +108,7 @@ def source(self):
107108
"*[%s]" % xpath_cls("djn-drag-handler"),
108109
]
109110
)
110-
self._source = source_item.find_element_by_xpath(drag_handler_xpath)
111+
self._source = source_item.find_element(By.XPATH, drag_handler_xpath)
111112
return self._source
112113

113114
@property
@@ -127,7 +128,7 @@ def target(self):
127128
"item_pred": xpath_item(),
128129
"item_pos": self.to_indexes[-1][1] + 1,
129130
}
130-
self._target = target_inline_parent.find_element_by_xpath(target_xpath)
131+
self._target = target_inline_parent.find_element(By.XPATH, target_xpath)
131132
return self._target
132133

133134
def initialize_drag(self):
@@ -140,6 +141,10 @@ def initialize_drag(self):
140141
document.documentElement.scrollTop += (top - 16);
141142
} else {
142143
el.scrollIntoView();
144+
top = el.getBoundingClientRect().top;
145+
if (top <= 15) {
146+
document.documentElement.scrollTop += (top - 16);
147+
}
143148
}
144149
""",
145150
source,
@@ -148,15 +153,15 @@ def initialize_drag(self):
148153

149154
(
150155
ActionChains(self.selenium)
151-
.move_to_element_with_offset(source, 5, 5)
156+
.move_to_element_with_offset(source, 3, 3)
152157
.click_and_hold()
153158
.perform()
154159
)
155160

156161
time.sleep(0.05)
157-
ActionChains(self.selenium).move_by_offset(0, -15).perform()
162+
ActionChains(self.selenium).move_by_offset(0, -10).perform()
158163
time.sleep(0.05)
159-
ActionChains(self.selenium).move_by_offset(0, 15).perform()
164+
ActionChains(self.selenium).move_by_offset(0, 10).perform()
160165

161166
with self.test_case.visible_selector(".ui-sortable-helper") as el:
162167
return el
@@ -189,7 +194,7 @@ def _match_helper_with_target(self, helper_element, target_element):
189194
15, min(viewport_height // 3, (2 * inline_height) // 3, abs(dy) // 2)
190195
)
191196

192-
max_iter = 50
197+
max_iter = 120
193198
i = 0
194199
prev_pos_diff = None
195200
direction = None
@@ -220,9 +225,9 @@ def _match_helper_with_target(self, helper_element, target_element):
220225
if flip_count > 3:
221226
increment = 10
222227
elif flip_count > 5:
223-
increment = 5
228+
increment = 2
224229
else:
225-
increment = max(abs(dy // 2), flip_count * flip_multiplier)
230+
increment = min(abs(dy // 2), flip_count * flip_multiplier)
226231
direction_flip *= -1
227232
direction = pos_diff * direction_flip
228233
inc = increment * direction
@@ -252,7 +257,7 @@ def _num_preceding_siblings(self, ctx, condition):
252257
is extraordinarily slow. So we just grab all siblings and iterate
253258
through the elements in python.
254259
"""
255-
siblings = ctx.find_element_by_xpath("parent::*").find_elements_by_xpath("*")
260+
siblings = ctx.find_element(By.XPATH, "parent::*").find_elements(By.XPATH, "*")
256261
count = 0
257262
for el in siblings:
258263
if el.id == ctx.id:
@@ -278,8 +283,8 @@ def is_djn_group(el):
278283

279284
@property
280285
def current_position(self):
281-
placeholder = self.selenium.find_element_by_css_selector(
282-
".ui-sortable-placeholder"
286+
placeholder = self.selenium.find_element(
287+
By.CSS_SELECTOR, ".ui-sortable-placeholder"
283288
)
284289
pos = []
285290
ctx = None
@@ -288,10 +293,10 @@ def current_position(self):
288293
if ctx is None:
289294
ctx = placeholder
290295
else:
291-
ctx = ctx.find_element_by_xpath(ancestor_xpath)
296+
ctx = ctx.find_element(By.XPATH, ancestor_xpath)
292297
item_index = self._num_preceding_djn_items(ctx)
293-
ctx = ctx.find_element_by_xpath(
294-
"ancestor::*[%s][1]" % xpath_cls("djn-group")
298+
ctx = ctx.find_element(
299+
By.XPATH, "ancestor::*[%s][1]" % xpath_cls("djn-group")
295300
)
296301
inline_index = self._num_preceding_djn_groups(ctx)
297302
pos.insert(0, (inline_index, item_index))

0 commit comments

Comments
 (0)