-
Notifications
You must be signed in to change notification settings - Fork 10
UI: Add tests for SBOM Upload page #55
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| { | ||
| "spdxVersion": "SPDX-2.3", | ||
| "dataLicense": "CC0-1.0", | ||
| "SPDXID": "SPDXRef-SBOM-Upload-001", | ||
| "name": "test-upload-001", | ||
| "documentNamespace": "trustify-tests", | ||
| "creationInfo": { | ||
| "creators": ["trustify-tests contributors"], | ||
| "created": "2025-04-29T12:34:56Z" | ||
| }, | ||
| "packages": [ | ||
| { | ||
| "name": "test-upload-package-001", | ||
| "SPDXID": "SPDXRef-Package-test-upload-001", | ||
| "versionInfo": "0.0.1", | ||
| "downloadLocation": "NOASSERTION", | ||
| "sourceInfo": "written by hand", | ||
| "licenseConcluded": "NONE", | ||
| "licenseDeclared": "NOASSERTION", | ||
| "copyrightText": "NOASSERTION", | ||
| "externalRefs": [ | ||
| { | ||
| "referenceCategory": "PACKAGE_MANAGER", | ||
| "referenceLocator": "pkg:trustify-tests/upload/tt-upload@0.0.1?fakeQuali=fier", | ||
| "referenceType": "purl" | ||
| } | ||
| ] | ||
| } | ||
| ], | ||
| "relationships": [ | ||
| { | ||
| "spdxElementId": "SPDXRef-SBOM-Upload-001", | ||
| "relatedSpdxElement": "SPDXRef-Package-test-upload-001", | ||
| "relationshipType": "DESCRIBES" | ||
| } | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| { | ||
| "spdxVersion": "SPDX-2.3", | ||
| "dataLicense": "CC0-1.0", | ||
| "SPDXID": "SPDXRef-SBOM-Upload-002", | ||
| "name": "test-upload-002", | ||
| "documentNamespace": "trustify-tests", | ||
| "creationInfo": { | ||
| "creators": ["trustify-tests contributors"], | ||
| "created": "2025-04-29T12:34:56Z" | ||
| }, | ||
| "packages": [ | ||
| { | ||
| "name": "test-upload-package-002", | ||
| "SPDXID": "SPDXRef-Package-test-upload-002", | ||
| "versionInfo": "0.0.2", | ||
| "downloadLocation": "NOASSERTION", | ||
| "sourceInfo": "written by hand", | ||
| "licenseConcluded": "NONE", | ||
| "licenseDeclared": "NOASSERTION", | ||
| "copyrightText": "NOASSERTION", | ||
| "externalRefs": [ | ||
| { | ||
| "referenceCategory": "PACKAGE_MANAGER", | ||
| "referenceLocator": "pkg:trustify-tests/upload/tt-upload@0.0.2?fakeQuali=fier", | ||
| "referenceType": "purl" | ||
| } | ||
| ] | ||
| } | ||
| ], | ||
| "relationships": [ | ||
| { | ||
| "spdxElementId": "SPDXRef-SBOM-Upload-002", | ||
| "relatedSpdxElement": "SPDXRef-Package-test-upload-002", | ||
| "relationshipType": "DESCRIBES" | ||
| } | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| { | ||
| "spdxVersion": "SPDX-2.3", | ||
| "dataLicense": "CC0-1.0", | ||
| "SPDXID": "SPDXRef-SBOM-Upload-003", | ||
| "name": "test-upload-003", | ||
| "documentNamespace": "trustify-tests", | ||
| "creationInfo": { | ||
| "creators": ["trustify-tests contributors"], | ||
| "created": "2025-04-29T12:34:56Z" | ||
| }, | ||
| "packages": [ | ||
| { | ||
| "name": "test-upload-package-003", | ||
| "SPDXID": "SPDXRef-Package-test-upload-003", | ||
| "versionInfo": "0.0.3", | ||
| "downloadLocation": "NOASSERTION", | ||
| "sourceInfo": "written by hand", | ||
| "licenseConcluded": "NONE", | ||
| "licenseDeclared": "NOASSERTION", | ||
| "copyrightText": "NOASSERTION", | ||
| "externalRefs": [ | ||
| { | ||
| "referenceCategory": "PACKAGE_MANAGER", | ||
| "referenceLocator": "pkg:trustify-tests/upload/tt-upload@0.0.3?fakeQuali=fier", | ||
| "referenceType": "purl" | ||
| } | ||
| ] | ||
| } | ||
| ], | ||
| "relationships": [ | ||
| { | ||
| "spdxElementId": "SPDXRef-SBOM-Upload-003", | ||
| "relatedSpdxElement": "SPDXRef-Package-test-upload-003", | ||
| "relationshipType": "DESCRIBES" | ||
| } | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| { | ||
| "bomFormat": "CycloneDX", | ||
| "specVersion": "1.3", | ||
| "serialNumber": "urn:uuid:00000000-1111-2222-4444-555500000004", | ||
| "version": 1, | ||
| "metadata": { | ||
| "timestamp": "2023-02-27T23:15:50-08:00", | ||
| "tools": [ | ||
| { | ||
| "vendor": "trustify-tests", | ||
| "name": "contributors", | ||
| "version": "1" | ||
| } | ||
| ], | ||
| "component": { | ||
| "bom-ref": "0040000000000001", | ||
| "type": "container", | ||
| "name": "test-upload-container-004", | ||
| "version": "0.0.4", | ||
| "purl": "pkg:trustify-tests/upload/tt-upload-cont@0.0.4" | ||
| } | ||
| }, | ||
| "components": [ | ||
| { | ||
| "bom-ref": "0040000000000002", | ||
| "type": "application", | ||
| "name": "test-upload-package-004", | ||
| "version": "0.0.4", | ||
| "cpe": "cpe:2.3:a:trustify-tests/upload:tt-upload:0.0.4:*:*:*:*:*:*:*", | ||
| "purl": "pkg:trustify-tests/upload/tt-upload@0.0.4" | ||
| } | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| { | ||
| "bomFormat": "CycloneDX", | ||
| "specVersion": "1.3", | ||
| "serialNumber": "urn:uuid:00000000-1111-2222-4444-555500000005", | ||
| "version": 1, | ||
| "metadata": { | ||
| "timestamp": "2023-02-27T23:15:50-08:00", | ||
| "tools": [ | ||
| { | ||
| "vendor": "trustify-tests", | ||
| "name": "contributors", | ||
| "version": "1" | ||
| } | ||
| ], | ||
| "component": { | ||
| "bom-ref": "0050000000000001", | ||
| "type": "container", | ||
| "name": "test-upload-container-005", | ||
| "version": "0.0.5", | ||
| "purl": "pkg:trustify-tests/upload/tt-upload-cont@0.0.5" | ||
| } | ||
| }, | ||
| "components": [ | ||
| { | ||
| "bom-ref": "0050000000000002", | ||
| "type": "application", | ||
| "name": "test-upload-package-005", | ||
| "version": "0.0.5", | ||
| "cpe": "cpe:2.3:a:trustify-tests/upload:tt-upload:0.0.5:*:*:*:*:*:*:*", | ||
| "purl": "pkg:trustify-tests/upload/tt-upload@0.0.5" | ||
| } | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| { | ||
| "bomFormat": "CycloneDX", | ||
| "specVersion": "1.3", | ||
| "serialNumber": "urn:uuid:00000000-1111-2222-4444-555500000006", | ||
| "version": 1, | ||
| "metadata": { | ||
| "timestamp": "2023-02-27T23:15:50-08:00", | ||
| "tools": [ | ||
| { | ||
| "vendor": "trustify-tests", | ||
| "name": "contributors", | ||
| "version": "1" | ||
| } | ||
| ], | ||
| "component": { | ||
| "bom-ref": "0060000000000001", | ||
| "type": "container", | ||
| "name": "test-upload-container-006", | ||
| "version": "0.0.6", | ||
| "purl": "pkg:trustify-tests/upload/tt-upload-cont@0.0.6" | ||
| } | ||
| }, | ||
| "components": [ | ||
| { | ||
| "bom-ref": "0060000000000002", | ||
| "type": "application", | ||
| "name": "test-upload-package-006", | ||
| "version": "0.0.6", | ||
| "cpe": "cpe:2.3:a:trustify-tests/upload:tt-upload:0.0.6:*:*:*:*:*:*:*", | ||
| "purl": "pkg:trustify-tests/upload/tt-upload@0.0.6" | ||
| } | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| Feature: Upload SBOM | ||
|
|
||
| Background: | ||
| Given User is authenticated | ||
|
|
||
| Scenario Outline: Verify Upload SBOM page content | ||
| When User visits Upload page | ||
| Then SBOM upload tab is selected | ||
| And Drag and drop instructions are visible | ||
| And Upload button is present | ||
| And Accepted file types are described | ||
|
|
||
| Scenario Outline: Upload single SBOM | ||
| Given User visits Upload page | ||
| When User uploads "single SBOM" | ||
| Then Total uploaded count is shown for "single SBOM" | ||
| And Results of uploading "single SBOM" are visible | ||
|
|
||
| Scenario Outline: Upload multiple SBOMs | ||
| Given User visits Upload page | ||
| When User uploads "multiple SBOMs" | ||
| Then Total uploaded count is shown for "multiple SBOMs" | ||
| And Results of uploading "multiple SBOMs" are visible | ||
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
| @@ -0,0 +1,126 @@ | ||||
| import fs from "fs"; | ||||
| import path from "path"; | ||||
| import { expect, test } from "@playwright/test"; | ||||
| import { createBdd } from "playwright-bdd"; | ||||
|
|
||||
| export const { Given, When, Then, Step } = createBdd(); | ||||
|
|
||||
| const MENU_UPLOAD = "Upload"; | ||||
| const HEADER_UPLOAD = "Upload"; | ||||
| const BUTTON_UPLOAD = "Upload"; | ||||
| const TAB_SBOM = "SBOM"; | ||||
| const DRAG_INSTRUCTIONS = "Drag and drop files here or"; | ||||
| const ACCEPTED_TYPES_DESC = "Accepted file types:"; | ||||
|
|
||||
| const TIMEOUT_PAGELOAD_IMPORT = 2_000; | ||||
| const TIMEOUT_UPLOAD_DONE = 90_000; | ||||
|
|
||||
| const SBOM_SET = { | ||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure about keeping these SBOMs out of the
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The reason i went with placing them next to test and not in common/assets ... as they are not 'common', they should NOT be uploaded e.g. during initial ingestion or such - they are specific meant only for this test of Upload functionality. |
||||
| "single SBOM": ["test-upload-001.spdx.json"], | ||||
| "multiple SBOMs": [ | ||||
| "test-upload-002.spdx.json", | ||||
| "test-upload-003.spdx.json.bz2", | ||||
| "test-upload-004.cdx.json", | ||||
| "test-upload-005.cdx.json", | ||||
| "test-upload-006.cdx.json.bz2", | ||||
| ], | ||||
| }; | ||||
|
|
||||
| const visitUploadPage = async ({ page }) => { | ||||
| await page.goto("/importers"); // dont care which page here, importers is lot faster than Dashboard | ||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this necessary, actually? Isn't the side menu always visible?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Menu on the left is not visible if there is no page loaded at start ... which seems there is not if auth is not enabled (so e.g. in dev/local or ci or such). |
||||
| await page.getByRole("link", { name: MENU_UPLOAD }).click(); | ||||
|
|
||||
| const header = page.locator(`xpath=(//h1[text()="${HEADER_UPLOAD}"])`); | ||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a soft suggestion, but in general I would try not to rely on xpaths and CSS selectors if not necessary. You can let Playwright do the work, e.g.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will look into this, thanks! |
||||
| await header.waitFor({ state: "visible", timeout: TIMEOUT_PAGELOAD_IMPORT }); | ||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure if I understand this - is this a timeout for loading the page? Why specifically change it here? |
||||
| }; | ||||
| Step("User visits Upload page", visitUploadPage); | ||||
|
|
||||
| function assetsPath(files: string[]): string[] { | ||||
| return files.map((e) => path.join(__dirname, "assets", e)); | ||||
| } | ||||
|
|
||||
| // PAGE CONTENT | ||||
|
|
||||
| Then("SBOM upload tab is selected", async ({ page }) => { | ||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We already have this function here. I'd prefer not duplicating these, if possible.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice, i've missed it will switch to it. |
||||
| await expect(page.getByRole("tab", { name: TAB_SBOM })).toHaveAttribute( | ||||
| "aria-selected", | ||||
| "true" | ||||
| ); | ||||
| }); | ||||
|
|
||||
| Then("Drag and drop instructions are visible", async ({ page }) => { | ||||
| await expect( | ||||
| page.getByLabel("SBOM").getByText(DRAG_INSTRUCTIONS) | ||||
| ).toBeVisible(); | ||||
| }); | ||||
|
|
||||
| Then("Upload button is present", async ({ page }) => { | ||||
| await expect(page.getByRole("button", { name: "Upload" })).toBeVisible(); | ||||
| }); | ||||
|
|
||||
| Then("Accepted file types are described", async ({ page }) => { | ||||
| expect(page.getByLabel("SBOM").getByText(ACCEPTED_TYPES_DESC)).toBeVisible(); | ||||
| }); | ||||
|
|
||||
| // FILE UPLOAD | ||||
|
|
||||
| When("User uploads {string}", async ({ page }, data_set_key) => { | ||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another soft suggestion - it would be easier to read without having to scroll up if instead of |
||||
| const files = assetsPath(SBOM_SET[data_set_key]); | ||||
|
|
||||
| const fileChooserPromise = page.waitForEvent("filechooser"); | ||||
| await page.getByRole("button", { name: BUTTON_UPLOAD, exact: true }).click(); | ||||
| const fileChooser = await fileChooserPromise; | ||||
|
|
||||
| await fileChooser.setFiles(files); | ||||
| }); | ||||
|
|
||||
| // FILE UPLOAD RESULTS | ||||
|
|
||||
| Then( | ||||
| "Total uploaded count is shown for {string}", | ||||
| async ({ page }, data_set_key) => { | ||||
| test.setTimeout(TIMEOUT_UPLOAD_DONE); | ||||
| const count = SBOM_SET[data_set_key].length; | ||||
|
|
||||
| await expect( | ||||
| page.locator( | ||||
| "#upload-sbom-tab-content .pf-v6-c-multiple-file-upload__status-progress" | ||||
| ) | ||||
| ).toContainText(`${count} of ${count} files uploaded`, { | ||||
| timeout: TIMEOUT_UPLOAD_DONE, | ||||
| }); | ||||
| } | ||||
| ); | ||||
|
|
||||
| Then( | ||||
| "Results of uploading {string} are visible", | ||||
| async ({ page }, data_set_key) => { | ||||
| const individual_results = page.locator( | ||||
| ".pf-v6-c-expandable-section__content .pf-v6-c-multiple-file-upload__status-item" | ||||
| ); | ||||
|
|
||||
| // expect correct count of individual result entries | ||||
| await expect(individual_results).toHaveCount(SBOM_SET[data_set_key].length); | ||||
|
|
||||
| // now upload itself may take a while so increase overall test timeout | ||||
| test.setTimeout(TIMEOUT_UPLOAD_DONE); | ||||
|
|
||||
| // expect name of each sbom to be in the list of results | ||||
| await expect( | ||||
| individual_results.locator( | ||||
| ".pf-v6-c-multiple-file-upload__status-item-progress-text" | ||||
| ) | ||||
| ).toContainText(SBOM_SET[data_set_key], { | ||||
| timeout: TIMEOUT_UPLOAD_DONE, | ||||
| }); | ||||
|
|
||||
| // expect result for each sbom to be present and have 100% state | ||||
| await expect( | ||||
| individual_results.locator( | ||||
| ".pf-v6-c-progress__status .pf-v6-c-progress__measure" | ||||
| ) | ||||
| ).toContainText(new Array(SBOM_SET[data_set_key].length).fill("100%"), { | ||||
| timeout: TIMEOUT_UPLOAD_DONE, | ||||
| }); | ||||
| } | ||||
| ); | ||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For your consideration - should we also check if the SBOM is present in the DB? And should we check for both the file and the DB record, as they are two separate things? I think the former would be much harder to check for in a deployed instance, though, so maybe it's out of scope for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah i would consider these out of scope as these checks would need access to and knowledge of internal structures.
What could be better fit here is confirming upload/ingestion by search/viewing details of the sboms e.g. the SBOM Explorer/Details view.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, we should have the steps for that implemented, so that should be easy to do.