11import os
2+ from time import sleep
23import pytest
34from django .urls import reverse
45from django .contrib .auth import get_user_model
@@ -203,22 +204,19 @@ def setUpClass(cls):
203204 cls .playwright = sync_playwright ().start ()
204205 cls .browser = cls .playwright .chromium .launch (headless = cls .HEADLESS )
205206
206- cls .admin = get_user_model ().objects .create_superuser (
207- username = cls .admin_username , email = "admin@example.com" , password = cls .admin_password
208- )
209-
210207 @classmethod
211208 def tearDownClass (cls ):
212209 """Clean up Playwright instance after tests."""
213210 cls .browser .close ()
214211 cls .playwright .stop ()
215- if cls .admin :
216- cls .admin .delete ()
217212 super ().tearDownClass ()
218213 del os .environ ["DJANGO_ALLOW_ASYNC_UNSAFE" ]
219214
220215 def setUp (self ):
221216 """Create an admin user before running tests."""
217+ self .admin = get_user_model ().objects .create_superuser (
218+ username = self .admin_username , email = "admin@example.com" , password = self .admin_password
219+ )
222220 self .page = self .browser .new_page ()
223221 # Log in to the Django admin
224222 self .page .goto (f"{ self .live_server_url } /admin/login/" )
@@ -235,13 +233,10 @@ def tearDown(self):
235233
236234
237235class StackedInlineTests (_GenericAdminFormTest ):
238- def setUp (self ):
239- super ().setUp ()
240- for name in ["Brian" , "Alice" , "Emma" , "Anna" ]:
241- PlainA .objects .create (field1 = name )
242-
243236 def test_admin_inline_add_autocomplete (self ):
244237 # https://github.com/jazzband/django-polymorphic/issues/546
238+ for name in ["Brian" , "Alice" , "Emma" , "Anna" ]:
239+ PlainA .objects .create (field1 = name )
245240 self .page .goto (self .add_url (InlineParent ))
246241 self .page .fill ("input[name='title']" , "Parent 1" )
247242 with self .page .expect_navigation (timeout = 10000 ) as nav_info :
@@ -283,6 +278,71 @@ def test_admin_inline_add_autocomplete(self):
283278 suggestions = self .page .locator ("ul.select2-results__options > li" ).all_inner_texts ()
284279 assert suggestions == ["Brian" ]
285280
281+ def test_inline_form_ordering_and_removal (self ):
282+ """
283+ Test that the javascript places the inline forms in the correct order on
284+ repeated adds without a save.
285+
286+ https://github.com/jazzband/django-polymorphic/issues/426
287+ """
288+ self .page .goto (self .add_url (InlineParent ))
289+
290+ polymorphic_menu = self .page .locator (
291+ "div.polymorphic-add-choice div.polymorphic-type-menu"
292+ )
293+
294+ self .page .click ("div.polymorphic-add-choice a" )
295+ polymorphic_menu .wait_for (state = "visible" )
296+ self .page .click ("div.polymorphic-type-menu a[data-type='inlinemodelb']" )
297+ polymorphic_menu .wait_for (state = "hidden" )
298+ self .page .click ("div.polymorphic-add-choice a" )
299+ polymorphic_menu .wait_for (state = "visible" )
300+ self .page .click ("div.polymorphic-type-menu a[data-type='inlinemodela']" )
301+ polymorphic_menu .wait_for (state = "hidden" )
302+ self .page .click ("div.polymorphic-add-choice a" )
303+ polymorphic_menu .wait_for (state = "visible" )
304+ self .page .click ("div.polymorphic-type-menu a[data-type='inlinemodela']" )
305+ polymorphic_menu .wait_for (state = "hidden" )
306+ self .page .click ("div.polymorphic-add-choice a" )
307+ polymorphic_menu .wait_for (state = "visible" )
308+ self .page .click ("div.polymorphic-type-menu a[data-type='inlinemodelb']" )
309+ polymorphic_menu .wait_for (state = "hidden" )
310+
311+ inline0 = self .page .locator ("div#inline_children-0" )
312+ inline1 = self .page .locator ("div#inline_children-1" )
313+ inline2 = self .page .locator ("div#inline_children-2" )
314+ inline3 = self .page .locator ("div#inline_children-3" )
315+
316+ inline0 .wait_for (state = "visible" )
317+ inline1 .wait_for (state = "visible" )
318+ inline2 .wait_for (state = "visible" )
319+ inline3 .wait_for (state = "visible" )
320+
321+ assert "model b" in inline0 .inner_text () and "#1" in inline0 .inner_text ()
322+ assert "model a" in inline1 .inner_text () and "#2" in inline1 .inner_text ()
323+ assert "model a" in inline2 .inner_text () and "#3" in inline2 .inner_text ()
324+ assert "model b" in inline3 .inner_text () and "#4" in inline3 .inner_text ()
325+
326+ # Now remove inline 2 and check the numbering is correct
327+ inline1 .locator ("a.inline-deletelink" ).click ()
328+ # the ids are updated - so we expect the last div id to be removed
329+ inline3 .wait_for (state = "detached" )
330+ assert "model b" in inline0 .inner_text () and "#1" in inline0 .inner_text ()
331+ assert "model a" in inline1 .inner_text () and "#2" in inline1 .inner_text ()
332+ assert "model b" in inline2 .inner_text () and "#3" in inline2 .inner_text ()
333+
334+ inline0 .locator ("a.inline-deletelink" ).click ()
335+ inline2 .wait_for (state = "detached" )
336+ assert "model a" in inline0 .inner_text () and "#1" in inline0 .inner_text ()
337+ assert "model b" in inline1 .inner_text () and "#2" in inline1 .inner_text ()
338+
339+ inline1 .locator ("a.inline-deletelink" ).click ()
340+ inline1 .wait_for (state = "detached" )
341+ assert "model a" in inline0 .inner_text () and "#1" in inline0 .inner_text ()
342+
343+ inline0 .locator ("a.inline-deletelink" ).click ()
344+ inline0 .wait_for (state = "detached" )
345+
286346
287347class PolymorphicFormTests (_GenericAdminFormTest ):
288348 def test_admin_polymorphic_add (self ):
0 commit comments