Skip to content

Add file polling step#6574

Merged
valfirst merged 1 commit intomasterfrom
files
Apr 17, 2026
Merged

Add file polling step#6574
valfirst merged 1 commit intomasterfrom
files

Conversation

@uarlouski
Copy link
Copy Markdown
Member

@uarlouski uarlouski commented Apr 16, 2026

Summary by CodeRabbit

  • New Features

    • Added a step to wait for a file matching a name pattern to appear in a directory with configurable timeout and polling; when exactly one match appears, its absolute path is saved to a variable.
  • Documentation

    • Added docs and a Gherkin example showing timeout, polling, regex matching, and variable storage.
  • Tests

    • Added unit and integration tests for success, timeout/no-match, multiple-match, and non-directory cases with clear assertion messages.
  • Bug Fixes

    • Improved validation and failure reporting for directory, no-match, and multi-match scenarios.

@uarlouski uarlouski requested a review from a team as a code owner April 16, 2026 09:20
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 16, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a polling-based step that waits for exactly one file matching a filename regex to appear in a directory (or until timeout), records failures via soft assertions for 0 or multiple matches, and saves the matched file's absolute path into a variable.

Changes

Cohort / File(s) Summary
Documentation
docs/modules/commons/pages/vividus-steps.adoc
Added “Files → Wait for a file to appear in a directory” step docs, parameters ($timeout, $pollingTimeout, $fileNameRegex, $directoryPath, $scopes, $variableName) and a Gherkin example.
Implementation
vividus/src/main/java/org/vividus/steps/FileSteps.java
Switched to constructor DI for VariableContext and ISoftAssert; added waitForFileAndSavePath(...) step that validates the directory, polls with DurationBasedWaiter, matches filenames via Pattern, records soft assertions for 0/multiple matches, and stores the single-match absolute path into variable scopes.
Unit tests
vividus/src/test/java/org/vividus/steps/FileStepsTests.java
Added ISoftAssert mock, new constants, and four tests covering: non-existent directory (exception), single match (variable stored), no match (soft assertion), and multiple matches (soft assertion with filenames).
Integration story
vividus-tests/src/main/resources/story/integration/FileSteps.story
Added scenario that creates a temp file, derives its folder, waits/polls for mydata.*.txt, stores discovered path, and asserts the stored path equals the created file path.

Sequence Diagram(s)

sequenceDiagram
    participant JBehave as "JBehave (Step runner)"
    participant FileSteps as "FileSteps"
    participant Waiter as "DurationBasedWaiter"
    participant FS as "File System"
    participant SoftAssert as "ISoftAssert"
    participant VarCtx as "VariableContext"

    JBehave->>FileSteps: invoke waitForFileAndSavePath(timeout, poll, regex, dir, scopes, varName)
    FileSteps->>FileSteps: validate directory exists
    FileSteps->>Waiter: start polling (timeout, pollingInterval)
    loop polling
        Waiter->>FS: list regular files in dir
        FS-->>Waiter: filenames
        Waiter->>FileSteps: return matches
    end
    alt exactly one match
        FileSteps->>VarCtx: putVariable(scopes, varName, matchedFileAbsolutePath)
    else zero matches
        FileSteps->>SoftAssert: recordFailedAssertion("No files matching ...")
    else multiple matches
        FileSteps->>SoftAssert: recordFailedAssertion("Multiple files found: ...")
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add file polling step' accurately and concisely describes the main change—adding a new file polling step to the FileSteps class for waiting and saving file paths.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch files

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 16, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.77%. Comparing base (475ee76) to head (76bacf8).
⚠️ Report is 2 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff            @@
##             master    #6574   +/-   ##
=========================================
  Coverage     97.77%   97.77%           
  Complexity     7417     7417           
=========================================
  Files          1015     1015           
  Lines         21464    21484   +20     
  Branches       1408     1410    +2     
=========================================
+ Hits          20987    21007   +20     
  Misses          361      361           
  Partials        116      116           

☔ View full report in Codecov by Sentry.
📢 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.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@vividus-tests/src/main/resources/story/integration/File.story`:
- Around line 3-5: The path handling is Windows-incompatible:
createTemporaryFile produces Path.toString() with backslashes so
substringBeforeLast(filePath, '/') and the final assertion
`${folderName}/data.*\.txt` will fail; update the folder extraction (where you
call eval(stringUtils:substringBeforeLast(filePath, '/')) or similar) to be
separator-aware (either use the platform file separator or normalize filePath by
replacing backslashes with forward slashes before taking substringBeforeLast)
and make the final regex separator-agnostic by using a character class for
separators (e.g. use [\\/]) when building the expected path for `${textFile}`
and `${folderName}` comparisons.
- Line 4: The step using the filename regex currently uses `.*.txt` which treats
the dot as "any char" and will match unintended names; update the pattern in the
story step to escape the literal dot (use `.*\.txt`) so it only matches files
ending with the `.txt` extension—change the pattern in the step that reads "When
I wait `PT30S` with `PT1S` polling until file matching `.*.txt` appears in
directory `${folderName}` and save path to scenario variable `textFile`" to use
the escaped form.

In `@vividus/src/main/java/org/vividus/steps/FileSteps.java`:
- Around line 108-110: The stream returned by Files.list(dir) currently matches
directory entries whose names fit fileNameRegex; update the predicate in the
code that builds the Stream<Path> (the try-with-resources using Stream<Path>
paths and the filter that references fileNameRegex) to first restrict to regular
files (Files::isRegularFile or Files::isRegularFile with p) before applying
fileNameRegex.matcher(p.getFileName().toString()).matches(), and add a
unit/regression test that creates a directory named like a matching file (e.g.,
"report-1.csv" as a directory) to assert it is not returned by the method in
FileSteps.java.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c4e0a57d-5723-4743-af20-828f21930b27

📥 Commits

Reviewing files that changed from the base of the PR and between 475ee76 and 7554f8a.

📒 Files selected for processing (4)
  • docs/modules/commons/pages/vividus-steps.adoc
  • vividus-tests/src/main/resources/story/integration/File.story
  • vividus/src/main/java/org/vividus/steps/FileSteps.java
  • vividus/src/test/java/org/vividus/steps/FileStepsTests.java

Comment thread vividus-tests/src/main/resources/story/integration/File.story Outdated
Comment thread vividus-tests/src/main/resources/story/integration/FileSteps.story Outdated
Comment thread vividus/src/main/java/org/vividus/steps/FileSteps.java Outdated
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 16, 2026

Qodana for JVM

It seems all right 👌

No new problems were found according to the checks applied

💡 Qodana analysis was run in the pull request mode: only the changed files were checked
☁️ View the detailed Qodana report

Contact Qodana team

Contact us at qodana-support@jetbrains.com

When I create temporary file with name `mydata.txt` and content `Hello World!` and put path to scenario variable `filePath`
Given I initialize scenario variable `folderName` with value `#{eval(stringUtils:substringBefore(filePath, 'mydata'))}`
When I wait `PT30S` with `PT1S` polling until file matching `.*.txt` appears in directory `${folderName}` and save path to scenario variable `textFile`
Then `${textFile}` matches `${folderName}mydata.*\.txt`
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.

Suggested change
Then `${textFile}` matches `${folderName}mydata.*\.txt`
Then `${textFile}` is equal to `${filePath}`

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

actual temp file name is mydata2313123123.txt that's why matcher is used

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.

but shouldn't ${filePath} contain full path to temp file?

Comment thread vividus/src/main/java/org/vividus/steps/FileSteps.java Outdated
Comment thread vividus/src/main/java/org/vividus/steps/FileSteps.java Outdated
Comment thread vividus/src/main/java/org/vividus/steps/FileSteps.java Outdated
Comment thread vividus/src/main/java/org/vividus/steps/FileSteps.java Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@vividus/src/test/java/org/vividus/steps/FileStepsTests.java`:
- Around line 101-109: Add a regression test in FileStepsTests named
shouldIgnoreDirectoryWithMatchingName that creates a directory named like the
file pattern (e.g., Files.createDirectory(tempDir.resolve("report-99.csv"))),
creates a real file (e.g., "report-42.csv"), calls
fileSteps.waitForFileAndSavePath(TIMEOUT, TIMEOUT, FILE_PATTERN, tempDir,
SCOPES, VARIABLE), and then verifies variableContext.putVariable(SCOPES,
VARIABLE, tempDir.resolve(csv).toAbsolutePath().toString()) and
verifyNoInteractions(softAssert) to ensure directories matching the pattern are
ignored.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 87f8e286-96bc-43b4-b6b2-122f964b087f

📥 Commits

Reviewing files that changed from the base of the PR and between 7554f8a and b60f6ce.

📒 Files selected for processing (4)
  • docs/modules/commons/pages/vividus-steps.adoc
  • vividus-tests/src/main/resources/story/integration/FileSteps.story
  • vividus/src/main/java/org/vividus/steps/FileSteps.java
  • vividus/src/test/java/org/vividus/steps/FileStepsTests.java

Comment on lines +101 to +109
@Test
void shouldSaveFilePathWhenExactlyOneFileMatches(@TempDir Path tempDir) throws IOException
{
String csv = "report-42.csv";
Files.createFile(tempDir.resolve(csv));
fileSteps.waitForFileAndSavePath(TIMEOUT, TIMEOUT, FILE_PATTERN, tempDir, SCOPES, VARIABLE);
verify(variableContext).putVariable(SCOPES, VARIABLE, tempDir.resolve(csv).toAbsolutePath().toString());
verifyNoInteractions(softAssert);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider adding a regression test for directory filtering.

The implementation correctly filters out directories using Files::isRegularFile, but there's no test verifying that a directory with a matching name (e.g., report-42.csv/) is excluded from results. This would guard against future regressions.

📋 Suggested test case
`@Test`
void shouldIgnoreDirectoryWithMatchingName(`@TempDir` Path tempDir) throws IOException
{
    // Create a directory with a name matching the pattern
    Files.createDirectory(tempDir.resolve("report-99.csv"));
    // Create an actual file
    String csv = "report-42.csv";
    Files.createFile(tempDir.resolve(csv));
    fileSteps.waitForFileAndSavePath(TIMEOUT, TIMEOUT, FILE_PATTERN, tempDir, SCOPES, VARIABLE);
    verify(variableContext).putVariable(SCOPES, VARIABLE, tempDir.resolve(csv).toAbsolutePath().toString());
    verifyNoInteractions(softAssert);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@vividus/src/test/java/org/vividus/steps/FileStepsTests.java` around lines 101
- 109, Add a regression test in FileStepsTests named
shouldIgnoreDirectoryWithMatchingName that creates a directory named like the
file pattern (e.g., Files.createDirectory(tempDir.resolve("report-99.csv"))),
creates a real file (e.g., "report-42.csv"), calls
fileSteps.waitForFileAndSavePath(TIMEOUT, TIMEOUT, FILE_PATTERN, tempDir,
SCOPES, VARIABLE), and then verifies variableContext.putVariable(SCOPES,
VARIABLE, tempDir.resolve(csv).toAbsolutePath().toString()) and
verifyNoInteractions(softAssert) to ensure directories matching the pattern are
ignored.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (2)
vividus-tests/src/main/resources/story/integration/FileSteps.story (1)

4-4: ⚠️ Potential issue | 🟡 Minor

Escape the literal dot in the test regex.

.*.txt still treats . as “any character”, so this scenario can match unintended names. Use .*\.txt if the intent is “any .txt temp file”.

Suggested fix
-When I wait `PT30S` with `PT1S` polling until file matching `.*.txt` appears in directory `${folderName}` and save path to scenario variable `textFile`
+When I wait `PT30S` with `PT1S` polling until file matching `.*\.txt` appears in directory `${folderName}` and save path to scenario variable `textFile`
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@vividus-tests/src/main/resources/story/integration/FileSteps.story` at line
4, The regex in the scenario step "When I wait `PT30S` with `PT1S` polling until
file matching `.*.txt` appears..." treats the dot as "any character"; update the
pattern to escape the literal dot so it only matches .txt files by changing the
pattern from `.*.txt` to `.*\.txt` inside the step in FileSteps.story (the step
that saves path to scenario variable `textFile`).
vividus/src/test/java/org/vividus/steps/FileStepsTests.java (1)

101-133: 🧹 Nitpick | 🔵 Trivial

Add the missing regression case for matching directories.

The production code now excludes directories via Files::isRegularFile, but the new tests still don't lock that behavior in. A directory named like report-99.csv would currently regress silently here.

Suggested test
 `@Test`
 void shouldSaveFilePathWhenExactlyOneFileMatches(`@TempDir` Path tempDir) throws IOException
 {
     String csv = "report-42.csv";
     Files.createFile(tempDir.resolve(csv));
     fileSteps.waitForFileAndSavePath(TIMEOUT, TIMEOUT, FILE_PATTERN, tempDir, SCOPES, VARIABLE);
     verify(variableContext).putVariable(SCOPES, VARIABLE, tempDir.resolve(csv).toAbsolutePath().toString());
     verifyNoInteractions(softAssert);
 }
+
+@Test
+void shouldIgnoreDirectoryWithMatchingName(`@TempDir` Path tempDir) throws IOException
+{
+    Files.createDirectory(tempDir.resolve("report-99.csv"));
+    String csv = "report-42.csv";
+    Files.createFile(tempDir.resolve(csv));
+
+    fileSteps.waitForFileAndSavePath(TIMEOUT, TIMEOUT, FILE_PATTERN, tempDir, SCOPES, VARIABLE);
+
+    verify(variableContext).putVariable(SCOPES, VARIABLE, tempDir.resolve(csv).toAbsolutePath().toString());
+    verifyNoInteractions(softAssert);
+}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@vividus/src/test/java/org/vividus/steps/FileStepsTests.java` around lines 101
- 133, Add a regression test that verifies directories matching the file pattern
are ignored by waitForFileAndSavePath: create a directory named like
"report-99.csv" inside the TempDir (using Files.createDirectory) and also create
the actual file(s) needed for the scenario, then call
fileSteps.waitForFileAndSavePath(TIMEOUT, TIMEOUT, FILE_PATTERN, tempDir,
SCOPES, VARIABLE) and assert expected behavior (e.g., when only a matching
directory exists, recordFailedAssertion is called; when both directory and file
exist, only the file is considered and variableContext.putVariable is invoked).
Reference the existing test helper and assertions around
fileSteps.waitForFileAndSavePath, softAssert.recordFailedAssertion, and
variableContext.putVariable to locate where to add the new test(s).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@vividus-tests/src/main/resources/story/integration/FileSteps.story`:
- Line 4: The regex in the scenario step "When I wait `PT30S` with `PT1S`
polling until file matching `.*.txt` appears..." treats the dot as "any
character"; update the pattern to escape the literal dot so it only matches .txt
files by changing the pattern from `.*.txt` to `.*\.txt` inside the step in
FileSteps.story (the step that saves path to scenario variable `textFile`).

In `@vividus/src/test/java/org/vividus/steps/FileStepsTests.java`:
- Around line 101-133: Add a regression test that verifies directories matching
the file pattern are ignored by waitForFileAndSavePath: create a directory named
like "report-99.csv" inside the TempDir (using Files.createDirectory) and also
create the actual file(s) needed for the scenario, then call
fileSteps.waitForFileAndSavePath(TIMEOUT, TIMEOUT, FILE_PATTERN, tempDir,
SCOPES, VARIABLE) and assert expected behavior (e.g., when only a matching
directory exists, recordFailedAssertion is called; when both directory and file
exist, only the file is considered and variableContext.putVariable is invoked).
Reference the existing test helper and assertions around
fileSteps.waitForFileAndSavePath, softAssert.recordFailedAssertion, and
variableContext.putVariable to locate where to add the new test(s).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 01901841-7859-4c6e-81f1-3d8cc09ba297

📥 Commits

Reviewing files that changed from the base of the PR and between b60f6ce and 1c6e7cd.

📒 Files selected for processing (4)
  • docs/modules/commons/pages/vividus-steps.adoc
  • vividus-tests/src/main/resources/story/integration/FileSteps.story
  • vividus/src/main/java/org/vividus/steps/FileSteps.java
  • vividus/src/test/java/org/vividus/steps/FileStepsTests.java

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
vividus/src/test/java/org/vividus/steps/FileStepsTests.java (1)

101-109: 🧹 Nitpick | 🔵 Trivial

Consider adding a test case for directory filtering.

The tests cover the main scenarios well. As noted in a previous review, adding a regression test that creates a directory with a matching name (e.g., report-99.csv/) and verifying it gets filtered out would strengthen coverage of the Files::isRegularFile filter in the implementation.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@vividus/src/test/java/org/vividus/steps/FileStepsTests.java` around lines 101
- 109, Add a regression test in FileStepsTests that ensures directories with
matching names are ignored by the Files::isRegularFile filter: create a
directory whose name matches the file pattern (e.g.,
Files.createDirectory(tempDir.resolve("report-99.csv"))) alongside a real
matching file (or reuse the existing file creation), call
fileSteps.waitForFileAndSavePath(...) and verify
variableContext.putVariable(...) is called with the real file's absolute path
and not the directory; reference the existing test method
shouldSaveFilePathWhenExactlyOneFileMatches to mirror setup and assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@vividus/src/test/java/org/vividus/steps/FileStepsTests.java`:
- Around line 101-109: Add a regression test in FileStepsTests that ensures
directories with matching names are ignored by the Files::isRegularFile filter:
create a directory whose name matches the file pattern (e.g.,
Files.createDirectory(tempDir.resolve("report-99.csv"))) alongside a real
matching file (or reuse the existing file creation), call
fileSteps.waitForFileAndSavePath(...) and verify
variableContext.putVariable(...) is called with the real file's absolute path
and not the directory; reference the existing test method
shouldSaveFilePathWhenExactlyOneFileMatches to mirror setup and assertions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: dae3d0e2-a439-40f2-8df8-012d2c2c49c6

📥 Commits

Reviewing files that changed from the base of the PR and between 1c6e7cd and 64fddef.

📒 Files selected for processing (4)
  • docs/modules/commons/pages/vividus-steps.adoc
  • vividus-tests/src/main/resources/story/integration/FileSteps.story
  • vividus/src/main/java/org/vividus/steps/FileSteps.java
  • vividus/src/test/java/org/vividus/steps/FileStepsTests.java

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/modules/commons/pages/vividus-steps.adoc`:
- Around line 475-496: Add a one-line failure semantics note to the "Wait for a
file to appear in a directory" step description clarifying that if the poll
times out with zero matches or if multiple files match `$fileNameRegex` (not
just the full path) this results in a soft-assertion failure and the target
variable `$variableName` is not set, so downstream steps referencing
`$variableName` may fail; place this sentence near the paragraph that currently
explains `exactly one file` alongside the parameter list (`$timeout`,
`$pollingTimeout`, `$fileNameRegex`, `$directoryPath`, `$scopes`,
`$variableName`) so readers see the behavior immediately.

In `@vividus/src/main/java/org/vividus/steps/FileSteps.java`:
- Around line 118-133: Replace the positional access found.get(0) with the
clearer first-element accessor found.getFirst() in the success branch of the
block handling found files; update the declaration/collection type of found (or
how it is constructed) so that getFirst() is available (e.g., use
LinkedList<Path> or Deque<Path> instead of List<Path>) and keep the call to
variableContext.putVariable(scopes, variableName,
found.getFirst().toAbsolutePath().toString()); ensure any imports or stream
collectors that build found are adjusted accordingly (references:
variableContext.putVariable, found, fileNameRegex, directoryPath, timeout).

In `@vividus/src/test/java/org/vividus/steps/FileStepsTests.java`:
- Around line 121-134: The test shouldFailWhenMultipleFilesMatch can be
simplified by removing the ArgumentCaptor and asserting the deterministic
failure message directly: call fileSteps.waitForFileAndSavePath(...) as before,
then verify that softAssert.recordFailedAssertion was called with the exact
expected string (constructed using FILE_PATTERN and tempDir and the sorted
filenames "report-1.csv, report-2.csv"), and keep
verifyNoInteractions(variableContext); remove the ArgumentCaptor import and its
usage and replace the capture/assertEquals block with a direct
verify(softAssert).recordFailedAssertion(expectedMessage) assertion referencing
the method names shown.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: d0416b60-00b5-4b80-93f0-4c07c28f8089

📥 Commits

Reviewing files that changed from the base of the PR and between 64fddef and 76bacf8.

📒 Files selected for processing (4)
  • docs/modules/commons/pages/vividus-steps.adoc
  • vividus-tests/src/main/resources/story/integration/FileSteps.story
  • vividus/src/main/java/org/vividus/steps/FileSteps.java
  • vividus/src/test/java/org/vividus/steps/FileStepsTests.java

Comment on lines +475 to +496
=== Wait for a file to appear in a directory

Polls the given directory until exactly one file whose name matches the regular expression appears or the timeout
expires, then saves the absolute path of that file to a variable.

[source,gherkin]
----
When I wait `$timeout` with `$pollingTimeout` polling until file matching `$fileNameRegex` appears in directory `$directoryPath` and save path to $scopes variable `$variableName`
----

* `$timeout` - The maximum time to wait in {durations-format-link} format.
* `$pollingTimeout` - The interval between polling attempts in {durations-format-link} format.
* `$fileNameRegex` - The regular expression matched against file names only (not the full path), e.g. `report-\d+\.csv`.
* `$directoryPath` - The path to an existing directory to watch.
* `$scopes` - xref:commons:variables.adoc#_scopes[The comma-separated set of the variables scopes].
* `$variableName` - The name of the variable to store the absolute path of the matched file.

.Wait up to 30 seconds for a CSV report to arrive and store its path
[source,gherkin]
----
When I wait `PT30S` with `PT1S` polling until file matching `report-\d+\.csv` appears in directory `/tmp/reports` and save path to SCENARIO variable `report-path`
----
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Document the failure semantics (zero vs. multiple matches) and that $variableName is not set on failure.

The intro mentions polling until "exactly one file" appears but doesn't explicitly state that: (a) finding zero files at timeout and (b) finding multiple matching files both result in soft-assertion failures, and (c) the target variable is not set in those cases — which affects any downstream steps referencing it. A one-liner would prevent surprises.

✍️ Proposed addition
 Polls the given directory until exactly one file whose name matches the regular expression appears or the timeout
 expires, then saves the absolute path of that file to a variable.
+
+If no matching file appears within the timeout, or if more than one matching file is found during polling, a soft
+assertion failure is recorded and the variable is not set.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
=== Wait for a file to appear in a directory
Polls the given directory until exactly one file whose name matches the regular expression appears or the timeout
expires, then saves the absolute path of that file to a variable.
[source,gherkin]
----
When I wait `$timeout` with `$pollingTimeout` polling until file matching `$fileNameRegex` appears in directory `$directoryPath` and save path to $scopes variable `$variableName`
----
* `$timeout` - The maximum time to wait in {durations-format-link} format.
* `$pollingTimeout` - The interval between polling attempts in {durations-format-link} format.
* `$fileNameRegex` - The regular expression matched against file names only (not the full path), e.g. `report-\d+\.csv`.
* `$directoryPath` - The path to an existing directory to watch.
* `$scopes` - xref:commons:variables.adoc#_scopes[The comma-separated set of the variables scopes].
* `$variableName` - The name of the variable to store the absolute path of the matched file.
.Wait up to 30 seconds for a CSV report to arrive and store its path
[source,gherkin]
----
When I wait `PT30S` with `PT1S` polling until file matching `report-\d+\.csv` appears in directory `/tmp/reports` and save path to SCENARIO variable `report-path`
----
=== Wait for a file to appear in a directory
Polls the given directory until exactly one file whose name matches the regular expression appears or the timeout
expires, then saves the absolute path of that file to a variable.
If no matching file appears within the timeout, or if more than one matching file is found during polling, a soft
assertion failure is recorded and the variable is not set.
[source,gherkin]
----
When I wait `$timeout` with `$pollingTimeout` polling until file matching `$fileNameRegex` appears in directory `$directoryPath` and save path to $scopes variable `$variableName`
----
* `$timeout` - The maximum time to wait in {durations-format-link} format.
* `$pollingTimeout` - The interval between polling attempts in {durations-format-link} format.
* `$fileNameRegex` - The regular expression matched against file names only (not the full path), e.g. `report-\d+\.csv`.
* `$directoryPath` - The path to an existing directory to watch.
* `$scopes` - xref:commons:variables.adoc#_scopes[The comma-separated set of the variables scopes].
* `$variableName` - The name of the variable to store the absolute path of the matched file.
.Wait up to 30 seconds for a CSV report to arrive and store its path
[source,gherkin]
----
When I wait `PT30S` with `PT1S` polling until file matching `report-\d+\.csv` appears in directory `/tmp/reports` and save path to SCENARIO variable `report-path`
----
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/modules/commons/pages/vividus-steps.adoc` around lines 475 - 496, Add a
one-line failure semantics note to the "Wait for a file to appear in a
directory" step description clarifying that if the poll times out with zero
matches or if multiple files match `$fileNameRegex` (not just the full path)
this results in a soft-assertion failure and the target variable `$variableName`
is not set, so downstream steps referencing `$variableName` may fail; place this
sentence near the paragraph that currently explains `exactly one file` alongside
the parameter list (`$timeout`, `$pollingTimeout`, `$fileNameRegex`,
`$directoryPath`, `$scopes`, `$variableName`) so readers see the behavior
immediately.

Comment on lines +118 to +133
if (found.size() > 1)
{
List<String> fileNames = found.stream().map(Path::getFileName).map(Path::toString).sorted().toList();
softAssert
.recordFailedAssertion("Expected exactly 1 file matching `%s` in directory '%s', but found %d: %s."
.formatted(fileNameRegex, directoryPath, found.size(), String.join(", ", fileNames)));
}
else if (found.isEmpty())
{
softAssert.recordFailedAssertion("No file matching `%s` appeared in directory '%s' within %s"
.formatted(fileNameRegex, directoryPath, timeout));
}
else
{
variableContext.putVariable(scopes, variableName, found.get(0).toAbsolutePath().toString());
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

🧩 Analysis chain

🌐 Web query:

What is the minimum Java version required by the vividus-framework/vividus project on master?

💡 Result:

The minimum Java version required by the vividus-framework/vividus project on master is Java 21.

Citations:


🏁 Script executed:

find . -name "FileSteps.java" -type f

Repository: vividus-framework/vividus

Length of output: 125


🏁 Script executed:

sed -n '118,133p' ./vividus/src/main/java/org/vividus/steps/FileSteps.java

Repository: vividus-framework/vividus

Length of output: 860


Prefer List.getFirst() over get(0) for better readability.

Since the project targets Java 21+, getFirst() is available and expresses intent more clearly.

♻️ Proposed change
-            variableContext.putVariable(scopes, variableName, found.get(0).toAbsolutePath().toString());
+            variableContext.putVariable(scopes, variableName, found.getFirst().toAbsolutePath().toString());
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@vividus/src/main/java/org/vividus/steps/FileSteps.java` around lines 118 -
133, Replace the positional access found.get(0) with the clearer first-element
accessor found.getFirst() in the success branch of the block handling found
files; update the declaration/collection type of found (or how it is
constructed) so that getFirst() is available (e.g., use LinkedList<Path> or
Deque<Path> instead of List<Path>) and keep the call to
variableContext.putVariable(scopes, variableName,
found.getFirst().toAbsolutePath().toString()); ensure any imports or stream
collectors that build found are adjusted accordingly (references:
variableContext.putVariable, found, fileNameRegex, directoryPath, timeout).

Comment on lines +121 to +134
@Test
void shouldFailWhenMultipleFilesMatch(@TempDir Path tempDir) throws IOException
{
Files.createFile(tempDir.resolve("report-1.csv"));
Files.createFile(tempDir.resolve("report-2.csv"));
fileSteps.waitForFileAndSavePath(TIMEOUT, TIMEOUT, FILE_PATTERN, tempDir, SCOPES, VARIABLE);
var messageCaptor = ArgumentCaptor.forClass(String.class);
verify(softAssert).recordFailedAssertion(messageCaptor.capture());
assertEquals(
"Expected exactly 1 file matching `%s` in directory '%s', but found 2: report-1.csv, report-2.csv."
.formatted(FILE_PATTERN, tempDir),
messageCaptor.getValue());
verifyNoInteractions(variableContext);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Simplify: drop ArgumentCaptor — the expected message is deterministic.

Filenames are sorted before being joined, so the expected assertion message is fully deterministic and you can verify it directly.

♻️ Proposed simplification
-        fileSteps.waitForFileAndSavePath(TIMEOUT, TIMEOUT, FILE_PATTERN, tempDir, SCOPES, VARIABLE);
-        var messageCaptor = ArgumentCaptor.forClass(String.class);
-        verify(softAssert).recordFailedAssertion(messageCaptor.capture());
-        assertEquals(
-                "Expected exactly 1 file matching `%s` in directory '%s', but found 2: report-1.csv, report-2.csv."
-                        .formatted(FILE_PATTERN, tempDir),
-                messageCaptor.getValue());
-        verifyNoInteractions(variableContext);
+        fileSteps.waitForFileAndSavePath(TIMEOUT, TIMEOUT, FILE_PATTERN, tempDir, SCOPES, VARIABLE);
+        verify(softAssert).recordFailedAssertion(
+                "Expected exactly 1 file matching `%s` in directory '%s', but found 2: report-1.csv, report-2.csv."
+                        .formatted(FILE_PATTERN, tempDir));
+        verifyNoInteractions(variableContext);

You can then remove the ArgumentCaptor import.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@vividus/src/test/java/org/vividus/steps/FileStepsTests.java` around lines 121
- 134, The test shouldFailWhenMultipleFilesMatch can be simplified by removing
the ArgumentCaptor and asserting the deterministic failure message directly:
call fileSteps.waitForFileAndSavePath(...) as before, then verify that
softAssert.recordFailedAssertion was called with the exact expected string
(constructed using FILE_PATTERN and tempDir and the sorted filenames
"report-1.csv, report-2.csv"), and keep verifyNoInteractions(variableContext);
remove the ArgumentCaptor import and its usage and replace the
capture/assertEquals block with a direct
verify(softAssert).recordFailedAssertion(expectedMessage) assertion referencing
the method names shown.

@sonarqubecloud
Copy link
Copy Markdown

@valfirst valfirst merged commit dd6c257 into master Apr 17, 2026
16 of 18 checks passed
@valfirst valfirst deleted the files branch April 17, 2026 07:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants