コード例 #1
0
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."
コード例 #2
0
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"] )
コード例 #3
0
    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()
コード例 #4
0
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
コード例 #5
0
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
コード例 #6
0
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")
コード例 #7
0
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
コード例 #8
0
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.")
コード例 #9
0
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
コード例 #10
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