From b3f0fcb5c513aa899406cc918ee425db2bf43ce2 Mon Sep 17 00:00:00 2001 From: duker33 Date: Tue, 25 Dec 2018 18:54:46 +0300 Subject: [PATCH] #340 Improve Tags tests (#387) * #340 Rm code doubling with BaseCatalogTestCase class * #340 Create selenium test for products count --- stroyprombeton/tests/tests_selenium.py | 27 +++- stroyprombeton/tests/tests_views.py | 132 ++++++++----------- templates/catalog/category.html | 2 +- templates/components/show_more_products.html | 1 + 4 files changed, 86 insertions(+), 76 deletions(-) diff --git a/stroyprombeton/tests/tests_selenium.py b/stroyprombeton/tests/tests_selenium.py index 34c3c6d0..a9751290 100644 --- a/stroyprombeton/tests/tests_selenium.py +++ b/stroyprombeton/tests/tests_selenium.py @@ -345,6 +345,13 @@ def click_load_more_button(self): lambda browser: self.get_tables_rows_count(browser) > count ) + def get_loaded_products_count(self): + return ( + self.browser + .find_element_by_id(self.LOAD_MORE_ID) + .get_attribute('data-total-products') + ) + def is_load_more_disabled(self, browser=None): browser = browser or self.browser return 'disabled' in ( @@ -445,7 +452,7 @@ def wait_filter(browser): self.assertGreater(before_filter_products, after_filter_products) - def test_filter_by_tag_button(self): + def test_tag_button_change_url(self): """Filter button with the filled one of tag checkboxes should change url to tag.""" # set one product with no tags in test_db # check if this prod is not in list after filtering @@ -462,6 +469,24 @@ def test_filter_by_tag_button(self): self.assertIn(tagged_category_path, self.browser.current_url) self.browser.find_element_by_class_name('js-clear-tag-filter').click() + def test_tag_button_filter_products(self): + # this category contains 25 tags. It's less then products on page limit. + category = stb_models.Category.objects.get(name='Category #1 of #2') + tag_slug = '1-m' + tag_selector = self.FILTER_TAG_TEMPLATE.format(tag_slug=tag_slug) + self.load_category_page(category) + before_products_count = self.get_loaded_products_count() + + self.browser.find_element_by_css_selector(tag_selector).click() + self.apply_tags() + + after_products_count = self.get_loaded_products_count() + self.assertGreater(int(before_products_count), int(after_products_count)) + + self.browser.find_element_by_class_name('js-clear-tag-filter').click() + after_products_count = self.get_loaded_products_count() + self.assertEqual(int(before_products_count), int(after_products_count)) + def test_flush_button(self): category = self.root_category self.load_category_page( diff --git a/stroyprombeton/tests/tests_views.py b/stroyprombeton/tests/tests_views.py index bda3be1e..973b7e87 100644 --- a/stroyprombeton/tests/tests_views.py +++ b/stroyprombeton/tests/tests_views.py @@ -36,6 +36,40 @@ def json_to_dict(response: HttpResponse) -> dict(): return json.loads(response.content) +class BaseCatalogTestCase(TestCase): + + fixtures = ['dump.json'] + + def setUp(self): + self.root_category = models.Category.objects.filter(parent=None).first() + self.category = models.Category.objects.root_nodes().select_related('page').first() + self.tags = models.Tag.objects.order_by(*settings.TAGS_ORDER).all() + + def get_category_url( + self, + category: models.Category = None, + tags: models.TagQuerySet = None, + sorting: int = None, + query_string: dict = None, + ): + category = category or self.category + return reverse_catalog_url( + 'category', {'category_id': category.id}, tags, sorting, query_string, + ) + + def get_category_page(self, *args, **kwargs): + return self.client.get(self.get_category_url(*args, **kwargs)) + + def get_category_soup(self, *args, **kwargs) -> BeautifulSoup: + category_page = self.get_category_page(*args, **kwargs) + return BeautifulSoup( + category_page.content.decode('utf-8'), + 'html.parser' + ) + + +# @todo #340:60m Move TestPageMixin to some PageData class. +# And remove CategoryTable().response field. class TestPageMixin: @property @@ -128,7 +162,7 @@ def test_children_category_name(self): @tag('fast') -class CategoryTable(TestCase, TestPageMixin): +class CategoryTable(BaseCatalogTestCase, TestPageMixin): """ Test for CategoryPage view. @@ -139,6 +173,8 @@ class CategoryTable(TestCase, TestPageMixin): def setUp(self): """Create category and product.""" + super().setUp() + category_data = { 'name': 'Test root category', 'page': ModelPage.objects.create( @@ -146,8 +182,6 @@ def setUp(self): content='Козырьки устанавливают над входами зданий.', ) } - - self.root_category = models.Category.objects.filter(parent=None).first() self.category = models.Category.objects.create(**category_data) self.data = { @@ -161,39 +195,14 @@ def setUp(self): content='Козырьки устанавливают над входами зданий.', ) } - models.Product.objects.create(**self.data) self.response = self.client.get(self.get_category_url()) - def get_category_url( - self, - category: models.Category = None, - tags: models.TagQuerySet = None, - sorting: int = None, - query_string: dict = None, - ): - category = category or self.category - return reverse_catalog_url( - 'category', {'category_id': category.id}, tags, sorting, query_string, - ) - @property def response_product(self): return self.response.context['products'][0] - def get_category_page( # Ignore CPDBear - self, - category: models.Category=None, - tags: models.TagQuerySet=None, - sorting: int=None, - query_string: dict=None, - ): - category = category or self.category - return self.client.get(reverse_catalog_url( - 'category', {'category_id': category.id}, tags, sorting, query_string, - )) - def test_products_quantity(self): self.assertEqual(len(self.response.context['products']), 1) @@ -223,25 +232,6 @@ def test_inactive_product_not_in_category(self): response = self.client.get(reverse('category', args=(test_product.category_id,))) self.assertNotIn(test_product, response.context['products']) - # @todo #320:60m Take CatalogTags tests from SE. - # `shopelectro.tests.tests_views.CatalogTags` - def test_filter_products_by_tags(self): - """Category page should not contain products, excluded by tags selection.""" - tag_slug = '2-m' - tag_qs = models.Tag.objects.filter(slug=tag_slug) - tag = tag_qs.first() - response = self.get_category_page(category=self.root_category, tags=tag_qs) - - # find product: it has no tag and it's descendant of root_category - disappeared_products = ( - models.Product.objects.active() - .get_category_descendants(self.root_category) - .exclude(tags=tag) - ) - self.assertFalse( - any(p.name in response.content.decode() for p in disappeared_products) - ) - def test_load_more_context_data(self): """App should response with products data on load_more request.""" db_products = models.Product.objects.active().get_category_descendants( @@ -601,33 +591,10 @@ def test_price_list(self): @tag('fast') -class CatalogTags(TestCase, CategoryTestMixin): +class CatalogTags(BaseCatalogTestCase, CategoryTestMixin): fixtures = ['dump.json'] - def setUp(self): - self.category = models.Category.objects.root_nodes().select_related('page').first() - self.tags = models.Tag.objects.order_by(*settings.TAGS_ORDER).all() - - def get_category_page( - self, - category: models.Category=None, - tags: models.TagQuerySet=None, - sorting: int=None, - query_string: dict=None, - ): - category = category or self.category - return self.client.get(reverse_catalog_url( - 'category', {'category_id': category.id}, tags, query_string, - )) - - def get_category_soup(self, *args, **kwargs) -> BeautifulSoup: - category_page = self.get_category_page(*args, **kwargs) - return BeautifulSoup( - category_page.content.decode('utf-8'), - 'html.parser' - ) - def test_category_page_contains_all_tags(self): """Category contains all Product's tags.""" response = self.client.get(self.get_category_path()) @@ -721,12 +688,12 @@ def test_tag_titles_content_conjunction(self): tag_titles = delimiter.join(t.name for t in tags) self.assertContains(response, tag_titles) - def test_tags_var(self): + def test_tags_var_in_db_template(self): """ Test CategoryTagsPage with canonical tags. - CategoryTagsPage should contain "tags" template var tag=each(tags) is Tag - class instance. + "tags" db template at CategoryTagsPage + should render tag names. For example "1 м, 20 кг". """ tags = models.Tag.objects.order_by(*settings.TAGS_ORDER).all() response = self.client.get(self.get_category_path(tags=tags)) @@ -805,3 +772,20 @@ def test_too_many_tags_collapse(self): # @todo #374:30m Create test for from_index, to_index correctness. # Dangerous case: ['1 м', '2 м', '11 м'] -> 'от 1 м до 2 м' self.assertIn(f'от {from_index} м до {to_index - 1} м', tags_text) + + def test_filter_products_by_tags(self): + """Category page should not contain products, excluded by tags selection.""" + tag_slug = '2-m' + tag_qs = models.Tag.objects.filter(slug=tag_slug) + tag = tag_qs.first() + response = self.get_category_page(category=self.root_category, tags=tag_qs) + + # find product: it has no tag and it's descendant of root_category + disappeared_products = ( + models.Product.objects.active() + .get_category_descendants(self.root_category) + .exclude(tags=tag) + ) + self.assertFalse( + any(p.name in response.content.decode() for p in disappeared_products) + ) diff --git a/templates/catalog/category.html b/templates/catalog/category.html index c7535136..6d0a2a40 100644 --- a/templates/catalog/category.html +++ b/templates/catalog/category.html @@ -61,7 +61,7 @@

Товары в категории

- {% include 'components/show_more_products.html' with category=category only %} + {% include 'components/show_more_products.html' with category=category total_products=total_products only %} {% include 'catalog/seo_links.html' with page_obj=page_obj paginator_links=paginator_links pagination_param=pagination_param only %} diff --git a/templates/components/show_more_products.html b/templates/components/show_more_products.html index 2fa2fcf7..8d2fa12d 100644 --- a/templates/components/show_more_products.html +++ b/templates/components/show_more_products.html @@ -3,6 +3,7 @@ data-category="{{ category.id }}" data-url="{{ category.url }}" data-load-count="30" + data-total-products="{{ total_products }}" > Показать еще