This commit is contained in:
Bob Silverberg 2015-02-03 10:21:17 -05:00
Родитель 572dc04fbe
Коммит b4a52e8f28
15 изменённых файлов: 418 добавлений и 1 удалений

18
.travis.yml Normal file
Просмотреть файл

@ -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"

15
conftest.py Normal file
Просмотреть файл

@ -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
fxapom/__init__.py Normal file
Просмотреть файл

57
fxapom/fxapom.py Normal file
Просмотреть файл

@ -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
fxapom/pages/__init__.py Normal file
Просмотреть файл

17
fxapom/pages/base.py Normal file
Просмотреть файл

@ -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()

92
fxapom/pages/page.py Normal file
Просмотреть файл

@ -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)

75
fxapom/pages/sign_in.py Normal file
Просмотреть файл

@ -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()

3
mozwebqa.cfg Normal file
Просмотреть файл

@ -0,0 +1,3 @@
[DEFAULT]
baseurl = https://123done-stable.dev.lcip.org/
tags = fxapom

Просмотреть файл

@ -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/.

3
requirements.txt Normal file
Просмотреть файл

@ -0,0 +1,3 @@
PyFxA==0.0.1
pytest-mozwebqa
unittestzero

26
setup.py Normal file
Просмотреть файл

@ -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
tests/__init__.py Normal file
Просмотреть файл

Просмотреть файл

@ -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))

21
tests/test_login.py Normal file
Просмотреть файл

@ -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())