from urllib.parse import urlparse from bs4 import BeautifulSoup from flask import render_template, url_for from atlas_um.helpers.pagination import QueryPagination from tests.atlas_um.factories import ApplicationFactory from tests.atlas_um.pytest_helpers import ( get_default_applications_list_ordering, UrlForThisMixin, ) from . import helpers class TestListApplications(UrlForThisMixin): ENDPOINT = "applications.list" def test_title(self, app, faker, pgdb_session): with app.test_request_context(self.url_for_this(app)): applications = self.get_applications(pgdb_session, faker) page = self.render_soup( applications=applications, pagination=QueryPagination(), ordering=get_default_applications_list_ordering(applications), ) assert page.title.get_text(strip=True) == "Applications" page_head = page.find(id="subnav-header") assert page_head.get_text(strip=True) == "Applications" def test_search_elements(self, app, faker, pgdb_session, authorized_admin): with app.test_request_context(self.url_for_this(app)): applications = self.get_applications(pgdb_session, faker) page = self.render_soup( applications=applications, pagination=QueryPagination(), ordering=get_default_applications_list_ordering(applications), ) assert all( helpers.added_search_form_elements(page) ), "Bad search elements" def test_search_form(self, app, faker, pgdb_session, authorized_admin): with app.test_request_context(self.url_for_this(app)): applications = self.get_applications(pgdb_session, faker) page = self.render_soup( applications=applications, pagination=QueryPagination(), ordering=get_default_applications_list_ordering(applications), ) assert all(helpers.added_search_form_elements(page)), "Bad search form" def test_new_application_button(self, app, faker, pgdb_session): with app.test_request_context(self.url_for_this(app)): applications = self.get_applications(pgdb_session, faker) page = self.render_soup( applications=applications, pagination=QueryPagination(), ordering=get_default_applications_list_ordering(applications), ) button = page.find(id="new-application-button") expected_url = self.urlspec_for_this( app, endpoint="applications.create" ).geturl() assert button.get_text(strip=True) == "New application" assert button.attrs.get("href") == expected_url def test_applications_table_header_admin( self, app, faker, pgdb_session, authorized_admin ): with app.test_request_context(self.url_for_this(app)): applications = self.get_applications(pgdb_session, faker) page = self.render_soup( applications=applications, pagination=QueryPagination(), ordering=get_default_applications_list_ordering(applications), ) applications_table = page.find("table", id="applications-table") table_head = applications_table.find("thead") headers = table_head.find_all("th") assert len(headers) == 5 assert headers[0]["class"] == ["application-name"] assert headers[0].get_text(strip=True) == "Name" assert headers[0].find("a").attrs.get("href") in self.url_for_this( app, order="name" ) assert headers[1]["class"] == ["application-dna_account"] assert headers[1].get_text(strip=True) == "DNA account" assert headers[1].find("a").attrs.get("href") in self.url_for_this( app, order="-dna_account_id" ) assert headers[2]["class"] == ["application-client_id"] assert headers[2].get_text(strip=True) == "Client ID" assert headers[2].find("a").attrs.get("href") in self.url_for_this( app, order="client_id" ) assert headers[3]["class"] == ["application-resource_group"] assert headers[3].get_text(strip=True) == "Product" assert headers[3].find("a").attrs.get("href") in self.url_for_this( app, order="resource_group_id" ) assert headers[4]["class"] == ["application-audience"] assert headers[4].get_text(strip=True) == "Audience" assert headers[4].find("a").attrs.get("href") in self.url_for_this( app, order="audience" ) def test_applications_table_header_developer( self, app, faker, pgdb_session ): with app.test_request_context(self.url_for_this(app)): applications = self.get_applications(pgdb_session, faker) page = self.render_soup( applications=applications, pagination=QueryPagination(), ordering=get_default_applications_list_ordering(applications), ) applications_table = page.find("table", id="applications-table") table_head = applications_table.find("thead") headers = table_head.find_all("th") assert len(headers) == 4 assert headers[0]["class"] == ["application-name"] assert headers[0].get_text(strip=True) == "Name" assert headers[0].find("a").attrs.get("href") in self.url_for_this( app, order="name" ) assert headers[1]["class"] == ["application-client_id"] assert headers[1].get_text(strip=True) == "Client ID" assert headers[2]["class"] == ["application-resource_group"] assert headers[2].get_text(strip=True) == "Product" assert headers[3]["class"] == ["application-audience"] assert headers[3].get_text(strip=True) == "Audience" def test_applications_table_body_admin( self, app, faker, pgdb_session, authorized_admin ): with app.test_request_context(self.url_for_this(app)): applications = self.get_applications(pgdb_session, faker) page = self.render_soup( applications=applications, pagination=QueryPagination(), ordering=get_default_applications_list_ordering(applications), ) applications_table = page.find("table", id="applications-table") rows = applications_table.find("tbody").find_all("tr") assert len(rows) == len(applications) for application, row in zip(applications, rows): dna_account_cell = row.find("td", class_="application-dna_account") assert ( dna_account_cell.get_text(strip=True) == application.dna_account.email ) name_cell = row.find("td", class_="application-name") anchor = name_cell.find("a") assert anchor["href"] in url_for( "applications.update", application_id=application.id ) client_id_cell = row.find("td", class_="application-client_id") assert client_id_cell.get_text(strip=True) == application.client_id resource_group_cell = row.find( "td", class_="application-resource_group" ) assert ( resource_group_cell.get_text(strip=True) == application.resource_group.name ) audience_cell = row.find("td", class_="application-audience") assert audience_cell.get_text(strip=True) == application.audience def test_applications_table_body_developer(self, app, faker, pgdb_session): with app.test_request_context(self.url_for_this(app)): applications = self.get_applications(pgdb_session, faker) page = self.render_soup( applications=applications, pagination=QueryPagination(), ordering=get_default_applications_list_ordering(applications), ) applications_table = page.find("table", id="applications-table") rows = applications_table.find("tbody").find_all("tr") assert len(rows) == len(applications) for application, row in zip(applications, rows): dna_account_cell = row.find("td", class_="application-dna_account") assert dna_account_cell is None name_cell = row.find("td", class_="application-name") anchor = name_cell.find("a") assert anchor["href"] in url_for( "applications.update", application_id=application.id ) client_id_cell = row.find("td", class_="application-client_id") assert client_id_cell.get_text(strip=True) == application.client_id resource_group_cell = row.find( "td", class_="application-resource_group" ) assert ( resource_group_cell.get_text(strip=True) == application.resource_group.name ) audience_cell = row.find("td", class_="application-audience") assert audience_cell.get_text(strip=True) == application.audience def test_applications_table_body_empty(self, app, faker, pgdb_session): with app.test_request_context(self.url_for_this(app)): applications = [] page = self.render_soup( applications=applications, pagination=QueryPagination(), ordering=get_default_applications_list_ordering(applications), ) applications_table = page.find("table", id="applications-table") rows = applications_table.find("tbody").find_all("tr") assert len(rows) == 1 row = rows[0] expected = "No applications to show." assert row.get_text(" ", strip=True) == expected header_cols = applications_table.find("thead").find_all("th") assert int(row.find("td")["colspan"]) == len(header_cols) def test_table_page_navigator(self, app, faker, pgdb_session): per_page = 10 next_page = 2 url = self.url_for_this(app, per_page=per_page) applications = self.get_applications(pgdb_session, faker, 20) count = len(applications) with app.test_request_context(url): page = self.render_soup( applications=applications, pagination=QueryPagination(total=count), ordering=get_default_applications_list_ordering(applications), ) paginator = page.find(class_="pages-links") items = paginator.findAll("a") assert len(items) == 4 next_page_url = self.url_for_this( app, per_page=per_page, page=next_page ) # back link assert items[0].find("i", class_="angle left icon") is not None assert items[0].attrs.get("href") is None assert items[0].get_text(strip=True) == "" # first page link assert items[1].attrs.get("href") is None assert items[1].get_text(strip=True) == "1" # second page link assert items[2].attrs.get("href") in next_page_url assert items[2].get_text(strip=True) == str(next_page) # forward link assert items[3].attrs.get("href") in next_page_url assert items[3].find("i", class_="angle right icon") is not None def test_paginator_size_select(self, app, faker, pgdb_session): per_page = 10 url = self.url_for_this(app, per_page=per_page) applications = self.get_applications(pgdb_session, faker, 20) count = len(applications) with app.test_request_context(url): page = self.render_soup( applications=applications, pagination=QueryPagination(total=count), ordering=get_default_applications_list_ordering(applications), ) page_size_selector = page.find(class_="items-on-page") assert page_size_selector is not None assert len(page_size_selector.find_all("option")) == 3 page_size_selected = page_size_selector.find("option", selected="") assert page_size_selected["value"] in url assert page_size_selected.get_text(strip=True) == "10 / Page" def test_paginator_go_to_form(self, app, faker, pgdb_session): per_page = 10 url = self.url_for_this(app, per_page=per_page) applications = self.get_applications(pgdb_session, faker, 20) count = len(applications) with app.test_request_context(url): page = self.render_soup( applications=applications, pagination=QueryPagination(total=count), ordering=get_default_applications_list_ordering(applications), ) go_to_form = page.find("form", id="pagination-go-to") assert go_to_form.attrs.get("method").upper() == "GET" assert go_to_form.attrs.get("action") in url assert ( go_to_form.find("input", class_="goto-page", type="number") is not None ) assert ( go_to_form.find("input", attrs={"name": "page", "type": "number"}) is not None ) assert ( go_to_form.find( "input", attrs={"name": "per_page", "type": "hidden"} ) is not None ) def test_no_table_paginator_for_empty_table(self, app, faker): applications = [] per_page = 10 url = self.url_for_this(app, per_page=per_page) count = len(applications) with app.test_request_context(url): page = self.render_soup( applications=applications, pagination=QueryPagination(total=count), ordering=get_default_applications_list_ordering(applications), ) assert page.find(class_="table-paginator") is None def test_copyright_notice(self, app, faker, expected_copyright_notice): applications = [] per_page = 10 url = self.url_for_this(app, per_page=per_page) count = len(applications) with app.test_request_context(url): page = self.render_soup( applications=applications, pagination=QueryPagination(total=count), ordering=get_default_applications_list_ordering(applications), ) footer = page.find("footer") copyright_notice = footer.find(id="copyright-notice") assert ( copyright_notice.get_text(strip=True) == expected_copyright_notice ) def test_frontend_libs_are_added( self, app, faker, expected_copyright_notice ): applications = [] per_page = 10 url = self.url_for_this(app, per_page=per_page) count = len(applications) with app.test_request_context(url): page = self.render_soup( applications=applications, pagination=QueryPagination(total=count), ordering=get_default_applications_list_ordering(applications), ) assert all(helpers.added_frontend_libs(page)) # Helpers def get_applications(self, pgdb_session, faker, qty=None): applications = ApplicationFactory.build_batch( qty or faker.pyint(max_value=5) ) for application in applications: pgdb_session.add(application) pgdb_session.commit() return applications def urlspec_for_this(self, app, **kwargs): u = urlparse(self.url_for_this(app, **kwargs)) return u._replace(scheme="", netloc="") def render_soup(self, *args, **kwargs): rt = render_template( "applications/list_applications.html", *args, **kwargs ) return BeautifulSoup(rt, "html.parser")