Skip to content

Commit ebcf354

Browse files
committed
Add integration tests
1 parent 1aa0cab commit ebcf354

File tree

10 files changed

+455
-10
lines changed

10 files changed

+455
-10
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ dist
1010
htmlcov
1111
node_modules
1212
yarn.lock
13+
test-results

pyproject.toml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,10 @@ dependencies = [
4040
optional-dependencies.tests = [
4141
"coverage",
4242
"pytest",
43+
"pytest-asyncio",
44+
"pytest-cov",
4345
"pytest-django",
46+
"pytest-playwright",
4447
]
4548
urls.Homepage = "https://github.com/matthiask/django-content-editor/"
4649

@@ -116,4 +119,6 @@ lint.mccabe.max-complexity = 15
116119
[tool.pytest.ini_options]
117120
DJANGO_SETTINGS_MODULE = "testapp.settings"
118121
python_files = [ "tests.py", "test_*.py" ]
119-
addopts = "--reuse-db"
122+
addopts = "--strict-markers"
123+
asyncio_mode = "strict"
124+
asyncio_default_fixture_loop_scope = "function"

tests/README_integration_tests.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Integration Tests with Playwright
2+
3+
This directory contains integration tests for django-content-editor using Playwright.
4+
5+
## Overview
6+
7+
The integration tests use Playwright to test the content editor's functionality in a real browser environment, interacting with the actual UI components of the Django admin interface.
8+
9+
## Running Tests
10+
11+
To run the integration tests:
12+
13+
```bash
14+
# Run with tox
15+
tox -e py313-dj52-playwright
16+
17+
# Run tests directly (after installing dependencies)
18+
cd tests
19+
python -m pytest testapp/test_playwright.py -v
20+
```
21+
22+
## Test Structure
23+
24+
The tests are organized as follows:
25+
26+
- `test_playwright.py`: Main test file containing test cases for various content editor features
27+
- `test_playwright_helpers.py`: Helper functions for common operations like login and creating articles
28+
- `conftest.py`: Pytest configuration and shared fixtures
29+
30+
## Test Coverage
31+
32+
The integration tests cover the following functionality:
33+
34+
1. **Basic Content Editor Loading**: Verifies that the content editor loads correctly in the admin interface
35+
2. **Adding Content**: Tests adding rich text and other content types to an article
36+
3. **Drag-and-Drop Ordering**: Tests the drag-and-drop functionality for reordering content items
37+
4. **Tabbed Fieldsets**: Tests the tabbed interface if present
38+
5. **Multiple Content Types**: Tests adding different types of content to an article
39+
6. **Save Shortcut**: Tests the Ctrl+S keyboard shortcut for saving changes
40+
41+
## Troubleshooting
42+
43+
If you encounter issues with the tests:
44+
45+
- **Browser not found**: Make sure Playwright browsers are installed (`playwright install chromium`)
46+
- **Element not found**: The selectors may need adjustment based on the actual DOM structure
47+
- **Timeouts**: Increase timeout values if operations take longer than expected
48+
49+
## Extending the Tests
50+
51+
To add new tests:
52+
53+
1. Add new test functions to `test_playwright.py`
54+
2. Use the helper functions from `test_playwright_helpers.py` for common operations
55+
3. If needed, add new helper functions for specific interactions

tests/conftest.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import os
2+
3+
import pytest
4+
from django.contrib.auth.models import User
5+
6+
7+
# Set DJANGO_ALLOW_ASYNC_UNSAFE
8+
os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true"
9+
10+
# Default browser to run tests with
11+
os.environ.setdefault("PLAYWRIGHT_BROWSER_NAME", "chromium")
12+
13+
14+
@pytest.fixture
15+
def user():
16+
"""Create a test superuser."""
17+
u = User.objects.create(
18+
username="test", is_active=True, is_staff=True, is_superuser=True
19+
)
20+
u.set_password("test")
21+
u.save()
22+
return u
23+
24+
25+
@pytest.fixture
26+
def client(client, user):
27+
"""Login the test client."""
28+
client.login(username="test", password="test")
29+
return client
30+
31+
32+
@pytest.fixture(scope="session")
33+
def browser_type_launch_args():
34+
"""Configure browser launch arguments."""
35+
return {
36+
"headless": True,
37+
"args": [
38+
"--no-sandbox",
39+
"--disable-gpu",
40+
"--disable-dev-shm-usage",
41+
"--disable-setuid-sandbox",
42+
],
43+
}
44+
45+
46+
@pytest.fixture(scope="function")
47+
def browser_context_args(browser_context_args):
48+
"""Modify browser context arguments for tracing."""
49+
return {
50+
**browser_context_args,
51+
"record_video_dir": os.path.join(os.getcwd(), "test-results/videos/"),
52+
"record_har_path": os.path.join(os.getcwd(), "test-results/har/", "test.har"),
53+
"ignore_https_errors": True,
54+
"java_script_enabled": True,
55+
}
56+
57+
58+
@pytest.fixture
59+
def page(page):
60+
"""Add console and network error logging to the page."""
61+
# Capture console logs
62+
page.on("console", lambda msg: print(f"BROWSER CONSOLE {msg.type}: {msg.text}"))
63+
64+
# Capture JavaScript errors
65+
page.on("pageerror", lambda err: print(f"BROWSER JS ERROR: {err}"))
66+
67+
# Capture request failures
68+
page.on(
69+
"requestfailed",
70+
lambda request: print(f"NETWORK ERROR: {request.url} {request.failure}"),
71+
)
72+
73+
return page
74+
75+
76+
@pytest.hookimpl(hookwrapper=True)
77+
def pytest_runtest_makereport(item, call):
78+
"""Handle reporting and artifact generation."""
79+
outcome = yield
80+
report = outcome.get_result()
81+
82+
# Take screenshot of failed tests
83+
if report.when == "call" and report.failed:
84+
try:
85+
page = item.funcargs["page"]
86+
# Take screenshot and save it with test name
87+
screenshot_dir = os.path.join(os.getcwd(), "test-results/screenshots/")
88+
os.makedirs(screenshot_dir, exist_ok=True)
89+
screenshot_path = os.path.join(screenshot_dir, f"{item.name}_failed.png")
90+
page.screenshot(path=screenshot_path)
91+
# Save page HTML
92+
html_path = os.path.join(screenshot_dir, f"{item.name}_failed.html")
93+
with open(html_path, "w", encoding="utf-8") as f:
94+
f.write(page.content())
95+
96+
# Add to report
97+
report.extra = [
98+
{
99+
"name": "Screenshot",
100+
"content": screenshot_path,
101+
"mime_type": "image/png",
102+
},
103+
{"name": "HTML", "content": html_path, "mime_type": "text/html"},
104+
]
105+
except Exception as e:
106+
print(f"Failed to capture artifacts: {e}")

tests/testapp/admin.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,6 @@ class RichTextInline(ContentEditorInline):
2525
fieldsets = [(None, {"fields": ("text", "region", "ordering")})]
2626
regions = allow_regions({"main"})
2727

28-
class Media:
29-
js = ("//cdn.ckeditor.com/4.5.6/standard/ckeditor.js", "app/plugin_ckeditor.js")
30-
3128

3229
class ThingInline(admin.TabularInline):
3330
model = Thing

tests/testapp/settings.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66
"django.contrib.contenttypes",
77
"django.contrib.messages",
88
"django.contrib.sessions",
9-
"django.contrib.sitemaps",
10-
"django.contrib.sites",
119
"django.contrib.staticfiles",
1210
"testapp",
1311
"content_editor",
@@ -33,7 +31,6 @@
3331
"django.contrib.messages.middleware.MessageMiddleware",
3432
"django.middleware.locale.LocaleMiddleware",
3533
)
36-
SILENCED_SYSTEM_CHECKS = ["1_10.W001"]
3734
USE_TZ = True
3835
LANGUAGES = (("en", "English"), ("de", "German"))
3936
TEMPLATES = [

0 commit comments

Comments
 (0)