added framework for end to end tests, also added first test 'deploy new contract'
This commit is contained in:
parent
4303181781
commit
0d712c54eb
|
@ -17,6 +17,10 @@ profiles.clj
|
|||
*.iml
|
||||
*.ipr
|
||||
*.log
|
||||
*.pyc
|
||||
*result.xml
|
||||
.cache
|
||||
.idea
|
||||
resources/contracts
|
||||
node_modules
|
||||
.DS_Store
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
import logging
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.common.exceptions import TimeoutException
|
||||
from selenium.webdriver.support.wait import WebDriverWait
|
||||
from selenium.webdriver.support import expected_conditions
|
||||
|
||||
|
||||
class BaseElement(object):
|
||||
|
||||
class Locator(object):
|
||||
|
||||
def __init__(self, by, value):
|
||||
self.by = by
|
||||
self.value = value
|
||||
|
||||
@classmethod
|
||||
def xpath_selector(locator, value):
|
||||
return locator(By.XPATH, value)
|
||||
|
||||
@classmethod
|
||||
def css_selector(locator, value):
|
||||
return locator(By.CSS_SELECTOR, value)
|
||||
|
||||
@classmethod
|
||||
def id(locator, value):
|
||||
return locator(By.ID, value)
|
||||
|
||||
@classmethod
|
||||
def name(locator, value):
|
||||
return locator(By.NAME, value)
|
||||
|
||||
def __str__(self, *args):
|
||||
return "%s:%s" % (self.by, self.value)
|
||||
|
||||
def __init__(self, driver):
|
||||
self.driver = driver
|
||||
self.locator = None
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self.__class__.__name__
|
||||
|
||||
def navigate(self):
|
||||
return None
|
||||
|
||||
def find_element(self):
|
||||
logging.info('Looking for %s' % self.name)
|
||||
return self.wait_for_element()
|
||||
|
||||
def find_elements(self):
|
||||
logging.info('Looking for %s' % self.name)
|
||||
return self.driver.find_elements(self.locator.by,
|
||||
self.locator.value)
|
||||
|
||||
def wait_for_element(self, seconds=5):
|
||||
return WebDriverWait(self.driver, seconds).until(
|
||||
expected_conditions.presence_of_element_located((self.locator.by, self.locator.value)))
|
||||
|
||||
def wait_for_clickable(self, seconds=5):
|
||||
return WebDriverWait(self.driver, seconds).until(
|
||||
expected_conditions.element_to_be_clickable((self.locator.by, self.locator.value)))
|
||||
|
||||
def is_element_present(self, sec=5):
|
||||
try:
|
||||
self.wait_for_element(sec)
|
||||
return True
|
||||
except TimeoutException:
|
||||
return False
|
||||
|
||||
|
||||
class BaseEditBox(BaseElement):
|
||||
|
||||
def __init__(self, driver):
|
||||
super(BaseEditBox, self).__init__(driver)
|
||||
|
||||
def send_keys(self, value):
|
||||
self.find_element().send_keys(value)
|
||||
logging.info('Type %s to %s' % (value, self.name))
|
||||
|
||||
def clear(self):
|
||||
self.find_element().clear()
|
||||
logging.info('Clear text in %s' % self.name)
|
||||
|
||||
|
||||
class BaseText(BaseElement):
|
||||
|
||||
def __init__(self, driver):
|
||||
super(BaseText, self).__init__(driver)
|
||||
|
||||
@property
|
||||
def text(self):
|
||||
text = self.find_element().text
|
||||
logging.info('%s is %s' % (self.name, text))
|
||||
return text
|
||||
|
||||
|
||||
class BaseButton(BaseElement):
|
||||
|
||||
def __init__(self, driver):
|
||||
super(BaseButton, self).__init__(driver)
|
||||
|
||||
def click(self):
|
||||
self.wait_for_clickable().click()
|
||||
logging.info('Tap on %s' % self.name)
|
||||
return self.navigate()
|
|
@ -0,0 +1,17 @@
|
|||
from datetime import datetime
|
||||
|
||||
|
||||
class BasePageObject(object):
|
||||
|
||||
def __init__(self, driver):
|
||||
self.driver = driver
|
||||
|
||||
def get_url(self, url):
|
||||
self.driver.get(url)
|
||||
|
||||
def refresh(self):
|
||||
self.driver.refresh()
|
||||
|
||||
@property
|
||||
def time_now(self):
|
||||
return datetime.now().strftime('%-m%-d%-H%-M%-S')
|
|
@ -0,0 +1,25 @@
|
|||
from pages.base_page import BasePageObject
|
||||
from pages.base_element import *
|
||||
|
||||
|
||||
class BountiesHeader(BaseText):
|
||||
|
||||
def __init__(self, driver):
|
||||
super(BountiesHeader, self).__init__(driver)
|
||||
self.locator = self.Locator.css_selector('.open-bounties-header')
|
||||
|
||||
|
||||
class TopHuntersHeader(BaseText):
|
||||
|
||||
def __init__(self, driver):
|
||||
super(TopHuntersHeader, self).__init__(driver)
|
||||
self.locator = self.Locator.css_selector('.top-hunters-header')
|
||||
|
||||
|
||||
class BountiesPage(BasePageObject):
|
||||
def __init__(self, driver):
|
||||
super(BountiesPage, self).__init__(driver)
|
||||
self.driver = driver
|
||||
|
||||
self.bounties_header = BountiesHeader(self.driver)
|
||||
self.top_hunters_header = TopHuntersHeader(self.driver)
|
|
@ -0,0 +1,23 @@
|
|||
from pages.base_page import BasePageObject
|
||||
from pages.base_element import *
|
||||
|
||||
|
||||
class LoginButton(BaseButton):
|
||||
def __init__(self, driver):
|
||||
super(LoginButton, self).__init__(driver)
|
||||
self.locator = self.Locator.id('button-login')
|
||||
|
||||
def navigate(self):
|
||||
from pages.thirdparty.github import GithubPage
|
||||
return GithubPage(self.driver)
|
||||
|
||||
|
||||
class LandingPage(BasePageObject):
|
||||
def __init__(self, driver):
|
||||
super(LandingPage, self).__init__(driver)
|
||||
self.driver = driver
|
||||
|
||||
self.login_button = LoginButton(self.driver)
|
||||
|
||||
def get_landing_page(self):
|
||||
self.driver.get('https://openbounty.status.im:444/')
|
|
@ -0,0 +1,173 @@
|
|||
import time, pytest
|
||||
from pages.base_element import *
|
||||
from pages.base_page import BasePageObject
|
||||
|
||||
|
||||
class EmailEditbox(BaseEditBox):
|
||||
|
||||
def __init__(self, driver):
|
||||
super(EmailEditbox, self).__init__(driver)
|
||||
self.locator = self.Locator.id('login_field')
|
||||
|
||||
|
||||
class PasswordEditbox(BaseEditBox):
|
||||
|
||||
def __init__(self, driver):
|
||||
super(PasswordEditbox, self).__init__(driver)
|
||||
self.locator = self.Locator.id('password')
|
||||
|
||||
|
||||
class SignInButton(BaseButton):
|
||||
|
||||
def __init__(self, driver):
|
||||
super(SignInButton, self).__init__(driver)
|
||||
self.locator = self.Locator.name('commit')
|
||||
|
||||
|
||||
class AuthorizeStatusOpenBounty(BaseButton):
|
||||
def __init__(self, driver):
|
||||
super(AuthorizeStatusOpenBounty, self).__init__(driver)
|
||||
self.locator = self.Locator.css_selector('[data-octo-click="oauth_application_authorization"]')
|
||||
|
||||
def navigate(self):
|
||||
from pages.openbounty.bounties import BountiesPage
|
||||
return BountiesPage(self.driver)
|
||||
|
||||
|
||||
class PermissionTypeText(BaseText):
|
||||
def __init__(self, driver):
|
||||
super(PermissionTypeText, self).__init__(driver)
|
||||
self.locator = self.Locator.css_selector('.permission-title')
|
||||
|
||||
|
||||
class InstallButton(BaseButton):
|
||||
def __init__(self, driver):
|
||||
super(InstallButton, self).__init__(driver)
|
||||
self.locator = self.Locator.css_selector('.btn-primary')
|
||||
|
||||
|
||||
class OrganizationButton(BaseButton):
|
||||
def __init__(self, driver):
|
||||
super(OrganizationButton, self).__init__(driver)
|
||||
self.locator = self.Locator.css_selector('[alt="@Org4"]')
|
||||
|
||||
|
||||
class AllRepositoriesButton(BaseButton):
|
||||
def __init__(self, driver):
|
||||
super(AllRepositoriesButton, self).__init__(driver)
|
||||
self.locator = self.Locator.id('install_target_all')
|
||||
|
||||
|
||||
class IntegrationPermissionsGroup(BaseText):
|
||||
def __init__(self, driver):
|
||||
super(IntegrationPermissionsGroup, self).__init__(driver)
|
||||
self.locator = self.Locator.css_selector('.integrations-permissions-group')
|
||||
|
||||
|
||||
class NewIssueButton(BaseButton):
|
||||
def __init__(self, driver):
|
||||
super(NewIssueButton, self).__init__(driver)
|
||||
self.locator = self.Locator.css_selector(".subnav [role='button']")
|
||||
|
||||
|
||||
class IssueTitleEditBox(BaseEditBox):
|
||||
def __init__(self, driver):
|
||||
super(IssueTitleEditBox, self).__init__(driver)
|
||||
self.locator = self.Locator.id("issue_title")
|
||||
|
||||
|
||||
class LabelsButton(BaseButton):
|
||||
def __init__(self, driver):
|
||||
super(LabelsButton, self).__init__(driver)
|
||||
self.locator = self.Locator.css_selector("button[aria-label='Apply labels to this issue']")
|
||||
|
||||
class BountyLabel(BaseButton):
|
||||
def __init__(self, driver):
|
||||
super(LabelsButton.BountyLabel, self).__init__(driver)
|
||||
self.locator = self.Locator.css_selector("[data-name='bounty']")
|
||||
|
||||
class CrossButton(BaseButton):
|
||||
def __init__(self, driver):
|
||||
super(LabelsButton.CrossButton, self).__init__(driver)
|
||||
self.locator = self.Locator.xpath_selector(
|
||||
"//span[text()='Apply labels to this issue']/../*[@aria-label='Close']")
|
||||
|
||||
|
||||
class SubmitNewIssueButton(BaseButton):
|
||||
def __init__(self, driver):
|
||||
super(SubmitNewIssueButton, self).__init__(driver)
|
||||
self.locator = self.Locator.xpath_selector("//button[contains(text(), "
|
||||
"'Submit new issue')]")
|
||||
|
||||
|
||||
class ContractBody(BaseText):
|
||||
def __init__(self, driver):
|
||||
super(ContractBody, self).__init__(driver)
|
||||
self.locator = self.Locator.xpath_selector("//tbody//p[contains(text(), "
|
||||
"'Current balance: 0.000000 ETH')]")
|
||||
|
||||
|
||||
class GithubPage(BasePageObject):
|
||||
def __init__(self, driver):
|
||||
super(GithubPage, self).__init__(driver)
|
||||
|
||||
self.driver = driver
|
||||
|
||||
self.email_input = EmailEditbox(self.driver)
|
||||
self.password_input = PasswordEditbox(self.driver)
|
||||
self.sign_in_button = SignInButton(self.driver)
|
||||
|
||||
self.authorize_sob = AuthorizeStatusOpenBounty(self.driver)
|
||||
self.permission_type = PermissionTypeText(self.driver)
|
||||
|
||||
self.install_button = InstallButton(self.driver)
|
||||
self.organization_button = OrganizationButton(self.driver)
|
||||
self.all_repositories_button = AllRepositoriesButton(self.driver)
|
||||
self.integration_permissions_group = IntegrationPermissionsGroup(self.driver)
|
||||
|
||||
self.new_issue_button = NewIssueButton(self.driver)
|
||||
self.issue_title_input = IssueTitleEditBox(self.driver)
|
||||
self.labels_button = LabelsButton(self.driver)
|
||||
self.bounty_label = LabelsButton.BountyLabel(self.driver)
|
||||
self.cross_button = LabelsButton.CrossButton(self.driver)
|
||||
self.submit_new_issue_button = SubmitNewIssueButton(self.driver)
|
||||
self.contract_body = ContractBody(self.driver)
|
||||
|
||||
def get_issues_page(self):
|
||||
self.driver.get('https://github.com/Org4/nov13/issues')
|
||||
|
||||
def get_sob_plugin_page(self):
|
||||
self.driver.get('http://github.com/apps/status-open-bounty-app-test')
|
||||
|
||||
def sign_in(self, email, password):
|
||||
self.email_input.send_keys(email)
|
||||
self.password_input.send_keys(password)
|
||||
self.sign_in_button.click()
|
||||
|
||||
def install_sob_plugin(self):
|
||||
initial_url = self.driver.current_url
|
||||
self.get_sob_plugin_page()
|
||||
self.install_button.click()
|
||||
self.organization_button.click()
|
||||
self.all_repositories_button.click()
|
||||
self.install_button.click()
|
||||
self.driver.get(initial_url)
|
||||
|
||||
def create_new_bounty(self):
|
||||
self.get_issues_page()
|
||||
self.new_issue_button.click()
|
||||
self.issue_title_input.send_keys('auto_test_bounty_%s' % self.time_now)
|
||||
self.labels_button.click()
|
||||
self.bounty_label.click()
|
||||
self.cross_button.click()
|
||||
self.submit_new_issue_button.click()
|
||||
|
||||
def get_deployed_contract(self, wait=120):
|
||||
for i in range(wait):
|
||||
self.refresh()
|
||||
try:
|
||||
return self.contract_body.text
|
||||
except TimeoutException:
|
||||
time.sleep(10)
|
||||
pass
|
||||
pytest.fail('Contract is not deployed in %s minutes!' % str(wait/60))
|
|
@ -0,0 +1,88 @@
|
|||
import time
|
||||
from pages.base_page import BasePageObject
|
||||
from pages.base_element import *
|
||||
from selenium.webdriver import ActionChains
|
||||
|
||||
|
||||
class BasePluginButton(BaseButton):
|
||||
|
||||
def click(self):
|
||||
time.sleep(2)
|
||||
self.find_element().click()
|
||||
|
||||
|
||||
class AcceptButton(BasePluginButton):
|
||||
|
||||
def __init__(self, driver):
|
||||
super(AcceptButton, self).__init__(driver)
|
||||
self.locator = self.Locator.xpath_selector("//button[.='Accept']")
|
||||
|
||||
|
||||
class PrivacyText(BaseText):
|
||||
|
||||
def __init__(self, driver):
|
||||
super(PrivacyText, self).__init__(driver)
|
||||
self.locator = self.Locator.xpath_selector("//a[.='Privacy']")
|
||||
|
||||
|
||||
class ExportDenButton(BaseButton):
|
||||
|
||||
def __init__(self, driver):
|
||||
super(ExportDenButton, self).__init__(driver)
|
||||
self.locator = self.Locator.xpath_selector("//p[.='Import Existing DEN']")
|
||||
|
||||
|
||||
class SecretPhraseEditBox(BaseEditBox):
|
||||
|
||||
def __init__(self, driver):
|
||||
super(SecretPhraseEditBox, self).__init__(driver)
|
||||
self.locator = self.Locator.xpath_selector("//textarea")
|
||||
|
||||
|
||||
class PasswordEditBox(BaseEditBox):
|
||||
|
||||
def __init__(self, driver):
|
||||
super(PasswordEditBox, self).__init__(driver)
|
||||
self.locator = self.Locator.id('password-box')
|
||||
|
||||
|
||||
class PasswordConfirmEditBox(BaseEditBox):
|
||||
|
||||
def __init__(self, driver):
|
||||
super(PasswordConfirmEditBox, self).__init__(driver)
|
||||
self.locator = self.Locator.id('password-box-confirm')
|
||||
|
||||
|
||||
class OkButton(BasePluginButton):
|
||||
|
||||
def __init__(self, driver):
|
||||
super(OkButton, self).__init__(driver)
|
||||
self.locator = self.Locator.xpath_selector("//button[.='OK']")
|
||||
|
||||
|
||||
class MetaMaskPlugin(BasePageObject):
|
||||
def __init__(self, driver):
|
||||
super(MetaMaskPlugin, self).__init__(driver)
|
||||
self.driver = driver
|
||||
|
||||
self.accept_button = AcceptButton(self.driver)
|
||||
self.privacy_text = PrivacyText(self.driver)
|
||||
self.enter_secret_phrase = SecretPhraseEditBox(self.driver)
|
||||
self.export_den_button = ExportDenButton(self.driver)
|
||||
self.password_edit_box = PasswordEditBox(self.driver)
|
||||
self.password_box_confirm = PasswordConfirmEditBox(self.driver)
|
||||
self.ok_button = OkButton(self.driver)
|
||||
|
||||
def recover_access(self, passphrase, password, confirm_password):
|
||||
|
||||
self.get_url('chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/popup.html')
|
||||
self.accept_button.click()
|
||||
ActionChains(self.driver).move_to_element(self.privacy_text.find_element()).perform()
|
||||
self.accept_button.click()
|
||||
|
||||
self.export_den_button.click()
|
||||
self.enter_secret_phrase.send_keys(passphrase)
|
||||
self.password_edit_box.send_keys(password)
|
||||
self.password_box_confirm.send_keys(confirm_password)
|
||||
self.ok_button.click()
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
[pytest]
|
||||
norecursedirs = .git pages
|
||||
addopts = -s -v --junitxml=result.xml --tb=short
|
|
@ -0,0 +1,18 @@
|
|||
allpairspy==2.3.0
|
||||
apipkg==1.4
|
||||
certifi==2017.7.27.1
|
||||
chardet==3.0.4
|
||||
execnet==1.4.1
|
||||
idna==2.5
|
||||
lxml==3.8.0
|
||||
multidict==3.1.3
|
||||
namedlist==1.7
|
||||
py==1.4.34
|
||||
pytest==3.2.1
|
||||
pytest-forked==0.2
|
||||
pytest-xdist==1.20.0
|
||||
requests==2.18.3
|
||||
selenium==2.53.6
|
||||
six==1.10.0
|
||||
urllib3==1.22
|
||||
yarl==0.12.0
|
Binary file not shown.
|
@ -0,0 +1,8 @@
|
|||
|
||||
|
||||
class TestData(object):
|
||||
|
||||
def __init__(self):
|
||||
self.test_name = None
|
||||
|
||||
test_data = TestData()
|
|
@ -0,0 +1,70 @@
|
|||
import pytest, sys
|
||||
from selenium import webdriver
|
||||
from selenium.common.exceptions import WebDriverException
|
||||
from tests.postconditions import remove_application, remove_installation
|
||||
from os import environ, path
|
||||
from tests import test_data
|
||||
|
||||
|
||||
class BaseTestCase:
|
||||
|
||||
@property
|
||||
def sauce_username(self):
|
||||
return environ.get('SAUCE_USERNAME')
|
||||
|
||||
|
||||
@property
|
||||
def sauce_access_key(self):
|
||||
return environ.get('SAUCE_ACCESS_KEY')
|
||||
|
||||
|
||||
@property
|
||||
def executor_sauce_lab(self):
|
||||
return 'http://%s:%s@ondemand.saucelabs.com:80/wd/hub' % (self.sauce_username, self.sauce_access_key)
|
||||
|
||||
def print_sauce_lab_info(self, driver):
|
||||
sys.stdout = sys.stderr
|
||||
print("SauceOnDemandSessionID=%s job-name=%s" % (driver.session_id,
|
||||
pytest.config.getoption('build')))
|
||||
|
||||
@property
|
||||
def capabilities_sauce_lab(self):
|
||||
|
||||
desired_caps = dict()
|
||||
desired_caps['name'] = test_data.test_name
|
||||
desired_caps['build'] = pytest.config.getoption('build')
|
||||
desired_caps['platform'] = "MAC"
|
||||
desired_caps['browserName'] = 'Chrome'
|
||||
desired_caps['screenResolution'] = '2048x1536'
|
||||
desired_caps['captureHtml'] = False
|
||||
return desired_caps
|
||||
|
||||
def setup_method(self):
|
||||
|
||||
self.errors = []
|
||||
|
||||
# options = webdriver.ChromeOptions()
|
||||
# options.add_argument('--start-fullscreen')
|
||||
# options.add_extension(path.abspath('/resources/metamask3_12_0.crx'))
|
||||
|
||||
self.driver = webdriver.Remote(self.executor_sauce_lab,
|
||||
desired_capabilities=self.capabilities_sauce_lab)
|
||||
self.driver.implicitly_wait(5)
|
||||
|
||||
def verify_no_errors(self):
|
||||
if self.errors:
|
||||
msg = ''
|
||||
for error in self.errors:
|
||||
msg += (error + '\n')
|
||||
pytest.fail(msg, pytrace=False)
|
||||
|
||||
def teardown_method(self):
|
||||
|
||||
remove_application(self.driver)
|
||||
remove_installation(self.driver)
|
||||
|
||||
try:
|
||||
self.print_sauce_lab_info(self.driver)
|
||||
self.driver.quit()
|
||||
except WebDriverException:
|
||||
pass
|
|
@ -0,0 +1,23 @@
|
|||
from tests import test_data
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
def pytest_addoption(parser):
|
||||
parser.addoption("--build",
|
||||
action="store",
|
||||
default='SOB-' + datetime.now().strftime('%d-%b-%Y-%H-%M'),
|
||||
help="Specify build name")
|
||||
parser.addoption('--log',
|
||||
action='store',
|
||||
default=True,
|
||||
help='Display each test step in terminal as plain text: True/False')
|
||||
|
||||
|
||||
def pytest_configure(config):
|
||||
if config.getoption('log'):
|
||||
import logging
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
|
||||
def pytest_runtest_setup(item):
|
||||
test_data.test_name = item.name
|
|
@ -0,0 +1,22 @@
|
|||
from selenium.webdriver.common.by import By
|
||||
from selenium.common.exceptions import NoSuchElementException
|
||||
|
||||
|
||||
def remove_application(driver):
|
||||
try:
|
||||
driver.get('https://github.com/settings/applications')
|
||||
driver.find_element(By.CSS_SELECTOR, '.BtnGroup-item').click()
|
||||
driver.find_element(By.CSS_SELECTOR, '.facebox-popup .btn-danger').click()
|
||||
except NoSuchElementException:
|
||||
pass
|
||||
|
||||
|
||||
def remove_installation(driver):
|
||||
try:
|
||||
driver.get('https://github.com/organizations/Org4/settings/installations')
|
||||
driver.find_element(By.CSS_SELECTOR, '.iconbutton').click()
|
||||
driver.find_element(By.XPATH, "//a[@class='btn btn-danger']").click()
|
||||
driver.find_element(By.CSS_SELECTOR, '.facebox-popup .btn-danger').click()
|
||||
except NoSuchElementException:
|
||||
pass
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
import pytest
|
||||
from os import environ
|
||||
from pages.openbounty.landing import LandingPage
|
||||
from tests.basetestcase import BaseTestCase
|
||||
|
||||
|
||||
@pytest.mark.sanity
|
||||
class TestLogin(BaseTestCase):
|
||||
|
||||
def test_deploy_new_contract(self):
|
||||
landing = LandingPage(self.driver)
|
||||
landing.get_landing_page()
|
||||
github = landing.login_button.click()
|
||||
github.sign_in('anna04test',
|
||||
'f@E23D3H15Rd')
|
||||
assert github.permission_type.text == 'Personal user data'
|
||||
bounties_page = github.authorize_sob.click()
|
||||
github.install_sob_plugin()
|
||||
assert bounties_page.bounties_header.text == 'Bounties'
|
||||
assert bounties_page.top_hunters_header.text == 'Top hunters'
|
||||
github.create_new_bounty()
|
||||
github.get_deployed_contract()
|
Loading…
Reference in New Issue