|
30 | 30 | ) |
31 | 31 |
|
32 | 32 | from playwright.sync_api import sync_playwright, expect |
| 33 | +from urllib.parse import urljoin |
33 | 34 |
|
34 | 35 |
|
35 | 36 | class PolymorphicAdminTests(AdminTestCase): |
@@ -170,6 +171,9 @@ class _GenericAdminFormTest(StaticLiveServerTestCase): |
170 | 171 | admin_password = "password" |
171 | 172 | admin = None |
172 | 173 |
|
| 174 | + def admin_url(self): |
| 175 | + return f"{self.live_server_url}{reverse('admin:index')}" |
| 176 | + |
173 | 177 | def add_url(self, model): |
174 | 178 | path = reverse(f"admin:{model._meta.label_lower.replace('.', '_')}_add") |
175 | 179 | return f"{self.live_server_url}{path}" |
@@ -371,3 +375,88 @@ def test_admin_no_polymorphic_children(self): |
371 | 375 | added = NoChildren.objects.get(field1="NoChildren1") |
372 | 376 | self.page.goto(self.change_url(NoChildren, added.pk)) |
373 | 377 | assert self.page.locator("input[name='field1']").input_value() == "NoChildren1" |
| 378 | + |
| 379 | + |
| 380 | +class AdminRecentActionsTests(_GenericAdminFormTest): |
| 381 | + def test_admin_recent_actions(self): |
| 382 | + """ |
| 383 | + Test that recent actions links respect polymorphism |
| 384 | + """ |
| 385 | + model2a_ct = ContentType.objects.get_for_model(Model2A) |
| 386 | + model2d_ct = ContentType.objects.get_for_model(Model2D) |
| 387 | + |
| 388 | + for model_type, fields in [ |
| 389 | + ( |
| 390 | + model2a_ct, |
| 391 | + { |
| 392 | + "field1": "2A1", |
| 393 | + }, |
| 394 | + ), |
| 395 | + ( |
| 396 | + model2d_ct, |
| 397 | + { |
| 398 | + "field1": "2D1", |
| 399 | + "field2": "2D2", |
| 400 | + "field3": "2D3", |
| 401 | + "field4": "2D4", |
| 402 | + }, |
| 403 | + ), |
| 404 | + ]: |
| 405 | + self.page.goto(self.add_url(Model2A)) |
| 406 | + self.page.locator(f"input[type=radio][value='{model_type.pk}']").check() |
| 407 | + with self.page.expect_navigation(timeout=10000) as nav_info: |
| 408 | + self.page.click("input[name='_save']") |
| 409 | + |
| 410 | + response = nav_info.value |
| 411 | + assert response.status < 400 |
| 412 | + |
| 413 | + for field, value in fields.items(): |
| 414 | + self.page.fill(f"input[name='{field}']", value) |
| 415 | + |
| 416 | + with self.page.expect_navigation(timeout=10000) as nav_info: |
| 417 | + self.page.click("input[name='_save']") |
| 418 | + |
| 419 | + response = nav_info.value |
| 420 | + assert response.status < 400 |
| 421 | + |
| 422 | + self.page.goto(self.admin_url()) |
| 423 | + links = self.page.locator("ul.actionlist a") |
| 424 | + count = links.count() |
| 425 | + |
| 426 | + # Collect hrefs |
| 427 | + hrefs = [] |
| 428 | + for i in range(count): |
| 429 | + href = links.nth(i).get_attribute("href") |
| 430 | + if href: # ignore missing hrefs just in case |
| 431 | + hrefs.append(href) |
| 432 | + |
| 433 | + assert hrefs, "No links found in .actionlist" |
| 434 | + |
| 435 | + # Visit each link and ensure the HTTP status is OK |
| 436 | + for href in hrefs: |
| 437 | + action_url = urljoin(self.live_server_url, href) |
| 438 | + response = self.page.goto(action_url) |
| 439 | + assert response is not None, f"No response for {action_url}" |
| 440 | + assert response.ok, f"{action_url} returned bad status {response.status}" |
| 441 | + if "model2a" in action_url: |
| 442 | + inputs = self.page.locator("#model2a_form input[type='text']") |
| 443 | + count = inputs.count() |
| 444 | + assert count == 1 |
| 445 | + |
| 446 | + values = [] |
| 447 | + for i in range(count): |
| 448 | + values.append(inputs.nth(i).input_value()) |
| 449 | + |
| 450 | + assert values == ["2A1"] |
| 451 | + elif "model2d" in action_url: |
| 452 | + inputs = self.page.locator("#model2d_form input[type='text']") |
| 453 | + count = inputs.count() |
| 454 | + assert count == 4 |
| 455 | + |
| 456 | + values = [] |
| 457 | + for i in range(count): |
| 458 | + values.append(inputs.nth(i).input_value()) |
| 459 | + |
| 460 | + assert values == ["2D1", "2D2", "2D3", "2D4"] |
| 461 | + else: |
| 462 | + assert False, f"Unexpected change url: {action_url}" |
0 commit comments