I'm trying to implement 'By' and 'Keys' with appium just like how I do it on selenium.
On selenium i could do this:
Locators
from selenium.webdriver.common.by import By
class LoginPageLocators(object):
HEADING = (By.CSS_SELECTOR, 'h3[class="panel-title"]')
USERNAME = (By.NAME, 'username')
PASSWORD = (By.NAME, 'password')
LOGIN_BTN = (By.CSS_SELECTOR, 'input[value="Login"]')
functions
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from base import Page
from locators.locators import *
class LoginPage(Page):
def __init__(self, context):
Page.__init__(
self,
context)
def goto_login_page(self, url):
self.open(url)
def enter_username(self, username):
uname = self.find_element(*LoginPageLocators.USERNAME)
uname.send_keys(username)
def enter_password(self, password):
pword = self.find_element(*LoginPageLocators.PASSWORD)
pword.send_keys(password)
def click_login(self):
login = self.find_element(*LoginPageLocators.LOGIN_BTN)
login.click()
def verify_dashboard_page(self, page):
self.verify_page(page)
Is there a way to this in appium? there is no module if i do this:
from appium.webdriver.common.by import By
from appium.webdriver.common.keys import Keys
from appium.webdriver.common.mobileby import By
from appium.webdriver.common.mobileby import MobileBy
class FirstPageLocators(object):
LOCATOR_ONE = (MobileBy.ACCESSIBILITY_ID, 'id')
LOCATOR_TWO = (MobileBy.XPATH, 'xpath_value')
I use another way for locators.
I have page_object directory, which contains files for the app pages and general file - base_page. In the Base_page.py I include general action methods for another pages. Example:
from appium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as ex_cond
from src.platform import PLATFORM, IS_IOS
class BasePage:
def __init__(self, driver: webdriver) -> None:
self._driver = driver
def get_element(self, locator: str, timeout=10):
by = get_locator_by_string(locator)
return WebDriverWait(self._driver, timeout).until(
ex_cond.visibility_of_element_located(by), ' : '.join(by))
def get_no_element(self, locator: str, timeout=10):
by = get_locator_by_string(locator)
element = WebDriverWait(self._driver, timeout).until(
ex_cond.invisibility_of_element_located(by), ' : '.join(by))
if element is None:
return 'No element found'
def get_element_text(self, locator: str, timeout=10):
by = get_locator_by_string(locator)
element = WebDriverWait(self._driver, timeout).until(
ex_cond.visibility_of_element_located(by), ' : '.join(by))
if IS_IOS:
return element.get_attribute('label')
else:
return element.text
def get_element_and_click(self, locator: str, timeout=10):
by = get_locator_by_string(locator)
element = WebDriverWait(self._driver, timeout).until(
ex_cond.visibility_of_element_located(by), ' : '.join(by))
return element.click()
To find the right way for locators, I created the custom method in the same file - base_page.py. Example:
def get_locator_by_string(locator_with_type):
exploided_locator = locator_with_type.split(':', 1)
by_type = exploided_locator[0]
locator = exploided_locator[1]
if by_type == 'xpath':
return (MobileBy.XPATH, locator)
elif by_type == 'css':
return (MobileBy.CSS_SELECTOR, locator)
elif by_type == 'id':
return (MobileBy.ID, locator)
elif by_type == 'accessibility_id':
return (MobileBy.ACCESSIBILITY_ID, locator)
elif by_type == 'android_uiautomator':
return (MobileBy.ANDROID_UIAUTOMATOR, locator)
elif by_type == 'ios_uiautomation':
return (MobileBy.IOS_UIAUTOMATION, locator)
elif by_type == 'ios_predicate':
return (MobileBy.IOS_PREDICATE, locator)
elif by_type == 'class':
return (MobileBy.CLASS_NAME, locator)
else:
raise Exception(f'Cannot get type of locator. Locator
{locator_with_type}')
Get_locator_by_string doesn't consist all search methods. There is methods I need.
In the locators file I have two ways - for Android and for iOS. Example:
import allure
from src.config import BUNDLE_APP
from src.ui.base_page import BasePage, locator_for_platform
class AuthorPage(BasePage):
_author_title = locator_for_platform({
'ANDROID': 'id:%s:id/authorName' % BUNDLE_APP,
'IOS': 'accessibility_id:author_name'
})
_subscribe_button = locator_for_platform({
'ANDROID': 'id:%s:id/subscribeBackground' % BUNDLE_APP,
'IOS': 'accessibility_id:author_subscribe_button'
})
#allure.step('Press subscribe button')
def press_subscribe_button(self):
super().get_element_and_click(self._subscribe_button)
#allure.step('Get subscribe button text')
def get_subscribe_button_text(self):
return super().get_element_text(self._subscribe_button_text)
#allure.step('Get author\'s name text')
def get_author_name_text(self):
return super().get_element_text(self._author_title)
To choose correct locators for platform I use methods for platform's checking. Example:
def locator_for_platform(selectors):
return selectors.get(PLATFORM, 'Undefined Selector')
To check current platform this file:
import os
from appium import webdriver
from src import config
def get_env(key, default=None):
return os.environ.get(key=key, default=default)
def get():
if IS_ANDROID:
return webdriver.Remote(
command_executor=config.APPIUM_HOST,
desired_capabilities=config.DESIRED_CAPS_ANDROID_RESET
)
else:
return webdriver.Remote(
command_executor=config.APPIUM_HOST,
desired_capabilities=config.DESIRED_CAPS_ANDROID_RESET
)
PLATFORM = get_env('PLATFORM', 'IOS')
IS_ANDROID = PLATFORM == 'ANDROID'
IS_IOS = PLATFORM == 'IOS'
And the last file for setup current platform for test:
#!/usr/bin/env bash
export PLATFORM=IOS
export PLATFORM=ANDROID
APPIUM_HOST and DESIRED_CAPS_ANDROID_RESET / DESIRED_CAPS_ANDROID_RESET in other file. Example:
APPIUM_HOST = 'http://localhost:4445/wd/hub'
DESIRED_CAPS_IOS = {
'platformName': 'iOS',
'platformVersion': '13.3',
'deviceName': 'iPhone 8',
'automationNam': 'XCUITest',
'app': MY_APP_IOS
}
DESIRED_CAPS_ANDROID_RESET = {
'platformName': 'Android',
'platformVersion': '10',
'automationName': 'uiautomator2',
'deviceName': DEVICES['Pixel Emulator (10.0)'],
'app': MY_APP_ANDROID,
'unicodeKeyboard': 'true',
'resetKeyboard': 'true',
'disableWindowAnimation': 'true',
'autoWebviewTimeout': '2000',
'clearDeviceLogsOnStart': 'true'
}
DEVICES and MY_APP_ANDROID is another dictionaries.
I hope my experience will be helpful for you.
Related
I'm writing a bot to access instagram, but it is giving me an error that I cannot identify.I am a beginner and I am having trouble finding the error. I am open to new friendships and to share knowledge. Thanks guys!!
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time
import random
class InstagramBot:
def __init__(self, username, password):
self.username = username
self.password = password
self.driver = webdriver.Firefox(executable_path=r"C:\Users\bruno\Desktop\geckodriver-v0.29.1-win64\geckodriver.exe")
def login(self):
driver = self.driver
driver.get("https://www.instagram.com/accounts/login/?hl=pt-br")
campo_usuario = driver.find_element_by_xpath("//input[#name='username']")
campo_usuario.click()
campo_usuario.clear()
campo_usuario.send_keys(self.username)
campo_senha = driver.find_element_by_xpath("//input[#name='password']")
campo_senha.click()
campo_senha.clear()
campo_senha.send_keys(self.password)
campo_senha.send_keys(Keys.RETURN)
brunoBot = InstagramBot("user","senha1234")
brunoBot.login()
error:
File "c:\Users\bruno\Desktop\geckodriver-v0.29.1-win64\igBot.py", line 16, in InstagramBot
campo_usuario = driver.find_element_by_xpath("//input[#name='username']")
NameError: name 'driver' is not defined
PS C:\Users\bruno\Desktop\geckodriver-v0.29.1-win64>
You have to respect the identation for the login method:
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time
import random
class InstagramBot:
def __init__(self, username, password):
self.username = username
self.password = password
self.driver = webdriver.Firefox(executable_path=r"C:\Users\bruno\Desktop\geckodriver-v0.29.1-win64\geckodriver.exe")
def login(self):
driver = self.driver
driver.get("https://www.instagram.com/accounts/login/?hl=pt-br")
campo_usuario = driver.find_element_by_xpath("//input[#name='username']")
campo_usuario.click()
campo_usuario.clear()
campo_usuario.send_keys(self.username)
campo_senha = driver.find_element_by_xpath("//input[#name='password']")
campo_senha.click()
campo_senha.clear()
campo_senha.send_keys(self.password)
campo_senha.send_keys(Keys.RETURN)
brunoBot = InstagramBot("user","senha1234")
brunoBot.login()
I am trying to add screenshots of failed tests to html reports generated by pytest-html plugin,pytest libraries.I followed this How do I include screenshot in python pytest html report. However i always ended up with the below error.
Error occuring:
feature_request = item.funcargs['request']
KeyError: 'request'
conftest.py
import pytest
from datetime import datetime
from selenium import webdriver
import pytest_html
#pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item, call):
timestamp = datetime.now().strftime('%H-%M-%S')
pytest_html = item.config.pluginmanager.getplugin('html')
outcome = yield
report = outcome.get_result()
extra = getattr(report, 'extra', [])
if report.when == 'call':
feature_request = item.funcargs['request']
driver = feature_request.getfixturevalue('browser')
driver.save_screenshot('Screenshots/scr' + timestamp + '.png')
extra.append(pytest_html.extras.image('Screenshots/scr' + timestamp + '.png'))
extra.append(pytest_html.extras.url('http://www.example.com/'))
xfail = hasattr(report, 'wasxfail')
if (report.skipped and xfail) or (report.failed and not xfail):
# only add additional html on failure
extra.append(pytest_html.extras.image('Screenshots/scr.png'))
extra.append(pytest_html.extras.html('<div>Additional HTML</div>'))
report.extra = extra
#pytest.fixture(scope='session')
def setup():
print('Starting')
url = 'https://www.google.com/'
driver = webdriver.Firefox()
driver.get(url)
return driver
test_google.py
class Test_One():
def test_login(setup):
print('Opend google')
assert False
Whats wrong with my code ? how can i append screenshot on failed test cases ?
you should have a fixture with request argument on your conftest.py, specifically your driver.
#pytest.fixture(scope='session')
def setup(request):
then use driver = feature_request.getfixturevalue('setup') #referencing your def setup
I have a project with structure the same as this:
https://github.com/RomanBaggins/Test_automatisation_final_project.
In conftest.py i get parameters from cmd.
In one of the functions in mane_page.py I need to assign a value(= parameter in cmd) to a variable.
For example:
cmd: pytest -s -v --language=en --status=empty test_main_page.py
import pytest
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
def pytest_addoption(parser):
parser.addoption('--language', action='store', default=None, help="Choose language")
parser.addoption('--status', action='store', default=None, help="Choose status")
#pytest.fixture(scope="function")
def browser(request):
user_language = request.config.getoption("language")
if user_language:
options = Options()
options.add_experimental_option('prefs', {'intl.accept_languages': user_language})
browser = webdriver.Chrome(options=options)
browser.implicitly_wait(5)
yield browser
else:
raise pytest.UsageError("--choose your language again")
browser.quit()
#pytest.fixture
def choose_status(request):
return request.config.getoption("status")
Then in main_page.py I want to do this:
from .base_page import BasePage
from selenium.webdriver.common.by import By
from .locators import MainPageLocators
class MainPage(BasePage):
def go_to_login_page(self):
login_link = self.browser.find_element(By.CSS_SELECTOR, "#login_link")
login_link.click()
**chosen status = status** <-----------
def should_be_login_link(self):
assert self.is_element_present(*MainPageLocators.LOGIN_LINK)
Finally I need this result: chosen_satus = "empty"
How can I do it?
You can send it as a parameter to go_to_login_page() from the test
#pytest.fixture
def choose_status(request):
return request.config.getoption("status")
def test_main_page(choose_status):
MainPage().go_to_login_page(choose_status)
class MainPage(BasePage):
def go_to_login_page(self, status):
# status = empty
Here is the final code, that solve my problem:
conftest.py
#pytest.fixture
def choose_status(request):
return request.config.getoption("status")
def test_main_page(choose_status):
MainPage().go_to_login_page(choose_status)
In main_page.py
class MainPage(BasePage):
def go_to_login_page(self,choose_status):
if choose_status == "clear":
#do smth
In test_main_page:
def test_guest_can_go_to_login_page(browser,choose_status):
link = "someurl"
page = MainPage(browser, link)
page.open()
page.go_to_login_page(choose_status)
page.should_be_login_link()
Requirement: Need to open the two instance of chrome, one in normal mode and another in incognito mode. Load the same URL lets say facebook.com on both instance. And try to login with different credential.
I am able to open two instances in normal and incognito mode, but when setting the values it get written to same page. Below is the code:
MyTest script:
#calling the driver
self.driverObj1 = WebAction().get_private_browser_driver(browser)
self.driverObj2 = WebAction().get_browser_driver(browser)
#Opening the browser and load the URL
WebAction().explicit_load_url_in_browser(self.driverObj1, self.url, self.elementforwait)
WebAction().explicit_load_url_in_browser(self.driverObj2, self.url,self.elementforwait)
#Locating the User Input
page1_loginuser = WebModel().get_element_by_xpath(self.driverObj1, self.inputxpath['logintext'])
page1_loginpass = WebModel().get_element_by_xpath(self.driverObj1, self.inputxpath['loginpass'])
page1_loginbutton = WebModel().get_element_by_xpath(self.driverObj1, self.inputxpath['loginbutton'])
page2_loginuser = WebModel().get_element_by_xpath(self.driverObj1, self.inputxpath['logintext'])
page2_loginpass = WebModel().get_element_by_xpath(self.driverObj1, self.inputxpath['loginpass'])
page2_loginbutton = WebModel().get_element_by_xpath(self.driverObj1, self.inputxpath['loginbutton'])
#filling the details and click on button
page1_loginuser.send_keys(self.logininput1['username'])
page1_loginpass.send_keys(self.logininput1['pass'])
page1_loginbutton.click()
page2_loginuser.send_keys(self.logininput2['username'])
page2_loginpass.send_keys(self.logininput2['pass'])
page2_loginbutton.click()
My class WebModel has two methods
# Method - to get chrome driver
def get_chrome_driver(self):
path_of_driver = self.get_chrome_driver_path()
self.chromeDriver = webdriver.Chrome(path_of_driver)
return self.chromeDriver
# Method - to get chrome incognito
def get_private_chrome_driver(self):
path_of_driver = self.get_chrome_driver_path()
chrome_option = webdriver.ChromeOptions()
chrome_option.add_argument("--incognito")
self.chromeDriver = webdriver.Chrome(path_of_driver, chrome_options=chrome_option)
return self.chromeDriver
class WebAction has two methods
def get_browser_driver(self, browserName):
try:
if browserName == 'chrome':
self.browserDriver = self.webmodelObj.get_chrome_driver()
elif browserName == 'firefox':
self.browserDriver = self.webmodelObj.get_firfox_driver()
elif browserName == 'ie':
self.browserDriver = self.webmodelObj.get_ie_driver()
else:
self.browserDriver = self.webmodelObj.get_safari_driver()
return self.browserDriver
except:
print "FAIL: UNABLE TO CONNECT WITH %s"%browserName
return False
def get_private_browser_driver(self,browserName):
self.browserDriver = False
try:
if browserName == 'chrome':
self.browserDriver = self.webmodelObj.get_private_chrome_driver()
return self.browserDriver
except:
print "Fail: Unable to Connect with %s" %(browserName)
return self.browserDriver
#Locating the User Input
page2_loginuser = WebModel().get_element_by_xpath(self.driverObj1, self.inputxpath['logintext'])
page2_loginpass = WebModel().get_element_by_xpath(self.driverObj1, self.inputxpath['loginpass'])
page2_loginbutton = WebModel().get_element_by_xpath(self.driverObj1, self.inputxpath['loginbutton'])
You are using self.driverObj1 to find page2_login elements.
I'm trying to make a little testing script which can post something to my testing facebook group. I think that the best way is to use Selenium webdriver if I don't use Graph API.
Login works correctly. Then I get the group (self.driver.get(group_url)). Now, I locate an textarea element and send my text there - this works (according to self.driver.save_screenshot..).
Now, I'm going to locate a submit/post button. I think it is located correctly because I've tested it by copying XPATH by inspect element in Chrome.
So I do click(). Everything seems work good but there is no new post.
# -*- coding: utf-8 -*-
from selenium import webdriver
import mLib
from selenium.webdriver.common.keys import Keys
import time
GROUPS = ['https://www.facebook.com/groups/1467884653516334/']
TEST = 'TESTING TEXT'
class base():
def __init__(self):
self.driver = webdriver.PhantomJS()
self.settings = {}
self.user = None
self.password = None
self.logged = False
self.groups = []
self.set_settings()
self.groups = self.get_groups()
def get_post_button(self):
print self.driver.find_elements_by_xpath("//*[contains(text(), 'Post')]")
def get_groups(self):
# if 'groups' in self.settings.keys():
# groups = self.settings['groups'].split('*')
# return groups if groups is not None else []
return GROUPS
def set_settings(self):
with open('settings.txt') as f:
for line in f:
splitted = line.split('::')
self.settings[splitted[0]]=splitted[1]
def login(self,username,password):
self.driver.get('http://www.facebook.com')
user_form = self.driver.find_element_by_id('email')
user_form.send_keys(username)
pass_form = self.driver.find_element_by_id('pass')
pass_form.send_keys(password)
pass_form.send_keys(Keys.RETURN)
time.sleep(5)
if 'You must log in to continue' in self.driver.page_source:
self.login()
self.logged = True
def send_post(self,text,group):
assert self.logged == True
self.driver.get(group)
mLib.printToFile('source.txt',self.driver.page_source.encode('utf-8'))
inner_wraps = self.driver.find_elements_by_css_selector('div.innerWrap')
for iw in inner_wraps:
try:
text_field = iw.find_element_by_css_selector('textarea')
text_field.send_keys(text)
self.driver.save_screenshot('screen.png')
except:
continue
button = self.driver.find_element_by_xpath('//*[#id="u_0_1w"]/div/div[5]/div/ul/li[2]/button')
print button.get_attribute('outerHTML').encode('utf-8')
webdriver.ActionChains(self.driver).move_to_element(button).click(button).perform()
bs = base()
bs.login('email#gmail.com','password')
bs.send_post(TEST,GROUPS[0])
print 'OK'
bs.driver.quit()
Why is that so? Do you have any advice?