Skip to content
This repository has been archived by the owner on Feb 8, 2018. It is now read-only.

Commit

Permalink
Get new mondo integration test passing
Browse files Browse the repository at this point in the history
- dodge race condition around clicks and page reloading
- factor out helpers into proper classes
- simplify remove action to maintain proper state (presence of add form)
- minor formatting tweak in sptfile
  • Loading branch information
chadwhitacre committed Sep 8, 2017
1 parent dad87d6 commit dd375b8
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 68 deletions.
10 changes: 10 additions & 0 deletions gratipay/testing/browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@
import atexit
import os
import time
from contextlib import contextmanager

from splinter.browser import _DRIVERS
from selenium.common.exceptions import StaleElementReferenceException
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support.expected_conditions import staleness_of

from gratipay.security import user

Expand Down Expand Up @@ -148,6 +151,13 @@ def wait_for_error(self, message=None):
"""
return self.wait_for_notification('error', message)

@contextmanager
def page_reload_afterwards(self, timeout=2):
# www.obeythetestinggoat.com/how-to-get-selenium-to-wait-for-page-load-after-a-click.html
old_page = self._browser.driver.find_element_by_tag_name('html')
yield
WebDriverWait(self._browser, timeout).until(staleness_of(old_page))

def __getattr__(self, name):
try:
out = self.__getattribute__(name)
Expand Down
3 changes: 0 additions & 3 deletions js/gratipay/emails.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,6 @@ Gratipay.emails.post = function() {
case 'resend':
Gratipay.notification(msg, 'success');
break;
case 'remove':
$row.slideUp(function() { $row.remove() });
break;
default:
window.location.reload();
};
Expand Down
178 changes: 117 additions & 61 deletions tests/ttw/test_email_verification.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,100 +5,156 @@
from gratipay.testing import BrowserHarness, QueuedEmailHarness


class Row(object):
"""Represent one row in the listing on the emails page.
"""

def __init__(self, harness, element):
self.harness = harness
self.element = element

def buttons(self):
"""Return a list of the text of all buttons in the row.
"""
return [b.text for b in self.harness.css('button', self.element)]

def click(self, text):
"""Given the text of a button in the row, click the button.
"""
[b for b in self.harness.css('button', self.element) if b.text == text][0].click()

def add(self, email_address, expecting_reload=True):
"""Given an email address, add it via the UI. Only works for add rows!
"""
self.click('Add an email address')
self.harness.css('input', self.element).fill(email_address)
if expecting_reload:
with self.harness.page_reload_afterwards():
self.click('Send verification')
else:
self.click('Send verification')

def remove(self):
with self.harness.page_reload_afterwards():
self.click('Remove')


class Page(object):
"""Represent the emails page.
"""

def __init__(self, harness):
self.harness = harness

@property
def rows(self):
return [Row(self.harness, element) for element in self.harness.css('.emails.listing tr')]

@property
def names(self):
out = [x.text for x in self.harness.css('.emails.listing tr.existing .listing-name')]
add_row = self.harness.css('.emails.listing tr.add')
if add_row:
out.append('<add form>')
return out

def add_and_verify(self, email_address):
self.add(email_address)
assert self.harness.pop_email_message()['subject'] == 'New activity on your account'
self.harness.visit(self.harness.get_verification_link())


class Tests(BrowserHarness, QueuedEmailHarness):

def get_verification_link(self):
"""Return the verification link sent via email.
"""
verification_email = self.pop_email_message()
verification_link = re.match( r'.*?(http.*?$).*'
, verification_email['body_text']
, re.DOTALL | re.MULTILINE
).groups()[0]
return verification_link

def test(self):
self.alice = self.make_participant('alice', claimed_time='now')
self.sign_in('alice')
self.visit('/~alice/emails/')

# Helpers

buttons = lambda: [b.text for b in self.css('button', row)]
click = lambda text: [b for b in self.css('button', row) if b.text == text][0].click()

def add(email_address):
click('Add an email address')
self.css('input', row).fill(email_address)
click('Send verification')

def get_verification_link():
verification_email = self.pop_email_message()
verification_link = re.match( r'.*?(http.*?$).*'
, verification_email['body_text']
, re.DOTALL | re.MULTILINE
).groups()[0]
return verification_link

page = Page(self) # state manager

# Starts clean.
rows = self.css('#content tr')
assert len(rows) == 1
row = rows[0]
assert buttons() == ['Add an email address', '', '']
assert len(page.rows) == 1
row = page.rows[0]
assert row.buttons() == ['Add an email address', '', '']

# Can toggle add form on and off and on again.
click('Add an email address')
assert buttons() == ['', 'Send verification', 'Cancel']
click('Cancel')
assert buttons() == ['Add an email address', '', '']
row.click('Add an email address')
assert row.buttons() == ['', 'Send verification', 'Cancel']
row.click('Cancel')
assert row.buttons() == ['Add an email address', '', '']

# Can submit add form.
add('[email protected]')
row = self.wait_for('tr.existing')
assert buttons() == ['Resend verification']
self.pop_email_message() # throw away verification message
row.add('[email protected]')
assert len(page.rows) == 1 # no add form when verification pending
row = page.rows[0]
assert row.buttons() == ['Resend', 'Remove']

# Can resend verification.
click('Resend verification')
self.pop_email_message() # oops! lost the verification message
row.click('Resend')
assert self.wait_for_success() == 'Check your inbox for a verification link.'

# Can verify.
self.visit(get_verification_link())
self.visit(self.get_verification_link())
assert self.css('#content h1').text == 'Success!'

# Now listed as primary -- nothing to be done with primary.
self.visit('/~alice/emails/')
rows = self.css('.emails.listing tr')
assert len(rows) == 2
row = rows[0]
assert buttons() == []
assert row.text.endswith('Your primary email address')
assert len(page.rows) == 2
row = page.rows[0]
assert row.buttons() == []
assert row.element.text.endswith('Your primary email address')

# ... but the add form is back. Can we re-add the same?
row = rows[1]
add('[email protected]')
row = page.rows[1]
row.add('[email protected]', expecting_reload=False)
assert self.wait_for_error() == 'You have already added and verified that address.' # No!
click('Cancel')
row.click('Cancel')

# Let's add another!
add('[email protected]')
self.wait_for('.emails.listing tr')
row.add('[email protected]')
assert self.pop_email_message()['subject'] == 'New activity on your account'
self.visit(get_verification_link())
self.visit(self.get_verification_link())
with self.page_reload_afterwards():
self.css('#content a').click() # back over to /emails/

# Now we should have a primary and a linked address, and an add row.
self.css('#content a').click()
rows = self.wait_for('.emails.listing tr')
assert len(rows) == 3
email_addresses = [x.text for x in self.css('.existing .listing-name')]
assert email_addresses == ['[email protected]', '[email protected]']
row = rows[2]
assert buttons() == ['Add an email address', '', '']
assert page.names == ['[email protected]', '[email protected]', '<add form>']
assert page.rows[2].buttons() == ['Add an email address', '', '']

# We can promote the secondary account to primary.
row = rows[1]
click('Set as primary')
rows = self.wait_for('.emails.listing tr')
assert len(rows) == 3
with self.page_reload_afterwards():
page.rows[1].click('Set as primary')

# ... and now the order is reversed.
email_addresses = [x.text for x in self.css('.existing .listing-name')]
assert email_addresses == ['[email protected]', '[email protected]']
assert page.names == ['[email protected]', '[email protected]', '<add form>']

# We can remove the (new) secondary account.
row = rows[1]
click('Remove')
self.wait_to_disappear('tr[data-email="[email protected]"]')
assert len(self.css('.emails.listing tr')) == 2
page.rows[1].remove()
assert page.names == ['[email protected]', '<add form>']

# Add another again.
page.rows[1].add('[email protected]')
assert self.pop_email_message()['subject'] == 'New activity on your account'
verification_link = self.get_verification_link()

# No add form while pending
assert page.names == ['[email protected]', '[email protected]']

# But we can remove a pending verification.
page.rows[1].remove()
assert page.names == ['[email protected]', '<add form>']

# The link no longer works, of course.
self.visit(verification_link)
assert self.css('#content h1').text == 'Bad Info'
9 changes: 5 additions & 4 deletions www/~/%username/emails/index.spt
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,16 @@ page_id = 'emails'
{% else %}
<span class="status-icon success">
{{ icons.STATUS_ICONS['success']|safe }}</span>
{{ _('Linked') }}
&middot; <button class="set-primary">{{ _('Set as primary') }}</button>
&middot; <button class="remove">{{ _('Remove') }}</button>
{{ _('Linked') }} &middot;
<button class="set-primary">{{ _('Set as primary') }}</button> &middot;
<button class="remove">{{ _('Remove') }}</button>
{% endif %}
{% else %}
<span class="status-icon warning">
{{ icons.STATUS_ICONS['warning']|safe }}</span>
{{ _('Check your inbox') }} &middot;
<button class="resend">{{ _('Resend verification') }}</button>
<button class="resend">{{ _('Resend') }}</button> &middot;
<button class="remove">{{ _('Remove') }}</button>
{% endif %}
</div>
</td>
Expand Down

0 comments on commit dd375b8

Please sign in to comment.