From e72476c0d95ada65e7c48e5c69e690df6212ee91 Mon Sep 17 00:00:00 2001 From: Alina Dorofeyeva Date: Sat, 8 Mar 2025 15:06:55 -0800 Subject: [PATCH 1/8] Update search script --- sample_script.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sample_script.py b/sample_script.py index 3925e9462..0804f58ce 100755 --- a/sample_script.py +++ b/sample_script.py @@ -18,7 +18,7 @@ # 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) @@ -27,7 +27,7 @@ driver.find_element(By.NAME, 'btnK').click() # verify search results -assert 'car'.lower() in driver.current_url.lower(), f"Expected query not in {driver.current_url.lower()}" +assert 'table'.lower() in driver.current_url.lower(), f"Expected query not in {driver.current_url.lower()}" print('Test Passed') driver.quit() From 8d4c6d7c2e61fb15b1be9843723b147da8213670 Mon Sep 17 00:00:00 2001 From: Alina Dorofeyeva Date: Sat, 8 Mar 2025 21:48:45 -0800 Subject: [PATCH 2/8] Update search script --- Week 2 HW.py | 66 ++++++++++++++++++++++++++++++++++++++++++++++++ locator.py | 42 ++++++++++++++++++++++++++++++ molly | 8 ++++++ molly.pub | 1 + sample_script.py | 22 ++++++++-------- 5 files changed, 127 insertions(+), 12 deletions(-) create mode 100644 Week 2 HW.py create mode 100644 locator.py create mode 100644 molly create mode 100644 molly.pub diff --git a/Week 2 HW.py b/Week 2 HW.py new file mode 100644 index 000000000..cc3013d85 --- /dev/null +++ b/Week 2 HW.py @@ -0,0 +1,66 @@ +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() +driver.maximize_window() + +# Open Amazon Sign-In page +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&") + + +#Amazon logo +driver.find_element(By.XPATH, "//a[@class='a-logo']") + +#Email field +driver.find_element(By.XPATH, "//input[@type='email' or @name='email']") + +#Continue button +driver.find_element(By.XPATH, "//input[@id='continue']") + +#Conditions of use link +driver.find_element(By.XPATH, "//a[contains(@href, 'condition_of_use')]") + +#Privacy Notice link +driver.find_element(By.XPATH, "//a[contains(@href, '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(@href, 'forgotpassword')]") + +#Other issues with Sign-In link +driver.find_element(By.XPATH, "//a[contains(@href, 'ap_signin_notification_popup')]") + +#Create your Amazon account button +driver.find_element(By.XPATH, "//a[@id='createAccountSubmit']") + +#Create a test case for the SignIn page using python & selenium script. +# Open Target +driver.get("https://www.target.com/") + +#Click SignIn button +driver.find_element(By.XPATH, "//a[@id='account']") + +#Click SignIn from side navigation +driver.find_element(By.XPATH, "//a[contains(text(),'Sign in')]") + +#Verify SignIn page opened: +driver.find_element(By.XPATH, "//h1[text()='Sign into your Target account']") + +#SignIn button is shown (you can just use driver.find_element() to check for element’s presence, no need to assert here) +driver.find_element(By.XPATH,"//button[contains(@type,'submit') and contains(text(),'Sign in')]") + + + + + + diff --git a/locator.py b/locator.py new file mode 100644 index 000000000..552942a50 --- /dev/null +++ b/locator.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() +driver.maximize_window() + +# open the url +driver.get('https://www.amazon.com/') + +# By ID +driver.find_element(By.ID, 'twotabsearchtextbox') +driver.find_element(By.ID, 'nav-search-submit-button') + +# By Xpath +driver.find_element(By.XPATH, "//input[@aria-label='Search Amazon']") # //tag[@attr='value'] +driver.find_element(By.XPATH, "//input[@role='searchbox']") + +# By Xpath, multiple attributes +driver.find_element(By.XPATH, "//input[@tabindex='0' and @name='field-keywords']") +driver.find_element(By.XPATH, "//input[@tabindex='0' and @name='field-keywords' and @role='searchbox']") +driver.find_element(By.XPATH, "//input[@name='field-keywords' and @tabindex='0' and @role='searchbox']") + +# By Xpath, any tag +driver.find_element(By.XPATH, "//*[@aria-label='Search Amazon']") + +# By Xpath, using text +driver.find_element(By.XPATH, "//a[text()='Best Sellers']") +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']") + +# partial text match +driver.find_element(By.XPATH, "//h2[contains(text(), 'Luxury')]") +# partial attr match +driver.find_element(By.XPATH, "//select[contains(@class, 'nav-search-dropdown')]") \ No newline at end of file diff --git a/molly b/molly new file mode 100644 index 000000000..24c5539b0 --- /dev/null +++ b/molly @@ -0,0 +1,8 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABAcWJ4WWD +iJ08W1rapTgwLhAAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIK7nvdjroTbSRVjZ +IyFNJpWnsnu87zWNxHTVRd177IWRAAAAoP5koFxWXGKS23iVOew3cxAdAX4I40Ci4J/Do9 +cNvHjZF2c+mvtXkWqXsoao6lU3zH7qWYas8dSuiz46BEDJxYvUKxgtaaEEyKrEiChxPKWj +5h8+E9+HAgZABc03j6wyUzb7YvDlboupz6UzfI3Mz6iXpn2XrDUiQxNlcv80cPVJmwUJS8 +VSEKLPD3Dm71Qu4DWjIS7VxiF1920M0qYAbbU= +-----END OPENSSH PRIVATE KEY----- diff --git a/molly.pub b/molly.pub new file mode 100644 index 000000000..fed3dbb88 --- /dev/null +++ b/molly.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK7nvdjroTbSRVjZIyFNJpWnsnu87zWNxHTVRd177IWR mollyally1988@gmail.com diff --git a/sample_script.py b/sample_script.py index 0804f58ce..0af69a950 100755 --- a/sample_script.py +++ b/sample_script.py @@ -13,21 +13,19 @@ driver.maximize_window() # open the url -driver.get('https://www.google.com/') +driver.get("https://www.target.com/") -# populate search field -search = driver.find_element(By.NAME, 'q') -search.clear() -search.send_keys('Table') +#Click SignIn button +driver.find_element(By.XPATH, "//a[@id='account']") -# wait for 4 sec -sleep(4) +#Click SignIn from side navigation +driver.find_element(By.XPATH, "//a[contains(text(),'Sign in')]") -# click search button -driver.find_element(By.NAME, 'btnK').click() +#Verify SignIn page opened: +driver.find_element(By.XPATH, "//h1[text()='Sign into your Target account']") + +#SignIn button is shown (you can just use driver.find_element() to check for element’s presence, no need to assert here) +driver.find_element(By.XPATH,"//button[contains(@type,'submit') and contains(text(),'Sign in')]") -# verify search results -assert 'table'.lower() in driver.current_url.lower(), f"Expected query not in {driver.current_url.lower()}" -print('Test Passed') driver.quit() From 2f2badf3f1c325bb1246b514954cb23a9ebd1576 Mon Sep 17 00:00:00 2001 From: Alina Dorofeyeva Date: Tue, 18 Mar 2025 00:02:07 -0700 Subject: [PATCH 3/8] Update search script --- Target_signin.py | 28 ++++++++ Week 3 HW.py | 98 +++++++++++++++++++++++++++ cart.feature | 23 +++++++ features/steps/product_search.py | 2 - features/steps/target_search_steps.py | 34 ++++++++++ features/tests/target_serach.feature | 22 ++++++ main_page_steps.py | 0 7 files changed, 205 insertions(+), 2 deletions(-) create mode 100644 Target_signin.py create mode 100644 Week 3 HW.py create mode 100644 cart.feature create mode 100644 features/steps/target_search_steps.py create mode 100644 features/tests/target_serach.feature create mode 100644 main_page_steps.py diff --git a/Target_signin.py b/Target_signin.py new file mode 100644 index 000000000..93eeeeee3 --- /dev/null +++ b/Target_signin.py @@ -0,0 +1,28 @@ +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 + +# Start Chrome browser: +driver_path = ChromeDriverManager().install() +driver = webdriver.Chrome(service=Service(driver_path)) +driver.maximize_window() +driver.implicitly_wait(5) + +# Open target.com +driver.get('https://www.target.com/') + +driver.find_element(By.XPATH, "//*[@data-test='@web/AccountLink']").click() +driver.find_element(By.XPATH, "//*[@data-test='accountNav-signIn']").click() + +# Verification +expected = 'Sign into your Target account' +actual = driver.find_element(By.XPATH, "//h1[contains(@class, 'styles_ndsHeading')]").text +assert expected == actual, f'Expected {expected} did not match actual {actual}' + +# OR: +# driver.find_element(By.XPATH, "//span[text()='Sign into your Target account']") + +# Make sure login button is shown +driver.find_element(By.ID, 'login') \ No newline at end of file diff --git a/Week 3 HW.py b/Week 3 HW.py new file mode 100644 index 000000000..36a7bb797 --- /dev/null +++ b/Week 3 HW.py @@ -0,0 +1,98 @@ +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 + +# Start Chrome browser: +driver_path = ChromeDriverManager().install() +driver = webdriver.Chrome(service=Service(driver_path)) +driver.maximize_window() +driver.implicitly_wait(5) + +# 1. Find the most optimal locators for Create Account on amazon.com (Registration) page elements: +#Amazon log +$$("#a-page [href*='frn_logo']") +#Create account +$$("#ap_register_form [href*='ap_register_form']") +#Your name +$$("#ap_customer_name_context_message_section") +#Email +$$("#ap_email") +#Password +$$("#ap_password") +#Passwords must be at least 6 characters +$$("#auth-password-requirement-info") +#Re-enter password +$$("#ap_password_check") + +#Condition of use +$$("#legalTextRow [href*='condition_of_use']") +#Privacy notice +$$("#legalTextRow [href*=' Date: Tue, 25 Mar 2025 23:52:05 -0700 Subject: [PATCH 4/8] Lesson 4 updates --- features/tests/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/features/tests/__init__.py b/features/tests/__init__.py index e69de29bb..b26addcb3 100755 --- a/features/tests/__init__.py +++ b/features/tests/__init__.py @@ -0,0 +1 @@ +exi \ No newline at end of file From 81c7e84f4885a4415949393a075d1a452c90ac31 Mon Sep 17 00:00:00 2001 From: Alina Dorofeyeva Date: Wed, 26 Mar 2025 22:31:56 -0700 Subject: [PATCH 5/8] Lesson 4 --- features/environment.py | 32 ++++++------ features/steps/cart_steps.py | 24 +++++++++ features/steps/circle_steps.py | 70 ++++++++++++++++++++++++++ features/steps/main_page_steps.py | 41 +++++++++++++++ features/steps/product_search.py | 30 ----------- features/steps/search_results_steps.py | 26 ++++++++++ features/steps/target_search_steps.py | 34 ------------- features/tests/__init__.py | 1 - features/tests/cart.feature | 24 +++++++++ features/tests/circle.feature | 5 ++ features/tests/main_page_ui.feature | 15 ++++++ features/tests/product_search.feature | 7 --- features/tests/target_search.feature | 40 +++++++++++++++ features/tests/target_serach.feature | 22 -------- main_page_steps.py | 0 15 files changed, 261 insertions(+), 110 deletions(-) create mode 100644 features/steps/cart_steps.py create mode 100644 features/steps/circle_steps.py create mode 100644 features/steps/main_page_steps.py delete mode 100755 features/steps/product_search.py create mode 100644 features/steps/search_results_steps.py delete mode 100644 features/steps/target_search_steps.py create mode 100644 features/tests/cart.feature create mode 100644 features/tests/circle.feature create mode 100644 features/tests/main_page_ui.feature delete mode 100755 features/tests/product_search.feature create mode 100644 features/tests/target_search.feature delete mode 100644 features/tests/target_serach.feature delete mode 100644 main_page_steps.py diff --git a/features/environment.py b/features/environment.py index 1275460a0..cf16957e1 100755 --- a/features/environment.py +++ b/features/environment.py @@ -1,33 +1,33 @@ from selenium import webdriver from selenium.webdriver.chrome.service import Service +from selenium.webdriver.support.wait import WebDriverWait from webdriver_manager.chrome import ChromeDriverManager - def browser_init(context): - """ - :param context: Behave context - """ - driver_path = ChromeDriverManager().install() - service = Service(driver_path) - context.driver = webdriver.Chrome(service=service) + context.driver = webdriver.Chrome(service=Service(ChromeDriverManager().install())) context.driver.maximize_window() context.driver.implicitly_wait(4) + context.driver.wait = WebDriverWait(context.driver, timeout=10) + def before_scenario(context, scenario): - print('\nStarted scenario: ', scenario.name) - browser_init(context) + """Hook executed before each test scenario""" + browser_init(context) # Initialize the browser -def before_step(context, step): - print('\nStarted step: ', step) +def after_scenario(context, scenario): + """Hook executed after each test scenario""" + context.driver.quit() # Quit the browser after scenario -def after_step(context, step): - if step.status == 'failed': - print('\nStep failed: ', step) +def before_all(context): + # Initialize the browser instance here (e.g., Chrome WebDriver). + context.browser = webdriver.Chrome() # Use the appropriate WebDriver for your setup. -def after_scenario(context, feature): - context.driver.quit() +def after_all(context): + # Quit the browser after all tests complete to clean up resources. + if context.browser: + context.browser.quit() diff --git a/features/steps/cart_steps.py b/features/steps/cart_steps.py new file mode 100644 index 000000000..0d2059c63 --- /dev/null +++ b/features/steps/cart_steps.py @@ -0,0 +1,24 @@ +from selenium.webdriver.common.by import By +from behave import given, when, then + + +CART_SUMMARY = (By.XPATH, "//div[./span[contains(text(), 'subtotal')]]") +CART_ITEM_TITLE = (By.CSS_SELECTOR, "[data-test='cartItem-title']") + + +@when('Open cart page') +def open_cart(context): + context.driver.get('https://www.target.com/cart') + + +@then('Verify cart has {amount} item(s)') +def verify_cart_items(context, amount): + cart_summary = context.driver.find_element(*CART_SUMMARY).text + assert f'{amount} item' in cart_summary, f"Expected {amount} items but got {cart_summary}" + + +@then("Verify 'Your cart is empty' message is shown") +def verify_cart_empty(context): + expected_result = 'Your cart is empty' + actual_result = context.driver.find_element(By.CSS_SELECTOR, "[data-test='boxEmptyMsg']").text + assert expected_result == actual_result, f'Expected {expected_result} did not match actual {actual_result}' diff --git a/features/steps/circle_steps.py b/features/steps/circle_steps.py new file mode 100644 index 000000000..aaa3a96ca --- /dev/null +++ b/features/steps/circle_steps.py @@ -0,0 +1,70 @@ +from behave import given, when, then +from selenium.webdriver.common.by import By +from selenium.webdriver.common.keys import Keys +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC + + +@given('Open the Target Circle page') +def open_target_circle_page(context): + context.driver.get("https://www.target.com/circle") + + +@then('Verify there are at least 10 benefit cells') +def verify_benefit_cells(context): + benefits = WebDriverWait(context.driver, 10).until( + EC.presence_of_all_elements_located((By.CSS_SELECTOR, ".cell-item-content")) + ) + assert len(benefits) >= 10, f"Expected at least 10 benefit cells, got {len(benefits)}" + + +@given('Open the Target homepage') +def open_target_homepage(context): + # Replace with the actual URL of Target's homepage + context.driver.get("https://www.target.com") + # Verify homepage has loaded by checking the title + assert "Target" in context.driver.title, "Target homepage did not load properly." + + +@when('Search for a product "{product_name}"') +def search_for_product(context, product_name): + # Find the search input field + search_box = WebDriverWait(context.driver, 10).until( + EC.presence_of_element_located((By.CSS_SELECTOR, "input#search")) + ) + + # Enter the product name and press ENTER + search_box.send_keys(product_name + Keys.RETURN) + + +@when('Add the first product to the cart') +def add_first_product_to_cart(context): + # Wait for the search results to load and select the first product + first_product = WebDriverWait(context.driver, 10).until( + EC.presence_of_element_located((By.CSS_SELECTOR, "div[data-test='product-card']")) + ) + first_product.click() + + # Wait for the "Add to Cart" button to be clickable + add_to_cart_button = WebDriverWait(context.driver, 10).until( + EC.element_to_be_clickable((By.CSS_SELECTOR, "button[data-test='addToCartButton']")) + ) + add_to_cart_button.click() + + +@then('Verify the product is in the cart') +def verify_product_in_cart(context): + # Open the cart page + cart_icon = WebDriverWait(context.driver, 10).until( + EC.presence_of_element_located((By.CSS_SELECTOR, "a[data-test='cart']")) + ) + cart_icon.click() + + # Check if the cart contains at least one cart item + cart_items = WebDriverWait(context.driver, 10).until( + EC.presence_of_all_elements_located((By.CSS_SELECTOR, "div[data-test='cart-item']")) + ) + + # Assert there is at least one item in the cart + assert len(cart_items) > 0, "No items were found in the cart!" + print(f"Cart contains {len(cart_items)} item(s).") diff --git a/features/steps/main_page_steps.py b/features/steps/main_page_steps.py new file mode 100644 index 000000000..6c8352b89 --- /dev/null +++ b/features/steps/main_page_steps.py @@ -0,0 +1,41 @@ +from selenium.webdriver.common.by import By +from behave import given, when, then +from time import sleep + + +SEARCH_FIELD = (By.ID, 'search') +SEARCH_BTN = (By.XPATH, "//button[@data-test='@web/Search/SearchButton']") +CART_ICON = (By.CSS_SELECTOR, "[data-test='@web/CartLink']") +HEADER_LINKS = (By.CSS_SELECTOR, "[id*='utilityNav']") + + +@given('Open target main page') +def open_target_main(context): + context.driver.get('https://www.target.com/') + sleep(2) + + +@when('Search for {search_word}') +def search_product(context, search_word): + context.driver.find_element(*SEARCH_FIELD).send_keys(search_word) + context.driver.find_element(*SEARCH_BTN).click() + sleep(6) + + +@when('Click on Cart icon') +def click_cart(context): + context.driver.find_element(*CART_ICON).click() + + +@then('Verify at least 1 link shown') +def verify_1_header_link_shown(context): + link = context.driver.find_element(*HEADER_LINKS) + print(link) + + +@then('Verify {link_amount} links shown') +def verify_all_header_links_shown(context, link_amount): + link_amount = int(link_amount) # "6" => int 6 + links = context.driver.find_elements(*HEADER_LINKS) + print(links) + assert len(links) == link_amount, f'Expected {link_amount} links, but got {len(links)}' diff --git a/features/steps/product_search.py b/features/steps/product_search.py deleted file mode 100755 index 5c96f8ce0..000000000 --- a/features/steps/product_search.py +++ /dev/null @@ -1,30 +0,0 @@ -from selenium.webdriver.common.by import By -from behave import given, when, then -from time import sleep - - -SEARCH_INPUT = (By.NAME, 'q') -SEARCH_SUBMIT = (By.NAME, 'btnK') - - -@given('Open Google page') -def open_google(context): - context.driver.get('https://www.google.com/') - - -@when('Input {search_word} into search field') -def input_search(context, search_word): - search = context.driver.find_element(*SEARCH_INPUT) - search.clear() - search.send_keys(search_word) - sleep(4) - -@when('Click on search icon') -def click_search_icon(context): - context.driver.find_element(*SEARCH_SUBMIT).click() - sleep(1) - -@then('Product results for {search_word} are shown') -def verify_found_results_text(context, search_word): - assert search_word.lower() in context.driver.current_url.lower(), \ - f'Expected query not in {context.driver.current_url.lower()}' diff --git a/features/steps/search_results_steps.py b/features/steps/search_results_steps.py new file mode 100644 index 000000000..8db0b8ea0 --- /dev/null +++ b/features/steps/search_results_steps.py @@ -0,0 +1,26 @@ +from selenium.webdriver.common.by import By +from selenium.webdriver.support import expected_conditions as EC +from behave import given, when, then +from time import sleep + +SEARCH_RESULTS_TEXT = (By.XPATH, "//div[@data-test='lp-resultsCount']") +ADD_TO_CART_BTN = (By.CSS_SELECTOR, "[id*='addToCartButton']") +SIDE_NAV_PRODUCT_NAME = (By.CSS_SELECTOR, "[data-test='content-wrapper'] h4") +SIDE_NAV_ADD_TO_CART_BTN = (By.CSS_SELECTOR, "[data-test='content-wrapper'] [id*='addToCart']") + + +@when('Click on Add to Cart button') +def click_add_to_cart(context): + context.driver.find_element(*ADD_TO_CART_BTN).click() + + +@when('Confirm Add to Cart button from side navigation') +def side_nav_click_add_to_cart(context): + context.driver.find_element(*SIDE_NAV_ADD_TO_CART_BTN).click() + sleep(5) + + +@then('Verify correct search results shown for {expected_text}') +def verify_search_results(context, expected_text): + actual_text = context.driver.find_element(*SEARCH_RESULTS_TEXT).text + assert expected_text in actual_text, f'Error. Text {expected_text} not in {actual_text}' \ No newline at end of file diff --git a/features/steps/target_search_steps.py b/features/steps/target_search_steps.py deleted file mode 100644 index 314e8785c..000000000 --- a/features/steps/target_search_steps.py +++ /dev/null @@ -1,34 +0,0 @@ -from selenium.webdriver.common.by import By -from behave import given, when, then -from time import sleep - - -@given('Open target main page') -def open_target_main(context): - context.driver.get('https://www.target.com/') - - -@when('Search for tea') -def search_product(context): - context.driver.find_element(By.ID, 'search').send_keys('tea') - context.driver.find_element(By.XPATH, "//button[@data-test='@web/Search/SearchButton']").click() - sleep(6) - - -#@then('Verify correct search results show') -#def verify_search_results(context): -# def click_cart(context): -# actual_text = context.driver.find_element(By.XPATH, "//div[@data-test='lp-resultsCount']").text -# expected_text = 'tea' -# assert expected_text in actual_text, f'Error. Text {expected_text} not in {actual_text}' - -@when('Click on Cart icon') -def click_cart(context): - context.driver.find_element(By.CSS_SELECTOR, "[data-test='@web/CartLink']").click() - -@then("Verify 'Your cart is empty' message is shown") -def virify_cart_is_empty(context): - expected_text = 'Your cart is empty' - actual_results = context.driver.find_element(By.CSS_SELECTOR, "[data-test='boxEmptyMsg']").text - assert expected_text == actual_results, f'Expected {expected_text} did not match actual {actual_results}' - print("Test passed") diff --git a/features/tests/__init__.py b/features/tests/__init__.py index b26addcb3..e69de29bb 100755 --- a/features/tests/__init__.py +++ b/features/tests/__init__.py @@ -1 +0,0 @@ -exi \ No newline at end of file diff --git a/features/tests/cart.feature b/features/tests/cart.feature new file mode 100644 index 000000000..ff825b646 --- /dev/null +++ b/features/tests/cart.feature @@ -0,0 +1,24 @@ +#Feature: Cart tests + +# Scenario: 'Your cart is empty' message is shown for empty cart +# Given Open target main page + # When Click on Cart icon +# Then Verify 'Your cart is empty' message is shown + +#Feature: Verify Target Circle Benefits + +# Scenario: Verify benefit cells on the Target Circle page + # Given Open the Target Circle page + # Then Verify there are at least 10 benefit cells + + + + + + + + + + + + diff --git a/features/tests/circle.feature b/features/tests/circle.feature new file mode 100644 index 000000000..f4fdbb8a1 --- /dev/null +++ b/features/tests/circle.feature @@ -0,0 +1,5 @@ +Feature: Target Circle Benefits + + Scenario: Verify at least 10 benefit cells are displayed on the Target Circle page + Given Open the Target Circle page + Then Verify there are at least 10 benefit cells diff --git a/features/tests/main_page_ui.feature b/features/tests/main_page_ui.feature new file mode 100644 index 000000000..875f456f4 --- /dev/null +++ b/features/tests/main_page_ui.feature @@ -0,0 +1,15 @@ +Feature: Main page UI test + + Scenario: Verify header links has at least 1 link + Given Open target main page + Then Verify at least 1 link shown + + Scenario: Verify all header links shown + Given Open target main page + Then Verify 6 links shown + + Feature: Verify Target Circle Benefits + + Scenario: Verify benefit cells on the Target Circle page + Given Open the Target Circle page + Then Verify there are at least 10 benefit cells \ No newline at end of file diff --git a/features/tests/product_search.feature b/features/tests/product_search.feature deleted file mode 100755 index 36d6913cf..000000000 --- a/features/tests/product_search.feature +++ /dev/null @@ -1,7 +0,0 @@ -Feature: Test Scenarios for Search functionality - - Scenario: User can search for a product - Given Open Google page - When Input Car into search field - And Click on search icon - Then Product results for Car are shown \ No newline at end of file diff --git a/features/tests/target_search.feature b/features/tests/target_search.feature new file mode 100644 index 000000000..e2c94fafe --- /dev/null +++ b/features/tests/target_search.feature @@ -0,0 +1,40 @@ +Feature: Target search test cases + + Scenario: User can search for a product on Target +# Scenario: User can search for a tea on Target +# Given Open target main page +# When Search for tea +# Then Verify correct search results shown for tea +# +# Scenario: User can search for a iPhone on Target +# Given Open target main page +# When Search for iPhone +# Then Verify correct search results shown for iPhone +# +# Scenario: User can search for a dress on Target +# Given Open target main page +# When Search for dress +# Then Verify correct search results shown for dress + + Scenario Outline: User can search for a product on Target + Given Open target main page + When Search for tea + Then Verify correct search results show + When Search for + Then Verify correct search results shown for + Examples: + |search_word |expected_text | + |tea |tea | + |iPhone |iPhone | + |dress |dress | + + +Scenario: User can add a product to cart + Given Open target main page + When Search for plates + And Click on Add to Cart button + And Confirm Add to Cart button from side navigation + And Open cart page + Then Verify cart has 1 item(s) + + diff --git a/features/tests/target_serach.feature b/features/tests/target_serach.feature deleted file mode 100644 index aa2ed2bef..000000000 --- a/features/tests/target_serach.feature +++ /dev/null @@ -1,22 +0,0 @@ -Feature: Target search test cases - - #Scenario: User can search for a tea on Target - # Given Open target main page - # When Search for tea - # Then Verify correct search results shown for tea - - # Scenario Outline: User can search for a product on Target - # Given Open Google page - # When Input Car into search field - #And Click on search icon - # Then Product results for Car are shown - - Scenario: 'Your cart is empty' search is shown for empty cart - Given Open target main page - When Click on Cart icon - Then Verify 'Your cart is empty' - - Scenario: 'Sign into your Target account' search is shown for sing in - Given Open target main page - When Click on sing in icon - Then Verify 'Sign into your Target account' \ No newline at end of file diff --git a/main_page_steps.py b/main_page_steps.py deleted file mode 100644 index e69de29bb..000000000 From beb05e9573bde8bd248953c4d7dd4c662c9e353a Mon Sep 17 00:00:00 2001 From: Alina Dorofeyeva Date: Thu, 27 Mar 2025 23:10:44 -0700 Subject: [PATCH 6/8] Lesson 5 --- features/environment.py | 28 ++++++------ features/steps/cart_steps.py | 7 +++ features/steps/main_page_steps.py | 9 +++- features/steps/product_details_page.py | 60 ++++++++++++++++++++++++++ features/steps/search_results_steps.py | 36 ++++++++++++++-- features/tests/product_details.feature | 16 +++++++ features/tests/target_search.feature | 44 +++++++++++-------- 7 files changed, 161 insertions(+), 39 deletions(-) create mode 100644 features/steps/product_details_page.py create mode 100644 features/tests/product_details.feature diff --git a/features/environment.py b/features/environment.py index cf16957e1..f33cbe409 100755 --- a/features/environment.py +++ b/features/environment.py @@ -5,29 +5,27 @@ def browser_init(context): - context.driver = webdriver.Chrome(service=Service(ChromeDriverManager().install())) + driver_path = ChromeDriverManager().install() + service = Service(driver_path) + context.driver = webdriver.Chrome(service=service) + context.driver.maximize_window() context.driver.implicitly_wait(4) context.driver.wait = WebDriverWait(context.driver, timeout=10) - - def before_scenario(context, scenario): - """Hook executed before each test scenario""" - browser_init(context) # Initialize the browser + print('\nStarted scenario: ', scenario.name) + browser_init(context) -def after_scenario(context, scenario): - """Hook executed after each test scenario""" - context.driver.quit() # Quit the browser after scenario +def before_step(context, step): + print('\nStarted step: ', step) -def before_all(context): - # Initialize the browser instance here (e.g., Chrome WebDriver). - context.browser = webdriver.Chrome() # Use the appropriate WebDriver for your setup. +def after_step(context, step): + if step.status == 'failed': + print('\nStep failed: ', step) -def after_all(context): - # Quit the browser after all tests complete to clean up resources. - if context.browser: - context.browser.quit() +def after_scenario(context, feature): + context.driver.quit() diff --git a/features/steps/cart_steps.py b/features/steps/cart_steps.py index 0d2059c63..81c1cd2c4 100644 --- a/features/steps/cart_steps.py +++ b/features/steps/cart_steps.py @@ -10,6 +10,13 @@ def open_cart(context): context.driver.get('https://www.target.com/cart') +@then('Verify cart has correct product') +def verify_product_name(context): + # context.product_name => stored before + product_name_in_cart = context.driver.find_element(*CART_ITEM_TITLE).text + print('Name in cart: ', product_name_in_cart) + assert context.product_name[:20] == product_name_in_cart[:20], \ + f'Expected {context.product_name[:20]} did not match {product_name_in_cart[:20]}' @then('Verify cart has {amount} item(s)') def verify_cart_items(context, amount): diff --git a/features/steps/main_page_steps.py b/features/steps/main_page_steps.py index 6c8352b89..0e973a838 100644 --- a/features/steps/main_page_steps.py +++ b/features/steps/main_page_steps.py @@ -1,4 +1,5 @@ from selenium.webdriver.common.by import By +from selenium.webdriver.support import expected_conditions as EC from behave import given, when, then from time import sleep @@ -12,14 +13,18 @@ @given('Open target main page') def open_target_main(context): context.driver.get('https://www.target.com/') - sleep(2) + + context.driver.wait.until( + EC.element_to_be_clickable(SEARCH_FIELD), + message='Search field not clickable' + ) @when('Search for {search_word}') def search_product(context, search_word): context.driver.find_element(*SEARCH_FIELD).send_keys(search_word) context.driver.find_element(*SEARCH_BTN).click() - sleep(6) + sleep(7) @when('Click on Cart icon') diff --git a/features/steps/product_details_page.py b/features/steps/product_details_page.py new file mode 100644 index 000000000..3f1357544 --- /dev/null +++ b/features/steps/product_details_page.py @@ -0,0 +1,60 @@ +from selenium.webdriver.common.by import By +from selenium.webdriver.common.action_chains import ActionChains +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC +from behave import given, then +from time import sleep + + + +COLOR_OPTIONS = (By.CSS_SELECTOR, "div[aria-label='Carousel'] li img") +SELECTED_COLOR = (By.CSS_SELECTOR, "[data-test='@web/VariationComponent'] div") + + +@given('Open target product {product_id} page') +def open_target(context, product_id): + context.driver.get(f'https://www.target.com/p/{product_id}') + WebDriverWait(context.driver, 10).until( + EC.presence_of_all_elements_located(COLOR_OPTIONS) + ) + + +#@then('Verify user can click through colors') +#def click_and_verify_colors(context): +# expected_colors = ['dark khaki', 'grey', 'navy/tan', 'white/navy/red', 'white/sand/tan', 'black/gum'] +# actual_colors = [] + +@then('Verify user can click through colors') +def click_and_verify_colors(context): + expected_colors = ['Blue Tint', 'Denim Blue', 'Marine', 'Raven'] + actual_colors = [] + + # Wait for the color options to be visible + colors = WebDriverWait(context.driver, 10).until( + EC.presence_of_all_elements_located(COLOR_OPTIONS) + ) + + for color in colors: + try: + # Scroll into view to ensure it’s visible and ready to be clicked + context.driver.execute_script("arguments[0].scrollIntoView(true);", color) + + # Use ActionsChains for safe clicking + ActionChains(context.driver).move_to_element(color).click().perform() + + # Retrieve the selected color text + selected_color = WebDriverWait(context.driver, 10).until( + EC.visibility_of_element_located(SELECTED_COLOR) + ).text + print('Current color:', selected_color) + + # Extract the color name and append to actual colors + selected_color = selected_color.split('\n')[1] + actual_colors.append(selected_color) + + except Exception as e: + print(f"Error clicking color: {e}") + continue + + # Assert the colors match the expected list + assert expected_colors == actual_colors, f'Expected {expected_colors} did not match actual {actual_colors}' diff --git a/features/steps/search_results_steps.py b/features/steps/search_results_steps.py index 8db0b8ea0..7e35154f4 100644 --- a/features/steps/search_results_steps.py +++ b/features/steps/search_results_steps.py @@ -3,15 +3,28 @@ from behave import given, when, then from time import sleep + SEARCH_RESULTS_TEXT = (By.XPATH, "//div[@data-test='lp-resultsCount']") ADD_TO_CART_BTN = (By.CSS_SELECTOR, "[id*='addToCartButton']") SIDE_NAV_PRODUCT_NAME = (By.CSS_SELECTOR, "[data-test='content-wrapper'] h4") SIDE_NAV_ADD_TO_CART_BTN = (By.CSS_SELECTOR, "[data-test='content-wrapper'] [id*='addToCart']") - +LISTINGS = (By.CSS_SELECTOR, "[data-test='@web/site-top-of-funnel/ProductCardWrapper']") +PRODUCT_TITLE = (By.CSS_SELECTOR, "[data-test='product-title']") +PRODUCT_IMG = (By.CSS_SELECTOR, 'img') @when('Click on Add to Cart button') def click_add_to_cart(context): - context.driver.find_element(*ADD_TO_CART_BTN).click() + context.driver.find_element(*ADD_TO_CART_BTN).click() # always clicks on 1st Add to cart btn + + +@when('Store product name') +def store_product_name(context): + context.driver.wait.until( + EC.visibility_of_element_located(SIDE_NAV_PRODUCT_NAME), + message='Product name not visible' + ) + context.product_name = context.driver.find_element(*SIDE_NAV_PRODUCT_NAME).text + print('Product name stored: ', context.product_name) @when('Confirm Add to Cart button from side navigation') @@ -23,4 +36,21 @@ def side_nav_click_add_to_cart(context): @then('Verify correct search results shown for {expected_text}') def verify_search_results(context, expected_text): actual_text = context.driver.find_element(*SEARCH_RESULTS_TEXT).text - assert expected_text in actual_text, f'Error. Text {expected_text} not in {actual_text}' \ No newline at end of file + context.app.search_results_page.verify_search_results(expected_text) + + +@then('Verify that every product has a name and an image') +def verify_products_name_img(context): + # To see ALL listings (comment out if you only check top ones): + # context.driver.execute_script("window.scrollBy(0,2000)", "") + # sleep(2) + # context.driver.execute_script("window.scrollBy(0,1000)", "") + # sleep(2) + + products = context.driver.find_elements(*LISTINGS)[:8] # [WebEl1, WebEl2, WebEl3, WebEl4] + + for product in products: + title = product.find_element(*PRODUCT_TITLE).text + assert title, 'Product title not shown' + print(title) + product.find_element(*PRODUCT_IMG) \ No newline at end of file diff --git a/features/tests/product_details.feature b/features/tests/product_details.feature new file mode 100644 index 000000000..d6a18cf2e --- /dev/null +++ b/features/tests/product_details.feature @@ -0,0 +1,16 @@ +Feature: Tests for product page + +# Scenario: User can select colors +# Given Open target product A-54551690 page +# Then Verify user can click through colors + + + + Scenario: User can select colors + Given Open target product A-91511634 page + Then Verify user can click through colors + + + Scenario: User can select colors + Given Open target product A-54551690 page + Then Verify user can click through colors \ No newline at end of file diff --git a/features/tests/target_search.feature b/features/tests/target_search.feature index e2c94fafe..db6332520 100644 --- a/features/tests/target_search.feature +++ b/features/tests/target_search.feature @@ -1,6 +1,6 @@ Feature: Target search test cases - Scenario: User can search for a product on Target +# Scenario: User can search for a product on Target # Scenario: User can search for a tea on Target # Given Open target main page # When Search for tea @@ -16,25 +16,31 @@ Feature: Target search test cases # When Search for dress # Then Verify correct search results shown for dress - Scenario Outline: User can search for a product on Target - Given Open target main page - When Search for tea - Then Verify correct search results show - When Search for - Then Verify correct search results shown for - Examples: - |search_word |expected_text | - |tea |tea | - |iPhone |iPhone | - |dress |dress | +# Scenario Outline: User can search for a product on Target +# Given Open target main page +# When Search for tea +# Then Verify correct search results show +# When Search for +# Then Verify correct search results shown for +# Examples: +# |search_word |expected_text | +# |tea |tea | +# |iPhone |iPhone | +# |dress |dress | -Scenario: User can add a product to cart - Given Open target main page - When Search for plates - And Click on Add to Cart button - And Confirm Add to Cart button from side navigation - And Open cart page - Then Verify cart has 1 item(s) + Scenario: User can add a product to cart + Given Open target main page + When Search for mug + And Click on Add to Cart button + And Store product name + And Confirm Add to Cart button from side navigation + And Open cart page + Then Verify cart has 1 item(s) + Then Verify cart has correct product + Scenario: Verify that user can see product names and images + Given Open target main page + When Search for airfryer + Then Verify that every product has a name and an image From 7df88fd2c35f2c4ea6cad6b3c27b5cd5bf8b97a4 Mon Sep 17 00:00:00 2001 From: Alina Dorofeyeva Date: Fri, 28 Mar 2025 16:36:08 -0700 Subject: [PATCH 7/8] Lesson 5 --- Week 2 HW.py | 1 - features/steps/product_details_page.py | 17 +++++++++------ features/tests/product_details.feature | 8 +++---- python-selenium-automation | 1 + sample_script.py | 30 +++++++++++++++----------- 5 files changed, 33 insertions(+), 24 deletions(-) create mode 160000 python-selenium-automation diff --git a/Week 2 HW.py b/Week 2 HW.py index cc3013d85..5995ee64d 100644 --- a/Week 2 HW.py +++ b/Week 2 HW.py @@ -63,4 +63,3 @@ - diff --git a/features/steps/product_details_page.py b/features/steps/product_details_page.py index 3f1357544..5558a6988 100644 --- a/features/steps/product_details_page.py +++ b/features/steps/product_details_page.py @@ -6,10 +6,12 @@ from time import sleep - COLOR_OPTIONS = (By.CSS_SELECTOR, "div[aria-label='Carousel'] li img") SELECTED_COLOR = (By.CSS_SELECTOR, "[data-test='@web/VariationComponent'] div") +# COLOR_OPTIONS = (By.CSS_SELECTOR, "div[aria-label='Carousel'] li img") +# SELECTED_COLOR = (By.CSS_SELECTOR, "[data-test='@web/VariationComponent'] div") + @given('Open target product {product_id} page') def open_target(context, product_id): @@ -19,16 +21,16 @@ def open_target(context, product_id): ) -#@then('Verify user can click through colors') -#def click_and_verify_colors(context): -# expected_colors = ['dark khaki', 'grey', 'navy/tan', 'white/navy/red', 'white/sand/tan', 'black/gum'] -# actual_colors = [] - @then('Verify user can click through colors') def click_and_verify_colors(context): - expected_colors = ['Blue Tint', 'Denim Blue', 'Marine', 'Raven'] + expected_colors = ['Black', 'Gray', 'Khaki'] actual_colors = [] + # @then('Verify user can click through colors') + # def click_and_verify_colors(context): + # expected_colors = ['Blue Tint', 'Denim Blue', 'Marine', 'Raven'] + # actual_colors = [] + # Wait for the color options to be visible colors = WebDriverWait(context.driver, 10).until( EC.presence_of_all_elements_located(COLOR_OPTIONS) @@ -58,3 +60,4 @@ def click_and_verify_colors(context): # Assert the colors match the expected list assert expected_colors == actual_colors, f'Expected {expected_colors} did not match actual {actual_colors}' + diff --git a/features/tests/product_details.feature b/features/tests/product_details.feature index d6a18cf2e..64347ef80 100644 --- a/features/tests/product_details.feature +++ b/features/tests/product_details.feature @@ -7,10 +7,10 @@ Feature: Tests for product page Scenario: User can select colors - Given Open target product A-91511634 page + Given Open target product A-93537521 page Then Verify user can click through colors - Scenario: User can select colors - Given Open target product A-54551690 page - Then Verify user can click through colors \ No newline at end of file + # Scenario: User can select colors + # Given Open target product A-54551690 page + # Then Verify user can click through colors \ No newline at end of file diff --git a/python-selenium-automation b/python-selenium-automation new file mode 160000 index 000000000..2f2badf3f --- /dev/null +++ b/python-selenium-automation @@ -0,0 +1 @@ +Subproject commit 2f2badf3f1c325bb1246b514954cb23a9ebd1576 diff --git a/sample_script.py b/sample_script.py index 0af69a950..480534519 100755 --- a/sample_script.py +++ b/sample_script.py @@ -2,6 +2,8 @@ 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 @@ -10,22 +12,26 @@ # create a new Chrome browser instance service = Service(driver_path) driver = webdriver.Chrome(service=service) +# driver.implicitly_wait(4) # checks for element every 100 ms driver.maximize_window() +driver.wait = WebDriverWait(driver, timeout=10) # open the url -driver.get("https://www.target.com/") +driver.get('https://www.google.com/') +# populate search field +search = driver.find_element(By.NAME, 'q') +search.clear() +search.send_keys('Car') -#Click SignIn button -driver.find_element(By.XPATH, "//a[@id='account']") +# wait for 4 sec -#Click SignIn from side navigation -driver.find_element(By.XPATH, "//a[contains(text(),'Sign in')]") +# sleep(4) +# click search button +driver.wait.until(EC.element_to_be_clickable((By.NAME, 'btnK')), message='Search btn not clickable').click() -#Verify SignIn page opened: -driver.find_element(By.XPATH, "//h1[text()='Sign into your Target account']") +sleep(5) -#SignIn button is shown (you can just use driver.find_element() to check for element’s presence, no need to assert here) -driver.find_element(By.XPATH,"//button[contains(@type,'submit') and contains(text(),'Sign in')]") - - -driver.quit() +# verify search results +assert 'car'.lower() in driver.current_url.lower(), f"Expected query not in {driver.current_url.lower()}" +print('Test Passed') +driver.quit() \ No newline at end of file From b943628ac985014f0da11f4be1ecfa9faa669a96 Mon Sep 17 00:00:00 2001 From: Alina Dorofeyeva Date: Sat, 29 Mar 2025 18:57:35 -0700 Subject: [PATCH 8/8] Lesson 6 --- app/__init__.py | 0 app/application.py | 0 pages/__init__.py | 0 pages/base_page.py | 0 pages/cart_page.py | 0 pages/header.py | 0 pages/main_page.py | 0 pages/search_results_page.py | 0 8 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 app/__init__.py create mode 100644 app/application.py create mode 100644 pages/__init__.py create mode 100644 pages/base_page.py create mode 100644 pages/cart_page.py create mode 100644 pages/header.py create mode 100644 pages/main_page.py create mode 100644 pages/search_results_page.py 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..e69de29bb diff --git a/pages/__init__.py b/pages/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/pages/base_page.py b/pages/base_page.py new file mode 100644 index 000000000..e69de29bb diff --git a/pages/cart_page.py b/pages/cart_page.py new file mode 100644 index 000000000..e69de29bb diff --git a/pages/header.py b/pages/header.py new file mode 100644 index 000000000..e69de29bb diff --git a/pages/main_page.py b/pages/main_page.py new file mode 100644 index 000000000..e69de29bb diff --git a/pages/search_results_page.py b/pages/search_results_page.py new file mode 100644 index 000000000..e69de29bb