Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 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.9.0</version>
<version>0.9.1</version>
<packaging>jar</packaging>

<name>Testomat.io Reporter Core</name>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package io.testomat.core.constants;

public class CommonConstants {
public static final String REPORTER_VERSION = "0.8.7";
public static final String REPORTER_VERSION = "0.9.1";

public static final String TESTS_STRING = "tests";
public static final String API_KEY_STRING = "api_key";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package io.testomat.core.step;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class StepTimer {

private static final Map<String, Long> START_TIMES = new ConcurrentHashMap<>();

public static void start(String key) {
START_TIMES.put(key, System.currentTimeMillis());
}

public static long stop(String key) {
Long start = START_TIMES.remove(key);
if (start == null) {
return -1;
}
return System.currentTimeMillis() - start;
}
}
4 changes: 2 additions & 2 deletions java-reporter-cucumber/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>io.testomat</groupId>
<artifactId>java-reporter-cucumber</artifactId>
<version>0.7.10</version>
<version>0.7.11</version>
<packaging>jar</packaging>

<name>Testomat.io Java Reporter Cucumber</name>
Expand Down Expand Up @@ -51,7 +51,7 @@
<dependency>
<groupId>io.testomat</groupId>
<artifactId>java-reporter-core</artifactId>
<version>0.9.0</version>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
Expand Down
4 changes: 2 additions & 2 deletions java-reporter-junit/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>io.testomat</groupId>
<artifactId>java-reporter-junit</artifactId>
<version>0.8.1</version>
<version>0.8.2</version>
<packaging>jar</packaging>

<name>Testomat.io Java Reporter JUnit</name>
Expand Down Expand Up @@ -51,7 +51,7 @@
<dependency>
<groupId>io.testomat</groupId>
<artifactId>java-reporter-core</artifactId>
<version>0.9.0</version>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
Expand Down
6 changes: 3 additions & 3 deletions java-reporter-karate/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>io.testomat</groupId>
<artifactId>java-reporter-karate</artifactId>
<version>0.2.2</version>
<version>0.2.3</version>
<packaging>jar</packaging>

<name>Testomat.io Java Reporter Karate</name>
Expand Down Expand Up @@ -52,7 +52,7 @@
<dependency>
<groupId>io.testomat</groupId>
<artifactId>java-reporter-core</artifactId>
<version>0.9.0</version>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>io.karatelabs</groupId>
Expand Down Expand Up @@ -117,7 +117,7 @@
<artifactId>maven-javadoc-plugin</artifactId>
<version>${maven-javadoc-plugin.version}</version>
<configuration>
<source>11</source>
<source>17</source>
<quiet>true</quiet>
<doclint>none</doclint>
</configuration>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package io.testomat.karate.adapter;

import com.intuit.karate.core.ScenarioEngine;
import com.intuit.karate.core.Variable;

public class CustomKarateEngineAdapter implements KarateEngineAdapter {

@Override
public Object getVariable(String name) {
ScenarioEngine engine = ScenarioEngine.get();
Variable variable = engine.vars.get(name);
return variable != null ? variable.getValue() : null;
}

@Override
public void setVariable(String name, Object value) {
ScenarioEngine.get().setVariable(name, value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package io.testomat.karate.adapter;

public interface KarateEngineAdapter {

Object getVariable(String name);

void setVariable(String name, Object value);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import static io.testomat.core.constants.CommonConstants.FAILED;
import static io.testomat.core.constants.CommonConstants.PASSED;
import static io.testomat.core.constants.CommonConstants.SKIPPED;
import static java.util.Objects.isNull;

import com.intuit.karate.core.ScenarioResult;
import com.intuit.karate.core.ScenarioRuntime;
Expand All @@ -12,9 +11,7 @@
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Stream;
import org.slf4j.Logger;
Expand Down Expand Up @@ -47,47 +44,21 @@ public ExceptionDetails extractExceptionDetails(ScenarioRuntime sr) {
}

/**
* Extracts a test identifier from a Karate test execution context.
* Extracts a test identifier from scenario tags.
* <p>
* The method attempts to resolve the test identifier using the following priority:
* <ol>
* <li>
* Scenario Outline {@code Examples} data — looks for an example column
* whose name matches the configured test id prefix.
* </li>
* <li>
* Scenario tags — looks for tags starting with the same prefix
* (for example, {@code TestId:}).
* </li>
* </ol>
* <p>
* The extracted value is validated against the configured test id format.
* No additional normalization is applied to the returned value.
* <p>
* If no valid test identifier is found in either source, the method returns {@code null}.
* Searches for the first tag that starts with the configured {@code TEST_ID_PREFIX}
* (case-insensitive), removes the prefix, and validates the remaining value.
*
* @param sr the {@link ScenarioRuntime} representing the executed Karate test case
* @return the extracted test identifier, or {@code null} if not found
* @param sr the {@link ScenarioRuntime} of the executed Karate scenario
* @return the extracted test identifier, or {@code null} if none is found
*/
public String extractTestId(ScenarioRuntime sr) {
String testId = findFirstValidTestId(
Optional.ofNullable(sr.scenario.getExampleData())
.stream()
.flatMap(m -> m.entrySet().stream())
.filter(e -> e.getKey().equalsIgnoreCase(TEST_ID_PREFIX))
.map(Map.Entry::getValue)
.map(Object::toString)
);

if (isNull(testId)) {
testId = findFirstValidTestId(sr.tags.getTags().stream()
.filter(Objects::nonNull)
.filter(tag -> tag.regionMatches(true, 0,
TEST_ID_PREFIX, 0, TEST_ID_PREFIX.length()))
.map(tag -> tag.substring(TEST_ID_PREFIX.length() + 1)));
}
return findFirstValidTestId(sr.tags.getTags().stream()
.filter(Objects::nonNull)
.filter(tag -> tag.regionMatches(true, 0,
TEST_ID_PREFIX, 0, TEST_ID_PREFIX.length()))
.map(tag -> tag.substring(TEST_ID_PREFIX.length() + 1)));

return testId;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,49 @@
import com.intuit.karate.RuntimeHook;
import com.intuit.karate.Suite;
import com.intuit.karate.core.ScenarioRuntime;
import com.intuit.karate.core.Step;
import com.intuit.karate.core.StepResult;
import io.testomat.core.exception.ReportTestResultException;
import io.testomat.core.model.TestResult;
import io.testomat.core.runmanager.GlobalRunManager;
import io.testomat.core.step.StepStorage;
import io.testomat.core.step.StepTimer;
import io.testomat.core.step.TestStep;
import io.testomat.karate.adapter.CustomKarateEngineAdapter;
import io.testomat.karate.adapter.KarateEngineAdapter;
import io.testomat.karate.constructor.KarateTestResultConstructor;
import io.testomat.karate.exception.KarateHookException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Runtime hook for integrating Karate test execution with Testomat.io.
* Reports Karate test execution results to the Testomat.io platform.
* Reports Karate test execution results to the
* Testomat.io platform.
*/
public class KarateHook implements RuntimeHook {

private static final Logger log = LoggerFactory.getLogger(KarateHook.class);

private static final String LOG_NEXT_STEP = "log_next_step";
private static final String LOG_NEXT_STEP_TITLE = "log_next_step_title";
private static final String LOG_STEPS = "LogSteps";

private final KarateTestResultConstructor resultConstructor;
private final FacadeFunctionsHandler functionsHandler;
private final GlobalRunManager runManager;
private final KarateEngineAdapter engine;

public KarateHook(
KarateTestResultConstructor resultConstructor,
FacadeFunctionsHandler functionsHandler,
GlobalRunManager runManager
GlobalRunManager runManager,
KarateEngineAdapter engine
) {
this.resultConstructor = resultConstructor;
this.functionsHandler = functionsHandler;
this.runManager = runManager;
this.engine = engine;
}

/**
Expand All @@ -39,7 +58,8 @@ public KarateHook() {
this(
new KarateTestResultConstructor(),
new FacadeFunctionsHandler(),
GlobalRunManager.getInstance()
GlobalRunManager.getInstance(),
new CustomKarateEngineAdapter()
);
}

Expand All @@ -48,6 +68,51 @@ public void beforeSuite(Suite suite) {
runManager.incrementSuiteCounter();
}

@Override
public boolean beforeStep(Step step, ScenarioRuntime sr) {
boolean isMarked = Boolean.TRUE.equals(engine.getVariable(LOG_NEXT_STEP));

boolean isDslStep = switch (step.getPrefix() == null ? "" : step.getPrefix()) {
case "Given", "When", "Then", "And", "But" -> true;
default -> false;
};

boolean logAllSteps = isDslStep && sr.tags.getTags().contains(LOG_STEPS);

if (logAllSteps || isMarked) {
engine.setVariable(LOG_NEXT_STEP, false);
String stepId = Thread.currentThread().getId() + ":" + System.identityHashCode(step);
StepTimer.start(stepId);
}
return true;
}

@Override
public void afterStep(StepResult result, ScenarioRuntime sr) {
Step step = result.getStep();
String stepId = Thread.currentThread().getId() + ":" + System.identityHashCode(step);
long durationMillis = StepTimer.stop(stepId);

if (durationMillis != -1) {
String stepName = step.getText();
Object title = engine.getVariable(LOG_NEXT_STEP_TITLE);

if (title != null) {
stepName = title.toString();
engine.setVariable(LOG_NEXT_STEP_TITLE, null);
}

TestStep testStep = new TestStep();
testStep.setCategory("user");
testStep.setStepTitle(stepName);
testStep.setDuration(durationMillis);

StepStorage.addStep(testStep);

log.debug("Step '{}' completed in {} ms", stepName, durationMillis);
}
}

@Override
public void afterScenario(ScenarioRuntime sr) {
if (!runManager.isActive()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package io.testomat.karate.marker;

import com.intuit.karate.core.ScenarioEngine;

public final class StepMarker {

private static final String LOG_NEXT_STEP = "log_next_step";
private static final String LOG_NEXT_STEP_TITLE = "log_next_step_title";

private StepMarker() {

}

public static void mark() {
ScenarioEngine engine = ScenarioEngine.get();
if (engine != null) {
engine.setVariable(LOG_NEXT_STEP, true);
}
}

public static void mark(String title) {
ScenarioEngine engine = ScenarioEngine.get();
if (engine != null) {
engine.setVariable(LOG_NEXT_STEP, true);
engine.setVariable(LOG_NEXT_STEP_TITLE, title);
}
}
}
Loading