Tightened up the test suite.

master
Pacman Ghost 4 years ago
parent 71d99f8837
commit e7444a6b27
  1. 289
      asl_articles/tests/test_articles.py
  2. 24
      asl_articles/tests/test_authors.py
  3. 264
      asl_articles/tests/test_publications.py
  4. 188
      asl_articles/tests/test_publishers.py
  5. 24
      asl_articles/tests/test_scenarios.py
  6. 6
      asl_articles/tests/test_search.py
  7. 27
      asl_articles/tests/test_tags.py
  8. 51
      asl_articles/tests/utils.py

@ -6,9 +6,11 @@ import urllib.error
import json
import base64
from asl_articles.tests.utils import init_tests, do_search, get_result_names, \
wait_for, wait_for_elem, find_child, find_children, set_elem_text, \
set_toast_marker, check_toast, send_upload_data, check_ask_dialog, check_error_msg
from asl_articles.search import SEARCH_ALL_ARTICLES
from asl_articles.tests.utils import init_tests, \
do_search, get_search_results, find_search_result, get_search_result_names, check_search_result, \
wait_for, wait_for_elem, wait_for_not_elem, find_child, find_children, \
set_elem_text, set_toast_marker, check_toast, send_upload_data, check_ask_dialog, check_error_msg
from asl_articles.tests.react_select import ReactSelect
# ---------------------------------------------------------------------
@ -19,11 +21,10 @@ def test_edit_article( webdriver, flask_app, dbconn ):
# initialize
init_tests( webdriver, flask_app, dbconn, fixtures="articles.json" )
# edit "What To Do If You Have A Tin Can"
results = do_search( '"tin can"' )
assert len(results) == 1
result = results[0]
edit_article( result, {
# edit an article
results = do_search( SEARCH_ALL_ARTICLES )
sr = find_search_result( "What To Do If You Have A Tin Can", results )
edit_article( sr, {
"title": " Updated title ",
"subtitle": " Updated subtitle ",
"snippet": " Updated snippet. ",
@ -32,34 +33,28 @@ def test_edit_article( webdriver, flask_app, dbconn ):
} )
# check that the search result was updated in the UI
results = find_children( "#search-results .search-result" )
result = results[0]
_check_result( result,
[ "Updated title", "Updated subtitle", "Updated snippet.", ["abc","xyz"], "http://updated-article.com/" ]
)
sr = check_search_result( "Updated title", _check_sr, [
"Updated title", "Updated subtitle", "Updated snippet.", ["abc","xyz"], "http://updated-article.com/"
] )
# try to remove all fields from the article (should fail)
edit_article( result,
edit_article( sr,
{ "title": "", "subtitle": "", "snippet": "", "tags": ["-abc","-xyz"], "url": "" },
expected_error = "Please specify the article's title."
)
# enter something for the name
dlg = find_child( "#modal-form" )
set_elem_text( find_child( ".title input", dlg ), "Tin Cans Rock!" )
dlg = find_child( "#modal-form" ) # nb: the form is still on-screen
set_elem_text( find_child( ".row.title input", dlg ), "Tin Cans Rock!" )
find_child( "button.ok", dlg ).click()
# check that the search result was updated in the UI
results = find_children( "#search-results .search-result" )
result = results[0]
assert find_child( ".title a", result ) is None
assert find_child( ".title", result ).text == "Tin Cans Rock!"
assert find_child( ".snippet", result ).text == ""
assert find_children( ".tag", result ) == []
expected = [ "Tin Cans Rock!", None, "", [], None ]
check_search_result( expected[0], _check_sr, expected )
# check that the search result was updated in the database
results = do_search( '"tin can"' )
_check_result( results[0], [ "Tin Cans Rock!", None, "", [], None ] )
# check that the article was updated in the database
do_search( SEARCH_ALL_ARTICLES )
check_search_result( expected[0], _check_sr, expected )
# ---------------------------------------------------------------------
@ -67,38 +62,29 @@ def test_create_article( webdriver, flask_app, dbconn ):
"""Test creating new articles."""
# initialize
init_tests( webdriver, flask_app, dbconn )
init_tests( webdriver, flask_app, dbconn, fixtures="articles.json" )
do_search( SEARCH_ALL_ARTICLES )
# try creating a article with no name (should fail)
create_article( {}, toast_type=None )
check_error_msg( "Please specify the article's title." )
# enter a name and other details
dlg = find_child( "#modal-form" ) # nb: the form is still on-screen
set_elem_text( find_child( ".title input", dlg ), "New article" )
set_elem_text( find_child( ".subtitle input", dlg ), "New subtitle" )
set_elem_text( find_child( ".snippet textarea", dlg ), "New snippet." )
select = ReactSelect( find_child( ".tags .react-select", dlg ) )
select.update_multiselect_values( "+111", "+222", "+333" )
set_elem_text( find_child( ".url input", dlg ), "http://new-snippet.com" )
set_toast_marker( "info" )
find_child( "button.ok", dlg ).click()
wait_for( 2,
lambda: check_toast( "info", "created OK", contains=True )
)
edit_article( None, { # nb: the form is still on-screen
"title": "New article",
"subtitle": "New subtitle",
"snippet": "New snippet.",
"tags": [ "+111", "+222", "+333" ],
"url": "http://new-snippet.com"
} )
# check that the new article appears in the UI
def check_new_article( result ):
_check_result( result, [
"New article", "New subtitle", "New snippet.", ["111","222","333"], "http://new-snippet.com/"
] )
results = find_children( "#search-results .search-result" )
check_new_article( results[0] )
expected = [ "New article", "New subtitle", "New snippet.", ["111","222","333"], "http://new-snippet.com/" ]
check_search_result( expected[0], _check_sr, expected )
# check that the new article has been saved in the database
results = do_search( "new" )
assert len( results ) == 1
check_new_article( results[0] )
do_search( SEARCH_ALL_ARTICLES )
check_search_result( expected[0], _check_sr, expected )
# ---------------------------------------------------------------------
@ -108,37 +94,34 @@ def test_delete_article( webdriver, flask_app, dbconn ):
# initialize
init_tests( webdriver, flask_app, dbconn, fixtures="articles.json" )
# start to delete article "Smoke Gets In Your Eyes", but cancel the operation
results = do_search( "smoke" )
assert len(results) == 1
result = results[0]
find_child( ".delete", result ).click()
check_ask_dialog( ( "Delete this article?", "Smoke Gets In Your Eyes" ), "cancel" )
# start to delete an article, but cancel the operation
article_name = "Smoke Gets In Your Eyes"
results = do_search( SEARCH_ALL_ARTICLES )
sr = find_search_result( article_name, results )
find_child( ".delete", sr ).click()
check_ask_dialog( ( "Delete this article?", article_name ), "cancel" )
# check that search results are unchanged on-screen
results2 = find_children( "#search-results .search-result" )
results2 = get_search_results()
assert results2 == results
# check that the search results are unchanged in the database
results3 = do_search( "smoke" )
results3 = do_search( SEARCH_ALL_ARTICLES )
assert results3 == results
# delete the article "Smoke Gets In Your Eyes"
result = results3[0]
find_child( ".delete", result ).click()
# delete the article
sr = find_search_result( article_name, results3 )
find_child( ".delete", sr ).click()
set_toast_marker( "info" )
check_ask_dialog( ( "Delete this article?", "Smoke Gets In Your Eyes" ), "ok" )
wait_for( 2,
lambda: check_toast( "info", "The article was deleted." )
)
check_ask_dialog( ( "Delete this article?", article_name ), "ok" )
wait_for( 2, lambda: check_toast( "info", "The article was deleted." ) )
# check that search result was removed on-screen
results = find_children( "#search-results .search-result" )
assert get_result_names( results ) == []
wait_for( 2, lambda: article_name not in get_search_result_names() )
# check that the search result was deleted from the database
results = do_search( "smoke" )
assert get_result_names( results ) == []
results = do_search( SEARCH_ALL_ARTICLES )
assert article_name not in get_search_result_names( results )
# ---------------------------------------------------------------------
@ -147,17 +130,20 @@ def test_images( webdriver, flask_app, dbconn ): #pylint: disable=too-many-state
# initialize
init_tests( webdriver, flask_app, dbconn, max_image_upload_size=2*1024 )
article_sr = article_id = None
def check_image( expected ):
# check the image in the article's search result
img = find_child( "img.image", article_sr )
if expected:
expected_url = flask_app.url_for( "get_image", image_type="article", image_id=article_id )
image_url = img.get_attribute( "src" ).split( "?" )[0]
assert image_url == expected_url
else:
assert not img
def check_sr_image():
img = find_child( "img.image", article_sr )
if expected:
expected_url = flask_app.url_for( "get_image", image_type="article", image_id=article_id )
image_url = img.get_attribute( "src" ).split( "?" )[0]
return image_url == expected_url
else:
return not img
wait_for( 2, check_sr_image )
# check the image in the article's config
find_child( ".edit", article_sr ).click()
@ -172,7 +158,7 @@ def test_images( webdriver, flask_app, dbconn ): #pylint: disable=too-many-state
assert btn.is_displayed()
# make sure the article's image is correct
resp = urllib.request.urlopen( image_url ).read()
assert resp == open(expected,"rb").read()
assert resp == open( expected, "rb" ).read()
else:
# make sure there is no image
img = find_child( ".row.image img.image", dlg )
@ -191,7 +177,7 @@ def test_images( webdriver, flask_app, dbconn ): #pylint: disable=too-many-state
# create an article with no image
create_article( { "title": "Test Article" } )
results = find_children( "#search-results .search-result" )
results = get_search_results()
assert len(results) == 1
article_sr = results[0]
article_id = article_sr.get_attribute( "testing--article_id" )
@ -228,51 +214,56 @@ def test_parent_publisher( webdriver, flask_app, dbconn ):
# initialize
init_tests( webdriver, flask_app, dbconn, fixtures="parents.json" )
article_sr = None
def check_results( expected_parent ):
def check_result( sr, expected_parent ): #pylint: disable=too-many-return-statements
# check that the parent publication was updated in the UI
nonlocal article_sr
elem = find_child( ".title .publication", article_sr )
elem = find_child( ".title .publication", sr )
if expected_parent:
assert elem.text == "[{}]".format( expected_parent[1] )
if elem.text != "[{}]".format( expected_parent[1] ):
return None
else:
assert elem is None
if elem is not None:
return None
# check that the parent publication was updated in the database
article_id = article_sr.get_attribute( "testing--article_id" )
article_id = sr.get_attribute( "testing--article_id" )
url = flask_app.url_for( "get_article", article_id=article_id )
article = json.load( urllib.request.urlopen( url ) )
if expected_parent:
assert article["pub_id"] == expected_parent[0]
if article["pub_id"] != expected_parent[0]:
return None
else:
assert article["pub_id"] is None
if article["pub_id"] is not None:
return None
# check that the parent publication was updated in the UI
results = do_search( '"My Article"' )
assert len(results) == 1
article_sr = results[0]
elem = find_child( ".title .publication", article_sr )
sr = results[0]
elem = find_child( ".title .publication", sr )
if expected_parent:
assert elem.text == "[{}]".format( expected_parent[1] )
if elem.text != "[{}]".format( expected_parent[1] ):
return None
else:
assert elem is None
if elem is not None:
return None
return sr
# create an article with no parent publication
create_article( { "title": "My Article" } )
results = find_children( "#search-results .search-result" )
results = get_search_results()
assert len(results) == 1
article_sr = results[0]
check_results( None )
sr = wait_for( 2, lambda: check_result( results[0], None ) )
# change the article to have a publication
edit_article( article_sr, { "publication": "ASL Journal" } )
check_results( (1, "ASL Journal") )
edit_article( sr, { "publication": "ASL Journal" } )
sr = wait_for( 2, lambda: check_result( sr, (1, "ASL Journal") ) )
# change the article back to having no publication
edit_article( article_sr, { "publication": "(none)" } )
check_results( None )
edit_article( sr, { "publication": "(none)" } )
sr = wait_for( 2, lambda: check_result( sr, None ) )
# ---------------------------------------------------------------------
@ -292,9 +283,9 @@ def test_unicode( webdriver, flask_app, dbconn ):
} )
# check that the new article is showing the Unicode content correctly
results = do_search( "japan" )
assert len( results ) == 1
_check_result( results[0], [
results = do_search( SEARCH_ALL_ARTICLES )
assert len(results) == 1
check_search_result( results[0], _check_sr, [
"japan = \u65e5\u672c",
"s.korea = \ud55c\uad6d",
"greece = \u0395\u03bb\u03bb\u03ac\u03b4\u03b1",
@ -318,108 +309,130 @@ def test_clean_html( webdriver, flask_app, dbconn ):
}, toast_type="warning" )
# check that the HTML was cleaned
results = wait_for( 2,
lambda: find_children( "#search-results .search-result" )
)
assert len( results ) == 1
result = results[0]
_check_result( result, [ "title: bold xxx italic", "italicized subtitle", "bad stuff here:", [], None ] )
assert find_child( ".title span" ).get_attribute( "innerHTML" ) \
sr = check_search_result( None, _check_sr, [
"title: bold xxx italic", "italicized subtitle", "bad stuff here:", [], None
] )
assert find_child( ".title span", sr ).get_attribute( "innerHTML" ) \
== "title: <span> <b>bold</b> xxx <i>italic</i></span>"
assert find_child( ".subtitle" ).get_attribute( "innerHTML" ) \
assert find_child( ".subtitle", sr ).get_attribute( "innerHTML" ) \
== "<i>italicized subtitle</i>"
assert check_toast( "warning", "Some values had HTML removed.", contains=True )
# update the article with new HTML content
edit_article( result, {
edit_article( sr, {
"title": "<div style='...'>updated</div>"
}, toast_type="warning" )
def check_result():
results = find_children( "#search-results .search-result" )
assert len( results ) == 1
result = results[0]
return find_child( ".title span", result ).text == "updated"
wait_for( 2, check_result )
wait_for( 2, lambda: get_search_result_names() == ["updated"] )
assert check_toast( "warning", "Some values had HTML removed.", contains=True )
# ---------------------------------------------------------------------
def create_article( vals, toast_type="info" ):
"""Create a new article."""
# initialize
if toast_type:
set_toast_marker( toast_type )
# create the new article
find_child( "#menu .new-article" ).click()
dlg = wait_for_elem( 2, "#modal-form" )
for key,val in vals.items():
if key in ["authors","scenarios","tags"]:
select = ReactSelect( find_child( ".{} .react-select".format(key), dlg ) )
select = ReactSelect( find_child( ".row.{} .react-select".format(key), dlg ) )
select.update_multiselect_values( *val )
else:
sel = ".{} {}".format( key , "textarea" if key == "snippet" else "input" )
sel = ".row.{} {}".format( key , "textarea" if key == "snippet" else "input" )
set_elem_text( find_child( sel, dlg ), val )
find_child( "button.ok", dlg ).click()
if toast_type:
# check that the new article was created successfully
wait_for( 2,
lambda: check_toast( toast_type, "created OK", contains=True )
)
wait_for_not_elem( 2, "#modal-form" )
def edit_article( result, vals, toast_type="info", expected_error=None ):
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
def edit_article( sr, vals, toast_type="info", expected_error=None ):
"""Edit a article's details."""
# update the specified article's details
find_child( ".edit", result ).click()
# initialize
if sr:
find_child( ".edit", sr ).click()
else:
pass # nb: we assume that the dialog is already on-screen
dlg = wait_for_elem( 2, "#modal-form" )
# update the specified article's details
for key,val in vals.items():
if key == "image":
if val:
data = base64.b64encode( open( val, "rb" ).read() )
data = "{}|{}".format( os.path.split(val)[1], data.decode("ascii") )
send_upload_data( data,
lambda: find_child( ".image img", dlg ).click()
lambda: find_child( ".row.image img.image", dlg ).click()
)
else:
find_child( ".remove-image", dlg ).click()
find_child( ".row.image .remove-image", dlg ).click()
elif key == "publication":
select = ReactSelect( find_child( ".publication .react-select", dlg ) )
select = ReactSelect( find_child( ".row.publication .react-select", dlg ) )
select.select_by_name( val )
elif key in ["authors","scenarios","tags"]:
select = ReactSelect( find_child( ".{} .react-select".format(key), dlg ) )
select = ReactSelect( find_child( ".row.{} .react-select".format(key), dlg ) )
select.update_multiselect_values( *val )
else:
sel = ".{} {}".format( key , "textarea" if key == "snippet" else "input" )
sel = ".row.{} {}".format( key , "textarea" if key == "snippet" else "input" )
set_elem_text( find_child( sel, dlg ), val )
set_toast_marker( toast_type )
find_child( "button.ok", dlg ).click()
# check what happened
if expected_error:
# we were expecting an error, confirm the error message
check_error_msg( expected_error )
else:
# we were expecting the update to work, confirm this
expected = "updated OK" if sr else "created OK"
wait_for( 2,
lambda: check_toast( toast_type, "updated OK", contains=True )
lambda: check_toast( toast_type, expected, contains=True )
)
wait_for_not_elem( 2, "#modal-form" )
# ---------------------------------------------------------------------
def _check_result( result, expected ):
"""Check a result."""
def _check_sr( sr, expected ): #pylint: disable=too-many-return-statements
"""Check a search result."""
# check the title and subtitle
assert find_child( ".title span", result ).text == expected[0]
elem = find_child( ".subtitle", result )
if elem:
assert elem.text == expected[1]
names = get_search_result_names( [sr] )
if names[0] != expected[0]:
return False
elem = find_child( ".subtitle", sr )
if expected[1]:
if not elem or elem.text != expected[1]:
return False
else:
assert expected[1] is None
if elem is not None:
return False
# check the snippet
assert find_child( ".snippet", result ).text == expected[2]
if find_child( ".snippet", sr ).text != expected[2]:
return False
# check the tags
tags = [ t.text for t in find_children( ".tag", result ) ]
assert tags == expected[3]
tags = [ t.text for t in find_children( ".tag", sr ) ]
if tags != expected[3]:
return False
# check the article's link
elem = find_child( ".title a", result )
if elem:
assert elem.get_attribute( "href" ) == expected[4]
elem = find_child( ".title a", sr )
if expected[4]:
if not elem or elem.get_attribute( "href" ) != expected[4]:
return False
else:
assert expected[4] is None
if elem is not None:
return False
return True

@ -3,7 +3,7 @@
import urllib.request
import json
from asl_articles.tests.utils import init_tests, find_child, find_children, wait_for_elem, find_search_result
from asl_articles.tests.utils import init_tests, wait_for, wait_for_elem, find_child, find_children, find_search_result
from asl_articles.tests.react_select import ReactSelect
from asl_articles.tests.test_articles import create_article, edit_article
@ -61,22 +61,30 @@ def _check_authors( flask_app, all_authors, expected ):
for authors in expected:
all_authors.update( authors )
def check_authors( sr_name, expected ):
sr = find_search_result( sr_name )
sr_authors = [ a.text for a in find_children( ".author", sr ) ]
if sr_authors == expected:
return sr
return None
# check the authors in the UI
for article_no,authors in enumerate( expected ):
for article_no,expected_authors in enumerate( expected ):
# check the authors in the article's search result
sr = find_search_result( "article {}".format( 1+article_no ) )
sr_authors = [ a.text for a in find_children( ".author", sr ) ]
assert sr_authors == authors
sr_name = "article {}".format( 1+article_no )
sr = wait_for( 2,
lambda n=sr_name, e=expected_authors: check_authors( n, e )
)
# check the authors in the article's config
find_child( ".edit", sr ).click()
dlg = wait_for_elem( 2, "#modal-form" )
select = ReactSelect( find_child( ".authors .react-select", dlg ) )
assert select.get_multiselect_values() == authors
select = ReactSelect( find_child( ".row.authors .react-select", dlg ) )
assert select.get_multiselect_values() == expected_authors
# check that the list of available authors is correct
assert select.get_multiselect_choices() == sorted( all_authors.difference( authors ) )
assert select.get_multiselect_choices() == sorted( all_authors.difference( expected_authors ) )
# close the dialog
find_child( "button.cancel", dlg ).click()

@ -9,9 +9,10 @@ import base64
from selenium.webdriver.common.keys import Keys
from selenium.common.exceptions import StaleElementReferenceException
from asl_articles.search import SEARCH_ALL
from asl_articles.tests.utils import init_tests, load_fixtures, do_search, get_result_names, \
wait_for, wait_for_elem, find_child, find_children, find_search_result, set_elem_text, \
from asl_articles.search import SEARCH_ALL, SEARCH_ALL_PUBLICATIONS, SEARCH_ALL_ARTICLES
from asl_articles.tests.utils import init_tests, load_fixtures, \
do_search, get_search_results, get_search_result_names, check_search_result, \
wait_for, wait_for_elem, wait_for_not_elem, find_child, find_children, find_search_result, set_elem_text, \
set_toast_marker, check_toast, send_upload_data, check_ask_dialog, check_error_msg
from asl_articles.tests.react_select import ReactSelect
@ -24,10 +25,9 @@ def test_edit_publication( webdriver, flask_app, dbconn ):
init_tests( webdriver, flask_app, dbconn, fixtures="publications.json" )
# edit "ASL Journal #2"
results = do_search( '"asl journal"' )
assert len(results) == 2
result = results[1]
edit_publication( result, {
results = do_search( SEARCH_ALL_PUBLICATIONS )
sr = find_search_result( "ASL Journal (2)", results )
edit_publication( sr, {
"name": " ASL Journal (updated) ",
"edition": " 2a ",
"description": " Updated ASLJ description. ",
@ -36,19 +36,18 @@ def test_edit_publication( webdriver, flask_app, dbconn ):
} )
# check that the search result was updated in the UI
results = find_children( "#search-results .search-result" )
result = results[1]
_check_result( result,
[ "ASL Journal (updated)", "2a", "Updated ASLJ description.", ["abc","xyz"], "http://aslj-updated.com/" ]
)
expected = [ "ASL Journal (updated)", "2a", "Updated ASLJ description.", ["abc","xyz"], "http://aslj-updated.com/" ]
sr = find_search_result( "ASL Journal (updated) (2a)" )
check_search_result( sr, _check_sr, expected )
# NOTE: We used to try to remove all fields from "ASL Journal #2" (which should fail, since we can't
# have an empty name), but we can no longer remove an existing publication name (which is OK, since we
# don't want to allow that, only change it).
# check that the search result was updated in the database
results = do_search( '"ASL Journal"' )
assert get_result_names( results ) == [ "ASL Journal (1)", "ASL Journal (updated) (2a)" ]
# check that the publication was updated in the database
results = do_search( SEARCH_ALL_PUBLICATIONS )
sr = find_search_result( "ASL Journal (updated) (2a)", results )
check_search_result( sr, _check_sr, expected )
# ---------------------------------------------------------------------
@ -56,39 +55,32 @@ def test_create_publication( webdriver, flask_app, dbconn ):
"""Test creating new publications."""
# initialize
init_tests( webdriver, flask_app, dbconn )
init_tests( webdriver, flask_app, dbconn, fixtures="publications.json" )
do_search( SEARCH_ALL_PUBLICATIONS )
# try creating a publication with no name (should fail)
create_publication( {}, toast_type=None )
check_error_msg( "Please specify the publication's name." )
# enter a name and other details
dlg = find_child( "#modal-form" ) # nb: the form is still on-screen
elem = find_child( ".name .react-select input", dlg )
elem.send_keys( "New publication", Keys.RETURN )
set_elem_text( find_child( ".edition input", dlg ), "#1" )
set_elem_text( find_child( ".description textarea", dlg ), "New publication description." )
select = ReactSelect( find_child( ".tags .react-select", dlg ) )
select.update_multiselect_values( "+111", "+222", "+333" )
set_elem_text( find_child( ".url input", dlg ), "http://new-publication.com" )
set_toast_marker( "info" )
find_child( "button.ok", dlg ).click()
wait_for( 2,
lambda: check_toast( "info", "created OK", contains=True )
)
edit_publication( None, { # nb: the form is still on-screen
"name": "New publication",
"edition": "#1",
"description": "New publication description.",
"tags": [ "+111", "+222", "+333" ],
"url": "http://new-publication.com"
} )
# check that the new publication appears in the UI
def check_new_publication( result ):
_check_result( result, [
"New publication", "#1", "New publication description.", ["111","222","333"], "http://new-publication.com/"
] )
results = find_children( "#search-results .search-result" )
check_new_publication( results[0] )
expected = [ "New publication", "#1", "New publication description.",
["111","222","333"], "http://new-publication.com/"
]
check_search_result( "New publication (#1)", _check_sr, expected )
# check that the new publication has been saved in the database
results = do_search( "new" )
assert len( results ) == 1
check_new_publication( results[0] )
results = do_search( SEARCH_ALL_PUBLICATIONS )
sr = find_search_result( "New publication (#1)", results )
check_search_result( sr, _check_sr, expected )
# ---------------------------------------------------------------------
@ -98,37 +90,34 @@ def test_delete_publication( webdriver, flask_app, dbconn ):
# initialize
init_tests( webdriver, flask_app, dbconn, fixtures="publications.json" )
# start to delete publication "ASL Journal #1", but cancel the operation
results = do_search( '"ASL Journal"' )
assert len(results) == 2
sr = find_search_result( "ASL Journal (2)", results )
# start to delete a publication, but cancel the operation
article_name = "ASL Journal (2)"
results = do_search( SEARCH_ALL_PUBLICATIONS )
sr = find_search_result( article_name, results )
find_child( ".delete", sr ).click()
check_ask_dialog( ( "Delete this publication?", "ASL Journal (2)" ), "cancel" )
check_ask_dialog( ( "Delete this publication?", article_name ), "cancel" )
# check that search results are unchanged on-screen
results2 = find_children( "#search-results .search-result" )
results2 = get_search_results()
assert results2 == results
# check that the search results are unchanged in the database
results3 = do_search( '"ASL Journal"' )
results3 = do_search( SEARCH_ALL_PUBLICATIONS )
assert results3 == results
# delete the publication "ASL Journal 2"
sr = find_search_result( "ASL Journal (2)", results3 )
# delete the publication
sr = find_search_result( article_name, results3 )
find_child( ".delete", sr ).click()
set_toast_marker( "info" )
check_ask_dialog( ( "Delete this publication?", "ASL Journal (2)" ), "ok" )
wait_for( 2,
lambda: check_toast( "info", "The publication was deleted." )
)
check_ask_dialog( ( "Delete this publication?", article_name ), "ok" )
wait_for( 2, lambda: check_toast( "info", "The publication was deleted." ) )
# check that search result was removed on-screen
results = find_children( "#search-results .search-result" )
assert get_result_names( results ) == [ "ASL Journal (1)" ]
wait_for( 2, lambda: article_name not in get_search_result_names() )
# check that the search result was deleted from the database
results = do_search( '"ASL Journal"' )
assert get_result_names( results ) == [ "ASL Journal (1)" ]
results = do_search( SEARCH_ALL_PUBLICATIONS )
assert article_name not in get_search_result_names( results )
# ---------------------------------------------------------------------
@ -141,13 +130,15 @@ def test_images( webdriver, flask_app, dbconn ): #pylint: disable=too-many-state
def check_image( expected ):
# check the image in the publication's search result
img = find_child( "img.image", pub_sr )
if expected:
expected_url = flask_app.url_for( "get_image", image_type="publication", image_id=pub_id )
image_url = img.get_attribute( "src" ).split( "?" )[0]
assert image_url == expected_url
else:
assert not img
def check_sr_image( expected ):
img = find_child( "img.image", pub_sr )
if expected:
expected_url = flask_app.url_for( "get_image", image_type="publication", image_id=pub_id )
image_url = img.get_attribute( "src" ).split( "?" )[0]
return image_url == expected_url
else:
return not img
wait_for( 2, lambda: check_sr_image( expected ) )
# check the image in the publisher's config
find_child( ".edit", pub_sr ).click()
@ -162,7 +153,7 @@ def test_images( webdriver, flask_app, dbconn ): #pylint: disable=too-many-state
assert btn.is_displayed()
# make sure the publication's image is correct
resp = urllib.request.urlopen( image_url ).read()
assert resp == open(expected,"rb").read()
assert resp == open( expected, "rb" ).read()
else:
# make sure there is no image
img = find_child( ".row.image img.image", dlg )
@ -181,7 +172,7 @@ def test_images( webdriver, flask_app, dbconn ): #pylint: disable=too-many-state
# create an publication with no image
create_publication( {"name": "Test Publication" } )
results = find_children( "#search-results .search-result" )
results = get_search_results()
assert len(results) == 1
pub_sr = results[0]
pub_id = pub_sr.get_attribute( "testing--pub_id" )
@ -218,51 +209,56 @@ def test_parent_publisher( webdriver, flask_app, dbconn ):
# initialize
init_tests( webdriver, flask_app, dbconn, fixtures="parents.json" )
pub_sr = None
def check_results( expected_parent ):
def check_result( sr, expected_parent ): #pylint: disable=too-many-return-statements
# check that the parent publisher was updated in the UI
nonlocal pub_sr
elem = find_child( ".name .publisher", pub_sr )
elem = find_child( ".name .publisher", sr )
if expected_parent:
assert elem.text == "({})".format( expected_parent[1] )
if elem.text != "({})".format( expected_parent[1] ):
return None
else:
assert elem is None
if elem is not None:
return None
# check that the parent publisher was updated in the database
pub_id = pub_sr.get_attribute( "testing--pub_id" )
pub_id = sr.get_attribute( "testing--pub_id" )
url = flask_app.url_for( "get_publication", pub_id=pub_id )
pub = json.load( urllib.request.urlopen( url ) )
if expected_parent:
assert pub["publ_id"] == expected_parent[0]
if pub["publ_id"] != expected_parent[0]:
return None
else:
assert pub["publ_id"] is None
if pub["publ_id"] is not None:
return None
# check that the parent publisher was updated in the UI
results = do_search( '"MMP News"' )
assert len(results) == 1
pub_sr = results[0]
elem = find_child( ".name .publisher", pub_sr )
sr = results[0]
elem = find_child( ".name .publisher", sr )
if expected_parent:
assert elem.text == "({})".format( expected_parent[1] )
if elem.text != "({})".format( expected_parent[1] ):
return None
else:
assert elem is None
if elem is not None:
return None
return sr
# create a publication with no parent publisher
create_publication( { "name": "MMP News" } )
results = find_children( "#search-results .search-result" )
results = get_search_results()
assert len(results) == 1
pub_sr = results[0]
check_results( None )
sr = wait_for( 2, lambda: check_result( results[0], None ) )
# change the publication to have a publisher
edit_publication( pub_sr, { "publisher": "Multiman Publishing" } )
check_results( (1, "Multiman Publishing") )
edit_publication( sr, { "publisher": "Multiman Publishing" } )
sr = wait_for( 2, lambda: check_result( sr, (1, "Multiman Publishing") ) )
# change the publication back to having no publisher
edit_publication( pub_sr, { "publisher": "(none)" } )
check_results( None )
edit_publication( sr, { "publisher": "(none)" } )
sr = wait_for( 2, lambda: check_result( results[0], None ) )
# ---------------------------------------------------------------------
@ -289,9 +285,8 @@ def test_cascading_deletes( webdriver, flask_app, dbconn ):
def get_results( expected_len ):
# NOTE: The UI will remove anything that has been deleted, so we need to
# give it a bit of time to finish doing this.
results = find_children( "#search-results .search-result" )
try:
results = [ find_child( ".name span", r ).text for r in results ]
results = get_search_result_names()
except StaleElementReferenceException:
return None
results = [ r for r in results if r.startswith( "article" ) ]
@ -303,7 +298,7 @@ def test_cascading_deletes( webdriver, flask_app, dbconn ):
check_results()
# check that associated articles were removed from the database
results = do_search( "article" )
results = do_search( SEARCH_ALL_ARTICLES )
check_results()
# do the tests
@ -335,9 +330,9 @@ def test_unicode( webdriver, flask_app, dbconn ):
} )
# check that the new publication is showing the Unicode content correctly
results = do_search( "japan" )
results = do_search( SEARCH_ALL_PUBLICATIONS )
assert len( results ) == 1
_check_result( results[0], [
check_search_result( results[0], _check_sr, [
"japan = \u65e5\u672c", "\u263a",
"greece = \u0395\u03bb\u03bb\u03ac\u03b4\u03b1",
[ "\u0e51", "\u0e52", "\u0e53" ],
@ -360,112 +355,131 @@ def test_clean_html( webdriver, flask_app, dbconn ):
}, toast_type="warning" )
# check that the HTML was cleaned
results = wait_for( 2,
lambda: find_children( "#search-results .search-result" )
)
assert len( results ) == 1
result = results[0]
_check_result( result, [ "name: bold xxx italic", "2", "bad stuff here:", [], None ] )
assert find_child( ".name span" ).get_attribute( "innerHTML" ) \
sr = check_search_result( None, _check_sr, [
"name: bold xxx italic", "2", "bad stuff here:", [], None
] )
assert find_child( ".name span", sr ).get_attribute( "innerHTML" ) \
== "name: <span> <b>bold</b> xxx <i>italic</i></span> (<i>2</i>)"
assert check_toast( "warning", "Some values had HTML removed.", contains=True )
# update the publication with new HTML content
edit_publication( result, {
edit_publication( sr, {
"name": "<div style='...'>updated</div>"
}, toast_type="warning" )
def check_result():
results = find_children( "#search-results .search-result" )
assert len( results ) == 1
result = results[0]
return find_child( ".name", result ).text == "updated (2)"
wait_for( 2, check_result )
results = get_search_results()
assert len(results) == 1
wait_for( 2, lambda: find_child( ".name", results[0] ).text == "updated (2)" )
assert check_toast( "warning", "Some values had HTML removed.", contains=True )
# ---------------------------------------------------------------------
def create_publication( vals, toast_type="info" ):
"""Create a new publication."""
# initialize
if toast_type:
set_toast_marker( toast_type )
# create the new publication
find_child( "#menu .new-publication" ).click()
dlg = wait_for_elem( 2, "#modal-form" )
for key,val in vals.items():
if key == "name":
elem = find_child( ".name .react-select input" )
elem = find_child( ".row.name .react-select input", dlg )
set_elem_text( elem, val )
elem.send_keys( Keys.RETURN )
elif key == "tags":
select = ReactSelect( find_child( ".tags .react-select", dlg ) )
select = ReactSelect( find_child( ".row.tags .react-select", dlg ) )
select.update_multiselect_values( *val )
else:
sel = ".{} {}".format( key , "textarea" if key == "description" else "input" )
sel = ".row.{} {}".format( key , "textarea" if key == "description" else "input" )
set_elem_text( find_child( sel, dlg ), val )
find_child( "button.ok", dlg ).click()
if toast_type:
# check that the new publication was created successfully
wait_for( 2,
lambda: check_toast( toast_type, "created OK", contains=True )
)
wait_for_not_elem( 2, "#modal-form" )
def edit_publication( result, vals, toast_type="info", expected_error=None ):
def edit_publication( sr, vals, toast_type="info", expected_error=None ):
"""Edit a publication's details."""
# update the specified publication's details
find_child( ".edit", result ).click()
# initialize
if sr:
find_child( ".edit", sr ).click()
else:
pass # nb: we assume that the dialog is already on-screen
dlg = wait_for_elem( 2, "#modal-form" )
# update the specified publication's details
for key,val in vals.items():
if key == "image":
if val:
data = base64.b64encode( open( val, "rb" ).read() )
data = "{}|{}".format( os.path.split(val)[1], data.decode("ascii") )
send_upload_data( data,
lambda: find_child( ".image img", dlg ).click()
lambda: find_child( ".row.image img", dlg ).click()
)
else:
find_child( ".remove-image", dlg ).click()
find_child( ".row.image .remove-image", dlg ).click()
elif key == "name":
elem = find_child( ".name .react-select input" )
elem = find_child( ".row.name .react-select input", dlg )
set_elem_text( elem, val )
elem.send_keys( Keys.RETURN )
elif key == "publisher":
select = ReactSelect( find_child( ".publisher .react-select", dlg ) )
select = ReactSelect( find_child( ".row.publisher .react-select", dlg ) )
select.select_by_name( val )
elif key == "tags":
select = ReactSelect( find_child( ".tags .react-select", dlg ) )
select = ReactSelect( find_child( ".row.tags .react-select", dlg ) )
select.update_multiselect_values( *val )
else:
sel = ".{} {}".format( key , "textarea" if key == "description" else "input" )
sel = ".row.{} {}".format( key , "textarea" if key == "description" else "input" )
set_elem_text( find_child( sel, dlg ), val )
set_toast_marker( toast_type )
find_child( "button.ok", dlg ).click()
# check what happened
if expected_error:
# we were expecting an error, confirm the error message
check_error_msg( expected_error )
else:
# we were expecting the update to work, confirm this
expected = "updated OK" if sr else "created OK"
wait_for( 2,
lambda: check_toast( toast_type, "updated OK", contains=True )
lambda: check_toast( toast_type, expected, contains=True )
)
wait_for_not_elem( 2, "#modal-form" )
# ---------------------------------------------------------------------
def _check_result( result, expected ):
"""Check a result."""
def _check_sr( sr, expected ):
"""Check a search result."""
# check the name and edition
expected_name = expected[0]
if expected[1]:
expected_name += " ({})".format( expected[1] )
assert find_child( ".name", result ).text == expected_name
if find_child( ".name", sr ).text != expected_name:
return False
# check the description
assert find_child( ".description", result ).text == expected[2]
if find_child( ".description", sr ).text != expected[2]:
return False
# check the tags
tags = [ t.text for t in find_children( ".tag", result ) ]
assert tags == expected[3]
tags = [ t.text for t in find_children( ".tag", sr ) ]
if tags != expected[3]:
return False
# check the publication's link
elem = find_child( ".name a", result )
elem = find_child( ".name a", sr )
if elem:
assert elem.get_attribute( "href" ) == expected[4]
if elem.get_attribute( "href" ) != expected[4]:
return False
else:
assert expected[4] is None
if expected[4] is not None:
return False
return True

@ -8,8 +8,9 @@ import base64
from selenium.common.exceptions import StaleElementReferenceException
from asl_articles.search import SEARCH_ALL, SEARCH_ALL_PUBLISHERS
from asl_articles.tests.utils import init_tests, load_fixtures, do_search, get_result_names, \
wait_for, wait_for_elem, find_child, find_children, find_search_result, set_elem_text, \
from asl_articles.tests.utils import init_tests, load_fixtures, \
do_search, get_search_results, get_search_result_names, check_search_result, \
wait_for, wait_for_elem, wait_for_not_elem, find_child, find_search_result, set_elem_text, \
set_toast_marker, check_toast, send_upload_data, check_ask_dialog, check_error_msg
# ---------------------------------------------------------------------
@ -22,41 +23,36 @@ def test_edit_publisher( webdriver, flask_app, dbconn ):
# edit "Avalon Hill"
results = do_search( SEARCH_ALL_PUBLISHERS )
result = results[0]
assert find_child( ".name", result ).text == "Avalon Hill"
edit_publisher( result, {
sr = find_search_result( "Avalon Hill", results )
edit_publisher( sr, {
"name": " Avalon Hill (updated) ",
"description": " Updated AH description. ",
"url": " http://ah-updated.com "
} )
# check that the search result was updated in the UI
results = find_children( "#search-results .search-result" )
result = results[0]
_check_result( result, [ "Avalon Hill (updated)", "Updated AH description.", "http://ah-updated.com/" ] )
sr = check_search_result( "Avalon Hill (updated)", _check_sr, [
"Avalon Hill (updated)", "Updated AH description.", "http://ah-updated.com/"
] )
# try to remove all fields from "Avalon Hill" (should fail)
edit_publisher( result,
# try to remove all fields from the publisher (should fail)
edit_publisher( sr,
{ "name": "", "description": "", "url": "" },
expected_error = "Please specify the publisher's name."
)
# enter something for the name
dlg = find_child( "#modal-form" )
set_elem_text( find_child( ".name input", dlg ), "Updated Avalon Hill" )
set_elem_text( find_child( ".row.name input", dlg ), "Updated Avalon Hill" )
find_child( "button.ok", dlg ).click()
# check that the search result was updated in the UI
results = find_children( "#search-results .search-result" )
result = results[0]
assert find_child( ".name a", result ) is None
assert find_child( ".name", result ).text == "Updated Avalon Hill"
assert find_child( ".description", result ).text == ""
expected = [ "Updated Avalon Hill", "", None ]
check_search_result( expected[0], _check_sr, expected )
# check that the search result was updated in the database
# check that the publisher was updated in the database
results = do_search( SEARCH_ALL_PUBLISHERS )
assert set( get_result_names( results ) ) == \
set([ "Le Franc Tireur", "Multiman Publishing", "Updated Avalon Hill" ])
check_search_result( expected[0], _check_sr, expected )
# ---------------------------------------------------------------------
@ -64,33 +60,27 @@ def test_create_publisher( webdriver, flask_app, dbconn ):
"""Test creating new publishers."""
# initialize
init_tests( webdriver, flask_app, dbconn )
init_tests( webdriver, flask_app, dbconn, fixtures="publishers.json" )
do_search( SEARCH_ALL_PUBLISHERS )
# try creating a publisher with no name (should fail)
create_publisher( {}, toast_type=None )
check_error_msg( "Please specify the publisher's name." )
# enter a name and other details
dlg = find_child( "#modal-form" ) # nb: the form is still on-screen
set_elem_text( find_child( ".name input", dlg ), "New publisher" )
set_elem_text( find_child( ".url input", dlg ), "http://new-publisher.com" )
set_elem_text( find_child( ".description textarea", dlg ), "New publisher description." )
set_toast_marker( "info" )
find_child( "button.ok", dlg ).click()
wait_for( 2,
lambda: check_toast( "info", "created OK", contains=True )
)
edit_publisher( None, { # nb: the form is still on-screen
"name": "New publisher",
"url": "http://new-publisher.com",
"description": "New publisher description."
} )
# check that the new publisher appears in the UI
def check_new_publisher( result ):
_check_result( result, [ "New publisher", "New publisher description.", "http://new-publisher.com/" ] )
results = find_children( "#search-results .search-result" )
check_new_publisher( results[0] )
expected = [ "New publisher", "New publisher description.", "http://new-publisher.com/" ]
check_search_result( expected[0], _check_sr, expected )
# check that the new publisher has been saved in the database
results = do_search( "new" )
assert len( results ) == 1
check_new_publisher( results[0] )
do_search( SEARCH_ALL_PUBLISHERS )
check_search_result( expected[0], _check_sr, expected )
# ---------------------------------------------------------------------
@ -100,37 +90,36 @@ def test_delete_publisher( webdriver, flask_app, dbconn ):
# initialize
init_tests( webdriver, flask_app, dbconn, fixtures="publishers.json" )
# start to delete publisher "Le Franc Tireur", but cancel the operation
# start to delete a publisher, but cancel the operation
article_name = "Le Franc Tireur"
results = do_search( SEARCH_ALL_PUBLISHERS )
sr = find_search_result( "Le Franc Tireur", results )
assert find_child( ".name", sr ).text == "Le Franc Tireur"
sr = find_search_result( article_name, results )
find_child( ".delete", sr ).click()
check_ask_dialog( ( "Delete this publisher?", "Le Franc Tireur" ), "cancel" )
check_ask_dialog( ( "Delete this publisher?", article_name ), "cancel" )
# check that search results are unchanged on-screen
results2 = find_children( "#search-results .search-result" )
results2 = get_search_results()
assert results2 == results
# check that the search results are unchanged in the database
results3 = do_search( SEARCH_ALL_PUBLISHERS )
assert results3 == results
# delete the publisher "Le Franc Tireur"
sr = find_search_result( "Le Franc Tireur", results3 )
# delete the publisher
sr = find_search_result( article_name, results3 )
find_child( ".delete", sr ).click()
set_toast_marker( "info" )
check_ask_dialog( ( "Delete this publisher?", "Le Franc Tireur" ), "ok" )
check_ask_dialog( ( "Delete this publisher?", article_name ), "ok" )
wait_for( 2,
lambda: check_toast( "info", "The publisher was deleted." )
)
# check that search result was removed on-screen
results = find_children( "#search-results .search-result" )
assert set( get_result_names( results ) ) == set([ "Avalon Hill", "Multiman Publishing" ])
wait_for( 2, lambda: article_name not in get_search_result_names() )
# check that the search result was deleted from the database
results = do_search( SEARCH_ALL_PUBLISHERS )
assert set( get_result_names( results ) ) == set([ "Avalon Hill", "Multiman Publishing" ])
assert article_name not in get_search_result_names( results )
# ---------------------------------------------------------------------
@ -143,13 +132,15 @@ def test_images( webdriver, flask_app, dbconn ): #pylint: disable=too-many-state
def check_image( expected ):
# check the image in the publisher's search result
img = find_child( "img.image", publ_sr )
if expected:
expected_image_url = flask_app.url_for( "get_image", image_type="publisher", image_id=publ_id )
image_url = img.get_attribute( "src" ).split( "?" )[0]
assert image_url == expected_image_url
else:
assert not img
def check_sr_image():
img = find_child( "img.image", publ_sr )
if expected:
expected_image_url = flask_app.url_for( "get_image", image_type="publisher", image_id=publ_id )
image_url = img.get_attribute( "src" ).split( "?" )[0]
return image_url == expected_image_url
else:
return not img
wait_for( 2, check_sr_image )
# check the image in the publisher's config
find_child( ".edit", publ_sr ).click()
@ -183,7 +174,7 @@ def test_images( webdriver, flask_app, dbconn ): #pylint: disable=too-many-state
# create an publisher with no image
create_publisher( { "name": "Test Publisher" } )
results = find_children( "#search-results .search-result" )
results = get_search_results()
assert len(results) == 1
publ_sr = results[0]
publ_id = publ_sr.get_attribute( "testing--publ_id" )
@ -229,9 +220,8 @@ def test_cascading_deletes( webdriver, flask_app, dbconn ):
def get_results( sr_type, expected_len ):
# NOTE: The UI will remove anything that has been deleted, so we need to
# give it a bit of time to finish doing this.
results = find_children( "#search-results .search-result" )
try:
results = [ find_child( ".name span", r ).text for r in results ]
results = get_search_result_names()
except StaleElementReferenceException:
return None
results = [ r for r in results if r.startswith( sr_type ) ]
@ -318,9 +308,9 @@ def test_unicode( webdriver, flask_app, dbconn ):
} )
# check that the new publisher is showing the Unicode content correctly
results = do_search( "japan" )
assert len( results ) == 1
_check_result( results[0], [
results = do_search( SEARCH_ALL_PUBLISHERS )
assert len(results) == 1
check_search_result( results[0], _check_sr, [
"japan = \u65e5\u672c",
"greece = \u0395\u03bb\u03bb\u03ac\u03b4\u03b1",
"http://xn--3e0b707e.com/"
@ -341,85 +331,105 @@ def test_clean_html( webdriver, flask_app, dbconn ):
}, toast_type="warning" )
# check that the HTML was cleaned
results = wait_for( 2,
lambda: find_children( "#search-results .search-result" )
)
assert len( results ) == 1
result = results[0]
_check_result( result, [ "name: bold xxx italic", "bad stuff here:", None ] )
assert find_child( ".name span" ).get_attribute( "innerHTML" ) \
sr = check_search_result( None, _check_sr, [
"name: bold xxx italic", "bad stuff here:", None
] )
assert find_child( ".name span", sr ).get_attribute( "innerHTML" ) \
== "name: <span> <b>bold</b> xxx <i>italic</i></span>"
assert check_toast( "warning", "Some values had HTML removed.", contains=True )
# update the publisher with new HTML content
edit_publisher( result, {
edit_publisher( sr, {
"name": "<div style='...'>updated</div>"
}, toast_type="warning" )
def check_result():
results = find_children( "#search-results .search-result" )
assert len( results ) == 1
result = results[0]
return find_child( ".name", result ).text == "updated"
wait_for( 2, check_result )
results = get_search_results()
assert len(results) == 1
wait_for( 2, lambda: find_child( ".name", sr ).text == "updated" )
assert check_toast( "warning", "Some values had HTML removed.", contains=True )
# ---------------------------------------------------------------------
def create_publisher( vals, toast_type="info" ):
"""Create a new publisher."""
# initialize
if toast_type:
set_toast_marker( toast_type )
# create the new publisher
find_child( "#menu .new-publisher" ).click()
dlg = wait_for_elem( 2, "#modal-form" )
for key,val in vals.items():
sel = ".{} {}".format( key , "textarea" if key == "description" else "input" )
sel = ".row.{} {}".format( key , "textarea" if key == "description" else "input" )
set_elem_text( find_child( sel, dlg ), val )
find_child( "button.ok", dlg ).click()
if toast_type:
# check that the new publisher was created successfully
wait_for( 2,
lambda: check_toast( toast_type, "created OK", contains=True )
)
wait_for_not_elem( 2, "#modal-form" )
def edit_publisher( result, vals, toast_type="info", expected_error=None ):
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
def edit_publisher( sr, vals, toast_type="info", expected_error=None ):
"""Edit a publisher's details."""
# update the specified publisher's details
find_child( ".edit", result ).click()
# initialize
if sr:
find_child( ".edit", sr ).click()
else:
pass # nb: we assume that the dialog is already on-screen
dlg = wait_for_elem( 2, "#modal-form" )
# update the specified publisher's details
for key,val in vals.items():
if key == "image":
if val:
data = base64.b64encode( open( val, "rb" ).read() )
data = "{}|{}".format( os.path.split(val)[1], data.decode("ascii") )
send_upload_data( data,
lambda: find_child( ".image img", dlg ).click()
lambda: find_child( ".row.image img", dlg ).click()
)
else:
find_child( ".remove-image", dlg ).click()
find_child( ".row.image .remove-image", dlg ).click()
else:
sel = ".{} {}".format( key , "textarea" if key == "description" else "input" )
sel = ".row.{} {}".format( key , "textarea" if key == "description" else "input" )
set_elem_text( find_child( sel, dlg ), val )
set_toast_marker( toast_type )
find_child( "button.ok", dlg ).click()
# check what happened
if expected_error:
# we were expecting an error, confirm the error message
check_error_msg( expected_error )
else:
# we were expecting the update to work, confirm this
expected = "updated OK" if sr else "created OK"
wait_for( 2,
lambda: check_toast( toast_type, "updated OK", contains=True )
lambda: check_toast( toast_type, expected, contains=True )
)
wait_for_not_elem( 2, "#modal-form" )
# ---------------------------------------------------------------------
def _check_result( result, expected ):
"""Check a result."""
assert find_child( ".name", result ).text == expected[0]
assert find_child( ".description", result ).text == expected[1]
elem = find_child( ".name a", result )
def _check_sr( sr, expected ):
"""Check a search result."""
# check the name and description
if find_child( ".name", sr ).text != expected[0]:
return False
if find_child( ".description", sr ).text != expected[1]:
return False
# check the publisher's link
elem = find_child( ".name a", sr )
if elem:
assert elem.get_attribute( "href" ) == expected[2]
if elem.get_attribute( "href" ) != expected[2]:
return False
else:
assert expected[2] is None
if expected[2] is not None:
return False
return True

@ -3,7 +3,7 @@
import urllib.request
import json
from asl_articles.tests.utils import init_tests, find_child, find_children, wait_for_elem, find_search_result
from asl_articles.tests.utils import init_tests, find_child, find_children, wait_for, wait_for_elem, find_search_result
from asl_articles.tests.react_select import ReactSelect
from asl_articles.tests.test_articles import create_article, edit_article
@ -72,23 +72,31 @@ def _check_scenarios( flask_app, all_scenarios, expected ):
for scenarios in expected:
all_scenarios.update( scenarios )
def check_scenarios( sr_name, expected ):
sr = find_search_result( sr_name )
sr_scenarios = [ s.text for s in find_children( ".scenario", sr ) ]
if sr_scenarios == expected:
return sr
return None
# check the scenarios in the UI
for article_no,scenarios in enumerate( expected ):
for article_no,expected_scenarios in enumerate( expected ):
# check the scenarios in the article's search result
sr = find_search_result( "article {}".format( 1+article_no ) )
sr_scenarios = [ s.text for s in find_children( ".scenario", sr ) ]
assert sr_scenarios == scenarios
sr_name = "article {}".format( 1+article_no )
sr = wait_for( 2,
lambda n=sr_name, e=expected_scenarios: check_scenarios( n, e )
)
# check the scenarios in the article's config
find_child( ".edit", sr ).click()
dlg = wait_for_elem( 2, "#modal-form" )
select = ReactSelect( find_child( ".scenarios .react-select", dlg ) )
assert select.get_multiselect_values() == scenarios
select = ReactSelect( find_child( ".row.scenarios .react-select", dlg ) )
assert select.get_multiselect_values() == expected_scenarios
# check that the list of available scenarios is correct
assert select.get_multiselect_choices() == \
sorted( all_scenarios.difference( scenarios ), key=lambda s: s.lower() )
sorted( all_scenarios.difference( expected_scenarios ), key=lambda s: s.lower() )
# close the dialog
find_child( "button.cancel", dlg ).click()

@ -6,7 +6,7 @@ from asl_articles.tests.test_publishers import create_publisher, edit_publisher
from asl_articles.tests.test_publications import create_publication, edit_publication
from asl_articles.tests.test_articles import create_article, edit_article
from asl_articles.tests.utils import init_tests, wait_for_elem, find_child, find_children, check_ask_dialog, \
do_search, get_result_names, find_search_result
do_search, get_search_result_names, find_search_result
# ---------------------------------------------------------------------
@ -149,7 +149,7 @@ def test_empty_search( webdriver, flask_app, dbconn ):
"""Test handling of an empty search string."""
# initialize
init_tests( webdriver, flask_app, dbconn, fixtures="search.json" )
init_tests( webdriver, flask_app, dbconn )
# search for an empty string
form = find_child( "#search-form" )
@ -367,7 +367,7 @@ def test_make_fts_query_string():
def _do_test_search( query, expected ):
"""Run a search and check the results."""
results = do_search( query )
assert set( get_result_names( results ) ) == set( expected )
assert set( get_search_result_names( results ) ) == set( expected )
return results
def _do_test_searches( queries, expected ):

@ -3,8 +3,8 @@
import urllib.request
import json
from asl_articles.tests.utils import init_tests, wait_for_elem, find_child, find_children, \
find_search_result, get_result_names
from asl_articles.tests.utils import init_tests, find_search_result, get_search_results, get_search_result_names, \
wait_for, wait_for_elem, find_child, find_children
from asl_articles.tests.react_select import ReactSelect
from asl_articles.tests.test_publications import create_publication, edit_publication
@ -100,7 +100,7 @@ def test_clean_html( webdriver, flask_app, dbconn ):
# ---------------------------------------------------------------------
def _check_tags( flask_app, expected ):
def _check_tags( flask_app, expected ): #pylint: disable=too-many-locals
"""Check the tags in the UI and database."""
# get the complete list of expected tags
@ -108,20 +108,25 @@ def _check_tags( flask_app, expected ):
for tags in expected.values():
expected_available.update( tags )
def check_tags( sr ):
name = get_search_result_names( [sr] )[ 0 ]
tags = [ t.text for t in find_children( ".tag", sr ) ]
if tags == expected[name]:
return name
return None
# check the tags in the UI
elems = find_children( "#search-results .search-result" )
assert set( get_result_names( elems ) ) == set( expected.keys() )
for sr in elems:
results = get_search_results()
assert set( get_search_result_names( results ) ) == set( expected.keys() )
for sr in results:
# check the tags in the search result
name = find_child( ".name span", sr ).text
tags = [ t.text for t in find_children( ".tag", sr ) ]
assert tags == expected[ name ]
name = wait_for( 2, lambda sr=sr: check_tags( sr ) )
# check the tags in the publication/article
find_child( ".edit", sr ).click()
dlg = wait_for_elem( 2, "#modal-form" )
select = ReactSelect( find_child( ".tags .react-select", dlg ) )
select = ReactSelect( find_child( ".row.tags .react-select", dlg ) )
assert select.get_multiselect_values() == expected[ name ]
# check that the list of available tags is correct
@ -135,7 +140,7 @@ def _check_tags( flask_app, expected ):
return [] if tags is None else tags
# check the tags in the database
for sr in elems:
for sr in results:
if sr.text.startswith( "publication" ):
pub_id = sr.get_attribute( "testing--pub_id" )
url = flask_app.url_for( "get_publication", pub_id=pub_id )

@ -10,10 +10,11 @@ import sqlalchemy.orm
import sqlalchemy.sql.expression
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.remote.webelement import WebElement
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import NoSuchElementException, StaleElementReferenceException
from asl_articles import search
import asl_articles.models
@ -109,23 +110,51 @@ def do_search( query ):
# return the results
wait_for( 2, lambda: get_seqno() != curr_seqno )
return get_search_results()
def get_search_results():
"""Get the search results."""
return find_children( "#search-results .search-result" )
def get_result_names( results ):
"""Get the names from a list of search results."""
return [
find_child( ".name span", r ).text
for r in results
]
def get_search_result_names( results=None ):
"""Get the names from the search results."""
if not results:
results = get_search_results()
return [ find_child( ".name span", r ).text for r in results ]
def find_search_result( name, results=None ):
"""Find a search result."""
if not results:
results = find_children( "#search-results .search-result" )
results = get_search_results()
results = [ r for r in results if find_child( ".name span", r ).text == name ]
assert len(results) == 1
return results[0]
def check_search_result( sr, check, expected ):
"""Check a search result in the UI."""
# figure out which search result to check
if not sr:
# NOTE: If the caller doesn't explicitly provide a search result, we assume we're working with
# a single search result that is already on-screen.
results = get_search_results()
assert len(results) == 1
sr = results[0]
elif isinstance( sr, str ):
sr = find_search_result( sr )
else:
assert isinstance( sr, WebElement )
# wait for the search result to match what we expect
def check_sr():
try:
if check( sr, expected ):
return sr
return None
except StaleElementReferenceException:
return None # nb: the web page updated while we were checking it
return wait_for( 2, check_sr )
# ---------------------------------------------------------------------
def wait_for( timeout, func ):
@ -141,6 +170,12 @@ def wait_for_elem( timeout, sel, visible=True ):
func( ( By.CSS_SELECTOR, sel ) )
)
def wait_for_not_elem( timeout, sel ):
"""Wait for an element to be removed from the DOM."""
return WebDriverWait( _webdriver, timeout, 0.1 ).until(
EC.invisibility_of_element_located( ( By.CSS_SELECTOR, sel ) )
)
# ---------------------------------------------------------------------
def find_child( sel, parent=None ):

Loading…
Cancel
Save