from bs4 import BeautifulSoup as Soup from .fixtures import app_client import pytest import re import urllib.parse pytest.fixture(scope='module')(app_client) def test_homepage(app_client): response = app_client.get('/', gather_request=False) assert response.status == 200 assert 'test_tables' in response.text def test_database_page(app_client): response = app_client.get('/test_tables', allow_redirects=False, gather_request=False) assert response.status == 302 response = app_client.get('/test_tables', gather_request=False) assert 'test_tables' in response.text def test_invalid_custom_sql(app_client): response = app_client.get( '/test_tables?sql=.schema', gather_request=False ) assert response.status == 400 assert 'Statement must be a SELECT' in response.text def test_view(app_client): response = app_client.get('/test_tables/simple_view', gather_request=False) assert response.status == 200 def test_row(app_client): response = app_client.get( '/test_tables/simple_primary_key/1', allow_redirects=False, gather_request=False ) assert response.status == 302 assert response.headers['Location'].endswith('/1') response = app_client.get('/test_tables/simple_primary_key/1', gather_request=False) assert response.status == 200 def test_add_filter_redirects(app_client): filter_args = urllib.parse.urlencode({ '_filter_column': 'content', '_filter_op': 'startswith', '_filter_value': 'x' }) # First we need to resolve the correct path before testing more redirects path_base = app_client.get( '/test_tables/simple_primary_key', allow_redirects=False, gather_request=False ).headers['Location'] path = path_base + '?' + filter_args response = app_client.get(path, allow_redirects=False, gather_request=False) assert response.status == 302 assert response.headers['Location'].endswith('?content__startswith=x') # Adding a redirect to an existing querystring: path = path_base + '?foo=bar&' + filter_args response = app_client.get(path, allow_redirects=False, gather_request=False) assert response.status == 302 assert response.headers['Location'].endswith('?foo=bar&content__startswith=x') # Test that op with a __x suffix overrides the filter value path = path_base + '?' + urllib.parse.urlencode({ '_filter_column': 'content', '_filter_op': 'isnull__5', '_filter_value': 'x' }) response = app_client.get(path, allow_redirects=False, gather_request=False) assert response.status == 302 assert response.headers['Location'].endswith('?content__isnull=5') def test_existing_filter_redirects(app_client): filter_args = { '_filter_column_1': 'name', '_filter_op_1': 'contains', '_filter_value_1': 'hello', '_filter_column_2': 'age', '_filter_op_2': 'gte', '_filter_value_2': '22', '_filter_column_3': 'age', '_filter_op_3': 'lt', '_filter_value_3': '30', '_filter_column_4': 'name', '_filter_op_4': 'contains', '_filter_value_4': 'world', } path_base = app_client.get( '/test_tables/simple_primary_key', allow_redirects=False, gather_request=False ).headers['Location'] path = path_base + '?' + urllib.parse.urlencode(filter_args) response = app_client.get(path, allow_redirects=False, gather_request=False) assert response.status == 302 assert_querystring_equal( 'name__contains=hello&age__gte=22&age__lt=30&name__contains=world', response.headers['Location'].split('?')[1], ) # Setting _filter_column_3 to empty string should remove *_3 entirely filter_args['_filter_column_3'] = '' path = path_base + '?' + urllib.parse.urlencode(filter_args) response = app_client.get(path, allow_redirects=False, gather_request=False) assert response.status == 302 assert_querystring_equal( 'name__contains=hello&age__gte=22&name__contains=world', response.headers['Location'].split('?')[1], ) # ?_filter_op=exact should be removed if unaccompanied by _fiter_column response = app_client.get(path_base + '?_filter_op=exact', allow_redirects=False, gather_request=False) assert response.status == 302 assert '?' not in response.headers['Location'] def test_empty_search_parameter_gets_removed(app_client): path_base = app_client.get( '/test_tables/simple_primary_key', allow_redirects=False, gather_request=False ).headers['Location'] path = path_base + '?' + urllib.parse.urlencode({ '_search': '', '_filter_column': 'name', '_filter_op': 'exact', '_filter_value': 'chidi', }) response = app_client.get(path, allow_redirects=False, gather_request=False) assert response.status == 302 assert response.headers['Location'].endswith( '?name__exact=chidi' ) def test_sort_by_desc_redirects(app_client): path_base = app_client.get( '/test_tables/sortable', allow_redirects=False, gather_request=False ).headers['Location'] path = path_base + '?' + urllib.parse.urlencode({ '_sort': 'sortable', '_sort_by_desc': '1', }) response = app_client.get(path, allow_redirects=False, gather_request=False) assert response.status == 302 assert response.headers['Location'].endswith('?_sort_desc=sortable') def test_sort_links(app_client): response = app_client.get( '/test_tables/sortable?_sort=sortable', gather_request=False ) assert response.status == 200 ths = Soup(response.body, 'html.parser').findAll('th') attrs_and_link_attrs = [{ 'attrs': th.attrs, 'a_href': ( th.find('a')['href'].split('/')[-1] if th.find('a') else None ), } for th in ths] assert [ { "attrs": {"class": ["col-Link"], "scope": "col"}, "a_href": None }, { "attrs": {"class": ["col-pk1"], "scope": "col"}, "a_href": None }, { "attrs": {"class": ["col-pk2"], "scope": "col"}, "a_href": None }, { "attrs": {"class": ["col-content"], "scope": "col"}, "a_href": None }, { "attrs": {"class": ["col-sortable"], "scope": "col"}, "a_href": "sortable?_sort_desc=sortable", }, { "attrs": {"class": ["col-sortable_with_nulls"], "scope": "col"}, "a_href": "sortable?_sort=sortable_with_nulls", }, { "attrs": {"class": ["col-sortable_with_nulls_2"], "scope": "col"}, "a_href": "sortable?_sort=sortable_with_nulls_2", }, { "attrs": {"class": ["col-text"], "scope": "col"}, "a_href": "sortable?_sort=text", }, ] == attrs_and_link_attrs def test_facets_persist_through_filter_form(app_client): response = app_client.get( '/test_tables/facetable?_facet=planet_int&_facet=city_id', gather_request=False ) assert response.status == 200 inputs = Soup(response.body, 'html.parser').find('form').findAll('input') hiddens = [i for i in inputs if i['type'] == 'hidden'] assert [ ('_facet', 'city_id'), ('_facet', 'planet_int'), ] == [ (hidden['name'], hidden['value']) for hidden in hiddens ] @pytest.mark.parametrize('path,expected_classes', [ ('/', ['index']), ('/test_tables', ['db', 'db-test_tables']), ('/test_tables/simple_primary_key', [ 'table', 'db-test_tables', 'table-simple_primary_key' ]), ('/test_tables/table%2Fwith%2Fslashes.csv', [ 'table', 'db-test_tables', 'table-tablewithslashescsv-fa7563' ]), ('/test_tables/simple_primary_key/1', [ 'row', 'db-test_tables', 'table-simple_primary_key' ]), ]) def test_css_classes_on_body(app_client, path, expected_classes): response = app_client.get(path, gather_request=False) assert response.status == 200 classes = re.search(r'
', response.text).group(1).split() assert classes == expected_classes def test_table_html_simple_primary_key(app_client): response = app_client.get('/test_tables/simple_primary_key', gather_request=False) assert response.status == 200 table = Soup(response.body, 'html.parser').find('table') assert table['class'] == ['rows-and-columns'] ths = table.findAll('th') assert 'id' == ths[0].find('a').string.strip() for expected_col, th in zip(('content',), ths[1:]): a = th.find('a') assert expected_col == a.string assert a['href'].endswith('/simple_primary_key?_sort={}'.format( expected_col )) assert ['nofollow'] == a['rel'] assert [ [ '