def test_empty_search(webdriver, flask_app, dbconn): """Test handling of an empty search string.""" # initialize init_tests(webdriver, flask_app, dbconn) # search for an empty string form = find_child("#search-form") find_child(".query", form).send_keys(" ") find_child("button[type='submit']", form).click() dlg = wait_for_elem(2, "#ask") assert find_child(".MuiDialogContent-root", dlg).text == "Please enter something to search for."
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 edit_article(sr, vals, toast_type="info", expected_error=None, expected_constraints=None, accept_constraints=False): #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, "ok" if accept_constraints else "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 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_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_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_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
def _get_db_report(): #pylint: disable=too-many-locals """Generate the database report.""" # generate the report select_main_menu_option("db-report") wait_for_elem(2, "#db-report .db-images") # unload the row counts row_counts = {} table = find_child("#db-report .db-row-counts") for row in find_children("tr", table): cells = find_children("td", row) mo = re.search(r"^(\d+)( \((\d+) images?\))?$", cells[1].text) key = cells[0].text.lower()[:-1] row_counts[key] = int(mo.group(1)) if mo.group(3): row_counts[key[:-1] + "_images"] = int(mo.group(3)) # unload the links links = {} table = find_child("#db-report .db-links") last_key = None for row in find_children("tr", table): cells = find_children("td", row) if len(cells) == 2: last_key = cells[0].text.lower()[:-1] links[last_key] = [int(cells[1].text), []] else: mo = re.search(r"^(.+) \((.+)\)$", cells[0].text) tags = find_children("a", cells[0]) url = _fixup_url(tags[0].get_attribute("href")) links[last_key][1].append((mo.group(1), url, mo.group(2))) # unload duplicate images dupe_images = [] for row in find_children("#db-report .dupe-analysis .dupe-image"): elem = find_child(".caption .hash", row) mo = re.search(r"^\(md5:(.+)\)$", elem.text) image_hash = mo.group(1) image_url = _fixup_url(find_child("img", row).get_attribute("src")) parents = [] for entry in find_children(".collapsible li", row): url = _fixup_url(find_child("a", entry).get_attribute("href")) parents.append((entry.text, url)) dupe_images.append( list(itertools.chain([image_hash, image_url], parents))) # unload the image sizes tab_ctrl = find_child("#db-report .db-images .react-tabs") image_sizes = {} for tab in find_children(".react-tabs__tab", tab_ctrl): key = tab.text.lower() tab_id = tab.get_attribute("id") tab.click() sel = ".react-tabs__tab-panel[aria-labelledby='{}'].react-tabs__tab-panel--selected".format( tab_id) tab_page = wait_for( 2, lambda: find_child(sel, tab_ctrl) #pylint: disable=cell-var-from-loop ) parents = [] for row_no, row in enumerate( find_children("table.image-sizes tr", tab_page)): if row_no == 0: continue cells = find_children("td", row) image_url = _fixup_url( find_child("img", cells[0]).get_attribute("src")) url = _fixup_url(find_child("a", cells[2]).get_attribute("href")) parents.append((cells[2].text, url, image_url)) if parents: image_sizes[key] = parents else: assert tab_page.text == "No images found." return row_counts, links, dupe_images, image_sizes