Skip to content

Conversation

@ikanias
Copy link

@ikanias ikanias commented Sep 9, 2025

This PR contains the .feature file and the .ts file of importer-explorer for the importers UI automation in TPA.

Summary by Sourcery

Add end-to-end UI automation for the Importer Explorer in TPA, including Playwright BDD step definitions and feature scenarios covering navigation, filtering, search, row expansion, pagination, and importer action workflows.

Tests:

  • Add Playwright BDD step definitions for the Importer Explorer UI
  • Add importer-explorer.feature covering navigation, filtering, search, row expansion, pagination, and action menu scenarios
  • Add tests to verify table column headers, filter icon, and search results
  • Add tests to expand importer rows and validate sub-tables, pagination states, and importer statuses
  • Add tests to assert pagination controls disablement and non-editability of page inputs
  • Add tests to verify Call to Action menu options for running, enabling, and disabling importers with confirmation dialogs and status updates

@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Sep 9, 2025

Reviewer's Guide

This PR adds comprehensive BDD automation for the Importers UI in TPA by introducing a new Playwright-BDD step definitions file and a corresponding feature file. The step definitions implement navigation, table header checks, filter/search interactions, row expansion with state validations, pagination controls, and call-to-action menu operations (enable/disable/run). The feature file defines scenarios that drive these steps end-to-end.

File-Level Changes

Change Details Files
Importers UI automation step definitions
  • Imported createBdd from playwright-bdd and created Given/When/Then steps
  • Navigates to dashboard and importers pages using XPath locators
  • Validates table headers, filter icon, search box, and pagination states
  • Handles row expansion and detailed sub-table visibility with input value checks
  • Implements call-to-action menu interactions for enable, disable, and run flows
e2e/tests/ui/features/@importer-explorer/importer-explorer.step.ts
Importer Explorer feature scenarios
  • Defined feature and scenarios for Importer Explorer page navigation
  • Covers header and CTA button visibility checks
  • Includes filter and search scenarios with example data
  • Adds scenarios for expanding rows, pagination controls, and menu actions
  • Specifies enable/disable and run workflows with confirmation and progress checks
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

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey there - I've reviewed your changes - here's some feedback:

  • Several loops have trailing semicolons (e.g. for(i=0; i<=5; i++);) which nullify their bodies—please remove those semicolons to ensure the loops execute correctly.
  • There’s a lot of repeated navigation and locator code—consider extracting common steps and selectors into reusable functions or constants to reduce duplication and improve maintainability.
  • Avoid using hard waitForTimeout calls; replace them with Playwright’s built-in waiting methods (e.g., waitForSelector or expect(locator).toBeVisible()) for more reliable synchronization.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Several loops have trailing semicolons (e.g. for(i=0; i<=5; i++);) which nullify their bodies—please remove those semicolons to ensure the loops execute correctly.
- There’s a lot of repeated navigation and locator code—consider extracting common steps and selectors into reusable functions or constants to reduce duplication and improve maintainability.
- Avoid using hard waitForTimeout calls; replace them with Playwright’s built-in waiting methods (e.g., waitForSelector or expect(locator).toBeVisible()) for more reliable synchronization.

## Individual Comments

### Comment 1
<location> `e2e/tests/ui/features/@importer-explorer/importer-explorer.step.ts:143` </location>
<code_context>
+Then("The user expands all the importers rows", 
</code_context>

<issue_to_address>
Expand button validation uses hardcoded indices and may not cover all edge cases.

Instead of relying on fixed indices, iterate over the actual table rows to expand each importer. This will make the test robust to changes in the number of importers.

Suggested implementation:

```typescript
Then("The user expands all the importers rows", 
  async ({ page }) => {
    // Select all importer rows in the table
    const importerRows = await page.$$('tr[data-ouia-component-type="importer-row"]');
    for (const row of importerRows) {
      // Find the expand button within the row
      const expandButton = await row.$('button[aria-label="Expand row"]');
      if (expandButton) {
        await expandButton.click();
      }
    }

```

- If your table rows do not use `data-ouia-component-type="importer-row"`, adjust the selector to match your actual markup.
- If the expand button uses a different `aria-label` or selector, update `'button[aria-label="Expand row"]'` accordingly.
- Remove any unused variables or logic related to hardcoded indices and button names elsewhere in the file if present.
</issue_to_address>

### Comment 2
<location> `e2e/tests/ui/features/@importer-explorer/importer-explorer.step.ts:240` </location>
<code_context>
+  }
+);
+
+Then("It should not be possible to add page numbers in the pagination text box",
+  async ({ page }) => {
+    expect(page.locator(`xpath=//div[@id='importer-table-pagination-top']//input[@aria-label='Current page']`)).not.toBeEditable();
+    expect(page.locator(`xpath=//div[@id='importer-table-pagination-bottom']//input[@aria-label='Current page']`)).not.toBeEditable();
+  }
+);
+
</code_context>

<issue_to_address>
Missing await for locator assertions.

Add 'await' before the locator assertions to ensure proper test execution and accurate results.
</issue_to_address>

### Comment 3
<location> `e2e/tests/ui/features/@importer-explorer/importer-explorer.step.ts:265` </location>
<code_context>
+Then("The user clicks on all the Call to Action buttons and Enable, Disable and Run options will be displayed",
</code_context>

<issue_to_address>
Loop logic and missing awaits may cause unreliable test execution.

The loop increments 'i' only within conditional blocks, risking infinite loops or missed iterations. Additionally, some locator actions lack 'await'. Refactor the loop for correct iteration and ensure all async actions are properly awaited.
</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.

"Application navigates to Importers page",
async ({ page }) => {
await page.locator(`xpath=//h1[@class='pf-v6-c-content--h1' and text()="Importers"]`).isVisible();
});
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion (testing): Expand button validation uses hardcoded indices and may not cover all edge cases.

Instead of relying on fixed indices, iterate over the actual table rows to expand each importer. This will make the test robust to changes in the number of importers.

Suggested implementation:

Then("The user expands all the importers rows", 
  async ({ page }) => {
    // Select all importer rows in the table
    const importerRows = await page.$$('tr[data-ouia-component-type="importer-row"]');
    for (const row of importerRows) {
      // Find the expand button within the row
      const expandButton = await row.$('button[aria-label="Expand row"]');
      if (expandButton) {
        await expandButton.click();
      }
    }
  • If your table rows do not use data-ouia-component-type="importer-row", adjust the selector to match your actual markup.
  • If the expand button uses a different aria-label or selector, update 'button[aria-label="Expand row"]' accordingly.
  • Remove any unused variables or logic related to hardcoded indices and button names elsewhere in the file if present.

"The user navigates to TPA URL page successfully",
async ({ page }) => {
await page.locator(`xpath=//div[@class='pf-v6-c-card__title-text' and text()="Dashboard"]`).isVisible();
}
Copy link
Contributor

Choose a reason for hiding this comment

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

issue (testing): Missing await for locator assertions.

Add 'await' before the locator assertions to ensure proper test execution and accurate results.

async ({ page }) => {
await page.locator(`xpath=//div[@class='pf-v6-c-card__title-text' and text()="Dashboard"]`).isVisible();
}
);
Copy link
Contributor

Choose a reason for hiding this comment

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

issue (bug_risk): Loop logic and missing awaits may cause unreliable test execution.

The loop increments 'i' only within conditional blocks, risking infinite loops or missed iterations. Additionally, some locator actions lack 'await'. Refactor the loop for correct iteration and ensure all async actions are properly awaited.

}
));
const impTableHeader = ['Name', 'Type', 'Description', 'Source', 'Period', 'State'];
var i: number;
Copy link
Contributor

Choose a reason for hiding this comment

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

issue (code-quality): Use const or let instead of var. (avoid-using-var)

Explanation`const` is preferred as it ensures you cannot reassign references (which can lead to buggy and confusing code). `let` may be used if you need to reassign references - it's preferred to `var` because it is block- rather than function-scoped.

From the Airbnb JavaScript Style Guide

Then(
"Call to Action button for each importer should be visible",
async ({ page }) => {
var i: number;
Copy link
Contributor

Choose a reason for hiding this comment

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

issue (code-quality): Use const or let instead of var. (avoid-using-var)

Explanation`const` is preferred as it ensures you cannot reassign references (which can lead to buggy and confusing code). `let` may be used if you need to reassign references - it's preferred to `var` because it is block- rather than function-scoped.

From the Airbnb JavaScript Style Guide


Then("The user clicks on all the Call to Action buttons and Enable, Disable and Run options will be displayed",
async ({ page }) => {
for(var i=0; i<=4;)
Copy link
Contributor

Choose a reason for hiding this comment

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

issue (code-quality): Use const or let instead of var. (avoid-using-var)

Explanation`const` is preferred as it ensures you cannot reassign references (which can lead to buggy and confusing code). `let` may be used if you need to reassign references - it's preferred to `var` because it is block- rather than function-scoped.

From the Airbnb JavaScript Style Guide


Then("The user has clicked on the Call to Action button under an enabled importer and disables the importer",
async ({ page }) => {
for(var i=0; i<=1;) {
Copy link
Contributor

Choose a reason for hiding this comment

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

issue (code-quality): Use const or let instead of var. (avoid-using-var)

Explanation`const` is preferred as it ensures you cannot reassign references (which can lead to buggy and confusing code). `let` may be used if you need to reassign references - it's preferred to `var` because it is block- rather than function-scoped.

From the Airbnb JavaScript Style Guide


Then("The user has clicked on the Call to Action icon under a disabled importer and the Disable option is not visible",
async ({ page }) => {
for(var i=0; i<=2;) {
Copy link
Contributor

Choose a reason for hiding this comment

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

issue (code-quality): Use const or let instead of var. (avoid-using-var)

Explanation`const` is preferred as it ensures you cannot reassign references (which can lead to buggy and confusing code). `let` may be used if you need to reassign references - it's preferred to `var` because it is block- rather than function-scoped.

From the Airbnb JavaScript Style Guide


Then("The user has clicked on the Kebab icon under a disabled importer and enables it",
async ({ page }) => {
for(var i=0; i<=2;) {
Copy link
Contributor

Choose a reason for hiding this comment

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

issue (code-quality): Use const or let instead of var. (avoid-using-var)

Explanation`const` is preferred as it ensures you cannot reassign references (which can lead to buggy and confusing code). `let` may be used if you need to reassign references - it's preferred to `var` because it is block- rather than function-scoped.

From the Airbnb JavaScript Style Guide


Then("The user has clicked on the Call to Action icon under an enabled importer and then selects Run",
async ({ page }) => {
for(var i=0; i<=1;) {
Copy link
Contributor

Choose a reason for hiding this comment

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

issue (code-quality): Use const or let instead of var. (avoid-using-var)

Explanation`const` is preferred as it ensures you cannot reassign references (which can lead to buggy and confusing code). `let` may be used if you need to reassign references - it's preferred to `var` because it is block- rather than function-scoped.

From the Airbnb JavaScript Style Guide

@queria queria added the tests label Sep 9, 2025
Given User is authenticated

Scenario: The user navigates to Importers Explorer page
Given The user navigates to TPA URL page successfully
Copy link

Choose a reason for hiding this comment

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

I think it is better to avoid these extra 'navigate to homepage' generic steps. And especially repeating them in every scenario too.

  • You already have Background - Given User is authenticated (similar to existing advisory/sbom/vulnerability-explorer tests)
    -- That can be expected to result in You being already on TPA website (so have left menu available)

Also if similar step would be needed, then:

  • If all Scenarios under the Feature need such step it would be better to add it to Background instead
  • The TPA URL page - You try to navigate to Dashboard, we should in current situation avoid navigation to Dashboard unless it is relevant for the test (e.g. of dashboard itself), as currently it is very expensive page (compared to most), so if then it is better for these universal-not-related-to-test cases better to navigate elsewhere like to /upload or /importers which are way faster and do not slow test execution that much, as also do not cause that big load on the backend

Copy link
Contributor

Choose a reason for hiding this comment

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

@queria @ikanias FYI, this has been addressed in this PR. If we expect the user to be authenticated, then we should already expect them to be at the /importers page, so this step is now unnecessary.

import { DetailsPage } from "../../helpers/DetailsPage";
import { ToolbarTable } from "../../helpers/ToolbarTable";
import { SearchPage } from "../../helpers/SearchPage";
import { test } from "@playwright/test";
Copy link

Choose a reason for hiding this comment

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

We have tweaked/extended 'test' object, so please replace with:

Suggested change
import { test } from "@playwright/test";
import { test } from "../../fixtures";

@carlosthe19916
Copy link
Collaborator

@ikanias @queria any progress on this PR? If you agree, let's switch it to "Draft" status until all github ci workflows pass successfully.

Copy link
Contributor

@vobratil vobratil left a comment

Choose a reason for hiding this comment

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

@ikanias Unfortunately this PR has quite a lot of problems with things like defining redundant test steps, not taking advantage of already defined methods, relying heavily on very specific xpaths, unnecessary imports and others. I can suggest taking a look at how other user stories and their steps are defined, using the Playwright Code Gen tool to help create maintainable code and perhaps even take advantage of the Trustify MCP server to generate good test steps from the BDD file.

Given User is authenticated

Scenario: The user navigates to Importers Explorer page
Given The user navigates to TPA URL page successfully
Copy link
Contributor

Choose a reason for hiding this comment

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

@queria @ikanias FYI, this has been addressed in this PR. If we expect the user to be authenticated, then we should already expect them to be at the /importers page, so this step is now unnecessary.


Scenario: The user navigates to Importers Explorer page
Given The user navigates to TPA URL page successfully
When The user selects the importers menu option
Copy link
Contributor

Choose a reason for hiding this comment

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

Since we can expect a similar step in other scenarios, too, it would make sense to make this one re-usable, e.g. User selects the <menuOption> option and put it in a shared file in the ui/features directory.

Scenario: The user navigates to Importers Explorer page
Given The user navigates to TPA URL page successfully
When The user selects the importers menu option
Then Application navigates to Importers page
Copy link
Contributor

Choose a reason for hiding this comment

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

We already have a similar step here. We should make it general and re-usable and move it to a shared file in ui/features, so that we avoid redundancy.

Given The user navigates to TPA URL page successfully
When The user selects the importers menu option
Then Application navigates to Importers page
Then Call to Action button for each importer should be visible
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't believe this is up-to-date, anymore.

Scenario: The user can see that the filter icon appears in importers page
Given The user navigates to TPA URL page
When The user selects the importers overview page
Then The column headers should be visible
Copy link
Contributor

Choose a reason for hiding this comment

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

The names of the actual headers should definitely be a part of the feature file, otherwise this step is pretty vague.

));
const impTableHeader = ['Name', 'Type', 'Description', 'Source', 'Period', 'State'];
var i: number;
for(i=0; i<=5; i++);
Copy link
Contributor

Choose a reason for hiding this comment

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

The code would be more readable if we used Playwright methods such as getByText("Source").

Copy link
Contributor

Choose a reason for hiding this comment

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

Actually, we already have a method for this, even if deprecated verifyTableHeaderContains(columnHeader).

{
if (actualHeaders[i] != impTableHeader[i])
{
console.log('The column name ' + impTableHeader[i] + 'is displayed incorrectly')
Copy link
Contributor

Choose a reason for hiding this comment

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

This way the log won't appear in the logs. We need to use logger.info(message). However, if the assertion fails, Playwright informs us about it, so we don't need to actually log this.

Given(
"The user navigates to TPA URL page",
async ({ page }) => {
await page.locator(`xpath=//div[@class='pf-v5-c-card__title-text' and text()="Your dashboard"]`).isVisible();
Copy link
Contributor

Choose a reason for hiding this comment

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

Why re-define these steps, if we can re-use the ones defined previously? Also, the previous method uses v6 and this one v5, demonstrating that these xpaths are brittle and should be avoided if possible.

);

When(
"The user selects the importers overview page",
Copy link
Contributor

Choose a reason for hiding this comment

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

Same as above. This is probably true for many steps in this file.

Then("The importers page should be displayed with pagination and correct paging values",
async ({ page }) => {
await expect(page.locator(`xpath=//div[@id='importer-table-pagination-top']//span[normalize-space(text())='of' and text()=1]`)).toBeVisible();
await expect(page.locator(`xpath=//button[@id='pagination-id-top-toggle']//b[normalize-space(text())=1 and text()='5']`)).toBeVisible();
Copy link
Contributor

Choose a reason for hiding this comment

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

We definitely shouldn't hardcode values like text()=5`` here.

Copy link
Contributor

Choose a reason for hiding this comment

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

The current implementation heavily uses XPath, hardcoded identifiers such as "OUIA-Generated-Button-plain-11" and static conditioned for loops. Optimizing this step definition would improve test stability and maintainability. As @vobratil mentioned, playwright MCP can be used for these.

Copy link
Contributor

@mrrajan mrrajan left a comment

Choose a reason for hiding this comment

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

I believe the step definitions could benefit from optimization.

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants