def check_article_order(expected): # check the article order in the database articles = defaultdict(list) query = dbconn.execute("SELECT pub_name, article_title, article_seqno" " FROM article LEFT JOIN publication" " ON article.pub_id = publication.pub_id" " ORDER BY article.pub_id, article_seqno") for row in query: articles[row[0]].append((row[1], row[2])) assert articles == expected # check the article order in the UI results = do_search(SEARCH_ALL) for pub_name in expected: if not pub_name: continue sr = find_search_result(pub_name, results) select_sr_menu_option(sr, "edit") dlg = wait_for_elem(2, "#publication-form") articles = [ a.text for a in find_children(".articles li.draggable", dlg) ] find_child(".cancel", dlg).click() assert articles == [a[0] for a in expected[pub_name]]
def edit_article(sr, vals, toast_type="info", expected_error=None, expected_constraints=None): #pylint: disable=too-many-branches """Edit a article's details.""" # initialize if sr: select_sr_menu_option(sr, "edit") else: pass # nb: we assume that the dialog is already on-screen dlg = wait_for_elem(2, "#article-form") # update the specified article's details _update_values(dlg, vals) 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) return dlg # nb: the dialog is left on-screen elif expected_constraints: # we were expecting constraint warnings, confirm them check_constraint_warnings("Do you want to update this article?", expected_constraints, "cancel") return dlg # nb: the dialog is left on-screen 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, expected, contains=True)) wait_for_not_elem(2, "#article-form") return None
def _update_values(dlg, vals): """Update a publication's values in the form.""" for key, val in vals.items(): if key == "image": if val: change_image(dlg, val) else: remove_image(dlg) elif key == "name": 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(".row.publisher .react-select", dlg)) select.select_by_name(val) elif key == "tags": select = ReactSelect(find_child(".row.tags .react-select", dlg)) select.update_multiselect_values(*val) else: if key == "edition": sel = "input.edition" elif key == "description": sel = ".row.description textarea" else: sel = ".row.{} input".format(key) set_elem_text(find_child(sel, dlg), val)
def remove_multiselect_value(self, val): """Remove a multi-select value.""" for elem in find_children(".react-select__multi-value", self.select): if elem.text == val: find_child(".react-select__multi-value__remove", elem).click() return assert False, "Can't find multi-select value: {}".format(val)
def edit_publisher( sr, vals, toast_type="info", expected_error=None ): """Edit a publisher's details.""" # initialize if sr: select_sr_menu_option( sr, "edit" ) else: pass # nb: we assume that the dialog is already on-screen dlg = wait_for_elem( 2, "#publisher-form" ) # update the specified publisher's details _update_values( dlg, vals ) 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, expected, contains=True ) ) wait_for_not_elem( 2, "#publisher-form" )
def create_article(vals, toast_type="info", expected_error=None, expected_constraints=None, dlg=None): """Create a new article.""" # initialize set_toast_marker(toast_type) # create the new article if not dlg: select_main_menu_option("new-article") dlg = wait_for_elem(2, "#article-form") _update_values(dlg, vals) 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) return dlg # nb: the dialog is left on-screen elif expected_constraints: # we were expecting constraint warnings, confirm them check_constraint_warnings("Do you want to create this article?", expected_constraints, "cancel") return dlg # nb: the dialog is left on-screen else: # we were expecting the create to work, confirm this wait_for(2, lambda: check_toast(toast_type, "created OK", contains=True)) wait_for_not_elem(2, "#article-form") return None
def check_publication_highlights( query, expected, name, description, tags ): results = _do_test_search( query, [expected] ) assert len(results) == 1 sr = results[0] assert find_highlighted( find_child( ".name", sr ) ) == name assert find_highlighted( find_child( ".description", sr ) ) == description assert find_highlighted( find_children( ".tag", sr ) ) == tags
def create_publisher( vals, toast_type="info", expected_error=None, dlg=None ): """Create a new publisher.""" # initialize set_toast_marker( toast_type ) # create the new publisher if not dlg: select_main_menu_option( "new-publisher" ) dlg = wait_for_elem( 2, "#publisher-form" ) _update_values( dlg, vals ) 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 ) return dlg # nb: the dialog is left on-screen else: # we were expecting the create to work, confirm this wait_for( 2, lambda: check_toast( toast_type, "created OK", contains=True ) ) wait_for_not_elem( 2, "#publisher-form" ) return None
def test_clean_html( webdriver, flask_app, dbconn ): """Test cleaning HTML content.""" # initialize init_tests( webdriver, flask_app, dbconn ) replace = [ "[\u00ab\u00bb\u201c\u201d\u201e\u201f foo\u2014bar \u2018\u2019\u201a\u201b\u2039\u203a]", "[\"\"\"\"\"\" foo - bar '''''']" ] # create a publisher with HTML content create_publisher( { "name": "name: <span onclick='boo!'> <b>bold</b> <xxx>xxx</xxx> <i>italic</i> {}".format( replace[0] ), "description": "bad stuff here: <script>HCF</script> {}".format( replace[0] ) }, toast_type="warning" ) # check that the HTML was cleaned sr = check_search_result( None, _check_sr, [ "name: bold xxx italic {}".format( replace[1] ), "bad stuff here: {}".format( replace[1] ), None ] ) assert find_child( ".name", sr ).get_attribute( "innerHTML" ) \ == "name: <span> <b>bold</b> xxx <i>italic</i> {}</span>".format( replace[1] ) assert check_toast( "warning", "Some values had HTML cleaned up.", contains=True ) # update the publisher with new HTML content edit_publisher( sr, { "name": "<div onclick='...'>updated</div>" }, toast_type="warning" ) 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 cleaned up.", contains=True )
def check_result(sr, expected_parent): #pylint: disable=too-many-return-statements # check that the parent publication was updated in the UI elem = find_child(".header .publication", sr) if expected_parent: if elem.text != "{}".format(expected_parent[1]): return None else: if elem is not None: return None # check that the parent publication was updated in the database 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: if article["pub_id"] != expected_parent[0]: return None else: 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 sr = results[0] elem = find_child(".header .publication", sr) if expected_parent: if elem.text != "{}".format(expected_parent[1]): return None else: if elem is not None: return None return sr
def select_by_name(self, val): """Select an option by name.""" find_child(".react-select__dropdown-indicator", 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 _check_previewable_images( sr ): """Check that previewable images are working correctly.""" images = list( find_children( "a.preview img", sr ) ) assert len(images) == 2 for img in images: assert find_child( ".jquery-image-zoom" ) is None img.click() preview = wait_for( 2, lambda: find_child( ".jquery-image-zoom" ) ) call_with_retry( preview.click, [ElementClickInterceptedException] ) wait_for( 2, lambda: find_child( ".jquery-image-zoom" ) is None )
def check_article_highlights( query, expected, title, subtitle, snippet, authors, scenarios, tags ): results = _do_test_search( query, [expected] ) assert len(results) == 1 sr = results[0] assert find_highlighted( find_child( ".title", sr ) ) == title assert find_highlighted( find_child( ".subtitle", sr ) ) == subtitle assert find_highlighted( find_child( ".snippet", sr ) ) == snippet assert find_highlighted( find_children( ".author", sr ) ) == authors assert find_highlighted( find_children( ".scenario", sr ) ) == scenarios assert find_highlighted( find_children( ".tag", sr ) ) == tags
def check_parent_in_dlg(dlg, pub, publ): """Check the article's parent publication/publication in the edit dialog.""" if pub: select = find_child(".row.publication .react-select", dlg) assert select.is_displayed() assert select.text == pub elif publ: select = find_child(".row.publisher .react-select", dlg) assert select.is_displayed() assert select.text == publ else: assert False, "At least one publication/publisher must be specified."
def check_publications( results, expected ): for publ_name,pub_name in expected.items(): publ_sr = find_search_result( publ_name, results ) pubs = find_child( ".collapsible", publ_sr ) if pub_name: # check that the publication appears in the publisher's search result assert find_child( ".caption", pubs ).text == "Publications:" pubs = find_children( "li", pubs ) assert len(pubs) == 1 assert pubs[0].text == pub_name else: # check that the publisher has no associated publications assert pubs is None
def click_on_publication( sr, expected_pub, expected_sr ): classes = sr.get_attribute( "class" ).split() if "article" in classes: elem = find_child( ".header .publication", sr ) elif "publisher" in classes: elems = find_children( ".content .collapsible li", sr ) elem = elems[0] # nb: we just use the first one else: assert "publication" in classes elem = find_child( ".header .name", sr ) assert elem.text == expected_pub elem.click() wait_for( 2, lambda: get_search_result_names() == expected_sr )
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 expected_available = set() 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 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 = wait_for( 2, lambda sr=sr: check_tags( sr ) ) # check the tags in the publication/article select_sr_menu_option( sr, "edit" ) dlg = wait_for_elem( 2, ".modal-form" ) 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 # NOTE: We don't bother checking the tag order here. assert set( select.get_multiselect_choices() ) == expected_available.difference( expected[name] ) # close the dialog find_child( "button.cancel", dlg ).click() def fixup_tags( tags ): return [] if tags is None else tags # check the tags in the database 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 ) pub = json.load( urllib.request.urlopen( url ) ) assert expected[ pub["pub_name"] ] == fixup_tags( pub["pub_tags"] ) elif sr.text.startswith( "article" ): 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 ) ) assert expected[ article["article_title"] ] == fixup_tags( article["article_tags"] )
def check_articles(results, expected): for pub_name, article_title in expected.items(): pub_sr = find_search_result(pub_name, results) articles = find_child(".collapsible", pub_sr) if article_title: # check that the article appears in the publication's search result assert find_child(".caption", articles).text == "Articles:" articles = find_children("li", articles) assert len(articles) == 1 assert articles[0].text == article_title # check that the "edit publication" dialog is correct select_sr_menu_option(pub_sr, "edit") dlg = find_child(".MuiDialog-root") articles = find_children(".articles li", dlg) assert len(articles) == 1 assert articles[0].text == article_title find_child("button.cancel", dlg).click() else: # check that the publication has no associated articles assert articles is None # check that the "edit publication" dialog is correct select_sr_menu_option(pub_sr, "edit") dlg = find_child(".MuiDialog-root") articles = find_children(".articles", dlg) assert len(articles) == 0 find_child("button.cancel", dlg).click()
def do_test( msg_type ): # check that the startup message was shown in the UI correctly set_toast_marker( msg_type ) assert startup_msgs[ msg_type ] == [] asl_articles.startup.log_startup_msg( msg_type, "TEST: {}", msg_type ) webdriver.refresh() expected = startup_msgs[ msg_type ][0] wait_for( 2, lambda: check_toast( msg_type, expected ) ) startup_msgs[ msg_type ] = [] # check if the webapp started up or not if msg_type == "error": assert not find_child( "#search-form" ) else: assert find_child( "#search-form" )
def check_parent_in_sr(sr, pub, publ): """Check the article's parent publication/publisher in a search result.""" if pub: elem = wait_for(2, lambda: find_child(".header a.publication", sr)) assert elem.is_displayed() assert elem.text == pub assert re.search(r"^http://.+?/publication/\d+", elem.get_attribute("href")) elif publ: elem = wait_for(2, lambda: find_child(".header a.publisher", sr)) assert elem.is_displayed() assert elem.text == publ assert re.search(r"^http://.+?/publisher/\d+", elem.get_attribute("href")) else: assert False, "At least one publication/publisher must be specified."
def click_on_article( sr, expected_pub, expected_sr ): elems = find_children( ".content .collapsible li", sr ) elem = elems[0] # nb: we just use the first one assert elem.text == expected_pub elem.click() wait_for( 2, lambda: get_search_result_names() == expected_sr ) assert find_child( "#search-form input.query" ).get_attribute( "value" ) == ""
def do_check(): elem = find_child(".article_date", article_sr) article_id = article_sr.get_attribute("testing--article_id") row = get_article_row(dbconn, article_id, ["article_date"]) if has_date: return elem.text == article_date and row[0] == article_date else: return not elem and not row[0]
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
def _update_values(dlg, vals): """Update an article's values in the form.""" for key, val in vals.items(): if key == "image": if val: change_image(dlg, val) else: remove_image(dlg) elif key in ("publication", "publisher"): row = find_child(".row.{}".format(key), dlg) select = ReactSelect(find_child(".react-select", row)) if not select.select.is_displayed(): key2 = "publisher" if key == "publication" else "publication" row2 = find_child(".row.{}".format(key2), dlg) find_child("label.parent-mode", row2).click() wait_for(2, select.select.is_displayed) select.select_by_name(val) elif key in ["authors", "scenarios", "tags"]: select = ReactSelect( find_child(".row.{} .react-select".format(key), dlg)) select.update_multiselect_values(*val) else: if key == "snippet": sel = ".row.snippet textarea" elif key == "pageno": sel = "input.pageno" else: sel = ".row.{} input".format(key) set_elem_text(find_child(sel, dlg), val)
def do_check_image(sr, expected): img = find_child("img.image", sr) if img: assert expected image_url = img.get_attribute("src") resp = urllib.request.urlopen(image_url).read() assert resp == image_data[expected] else: assert not expected
def get_multiselect_choices(self): """Get the available multi-select choices.""" btn = find_child(".react-select__dropdown-indicator", self.select) btn.click() # show the dropdown choices = [ e.text for e in find_children(".react-select__option", self.select) ] btn.click() # close the dropdown return choices
def test_clean_html(webdriver, flask_app, dbconn): """Test cleaning HTML content.""" # initialize init_tests(webdriver, flask_app, dbconn) replace = [ "[\u00ab\u00bb\u201c\u201d\u201e\u201f foo\u2014bar \u2018\u2019\u201a\u201b\u2039\u203a]", "[\"\"\"\"\"\" foo - bar '''''']" ] # create a article with HTML content create_article( { "title": "title: <span onclick='boo!'> <b>bold</b> <xxx>xxx</xxx> <i>italic</i> {}" .format(replace[0]), "subtitle": "<i>italicized subtitle</i> {}".format(replace[0]), "snippet": "bad stuff here: <script>HCF</script> {}".format(replace[0]) }, toast_type="warning") # check that the HTML was cleaned sr = check_search_result(None, _check_sr, [ "title: bold xxx italic {}".format( replace[1]), "italicized subtitle {}".format(replace[1]), "bad stuff here: {}".format(replace[1]), "", [], [], None ]) assert find_child( ".title", sr ).get_attribute( "innerHTML" ) \ == "title: <span> <b>bold</b> xxx <i>italic</i> {}</span>".format( replace[1] ) assert find_child( ".subtitle", sr ).get_attribute( "innerHTML" ) \ == "<i>italicized subtitle</i> {}".format( replace[1] ) assert check_toast("warning", "Some values had HTML cleaned up.", contains=True) # update the article with new HTML content edit_article(sr, {"title": "<div onclick='...'>updated</div>"}, toast_type="warning") wait_for(2, lambda: get_search_result_names() == ["updated"]) assert check_toast("warning", "Some values had HTML cleaned up.", contains=True)
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( "a.open-link", sr ) if expected[2]: assert elem if elem.get_attribute( "href" ) != expected[2]: return False else: assert elem is None return True
def _check_scenarios(flask_app, all_scenarios, expected): """Check the scenarios of the test articles.""" # update the complete list of scenarios # NOTE: Unlike tags, scenarios remain in the database even if no-one is referencing them, # so we need to track them over the life of the entire series of tests. 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, expected_scenarios in enumerate(expected): # check the scenarios in the article's search result 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 select_sr_menu_option(sr, "edit") dlg = wait_for_elem(2, "#article-form") 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( expected_scenarios ), key=lambda s: s.lower() ) # close the dialog find_child("button.cancel", dlg).click() # check the scenarios in the database url = flask_app.url_for("get_scenarios") scenarios = json.load(urllib.request.urlopen(url)) assert set(_make_scenario_display_name(a) for a in scenarios.values()) == all_scenarios
def _update_values( dlg, vals ): """Update a publishers's values in the form.""" for key,val in vals.items(): if key == "image": if val: change_image( dlg, val ) else: remove_image( dlg ) else: sel = ".row.{} {}".format( key , "textarea" if key == "description" else "input" ) set_elem_text( find_child( sel, dlg ), val )