The screenshots illustrating this UI change are: 1. Vulnerability with a linked CVE and an associated bug.
https://people.canonical.com/~guruprasad/vulnerability_with_cve_and_bugs.png The page title for this is something like "Bugs : Vulnerability CVE-1234-5678 : <distribution name>". 2. Vulnerability without a linked CVE and any associated bugs. https://people.canonical.com/~guruprasad/vulnerability_without_any_links.png The page title for this is something like "Bugs : Vulnerability #<vulnerability.id> : <distribution name>". Diff comments: > diff --git a/lib/lp/bugs/browser/tests/test_cve.py > b/lib/lp/bugs/browser/tests/test_cvereport.py > similarity index 100% > rename from lib/lp/bugs/browser/tests/test_cve.py > rename to lib/lp/bugs/browser/tests/test_cvereport.py > diff --git a/lib/lp/bugs/browser/tests/test_vulnerability.py > b/lib/lp/bugs/browser/tests/test_vulnerability.py > new file mode 100644 > index 0000000..eac7e06 > --- /dev/null > +++ b/lib/lp/bugs/browser/tests/test_vulnerability.py > @@ -0,0 +1,246 @@ > +# Copyright 2022 Canonical Ltd. This software is licensed under the > +# GNU Affero General Public License version 3 (see the file LICENSE). > + > +"""Vulnerability browser tests""" > +from datetime import datetime, timezone > + > +from soupmatchers import HTMLContains, Tag, Within > +from testtools.matchers import MatchesAll, Not > + > +from lp.services.webapp import canonical_url > +from lp.testing import ANONYMOUS, BrowserTestCase, login, person_logged_in > +from lp.testing.layers import DatabaseFunctionalLayer > + > + > +class TestVulnerabilityPage(BrowserTestCase): > + """Tests for the vulnerability browser page.""" > + > + layer = DatabaseFunctionalLayer > + > + def get_vulnerability_field_tag(self, name, text, element="span"): > + return Tag( > + name, > + element, > + attrs={"id": "-".join(name.lower().split())}, > + text=text, > + ) > + > + def test_page_title_vulnerability_without_linked_cve(self): > + vulnerability = self.factory.makeVulnerability() > + browser = self.getUserBrowser( > + canonical_url(vulnerability), > + user=self.factory.makePerson(), > + ) > + login(ANONYMOUS) > + self.assertThat( > + browser.contents, > + HTMLContains( > + Tag( > + "page title", > + "title", > + text="Bugs : Vulnerability #{} : {}".format( > + vulnerability.id, > + vulnerability.distribution.displayname, > + ), > + ) > + ), > + ) > + > + def test_page_title_vulnerability_with_linked_cve(self): > + cve = self.factory.makeCVE(sequence="2022-1234") > + vulnerability = self.factory.makeVulnerability(cve=cve) > + browser = self.getUserBrowser( > + canonical_url(vulnerability), > + user=self.factory.makePerson(), > + ) > + login(ANONYMOUS) > + self.assertThat( > + browser.contents, > + HTMLContains( > + Tag( > + "page title", > + "title", > + text="Bugs : Vulnerability CVE-2022-1234 : {}".format( > + vulnerability.distribution.displayname > + ), > + ) > + ), > + ) > + > + def test_vulnerability_page_contains_all_expected_fields(self): > + vulnerability = self.factory.makeVulnerability() > + browser = self.getUserBrowser( > + canonical_url(vulnerability), > + user=self.factory.makePerson(), > + ) > + fields = ( > + "Date created", > + "Date made public", > + "CVE", > + "Information type", > + "Status", > + "Importance", > + "Importance explanation", > + "Creator", > + "Notes", > + "Mitigation", > + ) > + matchers = [] > + for field in fields: > + matchers.append( > + HTMLContains(Tag(field, "b", text="{}:".format(field))) > + ) > + self.assertThat(browser.contents, MatchesAll(*matchers)) > + > + def test_vulnerability_page_default_values(self): > + vulnerability = self.factory.makeVulnerability() > + browser = self.getUserBrowser( > + canonical_url(vulnerability), > + user=self.factory.makePerson(), > + ) > + login(ANONYMOUS) > + self.assertThat( > + browser.contents, > + MatchesAll( > + HTMLContains( > + self.get_vulnerability_field_tag( > + "Date created", > + vulnerability.date_created.strftime("%Y-%m-%d"), > + ), > + self.get_vulnerability_field_tag( > + "Date made public", "None" > + ), > + self.get_vulnerability_field_tag("CVE", "None"), > + self.get_vulnerability_field_tag( > + "Information type", > + vulnerability.information_type.title, > + ), > + self.get_vulnerability_field_tag( > + "Status", vulnerability.status.title > + ), > + self.get_vulnerability_field_tag( > + "Importance", vulnerability.importance.title > + ), > + self.get_vulnerability_field_tag( > + # importance_explanation defaults to `None` but the > + # factory > + # method auto-generates a value for it. > + "Importance explanation", > + vulnerability.importance_explanation, > + ), > + Within( > + Tag("Creator", "span", attrs={"id": "creator"}), > + Tag( > + "Creator link", > + "a", > + attrs={ > + "href": canonical_url(vulnerability.creator), > + "class": "sprite person", > + }, > + text=vulnerability.creator.displayname, > + ), > + ), > + self.get_vulnerability_field_tag("Notes", "None"), > + self.get_vulnerability_field_tag("Mitigation", "None"), > + ), > + Not( > + HTMLContains( > + Tag( > + "Related bugs", "div", attrs={"id": > "related-bugs"} > + ) > + ) > + ), > + ), > + ) > + > + def test_vulnerability_cve_linked(self): > + cve = self.factory.makeCVE(sequence="2022-1234") > + vulnerability = self.factory.makeVulnerability(cve=cve) > + browser = self.getUserBrowser( > + canonical_url(vulnerability), > + user=self.factory.makePerson(), > + ) > + login(ANONYMOUS) > + self.assertThat( > + browser.contents, > + HTMLContains( > + Within( > + Tag( > + "CVE", > + "span", > + attrs={"id": "cve"}, > + ), > + Tag( > + "CVE link", > + "a", > + attrs={ > + "href": canonical_url(cve, force_local_path=True) > + }, > + ), > + ) > + ), > + ) > + > + def test_vulnerability_optional_parameters_set(self): > + vulnerability = self.factory.makeVulnerability( > + date_made_public=datetime(1970, 1, 1, tzinfo=timezone.utc), > + notes="These are some notes", > + mitigation="Here is a mitigation", > + ) > + browser = self.getUserBrowser( > + canonical_url(vulnerability), > + user=self.factory.makePerson(), > + ) > + login(ANONYMOUS) > + self.assertThat( > + browser.contents, > + HTMLContains( > + self.get_vulnerability_field_tag( > + "Date made public", > + vulnerability.date_made_public.strftime("%Y-%m-%d"), > + ), > + self.get_vulnerability_field_tag( > + "Notes", "These are some notes" > + ), > + self.get_vulnerability_field_tag( > + "Mitigation", "Here is a mitigation" > + ), > + ), > + ) > + > + def test_vulnerability_related_bugs_present(self): > + vulnerability = self.factory.makeVulnerability() > + bug1 = self.factory.makeBug() > + bug2 = self.factory.makeBug() > + with person_logged_in(vulnerability.distribution.owner): > + vulnerability.linkBug(bug1) > + vulnerability.linkBug(bug2) > + browser = self.getUserBrowser( > + canonical_url(vulnerability), > + user=self.factory.makePerson(), > + ) > + login(ANONYMOUS) > + self.assertThat( > + browser.contents, > + HTMLContains( This doesn't test the table rows corresponding to the bugtasks in each of the bugs because doing that felt a bit too much in the context of this page since it is just reusing an existing renderer from somewhere else. So let me know if I should still add checks for those. > + Tag("Related bugs", "div", attrs={"id": "related-bugs"}), > + Tag( > + "Bug #{}".format(bug1.id), > + "a", > + attrs={ > + "class": "sprite bug", > + "href": canonical_url(bug1, force_local_path=True), > + }, > + text="Bug #{}: {}".format(bug1.id, bug1.title), > + ), > + Tag( > + "Bug #{}".format(bug2.id), > + "a", > + attrs={ > + "class": "sprite bug", > + "href": canonical_url(bug2, force_local_path=True), > + }, > + text="Bug #{}: {}".format(bug2.id, bug2.title), > + ), > + ), > + ) -- https://code.launchpad.net/~lgp171188/launchpad/+git/launchpad/+merge/428481 Your team Launchpad code reviewers is requested to review the proposed merge of ~lgp171188/launchpad:vulnerability-ui into launchpad:master. _______________________________________________ Mailing list: https://launchpad.net/~launchpad-reviewers Post to : [email protected] Unsubscribe : https://launchpad.net/~launchpad-reviewers More help : https://help.launchpad.net/ListHelp

