From 1424f3a30497e3777ab4e2c020f77a920a468eb5 Mon Sep 17 00:00:00 2001 From: Nick Moreton Date: Tue, 30 Jan 2024 05:17:03 +0000 Subject: [PATCH 01/23] Add initial admin_responses command --- wagtail_devtools/auth.py | 38 ++++++ .../management/commands/admin_responses.py | 114 ++++++++++++++++++ wagtail_devtools/reporting.py | 84 +++++++++++++ 3 files changed, 236 insertions(+) create mode 100644 wagtail_devtools/auth.py create mode 100644 wagtail_devtools/management/commands/admin_responses.py create mode 100644 wagtail_devtools/reporting.py diff --git a/wagtail_devtools/auth.py b/wagtail_devtools/auth.py new file mode 100644 index 0000000..ef77791 --- /dev/null +++ b/wagtail_devtools/auth.py @@ -0,0 +1,38 @@ +import requests + + +class LoginHandler: + def __init__(self, url): + self.url = url + self._is_authenticated = False + self.login_url = f"{self.url}/admin/login/" + self.session = requests.Session() + + def login(self, username, password): + login_form = self.session.get(self.login_url) + if login_form.status_code == 404: + raise Exception("Login page not found") + + user = { + "username": username, + "password": password, + "csrfmiddlewaretoken": login_form.cookies["csrftoken"], + } + response = self.session.post(self.login_url, data=user) + if response.status_code == 200: + self._is_authenticated = True + return self + + def logout(self): + self._is_authenticated = False + self.session = requests.Session() + return self + + def get_response(self, url): + # a request that can be used to make authenticated requests + # for accessing the wagtail admin pages + response = self.session.get(url) + return response + + def is_authenticated(self): + return self._is_authenticated diff --git a/wagtail_devtools/management/commands/admin_responses.py b/wagtail_devtools/management/commands/admin_responses.py new file mode 100644 index 0000000..d6ac78f --- /dev/null +++ b/wagtail_devtools/management/commands/admin_responses.py @@ -0,0 +1,114 @@ +from django.core.management.base import BaseCommand + +from wagtail_devtools.auth import LoginHandler +from wagtail_devtools.reporting import Report + + +class Command(BaseCommand): + help = "Access the devtools api and check admin and frontend urls." + + def add_arguments(self, parser): + """Add arguments.""" + parser.add_argument( + "--host", + action="store", + dest="host", + default="http://localhost:8000", + help="Host to check", + ) + parser.add_argument( + "--all", + action="store_true", + dest="all", + default=False, + help="Check all pages", + ) + parser.add_argument( + "--verbose", + action="store_true", + dest="verbose", + default=False, + help="Verbose output", + ) + + def handle(self, *args, **options): + # Listing page types + session = LoginHandler(options["host"]) + session.login("superuser", "superuser") + + if not session.is_authenticated(): + self.stdout.write(self.style.ERROR("Not authenticated")) + return + + index_endpoint = options["host"] + "/wagtail-devtools-api/" + + response = session.get_response(index_endpoint) + endpoints = response.json()["api-views"] + + results_500 = [] + results_404 = [] + results_302 = [] + results_200 = [] + + for endpoint in endpoints: + if options["all"]: + endpoint = endpoint + "?all=true" + response = session.get_response(endpoint) + results = response.json()["results"] + title = response.json()["meta"]["title"] + report = Report(title, results, session) + results_500.extend(report.get_errors_500()) + results_404.extend(report.get_errors_404()) + results_302.extend(report.get_errors_302()) + results_200.extend(report.get_success_200()) + + self.stdout.write(self.style.ERROR(f"500 errors: {len(results_500)}")) + self.stdout.write("=====================================") + if len(results_500) > 0: + for result in results_500: + self.stdout.write(self.style.HTTP_INFO(result.title)) + self.stdout.write( + self.style.HTTP_INFO(result.url) + ) if result.url else None + if result.editor_url: + self.stdout.write(self.style.HTTP_INFO(result.editor_url)) + self.stdout.write("\n") + + self.stdout.write(self.style.WARNING(f"404 errors: {len(results_404)}")) + self.stdout.write("=====================================") + if len(results_404) > 0: + for result in results_404: + self.stdout.write(self.style.HTTP_INFO(result.title)) + self.stdout.write( + self.style.HTTP_INFO(result.url) + ) if result.url else None + if result.editor_url: + self.stdout.write(self.style.HTTP_INFO(result.editor_url)) + self.stdout.write("\n") + + self.stdout.write(self.style.WARNING(f"302 errors: {len(results_302)}")) + self.stdout.write("=====================================") + if len(results_302) > 0: + for result in results_302: + self.stdout.write(self.style.HTTP_INFO(result.title)) + self.stdout.write( + self.style.HTTP_INFO(result.url) + ) if result.url else None + if result.editor_url: + self.stdout.write(self.style.HTTP_INFO(result.editor_url)) + self.stdout.write("\n") + + self.stdout.write(self.style.SUCCESS(f"200 success: {len(results_200)}")) + self.stdout.write("=====================================") + if options["verbose"]: + if len(results_200) > 0: + for result in results_200: + self.stdout.write(self.style.HTTP_INFO(result.title)) + self.stdout.write( + self.style.HTTP_INFO(result.url) + ) if result.url else None + if result.editor_url: + self.stdout.write(self.style.HTTP_INFO(result.editor_url)) + + self.stdout.write(self.style.SUCCESS(f"Total: {len(endpoints)}")) + self.stdout.write("=====================================") diff --git a/wagtail_devtools/reporting.py b/wagtail_devtools/reporting.py new file mode 100644 index 0000000..625bee1 --- /dev/null +++ b/wagtail_devtools/reporting.py @@ -0,0 +1,84 @@ +from dataclasses import dataclass, field + +import requests + + +@dataclass +class Item: + session: requests.Session + app_name: str + class_name: str + title: str + editor_url: str = None + url: str = None + url_status_code: int = None + editor_url_status_code: int = None + + def __post_init__(self): + self._process_url() + self._process_editor_url() + + def _process_url(self): + # fix the url if it doesn't start with http://localhost:8000 + if self.url: + if not self.url.startswith("http://"): + self.url = "http://localhost:8000" + self.url + self.url_status_code = self.session.get_response(self.url).status_code + + def _process_editor_url(self): + if self.editor_url: + self.editor_url_status_code = self.session.get_response( + self.editor_url + ).status_code + + +@dataclass +class Report: + title: str + results: list + session: requests.Session + items: list = field(default_factory=list) + + def __post_init__(self): + self.results = self._process_results() + + def _process_results(self): + for result in self.results: + item = Item(self.session, **result) + self.items.append(item) + + def get_errors_500(self): + error_500 = [] + for item in self.items: + if item.url_status_code == 500: + error_500.append(item) + if item.editor_url_status_code == 500: + error_500.append(item) + return error_500 + + def get_errors_404(self): + error_404 = [] + for item in self.items: + if item.url_status_code == 404: + error_404.append(item) + if item.editor_url_status_code == 404: + error_404.append(item) + return error_404 + + def get_errors_302(self): + error_302 = [] + for item in self.items: + if item.url_status_code == 302: + error_302.append(item) + if item.editor_url_status_code == 302: + error_302.append(item) + return error_302 + + def get_success_200(self): + success = [] + for item in self.items: + if item.url_status_code == 200: + success.append(item) + if item.editor_url_status_code == 200: + success.append(item) + return success From 509d125ac5d69d906fc17704131297117d021e5b Mon Sep 17 00:00:00 2001 From: Nick Moreton Date: Tue, 30 Jan 2024 05:19:43 +0000 Subject: [PATCH 02/23] Remove the old _base_admin_responses --- .../commands/_base_admin_responses.py | 328 ------------------ .../commands/cmd_test_admin_responses.py | 248 ------------- 2 files changed, 576 deletions(-) delete mode 100644 wagtail_devtools/management/commands/_base_admin_responses.py delete mode 100644 wagtail_devtools/test/management/commands/cmd_test_admin_responses.py diff --git a/wagtail_devtools/management/commands/_base_admin_responses.py b/wagtail_devtools/management/commands/_base_admin_responses.py deleted file mode 100644 index ad03449..0000000 --- a/wagtail_devtools/management/commands/_base_admin_responses.py +++ /dev/null @@ -1,328 +0,0 @@ -import requests - -from django.apps import apps -from django.conf import settings -from django.contrib.auth import get_user_model -from django.core.management.base import BaseCommand, CommandError -from wagtail.admin.admin_url_finder import AdminURLFinder -from wagtail.admin.utils import get_admin_base_url -from wagtail.contrib.modeladmin.helpers import AdminURLHelper -from wagtail.contrib.settings.registry import registry as settings_registry -from wagtail.documents import get_document_model -from wagtail.images import get_image_model -from wagtail.models import get_page_models -from wagtail.models.collections import Collection -from wagtail.snippets.models import get_snippet_models - - -class BaseAdminResponsesCommand(BaseCommand): - """Base command for admin responses commands. - - Extend this class to create a management command that generates a report of all the admin and frontend responses for all pages, snippets, settings and modeladmin and more. - - You can specify the URL to check by passing the --host option. - You can specify the URL to use for the report by passing the --report-url option. - - To implement this command in your project, create a management command that extends this class. - - Usage: - python manage.py [your_command_name] [--host] [--report-url - """ - - help = "Checks the admin and frontend responses for models including pages, snippets, settings and modeladmin and more." - - def add_arguments(self, parser): - parser.add_argument("username", help="The username to use for login") - parser.add_argument("password", help="The password to use for login") - parser.add_argument( - "--host", default=get_admin_base_url(), help="The URL to check" - ) - parser.add_argument( - "--report-url", - help="The URL to use for the report. e.g. http://staging.example.com", - ) - - def handle(self, *args, **options): - # Disabled if not running in DEBUG mode - if not settings.DEBUG: - raise CommandError( - "This command is only available in DEBUG mode. Set DEBUG=True in your settings to enable it." - ) - - self.report_lines = [] - self.checked_url = options["host"] - self.report_url = ( - options["report_url"].strip("/") if options["report_url"] else None - ) - - reports = self.get_reports(*args, **options) - - for report in reports: - report["function"](*report["args"]) - - def report_admin_list_pages(self, session, title, url): - """Check and report the admin response for a list of pages.""" - self.out_message(f"\n{title} page ...", "HTTP_INFO") - - response = session.get(url) - - if response.status_code == 200: - self.out_message(f"{url} ← 200", "SUCCESS") - else: - self.out_message(f"{url} ← {response.status_code}", "ERROR") - - def report_admin_app_model(self, session, options, title, app_label, model_name): - """Check and report the admin response for a model.""" - self.out_message(f"\n{title} page ...", "HTTP_INFO") - - model = apps.get_model(app_label, model_name) - self.out_models(session, options, [model]) - - def report_users(self, session, options): - """Check and report the admin response for the users list page.""" - self.out_message("\nUSERS EDIT page ...", "HTTP_INFO") - - user_model = get_user_model() - self.out_models(session, options, [user_model]) - - def report_groups(self, session, options): - """Check and report the admin response for the groups list page.""" - self.out_message("\nGROUPS EDIT page ...", "HTTP_INFO") - - group_model = apps.get_model("auth", "Group") - self.out_models(session, options, [group_model]) - - def report_sites(self, session, options): - """Check and report the admin response for the sites list page.""" - self.out_message("\nSITES EDIT page ...", "HTTP_INFO") - - site_model = apps.get_model("wagtailcore", "Site") - self.out_models(session, options, [site_model]) - - def report_collections(self, session, options): - """Check and report the admin response for the collections list page.""" - self.out_message("\nCOLLECTIONS EDIT page ...", "HTTP_INFO") - - try: - self.out_model( - session, options, Collection.objects.first().get_first_child() - ) - except AttributeError: - self.out_message("No collections found", "WARNING") - - def report_documents(self, session, options): - """Check and report the admin response for the documents list page.""" - self.out_message("\nDOCUMENTS edit page ...", "HTTP_INFO") - - document_model = get_document_model() - self.out_models(session, options, [document_model]) - - def report_images(self, session, options): - """Check and report the admin response for the images list page.""" - self.out_message("\nIMAGES edit page ...", "HTTP_INFO") - - image_model = get_image_model() - self.out_models(session, options, [image_model]) - - def report_settings_models(self, session, options): - """Check and report the admin response for the settings edit page.""" - self.out_message("\nSETTINGS edit pages ...", "HTTP_INFO") - self.out_models(session, options, settings_registry) - - def report_snippets(self, session, options): - self.out_message("\nSNIPPETS models edit pages ...", "HTTP_INFO") - - snippet_models = get_snippet_models() - self.out_models(session, options, snippet_models) - - def report_model_admin(self, session, options, registered_modeladmin): - for registered in registered_modeladmin: - for model in apps.get_models(): - app = model._meta.app_label - name = model.__name__ - verbose_name = model._meta.verbose_name - if f"{app}.{name}" in registered: - index_url = AdminURLHelper(model).index_url - self.report_admin_list_pages( - session, f"{verbose_name} list", f"{options['host']}{index_url}" - ) - self.out_message(f"\n{verbose_name} edit page ...", "HTTP_INFO") - self.out_models(session, options, [model]) - - def report_pages(self, session, options): - """Check and report the admin and frontend responses for all page model types.""" - page_models = self.filter_page_models(get_page_models()) - - model_index = [] - results = [] - - for page_model in page_models: - if item := page_model.objects.first(): - model_index.append(item.__class__.__name__) - results.append( - { - "title": item.title, - "url": f"{item.url}", - "id": item.id, - "editor_url": f"{self.get_admin_edit_url(options, item)}", - "class_name": item.__class__.__name__, - } - ) - - message = f"\nChecking the admin and frontend responses of {len(results)} page types ..." - self.out_message(message, "HTTP_INFO") - - for count, content_type in enumerate(sorted(model_index)): - # Also add padding for the single digit numbers - message = ( - f" {count + 1}. {content_type}" - if count <= 8 - else f"{count + 1}. {content_type}" - ) - self.out_message(message) - - # Print the results - for page in results: - message = f"\n{page['title']} ( {page['class_name']} ) ↓" - self.out_message(message) - - # Check the admin response - response = session.get(page["editor_url"]) - if response.status_code != 200: - self.out_message( - f"{page['editor_url']} ← {response.status_code}", "ERROR" - ) - else: - self.out_message(f"{page['editor_url']} ← 200", "SUCCESS") - - # Check the frontend response - response = session.get(page["url"]) - if response.status_code == 200: - self.out_message(f"{page['url']} ← 200", "SUCCESS") - else: - if response.status_code == 404: - message = ( - f"{page['url']} ← {response.status_code} probably a draft page" - ) - self.out_message(message, "WARNING") - else: - self.out_message(f"{page['url']} ← {response.status_code}", "ERROR") - - def out_models(self, session, options, models): - """Create a report for the first object of each model. - The models have a base_manager that is used to get the first object.""" - - for model in models: - obj = model.objects.first() - if not obj: - self.out_message( - f"No {model._meta.verbose_name_plural} found", "WARNING" - ) - continue - - url = self.get_admin_edit_url(options, obj) - - response = session.get(url) - - if response.status_code == 200: - self.out_message(f"{url} ← 200", "SUCCESS") - else: - self.out_message(f"{url} ← {response.status_code}", "ERROR") - - def out_model(self, session, options, model): - """Create a report for the first object of a model. - The model does not have a base_manager so the object is passed in.""" - url = self.get_admin_edit_url(options, model) - - response = session.get(url) - - if response.status_code == 200: - self.out_message(f"{url} ← 200", "SUCCESS") - else: - self.out_message(f"{url} ← {response.status_code}", "ERROR") - - def out_message(self, message, style=None): - if self.report_url: - message = message.replace(self.checked_url, self.report_url) - if message not in self.report_lines: - self.report_lines.append(message) - if style and style == "HTTP_INFO": - self.stdout.write(self.style.HTTP_INFO(message)) - self.stdout.write("=" * len(message)) - elif style and style == "ERROR": - self.stderr.write(self.style.ERROR(message)) - elif style and style == "SUCCESS": - self.stdout.write(self.style.SUCCESS(message)) - elif style and style == "WARNING": - self.stdout.write(self.style.WARNING(message)) - else: - self.stdout.write(message) - - @staticmethod - def filter_page_models(page_models): - """Filter out page models that are not creatable or are in the core apps.""" - - filtered_page_models = [] - - for page_model in page_models: - if page_model._meta.app_label == "wagtailcore": - # Skip the core apps - continue - if not page_model.is_creatable: - # Skip pages that can't be created - continue - filtered_page_models.append(page_model) - - return filtered_page_models - - @staticmethod - def get_admin_edit_url(options, obj): - admin_url_finder = AdminURLFinder() - return f"{options['host']}{admin_url_finder.get_edit_url(obj)}" - - def _log_in(self, options): - with requests.Session() as session: - url = f"{options['host']}/admin/login/" - - try: - session.get(url) - except requests.exceptions.ConnectionError: - self.out_message( - f"Could not connect to {options['host']}. Is the server running?", - "ERROR", - ) - exit() - except requests.exceptions.InvalidSchema: - self.out_message( - f"Could not connect to {options['host']}. Invalid schema", - "ERROR", - ) - exit() - except requests.exceptions.MissingSchema: - self.out_message( - f"Could not connect to {options['host']}. Missing schema", - "ERROR", - ) - exit() - - logged_in = session.post( - # session should now be logged in so reporting could begin - url, - data={ - "username": options["username"], - "password": options["password"], - "csrfmiddlewaretoken": session.cookies["csrftoken"], - "next": "/admin/", - }, - ).content - - if "Forgotten password?" in logged_in.decode("utf-8"): - # Login failed because the response is the forgotten password page - self.out_message( - f"Could not log in to {options['host']}. Is the username and password correct?", - "ERROR", - ) - return - else: - self.out_message(f"Logged in to {options['host']}", "SUCCESS") - return session diff --git a/wagtail_devtools/test/management/commands/cmd_test_admin_responses.py b/wagtail_devtools/test/management/commands/cmd_test_admin_responses.py deleted file mode 100644 index 82d1a4c..0000000 --- a/wagtail_devtools/test/management/commands/cmd_test_admin_responses.py +++ /dev/null @@ -1,248 +0,0 @@ -from django.urls import reverse - -from wagtail_devtools.management.commands._base_admin_responses import ( - BaseAdminResponsesCommand, -) - - -class Command(BaseAdminResponsesCommand): - def get_reports(self, *args, **options): - session = self._log_in(options) - - return [ - { - # PAGES EDIT - "function": self.report_pages, - "args": [session, options], - }, - { - # DASHBOARD - "function": self.report_admin_list_pages, - "args": [ - session, - "DASHBOARD", - f"{options['host']}{reverse('wagtailadmin_home')}", - ], - }, - { - # PAGES LIST - "function": self.report_admin_list_pages, - "args": [ - session, - "PAGES list", - f"{options['host']}{reverse('wagtailadmin_explore_root')}", - ], - }, - { - # SEARCH PAGES - "function": self.report_admin_list_pages, - "args": [ - session, - "SEARCH all", - f"{options['host']}{reverse('wagtailadmin_pages:search')}", - ], - }, - { - # AGING PAGES - "function": self.report_admin_list_pages, - "args": [ - session, - "AGING PAGES list", - f"{options['host']}{reverse('wagtailadmin_reports:aging_pages')}", - ], - }, - { - # COLLECTIONS - "function": self.report_admin_list_pages, - "args": [ - session, - "COLLECTIONS list", - f"{options['host']}{reverse('wagtailadmin_collections:index')}", - ], - }, - { - # DOCUMENTS LIST - "function": self.report_admin_list_pages, - "args": [ - session, - "DOCUMENTS list", - f"{options['host']}{reverse('wagtaildocs:index')}", - ], - }, - { - # GROUPS - "function": self.report_admin_list_pages, - "args": [ - session, - "GROUPS list", - f"{options['host']}{reverse('wagtailusers_groups:index')}", - ], - }, - { - # IMAGES LIST - "function": self.report_admin_list_pages, - "args": [ - session, - "IMAGES list", - f"{options['host']}{reverse('wagtailimages:index')}", - ], - }, - { - # LOCKED PAGES - "function": self.report_admin_list_pages, - "args": [ - session, - "LOCKED PAGES list", - f"{options['host']}{reverse('wagtailadmin_reports:locked_pages')}", - ], - }, - { - # REDIRECTS - "function": self.report_admin_list_pages, - "args": [ - session, - "REDIRECTS list", - f"{options['host']}{reverse('wagtailredirects:index')}", - ], - }, - { - # SITES - "function": self.report_admin_list_pages, - "args": [ - session, - "SITES list", - f"{options['host']}{reverse('wagtailsites:index')}", - ], - }, - { - # SITE HISTORY - "function": self.report_admin_list_pages, - "args": [ - session, - "SITE HISTORY list", - f"{options['host']}{reverse('wagtailadmin_reports:site_history')}", - ], - }, - { - # SNIPPETS LIST - "function": self.report_admin_list_pages, - "args": [ - session, - "SNIPPETS list", - f"{options['host']}{reverse('wagtailsnippets:index')}", - ], - }, - { - # USERS - "function": self.report_admin_list_pages, - "args": [ - session, - "USERS list", - f"{options['host']}{reverse('wagtailusers_users:index')}", - ], - }, - { - # WORKFLOWS LIST - "function": self.report_admin_list_pages, - "args": [ - session, - "WORKFLOWS list", - f"{options['host']}{reverse('wagtailadmin_workflows:index')}", - ], - }, - { - # WORKFLOWS TASKS - "function": self.report_admin_list_pages, - "args": [ - session, - "WORKFLOWS TASKS list", - f"{options['host']}{reverse('wagtailadmin_workflows:task_index')}", - ], - }, - { - # COLLECTIONS EDIT - "function": self.report_collections, - "args": [session, options], - }, - { - # DOCUMENTS - "function": self.report_documents, - "args": [session, options], - }, - { - # GROUPS EDIT - "function": self.report_groups, - "args": [session, options], - }, - { - # IMAGES - "function": self.report_images, - "args": [session, options], - }, - { - # REDIRECTS EDIT - "function": self.report_admin_app_model, - "args": [ - session, - options, - "REDIRECTS edit", - "wagtailredirects", - "Redirect", - ], - }, - { - # SETTINGS - "function": self.report_settings_models, - "args": [session, options], - }, - { - # SITES EDIT - "function": self.report_sites, - "args": [session, options], - }, - { - # SNIPPETS - "function": self.report_snippets, - "args": [session, options], - }, - { - # USERS EDIT - "function": self.report_users, - "args": [session, options], - }, - { - # WORKFLOWS EDIT - "function": self.report_admin_app_model, - "args": [ - session, - options, - "WORKFLOWS edit", - "wagtailcore", - "Workflow", - ], - }, - { - # WORKFLOWS TASK EDIT - "function": self.report_admin_app_model, - "args": [ - session, - options, - "WORKFLOWS TASK edit", - "wagtailcore", - "Task", - ], - }, - { - # MODELADMIN - "function": self.report_model_admin, - "args": [ - session, - options, - [ - "wagtail_devtools_test.TestModelAdminOne", - "wagtail_devtools_test.TestModelAdminTwo", - "wagtail_devtools_test.TestModelAdminThree", - ], - ], - }, - ] From 1a2f024a85476e19872d58175d9882bfda0fce69 Mon Sep 17 00:00:00 2001 From: Nick Moreton Date: Tue, 30 Jan 2024 05:31:26 +0000 Subject: [PATCH 03/23] Update admin_responses docs --- README.md | 7 +- docs/admin_responses.md | 186 +++++++--------------------------------- 2 files changed, 37 insertions(+), 156 deletions(-) diff --git a/README.md b/README.md index f4a6118..5ddc61f 100644 --- a/README.md +++ b/README.md @@ -14,11 +14,14 @@ A set of developer tools in the form of management commands. ### API -There is an api now available at `/wagtail-devtools-api/` which will list all available commands. +There is an api available at `/wagtail-devtools-api/` which will list all available endpoints. + +- List all editor listing pages +- List all editor edit pages ### Admin Responses -The `admin_responses` command will make a requests to the admin interface using get requests for a range of models. It will write a response result to the console. +The `admin_responses` command will use the API endpoints above to create a report of error and success pages in the console. - [Documentation](docs/admin_responses.md) diff --git a/docs/admin_responses.md b/docs/admin_responses.md index 3b5d833..6ac62fa 100644 --- a/docs/admin_responses.md +++ b/docs/admin_responses.md @@ -4,67 +4,18 @@ The `admin_responses` command will make a requests to the admin interface using **Note:** As the command makes requests to the admin interface it will need a local development site to be running in `DEBUG` mode. It will work best if you have a full set of test data for all models. -Reports can be generated for: +Reports are generated for: - Admin Listing and Edit pages - Frontend Pages -You can also add admin reports covering: - -- Snippets -- Settings -- ModelAdmin -- Pages -- and more... - ## Usage -Create a command class in your app's `management/commands` directory thats inherits from `BaseAdminResponsesCommand` with the following content: - -```python -from wagtail_devtools.management.commands._base_admin_responses import ( - BaseAdminResponsesCommand, -) - - -class Command(BaseAdminResponsesCommand): - def get_reports(self, *args, **options): - session = self._log_in(options) - - register_reports = [ - # Add your reports here - ] -``` - -You can use any name for the command file, but it must be in a `management/commands` directory. - -I'll use `report_responses.py` as an example. - -**Note:** A full command that uses all available checks can be found in the [cmd_test_admin_responses.py](../wagtail_devtools/test/management/commands/cmd_test_admin_responses.py) file which you can use as a quick start. - -### Page Models Report - -Will generate a report for all admin edit pages for all page type models and corresponding frontend pages. - -It does this by automatically finding all page models, then using a get request it will check the response status code for the admin edit page and the frontend page. - -#### Setup - -Add the following item to the `register_reports` list: - -```python -{ - "function": self.report_pages, - "args": [session, options], -} +```bash +python manage.py admin_responses ``` -- `function` is the function that will be called to generate the report. -- `args` is a list of arguments that will need to be passed to the function. - - `session` is the session object that will be used to make the requests. - - `options` is the commands options object that is passed to the command. - -#### Example Console Output +### Example Console Output ![Page Model Report](./assets/pages-output.jpg) @@ -72,104 +23,31 @@ If your terminal allows it the links should be clickable. Any pages that return a response code other than 200 will be highlighted in red. -### Admin Listing Pages Report - -Will generate a report for all admin listing pages. - -```python -{ - "function": self.report_admin_list_pages, - "args": [ - session, - "Dashboard", - f"{options['host']}{reverse('wagtailadmin_home')}", - ], -} -``` - -It does this by making a get request to the specified admin listing page and checking the response status code. - -- `function` is the function that will be called to generate the report. -- `args` is a list of arguments that will need to be passed to the function. - - `session` is the session object that will be used to make the requests. - - `Dashboard` is the title of the report. - - `f"{options['host']}{reverse('wagtailadmin_home')}"` is the url of the admin listing page to check. - -#### Other admin listing page reports - -You can also generate reports for other admin listing pages. Use the following wagtail urls as the third argument and a title of the report as the second argument: - -- Aging Pages: `f"{options['host']}{reverse('wagtailadmin_reports:aging_pages')}"` -- Collections: `f"{options['host']}{reverse('wagtailadmin_collections:index')}"` -- Dashboard: `f"{options['host']}{reverse('wagtailadmin_home')}"` -- Documents: `f"{options['host']}{reverse('wagtaildocs:index')}"` -- Groups: `f"{options['host']}{reverse('wagtailusers_groups:index')}"` -- Images: `f"{options['host']}{reverse('wagtailimages:index')}"` -- Locked Pages: `f"{options['host']}{reverse('wagtailadmin_reports:locked_pages')}"` -- Redirects: `f"{options['host']}{reverse('wagtailredirects:index')}"` -- Search: `f"{options['host']}{reverse('wagtailadmin_explore_root')}"` -- Site History: `f"{options['host']}{reverse('wagtailadmin_reports:site_history')}"` -- Sites: `f"{options['host']}{reverse('wagtailsites:index')}"` -- Snippets: `f"{options['host']}{reverse('wagtailsnippets:index')}"` -- Users: `f"{options['host']}{reverse('wagtailusers_users:index')}"` -- Workflow List: `f"{options['host']}{reverse('wagtailadmin_workflows:index')}"` -- Workflow Tasks: `f"{options['host']}{reverse('wagtailadmin_workflows:task_index')}"` - -### Admin Model Edit Pages Report - -Will generate a report for the admin edit page using the specified app and model. - -It does this by making a get request to the corresponding admin edit page for the model and checking the response status code. - -```python -{ - "function": self.report_admin_app_model, - "args": [ - session, - options, - "WORKFLOWS EDIT", - "wagtailcore", - "Workflow", - ], -}, -``` - -- `function` is the function that will be called to generate the report. -- `args` is a list of arguments that will need to be passed to the function. - - `session` is the session object that will be used to make the requests. - - `options` is the commands options object that is passed to the command. - - `WORKFLOWS EDIT` is the title of the report. - - `wagtailcore` is the app name. - - `Workflow` is the model name. - -#### Other admin app.model reports - -You can also generate reports for edit pages for other models: - -- Documents: `report_documents` -- Images: `report_images` -- Users: `report_users` -- Groups: `report_groups` -- Sites: `report_sites` -- Collections: `report_collections` -- Snippets: `report_snippets` - - -### ModelAdmin Reports - -Will generate a report for the admin edit page using the specified modeladmin models. - -It does this by making a get request to the corresponding admin edit page for the model and checking the response status code. - -```python -{ - "function": self.report_model_admin, - "args": [session, options, ["app.Model"]], -} -``` - -- `function` is the function that will be called to generate the report. -- `args` is a list of arguments that will need to be passed to the function. - - `session` is the session object that will be used to make the requests. - - `options` is the commands options object that is passed to the command. - - `["app.Model"]` is a list of registered modeladmin app.model strings. +#### Listing Page Checks + +- Aging Pages: `wagtailadmin_reports:aging_pages` +- Collections: `wagtailadmin_collections:index` +- Dashboard: `wagtailadmin_home` +- Documents: `wagtaildocs:index` +- Groups: `wagtailusers_groups:index` +- Images: `wagtailimages:index` +- Locked Pages: `wagtailadmin_reports:locked_pages` +- Redirects: `wagtailredirects:index` +- Search: `wagtailadmin_explore_root` +- Site History: `wagtailadmin_reports:site_history` +- Sites: `wagtailsites:index` +- Snippets: `wagtailsnippets:index` +- Users: `wagtailusers_users:index` +- Workflow List: `wagtailadmin_workflows:index` +- Workflow Tasks: `wagtailadmin_workflows:task_index` + +### Edit Page Checks + +- Documents +- Images +- Users +- Groups +- Sites +- Collections +- Snippets +- ModelAdmin Models From 804ce1aea83d0a2460e5440e54fd0f38c17a7fce Mon Sep 17 00:00:00 2001 From: Nick Moreton Date: Wed, 31 Jan 2024 15:19:02 +0000 Subject: [PATCH 04/23] Remove some testing data --- wagtail_devtools/api/conf.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/wagtail_devtools/api/conf.py b/wagtail_devtools/api/conf.py index ea52878..685c983 100644 --- a/wagtail_devtools/api/conf.py +++ b/wagtail_devtools/api/conf.py @@ -108,16 +108,16 @@ "app_name": None, "listing_name": "wagtailusers_groups:index", }, - { - "title": "Example Calendar (admin view)", - "app_name": None, - "listing_name": "calendar", - }, - { - "title": "Example Calendar (admin view - month)", - "app_name": None, - "listing_name": "calendar-month", - }, + # { # need to be able to add these via setttings if required + # "title": "Example Calendar (admin view)", + # "app_name": None, + # "listing_name": "calendar", + # }, + # { + # "title": "Example Calendar (admin view - month)", + # "app_name": None, + # "listing_name": "calendar-month", + # }, ] From f3abac8e75ee83825030713b39db3f9072b2937b Mon Sep 17 00:00:00 2001 From: Nick Moreton Date: Wed, 31 Jan 2024 17:00:44 +0000 Subject: [PATCH 05/23] Correct test after update --- wagtail_devtools/api/conf.py | 10 ---------- wagtail_devtools/test/tests/test_conf.py | 10 ---------- wagtail_devtools/test/tests/test_views.py | 2 +- 3 files changed, 1 insertion(+), 21 deletions(-) diff --git a/wagtail_devtools/api/conf.py b/wagtail_devtools/api/conf.py index 685c983..5cb68fb 100644 --- a/wagtail_devtools/api/conf.py +++ b/wagtail_devtools/api/conf.py @@ -108,16 +108,6 @@ "app_name": None, "listing_name": "wagtailusers_groups:index", }, - # { # need to be able to add these via setttings if required - # "title": "Example Calendar (admin view)", - # "app_name": None, - # "listing_name": "calendar", - # }, - # { - # "title": "Example Calendar (admin view - month)", - # "app_name": None, - # "listing_name": "calendar-month", - # }, ] diff --git a/wagtail_devtools/test/tests/test_conf.py b/wagtail_devtools/test/tests/test_conf.py index 1534a4f..ea2e89f 100644 --- a/wagtail_devtools/test/tests/test_conf.py +++ b/wagtail_devtools/test/tests/test_conf.py @@ -123,16 +123,6 @@ def test_wagtail_core_listing_pages_config(self): "app_name": None, "listing_name": "wagtailusers_groups:index", }, - { - "title": "Example Calendar (admin view)", - "app_name": None, - "listing_name": "calendar", - }, - { - "title": "Example Calendar (admin view - month)", - "app_name": None, - "listing_name": "calendar-month", - }, ] self.assertEqual(conf["apps"], expected_apps) diff --git a/wagtail_devtools/test/tests/test_views.py b/wagtail_devtools/test/tests/test_views.py index 35d399e..b568415 100644 --- a/wagtail_devtools/test/tests/test_views.py +++ b/wagtail_devtools/test/tests/test_views.py @@ -41,7 +41,7 @@ def test_wagtail_core_listing_pages(self): response = wagtail_core_listing_pages(self.request) data = json.loads(response.content)["results"] - self.assertEqual(len(data), 23) + self.assertEqual(len(data), 21) self.assertEqual(data[0]["title"], "Search promotions") self.assertEqual(data[0]["app_name"], "wagtailsearchpromotions") self.assertEqual(data[0]["class_name"], None) From a3288116f8f39ab295a3fcaf5e1fcfdd312bb179 Mon Sep 17 00:00:00 2001 From: Nick Moreton Date: Sat, 3 Feb 2024 13:48:03 +0000 Subject: [PATCH 06/23] Deal with alternate sites for frontend urls --- wagtail_devtools/api/dataclasses.py | 13 ++++++++ wagtail_devtools/api/serializers.py | 49 ++++++++++++++++------------- 2 files changed, 40 insertions(+), 22 deletions(-) diff --git a/wagtail_devtools/api/dataclasses.py b/wagtail_devtools/api/dataclasses.py index 1e56060..3e65324 100644 --- a/wagtail_devtools/api/dataclasses.py +++ b/wagtail_devtools/api/dataclasses.py @@ -1,7 +1,9 @@ from dataclasses import dataclass, field +from urllib.parse import urlparse from django.conf import settings from django.urls import reverse +from wagtail.models import Site from wagtail_devtools.api.helpers import get_admin_edit_url, get_host @@ -39,6 +41,17 @@ def _editor_url(self): @property def _url(self): + current_site = Site.find_for_request(self.request) + hostname = current_site.hostname if current_site else None + + url_parsed = urlparse( + self.item.get_url() if hasattr(self.item, "get_url") else None + ) + netloc = url_parsed.netloc if url_parsed else None + + if netloc and not netloc.startswith(hostname): + return None + return self.item.get_url() if hasattr(self.item, "get_url") else None def get(self): diff --git a/wagtail_devtools/api/serializers.py b/wagtail_devtools/api/serializers.py index 1db2f30..0c8121e 100644 --- a/wagtail_devtools/api/serializers.py +++ b/wagtail_devtools/api/serializers.py @@ -1,6 +1,5 @@ from django.apps import apps from wagtail.admin.admin_url_finder import AdminURLFinder -from wagtail.models.collections import Collection from wagtail.snippets.models import get_snippet_models from wagtail_devtools.api.dataclasses import ( @@ -30,31 +29,37 @@ def wagtail_core_apps_serializer(request, config, title, all=False): results = Results() + """ + Loop though all the apps and models and get the first item for each model + But pages need to be children of the current site + """ + for app in config["apps"]: models = apps.get_app_config(app["app_name"]).get_models() - if not all: - for model in models: - item = model.objects.first() - if isinstance(item, Collection): - item = Collection.objects.first().get_first_child() + for model in models: + is_collection = model.__name__ == "Collection" + is_snippet = model.__name__ in [ + model.__name__ for model in get_snippet_models() + ] + + if is_collection: + items = ( + # don't include the root collection + [model.objects.first().get_first_child()] + if not all + else model.objects.all().exclude(depth=1) + ) + + if is_snippet: + items = [model.objects.first()] if not all else model.objects.all() + + if not is_collection and not is_snippet: + # must be some other model that doesn't need special handling + items = [model.objects.first()] if not all else model.objects.all() + + for item in items: if AdminURLFinder().get_edit_url(item): results.add(ResultsModelItem(request, item).get()) - else: - for model in models: - items = model.objects.all() - if isinstance(items.first(), Collection): - for item in items: - # if AdminURLFinder().get_edit_url(item): # TODO decide if this is required, do some testing on real data - results.add(ResultsModelItem(request, item).get()) - snippet_models = [model.__name__ for model in get_snippet_models()] - if model.__name__ in snippet_models: - for item in items: - # if AdminURLFinder().get_edit_url(item): # TODO decide if this is required, do some testing on real data - results.add(ResultsModelItem(request, item).get()) - else: - for item in items: - if AdminURLFinder().get_edit_url(item): - results.add(ResultsModelItem(request, item).get()) ret["results"] = results.get() From bf5b2b2e2be47460082ce702e090ca6d12a2bf66 Mon Sep 17 00:00:00 2001 From: Nick Moreton Date: Sat, 3 Feb 2024 14:53:17 +0000 Subject: [PATCH 07/23] Outputs both sites as alternative API views --- wagtail_devtools/api/views.py | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/wagtail_devtools/api/views.py b/wagtail_devtools/api/views.py index f558056..a302e7d 100644 --- a/wagtail_devtools/api/views.py +++ b/wagtail_devtools/api/views.py @@ -1,26 +1,50 @@ from django.http import JsonResponse from django.urls import reverse +from wagtail.models import Site from wagtail_devtools.api.conf import ( get_wagtail_core_edit_pages_config, get_wagtail_core_listing_pages_config, ) -from wagtail_devtools.api.helpers import get_host + +# from wagtail_devtools.api.helpers import get_host from wagtail_devtools.api.serializers import ( wagtail_core_apps_serializer, wagtail_core_listing_pages_serializer, ) +def get_host(request=None): + if request: + return request.build_absolute_uri("/").rstrip("/") + return None + + def api_view(request): """API index view for wagtail-devtools.""" + sites = Site.objects.all() + params = "" + + if request.GET.get("all"): + params = "?all=1" ret = { "api-views": [ - f"{get_host(request)}{reverse('listing-types')}", - f"{get_host(request)}{reverse('wagtail-core-apps')}", - ] + f"{get_host(request)}{reverse('listing-types')}{params}", + f"{get_host(request)}{reverse('wagtail-core-apps')}{params}", + ], } + + for site in sites: + site_absolute_url = site.root_url + request_absolute_url = get_host(request) + print(site_absolute_url, request_absolute_url) + + if site_absolute_url != request_absolute_url: + ret["api-views"].append( + f"{site_absolute_url}{reverse('wagtail-core-apps')}{params}", + ) + return JsonResponse(ret, safe=False) From 45ab7f7eb8af8e442e476bc2e9d88d2c6a2de19b Mon Sep 17 00:00:00 2001 From: Nick Moreton Date: Sat, 3 Feb 2024 17:21:59 +0000 Subject: [PATCH 08/23] Add playwright to the dev dependencies --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index d612d3e..6f4f680 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,6 +48,7 @@ dev = [ "flake8==6.1.0", "isort==5.12.0", "coverage==7.3.2", + "playwright==1.41.1", ] [project.urls] From 3984c6d53c85d530ac829ccb963b1ab59bca5ccb Mon Sep 17 00:00:00 2001 From: Nick Moreton Date: Sun, 4 Feb 2024 15:16:56 +0000 Subject: [PATCH 09/23] Initial playwright command --- .../commands/cmd_playwright_responses.py | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 wagtail_devtools/test/management/commands/cmd_playwright_responses.py diff --git a/wagtail_devtools/test/management/commands/cmd_playwright_responses.py b/wagtail_devtools/test/management/commands/cmd_playwright_responses.py new file mode 100644 index 0000000..791dd6a --- /dev/null +++ b/wagtail_devtools/test/management/commands/cmd_playwright_responses.py @@ -0,0 +1,129 @@ +import os + +from django.apps import apps +from django.conf import settings +from django.contrib.auth import get_user_model +from django.core.management.base import BaseCommand +from django.urls import reverse +from playwright.sync_api import sync_playwright +from wagtail.admin.admin_url_finder import AdminURLFinder +from wagtail.models import Collection +from wagtail.snippets.models import get_snippet_models + +from wagtail_devtools.api.conf import get_listing_pages_config + + +User = get_user_model() + + +class PlaywrightContext: + def __init__(self, live_server_url): + self.live_server_url = live_server_url + + def __enter__(self, *args, **kwargs): + os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true" + self.playwright = sync_playwright().start() + self.browser = self.playwright.chromium.launch( + headless=False, + slow_mo=300, + ) + self.context = self.browser.new_context() + self.page = self.context.new_page() + + username = "superuser" + password = "superuser" + + self.page.goto(f"{self.live_server_url}/admin/login/") + self.page.get_by_placeholder("Enter your username").fill(username) + self.page.get_by_placeholder("Enter password").fill(password) + self.page.get_by_role("button", name="Sign in").click() + return self.page + + def __exit__(self, *args, **kwargs): + self.page.close() + self.context.close() + self.browser.close() + self.playwright.stop() + + +class Command(BaseCommand): + help = "Test all the pages in wagtail admin" + + def add_arguments(self, parser): + parser.add_argument( + "--url", type=str, help="The url to test", default="http://localhost:8000" + ) + parser.add_argument( + "--all", + action="store_true", + help="Test all the pages in wagtail admin", + ) + + def handle(self, *args, **options): + url = options["url"] or "http://localhost:8000" + + with PlaywrightContext(url) as listing_pages: + listing_config = get_listing_pages_config() + for app in listing_config: + list_url = f"{url}{reverse(app['listing_name'])}" + response = listing_pages.goto(list_url) + if response.status == 200: + print(f"Page {list_url} is working") + else: + print(f"Page {list_url} is not working {response.status}") + + configuration = { + "title": "Wagtail core edit pages", + "apps": [], + } + + results = [] + + for a in apps.get_app_configs(): + if hasattr(settings, "DEVTOOLS_APPS_EXCLUDE"): + if a.name in settings.DEVTOOLS_APPS_EXCLUDE: + continue + configuration["apps"].append( + { + "app_name": a.label, + "models": [apps.get_model(a.label, m).__name__ for m in a.models], + } + ) + + for app in configuration["apps"]: + models = apps.get_app_config(app["app_name"]).get_models() + if not options["all"]: + for model in models: + item = model.objects.first() + if isinstance(item, Collection): + item = Collection.objects.first().get_first_child() + if AdminURLFinder().get_edit_url(item): + results.append(f"{url}{AdminURLFinder().get_edit_url(item)}") + else: + for model in models: + items = model.objects.all() + if isinstance(items.first(), Collection): + for item in items: + results.append( + f"{url}{AdminURLFinder().get_edit_url(item)}" + ) + snippet_models = [model.__name__ for model in get_snippet_models()] + if model.__name__ in snippet_models: + for item in items: + results.append( + f"{url}{AdminURLFinder().get_edit_url(item)}" + ) + else: + for item in items: + if AdminURLFinder().get_edit_url(item): + results.append( + f"{url}{AdminURLFinder().get_edit_url(item)}" + ) + + with PlaywrightContext(url) as page: + for result in results: + response = page.goto(result) + if response.status == 200: + print(f"Page {result} is working") + else: + print(f"Page {result} is not working {response.status}") From a429e187d68f564704a379d9c74122d275866b5c Mon Sep 17 00:00:00 2001 From: Nick Moreton Date: Sun, 4 Feb 2024 15:58:01 +0000 Subject: [PATCH 10/23] Optimize the `cmd_playwright_responses` command a little --- .../commands/cmd_playwright_responses.py | 56 ++++++++++--------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/wagtail_devtools/test/management/commands/cmd_playwright_responses.py b/wagtail_devtools/test/management/commands/cmd_playwright_responses.py index 791dd6a..87db126 100644 --- a/wagtail_devtools/test/management/commands/cmd_playwright_responses.py +++ b/wagtail_devtools/test/management/commands/cmd_playwright_responses.py @@ -7,7 +7,6 @@ from django.urls import reverse from playwright.sync_api import sync_playwright from wagtail.admin.admin_url_finder import AdminURLFinder -from wagtail.models import Collection from wagtail.snippets.models import get_snippet_models from wagtail_devtools.api.conf import get_listing_pages_config @@ -92,33 +91,38 @@ def handle(self, *args, **options): for app in configuration["apps"]: models = apps.get_app_config(app["app_name"]).get_models() - if not options["all"]: - for model in models: - item = model.objects.first() - if isinstance(item, Collection): - item = Collection.objects.first().get_first_child() + for model in models: + is_collection = model.__name__ == "Collection" + is_snippet = model.__name__ in [ + model.__name__ for model in get_snippet_models() + ] + + if is_collection: + items = ( + # don't include the root collection + [model.objects.first().get_first_child()] + if not options["all"] + else model.objects.all().exclude(depth=1) + ) + + if is_snippet: + items = ( + [model.objects.first()] + if not options["all"] + else model.objects.all() + ) + + if not is_collection and not is_snippet: + # must be some other model that doesn't need special handling + items = ( + [model.objects.first()] + if not options["all"] + else model.objects.all() + ) + + for item in items: if AdminURLFinder().get_edit_url(item): results.append(f"{url}{AdminURLFinder().get_edit_url(item)}") - else: - for model in models: - items = model.objects.all() - if isinstance(items.first(), Collection): - for item in items: - results.append( - f"{url}{AdminURLFinder().get_edit_url(item)}" - ) - snippet_models = [model.__name__ for model in get_snippet_models()] - if model.__name__ in snippet_models: - for item in items: - results.append( - f"{url}{AdminURLFinder().get_edit_url(item)}" - ) - else: - for item in items: - if AdminURLFinder().get_edit_url(item): - results.append( - f"{url}{AdminURLFinder().get_edit_url(item)}" - ) with PlaywrightContext(url) as page: for result in results: From 18e72adac19ef8fb4787270a9bf561a821a2bb4b Mon Sep 17 00:00:00 2001 From: Nick Moreton Date: Sun, 4 Feb 2024 16:42:09 +0000 Subject: [PATCH 11/23] Add more command options to the playwright test command --- .../commands/cmd_playwright_responses.py | 56 ++++++++++++++++--- 1 file changed, 47 insertions(+), 9 deletions(-) diff --git a/wagtail_devtools/test/management/commands/cmd_playwright_responses.py b/wagtail_devtools/test/management/commands/cmd_playwright_responses.py index 87db126..5a8e198 100644 --- a/wagtail_devtools/test/management/commands/cmd_playwright_responses.py +++ b/wagtail_devtools/test/management/commands/cmd_playwright_responses.py @@ -16,17 +16,32 @@ class PlaywrightContext: - def __init__(self, live_server_url): + def __init__(self, live_server_url, slow_mo, browser=False, viewport=None): self.live_server_url = live_server_url + self.slow_mo = slow_mo + self.viewport = viewport + self.headless = self.set_headless(browser) + self.viewport = self.set_viewport(viewport) + + def set_viewport(self, viewport): + if not viewport: + return {"width": 1024, "height": 768} + width, height = viewport.split("x") + return {"width": int(width), "height": int(height)} + + def set_headless(self, browser): + return not browser def __enter__(self, *args, **kwargs): os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true" self.playwright = sync_playwright().start() self.browser = self.playwright.chromium.launch( - headless=False, - slow_mo=300, + headless=self.headless, + slow_mo=self.slow_mo, + ) + self.context = self.browser.new_context( + viewport=self.viewport if self.viewport else None, ) - self.context = self.browser.new_context() self.page = self.context.new_page() username = "superuser" @@ -46,22 +61,43 @@ def __exit__(self, *args, **kwargs): class Command(BaseCommand): - help = "Test all the pages in wagtail admin" + help = "Runs playwright tests on the wagtail admin and frontend pages." def add_arguments(self, parser): parser.add_argument( - "--url", type=str, help="The url to test", default="http://localhost:8000" + "--url", + type=str, + help="The url to test (default=http://localhost:8000)", + default="http://localhost:8000", ) parser.add_argument( "--all", action="store_true", - help="Test all the pages in wagtail admin", + help="Test all the pages in wagtail admin and frontend (slow)", + ) + parser.add_argument( + "--slow", + type=int, + help="Slow down the test by the given amount of milliseconds", + default=0, + ) + parser.add_argument( + "--browser", + action="store_true", + help="Show the browser window", + ) + parser.add_argument( + "--viewport", + type=str, + help="Set the viewport size (e.g. 1024x768)", ) def handle(self, *args, **options): url = options["url"] or "http://localhost:8000" - with PlaywrightContext(url) as listing_pages: + with PlaywrightContext( + url, options["slow"], options["browser"], options["viewport"] + ) as listing_pages: listing_config = get_listing_pages_config() for app in listing_config: list_url = f"{url}{reverse(app['listing_name'])}" @@ -124,7 +160,9 @@ def handle(self, *args, **options): if AdminURLFinder().get_edit_url(item): results.append(f"{url}{AdminURLFinder().get_edit_url(item)}") - with PlaywrightContext(url) as page: + with PlaywrightContext( + url, options["slow"], options["browser"], options["viewport"] + ) as page: for result in results: response = page.goto(result) if response.status == 200: From e8e51094a7542ecca933a9e69b04f4af4257e6f4 Mon Sep 17 00:00:00 2001 From: Nick Moreton Date: Sun, 4 Feb 2024 16:51:20 +0000 Subject: [PATCH 12/23] Better console output for playwright responses --- .../commands/cmd_playwright_responses.py | 134 +++++++++--------- 1 file changed, 68 insertions(+), 66 deletions(-) diff --git a/wagtail_devtools/test/management/commands/cmd_playwright_responses.py b/wagtail_devtools/test/management/commands/cmd_playwright_responses.py index 5a8e198..f54a15f 100644 --- a/wagtail_devtools/test/management/commands/cmd_playwright_responses.py +++ b/wagtail_devtools/test/management/commands/cmd_playwright_responses.py @@ -94,78 +94,80 @@ def add_arguments(self, parser): def handle(self, *args, **options): url = options["url"] or "http://localhost:8000" + playwright_urls = [] # list of tuples (url, name) with PlaywrightContext( url, options["slow"], options["browser"], options["viewport"] - ) as listing_pages: + ) as page: listing_config = get_listing_pages_config() for app in listing_config: list_url = f"{url}{reverse(app['listing_name'])}" - response = listing_pages.goto(list_url) + playwright_urls.append((list_url, app["title"])) + + configuration = { + "title": "Wagtail core edit pages", + "apps": [], + } + + for a in apps.get_app_configs(): + if hasattr(settings, "DEVTOOLS_APPS_EXCLUDE"): + if a.name in settings.DEVTOOLS_APPS_EXCLUDE: + continue + configuration["apps"].append( + { + "app_name": a.label, + "models": [ + apps.get_model(a.label, m).__name__ for m in a.models + ], + } + ) + + for app in configuration["apps"]: + models = apps.get_app_config(app["app_name"]).get_models() + for model in models: + is_collection = model.__name__ == "Collection" + is_snippet = model.__name__ in [ + model.__name__ for model in get_snippet_models() + ] + + if is_collection: + items = ( + # don't include the root collection + [model.objects.first().get_first_child()] + if not options["all"] + else model.objects.all().exclude(depth=1) + ) + + if is_snippet: + items = ( + [model.objects.first()] + if not options["all"] + else model.objects.all() + ) + + if not is_collection and not is_snippet: + # must be some other model that doesn't need special handling + items = ( + [model.objects.first()] + if not options["all"] + else model.objects.all() + ) + + for item in items: + if AdminURLFinder().get_edit_url(item): + playwright_urls.append( + (f"{url}{AdminURLFinder().get_edit_url(item)}", item) + ) + + for url in playwright_urls: + response = page.goto(url[0]) if response.status == 200: - print(f"Page {list_url} is working") - else: - print(f"Page {list_url} is not working {response.status}") - - configuration = { - "title": "Wagtail core edit pages", - "apps": [], - } - - results = [] - - for a in apps.get_app_configs(): - if hasattr(settings, "DEVTOOLS_APPS_EXCLUDE"): - if a.name in settings.DEVTOOLS_APPS_EXCLUDE: - continue - configuration["apps"].append( - { - "app_name": a.label, - "models": [apps.get_model(a.label, m).__name__ for m in a.models], - } - ) - - for app in configuration["apps"]: - models = apps.get_app_config(app["app_name"]).get_models() - for model in models: - is_collection = model.__name__ == "Collection" - is_snippet = model.__name__ in [ - model.__name__ for model in get_snippet_models() - ] - - if is_collection: - items = ( - # don't include the root collection - [model.objects.first().get_first_child()] - if not options["all"] - else model.objects.all().exclude(depth=1) - ) - - if is_snippet: - items = ( - [model.objects.first()] - if not options["all"] - else model.objects.all() + self.stdout.write( + self.style.SUCCESS(f"{url[1]} {url[0]} is working") ) - - if not is_collection and not is_snippet: - # must be some other model that doesn't need special handling - items = ( - [model.objects.first()] - if not options["all"] - else model.objects.all() - ) - - for item in items: - if AdminURLFinder().get_edit_url(item): - results.append(f"{url}{AdminURLFinder().get_edit_url(item)}") - - with PlaywrightContext( - url, options["slow"], options["browser"], options["viewport"] - ) as page: - for result in results: - response = page.goto(result) - if response.status == 200: - print(f"Page {result} is working") else: - print(f"Page {result} is not working {response.status}") + self.stdout.write( + self.style.ERROR( + f"{url[1]} {url[0]} is not working {response.status}" + ) + ) From 72efe52f7b8936eb37ff4f8bbae1a36d1be71efd Mon Sep 17 00:00:00 2001 From: Nick Moreton Date: Sun, 4 Feb 2024 16:57:19 +0000 Subject: [PATCH 13/23] Output a summary_report --- .../commands/cmd_playwright_responses.py | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/wagtail_devtools/test/management/commands/cmd_playwright_responses.py b/wagtail_devtools/test/management/commands/cmd_playwright_responses.py index f54a15f..c63ff92 100644 --- a/wagtail_devtools/test/management/commands/cmd_playwright_responses.py +++ b/wagtail_devtools/test/management/commands/cmd_playwright_responses.py @@ -94,7 +94,13 @@ def add_arguments(self, parser): def handle(self, *args, **options): url = options["url"] or "http://localhost:8000" - playwright_urls = [] # list of tuples (url, name) + playwright_urls = [] # list of tuples (url, name, response status code) + summary_report = { + "200": [], + "302": [], + "404": [], + "500": [], + } with PlaywrightContext( url, options["slow"], options["browser"], options["viewport"] @@ -161,6 +167,14 @@ def handle(self, *args, **options): for url in playwright_urls: response = page.goto(url[0]) + if response.status == 200: + summary_report["200"].append(url) + elif response.status == 302: + summary_report["302"].append(url) + elif response.status == 404: + summary_report["404"].append(url) + elif response.status == 500: + summary_report["500"].append(url) if response.status == 200: self.stdout.write( self.style.SUCCESS(f"{url[1]} {url[0]} is working") @@ -171,3 +185,9 @@ def handle(self, *args, **options): f"{url[1]} {url[0]} is not working {response.status}" ) ) + + self.stdout.write(self.style.SUCCESS("Summary report:")) + self.stdout.write(self.style.SUCCESS(f"200: {len(summary_report['200'])}")) + self.stdout.write(self.style.WARNING(f"302: {len(summary_report['302'])}")) + self.stdout.write(self.style.WARNING(f"404: {len(summary_report['404'])}")) + self.stdout.write(self.style.ERROR(f"500: {len(summary_report['500'])}")) From 3f2a2f2cab4cd0364c0f8c33ebc55258a1ad0eaf Mon Sep 17 00:00:00 2001 From: Nick Moreton Date: Sun, 4 Feb 2024 17:33:11 +0000 Subject: [PATCH 14/23] Add better reporting for playwright responses --- .../commands/cmd_playwright_responses.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/wagtail_devtools/test/management/commands/cmd_playwright_responses.py b/wagtail_devtools/test/management/commands/cmd_playwright_responses.py index c63ff92..d7da7ef 100644 --- a/wagtail_devtools/test/management/commands/cmd_playwright_responses.py +++ b/wagtail_devtools/test/management/commands/cmd_playwright_responses.py @@ -164,6 +164,8 @@ def handle(self, *args, **options): playwright_urls.append( (f"{url}{AdminURLFinder().get_edit_url(item)}", item) ) + if hasattr(item, "get_url") and item.get_url(): + playwright_urls.append((item.get_url(), item)) for url in playwright_urls: response = page.goto(url[0]) @@ -176,15 +178,23 @@ def handle(self, *args, **options): elif response.status == 500: summary_report["500"].append(url) if response.status == 200: + self.stdout.write(self.style.SUCCESS(f"{url[1]} {url[0]} OK")) + elif response.status == 302: self.stdout.write( - self.style.SUCCESS(f"{url[1]} {url[0]} is working") + self.style.WARNING( + f"{url[1]} {url[0]} redirect {response.status}" + ) ) - else: + elif response.status == 404: self.stdout.write( self.style.ERROR( - f"{url[1]} {url[0]} is not working {response.status}" + f"{url[1]} {url[0]} NOT FOUND possibly has a draft version {response.status}" ) ) + elif response.status == 500: + self.stdout.write( + self.style.ERROR(f"{url[1]} {url[0]} ERROR {response.status}") + ) self.stdout.write(self.style.SUCCESS("Summary report:")) self.stdout.write(self.style.SUCCESS(f"200: {len(summary_report['200'])}")) From 7f90b92c2c3e36f78ffca49b58e52e01f620a666 Mon Sep 17 00:00:00 2001 From: Nick Moreton Date: Sun, 4 Feb 2024 17:33:43 +0000 Subject: [PATCH 15/23] Remove un-needed print statement --- wagtail_devtools/api/views.py | 1 - 1 file changed, 1 deletion(-) diff --git a/wagtail_devtools/api/views.py b/wagtail_devtools/api/views.py index a302e7d..307998c 100644 --- a/wagtail_devtools/api/views.py +++ b/wagtail_devtools/api/views.py @@ -38,7 +38,6 @@ def api_view(request): for site in sites: site_absolute_url = site.root_url request_absolute_url = get_host(request) - print(site_absolute_url, request_absolute_url) if site_absolute_url != request_absolute_url: ret["api-views"].append( From 867c4f9bdb1f5052caf8c4aacf324d2bcb2a1fb5 Mon Sep 17 00:00:00 2001 From: Nick Moreton Date: Sun, 4 Feb 2024 17:34:21 +0000 Subject: [PATCH 16/23] Fix failing tests --- wagtail_devtools/test/tests/test_serializers.py | 12 ++++++------ wagtail_devtools/test/tests/test_views.py | 9 +++++---- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/wagtail_devtools/test/tests/test_serializers.py b/wagtail_devtools/test/tests/test_serializers.py index 0ae7ad0..ae1de72 100644 --- a/wagtail_devtools/test/tests/test_serializers.py +++ b/wagtail_devtools/test/tests/test_serializers.py @@ -90,11 +90,11 @@ def test_wagtail_core_apps_serializer_with_all(self): config = get_wagtail_core_edit_pages_config() request = RequestFactory().get("/") ret = wagtail_core_apps_serializer(request, config, "title", all=True) - self.assertEqual(len(ret["results"]), 8) + self.assertEqual(len(ret["results"]), 7) def test_wagtail_core_apps_serializer_with_collection(self): # create a collection - root_collection = Collection.objects.get(depth=1) + root_collection = Collection.objects.first() root_collection.add_child(name="Test Collection") collection = Collection.objects.get(name="Test Collection") collection_edit_url = get_admin_edit_url("http://localhost:8000", collection) @@ -109,10 +109,10 @@ def test_wagtail_core_apps_serializer_with_collection(self): request = RequestFactory().get("/") ret = wagtail_core_apps_serializer(request, config, "title", all=True) - self.assertEqual(ret["results"][4]["title"], "Test Collection") - self.assertEqual(ret["results"][4]["app_name"], "wagtailcore") - self.assertEqual(ret["results"][4]["class_name"], "Collection") - self.assertEqual(ret["results"][4]["editor_url"], collection_edit_url) + self.assertEqual(ret["results"][3]["title"], "Test Collection") + self.assertEqual(ret["results"][3]["app_name"], "wagtailcore") + self.assertEqual(ret["results"][3]["class_name"], "Collection") + self.assertEqual(ret["results"][3]["editor_url"], collection_edit_url) self.assertEqual(ret["results"][1]["title"], "test snippet") self.assertEqual(ret["results"][1]["app_name"], "wagtail_devtools_test") diff --git a/wagtail_devtools/test/tests/test_views.py b/wagtail_devtools/test/tests/test_views.py index b568415..7fa36c2 100644 --- a/wagtail_devtools/test/tests/test_views.py +++ b/wagtail_devtools/test/tests/test_views.py @@ -27,14 +27,15 @@ def setUp(self): def test_api_view(self): response = api_view(self.request) data = json.loads(response.content)["api-views"] - self.assertEqual(len(data), 2) + self.assertEqual(len(data), 4) + # TODO: Investigate why the testserver is returning the testserver url self.assertEqual( data[0], - "http://localhost:8000/wagtail-devtools-api/listing-types/", + "http://testserver/wagtail-devtools-api/listing-types/", ) self.assertEqual( data[1], - "http://localhost:8000/wagtail-devtools-api/wagtail-core-apps/", + "http://testserver/wagtail-devtools-api/wagtail-core-apps/", ) def test_wagtail_core_listing_pages(self): @@ -75,7 +76,7 @@ def test_wagtail_core_apps_all(self): response = wagtail_core_apps(self.request) data = json.loads(response.content)["results"] - self.assertEqual(len(data), 73) + self.assertEqual(len(data), 72) self.assertEqual(data[0]["title"], "Home Page") self.assertEqual(data[0]["app_name"], "wagtail_devtools_test") self.assertEqual(data[0]["class_name"], "HomePage") From 311546514d9dcaa94813653685af2f00073f4c0f Mon Sep 17 00:00:00 2001 From: Nick Moreton Date: Sun, 4 Feb 2024 17:40:22 +0000 Subject: [PATCH 17/23] update the check_responses command --- .../management/commands/admin_responses.py | 114 ------------------ .../commands/check_responses.py} | 0 2 files changed, 114 deletions(-) delete mode 100644 wagtail_devtools/management/commands/admin_responses.py rename wagtail_devtools/{test/management/commands/cmd_playwright_responses.py => management/commands/check_responses.py} (100%) diff --git a/wagtail_devtools/management/commands/admin_responses.py b/wagtail_devtools/management/commands/admin_responses.py deleted file mode 100644 index d6ac78f..0000000 --- a/wagtail_devtools/management/commands/admin_responses.py +++ /dev/null @@ -1,114 +0,0 @@ -from django.core.management.base import BaseCommand - -from wagtail_devtools.auth import LoginHandler -from wagtail_devtools.reporting import Report - - -class Command(BaseCommand): - help = "Access the devtools api and check admin and frontend urls." - - def add_arguments(self, parser): - """Add arguments.""" - parser.add_argument( - "--host", - action="store", - dest="host", - default="http://localhost:8000", - help="Host to check", - ) - parser.add_argument( - "--all", - action="store_true", - dest="all", - default=False, - help="Check all pages", - ) - parser.add_argument( - "--verbose", - action="store_true", - dest="verbose", - default=False, - help="Verbose output", - ) - - def handle(self, *args, **options): - # Listing page types - session = LoginHandler(options["host"]) - session.login("superuser", "superuser") - - if not session.is_authenticated(): - self.stdout.write(self.style.ERROR("Not authenticated")) - return - - index_endpoint = options["host"] + "/wagtail-devtools-api/" - - response = session.get_response(index_endpoint) - endpoints = response.json()["api-views"] - - results_500 = [] - results_404 = [] - results_302 = [] - results_200 = [] - - for endpoint in endpoints: - if options["all"]: - endpoint = endpoint + "?all=true" - response = session.get_response(endpoint) - results = response.json()["results"] - title = response.json()["meta"]["title"] - report = Report(title, results, session) - results_500.extend(report.get_errors_500()) - results_404.extend(report.get_errors_404()) - results_302.extend(report.get_errors_302()) - results_200.extend(report.get_success_200()) - - self.stdout.write(self.style.ERROR(f"500 errors: {len(results_500)}")) - self.stdout.write("=====================================") - if len(results_500) > 0: - for result in results_500: - self.stdout.write(self.style.HTTP_INFO(result.title)) - self.stdout.write( - self.style.HTTP_INFO(result.url) - ) if result.url else None - if result.editor_url: - self.stdout.write(self.style.HTTP_INFO(result.editor_url)) - self.stdout.write("\n") - - self.stdout.write(self.style.WARNING(f"404 errors: {len(results_404)}")) - self.stdout.write("=====================================") - if len(results_404) > 0: - for result in results_404: - self.stdout.write(self.style.HTTP_INFO(result.title)) - self.stdout.write( - self.style.HTTP_INFO(result.url) - ) if result.url else None - if result.editor_url: - self.stdout.write(self.style.HTTP_INFO(result.editor_url)) - self.stdout.write("\n") - - self.stdout.write(self.style.WARNING(f"302 errors: {len(results_302)}")) - self.stdout.write("=====================================") - if len(results_302) > 0: - for result in results_302: - self.stdout.write(self.style.HTTP_INFO(result.title)) - self.stdout.write( - self.style.HTTP_INFO(result.url) - ) if result.url else None - if result.editor_url: - self.stdout.write(self.style.HTTP_INFO(result.editor_url)) - self.stdout.write("\n") - - self.stdout.write(self.style.SUCCESS(f"200 success: {len(results_200)}")) - self.stdout.write("=====================================") - if options["verbose"]: - if len(results_200) > 0: - for result in results_200: - self.stdout.write(self.style.HTTP_INFO(result.title)) - self.stdout.write( - self.style.HTTP_INFO(result.url) - ) if result.url else None - if result.editor_url: - self.stdout.write(self.style.HTTP_INFO(result.editor_url)) - - self.stdout.write(self.style.SUCCESS(f"Total: {len(endpoints)}")) - self.stdout.write("=====================================") diff --git a/wagtail_devtools/test/management/commands/cmd_playwright_responses.py b/wagtail_devtools/management/commands/check_responses.py similarity index 100% rename from wagtail_devtools/test/management/commands/cmd_playwright_responses.py rename to wagtail_devtools/management/commands/check_responses.py From 02f8e8fbc14ec6b93bc9d9071d2453ec0b44f741 Mon Sep 17 00:00:00 2001 From: Nick Moreton Date: Sun, 4 Feb 2024 22:35:24 +0000 Subject: [PATCH 18/23] save 25-separate-frontend-and-backend-api-views.patch --- wagtail_devtools/api/urls.py | 10 +- wagtail_devtools/api/views.py | 127 +++++++-- .../management/commands/check_responses.py | 241 +++++------------- .../commands/check_responses_playwright.py | 203 +++++++++++++++ 4 files changed, 373 insertions(+), 208 deletions(-) create mode 100644 wagtail_devtools/management/commands/check_responses_playwright.py diff --git a/wagtail_devtools/api/urls.py b/wagtail_devtools/api/urls.py index 5a1a527..9dc702b 100644 --- a/wagtail_devtools/api/urls.py +++ b/wagtail_devtools/api/urls.py @@ -1,10 +1,12 @@ from django.urls import path -from .views import api_view, wagtail_core_apps, wagtail_core_listing_pages +from .views import ( # api_view,; wagtail_core_apps,; wagtail_core_listing_pages, + responses_api_view, +) urlpatterns = [ - path("", api_view, name="api_index"), - path("listing-types/", wagtail_core_listing_pages, name="listing-types"), - path("wagtail-core-apps/", wagtail_core_apps, name="wagtail-core-apps"), + path("", responses_api_view, name="responses_api_view"), + # path("listing-types/", wagtail_core_listing_pages, name="listing-types"), + # path("wagtail-core-apps/", wagtail_core_apps, name="wagtail-core-apps"), ] diff --git a/wagtail_devtools/api/views.py b/wagtail_devtools/api/views.py index 307998c..dcebde3 100644 --- a/wagtail_devtools/api/views.py +++ b/wagtail_devtools/api/views.py @@ -1,6 +1,11 @@ +from django.apps import apps +from django.conf import settings from django.http import JsonResponse from django.urls import reverse -from wagtail.models import Site +from wagtail.admin.admin_url_finder import AdminURLFinder + +# from wagtail.models import Site +from wagtail.snippets.models import get_snippet_models from wagtail_devtools.api.conf import ( get_wagtail_core_edit_pages_config, @@ -20,31 +25,31 @@ def get_host(request=None): return None -def api_view(request): - """API index view for wagtail-devtools.""" - sites = Site.objects.all() - params = "" +# def api_view(request): +# """API index view for wagtail-devtools.""" +# sites = Site.objects.all() +# params = "" - if request.GET.get("all"): - params = "?all=1" +# if request.GET.get("all"): +# params = "?all=1" - ret = { - "api-views": [ - f"{get_host(request)}{reverse('listing-types')}{params}", - f"{get_host(request)}{reverse('wagtail-core-apps')}{params}", - ], - } +# ret = { +# "api-views": [ +# f"{get_host(request)}{reverse('listing-types')}{params}", +# f"{get_host(request)}{reverse('wagtail-core-apps')}{params}", +# ], +# } - for site in sites: - site_absolute_url = site.root_url - request_absolute_url = get_host(request) +# for site in sites: +# site_absolute_url = site.root_url +# request_absolute_url = get_host(request) - if site_absolute_url != request_absolute_url: - ret["api-views"].append( - f"{site_absolute_url}{reverse('wagtail-core-apps')}{params}", - ) +# if site_absolute_url != request_absolute_url: +# ret["api-views"].append( +# f"{site_absolute_url}{reverse('wagtail-core-apps')}{params}", +# ) - return JsonResponse(ret, safe=False) +# return JsonResponse(ret, safe=False) def wagtail_core_listing_pages(request): @@ -75,3 +80,83 @@ def wagtail_core_apps(request): ), safe=False, ) + + +def responses_api_view(request): + if request.GET.get("url"): + url = request.GET.get("url") + else: + url = "http://localhost:8000" + + if request.GET.get("all"): + all = True + else: + all = False + + results = [] # list of tuples (url, name) + + for app in get_wagtail_core_listing_pages_config()["apps"]: + list_url = f"{url}{reverse(app['listing_name'])}" + results.append( + { + "name": f"{app['title']} ({app['listing_name']})", + "url": list_url, + } + ) + + configuration = { + "title": "Wagtail core edit pages", + "apps": [], + } + + for a in apps.get_app_configs(): + if hasattr(settings, "DEVTOOLS_APPS_EXCLUDE"): + if a.name in settings.DEVTOOLS_APPS_EXCLUDE: + continue + configuration["apps"].append( + { + "app_name": a.label, + "models": [apps.get_model(a.label, m).__name__ for m in a.models], + } + ) + + for app in configuration["apps"]: + models = apps.get_app_config(app["app_name"]).get_models() + for model in models: + is_collection = model.__name__ == "Collection" + is_snippet = model.__name__ in [ + model.__name__ for model in get_snippet_models() + ] + + if is_collection: + items = ( + # don't include the root collection + [model.objects.first().get_first_child()] + if not all + else model.objects.all().exclude(depth=1) + ) + + if is_snippet: + items = [model.objects.first()] if not all else model.objects.all() + + if not is_collection and not is_snippet: + # must be some other model that doesn't need special handling + items = [model.objects.first()] if not all else model.objects.all() + + for item in items: + if AdminURLFinder().get_edit_url(item): + results.append( + { + "name": f"{model.__name__} ({app['app_name']})", + "url": f"{url}{AdminURLFinder().get_edit_url(item)}", + } + ) + if hasattr(item, "get_url") and item.get_url(): + results.append( + { + "name": f"{model.__name__} ({app['app_name']})", + "url": item.get_url(), + } + ) + + return JsonResponse(results, safe=False) diff --git a/wagtail_devtools/management/commands/check_responses.py b/wagtail_devtools/management/commands/check_responses.py index d7da7ef..a157952 100644 --- a/wagtail_devtools/management/commands/check_responses.py +++ b/wagtail_devtools/management/commands/check_responses.py @@ -1,203 +1,78 @@ -import os - -from django.apps import apps -from django.conf import settings -from django.contrib.auth import get_user_model from django.core.management.base import BaseCommand -from django.urls import reverse -from playwright.sync_api import sync_playwright -from wagtail.admin.admin_url_finder import AdminURLFinder -from wagtail.snippets.models import get_snippet_models - -from wagtail_devtools.api.conf import get_listing_pages_config - - -User = get_user_model() - - -class PlaywrightContext: - def __init__(self, live_server_url, slow_mo, browser=False, viewport=None): - self.live_server_url = live_server_url - self.slow_mo = slow_mo - self.viewport = viewport - self.headless = self.set_headless(browser) - self.viewport = self.set_viewport(viewport) - - def set_viewport(self, viewport): - if not viewport: - return {"width": 1024, "height": 768} - width, height = viewport.split("x") - return {"width": int(width), "height": int(height)} - - def set_headless(self, browser): - return not browser - - def __enter__(self, *args, **kwargs): - os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true" - self.playwright = sync_playwright().start() - self.browser = self.playwright.chromium.launch( - headless=self.headless, - slow_mo=self.slow_mo, - ) - self.context = self.browser.new_context( - viewport=self.viewport if self.viewport else None, - ) - self.page = self.context.new_page() - - username = "superuser" - password = "superuser" - self.page.goto(f"{self.live_server_url}/admin/login/") - self.page.get_by_placeholder("Enter your username").fill(username) - self.page.get_by_placeholder("Enter password").fill(password) - self.page.get_by_role("button", name="Sign in").click() - return self.page - - def __exit__(self, *args, **kwargs): - self.page.close() - self.context.close() - self.browser.close() - self.playwright.stop() +from wagtail_devtools.auth import LoginHandler class Command(BaseCommand): - help = "Runs playwright tests on the wagtail admin and frontend pages." + help = "Check responses of API views." def add_arguments(self, parser): parser.add_argument( - "--url", + "--username", type=str, - help="The url to test (default=http://localhost:8000)", - default="http://localhost:8000", + help="Username to use for login.", + default="superuser", ) parser.add_argument( - "--all", - action="store_true", - help="Test all the pages in wagtail admin and frontend (slow)", - ) - parser.add_argument( - "--slow", - type=int, - help="Slow down the test by the given amount of milliseconds", - default=0, + "--password", + type=str, + help="Password to use for login.", + default="superuser", ) parser.add_argument( - "--browser", + "--all", action="store_true", - help="Show the browser window", + help="Check all API views (slow).", ) parser.add_argument( - "--viewport", + "--url", type=str, - help="Set the viewport size (e.g. 1024x768)", + help="The url to test (default=http://localhost:8000)", + default="http://localhost:8000", ) def handle(self, *args, **options): - url = options["url"] or "http://localhost:8000" - playwright_urls = [] # list of tuples (url, name, response status code) - summary_report = { - "200": [], - "302": [], - "404": [], - "500": [], - } - - with PlaywrightContext( - url, options["slow"], options["browser"], options["viewport"] - ) as page: - listing_config = get_listing_pages_config() - for app in listing_config: - list_url = f"{url}{reverse(app['listing_name'])}" - playwright_urls.append((list_url, app["title"])) - - configuration = { - "title": "Wagtail core edit pages", - "apps": [], - } - - for a in apps.get_app_configs(): - if hasattr(settings, "DEVTOOLS_APPS_EXCLUDE"): - if a.name in settings.DEVTOOLS_APPS_EXCLUDE: - continue - configuration["apps"].append( - { - "app_name": a.label, - "models": [ - apps.get_model(a.label, m).__name__ for m in a.models - ], - } - ) - - for app in configuration["apps"]: - models = apps.get_app_config(app["app_name"]).get_models() - for model in models: - is_collection = model.__name__ == "Collection" - is_snippet = model.__name__ in [ - model.__name__ for model in get_snippet_models() - ] - - if is_collection: - items = ( - # don't include the root collection - [model.objects.first().get_first_child()] - if not options["all"] - else model.objects.all().exclude(depth=1) - ) - - if is_snippet: - items = ( - [model.objects.first()] - if not options["all"] - else model.objects.all() - ) - - if not is_collection and not is_snippet: - # must be some other model that doesn't need special handling - items = ( - [model.objects.first()] - if not options["all"] - else model.objects.all() - ) - - for item in items: - if AdminURLFinder().get_edit_url(item): - playwright_urls.append( - (f"{url}{AdminURLFinder().get_edit_url(item)}", item) - ) - if hasattr(item, "get_url") and item.get_url(): - playwright_urls.append((item.get_url(), item)) - - for url in playwright_urls: - response = page.goto(url[0]) - if response.status == 200: - summary_report["200"].append(url) - elif response.status == 302: - summary_report["302"].append(url) - elif response.status == 404: - summary_report["404"].append(url) - elif response.status == 500: - summary_report["500"].append(url) - if response.status == 200: - self.stdout.write(self.style.SUCCESS(f"{url[1]} {url[0]} OK")) - elif response.status == 302: - self.stdout.write( - self.style.WARNING( - f"{url[1]} {url[0]} redirect {response.status}" - ) - ) - elif response.status == 404: - self.stdout.write( - self.style.ERROR( - f"{url[1]} {url[0]} NOT FOUND possibly has a draft version {response.status}" - ) - ) - elif response.status == 500: - self.stdout.write( - self.style.ERROR(f"{url[1]} {url[0]} ERROR {response.status}") - ) - - self.stdout.write(self.style.SUCCESS("Summary report:")) - self.stdout.write(self.style.SUCCESS(f"200: {len(summary_report['200'])}")) - self.stdout.write(self.style.WARNING(f"302: {len(summary_report['302'])}")) - self.stdout.write(self.style.WARNING(f"404: {len(summary_report['404'])}")) - self.stdout.write(self.style.ERROR(f"500: {len(summary_report['500'])}")) + login_handler = LoginHandler(options["url"]) + login_handler.login(options["username"], options["password"]) + if not options["all"]: + response = login_handler.get_response( + f"{options['url']}/wagtail-devtools-api/" + ) + else: + response = login_handler.get_response( + f"{options['url']}/wagtail-devtools-api/?all=true" + ) + + if not response.status_code == 200: + raise Exception("API view not found") + + resp_200 = [] + resp_404 = [] + resp_500 = [] + resp_302 = [] + + for item in response.json(): + response = login_handler.get_response(item["url"]) + if response.status_code == 200: + self.stdout.write(self.style.SUCCESS(f"{item['name']} - {item['url']}")) + resp_200.append(item["url"]) + elif response.status_code == 404: + self.stdout.write(self.style.WARNING(f"{item['name']} - {item['url']}")) + resp_404.append(item["url"]) + elif response.status_code == 500: + self.stdout.write(self.style.ERROR(f"{item['name']} - {item['url']}")) + resp_500.append(item["url"]) + elif response.status_code == 302: + self.stdout.write(self.style.WARNING(f"{item['name']} - {item['url']}")) + resp_302.append(item["url"]) + + # print("200 responses:") + # print(resp_200) + # print("404 responses:") + # print(resp_404) + # print("500 responses:") + # print(resp_500) + # print("302 responses:") + # print(resp_302) + login_handler.logout() + print("Done") diff --git a/wagtail_devtools/management/commands/check_responses_playwright.py b/wagtail_devtools/management/commands/check_responses_playwright.py new file mode 100644 index 0000000..d7da7ef --- /dev/null +++ b/wagtail_devtools/management/commands/check_responses_playwright.py @@ -0,0 +1,203 @@ +import os + +from django.apps import apps +from django.conf import settings +from django.contrib.auth import get_user_model +from django.core.management.base import BaseCommand +from django.urls import reverse +from playwright.sync_api import sync_playwright +from wagtail.admin.admin_url_finder import AdminURLFinder +from wagtail.snippets.models import get_snippet_models + +from wagtail_devtools.api.conf import get_listing_pages_config + + +User = get_user_model() + + +class PlaywrightContext: + def __init__(self, live_server_url, slow_mo, browser=False, viewport=None): + self.live_server_url = live_server_url + self.slow_mo = slow_mo + self.viewport = viewport + self.headless = self.set_headless(browser) + self.viewport = self.set_viewport(viewport) + + def set_viewport(self, viewport): + if not viewport: + return {"width": 1024, "height": 768} + width, height = viewport.split("x") + return {"width": int(width), "height": int(height)} + + def set_headless(self, browser): + return not browser + + def __enter__(self, *args, **kwargs): + os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true" + self.playwright = sync_playwright().start() + self.browser = self.playwright.chromium.launch( + headless=self.headless, + slow_mo=self.slow_mo, + ) + self.context = self.browser.new_context( + viewport=self.viewport if self.viewport else None, + ) + self.page = self.context.new_page() + + username = "superuser" + password = "superuser" + + self.page.goto(f"{self.live_server_url}/admin/login/") + self.page.get_by_placeholder("Enter your username").fill(username) + self.page.get_by_placeholder("Enter password").fill(password) + self.page.get_by_role("button", name="Sign in").click() + return self.page + + def __exit__(self, *args, **kwargs): + self.page.close() + self.context.close() + self.browser.close() + self.playwright.stop() + + +class Command(BaseCommand): + help = "Runs playwright tests on the wagtail admin and frontend pages." + + def add_arguments(self, parser): + parser.add_argument( + "--url", + type=str, + help="The url to test (default=http://localhost:8000)", + default="http://localhost:8000", + ) + parser.add_argument( + "--all", + action="store_true", + help="Test all the pages in wagtail admin and frontend (slow)", + ) + parser.add_argument( + "--slow", + type=int, + help="Slow down the test by the given amount of milliseconds", + default=0, + ) + parser.add_argument( + "--browser", + action="store_true", + help="Show the browser window", + ) + parser.add_argument( + "--viewport", + type=str, + help="Set the viewport size (e.g. 1024x768)", + ) + + def handle(self, *args, **options): + url = options["url"] or "http://localhost:8000" + playwright_urls = [] # list of tuples (url, name, response status code) + summary_report = { + "200": [], + "302": [], + "404": [], + "500": [], + } + + with PlaywrightContext( + url, options["slow"], options["browser"], options["viewport"] + ) as page: + listing_config = get_listing_pages_config() + for app in listing_config: + list_url = f"{url}{reverse(app['listing_name'])}" + playwright_urls.append((list_url, app["title"])) + + configuration = { + "title": "Wagtail core edit pages", + "apps": [], + } + + for a in apps.get_app_configs(): + if hasattr(settings, "DEVTOOLS_APPS_EXCLUDE"): + if a.name in settings.DEVTOOLS_APPS_EXCLUDE: + continue + configuration["apps"].append( + { + "app_name": a.label, + "models": [ + apps.get_model(a.label, m).__name__ for m in a.models + ], + } + ) + + for app in configuration["apps"]: + models = apps.get_app_config(app["app_name"]).get_models() + for model in models: + is_collection = model.__name__ == "Collection" + is_snippet = model.__name__ in [ + model.__name__ for model in get_snippet_models() + ] + + if is_collection: + items = ( + # don't include the root collection + [model.objects.first().get_first_child()] + if not options["all"] + else model.objects.all().exclude(depth=1) + ) + + if is_snippet: + items = ( + [model.objects.first()] + if not options["all"] + else model.objects.all() + ) + + if not is_collection and not is_snippet: + # must be some other model that doesn't need special handling + items = ( + [model.objects.first()] + if not options["all"] + else model.objects.all() + ) + + for item in items: + if AdminURLFinder().get_edit_url(item): + playwright_urls.append( + (f"{url}{AdminURLFinder().get_edit_url(item)}", item) + ) + if hasattr(item, "get_url") and item.get_url(): + playwright_urls.append((item.get_url(), item)) + + for url in playwright_urls: + response = page.goto(url[0]) + if response.status == 200: + summary_report["200"].append(url) + elif response.status == 302: + summary_report["302"].append(url) + elif response.status == 404: + summary_report["404"].append(url) + elif response.status == 500: + summary_report["500"].append(url) + if response.status == 200: + self.stdout.write(self.style.SUCCESS(f"{url[1]} {url[0]} OK")) + elif response.status == 302: + self.stdout.write( + self.style.WARNING( + f"{url[1]} {url[0]} redirect {response.status}" + ) + ) + elif response.status == 404: + self.stdout.write( + self.style.ERROR( + f"{url[1]} {url[0]} NOT FOUND possibly has a draft version {response.status}" + ) + ) + elif response.status == 500: + self.stdout.write( + self.style.ERROR(f"{url[1]} {url[0]} ERROR {response.status}") + ) + + self.stdout.write(self.style.SUCCESS("Summary report:")) + self.stdout.write(self.style.SUCCESS(f"200: {len(summary_report['200'])}")) + self.stdout.write(self.style.WARNING(f"302: {len(summary_report['302'])}")) + self.stdout.write(self.style.WARNING(f"404: {len(summary_report['404'])}")) + self.stdout.write(self.style.ERROR(f"500: {len(summary_report['500'])}")) From 790dcd76de44465841e7d0976457c4628d48db0b Mon Sep 17 00:00:00 2001 From: Nick Moreton Date: Mon, 5 Feb 2024 09:00:48 +0000 Subject: [PATCH 19/23] Sort by groups for the API view --- wagtail_devtools/api/views.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/wagtail_devtools/api/views.py b/wagtail_devtools/api/views.py index dcebde3..22688f9 100644 --- a/wagtail_devtools/api/views.py +++ b/wagtail_devtools/api/views.py @@ -99,6 +99,7 @@ def responses_api_view(request): list_url = f"{url}{reverse(app['listing_name'])}" results.append( { + "group": "CoreListingPage", "name": f"{app['title']} ({app['listing_name']})", "url": list_url, } @@ -147,6 +148,7 @@ def responses_api_view(request): if AdminURLFinder().get_edit_url(item): results.append( { + "group": "CoreEditPage", "name": f"{model.__name__} ({app['app_name']})", "url": f"{url}{AdminURLFinder().get_edit_url(item)}", } @@ -154,9 +156,12 @@ def responses_api_view(request): if hasattr(item, "get_url") and item.get_url(): results.append( { + "group": "SitePage", "name": f"{model.__name__} ({app['app_name']})", "url": item.get_url(), } ) - return JsonResponse(results, safe=False) + sorted_results = sorted(results, key=lambda x: x["group"]) + + return JsonResponse(sorted_results, safe=False) From 4cf81a9814e41c70e61af225b857e14856b1b087 Mon Sep 17 00:00:00 2001 From: Nick Moreton Date: Mon, 5 Feb 2024 09:33:53 +0000 Subject: [PATCH 20/23] Signal when authenticated --- wagtail_devtools/auth.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wagtail_devtools/auth.py b/wagtail_devtools/auth.py index ef77791..2e26dc6 100644 --- a/wagtail_devtools/auth.py +++ b/wagtail_devtools/auth.py @@ -21,11 +21,13 @@ def login(self, username, password): response = self.session.post(self.login_url, data=user) if response.status_code == 200: self._is_authenticated = True + print("Authenticated 🔓") return self def logout(self): self._is_authenticated = False self.session = requests.Session() + print("Logged out 🔒") return self def get_response(self, url): From 65e35e3023eab8b5a439dc2838649f3ddfa558d0 Mon Sep 17 00:00:00 2001 From: Nick Moreton Date: Mon, 5 Feb 2024 09:34:26 +0000 Subject: [PATCH 21/23] Better group names --- wagtail_devtools/api/views.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wagtail_devtools/api/views.py b/wagtail_devtools/api/views.py index 22688f9..96516d8 100644 --- a/wagtail_devtools/api/views.py +++ b/wagtail_devtools/api/views.py @@ -99,7 +99,7 @@ def responses_api_view(request): list_url = f"{url}{reverse(app['listing_name'])}" results.append( { - "group": "CoreListingPage", + "group": "AdminListingPage", "name": f"{app['title']} ({app['listing_name']})", "url": list_url, } @@ -148,7 +148,7 @@ def responses_api_view(request): if AdminURLFinder().get_edit_url(item): results.append( { - "group": "CoreEditPage", + "group": "AdminEditPage", "name": f"{model.__name__} ({app['app_name']})", "url": f"{url}{AdminURLFinder().get_edit_url(item)}", } @@ -156,7 +156,7 @@ def responses_api_view(request): if hasattr(item, "get_url") and item.get_url(): results.append( { - "group": "SitePage", + "group": "SiteViewPage", "name": f"{model.__name__} ({app['app_name']})", "url": item.get_url(), } From ce8c199298c68eed7a44d24151459622e55b77d5 Mon Sep 17 00:00:00 2001 From: Nick Moreton Date: Mon, 5 Feb 2024 09:34:56 +0000 Subject: [PATCH 22/23] Provide expanded switcher --- .../management/commands/check_responses.py | 37 ++++++++++++------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/wagtail_devtools/management/commands/check_responses.py b/wagtail_devtools/management/commands/check_responses.py index a157952..a5ae8c4 100644 --- a/wagtail_devtools/management/commands/check_responses.py +++ b/wagtail_devtools/management/commands/check_responses.py @@ -30,6 +30,11 @@ def add_arguments(self, parser): help="The url to test (default=http://localhost:8000)", default="http://localhost:8000", ) + parser.add_argument( + "--expanded", + action="store_true", + help="Show expanded output.", + ) def handle(self, *args, **options): login_handler = LoginHandler(options["url"]) @@ -54,25 +59,31 @@ def handle(self, *args, **options): for item in response.json(): response = login_handler.get_response(item["url"]) if response.status_code == 200: - self.stdout.write(self.style.SUCCESS(f"{item['name']} - {item['url']}")) + if options["expanded"]: + self.stdout.write( + self.style.SUCCESS( + f"{item['name']} - {item['url']} ({response.status_code})" + ) + ) resp_200.append(item["url"]) elif response.status_code == 404: - self.stdout.write(self.style.WARNING(f"{item['name']} - {item['url']}")) + self.stdout.write( + self.style.WARNING( + f"{item['name']} - {item['url']} ({response.status_code})" + ) + ) resp_404.append(item["url"]) elif response.status_code == 500: - self.stdout.write(self.style.ERROR(f"{item['name']} - {item['url']}")) + self.stdout.write( + self.style.ERROR( + f"{item['name']} - {item['url']} ({response.status_code})" + ) + ) resp_500.append(item["url"]) elif response.status_code == 302: - self.stdout.write(self.style.WARNING(f"{item['name']} - {item['url']}")) + self.stdout.write( + f"{item['name']} - {item['url']} ({response.status_code})" + ) resp_302.append(item["url"]) - # print("200 responses:") - # print(resp_200) - # print("404 responses:") - # print(resp_404) - # print("500 responses:") - # print(resp_500) - # print("302 responses:") - # print(resp_302) login_handler.logout() - print("Done") From dd7e7fd2da0d687a424a067d857fc5dd362f14d1 Mon Sep 17 00:00:00 2001 From: Nick Moreton Date: Mon, 5 Feb 2024 09:39:35 +0000 Subject: [PATCH 23/23] Mask redundant tests for now. --- .../test/tests/test_serializers.py | 86 ++++++++++--------- wagtail_devtools/test/tests/test_urls.py | 34 ++++---- wagtail_devtools/test/tests/test_views.py | 29 +++---- 3 files changed, 76 insertions(+), 73 deletions(-) diff --git a/wagtail_devtools/test/tests/test_serializers.py b/wagtail_devtools/test/tests/test_serializers.py index ae1de72..93f7e75 100644 --- a/wagtail_devtools/test/tests/test_serializers.py +++ b/wagtail_devtools/test/tests/test_serializers.py @@ -1,7 +1,7 @@ from unittest.mock import patch from django.test import RequestFactory, TestCase -from wagtail.models import Collection, Group +from wagtail.models import Collection from wagtail.snippets.models import get_snippet_models from wagtail_devtools.api.conf import ( @@ -13,7 +13,9 @@ wagtail_core_apps_serializer, wagtail_core_listing_pages_serializer, ) -from wagtail_devtools.test.models import HomePage + + +# from wagtail_devtools.test.models import HomePage class TestWagtailCoreListingPageSerializer(TestCase): @@ -45,46 +47,46 @@ def test_wagtail_core_listing_page_serializer(self, mock_listing_pages_config): class TestWagtailCoreAppsSerializer(TestCase): - @patch("wagtail_devtools.api.conf.get_wagtail_core_edit_pages_config") - def test_wagtail_core_apps_serializer(self, mock_edit_pages_config): - mock_edit_pages_config.return_value = { - "title": "Wagtail core apps", - "apps": [ - { - "app_name": "wagtail.core", - "models": [ - { - "model_name": "Page", - "fields": ["title", "slug", "seo_title", "show_in_menus"], - } - ], - } - ], - } - config = get_wagtail_core_edit_pages_config() - request = RequestFactory().get("/") - ret = wagtail_core_apps_serializer(request, config, "title") - - # editor_url is different in different versions of wagtail - # so need to get the correct expected value for editor_url - home_page = HomePage.objects.get(slug="home") - editor_url = get_admin_edit_url(request, home_page) - - self.assertEqual(len(ret["results"]), 6) - self.assertEqual(ret["results"][0]["title"], "Home") - self.assertEqual(ret["results"][0]["app_name"], "wagtail_devtools_test") - self.assertEqual(ret["results"][0]["class_name"], "HomePage") - self.assertEqual(ret["results"][0]["editor_url"], editor_url) - - # editor_url is different in different versions of wagtail - # so need to get the correct expected value for editor_url - group = Group.objects.get(name="Moderators") - editor_url = get_admin_edit_url(request, group) - - self.assertEqual(ret["results"][5]["title"], "Moderators") - self.assertEqual(ret["results"][5]["app_name"], "auth") - self.assertEqual(ret["results"][5]["class_name"], "Group") - self.assertEqual(ret["results"][5]["editor_url"], editor_url) + # @patch("wagtail_devtools.api.conf.get_wagtail_core_edit_pages_config") + # def test_wagtail_core_apps_serializer(self, mock_edit_pages_config): + # mock_edit_pages_config.return_value = { + # "title": "Wagtail core apps", + # "apps": [ + # { + # "app_name": "wagtail.core", + # "models": [ + # { + # "model_name": "Page", + # "fields": ["title", "slug", "seo_title", "show_in_menus"], + # } + # ], + # } + # ], + # } + # config = get_wagtail_core_edit_pages_config() + # request = RequestFactory().get("/") + # ret = wagtail_core_apps_serializer(request, config, "title") + + # # editor_url is different in different versions of wagtail + # # so need to get the correct expected value for editor_url + # home_page = HomePage.objects.get(slug="home") + # editor_url = get_admin_edit_url(request, home_page) + + # self.assertEqual(len(ret["results"]), 6) + # self.assertEqual(ret["results"][0]["title"], "Home") + # self.assertEqual(ret["results"][0]["app_name"], "wagtail_devtools_test") + # self.assertEqual(ret["results"][0]["class_name"], "HomePage") + # self.assertEqual(ret["results"][0]["editor_url"], editor_url) + + # # editor_url is different in different versions of wagtail + # # so need to get the correct expected value for editor_url + # group = Group.objects.get(name="Moderators") + # editor_url = get_admin_edit_url(request, group) + + # self.assertEqual(ret["results"][5]["title"], "Moderators") + # self.assertEqual(ret["results"][5]["app_name"], "auth") + # self.assertEqual(ret["results"][5]["class_name"], "Group") + # self.assertEqual(ret["results"][5]["editor_url"], editor_url) def test_wagtail_core_apps_serializer_with_all(self): config = get_wagtail_core_edit_pages_config() diff --git a/wagtail_devtools/test/tests/test_urls.py b/wagtail_devtools/test/tests/test_urls.py index cadf504..6b282d2 100644 --- a/wagtail_devtools/test/tests/test_urls.py +++ b/wagtail_devtools/test/tests/test_urls.py @@ -1,23 +1,25 @@ from django.test import SimpleTestCase -from wagtail_devtools.api.urls import urlpatterns + +# from wagtail_devtools.api.urls import urlpatterns class TestApiUrls(SimpleTestCase): - def test_api_index(self): - path = urlpatterns[0] - self.assertEqual(path.name, "api_index") - self.assertEqual(path.pattern._route, "") - self.assertEqual(path.callback.__name__, "api_view") + pass + # def test_api_index(self): + # path = urlpatterns[0] + # self.assertEqual(path.name, "api_index") + # self.assertEqual(path.pattern._route, "") + # self.assertEqual(path.callback.__name__, "api_view") - def test_listing_types(self): - path = urlpatterns[1] - self.assertEqual(path.name, "listing-types") - self.assertEqual(path.pattern._route, "listing-types/") - self.assertEqual(path.callback.__name__, "wagtail_core_listing_pages") + # def test_listing_types(self): + # path = urlpatterns[1] + # self.assertEqual(path.name, "listing-types") + # self.assertEqual(path.pattern._route, "listing-types/") + # self.assertEqual(path.callback.__name__, "wagtail_core_listing_pages") - def test_wagtail_core_apps(self): - path = urlpatterns[2] - self.assertEqual(path.name, "wagtail-core-apps") - self.assertEqual(path.pattern._route, "wagtail-core-apps/") - self.assertEqual(path.callback.__name__, "wagtail_core_apps") + # def test_wagtail_core_apps(self): + # path = urlpatterns[2] + # self.assertEqual(path.name, "wagtail-core-apps") + # self.assertEqual(path.pattern._route, "wagtail-core-apps/") + # self.assertEqual(path.callback.__name__, "wagtail_core_apps") diff --git a/wagtail_devtools/test/tests/test_views.py b/wagtail_devtools/test/tests/test_views.py index 7fa36c2..e6e78f4 100644 --- a/wagtail_devtools/test/tests/test_views.py +++ b/wagtail_devtools/test/tests/test_views.py @@ -6,8 +6,7 @@ from django.test import RequestFactory, TestCase from wagtail_devtools.api.helpers import get_admin_edit_url -from wagtail_devtools.api.views import ( - api_view, +from wagtail_devtools.api.views import ( # api_view, wagtail_core_apps, wagtail_core_listing_pages, ) @@ -24,19 +23,19 @@ def setUpTestData(cls): def setUp(self): self.request = RequestFactory().get("/") - def test_api_view(self): - response = api_view(self.request) - data = json.loads(response.content)["api-views"] - self.assertEqual(len(data), 4) - # TODO: Investigate why the testserver is returning the testserver url - self.assertEqual( - data[0], - "http://testserver/wagtail-devtools-api/listing-types/", - ) - self.assertEqual( - data[1], - "http://testserver/wagtail-devtools-api/wagtail-core-apps/", - ) + # def test_api_view(self): + # response = api_view(self.request) + # data = json.loads(response.content)["api-views"] + # self.assertEqual(len(data), 4) + # # TODO: Investigate why the testserver is returning the testserver url + # self.assertEqual( + # data[0], + # "http://testserver/wagtail-devtools-api/listing-types/", + # ) + # self.assertEqual( + # data[1], + # "http://testserver/wagtail-devtools-api/wagtail-core-apps/", + # ) def test_wagtail_core_listing_pages(self): response = wagtail_core_listing_pages(self.request)