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:

    +
      +
    1. Heat broiler.
    2. +
    3. Put olive oil in a large heavy-bottomed soup pot or Dutch oven over medium heat.
    4. +
    5. Add the minced garlic and red pepper and let sizzle for 30 seconds without browning.
    6. +
    7. Add the mussels, stir to coat and increase heat to high.
    8. +
    9. Add the wine or water, and put on lid.
    10. +
    11. After 2 minutes, give the mussels a stir, then replace lid and continue cooking until all + mussels have opened, 6 to 8 minutes. +
    12. +
    +

    +

    +

    Step 2:

    +
      +
    1. Paint cut sides of the baguette pieces with oil and place cut side up under broiler to toast. +
    2. +
    3. Rub toasts with the remaining garlic cloves.
    4. +
    +

    +

    +

    Step 3:

    +
      +
    1. Stir the chopped parsley into the mussels, then ladle mussels and broth into bowls.
    2. +
    3. Serve with the garlic toasts.
    4. +
    +

    +
    +
    + + + + \ 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