View
@@ -1,19 +1,10 @@
imports = [
'ngResource'
'h.flash'
'h.helpers'
]
# bw compat
sessionPersonaInterceptor = (response) ->
data = response.data
if angular.isObject(data.persona)
persona = data.persona
data.persona = "acct:#{persona.username}@#{persona.provider}"
data.personas = for persona in data.personas
"acct:#{persona.username}@#{persona.provider}"
response
ACTION = [
'login'
'logout'
@@ -26,17 +17,19 @@ ACTION_OPTION =
load:
method: 'GET'
withCredentials: true
interceptor:
response: sessionPersonaInterceptor
for action in ACTION
ACTION_OPTION[action] =
method: 'POST'
params:
__formid__: action
withCredentials: true
interceptor:
response: sessionPersonaInterceptor
# Global because $resource doesn't support request interceptors, so a
# the default http request interceptor and the session resource interceptor
# need to share it.
csrfToken = null
# Class providing a server-side session resource.
@@ -67,16 +60,67 @@ class SessionProvider
@options = {}
$get: [
'$resource', 'baseURI'
($resource, baseURI) ->
'$q', '$resource', 'baseURI', 'flash',
($q, $resource, baseURI, flash) ->
actions = {}
_process = (response) ->
data = response.data
model = data.model
# bw compat
if angular.isObject(data.persona)
persona = data.persona
data.persona = "acct:#{persona.username}@#{persona.provider}"
data.personas = for persona in data.personas
"acct:#{persona.username}@#{persona.provider}"
# end bw compat
# Fire flash messages.
for q, msgs of data.flash
flash q, msgs
# Capture the cross site request forgery token without cookies.
# If cookies are blocked this is our only way to get it.
csrfToken = model.csrf
delete model.csrf
# Lift the model object so it becomes the response data.
# Return the response or a rejected response.
if data.status is 'failure'
flash 'error', data.reason
$q.reject(data.errors)
else
model
for name, options of ACTION_OPTION
actions[name] = angular.extend {}, options, @options
actions[name].interceptor =
response: _process
responseError: _process
model = $resource("#{baseURI}app", {}, actions).load()
$resource("#{baseURI}app", {}, actions).load()
]
angular.module('h.session', imports)
configure = ['$httpProvider', ($httpProvider) ->
defaults = $httpProvider.defaults
# Use the Pyramid XSRF header name
defaults.xsrfHeaderName = 'X-CSRF-Token'
$httpProvider.interceptors.push ['baseURI', (baseURI) ->
request: (config) ->
if config.url.match("#{baseURI}app")?.index == 0
# Set the cross site request forgery token
cookieName = config.xsrfCookieName || defaults.xsrfCookieName
headerName = config.xsrfHeaderName || defaults.xsrfHeaderName
config.headers[headerName] ?= csrfToken
config
]
]
angular.module('h.session', imports, configure)
.provider('session', SessionProvider)
View
@@ -4,16 +4,16 @@
<div class="topbar"
ng-class="frame.visible && 'shown'"
ng-mouseenter="showViewSort(true, true)">
<div class="inner" ng-switch="!!auth.persona">
<div class="inner" ng-switch="!!model.persona">
<a class="pull-right" href=""
ng-click="sheet.collapsed = false;
sheet.tab = 'login';"
ng-switch-when="false"
ng-show="sheet.collapsed">Sign in</a>
<div class="pull-right user-picker"
ng-switch-default
data-user-picker-model="auth.persona"
data-user-picker-options="auth.personas">
data-user-picker-model="model.persona"
data-user-picker-options="model.personas">
</div>
<!-- Searchbar -->
View
@@ -8,6 +8,9 @@
"clean-css": "2.2.2",
"uglify-js": "2.4.14"
},
"devDependencies": {
"phantomjs": "1.9.7-10"
},
"engines": {
"node": "0.10.x"
},
View
@@ -16,7 +16,6 @@
API.
"""
import os
import unittest
from selenium import webdriver
@@ -28,31 +27,9 @@
class SeleniumTestCase(unittest.TestCase):
def setUp(self):
self.base_url = "http://localhost:4000"
env = os.environ
if 'SAUCE_USERNAME' in env and 'SAUCE_ACCESS_KEY' in env:
username = env['SAUCE_USERNAME']
key = env['SAUCE_ACCESS_KEY']
caps = webdriver.DesiredCapabilities.FIREFOX
caps['name'] = str(self)
caps['platform'] = "Linux"
caps['build'] = env['TRAVIS_BUILD_NUMBER']
caps['tags'] = [env['TRAVIS_PYTHON_VERSION'], 'CI']
caps['tunnel-identifier'] = env['TRAVIS_JOB_NUMBER']
hub_url = 'http://%s:%s@localhost:4445/wd/hub' % (username, key)
self.driver = webdriver.Remote(
desired_capabilities=caps,
command_executor=hub_url
)
self.sauce_url = 'https://saucelabs.com/jobs/{0}'.format(
self.driver.session_id
)
else:
self.driver = webdriver.Firefox()
self.driver = webdriver.PhantomJS('./node_modules/.bin/phantomjs')
self.driver.implicitly_wait(10)
self.driver.maximize_window()
self.verificationErrors = []
self.accept_next_alert = True
@@ -65,20 +42,22 @@ def login(self):
with Annotator(driver):
# Find the signin link and click it
signin = driver.find_element_by_link_text("Sign in")
ec = expected_conditions.visibility_of(signin)
WebDriverWait(driver, 30).until(ec)
signin.click()
# Find the authentication form sheet
auth = driver.find_element_by_class_name('sheet')
# Find the login pane
form = auth.find_element_by_name('login')
ec = expected_conditions.visibility_of(form)
WebDriverWait(driver, 30).until(ec)
username = form.find_element_by_name('username')
username.clear()
username.send_keys("test")
password = form.find_element_by_name('password')
password.clear()
password.send_keys("test")
form.submit()
@@ -88,7 +67,7 @@ def logout(self):
with Annotator(driver):
picker = (By.CLASS_NAME, 'user-picker')
ec = expected_conditions.visibility_of_element_located(picker)
WebDriverWait(self.driver, 3).until(ec)
WebDriverWait(self.driver, 30).until(ec)
picker = driver.find_element_by_class_name('user-picker')
dropdown = picker.find_element_by_class_name('dropdown-toggle')
@@ -101,6 +80,8 @@ def register(self):
with Annotator(driver):
# Find the signin link and click it
signin = driver.find_element_by_link_text("Sign in")
ec = expected_conditions.visibility_of(signin)
WebDriverWait(driver, 30).until(ec)
signin.click()
# Find the authentication form sheet
@@ -111,24 +92,23 @@ def register(self):
# Get the registration pane
form = auth.find_element_by_name('register')
ec = expected_conditions.visibility_of(form)
WebDriverWait(driver, 30).until(ec)
username = form.find_element_by_name('username')
username.clear()
username.send_keys("test")
email = form.find_element_by_name('email')
email.clear()
email.send_keys("test@example.org")
password = form.find_element_by_name('password')
password.clear()
password.send_keys("test")
form.submit()
picker = (By.CLASS_NAME, 'user-picker')
ec = expected_conditions.visibility_of_element_located(picker)
WebDriverWait(self.driver, 3).until(ec)
WebDriverWait(self.driver, 30).until(ec)
def highlight(self, css_selector):
"""A hack to select some text on the page, and trigger the
@@ -177,7 +157,7 @@ def __enter__(self):
ec = expected_conditions.frame_to_be_available_and_switch_to_it(
'hyp_sidebar_frame')
WebDriverWait(driver, 3).until(ec)
WebDriverWait(driver, 30).until(ec)
def __exit__(self, typ, value, traceback):
count = self.g_state[self.driver]
View
@@ -17,6 +17,7 @@ def prepare(worker):
app = Application('development.ini', settings)
app.cfg.set('post_worker_init', prepare)
app.cfg.set('logconfig', None)
app.run()
View
@@ -1,17 +1,10 @@
# -*- coding: utf-8 -*-
import os
import pytest
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait
from . import SeleniumTestCase, Annotator
pytestmark = pytest.mark.skipif(
os.environ.get('TRAVIS_SECURE_ENV_VARS') == 'false',
reason="No access to secure Travis variables.")
class TestAnnotator(SeleniumTestCase):
def test_login(self):
@@ -26,11 +19,22 @@ def test_login(self):
picker = (By.CLASS_NAME, 'user-picker')
ec = expected_conditions.visibility_of_element_located(picker)
WebDriverWait(driver, 3).until(ec)
WebDriverWait(driver, 30).until(ec)
picker = driver.find_element_by_class_name('user-picker')
dropdown = picker.find_element_by_class_name('dropdown-toggle')
assert dropdown.text == 'test/localhost'
user_element = picker.find_element_by_class_name('dropdown-toggle')
# Because the provider is hidden until hover, we access the
# textContent property here to get the full username. Selenium
# only returns the visible content otherwise.
actual_username = user_element.get_attribute('textContent')
# Some systems (OSX) seem to set the SERVER_NAME request
# environment variable to 127.0.0.1 rather than localhost. This
# should be configurable but I haven't found the setting yet.
# In the mean time we just allow a range of valid values.
accepted_usernames = ('test/localhost', 'test/127.0.0.1')
assert actual_username in accepted_usernames
def test_annotation(self):
driver = self.driver
@@ -52,7 +56,7 @@ def test_annotation(self):
# Wait for save
ts = (By.TAG_NAME, "fuzzytime")
saved = expected_conditions.visibility_of_element_located(ts)
WebDriverWait(driver, 3).until(saved)
WebDriverWait(driver, 30).until(saved)
def get_labels(d):
return d.find_elements_by_css_selector(".heatmap-pointer")
@@ -71,7 +75,7 @@ def get_labels(d):
with Annotator(driver):
annotation = (By.CLASS_NAME, 'annotation')
ec = expected_conditions.visibility_of_element_located(annotation)
WebDriverWait(driver, 3).until(ec)
WebDriverWait(driver, 30).until(ec)
annotation = driver.find_element_by_class_name('annotation')
user = annotation.find_element_by_class_name('user')
body = annotation.find_element_by_css_selector('markdown div p')