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 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 test_search_publications( webdriver, flask_app, dbconn ): """Test searching publications.""" # initialize init_tests( webdriver, flask_app, dbconn ) # test searching publication names/descriptions _do_test_searches( ["journal","good"], [] ) create_publication( { "name": "ASL Journal", "description": "A pretty good magazine." } ) _do_test_searches( ["journal","good"], ["ASL Journal"] ) # edit the publication sr = find_search_result( "ASL Journal" ) edit_publication( sr, { "name": "ASL Magazine", "description": "Not a bad magazine." } ) _do_test_searches( ["journal","good"], [] ) _do_test_searches( ["magazine","bad"], ["ASL Magazine"] ) # delete the publication sr = find_search_result( "ASL Magazine" ) select_sr_menu_option( sr, "delete" ) check_ask_dialog( "Delete this publication?", "ok" ) _do_test_searches( ["journal","good","magazine","bad"], [] )
def test_search_articles( webdriver, flask_app, dbconn ): """Test searching articles.""" # initialize init_tests( webdriver, flask_app, dbconn ) # test searching article titles/subtitles/snippets _do_test_searches( ["low","some","game"], [] ) create_article( { "title": "Hit 'Em High, Or Hit 'Em Low", "subtitle": "Some things about light mortars you might like to know", "snippet": "Light mortars in ASL can be game winners." } ) _do_test_searches( ["low","some","game"], ["Hit 'Em High, Or Hit 'Em Low"] ) # edit the article sr = find_search_result( "Hit 'Em High, Or Hit 'Em Low" ) edit_article( sr, { "title": "Hit 'Em Hard", "subtitle": "Where it hurts!", "snippet": "Always the best way to do things." } ) _do_test_searches( ["low","some","game"], [] ) _do_test_searches( ["hard","hurt","best"], ["Hit 'Em Hard"] ) # delete the article sr = find_search_result( "Hit 'Em Hard" ) select_sr_menu_option( sr, "delete" ) check_ask_dialog( "Delete this article?", "ok" ) _do_test_searches( ["hard","hurt","best"], [] )
def do_test( publ_name, expected_warning, expected_deletions ): # initialize load_fixtures( session, "cascading-deletes-1.json" ) results = do_search( SEARCH_ALL ) # delete the specified publisher sr = find_search_result( publ_name, results ) select_sr_menu_option( sr, "delete" ) check_ask_dialog( ( "Delete this publisher?", publ_name, expected_warning ), "ok" ) # check that deleted associated publications/articles were removed from the UI def check_publications(): check_results( "publication", [ "2", "3", "4", "5a", "5b", "6a", "6b", "7a", "7b", "8a", "8b" ], expected_deletions ) def check_articles(): check_results( "article", [ "3", "4a", "4b", "6a", "7a", "7b", "8a.1", "8a.2", "8b.1", "8b.2" ], expected_deletions ) check_publications() check_articles() # check that associated publications/articles were removed from the database results = do_search( SEARCH_ALL ) check_publications() check_articles()
def do_test(pub_name, expected_warning, expected_articles): # initialize load_fixtures(session, "cascading-deletes-2.json") results = do_search(SEARCH_ALL) # delete the specified publication sr = find_search_result(pub_name, results) select_sr_menu_option(sr, "delete") check_ask_dialog( ("Delete this publication?", pub_name, expected_warning), "ok") def check_results(): results = wait_for(2, lambda: get_results(len(expected_articles))) assert set(results) == set(expected_articles) 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. try: results = get_search_result_names() except StaleElementReferenceException: return None results = [r for r in results if r.startswith("article")] if len(results) == expected_len: return results return None # check that deleted associated articles were removed from the UI check_results() # check that associated articles were removed from the database results = do_search(SEARCH_ALL_ARTICLES) check_results()
def test_delete_article(webdriver, flask_app, dbconn): """Test deleting articles.""" # initialize init_tests(webdriver, flask_app, dbconn, fixtures="articles.json") # start to delete an article, but cancel the operation article_name = "Smoke Gets In Your Eyes" results = do_search(SEARCH_ALL_ARTICLES) result_names = get_search_result_names(results) sr = find_search_result(article_name, results) select_sr_menu_option(sr, "delete") check_ask_dialog(("Delete this article?", article_name), "cancel") # check that search results are unchanged on-screen results2 = get_search_results() assert results2 == results # check that the search results are unchanged in the database results3 = do_search(SEARCH_ALL_ARTICLES) assert get_search_result_names(results3) == result_names # delete the article sr = find_search_result(article_name, results3) select_sr_menu_option(sr, "delete") set_toast_marker("info") 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 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_ARTICLES) assert article_name not in get_search_result_names(results)
def test_delete_publication(webdriver, flask_app, dbconn): """Test deleting publications.""" # initialize init_tests(webdriver, flask_app, dbconn, fixtures="publications.json") # start to delete a publication, but cancel the operation article_title = "ASL Journal (2)" results = do_search(SEARCH_ALL_PUBLICATIONS) result_names = get_search_result_names(results) sr = find_search_result(article_title, results) select_sr_menu_option(sr, "delete") check_ask_dialog(("Delete this publication?", article_title), "cancel") # check that search results are unchanged on-screen results2 = get_search_results() assert results2 == results # check that the search results are unchanged in the database results3 = do_search(SEARCH_ALL_PUBLICATIONS) assert get_search_result_names(results3) == result_names # delete the publication sr = find_search_result(article_title, results3) select_sr_menu_option(sr, "delete") set_toast_marker("info") check_ask_dialog(("Delete this publication?", article_title), "ok") wait_for(2, lambda: check_toast("info", "The publication was deleted.")) # check that search result was removed on-screen wait_for(2, lambda: article_title not in get_search_result_names()) # check that the search result was deleted from the database results = do_search(SEARCH_ALL_PUBLICATIONS) assert article_title not in get_search_result_names(results)
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 test_search_publishers( webdriver, flask_app, dbconn ): """Test searching publishers.""" # initialize init_tests( webdriver, flask_app, dbconn ) # test searching publisher names/descriptions _do_test_searches( ["hill","original"], [] ) create_publisher( { "name": "Avalon Hill", "description": "The original ASL vendor." } ) _do_test_searches( ["hill","original"], ["Avalon Hill"] ) # edit the publisher sr = find_search_result( "Avalon Hill" ) edit_publisher( sr, { "name": "Avalon Mountain", "description": "The first ASL vendor." } ) _do_test_searches( ["hill","original"], [] ) _do_test_searches( ["mountain","first"], ["Avalon Mountain"] ) # delete the publisher sr = find_search_result( "Avalon Mountain" ) select_sr_menu_option( sr, "delete" ) check_ask_dialog( "Delete this publisher?", "ok" ) _do_test_searches( ["hill","original","mountain","first"], [] )
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 test_publication_lists( webdriver, flask_app, dbconn ): """Test showing publications that belong a publisher.""" # initialize init_tests( webdriver, flask_app, dbconn, fixtures="publishers.json" ) 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 # check that the publishers have no publications associated with them results = do_search( SEARCH_ALL_PUBLISHERS ) publ_name1, publ_name2 = "Avalon Hill", "Multiman Publishing" check_publications( results, { publ_name1: None, publ_name2: None } ) # create a publication that has no parent publisher create_publication( { "name": "no parent" } ) check_publications( results, { publ_name1: None, publ_name2: None } ) # create a publication that has a parent publisher pub_name = "test publication" create_publication( { "name": pub_name, "publisher": publ_name1 } ) check_publications( results, { publ_name1: pub_name, publ_name2: None } ) # move the publication to another publisher pub_sr = find_search_result( pub_name ) edit_publication( pub_sr, { "publisher": publ_name2 } ) check_publications( results, { publ_name1: None, publ_name2: pub_name } ) # change the publication to have no parent publisher edit_publication( pub_sr, { "publisher": "(none)" } ) check_publications( results, { publ_name1: None, publ_name2: None } ) # move the publication back to a publisher edit_publication( pub_sr, { "publisher": publ_name1 } ) check_publications( results, { publ_name1: pub_name, publ_name2: None } ) # delete the publication select_sr_menu_option( pub_sr, "delete" ) check_ask_dialog( ( "Delete this publication?", pub_name ), "ok" ) check_publications( results, { publ_name1: None, publ_name2: None } )
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_image(expected): # check the image in the article's search result 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 select_sr_menu_option(article_sr, "edit") dlg = wait_for_elem(2, "#article-form") if expected: # make sure there is an image img = find_child(".row.image img.image", dlg) image_url = img.get_attribute("src") assert "/images/article/{}".format(article_id) in image_url # make sure the "remove image" icon is visible btn = find_child(".row.image .remove-image", dlg) 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() else: # make sure there is no image img = find_child(".row.image img.image", dlg) assert img.get_attribute("src").endswith("/images/placeholder.png") # make sure the "remove image" icon is hidden btn = find_child(".row.image .remove-image", dlg) assert not btn.is_displayed() # make sure the article's image is not available url = flask_app.url_for("get_image", image_type="article", image_id=article_id) try: resp = urllib.request.urlopen(url) assert False, "Should never get here!" except urllib.error.HTTPError as ex: assert ex.code == 404 find_child(".cancel", dlg).click()
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 test_images(webdriver, flask_app, dbconn): #pylint: disable=too-many-statements """Test article images.""" # 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 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 select_sr_menu_option(article_sr, "edit") dlg = wait_for_elem(2, "#article-form") if expected: # make sure there is an image img = find_child(".row.image img.image", dlg) image_url = img.get_attribute("src") assert "/images/article/{}".format(article_id) in image_url # make sure the "remove image" icon is visible btn = find_child(".row.image .remove-image", dlg) 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() else: # make sure there is no image img = find_child(".row.image img.image", dlg) assert img.get_attribute("src").endswith("/images/placeholder.png") # make sure the "remove image" icon is hidden btn = find_child(".row.image .remove-image", dlg) assert not btn.is_displayed() # make sure the article's image is not available url = flask_app.url_for("get_image", image_type="article", image_id=article_id) try: resp = urllib.request.urlopen(url) assert False, "Should never get here!" except urllib.error.HTTPError as ex: assert ex.code == 404 find_child(".cancel", dlg).click() # create an article with no image create_article({"title": "Test Article"}) results = get_search_results() assert len(results) == 1 article_sr = results[0] article_id = article_sr.get_attribute("testing--article_id") check_image(None) # add an image to the article fname = os.path.join(os.path.split(__file__)[0], "fixtures/images/1.gif") edit_article(article_sr, {"image": fname}) check_image(fname) # change the article's image fname = os.path.join(os.path.split(__file__)[0], "fixtures/images/2.gif") edit_article(article_sr, {"image": fname}) check_image(fname) # remove the article's image edit_article(article_sr, {"image": None}) check_image(None) # try to upload an image that's too large select_sr_menu_option(article_sr, "edit") dlg = wait_for_elem(2, "#article-form") data = base64.b64encode(5000 * b" ") data = "{}|{}".format("too-big.png", data.decode("ascii")) send_upload_data(data, lambda: find_child(".row.image img.image", dlg).click()) check_error_msg("The file must be no more than 2 KB in size.")
def test_article_lists(webdriver, flask_app, dbconn): """Test showing articles that belong to a publication.""" # initialize init_tests(webdriver, flask_app, dbconn, fixtures="publications.json") 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() # check that the publications have no articles associated with them results = do_search(SEARCH_ALL_PUBLICATIONS) pub_name1, pub_name2 = "ASL Journal (1)", "MMP News" check_articles(results, {pub_name1: None, pub_name2: None}) # create an article that has no parent publication create_article({"title": "no parent"}) check_articles(results, {pub_name1: None, pub_name2: None}) # create an article that has a parent publication article_title = "test article" create_article({"title": article_title, "publication": pub_name1}) check_articles(results, {pub_name1: article_title, pub_name2: None}) # move the article to another publication article_sr = find_search_result(article_title) edit_article(article_sr, {"publication": pub_name2}) check_articles(None, {pub_name1: None, pub_name2: article_title}) # change the article to have no parent publication edit_article(article_sr, {"publication": "(none)"}) check_articles(None, {pub_name1: None, pub_name2: None}) # move the article back into a publication edit_article(article_sr, {"publication": pub_name1}) check_articles(None, {pub_name1: article_title, pub_name2: None}) # delete the article select_sr_menu_option(article_sr, "delete") check_ask_dialog(("Delete this article?", article_title), "ok") check_articles(None, {pub_name1: None, pub_name2: None})
def test_db_report(webdriver, flask_app, dbconn): """Test the database report.""" # initialize init_tests(webdriver, flask_app, dbconn, fixtures="db-report.json") # check the initial report row_counts, links, dupe_images, image_sizes = _get_db_report() assert row_counts == { "publishers": 2, "publications": 3, "articles": 5, "authors": 3, "scenarios": 2 } assert links == { "publishers": [2, []], "publications": [2, []], "articles": [2, []], } assert dupe_images == [] assert image_sizes == {} # add some images do_search(SEARCH_ALL) publ_sr = find_search_result("Avalon Hill", wait=2) fname = os.path.join(os.path.split(__file__)[0], "fixtures/images/1.gif") edit_publisher(publ_sr, {"image": fname}) results = get_search_results() pub_sr = find_search_result("ASL Journal (1)", results) fname = os.path.join(os.path.split(__file__)[0], "fixtures/images/2.gif") edit_publication(pub_sr, {"image": fname}) article_sr = find_search_result("ASLJ article 1", results) fname = os.path.join(os.path.split(__file__)[0], "fixtures/images/3.gif") edit_article(article_sr, {"image": fname}) article_sr = find_search_result("ASLJ article 2", results) fname = os.path.join(os.path.split(__file__)[0], "fixtures/images/3.gif") edit_article(article_sr, {"image": fname}) # check the updated report row_counts, _, dupe_images, image_sizes = _get_db_report() assert row_counts == { "publishers": 2, "publisher_images": 1, "publications": 3, "publication_images": 1, "articles": 5, "article_images": 2, "authors": 3, "scenarios": 2 } assert dupe_images == [[ "f0457ea742376e76ff276ce62c7a8540", "/images/article/100", ("ASLJ article 1", "/article/100"), ("ASLJ article 2", "/article/101"), ]] assert image_sizes == { "publishers": [ ("Avalon Hill", "/publisher/1", "/images/publisher/1"), ], "publications": [ ("ASL Journal (1)", "/publication/10", "/images/publication/10"), ], "articles": [ ("ASLJ article 1", "/article/100", "/images/article/100"), ("ASLJ article 2", "/article/101", "/images/article/101"), ] } # delete all the publishers (and associated objects), then check the updated report do_search(SEARCH_ALL) publ_sr = find_search_result("Avalon Hill", wait=2) select_sr_menu_option(publ_sr, "delete") check_ask_dialog("Delete this publisher?", "ok") results = get_search_results() publ_sr = find_search_result("Multiman Publishing", results) select_sr_menu_option(publ_sr, "delete") check_ask_dialog("Delete this publisher?", "ok") row_counts, links, dupe_images, image_sizes = _get_db_report() assert row_counts == { "publishers": 0, "publications": 0, "articles": 0, "authors": 3, "scenarios": 2 } assert links == { "publishers": [0, []], "publications": [0, []], "articles": [0, []], } assert dupe_images == [] assert image_sizes == {}
def test_publisher_articles(webdriver, flask_app, dbconn): #pylint: disable=too-many-statements """Test articles that are associated with a publisher (not publication).""" # initialize init_tests(webdriver, flask_app, dbconn, fixtures="publisher-articles.json") 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 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." # create an article associated with LFT create_article({"title": "test article", "publisher": "Le Franc Tireur"}) results = wait_for(2, get_search_results) assert len(results) == 1 sr = results[0] check_parent_in_sr(sr, None, "Le Franc Tireur") # open the article's dialog select_sr_menu_option(sr, "edit") dlg = wait_for_elem(2, "#article-form") check_parent_in_dlg(dlg, None, "Le Franc Tireur") # change the article to be associated with an MMP publication find_child(".row.publisher label.parent-mode").click() select = wait_for_elem(2, ".row.publication .react-select") ReactSelect(select).select_by_name("MMP News") find_child("button.ok", dlg).click() results = wait_for(2, get_search_results) assert len(results) == 1 sr = results[0] check_parent_in_sr(sr, "MMP News", None) # open the article's dialog select_sr_menu_option(sr, "edit") dlg = wait_for_elem(2, "#article-form") check_parent_in_dlg(dlg, "MMP News", None) # change the article to be associated with MMP (publisher) find_child(".row.publication label.parent-mode").click() select = wait_for_elem(2, ".row.publisher .react-select") ReactSelect(select).select_by_name("Multiman Publishing") find_child("button.ok", dlg).click() results = wait_for(2, get_search_results) assert len(results) == 1 sr = results[0] check_parent_in_sr(sr, None, "Multiman Publishing") # show the MMP publisher results = do_search("multiman") assert len(results) == 1 sr = results[0] collapsibles = find_children(".collapsible", sr) assert len(collapsibles) == 2 items = find_children("li a", collapsibles[1]) assert len(items) == 1 item = items[0] assert item.text == "test article" assert re.search(r"^http://.+?/article/\d+", item.get_attribute("href")) # delete the MMP publisher # NOTE: There are 2 MMP articles, the one that is in the "MMP News" publication, # and the test article we created above that is associated with the publisher. select_sr_menu_option(sr, "delete") check_ask_dialog( ("Delete this publisher?", "2 articles will also be deleted"), "ok") query = dbconn.execute("SELECT count(*) FROM article") assert query.scalar() == 0