Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
bc70dc8
Added new rid generator.
YevheniiVlasenko Sep 11, 2025
3a3ef1d
Added ArtifactManagementException
YevheniiVlasenko Sep 11, 2025
1943ff8
Added Testomatio facade class and ServiceRegistry
YevheniiVlasenko Sep 11, 2025
85688f9
Added CVS writer
YevheniiVlasenko Sep 11, 2025
e853eaf
Implemented ArtifactManager.java
YevheniiVlasenko Sep 11, 2025
e193c9a
logic change to temporal storage. (deleted excess files, not fully im…
YevheniiVlasenko Sep 12, 2025
1c513d6
checkpoint
YevheniiVlasenko Sep 13, 2025
a77e5e5
Implemented core artifact support via ThreadLocal temp storage
YevheniiVlasenko Sep 13, 2025
842c821
implemented artifact link upload to Testomat logic
YevheniiVlasenko Sep 15, 2025
b470b67
removed s3presigner usage
YevheniiVlasenko Sep 16, 2025
4c153bf
Link report body fix(junit)
YevheniiVlasenko Sep 17, 2025
d031136
checkpoint
YevheniiVlasenko Sep 17, 2025
9b72600
implemented secondary tests sending with artifacts
YevheniiVlasenko Sep 18, 2025
60d5c35
implemented testomatio.disable.artifacts (junit)
YevheniiVlasenko Sep 18, 2025
6125ccd
implemented env properties logic: ARTIFACT_DISABLE_PROPERTY_NAME, BUC…
YevheniiVlasenko Sep 18, 2025
d76bae1
implemented custom endpoint and forcepath. refactoring required.
YevheniiVlasenko Sep 18, 2025
7370edc
implemented custom endpoint and forcepath. refactoring required.
YevheniiVlasenko Sep 19, 2025
2904eb7
minor refactoring
YevheniiVlasenko Sep 22, 2025
6658a31
refactored artifact related code
YevheniiVlasenko Sep 22, 2025
05d4cc6
Added ACL check logic
YevheniiVlasenko Sep 22, 2025
24d8ae3
JavaDoc for artifact related classes
YevheniiVlasenko Sep 22, 2025
231f60f
JavaDoc for artifact related classes (missed in first commit)
YevheniiVlasenko Sep 22, 2025
9bfad79
Added disable reporting logic
YevheniiVlasenko Sep 23, 2025
260a1de
Fixed isValidFilePath in ArtifactManager signature
YevheniiVlasenko Sep 23, 2025
a97ef00
Refactored GlobalRunManager, added tests
YevheniiVlasenko Sep 23, 2025
ab27c1f
Added artifact description to the README
YevheniiVlasenko Sep 23, 2025
3c7b456
checkpoint
YevheniiVlasenko Sep 24, 2025
585c29b
fixed TestNg artifact bugs
YevheniiVlasenko Sep 24, 2025
70ac825
Added the delay before artifact batch sending
YevheniiVlasenko Sep 26, 2025
6576b47
Fixed artifact paths for all frameworks
YevheniiVlasenko Sep 28, 2025
20a141d
Refactored AwsService
YevheniiVlasenko Sep 28, 2025
5f66cbc
Fixed tests in Cucumber module. Added new tests
YevheniiVlasenko Sep 29, 2025
11f9293
Fixed artifacts related property names
YevheniiVlasenko Sep 29, 2025
64731f3
Updated README.md
YevheniiVlasenko Sep 29, 2025
c7cc19f
Added message on before-artifact-delay
YevheniiVlasenko Sep 29, 2025
87ea07c
Update README.md
YevheniiVlasenko Sep 29, 2025
63714dc
Update README.md
YevheniiVlasenko Sep 29, 2025
5b0ecbc
Update README.md
YevheniiVlasenko Sep 29, 2025
7f3cdcb
Readme minor fix
YevheniiVlasenko Sep 29, 2025
9c0eaeb
Readme minor fix
YevheniiVlasenko Sep 29, 2025
525516b
Merge branch '1.x' into artifact-support-implementation
YevheniiVlasenko Sep 29, 2025
b6ba521
Minor fixes in JunitListener.java;
YevheniiVlasenko Sep 29, 2025
969fd95
fixed namespaces to run github workflow
YevheniiVlasenko Sep 29, 2025
2ed01e5
Changed version in core pom for separate deploy
YevheniiVlasenko Sep 29, 2025
bd907f0
Changed versions in framework modules poms for auto deploy
YevheniiVlasenko Sep 29, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 72 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ and team collaboration features.
| **Advanced error reporting** | Detailed test failure/skip descriptions | ✅ | ✅ | ✅ |
| **TestId import** | Import test IDs from testomat.io into the codebase | ✅ | ✅ | ✅ |
| **Parametrized tests support** | Enhanced support for parameterized testing | ✅ | ✅ | ✅ |
| **Test artifacts support** | Screenshots, logs, and file attachments | | | |
| **Test artifacts support** | Screenshots, logs, and file attachments | | | |
| **Step-by-step reporting** | Detailed test step execution tracking | ⏳ | ⏳ | ⏳ |
| **Other frameworks support** | Karate, Gauge, etc. (Priority may change) | | | |

Expand Down Expand Up @@ -100,7 +100,9 @@ Create the `cucumber.properties` file if you don't have one yet and add this lin
```properties
cucumber.plugin=io.testomat.cucumber.listener.CucumberListener
```

---

### 🔧 Advanced Custom Setup

> **⚠️ Only use this if you need custom behavior** - like adding extra logic to test lifecycle events.
Expand Down Expand Up @@ -279,7 +281,75 @@ Use these oneliners to **download jar and update** ids in one move

---

## 💡 Usage Examples
## 📎 Test Artifacts Support

The Java Reporter supports attaching files (screenshots, logs, videos, etc.) to your test results and uploading them to
S3-compatible storage.
Artifacts handling is enabled by default, but it won't affect the run if there are no artifacts provided (see options below).

### Configuration

Artifacts are stored in external S3 buckets. S3 Access can be configured in **two different ways**:

1. Make configurations on the [Testomat.io](https://app.testomat.io):
Choose your project -> click **Settings** button on the left panel -> click **Artifacts** -> Toggle "**Share
credentials**..."
<img src=img/artifactsOnServerTurnOn.png alt="artifact example" width=50% />

2. Provide options as environment variables/jvm property/testomatio.properties file.

> NOTE: Environment variables(env/jvm/testomatio.properties) take precedence over server-provided credentials.

| Setting | Description | Default |
|-------------------------------|--------------------------------------------------|-------------|
| `testomatio.artifact.disable` | Completely disable artifact uploading | `false` |
| `testomatio.artifact.private` | Keep artifacts private (no public URLs) | `false` |
| `s3.force-path-style` | Use path-style URLs for S3-compatible storage | `false` |
| `s3.endpoint` | Custom endpoint ot be used with force-path-style | `false` |
| `s3.bucket` | Provides bucket name for configuration | |
| `s3.access-key-id` | Access key for the bucket | |
| `s3.region` | Bucket region | `us-west-1` |

**Note**: S3 credentials can be configured either in properties file or provided automatically on Testomat.io UI.
Environment variables take precedence over server-provided credentials.

### Usage

Use the `Testomatio` facade to attach files to your tests:
Copy link
Contributor

Choose a reason for hiding this comment

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

What other methods Testomatio contains?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

None yet

Multiple files can be provided to the `Testomatio.artifact(String ...)` method.

```java
import io.testomat.core.facade.Testomatio;

public class MyTest {

@Test
public void testWithScreenshot() {
// Your test logic

// Attach artifacts (screenshots, logs, etc.)
Testomatio.artifact(
"/path/to/screenshot.png",
"/path/to/test.log"
);
}
}
```
Please, make sure you provide path to artifact file including its extension.

### How It Works

1. **S3 Upload**: Files are uploaded to your S3 bucket with organized folder structure
2. **Link Generation**: Public URLs are generated and attached to test results
3. Artifacts are visible at the test info on UI


As the result you will see something like this on UI after run completed:
<img src=img/artifactExample.png alt="artifact example" width=50% />

---

## 💡 Library Usage Examples

### Basic Usage

Expand Down
Binary file added img/artifactExample.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/artifactsOnServerTurnOn.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 11 additions & 1 deletion java-reporter-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

<groupId>io.testomat</groupId>
<artifactId>java-reporter-core</artifactId>
<version>0.6.8</version>
<version>0.7.9</version>
<packaging>jar</packaging>

<name>Testomat.io Reporter Core</name>
Expand Down Expand Up @@ -67,6 +67,16 @@
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>software.amazon.awssdk</groupId>-->
<!-- <artifactId>s3</artifactId>-->
<!-- <version>2.33.4</version>-->
<!-- </dependency>-->
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>aws-sdk-java</artifactId>
<version>2.33.6</version>
</dependency>

<!-- Test dependencies -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package io.testomat.core.artifact;

import java.util.List;

/**
* Data class representing the relationship between test execution and its associated artifact links.
* Contains test metadata and S3 URLs for uploaded artifacts.
*/
public class ArtifactLinkData {
private String rid;
private final String testId;
private final String testName;

private final List<String> links;

/**
* Creates artifact link data for a test execution.
*
* @param testName name of the test
* @param rid request identifier
* @param testId unique test identifier
* @param links list of S3 URLs for uploaded artifacts
*/
public ArtifactLinkData(String testName, String rid, String testId, List<String> links) {
this.testName = testName;
this.rid = rid;
this.testId = testId;
this.links = links;
}

/**
* Returns the list of artifact URLs.
*
* @return list of S3 URLs for artifacts
*/
public List<String> getLinks() {
return links;
}

/**
* Returns the unique test identifier.
*
* @return test ID
*/
public String getTestId() {
return testId;
}

/**
* Returns the request identifier.
*
* @return request ID
*/
public String getRid() {
return rid;
}

/**
* Sets the request identifier.
*
* @param rid request ID to set
*/
public void setRid(String rid) {
this.rid = rid;
}

/**
* Returns the test name.
*
* @return name of the test
*/
public String getTestName() {
return testName;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package io.testomat.core.artifact;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
* Thread-safe storage for artifact link data collected during test execution.
* Maintains a collection of artifact links that will be associated with test results.
*/
public class ArtifactLinkDataStorage {
public static final List<ArtifactLinkData> ARTEFACT_LINK_DATA_STORAGE =
new CopyOnWriteArrayList<>();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package io.testomat.core.artifact;

/**
* Builder for creating JSON request bodies containing artifact links for upload to the server.
* Handles serialization of artifact data and test run information.
*/

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LinkUploadBodyBuilder {
private static final Logger log = LoggerFactory.getLogger(LinkUploadBodyBuilder.class);

public String buildLinkUploadRequestBody(List<ArtifactLinkData> storedLinkData, String apiKey) {
ObjectMapper mapper = new ObjectMapper();
ObjectNode rootNode = mapper.createObjectNode();
ArrayNode testsArray = mapper.createArrayNode();

for (ArtifactLinkData data : storedLinkData) {
ObjectNode testNode = mapper.createObjectNode();
testNode.put("rid", data.getRid());
testNode.put("test_id", data.getTestId());
testNode.put("title", data.getTestName());
testNode.put("overwrite", "true");
testNode.set("artifacts", mapper.valueToTree(data.getLinks()));
testsArray.add(testNode);
}

rootNode.put("api_key", apiKey);
rootNode.set("tests", testsArray);

String json = null;
try {
json = mapper.writeValueAsString(rootNode);
} catch (JsonProcessingException e) {
log.warn("Failed to convert convert link storage to json body");
}
return json;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package io.testomat.core.artifact;

import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Thread-safe storage for reported test data with artifact linking capabilities.
* Maintains test execution results and allows linking artifacts to specific tests by RID.
*/
public class ReportedTestStorage {
private static final Logger log = LoggerFactory.getLogger(ReportedTestStorage.class);
private static final List<Map<String, Object>> STORAGE = new CopyOnWriteArrayList<>();

/**
* Stores test execution data.
*
* @param body test data map containing test results and metadata
*/
public static void store(Map<String, Object> body) {
STORAGE.add(body);
log.debug("Stored body: {}", body);
}

/**
* Returns all stored test data.
*
* @return list of test data maps
*/
public static List<Map<String, Object>> getStorage() {
return STORAGE;
}

/**
* Links artifacts to their corresponding tests using RID matching.
*
* @param artifactLinkData list of artifact link data to associate with tests
*/
public static void linkArtifactsToTests(List<ArtifactLinkData> artifactLinkData) {
for (ArtifactLinkData data : artifactLinkData) {
STORAGE.stream()
.filter(body -> data.getRid().equals(body.get("rid")))
.forEach(body -> body.put("artifacts", data.getLinks()));
}
for (ArtifactLinkData data : artifactLinkData) {
log.debug("Linked: testId - {}, testName - {}, rid - {}, links - {}",
data.getTestId(), data.getTestName(), data.getRid(), data.getLinks().get(0));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package io.testomat.core.artifact;

import java.util.ArrayList;
import java.util.List;

/**
* Thread-local storage for temporarily holding artifact file paths during test execution.
* Ensures thread safety when multiple tests run concurrently.
*/
public class TempArtifactDirectoriesStorage {
public static final ThreadLocal<List<String>> DIRECTORIES = ThreadLocal.withInitial(ArrayList::new);

public static void store(String dir) {
DIRECTORIES.get().add(dir);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package io.testomat.core.artifact.client;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import software.amazon.awssdk.services.s3.S3Client;

/**
* AWS S3 client wrapper with built-in configuration management.
* Provides singleton S3Client instances with custom endpoint and credential support.
*/
public class AwsClient {
private static final Logger log = LoggerFactory.getLogger(AwsClient.class);
private volatile S3Client s3Client;
private final S3ClientFactory clientFactory;

public AwsClient() {
this.clientFactory = new S3ClientFactory();
}

/**
* Test Constructor
*/
public AwsClient(S3ClientFactory s3ClientFactory) {
this.clientFactory = s3ClientFactory;
}

/**
* Returns a configured S3Client instance using lazy initialization.
*
* @return configured S3Client instance
*/
public S3Client getS3Client() {
if (s3Client == null) {
s3Client = clientFactory.createS3Client();
return s3Client;
}
return s3Client;
}
}
Loading