wagtail 7.2.1__py3-none-any.whl → 7.3rc1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- wagtail/__init__.py +1 -1
- wagtail/actions/copy_for_translation.py +4 -2
- wagtail/admin/action_menu.py +4 -1
- wagtail/admin/api/actions/convert_alias.py +2 -2
- wagtail/admin/api/actions/copy.py +2 -2
- wagtail/admin/api/actions/copy_for_translation.py +2 -2
- wagtail/admin/api/actions/create_alias.py +2 -2
- wagtail/admin/api/actions/delete.py +1 -1
- wagtail/admin/api/actions/move.py +1 -1
- wagtail/admin/api/actions/publish.py +2 -2
- wagtail/admin/api/actions/revert_to_page_revision.py +2 -2
- wagtail/admin/api/actions/unpublish.py +1 -1
- wagtail/admin/api/filters.py +2 -2
- wagtail/admin/compare.py +22 -0
- wagtail/admin/forms/account.py +52 -1
- wagtail/admin/forms/comments.py +53 -0
- wagtail/admin/forms/models.py +36 -0
- wagtail/admin/forms/pages.py +7 -0
- wagtail/admin/forms/workflows.py +5 -2
- wagtail/admin/icons.py +4 -3
- wagtail/admin/locale/ar/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/ar/LC_MESSAGES/django.po +35 -0
- wagtail/admin/locale/en/LC_MESSAGES/django.po +262 -234
- wagtail/admin/locale/en/LC_MESSAGES/djangojs.po +72 -43
- wagtail/admin/locale/it/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/it/LC_MESSAGES/django.po +566 -6
- wagtail/admin/locale/it/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/it/LC_MESSAGES/djangojs.po +40 -2
- wagtail/admin/locale/nl/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/nl/LC_MESSAGES/django.po +2 -2
- wagtail/admin/locale/sv/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/sv/LC_MESSAGES/django.po +330 -15
- wagtail/admin/locale/sv/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/sv/LC_MESSAGES/djangojs.po +3 -2
- wagtail/admin/panels/comment_panel.py +1 -51
- wagtail/admin/panels/title_field_panel.py +3 -1
- wagtail/admin/static/wagtailadmin/css/core.css +1 -1
- wagtail/admin/static/wagtailadmin/js/bulk-actions.js +1 -1
- wagtail/admin/static/wagtailadmin/js/comments.js +1 -1
- wagtail/admin/static/wagtailadmin/js/core.js +1 -1
- wagtail/admin/static/wagtailadmin/js/core.js.LICENSE.txt +1 -1
- wagtail/admin/static/wagtailadmin/js/draftail.js +1 -1
- wagtail/admin/static/wagtailadmin/js/privacy-switch.js +1 -1
- wagtail/admin/static/wagtailadmin/js/sidebar.js +1 -1
- wagtail/admin/static/wagtailadmin/js/telepath/blocks.js +1 -1
- wagtail/admin/static/wagtailadmin/js/userbar.js +1 -1
- wagtail/admin/static/wagtailadmin/js/userbar.js.LICENSE.txt +1 -1
- wagtail/admin/static/wagtailadmin/js/vendor.js +1 -1
- wagtail/admin/static/wagtailadmin/js/wagtailadmin.js +1 -1
- wagtail/admin/templates/wagtailadmin/base.html +1 -1
- wagtail/admin/templates/wagtailadmin/generic/edit_partials.html +100 -0
- wagtail/admin/templates/wagtailadmin/generic/form.html +26 -5
- wagtail/admin/templates/wagtailadmin/generic/includes/_loaded_revision_inputs.html +3 -0
- wagtail/admin/templates/wagtailadmin/generic/listing_results.html +1 -17
- wagtail/admin/templates/wagtailadmin/pages/create.html +14 -4
- wagtail/admin/templates/wagtailadmin/pages/edit.html +16 -3
- wagtail/admin/templates/wagtailadmin/pages/edit_partials.html +31 -0
- wagtail/admin/templates/wagtailadmin/pages/index_results.html +1 -9
- wagtail/admin/templates/wagtailadmin/shared/autosave/indicator.html +36 -0
- wagtail/admin/templates/wagtailadmin/shared/breadcrumbs.html +1 -1
- wagtail/admin/templates/wagtailadmin/shared/editing_sessions/list.html +2 -2
- wagtail/admin/templates/wagtailadmin/shared/editing_sessions/module.html +19 -3
- wagtail/admin/templates/wagtailadmin/shared/headers/_actions.html +5 -0
- wagtail/admin/templates/wagtailadmin/shared/headers/_history_icon_link.html +2 -2
- wagtail/admin/templates/wagtailadmin/shared/headers/slim_header.html +8 -9
- wagtail/admin/templates/wagtailadmin/shared/listing/filter_partials.html +19 -0
- wagtail/admin/templates/wagtailadmin/shared/side_panel_toggle.html +3 -3
- wagtail/admin/templates/wagtailadmin/shared/side_panels/includes/side_panel.html +3 -0
- wagtail/admin/templates/wagtailadmin/shared/side_panels/includes/status/privacy.html +18 -6
- wagtail/admin/templates/wagtailadmin/shared/side_panels/includes/status/usage.html +11 -4
- wagtail/admin/templates/wagtailadmin/shared/side_panels/includes/status/workflow.html +22 -1
- wagtail/admin/templates/wagtailadmin/shared/side_panels/preview.html +1 -0
- wagtail/admin/templates/wagtailadmin/shared/side_panels.html +1 -2
- wagtail/admin/templates/wagtailadmin/shared/unsaved_changes_warning.html +20 -20
- wagtail/admin/templates/wagtailadmin/userbar/base.html +2 -0
- wagtail/admin/templates/wagtailadmin/userbar/item_accessibility.html +1 -1
- wagtail/admin/templatetags/wagtailadmin_tags.py +6 -14
- wagtail/admin/tests/pages/test_create_page.py +298 -1
- wagtail/admin/tests/pages/test_custom_listing.py +48 -2
- wagtail/admin/tests/pages/test_edit_page.py +721 -7
- wagtail/admin/tests/pages/test_explorer_view.py +9 -5
- wagtail/admin/tests/pages/test_page_search.py +15 -0
- wagtail/admin/tests/pages/test_revisions.py +4 -0
- wagtail/admin/tests/test_account_management.py +88 -2
- wagtail/admin/tests/test_collections_views.py +15 -15
- wagtail/admin/tests/test_compare.py +34 -0
- wagtail/admin/tests/test_editing_sessions.py +230 -8
- wagtail/admin/tests/test_forms.py +192 -1
- wagtail/admin/tests/test_icon_sprite.py +22 -2
- wagtail/admin/tests/test_page_chooser.py +13 -13
- wagtail/admin/tests/test_reports_views.py +4 -1
- wagtail/admin/tests/test_userbar.py +69 -5
- wagtail/admin/tests/test_views_generic.py +5 -5
- wagtail/admin/tests/test_workflows.py +14 -12
- wagtail/admin/tests/viewsets/test_model_viewset.py +13 -0
- wagtail/admin/ui/autosave.py +5 -0
- wagtail/admin/ui/editing_sessions.py +3 -0
- wagtail/admin/ui/side_panels.py +19 -20
- wagtail/admin/userbar.py +48 -27
- wagtail/admin/views/bulk_action/dispatcher.py +2 -2
- wagtail/admin/views/chooser.py +6 -6
- wagtail/admin/views/editing_sessions.py +20 -7
- wagtail/admin/views/generic/__init__.py +1 -0
- wagtail/admin/views/generic/chooser.py +5 -5
- wagtail/admin/views/generic/mixins.py +143 -26
- wagtail/admin/views/generic/models.py +157 -27
- wagtail/admin/views/generic/ordering.py +1 -1
- wagtail/admin/views/generic/preview.py +4 -4
- wagtail/admin/views/home.py +3 -1
- wagtail/admin/views/pages/create.py +77 -29
- wagtail/admin/views/pages/edit.py +160 -34
- wagtail/admin/views/pages/preview.py +4 -4
- wagtail/admin/views/pages/revisions.py +3 -0
- wagtail/admin/views/pages/search.py +4 -4
- wagtail/admin/views/pages/usage.py +2 -2
- wagtail/admin/views/tags.py +2 -2
- wagtail/admin/views/workflows.py +73 -54
- wagtail/admin/viewsets/model.py +34 -8
- wagtail/admin/widgets/button.py +11 -4
- wagtail/admin/widgets/chooser.py +6 -4
- wagtail/admin/widgets/slug.py +1 -1
- wagtail/api/v2/filters.py +27 -21
- wagtail/api/v2/pagination.py +4 -4
- wagtail/api/v2/views.py +7 -7
- wagtail/blocks/list_block.py +0 -8
- wagtail/blocks/migrations/migrate_operation.py +8 -1
- wagtail/blocks/stream_block.py +2 -10
- wagtail/blocks/struct_block.py +192 -12
- wagtail/compat.py +2 -2
- wagtail/contrib/forms/locale/en/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/forms/locale/it/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/forms/locale/it/LC_MESSAGES/django.po +40 -2
- wagtail/contrib/frontend_cache/backends/azure.py +6 -6
- wagtail/contrib/frontend_cache/backends/cloudfront.py +2 -2
- wagtail/contrib/frontend_cache/utils.py +1 -1
- wagtail/contrib/redirects/forms.py +1 -1
- wagtail/contrib/redirects/locale/en/LC_MESSAGES/django.po +14 -14
- wagtail/contrib/redirects/locale/it/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/redirects/locale/it/LC_MESSAGES/django.po +15 -2
- wagtail/contrib/redirects/locale/sv/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/redirects/locale/sv/LC_MESSAGES/django.po +16 -3
- wagtail/contrib/redirects/middleware.py +11 -7
- wagtail/contrib/redirects/models.py +17 -1
- wagtail/contrib/redirects/tests/test_import_admin_views.py +3 -3
- wagtail/contrib/redirects/tests/test_redirects.py +56 -8
- wagtail/contrib/search_promotions/locale/en/LC_MESSAGES/django.po +6 -6
- wagtail/contrib/search_promotions/locale/it/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/it/LC_MESSAGES/django.po +43 -2
- wagtail/contrib/search_promotions/tests.py +1 -1
- wagtail/contrib/search_promotions/views/settings.py +24 -25
- wagtail/contrib/settings/jinja2tags.py +2 -2
- wagtail/contrib/settings/locale/en/LC_MESSAGES/django.po +2 -2
- wagtail/contrib/settings/locale/it/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/settings/locale/it/LC_MESSAGES/django.po +6 -2
- wagtail/contrib/settings/locale/sv/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/settings/locale/sv/LC_MESSAGES/django.po +3 -2
- wagtail/contrib/settings/tests/generic/test_admin.py +118 -2
- wagtail/contrib/settings/tests/site_specific/test_admin.py +78 -15
- wagtail/contrib/settings/tests/site_specific/test_model.py +2 -2
- wagtail/contrib/settings/views.py +7 -0
- wagtail/contrib/simple_translation/locale/en/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/simple_translation/locale/it/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/simple_translation/locale/it/LC_MESSAGES/django.po +5 -2
- wagtail/contrib/styleguide/locale/en/LC_MESSAGES/django.po +7 -7
- wagtail/contrib/styleguide/tests.py +2 -0
- wagtail/contrib/styleguide/views.py +4 -5
- wagtail/contrib/table_block/locale/ar/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/table_block/locale/ar/LC_MESSAGES/django.po +5 -1
- wagtail/contrib/table_block/locale/en/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/table_block/locale/it/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/table_block/locale/it/LC_MESSAGES/django.po +5 -2
- wagtail/contrib/typed_table_block/blocks.py +37 -0
- wagtail/contrib/typed_table_block/locale/en/LC_MESSAGES/django.po +10 -10
- wagtail/contrib/typed_table_block/locale/sv/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/typed_table_block/locale/sv/LC_MESSAGES/django.po +4 -3
- wagtail/contrib/typed_table_block/tests.py +108 -0
- wagtail/coreutils.py +4 -4
- wagtail/documents/__init__.py +4 -4
- wagtail/documents/locale/en/LC_MESSAGES/django.po +15 -15
- wagtail/documents/locale/it/LC_MESSAGES/django.mo +0 -0
- wagtail/documents/locale/it/LC_MESSAGES/django.po +32 -2
- wagtail/documents/locale/sv/LC_MESSAGES/django.mo +0 -0
- wagtail/documents/locale/sv/LC_MESSAGES/django.po +32 -4
- wagtail/documents/models.py +1 -1
- wagtail/documents/tests/test_admin_views.py +19 -4
- wagtail/documents/tests/test_search.py +0 -2
- wagtail/documents/tests/test_views.py +6 -4
- wagtail/documents/views/bulk_actions/add_tags.py +1 -1
- wagtail/embeds/finders/facebook.py +3 -3
- wagtail/embeds/finders/instagram.py +3 -3
- wagtail/embeds/finders/oembed.py +7 -2
- wagtail/embeds/locale/en/LC_MESSAGES/django.po +1 -1
- wagtail/embeds/oembed_providers.py +8 -0
- wagtail/embeds/tests/test_embeds.py +35 -0
- wagtail/images/__init__.py +4 -4
- wagtail/images/admin_urls.py +3 -3
- wagtail/images/blocks.py +1 -1
- wagtail/images/formats.py +2 -2
- wagtail/images/image_operations.py +2 -2
- wagtail/images/locale/en/LC_MESSAGES/django.po +44 -40
- wagtail/images/locale/it/LC_MESSAGES/django.mo +0 -0
- wagtail/images/locale/it/LC_MESSAGES/django.po +50 -4
- wagtail/images/locale/nl/LC_MESSAGES/django.mo +0 -0
- wagtail/images/locale/nl/LC_MESSAGES/django.po +3 -3
- wagtail/images/locale/sv/LC_MESSAGES/django.mo +0 -0
- wagtail/images/locale/sv/LC_MESSAGES/django.po +49 -6
- wagtail/images/models.py +11 -10
- wagtail/images/templates/wagtailimages/images/index_results.html +1 -1
- wagtail/images/templates/wagtailimages/images/url_generator.html +17 -38
- wagtail/images/templates/wagtailimages/images/url_generator_output.html +28 -0
- wagtail/images/templatetags/wagtailimages_tags.py +4 -4
- wagtail/images/tests/test_admin_views.py +432 -59
- wagtail/images/tests/test_image_operations.py +2 -2
- wagtail/images/tests/test_models.py +0 -2
- wagtail/images/views/bulk_actions/add_tags.py +1 -1
- wagtail/images/views/images.py +72 -39
- wagtail/locale/en/LC_MESSAGES/django.po +103 -105
- wagtail/locale/it/LC_MESSAGES/django.mo +0 -0
- wagtail/locale/it/LC_MESSAGES/django.po +105 -2
- wagtail/locale/sv/LC_MESSAGES/django.mo +0 -0
- wagtail/locale/sv/LC_MESSAGES/django.po +95 -12
- wagtail/locales/locale/en/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/it/LC_MESSAGES/django.mo +0 -0
- wagtail/locales/locale/it/LC_MESSAGES/django.po +13 -2
- wagtail/locales/locale/sv/LC_MESSAGES/django.mo +0 -0
- wagtail/locales/locale/sv/LC_MESSAGES/django.po +4 -3
- wagtail/locales/tests.py +5 -5
- wagtail/models/i18n.py +4 -6
- wagtail/models/media.py +18 -0
- wagtail/models/pages.py +65 -11
- wagtail/models/reference_index.py +15 -0
- wagtail/models/revisions.py +40 -12
- wagtail/models/workflows.py +0 -3
- wagtail/permission_policies/base.py +2 -2
- wagtail/permission_policies/collections.py +2 -2
- wagtail/project_template/requirements.txt +2 -2
- wagtail/search/locale/en/LC_MESSAGES/django.po +1 -1
- wagtail/signal_handlers.py +1 -0
- wagtail/sites/locale/en/LC_MESSAGES/django.po +1 -1
- wagtail/sites/locale/sv/LC_MESSAGES/django.mo +0 -0
- wagtail/sites/locale/sv/LC_MESSAGES/django.po +3 -2
- wagtail/sites/tests.py +7 -7
- wagtail/snippets/action_menu.py +2 -1
- wagtail/snippets/locale/en/LC_MESSAGES/django.po +23 -13
- wagtail/snippets/locale/sv/LC_MESSAGES/django.mo +0 -0
- wagtail/snippets/locale/sv/LC_MESSAGES/django.po +12 -1
- wagtail/snippets/tests/test_chooser_block.py +197 -0
- wagtail/snippets/tests/test_chooser_panel.py +149 -0
- wagtail/snippets/tests/test_chooser_views.py +375 -0
- wagtail/snippets/tests/test_chooser_widget.py +22 -0
- wagtail/snippets/tests/test_compare_revisions_view.py +167 -0
- wagtail/snippets/tests/test_copy_view.py +38 -0
- wagtail/snippets/tests/test_create_view.py +1159 -0
- wagtail/snippets/tests/test_delete_view.py +225 -0
- wagtail/snippets/tests/test_edit_view.py +2882 -0
- wagtail/snippets/tests/test_history_view.py +182 -0
- wagtail/snippets/tests/test_index_view.py +105 -0
- wagtail/snippets/tests/test_list_view.py +786 -0
- wagtail/snippets/tests/test_locking.py +28 -0
- wagtail/snippets/tests/test_permissions.py +167 -0
- wagtail/snippets/tests/test_preview.py +3 -3
- wagtail/snippets/tests/test_revert_view.py +343 -0
- wagtail/snippets/tests/test_snippet_models.py +112 -0
- wagtail/snippets/tests/test_unpublish_view.py +228 -0
- wagtail/snippets/tests/test_unschedule_view.py +151 -0
- wagtail/snippets/tests/test_viewset.py +38 -5
- wagtail/snippets/views/snippets.py +78 -30
- wagtail/snippets/widgets.py +2 -2
- wagtail/templatetags/wagtailcore_tags.py +2 -2
- wagtail/test/dummy_external_storage.py +1 -1
- wagtail/test/testapp/fixtures/test.json +22 -0
- wagtail/test/testapp/fixtures/test_empty.json +2 -0
- wagtail/test/testapp/fixtures/test_explorable_pages.json +10 -0
- wagtail/test/testapp/fixtures/test_specific.json +9 -0
- wagtail/test/testapp/migrations/0059_nopromotepage.py +25 -0
- wagtail/test/testapp/models.py +7 -0
- wagtail/test/testapp/views.py +5 -0
- wagtail/test/utils/page_tests.py +7 -7
- wagtail/test/utils/wagtail_factories/builder.py +2 -2
- wagtail/tests/streamfield_migrations/test_migrations.py +35 -0
- wagtail/tests/test_blocks.py +321 -61
- wagtail/tests/test_collection_model.py +12 -0
- wagtail/tests/test_page_model.py +190 -1
- wagtail/tests/test_page_privacy.py +5 -0
- wagtail/tests/test_reference_index.py +42 -0
- wagtail/tests/test_revision_model.py +118 -1
- wagtail/tests/test_utils.py +2 -2
- wagtail/users/locale/en/LC_MESSAGES/django.po +14 -14
- wagtail/users/locale/id_ID/LC_MESSAGES/django.mo +0 -0
- wagtail/users/locale/id_ID/LC_MESSAGES/django.po +6 -2
- wagtail/users/locale/it/LC_MESSAGES/django.mo +0 -0
- wagtail/users/locale/it/LC_MESSAGES/django.po +14 -2
- wagtail/users/locale/sv/LC_MESSAGES/django.mo +0 -0
- wagtail/users/locale/sv/LC_MESSAGES/django.po +48 -17
- wagtail/users/tests/test_admin_views.py +39 -25
- wagtail/users/utils.py +4 -1
- wagtail/users/views/groups.py +19 -5
- wagtail/users/wagtail_hooks.py +1 -1
- wagtail/utils/loading.py +2 -2
- wagtail-7.3rc1.data/data/AGENTS.md +21 -0
- wagtail-7.3rc1.data/data/CHANGELOG.txt +4941 -0
- wagtail-7.3rc1.data/data/CODE_OF_CONDUCT.md +71 -0
- wagtail-7.3rc1.data/data/CONTRIBUTORS.md +999 -0
- wagtail-7.3rc1.data/data/SPONSORS.md +55 -0
- {wagtail-7.2.1.dist-info → wagtail-7.3rc1.dist-info}/METADATA +6 -6
- {wagtail-7.2.1.dist-info → wagtail-7.3rc1.dist-info}/RECORD +310 -280
- {wagtail-7.2.1.dist-info → wagtail-7.3rc1.dist-info}/WHEEL +1 -1
- wagtail/images/static/wagtailimages/js/image-url-generator.js +0 -1
- wagtail/snippets/tests/test_snippets.py +0 -6377
- {wagtail-7.2.1.dist-info → wagtail-7.3rc1.dist-info}/entry_points.txt +0 -0
- {wagtail-7.2.1.dist-info → wagtail-7.3rc1.dist-info}/licenses/LICENSE +0 -0
- {wagtail-7.2.1.dist-info → wagtail-7.3rc1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,786 @@
|
|
|
1
|
+
from django.contrib.admin.utils import quote
|
|
2
|
+
from django.contrib.auth import get_user_model
|
|
3
|
+
from django.contrib.auth.models import Permission
|
|
4
|
+
from django.contrib.contenttypes.models import ContentType
|
|
5
|
+
from django.test import TestCase, TransactionTestCase
|
|
6
|
+
from django.test.utils import override_settings
|
|
7
|
+
from django.urls import reverse
|
|
8
|
+
from django.utils.timezone import now
|
|
9
|
+
|
|
10
|
+
from wagtail import hooks
|
|
11
|
+
from wagtail.admin.forms.search import SearchForm
|
|
12
|
+
from wagtail.admin.staticfiles import versioned_static
|
|
13
|
+
from wagtail.admin.widgets.button import Button, ButtonWithDropdown, ListingButton
|
|
14
|
+
from wagtail.models import Locale, ModelLogEntry
|
|
15
|
+
from wagtail.snippets.widgets import (
|
|
16
|
+
SnippetListingButton,
|
|
17
|
+
)
|
|
18
|
+
from wagtail.test.snippets.models import (
|
|
19
|
+
NonAutocompleteSearchableSnippet,
|
|
20
|
+
SearchableSnippet,
|
|
21
|
+
StandardSnippetWithCustomPrimaryKey,
|
|
22
|
+
TranslatableSnippet,
|
|
23
|
+
)
|
|
24
|
+
from wagtail.test.testapp.models import (
|
|
25
|
+
Advert,
|
|
26
|
+
DraftStateModel,
|
|
27
|
+
FullFeaturedSnippet,
|
|
28
|
+
)
|
|
29
|
+
from wagtail.test.utils import WagtailTestUtils
|
|
30
|
+
from wagtail.utils.deprecation import RemovedInWagtail80Warning
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class TestSnippetListView(WagtailTestUtils, TestCase):
|
|
34
|
+
def setUp(self):
|
|
35
|
+
self.login()
|
|
36
|
+
user_model = get_user_model()
|
|
37
|
+
self.user = user_model.objects.get()
|
|
38
|
+
|
|
39
|
+
def get(self, params=None):
|
|
40
|
+
return self.client.get(reverse("wagtailsnippets_tests_advert:list"), params)
|
|
41
|
+
|
|
42
|
+
def test_simple(self):
|
|
43
|
+
response = self.get()
|
|
44
|
+
self.assertEqual(response.status_code, 200)
|
|
45
|
+
self.assertTemplateUsed(response, "wagtailsnippets/snippets/index.html")
|
|
46
|
+
self.assertEqual(response.context["header_icon"], "snippet")
|
|
47
|
+
|
|
48
|
+
def get_with_limited_permissions(self):
|
|
49
|
+
self.user.is_superuser = False
|
|
50
|
+
self.user.user_permissions.add(
|
|
51
|
+
Permission.objects.get(
|
|
52
|
+
content_type__app_label="wagtailadmin", codename="access_admin"
|
|
53
|
+
)
|
|
54
|
+
)
|
|
55
|
+
self.user.save()
|
|
56
|
+
|
|
57
|
+
response = self.get()
|
|
58
|
+
self.assertEqual(response.status_code, 302)
|
|
59
|
+
|
|
60
|
+
def get_with_edit_permission_only(self):
|
|
61
|
+
self.user.is_superuser = False
|
|
62
|
+
self.user.user_permissions.add(
|
|
63
|
+
Permission.objects.get(
|
|
64
|
+
content_type__app_label="wagtailadmin", codename="access_admin"
|
|
65
|
+
),
|
|
66
|
+
Permission.objects.get(
|
|
67
|
+
content_type__app_label="tests", codename="change_advert"
|
|
68
|
+
),
|
|
69
|
+
)
|
|
70
|
+
self.user.save()
|
|
71
|
+
|
|
72
|
+
response = self.get()
|
|
73
|
+
self.assertEqual(response.status_code, 200)
|
|
74
|
+
self.assertContains(
|
|
75
|
+
response,
|
|
76
|
+
"<p>There are no adverts to display.</p>",
|
|
77
|
+
html=True,
|
|
78
|
+
)
|
|
79
|
+
self.assertNotContains(response, reverse("wagtailsnippets_tests_advert:add"))
|
|
80
|
+
|
|
81
|
+
def test_ordering(self):
|
|
82
|
+
"""
|
|
83
|
+
Listing should be ordered descending by PK if no ordering has been set on the model
|
|
84
|
+
"""
|
|
85
|
+
for i in range(1, 11):
|
|
86
|
+
Advert.objects.create(pk=i, text="advert %d" % i)
|
|
87
|
+
response = self.get()
|
|
88
|
+
self.assertEqual(response.status_code, 200)
|
|
89
|
+
self.assertEqual(response.context["page_obj"][0].text, "advert 10")
|
|
90
|
+
|
|
91
|
+
def test_simple_pagination(self):
|
|
92
|
+
pages = ["0", "1", "-1", "9999", "Not a page"]
|
|
93
|
+
for page in pages:
|
|
94
|
+
response = self.get({"p": page})
|
|
95
|
+
self.assertEqual(response.status_code, 200)
|
|
96
|
+
self.assertTemplateUsed(response, "wagtailsnippets/snippets/index.html")
|
|
97
|
+
|
|
98
|
+
def test_displays_add_button(self):
|
|
99
|
+
self.assertContains(self.get(), "Add advert")
|
|
100
|
+
|
|
101
|
+
def test_not_searchable(self):
|
|
102
|
+
self.assertFalse(self.get().context.get("search_form"))
|
|
103
|
+
|
|
104
|
+
def test_register_snippet_listing_buttons_hook_deprecated_class(self):
|
|
105
|
+
advert = Advert.objects.create(text="My Lovely advert")
|
|
106
|
+
|
|
107
|
+
def snippet_listing_buttons(snippet, user, next_url=None):
|
|
108
|
+
self.assertEqual(snippet, advert)
|
|
109
|
+
self.assertEqual(user, self.user)
|
|
110
|
+
self.assertEqual(next_url, reverse("wagtailsnippets_tests_advert:list"))
|
|
111
|
+
|
|
112
|
+
yield SnippetListingButton(
|
|
113
|
+
"Another useless snippet listing button", "/custom-url", priority=10
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
with hooks.register_temporarily(
|
|
117
|
+
"register_snippet_listing_buttons", snippet_listing_buttons
|
|
118
|
+
):
|
|
119
|
+
with self.assertWarnsMessage(
|
|
120
|
+
RemovedInWagtail80Warning,
|
|
121
|
+
"`SnippetListingButton` is deprecated. "
|
|
122
|
+
"Use `wagtail.admin.widgets.button.Button` "
|
|
123
|
+
"or `wagtail.admin.widgets.button.ListingButton` instead.",
|
|
124
|
+
):
|
|
125
|
+
response = self.get()
|
|
126
|
+
|
|
127
|
+
self.assertEqual(response.status_code, 200)
|
|
128
|
+
self.assertTemplateUsed(response, "wagtailadmin/shared/buttons.html")
|
|
129
|
+
|
|
130
|
+
soup = self.get_soup(response.content)
|
|
131
|
+
actions = soup.select_one("tbody tr td ul.actions")
|
|
132
|
+
top_level_custom_button = actions.select_one("li > a[href='/custom-url']")
|
|
133
|
+
self.assertIsNone(top_level_custom_button)
|
|
134
|
+
custom_button = actions.select_one(
|
|
135
|
+
"li [data-controller='w-dropdown'] a[href='/custom-url']"
|
|
136
|
+
)
|
|
137
|
+
self.assertIsNotNone(custom_button)
|
|
138
|
+
self.assertEqual(
|
|
139
|
+
custom_button.text.strip(),
|
|
140
|
+
"Another useless snippet listing button",
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
def test_register_snippet_listing_buttons_hook(self):
|
|
144
|
+
advert = Advert.objects.create(text="My Lovely advert")
|
|
145
|
+
|
|
146
|
+
def snippet_listing_buttons(snippet, user, next_url=None):
|
|
147
|
+
self.assertEqual(snippet, advert)
|
|
148
|
+
self.assertEqual(user, self.user)
|
|
149
|
+
self.assertEqual(next_url, reverse("wagtailsnippets_tests_advert:list"))
|
|
150
|
+
|
|
151
|
+
yield ListingButton(
|
|
152
|
+
"A useless top-level snippet listing button",
|
|
153
|
+
"/custom-url",
|
|
154
|
+
priority=10,
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
yield Button(
|
|
158
|
+
"A useless snippet listing button inside the 'More' dropdown",
|
|
159
|
+
"/custom-url",
|
|
160
|
+
priority=10,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
with hooks.register_temporarily(
|
|
164
|
+
"register_snippet_listing_buttons", snippet_listing_buttons
|
|
165
|
+
):
|
|
166
|
+
response = self.get()
|
|
167
|
+
|
|
168
|
+
self.assertEqual(response.status_code, 200)
|
|
169
|
+
self.assertTemplateUsed(response, "wagtailadmin/shared/buttons.html")
|
|
170
|
+
|
|
171
|
+
soup = self.get_soup(response.content)
|
|
172
|
+
actions = soup.select_one("tbody tr td ul.actions")
|
|
173
|
+
custom_buttons = actions.select("a[href='/custom-url']")
|
|
174
|
+
top_level_custom_button = actions.select_one("li > a[href='/custom-url']")
|
|
175
|
+
self.assertIs(top_level_custom_button, custom_buttons[0])
|
|
176
|
+
self.assertEqual(
|
|
177
|
+
top_level_custom_button.text.strip(),
|
|
178
|
+
"A useless top-level snippet listing button",
|
|
179
|
+
)
|
|
180
|
+
in_dropdown_custom_button = actions.select_one(
|
|
181
|
+
"li [data-controller='w-dropdown'] a[href='/custom-url']"
|
|
182
|
+
)
|
|
183
|
+
self.assertIs(in_dropdown_custom_button, custom_buttons[1])
|
|
184
|
+
self.assertEqual(
|
|
185
|
+
in_dropdown_custom_button.text.strip(),
|
|
186
|
+
"A useless snippet listing button inside the 'More' dropdown",
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
def test_register_snippet_listing_buttons_hook_with_dropdown(self):
|
|
190
|
+
advert = Advert.objects.create(text="My Lovely advert")
|
|
191
|
+
|
|
192
|
+
def snippet_listing_buttons(snippet, user, next_url=None):
|
|
193
|
+
self.assertEqual(snippet, advert)
|
|
194
|
+
self.assertEqual(user, self.user)
|
|
195
|
+
self.assertEqual(next_url, reverse("wagtailsnippets_tests_advert:list"))
|
|
196
|
+
yield ButtonWithDropdown(
|
|
197
|
+
label="Moar pls!",
|
|
198
|
+
buttons=[ListingButton("Alrighty", "/cheers", priority=10)],
|
|
199
|
+
attrs={"data-foo": "bar"},
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
with hooks.register_temporarily(
|
|
203
|
+
"register_snippet_listing_buttons", snippet_listing_buttons
|
|
204
|
+
):
|
|
205
|
+
response = self.get()
|
|
206
|
+
|
|
207
|
+
self.assertEqual(response.status_code, 200)
|
|
208
|
+
self.assertTemplateUsed(response, "wagtailadmin/shared/buttons.html")
|
|
209
|
+
|
|
210
|
+
soup = self.get_soup(response.content)
|
|
211
|
+
actions = soup.select_one("tbody tr td ul.actions")
|
|
212
|
+
nested_dropdown = actions.select_one(
|
|
213
|
+
"li [data-controller='w-dropdown'] [data-controller='w-dropdown']"
|
|
214
|
+
)
|
|
215
|
+
self.assertIsNone(nested_dropdown)
|
|
216
|
+
dropdown_buttons = actions.select("li > [data-controller='w-dropdown']")
|
|
217
|
+
# Default "More" button and the custom "Moar pls!" button
|
|
218
|
+
self.assertEqual(len(dropdown_buttons), 2)
|
|
219
|
+
custom_dropdown = None
|
|
220
|
+
for button in dropdown_buttons:
|
|
221
|
+
if "Moar pls!" in button.text.strip():
|
|
222
|
+
custom_dropdown = button
|
|
223
|
+
self.assertIsNotNone(custom_dropdown)
|
|
224
|
+
self.assertEqual(custom_dropdown.select_one("button").text.strip(), "Moar pls!")
|
|
225
|
+
self.assertEqual(custom_dropdown.get("data-foo"), "bar")
|
|
226
|
+
# Should contain the custom button inside the custom dropdown
|
|
227
|
+
custom_button = custom_dropdown.find("a", attrs={"href": "/cheers"})
|
|
228
|
+
self.assertIsNotNone(custom_button)
|
|
229
|
+
self.assertEqual(custom_button.text.strip(), "Alrighty")
|
|
230
|
+
|
|
231
|
+
def test_construct_snippet_listing_buttons_hook(self):
|
|
232
|
+
Advert.objects.create(text="My Lovely advert")
|
|
233
|
+
|
|
234
|
+
# testapp implements a construct_snippet_listing_buttons hook
|
|
235
|
+
# that adds a dummy button with the label 'Dummy Button' which points
|
|
236
|
+
# to '/dummy-button' and is placed inside the default "More" dropdown button
|
|
237
|
+
response = self.get()
|
|
238
|
+
self.assertEqual(response.status_code, 200)
|
|
239
|
+
self.assertTemplateUsed(response, "wagtailadmin/shared/buttons.html")
|
|
240
|
+
|
|
241
|
+
soup = self.get_soup(response.content)
|
|
242
|
+
dropdowns = soup.select(
|
|
243
|
+
"tbody tr td ul.actions > li > [data-controller='w-dropdown']"
|
|
244
|
+
)
|
|
245
|
+
self.assertEqual(len(dropdowns), 1)
|
|
246
|
+
more_dropdown = dropdowns[0]
|
|
247
|
+
dummy_button = more_dropdown.find("a", attrs={"href": "/dummy-button"})
|
|
248
|
+
self.assertIsNotNone(dummy_button)
|
|
249
|
+
self.assertEqual(dummy_button.text.strip(), "Dummy Button")
|
|
250
|
+
|
|
251
|
+
def test_construct_snippet_listing_buttons_hook_contains_default_buttons(self):
|
|
252
|
+
advert = Advert.objects.create(text="My Lovely advert")
|
|
253
|
+
delete_url = reverse(
|
|
254
|
+
"wagtailsnippets_tests_advert:delete", args=[quote(advert.pk)]
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
def hide_delete_button_for_lovely_advert(buttons, snippet, user):
|
|
258
|
+
# Edit, delete, dummy button, copy button
|
|
259
|
+
self.assertEqual(len(buttons), 4)
|
|
260
|
+
buttons[:] = [button for button in buttons if button.url != delete_url]
|
|
261
|
+
self.assertEqual(len(buttons), 3)
|
|
262
|
+
|
|
263
|
+
with hooks.register_temporarily(
|
|
264
|
+
"construct_snippet_listing_buttons",
|
|
265
|
+
hide_delete_button_for_lovely_advert,
|
|
266
|
+
):
|
|
267
|
+
response = self.get()
|
|
268
|
+
|
|
269
|
+
self.assertEqual(response.status_code, 200)
|
|
270
|
+
self.assertTemplateUsed(response, "wagtailadmin/shared/buttons.html")
|
|
271
|
+
self.assertNotContains(response, delete_url)
|
|
272
|
+
|
|
273
|
+
def test_dropdown_not_rendered_when_no_child_buttons_exist(self):
|
|
274
|
+
Advert.objects.create(text="My Lovely advert")
|
|
275
|
+
|
|
276
|
+
def remove_all_buttons(buttons, snippet, user):
|
|
277
|
+
buttons[:] = []
|
|
278
|
+
self.assertEqual(len(buttons), 0)
|
|
279
|
+
|
|
280
|
+
with hooks.register_temporarily(
|
|
281
|
+
"construct_snippet_listing_buttons",
|
|
282
|
+
remove_all_buttons,
|
|
283
|
+
):
|
|
284
|
+
response = self.get()
|
|
285
|
+
|
|
286
|
+
soup = self.get_soup(response.content)
|
|
287
|
+
actions = soup.select_one("tbody tr td ul.actions")
|
|
288
|
+
self.assertIsNone(actions)
|
|
289
|
+
|
|
290
|
+
def test_use_latest_draft_as_title(self):
|
|
291
|
+
snippet = DraftStateModel.objects.create(text="Draft-enabled Foo, Published")
|
|
292
|
+
snippet.save_revision().publish()
|
|
293
|
+
snippet.text = "Draft-enabled Bar, In Draft"
|
|
294
|
+
snippet.save_revision()
|
|
295
|
+
|
|
296
|
+
response = self.client.get(
|
|
297
|
+
reverse("wagtailsnippets_tests_draftstatemodel:list"),
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
edit_url = reverse(
|
|
301
|
+
"wagtailsnippets_tests_draftstatemodel:edit",
|
|
302
|
+
args=[quote(snippet.pk)],
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
# Should use the latest draft title in the listing
|
|
306
|
+
self.assertContains(
|
|
307
|
+
response,
|
|
308
|
+
f"""
|
|
309
|
+
<a href="{edit_url}">
|
|
310
|
+
<span id="snippet_{quote(snippet.pk)}_title">
|
|
311
|
+
Draft-enabled Bar, In Draft
|
|
312
|
+
</span>
|
|
313
|
+
</a>
|
|
314
|
+
""",
|
|
315
|
+
html=True,
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
def test_use_fallback_for_blank_string_representation(self):
|
|
319
|
+
snippet = DraftStateModel.objects.create(text="", live=False)
|
|
320
|
+
|
|
321
|
+
response = self.client.get(
|
|
322
|
+
reverse("wagtailsnippets_tests_draftstatemodel:list"),
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
edit_url = reverse(
|
|
326
|
+
"wagtailsnippets_tests_draftstatemodel:edit",
|
|
327
|
+
args=[quote(snippet.pk)],
|
|
328
|
+
)
|
|
329
|
+
title = f"DraftStateModel object ({snippet.pk})"
|
|
330
|
+
|
|
331
|
+
self.assertContains(
|
|
332
|
+
response,
|
|
333
|
+
f"""
|
|
334
|
+
<a href="{edit_url}">
|
|
335
|
+
<span id="snippet_{quote(snippet.pk)}_title">
|
|
336
|
+
{title}
|
|
337
|
+
</span>
|
|
338
|
+
</a>
|
|
339
|
+
""",
|
|
340
|
+
html=True,
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
def test_use_fallback_for_blank_title_field(self):
|
|
344
|
+
# FullFeaturedSnippet's listing view uses the "text" field as the title column,
|
|
345
|
+
# rather than the str() representation. If this is blank, we show "(blank)" so that
|
|
346
|
+
# there is something to click on
|
|
347
|
+
snippet = FullFeaturedSnippet.objects.create(text="", live=False)
|
|
348
|
+
response = self.client.get(
|
|
349
|
+
reverse("some_namespace:list"),
|
|
350
|
+
)
|
|
351
|
+
edit_url = reverse(
|
|
352
|
+
"some_namespace:edit",
|
|
353
|
+
args=[quote(snippet.pk)],
|
|
354
|
+
)
|
|
355
|
+
self.assertContains(
|
|
356
|
+
response,
|
|
357
|
+
f"""
|
|
358
|
+
<a href="{edit_url}">
|
|
359
|
+
<span id="snippet_{quote(snippet.pk)}_title">
|
|
360
|
+
(blank)
|
|
361
|
+
</span>
|
|
362
|
+
</a>
|
|
363
|
+
""",
|
|
364
|
+
html=True,
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
def test_bulk_action_rendered(self):
|
|
368
|
+
response = self.get()
|
|
369
|
+
self.assertEqual(response.status_code, 200)
|
|
370
|
+
# Should render bulk actions markup
|
|
371
|
+
bulk_actions_js = versioned_static("wagtailadmin/js/bulk-actions.js")
|
|
372
|
+
soup = self.get_soup(response.content)
|
|
373
|
+
script = soup.select_one(f"script[src='{bulk_actions_js}']")
|
|
374
|
+
self.assertIsNotNone(script)
|
|
375
|
+
bulk_actions = soup.select("[data-bulk-action-button]")
|
|
376
|
+
self.assertTrue(bulk_actions)
|
|
377
|
+
# 'next' parameter is constructed client-side later based on filters state
|
|
378
|
+
for action in bulk_actions:
|
|
379
|
+
self.assertNotIn("next=", action["href"])
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
@override_settings(WAGTAIL_I18N_ENABLED=True)
|
|
383
|
+
class TestLocaleFeaturesOnList(WagtailTestUtils, TestCase):
|
|
384
|
+
@classmethod
|
|
385
|
+
def setUpTestData(cls):
|
|
386
|
+
cls.fr_locale = Locale.objects.create(language_code="fr")
|
|
387
|
+
cls.list_url = reverse("wagtailsnippets_snippetstests_translatablesnippet:list")
|
|
388
|
+
cls.add_url = reverse("wagtailsnippets_snippetstests_translatablesnippet:add")
|
|
389
|
+
|
|
390
|
+
def setUp(self):
|
|
391
|
+
self.user = self.login()
|
|
392
|
+
|
|
393
|
+
def _add_snippets(self):
|
|
394
|
+
TranslatableSnippet.objects.create(text="English snippet")
|
|
395
|
+
TranslatableSnippet.objects.create(text="French snippet", locale=self.fr_locale)
|
|
396
|
+
|
|
397
|
+
@override_settings(
|
|
398
|
+
WAGTAIL_CONTENT_LANGUAGES=[
|
|
399
|
+
("ar", "Arabic"),
|
|
400
|
+
("en", "English"),
|
|
401
|
+
("fr", "French"),
|
|
402
|
+
]
|
|
403
|
+
)
|
|
404
|
+
def test_locale_selector(self):
|
|
405
|
+
response = self.client.get(self.list_url)
|
|
406
|
+
soup = self.get_soup(response.content)
|
|
407
|
+
|
|
408
|
+
# Should only show languages that also have the corresponding Locale
|
|
409
|
+
# (the Arabic locale is not created in the setup, so it should not be shown)
|
|
410
|
+
arabic_input = soup.select_one('input[name="locale"][value="ar"]')
|
|
411
|
+
self.assertIsNone(arabic_input)
|
|
412
|
+
|
|
413
|
+
french_input = soup.select_one('input[name="locale"][value="fr"]')
|
|
414
|
+
self.assertIsNotNone(french_input)
|
|
415
|
+
|
|
416
|
+
# Check that the add URLs include the locale
|
|
417
|
+
add_url = f"{self.add_url}?locale=en"
|
|
418
|
+
add_buttons = soup.select(f'a[href="{add_url}"]')
|
|
419
|
+
self.assertEqual(len(add_buttons), 2)
|
|
420
|
+
self.assertContains(
|
|
421
|
+
response,
|
|
422
|
+
f"""<p>There are no translatable snippets to display.
|
|
423
|
+
Why not <a href="{add_url}">add one</a>?</p>""",
|
|
424
|
+
html=True,
|
|
425
|
+
)
|
|
426
|
+
|
|
427
|
+
def test_no_locale_filter_when_only_one_locale(self):
|
|
428
|
+
self.fr_locale.delete()
|
|
429
|
+
response = self.client.get(self.list_url)
|
|
430
|
+
soup = self.get_soup(response.content)
|
|
431
|
+
|
|
432
|
+
locale_input = soup.select_one('input[name="locale"]')
|
|
433
|
+
self.assertIsNone(locale_input)
|
|
434
|
+
|
|
435
|
+
# The viewset has no other filters configured,
|
|
436
|
+
# so the filters drilldown should not be present
|
|
437
|
+
filters_drilldown = soup.select_one("#filters-drilldown")
|
|
438
|
+
self.assertIsNone(filters_drilldown)
|
|
439
|
+
|
|
440
|
+
@override_settings(WAGTAIL_I18N_ENABLED=False)
|
|
441
|
+
def test_locale_selector_not_present_when_i18n_disabled(self):
|
|
442
|
+
response = self.client.get(self.list_url)
|
|
443
|
+
soup = self.get_soup(response.content)
|
|
444
|
+
|
|
445
|
+
input_element = soup.select_one('input[name="locale"]')
|
|
446
|
+
self.assertIsNone(input_element)
|
|
447
|
+
|
|
448
|
+
# Check that the add URLs don't include the locale
|
|
449
|
+
add_url = self.add_url
|
|
450
|
+
soup = self.get_soup(response.content)
|
|
451
|
+
add_buttons = soup.select(f'a[href="{add_url}"]')
|
|
452
|
+
self.assertEqual(len(add_buttons), 2)
|
|
453
|
+
self.assertContains(
|
|
454
|
+
response,
|
|
455
|
+
f"""<p>There are no translatable snippets to display.
|
|
456
|
+
Why not <a href="{add_url}">add one</a>?</p>""",
|
|
457
|
+
html=True,
|
|
458
|
+
)
|
|
459
|
+
|
|
460
|
+
def test_locale_selector_not_present_on_non_translatable_snippet(self):
|
|
461
|
+
response = self.client.get(reverse("wagtailsnippets_tests_advert:list"))
|
|
462
|
+
soup = self.get_soup(response.content)
|
|
463
|
+
|
|
464
|
+
input_element = soup.select_one('input[name="locale"]')
|
|
465
|
+
self.assertIsNone(input_element)
|
|
466
|
+
|
|
467
|
+
# Check that the add URLs don't include the locale
|
|
468
|
+
add_url = reverse("wagtailsnippets_tests_advert:add")
|
|
469
|
+
soup = self.get_soup(response.content)
|
|
470
|
+
add_buttons = soup.select(f'a[href="{add_url}"]')
|
|
471
|
+
self.assertEqual(len(add_buttons), 2)
|
|
472
|
+
self.assertContains(
|
|
473
|
+
response,
|
|
474
|
+
f"""<p>There are no adverts to display.
|
|
475
|
+
Why not <a href="{add_url}">add one</a>?</p>""",
|
|
476
|
+
html=True,
|
|
477
|
+
)
|
|
478
|
+
|
|
479
|
+
def test_locale_column(self):
|
|
480
|
+
self._add_snippets()
|
|
481
|
+
response = self.client.get(self.list_url)
|
|
482
|
+
soup = self.get_soup(response.content)
|
|
483
|
+
labels = soup.select("main table td .w-status--label")
|
|
484
|
+
self.assertEqual(len(labels), 2)
|
|
485
|
+
self.assertEqual(
|
|
486
|
+
sorted(label.text.strip() for label in labels),
|
|
487
|
+
["English", "French"],
|
|
488
|
+
)
|
|
489
|
+
|
|
490
|
+
@override_settings(WAGTAIL_I18N_ENABLED=False)
|
|
491
|
+
def test_locale_column_not_present_with_i18n_disabled(self):
|
|
492
|
+
self._add_snippets()
|
|
493
|
+
response = self.client.get(self.list_url)
|
|
494
|
+
soup = self.get_soup(response.content)
|
|
495
|
+
labels = soup.select("main table td .w-status--label")
|
|
496
|
+
self.assertEqual(len(labels), 0)
|
|
497
|
+
|
|
498
|
+
def test_locale_column_not_present_for_non_translatable_snippet(self):
|
|
499
|
+
response = self.client.get(reverse("wagtailsnippets_tests_advert:list"))
|
|
500
|
+
Advert.objects.create(text="English text")
|
|
501
|
+
soup = self.get_soup(response.content)
|
|
502
|
+
labels = soup.select("main table td .w-status--label")
|
|
503
|
+
self.assertEqual(len(labels), 0)
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
class TestListViewOrdering(WagtailTestUtils, TestCase):
|
|
507
|
+
@classmethod
|
|
508
|
+
def setUpTestData(cls):
|
|
509
|
+
for i in range(1, 10):
|
|
510
|
+
advert = Advert.objects.create(text=f"{i * 'a'}dvert {i}")
|
|
511
|
+
draft = DraftStateModel.objects.create(
|
|
512
|
+
text=f"{i * 'd'}raft {i}", live=False
|
|
513
|
+
)
|
|
514
|
+
if i % 2 == 0:
|
|
515
|
+
ModelLogEntry.objects.create(
|
|
516
|
+
content_type=ContentType.objects.get_for_model(Advert),
|
|
517
|
+
label="Test Advert",
|
|
518
|
+
action="wagtail.create",
|
|
519
|
+
timestamp=now(),
|
|
520
|
+
object_id=advert.pk,
|
|
521
|
+
)
|
|
522
|
+
draft.save_revision().publish()
|
|
523
|
+
|
|
524
|
+
def setUp(self):
|
|
525
|
+
self.login()
|
|
526
|
+
|
|
527
|
+
def test_listing_orderable_columns_with_no_mixin(self):
|
|
528
|
+
list_url = reverse("wagtailsnippets_tests_advert:list")
|
|
529
|
+
response = self.client.get(list_url)
|
|
530
|
+
sort_updated_url = list_url + "?ordering=_updated_at"
|
|
531
|
+
sort_live_url = list_url + "?ordering=live"
|
|
532
|
+
|
|
533
|
+
self.assertEqual(response.status_code, 200)
|
|
534
|
+
# Should use the tables framework
|
|
535
|
+
self.assertTemplateUsed(response, "wagtailadmin/tables/table.html")
|
|
536
|
+
# The Updated column header should be a link with the correct query param
|
|
537
|
+
self.assertContains(
|
|
538
|
+
response,
|
|
539
|
+
f'<th><a href="{sort_updated_url}" title="Sort by 'Updated' in ascending order." class="icon icon-arrow-down-after label">Updated</a></th>',
|
|
540
|
+
html=True,
|
|
541
|
+
)
|
|
542
|
+
# Should not contain the Status column header
|
|
543
|
+
self.assertNotContains(
|
|
544
|
+
response,
|
|
545
|
+
f'<th><a href="{sort_live_url}" title="Sort by 'Status' in ascending order." class="icon icon-arrow-down-after label">Status</a></th>',
|
|
546
|
+
html=True,
|
|
547
|
+
)
|
|
548
|
+
|
|
549
|
+
def test_listing_orderable_columns_with_draft_state_mixin(self):
|
|
550
|
+
list_url = reverse("wagtailsnippets_tests_draftstatemodel:list")
|
|
551
|
+
response = self.client.get(list_url)
|
|
552
|
+
sort_updated_url = list_url + "?ordering=_updated_at"
|
|
553
|
+
sort_live_url = list_url + "?ordering=live"
|
|
554
|
+
|
|
555
|
+
self.assertEqual(response.status_code, 200)
|
|
556
|
+
# Should use the tables framework
|
|
557
|
+
self.assertTemplateUsed(response, "wagtailadmin/tables/table.html")
|
|
558
|
+
# The Updated column header should be a link with the correct query param
|
|
559
|
+
self.assertContains(
|
|
560
|
+
response,
|
|
561
|
+
f'<th><a href="{sort_updated_url}" title="Sort by 'Updated' in ascending order." class="icon icon-arrow-down-after label">Updated</a></th>',
|
|
562
|
+
html=True,
|
|
563
|
+
)
|
|
564
|
+
# The Status column header should be a link with the correct query param
|
|
565
|
+
self.assertContains(
|
|
566
|
+
response,
|
|
567
|
+
f'<th><a href="{sort_live_url}" title="Sort by 'Status' in ascending order." class="icon icon-arrow-down-after label">Status</a></th>',
|
|
568
|
+
html=True,
|
|
569
|
+
)
|
|
570
|
+
|
|
571
|
+
def test_order_by_updated_at_with_no_mixin(self):
|
|
572
|
+
list_url = reverse("wagtailsnippets_tests_advert:list")
|
|
573
|
+
response = self.client.get(list_url + "?ordering=_updated_at")
|
|
574
|
+
|
|
575
|
+
self.assertEqual(response.status_code, 200)
|
|
576
|
+
|
|
577
|
+
# With ascending order, empty updated_at information should be shown first
|
|
578
|
+
self.assertIsNone(response.context["page_obj"][0]._updated_at)
|
|
579
|
+
# The most recently updated should be at the bottom
|
|
580
|
+
self.assertEqual(response.context["page_obj"][-1].text, "aaaaaaaadvert 8")
|
|
581
|
+
self.assertIsNotNone(response.context["page_obj"][-1]._updated_at)
|
|
582
|
+
|
|
583
|
+
# Should contain a link to reverse the order
|
|
584
|
+
self.assertContains(response, list_url + "?ordering=-_updated_at")
|
|
585
|
+
|
|
586
|
+
response = self.client.get(list_url + "?ordering=-_updated_at")
|
|
587
|
+
|
|
588
|
+
self.assertEqual(response.status_code, 200)
|
|
589
|
+
|
|
590
|
+
# With descending order, the first object should be the one that was last updated
|
|
591
|
+
self.assertEqual(response.context["page_obj"][0].text, "aaaaaaaadvert 8")
|
|
592
|
+
self.assertIsNotNone(response.context["page_obj"][0]._updated_at)
|
|
593
|
+
|
|
594
|
+
# Should contain a link to reverse the order
|
|
595
|
+
self.assertContains(response, list_url + "?ordering=_updated_at")
|
|
596
|
+
|
|
597
|
+
def test_order_by_updated_at_with_draft_state_mixin(self):
|
|
598
|
+
list_url = reverse("wagtailsnippets_tests_draftstatemodel:list")
|
|
599
|
+
response = self.client.get(list_url + "?ordering=_updated_at")
|
|
600
|
+
|
|
601
|
+
self.assertEqual(response.status_code, 200)
|
|
602
|
+
|
|
603
|
+
# With ascending order, empty updated_at information should be shown first
|
|
604
|
+
self.assertIsNone(response.context["page_obj"][0]._updated_at)
|
|
605
|
+
# The most recently updated should be at the bottom
|
|
606
|
+
self.assertEqual(response.context["page_obj"][-1].text, "ddddddddraft 8")
|
|
607
|
+
self.assertIsNotNone(response.context["page_obj"][-1]._updated_at)
|
|
608
|
+
|
|
609
|
+
# Should contain a link to reverse the order
|
|
610
|
+
self.assertContains(response, list_url + "?ordering=-_updated_at")
|
|
611
|
+
|
|
612
|
+
response = self.client.get(list_url + "?ordering=-_updated_at")
|
|
613
|
+
|
|
614
|
+
self.assertEqual(response.status_code, 200)
|
|
615
|
+
|
|
616
|
+
# With descending order, the first object should be the one that was last updated
|
|
617
|
+
self.assertEqual(response.context["page_obj"][0].text, "ddddddddraft 8")
|
|
618
|
+
self.assertIsNotNone(response.context["page_obj"][0]._updated_at)
|
|
619
|
+
|
|
620
|
+
# Should contain a link to reverse the order
|
|
621
|
+
self.assertContains(response, list_url + "?ordering=_updated_at")
|
|
622
|
+
|
|
623
|
+
def test_order_by_live(self):
|
|
624
|
+
list_url = reverse("wagtailsnippets_tests_draftstatemodel:list")
|
|
625
|
+
response = self.client.get(list_url + "?ordering=live")
|
|
626
|
+
|
|
627
|
+
self.assertEqual(response.status_code, 200)
|
|
628
|
+
|
|
629
|
+
# With ascending order, live=False should be shown first
|
|
630
|
+
self.assertFalse(response.context["page_obj"][0].live)
|
|
631
|
+
# The last one should be live=True
|
|
632
|
+
self.assertTrue(response.context["page_obj"][-1].live)
|
|
633
|
+
|
|
634
|
+
# Should contain a link to reverse the order
|
|
635
|
+
self.assertContains(response, list_url + "?ordering=-live")
|
|
636
|
+
|
|
637
|
+
response = self.client.get(list_url + "?ordering=-live")
|
|
638
|
+
|
|
639
|
+
self.assertEqual(response.status_code, 200)
|
|
640
|
+
|
|
641
|
+
# With descending order, live=True should be shown first
|
|
642
|
+
self.assertTrue(response.context["page_obj"][0].live)
|
|
643
|
+
# The last one should be live=False
|
|
644
|
+
self.assertFalse(response.context["page_obj"][-1].live)
|
|
645
|
+
|
|
646
|
+
# Should contain a link to reverse the order
|
|
647
|
+
self.assertContains(response, list_url + "?ordering=live")
|
|
648
|
+
|
|
649
|
+
|
|
650
|
+
class TestSnippetListViewWithSearchableSnippet(WagtailTestUtils, TransactionTestCase):
|
|
651
|
+
def setUp(self):
|
|
652
|
+
self.login()
|
|
653
|
+
|
|
654
|
+
# Create some instances of the searchable snippet for testing
|
|
655
|
+
self.snippet_a = SearchableSnippet.objects.create(text="Hello")
|
|
656
|
+
self.snippet_b = SearchableSnippet.objects.create(text="World")
|
|
657
|
+
self.snippet_c = SearchableSnippet.objects.create(text="Hello World")
|
|
658
|
+
|
|
659
|
+
def get(self, params=None):
|
|
660
|
+
return self.client.get(
|
|
661
|
+
reverse("wagtailsnippets_snippetstests_searchablesnippet:list"),
|
|
662
|
+
params,
|
|
663
|
+
)
|
|
664
|
+
|
|
665
|
+
def test_simple(self):
|
|
666
|
+
response = self.get()
|
|
667
|
+
self.assertEqual(response.status_code, 200)
|
|
668
|
+
self.assertTemplateUsed(response, "wagtailsnippets/snippets/index.html")
|
|
669
|
+
|
|
670
|
+
# All snippets should be in items
|
|
671
|
+
items = list(response.context["page_obj"].object_list)
|
|
672
|
+
self.assertIn(self.snippet_a, items)
|
|
673
|
+
self.assertIn(self.snippet_b, items)
|
|
674
|
+
self.assertIn(self.snippet_c, items)
|
|
675
|
+
|
|
676
|
+
# The search box should not raise an error
|
|
677
|
+
self.assertNotContains(response, "This field is required.")
|
|
678
|
+
|
|
679
|
+
def test_empty_q(self):
|
|
680
|
+
response = self.get({"q": ""})
|
|
681
|
+
self.assertEqual(response.status_code, 200)
|
|
682
|
+
self.assertTemplateUsed(response, "wagtailsnippets/snippets/index.html")
|
|
683
|
+
|
|
684
|
+
# All snippets should be in items
|
|
685
|
+
items = list(response.context["page_obj"].object_list)
|
|
686
|
+
self.assertIn(self.snippet_a, items)
|
|
687
|
+
self.assertIn(self.snippet_b, items)
|
|
688
|
+
self.assertIn(self.snippet_c, items)
|
|
689
|
+
|
|
690
|
+
# The search box should not raise an error
|
|
691
|
+
self.assertNotContains(response, "This field is required.")
|
|
692
|
+
|
|
693
|
+
def test_is_searchable(self):
|
|
694
|
+
self.assertIsInstance(self.get().context["search_form"], SearchForm)
|
|
695
|
+
|
|
696
|
+
def test_search_hello(self):
|
|
697
|
+
response = self.get({"q": "Hello"})
|
|
698
|
+
|
|
699
|
+
# Just snippets with "Hello" should be in items
|
|
700
|
+
items = list(response.context["page_obj"].object_list)
|
|
701
|
+
self.assertIn(self.snippet_a, items)
|
|
702
|
+
self.assertNotIn(self.snippet_b, items)
|
|
703
|
+
self.assertIn(self.snippet_c, items)
|
|
704
|
+
|
|
705
|
+
def test_search_world_autocomplete(self):
|
|
706
|
+
response = self.get({"q": "wor"})
|
|
707
|
+
|
|
708
|
+
# Just snippets with "World" should be in items
|
|
709
|
+
items = list(response.context["page_obj"].object_list)
|
|
710
|
+
self.assertNotIn(self.snippet_a, items)
|
|
711
|
+
self.assertIn(self.snippet_b, items)
|
|
712
|
+
self.assertIn(self.snippet_c, items)
|
|
713
|
+
|
|
714
|
+
|
|
715
|
+
class TestSnippetListViewWithNonAutocompleteSearchableSnippet(
|
|
716
|
+
WagtailTestUtils, TransactionTestCase
|
|
717
|
+
):
|
|
718
|
+
"""
|
|
719
|
+
Test that searchable snippets with no AutocompleteFields defined can still be searched using
|
|
720
|
+
full words
|
|
721
|
+
"""
|
|
722
|
+
|
|
723
|
+
def setUp(self):
|
|
724
|
+
self.login()
|
|
725
|
+
|
|
726
|
+
# Create some instances of the searchable snippet for testing
|
|
727
|
+
self.snippet_a = NonAutocompleteSearchableSnippet.objects.create(text="Hello")
|
|
728
|
+
self.snippet_b = NonAutocompleteSearchableSnippet.objects.create(text="World")
|
|
729
|
+
self.snippet_c = NonAutocompleteSearchableSnippet.objects.create(
|
|
730
|
+
text="Hello World"
|
|
731
|
+
)
|
|
732
|
+
|
|
733
|
+
def get(self, params=None):
|
|
734
|
+
return self.client.get(
|
|
735
|
+
reverse(
|
|
736
|
+
"wagtailsnippets_snippetstests_nonautocompletesearchablesnippet:list"
|
|
737
|
+
),
|
|
738
|
+
params,
|
|
739
|
+
)
|
|
740
|
+
|
|
741
|
+
def test_search_hello(self):
|
|
742
|
+
with self.assertWarnsRegex(
|
|
743
|
+
RuntimeWarning, "does not specify any AutocompleteFields"
|
|
744
|
+
):
|
|
745
|
+
response = self.get({"q": "Hello"})
|
|
746
|
+
|
|
747
|
+
# Just snippets with "Hello" should be in items
|
|
748
|
+
items = list(response.context["page_obj"].object_list)
|
|
749
|
+
self.assertIn(self.snippet_a, items)
|
|
750
|
+
self.assertNotIn(self.snippet_b, items)
|
|
751
|
+
self.assertIn(self.snippet_c, items)
|
|
752
|
+
|
|
753
|
+
|
|
754
|
+
class TestSnippetListViewWithCustomPrimaryKey(WagtailTestUtils, TestCase):
|
|
755
|
+
def setUp(self):
|
|
756
|
+
self.login()
|
|
757
|
+
|
|
758
|
+
# Create some instances of the searchable snippet for testing
|
|
759
|
+
self.snippet_a = StandardSnippetWithCustomPrimaryKey.objects.create(
|
|
760
|
+
snippet_id="snippet/01", text="Hello"
|
|
761
|
+
)
|
|
762
|
+
self.snippet_b = StandardSnippetWithCustomPrimaryKey.objects.create(
|
|
763
|
+
snippet_id="snippet/02", text="Hello"
|
|
764
|
+
)
|
|
765
|
+
self.snippet_c = StandardSnippetWithCustomPrimaryKey.objects.create(
|
|
766
|
+
snippet_id="snippet/03", text="Hello"
|
|
767
|
+
)
|
|
768
|
+
|
|
769
|
+
def get(self, params=None):
|
|
770
|
+
return self.client.get(
|
|
771
|
+
reverse(
|
|
772
|
+
"wagtailsnippets_snippetstests_standardsnippetwithcustomprimarykey:list"
|
|
773
|
+
),
|
|
774
|
+
params,
|
|
775
|
+
)
|
|
776
|
+
|
|
777
|
+
def test_simple(self):
|
|
778
|
+
response = self.get()
|
|
779
|
+
self.assertEqual(response.status_code, 200)
|
|
780
|
+
self.assertTemplateUsed(response, "wagtailsnippets/snippets/index.html")
|
|
781
|
+
|
|
782
|
+
# All snippets should be in items
|
|
783
|
+
items = list(response.context["page_obj"].object_list)
|
|
784
|
+
self.assertIn(self.snippet_a, items)
|
|
785
|
+
self.assertIn(self.snippet_b, items)
|
|
786
|
+
self.assertIn(self.snippet_c, items)
|