From 58ce04f63da86dbeec9722e943a1f3023231f869 Mon Sep 17 00:00:00 2001 From: Eric Sherman Date: Wed, 11 Jan 2023 17:01:13 -0600 Subject: [PATCH 1/6] add test for lookup by url_path This also wraps the LOOKUP_FIELD initialization in a function, so that settings can be initialized correctly --- tests/tests/test_import.py | 67 ++++++++++++++++++++++++++++++++++++ wagtail_transfer/locators.py | 21 ++++++++--- 2 files changed, 84 insertions(+), 4 deletions(-) diff --git a/tests/tests/test_import.py b/tests/tests/test_import.py index 84595b4..302cabc 100644 --- a/tests/tests/test_import.py +++ b/tests/tests/test_import.py @@ -134,6 +134,73 @@ def test_import_pages(self): created_page_revision = created_page.get_latest_revision_as_page() self.assertEqual(created_page_revision.intro, "This page is imported from the source site") + @override_settings(WAGTAILTRANSFER_LOOKUP_FIELDS = {'tests.category': ['name'],'wagtailcore.page': ['url_path'],}) + def test_import_pages_via_lookup(self): + # make a draft edit to the homepage + home = SimplePage.objects.get(slug='home') + home.title = "Draft home" + home.save_revision() + + data = """{ + "ids_for_import": [ + ["wagtailcore.page", 12], + ["wagtailcore.page", 15] + ], + "mappings": [ + ["wagtailcore.page", 12, "/home/"], + ["wagtailcore.page", 15, "/home/imported-child-page"] + ], + "objects": [ + { + "model": "tests.simplepage", + "pk": 15, + "parent_id": 12, + "fields": { + "title": "Imported child page", + "show_in_menus": false, + "live": true, + "slug": "imported-child-page", + "intro": "This page is imported from the source site", + "wagtail_admin_comments": [] + } + }, + { + "model": "tests.simplepage", + "pk": 12, + "parent_id": 1, + "fields": { + "title": "New home", + "show_in_menus": false, + "live": true, + "slug": "home", + "intro": "This is the updated homepage", + "wagtail_admin_comments": [] + } + } + ] + }""" + + importer = ImportPlanner(root_page_source_pk=12, destination_parent_id=None) + importer.add_json(data) + importer.run() + + updated_page = SimplePage.objects.get(url_path='/home/') + self.assertEqual(updated_page.intro, "This is the updated homepage") + self.assertEqual(updated_page.title, "New home") + self.assertEqual(updated_page.draft_title, "New home") + + # get_latest_revision (as used in the edit-page view) should also reflect the imported content + updated_page_revision = updated_page.get_latest_revision_as_page() + self.assertEqual(updated_page_revision.intro, "This is the updated homepage") + self.assertEqual(updated_page_revision.title, "New home") + + created_page = SimplePage.objects.get(url_path='/home/imported-child-page/') + self.assertEqual(created_page.intro, "This page is imported from the source site") + # An initial page revision should also be created + self.assertTrue(created_page.get_latest_revision()) + created_page_revision = created_page.get_latest_revision_as_page() + self.assertEqual(created_page_revision.intro, "This page is imported from the source site") + def test_import_pages_with_fk(self): data = """{ "ids_for_import": [ diff --git a/wagtail_transfer/locators.py b/wagtail_transfer/locators.py index 7be2e91..00db8de 100644 --- a/wagtail_transfer/locators.py +++ b/wagtail_transfer/locators.py @@ -23,8 +23,13 @@ 'taggit.tag': ['slug'], # sensible default for taggit; can still be overridden 'wagtailcore.locale': ["language_code"] } -for model_label, fields in getattr(settings, 'WAGTAILTRANSFER_LOOKUP_FIELDS', {}).items(): - LOOKUP_FIELDS[model_label.lower()] = fields +def get_lookup_fields(LOOKUP_FIELDS): + """ get all fields for lookup, including those declared in settings """ + for model_label, fields in getattr(settings, 'WAGTAILTRANSFER_LOOKUP_FIELDS', {}).items(): + LOOKUP_FIELDS[model_label.lower()] = fields + return LOOKUP_FIELDS + + class IDMappingLocator: @@ -122,12 +127,19 @@ def uid_from_json(self, json_uid): # A UID coming from JSON data will arrive as a list (because JSON has no tuple type), # but we need a tuple because the importer logic expects a hashable type that we can use # in sets and dict keys + if type(json_uid) is str: + json_uid = [json_uid] return tuple(json_uid) def find(self, uid): # pair up field names with their respective items in the UID tuple, to form a filter dict # that we can use for an ORM lookup - filters = dict(zip(self.fields, uid)) + if type(uid) == tuple: + filters = dict(zip(self.fields, uid)) + elif type(uid) == str: + # if lookup fields are configured for wagtailcore.page, then in the admin view those + # fields get passed along as a string + filters = dict(zip(self.fields, uid.split(","))) try: return self.model.objects.get(**filters) @@ -138,9 +150,10 @@ def find(self, uid): @lru_cache(maxsize=None) def get_locator_for_model(model): base_model = get_base_model(model) + field_lookups = get_lookup_fields(LOOKUP_FIELDS) try: # Use FieldLocator if an entry exists in LOOKUP_FIELDS - fields = LOOKUP_FIELDS[base_model._meta.label_lower] + fields = field_lookups[base_model._meta.label_lower] return FieldLocator(base_model, fields) except KeyError: # Fall back on IDMappingLocator From 6cccf717549e4f7de2ea9d4ee7e01eed91c5c785 Mon Sep 17 00:00:00 2001 From: Eric Sherman Date: Wed, 11 Jan 2023 17:51:41 -0600 Subject: [PATCH 2/6] api test for field lookup --- tests/tests/test_api.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/tests/test_api.py b/tests/tests/test_api.py index c96388e..527d600 100644 --- a/tests/tests/test_api.py +++ b/tests/tests/test_api.py @@ -460,6 +460,18 @@ def test_related_model_with_field_lookup(self): # Category objects in the mappings section should be identified by name, not UUID self.assertIn(['tests.category', 1, ['Cars']], mappings) + + @override_settings(WAGTAILTRANSFER_LOOKUP_FIELDS = {'tests.category': ['name'],'wagtailcore.page': ['url_path'],}) + def test_get_page_with_field_lookup(self): + response = self.get(5) + self.assertEqual(response.status_code, 200) + data = response.json() + mappings = data['mappings'] + + # Page objects in the mappings section should be identified by url_path + self.assertIn(['wagtailcore.page', 5, ['/home/oil-is-great/']], mappings) + + class TestObjectsApi(TestCase): From 9ac5966724f04da93f97bbf8453ae64d73d6ba16 Mon Sep 17 00:00:00 2001 From: Eric Sherman Date: Thu, 12 Jan 2023 10:17:42 -0600 Subject: [PATCH 3/6] clear cache for test settings --- tests/settings.py | 10 ++++++++++ wagtail_transfer/locators.py | 14 ++++++-------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/tests/settings.py b/tests/settings.py index 23bf1cd..6df6244 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -1,4 +1,5 @@ import os +from django.core.signals import setting_changed BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) from wagtail import VERSION as WAGTAIL_VERSION @@ -150,6 +151,15 @@ 'tests.category': ['name'] } +# some settings are modified in tests, in which case this caching interfers with the ability to test +# effectivley. Since settings are unlikely to change in the middle of usage in the real world, +# it makes sense to clear these just inside our test settings +def clear_locator_cache(setting, value, **kwargs): + from wagtail_transfer.locators import get_locator_for_model + get_locator_for_model.cache_clear() + +setting_changed.connect(clear_locator_cache) + # The default name for the Page -> Comment relation from Wagtail 2.15 onward. Setting this ensures that # 2.13.x (from 2.13.5 onward) and 2.14.x (from 2.14.2 onward) adopt the 2.15 behaviour, allowing us to # use the same test fixtures across all versions. diff --git a/wagtail_transfer/locators.py b/wagtail_transfer/locators.py index 00db8de..cb24f85 100644 --- a/wagtail_transfer/locators.py +++ b/wagtail_transfer/locators.py @@ -23,14 +23,12 @@ 'taggit.tag': ['slug'], # sensible default for taggit; can still be overridden 'wagtailcore.locale': ["language_code"] } -def get_lookup_fields(LOOKUP_FIELDS): +def get_lookup_fields(): """ get all fields for lookup, including those declared in settings """ + lookup_fields = LOOKUP_FIELDS.copy() for model_label, fields in getattr(settings, 'WAGTAILTRANSFER_LOOKUP_FIELDS', {}).items(): - LOOKUP_FIELDS[model_label.lower()] = fields - return LOOKUP_FIELDS - - - + lookup_fields[model_label.lower()] = fields + return lookup_fields class IDMappingLocator: def __init__(self, model): @@ -150,10 +148,10 @@ def find(self, uid): @lru_cache(maxsize=None) def get_locator_for_model(model): base_model = get_base_model(model) - field_lookups = get_lookup_fields(LOOKUP_FIELDS) + lookup_fields = get_lookup_fields() try: # Use FieldLocator if an entry exists in LOOKUP_FIELDS - fields = field_lookups[base_model._meta.label_lower] + fields = lookup_fields[base_model._meta.label_lower] return FieldLocator(base_model, fields) except KeyError: # Fall back on IDMappingLocator From e0c1a29d86104fa2d0ae867e4b55fc46b5868d6c Mon Sep 17 00:00:00 2001 From: Eric Sherman Date: Thu, 12 Jan 2023 13:04:24 -0600 Subject: [PATCH 4/6] add api test to get page with multiple lookups --- tests/tests/test_api.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/tests/test_api.py b/tests/tests/test_api.py index 527d600..82c638c 100644 --- a/tests/tests/test_api.py +++ b/tests/tests/test_api.py @@ -471,6 +471,16 @@ def test_get_page_with_field_lookup(self): # Page objects in the mappings section should be identified by url_path self.assertIn(['wagtailcore.page', 5, ['/home/oil-is-great/']], mappings) + @override_settings(WAGTAILTRANSFER_LOOKUP_FIELDS = {'tests.category': ['name'],'wagtailcore.page': ['slug', 'locale_id'],}) + def test_get_page_with_field_lookup_multiple(self): + response = self.get(5) + self.assertEqual(response.status_code, 200) + data = response.json() + mappings = data['mappings'] + + # Page objects in the mappings section should be identified by url_path + self.assertIn(['wagtailcore.page', 5, ['oil-is-great', 1]], mappings) + From 89fdf70478f0b61fbe71eeb2d116c428346059bb Mon Sep 17 00:00:00 2001 From: Eric Sherman Date: Thu, 12 Jan 2023 13:05:23 -0600 Subject: [PATCH 5/6] add import tests with lookup fields for page --- tests/tests/test_import.py | 199 ++++++++++++++++++++++++------------- 1 file changed, 132 insertions(+), 67 deletions(-) diff --git a/tests/tests/test_import.py b/tests/tests/test_import.py index 302cabc..7f021aa 100644 --- a/tests/tests/test_import.py +++ b/tests/tests/test_import.py @@ -134,73 +134,6 @@ def test_import_pages(self): created_page_revision = created_page.get_latest_revision_as_page() self.assertEqual(created_page_revision.intro, "This page is imported from the source site") - @override_settings(WAGTAILTRANSFER_LOOKUP_FIELDS = {'tests.category': ['name'],'wagtailcore.page': ['url_path'],}) - def test_import_pages_via_lookup(self): - # make a draft edit to the homepage - home = SimplePage.objects.get(slug='home') - home.title = "Draft home" - home.save_revision() - - data = """{ - "ids_for_import": [ - ["wagtailcore.page", 12], - ["wagtailcore.page", 15] - ], - "mappings": [ - ["wagtailcore.page", 12, "/home/"], - ["wagtailcore.page", 15, "/home/imported-child-page"] - ], - "objects": [ - { - "model": "tests.simplepage", - "pk": 15, - "parent_id": 12, - "fields": { - "title": "Imported child page", - "show_in_menus": false, - "live": true, - "slug": "imported-child-page", - "intro": "This page is imported from the source site", - "wagtail_admin_comments": [] - } - }, - { - "model": "tests.simplepage", - "pk": 12, - "parent_id": 1, - "fields": { - "title": "New home", - "show_in_menus": false, - "live": true, - "slug": "home", - "intro": "This is the updated homepage", - "wagtail_admin_comments": [] - } - } - ] - }""" - - importer = ImportPlanner(root_page_source_pk=12, destination_parent_id=None) - importer.add_json(data) - importer.run() - - updated_page = SimplePage.objects.get(url_path='/home/') - self.assertEqual(updated_page.intro, "This is the updated homepage") - self.assertEqual(updated_page.title, "New home") - self.assertEqual(updated_page.draft_title, "New home") - - # get_latest_revision (as used in the edit-page view) should also reflect the imported content - updated_page_revision = updated_page.get_latest_revision_as_page() - self.assertEqual(updated_page_revision.intro, "This is the updated homepage") - self.assertEqual(updated_page_revision.title, "New home") - - created_page = SimplePage.objects.get(url_path='/home/imported-child-page/') - self.assertEqual(created_page.intro, "This page is imported from the source site") - # An initial page revision should also be created - self.assertTrue(created_page.get_latest_revision()) - created_page_revision = created_page.get_latest_revision_as_page() - self.assertEqual(created_page_revision.intro, "This page is imported from the source site") - def test_import_pages_with_fk(self): data = """{ "ids_for_import": [ @@ -805,6 +738,138 @@ def test_import_page_with_new_list_block_format(self): self.assertEqual(imported_streamfield, [{'type': 'list_of_captioned_pages', 'value': [{'type': 'item', 'value': {'page': 1, 'text': 'a caption'}, 'id': '8c0d7de7-4f77-4477-be67-7d990d0bfb82'}], 'id': '21ffe52a-c0fc-4ecc-92f1-17b356c9cc94'}]) + @override_settings(WAGTAILTRANSFER_LOOKUP_FIELDS = {'tests.category': ['name'],'wagtailcore.page': ['url_path'],}) + def test_import_pages_via_lookup(self): + # make a draft edit to the homepage + home = SimplePage.objects.get(slug='home') + home.title = "Draft home" + home.save_revision() + data = """{ + "ids_for_import": [ + ["wagtailcore.page", 12], + ["wagtailcore.page", 15] + ], + "mappings": [ + ["wagtailcore.page", 12, "/home/"], + ["wagtailcore.page", 15, "/home/imported-child-page"] + ], + "objects": [ + { + "model": "tests.simplepage", + "pk": 15, + "parent_id": 12, + "fields": { + "title": "Imported child page", + "show_in_menus": false, + "live": true, + "slug": "imported-child-page", + "intro": "This page is imported from the source site", + "wagtail_admin_comments": [] + } + }, + { + "model": "tests.simplepage", + "pk": 12, + "parent_id": 1, + "fields": { + "title": "New home", + "show_in_menus": false, + "live": true, + "slug": "home", + "intro": "This is the updated homepage", + "wagtail_admin_comments": [] + } + } + ] + }""" + + importer = ImportPlanner(root_page_source_pk=12, destination_parent_id=None) + importer.add_json(data) + importer.run() + + updated_page = SimplePage.objects.get(url_path='/home/') + self.assertEqual(updated_page.intro, "This is the updated homepage") + self.assertEqual(updated_page.title, "New home") + self.assertEqual(updated_page.draft_title, "New home") + + # get_latest_revision (as used in the edit-page view) should also reflect the imported content + updated_page_revision = updated_page.get_latest_revision_as_page() + self.assertEqual(updated_page_revision.intro, "This is the updated homepage") + self.assertEqual(updated_page_revision.title, "New home") + + created_page = SimplePage.objects.get(url_path='/home/imported-child-page/') + self.assertEqual(created_page.intro, "This page is imported from the source site") + # An initial page revision should also be created + self.assertTrue(created_page.get_latest_revision()) + created_page_revision = created_page.get_latest_revision_as_page() + self.assertEqual(created_page_revision.intro, "This page is imported from the source site") + + @override_settings(WAGTAILTRANSFER_LOOKUP_FIELDS = {'tests.category': ['name'],'wagtailcore.page': ['slug', 'locale_id'],}) + def test_import_pages_via_lookup_multiple(self): + # make a draft edit to the homepage + home = SimplePage.objects.get(slug='home') + home.title = "Draft home" + home.save_revision() + data = """{ + "ids_for_import": [ + ["wagtailcore.page", 12], + ["wagtailcore.page", 15] + ], + "mappings": [ + ["wagtailcore.page", 12, ["home", 1]], + ["wagtailcore.page", 15, ["imported-child-page",1]] + ], + "objects": [ + { + "model": "tests.simplepage", + "pk": 15, + "parent_id": 12, + "fields": { + "title": "Imported child page", + "show_in_menus": false, + "live": true, + "slug": "imported-child-page", + "intro": "This page is imported from the source site", + "wagtail_admin_comments": [] + } + }, + { + "model": "tests.simplepage", + "pk": 12, + "parent_id": 1, + "fields": { + "title": "New home", + "show_in_menus": false, + "live": true, + "slug": "home", + "intro": "This is the updated homepage", + "wagtail_admin_comments": [] + } + } + ] + }""" + + importer = ImportPlanner(root_page_source_pk=12, destination_parent_id=None) + importer.add_json(data) + importer.run() + + updated_page = SimplePage.objects.get(url_path='/home/') + self.assertEqual(updated_page.intro, "This is the updated homepage") + self.assertEqual(updated_page.title, "New home") + self.assertEqual(updated_page.draft_title, "New home") + + # get_latest_revision (as used in the edit-page view) should also reflect the imported content + updated_page_revision = updated_page.get_latest_revision_as_page() + self.assertEqual(updated_page_revision.intro, "This is the updated homepage") + self.assertEqual(updated_page_revision.title, "New home") + + created_page = SimplePage.objects.get(url_path='/home/imported-child-page/') + self.assertEqual(created_page.intro, "This page is imported from the source site") + # An initial page revision should also be created + self.assertTrue(created_page.get_latest_revision()) + created_page_revision = created_page.get_latest_revision_as_page() + self.assertEqual(created_page_revision.intro, "This page is imported from the source site") + @mock.patch('requests.get') def test_import_image_with_file(self, get): get.return_value.status_code = 200 From 8974f04c3003a1a5df40dc4a1813b83c95c93595 Mon Sep 17 00:00:00 2001 From: Eric Sherman Date: Thu, 12 Jan 2023 13:05:54 -0600 Subject: [PATCH 6/6] test page existence view parses correctly --- tests/tests/test_views.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/tests/test_views.py b/tests/tests/test_views.py index 668f493..e6bc81b 100644 --- a/tests/tests/test_views.py +++ b/tests/tests/test_views.py @@ -24,6 +24,17 @@ def test_get(self): self.assertEqual(response.status_code, 200) self.assertContains(response, 'data-wagtail-component="content-import-form"') +class TestCheckUIDView(TestCase): + fixtures = ['test.json'] + + def setUp(self): + self.client.login(username='admin', password='password') + + def test_get(self): + with self.settings(WAGTAILTRANSFER_LOOKUP_FIELDS = {'wagtailcore.page': ['slug', 'locale_id'],}): + # the view should parse comma-seperated params correctly + response = self.client.get('/admin/wagtail-transfer/api/check_uid/?uid=home,1') + self.assertEqual(response.status_code, 200) @mock.patch('requests.post') @mock.patch('requests.get') @@ -383,6 +394,7 @@ def _test_view(self, method, url, data=None, success_url=None): with self.subTest(user=user): self.client.login(username=user.username, password="password") request = getattr(self.client, method) + breakpoint() response = request(url, data) if success_url: self.assertRedirects(response, success_url)