Added tests to check a publication/article's parent.

master
Pacman Ghost 5 years ago
parent 2c4e1bbd4a
commit cb4ecd7cee
  1. 12
      asl_articles/tests/fixtures/parents.json
  2. 65
      asl_articles/tests/test_articles.py
  3. 65
      asl_articles/tests/test_publications.py
  4. 15
      asl_articles/tests/utils.py
  5. 11
      web/src/App.js
  6. 6
      web/src/ArticleSearchResult.js
  7. 7
      web/src/PublicationSearchResult.js
  8. 4
      web/src/PublisherSearchResult.js

@ -0,0 +1,12 @@
{
"publisher": [
{ "publ_id": 1, "publ_name": "Multiman Publishing" }
],
"publication": [
{ "pub_id": 1, "pub_name": "ASL Journal" }
]
}

@ -1,8 +1,12 @@
""" Test article operations. """
import urllib.request
import json
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, check_ask_dialog, check_error_msg
from asl_articles.tests.utils import ReactSelect
# ---------------------------------------------------------------------
@ -129,6 +133,59 @@ def test_delete_article( webdriver, flask_app, dbconn ):
# ---------------------------------------------------------------------
def test_parent_publisher( webdriver, flask_app, dbconn ):
"""Test setting an article's parent publication."""
# initialize
init_tests( webdriver, flask_app, dbconn, "parents.json" )
article_sr = None
def check_results( expected_parent ):
# check that the parent publication was updated in the UI
nonlocal article_sr
elem = find_child( ".title .publication", article_sr )
if expected_parent:
assert elem.text == "({})".format( expected_parent[1] )
else:
assert elem is None
# check that the parent publication was updated in the database
article_id = article_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]
else:
assert article["pub_id"] is 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 )
if expected_parent:
assert elem.text == "({})".format( expected_parent[1] )
else:
assert elem is None
# create an article with no parent publication
_create_article( { "title": "My Article" } )
results = find_children( "#search-results .search-result" )
assert len(results) == 1
article_sr = results[0]
check_results( None )
# change the article to have a publication
_edit_article( article_sr, { "publication": "ASL Journal" } )
check_results( (1, "ASL Journal") )
# change the article back to having no publication
_edit_article( article_sr, { "publication": "(none)" } )
check_results( None )
# ---------------------------------------------------------------------
def test_unicode( webdriver, flask_app, dbconn ):
"""Test Unicode content."""
@ -219,8 +276,12 @@ def _edit_article( result, vals, toast_type="info", expected_error=None ):
find_child( ".edit", result ).click()
dlg = wait_for_elem( 2, "#modal-form" )
for k,v in vals.items():
sel = ".{} {}".format( k , "textarea" if k == "snippet" else "input" )
set_elem_text( find_child( sel, dlg ), v )
if k == "publication":
select = ReactSelect( find_child( ".publication .react-select", dlg ) )
select.select_by_name( v )
else:
sel = ".{} {}".format( k , "textarea" if k == "snippet" else "input" )
set_elem_text( find_child( sel, dlg ), v )
set_toast_marker( toast_type )
find_child( "button.ok", dlg ).click()
if expected_error:

@ -1,8 +1,12 @@
""" Test publication operations. """
import urllib.request
import json
from asl_articles.tests.utils import init_tests, init_db, do_search, get_result_names, \
wait_for, wait_for_elem, find_child, find_children, set_elem_text, \
set_toast_marker, check_toast, check_ask_dialog, check_error_msg
from asl_articles.tests.utils import ReactSelect
# ---------------------------------------------------------------------
@ -131,6 +135,59 @@ def test_delete_publication( webdriver, flask_app, dbconn ):
# ---------------------------------------------------------------------
def test_parent_publisher( webdriver, flask_app, dbconn ):
"""Test setting a publication's parent publisher."""
# initialize
init_tests( webdriver, flask_app, dbconn, "parents.json" )
pub_sr = None
def check_results( expected_parent ):
# check that the parent publisher was updated in the UI
nonlocal pub_sr
elem = find_child( ".name .publisher", pub_sr )
if expected_parent:
assert elem.text == "({})".format( expected_parent[1] )
else:
assert elem is None
# check that the parent publisher was updated in the database
pub_id = pub_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]
else:
assert pub["publ_id"] is 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 )
if expected_parent:
assert elem.text == "({})".format( expected_parent[1] )
else:
assert elem is None
# create a publication with no parent publisher
_create_publication( { "name": "MMP News" } )
results = find_children( "#search-results .search-result" )
assert len(results) == 1
pub_sr = results[0]
check_results( None )
# change the publication to have a publisher
_edit_publication( pub_sr, { "publisher": "Multiman Publishing" } )
check_results( (1, "Multiman Publishing") )
# change the publication back to having no publisher
_edit_publication( pub_sr, { "publisher": "(none)" } )
check_results( None )
# ---------------------------------------------------------------------
def test_cascading_deletes( webdriver, flask_app, dbconn ):
"""Test cascading deletes."""
@ -260,8 +317,12 @@ def _edit_publication( result, vals, toast_type="info", expected_error=None ):
find_child( ".edit", result ).click()
dlg = wait_for_elem( 2, "#modal-form" )
for k,v in vals.items():
sel = ".{} {}".format( k , "textarea" if k == "description" else "input" )
set_elem_text( find_child( sel, dlg ), v )
if k == "publisher":
select = ReactSelect( find_child( ".publisher .react-select", dlg ) )
select.select_by_name( v )
else:
sel = ".{} {}".format( k , "textarea" if k == "description" else "input" )
set_elem_text( find_child( sel, dlg ), v )
set_toast_marker( toast_type )
find_child( "button.ok", dlg ).click()
if expected_error:

@ -203,6 +203,21 @@ def _make_toast_stored_msg_id( toast_type ):
# ---------------------------------------------------------------------
class ReactSelect:
"""Control a react-select droplist."""
def __init__( self, elem ):
self.select = elem
def select_by_name( self, val ):
"""Select an option by name."""
find_child( "svg", self.select ).click()
options = [ e for e in find_children( ".react-select__option", self.select )
if e.text == val
]
assert len( options ) == 1
options[0].click()
# ---------------------------------------------------------------------
def set_elem_text( elem, val ):
"""Set the text for an element."""
elem.clear()

@ -31,13 +31,13 @@ export default class App extends React.Component
// initialize
const args = queryString.parse( window.location.search ) ;
this._storeMsgs = process.env.REACT_APP_TEST_MODE && args.store_msgs ;
this._storeMsgs = this.isTestMode() && args.store_msgs ;
// figure out the base URL of the Flask backend server
// NOTE: We allow the caller to do this since the test suite will usually spin up
// it's own Flask server, but talks to an existing React server, so we need some way
// for pytest to change which Flask server the React frontend code should tak to.
this._flaskBaseUrl = process.env.REACT_APP_TEST_MODE ? args._flask : null ;
this._flaskBaseUrl = this.isTestMode() ? args._flask : null ;
if ( ! this._flaskBaseUrl )
this._flaskBaseUrl = process.env.REACT_APP_FLASK_URL ;
}
@ -210,4 +210,11 @@ export default class App extends React.Component
return url ;
}
isTestMode() { return process.env.REACT_APP_TEST_MODE ; }
setTestAttribute( obj, attrName, attrVal ) {
// set an attribute on an element (for testing porpoises)
if ( obj && this.isTestMode() )
obj.setAttribute( "testing--"+attrName, attrVal ) ;
}
}

@ -15,7 +15,9 @@ export class ArticleSearchResult extends React.Component
const pub = gAppRef.caches.publications[ this.props.data.pub_id ] ;
// NOTE: The "title" field is also given the CSS class "name" so that the normal CSS will apply to it.
// Some tests also look for a generic ".name" class name when checking search results.
return ( <div className="search-result article">
return ( <div className="search-result article"
ref = { r => gAppRef.setTestAttribute( r, "article_id", this.props.data.article_id ) }
>
<div className="title name"> { makeOptionalLink( this.props.data.article_title, this.props.data.article_url ) }
{ pub && <span className="publication"> ({pub.pub_name}) </span> }
<img src="/images/edit.png" className="edit" onClick={this.onEditArticle.bind(this)} alt="Edit this article." />
@ -94,7 +96,7 @@ export class ArticleSearchResult extends React.Component
<input type="text" defaultValue={vals.article_subtitle} ref={(r) => refs.article_subtitle=r} />
</div>
<div className="row publication"> <label> Publication: </label>
<Select options={publications} isSearchable={true}
<Select className="react-select" classNamePrefix="react-select" options={publications} isSearchable={true}
defaultValue = { publications[ currPub ] }
ref = { (r) => refs.pub_id=r }
/>

@ -13,7 +13,9 @@ export class PublicationSearchResult extends React.Component
render() {
const publ = gAppRef.caches.publishers[ this.props.data.publ_id ] ;
return ( <div className="search-result publication">
return ( <div className="search-result publication"
ref = { r => gAppRef.setTestAttribute( r, "pub_id", this.props.data.pub_id ) }
>
<div className="name"> { makeOptionalLink( this._makeDisplayName(), this.props.data.pub_url ) }
{ publ && <span className="publisher"> ({publ.publ_name}) </span> }
<img src="/images/edit.png" className="edit" onClick={this.onEditPublication.bind(this)} alt="Edit this publication." />
@ -87,6 +89,7 @@ export class PublicationSearchResult extends React.Component
publishers.sort( (lhs,rhs) => {
return ReactDOMServer.renderToStaticMarkup( lhs.label ).localeCompare( ReactDOMServer.renderToStaticMarkup( rhs.label ) ) ;
} ) ;
const content = <div>
<div className="row name"> <label> Name: </label>
<input type="text" defaultValue={vals.pub_name} ref={(r) => refs.pub_name=r} />
@ -95,7 +98,7 @@ export class PublicationSearchResult extends React.Component
<input type="text" defaultValue={vals.pub_edition} ref={(r) => refs.pub_edition=r} />
</div>
<div className="row publisher"> <label> Publisher: </label>
<Select options={publishers} isSearchable={true}
<Select className="react-select" classNamePrefix="react-select" options={publishers} isSearchable={true}
defaultValue = { publishers[ currPubl ] }
ref = { (r) => refs.publ_id=r }
/>

@ -10,7 +10,9 @@ export class PublisherSearchResult extends React.Component
{
render() {
return ( <div className="search-result publisher">
return ( <div className="search-result publisher"
ref = { r => gAppRef.setTestAttribute( r, "publ_id", this.props.data.publ_id ) }
>
<div className="name"> { makeOptionalLink( this.props.data.publ_name, this.props.data.publ_url ) }
<img src="/images/edit.png" className="edit" onClick={this.onEditPublisher.bind(this)} alt="Edit this publisher." />
<img src="/images/delete.png" className="delete" onClick={this.onDeletePublisher.bind(this)} alt="Delete this publisher." />

Loading…
Cancel
Save