Skip to content

Commit 8d1f67b

Browse files
committed
Playwright will not be available in all environments
1 parent 9a7284f commit 8d1f67b

File tree

1 file changed

+139
-122
lines changed

1 file changed

+139
-122
lines changed

tests/testapp/tests/test_template_change_e2e.py

Lines changed: 139 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -3,139 +3,156 @@
33
44
This test verifies that the JavaScript fix actually works in a real browser environment
55
by checking that submit buttons are disabled after a template change is triggered.
6+
7+
NOTE: This test requires pytest and playwright. It will be skipped if they are not installed.
8+
Run with: tox -e e2e
69
"""
710

811
import os
12+
import unittest
913

10-
import pytest
11-
from django.contrib.auth.models import User
12-
from playwright.sync_api import expect
14+
try:
15+
import pytest
16+
from playwright.sync_api import expect
1317

18+
PLAYWRIGHT_AVAILABLE = True
19+
except ImportError:
20+
PLAYWRIGHT_AVAILABLE = False
21+
pytest = None
22+
expect = None
23+
24+
from django.contrib.auth.models import User
1425
from feincms.module.page.models import Page
1526

1627

1728
# Set Django async unsafe to allow database operations in tests
1829
os.environ.setdefault("DJANGO_ALLOW_ASYNC_UNSAFE", "true")
1930

2031

21-
@pytest.mark.django_db
22-
@pytest.mark.e2e
23-
def test_double_submission_protection(page, live_server):
24-
"""
25-
Test that submit buttons are disabled after template change.
26-
27-
This verifies the fix for Issue #677 by:
28-
1. Logging into the admin
29-
2. Navigating to a Page change view
30-
3. Changing the template to trigger the on_template_key_changed event
31-
4. Accepting the confirmation alert
32-
5. Verifying that the Save button is immediately disabled
33-
"""
34-
# Register templates
35-
Page._feincms_templates = {}
36-
Page.register_templates(
37-
{
38-
"key": "base",
39-
"title": "Base Template",
40-
"path": "feincms_base.html",
41-
"regions": (
42-
("main", "Main content area"),
43-
("sidebar", "Sidebar", "inherited"),
44-
),
45-
},
46-
{
47-
"key": "theother",
48-
"title": "Alternative Template",
49-
"path": "base.html",
50-
"regions": (
51-
("main", "Main content area"),
52-
("sidebar", "Sidebar", "inherited"),
53-
),
54-
},
55-
)
56-
57-
# Create superuser
58-
username = "admin"
59-
password = "testpass123"
60-
User.objects.create_superuser(
61-
username=username, email="admin@test.com", password=password
62-
)
63-
64-
# Create a test Page with initial template_key='base'
65-
test_page = Page.objects.create(
66-
title="Test Page",
67-
slug="test-page",
68-
template_key="base",
69-
active=True,
70-
in_navigation=True,
71-
)
72-
73-
# Log in to Django admin
74-
page.goto(f"{live_server.url}/admin/login/")
75-
page.fill("#id_username", username)
76-
page.fill("#id_password", password)
77-
page.click('input[type="submit"]')
78-
79-
# Navigate to the Page change view
80-
page.goto(f"{live_server.url}/admin/page/page/{test_page.pk}/change/")
81-
82-
# Wait for the page to load - check specifically for the form
83-
page.wait_for_load_state("networkidle")
84-
page.wait_for_selector('form[method="post"]')
85-
86-
# Verify jQuery and JavaScript are loaded
87-
jquery_loaded = page.evaluate("typeof jQuery !== 'undefined'")
88-
template_regions_loaded = page.evaluate("typeof template_regions !== 'undefined'")
89-
90-
if not jquery_loaded or not template_regions_loaded:
91-
pytest.skip(
92-
f"JavaScript not properly loaded (jQuery: {jquery_loaded}, "
93-
f"template_regions: {template_regions_loaded}). This test requires full admin static files."
32+
if PLAYWRIGHT_AVAILABLE:
33+
34+
@pytest.mark.django_db
35+
@pytest.mark.e2e
36+
def test_double_submission_protection(page, live_server):
37+
"""
38+
Test that submit buttons are disabled after template change.
39+
40+
This verifies the fix for Issue #677 by:
41+
1. Logging into the admin
42+
2. Navigating to a Page change view
43+
3. Changing the template to trigger the on_template_key_changed event
44+
4. Accepting the confirmation alert
45+
5. Verifying that the Save button is immediately disabled
46+
"""
47+
# Register templates
48+
Page._feincms_templates = {}
49+
Page.register_templates(
50+
{
51+
"key": "base",
52+
"title": "Base Template",
53+
"path": "feincms_base.html",
54+
"regions": (
55+
("main", "Main content area"),
56+
("sidebar", "Sidebar", "inherited"),
57+
),
58+
},
59+
{
60+
"key": "theother",
61+
"title": "Alternative Template",
62+
"path": "base.html",
63+
"regions": (
64+
("main", "Main content area"),
65+
("sidebar", "Sidebar", "inherited"),
66+
),
67+
},
68+
)
69+
70+
# Create superuser
71+
username = "admin"
72+
password = "testpass123"
73+
User.objects.create_superuser(
74+
username=username, email="admin@test.com", password=password
75+
)
76+
77+
# Create a test Page with initial template_key='base'
78+
test_page = Page.objects.create(
79+
title="Test Page",
80+
slug="test-page",
81+
template_key="base",
82+
active=True,
83+
in_navigation=True,
84+
)
85+
86+
# Log in to Django admin
87+
page.goto(f"{live_server.url}/admin/login/")
88+
page.fill("#id_username", username)
89+
page.fill("#id_password", password)
90+
page.click('input[type="submit"]')
91+
92+
# Navigate to the Page change view
93+
page.goto(f"{live_server.url}/admin/page/page/{test_page.pk}/change/")
94+
95+
# Wait for the page to load - check specifically for the form
96+
page.wait_for_load_state("networkidle")
97+
page.wait_for_selector('form[method="post"]')
98+
99+
# Verify jQuery and JavaScript are loaded
100+
jquery_loaded = page.evaluate("typeof jQuery !== 'undefined'")
101+
template_regions_loaded = page.evaluate(
102+
"typeof template_regions !== 'undefined'"
94103
)
95104

96-
# Find the template key input - could be either select or radio buttons
97-
template_inputs = page.locator('input[name="template_key"]').count()
98-
99-
# Set up dialog handler BEFORE changing the template
100-
dialog_accepted = []
101-
102-
def handle_dialog(dialog):
103-
dialog.accept()
104-
dialog_accepted.append(True)
105-
106-
page.on("dialog", handle_dialog)
107-
108-
if template_inputs == 0:
109-
# It's a select dropdown
110-
select = page.locator('select[name="template_key"]')
111-
current_value = select.input_value()
112-
# Change to a different template
113-
options = select.locator("option").all()
114-
for option in options:
115-
value = option.get_attribute("value")
116-
if value and value != current_value:
117-
select.select_option(value)
118-
break
119-
else:
120-
# It's radio buttons - use JavaScript to trigger the change since inputs are hidden
121-
radios = page.locator('input[name="template_key"]').all()
122-
for i, radio in enumerate(radios):
123-
if not radio.is_checked():
124-
# Use JavaScript to check the radio and trigger the change event
125-
radio.evaluate("el => { el.checked = true; el.dispatchEvent(new Event('change', { bubbles: true })); el.click(); }")
126-
break
127-
128-
# Wait for the dialog to be handled
129-
page.wait_for_timeout(500)
130-
131-
# Verify that the dialog was shown and accepted
132-
assert len(dialog_accepted) > 0, "Expected confirmation dialog did not appear"
133-
134-
# CRITICAL ASSERTION: Verify that the Save button is disabled
135-
# This should happen immediately after accepting the dialog
136-
# Give a bit of time for the JavaScript to disable the button
137-
page.wait_for_timeout(100)
138-
save_button = page.locator('input[name="_save"]')
139-
140-
# Check if the button is disabled (it should be to prevent double submission)
141-
expect(save_button).to_be_disabled(timeout=1000)
105+
if not jquery_loaded or not template_regions_loaded:
106+
pytest.skip(
107+
f"JavaScript not properly loaded (jQuery: {jquery_loaded}, "
108+
f"template_regions: {template_regions_loaded}). This test requires full admin static files."
109+
)
110+
111+
# Find the template key input - could be either select or radio buttons
112+
template_inputs = page.locator('input[name="template_key"]').count()
113+
114+
# Set up dialog handler BEFORE changing the template
115+
dialog_accepted = []
116+
117+
def handle_dialog(dialog):
118+
dialog.accept()
119+
dialog_accepted.append(True)
120+
121+
page.on("dialog", handle_dialog)
122+
123+
if template_inputs == 0:
124+
# It's a select dropdown
125+
select = page.locator('select[name="template_key"]')
126+
current_value = select.input_value()
127+
# Change to a different template
128+
options = select.locator("option").all()
129+
for option in options:
130+
value = option.get_attribute("value")
131+
if value and value != current_value:
132+
select.select_option(value)
133+
break
134+
else:
135+
# It's radio buttons - use JavaScript to trigger the change since inputs are hidden
136+
radios = page.locator('input[name="template_key"]').all()
137+
for i, radio in enumerate(radios):
138+
if not radio.is_checked():
139+
# Use JavaScript to check the radio and trigger the change event
140+
radio.evaluate(
141+
"el => { el.checked = true; el.dispatchEvent(new Event('change', { bubbles: true })); el.click(); }"
142+
)
143+
break
144+
145+
# Wait for the dialog to be handled
146+
page.wait_for_timeout(500)
147+
148+
# Verify that the dialog was shown and accepted
149+
assert len(dialog_accepted) > 0, "Expected confirmation dialog did not appear"
150+
151+
# CRITICAL ASSERTION: Verify that the Save button is disabled
152+
# This should happen immediately after accepting the dialog
153+
# Give a bit of time for the JavaScript to disable the button
154+
page.wait_for_timeout(100)
155+
save_button = page.locator('input[name="_save"]')
156+
157+
# Check if the button is disabled (it should be to prevent double submission)
158+
expect(save_button).to_be_disabled(timeout=1000)

0 commit comments

Comments
 (0)