Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#938 #939 test order page remove goal #943

Merged
merged 11 commits into from
Jul 12, 2019
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ database/
.DS_store
*.orig
venv/
.sublime-project
.sublime-workspace
*.sublime-project
*.sublime-workspace

3 changes: 3 additions & 0 deletions shopelectro/selenium/analytics_goals.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ def __iter__(self):
def __getitem__(self, index: int):
raise NotImplementedError

def __bool__(self):
return bool(list(self))


class YandexEcommerceGoals(Goals): # Ignore PyDocStyleBear
"""
Expand Down
5 changes: 3 additions & 2 deletions shopelectro/selenium/elements/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from .button import Button
from .exceptions import Unavailable
from .button import Button
from .input import Input
from .product import CatalogCard, ProductCard, CartPosition
from .product import *
from .cart import Cart
from .positions import Positions
38 changes: 7 additions & 31 deletions shopelectro/selenium/elements/cart.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
from contextlib import contextmanager

from selenium.common.exceptions import TimeoutException
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support import expected_conditions as EC
Expand All @@ -15,6 +12,11 @@ class Cart:

def __init__(self, driver: SiteDriver):
self.driver = driver
self.positions = elements.Positions(
driver,
elements.CartPosition,
(By.CLASS_NAME, 'basket-item'),
)

def _hover(self):
cart = self.driver.wait.until(EC.visibility_of_element_located(
Expand All @@ -25,36 +27,10 @@ def _hover(self):
(By.CLASS_NAME, 'js-cart-wrapper')
))

# @todo #920:15m Document the Cart.wait_changes.
# Cover corner cases with TimeoutException.

@contextmanager
def wait_changes(self):
def wait_changes(browser):
try:
return positions_before != self.positions()
except TimeoutException:
return False

positions_before = self.positions()
yield
self.driver.wait.until(wait_changes)

def positions(self) -> [elements.CartPosition]:
try:
# use short_wait to avoid long pauses in case of the empty cart
positions_count = len(self.driver.short_wait.until(EC.presence_of_all_elements_located(
(By.CLASS_NAME, 'basket-item')
)))
except TimeoutException:
positions_count = 0

return [elements.CartPosition(self.driver, i) for i in range(positions_count)]

def remove(self, position: elements.CartPosition):
with self.wait_changes():
with self.positions.wait_changes():
self._hover()
position.remove_from_cart()
position.remove()

def clear(self):
self._hover()
Expand Down
45 changes: 45 additions & 0 deletions shopelectro/selenium/elements/positions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from contextlib import contextmanager

from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support import expected_conditions as EC

from shopelectro.selenium import SiteDriver, elements


class Positions:

def __init__(self, driver: SiteDriver, position_type: elements.Product, locator):
self.driver = driver
self.position_type = position_type
self.condition = EC.presence_of_all_elements_located(locator)

@contextmanager
def wait_changes(self):
def are_changed(_):
try:
return positions_before != self.all()
except TimeoutException:
"""
An exception can be raised from a position's equality method.
In most cases this means that some positions are stale,
so we continue waiting changes.
"""
return False

positions_before = self.all()
yield
self.driver.wait.until(are_changed)

def first(self) -> elements.Product:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

elements.Product is not clear returning type. Seems we can't set type here without generic types lang feature

return self.position_type(self.driver, 0)

def all(self) -> [elements.Product]:
try:
# use short_wait to avoid long pauses in case of the empty cart
positions_count = len(self.driver.short_wait.until(
self.condition
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could be formatted in one string

))
except TimeoutException:
positions_count = 0

return [self.position_type(self.driver, i) for i in range(positions_count)]
57 changes: 49 additions & 8 deletions shopelectro/selenium/elements/product.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,18 @@ def price(self):
def quantity(self):
raise Unavailable('determine the product quantity.')

def add_to_cart(self):
def add(self):
raise Unavailable('add the product to the card.')

def remove_from_cart(self):
def remove(self):
raise Unavailable('remove the product from the card.')

def __hash__(self):
raise NotImplementedError('Provide __hash__ implementation for the class.')

def __eq__(self, other: 'Product'):
return hash(self) == hash(other)


class CatalogCard(Product):

Expand Down Expand Up @@ -79,7 +85,7 @@ def vendor_code(self):
(By.XPATH, self._build_xpath('div[2]/div[1]'))
)).text.split(' ')[1]

def add_to_cart(self):
def add(self):
Button(self.driver, (By.XPATH, self._build_xpath('div[2]/div[5]/button'))).click()


Expand All @@ -88,7 +94,7 @@ class ProductCard(Product):
def __init__(self, driver: SiteDriver):
self.driver = driver

def add_to_cart(self):
def add(self):
Button(self.driver, (By.CLASS_NAME, 'js-to-cart-on-product-page')).click()


Expand All @@ -107,9 +113,6 @@ def __hash__(self):
+ el.get_attribute('data-product-count')
)

def __eq__(self, other: 'CartPosition'):
return hash(self) == hash(other)

def _data_element(self):
# use short_wait, because a position could be stale
return self.driver.short_wait.until(EC.presence_of_element_located(
Expand All @@ -125,5 +128,43 @@ def price(self):
def quantity(self):
return self._data_element().get_attribute('data-product-count')

def remove_from_cart(self):
def remove(self):
Button(self.driver, (By.XPATH, f'{self.xpath}i')).click()


class OrderPosition(Product):
"""Represent a product position on order page."""

def __init__(self, driver: SiteDriver, index: int):
self.driver = driver
# xpath indexes starts from 1
self.xpath = f'//div[@id="js-order-list"]/div[2]/div[{index + 1}]/'

def __hash__(self):
return hash(
self.vendor_code()
+ '/'
+ self.quantity()
)

def vendor_code(self):
return self.driver.short_wait.until(EC.visibility_of_element_located(
(By.XPATH, f'{self.xpath}div[1]')
)).text

def quantity(self):
return self.driver.short_wait.until(EC.visibility_of_element_located(
(By.XPATH, f'{self.xpath}//input')
)).value

def set(self, quantity: int):
raise NotImplementedError

def increase(self, times=1):
raise NotImplementedError

def decrease(self, times=1):
raise NotImplementedError

def remove(self):
Button(self.driver, (By.XPATH, f'{self.xpath}div[6]/div')).click()
4 changes: 2 additions & 2 deletions shopelectro/selenium/pages/category.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,6 @@ def add_to_cart(self, products: typing.List[elements.CatalogCard] = None):
default = [elements.CatalogCard.with_index(self.driver, i) for i in range(6)]
products = products or default

with self.cart().wait_changes():
with self.cart().positions.wait_changes():
for product in products:
product.add_to_cart()
product.add()
36 changes: 28 additions & 8 deletions shopelectro/selenium/pages/order.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,45 @@
from shopelectro.models import PaymentOptions
from shopelectro.selenium.elements import Input, Button
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
from shopelectro.models import PaymentOptions
from shopelectro.selenium import elements, SiteDriver
from shopelectro.selenium.pages import Page

# @todo #682:120m Implement and reuse shopelectro.selenium.OrderPage for selenium tests.


class OrderPage(Page):

def __init__(self, driver):
def __init__(self, driver: SiteDriver):
super().__init__(driver)
self.submit_button = Button(self.driver, (By.ID, 'submit-order'))
self.submit_button = elements.Button(self.driver, (By.ID, 'submit-order'))
self.positions = elements.Positions(
driver,
elements.OrderPosition,
(By.XPATH, '//div[@id="js-order-list"]/div[2]/div'),
)

@property
def path(self):
return CustomPage.objects.get(slug='order').url

def set(self, position: elements.OrderPosition, quantity: int):
with self.positions.wait_changes():
position.set(quantity)

def increase(self, position: elements.OrderPosition):
with self.positions.wait_changes():
position.increase()

def decrease(self, position: elements.OrderPosition):
with self.positions.wait_changes():
position.decrease()

def remove(self, position: elements.OrderPosition):
with self.positions.wait_changes():
position.remove()

def fill_contacts(
self, name='Name', city='Санкт-Петербург', phone='2222222222', email='[email protected]',
):
Expand All @@ -31,7 +51,7 @@ def fill_contacts(
}

for id_, value in contacts.items():
Input(self.driver, (By.ID, id_)).send_keys(value)
elements.Input(self.driver, (By.ID, id_)).send_keys(value)

def make_order(self):
self.submit_button.click()
Expand All @@ -44,7 +64,7 @@ def select_payment_type(self, payment_option: PaymentOptions):
f'It should be one of: {PaymentOptions}'
)

item = Button(
item = elements.Button(
self.driver,
(By.CSS, f'input[name="payment_type"][value="{payment_option.name}"]'),
)
Expand Down
4 changes: 2 additions & 2 deletions shopelectro/selenium/pages/product.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@ def path(self):
return reverse('product', args=(self.vendor_code,))

def add_to_cart(self):
with self.cart().wait_changes():
elements.ProductCard(self.driver).add_to_cart()
with self.cart().positions.wait_changes():
elements.ProductCard(self.driver).add()
Loading