Initial version
This commit is contained in:
Родитель
572dc04fbe
Коммит
b4a52e8f28
|
@ -0,0 +1,18 @@
|
|||
language: python
|
||||
python:
|
||||
- 2.6
|
||||
- 2.7
|
||||
|
||||
before_install:
|
||||
- export DISPLAY=':99.0'
|
||||
- sh -e /etc/init.d/xvfb start
|
||||
|
||||
install: "pip install flake8"
|
||||
|
||||
before_script: "flake8 . --ignore=E501"
|
||||
|
||||
script: py.test --baseurl=https://123done-stable.dev.lcip.org/ --driver=firefox --webqatimeout=90 --destructive tests
|
||||
|
||||
notifications:
|
||||
email: webqa-ci@mozilla.org
|
||||
irc: "irc.mozilla.org#mozwebqa"
|
|
@ -0,0 +1,15 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
|
||||
|
||||
def pytest_funcarg__mozwebqa(request):
|
||||
fxa_login_button_locator_css = 'button.signin'
|
||||
mozwebqa = request.getfuncargvalue('mozwebqa')
|
||||
mozwebqa.selenium.get('%s/' % mozwebqa.base_url)
|
||||
WebDriverWait(mozwebqa.selenium, mozwebqa.timeout).until(
|
||||
lambda s: s.find_element_by_css_selector(fxa_login_button_locator_css).is_displayed())
|
||||
mozwebqa.selenium.find_element_by_css_selector(fxa_login_button_locator_css).click()
|
||||
return mozwebqa
|
|
@ -0,0 +1,57 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
import string
|
||||
import random
|
||||
from datetime import datetime
|
||||
|
||||
from fxa.core import Client
|
||||
from fxa.tests.utils import TestEmailAccount
|
||||
|
||||
|
||||
class WebDriverFxA(object):
|
||||
|
||||
def __init__(self, testsetup):
|
||||
self.testsetup = testsetup
|
||||
|
||||
def sign_in(self, email=None, password=None):
|
||||
"""Signs in a user, either with the specified email address and password, or a returning user."""
|
||||
from pages.sign_in import SignIn
|
||||
sign_in = SignIn(self.testsetup)
|
||||
sign_in.sign_in(email, password)
|
||||
|
||||
|
||||
class FxATestAccount:
|
||||
"""A base test class that can be extended by other tests to include utility methods."""
|
||||
|
||||
password = ''.join([random.choice(string.letters) for i in range(8)])
|
||||
|
||||
def __init__(self, base_url=None):
|
||||
if base_url and '-dev.allizom' in base_url:
|
||||
self.fxa_url = 'https://stable.dev.lcip.org/auth/'
|
||||
else:
|
||||
self.fxa_url = 'https://api.accounts.firefox.com/'
|
||||
|
||||
def create_account(self):
|
||||
random_string = ''.join(random.choice(string.ascii_lowercase) for _ in range(12))
|
||||
email_pattern = random_string + '@{hostname}'
|
||||
self.account = TestEmailAccount(email=email_pattern)
|
||||
client = Client(self.fxa_url)
|
||||
# Create and verify the Firefox account
|
||||
self.session = client.create_account(self.account.email, self.password)
|
||||
print 'fxapom created an account for email: %s at %s on %s' % (
|
||||
self.account.email, self.fxa_url, datetime.now())
|
||||
m = self.account.wait_for_email(lambda m: "x-verify-code" in m["headers"])
|
||||
if not m:
|
||||
raise RuntimeError("Verification email was not received")
|
||||
self.session.verify_email_code(m["headers"]["x-verify-code"])
|
||||
return self
|
||||
|
||||
@property
|
||||
def email(self):
|
||||
return self.account.email
|
||||
|
||||
@property
|
||||
def is_verified(self):
|
||||
return self.session.get_email_status()['verified']
|
|
@ -0,0 +1,17 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
from page import Page
|
||||
|
||||
|
||||
class Base(Page):
|
||||
|
||||
def __init__(self, testsetup):
|
||||
super(Base, self).__init__(testsetup)
|
||||
self._main_window_handle = self.selenium.current_window_handle
|
||||
|
||||
def switch_to_main_window(self):
|
||||
self.selenium.switch_to_window(self._main_window_handle)
|
||||
|
||||
def close_window(self):
|
||||
self.selenium.close()
|
|
@ -0,0 +1,92 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
import time
|
||||
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
from selenium.common.exceptions import NoSuchElementException
|
||||
from selenium.common.exceptions import ElementNotVisibleException
|
||||
from selenium.common.exceptions import TimeoutException
|
||||
from unittestzero import Assert
|
||||
|
||||
|
||||
class Page(object):
|
||||
"""Base class for all Pages"""
|
||||
|
||||
def __init__(self, testsetup):
|
||||
"""Constructor"""
|
||||
|
||||
self.testsetup = testsetup
|
||||
self.base_url = testsetup.base_url
|
||||
self.selenium = testsetup.selenium
|
||||
self.timeout = testsetup.timeout
|
||||
self._selenium_root = hasattr(self, '_root_element') and self._root_element or self.selenium
|
||||
|
||||
def is_element_present(self, *locator):
|
||||
"""
|
||||
Return true if the element at the specified locator is present in the DOM.
|
||||
Note: It returns false immediately if the element is not found.
|
||||
"""
|
||||
self.selenium.implicitly_wait(0)
|
||||
try:
|
||||
self._selenium_root.find_element(*locator)
|
||||
return True
|
||||
except NoSuchElementException:
|
||||
return False
|
||||
finally:
|
||||
# set the implicit wait back
|
||||
self.selenium.implicitly_wait(self.testsetup.default_implicit_wait)
|
||||
|
||||
def is_element_visible(self, *locator):
|
||||
"""
|
||||
Return true if the element at the specified locator is visible in the browser.
|
||||
Note: It uses an implicit wait if it cannot find the element immediately.
|
||||
"""
|
||||
try:
|
||||
return self._selenium_root.find_element(*locator).is_displayed()
|
||||
except (NoSuchElementException, ElementNotVisibleException):
|
||||
return False
|
||||
|
||||
def is_element_not_visible(self, *locator):
|
||||
"""
|
||||
Return true if the element at the specified locator is not visible in the browser.
|
||||
Note: It returns true immediately if the element is not found.
|
||||
"""
|
||||
self.selenium.implicitly_wait(0)
|
||||
try:
|
||||
return not self._selenium_root.find_element(*locator).is_displayed()
|
||||
except (NoSuchElementException, ElementNotVisibleException):
|
||||
return True
|
||||
finally:
|
||||
# set the implicit wait back
|
||||
self.selenium.implicitly_wait(self.testsetup.default_implicit_wait)
|
||||
|
||||
def wait_for_element_present(self, *locator):
|
||||
"""Wait for the element at the specified locator to be present in the DOM."""
|
||||
count = 0
|
||||
while not self.is_element_present(*locator):
|
||||
time.sleep(1)
|
||||
count += 1
|
||||
if count == self.timeout:
|
||||
raise Exception(*locator + ' has not loaded')
|
||||
|
||||
def wait_for_element_visible(self, *locator):
|
||||
"""Wait for the element at the specified locator to be visible in the browser."""
|
||||
count = 0
|
||||
while not self.is_element_visible(*locator):
|
||||
time.sleep(1)
|
||||
count += 1
|
||||
if count == self.timeout:
|
||||
raise Exception(*locator + ' is not visible')
|
||||
|
||||
def wait_for_element_not_present(self, *locator):
|
||||
"""Wait for the element at the specified locator to be not present in the DOM."""
|
||||
self.selenium.implicitly_wait(0)
|
||||
try:
|
||||
WebDriverWait(self.selenium, self.timeout).until(lambda s: len(self.find_elements(*locator)) < 1)
|
||||
return True
|
||||
except TimeoutException:
|
||||
Assert.fail(TimeoutException)
|
||||
finally:
|
||||
self.selenium.implicitly_wait(self.testsetup.default_implicit_wait)
|
|
@ -0,0 +1,75 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
from base import Base
|
||||
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
|
||||
|
||||
class SignIn(Base):
|
||||
|
||||
_fox_logo_locator = (By.ID, 'fox-logo')
|
||||
_email_input_locator = (By.CSS_SELECTOR, '.input-row .email')
|
||||
_next_button_locator = (By.ID, 'email-button')
|
||||
_password_input_locator = (By.ID, 'password')
|
||||
_sign_in_locator = (By.ID, 'submit-btn')
|
||||
|
||||
def __init__(self, testsetup):
|
||||
Base.__init__(self, testsetup)
|
||||
|
||||
if len(self.selenium.window_handles) > 1:
|
||||
self.popup = True
|
||||
for handle in self.selenium.window_handles:
|
||||
self.selenium.switch_to.window(handle)
|
||||
if self.is_element_present(*self._fox_logo_locator):
|
||||
self.wait_for_element_visible(*self._email_input_locator)
|
||||
self._sign_in_window_handle = handle
|
||||
break
|
||||
else:
|
||||
raise Exception('Popup has not loaded')
|
||||
else:
|
||||
self.popup = False
|
||||
|
||||
def sign_in(self, email, password):
|
||||
"""Signs in using the specified email address and password."""
|
||||
self.email = email
|
||||
if self.is_element_present(*self._next_button_locator):
|
||||
self.click_next()
|
||||
self.login_password = password
|
||||
self.click_sign_in()
|
||||
|
||||
@property
|
||||
def email(self):
|
||||
"""Get the value of the email field."""
|
||||
return self.selenium.find_element(*self._email_locator).get_attribute('value')
|
||||
|
||||
@email.setter
|
||||
def email(self, value):
|
||||
"""Set the value of the email field."""
|
||||
email = self.selenium.find_element(*self._email_input_locator)
|
||||
email.clear()
|
||||
email.send_keys(value)
|
||||
|
||||
@property
|
||||
def login_password(self):
|
||||
"""Get the value of the login password field."""
|
||||
return self.selenium.find_element(*self._login_password_locator).get_attribute('value')
|
||||
|
||||
@login_password.setter
|
||||
def login_password(self, value):
|
||||
"""Set the value of the login password field."""
|
||||
password = self.selenium.find_element(*self._password_input_locator)
|
||||
password.clear()
|
||||
password.send_keys(value)
|
||||
|
||||
def click_next(self):
|
||||
self.selenium.find_element(*self._next_button_locator).click()
|
||||
|
||||
def click_sign_in(self):
|
||||
self.selenium.find_element(*self._sign_in_locator).click()
|
||||
if self.popup:
|
||||
WebDriverWait(self.selenium, self.timeout).until(
|
||||
lambda s: self._sign_in_window_handle not in self.selenium.window_handles)
|
||||
self.switch_to_main_window()
|
|
@ -0,0 +1,3 @@
|
|||
[DEFAULT]
|
||||
baseurl = https://123done-stable.dev.lcip.org/
|
||||
tags = fxapom
|
63
readme.md
63
readme.md
|
@ -1 +1,62 @@
|
|||
readme
|
||||
Firefox Accounts Page Object Model
|
||||
==================================
|
||||
[Selenium WebDriver][webdriver] compatible page object model and utilities for [Firefox Accounts][FxA]
|
||||
|
||||
[FxA]: https://accounts.firefox.com
|
||||
[webdriver]: http://docs.seleniumhq.org/docs/03_webdriver.jsp
|
||||
|
||||
Overview
|
||||
-------------
|
||||
This package contains a utility to create a test Firefox Account on either the dev or prod instance of Firefox Accounts,
|
||||
as well as a set of page objects that can be used to interact with Firefox Accounts' sign in screens.
|
||||
|
||||
Usage
|
||||
-----
|
||||
To create a test Firefox Account, use the `create_account` method in the `FxATestAccount` object.
|
||||
You should pass the base url for the site for which you are creating the account into the constructor
|
||||
for `FxATestAccount`, or, if you know you want to create a production Account, you can omit that argument.
|
||||
|
||||
Example:
|
||||
```python
|
||||
from fxapom.fxapom import FxATestAccount
|
||||
acct = FxATestAccount(base_url='https://www-dev.allizom.org').create_account()
|
||||
```
|
||||
|
||||
To sign in via Firefox Accounts, use the `sign_in` method in the `WebDriverFxA` object,
|
||||
passing in the email addresss and password.
|
||||
|
||||
Example:
|
||||
```python
|
||||
from fxapom.fxapom import WebDriverFxA
|
||||
fxa = WebDriverFxA(mozwebqa)
|
||||
fxa.sign_in(email_address, password)
|
||||
```
|
||||
|
||||
Note that we are passing `mozwebqa` into the constructor of `WebDriverFxA`, which is only
|
||||
generally available when using our in-house plugin [pytest-mozwebqa][plugin].
|
||||
|
||||
[plugin]: https://github.com/mozilla/pytest-mozwebqa
|
||||
|
||||
To create an account and then use it to sign in, use both tools described above.
|
||||
|
||||
Example:
|
||||
```python
|
||||
from fxapom.fxapom import FxATestAccount
|
||||
from fxapom.fxapom import WebDriverFxA
|
||||
acct = FxATestAccount(base_url='https://www-dev.allizom.org').create_account()
|
||||
fxa = WebDriverFxA(mozwebqa)
|
||||
fxa.sign_in(acct.email, acct.password)
|
||||
```
|
||||
|
||||
Running The Tests
|
||||
-----------------
|
||||
* Install the requirements using `pip install -r requirements.txt`
|
||||
* Run the tests using a local Firefox browser via `py.test --driver=Firefox tests`
|
||||
|
||||
License
|
||||
-------
|
||||
This software is licensed under the [MPL](http://www.mozilla.org/MPL/2.0/) 2.0:
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
PyFxA==0.0.1
|
||||
pytest-mozwebqa
|
||||
unittestzero
|
|
@ -0,0 +1,26 @@
|
|||
from setuptools import setup, find_packages
|
||||
import os
|
||||
|
||||
# get documentation from the README
|
||||
try:
|
||||
here = os.path.dirname(os.path.abspath(__file__))
|
||||
description = file(os.path.join(here, 'README.md')).read()
|
||||
except (OSError, IOError):
|
||||
description = ''
|
||||
|
||||
# dependencies
|
||||
deps = ['PyFxA==0.0.1']
|
||||
|
||||
setup(name='fxapom',
|
||||
version='1.0',
|
||||
description="Mozilla Firefox Accounts Page Object Model",
|
||||
long_description=description,
|
||||
classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers
|
||||
keywords='mozilla',
|
||||
author='Mozilla Web QA',
|
||||
author_email='mozwebqa@mozilla.org',
|
||||
url='https://github.com/mozilla/fxapom',
|
||||
license='MPL 2.0',
|
||||
packages=find_packages(exclude=['ez_setup', 'examples', 'tests']),
|
||||
install_requires=deps,
|
||||
include_package_data=True)
|
|
@ -0,0 +1,29 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
import re
|
||||
|
||||
import pytest
|
||||
from unittestzero import Assert
|
||||
|
||||
from fxapom.fxapom import FxATestAccount
|
||||
|
||||
|
||||
@pytest.mark.nondestructive
|
||||
@pytest.mark.skip_selenium
|
||||
class TestCreateAccount(object):
|
||||
|
||||
def test_create_new_account_on_dev(self):
|
||||
acct = FxATestAccount(base_url='https://www-dev.allizom.org').create_account()
|
||||
Assert.true(acct.is_verified)
|
||||
Assert.equal('https://stable.dev.lcip.org/auth/', acct.fxa_url)
|
||||
|
||||
def test_create_new_account_on_stage(self):
|
||||
acct = FxATestAccount(base_url='https://www.allizom.org').create_account()
|
||||
Assert.true(acct.is_verified)
|
||||
Assert.equal('https://api.accounts.firefox.com/', acct.fxa_url)
|
||||
|
||||
def test_new_account_pw_does_not_contain_numbers(self):
|
||||
acct = FxATestAccount(base_url='https://www-dev.allizom.org').create_account()
|
||||
test_regex = re.compile('\d')
|
||||
Assert.equal(None, test_regex.search(acct.password))
|
|
@ -0,0 +1,21 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
|
||||
from fxapom.fxapom import FxATestAccount
|
||||
from fxapom.fxapom import WebDriverFxA
|
||||
|
||||
|
||||
class TestLogin(object):
|
||||
|
||||
_fxa_logged_in_indicator_locator = (By.ID, 'loggedin')
|
||||
|
||||
def test_user_can_sign_in(self, mozwebqa):
|
||||
acct = FxATestAccount(base_url='https://www-dev.allizom.org').create_account()
|
||||
fxa = WebDriverFxA(mozwebqa)
|
||||
fxa.sign_in(acct.email, acct.password)
|
||||
WebDriverWait(mozwebqa.selenium, mozwebqa.timeout).until(
|
||||
lambda s: s.find_element(*self._fxa_logged_in_indicator_locator).is_displayed())
|
Загрузка…
Ссылка в новой задаче