Repository: fidals/shopelectro

Issue:
Use SiteDriver class instead of seleniumrequests.Remote
It will bring ability to use `shopelectro.selenium` classes in tests. 


Discussion:

Resolving patch:
diff --git a/shopelectro/selenium/pages/order.py b/shopelectro/selenium/pages/order.py
--- a/shopelectro/selenium/pages/order.py
+++ b/shopelectro/selenium/pages/order.py
@@ -3,6 +3,7 @@
 from shopelectro.selenium.pages import Page
 
 from selenium.webdriver.common.by import By
+from selenium.webdriver.support import expected_conditions as EC
 
 from pages.models import CustomPage
 
@@ -34,6 +35,7 @@ def fill_contacts(
 
     def make_order(self):
         self.submit_button.click()
+        self.driver.wait.until(EC.url_changes(self.path))
 
     def select_payment_type(self, payment_option: PaymentOptions):
         if payment_option not in PaymentOptions:
diff --git a/shopelectro/selenium/pages/page.py b/shopelectro/selenium/pages/page.py
--- a/shopelectro/selenium/pages/page.py
+++ b/shopelectro/selenium/pages/page.py
@@ -1,3 +1,5 @@
+from functools import wraps
+
 from shopelectro.selenium import SiteDriver
 
 from selenium.webdriver.common.by import By
@@ -17,10 +19,17 @@ def __init__(self, driver: SiteDriver):
         self.driver = driver
         self.path: str
 
+    def wait_loaded(self):
+        def loaded(driver):
+            is_sync = EC.url_contains(self.path)
+            is_rendered = EC.visibility_of_element_located(
+                (By.TAG_NAME, 'body')
+            )
+            return is_sync(driver) and is_rendered(driver)
+        self.driver.wait.until(loaded)
+
     def load(self):
         if not self.path:
             raise ValueError(f'Set a page path to {self.__class__.__name__}')
         self.driver.get(self.path)
-        self.driver.wait.until(EC.visibility_of_element_located(
-            (By.TAG_NAME, 'body')
-        ))
+        self.wait_loaded()
diff --git a/shopelectro/selenium/pages/success.py b/shopelectro/selenium/pages/success.py
--- a/shopelectro/selenium/pages/success.py
+++ b/shopelectro/selenium/pages/success.py
@@ -1,3 +1,6 @@
+from selenium.webdriver.common.by import By
+from selenium.webdriver.support import expected_conditions as EC
+
 from shopelectro.selenium.pages import Page
 
 from pages.models import CustomPage
@@ -7,7 +10,10 @@ class SuccessPage(Page):
 
     @property
     def path(self):
-        CustomPage.objects.get(slug='order-success').url
+        return CustomPage.objects.get(slug='order-success').url
 
     def is_success(self):
-        return 'Заказ принят' in self.driver.find_element_by_tag_name('h1').text
+        h1 = self.driver.wait.until(
+            EC.visibility_of_element_located((By.TAG_NAME, 'h1'))
+        ).text
+        return 'Заказ принят' in h1


Test patch:
diff --git a/shopelectro/tests/helpers.py b/shopelectro/tests/helpers.py
--- a/shopelectro/tests/helpers.py
+++ b/shopelectro/tests/helpers.py
@@ -8,11 +8,10 @@
 from selenium.common.exceptions import InvalidElementStateException, WebDriverException
 from selenium.webdriver.common.by import By
 from selenium.webdriver.common.action_chains import ActionChains
-from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
 from selenium.webdriver.remote.webelement import WebElement
 from selenium.webdriver.support import expected_conditions as EC
-from selenium.webdriver.support.ui import WebDriverWait
-from seleniumrequests import Remote  # We use this instead of standard selenium
+
+from shopelectro.selenium import SiteDriver
 
 disable_celery = override_settings(USE_CELERY=False)
 enable_russian_language = override_settings(
@@ -53,13 +52,9 @@ class SeleniumTestCase(LiveServerTestCase):
     def setUpClass(cls):
         """Instantiate browser instance."""
         super().setUpClass()
-        cls.browser = Remote(
-            command_executor=settings.SELENIUM_URL,
-            desired_capabilities=DesiredCapabilities.CHROME
-        )
+        cls.browser = SiteDriver(site_url=cls.live_server_url)
         # @todo #371:15m Move selenium timeout to env var. stb2
         #  To be able to change it from drone without touching code.
-        cls.wait = WebDriverWait(cls.browser, settings.SELENIUM_WAIT_SECONDS)
         cls.browser.implicitly_wait(30)
         cls.browser.set_page_load_timeout(settings.SELENIUM_TIMEOUT_SECONDS)
         # Fresh created browser failures on maximizing window.
@@ -71,7 +66,11 @@ def setUpClass(cls):
         try:
             cls.browser.maximize_window()
         except WebDriverException:
-            print('Failed to maximize window')
+            print('Failed to maximize window')  # Ignore CPDBear
+
+    @property
+    def wait(self):
+        return self.browser.wait
 
     @classmethod
     def tearDownClass(cls):
diff --git a/shopelectro/tests/tests_js_analytics.py b/shopelectro/tests/tests_js_analytics.py
new file mode 100644
--- /dev/null
+++ b/shopelectro/tests/tests_js_analytics.py
@@ -0,0 +1,36 @@
+from django.test import override_settings, tag
+
+from shopelectro import selenium
+from shopelectro.tests import helpers
+from shopelectro.models import CategoryPage, Order
+
+
+@tag('slow')
+@helpers.disable_celery
+@override_settings(DEBUG=True, INTERNAL_IPS=tuple())
+class GoogleEcommerce(helpers.SeleniumTestCase):
+
+    fixtures = ['dump.json']
+
+    def test_google_ecommerce_purchase(self):
+        category_page = selenium.CategoryPage(
+            self.browser,
+            CategoryPage.objects.first().slug,
+        )
+        category_page.load()
+        category_page.add_to_cart()
+
+        order_page = selenium.OrderPage(self.browser)
+        order_page.load()
+        order_page.fill_contacts()
+        order_page.make_order()
+
+        success_page = selenium.SuccessPage(self.browser)
+        success_page.wait_loaded()
+        self.assertTrue(success_page.is_success())
+
+        order = Order.objects.order_by('-created').first()  # Ignore PyFlakesBear
+        reached = self.browser.execute_script('return gaObject.results;')  # Ignore PyFlakesBear
+
+        # @todo #762:30m Match an order with a transaction of Google eCommerce analytics.
+        #  The transaction must contain correct order data and related products.
diff --git a/shopelectro/tests/tests_selenium.py b/shopelectro/tests/tests_selenium.py
--- a/shopelectro/tests/tests_selenium.py
+++ b/shopelectro/tests/tests_selenium.py
@@ -45,8 +45,8 @@ def show_cart_dropdown(browser):
     ))
 
 
-def add_to_cart(browser, live_server_url):
-    browser.get(live_server_url + Product.objects.first().url)
+def add_to_cart(browser):
+    browser.get(Product.objects.first().url)
     browser.find_element_by_class_name('btn-to-basket').click()
 
 
@@ -73,7 +73,7 @@ class Header(helpers.SeleniumTestCase):
 
     def setUp(self):
         """Set up testing urls and dispatch selenium webdriver."""
-        self.browser.get(self.live_server_url)
+        self.browser.get('/')
         wait_page_loading(self.browser)
 
     def test_call_modal_not_visible(self):
@@ -113,7 +113,7 @@ def test_cart_hover(self):
 
     def test_cart_flush(self):
         """We can flush cart from header's cart dropdown."""
-        add_to_cart(self.browser, self.live_server_url)
+        add_to_cart(self.browser)
         show_cart_dropdown(self.browser)
         self.wait.until(EC.visibility_of_element_located(
             (By.CLASS_NAME, 'basket-reset')
@@ -125,7 +125,7 @@ def test_cart_flush(self):
         self.assertTrue(is_cart_empty(self.browser))
 
     def test_product_total_price_in_dropdown(self):
-        add_to_cart(self.browser, self.live_server_url)
+        add_to_cart(self.browser)
         product_price = int(Product.objects.first().price)
         show_cart_dropdown(self.browser)
         product_total_price = self.wait.until(EC.visibility_of_element_located(
@@ -143,7 +143,7 @@ class CategoryPage(helpers.SeleniumTestCase):
 
     def setUp(self):
         def get_testing_url(slug):
-            return self.live_server_url + reverse('category', args=(slug,))
+            return reverse('category', args=(slug,))
 
         root_category = Category.objects.filter(parent=None).first()
         children_category = Category.objects.filter(parent=root_category).first()
@@ -422,9 +422,8 @@ def setUp(self):
         """Set up testing url and dispatch selenium webdriver."""
         self.browser.delete_all_cookies()
         self.product = Product.objects.get(id=self.PRODUCT_ID)
-        server = self.live_server_url
-        self.test_product_page = server + self.product.url
-        self.success_order = server + reverse(CustomPage.ROUTE, args=('order-success',))
+        self.test_product_page = self.product.url
+        self.success_order = reverse(CustomPage.ROUTE, args=('order-success',))
         self.product_name = self.product.name
         self.browser.get(self.test_product_page)
         wait_page_loading(self.browser)
@@ -504,9 +503,9 @@ def test_one_click_buy_action(self):
         self.browser.find_element_by_id(
             'input-one-click-phone').send_keys('2222222222')
         self.one_click.click()
-        self.wait.until(EC.url_to_be(self.success_order))
+        self.wait.until(EC.url_contains(self.success_order))
 
-        self.assertEqual(self.browser.current_url, self.success_order)
+        self.assertIn(self.success_order, self.browser.current_url)
 
     @helpers.disable_celery
     def test_one_click_buy_order_email(self):
@@ -525,7 +524,7 @@ def test_one_click_buy_order_email(self):
         phone_field = self.browser.find_element_by_id('input-one-click-phone')
         phone_field.send_keys('2222222222')
         self.one_click.click()
-        self.wait.until(EC.url_to_be(self.success_order))
+        self.wait.until(EC.url_contains(self.success_order))
 
         sent_mail_body = mail.outbox[0].body
         self.assertIn('+7 (222) 222 22 22', sent_mail_body)
@@ -637,13 +636,10 @@ def setUp(self):
         self.product_count = self.get_cell(pos=4, col='count') + '/div[2]/input'
         self.add_product = self.get_cell(pos=4, col='count') + '/div[2]/span[3]/button[1]'
         self.category = reverse('category', args=(Category.objects.first().page.slug,))
-        self.success_order_url = '{}{}'.format(
-            self.live_server_url,
-            reverse(CustomPage.ROUTE, args=('order-success',))
-        )
+        self.success_order_url = reverse(CustomPage.ROUTE, args=('order-success',))
         self.buy_products()
         self.wait.until_not(is_cart_empty)
-        self.browser.get(self.live_server_url + self.order_page.url)
+        self.browser.get(self.order_page.url)
         wait_page_loading(self.browser)
 
     def tearDown(self):
@@ -653,7 +649,7 @@ def tearDown(self):
         self.browser.execute_script('localStorage.clear();')
 
     def buy_products(self):
-        self.browser.get(self.live_server_url + self.category)
+        self.browser.get(self.category)
         for i in range(1, 6):
             self.browser.find_element_by_xpath(
                 '//*[@id="products-wrapper"]/div[{}]/div[2]/div[5]/button'
@@ -759,10 +755,10 @@ def test_confirm_order(self):
         self.append_products_to_cart()
         self.fill_contacts_data()
         self.submit_form()
-        self.wait.until(EC.url_to_be(self.success_order_url))
-        self.assertEqual(
+        self.wait.until(EC.url_contains(self.success_order_url))
+        self.assertIn(
+            reverse(CustomPage.ROUTE, args=('order-success', )),
             self.browser.current_url,
-            self.live_server_url + reverse(CustomPage.ROUTE, args=('order-success', ))
         )
 
     @helpers.disable_celery
@@ -776,7 +772,7 @@ def test_order_email(self):
 
         self.fill_contacts_data()
         self.submit_form()
-        self.wait.until(EC.url_to_be(self.success_order_url))
+        self.wait.until(EC.url_contains(self.success_order_url))
         self.assertEqual(len(mail.outbox), 1)
         sent_mail_body = mail.outbox[0].body
 
@@ -843,7 +839,7 @@ def setUp(self):
             parent=self.page_top
         )
         self.browser.delete_all_cookies()
-        self.browser.get(self.live_server_url + self.page_last.url)
+        self.browser.get(self.page_last.url)
         wait_page_loading(self.browser)
 
     def tearDown(self):
@@ -886,6 +882,9 @@ def test_accordion_minimize_by_double_click(self):
         self.assertFalse(accordion_content.is_displayed())
 
 
+# @todo #762:30m Move Ya.Metrica tests to tests_js_counters.py
+
+
 @tag('slow')
 @helpers.disable_celery
 @override_settings(DEBUG=True, INTERNAL_IPS=tuple())
@@ -894,16 +893,12 @@ class YandexMetrika(helpers.SeleniumTestCase):
     CART_LOCATOR = (By.CLASS_NAME, 'js-go-to-cart')
 
     def setUp(self):
-        server = self.live_server_url
         product_vendor_code = Product.objects.first().vendor_code
-        self.product_page = server + reverse('product', args=(product_vendor_code,))
-        self.category_page = server + reverse(
+        self.product_page = reverse('product', args=(product_vendor_code,))
+        self.category_page = reverse(
             'category', args=(Category.objects.first().page.slug,))
-        self.order_page_url = '{}{}'.format(
-            self.live_server_url,
-            reverse(CustomPage.ROUTE, args=('order',))
-        )
-        self.browser.get(self.live_server_url)
+        self.order_page_url = reverse(CustomPage.ROUTE, args=('order',))
+        self.browser.get('/')
         wait_page_loading(self.browser)
 
     @property
@@ -942,7 +937,7 @@ def buy_product(self):
 
     def go_to_cart(self):
         self.click(self.CART_LOCATOR)
-        self.wait.until(EC.url_to_be(self.order_page_url))
+        self.wait.until(EC.url_contains(self.order_page_url))
 
     def test_download_header_price(self):
         """User clicks Download price button in site's header."""
@@ -1048,7 +1043,7 @@ class Search(helpers.SeleniumTestCase):
     INPUT_LOCATOR = (By.CLASS_NAME, 'js-search-input')
 
     def setUp(self):
-        self.browser.get(self.live_server_url)
+        self.browser.get('/')
         wait_page_loading(self.browser)
 
     def tearDown(self):
diff --git a/shopelectro/tests/tests_selenium_admin.py b/shopelectro/tests/tests_selenium_admin.py
--- a/shopelectro/tests/tests_selenium_admin.py
+++ b/shopelectro/tests/tests_selenium_admin.py
@@ -62,9 +62,8 @@ class AdminPage(AdminSeleniumTestCase):
     @classmethod
     def setUpClass(cls):
         super().setUpClass()
-        cls.admin_page = cls.live_server_url + reverse('admin:index')
-        cls.change_products_url = cls.live_server_url + reverse(
-            'admin:shopelectro_productpage_changelist')
+        cls.admin_page = reverse('admin:index')
+        cls.change_products_url = reverse('admin:shopelectro_productpage_changelist')
         cls.title_text = 'Shopelectro administration'
         cls.active_products = '//*[@id="changelist-filter"]/ul[1]/li[2]/a'
         cls.inactive_products = '//*[@id="changelist-filter"]/ul[1]/li[3]/a'
@@ -354,7 +353,7 @@ def setUp(self):
 
     @property
     def admin_page(self):
-        return self.live_server_url + reverse('admin:index')
+        return reverse('admin:index')
 
     def wait_tableeditor_loading(self):
         self.wait_page_loading()
diff --git a/shopelectro/tests/tests_selenium_mobile.py b/shopelectro/tests/tests_selenium_mobile.py
--- a/shopelectro/tests/tests_selenium_mobile.py
+++ b/shopelectro/tests/tests_selenium_mobile.py
@@ -7,11 +7,10 @@
 from django.conf import settings
 from django.test import LiveServerTestCase, override_settings, tag
 from django.urls import reverse
-from selenium import webdriver
 from selenium.webdriver.common.by import By
 from selenium.webdriver.support import expected_conditions as EC
-from selenium.webdriver.support.ui import WebDriverWait
 
+from shopelectro.selenium import SiteDriver
 from shopelectro.models import Product
 
 
@@ -31,14 +30,17 @@ def setUpClass(cls):
                 'deviceName': 'Apple iPhone 5',
             },
         }
-        cls.browser = webdriver.Remote(
-            command_executor=settings.SELENIUM_URL,
-            desired_capabilities=capabilities
+        cls.browser = SiteDriver(
+            site_url=cls.live_server_url,
+            desired_capabilities=capabilities,
         )
-        cls.wait = WebDriverWait(cls.browser, 120)
         cls.browser.set_window_size(400, 800)
         cls.browser.implicitly_wait(10)
 
+    @property
+    def wait(self):
+        return self.browser.wait
+
     @classmethod
     def tearDownClass(cls):
         """Close selenium session."""
@@ -51,7 +53,7 @@ class Mobile(MobileSeleniumTestCase):
 
     def setUp(self):
         """Set up testing urls and dispatch selenium webdriver."""
-        self.browser.get(self.live_server_url)
+        self.browser.get('/')
         self.wait_page_loading()
 
     def wait_page_loading(self):
@@ -154,7 +156,7 @@ def wait_updates():
             ))
 
         product_vendor_code = Product.objects.first().vendor_code
-        product_page = self.live_server_url + reverse('product', args=(product_vendor_code,))
+        product_page = reverse('product', args=(product_vendor_code,))
         self.browser.get(product_page)
         self.wait_page_loading()