Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ language: python
sudo: false

python:
- "2.7"
- 2.7

env:
global:
- DISPLAY=:99.0
matrix:
- TOXENV=flake8
- TOXENV=docs
- TOXENV=assets
Expand All @@ -15,9 +18,8 @@ env:
- TOXENV=amo
- TOXENV=users
- TOXENV=main
- TOXENV=ui-tests

matrix:
fast_finish: true

cache:
pip: true
Expand All @@ -32,6 +34,7 @@ addons:
apt:
packages:
- swig
firefox: latest

before_install:
- scripts/travis_es.sh
Expand All @@ -47,6 +50,16 @@ install:
before_script:
- mysql -e 'create database olympia;'
- node --version
- |
if [[ $TOXENV == "ui-tests" ]]; then
wget -O /tmp/geckodriver.tar.gz https://github.com/mozilla/geckodriver/releases/download/v0.15.0/geckodriver-v0.15.0-linux64.tar.gz
mkdir $HOME/geckodriver && tar xvf /tmp/geckodriver.tar.gz -C $HOME/geckodriver
export PATH=$HOME/geckodriver:$PATH
firefox --version
geckodriver --version
sh -e /etc/init.d/xvfb start
sleep 10
fi

script:
- RUNNING_IN_CI=True tox -v
Expand Down
3 changes: 1 addition & 2 deletions circle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ dependencies:

test:
override:
- echo "Skipped...Running our uitests on every pull request breaks"
- echo "them irregularly and they're badly maintained at the moment."
- echo "UI Tests are run on Travis CI. https://travis-ci.org/mozilla/addons-server/"

deployment:
latest:
Expand Down
2 changes: 0 additions & 2 deletions scripts/setup-docker.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ python manage.py syncdb --noinput
python manage.py loaddata initial.json
python manage.py import_prod_versions
schematic --fake src/olympia/migrations/
#python manage.py createsuperuser
#python manage.py loaddata zadmin/users

# update_assets:
make update_assets
Expand Down
1 change: 0 additions & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ norecursedirs =
node_modules locale static media site-static user-media tmp
templates fixtures migrations
.* *.egg dist cache venv __pycache__
tests/ui
DJANGO_SETTINGS_MODULE = settings_test

[flake8]
Expand Down
40 changes: 40 additions & 0 deletions src/olympia/users/management/commands/createsuperuser.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
(http://bit.ly/2cTgsNV)
"""
import json
import os
from datetime import datetime

from olympia.api.models import APIKey
Expand Down Expand Up @@ -38,6 +39,24 @@ def add_arguments(self, parser):
help='Assign the user to the Accounts:SuperCreate group',
)

parser.add_argument(
'--save-api-credentials',
type=str,
dest='save_api_credentials',
default=False,
help='Saves the generated API credentials into a JSON file',
)

parser.add_argument(
'--hostname',
type=str,
dest='hostname',
default=False,
help='Sets the hostname of the credentials JSON file',
)

CreateSuperUserCommand.add_arguments(self, parser)

def handle(self, *args, **options):
user_data = {}

Expand Down Expand Up @@ -80,6 +99,27 @@ def handle(self, *args, **options):
'api-secret': apikey.secret
}))

if options.get('save_api_credentials', False):
hostname = options.get('hostname', os.environ.get(
'PYTEST_BASE_URL', False))
# json object for variables file
# set hostname to stdin or env variable

if hostname:
credentials = {
'api': {
hostname: {
'username': user.username,
'jwt_issuer': apikey.key,
'jwt_secret': apikey.secret,
}
}
}

# write to json file
with open(options.get('save_api_credentials'), 'w') as outfile:
json.dump(credentials, outfile, indent=2)

def get_value(self, field_name):
field = get_user_model()._meta.get_field(field_name)
value = None
Expand Down
120 changes: 97 additions & 23 deletions tests/ui/conftest.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
import datetime
import json
import os
import urlparse

from fxapom.fxapom import DEV_URL, PROD_URL, FxATestAccount
import jwt
import pytest
import requests
from django.core.management import call_command
from fxapom.fxapom import DEV_URL, PROD_URL, FxATestAccount
from olympia.amo.tests import create_switch
from pytest_django import live_server_helper


@pytest.fixture(scope='function')
def my_base_url(base_url, request):
return base_url or request.getfixturevalue("live_server").url


@pytest.fixture
Expand All @@ -14,16 +23,17 @@ def capabilities(capabilities):
capabilities['marionette'] = True
return capabilities


@pytest.fixture
def fxa_account(base_url):
url = DEV_URL if 'dev' in base_url else PROD_URL
def fxa_account(my_base_url):
url = DEV_URL if 'dev' or 'localhost' in my_base_url else PROD_URL
return FxATestAccount(url)


@pytest.fixture(scope='session')
def jwt_issuer(base_url, variables):
try:
hostname = [urlparse.urlsplit(base_url).hostname]
hostname = urlparse.urlsplit(base_url).hostname
return variables['api'][hostname]['jwt_issuer']
except KeyError:
return os.getenv('JWT_ISSUER')
Expand All @@ -32,35 +42,99 @@ def jwt_issuer(base_url, variables):
@pytest.fixture(scope='session')
def jwt_secret(base_url, variables):
try:
hostname = [urlparse.urlsplit(base_url).hostname]
hostname = urlparse.urlsplit(base_url).hostname
return variables['api'][hostname]['jwt_secret']
except KeyError:
return os.getenv('JWT_SECRET')


@pytest.fixture
def jwt_token(base_url, jwt_issuer, jwt_secret):
payload = {
'iss': jwt_issuer,
'iat': datetime.datetime.utcnow(),
'exp': datetime.datetime.utcnow() + datetime.timedelta(seconds=30)}
return jwt.encode(payload, jwt_secret, algorithm='HS256')
def initial_data(transactional_db):
from olympia.amo.tests import addon_factory, user_factory
from olympia.addons.models import AddonUser

for x in range(10):
AddonUser.objects.create(user=user_factory(), addon=addon_factory())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just noticed it, if you ever have the need to use it, there's also olympia.landfill.user:generate_addon_user_and_category which does this and creates a proper (featured) category too.



@pytest.fixture
def create_superuser(transactional_db, my_base_url, tmpdir, variables):
create_switch('super-create-accounts')
call_command('loaddata', 'initial.json')

call_command(
'createsuperuser',
interactive=False,
username='uitest',
email='uitester@mozilla.org',
add_to_supercreate_group=True,
save_api_credentials=str(tmpdir.join('variables.json')),
hostname=urlparse.urlsplit(my_base_url).hostname
)

with tmpdir.join('variables.json').open() as f:
variables.update(json.load(f))


@pytest.fixture
def user(base_url, fxa_account, jwt_token):
user = {
def user(create_superuser, my_base_url, fxa_account, jwt_token):
url = '{base_url}/api/v3/accounts/super-create/'.format(
base_url=my_base_url)

params = {
'email': fxa_account.email,
'password': fxa_account.password,
'username': fxa_account.email.split('@')[0]}
url = '{base_url}/api/v3/accounts/super-create/'.format(base_url=base_url)
params = {
'email': user['email'],
'username': user['username'],
'password': user['password'],
'fxa_id': fxa_account.session.uid}
headers = {'Authorization': 'JWT {token}'.format(token=jwt_token)}
r = requests.post(url, data=params, headers=headers)
assert requests.codes.created == r.status_code
user.update(r.json())
return user
response = requests.post(url, data=params, headers=headers)
assert requests.codes.created == response.status_code
params.update(response.json())
return params


@pytest.fixture(scope='function')
def live_server(request, transactional_db):
"""
This fixture overrides the live_server fixture provided by
pytest_django. live_server allows us to create a running version of the
addons django application within pytest for testing.

cgrebs:
From what I found out was that the `live_server` fixture (in our setup,
couldn't reproduce in a fresh project) apparently starts up the
LiveServerThread way too early before pytest-django configures the
settings correctly.

That resulted in the LiveServerThread querying the 'default' database
which was different from what the other fixtures and tests were using
which resulted in the problem that the just created api keys could not
be found in the api methods in the live-server.

I worked around that by implementing the live_server fixture ourselfs
and make it function-scoped so that it now runs in a proper
database-transaction.

This is a HACK and I'll work on a more permanent solution but for now
it should be enough to continue working on porting tests...

Also investigating if there are any problems in pytest-django directly.
"""

addr = (request.config.getvalue('liveserver') or
os.getenv('DJANGO_LIVE_TEST_SERVER_ADDRESS'))

if not addr:
addr = 'localhost:8081,8100-8200'

server = live_server_helper.LiveServer(addr)
yield server
server.stop()


@pytest.fixture
def jwt_token(base_url, jwt_issuer, jwt_secret):
payload = {
'iss': jwt_issuer,
'iat': datetime.datetime.utcnow(),
'exp': datetime.datetime.utcnow() + datetime.timedelta(seconds=30)}
return jwt.encode(payload, jwt_secret, algorithm='HS256')
2 changes: 1 addition & 1 deletion tests/ui/pages/desktop/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class Header(Region):
def click_login(self):
self.find_element(*self._login_locator).click()
from pages.desktop.login import Login
return Login(self.selenium, self.page.base_url)
return Login(self.selenium, self.page.base_url, timeout=30)

def click_logout(self):
user = self.find_element(*self._user_locator)
Expand Down
11 changes: 2 additions & 9 deletions tests/ui/pages/desktop/login.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,6 @@ class Login(Base):
_continue_locator = (By.CSS_SELECTOR, '#normal-login .login-source-button')

def login(self, email, password):
self.find_element(*self._email_locator).send_keys(email)
self.find_element(*self._continue_locator).click()
from fxapom.pages.sign_in import SignIn
sign_in = SignIn(self.selenium)
# TODO https://github.com/mozilla/fxapom/issues/33
self.wait.until(lambda s: self.is_element_displayed(
*sign_in._email_input_locator))
sign_in.login_password = password
sign_in.click_sign_in()
self.wait.until(lambda s: self.logged_in)
SignIn(self.selenium).sign_in(email, password)
self.wait.until(lambda _: self.logged_in)
2 changes: 1 addition & 1 deletion tests/ui/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ pytest-instafail==0.3.0
pytest-selenium==1.9.1
pytest-variables==1.5.1
pytest-xdist==1.15.0
selenium==3.0.2
selenium==3.3.1
2 changes: 1 addition & 1 deletion tests/ui/setup.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[tool:pytest]
addopts = -r=a --verbose
base_url = https://addons-dev.allizom.org
sensitive_url = mozilla\.(com|org)
xfail_strict = true
DJANGO_SETTINGS_MODULE = settings_test
12 changes: 8 additions & 4 deletions tests/ui/test_home.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,21 @@
from pages.desktop.home import Home


@pytest.mark.django_db
@pytest.mark.nondestructive
def test_there_are_ten_most_popular_extensions(base_url, selenium):
def test_there_are_ten_most_popular_extensions(
my_base_url, selenium, initial_data):
"""Ten most popular add-ons are listed"""
page = Home(selenium, base_url).open()
page = Home(selenium, my_base_url).open()
assert len(page.most_popular.extensions) == 10


@pytest.mark.django_db
@pytest.mark.nondestructive
def test_most_popular_extensions_are_sorted_by_users(base_url, selenium):
def test_most_popular_extensions_are_sorted_by_users(
my_base_url, selenium, initial_data):
"""Most popular add-ons are sorted by popularity"""
page = Home(selenium, base_url).open()
page = Home(selenium, my_base_url).open()
extensions = page.most_popular.extensions
sorted_by_users = sorted(extensions, key=lambda e: e.users, reverse=True)
assert sorted_by_users == extensions
15 changes: 8 additions & 7 deletions tests/ui/test_login.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
import os
import pytest

from pages.desktop.home import Home


@pytest.mark.skip(
reason='https://github.com/mozilla/addons-server/issues/2462')
def test_login(base_url, selenium, user):
@pytest.mark.skipif(os.environ.get('PYTEST_BASE_URL') is None,
reason='Live Server login currently not functioning')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct me if I'm blind, knowing that this is hard to fix, are there any tests currently running that are using the live-server and thus testing the overwritten live_server fixture? I can't see any but please correct me if I'm wrong.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, this should probably use another environment variable than PYTEST_BASE_URL to skip and switch between live_server and real-environments (e.g UITEST_USE_LIVESERVER or so) to be more explicit about any implications it has on the tests.

Also, can be changed later but we should abstract these switches and skips into something like

requires_real_environment = pytest.mark.skipif(os.environ.get('UITEST_USE_LIVESERVER') is None, reason='Requires a real environment not the live_server')

@requires_real_environment
def test_login(my_base_url, selenium, user):
    # ...

And set the actual reason in the docstring. Again, let's not do this now but maybe later when we have more tests that need switching back and forth.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct me if I'm blind, knowing that this is hard to fix, are there any tests currently running that are using the live-server and thus testing the overwritten live_server fixture? I can't see any but please correct me if I'm wrong.

my_base_url starts the live server for each test.

Also, this should probably use another environment variable than PYTEST_BASE_URL to skip and switch between live_server and real-environments (e.g UITEST_USE_LIVESERVER or so) to be more explicit about any implications it has on the tests.

I have a patch for this, but I will keep this in mind.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my_base_url starts the live server for each test.

right but no test is actually using it right now, right? Just want to make sure I'm not missing anything.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They all use my_base_url

def test_login(my_base_url, selenium, user):
"""User can login"""
page = Home(selenium, base_url).open()
page = Home(selenium, my_base_url).open()
assert not page.logged_in
page.login(user['email'], user['password'])
assert page.logged_in


@pytest.mark.skip(
reason='https://github.com/mozilla/addons-server/issues/2462')
def test_logout(base_url, selenium, user):
reason='https://github.com/mozilla/geckodriver/issues/233')
def test_logout(my_base_url, selenium, user):
"""User can logout"""
page = Home(selenium, base_url).open()
page = Home(selenium, my_base_url).open()
page.login(user['email'], user['password'])
page.logout()
assert not page.logged_in
Loading