Skip to content
This repository was archived by the owner on Sep 11, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 49 additions & 8 deletions tests/ui/features/@sbom-explorer/sbom-explorer.feature
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ Feature: SBOM Explorer - View SBOM details
Given User is authenticated

Scenario Outline: View SBOM Overview
Given User visits SBOM details Page of "<sbomName>"
Given An ingested "<sbomType>" SBOM "<sbomName>" is available
Copy link
Copy Markdown
Member

@carlosthe19916 carlosthe19916 Apr 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The <sbomType> is not used neither declared in the Examples section of the Scenario definition. I wonder if we should remove it.

When User visits SBOM details Page of "<sbomName>"
Then The page title is "<sbomName>"
And Tab "Info" is visible
And Tab "Packages" is visible
Expand All @@ -15,7 +16,8 @@ Feature: SBOM Explorer - View SBOM details
| quarkus-bom |

Scenario Outline: View SBOM Info (Metadata)
Given User visits SBOM details Page of "<sbomName>"
Given An ingested "<sbomType>" SBOM "<sbomName>" is available
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above, <sbomType> is not used nor needed.

When User visits SBOM details Page of "<sbomName>"
Then Tab "Info" is selected
Then "SBOM's name" is visible
And "SBOM's namespace" is visible
Expand All @@ -26,18 +28,20 @@ Feature: SBOM Explorer - View SBOM details
Examples:
| sbomName |
| quarkus-bom |

Scenario Outline: Downloading SBOM file
Given User visits SBOM details Page of "<sbomName>"
Given An ingested "<sbomType>" SBOM "<sbomName>" is available
When User visits SBOM details Page of "<sbomName>"
Then "Download SBOM" action is invoked and downloaded filename is "<expectedSbomFilename>"
Then "Download License Report" action is invoked and downloaded filename is "<expectedLicenseFilename>"

Examples:
| sbomName | expectedSbomFilename | expectedLicenseFilename |
| quarkus-bom | quarkus-bom.json | quarkus-bom_licenses.tar.gz |

Scenario Outline: View list of SBOM Packages
Given User visits SBOM details Page of "<sbomName>"
Given An ingested "<sbomType>" SBOM "<sbomName>" is available
When User visits SBOM details Page of "<sbomName>"
When User selects the Tab "Packages"
# confirms its visible for all tabs
Then The page title is "<sbomName>"
Expand All @@ -55,5 +59,42 @@ Feature: SBOM Explorer - View SBOM details
Then The Package table total results is greather than 1

Examples:
| sbomName | packageName |
| quarkus-bom | jdom |
| sbomType | sbomName | packageName |
| SPDX | quarkus-bom | jdom |

Scenario Outline: View <sbomType> SBOM Vulnerabilities
Given An ingested "<sbomType>" SBOM "<sbomName>" containing Vulnerabilities
When User visits SBOM details Page of "<sbomName>"
When User selects the Tab "Vulnerabilities"
When User Clicks on Vulnerabilities Tab Action
Then Vulnerability Popup menu appears with message
Then Vulnerability Risk Profile circle should be visible
Then Vulnerability Risk Profile shows summary of vulnerabilities
Comment thread
mrrajan marked this conversation as resolved.
Then SBOM Name "<sbomName>" should be visible inside the tab
Then SBOM Version should be visible inside the tab
Then SBOM Creation date should be visible inside the tab
# Then List of related Vulnerabilities should be sorted by "CVSS" in descending order

Examples:
| sbomType | sbomName |
| SPDX | quarkus-bom |

@slow
Scenario Outline: Pagination of <sbomType> SBOM Vulnerabilities
Given An ingested "<sbomType>" SBOM "<sbomName>" containing Vulnerabilities
When User visits SBOM details Page of "<sbomName>"
When User selects the Tab "Vulnerabilities"
Then Pagination of Vulnerabilities list works
Examples:
| sbomType | sbomName |
| SPDX | quarkus-bom |

@slow
Scenario Outline: View paginated list of <sbomType> SBOM Packages
Given An ingested "<sbomType>" SBOM "<sbomName>" is available
When User visits SBOM details Page of "<sbomName>"
When User selects the Tab "Packages"
Then Pagination of Packages list works
Examples:
| sbomType | sbomName |
| SPDX | quarkus-bom |
107 changes: 98 additions & 9 deletions tests/ui/features/@sbom-explorer/sbom-explorer.step.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,24 @@ import { createBdd } from "playwright-bdd";
import { expect } from "playwright/test";
import { DetailsPage } from "../../helpers/DetailsPage";
import { ToolbarTable } from "../../helpers/ToolbarTable";
import { SearchPage } from "../../helpers/SearchPage";

export const { Given, When, Then } = createBdd();

const PACKAGE_TABLE_NAME = "Package table";
const VULN_TABLE_NAME = "Vulnerability table";

Given(
"An ingested {string} SBOM {string} is available",
async ({ page }, _sbomType, sbomName) => {
const searchPage = new SearchPage(page);
await searchPage.dedicatedSearch("SBOMs", sbomName);
}
);

When(
"User visits SBOM details Page of {string}",
async ({ page }, sbomName) => {
await page.goto("/");
await page.getByRole("link", { name: "SBOMs" }).click();

await page.getByPlaceholder("Search").click();
await page.getByPlaceholder("Search").fill(sbomName);
await page.getByPlaceholder("Search").press("Enter");

await page.getByRole("link", { name: sbomName, exact: true }).click();
}
);
Expand All @@ -31,7 +34,7 @@ Then(
const downloadPromise = page.waitForEvent("download");

const detailsPage = new DetailsPage(page);
detailsPage.clickOnPageAction(actionName);
await detailsPage.clickOnPageAction(actionName);

const download = await downloadPromise;

Expand All @@ -44,7 +47,7 @@ Then(
"The Package table is sorted by {string}",
async ({ page }, columnName) => {
const toolbarTable = new ToolbarTable(page, PACKAGE_TABLE_NAME);
toolbarTable.verifyTableIsSortedBy(columnName);
await toolbarTable.verifyTableIsSortedBy(columnName);
}
);

Expand Down Expand Up @@ -78,3 +81,89 @@ Then(
await toolbarTable.verifyColumnContainsText(columnName, expectedValue);
}
);

Given(
"An ingested {string} SBOM {string} containing Vulnerabilities",
async ({ page }, _sbomType, sbomName) => {
const searchPage = new SearchPage(page);
await searchPage.dedicatedSearch("SBOMs", sbomName);
Copy link
Copy Markdown
Member

@carlosthe19916 carlosthe19916 Apr 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the dedicatedSearch method receives a "string" that represents the page we want to see. I think the first parameter does not belong there. What about:

// Here we define that when we instantiate a Search Object we want to move the page to the SBOMs page.
const searchPage = new SearchPage(page, "SBOMs");
await searchPage.search("sbom_name");

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Functionality wise, this wont make much difference. But, it makes sense for code maintenance and readability. I will try including this change as part of next PR.

const element = await page.locator(
`xpath=(//tr[contains(.,'${sbomName}')]/td[@data-label='Vulnerabilities']/div)[1]`
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see many xpath selectors which by default is ok. But:

  • We can decide to keep working with xpath OR
  • We can also change certain parts of the UI's source code to make selection easier. E.g. in the past I faced a similar problem and I ended up adding aria selectors like in this PR https://github.com/trustification/trustify-ui/pull/313/files . Then use those aria selectors to avoid complex xpath selectors. As a bonus, the aria selectors are going to enrich the assistive technologies, such as screen readers in the UI.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In most of the conditions - I agree, we should consider aria-label or clean object selectors. But dynamic xpath's would be super helpful with reusable functions. For example, the pagination or per page selector validation dynamic xpath will be more useful. Let's consider where we can have dynamic xpath and static locators.

);
await expect(element, "SBOM have no vulnerabilities").toHaveText(
/^(?!0$).+/
);
}
);

When("User Clicks on Vulnerabilities Tab Action", async ({ page }) => {
await page.getByLabel("Tab action").click();
});

Then("Vulnerability Popup menu appears with message", async ({ page }) => {
await page.getByText("Any found vulnerabilities").isVisible();
await page.getByLabel("Close").click();
});

Then(
"Vulnerability Risk Profile circle should be visible",
async ({ page }) => {
await page.locator(`xpath=//div[contains(@class, 'chart')]`).isVisible();
}
);

Then(
"Vulnerability Risk Profile shows summary of vulnerabilities",
async ({ page }) => {
const detailsPage = new DetailsPage(page);
await detailsPage.verifyVulnerabilityPanelcount();
}
);

Then(
"SBOM Name {string} should be visible inside the tab",
async ({ page }, sbomName) => {
const panelSbomName = await page.locator(
`xpath=//section[@id='refVulnerabilitiesSection']//dt[contains(.,'Name')]/following-sibling::dd`
Comment thread
mrrajan marked this conversation as resolved.
);
await panelSbomName.isVisible();
await expect(await panelSbomName.textContent()).toEqual(sbomName);
}
);

Then("SBOM Version should be visible inside the tab", async ({ page }) => {
const panelSBOMVersion = await page.locator(
`xpath=//section[@id='refVulnerabilitiesSection']//dt[contains(.,'Version')]/following-sibling::dd`
Comment thread
mrrajan marked this conversation as resolved.
);
await panelSBOMVersion.isVisible();
});

Then(
"SBOM Creation date should be visible inside the tab",
async ({ page }) => {
const panelSBOMVersion = await page.locator(
`xpath=//section[@id='refVulnerabilitiesSection']//dt[contains(.,'Creation date')]/following-sibling::dd`
Comment thread
mrrajan marked this conversation as resolved.
);
await panelSBOMVersion.isVisible();
}
);

Then(
"List of related Vulnerabilities should be sorted by {string} in descending order",
async ({ page }, columnName) => {
const toolbarTable = new ToolbarTable(page, VULN_TABLE_NAME);
await toolbarTable.verifyTableIsSortedBy(columnName, false);
}
);

Then("Pagination of Vulnerabilities list works", async ({ page }) => {
const toolbarTable = new ToolbarTable(page, VULN_TABLE_NAME);
const vulnTableTopPagination = `xpath=//div[@id="vulnerability-table-pagination-top"]`;
await toolbarTable.verifyPagination(vulnTableTopPagination);
});

Then("Pagination of Packages list works", async ({ page }) => {
const toolbarTable = new ToolbarTable(page, PACKAGE_TABLE_NAME);
const vulnTableTopPagination = `xpath=//div[@id="package-table-pagination-top"]`;
await toolbarTable.verifyPagination(vulnTableTopPagination);
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createBdd } from "playwright-bdd";
import { ToolbarTable } from "../../helpers/ToolbarTable";
import { SearchPage } from "../../helpers/SearchPage";

export const { Given, When, Then } = createBdd();

Expand All @@ -9,13 +10,8 @@ const ADVISORY_TABLE_NAME = "Advisory table";
Given(
"User visits Vulnerability details Page of {string}",
async ({ page }, vulnerabilityID) => {
await page.goto("/");
await page.getByRole("link", { name: "Vulnerabilities" }).click();

await page.getByPlaceholder("Search").click();
await page.getByPlaceholder("Search").fill(vulnerabilityID);
await page.getByPlaceholder("Search").press("Enter");

const searchPage = new SearchPage(page);
await searchPage.dedicatedSearch("Vulnerabilities", vulnerabilityID);
await page.getByRole("link", { name: vulnerabilityID }).click();
}
);
Expand All @@ -24,7 +20,7 @@ Given(

Then("The SBOMs table is sorted by {string}", async ({ page }, columnName) => {
const toolbarTable = new ToolbarTable(page, SBOM_TABLE_NAME);
toolbarTable.verifyTableIsSortedBy(columnName);
await toolbarTable.verifyTableIsSortedBy(columnName);
});

Then(
Expand Down Expand Up @@ -63,7 +59,7 @@ Then(
"The Advisory table is sorted by {string}",
async ({ page }, columnName) => {
const toolbarTable = new ToolbarTable(page, ADVISORY_TABLE_NAME);
toolbarTable.verifyTableIsSortedBy(columnName);
await toolbarTable.verifyTableIsSortedBy(columnName);
}
);

Expand Down
8 changes: 4 additions & 4 deletions tests/ui/features/sbom-explorer.feature
Original file line number Diff line number Diff line change
Expand Up @@ -174,15 +174,15 @@ Feature: SBOM Explorer - View SBOM details
| SPDX |

Scenario Outline: View <sbomType> SBOM Vulnerabilities
Given there is ingested <sbomType> SBOM which is affected by Vulnerabilities
When user visits SBOM details page
And user selects Vulnerabilities tab
Given An ingested <sbomType> SBOM containing Vulnerabilities
When User visits SBOM details page
And Selects Vulnerabilities tab
Then Vulnerability Risk Profile circle should be visible
And Vulnerability Risk Profile shows summary of vulnerabilities
And SBOM Name should be visible inside the tab
And SBOM Version should be visible inside the tab
And SBOM Creation date should be visible inside the tab
And list of related Vulnerabilities should be sorted by CVSS in descending order
And List of related Vulnerabilities should be sorted by CVSS in descending order

Examples:
| sbomType |
Expand Down
Loading
Loading