You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
421 lines
14 KiB
421 lines
14 KiB
""" Test search. """
|
|
|
|
import logging
|
|
|
|
from selenium.webdriver.common.keys import Keys
|
|
|
|
from asl_rulebook2.webapp.search import load_search_config, _make_fts_query_string
|
|
from asl_rulebook2.webapp.startup import StartupMsgs
|
|
from asl_rulebook2.webapp.tests.utils import init_webapp, make_webapp_main_url, \
|
|
select_tabbed_page, get_curr_target, get_classes, \
|
|
wait_for, wait_for_elem, find_child, find_children, unload_elem, unload_sr_text
|
|
|
|
# ---------------------------------------------------------------------
|
|
|
|
def test_search( webapp, webdriver ):
|
|
"""Test search."""
|
|
|
|
# initialize
|
|
webapp.control_tests.set_data_dir( "simple" )
|
|
init_webapp( webapp, webdriver )
|
|
|
|
# test a search that finds nothing
|
|
results = do_search( "oogah, boogah!" )
|
|
assert results is None
|
|
|
|
# test error handling
|
|
results = do_search( "!:simulated-error:!" )
|
|
assert "Simulated error." in results
|
|
|
|
# do a search
|
|
results = do_search( "enemy" )
|
|
assert results == [
|
|
{ "sr_type": "index",
|
|
"title": "CCPh", "subtitle": "Close Combat Phase",
|
|
"ruleids": [ "A3.8" ],
|
|
"rulerefs": [
|
|
{ "caption": "((ENEMY)) Attacks", "ruleids": [ "S11.5" ] },
|
|
{ "caption": "dropping SW before CC", "ruleids": [ "A4.43" ] },
|
|
]
|
|
},
|
|
{ "sr_type": "index",
|
|
"title": "Double Time",
|
|
"content": "Also known as \"running really fast.\"",
|
|
"see_also": [ "CX" ],
|
|
"ruleids": [ "A4.5-.51", "S6.222" ],
|
|
"rulerefs": [
|
|
{ "caption": "((ENEMY)) Guard Automatic Action", "ruleids": [ "S6.303" ] },
|
|
{ "ruleids": [ "C10.3" ] },
|
|
{ "caption": "NA in Advance Phase", "ruleids": [ "A4.7" ] },
|
|
{ "caption": "'S?' is \"<NA>\"" },
|
|
]
|
|
},
|
|
]
|
|
|
|
# do another search
|
|
results = do_search( "gap" )
|
|
assert results == [
|
|
{ "sr_type": "index",
|
|
"title": "((Gaps)), Convoy",
|
|
"ruleids": [ "E11.21" ],
|
|
},
|
|
]
|
|
|
|
# ---------------------------------------------------------------------
|
|
|
|
def test_content_fixup( webapp, webdriver ):
|
|
"""Test fixing up of content returned by the search engine."""
|
|
|
|
# initialize
|
|
webapp.control_tests.set_data_dir( "simple" )
|
|
init_webapp( webapp, webdriver )
|
|
|
|
# search for a fraction
|
|
results = do_search( "3/4" )
|
|
assert len(results) == 1
|
|
assert results[0]["content"] == "HTML content: 2((\u00be)) MP"
|
|
|
|
# search for something that ends with a hash
|
|
results = do_search( "H#" )
|
|
assert len(results) == 1
|
|
assert results[0]["title"] == "((H#))"
|
|
|
|
# search for "U.S."
|
|
results = do_search( "U.S." )
|
|
assert len(results) == 1
|
|
assert results[0]["content"] == "The ((U.S.)) has lots of this."
|
|
|
|
# ---------------------------------------------------------------------
|
|
|
|
def test_targets( webapp, webdriver ):
|
|
"""Test clicking on search results."""
|
|
|
|
# initialize
|
|
webapp.control_tests.set_data_dir( "simple" )
|
|
init_webapp( webapp, webdriver, no_content=1, add_empty_doc=1 )
|
|
|
|
def do_test( query_string, sel, expected ):
|
|
|
|
# select the dummy document
|
|
select_tabbed_page( "content", "empty" )
|
|
|
|
# do the search
|
|
do_search( query_string )
|
|
|
|
# click on a target
|
|
elem = find_child( "#search-results {}".format( sel ) )
|
|
elem.click()
|
|
wait_for( 2, lambda: get_curr_target() == ( "simple!", expected ) )
|
|
|
|
# do the tests
|
|
do_test( "CC", ".sr .ruleids .ruleid a", "A3.8" )
|
|
do_test( "time", ".sr .rulerefs .ruleid a", "A4.7" )
|
|
|
|
# ---------------------------------------------------------------------
|
|
|
|
def test_target_search( webapp, webdriver ):
|
|
"""Test searching for targets."""
|
|
|
|
# initialize
|
|
webapp.control_tests.set_data_dir( "simple" )
|
|
init_webapp( webapp, webdriver )
|
|
|
|
# search for a target
|
|
results = do_search( "cc" )
|
|
assert len(results) > 0
|
|
results = do_search( "D1.4" )
|
|
assert len(results) == 0 # nb: previous search results should be removed
|
|
wait_for( 2, lambda: get_curr_target() == ( "simple!", "D1.4" ) )
|
|
|
|
# search for a target
|
|
results = do_search( "astral plane" )
|
|
assert results is None # nb: this is the "no results" message
|
|
results = do_search( "E11.21" )
|
|
assert len(results) == 0 # nb: the "no results" message should be cleared
|
|
wait_for( 2, lambda: get_curr_target() == ( "simple!", "E11.21" ) )
|
|
|
|
# search for a target
|
|
results = do_search( "*" )
|
|
assert isinstance( results, str ) # nb: this is an error message
|
|
results = do_search( "a4.7" )
|
|
assert len(results) == 0 # nb: the error message should be cleared
|
|
wait_for( 2, lambda: get_curr_target() == ( "simple!", "A4.7" ) )
|
|
|
|
# ---------------------------------------------------------------------
|
|
|
|
def test_see_also( webapp, webdriver ):
|
|
"""Test searching for "see also" fields."""
|
|
|
|
# initialize
|
|
webapp.control_tests.set_data_dir( "see-also" )
|
|
init_webapp( webapp, webdriver )
|
|
|
|
# do a search
|
|
results = do_search( "foo" )
|
|
assert results == [ {
|
|
"sr_type": "index",
|
|
"title": "((Foo))",
|
|
"see_also": [ "Bar", "Baz Baz" ]
|
|
} ]
|
|
|
|
def click_see_also( caption ):
|
|
elems = {
|
|
e.text: e
|
|
for e in find_children( "#search-results .see-also a" )
|
|
}
|
|
assert len(elems) == 2
|
|
elems[ caption ].click()
|
|
expected = '"{}"'.format( caption ) if " " in caption else caption
|
|
wait_for( 2, lambda: find_child( "input#query-string" ).get_attribute( "value" ) == expected )
|
|
return unload_search_results()
|
|
|
|
# click on the "Bar" link and check that it gets searched for
|
|
results = click_see_also( "Bar" )
|
|
assert results == [ {
|
|
"sr_type": "index",
|
|
"title": "((Bar))"
|
|
} ]
|
|
|
|
# search for "foo" again, and click on the "Baz Baz" link
|
|
do_search( "foo" )
|
|
results = click_see_also( "Baz Baz" )
|
|
assert results is None
|
|
|
|
# ---------------------------------------------------------------------
|
|
|
|
def test_bad_sr_highlights( webapp, webdriver ):
|
|
"""Test fixing up highlight markers that have been incorrectly inserted into search result content."""
|
|
|
|
# initialize
|
|
webapp.control_tests.set_data_dir( "bad-sr-highlights" )
|
|
init_webapp( webapp, webdriver )
|
|
|
|
# bring up an index search result with a problem in its content
|
|
results = do_search( "highlight" )
|
|
assert results == [ {
|
|
"sr_type": "index",
|
|
"title": "Bad ((highlight))",
|
|
"content": "before link after",
|
|
} ]
|
|
|
|
# bring up a Q+A entry with a problem in its question
|
|
results = do_search( "foo" )
|
|
assert len(results) == 1
|
|
assert results[0]["content"][0]["question"] == "((foo)) link ((foo))"
|
|
|
|
# bring up a Q+A entry with a problem in one of its answers
|
|
results = do_search( "bar" )
|
|
assert len(results) == 1
|
|
assert results[0]["content"][0]["answers"][0][0] == "..((bar)).. link ((bar))"
|
|
|
|
# ---------------------------------------------------------------------
|
|
|
|
def test_startup_search_query( webapp, webdriver ):
|
|
"""Test starting up with a search query."""
|
|
|
|
# initialize
|
|
webapp.control_tests.set_data_dir( "full" )
|
|
|
|
# test starting up with a query
|
|
init_webapp( webapp, webdriver, q="cc" )
|
|
wait_for( 2, lambda: len( unload_search_results() ) > 0 )
|
|
|
|
# test starting up with a query
|
|
init_webapp( webapp, webdriver, query="wp" )
|
|
wait_for_elem( 2,"#rule-info" )
|
|
find_child( ".close-rule-info" ).click()
|
|
assert len( unload_search_results() ) > 0
|
|
|
|
# test starting up with a query
|
|
url = make_webapp_main_url( {} )
|
|
assert "#" not in url
|
|
url += "#bu"
|
|
webdriver.get( url )
|
|
wait_for( 2, lambda: len( unload_search_results() ) > 0 )
|
|
|
|
# ---------------------------------------------------------------------
|
|
|
|
def test_make_fts_query_string():
|
|
"""Test generating the FTS query string."""
|
|
|
|
# initialize
|
|
startup_msgs = StartupMsgs()
|
|
load_search_config( startup_msgs, logging.getLogger("_unknown_") )
|
|
|
|
def check( query, expected ):
|
|
fts_query_string, _ = _make_fts_query_string(query)
|
|
assert fts_query_string == expected
|
|
assert not startup_msgs.msgs
|
|
|
|
# test some query strings
|
|
check( "", "" )
|
|
check( "hello", "hello" )
|
|
check( " hello, world! ", "hello AND world" )
|
|
check(
|
|
"foo 1+2 A-T K# bar",
|
|
'foo AND "1+2" AND "a-t" AND "k#" AND bar'
|
|
)
|
|
check(
|
|
"a'b a''b",
|
|
"\"a'b\" AND \"a''b\""
|
|
)
|
|
check(
|
|
'foo "set dc" bar',
|
|
'foo AND "set dc" AND bar'
|
|
)
|
|
|
|
# test some quoted phrases
|
|
check( '""', '' )
|
|
check( ' " " ', '' )
|
|
check(
|
|
'"hello world"',
|
|
'"hello world"'
|
|
)
|
|
check(
|
|
' foo "hello world" bar ',
|
|
'foo AND "hello world" AND bar'
|
|
)
|
|
check(
|
|
' foo " xyz " bar ',
|
|
'foo AND xyz AND bar'
|
|
)
|
|
check(
|
|
' foo " xyz 123 " bar ',
|
|
'foo AND "xyz 123" AND bar'
|
|
)
|
|
|
|
# test some incorrectly quoted phrases
|
|
check( '"', '' )
|
|
check( ' " " " ', '' )
|
|
check( ' a "b c d e', 'a AND "b c d e"' )
|
|
check( ' a b" c d e ', 'a AND b AND c AND d AND e' )
|
|
|
|
# test pass-through
|
|
check( "AND", "AND" )
|
|
check( " OR", "OR" )
|
|
check( "OR ", "OR" )
|
|
check( "foo OR bar", "foo OR bar" )
|
|
check( "(a OR b)", "(a OR b)" )
|
|
|
|
# test search replacements
|
|
check( "1/2 3/4 3/8 5/8", '"½" AND "¾" AND "⅜" AND "⅝"' )
|
|
check( "(r)", '"®"' )
|
|
|
|
# test search aliases
|
|
check( "entrenchment", "( ditch OR entrenchment OR foxhole OR trench )" )
|
|
check( "entrenchments", "( ditch OR entrenchments OR foxhole OR trench )" )
|
|
check( "foxhole", "foxhole" )
|
|
|
|
# test search synonyms
|
|
check( "armor", "( armor OR armour )" )
|
|
check( "american big armor", '( america OR american OR "u.s." ) AND big AND ( armor OR armour )' )
|
|
|
|
# ---------------------------------------------------------------------
|
|
|
|
def do_search( query_string ):
|
|
"""Do a search."""
|
|
|
|
def get_seq_no():
|
|
return find_child( "#search-results" ).get_attribute( "data-seqno" )
|
|
|
|
# submit the search
|
|
select_tabbed_page( "nav", "search" )
|
|
elem = find_child( "input#query-string" )
|
|
elem.clear()
|
|
elem.send_keys( query_string )
|
|
seq_no = get_seq_no()
|
|
elem.send_keys( Keys.RETURN )
|
|
|
|
# unload the results
|
|
wait_for( 2, lambda: get_seq_no() > seq_no )
|
|
return unload_search_results()
|
|
|
|
def unload_search_results():
|
|
"""Unload the search results."""
|
|
|
|
# check if there were any search results
|
|
elem = find_child( "#search-results .error" )
|
|
if elem:
|
|
return elem.text # nb: string = error message
|
|
elem = find_child( "#search-results .no-results" )
|
|
if elem:
|
|
if elem.text == "Nothing was found.":
|
|
return None # nb: None = no results
|
|
return elem.text
|
|
|
|
def unload_ruleids( result, key, parent ):
|
|
"""Unload a list of ruleid's."""
|
|
if not parent:
|
|
return
|
|
ruleids = []
|
|
for elem in find_children( ".ruleid", parent ):
|
|
ruleid = unload_sr_text( elem )
|
|
if ruleid.startswith( "[" ) and ruleid.endswith( "]" ):
|
|
ruleid = ruleid[1:-1]
|
|
if ruleid.endswith( "," ):
|
|
ruleid = ruleid[:-1]
|
|
ruleids.append( ruleid )
|
|
if ruleids:
|
|
result[key] = ruleids
|
|
|
|
def unload_rulerefs( result, key, parent ):
|
|
"""Unload a list of ruleref's."""
|
|
if not parent:
|
|
return
|
|
rulerefs = []
|
|
for elem in find_children( "li", parent ):
|
|
ruleref = {}
|
|
unload_elem( ruleref, "caption", find_child(".caption",elem), adjust_hilites=True )
|
|
unload_ruleids( ruleref, "ruleids", elem )
|
|
rulerefs.append( ruleref )
|
|
if rulerefs:
|
|
result[key] = rulerefs
|
|
|
|
def unload_index_sr( sr ): #pylint: disable=possibly-unused-variable
|
|
"""Unload an "index" search result."""
|
|
result = {}
|
|
unload_elem( result, "title", find_child("span.title",sr), adjust_hilites=True )
|
|
unload_elem( result, "subtitle", find_child(".subtitle",sr), adjust_hilites=True )
|
|
unload_elem( result, "content", find_child(".content",sr), adjust_hilites=True )
|
|
if unload_elem( result, "see_also", find_child(".see-also",sr) ):
|
|
assert result["see_also"].startswith( "See also:" )
|
|
result["see_also"] = [ s.strip() for s in result["see_also"][9:].split( "," ) ]
|
|
unload_ruleids( result, "ruleids", find_child(".ruleids",sr) )
|
|
unload_rulerefs( result, "rulerefs", find_child(".rulerefs",sr) )
|
|
return result
|
|
|
|
def unload_qa_sr( sr ): #pylint: disable=possibly-unused-variable
|
|
"""Unload a "qa" search result."""
|
|
from asl_rulebook2.webapp.tests.test_qa import unload_qa
|
|
return unload_qa( sr )
|
|
|
|
def unload_anno_sr( sr ): #pylint: disable=possibly-unused-variable
|
|
"""Unload an "anno" search result."""
|
|
from asl_rulebook2.webapp.tests.test_annotations import unload_anno
|
|
return unload_anno( sr )
|
|
|
|
def unload_asop_entry_sr( sr ): #pylint: disable=possibly-unused-variable
|
|
"""Unload an "ASOP entry" search result."""
|
|
result = {}
|
|
unload_elem( result, "caption", find_child(".caption",sr), adjust_hilites=True )
|
|
unload_elem( result, "content", find_child(".content",sr), adjust_hilites=True )
|
|
return result
|
|
|
|
# unload the search results
|
|
results = []
|
|
for sr in find_children( "#search-results .sr"):
|
|
if not sr.is_displayed():
|
|
continue
|
|
classes = get_classes( sr )
|
|
classes.remove( "sr" )
|
|
classes = [ c for c in classes if c in ["index-sr","qa","anno","asop-entry-sr"] ]
|
|
assert len(classes) == 1
|
|
sr_type = classes[0]
|
|
if sr_type.endswith( "-sr" ):
|
|
sr_type = sr_type[:-3]
|
|
func = locals()[ "unload_{}_sr".format( sr_type.replace("-","_") ) ]
|
|
sr = func( sr )
|
|
sr["sr_type"] = sr_type
|
|
results.append( sr )
|
|
|
|
return results
|
|
|