diff --git a/HomeWork/HW2/HW2_TestCase_SignIn.py b/HomeWork/HW2/HW2_TestCase_SignIn.py
new file mode 100644
index 000000000..9f5974a4e
--- /dev/null
+++ b/HomeWork/HW2/HW2_TestCase_SignIn.py
@@ -0,0 +1,32 @@
+from selenium import webdriver
+from selenium.webdriver.common.by import By
+from selenium.webdriver.chrome.service import Service
+from webdriver_manager.chrome import ChromeDriverManager
+from time import sleep
+
+# get the path to the ChromeDriver executable
+driver_path = ChromeDriverManager().install()
+
+# create a new Chrome browser instance
+service = Service(driver_path)
+driver = webdriver.Chrome(service=service)
+driver.maximize_window()
+
+# open the url
+driver.get('https://www.target.com/')
+# Click SignIn button
+driver.find_element(By.XPATH, "//span [@class='sc-58ad44c0-3 kwbrXj h-margin-r-x3']").click()
+# Click SignIn from side navigation
+driver.find_element(By.XPATH, "//a [@data-test= 'accountNav-signIn']").click()
+sleep(10)
+
+sing_in_text = driver.find_element(By.XPATH, "//h1//span[text()='Sign into your Target account']")
+
+sing_in_button = driver.find_element(By.XPATH, "//button[@type= 'submit']")
+
+if sing_in_text and sing_in_button:
+ print("Sign In page opened successfully.")
+else:
+ print("Sign In page did not open.")
+
+#sleep(6)
\ No newline at end of file
diff --git a/HomeWork/HW2/HW2_locators.py b/HomeWork/HW2/HW2_locators.py
new file mode 100644
index 000000000..623fc8537
--- /dev/null
+++ b/HomeWork/HW2/HW2_locators.py
@@ -0,0 +1,41 @@
+from selenium import webdriver
+from selenium.webdriver.common.by import By
+from selenium.webdriver.chrome.service import Service
+from webdriver_manager.chrome import ChromeDriverManager
+from time import sleep
+
+# get the path to the ChromeDriver executable
+driver_path = ChromeDriverManager().install()
+
+# create a new Chrome browser instance
+service = Service(driver_path)
+driver = webdriver.Chrome(service=service)
+driver.maximize_window()
+
+# open the url
+driver.get('https://www.amazon.com/ap/signin?openid.pape.max_auth_age=0&openid.return_to=https%3A%2F%2Fwww.amazon.com%2F%3Fref_%3Dnav_ya_signin&openid.identity=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&openid.assoc_handle=usflex&openid.mode=checkid_setup&openid.claimed_id=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&openid.ns=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0&')
+
+sleep(12)
+
+# Amazon logo SingIn
+driver.find_element(By.XPATH, "//h1[@class= 'a-spacing-small']")
+# driver.find_element(By.XPATH, "//h1[@text()= 'Sing in']") why its not working?
+# Amazon LOGO
+driver.find_element(By.XPATH,"//i[@class= 'a-icon a-icon-logo']")
+# Email field
+driver.find_element(By.XPATH, "//input[@type= 'email']")
+# Continue Button
+driver.find_element(By.XPATH, "//input[@class= 'a-button-input']")
+# Conditions of use link
+driver.find_element(By.XPATH, "//div//a[contains(text(), 'Conditions of Use') and @href= '/gp/help/customer/display.html/ref=ap_signin_notification_condition_of_use?ie=UTF8&nodeId=508088']")
+#driver.find_element(By.XPATH,"//a[text()= 'Conditions of Use']")
+# Privacy Notice link
+driver.find_element(By.XPATH,"//a[text()= 'Privacy Notice']")
+# Need help link
+driver.find_element(By.XPATH, "//span[@class= 'a-expander-prompt']")
+# Forgot your password link
+driver.find_element(By.XPATH, "//a[contains(text(),'Forgot your password?')]")
+# Other issues with Sign-In link
+driver.find_element(By.XPATH,"//a[contains(text(), 'Other issues with Sign-In')]")
+# Create your Amazon account button
+driver.find_element(By.XPATH, "//a[contains(text(), 'Other issues with Sign-In')]")
diff --git a/HomeWork/HW2/HW2_target_search.py b/HomeWork/HW2/HW2_target_search.py
new file mode 100644
index 000000000..51b5fccc5
--- /dev/null
+++ b/HomeWork/HW2/HW2_target_search.py
@@ -0,0 +1,31 @@
+from selenium import webdriver
+from selenium.webdriver.common.by import By
+from selenium.webdriver.chrome.service import Service
+from webdriver_manager.chrome import ChromeDriverManager
+from time import sleep
+
+# get the path to the ChromeDriver executable
+driver_path = ChromeDriverManager().install()
+
+# create a new Chrome browser instance
+service = Service(driver_path)
+driver = webdriver.Chrome(service=service)
+driver.maximize_window()
+
+# open the url
+driver.get('https://www.target.com/')
+
+# populate search field
+driver.find_element(By.ID, 'search').send_keys('airpods')
+
+driver.find_element(By.XPATH, "//button[@data-test ='@web/Search/SearchButton']").click()
+
+sleep(7)
+
+#verify
+expected_text = ('airpods')
+actual_text = driver.find_element(By.XPATH, "//div[@data-test = 'resultsHeading']").text
+
+assert expected_text in actual_text, f'Expected text {expected_text} is not in actual text {actual_text}'
+
+print("Test case passed")
\ No newline at end of file
diff --git a/HomeWork/HW3/Optimal_locators.py b/HomeWork/HW3/Optimal_locators.py
new file mode 100644
index 000000000..930fd7dc3
--- /dev/null
+++ b/HomeWork/HW3/Optimal_locators.py
@@ -0,0 +1,51 @@
+from selenium import webdriver
+from selenium.webdriver.common.by import By
+from selenium.webdriver.chrome.service import Service
+from webdriver_manager.chrome import ChromeDriverManager
+from time import sleep
+
+# get the path to the ChromeDriver executable
+driver_path = ChromeDriverManager().install()
+
+# create a new Chrome browser instance
+service = Service(driver_path)
+driver = webdriver.Chrome(service=service)
+driver.maximize_window()
+
+# open the url
+driver.get('https://www.amazon.com/ap/signin?openid.pape.max_auth_age=0&openid.return_to=https%3A%2F%2Fwww.amazon.com%2F%3Fref_%3Dnav_ya_signin&openid.identity=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&openid.assoc_handle=usflex&openid.mode=checkid_setup&openid.claimed_id=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&openid.ns=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0&')
+
+sleep(12)
+
+# Amazon LOGO
+driver.find_element(By.XPATH,"//i[@class= 'a-icon a-icon-logo']")
+
+# "Create Account" Header
+driver.find_element(By.CSS_SELECTOR, "h1.a-spacing-small")
+
+# "Your Name" field
+driver.find_element(By.CSS_SELECTOR, "#ap_customer_name")
+
+# "Email" field
+driver.find_element(By.CSS_SELECTOR, "#ap_email")
+
+# Password field
+driver.find_element(By.CSS_SELECTOR, "#ap_password")
+
+# Password characters info
+driver.find_element(By.CSS_SELECTOR, ".a-box.a-alert-inline.a-alert-inline-info")
+
+# "Re-enter Password" field
+driver.find_element(By.CSS_SELECTOR, "#ap_password_check")
+
+# "Create Your Account" Button
+driver.find_element(By.CSS_SELECTOR, "#continue")
+
+# "Conditions of Use" link
+driver.find_element(By.CSS_SELECTOR, "[href='/gp/help/customer/display.html/ref=ap_register_notification_condition_of_use?ie=UTF8&nodeId=508088']")
+
+# "Privacy Notice" link
+driver.find_element(By.CSS_SELECTOR, "[href='/gp/help/customer/display.html/ref=ap_register_notification_privacy_notice?ie=UTF8&nodeId=468496']")
+
+# "Sign In" link
+driver.find_element(By.CSS_SELECTOR, ".a-link-emphasis")
\ No newline at end of file
diff --git a/HomeWork/html/Images/mussels-1.jpg b/HomeWork/html/Images/mussels-1.jpg
new file mode 100644
index 000000000..84bd59b63
Binary files /dev/null and b/HomeWork/html/Images/mussels-1.jpg differ
diff --git a/HomeWork/html/Images/mussels.jpg b/HomeWork/html/Images/mussels.jpg
new file mode 100644
index 000000000..3fe4b23ef
Binary files /dev/null and b/HomeWork/html/Images/mussels.jpg differ
diff --git a/HomeWork/html/index.html b/HomeWork/html/index.html
new file mode 100644
index 000000000..bbe097d79
--- /dev/null
+++ b/HomeWork/html/index.html
@@ -0,0 +1,89 @@
+
+
+
+
+ Recipe Page
+
+
+
+
+
+
+
+ Steamed Mussels With Garlic and Parsley
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Ingredients:
+
Yield: 4 servings
+
+
2tablespoons extra-virgin olive oil, plus more for toasts
+
2garlic cloves, minced, plus 1 or 2 whole garlic cloves for rubbing toasts
+
Pinch of crushed red pepper
+
4pounds mussels, cleaned
+
¼cup white wine or water
+
1baguette, split lengthwise, then cut crosswise in half
+
1cup roughly chopped parsley
+
+
+
+
+
+
+ Preparation:
+
+
+
+
+
+
+ |
+
+
+ Step1:
+
+ - Heat broiler.
+ - Put olive oil in a large heavy-bottomed soup pot or Dutch oven over medium heat.
+ - Add the minced garlic and red pepper and let sizzle for 30 seconds without browning.
+ - Add the mussels, stir to coat and increase heat to high.
+ - Add the wine or water, and put on lid.
+ - After 2 minutes, give the mussels a stir, then replace lid and continue cooking until all
+ mussels have opened, 6 to 8 minutes.
+
+
+
+
+ Step 2:
+
+ - Paint cut sides of the baguette pieces with oil and place cut side up under broiler to toast.
+
+ - Rub toasts with the remaining garlic cloves.
+
+
+
+ Step 3:
+
+ - Stir the chopped parsley into the mussels, then ladle mussels and broth into bowls.
+ - Serve with the garlic toasts.
+
+
+ |
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/HomeWork/html/style.css b/HomeWork/html/style.css
new file mode 100644
index 000000000..ff1b4d3bc
--- /dev/null
+++ b/HomeWork/html/style.css
@@ -0,0 +1,3 @@
+h2 {
+ color:cornflowerblue;
+}
\ No newline at end of file
diff --git a/app/__init__.py b/app/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/app/application.py b/app/application.py
new file mode 100644
index 000000000..06cbdeef2
--- /dev/null
+++ b/app/application.py
@@ -0,0 +1,32 @@
+from pages.base_page import Page
+from pages.main_page import MainPage
+from pages.header import Header
+from pages.search_results_page import SearchResultsPage
+from pages.cart_page import CartPage
+from pages.account_side_menu import AccountSideMenu
+from pages.sign_in_page import SignInPage
+from pages.cart_side_menu import CartSideMenu
+from pages.target_app_page import TargetAppPage
+from pages.privacy_policy_page import PrivacyPolicyPage
+from pages.terms_and_conditions_page import TermsAndConditionsPage
+from pages.help_page import HelpPage
+
+
+class Application:
+ def __init__(self, driver):
+
+ self.base_page = Page(driver)
+
+ self.header = Header(driver)
+ self.main_page = MainPage(driver)
+ self.search_results_page = SearchResultsPage(driver)
+ self.cart_page = CartPage(driver)
+ self.account_side_menu = AccountSideMenu(driver)
+ self.sign_in_page = SignInPage(driver)
+ self.cart_side_menu = CartSideMenu(driver)
+ self.target_app_page = TargetAppPage(driver)
+ self.privacy_policy_page = PrivacyPolicyPage(driver)
+ self.terms_and_conditions_page = TermsAndConditionsPage(driver)
+ self.help_page = HelpPage(driver)
+
+
diff --git a/chromedriver b/chromedriver
new file mode 100755
index 000000000..8dadf52e2
Binary files /dev/null and b/chromedriver differ
diff --git a/css_selectors.py b/css_selectors.py
new file mode 100644
index 000000000..8c3bc99c8
--- /dev/null
+++ b/css_selectors.py
@@ -0,0 +1,47 @@
+from selenium import webdriver
+from selenium.webdriver.common.by import By
+from selenium.webdriver.chrome.service import Service
+from webdriver_manager.chrome import ChromeDriverManager
+from time import sleep
+
+# get the path to the ChromeDriver executable
+driver_path = ChromeDriverManager().install()
+
+# create a new Chrome browser instance
+service = Service(driver_path)
+driver = webdriver.Chrome(service=service)
+driver.maximize_window()
+
+# open the url
+driver.get('https://www.amazon.com/')
+
+# Find by css, using id:
+driver.find_element(By.CSS_SELECTOR, "#twotabsearchtextbox") # driver.find_element(By.ID, 'twotabsearchtextbox')
+driver.find_element(By.CSS_SELECTOR, "input#twotabsearchtextbox")
+
+# Find by css, using classes:
+driver.find_element(By.CSS_SELECTOR, ".nav-input")
+driver.find_element(By.CSS_SELECTOR, ".nav-progressive-attribute.nav-input")
+# tag + classes
+driver.find_element(By.CSS_SELECTOR, "input.nav-progressive-attribute.nav-input")
+# tag + id + classes
+driver.find_element(By.CSS_SELECTOR, "input#twotabsearchtextbox.nav-progressive-attribute.nav-input")
+# attributes:
+driver.find_element(By.CSS_SELECTOR, "[placeholder='Search Amazon']")
+driver.find_element(By.CSS_SELECTOR, "[placeholder='Search Amazon'][type='text']")
+driver.find_element(By.CSS_SELECTOR, "[placeholder='Search Amazon'][type='text']")
+# tag + attributes
+driver.find_element(By.CSS_SELECTOR, "input[placeholder='Search Amazon'][type='text']")
+# tag + #id + .class + [attributes]
+driver.find_element(By.CSS_SELECTOR, "input#twotabsearchtextbox.nav-input[placeholder='Search Amazon'][type='text']")
+# Order doesn't matter (although it's recommended to have: tag + #id + .class + [attributes])
+driver.find_element(By.CSS_SELECTOR, "input[type='text'].nav-input[placeholder='Search Amazon']#twotabsearchtextbox")
+
+# Attributes, partial match
+driver.find_element(By.CSS_SELECTOR, "[placeholder*='Search Ama']")
+driver.find_element(By.CSS_SELECTOR, "a[href*='ap_signin_notification_privacy_notice']")
+driver.find_element(By.CSS_SELECTOR, "a[class*='ap_signin_notification_privacy_notice']")
+
+# Multiple nodes, parent => child
+driver.find_element(By.CSS_SELECTOR, "div.a-box div#legalTextRow a[href*='condition']")
+
diff --git a/features/environment.py b/features/environment.py
index 1275460a0..d1a2f355e 100755
--- a/features/environment.py
+++ b/features/environment.py
@@ -1,31 +1,85 @@
+import os
+
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
+from selenium.webdriver.support.wait import WebDriverWait
from webdriver_manager.chrome import ChromeDriverManager
+from webdriver_manager.firefox import GeckoDriverManager
+from selenium.webdriver.chrome.options import Options
+
+from app.application import Application
+from support.logger import logger
+# Command to run tests with Allure & Behave:
+# behave -f allure_behave.formatter:AllureFormatter -o test_results/ features/tests/target_search.feature
def browser_init(context):
"""
:param context: Behave context
"""
- driver_path = ChromeDriverManager().install()
+ driver_path = "/Users/dang/Desktop/Careerist/Python_Selenium/python-selenium-automation/chromedriver"
+ #driver_path = ChromeDriverManager().install()
service = Service(driver_path)
context.driver = webdriver.Chrome(service=service)
+ ###FIREFOX MODE###
+ # driver_path = GeckoDriverManager().install()
+ # service = Service(driver_path)
+ # context.driver = webdriver.Firefox(service=service)
+
+
+ ### BROWSERS WITH DRIVERS: provide path to the driver file ###
+ # service = Service(executable_path='/Users/dang/careerist/19-python-selenium-automation/geckodriver')
+ # context.driver = webdriver.Firefox(service=service)
+
+ ### SAFARI ###
+ # context.driver = webdriver.Safari()
+
+ ### HEADLESS MODE ####
+ # options = webdriver.ChromeOptions()
+ # options.add_argument('headless')
+ # service = Service(ChromeDriverManager().install())
+ # context.driver = webdriver.Chrome(
+ # options=options,
+ # service=service
+ # )
+
+ ### BROWSERSTACK ###
+ # Register for BrowserStack, then grab it from https://www.browserstack.com/accounts/settings
+ # bs_user = ''
+ # bs_key = ''
+ # url = f'http://{bs_user}:{bs_key}@hub-cloud.browserstack.com/wd/hub'
+ #
+ # options = Options()
+ # bstack_options = {
+ # "os" : "Windows",
+ # "osVersion" : "11",
+ # 'browserName': 'edge',
+ # 'sessionName': scenario_name
+ # }
+ # options.set_capability('bstack:options', bstack_options)
+ # context.driver = webdriver.Remote(command_executor=url, options=options)
+
context.driver.maximize_window()
- context.driver.implicitly_wait(4)
+ context.driver.implicitly_wait(10)
+ context.driver.wait = WebDriverWait(context.driver, 15)
+ context.app = Application(context.driver)
def before_scenario(context, scenario):
print('\nStarted scenario: ', scenario.name)
+ logger.info(f'\nStarted scenario: {scenario.name}')
browser_init(context)
def before_step(context, step):
+ logger.info(f'Started step: {step}')
print('\nStarted step: ', step)
def after_step(context, step):
if step.status == 'failed':
+ logger.error(f'Step failed: {step}')
print('\nStep failed: ', step)
diff --git a/features/steps/cart_steps.py b/features/steps/cart_steps.py
new file mode 100644
index 000000000..c8cd2c4c4
--- /dev/null
+++ b/features/steps/cart_steps.py
@@ -0,0 +1,15 @@
+from selenium.webdriver.common.by import By
+from behave import given, when, then
+
+
+
+@then('Verify “Your cart is empty” message is shown')
+def verify_empty_cart_text(context):
+ context.app.cart_page.verify_empty_cart_msg()
+ # expected_text = 'Your cart is empty'
+ # actual_text = context.driver.find_element(By.CSS_SELECTOR, "h1.sc-fe064f5c-0").text
+ # assert expected_text in actual_text, f'Expected text {expected_text} is not in actual text {actual_text}'
+
+
+
+
diff --git a/features/steps/circle_page_steps.py b/features/steps/circle_page_steps.py
new file mode 100644
index 000000000..b4abf5fa1
--- /dev/null
+++ b/features/steps/circle_page_steps.py
@@ -0,0 +1,16 @@
+from selenium.webdriver.common.by import By
+from behave import given, when, then
+from time import sleep
+
+
+@given('Open Target Circle page')
+def open_target_circle(context):
+ context.driver.get('https://www.target.com/l/target-circle/-/N-pzno9')
+
+
+@then('Verify there are {number} benefit cells')
+def verify_benefit_cells(context, number):
+ number = int(number)
+ cells = context.driver.find_elements(By.CSS_SELECTOR, "[class*='cell-item-content']")
+ assert len(cells) == number, f'Expected {number} cellss, but got {len(cells)}'
+ print(cells)
\ No newline at end of file
diff --git a/features/steps/help_page_steps.py b/features/steps/help_page_steps.py
new file mode 100644
index 000000000..59945e62c
--- /dev/null
+++ b/features/steps/help_page_steps.py
@@ -0,0 +1,23 @@
+from selenium.webdriver.common.by import By
+from behave import then, given, when
+
+
+@given('Open Help page for Returns')
+def click_cart(context):
+ context.app.help_page.open_help_returns()
+
+
+@when('Select Help topic {option}')
+def select_topic(context, option):
+ context.app.help_page.select_topic(option)
+
+
+@then('Verify help {expected_text} page opened')
+def verify_help_page_header(context, expected_text):
+ context.app.help_page.verify_header(expected_text)
+
+
+# @then('Verify help {expected_text} page opened')
+# def verify_help_page_header_promotions(context, expected_text):
+# context.app.help_page.verify_promotions()
+
diff --git a/features/steps/main_page_steps.py b/features/steps/main_page_steps.py
new file mode 100644
index 000000000..1c6a22514
--- /dev/null
+++ b/features/steps/main_page_steps.py
@@ -0,0 +1,60 @@
+from selenium.webdriver.common.by import By
+from behave import given, when, then
+from selenium.webdriver.support.wait import WebDriverWait
+from selenium.webdriver.support import expected_conditions as EC
+from time import sleep
+
+SEARCH_FIELD = (By.ID, 'search')
+SEARCH_BTN = (By.XPATH, "//button[@data-test ='@web/Search/SearchButton']")
+CART_ICON_BTN = (By.CSS_SELECTOR, "[data-test='@web/CartIcon']")
+
+
+@given('Open Target main page')
+def open_target(context):
+ context.app.main_page.open()
+ #context.driver.get('https://www.target.com/')
+
+@when('Search for {product}')
+def search_product(context, product):
+ context.app.header.search_product(product)
+ #context.driver.find_element(*SEARCH_FIELD).send_keys(product)
+ #context.driver.find_element(*SEARCH_BTN).click()
+ #sleep(6) # Can't Replaced this sleep method by ether implicitly or explicitly method! if it possible please help!
+
+
+@when('Click on Cart icon')
+def click_cart(context):
+ context.app.header.cart_btn()
+ # button = context.driver.wait.until(EC.element_to_be_clickable(CART_ICON_BTN))
+ # #context.driver.find_element(By.CSS_SELECTOR, "[data-test='@web/CartIcon']").click()\
+ # button.click()
+ # #sleep(3)
+
+@when('Click Sign In')
+def click_sign_in(context):
+ context.app.header.sing_in_btn()
+ #context.driver.find_element(By.CSS_SELECTOR, "span.sc-58ad44c0-3").click()
+ #sleep(7) Replaced by context.driver.implicitly_wait(4)
+
+@when('From right side navigation menu, click Sign In')
+def navigation_menu(context):
+ context.app.account_side_menu.sign_in_btn()
+ #context.driver.find_element(By.CSS_SELECTOR, "[data-test = accountNav-signIn]").click()
+
+
+@then('Verify header in shown')
+def verify_header_present(context):
+ context.driver.find_element(By.CSS_SELECTOR, "[class*='utilityHeaderContainer']")
+
+
+@then('Verify header has {number} link')
+def verify_header_link_amount(context, number):
+ number = int(number)
+ links = context.driver.find_elements(By.CSS_SELECTOR, "[id*='utilityNav']")
+ assert len(links) == number, f'Expected {number} links, but got {len(links)}'
+ print(links)
+ # Make a loop to click all 6 links
+ for i in range(len(links)):
+ links = context.driver.find_elements(By.CSS_SELECTOR, "[id*='utilityNav']")
+ links[i].click()
+ sleep(4)
\ No newline at end of file
diff --git a/features/steps/product_search.py b/features/steps/product_search.py
index 4e142cb40..ba4b99bc6 100755
--- a/features/steps/product_search.py
+++ b/features/steps/product_search.py
@@ -1,5 +1,6 @@
from selenium.webdriver.common.by import By
from behave import given, when, then
+from selenium.webdriver.support.wait import WebDriverWait
from time import sleep
@@ -17,13 +18,13 @@ def input_search(context, search_word):
search = context.driver.find_element(*SEARCH_INPUT)
search.clear()
search.send_keys(search_word)
- sleep(4)
+ #sleep(4) Replaced by context.driver.implicitly_wait(4)
@when('Click on search icon')
def click_search_icon(context):
context.driver.find_element(*SEARCH_SUBMIT).click()
- sleep(1)
+ #sleep(1) Replaced by context.driver.implicitly_wait(4)
@then('Product results for {search_word} are shown')
diff --git a/features/steps/search_results_steps.py b/features/steps/search_results_steps.py
new file mode 100644
index 000000000..aed308582
--- /dev/null
+++ b/features/steps/search_results_steps.py
@@ -0,0 +1,73 @@
+from selenium.webdriver.common.by import By
+from selenium.webdriver.support.wait import WebDriverWait
+from selenium.webdriver.support import expected_conditions as EC
+from behave import then, given, when
+from time import sleep
+
+
+@then('Verify search results shown for {expected_product}')
+def verify_search_results(context, expected_product):
+ context.app.search_results_page.verify_search_results(expected_product)
+ # actual_text = context.driver.find_element(By.XPATH, "//div[@data-test = 'resultsHeading']").text
+ # assert expected_product in actual_text, f'Expected {expected_product} is not in actual text {actual_text}'
+
+
+@then('Verify correct search results URL opens for {expected_product}')
+def verify_search_worked(context, expected_product):
+ context.app.search_results_page.verify_product_in_url(expected_product)
+ # url = context.driver.current_url
+ # assert expected_product in url, f'Expected {expected_product} not in {url}'
+
+
+@when('Select the first product from the search results')
+def select_first_product(context):
+ context.app.search_results_page.select_first_item()
+ # #button = context.driver.wait.until(EC.element_to_be_clickable((By.XPATH, "//*[@id='addToCartButtonOrTextIdFor79545713']")))
+ # button = context.driver.wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, "button#addToCartButtonOrTextIdFor82572061")))
+ # #context.driver.find_element(By.XPATH, "//*[@id='addToCartButtonOrTextIdFor79545713']").click()
+ # button.click()
+ # #sleep(20)
+
+
+@when('Add the product to the cart')
+def add_product_to_cart(context):
+ context.app.cart_side_menu.add_product_to_cart()
+ #context.driver.find_element(By.CSS_SELECTOR, "[data-test= 'shippingButton']").click()
+
+
+@then('Verify that the cue is in the cart')
+def verify_cart(context):
+ context.app.cart_side_menu.verify_cart()
+ # expected_text = 'Added to cart'
+ # actual_text = context.driver.find_element(By.XPATH, "//*[text()='Added to cart']").text
+ # assert expected_text in actual_text, f'Expected {expected_text} is not in actual text {actual_text}'
+
+
+@then('Verify that search results has the product name and an image')
+def verify_product_name(context):
+ # To see ALL Listings (comment out if you only check the top ones):
+ context.driver.execute_script("window.scrollBy(0, 4000)", "")
+ sleep(4)
+ context.driver.execute_script("window.scrollBy(0, 4000)", "")
+
+ all_product_items = context.driver.find_elements(By.CSS_SELECTOR, "[data-test='product-title']" )
+
+ for product in all_product_items:
+ product_name = product.text
+ assert product_name, "Product name is missing for an item"
+ print(product_name)
+
+ product_image = context.driver.find_element(By.CSS_SELECTOR, 'img')
+ assert product_image.get_attribute('src'), f"Product image is missing for {product_name}."
+
+
+@when('Hover favorites icon')
+def hover_fav_icon(context):
+ context.app.search_results_page.hover_fav_icon()
+
+
+@then('Favorites tooltip is shown')
+def verify_fav_tooltip(context):
+ context.app.search_results_page.verify_fav_tooltip()
+
+
diff --git a/features/steps/sign_in_steps.py b/features/steps/sign_in_steps.py
new file mode 100644
index 000000000..92016730b
--- /dev/null
+++ b/features/steps/sign_in_steps.py
@@ -0,0 +1,65 @@
+from selenium.webdriver.common.by import By
+from behave import given, when, then
+from time import sleep
+
+
+@then('Verify Sign In form opened')
+def verify_sign_in_form(context):
+ context.app.sign_in_page.verify_sign_in_form()
+ # expected_text = 'Sign into your Target account'
+ # actual_text = context.driver.find_element(By.CSS_SELECTOR, "h1.sc-fe064f5c-0").text
+ # assert expected_text in actual_text, f'Expected text {expected_text} is not in actual text {actual_text}'
+
+
+@when('Enter email "{email}" and password "{password}"')
+def input_email_and_password(context, email, password):
+ context.app.sign_in_page.input_email_and_password(email, password)
+
+
+@when('Click Sign In With Password')
+def click_sign_in_with_password(context):
+ context.app.sign_in_page.sign_in_with_password()
+ sleep(6)
+
+
+@then('Verify user is logged in')
+def verify_user_is_logged_in(context):
+ context.app.sign_in_page.verify_user_login()
+
+
+@when('Store original window')
+def store_original_window(context):
+ context.original_window = context.app.sign_in_page.get_current_window()
+ print(f'Original window => {context.original_window}')
+
+
+@when('Click on Target terms and conditions link')
+def click_tc_link(context):
+ context.app.sign_in_page.click_tc_link()
+ sleep(3)
+
+
+@when('Switch to the newly opened window')
+def switch_window(context):
+ context.app.sign_in_page.switch_to_new_window()
+
+
+@then('Verify Terms and Conditions page is opened')
+def verify_pp_opened(context):
+ context.app.terms_and_conditions_page.verify_tc_url()
+
+
+@then('User can close new window')
+def close(context):
+ context.app.terms_and_conditions_page.close_page()
+
+
+@then('User can switch back to original')
+def return_to_original_window(context):
+ context.app.terms_and_conditions_page.switch_to_window_by_id(context.original_window)
+
+
+@then('Verify that warning message is shown')
+def verify_warning_message(context):
+ context.app.sign_in_page.verify_user_cannot_login()
+
diff --git a/features/steps/target_app_page_steps.py b/features/steps/target_app_page_steps.py
new file mode 100644
index 000000000..4dda46f58
--- /dev/null
+++ b/features/steps/target_app_page_steps.py
@@ -0,0 +1,37 @@
+from selenium.webdriver.common.by import By
+from behave import then, given, when
+from time import sleep
+
+
+@given('Open Target App page')
+def open_target_app(context):
+ context.app.target_app_page.open_target_app()
+
+@given('Store original window')
+def store_original_window(context):
+ context.original_window = context.app.target_app_page.get_current_window()
+ print(f'Original window => {context.original_window}')
+
+
+@when('Click Privacy Policy Link')
+def click_pp_link(context):
+ context.app.target_app_page.click_pp_link()
+
+@when('Switch to new window')
+def switch_window(context):
+ context.app.target_app_page.switch_to_new_window()
+
+
+@then('Verify Privacy Policy page opened')
+def verify_pp_opened(context):
+ context.app.privacy_policy_page.verify_pp_url()
+
+
+@then('Close current page')
+def close(context):
+ context.app.privacy_policy_page.close_page()
+
+
+@then('Return to original window')
+def return_to_original_window(context):
+ context.app.privacy_policy_page.switch_to_window_by_id(context.original_window)
diff --git a/features/tests/cart.feature b/features/tests/cart.feature
new file mode 100644
index 000000000..8925c9c95
--- /dev/null
+++ b/features/tests/cart.feature
@@ -0,0 +1,13 @@
+Feature: Tests for Cart functionality
+
+ Scenario: Verifies that “Your cart is empty” message is shown on the cart icon
+ Given Open Target main page
+ When Click on Cart icon
+ Then Verify “Your cart is empty” message is shown
+
+ Scenario: Verifies that item can be added to the cart
+ Given Open Target main page
+ When Search for cue
+ And Select the first product from the search results
+ And Add the product to the cart
+ Then Verify that the cue is in the cart
\ No newline at end of file
diff --git a/features/tests/help_tests.feature b/features/tests/help_tests.feature
new file mode 100644
index 000000000..a66d6eefd
--- /dev/null
+++ b/features/tests/help_tests.feature
@@ -0,0 +1,14 @@
+Feature: Tests for Help pages
+
+ Scenario: User can select Help Topic Promotions & Coupons
+ Given Open Help page for Returns
+ Then Verify help Returns page opened
+ When Select Help topic Promotions & Coupons
+ Then Verify help Current promotions page opened
+
+
+Scenario: User can select Help Topic Partner Programs
+ Given Open Help page for Returns
+ Then Verify help Returns page opened
+ When Select Help topic Partner Programs
+ Then Verify help Ulta Beauty at Target page opened
\ No newline at end of file
diff --git a/features/tests/main_page_ui_tests.feature b/features/tests/main_page_ui_tests.feature
new file mode 100644
index 000000000..fba47e85a
--- /dev/null
+++ b/features/tests/main_page_ui_tests.feature
@@ -0,0 +1,10 @@
+# Created by dang at 7/10/24
+Feature: Tests for main page UI
+
+ Scenario: Verify header in shown
+ Given Open Target main page
+ Then Verify header in shown
+
+ Scenario: Verify header has correct amount links
+ Given Open Target main page
+ Then Verify header has 6 link
diff --git a/features/tests/sing_in.feature b/features/tests/sing_in.feature
new file mode 100644
index 000000000..4e0ae9206
--- /dev/null
+++ b/features/tests/sing_in.feature
@@ -0,0 +1,34 @@
+Feature: Target Sign In feature tests
+
+ Scenario: Verify that a logged out user can navigate to Sign In
+ Given Open Target main page
+ When Click Sign In
+ When From right side navigation menu, click Sign In
+ Then Verify Sign In form opened
+
+ Scenario: User want to login with correct credentials
+ Given Open target main page
+ When Click Sign In
+ And From right side navigation menu, click Sign In
+ And Enter email "sorenveje@gmailod.com" and password "passcod220045!"
+ And Click Sign In With Password
+ Then Verify user is logged in
+
+ Scenario: User can open and close Terms and Conditions from sign in page
+ Given Open Target main page
+ When Click Sign In
+ And From right side navigation menu, click Sign In
+ And Store original window
+ And Click on Target terms and conditions link
+ And Switch to the newly opened window
+ Then Verify Terms and Conditions page is opened
+ And User can close new window
+ And User can switch back to original
+
+Scenario: User want to login with invalid credentials
+ Given Open target main page
+ When Click Sign In
+ And From right side navigation menu, click Sign In
+ And Enter email "veje@gmailod.com" and password "pas220045!"
+ And Click Sign In With Password
+ Then Verify that warning message is shown
diff --git a/features/tests/target_app_ui_tests.feature b/features/tests/target_app_ui_tests.feature
new file mode 100644
index 000000000..815141013
--- /dev/null
+++ b/features/tests/target_app_ui_tests.feature
@@ -0,0 +1,11 @@
+Feature: Tests for Target App page
+
+ Scenario: Users is able to open Privacy Policy
+ Given Open Target App page
+ And Store original window
+ When Click Privacy Policy Link
+ And Switch to new window
+ Then Verify Privacy Policy page opened
+ And Close current page
+ And Return to original window
+
diff --git a/features/tests/target_circle_page.feature b/features/tests/target_circle_page.feature
new file mode 100644
index 000000000..81cfae0f3
--- /dev/null
+++ b/features/tests/target_circle_page.feature
@@ -0,0 +1,5 @@
+Feature: Tests for Target Circle Page
+
+ Scenario: Verify correct amount of benefit cells
+ Given Open Target Circle page
+ Then Verify there are 10 benefit cells
diff --git a/features/tests/target_search.feature b/features/tests/target_search.feature
new file mode 100644
index 000000000..aec8d85c9
--- /dev/null
+++ b/features/tests/target_search.feature
@@ -0,0 +1,43 @@
+Feature: Target main page search tests
+
+ Scenario: User can search for a tea
+ Given Open Target main page
+ When Search for tea
+ Then Verify search results shown for tea
+ Then Verify correct search results URL opens for tea
+
+Scenario: User can search for a cue
+ Given Open Target main page
+ When Search for cue
+ Then Verify search results shown for cue
+ Then Verify correct search results URL opens for cue
+
+@smoke
+Scenario: User can search for a speaker
+ Given Open Target main page
+ When Search for speaker
+ Then Verify search results shown for speaker
+ Then Verify correct search results URL opens for speaker
+
+Scenario Outline: User can search for product
+ Given Open Target main page
+ When Search for
+ Then Verify search results shown for
+ Then Verify correct search results URL opens for
+ Examples:
+ |product |expected_result |
+ |tea |tea |
+ |cue |cue |
+ |speaker |speaker |
+
+ @smoke
+Scenario: Verify that every product on Target search result page has image and product name
+ Given Open Target main page
+ When Search for cup
+ Then Verify that search results has the product name and an image
+
+Scenario: User can see favorites tooltip for search result
+ Given Open Target main page
+ When Search for tea
+ And Hover favorites icon
+ Then Favorites tooltip is shown
diff --git a/frames.py b/frames.py
new file mode 100644
index 000000000..bbc780fe8
--- /dev/null
+++ b/frames.py
@@ -0,0 +1,28 @@
+from selenium.webdriver.chrome.service import Service
+from selenium import webdriver
+from selenium.webdriver.common.by import By
+from webdriver_manager.chrome import ChromeDriverManager
+
+# init driver
+driver_path = ChromeDriverManager().install()
+service = Service(driver_path)
+driver = webdriver.Chrome(service=service)
+
+driver.implicitly_wait(4)
+
+# open the url
+driver.get('https://www.w3schools.com/tags/tryit.asp?filename=tryhtml_iframe')
+
+# Switch to frame 1
+driver.switch_to.frame('iframeResult')
+
+# Switch to frame 2
+frame2 = driver.find_element(By.CSS_SELECTOR, "iframe[src='https://www.w3schools.com']")
+driver.switch_to.frame(frame2)
+
+menu = driver.find_element(By.CSS_SELECTOR, '.tnb-menu-btn')
+print(menu.get_attribute('title'))
+
+assert menu.get_attribute('title') == 'Menu', f"Title is {menu.get_attribute('title')} instead of Menu"
+
+driver.quit()
\ No newline at end of file
diff --git a/locators.py b/locators.py
new file mode 100644
index 000000000..874c257ac
--- /dev/null
+++ b/locators.py
@@ -0,0 +1,42 @@
+from selenium import webdriver
+from selenium.webdriver.common.by import By
+from selenium.webdriver.chrome.service import Service
+from webdriver_manager.chrome import ChromeDriverManager
+from time import sleep
+
+# get the path to the ChromeDriver executable
+driver_path = ChromeDriverManager().install()
+
+# create a new Chrome browser instance
+service = Service(driver_path)
+driver = webdriver.Chrome(service=service)
+driver.maximize_window()
+
+# open the url
+driver.get('https://www.amazon.com/')
+
+# find by ID
+driver.find_element(By.ID, 'twotabsearchtextbox')
+driver.find_element(By.ID, 'nav-logo-sprites')
+
+# find by XPATH
+driver.find_element(By.XPATH, "//input[@aria-label='Search Amazon']")
+driver.find_element(By.XPATH, "//input[@placeholder='Search Amazon']")
+# find by attribute only
+driver.find_element(By.XPATH, "//*[@placeholder='Search Amazon']")
+# find by multiple attributes
+driver.find_element(By.XPATH, "//input[@tabindex='0' and @type='text' and @dir='auto']")
+driver.find_element(By.XPATH, "//input[@tabindex='0' and @type='text']")
+
+driver.find_element(By.ID, 'searchDropdownBox') # driver.find_element(By.XPATH, "//select[@ID='searchDropdownBox']")
+
+# by text()
+driver.find_element(By.XPATH, "//a[text()='Best Sellers']")
+# text() and attributes
+driver.find_element(By.XPATH, "//a[text()='Best Sellers' and @class='nav-a ']")
+driver.find_element(By.XPATH, "//a[@class='nav-a ' and text()='Best Sellers']")
+# connecting to parent node
+driver.find_element(By.XPATH, "//div[@id='nav-xshop']//a[text()='Best Sellers']")
+driver.find_element(By.XPATH, "//div[@id='nav-xshop']//a[text()='Best Sellers']")
+# contains()
+driver.find_element(By.XPATH, "//h2[contains(text(), 'under $30')]")
\ No newline at end of file
diff --git a/pages/__init__.py b/pages/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/pages/account_side_menu.py b/pages/account_side_menu.py
new file mode 100644
index 000000000..9c94bd171
--- /dev/null
+++ b/pages/account_side_menu.py
@@ -0,0 +1,9 @@
+from selenium.webdriver.common.by import By
+from pages.base_page import Page
+from time import sleep
+
+class AccountSideMenu(Page):
+ SIGN_IN_BTN = (By.CSS_SELECTOR, "[data-test = accountNav-signIn]")
+
+ def sign_in_btn(self):
+ self.click(*self.SIGN_IN_BTN)
\ No newline at end of file
diff --git a/pages/base_page.py b/pages/base_page.py
new file mode 100644
index 000000000..d5f3aa94f
--- /dev/null
+++ b/pages/base_page.py
@@ -0,0 +1,99 @@
+from selenium.webdriver.support.wait import WebDriverWait
+from selenium.webdriver.support import expected_conditions as EC
+from selenium.webdriver.common.action_chains import ActionChains
+
+from support.logger import logger
+
+
+class Page:
+
+ def __init__(self, driver):
+ self.driver = driver
+ self.wait = WebDriverWait(driver, 15)
+
+ def open_url(self, url):
+ logger.info(f'Opening {url}...')
+ self.driver.get(url)
+
+ def find_element(self, *locator):
+ return self.driver.find_element(*locator)
+
+ def find_elements(self, *locator):
+ logger.info(f'Searching for element {locator}...')
+ return self.driver.find_elements(*locator)
+
+ def click(self, *locator):
+ logger.info(f'Clicking element {locator}...')
+ self.driver.find_element(*locator).click()
+
+ def input_text(self, text, *locator):
+ logger.info(f'Inputting text {text} for element {locator}...')
+ self.driver.find_element(*locator).send_keys(text)
+
+ def hover_element(self, *locator):
+ element = self.find_element(*locator)
+ actions = ActionChains(self.driver)
+ actions.move_to_element(element)
+ actions.perform()
+
+ def get_current_window(self):
+ window = self.driver.current_window_handle
+ print('Current window:', window)
+ return window
+
+ def switch_to_new_window(self):
+ self.wait.until(EC.new_window_is_opened)
+ windows = self.driver.window_handles
+ print(f'All windows => {windows}')
+ self.driver.switch_to.window(windows[1])
+ print(f'Switched to window => {windows[1]}')
+
+ def switch_to_window_by_id(self, window_id):
+ self.driver.switch_to.window(window_id)
+ print(f'Switched to window => {window_id}')
+
+ def close_page(self):
+ self.driver.close()
+
+ def wait_until_clickable(self, *locator):
+ self.wait.until(
+ EC.element_to_be_clickable(locator),
+ message ='Element by locator {locator} not clickable').click()
+
+ def wait_and_click(self, *locator):
+ self.wait.until(
+ EC.element_to_be_clickable(locator),
+ message='Element by locator {locator} not clickable').click()
+
+ def wait_for_element_appear(self, *locator):
+ self.wait.until(
+ EC.visibility_of_element_located(locator),
+ message='Element by locator {locator} not visible')
+
+ def wait_for_element_disappear(self, *locator):
+ self.wait.until(
+ EC.invisibility_of_element_located(locator),
+ message='Element by locator {locator} shown, but should not appear')
+
+ def verify_text(self, expected_text, *locator):
+ actual_text = self.driver.find_element(*locator).text
+ assert actual_text == expected_text, f'Expected {expected_text} did not match actual text {actual_text}'
+
+ def verify_partial_text(self, expected_partial_text, *locator):
+ actual_text = self.driver.find_element(*locator).text
+ assert expected_partial_text in actual_text, f'Expected {expected_partial_text} not in actual actual {actual_text}'
+
+ def verify_url(self, expected_url):
+ actual_url = self.driver.current_url
+ assert expected_url == actual_url, f'Expected url {expected_url} did not match actual {actual_url}'
+
+ def verify_partial_url(self, expected_partial_url):
+ actual_url = self.driver.current_url
+ assert expected_partial_url in actual_url, (f'Expected url {expected_partial_url} not in actual {actual_url}')
+
+
+
+
+
+
+
diff --git a/pages/cart_page.py b/pages/cart_page.py
new file mode 100644
index 000000000..d93198ee1
--- /dev/null
+++ b/pages/cart_page.py
@@ -0,0 +1,12 @@
+from selenium.webdriver.common.by import By
+from pages.base_page import Page
+
+class CartPage(Page):
+
+ EMPTY_CART_MSG = (By.CSS_SELECTOR, "h1.sc-fe064f5c-0")
+ def verify_empty_cart_msg(self):
+ self.verify_text('Your cart is empty', *self.EMPTY_CART_MSG)
+
+ # expected_text = 'Your cart is empty'
+ # actual_text = self.driver.find_element(*self.EMPTY_CART_MSG).text
+ # assert expected_text == actual_text, f'Expected text {expected_text} is not in actual text {actual_text}'
\ No newline at end of file
diff --git a/pages/cart_side_menu.py b/pages/cart_side_menu.py
new file mode 100644
index 000000000..eb93f676a
--- /dev/null
+++ b/pages/cart_side_menu.py
@@ -0,0 +1,12 @@
+from selenium.webdriver.common.by import By
+from pages.base_page import Page
+
+class CartSideMenu(Page):
+ ADD_TO_CART_BTN = (By.CSS_SELECTOR, "[data-test= 'shippingButton']")
+ ADD_TO_CART_MSG = (By.XPATH, "//*[text()='Added to cart']")
+
+ def add_product_to_cart(self):
+ self.click(*self.ADD_TO_CART_BTN)
+
+ def verify_cart(self):
+ self.verify_text('Added to cart', *self.ADD_TO_CART_MSG)
\ No newline at end of file
diff --git a/pages/header.py b/pages/header.py
new file mode 100644
index 000000000..f1e4219f4
--- /dev/null
+++ b/pages/header.py
@@ -0,0 +1,22 @@
+from selenium.webdriver.common.by import By
+from pages.base_page import Page
+from time import sleep
+
+class Header(Page):
+ SEARCH_FIELD = (By.ID, "search")
+ SEARCH_BTN = (By.XPATH, "//button[@data-test='@web/Search/SearchButton']")
+ CART_BTN = (By.CSS_SELECTOR, "[data-test='@web/CartIcon']")
+ SING_IN_BTB = (By.CSS_SELECTOR, "span.sc-58ad44c0-3")
+
+ def search_product(self, search_item):
+ self.input_text(search_item, *self.SEARCH_FIELD)
+ self.click(*self.SEARCH_BTN)
+ # wait for the page with search results to load
+ sleep(6)
+
+ def cart_btn(self):
+ self.click(*self.CART_BTN)
+ sleep(8)
+
+ def sing_in_btn(self):
+ self.click(*self.SING_IN_BTB)
\ No newline at end of file
diff --git a/pages/help_page.py b/pages/help_page.py
new file mode 100644
index 000000000..c6cb61cdb
--- /dev/null
+++ b/pages/help_page.py
@@ -0,0 +1,41 @@
+from selenium.webdriver.common.by import By
+from selenium.webdriver.support.ui import Select
+from pages.base_page import Page
+from time import sleep
+
+
+class HelpPage(Page):
+
+ HEADER_RETURNS = (By.XPATH, "//h1[text()=' Returns']")
+ HEADER_PROMOTIONS = (By.XPATH, "//h1[text()=' Current promotions']")
+ HEADER = (By.XPATH, "//h1[text()=' {SUBSTRING}']")
+ TOPIC_SELECTION = (By.CSS_SELECTOR, "select[id*='ViewHelpTopics']")
+
+ # Dynamic Locators:
+ def _get_locator(self, text):
+ # OLD (By.XPATH, "//h1[text()=' {SUBSTRING}']")
+ # => NEW if text Current promotions (By.XPATH, "//h1[text()=' Current promotions']")
+ # => NEW if text Returns (By.XPATH, "//h1[text()=' Returns']")
+ return [self.HEADER[0], self.HEADER[1].replace('{SUBSTRING}', text)]
+
+ def verify_header(self, expected_text):
+ locator = self._get_locator(expected_text)
+ self.wait_for_element_appear(*locator)
+
+ def open_help_returns(self):
+ self.open_url('https://help.target.com/help/subcategoryarticle?childcat=Returns&parentcat=Returns+%26+Exchanges&searchQuery=')
+
+ def select_topic(self, option):
+ dd = self.find_element(*self.TOPIC_SELECTION)
+ select = Select(dd)
+ select.select_by_value(option)
+
+ # Replace it for dynamic function VERIFY_HEADER
+ # def verify_returns(self):
+ # self.wait_for_element_appear(*self.HEADER_RETURNS)
+ #
+ #
+ # def verify_promotions(self):
+ # self.wait_for_element_appear(*self.HEADER_PROMOTIONS)
+
+
diff --git a/pages/main_page.py b/pages/main_page.py
new file mode 100644
index 000000000..80a8ed257
--- /dev/null
+++ b/pages/main_page.py
@@ -0,0 +1,5 @@
+from pages.base_page import Page
+
+class MainPage(Page):
+ def open(self):
+ self.open_url('https://www.target.com/')
\ No newline at end of file
diff --git a/pages/privacy_policy_page.py b/pages/privacy_policy_page.py
new file mode 100644
index 000000000..b40f42eb9
--- /dev/null
+++ b/pages/privacy_policy_page.py
@@ -0,0 +1,7 @@
+from selenium.webdriver.common.by import By
+from pages.base_page import Page
+
+class PrivacyPolicyPage(Page):
+
+ def verify_pp_url(self):
+ self.verify_partial_url('/target-privacy-policy')
\ No newline at end of file
diff --git a/pages/search_results_page.py b/pages/search_results_page.py
new file mode 100644
index 000000000..34c4db2b3
--- /dev/null
+++ b/pages/search_results_page.py
@@ -0,0 +1,36 @@
+from selenium.webdriver.common.by import By
+from selenium.webdriver.common.action_chains import ActionChains
+from pages.base_page import Page
+from time import sleep
+
+
+class SearchResultsPage(Page):
+
+ SEARCH_RESULTS_TXT = (By.XPATH, "//div[@data-test = 'resultsHeading']")
+ FIRST_ITEM_ADD_BTN = (By.CSS_SELECTOR, "#addToCartButtonOrTextIdFor79545713")
+ FAVORITES_BTN = (By.CSS_SELECTOR, "[data-test='FavoritesButton']")
+ FAVORITES_TOOLTIP_TXT = (By.XPATH, "//*[text()='Click to sign in and save']")
+
+ def hover_fav_icon(self):
+ self.wait_for_element_appear(*self.FAVORITES_BTN) # Will replace sleep (2)
+ self.hover_element(*self.FAVORITES_BTN)
+ # fav_icon = self.find_element(*self.FAVORITES_BTN)
+ # actions = ActionChains(self.driver)
+ # actions.move_to_element(fav_icon)
+ # actions.perform()
+ # sleep(2)
+
+ def verify_fav_tooltip(self):
+ self.wait_for_element_appear(*self.FAVORITES_TOOLTIP_TXT)
+
+ def verify_search_results(self, expected_product):
+ self.verify_partial_text(expected_product, *self.SEARCH_RESULTS_TXT)
+
+ def verify_product_in_url(self, expected_product):
+ self.verify_partial_url(expected_product)
+ # url = self.driver.current_url
+ # assert expected_product in url, f'Expected {expected_product} not in {url}'
+
+ def select_first_item(self):
+ self.click(*self.FIRST_ITEM_ADD_BTN)
+ sleep(6)
\ No newline at end of file
diff --git a/pages/sign_in_page.py b/pages/sign_in_page.py
new file mode 100644
index 000000000..307a4e160
--- /dev/null
+++ b/pages/sign_in_page.py
@@ -0,0 +1,34 @@
+from selenium.webdriver.common.by import By
+from pages.base_page import Page
+from time import sleep
+
+
+class SignInPage(Page):
+ SIGN_IN_PAGE_MSG = (By.CSS_SELECTOR, "h1.sc-fe064f5c-0")
+ EMAIL_INPUT_FIELD = (By.ID, 'username')
+ PASS_INPUT_FIELD = (By.ID, 'password')
+ SIGN_IN_WITH_PASS = (By.CSS_SELECTOR, "#login")
+ SIGN_IN_FORM = (By.CSS_SELECTOR, ".sc-fe064f5c-0")
+ TC_LINK =(By.XPATH, "//a[text()='Target terms and conditions']")
+ WARNING_MSG = (By.CSS_SELECTOR, "[data-test='authAlertDisplay']")
+
+ def click_tc_link(self):
+ self.click(*self.TC_LINK)
+
+ def verify_sign_in_form(self):
+ expected_text = 'Sign into your Target account'
+ actual_text = self.driver.find_element(*self.SIGN_IN_PAGE_MSG).text
+ assert expected_text == actual_text, f'Expected text {expected_text} is not in actual text {actual_text}'
+
+ def input_email_and_password(self, email, password):
+ self.input_text(email, *self.EMAIL_INPUT_FIELD)#.send_keys(email)
+ self.input_text(password,*self.PASS_INPUT_FIELD)#.send_keys(password)
+
+ def sign_in_with_password(self):
+ self.click(*self.SIGN_IN_WITH_PASS)
+
+ def verify_user_login(self):
+ self.wait_for_element_disappear(*self.SIGN_IN_FORM)
+
+ def verify_user_cannot_login(self):
+ self.wait_for_element_appear(*self.WARNING_MSG)
diff --git a/pages/target_app_page.py b/pages/target_app_page.py
new file mode 100644
index 000000000..1b669b661
--- /dev/null
+++ b/pages/target_app_page.py
@@ -0,0 +1,11 @@
+from selenium.webdriver.common.by import By
+from pages.base_page import Page
+
+class TargetAppPage(Page):
+ PP_LINK = (By.XPATH, "//a[text()='Privacy policy']")
+
+ def open_target_app(self):
+ self.open_url('https://www.taget.com/c/target-app/')
+
+ def click_pp_link(self):
+ self.click(*self.PP_LINK)
\ No newline at end of file
diff --git a/pages/terms_and_conditions_page.py b/pages/terms_and_conditions_page.py
new file mode 100644
index 000000000..0eae39d7e
--- /dev/null
+++ b/pages/terms_and_conditions_page.py
@@ -0,0 +1,7 @@
+from selenium.webdriver.common.by import By
+from pages.base_page import Page
+
+class TermsAndConditionsPage(Page):
+
+ def verify_tc_url(self):
+ self.verify_partial_url('/terms-conditions')
\ No newline at end of file
diff --git a/sample_script.py b/sample_script.py
index 23d64fc06..ddb7322f3 100755
--- a/sample_script.py
+++ b/sample_script.py
@@ -2,15 +2,20 @@
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
+from selenium.webdriver.support.wait import WebDriverWait
+from selenium.webdriver.support import expected_conditions as EC
from time import sleep
# get the path to the ChromeDriver executable
-driver_path = ChromeDriverManager().install()
+# driver_path = ChromeDriverManager().install()
+driver_path = '/Users/dang/Desktop/Careerist/Python_Selenium/python-selenium-automation/chromedriver'
# create a new Chrome browser instance
service = Service(driver_path)
driver = webdriver.Chrome(service=service)
driver.maximize_window()
+driver.implicitly_wait(5)
+driver.wait = WebDriverWait(driver, 15)
# open the url
driver.get('https://www.google.com/')
@@ -18,16 +23,24 @@
# populate search field
search = driver.find_element(By.NAME, 'q')
search.clear()
-search.send_keys('Car')
+search.send_keys('Table')
# wait for 4 sec
-sleep(4)
-
+#sleep(4)
+# Option 1 with expected_condition
+button = driver.wait.until(EC.element_to_be_clickable((By.NAME, 'btnK')))
# click search button
-driver.find_element(By.NAME, 'btnK').click()
+button.click()
+
+# # Option 2 with expected_condition
+# # create a variable for button
+# SEARCH_BTN = (By.NAME, 'btnK')
+# button = driver.wait.until(EC.element_to_be_clickable(SEARCH_BTN))
+# # click search button
+# button.click()
# verify search results
-assert 'car' in driver.current_url.lower(), f"Expected query not in {driver.current_url.lower()}"
+assert 'table' in driver.current_url.lower(), f"Expected query not in {driver.current_url.lower()}"
print('Test Passed')
driver.quit()
diff --git a/sandbox.py b/sandbox.py
new file mode 100644
index 000000000..c0b0cbbc5
--- /dev/null
+++ b/sandbox.py
@@ -0,0 +1,19 @@
+from selenium import webdriver
+from selenium.webdriver.common.by import By
+from selenium.webdriver.chrome.service import Service
+from webdriver_manager.chrome import ChromeDriverManager
+from time import sleep
+
+# get the path to the ChromeDriver executable
+driver_path = ChromeDriverManager().install()
+
+# create a new Chrome browser instance
+service = Service(driver_path)
+driver = webdriver.Chrome(service=service)
+driver.maximize_window()
+# Open the URL
+driver.get('https://www.target.com/login?client_id=ecom-web-1.0.0&ui_namespace=ui-default&back_button_action=browser&keep_me_signed_in=true&kmsi_default=false&actions=create_session_signin')
+
+sing_in_text = driver.find_element(By.XPATH, "//h1//span[text()='Sign into your Target account']")
+
+sleep(2)
diff --git a/support/__init__.py b/support/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/support/logger.py b/support/logger.py
new file mode 100644
index 000000000..48a2729fd
--- /dev/null
+++ b/support/logger.py
@@ -0,0 +1,16 @@
+import logging
+
+logger = logging.getLogger(__name__)
+# Determine which log messages are actually written to the log file.
+# logging.DEBUG will capture and log messages at all levels, including DEBUG, INFO, WARNING, ERROR, and CRITICAL.
+logger.setLevel(logging.DEBUG)
+
+# FileHandler is an object that defines how log messages should be written to a file
+# Create a file handler that will write log messages to a file named 'test_automation.log'
+handler = logging.FileHandler('./test_automation.log')
+handler.setLevel(logging.DEBUG)
+
+# Define the format for log messages, including timestamp, logger name, log level, and the actual message
+formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
+handler.setFormatter(formatter)
+logger.addHandler(handler)
\ No newline at end of file
diff --git a/target_search_script.py b/target_search_script.py
new file mode 100644
index 000000000..5087afd67
--- /dev/null
+++ b/target_search_script.py
@@ -0,0 +1,43 @@
+from selenium import webdriver
+from selenium.webdriver.common.by import By
+from selenium.webdriver.chrome.service import Service
+from webdriver_manager.chrome import ChromeDriverManager
+from time import sleep
+
+# get the path to the ChromeDriver executable
+driver_path = ChromeDriverManager().install()
+
+# create a new Chrome browser instance
+service = Service(driver_path)
+driver = webdriver.Chrome(service=service)
+driver.maximize_window()
+
+# open the url
+driver.get('https://www.target.com/')
+
+# populate search field
+driver.find_element(By.ID, 'search').send_keys('tea')
+
+driver.find_element(By.XPATH, "//button[@data-test ='@web/Search/SearchButton']").click()
+
+sleep(7)
+
+#verify
+expected_text = ('tea')
+actual_text = driver.find_element(By.XPATH, "//div[@data-test = 'resultsHeading']").text
+
+assert expected_text in actual_text, f'Expected text {expected_text} is not in actual text {actual_text}'
+
+print("Test case passed")
+
+
+
+# wait for 4 sec
+sleep(9)
+#
+# # click search button
+# driver.find_element(By.NAME, 'btnK').click()
+#
+# # verify search results
+# assert 'tea' in driver.current_url.lower(), f"Expected query not in {driver.current_url.lower()}"
+# print('Test Passed')
diff --git a/test_results/442ab9f7-69f9-42f9-943f-6a26a167e8fb-result.json b/test_results/442ab9f7-69f9-42f9-943f-6a26a167e8fb-result.json
new file mode 100644
index 000000000..432f8a874
--- /dev/null
+++ b/test_results/442ab9f7-69f9-42f9-943f-6a26a167e8fb-result.json
@@ -0,0 +1 @@
+{"name": "Verify that every product on Target search result page has image and product name", "status": "passed", "steps": [{"name": "Given Open Target main page", "status": "passed", "start": 1725560510436, "stop": 1725560511674}, {"name": "When Search for cup", "status": "passed", "start": 1725560511675, "stop": 1725560519928}, {"name": "Then Verify that search results has the product name and an image", "status": "passed", "start": 1725560519928, "stop": 1725560524186}], "start": 1725560508844, "stop": 1725560524405, "uuid": "c9b99ca7-a11d-4d79-a5fc-5219a2370039", "historyId": "cae710a4b6c0f400de80de8718901768", "fullName": "Target main page search tests: Verify that every product on Target search result page has image and product name", "labels": [{"name": "severity", "value": "normal"}, {"name": "tag", "value": "smoke"}, {"name": "feature", "value": "Target main page search tests"}, {"name": "framework", "value": "behave"}, {"name": "language", "value": "cpython3"}]}
\ No newline at end of file
diff --git a/test_results/47fa1763-2f40-4f59-b056-6da3e6d8a37e-result.json b/test_results/47fa1763-2f40-4f59-b056-6da3e6d8a37e-result.json
new file mode 100644
index 000000000..a147adbd7
--- /dev/null
+++ b/test_results/47fa1763-2f40-4f59-b056-6da3e6d8a37e-result.json
@@ -0,0 +1 @@
+{"name": "User can search for product -- @1.2 ", "status": "skipped", "steps": [{"name": "Given Open Target main page", "status": "skipped", "start": 1725560508842, "stop": 1725560508842}, {"name": "When Search for cue", "status": "skipped", "start": 1725560508842, "stop": 1725560508842}, {"name": "Then Verify search results shown for cue", "status": "skipped", "start": 1725560508842, "stop": 1725560508842}, {"name": "Then Verify correct search results URL opens for cue", "status": "skipped", "start": 1725560508842, "stop": 1725560508842}], "parameters": [{"name": "product", "value": "cue"}, {"name": "expected_result", "value": "cue"}], "start": 1725560508842, "stop": 1725560508842, "uuid": "f3a47171-b98f-479a-a2e4-5dca4548d039", "historyId": "b8d406510e47e12753f45f04cfb961ea", "fullName": "Target main page search tests: User can search for product", "labels": [{"name": "severity", "value": "normal"}, {"name": "feature", "value": "Target main page search tests"}, {"name": "framework", "value": "behave"}, {"name": "language", "value": "cpython3"}]}
\ No newline at end of file
diff --git a/test_results/718fdaa9-ad62-4011-b67d-137484314a76-result.json b/test_results/718fdaa9-ad62-4011-b67d-137484314a76-result.json
new file mode 100644
index 000000000..cf99530c6
--- /dev/null
+++ b/test_results/718fdaa9-ad62-4011-b67d-137484314a76-result.json
@@ -0,0 +1 @@
+{"name": "User can search for a tea", "status": "skipped", "steps": [{"name": "Given Open Target main page", "status": "skipped", "start": 1725560497314, "stop": 1725560497314}, {"name": "When Search for tea", "status": "skipped", "start": 1725560497314, "stop": 1725560497314}, {"name": "Then Verify search results shown for tea", "status": "skipped", "start": 1725560497314, "stop": 1725560497314}, {"name": "Then Verify correct search results URL opens for tea", "status": "skipped", "start": 1725560497314, "stop": 1725560497314}], "start": 1725560497313, "stop": 1725560497314, "uuid": "4352eac2-b698-4c1d-8f01-126f6c1ed73f", "historyId": "c46c517db530d707f9d2b7b3a55cba42", "fullName": "Target main page search tests: User can search for a tea", "labels": [{"name": "severity", "value": "normal"}, {"name": "feature", "value": "Target main page search tests"}, {"name": "framework", "value": "behave"}, {"name": "language", "value": "cpython3"}]}
\ No newline at end of file
diff --git a/test_results/777d01ab-3763-41b8-81b7-8d038d378f35-result.json b/test_results/777d01ab-3763-41b8-81b7-8d038d378f35-result.json
new file mode 100644
index 000000000..289699ae4
--- /dev/null
+++ b/test_results/777d01ab-3763-41b8-81b7-8d038d378f35-result.json
@@ -0,0 +1 @@
+{"name": "User can search for product -- @1.3 ", "status": "skipped", "steps": [{"name": "Given Open Target main page", "status": "skipped", "start": 1725560508843, "stop": 1725560508843}, {"name": "When Search for speaker", "status": "skipped", "start": 1725560508843, "stop": 1725560508843}, {"name": "Then Verify search results shown for speaker", "status": "skipped", "start": 1725560508843, "stop": 1725560508843}, {"name": "Then Verify correct search results URL opens for speaker", "status": "skipped", "start": 1725560508843, "stop": 1725560508843}], "parameters": [{"name": "product", "value": "speaker"}, {"name": "expected_result", "value": "speaker"}], "start": 1725560508843, "stop": 1725560508843, "uuid": "23612558-0ce8-45bb-9a51-6c446a86078a", "historyId": "00efa32c63ce6c8b22ffbd414e2cbfcd", "fullName": "Target main page search tests: User can search for product", "labels": [{"name": "severity", "value": "normal"}, {"name": "feature", "value": "Target main page search tests"}, {"name": "framework", "value": "behave"}, {"name": "language", "value": "cpython3"}]}
\ No newline at end of file
diff --git a/test_results/8e5bfcce-79aa-4411-a14c-1d7f9645432e-result.json b/test_results/8e5bfcce-79aa-4411-a14c-1d7f9645432e-result.json
new file mode 100644
index 000000000..fdddb758f
--- /dev/null
+++ b/test_results/8e5bfcce-79aa-4411-a14c-1d7f9645432e-result.json
@@ -0,0 +1 @@
+{"name": "User can search for product -- @1.1 ", "status": "skipped", "steps": [{"name": "Given Open Target main page", "status": "skipped", "start": 1725560508841, "stop": 1725560508841}, {"name": "When Search for tea", "status": "skipped", "start": 1725560508841, "stop": 1725560508841}, {"name": "Then Verify search results shown for tea", "status": "skipped", "start": 1725560508841, "stop": 1725560508841}, {"name": "Then Verify correct search results URL opens for tea", "status": "skipped", "start": 1725560508841, "stop": 1725560508841}], "parameters": [{"name": "product", "value": "tea"}, {"name": "expected_result", "value": "tea"}], "start": 1725560508841, "stop": 1725560508841, "uuid": "8f68c223-d481-463c-91aa-ce6e7e8c263c", "historyId": "c762bd0e945e5dadabf244b99f2ee87d", "fullName": "Target main page search tests: User can search for product", "labels": [{"name": "severity", "value": "normal"}, {"name": "feature", "value": "Target main page search tests"}, {"name": "framework", "value": "behave"}, {"name": "language", "value": "cpython3"}]}
\ No newline at end of file
diff --git a/test_results/c163641c-f315-4b48-bc33-92a40bfda8cb-result.json b/test_results/c163641c-f315-4b48-bc33-92a40bfda8cb-result.json
new file mode 100644
index 000000000..830ff04fb
--- /dev/null
+++ b/test_results/c163641c-f315-4b48-bc33-92a40bfda8cb-result.json
@@ -0,0 +1 @@
+{"name": "User can search for a cue", "status": "skipped", "steps": [{"name": "Given Open Target main page", "status": "skipped", "start": 1725560497316, "stop": 1725560497316}, {"name": "When Search for cue", "status": "skipped", "start": 1725560497316, "stop": 1725560497316}, {"name": "Then Verify search results shown for cue", "status": "skipped", "start": 1725560497316, "stop": 1725560497316}, {"name": "Then Verify correct search results URL opens for cue", "status": "skipped", "start": 1725560497316, "stop": 1725560497316}], "start": 1725560497316, "stop": 1725560497316, "uuid": "3627dee3-910f-4aa7-9070-d977b7580c68", "historyId": "96cbca0b85f45e0e016de63b32ef25f6", "fullName": "Target main page search tests: User can search for a cue", "labels": [{"name": "severity", "value": "normal"}, {"name": "feature", "value": "Target main page search tests"}, {"name": "framework", "value": "behave"}, {"name": "language", "value": "cpython3"}]}
\ No newline at end of file
diff --git a/test_results/d52edce6-3ea3-4e66-8e72-ad6c8d1a3f24-result.json b/test_results/d52edce6-3ea3-4e66-8e72-ad6c8d1a3f24-result.json
new file mode 100644
index 000000000..4e8bfb19b
--- /dev/null
+++ b/test_results/d52edce6-3ea3-4e66-8e72-ad6c8d1a3f24-result.json
@@ -0,0 +1 @@
+{"name": "User can see favorites tooltip for search result", "status": "skipped", "steps": [{"name": "Given Open Target main page", "status": "skipped", "start": 1725560524407, "stop": 1725560524407}, {"name": "When Search for tea", "status": "skipped", "start": 1725560524407, "stop": 1725560524407}, {"name": "And Hover favorites icon", "status": "skipped", "start": 1725560524407, "stop": 1725560524407}, {"name": "Then Favorites tooltip is shown", "status": "skipped", "start": 1725560524407, "stop": 1725560524407}], "start": 1725560524406, "stop": 1725560524407, "uuid": "6966dce4-34dd-4bf3-b7b4-c1cc83e246a0", "historyId": "9da95efdec313573a4b3b57da43cccb0", "fullName": "Target main page search tests: User can see favorites tooltip for search result", "labels": [{"name": "severity", "value": "normal"}, {"name": "feature", "value": "Target main page search tests"}, {"name": "framework", "value": "behave"}, {"name": "language", "value": "cpython3"}]}
\ No newline at end of file
diff --git a/test_results/f585b8ba-7f7b-4e64-876f-eb093aadb13f-result.json b/test_results/f585b8ba-7f7b-4e64-876f-eb093aadb13f-result.json
new file mode 100644
index 000000000..5c793482f
--- /dev/null
+++ b/test_results/f585b8ba-7f7b-4e64-876f-eb093aadb13f-result.json
@@ -0,0 +1 @@
+{"name": "User can search for a speaker", "status": "passed", "steps": [{"name": "Given Open Target main page", "status": "passed", "start": 1725560498650, "stop": 1725560500468}, {"name": "When Search for speaker", "status": "passed", "start": 1725560500468, "stop": 1725560508597}, {"name": "Then Verify search results shown for speaker", "status": "passed", "start": 1725560508597, "stop": 1725560508628}, {"name": "Then Verify correct search results URL opens for speaker", "status": "passed", "start": 1725560508628, "stop": 1725560508642}], "start": 1725560497317, "stop": 1725560508840, "uuid": "6776658b-35b4-4ce6-b142-4f4d6d648ffe", "historyId": "dd2b2ed0d4ba9a22466aa120465a5095", "fullName": "Target main page search tests: User can search for a speaker", "labels": [{"name": "severity", "value": "normal"}, {"name": "tag", "value": "smoke"}, {"name": "feature", "value": "Target main page search tests"}, {"name": "framework", "value": "behave"}, {"name": "language", "value": "cpython3"}]}
\ No newline at end of file