Skip to content

test(e2e): add SBOM Groups BDD test coverage#1108

Merged
mrrajan merged 16 commits into
guacsec:mainfrom
mrrajan:TC-3811
Jul 15, 2026
Merged

test(e2e): add SBOM Groups BDD test coverage#1108
mrrajan merged 16 commits into
guacsec:mainfrom
mrrajan:TC-3811

Conversation

@mrrajan

@mrrajan mrrajan commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Summary

  • 38 BDD scenarios covering SBOM Groups UI (up from 14 existing): product labels, hierarchical tree, parent group selection, invalid group ID, SBOM counts, product badge, edit/remove parent, breadcrumbs, sorting, pagination, 3-level hierarchy, delete guards, and filter-by-parent
  • 66 API tests across 11 suites: CRUD, ETag/optimistic locking, sorting, filtering/pagination, hierarchy, error cases, assignments (PUT/PATCH/bulk), and edge cases
  • New page objects: SbomGroupDetailPage, AddToGroupModal; enhanced: SbomGroupListPage, GroupFormModal, DetailsPageLayout, ConfirmDialog
  • API test helpers module: sbom-group-helpers.ts
  • Review feedback fixes: removed explicit timeouts from shared page objects (TC-5189), separated filter-existence check from clearAllFilters (TC-5190)

UI Scenarios Added

# Scenario Priority
1 Product label badge appears for product groups High
2 Expand and collapse hierarchical tree nodes High
3 Create child group with parent selection High
4 Navigate to invalid group ID shows error Medium
5 SBOM count is displayed for groups with SBOMs Medium
6 Product badge is displayed on group detail page Medium
7 Edit group to assign a parent Medium
8 Edit group to remove parent makes it a root group Medium
9 Breadcrumb navigation on group detail page Low
10 Pagination and sorting on group detail page Low
11 Filter matching child auto-expands parent High
12 Toggle product flag from Yes to No Medium
13 SBOM count decreases when SBOM is reassigned Medium
14 SBOM counts are independent between parent and child Medium
15 Expand and navigate 3-level hierarchy Low
16 Navigate into grandchild from 3-level tree Low
17 Remove parent from middle group in 3-level hierarchy Low
18 Delete group with SBOMs reassigns to parent Medium

API Test Suites

  • CRUD (16 tests): create, read, update, delete with name/description/labels/parent
  • ETag / optimistic locking (2 tests): stale ETag rejection
  • Sorting (2 tests): name ascending/descending
  • Filtering & pagination (6 tests): name filter, label filter, offset/limit
  • Hierarchy (5 tests): parent-child, multi-level, orphan on parent delete
  • Error cases (7 tests): duplicate name, missing name, not found, invalid parent
  • Assignments (28 tests): PUT replace, PATCH add/remove/clear, bulk assign, edge cases

Not Implemented (features not yet in codebase)

  • Remove SBOM from group — no UI exists for this action
  • "Groups" column on All SBOMs page — column does not exist in SBOM table
  • Loading states — requires component-level test, not E2E
  • React Query caching — requires unit/integration test, not E2E

Test plan

  • Run npm run e2e:test:ui to verify all UI scenarios pass
  • Run npm run e2e:test:api to verify all API tests pass
  • Verify new scenarios follow BDD standards

Implements TC-3811

@sourcery-ai

sourcery-ai Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Adds comprehensive SBOM Groups UI BDD coverage, enriches SBOM Groups list/detail page objects and shared UI helpers, and introduces extensive SBOM Groups API tests and helpers for CRUD, hierarchy, assignments, sorting, filtering, and pagination.

File-Level Changes

Change Details Files
Refactor SBOM Groups UI step definitions to use richer page objects and new helper abstractions for groups list/detail, forms, modals, and confirmations.
  • Reworked sbom-groups.step.ts to rely on SbomGroupListPage and new SbomGroupDetailPage for navigation, querying rows, treegrid operations, and interactions.
  • Centralized group form interactions via GroupFormModal.fromCurrentPage, including name/description/product flag, parent selection, labels, and validation helpers.
  • Replaced ad-hoc confirmation dialog handling with DeletionConfirmDialog helpers for confirm/cancel flows and added reusable alert/notification assertions.
  • Introduced compound steps for multi-SBOM add-to-group flows and shared fixture state (selectedSbomNames) to reduce repetition and improve test readability.
e2e/tests/ui/features/@sbom-groups/sbom-groups.step.ts
e2e/tests/ui/fixtures.ts
e2e/tests/ui/pages/sbom-group-list/GroupFormModal.ts
e2e/tests/ui/pages/ConfirmDialog.ts
Expand SBOM Groups BDD scenarios to cover groups hierarchy, product labels, SBOM counts, parent assignment/removal, invalid IDs, breadcrumbs, pagination, sorting, and validation.
  • Rewrote and extended sbom-groups.feature to add scenarios for product label badges, hierarchical tree expand/collapse, parent/child and 3-level hierarchies, breadcrumb navigation, SBOM counts, and detail-page filtering/sorting.
  • Adjusted existing CRUD and add-to-group scenarios to use deterministic group names, toolbar filters, and SBOM count assertions.
  • Documented several not-yet-implemented behaviors via commented-out scenarios (e.g., filtered children visibility) and negative/validation flows for duplicate names, self-parenting, and reserved labels.
e2e/tests/ui/features/@sbom-groups/sbom-groups.feature
Enhance SBOM Groups list and detail page objects plus shared UI helpers to support tree navigation, labels, counts, breadcrumbs, pagination, and safer toolbar operations.
  • Extended SbomGroupListPage with treegrid accessors, row lookup by group name, expand/collapse APIs, tree-level helpers, label badge and SBOM count queries, and kebab/menu handling.
  • Added SbomGroupDetailPage with access to group description, product/label badges, member SBOMs table, toolbar, pagination, breadcrumbs, and empty-state checks.
  • Improved Toolbar.clearAllFilters to no-op safely when button is absent and to wait for filter chip removal with higher timeout, and added a bulk-selected-count custom matcher.
  • Augmented DetailsPageLayout with breadcrumb verification/click helpers, Pagination with more robust items-per-page selection (timeout), and introduced AddToGroupModal for consistent group-selection flows.
e2e/tests/ui/pages/sbom-group-list/SbomGroupListPage.ts
e2e/tests/ui/pages/sbom-group-detail/SbomGroupDetailPage.ts
e2e/tests/ui/assertions/ToolbarMatchers.ts
e2e/tests/ui/pages/Toolbar.ts
e2e/tests/ui/pages/DetailsPageLayout.ts
e2e/tests/ui/pages/Pagination.ts
e2e/tests/ui/pages/sbom-group-list/AddToGroupModal.ts
Add comprehensive SBOM Groups API test suite and helper library covering CRUD, hierarchy, optimistic locking, filtering/sorting, assignments, and edge cases.
  • Introduced sbom-groups.ts Playwright API test file validating group CRUD, labels, hierarchy, cycle detection, ETag/If-Match semantics, filtering, sorting, totals, and extensive SBOM assignment behaviors (single/multi, bulk, PATCH add/remove, cleanup).
  • Added sbom-group-helpers.ts to encapsulate Axios calls for groups and assignments, including create/read/update/delete, list with query parameters, assignment read/update/bulk/patch, SBOM discovery, and teardown/cleanup utilities.
  • Ensured tests clean up created groups and assignments (children before parents) to keep the test backend stable and idempotent across runs.
e2e/tests/api/features/sbom-groups.ts
e2e/tests/api/helpers/sbom-group-helpers.ts
Tighten importer explorer tests around enable/disable flows and confirmation handling.
  • Tracked newly enabled importer names in importer-explorer.step.ts and added cleanup logic to disable them after verification using DeletionConfirmDialog.
  • Extended importer-explorer.feature scenarios to explicitly disable importers after scheduling/running and assert they return to a Disabled state.
e2e/tests/ui/features/@importer-explorer/importer-explorer.step.ts
e2e/tests/ui/features/@importer-explorer/importer-explorer.feature

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@codecov

codecov Bot commented Jun 25, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 53.57%. Comparing base (ac5cb68) to head (7a83ebb).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1108      +/-   ##
==========================================
+ Coverage   53.04%   53.57%   +0.52%     
==========================================
  Files         269      269              
  Lines        5904     5904              
  Branches     1849     1849              
==========================================
+ Hits         3132     3163      +31     
+ Misses       2465     2447      -18     
+ Partials      307      294      -13     
Flag Coverage Δ
e2e 71.41% <ø> (+0.71%) ⬆️
unit 6.82% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hey - I've found 2 issues, and left some high level feedback:

  • There is repeated setup logic for ensuring groups (and parent/child relationships) exist across multiple steps; consider extracting this into shared helper utilities to reduce duplication and keep test maintenance easier.
  • The invalid group ID error assertion uses a broad heading regex (/error|not found|something went wrong/i); tightening this to a known error message or a more specific selector would make the test less flaky and more resilient to copy changes.
  • Several selectors rely on visible text patterns (e.g., text=/\d+ SBOMs?/ and label text like "Product"); if UI copy changes are expected, you may want to pivot to more stable attributes or test-ids to avoid brittle tests.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- There is repeated setup logic for ensuring groups (and parent/child relationships) exist across multiple steps; consider extracting this into shared helper utilities to reduce duplication and keep test maintenance easier.
- The invalid group ID error assertion uses a broad heading regex (`/error|not found|something went wrong/i`); tightening this to a known error message or a more specific selector would make the test less flaky and more resilient to copy changes.
- Several selectors rely on visible text patterns (e.g., `text=/\d+ SBOMs?/` and label text like "Product"); if UI copy changes are expected, you may want to pivot to more stable attributes or test-ids to avoid brittle tests.

## Individual Comments

### Comment 1
<location path="e2e/tests/ui/features/@sbom-groups/sbom-groups.step.ts" line_range="598-602" />
<code_context>
+  await page.goto("/sbom-groups/invalid-group-id-12345");
+});
+
+Then("An error state is displayed for the invalid group", async ({ page }) => {
+  const errorHeading = page.getByRole("heading", {
+    name: /error|not found|something went wrong/i,
+  });
+  await expect(errorHeading).toBeVisible({ timeout: 10000 });
+});
+
</code_context>
<issue_to_address>
**suggestion (testing):** Error state assertion for invalid group ID is quite generic and may produce false positives

This assertion currently matches any heading with `/error|not found|something went wrong/i`, which could pick up unrelated page errors and cause false positives. To make the test reliably about SBOM group errors, assert against a more specific selector or message for the SBOM groups error UI (e.g., a dedicated data-testid or fixed heading text), and optionally check that the URL stays on the invalid group route. That way the test proves the invalid group ID triggers the intended error state, not just any generic error on the page.

Suggested implementation:

```typescript
 // Invalid group ID handling
When("User navigates to group details with invalid ID", async ({ page }) => {
  await page.goto("/sbom-groups/invalid-group-id-12345");
});

Then("An error state is displayed for the invalid group", async ({ page }) => {
  // Assert we stay on the invalid group route
  await expect(page).toHaveURL(/\/sbom-groups\/invalid-group-id-12345$/);

  // Assert the dedicated SBOM group error UI is shown
  const sbomGroupError = page.getByTestId("sbom-group-error");
  await expect(sbomGroupError).toBeVisible();
});

Then("The SBOM Groups table shows all groups", async ({ page }) => {
  const table = page.getByRole("treegrid", { name: "sbom-groups-table" });
  await expect(table).toBeVisible();

  const rows = table.getByRole("row");
  await expect(rows.first()).toBeVisible();
});
Then(

```

1. Ensure the SBOM group error UI in the application uses `data-testid="sbom-group-error"` on the container element (e.g., the alert or error panel). If a different test id or fixed heading text is already used (such as `"sbom-group-not-found-heading"` or a specific error message string), update the selector in this step definition to match the existing UI.
2. If the invalid-group route differs (for example, it includes a workspace/org prefix or uses a different path segment), adjust the regular expression in `toHaveURL` to match the actual route shape.
</issue_to_address>

### Comment 2
<location path="e2e/tests/ui/features/@sbom-groups/sbom-groups.step.ts" line_range="445" />
<code_context>
 Then(
   "The SBOM Groups table shows filtered results containing {string}",
   async ({ page }, searchTerm: string) => {
</code_context>
<issue_to_address>
**suggestion (testing):** SBOM count assertion doesn’t distinguish zero vs non-zero counts or missing content

The `Then "The SBOM count is displayed for group {string}"` step only asserts that text matching `"\d+ SBOMs?"` is visible, which passes for `0 SBOMs` and could miss cases where the count is absent but similar text appears. If the behavior requires at least one SBOM, please assert on the parsed numeric value (e.g., `> 0`). Otherwise, add a dedicated scenario for `0 SBOMs` so both zero and non-zero states are explicitly covered.

Suggested implementation:

```typescript
Then(
  "The SBOM count is displayed for group {string}",
  async ({ page }, groupName: string) => {
    const groupRow = page.getByRole("row", { name: new RegExp(groupName, "i") });
    await expect(groupRow).toBeVisible();

    const sbomCountLocator = groupRow.getByText(/\d+\s+SBOMs?/);
    const sbomCountText = await sbomCountLocator.textContent();

    expect(sbomCountText).not.toBeNull();

    const match = sbomCountText!.match(/(\d+)\s+SBOMs?/);
    expect(match).not.toBeNull();

    const sbomCount = Number.parseInt(match![1], 10);
    expect(sbomCount).toBeGreaterThan(0);
  },
);

```

1. Ensure the DOM structure for the SBOM count matches the locator assumptions (count text within the group row and formatted like `"N SBOMs"`). If the count is in a different element (e.g., a specific column), adjust `sbomCountLocator` accordingly (e.g., `groupRow.getByRole("cell", { name: /\d+\s+SBOMs?/ })`).
2. If the product must support a valid `0 SBOMs` state, add a dedicated scenario to your feature file (e.g., `"The SBOM count shows zero SBOMs for group {string}"`) that asserts `sbomCount === 0` instead of `> 0`, using the same parsing logic.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread e2e/tests/ui/features/@sbom-groups/sbom-groups.step.ts Outdated
Comment thread e2e/tests/ui/features/@sbom-groups/sbom-groups.step.ts
@mrrajan
mrrajan marked this pull request as draft June 26, 2026 12:41
@mrrajan
mrrajan force-pushed the TC-3811 branch 6 times, most recently from d7e3329 to a683422 Compare July 7, 2026 06:03
@mrrajan
mrrajan force-pushed the TC-3811 branch 4 times, most recently from 819e6f4 to 3003ccf Compare July 9, 2026 16:56
@mrrajan
mrrajan marked this pull request as ready for review July 9, 2026 16:56

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hey - I've found 2 issues, and left some high level feedback:

  • Methods like hasLabelBadge and hasSbomCount on SbomGroupListPage return Locators rather than booleans, which is slightly misleading given their names; consider renaming them (e.g. getLabelBadgeLocator, getSbomCountLocator) or changing them to actually perform existence checks.
  • The new toHaveBulkSelectedCount matcher in ToolbarMatchers hardcodes #bulk-selected-items-checkbox, which may be brittle if the DOM changes; it might be more robust to tie this to the existing toolbar structure or a data-testid rather than a raw ID.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Methods like `hasLabelBadge` and `hasSbomCount` on `SbomGroupListPage` return `Locator`s rather than booleans, which is slightly misleading given their names; consider renaming them (e.g. `getLabelBadgeLocator`, `getSbomCountLocator`) or changing them to actually perform existence checks.
- The new `toHaveBulkSelectedCount` matcher in `ToolbarMatchers` hardcodes `#bulk-selected-items-checkbox`, which may be brittle if the DOM changes; it might be more robust to tie this to the existing toolbar structure or a data-testid rather than a raw ID.

## Individual Comments

### Comment 1
<location path="e2e/tests/api/features/sbom-groups.ts" line_range="563-568" />
<code_context>
+    await cleanupGroups(axios, createdGroupIds.splice(0));
+  });
+
+  const findFirstSbomId = async (axios: import("axios").AxiosInstance) => {
+    const response = await axios.get("/api/v3/sbom", {
+      params: { limit: 1, offset: 0 },
+    });
+    expect(response.data.items.length).toBeGreaterThan(0);
+    return response.data.items[0].id as string;
+  };
+
</code_context>
<issue_to_address>
**suggestion:** Deduplicate `findFirstSbomId` / `findTwoSbomIds` helpers across describe blocks

These helpers are redefined in multiple `describe` blocks, which makes SBOM lookup logic harder to maintain consistently. Please extract them into a shared helper (in this file or a small module) and reuse them across the hierarchy and assignments suites so the tests stay DRY and are easier to update if the SBOM listing API changes.

Suggested implementation:

```typescript
const findFirstSbomId = async (axios: import("axios").AxiosInstance) => {
  const response = await axios.get("/api/v3/sbom", {
    params: { limit: 1, offset: 0 },
  });
  expect(response.data.items.length).toBeGreaterThan(0);
  return response.data.items[0].id as string;
};

const findTwoSbomIds = async (axios: import("axios").AxiosInstance) => {
  const response = await axios.get("/api/v3/sbom", {
    params: { limit: 2, offset: 0 },
  });
  expect(response.data.items.length).toBeGreaterThanOrEqual(2);
  return [response.data.items[0].id as string, response.data.items[1].id as string];
};

test.describe("SBOM Group hierarchy with assignments", () => {
  const createdGroupIds: string[] = [];

  test.afterEach(async ({ axios }) => {
    await cleanupGroups(axios, createdGroupIds.splice(0));
  });

```

```typescript
  test.afterEach(async ({ axios }) => {
    await cleanupGroups(axios, createdGroupIds.splice(0));
  });

test.describe("SBOM Group CRUD", () => {

```

The `findFirstSbomId` and `findTwoSbomIds` helpers are now defined once at the top level and should be reused across all `describe` blocks in this file:

1. Remove any other local `const findFirstSbomId = ...` and `const findTwoSbomIds = ...` definitions from other `describe` blocks, and update those tests to call the shared top-level helpers instead.
2. If this file currently imports `AxiosInstance` differently (e.g. `import type { AxiosInstance } from "axios";`), you may want to adjust the helper signatures to match the existing import style rather than using `import("axios").AxiosInstance` inline.
3. Ensure all call sites (in hierarchy and CRUD suites) now call the shared `findFirstSbomId(axios)` / `findTwoSbomIds(axios)` helpers, so that SBOM lookup logic is maintained in a single place.
</issue_to_address>

### Comment 2
<location path="e2e/tests/api/features/sbom-groups.ts" line_range="907-913" />
<code_context>
+  });
+});
+
+// Added Patch tests - disabled for now
+// test.describe("SBOM Group PATCH assignments", () => {
+//   const createdGroupIds: string[] = [];
+
+//   test.afterEach(async ({ axios }) => {
+//     await cleanupGroups(axios, createdGroupIds.splice(0));
+//   });
+
+//   const findFirstSbomId = async (axios: import("axios").AxiosInstance) => {
</code_context>
<issue_to_address>
**question (testing):** Clarify or re-enable the commented-out PATCH assignment regression tests

The entire `SBOM Group PATCH assignments` test suite, including cases like TC-5036 and TC-5058, is commented out, removing regression coverage for partial updates and bulk PATCH semantics. If PATCH support is intentionally disabled, please add a brief note (e.g., ticket/feature flag reference) and consider moving these tests to a `skip`/`todo` suite. If PATCH is supported in the API, please re-enable these tests (or a targeted subset) so assignment behavior remains covered by automation.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread e2e/tests/api/features/sbom-groups.ts Outdated
Comment thread e2e/tests/api/features/sbom-groups.ts Outdated
Comment on lines +907 to +913
// Added Patch tests - disabled for now
// test.describe("SBOM Group PATCH assignments", () => {
// const createdGroupIds: string[] = [];

// test.afterEach(async ({ axios }) => {
// await cleanupGroups(axios, createdGroupIds.splice(0));
// });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

question (testing): Clarify or re-enable the commented-out PATCH assignment regression tests

The entire SBOM Group PATCH assignments test suite, including cases like TC-5036 and TC-5058, is commented out, removing regression coverage for partial updates and bulk PATCH semantics. If PATCH support is intentionally disabled, please add a brief note (e.g., ticket/feature flag reference) and consider moving these tests to a skip/todo suite. If PATCH is supported in the API, please re-enable these tests (or a targeted subset) so assignment behavior remains covered by automation.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

[sdlc-workflow/verify-pr] Classified as question — asks for clarification about commented-out PATCH assignment tests; no code change needed. No sub-task created.

@mrrajan
mrrajan marked this pull request as draft July 9, 2026 16:59
@mrrajan

mrrajan commented Jul 10, 2026

Copy link
Copy Markdown
Contributor Author

[sdlc-workflow/verify-pr] Re: @sourcery-ai[bot] review (4569251325) — Classified as suggestion (3 items):

  1. Extract shared helper utilities for setup logic — no documented DRY test helper convention in CONVENTIONS.md
  2. Tighten error assertion regex — no documented testing selector convention
  3. Use stable attributes/test-ids over text patterns — data-testid is not an established E2E pattern in this repo (0 occurrences in e2e/)

No sub-tasks created.

@mrrajan

mrrajan commented Jul 10, 2026

Copy link
Copy Markdown
Contributor Author

[sdlc-workflow/verify-pr] Re: @sourcery-ai[bot] review (4665095148) — Classified as suggestion (2 items):

  1. Rename hasLabelBadge/hasSbomCount methods (return Locator, not boolean) — naming inconsistency is real but no documented method-naming convention for page objects in CONVENTIONS.md
  2. Hardcoded #bulk-selected-items-checkbox ID — no documented convention about avoiding hardcoded ID selectors in tests

No sub-tasks created.

@mrrajan

mrrajan commented Jul 10, 2026

Copy link
Copy Markdown
Contributor Author

Verification Report for TC-3811 (commit 3003ccf)

Check Result Details
Review Feedback PASS 6 items classified (4 suggestions, 1 question, 1 multi-suggestion review body); no code change requests
Root-Cause Investigation N/A No sub-tasks created
Scope Containment WARN All 15 files within e2e/; files beyond @sbom-groups/ are justified page objects, shared infrastructure, and test cleanup. User confirmed PR intentionally exceeds Jira scope
Diff Size PASS ~2,614 added / ~336 removed across 15 files — proportionate for ~17 test scenarios + API tests + page objects
Commit Traceability WARN 1/9 commits explicitly references TC-3811 (85385cc); remaining 8 have descriptive messages but lack Jira ID. Traceability maintained at PR level
Sensitive Patterns PASS No secrets, credentials, or API keys detected in any added lines
CI Status PASS All 9 required checks pass; e2e-integration-tests passed in 10m20s
Acceptance Criteria PASS All 13 feasible e2e items covered; 4 items correctly excluded (no UI exists or out of e2e scope); PR includes substantial additional API test coverage
Test Quality WARN Repetitive test detection: duplicate findFirstSbomId/findTwoSbomIds helpers and repetitive try/catch error patterns in API tests. Test documentation: PASS (BDD scenarios + descriptive test names). Eval Quality: N/A
Test Change Classification ADDITIVE All files new or net-additive (+2,613/-322 lines); removed lines are refactored code, not removed coverage
Verification Commands N/A No verification commands specified

Overall: WARN

Summary: The PR substantially exceeds the Jira task requirements — adding 11 BDD scenarios, comprehensive API test coverage, and page object improvements. All CI checks pass and all feasible acceptance criteria are satisfied. The WARN verdicts are minor:

  • Scope Containment WARN: Expected — user confirmed the PR intentionally includes API tests and infrastructure beyond the Jira's listed scenarios.
  • Commit Traceability WARN: Only the initial commit references TC-3811; follow-up commits lack the Jira ID but are on the TC-3811 branch.
  • Test Quality WARN: Duplicate helper functions (findFirstSbomId/findTwoSbomIds) and repetitive error-handling try/catch blocks could be extracted but do not affect correctness.

None of the reviewer suggestions (from sourcery-ai[bot]) were upgraded to code change requests — no matching CONVENTIONS.md patterns or established codebase conventions were found.


This comment was AI-generated by sdlc-workflow/verify-pr v0.11.0.

@mrrajan
mrrajan marked this pull request as ready for review July 10, 2026 09:49

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • Several helper methods in SbomGroupListPage (e.g. hasLabelBadge, hasSbomCount) are marked async and typed as returning Promise<Locator> but only synchronously construct and return a Locator; consider dropping async/Promise here to avoid confusion and clarify that these methods are purely selector builders.
  • A number of UI scenarios rely on hard-coded SBOM names like curl, openssl-3, ubi9-minimal-container, and claude-4-opus; if those fixtures change these tests will become brittle, so it may be worth centralizing or deriving these names from API/fixture helpers to make the tests more resilient.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Several helper methods in `SbomGroupListPage` (e.g. `hasLabelBadge`, `hasSbomCount`) are marked `async` and typed as returning `Promise<Locator>` but only synchronously construct and return a `Locator`; consider dropping `async`/`Promise` here to avoid confusion and clarify that these methods are purely selector builders.
- A number of UI scenarios rely on hard-coded SBOM names like `curl`, `openssl-3`, `ubi9-minimal-container`, and `claude-4-opus`; if those fixtures change these tests will become brittle, so it may be worth centralizing or deriving these names from API/fixture helpers to make the tests more resilient.

## Individual Comments

### Comment 1
<location path="e2e/tests/ui/features/@sbom-groups/sbom-groups.step.ts" line_range="63-72" />
<code_context>
+  await modal.submit();
 });

 Then(
-  "The SBOM Groups table contains {string}",
-  async ({ page }, groupName: string) => {
</code_context>
<issue_to_address>
**suggestion (testing):** Strengthen the "table does not contain" assertion to fail if the row still exists in the DOM

This step currently uses `await expect(row).not.toBeVisible();`, which will pass if the locator matches zero rows—even if the group still exists under a different representation or the locator broke. To align with the scenario intent that the row is removed from the table, please assert on count, e.g.:

```ts
const listPage = await SbomGroupListPage.fromCurrentPage(page);
const row = listPage.getGroupRow(groupName);
await expect(row).toHaveCount(0);
```

Optionally, you can add a visibility check after the count if needed.

Suggested implementation:

```typescript
Then(
  "The SBOM Groups table does not contain {string}",
  async ({ page }, groupName: string) => {
    const listPage = await SbomGroupListPage.fromCurrentPage(page);
    const row = listPage.getGroupRow(groupName);

    // Assert that the row has been removed from the DOM entirely
    await expect(row).toHaveCount(0);
  },
);

```

If the existing step text is slightly different (for example, `"The SBOM Groups table contains {string}"` with an inverted expectation), adjust the `Then(...)` string in the SEARCH/REPLACE block to match the exact wording currently in your file. Also ensure that:
1. `SbomGroupListPage` is imported at the top of this file (e.g., `import { SbomGroupListPage } from '../../page-objects/sbom-group-list.page';` or whatever path is used elsewhere).
2. `expect` from `@playwright/test` is already imported; if not, add `import { expect } from '@playwright/test';` alongside your other Playwright imports.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread e2e/tests/ui/features/@sbom-groups/sbom-groups.step.ts

@carlosthe19916 carlosthe19916 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Do not use: "timeout" statements, avoid explicit timeout-based waits because they make tests slower, less reliable, and dependent on machine performance. Playwright already provides automatic waiting for elements, navigation, assertions, and actions, which makes tests more deterministic and less flaky.

Comment thread e2e/tests/ui/pages/Pagination.ts Outdated
Comment on lines +46 to +48
await expect(this._pagination.locator("input")).toHaveValue("1", {
timeout: 5000,
});

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Why is it that we need to add timeout here?
The whole Pagination class is independent of the any asynchronous call like REST APIs.
If changing the page triggers a Table or any other component to be re-rendered then the "wait/timeout" action should be written in the Table but not here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

[sdlc-workflow/verify-pr] Classified as code change request — sub-task TC-5189 created to address this feedback.

Comment thread e2e/tests/ui/pages/Toolbar.ts Outdated
name: "Clear all filters",
});
await expect(clearButton).toBeVisible();
if (!(await clearButton.isVisible())) return;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Let's not mix logic. The function should clear filters, verifying whether or not the filters exist should be done outside this function. Prefer composition logic over conditional expansion

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

[sdlc-workflow/verify-pr] Classified as code change request — sub-task TC-5190 created to address this feedback.

Comment thread e2e/tests/ui/pages/Toolbar.ts Outdated
Comment on lines +179 to +182
// Give PatternFly label-group animations time to fully complete
await expect(this._toolbar.locator(".pf-m-label-group")).toHaveCount(0, {
timeout: 10000,
});

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Don't add this. The whole Toolbar class is independent of any asynchronous event. If the toolbar triggers other components to be re-rendered asynchronously then the "wait" or async logic should be there not here

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

[sdlc-workflow/verify-pr] Classified as code change request — sub-task TC-5189 created to address this feedback.

@mrrajan

mrrajan commented Jul 13, 2026

Copy link
Copy Markdown
Contributor Author

[sdlc-workflow/verify-pr] Re: @sourcery-ai[bot] review (4670363250) — Classified as suggestion (2 items):

  1. Async methods returning Locator synchronously — valid observation but no documented async/sync naming convention for page objects in CONVENTIONS.md
  2. Hardcoded SBOM names — no convention prohibiting hardcoded test data in Gherkin feature files

No sub-tasks created.

@mrrajan

mrrajan commented Jul 13, 2026

Copy link
Copy Markdown
Contributor Author

[sdlc-workflow/verify-pr] Re: @carlosthe19916 review (4682471904) — Classified as code change request — sub-task TC-5189 created to remove explicit timeout waits from Pagination and Toolbar page objects.

@mrrajan

mrrajan commented Jul 13, 2026

Copy link
Copy Markdown
Contributor Author

Verification Report for TC-3811 (commit c0d2035)

Check Result Details
Review Feedback WARN 4 code change requests from maintainer; sub-tasks TC-5189, TC-5190 created
Root-Cause Investigation DONE Convention gap identified; TC-5191 created to document Playwright auto-waiting and page object design conventions
Scope Containment WARN All 16 files in e2e/; files outside @sbom-groups/ are justified shared infrastructure and page objects
Diff Size PASS +2349/-292 across 16 files, proportionate for 11+ UI scenarios + API test suite
Commit Traceability WARN 1 of 11 commits references TC-3811; remaining use generic prefixes
Sensitive Patterns PASS No secrets detected in 2349 added lines
CI Status PASS All 11 required checks pass; 7 conditional jobs skipped
Acceptance Criteria PASS 9 of 9 criteria met — product labels, tree hierarchy, parent selection, invalid ID, SBOM count, breadcrumbs, edit parent, sorting, API CRUD
Test Quality WARN Repetitive Test Detection: 18 identical try/catch blocks in API tests; Test Documentation: PASS; Eval Quality: N/A
Test Change Classification MIXED +25 scenarios, +34 step defs; 2 scenarios removed, ~10 step defs replaced with POM-based implementations
Verification Commands N/A None specified

Overall: WARN

Issues requiring attention:

  1. Timeouts in shared page objects (TC-5189): Remove explicit timeout params from Pagination.ts and Toolbar.ts — Playwright auto-waiting handles this
  2. Mixed logic in clearAllFilters (TC-5190): Separate filter-existence check from clear action in Toolbar.ts
  3. Commit traceability: Only 1 of 11 commits references TC-3811; consider including the Jira key in all commit messages
  4. Repetitive test pattern: 18 identical try/catch error assertion blocks in API tests could be extracted into a shared expectHttpError() helper

This comment was AI-generated by sdlc-workflow/verify-pr v0.13.2.

@mrrajan mrrajan added the backport release/0.5.z This PR should be backported to release/0.5.z branch. label Jul 14, 2026
@mrrajan
mrrajan added this pull request to the merge queue Jul 14, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Jul 14, 2026
mrrajan and others added 15 commits July 14, 2026 21:58
Add missing E2E test coverage for SBOM Groups feature:
- Product label badge visibility in list and detail pages
- Hierarchical tree expand/collapse behavior
- Parent group selection in create and edit flows
- Invalid group ID error handling
- SBOM count display in group list
- Breadcrumb navigation on detail page
- Edit group parent assignment and removal
- Sorting by name column

Implements TC-3811

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Assisted-by: Claude Code
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: mrrajan <86094767+mrrajan@users.noreply.github.com.>
Signed-off-by: mrrajan <86094767+mrrajan@users.noreply.github.com.>
Signed-off-by: mrrajan <86094767+mrrajan@users.noreply.github.com.>
Signed-off-by: mrrajan <86094767+mrrajan@users.noreply.github.com.>
Signed-off-by: mrrajan <86094767+mrrajan@users.noreply.github.com.>
Signed-off-by: mrrajan <86094767+mrrajan@users.noreply.github.com.>
Signed-off-by: mrrajan <86094767+mrrajan@users.noreply.github.com.>
Signed-off-by: mrrajan <86094767+mrrajan@users.noreply.github.com.>
Wrap the three assignment describe blocks in a serial parent to prevent
parallel workers from racing on shared SBOMs via PUT replace semantics.
Extract duplicated findFirstSbomId/findTwoSbomIds helpers to the shared
helpers module. Add 8 new tests covering assignment edge cases (bulk
error paths, PATCH clear/idempotent-remove/add-remove-same-group,
delete-group-cleans-assignments, filter-by-parent).

Signed-off-by: mrrajan <86094767+mrrajan@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Playwright provides automatic waiting for elements and assertions,
making explicit timeouts unnecessary in shared infrastructure classes.

Implements TC-5189

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Assisted-by: Claude Code
Remove conditional guard from clearAllFilters() so the method only
performs the clear action. Callers are responsible for checking
filter existence before calling. Playwright auto-waiting on click()
handles element readiness.

Implements TC-5190

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Assisted-by: Claude Code
Signed-off-by: mrrajan <86094767+mrrajan@users.noreply.github.com.>
Signed-off-by: mrrajan <86094767+mrrajan@users.noreply.github.com.>
Signed-off-by: mrrajan <86094767+mrrajan@users.noreply.github.com.>
@mrrajan
mrrajan added this pull request to the merge queue Jul 14, 2026
@mrrajan
mrrajan removed this pull request from the merge queue due to a manual request Jul 14, 2026
@mrrajan
mrrajan added this pull request to the merge queue Jul 15, 2026
Merged via the queue into guacsec:main with commit b53e423 Jul 15, 2026
19 checks passed
@github-project-automation github-project-automation Bot moved this to Done in Trustify Jul 15, 2026
@trustify-ci-bot

Copy link
Copy Markdown
Contributor

Successfully created backport PR for release/0.5.z:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport release/0.5.z This PR should be backported to release/0.5.z branch.

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants