Skip to content

Commit e06f651

Browse files
committed
First commit
0 parents  commit e06f651

28 files changed

+515
-0
lines changed
Binary file not shown.

features/methods/get_driver.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from appium import webdriver
2+
3+
4+
class OpenApp():
5+
6+
# App Capabilities
7+
desired_caps = dict(
8+
platformName='Android',
9+
automationName='uiautomator2',
10+
deviceName='Android Emulator',
11+
app=(r'C:\\Users\\death\\Documents\\vsc\\super cook\\apk\\orca_share_media1659445078867_6960217132077753563.apk'),
12+
appWaitPackage= "com.supercook.app",
13+
appWaitActivity= "com.supercook.app.MainActivity",
14+
# newCommandTimeout= "5000"
15+
)
16+
17+
# Launch the app with the capabilities applied
18+
def get_driver(self):
19+
driver = webdriver.Remote('http://localhost:4723/wd/hub', self.desired_caps)
20+
return driver
21+

features/methods/locators.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
from selenium.webdriver.common.by import By
2+
3+
4+
# Locators which will be used when interacting with various elements
5+
class Locators(object):
6+
# INTRO
7+
SKIP_INTRO_BTN = (By.XPATH, "//*[@text='214.skip']")
8+
9+
# SELECT INGREDIENTS THROUGH SEARCH INPUT
10+
SEARCH_INPUT = (By.CLASS_NAME, "android.widget.EditText")
11+
FIND_RESULTS_BOX = (By.XPATH, "//*[ends-with(@text, 'add')]")
12+
FIRST_RESULT_COORDONATES = (144, 575)
13+
14+
# SELECT INGREDIENTS THROUGH THE CHECKBOXES MAIN SECTION
15+
SELECT_INGREDIENT_CB = (By.XPATH, "//*[@text='milk'][@index='3']")
16+
17+
# PANTRY
18+
MY_PANTRY_BTN = (By.XPATH, "//*[starts-with(@text, 'My Pantry')][@clickable='true']")
19+
PANTRY_INGREDIENT_TEXT = (By.XPATH, "//*[@text='milk']")
20+
PANTRY_ADDSHOPLIST_COORDONATES = (822, 832)
21+
22+
# DELETE INGREDIENT PASTRY SECTION
23+
DELETE_INGREDIENT_COORDONATES = (983, 823)
24+
25+
# DELETE INGREDIENT MAIN SECTION
26+
MORE_OPTIONS_BTN = (By.XPATH, "//android.view.View/android.view.View[1]/android.view.View[1]/android.view.View[1]/android.view.View[3]")
27+
REMOVE_ALL_INGREDIENTS = (515, 393)
28+
CONFIRM_REMOVE_BTN = (By.XPATH, "//*[@text='OK']")
29+
30+
# SHOPLIST
31+
SHOPLIST_CATEGORY_BTN = (By.XPATH, "//*[@text='Shopping List']")
32+
SHOPLIST_INPUT = (By.CLASS_NAME, "android.widget.EditText")
33+
SHOPLIST_RESULTS_BOX = (By.XPATH, "//android.webkit.WebView/android.view.View/android.view.View[3]")
34+
SHOPLIST_FIRST_RESULT_COORDONATES = (144, 575)
35+
36+
# CHANGE STATE OF INGREDIENTS IN SHOPLIST
37+
SHOPLIST_INLIST_BTN = (By.XPATH, "//*[contains(@text, 'milk')][@index='1']")
38+
SHOPLIST_FINISHED_TEXT = (By.XPATH, "//*[contains(@text, 'milk')][@index='2']")
39+
40+
# RECIPES
41+
INGREDIENT_BUTTER_BTN = (By.XPATH, "//android.view.View[2]/android.view.View[6]/android.view.View[1]")
42+
INGREDIENT_MILK_BTN = (By.XPATH, "//*[@text='milk'][@index='3']")
43+
RECIPES_CATEGORY_BTN = (By.XPATH, "//*[@text='See Recipes'][@index='1']")
44+
RECIPES_SEEALL_BTN = (By.XPATH, "//android.view.View/android.view.View[1]/android.view.View[3]/android.view.View[5]")
45+
RECIPES_SEARCH_INPUT = (By.CLASS_NAME, "android.widget.EditText")
46+
RECIPES_FIRST_RESULT_COORDONATES = (144, 575)
47+
RECIPES_RESULTS_BOX = (By.XPATH, "//android.webkit.WebView/android.view.View/android.view.View[3]")
48+
RECIPES_NAME_TEXT = (By.XPATH, "//android.view.View[3]/android.view.View[2]/android.view.View[1][@text='Scrambled Eggs']")
49+
RECIPES_ENOUGH_INGREDIENTS_TEXT = (By.XPATH, "//android.view.View[3]/android.view.View[2]/android.view.View[4]")

features/methods/main_methods.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
from appium import webdriver
2+
from selenium.webdriver.common.by import By
3+
from selenium.webdriver.support.ui import WebDriverWait
4+
from selenium.common.exceptions import TimeoutException
5+
from selenium.webdriver.support import expected_conditions as EC
6+
from appium.webdriver.common.touch_action import TouchAction
7+
8+
9+
# Main methods used when interacting with the elements
10+
class MainMethods():
11+
12+
# Initialize the app and set the webdriverwait
13+
def __init__(self, driver):
14+
self.driver = driver
15+
self.WebDriverWait = WebDriverWait(self.driver, 20)
16+
17+
# Wait until the app loaded the intro
18+
def wait_intro_load(self, locator):
19+
# time.sleep(5)
20+
self.WebDriverWait.until_not(EC.text_to_be_present_in_element_value((locator), '214.skip'))
21+
22+
# Wait until the given element is not visible anymore
23+
def find_element_absence(self, locator):
24+
try:
25+
self.WebDriverWait.until(EC.invisibility_of_element_located((locator)))
26+
except TimeoutException:
27+
return "Ingredient Not Removed"
28+
else:
29+
return "Ingredient Removed"
30+
31+
# Find the given element if it's visible
32+
def find_element(self, locator):
33+
self.WebDriverWait.until(EC.visibility_of_element_located((locator)))
34+
35+
# Find the given element text if it's visible
36+
def find_element_text(self, locator):
37+
return self.WebDriverWait.until(EC.visibility_of_element_located((locator))).text
38+
39+
# Click on the given element if it's clickable
40+
def click_on_element(self, locator):
41+
self.WebDriverWait.until(EC.element_to_be_clickable((locator))).click()
42+
43+
# Tap on the given coordonates, mostly used in cases where there is no proper element to click on
44+
def tap_coordonates(self, coordonates):
45+
self.driver.tap([coordonates])
46+
47+
# Input a text into an element, clear the field and also click on it for proper load of the input boxes
48+
def input(self, locator, text):
49+
element_input = self.WebDriverWait.until(EC.visibility_of_element_located((locator)))
50+
element_input.clear()
51+
element_input.send_keys(text)
52+
53+
# Close the app
54+
def closeapp(self):
55+
self.driver.close_app()

features/pages/ingredients_page.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
from features.methods.main_methods import MainMethods
2+
from features.methods.locators import Locators
3+
4+
5+
class Ingredients(MainMethods):
6+
7+
# Set the mobile driver and get the available locators
8+
def __init__(self, driver):
9+
super().__init__(driver)
10+
self.locator = Locators
11+
12+
# Wait for intro to load
13+
def intro_load(self):
14+
self.wait_intro_load(self.locator.SKIP_INTRO_BTN)
15+
16+
# Press skip intro
17+
def press_skip_intro(self):
18+
self.click_on_element(self.locator.SKIP_INTRO_BTN)
19+
20+
# Ingredient addition section
21+
22+
# Search for an ingredient in the search field, click on the field and wait for result box to load to avoid errors
23+
def search_for(self):
24+
self.click_on_element(self.locator.SEARCH_INPUT)
25+
self.input(self.locator.SEARCH_INPUT, "milk")
26+
self.find_element(self.locator.FIND_RESULTS_BOX)
27+
28+
# Select ingredient checkbox
29+
def select_ingredient(self):
30+
self.click_on_element(self.locator.SELECT_INGREDIENT_CB)
31+
32+
# Select all ingredients required for a recipe
33+
def select_all_ingredients(self):
34+
self.click_on_element(self.locator.INGREDIENT_BUTTER_BTN)
35+
self.click_on_element(self.locator.INGREDIENT_MILK_BTN)
36+
37+
# Tap on the first result displayed by the result box, by coordonates
38+
def open_first_result(self):
39+
self.tap_coordonates(self.locator.FIRST_RESULT_COORDONATES)
40+
41+
# Ingredient removal section
42+
43+
# Press on more options on the main page
44+
def press_more_options(self):
45+
self.click_on_element(self.locator.MORE_OPTIONS_BTN)
46+
47+
# Press on remove all ingredients button on main page
48+
def press_remove_ingredients(self):
49+
self.tap_coordonates(self.locator.REMOVE_ALL_INGREDIENTS)
50+
51+
# Press on confirm to remove all ingredients
52+
def press_confirm_remove(self):
53+
self.click_on_element(self.locator.CONFIRM_REMOVE_BTN)
54+
55+
# Close the app
56+
def closeapp(self):
57+
self.driver.close_app()
58+
59+
60+

features/pages/pastry_page.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from features.methods.main_methods import MainMethods
2+
from features.methods.locators import Locators
3+
4+
5+
class Pastry(MainMethods):
6+
7+
# Set the mobile driver and get the available locators
8+
def __init__(self, driver):
9+
super().__init__(driver)
10+
self.locator = Locators
11+
12+
# Open the pastry section
13+
def open_pastry(self):
14+
self.click_on_element(self.locator.MY_PANTRY_BTN)
15+
16+
# Find the given ingredient in the pantry section, return it if found
17+
def find_ingredient(self):
18+
ingredient = self.find_element_text(self.locator.PANTRY_INGREDIENT_TEXT)
19+
if ingredient != "milk":
20+
return "Not Found"
21+
else:
22+
return "Found"
23+
24+
# Delete the ingredient available in the pantry section
25+
def pastry_delete_ingredient(self):
26+
self.tap_coordonates(self.locator.DELETE_INGREDIENT_COORDONATES)
27+
28+
# Check if ingredient has successfully been deleted and not available in the dom
29+
def check_ingredient_deletion(self):
30+
ingredient = self.find_element_absence(self.locator.PANTRY_INGREDIENT_TEXT)
31+
return ingredient

features/pages/recipes_page.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
from features.methods.main_methods import MainMethods
2+
from features.methods.locators import Locators
3+
import time
4+
5+
6+
class Recipes(MainMethods):
7+
8+
# Set the mobile driver and get the available locators
9+
def __init__(self, driver):
10+
super().__init__(driver)
11+
self.locator = Locators
12+
13+
# Open the recipes category
14+
def open_recipes(self):
15+
self.click_on_element(self.locator.RECIPES_CATEGORY_BTN)
16+
17+
'''
18+
Because the input box on recipes list and the one on the main page are the same
19+
we will need to wait for that one to dissapear from dom
20+
first before finding the one from the shopping list
21+
'''
22+
23+
# Show all recipes and wait for the input box in the main page to dissapear from dom
24+
def show_all_recipes(self):
25+
self.click_on_element(self.locator.RECIPES_SEEALL_BTN)
26+
time.sleep(5)
27+
28+
# Type the recipe name in the input box, click on it to load the results and wait for the results dropdown to load
29+
def type_recipe_name(self):
30+
self.click_on_element(self.locator.RECIPES_SEARCH_INPUT)
31+
self.input(self.locator.RECIPES_SEARCH_INPUT, "scrambled eggs")
32+
self.find_element(self.locator.RECIPES_RESULTS_BOX)
33+
34+
# Select first recipe by tapping on coordonates
35+
def select_first_recipe_entry(self):
36+
self.tap_coordonates(self.locator.RECIPES_FIRST_RESULT_COORDONATES)
37+
38+
# Check If there is a recipe that it's available with the given search term and also the ingredients are available
39+
def get_match_recipe(self):
40+
recipe_name = self.find_element_text(self.locator.RECIPES_NAME_TEXT)
41+
recipe_availability = self.find_element_text(self.locator.RECIPES_ENOUGH_INGREDIENTS_TEXT)
42+
if recipe_name == "Scrambled Eggs" and recipe_availability.startswith("You have") == True:
43+
return "Recipe available with the current ingredients"
44+
else:
45+
return "There are no recipe available with the current ingredients"

features/pages/shoplist_page.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
from features.methods.main_methods import MainMethods
2+
from features.methods.locators import Locators
3+
import time
4+
5+
6+
class ShopList(MainMethods):
7+
8+
# Set the mobile driver and get the available locators
9+
def __init__(self, driver):
10+
super().__init__(driver)
11+
self.locator = Locators
12+
13+
'''
14+
Because the input box on shopping list and the one on the main page are the same
15+
we will need to wait for that one to dissapear from dom
16+
first before finding the one from the shopping list
17+
'''
18+
19+
# Click on shoplist button and wait for the input field from the main page to dissapear from dom
20+
def click_shoplist(self):
21+
self.click_on_element(self.locator.SHOPLIST_CATEGORY_BTN)
22+
time.sleep(5)
23+
24+
# Type ingredient name and click on it, wait for the result dropdown to load
25+
def type_shoplist_ingredient(self):
26+
self.find_element(self.locator.SEARCH_INPUT)
27+
self.click_on_element(self.locator.SHOPLIST_INPUT)
28+
self.input(self.locator.SHOPLIST_INPUT, "coconut milk")
29+
self.find_element(self.locator.SHOPLIST_RESULTS_BOX)
30+
31+
# Select ingredient by coordonates
32+
def select_shoplist_ingredient(self):
33+
self.tap_coordonates(self.locator.SHOPLIST_FIRST_RESULT_COORDONATES)
34+
35+
# Verify if the ingredient has been added to the list sucessfully
36+
def verify_shoplist_ingredient_existence(self):
37+
ingredient = self.find_element_text(self.locator.SHOPLIST_INLIST_BTN)
38+
if ingredient != "coconut milk":
39+
return "Not Found"
40+
else:
41+
return "Found"
42+
43+
# Mark the ingredient as bought by clicking on it
44+
def click_shoplist_ingredient(self):
45+
self.click_on_element(self.locator.SHOPLIST_INLIST_BTN)
46+
47+
# Verify if the ingredient has been sucessfully added to the finished/bought section
48+
def verify_shoplist_ingredient_state(self):
49+
ingredient = self.find_element_text(self.locator.SHOPLIST_FINISHED_TEXT)
50+
if ingredient != "coconut milk":
51+
return "Not Found"
52+
else:
53+
return "Found"
54+
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
from pages.ingredients_page import Ingredients
2+
from pages.pastry_page import Pastry
3+
from methods.get_driver import OpenApp
4+
from behave import given, when, then
5+
6+
@given(u'I open the app')
7+
def open_app(context):
8+
context.app = OpenApp().get_driver()
9+
context.Ingredients = Ingredients(context.app)
10+
11+
@given(u'I skip intro')
12+
def skip_intro(context):
13+
context.Ingredients.intro_load()
14+
context.Ingredients.press_skip_intro()
15+
16+
@when(u'I type the ingredient name in the search field')
17+
def type_ingredient_name(context):
18+
context.Ingredients.search_for()
19+
20+
@when(u'I Tap on the desired ingredient displayed after the search')
21+
def tap_first_result(context):
22+
context.Ingredients.open_first_result()
23+
24+
@when(u'I Press on My Pantry')
25+
def press_on_pastry(context):
26+
context.Pastry = Pastry(context.app)
27+
context.Pastry.open_pastry()
28+
29+
@then(u'Ingredient will be visible in the pantry section')
30+
def get_ingredient(context):
31+
ingredient = context.Pastry.find_ingredient()
32+
context.Ingredients.closeapp()
33+
assert ingredient == "Found"
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from behave import when
2+
3+
@when(u'I tap on the desired ingredient from the available categories')
4+
def tap_wanted_ingredient(context):
5+
context.Ingredients.select_ingredient()

features/steps/get_recipe_steps.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from pages.recipes_page import Recipes
2+
from behave import when, then
3+
4+
@when(u'I Select milk and butter as ingredients')
5+
def select_all_ingredients(context):
6+
context.Ingredients.select_all_ingredients()
7+
8+
@when(u'I Open all recipes')
9+
def open_recipes_category(context):
10+
context.Recipes = Recipes(context.app)
11+
context.Recipes.open_recipes()
12+
context.Recipes.show_all_recipes()
13+
14+
@when(u'I search for scrambled eggs in the search field')
15+
def select_dish(context):
16+
context.Recipes.type_recipe_name()
17+
context.Recipes.select_first_recipe_entry()
18+
19+
@then(u'The recipe that match my available ingredients will be displayed')
20+
def get_recipe(context):
21+
match_recipe = context.Recipes.get_match_recipe()
22+
context.Ingredients.closeapp()
23+
assert match_recipe == "Recipe available with the current ingredients"

0 commit comments

Comments
 (0)