diff --git a/.gitignore b/.gitignore index aaebf0f..30b9538 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ baseframe-packed.js *.sql.bz2 secrets.dev secrets.test +ghostdriver.log diff --git a/features/application.feature b/features/application.feature new file mode 100755 index 0000000..4b8eb74 --- /dev/null +++ b/features/application.feature @@ -0,0 +1,9 @@ +Feature: Add a client application + Scenario: login an existing user and add a client application + Given we have an existing user + and the user is logged in + when the user visits the client application page + then the user can add a new client application + and the user can edit the new client application + and the user can add a new organization + and the user profile page lists new application and organization diff --git a/features/environment.py b/features/environment.py index 0a9335f..841d30f 100644 --- a/features/environment.py +++ b/features/environment.py @@ -3,6 +3,7 @@ from selenium import webdriver from lastuser_core.models import db from lastuserapp import app +import selenium.webdriver.support.ui as ui server_name = app.config.get('SERVER_NAME') or 'localhost:7001' base_url = 'http://%s' % server_name @@ -15,8 +16,20 @@ def before_all(context): context.thread = threading.Thread(target=context.server.serve_forever) context.thread.start() context.browser = webdriver.PhantomJS() + context.browser.implicitly_wait(2) # waits 2 second if it cannot find an element immediately context.browser.visit = lambda url: context.browser.get(base_url + url) + context.test_user = dict( + fullname='Alyssa P Hacker', + email='alyssa@hacker.com', + username='alyssa', + password='alyssa', + confirm_password='alyssa', + test_new_password='alyssatest' + ) + + context.wait = ui.WebDriverWait(context.browser, 2) + def after_all(context): # Explicitly quits the browser, otherwise it won't once tests are done @@ -25,14 +38,11 @@ def after_all(context): context.browser.quit() -def after_step(context, step): - context.browser.delete_all_cookies() - - def before_feature(context, feature): db.create_all() def after_feature(context, feature): + context.browser.delete_all_cookies() db.session.commit() db.drop_all() diff --git a/features/profile_edit.feature b/features/profile_edit.feature new file mode 100755 index 0000000..ddc38d6 --- /dev/null +++ b/features/profile_edit.feature @@ -0,0 +1,8 @@ +Feature: Open user profile page + Scenario: login an existing user and open profile page + Given we have an existing user + and the user is logged in + when the user visits their profile page + then the user can see their details + and the user can edit their profile + and the user can change their password diff --git a/features/steps/application.py b/features/steps/application.py new file mode 100755 index 0000000..ea6536c --- /dev/null +++ b/features/steps/application.py @@ -0,0 +1,90 @@ +from utils import get_namespace_from_website + + +@when('the user visits the client application page') +def when_visit_app_page(context): + context.browser.visit('/apps/new') + context.test_app_name = 'testappname' + context.test_app_description = 'this is a test app' + context.test_app_website = 'http://test.auth.hasgeek.com' + context.test_app_redirect_uri = 'http://test.auth.hasgeek.com/redirect' + context.test_app_notification_uri = 'http://test.auth.hasgeek.com/notification' + context.test_app_iframe_uri = 'http://test.auth.hasgeek.com/iframe' + context.test_app_namespace = get_namespace_from_website(context.test_app_website) + context.test_org_title = 'testorgtitle' + context.test_org_name = 'testorgname' + + +@then('the user can add a new client application') +def then_new_app(context): + context.browser.find_element_by_id('title').send_keys(context.test_app_name) + context.browser.find_element_by_id('description').send_keys(context.test_app_description) + context.browser.find_element_by_id('website').send_keys(context.test_app_website) + context.browser.find_element_by_id('namespace').send_keys(context.test_app_namespace) + context.browser.find_element_by_id('redirect_uri').send_keys(context.test_app_redirect_uri) + context.browser.find_element_by_id('notification_uri').send_keys(context.test_app_notification_uri) + context.browser.find_element_by_id('iframe_uri').send_keys(context.test_app_iframe_uri) + context.browser.find_element_by_id('title').submit() + infoboxes = context.browser.find_elements_by_class_name('infobox') + + # print(context.browser.current_url) + # http://localhost:7001/apps/h6YvCJUtShOkIpkISWZQQQ + assert '/apps/new' not in context.browser.current_url + assert '/apps' in context.browser.current_url + + context.test_app_id = context.browser.current_url.split('/')[-1] + + assert len(infoboxes) > 0 + + infobox = infoboxes[0] + + assert context.test_app_name in infobox.text + assert context.test_app_description in infobox.text + assert context.test_app_website in infobox.text + assert context.test_app_redirect_uri in infobox.text + assert context.test_app_notification_uri in infobox.text + assert context.test_app_iframe_uri in infobox.text + assert context.test_app_namespace in infobox.text + + +@then('the user can edit the new client application') +def then_edit_app(context): + context.browser.visit('/apps/{id}/edit'.format(id=context.test_app_id)) + context.browser.find_element_by_id('title').clear() + context.browser.find_element_by_id('title').send_keys(context.test_app_name + 'test') + context.browser.find_element_by_id('title').submit() + + infoboxes = context.browser.find_elements_by_class_name('infobox') + + assert len(infoboxes) > 0 + infobox = infoboxes[0] + + assert context.test_app_name + 'test' in infobox.text + + +@then('the user can add a new organization') +def then_add_org(context): + context.browser.visit('/organizations/new') + context.browser.find_element_by_id('title').send_keys(context.test_org_title) + context.browser.find_element_by_id('name').send_keys(context.test_org_name) + context.browser.find_element_by_id('title').submit() + + new_team_buttons = context.browser.find_elements_by_xpath( + '//a[contains(@href, "/organizations/{name}/teams/new")]'.format(name=context.test_org_name)) + + assert len(new_team_buttons) > 0 + new_team_button = new_team_buttons[0] + + assert "New team" in new_team_button.text + + headers = context.browser.find_elements_by_xpath( + '//div[@class="page-header"]//h1[contains(text(), "Organization: {title}")]'.format(title=context.test_org_title)) + assert len(headers) > 0 + + +@then('the user profile page lists new application and organization') +def then_things_in_profile(context): + context.browser.visit('/profile') + container = context.browser.find_element_by_id('main-content') + assert context.test_app_name in container.text + assert context.test_org_title in container.text diff --git a/features/steps/failed_login.py b/features/steps/failed_login.py index 8282554..2451718 100644 --- a/features/steps/failed_login.py +++ b/features/steps/failed_login.py @@ -1,6 +1,7 @@ from flask import g from behave import when, then, given from lastuserapp import app +from .utils import login_user @given("we do not know that user") @@ -13,12 +14,10 @@ def given_existing_user(context): @when("the nonexisting user tries to log in") def when_login_form_submit(context): - context.test_user['form.id'] = "passwordlogin" - with app.test_client() as c: - c.post('/login', data=context.test_user, follow_redirects=True) - context.user = g.user + login_user(context, context.test_user) @then("we do not log the user in") def user_login(context): - assert context.user is None + wrong_password_error = context.browser.find_elements_by_xpath('//p[@class="help-error" and contains(text(), "User does not exist")]') + assert len(wrong_password_error) == 1 diff --git a/features/steps/failed_registration.py b/features/steps/failed_registration.py index 21421e0..4c31c62 100755 --- a/features/steps/failed_registration.py +++ b/features/steps/failed_registration.py @@ -2,25 +2,12 @@ from behave import given, when, then from lastuser_core.models import User from lastuserapp import app +from utils import register_test_user @given('a new user trying to register with a used username') def given_new_user(context): - context.test_user = dict( - fullname='Alyssa P Hacker', - email='alyssa@hacker.com', - username='alyssa', - password='alyssa', - confirm_password='alyssa' - ) - # registering the test user - context.browser.visit('/register') - assert context.browser.find_element_by_name('csrf_token').is_enabled() - for k, v in context.test_user.iteritems(): - context.browser.find_element_by_name(k).send_keys(v) - - register_form = context.browser.find_element_by_id('register') - register_form.submit() + register_test_user(context) @when('this new user submits the registration form with a username that has already been used') @@ -30,7 +17,8 @@ def when_form_submit(context): context.browser.visit('/register') assert context.browser.find_element_by_name('csrf_token').is_enabled() for k, v in context.test_user.iteritems(): - context.browser.find_element_by_name(k).send_keys(v) + if not k.startswith('test_'): + context.browser.find_element_by_name(k).send_keys(v) register_form = context.browser.find_element_by_id('register') register_form.submit() diff --git a/features/steps/login.py b/features/steps/login.py index 755d563..45c994c 100644 --- a/features/steps/login.py +++ b/features/steps/login.py @@ -1,46 +1,18 @@ from behave import when, then, given -import selenium.webdriver.support.ui as ui +from utils import register_test_user, login_test_user @given("we have an existing user") def given_existing_user(context): - context.test_user = dict( - fullname='Alyssa P Hacker', - email='alyssa@hacker.com', - username='alyssa', - password='alyssa', - confirm_password='alyssa' - ) - - context.browser.visit('/register') - assert context.browser.find_element_by_name('csrf_token').is_enabled() - for k, v in context.test_user.iteritems(): - context.browser.find_element_by_name(k).send_keys(v) - - register_form = context.browser.find_element_by_id('register') - register_form.submit() + register_test_user(context) @when("the user tries to log in") def when_login_form_submit(context): - context.login_data = { - 'username': context.test_user['username'], - 'password': context.test_user['password'] - } - wait = ui.WebDriverWait(context.browser, 2) - - context.browser.visit('/login') - assert context.browser.find_element_by_name('csrf_token').is_enabled() - - context.browser.find_element_by_id('showmore').click() - for k, v in context.login_data.iteritems(): - context.browser.find_element_by_name(k).send_keys(v) - - context.browser.find_element_by_name('username').submit() - - context.user_button = wait.until(lambda browser: browser.find_element_by_id('hg-user-btn')) + login_test_user(context) + context.wait.until(lambda browser: browser.find_element_by_id('hg-user-btn')) @then("we log the user in") def user_login(context): - assert context.user_button.is_enabled() + assert context.browser.find_element_by_id('hg-user-btn').is_enabled() diff --git a/features/steps/profile_edit.py b/features/steps/profile_edit.py new file mode 100644 index 0000000..9109ab6 --- /dev/null +++ b/features/steps/profile_edit.py @@ -0,0 +1,68 @@ +from flask import g +from behave import when, then, given +from lastuserapp import app +from utils import login_test_user, login_user + + +# @given("we have an existing user") +# already defined in login.py + + +@given("the user is logged in") +def user_logs_in(context): + login_test_user(context) + context.wait.until(lambda browser: browser.find_element_by_id('hg-user-btn')) + assert context.browser.find_element_by_id('hg-user-btn').is_enabled() + + +@when("the user visits their profile page") +def when_login_form_submit(context): + context.browser.visit('/profile') + + +@then("the user can see their details") +def visit_profile_page(context): + infoboxes = context.browser.find_elements_by_class_name('infobox') + assert len(infoboxes) > 0 + infobox = infoboxes[0] + assert context.test_user['username'] in infobox.text + + +@then("the user can edit their profile") +def edit_profile(context): + context.browser.visit('/profile/edit') + context.browser.find_element_by_name("fullname").clear() + context.browser.find_element_by_name("fullname").send_keys(context.test_user["fullname"] + "test") + context.browser.find_element_by_name("username").clear() + context.browser.find_element_by_name("username").send_keys(context.test_user["username"] + "test") + context.browser.find_element_by_name("fullname").submit() + + infoboxes = context.browser.find_elements_by_class_name('infobox') + assert len(infoboxes) > 0 + infobox = infoboxes[0] + assert context.test_user['username'] + "test" in infobox.text + + +@then("the user can change their password") +def change_password(context): + context.browser.visit('/profile/password') + # context.browser.save_screenshot('pre-password.jpg') + context.browser.find_element_by_name("old_password").send_keys(context.test_user["password"]) + context.browser.find_element_by_name("password").send_keys(context.test_user["test_new_password"]) + context.browser.find_element_by_name("confirm_password").send_keys(context.test_user["test_new_password"]) + context.browser.find_element_by_name("password").submit() + + alert = context.browser.find_elements_by_xpath("//div[@class='alert alert-success fade in']") + assert len(alert) == 1 + assert 'Your new password has been saved' in context.browser.page_source + + # now let's logout and login again + context.browser.delete_all_cookies() + login_user(context, dict(username=context.test_user["username"] + "test", password=context.test_user["test_new_password"])) + assert context.browser.find_element_by_id('hg-user-btn').is_enabled() + + # now let's logout and try logging in again with previous password + context.browser.delete_all_cookies() + login_user(context, dict(username=context.test_user["username"] + "test", password=context.test_user["password"])) + wrong_password_error = context.browser.find_elements_by_xpath('//p[@class="help-error" and contains(text(), "Incorrect password")]') + assert len(wrong_password_error) == 1 diff --git a/features/steps/registration.py b/features/steps/registration.py index 77b5912..29ecdae 100755 --- a/features/steps/registration.py +++ b/features/steps/registration.py @@ -1,28 +1,17 @@ from behave import given, when, then from lastuser_core.models import User +from utils import register_test_user @given('we have a new user') def given_new_user(context): - context.test_user = dict( - fullname='Alyssa P Hacker', - email='alyssa@hacker.com', - username='alyssa', - password='alyssa', - confirm_password='alyssa' - ) + # context.test_user already exists in environment.py + pass @when('a new user submits the registration form with the proper details') def when_form_submit(context): - context.browser.visit('/register') - - assert context.browser.find_element_by_name('csrf_token').is_enabled() - for k, v in context.test_user.iteritems(): - context.browser.find_element_by_name(k).send_keys(v) - - register_form = context.browser.find_element_by_id('register') - register_form.submit() + register_test_user(context) @then('the new user will be registered') diff --git a/features/steps/utils.py b/features/steps/utils.py new file mode 100644 index 0000000..4a095d7 --- /dev/null +++ b/features/steps/utils.py @@ -0,0 +1,47 @@ +def register_test_user(context, clear_cookies=True): + register_user(context, context.test_user, clear_cookies) + + +def register_user(context, user_dict, clear_cookies=True): + context.browser.visit('/register') + assert context.browser.find_element_by_name('csrf_token').is_enabled() + for k, v in user_dict.iteritems(): + if not k.startswith('test_'): + context.browser.find_element_by_name(k).send_keys(v) + + register_form = context.browser.find_element_by_id('register') + register_form.submit() + + context.wait.until(lambda browser: browser.find_element_by_id('hg-user-btn')) + + if clear_cookies: + context.browser.delete_all_cookies() + + +def login_test_user(context): + login_user(context, context.test_user) + + +def login_user(context, user_dict): + context.login_data = dict( + username=user_dict['username'], + password=user_dict['password'] + ) + + context.browser.visit('/login') + assert context.browser.find_element_by_name('csrf_token').is_enabled() + + context.browser.find_element_by_id('showmore').click() + for k, v in context.login_data.iteritems(): + context.browser.find_element_by_name(k).send_keys(v) + + context.browser.find_element_by_name('username').submit() + + +def get_namespace_from_website(website): + from urlparse import urlparse + + parsed_url = urlparse(website) + netloc_reversed = parsed_url.netloc.split('.') + netloc_reversed.reverse() + return '.'.join(netloc_reversed)