summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md3
-rwxr-xr-xservice/go2
-rw-r--r--service/pixelated/resources/login_resource.py4
-rw-r--r--service/pixelated/resources/mails_resource.py6
-rw-r--r--service/test/functional/features/environment.py145
-rw-r--r--service/test/functional/features/smoke.feature36
-rw-r--r--service/test/functional/features/steps/attachments.py24
-rw-r--r--service/test/functional/features/steps/common.py46
-rw-r--r--service/test/functional/features/steps/compose.py7
-rw-r--r--service/test/functional/features/steps/data_setup.py4
-rw-r--r--service/test/functional/features/steps/login.py24
-rw-r--r--service/test/functional/features/steps/mail_list.py44
-rw-r--r--service/test/functional/features/steps/mail_view.py12
-rw-r--r--service/test/functional/features/steps/search.py6
-rw-r--r--service/test/functional/features/steps/signup.py44
-rw-r--r--service/test/functional/features/steps/tag_list.py10
-rw-r--r--service/test/integration/test_multi_user_login.py2
-rw-r--r--service/test/unit/adapter/services/test_mail_sender.py20
-rw-r--r--service/test/unit/resources/test_login_resource.py2
-rw-r--r--service/test_requirements.txt4
20 files changed, 311 insertions, 134 deletions
diff --git a/README.md b/README.md
index 158ca3af..50c7cda9 100644
--- a/README.md
+++ b/README.md
@@ -3,6 +3,7 @@ Pixelated User Agent
[![Build Status](https://snap-ci.com/pixelated/pixelated-user-agent/branch/master/build_image)](https://snap-ci.com/pixelated/pixelated-user-agent/branch/master) [
![Coverage Status](https://coveralls.io/repos/pixelated/pixelated-user-agent/badge.svg?branch=master)](https://coveralls.io/r/pixelated/pixelated-user-agent?branch=master)
+[![Stories in Dev](https://badge.waffle.io/pixelated/pixelated-user-agent.svg?label=2%20-%20Development&title=Developing)](http://waffle.io/pixelated/pixelated-user-agent)
The Pixelated User Agent is the mail client of the Pixelated ecosystem. It is composed of two parts, a web interface written in JavaScript ([FlightJS](https://flightjs.github.io/)) and a Python API that interacts with a LEAP Provider, the e-mail platform that Pixelated is built on.
@@ -261,3 +262,5 @@ For people who want to run the user agent on docker container can use the Docker
## How to translate the user interface
See: [Contributor's Guide](https://github.com/pixelated/pixelated-user-agent/blob/master/CONTRIBUTING.md#translating-ui)
+
+
diff --git a/service/go b/service/go
index 7df9111b..8ceae7db 100755
--- a/service/go
+++ b/service/go
@@ -119,7 +119,7 @@ function runFunctionalTests {
echo "Executing Functional Tests on headless PhantomJS."
removeZmqCertificates
echo "You should execute it on Debian box for more similar results with CI environment."
- behave --tags ~@wip test/functional/features
+ behave --tags ~@wip --tags ~@smoke test/functional/features
echo "Done."
}
diff --git a/service/pixelated/resources/login_resource.py b/service/pixelated/resources/login_resource.py
index 5838003a..9bb771df 100644
--- a/service/pixelated/resources/login_resource.py
+++ b/service/pixelated/resources/login_resource.py
@@ -133,10 +133,10 @@ class LoginResource(BaseResource):
self._complete_bootstrap(user_auth, request)
def render_error(error):
- log.info('Login Error for %s' % request.args['username'][0])
+ log.info('Login error for %s' % request.args['username'][0])
log.info('%s' % error)
request.setResponseCode(UNAUTHORIZED)
- return self._render_template(request, 'Invalid credentials')
+ return self._render_template(request, 'Invalid username or password')
d = self._handle_login(request)
d.addCallbacks(render_response, render_error)
diff --git a/service/pixelated/resources/mails_resource.py b/service/pixelated/resources/mails_resource.py
index 79b8ae4d..93070dd6 100644
--- a/service/pixelated/resources/mails_resource.py
+++ b/service/pixelated/resources/mails_resource.py
@@ -67,7 +67,7 @@ class MailsDeleteResource(Resource):
def render_POST(self, request):
def response_failed(failure):
- log.error(failure, 'something failed')
+ log.error('something failed: %s' % failure.getErrorMessage())
request.finish()
idents = json.loads(request.content.read())['idents']
@@ -175,7 +175,7 @@ class MailsResource(BaseResource):
if isinstance(error.value, SMTPDownException):
respond_json_deferred({'message': str(error.value)}, request, status_code=503)
else:
- log.error(error, 'error occurred while sending')
+ log.error('error occurred while sending: %s' % error.getErrorMessage())
respond_json_deferred({'message': 'an error occurred while sending'}, request, status_code=422)
deferred = self._handle_post(request)
@@ -185,7 +185,7 @@ class MailsResource(BaseResource):
def render_PUT(self, request):
def onError(error):
- log.error(error, 'error saving draft')
+ log.error('error saving draft: %s' % error.getErrorMessage())
respond_json_deferred("", request, status_code=422)
deferred = self._handle_put(request)
diff --git a/service/test/functional/features/environment.py b/service/test/functional/features/environment.py
index 77efc499..d49016b6 100644
--- a/service/test/functional/features/environment.py
+++ b/service/test/functional/features/environment.py
@@ -13,31 +13,29 @@
#
# You should have received a copy of the GNU Affero General Public License
# along with Pixelated. If not, see <http://www.gnu.org/licenses/>.
-import uuid
import os
+import re
+import time
+from urlparse import urlparse
+import uuid
-from selenium import webdriver
from crochet import setup, wait_for
-
-from twisted.internet import defer
-from twisted.logger import globalLogBeginner, textFileLogObserver, Logger
-
-from test.support.integration import AppTestClient
-from steps.common import *
-
from leap.common.events.server import ensure_server
+from selenium import webdriver
+from twisted.internet import defer
from pixelated.application import UserAgentMode
from pixelated.config.site import PixelatedSite
from pixelated.resources.features_resource import FeaturesResource
+from test.support.integration import AppTestClient
+from steps.common import DEFAULT_IMPLICIT_WAIT_TIMEOUT_IN_S
-setup()
-observers = [textFileLogObserver(open(os.devnull, 'w'))]
+class UnsuportedWebDriverError(Exception):
+ pass
-globalLogBeginner.beginLoggingTo(observers)
-Logger('twisted')
+setup()
@wait_for(timeout=5.0)
@@ -46,61 +44,116 @@ def start_app_test_client(client, mode):
def before_all(context):
+ _setup_webdriver(context)
+ userdata = context.config.userdata
+ context.host = userdata.get('host', 'http://localhost')
+
+ if not context.host.startswith('http'):
+ context.host = 'https://{}'.format(context.host)
+
+ hostname = urlparse(context.host).hostname
+ context.signup_url = 'https://{}/signup'.format(hostname)
+ context.login_url = 'https://mail.{}/login'.format(hostname)
+ context.username = 'testuser_{}'.format(uuid.uuid4())
+
+ if 'localhost' in context.host:
+ _mock_user_agent(context)
+ context.login_url = context.multi_user_url + '/login'
+ context.username = 'username'
+
+
+def _setup_webdriver(context):
+ browser = context.config.userdata.get('webdriver', 'phantomjs')
+ supported_webdrivers = {
+ 'phantomjs': webdriver.PhantomJS,
+ 'firefox': webdriver.Firefox,
+ 'chrome': webdriver.Chrome,
+ }
+
+ try:
+ context.browser = supported_webdrivers[browser]()
+ except KeyError:
+ raise UnsuportedWebDriverError('{} is not a supported webdriver'.format(browser))
+
+ context.browser.set_window_size(1280, 1024)
+ context.browser.implicitly_wait(DEFAULT_IMPLICIT_WAIT_TIMEOUT_IN_S)
+ context.browser.set_page_load_timeout(60)
+
+
+def _mock_user_agent(context):
ensure_server()
PixelatedSite.disable_csp_requests()
- client = AppTestClient()
- start_app_test_client(client, UserAgentMode(is_single_user=True))
- client.listenTCP(port=8889)
FeaturesResource.DISABLED_FEATURES.append('autoRefresh')
- context.client = client
- multi_user_client = AppTestClient()
- start_app_test_client(multi_user_client, UserAgentMode(is_single_user=False))
- multi_user_client.listenTCP(port=MULTI_USER_PORT)
- context.multi_user_client = multi_user_client
+ context.single_user_url = _define_url(8889)
+ context.single_user_client = _start_user_agent(8889, is_single_user=True)
+
+ context.multi_user_url = _define_url(4568)
+ context.multi_user_client = _start_user_agent(4568, is_single_user=False)
+
+
+def _start_user_agent(port, is_single_user):
+ client = AppTestClient()
+ start_app_test_client(client, UserAgentMode(is_single_user=is_single_user))
+ client.listenTCP(port=port)
+ return client
+
+
+def _define_url(port):
+ return 'http://localhost:{port}'.format(port=port)
def after_all(context):
- context.client.stop()
+ context.browser.quit()
+ if 'localhost' in context.host:
+ context.single_user_client.stop()
def before_feature(context, feature):
- # context.browser = webdriver.Chrome()
- # context.browser = webdriver.Firefox()
- context.browser = webdriver.PhantomJS()
- context.browser.set_window_size(1280, 1024)
- context.browser.implicitly_wait(DEFAULT_IMPLICIT_WAIT_TIMEOUT_IN_S)
- context.browser.set_page_load_timeout(60) # wait for data
- context.browser.get(HOMEPAGE_URL)
+ if 'localhost' in context.host:
+ context.browser.get(context.single_user_url)
+
+
+def after_feature(context, feature):
+ if 'localhost' in context.host:
+ cleanup_all_mails(context)
+ context.last_mail = None
def after_step(context, step):
- if step.status == 'failed':
- id = str(uuid.uuid4())
- os.chdir("screenshots")
- context.browser.save_screenshot('failed ' + str(step.name) + '_' + id + ".png")
- save_source(context, 'failed ' + str(step.name) + '_' + id + ".html")
- os.chdir("../")
+ _debug_on_error(context, step)
+ _save_screenshot(context, step)
-def after_feature(context, feature):
- context.browser.quit()
+def _debug_on_error(context, step):
+ if step.status == 'failed' and context.config.userdata.getbool("debug"):
+ try:
+ import ipdb
+ ipdb.post_mortem(step.exc_traceback)
+ except ImportError:
+ import pdb
+ pdb.post_mortem(step.exc_traceback)
- cleanup_all_mails(context)
- context.last_mail = None
+
+def _save_screenshot(context, step):
+ if (step.status == 'failed' and
+ context.config.userdata.getbool("screenshots", True)):
+ timestamp = time.strftime("%Y-%m-%d-%H-%M-%S")
+ filename = _slugify('{} failed {}'.format(timestamp, str(step.name)))
+ filepath = os.path.join('screenshots', filename + '.png')
+ context.browser.save_screenshot(filepath)
+
+
+def _slugify(string_):
+ return re.sub('\W', '-', string_)
@wait_for(timeout=10.0)
def cleanup_all_mails(context):
@defer.inlineCallbacks
def _delete_all_mails():
- mails = yield context.client.mail_store.all_mails()
+ mails = yield context.single_user_client.mail_store.all_mails()
for mail in mails:
- yield context.client.mail_store.delete_mail(mail.ident)
+ yield context.single_user_client.mail_store.delete_mail(mail.ident)
return _delete_all_mails()
-
-
-def save_source(context, filename='/tmp/source.html'):
- with open(filename, 'w') as out:
- out.write(context.browser.page_source.encode('utf8'))
diff --git a/service/test/functional/features/smoke.feature b/service/test/functional/features/smoke.feature
new file mode 100644
index 00000000..724c680c
--- /dev/null
+++ b/service/test/functional/features/smoke.feature
@@ -0,0 +1,36 @@
+#
+# Copyright (c) 2016 ThoughtWorks, Inc.
+#
+# Pixelated is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pixelated is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with Pixelated. If not, see <http://www.gnu.org/licenses/>.
+
+@smoke
+Feature: sign up, login and logout
+ As a visitor of Pixelated
+ I want to sign up
+ So I can log in to my account and see the welcome email
+
+ Scenario: Visitor creates his account
+ Given a user is accessing the signup page
+ When I enter username, password and password confirmation
+ And I click on the signup button
+ Then I should see the user control panel
+
+ Scenario: Existing user logs into his account
+ Given a user is accessing the login page
+ When I enter username and password as credentials
+ And I click on the login button
+ Then I should see the fancy interstitial
+ Then I have mails
+ When I logout
+ Then I should see the login page
diff --git a/service/test/functional/features/steps/attachments.py b/service/test/functional/features/steps/attachments.py
index 28d88343..43948016 100644
--- a/service/test/functional/features/steps/attachments.py
+++ b/service/test/functional/features/steps/attachments.py
@@ -13,16 +13,21 @@
#
# You should have received a copy of the GNU Affero General Public License
# along with Pixelated. If not, see <http://www.gnu.org/licenses/>.
-from email.mime.application import MIMEApplication
-from time import sleep
-from leap.bitmask.mail.mail import Message
-from common import *
-from test.support.integration import MailBuilder
-from behave import given
-from crochet import wait_for
-from uuid import uuid4
from email.MIMEMultipart import MIMEMultipart
+from email.mime.application import MIMEApplication
from email.mime.text import MIMEText
+from uuid import uuid4
+
+from behave import given, then, when
+from crochet import wait_for
+from selenium.webdriver.common.by import By
+
+from common import (
+ fill_by_css_selector,
+ find_element_by_css_selector,
+ find_elements_by_css_selector,
+ page_has_css,
+ wait_until_element_is_visible_by_locator)
@given(u'I have a mail with an attachment in my inbox')
@@ -46,7 +51,7 @@ def build_mail_with_attachment(subject):
@wait_for(timeout=10.0)
def load_mail_into_soledad(context, mail):
- return context.client.mail_store.add_mail('INBOX', mail.as_string())
+ return context.single_user_client.mail_store.add_mail('INBOX', mail.as_string())
@then(u'I see the mail has an attachment')
@@ -107,6 +112,5 @@ def click_remove_icon(context):
@then(u'I should not see it attached')
def assert_attachment_removed(context):
- attachments_list_ul = find_elements_by_css_selector(context, '#attachment-list-item')
attachments_list_li = context.browser.find_elements(By.CSS_SELECTOR, '#attachment-list-item li a')
assert len(attachments_list_li) == 0
diff --git a/service/test/functional/features/steps/common.py b/service/test/functional/features/steps/common.py
index ccad842c..f2cab1a6 100644
--- a/service/test/functional/features/steps/common.py
+++ b/service/test/functional/features/steps/common.py
@@ -13,25 +13,22 @@
#
# You should have received a copy of the GNU Affero General Public License
# along with Pixelated. If not, see <http://www.gnu.org/licenses/>.
+
+import time
+
+from selenium.common.exceptions import (
+ StaleElementReferenceException,
+ TimeoutException)
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
-from selenium.common.exceptions import TimeoutException, StaleElementReferenceException
-import time
-from test.support.integration import MailBuilder
-LOADING = 'loading'
+from test.support.integration import MailBuilder
TIMEOUT_IN_S = 20
DEFAULT_IMPLICIT_WAIT_TIMEOUT_IN_S = 10.0
-HOMEPAGE_URL = 'http://localhost:8889/'
-
-MULTI_USER_PORT = 4568
-
-MULTI_USER_URL = 'http://localhost:%d/' % MULTI_USER_PORT
-
class ImplicitWait(object):
def __init__(self, context, timeout=5.0):
@@ -69,15 +66,9 @@ def wait_until_elements_are_visible_by_locator(context, locator_tuple, timeout=T
return context.browser.find_elements(locator_tuple[0], locator_tuple[1])
-def wait_until_elements_are_visible_by_xpath(context, locator_tuple, timeout=TIMEOUT_IN_S):
- wait = WebDriverWait(context.browser, timeout)
- wait.until(EC.presence_of_all_elements_located(locator_tuple))
- return context.browser.find_elements(locator_tuple[0], locator_tuple[1])
-
-
def wait_until_element_is_visible_by_locator(context, locator_tuple, timeout=TIMEOUT_IN_S):
wait = WebDriverWait(context.browser, timeout)
- wait.until(EC.visibility_of_element_located(locator_tuple))
+ wait.until(EC.presence_of_element_located(locator_tuple))
return context.browser.find_element(locator_tuple[0], locator_tuple[1])
@@ -91,8 +82,8 @@ def fill_by_xpath(context, xpath, text):
field.send_keys(text)
-def fill_by_css_selector(context, css_selector, text):
- field = find_element_by_css_selector(context, css_selector)
+def fill_by_css_selector(context, css_selector, text, timeout=TIMEOUT_IN_S):
+ field = find_element_by_css_selector(context, css_selector, timeout=timeout)
field.send_keys(text)
@@ -100,11 +91,6 @@ def take_screenshot(context, filename):
context.browser.save_screenshot(filename)
-def dump_source_to(context, filename):
- with open(filename, 'w') as out:
- out.write(context.browser.page_source.encode('utf8'))
-
-
def page_has_css(context, css):
try:
find_element_by_css_selector(context, css)
@@ -121,8 +107,8 @@ def find_element_by_id(context, id):
return wait_until_element_is_visible_by_locator(context, (By.ID, id))
-def find_element_by_css_selector(context, css_selector):
- return wait_until_element_is_visible_by_locator(context, (By.CSS_SELECTOR, css_selector))
+def find_element_by_css_selector(context, css_selector, timeout=TIMEOUT_IN_S):
+ return wait_until_element_is_visible_by_locator(context, (By.CSS_SELECTOR, css_selector), timeout=timeout)
def find_element_by_class_name(context, class_name):
@@ -133,8 +119,8 @@ def find_elements_by_css_selector(context, css_selector, timeout=TIMEOUT_IN_S):
return wait_until_elements_are_visible_by_locator(context, (By.CSS_SELECTOR, css_selector), timeout=timeout)
-def find_elements_by_xpath(context, xpath):
- return wait_until_elements_are_visible_by_xpath(context, (By.XPATH, xpath))
+def find_elements_by_xpath(context, xpath, timeout=TIMEOUT_IN_S):
+ return wait_until_elements_are_visible_by_locator(context, (By.XPATH, xpath), timeout=timeout)
def find_element_containing_text(context, text, element_type='*'):
@@ -186,9 +172,9 @@ def get_console_log(context):
for entry in logs:
msg = entry['message']
if not (msg.startswith('x off') or msg.startswith('<- on')):
- print entry['message']
+ print(entry['message'])
def create_email(context):
input_mail = MailBuilder().build_input_mail()
- context.client.add_mail_to_inbox(input_mail)
+ context.single_user_client.add_mail_to_inbox(input_mail)
diff --git a/service/test/functional/features/steps/compose.py b/service/test/functional/features/steps/compose.py
index 67b1bd51..c72b25e2 100644
--- a/service/test/functional/features/steps/compose.py
+++ b/service/test/functional/features/steps/compose.py
@@ -16,7 +16,12 @@
from time import sleep
from behave import when
-from common import *
+from selenium.webdriver.common.by import By
+
+from common import (
+ fill_by_css_selector,
+ wait_until_element_is_visible_by_locator,
+ find_element_by_css_selector)
@when('I compose a message with')
diff --git a/service/test/functional/features/steps/data_setup.py b/service/test/functional/features/steps/data_setup.py
index 167acf9a..3d28baed 100644
--- a/service/test/functional/features/steps/data_setup.py
+++ b/service/test/functional/features/steps/data_setup.py
@@ -26,8 +26,8 @@ def add_mail_impl(context):
input_mail = MailBuilder().with_subject(subject).build_input_mail()
- context.client.add_mail_to_inbox(input_mail)
- wait_for_condition(context, lambda _: context.client.search_engine.search(subject)[1] > 0, poll_frequency=0.1)
+ context.single_user_client.add_mail_to_inbox(input_mail)
+ wait_for_condition(context, lambda _: context.single_user_client.search_engine.search(subject)[1] > 0, poll_frequency=0.1)
context.last_subject = subject
diff --git a/service/test/functional/features/steps/login.py b/service/test/functional/features/steps/login.py
index 2a653030..b5e88608 100644
--- a/service/test/functional/features/steps/login.py
+++ b/service/test/functional/features/steps/login.py
@@ -15,49 +15,45 @@
# along with Pixelated. If not, see <http://www.gnu.org/licenses/>.
import time
-from behave import when, then
-from selenium.webdriver.common.by import By
+from behave import given, when, then
from common import (
fill_by_css_selector,
- find_element_by_css_selector,
- MULTI_USER_URL,
- wait_until_element_is_visible_by_locator)
+ find_element_by_css_selector)
+@given(u'a user is accessing the login page')
@when(u'I open the login page')
def login_page(context):
- context.browser.get(MULTI_USER_URL + '/login')
+ context.browser.get(context.login_url)
@when(u'I enter {username} and {password} as credentials')
def enter_credentials(context, username, password):
- fill_by_css_selector(context, 'input#email', username)
+ fill_by_css_selector(context, 'input#email', context.username)
fill_by_css_selector(context, 'input#password', password)
@when(u'I click on the login button')
def click_login(context):
- login_button = wait_until_element_is_visible_by_locator(context, (By.CSS_SELECTOR, 'input[name="login"]'))
- login_button.click()
+ find_element_by_css_selector(context, 'input[name="login"]').click()
@then(u'I should see the fancy interstitial')
def step_impl(context):
- assert find_element_by_css_selector(context, 'section#hive-section')
+ find_element_by_css_selector(context, 'section#hive-section')
_wait_for_interstitial_to_reload()
def _wait_for_interstitial_to_reload():
- time.sleep(6)
+ time.sleep(10)
@when(u'I logout')
def click_logout(context):
- logout_button = wait_until_element_is_visible_by_locator(context, (By.CSS_SELECTOR, 'ul#logout'))
- logout_button.click()
+ find_element_by_css_selector(context, '#logout-form div').click()
@then(u'I should see the login page')
def see_login_page(context):
- assert find_element_by_css_selector(context, 'form#login_form')
+ find_element_by_css_selector(context, 'form#login_form')
diff --git a/service/test/functional/features/steps/mail_list.py b/service/test/functional/features/steps/mail_list.py
index 82faa7af..23724bf2 100644
--- a/service/test/functional/features/steps/mail_list.py
+++ b/service/test/functional/features/steps/mail_list.py
@@ -13,12 +13,25 @@
#
# You should have received a copy of the GNU Affero General Public License
# along with Pixelated. If not, see <http://www.gnu.org/licenses/>.
-from common import *
-from selenium.common.exceptions import NoSuchElementException
+
+from behave import when, then, given
+from selenium.common.exceptions import TimeoutException
+from selenium.webdriver.common.by import By
+
+from common import (
+ ImplicitWait,
+ execute_ignoring_staleness,
+ find_element_by_id,
+ find_element_by_css_selector,
+ mail_list_with_subject_exists,
+ wait_for_condition,
+ wait_for_loading_to_finish,
+ wait_until_element_is_visible_by_locator,
+ wait_until_elements_are_visible_by_locator)
def find_current_mail(context):
- print 'searching for mail [%s]' % context.current_mail_id
+ print('searching for mail [%s]' % context.current_mail_id)
return find_element_by_id(context, '%s' % context.current_mail_id)
@@ -78,31 +91,28 @@ def impl(context):
@given('I have mails')
@then(u'I have mails')
def impl(context):
- emails = wait_until_elements_are_visible_by_locator(context, (By.CSS_SELECTOR, '.mail-list-entry__item'))
+ emails = wait_until_elements_are_visible_by_locator(context, (By.CSS_SELECTOR, '.mail-list-entry'))
assert len(emails) > 0
@when('I mark the first unread email as read')
def impl(context):
- emails = wait_until_elements_are_visible_by_locator(context, (By.CSS_SELECTOR, '.mail-list-entry'))
+ mail_id = find_element_by_css_selector(
+ context, '.mail-list-entry:not(.status-read)').get_attribute('id')
- for email in emails:
- if 'status-read' not in email.get_attribute('class'):
- context.current_mail_id = email.get_attribute('id') # we need to get the mail id before manipulating the page
- email.find_element_by_tag_name('input').click()
- find_element_by_id(context, 'mark-selected-as-read').click()
- break
- wait_until_elements_are_visible_by_locator(context, (By.CSS_SELECTOR, '#%s.status-read' % context.current_mail_id))
+ find_element_by_css_selector(context, '#%s input' % mail_id).click()
+ find_element_by_id(context, 'mark-selected-as-read').click()
+
+ find_element_by_css_selector(context, '#%s.status-read' % mail_id)
@when('I delete the email')
def impl(context):
- def last_email():
- return wait_until_element_is_visible_by_locator(context, (By.CSS_SELECTOR, '.mail-list-entry'))
- mail = last_email()
- context.current_mail_id = mail.get_attribute('id')
- mail.find_element_by_tag_name('input').click()
+ mail_id = find_element_by_css_selector(context, '.mail-list-entry').get_attribute('id')
+
+ find_element_by_css_selector(context, '#%s input' % mail_id).click()
find_element_by_id(context, 'delete-selected').click()
+
_wait_for_mail_list_to_be_empty(context)
diff --git a/service/test/functional/features/steps/mail_view.py b/service/test/functional/features/steps/mail_view.py
index 2db6dfc5..88ee5c5a 100644
--- a/service/test/functional/features/steps/mail_view.py
+++ b/service/test/functional/features/steps/mail_view.py
@@ -13,8 +13,18 @@
#
# You should have received a copy of the GNU Affero General Public License
# along with Pixelated. If not, see <http://www.gnu.org/licenses/>.
+
+from behave import then, when
from selenium.webdriver.common.keys import Keys
-from common import *
+from selenium.webdriver.common.by import By
+
+from common import (
+ click_button,
+ find_element_by_css_selector,
+ find_elements_by_css_selector,
+ reply_subject,
+ wait_until_button_is_visible,
+ wait_until_element_is_visible_by_locator)
@then('I see that the subject reads \'{subject}\'')
diff --git a/service/test/functional/features/steps/search.py b/service/test/functional/features/steps/search.py
index 879e834d..5a6d2d1c 100644
--- a/service/test/functional/features/steps/search.py
+++ b/service/test/functional/features/steps/search.py
@@ -13,10 +13,12 @@
#
# You should have received a copy of the GNU Affero General Public License
# along with Pixelated. If not, see <http://www.gnu.org/licenses/>.
-from selenium.webdriver import ActionChains
+from behave import when, then
+from selenium.webdriver import ActionChains
from selenium.webdriver.common.keys import Keys
-from common import *
+
+from common import find_element_by_css_selector, find_elements_by_css_selector
@when('I search for a mail with the words "{search_term}"')
diff --git a/service/test/functional/features/steps/signup.py b/service/test/functional/features/steps/signup.py
new file mode 100644
index 00000000..43480666
--- /dev/null
+++ b/service/test/functional/features/steps/signup.py
@@ -0,0 +1,44 @@
+#
+# Copyright (c) 2016 ThoughtWorks, Inc.
+#
+# Pixelated is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pixelated is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with Pixelated. If not, see <http://www.gnu.org/licenses/>.
+
+from behave import given, then, when
+
+from common import (
+ element_should_have_content,
+ fill_by_css_selector,
+ find_element_by_css_selector)
+
+
+@given(u'a user is accessing the signup page') # noqa
+def step_impl(context):
+ context.browser.get(context.signup_url)
+
+
+@when(u'I enter username, password and password confirmation') # noqa
+def step_impl(context):
+ fill_by_css_selector(context, '#srp_username', context.username)
+ fill_by_css_selector(context, '#srp_password', 'password')
+ fill_by_css_selector(context, '#srp_password_confirmation', 'password')
+
+
+@when(u'I click on the signup button') # noqa
+def step_impl(context):
+ find_element_by_css_selector(context, 'button[type=submit]').click()
+
+
+@then(u'I should see the user control panel') # noqa
+def step_impl(context):
+ element_should_have_content(context, 'h1', 'user control panel')
diff --git a/service/test/functional/features/steps/tag_list.py b/service/test/functional/features/steps/tag_list.py
index 8550a886..b9adea0a 100644
--- a/service/test/functional/features/steps/tag_list.py
+++ b/service/test/functional/features/steps/tag_list.py
@@ -13,7 +13,15 @@
#
# You should have received a copy of the GNU Affero General Public License
# along with Pixelated. If not, see <http://www.gnu.org/licenses/>.
-from common import *
+from behave import when
+from selenium.common.exceptions import TimeoutException
+from selenium.webdriver.common.by import By
+
+from common import (
+ find_element_by_class_name,
+ find_element_by_id,
+ wait_for_user_alert_to_disapear,
+ wait_until_element_is_visible_by_locator)
def click_first_element_with_class(context, classname):
diff --git a/service/test/integration/test_multi_user_login.py b/service/test/integration/test_multi_user_login.py
index e1f58202..2008b320 100644
--- a/service/test/integration/test_multi_user_login.py
+++ b/service/test/integration/test_multi_user_login.py
@@ -53,4 +53,4 @@ class MultiUserLoginTest(MultiUserSoledadTestBase):
response, login_request = self.app_test_client.login('username', 'wrong_password', session=first_request.getSession())
response_str = yield response
self.assertEqual(401, login_request.responseCode)
- self.assertIn('Invalid credentials', login_request.written)
+ self.assertIn('Invalid username or password', login_request.written)
diff --git a/service/test/unit/adapter/services/test_mail_sender.py b/service/test/unit/adapter/services/test_mail_sender.py
index 4daa7f24..23951214 100644
--- a/service/test/unit/adapter/services/test_mail_sender.py
+++ b/service/test/unit/adapter/services/test_mail_sender.py
@@ -14,6 +14,7 @@
# You should have received a copy of the GNU Affero General Public License
# along with Pixelated. If not, see <http://www.gnu.org/licenses/>.
from leap.bitmask.mail.outgoing.service import OutgoingMail
+from mock import patch
from twisted.mail.smtp import User
from twisted.trial import unittest
@@ -104,6 +105,25 @@ class MailSenderTest(unittest.TestCase):
self.assertTrue(recipient in e.email_error_map)
@defer.inlineCallbacks
+ def test_keymanager_encrypt_problem_raises_exception(self):
+ input_mail = InputMail.from_dict(mail_dict(), from_address='pixelated@org')
+
+ when(OutgoingMail)._maybe_attach_key(any(), any(), any()).thenReturn(
+ defer.succeed(None))
+ when(OutgoingMail)._fix_headers(any(), any(), any()).thenReturn(
+ defer.succeed((None, mock())))
+ when(self._keymanager_mock).encrypt(any(), any(), sign=any(),
+ fetch_remote=any()).thenReturn(defer.fail(Exception('pretend key expired')))
+
+ with patch('leap.bitmask.mail.outgoing.service.emit_async'):
+ try:
+ yield self.sender.sendmail(input_mail)
+ self.fail('Exception expected!')
+ except MailSenderException, e:
+ for recipient in flatten([input_mail.to, input_mail.cc, input_mail.bcc]):
+ self.assertTrue(recipient in e.email_error_map)
+
+ @defer.inlineCallbacks
def test_iterates_over_recipients_and_send_whitout_bcc_field(self):
input_mail = InputMail.from_dict(mail_dict(), from_address='pixelated@org')
bccs = input_mail.bcc
diff --git a/service/test/unit/resources/test_login_resource.py b/service/test/unit/resources/test_login_resource.py
index fcf76f5a..1161acc4 100644
--- a/service/test/unit/resources/test_login_resource.py
+++ b/service/test/unit/resources/test_login_resource.py
@@ -219,7 +219,7 @@ class TestLoginPOST(unittest.TestCase):
mock_authenticate.assert_called_once_with(self.username, self.password)
self.assertEqual(401, self.request.responseCode)
written_response = ''.join(self.request.written)
- self.assertIn('Invalid credentials', written_response)
+ self.assertIn('Invalid username or password', written_response)
self.assertFalse(mock_user_bootstrap_setup.called)
self.assertFalse(self.resource.get_session(self.request).is_logged_in())
diff --git a/service/test_requirements.txt b/service/test_requirements.txt
index b6b704c7..ae9443dd 100644
--- a/service/test_requirements.txt
+++ b/service/test_requirements.txt
@@ -1,5 +1,5 @@
-behave==1.2.4
-selenium==2.44.0
+behave==1.2.5
+selenium==3.0.1
mock==2.0.0
httmock==1.2.2
mockito==0.5.2