diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index bb09719d..319290ea 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -45,7 +45,10 @@ body:
- allure-jsonunit
- allure-junit-platform
- allure-junit4
+ - allure-jupiter
+ - allure-jupiter-assert
- allure-junit5
+ - allure-junit5-assert
- allure-karate
- allure-okhttp
- allure-okhttp3
diff --git a/.github/labeler.yml b/.github/labeler.yml
index a419c97f..367271a0 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -48,8 +48,8 @@
- "allure-junit4-aspect/**"
"theme:junit-platform":
- - "allure-junit5/**"
- - "allure-junit5-assert/**"
+ - "allure-jupiter/**"
+ - "allure-jupiter-assert/**"
- "allure-junit-platform/**"
"theme:karate":
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 8f537bc9..341788f5 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -13,22 +13,83 @@ on:
- 'main'
- 'hotfix-*'
+concurrency:
+ # On main, we don't want any jobs cancelled.
+ # On PR branches, we cancel the job if new commits are pushed.
+ group: ${{ github.ref }}
+ cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
+
jobs:
build:
name: "Build"
runs-on: ubuntu-latest
+ env:
+ ALLURE_MATRIX_ENV: ubuntu-jdk-21
+ ALLURE_TEST_DUMP_NAME: allure-results-test-jdk-21
steps:
- uses: actions/checkout@v6
+ - uses: actions/setup-node@v6
+ with:
+ node-version: '20.x'
+
- name: "Set up JDK"
uses: actions/setup-java@v5
with:
distribution: 'zulu'
java-version: 21
+ - name: "Setup Gradle"
+ uses: gradle/actions/setup-gradle@v6
+ with:
+ gradle-version: 'wrapper'
+
- name: "Build with Gradle"
run: ./gradlew build -x test --scan
- - name: "Run tests"
+ - name: "Run tests with Allure"
if: always()
- run: ./gradlew --no-build-cache cleanTest test
+ run: npx -y allure@3 run --config ./allurerc.mjs --rerun 2 --environment="${{ env.ALLURE_MATRIX_ENV }}" --dump="${{ env.ALLURE_TEST_DUMP_NAME }}" -- ./gradlew --no-build-cache cleanTest test
+
+ - name: "Upload Allure test dump"
+ if: always()
+ uses: actions/upload-artifact@v7
+ with:
+ name: ${{ env.ALLURE_TEST_DUMP_NAME }}
+ path: ./${{ env.ALLURE_TEST_DUMP_NAME }}.zip
+
+ report:
+ needs: [build]
+ name: "Build report"
+ runs-on: ubuntu-latest
+ if: always()
+ permissions:
+ contents: read
+ pull-requests: write
+ checks: write
+ env:
+ ALLURE_SERVICE_TOKEN: ${{ secrets.ALLURE_SERVICE_TOKEN }}
+ steps:
+ - uses: actions/checkout@v6
+
+ - uses: actions/setup-node@v6
+ with:
+ node-version: '20.x'
+
+ - name: "Download Allure dumps"
+ uses: actions/download-artifact@v8
+ continue-on-error: true
+ with:
+ pattern: allure-results-*
+ path: ./
+ merge-multiple: true
+
+ - name: "Generate Allure report"
+ run: npx -y allure@3 generate --config ./allurerc.mjs --dump="allure-results-*.zip" --output=./build/allure-report
+
+ - name: "Post Allure summary"
+ if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false
+ uses: allure-framework/allure-action@v0
+ with:
+ report-directory: ./build/allure-report
+ github-token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
index aeaa9e45..95443a12 100644
--- a/.idea/vcs.xml
+++ b/.idea/vcs.xml
@@ -10,7 +10,4 @@
-
-
-
-
+
\ No newline at end of file
diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 00000000..8b7a6aa4
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,8 @@
+# Project Guide
+
+Use [Allure Agent Mode](docs/allure-agent-mode.md) for all test-related work in this repository.
+
+- Read `docs/allure-agent-mode.md` before designing, writing, reviewing, validating, debugging, or enriching tests.
+- Run test-executing commands through `allure run`, including smoke checks after small edits.
+- Use `./gradlew` for repo-local test commands and scope runs to the smallest relevant module or task.
+- If agent-mode output is missing or incomplete, debug that first rather than relying on console-only conclusions.
diff --git a/README.md b/README.md
index e452787b..fd415585 100644
--- a/README.md
+++ b/README.md
@@ -37,11 +37,12 @@
- 📚 Example project — https://github.com/allure-examples?q=topic%3Ajunit4
- ✅ Generate a project in 10 seconds via Allure Start - https://allurereport.org/start/
-
-## JUnit 5
-
-- 🚀 Documentation — https://allurereport.org/docs/junit5/
-- 📚 Example project — https://github.com/allure-examples?q=topic%3Ajunit5
-- ✅ Generate a project in 10 seconds via Allure Start - https://allurereport.org/start/
+## JUnit Jupiter (JUnit 5 and 6)
+
+- 🚀 Documentation — https://allurereport.org/docs/junit5/
+- 📚 Example project — https://github.com/allure-examples?q=topic%3Ajunit5
+- ✅ Generate a project in 10 seconds via Allure Start - https://allurereport.org/start/
+- 🧩 Use `io.qameta.allure:allure-jupiter` for new setups. `allure-junit5` remains available as a deprecated compatibility alias during migration.
## Cucumber JVM
@@ -76,10 +77,72 @@ SelenideLogger.addListener("AllureSelenide", new AllureSelenide().enableLogs(Log
https://github.com/SeleniumHQ/selenium/wiki/Logging
```
-
-## Rest Assured
-
-Filter for rest-assured http client, that generates attachment for allure.
+## Playwright Java
+
+AspectJ-based integration for Playwright Java that reports browser actions as Allure steps and attaches
+Playwright screenshots automatically:
+
+```xml
+
+ io.qameta.allure
+ allure-playwright
+ $LATEST_VERSION
+
+```
+
+Enable the AspectJ weaver for automatic action steps:
+```
+-javaagent:/path/to/aspectjweaver.jar
+```
+
+Usage example with Playwright Java JUnit fixtures:
+```java
+@UsePlaywright
+class UiTest {
+
+ @Test
+ void shouldOpenPage(Page page) {
+ page.navigate("https://playwright.dev");
+ page.screenshot();
+ }
+}
+```
+
+The module registers an Allure test lifecycle listener automatically, so per-test cleanup, failure diagnostics,
+and final trace/log flush work with any test framework that reports through Allure. Playwright pages and
+contexts are tracked by the AspectJ integration when they are created or used. Use
+`AllurePlaywright.register(...)` only for pages or contexts the aspect cannot observe.
+
+Frameworks or custom runners that do not use the Allure lifecycle can call the reporting hooks directly:
+```java
+AllurePlaywright.beforeTest();
+try {
+ testBody();
+} catch (Throwable e) {
+ AllurePlaywright.afterTestFailure(e);
+ throw e;
+} finally {
+ AllurePlaywright.afterTest();
+}
+```
+
+The following defaults can be overridden in `allure.properties`:
+```
+allure.playwright.steps.enabled=true
+allure.playwright.steps.mode=actions
+allure.playwright.parameters=redacted
+allure.playwright.screenshots.attach=true
+allure.playwright.failure.screenshot=true
+allure.playwright.failure.page-source=true
+allure.playwright.close.trace=true
+allure.playwright.close.video=true
+allure.playwright.close.page-logs=true
+```
+
+
+## Rest Assured
+
+Filter for rest-assured http client, that generates attachment for allure.
```xml
@@ -95,14 +158,50 @@ Usage example:
```
You can specify custom templates, which should be placed in src/main/resources/tpl folder:
```
-.filter(new AllureRestAssured()
- .withRequestTemplate("custom-http-request.ftl")
- .withResponseTemplate("custom-http-response.ftl"))
-```
-
-## OkHttp
-
-Interceptor for OkHttp client, that generates attachment for allure.
+.filter(new AllureRestAssured()
+ .withRequestTemplate("custom-http-request.ftl")
+ .withResponseTemplate("custom-http-response.ftl"))
+```
+
+## Spring Web
+
+Interceptor for Spring synchronous HTTP clients, that generates attachments for allure.
+
+```xml
+
+ io.qameta.allure
+ allure-spring-web
+ $LATEST_VERSION
+
+```
+
+Usage example with `RestClient`:
+```
+RestClient restClient = RestClient.builder()
+ .requestFactory(new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()))
+ .requestInterceptor(new AllureRestTemplate())
+ .build();
+```
+Use a buffering request factory when the client should still be able to read the response body after Allure captures it.
+
+`RestTemplate` remains supported:
+```
+RestTemplate restTemplate = new RestTemplate(
+ new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory())
+);
+restTemplate.setInterceptors(Collections.singletonList(new AllureRestTemplate()));
+```
+
+You can specify custom templates, which should be placed in src/main/resources/tpl folder:
+```
+new AllureRestTemplate()
+ .setRequestTemplate("custom-http-request.ftl")
+ .setResponseTemplate("custom-http-response.ftl")
+```
+
+## OkHttp
+
+Interceptor for OkHttp client, that generates attachment for allure.
```xml
diff --git a/allure-assertj/src/main/java/io/qameta/allure/assertj/AllureAspectJ.java b/allure-assertj/src/main/java/io/qameta/allure/assertj/AllureAspectJ.java
index dbd92dc3..7ba05d2f 100644
--- a/allure-assertj/src/main/java/io/qameta/allure/assertj/AllureAspectJ.java
+++ b/allure-assertj/src/main/java/io/qameta/allure/assertj/AllureAspectJ.java
@@ -17,28 +17,22 @@
import io.qameta.allure.Allure;
import io.qameta.allure.AllureLifecycle;
-import io.qameta.allure.model.Status;
-import io.qameta.allure.model.StepResult;
-import io.qameta.allure.util.ObjectUtils;
import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
-import org.aspectj.lang.annotation.AfterThrowing;
+import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
-import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import org.assertj.core.api.AbstractAssert;
-import java.util.UUID;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import static io.qameta.allure.util.ResultsUtils.getStatus;
-import static io.qameta.allure.util.ResultsUtils.getStatusDetails;
+import java.util.function.Supplier;
/**
+ * Captures user-side AssertJ factories and fluent calls, then delegates assertion-chain state
+ * to {@link AssertJRecorder}.
+ *
* @author charlie (Dmitry Baev).
* @author sskorol (Sergey Korol).
*/
@@ -46,8 +40,6 @@
@Aspect
public class AllureAspectJ {
- private static final Logger LOGGER = LoggerFactory.getLogger(AllureAspectJ.class);
-
private static InheritableThreadLocal lifecycle = new InheritableThreadLocal() {
@Override
protected AllureLifecycle initialValue() {
@@ -55,64 +47,83 @@ protected AllureLifecycle initialValue() {
}
};
- @Pointcut("execution(!private org.assertj.core.api.AbstractAssert.new(..))")
- public void anyAssertCreation() {
+ private static final ThreadLocal RECORDER = ThreadLocal.withInitial(AssertJRecorder::new);
+
+ private static final ThreadLocal RECORDING_MUTED = ThreadLocal.withInitial(() -> false);
+
+ @Pointcut(
+ "("
+ + "call(public static * org.assertj.core.api.Assertions*.assertThat*(..))"
+ + " || call(public static * org.assertj.core.api.BDDAssertions*.then*(..))"
+ + " || call(public * org.assertj.core.api.*SoftAssertionsProvider+.assertThat*(..))"
+ + " || call(public * org.assertj.core.api.*SoftAssertionsProvider+.then*(..))"
+ + ")"
+ )
+ public void assertFactoryCall() {
//pointcut body, should be empty
}
- @Pointcut("execution(* org.assertj.core.api.AssertJProxySetup.*(..))")
- public void proxyMethod() {
+ @Pointcut(
+ "("
+ + "call(public * org.assertj.core.api.AbstractAssert+.*(..))"
+ + " || call(public * org.assertj.core.api.Assert+.*(..))"
+ + " || call(public * org.assertj.core.api.Descriptable+.*(..))"
+ + ")"
+ + " && target(assertion)"
+ )
+ public void assertOperationCall(final AbstractAssert, ?> assertion) {
//pointcut body, should be empty
}
- @Pointcut("execution(public * org.assertj.core.api.AbstractAssert+.*(..)) && !proxyMethod()")
- public void anyAssert() {
+ @Pointcut("!within(org.assertj..*) && !within(io.qameta.allure.assertj.AllureAspectJ)")
+ public void userCodeCall() {
//pointcut body, should be empty
}
- @After("anyAssertCreation()")
- public void logAssertCreation(final JoinPoint joinPoint) {
- final String actual = joinPoint.getArgs().length > 0
- ? ObjectUtils.toString(joinPoint.getArgs()[0])
- : ">";
- final String uuid = UUID.randomUUID().toString();
- final String name = String.format("assertThat \'%s\'", actual);
-
- final StepResult result = new StepResult()
- .setName(name)
- .setStatus(Status.PASSED);
+ @AfterReturning(
+ pointcut = "assertFactoryCall() && userCodeCall()",
+ returning = "result"
+ )
+ public void logAssertCreation(final JoinPoint joinPoint, final Object result) {
+ if (isRecordingMuted() || !(result instanceof AbstractAssert)) {
+ return;
+ }
- getLifecycle().startStep(uuid, result);
- getLifecycle().stopStep(uuid);
+ final AbstractAssert, ?> assertion = (AbstractAssert, ?>) result;
+ getRecorder().assertionCreated(getLifecycle(), assertion, firstArgumentOf(joinPoint));
}
- @Before("anyAssert()")
- public void stepStart(final JoinPoint joinPoint) {
- final MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
-
- final String uuid = UUID.randomUUID().toString();
- final String name = joinPoint.getArgs().length > 0
- ? String.format("%s \'%s\'", methodSignature.getName(), arrayToString(joinPoint.getArgs()))
- : methodSignature.getName();
-
- final StepResult result = new StepResult()
- .setName(name);
-
- getLifecycle().startStep(uuid, result);
- }
+ @Around("assertOperationCall(assertion) && userCodeCall()")
+ public Object logAssertOperation(final ProceedingJoinPoint joinPoint,
+ final AbstractAssert, ?> assertion)
+ throws Throwable {
+ final String methodName = getMethodName(joinPoint);
+ if (isRecordingMuted() || getRecorder().isIgnored(methodName)) {
+ return joinPoint.proceed();
+ }
- @AfterThrowing(pointcut = "anyAssert()", throwing = "e")
- public void stepFailed(final Throwable e) {
- getLifecycle().updateStep(s -> s
- .setStatus(getStatus(e).orElse(Status.BROKEN))
- .setStatusDetails(getStatusDetails(e).orElse(null)));
- getLifecycle().stopStep();
+ final AssertJOperation operation = getRecorder().startOperation(
+ getLifecycle(),
+ assertion,
+ methodName,
+ joinPoint.getArgs()
+ );
+ try {
+ final Object result = joinPoint.proceed();
+ getRecorder().operationPassed(operation, result);
+ return result;
+ } catch (Throwable throwable) {
+ getRecorder().operationFailed(operation, throwable);
+ throw throwable;
+ }
}
- @AfterReturning(pointcut = "anyAssert()")
- public void stepStop() {
- getLifecycle().updateStep(s -> s.setStatus(Status.PASSED));
- getLifecycle().stopStep();
+ @After(
+ "execution(public void org.assertj.core.api.DefaultAssertionErrorCollector.collectAssertionError("
+ + "java.lang.AssertionError)) && args(error)"
+ )
+ public void softAssertionFailed(final AssertionError error) {
+ getRecorder().softAssertionFailed(error);
}
/**
@@ -122,15 +133,40 @@ public void stepStop() {
*/
public static void setLifecycle(final AllureLifecycle allure) {
lifecycle.set(allure);
+ clearContext();
}
public static AllureLifecycle getLifecycle() {
return lifecycle.get();
}
- private static String arrayToString(final Object... array) {
- return Stream.of(array)
- .map(ObjectUtils::toString)
- .collect(Collectors.joining(" "));
+ public static void clearContext() {
+ RECORDER.remove();
+ }
+
+ static T withoutRecording(final Supplier supplier) {
+ final boolean previous = RECORDING_MUTED.get();
+ RECORDING_MUTED.set(true);
+ try {
+ return supplier.get();
+ } finally {
+ RECORDING_MUTED.set(previous);
+ }
+ }
+
+ private static AssertJRecorder getRecorder() {
+ return RECORDER.get();
+ }
+
+ private static boolean isRecordingMuted() {
+ return RECORDING_MUTED.get();
+ }
+
+ private static Object firstArgumentOf(final JoinPoint joinPoint) {
+ return joinPoint.getArgs().length == 0 ? null : joinPoint.getArgs()[0];
+ }
+
+ private static String getMethodName(final ProceedingJoinPoint joinPoint) {
+ return ((MethodSignature) joinPoint.getSignature()).getMethod().getName();
}
}
diff --git a/allure-assertj/src/main/java/io/qameta/allure/assertj/AssertJChain.java b/allure-assertj/src/main/java/io/qameta/allure/assertj/AssertJChain.java
new file mode 100644
index 00000000..c4d30c75
--- /dev/null
+++ b/allure-assertj/src/main/java/io/qameta/allure/assertj/AssertJChain.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2016-2026 Qameta Software Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.qameta.allure.assertj;
+
+import io.qameta.allure.model.Stage;
+import io.qameta.allure.model.Status;
+import io.qameta.allure.model.StatusDetails;
+import io.qameta.allure.model.StepResult;
+import org.assertj.core.api.AbstractAssert;
+
+import java.util.Optional;
+import java.util.UUID;
+
+/**
+ * Parent Allure step for one AssertJ assertion chain.
+ *
+ *
A chain is the stable container for all meaningful fluent operations produced by one AssertJ assertion object.
+ * {@link AssertJRecorder} creates it when user code calls an AssertJ factory such as {@code assertThat(actual)},
+ * stores it by assertion object identity, and appends one {@link AssertJOperation} child for every reported fluent
+ * call. Methods such as {@code extracting}, {@code first}, or {@code asInstanceOf} can return another assertion
+ * object, but they should still read as the same assertion story, so the returned assertion is associated with this
+ * chain instead of creating an unrelated top-level step.
This class is intentionally only a small mutable model around the retained {@link StepResult}. It owns the
+ * parent step name, status, timing, and child operation list. It does not decide which AssertJ methods are meaningful
+ * or how subjects and arguments are rendered; those decisions belong to {@link AssertJRecorder},
+ * {@link AssertJMethodSupport}, and {@link AssertJValueRenderer}.
+ */
+final class AssertJChain {
+
+ private static final String ASSERTJ_STEP_PREFIX = "assert ";
+
+ private final String uuid;
+
+ private final AbstractAssert, ?> assertion;
+
+ private final StepResult step;
+
+ AssertJChain(final AbstractAssert, ?> assertion, final String subject) {
+ this.uuid = UUID.randomUUID().toString();
+ this.assertion = assertion;
+ this.step = new StepResult()
+ .setName(chainName(subject))
+ .setStatus(Status.PASSED)
+ .setStage(Stage.FINISHED)
+ .setStart(System.currentTimeMillis())
+ .setStop(System.currentTimeMillis());
+ }
+
+ String getUuid() {
+ return uuid;
+ }
+
+ AbstractAssert, ?> getAssertion() {
+ return assertion;
+ }
+
+ StepResult getStep() {
+ return step;
+ }
+
+ void addOperation(final AssertJOperation operation) {
+ step.getSteps().add(operation.getStep());
+ }
+
+ void rename(final Optional description) {
+ description.ifPresent(value -> step.setName(chainName(value)));
+ }
+
+ void updateStatus(final Status status, final StatusDetails details) {
+ step
+ .setStatus(status)
+ .setStatusDetails(details);
+ finish();
+ }
+
+ void finish() {
+ step.setStop(System.currentTimeMillis());
+ }
+
+ private String chainName(final String subject) {
+ return AssertJValueRenderer.truncateStepName(ASSERTJ_STEP_PREFIX + subject);
+ }
+}
diff --git a/allure-assertj/src/main/java/io/qameta/allure/assertj/AssertJLifecycleListener.java b/allure-assertj/src/main/java/io/qameta/allure/assertj/AssertJLifecycleListener.java
new file mode 100644
index 00000000..c9ea7a1e
--- /dev/null
+++ b/allure-assertj/src/main/java/io/qameta/allure/assertj/AssertJLifecycleListener.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2016-2026 Qameta Software Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.qameta.allure.assertj;
+
+import io.qameta.allure.listener.FixtureLifecycleListener;
+import io.qameta.allure.listener.TestLifecycleListener;
+import io.qameta.allure.model.FixtureResult;
+import io.qameta.allure.model.TestResult;
+
+/**
+ * Clears per-thread AssertJ recorder state after Allure has finished owning the current result.
+ *
+ *
{@link AllureAspectJ} keeps an {@link AssertJRecorder} in a {@link ThreadLocal} so assertion objects can
+ * be matched by identity across later fluent calls. Test engines commonly reuse worker threads, so that
+ * thread-local map would otherwise keep old assertion objects, rendered steps, and operation stack state after
+ * the test or fixture result has already been written. The retained {@code StepResult}s are already attached to
+ * the Allure model by reference, so removing the recorder here does not remove any reported steps; it only
+ * releases per-thread bookkeeping before the next test or fixture starts on the same thread.
+ */
+public class AssertJLifecycleListener implements TestLifecycleListener, FixtureLifecycleListener {
+
+ @Override
+ public void afterTestWrite(final TestResult result) {
+ AllureAspectJ.clearContext();
+ }
+
+ @Override
+ public void afterFixtureStop(final FixtureResult result) {
+ AllureAspectJ.clearContext();
+ }
+}
diff --git a/allure-assertj/src/main/java/io/qameta/allure/assertj/AssertJMethodSupport.java b/allure-assertj/src/main/java/io/qameta/allure/assertj/AssertJMethodSupport.java
new file mode 100644
index 00000000..455b2803
--- /dev/null
+++ b/allure-assertj/src/main/java/io/qameta/allure/assertj/AssertJMethodSupport.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2016-2026 Qameta Software Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.qameta.allure.assertj;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Keeps method-name decisions out of the aspect and recorder flow.
+ */
+final class AssertJMethodSupport {
+
+ private static final String AS = "as";
+ private static final String DESCRIBED_AS = "describedAs";
+
+ private static final List IGNORED_METHODS = Arrays.asList(
+ "actual",
+ "descriptionText",
+ "equals",
+ "getWritableAssertionInfo",
+ "hashCode",
+ "toString"
+ );
+
+ private static final Set NAVIGATION_METHODS = new HashSet<>(
+ Arrays.asList(
+ "asBase64Decoded",
+ "asBoolean",
+ "asByte",
+ "asDouble",
+ "asFloat",
+ "asInstanceOf",
+ "asInt",
+ "asList",
+ "asLong",
+ "asShort",
+ "asString",
+ "bytes",
+ "decodedAsBase64",
+ "element",
+ "elements",
+ "extracting",
+ "extractingResultOf",
+ "first",
+ "flatExtracting",
+ "flatMap",
+ "last",
+ "map",
+ "rootCause",
+ "singleElement",
+ "size",
+ "usingRecursiveAssertion",
+ "usingRecursiveComparison"
+ )
+ );
+
+ private AssertJMethodSupport() {
+ throw new IllegalStateException("do not instantiate");
+ }
+
+ static boolean isIgnored(final String methodName) {
+ return IGNORED_METHODS.contains(methodName);
+ }
+
+ static String normalize(final String methodName) {
+ final int accessorIndex = methodName.indexOf("$accessor$");
+ if (accessorIndex > 0) {
+ return methodName.substring(0, accessorIndex);
+ }
+ if (DESCRIBED_AS.equals(methodName)) {
+ return AS;
+ }
+ return methodName;
+ }
+
+ static boolean isDescription(final String methodName) {
+ return AS.equals(methodName) || DESCRIBED_AS.equals(methodName);
+ }
+
+ static boolean isNavigation(final String methodName) {
+ return NAVIGATION_METHODS.contains(methodName);
+ }
+}
diff --git a/allure-assertj/src/main/java/io/qameta/allure/assertj/AssertJOperation.java b/allure-assertj/src/main/java/io/qameta/allure/assertj/AssertJOperation.java
new file mode 100644
index 00000000..f06b8be4
--- /dev/null
+++ b/allure-assertj/src/main/java/io/qameta/allure/assertj/AssertJOperation.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2016-2026 Qameta Software Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.qameta.allure.assertj;
+
+import io.qameta.allure.model.Parameter;
+import io.qameta.allure.model.Stage;
+import io.qameta.allure.model.Status;
+import io.qameta.allure.model.StatusDetails;
+import io.qameta.allure.model.StepResult;
+
+import java.util.List;
+
+import static io.qameta.allure.util.ResultsUtils.getStatus;
+import static io.qameta.allure.util.ResultsUtils.getStatusDetails;
+
+/**
+ * Child Allure step for one meaningful AssertJ fluent operation.
+ *
+ *
An operation is the report entry for one fluent method call inside an {@link AssertJChain}. The recorder creates
+ * it before proceeding with the intercepted AssertJ call, marks it passed or failed after the call returns, and keeps
+ * it attached to the chain that owns the assertion object. Earlier operations remain passed when a later operation
+ * fails, so the report shows the exact point where the assertion chain stopped matching the expectation.
+ *
+ *
For a simple assertion, each checked method becomes one operation:
For navigation methods, the operation name is enriched with the returned subject. The returned AssertJ object
+ * still belongs to the same chain, so the report stays readable as one story:
+ *
{@code
+ * assertThat(users).first(InstanceOfAssertFactories.STRING).startsWith("alice")
+ *
+ * assert 1 string
+ * first element as InstanceOfAssertFactory -> "alice@example.org"
+ * starts with "alice"
+ * }
+ *
+ *
For failures, this operation receives the failure status and status details, and the parent chain receives the
+ * same status. This makes the failed operation visible without losing the successful context before it:
Some AssertJ methods call other assertion methods internally. Those calls should not become extra child steps
+ * because they would duplicate implementation details instead of user intent. The {@code nestedLevel} counter lets the
+ * recorder reuse the active operation while those internal calls run, then finish only the user-visible operation.
+ */
+final class AssertJOperation {
+
+ private final AssertJChain chain;
+
+ private final String methodName;
+
+ private final StepResult step;
+
+ private final boolean navigation;
+
+ private String returnedSubject;
+
+ private int nestedLevel;
+
+ AssertJOperation(final AssertJChain chain,
+ final String methodName,
+ final String name,
+ final List parameters,
+ final boolean navigation) {
+ this.chain = chain;
+ this.methodName = methodName;
+ this.navigation = navigation;
+ this.step = new StepResult()
+ .setName(name)
+ .setParameters(parameters)
+ .setStage(Stage.RUNNING)
+ .setStart(System.currentTimeMillis());
+ }
+
+ AssertJChain getChain() {
+ return chain;
+ }
+
+ StepResult getStep() {
+ return step;
+ }
+
+ boolean isNavigation() {
+ return navigation;
+ }
+
+ boolean isDescription() {
+ return AssertJMethodSupport.isDescription(methodName);
+ }
+
+ boolean isNested() {
+ return nestedLevel > 0;
+ }
+
+ AssertJOperation nested() {
+ nestedLevel++;
+ return this;
+ }
+
+ void leaveNested() {
+ nestedLevel--;
+ }
+
+ void setReturnedSubject(final String subject) {
+ if (returnedSubject != null) {
+ return;
+ }
+
+ returnedSubject = subject;
+ step.setName(AssertJValueRenderer.truncateStepName(step.getName() + " -> " + subject));
+ }
+
+ void passed() {
+ if (step.getStatus() == null) {
+ step.setStatus(Status.PASSED);
+ }
+ finish();
+ }
+
+ void failed(final Throwable throwable) {
+ final Status status = getStatus(throwable).orElse(Status.BROKEN);
+ final StatusDetails details = getStatusDetails(throwable).orElse(null);
+ step
+ .setStatus(status)
+ .setStatusDetails(details);
+ chain.updateStatus(status, details);
+ finish();
+ }
+
+ private void finish() {
+ step
+ .setStage(Stage.FINISHED)
+ .setStop(System.currentTimeMillis());
+ chain.finish();
+ }
+}
diff --git a/allure-assertj/src/main/java/io/qameta/allure/assertj/AssertJRecorder.java b/allure-assertj/src/main/java/io/qameta/allure/assertj/AssertJRecorder.java
new file mode 100644
index 00000000..50bc7753
--- /dev/null
+++ b/allure-assertj/src/main/java/io/qameta/allure/assertj/AssertJRecorder.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright 2016-2026 Qameta Software Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.qameta.allure.assertj;
+
+import io.qameta.allure.AllureLifecycle;
+import org.assertj.core.api.AbstractAssert;
+
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * Records AssertJ objects by identity and builds one Allure step tree per assertion chain.
+ *
+ *
The recorder is the stateful part behind {@link AllureAspectJ}. The aspect only detects user-side AssertJ
+ * factory calls and fluent operation calls; this class decides which {@link AssertJChain} owns the assertion object,
+ * where a new {@link AssertJOperation} should be attached, and how pass/fail state should be reflected in the retained
+ * Allure {@code StepResult} tree.
+ *
+ *
Each aspect thread gets its own recorder instance. Assertion objects are tracked in an {@link IdentityHashMap}
+ * because AssertJ assertion classes can override {@code equals} and {@code hashCode}; object identity is the only safe
+ * way to know that a later fluent call belongs to the same assertion object that was created earlier.
Stored assertion instances keep separate chains because the map key is the assertion instance itself:
+ *
{@code
+ * final AbstractStringAssert> a = assertThat("alpha");
+ * final AbstractStringAssert> b = assertThat("bravo");
+ *
+ * a.isEqualTo("alpha");
+ * b.isEqualTo("bravo");
+ *
+ * assert "alpha"
+ * is equal to "alpha"
+ * assert "bravo"
+ * is equal to "bravo"
+ * }
+ *
+ *
Navigation operations such as {@code extracting}, {@code first}, and {@code asInstanceOf} may return new AssertJ
+ * assertion objects. Those returned objects are registered against the existing chain, so later checks stay under the
+ * same parent step:
The {@code operations} stack tracks the currently executing user-visible operation. It has two jobs: assertions
+ * created inside callbacks such as {@code satisfies} are attached beneath the active operation, and AssertJ internal
+ * calls on the same chain are counted as nested work instead of being reported as extra steps.
Soft assertion failures are reported before {@code assertAll()} throws. The AssertJ error collector callback calls
+ * {@link #softAssertionFailed(AssertionError)}, which marks the active operation and its chain as failed while
+ * preserving the earlier passed operations.
+ */
+final class AssertJRecorder {
+
+ private final Map, AssertJChain> chains = new IdentityHashMap<>();
+
+ private final Deque operations = new ArrayDeque<>();
+
+ private final AssertJValueRenderer renderer = new AssertJValueRenderer();
+
+ void assertionCreated(final AllureLifecycle lifecycle,
+ final AbstractAssert, ?> assertion,
+ final Object actual) {
+ if (chains.containsKey(assertion)) {
+ return;
+ }
+
+ final AssertJOperation activeOperation = activeOperation();
+ if (isNavigationResult(activeOperation)) {
+ chains.put(assertion, activeOperation.getChain());
+ return;
+ }
+
+ final AssertJChain chain = new AssertJChain(assertion, renderer.renderSubject(actual));
+ chains.put(assertion, chain);
+ attachChain(lifecycle, chain, activeOperation);
+ }
+
+ AssertJOperation startOperation(final AllureLifecycle lifecycle,
+ final AbstractAssert, ?> assertion,
+ final String methodName,
+ final Object... args) {
+ final AssertJChain chain = chainFor(lifecycle, assertion);
+ final String normalizedName = AssertJMethodSupport.normalize(methodName);
+
+ final AssertJOperation activeOperation = activeOperation();
+ if (isInternalCallOnSameChain(activeOperation, chain)) {
+ return activeOperation.nested();
+ }
+
+ final AssertJOperation operation = new AssertJOperation(
+ chain,
+ normalizedName,
+ renderer.renderOperation(normalizedName, args),
+ renderer.renderParameters(normalizedName, args),
+ AssertJMethodSupport.isNavigation(normalizedName)
+ );
+ chain.addOperation(operation);
+ operations.push(operation);
+ return operation;
+ }
+
+ void operationPassed(final AssertJOperation operation, final Object result) {
+ if (operation.isNested()) {
+ pop(operation);
+ return;
+ }
+
+ registerReturnedAssertion(operation, result);
+ renameChainFromDescription(operation);
+ operation.passed();
+ pop(operation);
+ }
+
+ void operationFailed(final AssertJOperation operation, final Throwable throwable) {
+ operation.failed(throwable);
+ pop(operation);
+ }
+
+ void softAssertionFailed(final AssertionError error) {
+ final AssertJOperation current = activeOperation();
+ if (current != null) {
+ current.failed(error);
+ }
+ }
+
+ boolean isIgnored(final String methodName) {
+ return AssertJMethodSupport.isIgnored(methodName);
+ }
+
+ private AssertJChain chainFor(final AllureLifecycle lifecycle, final AbstractAssert, ?> assertion) {
+ final AssertJChain chain = chains.get(assertion);
+ if (chain != null) {
+ return chain;
+ }
+
+ final AssertJChain created = new AssertJChain(assertion, renderer.renderSubject(actualOf(assertion)));
+ chains.put(assertion, created);
+ attachChain(lifecycle, created, activeOperation());
+ return created;
+ }
+
+ private void attachChain(final AllureLifecycle lifecycle,
+ final AssertJChain chain,
+ final AssertJOperation parentOperation) {
+ if (parentOperation == null) {
+ lifecycle.startStep(chain.getUuid(), chain.getStep());
+ lifecycle.stopStep(chain.getUuid());
+ return;
+ }
+
+ parentOperation.getStep().getSteps().add(chain.getStep());
+ }
+
+ private void registerReturnedAssertion(final AssertJOperation operation, final Object result) {
+ if (!(result instanceof AbstractAssert)) {
+ return;
+ }
+
+ final AbstractAssert, ?> returned = (AbstractAssert, ?>) result;
+ chains.put(returned, operation.getChain());
+ if (operation.isNavigation()) {
+ operation.setReturnedSubject(renderer.renderSubject(actualOf(returned)));
+ }
+ }
+
+ private void renameChainFromDescription(final AssertJOperation operation) {
+ if (operation.isDescription()) {
+ operation.getChain().rename(descriptionOf(operation.getChain().getAssertion()));
+ }
+ }
+
+ private AssertJOperation activeOperation() {
+ return operations.peek();
+ }
+
+ private boolean isNavigationResult(final AssertJOperation activeOperation) {
+ return activeOperation != null && activeOperation.isNavigation();
+ }
+
+ private boolean isInternalCallOnSameChain(final AssertJOperation activeOperation, final AssertJChain chain) {
+ return activeOperation != null && activeOperation.getChain() == chain;
+ }
+
+ private void pop(final AssertJOperation operation) {
+ if (operation.isNested()) {
+ operation.leaveNested();
+ return;
+ }
+ if (!operations.isEmpty() && operations.peek() == operation) {
+ operations.pop();
+ }
+ }
+
+ private Object actualOf(final AbstractAssert, ?> assertion) {
+ return AllureAspectJ.withoutRecording(() -> {
+ try {
+ return assertion.actual();
+ } catch (RuntimeException e) {
+ return null;
+ }
+ });
+ }
+
+ private Optional descriptionOf(final AbstractAssert, ?> assertion) {
+ return AllureAspectJ.withoutRecording(() -> {
+ try {
+ return Optional.ofNullable(assertion.descriptionText())
+ .map(String::trim)
+ .filter(value -> !value.isEmpty());
+ } catch (RuntimeException e) {
+ return Optional.empty();
+ }
+ });
+ }
+}
diff --git a/allure-assertj/src/main/java/io/qameta/allure/assertj/AssertJValueRenderer.java b/allure-assertj/src/main/java/io/qameta/allure/assertj/AssertJValueRenderer.java
new file mode 100644
index 00000000..3e33ae78
--- /dev/null
+++ b/allure-assertj/src/main/java/io/qameta/allure/assertj/AssertJValueRenderer.java
@@ -0,0 +1,558 @@
+/*
+ * Copyright 2016-2026 Qameta Software Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.qameta.allure.assertj;
+
+import io.qameta.allure.model.Parameter;
+import io.qameta.allure.util.ObjectUtils;
+import org.assertj.core.description.Description;
+import org.assertj.core.groups.Tuple;
+
+import java.lang.reflect.Array;
+import java.net.URI;
+import java.net.URL;
+import java.nio.file.Path;
+import java.time.temporal.TemporalAccessor;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import static io.qameta.allure.util.ResultsUtils.getLambdaName;
+
+/**
+ * Renders AssertJ subjects and arguments into semantic step names.
+ */
+@SuppressWarnings("all")
+final class AssertJValueRenderer {
+
+ private static final int STEP_NAME_LIMIT = 1000;
+
+ private static final int INLINE_VALUE_LIMIT = 3;
+
+ private static final String LAMBDA = "";
+
+ private static final String TRUNCATED = "...";
+
+ String renderSubject(final Object value) {
+ return truncateStepName(renderSubjectValue(value));
+ }
+
+ String renderOperation(final String methodName, final Object[] args) {
+ return truncateStepName(renderOperationName(methodName, args));
+ }
+
+ List renderParameters(final String methodName, final Object[] args) {
+ final Object[] values = parameterArguments(methodName, args);
+ if (values.length == 0) {
+ return Collections.emptyList();
+ }
+
+ final String renderedOperation = renderOperation(methodName, args);
+ final List parameters = new ArrayList<>();
+ for (int index = 0; index < values.length; index++) {
+ final String value = renderParameterValue(values[index]);
+ if (renderedOperation.contains(value)) {
+ continue;
+ }
+ parameters.add(
+ new Parameter()
+ .setName(parameterName(methodName, index))
+ .setValue(value)
+ .setMode(Parameter.Mode.DEFAULT)
+ );
+ }
+ return parameters;
+ }
+
+ static String truncateStepName(final String value) {
+ if (value == null || value.length() <= STEP_NAME_LIMIT) {
+ return value;
+ }
+ return value.substring(0, STEP_NAME_LIMIT - TRUNCATED.length()) + TRUNCATED;
+ }
+
+ private String renderSubjectValue(final Object value) {
+ if (value == null) {
+ return "null";
+ }
+ if (value instanceof CharSequence || isSimple(value)) {
+ return renderSimple(value);
+ }
+ if (value instanceof Collection) {
+ if (isInlineCollection((Collection>) value)) {
+ return renderCollectionValue((Collection>) value);
+ }
+ return renderCollectionSubject((Collection>) value);
+ }
+ if (value instanceof Map) {
+ return "map with " + renderEntryCount(((Map, ?>) value).size());
+ }
+ if (value.getClass().isArray()) {
+ return renderArraySubject(value);
+ }
+ if (value instanceof Iterable) {
+ return "iterable";
+ }
+ return simpleClassName(value);
+ }
+
+ private Object[] parameterArguments(final String methodName, final Object[] args) {
+ if (isDescriptionWithEmptyValues(args)) {
+ return new Object[]{args[0]};
+ }
+ if (isSingleVarargToUnwrap(methodName, args)) {
+ return new Object[]{Array.get(args[0], 0)};
+ }
+ return args;
+ }
+
+ private String parameterName(final String methodName, final int index) {
+ if ("hasFieldOrPropertyWithValue".equals(methodName)) {
+ return index == 0 ? "field or property" : "expected value";
+ }
+
+ if (index > 0) {
+ return "argument " + (index + 1);
+ }
+
+ switch (methodName) {
+ case "as":
+ return "description";
+ case "asInstanceOf":
+ case "first":
+ case "singleElement":
+ return "factory";
+ case "extracting":
+ case "flatExtracting":
+ return "extractor";
+ case "hasSize":
+ return "expected size";
+ case "satisfies":
+ return "condition";
+ case "contains":
+ case "containsExactly":
+ case "containsExactlyInAnyOrder":
+ case "endsWith":
+ case "isEqualTo":
+ case "startsWith":
+ return "expected";
+ default:
+ return "argument 1";
+ }
+ }
+
+ private String renderParameterValue(final Object value) {
+ return renderArgument(value);
+ }
+
+ private String renderOperationName(final String methodName, final Object[] args) {
+ if (args.length == 0) {
+ return readableMethodName(methodName);
+ }
+
+ final String arguments = renderArguments(methodName, args);
+ switch (methodName) {
+ case "as":
+ return "described as " + arguments;
+ case "asInstanceOf":
+ return "as instance of " + arguments;
+ case "contains":
+ return "contains " + arguments;
+ case "containsExactly":
+ return "contains exactly " + arguments;
+ case "containsExactlyInAnyOrder":
+ return "contains exactly in any order " + arguments;
+ case "endsWith":
+ return "ends with " + arguments;
+ case "extracting":
+ return "extracts " + arguments;
+ case "flatExtracting":
+ return "flat extracts " + arguments;
+ case "first":
+ return "first element as " + arguments;
+ case "hasFieldOrPropertyWithValue":
+ return renderHasFieldOrPropertyWithValue(args);
+ case "hasSize":
+ return "has size " + arguments;
+ case "isEqualTo":
+ return "is equal to " + arguments;
+ case "singleElement":
+ return "single element as " + arguments;
+ case "startsWith":
+ return "starts with " + arguments;
+ case "satisfies":
+ return "satisfies " + arguments;
+ default:
+ return readableMethodName(methodName) + " " + arguments;
+ }
+ }
+
+ private String renderHasFieldOrPropertyWithValue(final Object[] args) {
+ if (args.length != 2) {
+ return "has field or property with value " + renderEach(args);
+ }
+ return "has field or property " + renderArgument(args[0]) + " with value " + renderArgument(args[1]);
+ }
+
+ private String readableMethodName(final String methodName) {
+ if (methodName.startsWith("is") && methodName.length() > 2 && Character.isUpperCase(methodName.charAt(2))) {
+ return "is " + splitCamelCase(methodName.substring(2));
+ }
+ if (methodName.startsWith("has") && methodName.length() > 3 && Character.isUpperCase(methodName.charAt(3))) {
+ return "has " + splitCamelCase(methodName.substring(3));
+ }
+ return splitCamelCase(methodName);
+ }
+
+ private String splitCamelCase(final String value) {
+ return value
+ .replaceAll("([a-z0-9])([A-Z])", "$1 $2")
+ .toLowerCase();
+ }
+
+ private String renderArguments(final String methodName, final Object[] args) {
+ if (isDescriptionWithEmptyValues(args)) {
+ return renderArgument(args[0]);
+ }
+ if (isSingleVarargToUnwrap(methodName, args)) {
+ return renderArgument(Array.get(args[0], 0));
+ }
+ if (isSingleArrayArgument(args)) {
+ return renderArray(args[0]);
+ }
+ return renderEach(args);
+ }
+
+ private boolean isDescriptionWithEmptyValues(final Object[] args) {
+ return args.length == 2
+ && args[1] != null
+ && args[1].getClass().isArray()
+ && Array.getLength(args[1]) == 0;
+ }
+
+ private boolean isSingleVarargToUnwrap(final String methodName, final Object[] args) {
+ return args.length == 1
+ && args[0] != null
+ && args[0].getClass().isArray()
+ && Array.getLength(args[0]) == 1
+ && shouldUnwrapSingleVararg(methodName);
+ }
+
+ private boolean isSingleArrayArgument(final Object[] args) {
+ return args.length == 1
+ && args[0] != null
+ && args[0].getClass().isArray();
+ }
+
+ private boolean shouldUnwrapSingleVararg(final String methodName) {
+ return !methodName.contains("Any")
+ && !methodName.contains("Exactly")
+ && !methodName.contains("Only")
+ && !methodName.contains("Sequence")
+ && !methodName.contains("Subsequence")
+ && !methodName.endsWith("In");
+ }
+
+ private String renderEach(final Object[] args) {
+ final List values = new ArrayList<>();
+ for (Object arg : args) {
+ values.add(renderArgument(arg));
+ }
+ return values.stream().collect(Collectors.joining(", "));
+ }
+
+ private String renderArgument(final Object value) {
+ if (value == null) {
+ return "null";
+ }
+ if (isLambda(value)) {
+ return renderLambda(value);
+ }
+ if (value instanceof Description) {
+ return renderSimple(value.toString());
+ }
+ if (value instanceof Tuple) {
+ return renderTuple((Tuple) value);
+ }
+ if (value instanceof CharSequence || isSimple(value)) {
+ return renderSimple(value);
+ }
+ if (value instanceof Collection) {
+ if (isInlineCollection((Collection>) value)) {
+ return renderCollectionValue((Collection>) value);
+ }
+ return renderCollectionSubject((Collection>) value);
+ }
+ if (value instanceof Map) {
+ return "map with " + renderEntryCount(((Map, ?>) value).size());
+ }
+ if (value.getClass().isArray()) {
+ return renderArray(value);
+ }
+ return simpleClassName(value);
+ }
+
+ private String renderArray(final Object array) {
+ if (array instanceof byte[]) {
+ return ObjectUtils.toString(array);
+ }
+
+ final int length = Array.getLength(array);
+ if (array.getClass().getComponentType().isPrimitive()) {
+ return ObjectUtils.toString(array);
+ }
+ if (allLambdas(array, length)) {
+ return length == 1 ? renderLambda(Array.get(array, 0)) : lambdaList(array, length);
+ }
+ if (allSimple(array, length) || !array.getClass().getComponentType().isPrimitive()) {
+ return renderObjectArray(array, length);
+ }
+ return array.getClass().getComponentType().getSimpleName() + "[](length=" + length + ")";
+ }
+
+ private String renderCollectionSubject(final Collection> value) {
+ final int size = value.size();
+ if (size == 0) {
+ return "empty collection";
+ }
+ return commonElementType(value)
+ .map(type -> renderElementCount(size, type))
+ .orElseGet(() -> renderItemCount(size));
+ }
+
+ private String renderArraySubject(final Object array) {
+ final int length = Array.getLength(array);
+ if (isInlineArray(array, length)) {
+ return renderArrayValue(array, length);
+ }
+ if (array instanceof byte[]) {
+ return "byte array with " + renderByteCount(length);
+ }
+ return renderElementCount(length, array.getClass().getComponentType());
+ }
+
+ private boolean isInlineCollection(final Collection> value) {
+ return value.size() <= INLINE_VALUE_LIMIT && allInlineValues(value);
+ }
+
+ private boolean allInlineValues(final Collection> value) {
+ for (Object item : value) {
+ if (!isInlineValue(item)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean isInlineValue(final Object value) {
+ return value == null
+ || isLambda(value)
+ || value instanceof Description
+ || isInlineTuple(value)
+ || value instanceof CharSequence
+ || isSimple(value);
+ }
+
+ private boolean isInlineTuple(final Object value) {
+ if (!(value instanceof Tuple)) {
+ return false;
+ }
+ final Object[] values = ((Tuple) value).toArray();
+ return isInlineArray(values, values.length);
+ }
+
+ private String renderTuple(final Tuple tuple) {
+ final Object[] values = tuple.toArray();
+ return renderObjectArray(values, values.length)
+ .replaceFirst("^\\[", "(")
+ .replaceFirst("]$", ")");
+ }
+
+ private String renderCollectionValue(final Collection> value) {
+ final List values = new ArrayList<>();
+ for (Object item : value) {
+ values.add(renderArgument(item));
+ }
+ return values.stream().collect(Collectors.joining(", ", "[", "]"));
+ }
+
+ private boolean isInlineArray(final Object array, final int length) {
+ if (length > INLINE_VALUE_LIMIT || array instanceof byte[]) {
+ return false;
+ }
+ if (array.getClass().getComponentType().isPrimitive()) {
+ return true;
+ }
+ for (int i = 0; i < length; i++) {
+ if (!isInlineValue(Array.get(array, i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private String renderArrayValue(final Object array, final int length) {
+ if (array.getClass().getComponentType().isPrimitive()) {
+ return ObjectUtils.toString(array);
+ }
+ return renderObjectArray(array, length);
+ }
+
+ private Optional> commonElementType(final Collection> value) {
+ Class> result = null;
+ for (Object item : value) {
+ if (item == null) {
+ continue;
+ }
+ final Class> itemType = elementTypeOf(item);
+ if (result == null) {
+ result = itemType;
+ } else if (!result.equals(itemType)) {
+ return java.util.Optional.empty();
+ }
+ }
+ return java.util.Optional.ofNullable(result);
+ }
+
+ private Class> elementTypeOf(final Object item) {
+ if (item instanceof Collection) {
+ return Collection.class;
+ }
+ if (item instanceof Map) {
+ return Map.class;
+ }
+ return item.getClass();
+ }
+
+ private String renderElementCount(final int size, final Class> type) {
+ if (String.class.equals(type)) {
+ return size + " " + pluralize("string", size);
+ }
+ if (Boolean.class.equals(type) || Boolean.TYPE.equals(type)) {
+ return size + " " + pluralize("boolean", size);
+ }
+ if (Character.class.equals(type) || Character.TYPE.equals(type)) {
+ return size + " " + pluralize("character", size);
+ }
+ if (Number.class.isAssignableFrom(type) || type.isPrimitive() && !Boolean.TYPE.equals(type)
+ && !Character.TYPE.equals(type)) {
+ return size + " " + pluralize("number", size);
+ }
+ if (Collection.class.equals(type)) {
+ return size + " " + pluralize("collection", size);
+ }
+ if (Map.class.equals(type)) {
+ return size + " " + pluralize("map", size);
+ }
+ return size + " " + type.getSimpleName() + " " + pluralize("item", size);
+ }
+
+ private String renderItemCount(final int size) {
+ return size + " " + pluralize("item", size);
+ }
+
+ private String renderEntryCount(final int size) {
+ return size + " " + pluralize("entry", size);
+ }
+
+ private String renderByteCount(final int size) {
+ return size + " " + pluralize("byte", size);
+ }
+
+ private String pluralize(final String word, final int count) {
+ return count == 1 ? word : word + "s";
+ }
+
+ private String renderObjectArray(final Object array, final int length) {
+ final List values = new ArrayList<>();
+ for (int i = 0; i < length; i++) {
+ values.add(renderArgument(Array.get(array, i)));
+ }
+ return values.stream().collect(Collectors.joining(", ", "[", "]"));
+ }
+
+ private boolean allLambdas(final Object array, final int length) {
+ if (length == 0) {
+ return false;
+ }
+ for (int i = 0; i < length; i++) {
+ if (!isLambda(Array.get(array, i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private String lambdaList(final Object array, final int length) {
+ final List values = new ArrayList<>();
+ for (int i = 0; i < length; i++) {
+ values.add(renderLambda(Array.get(array, i)));
+ }
+ return values.stream().collect(Collectors.joining(", ", "[", "]"));
+ }
+
+ private boolean allSimple(final Object array, final int length) {
+ for (int i = 0; i < length; i++) {
+ final Object item = Array.get(array, i);
+ if (item != null && !isSimple(item) && !(item instanceof CharSequence)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean isSimple(final Object value) {
+ return value instanceof Number
+ || value instanceof Boolean
+ || value instanceof Character
+ || value instanceof Enum
+ || value instanceof Path
+ || value instanceof URI
+ || value instanceof URL
+ || value instanceof TemporalAccessor;
+ }
+
+ private boolean isLambda(final Object value) {
+ final Class> type = value.getClass();
+ return type.isSynthetic() || type.getName().contains("$$Lambda$");
+ }
+
+ private String renderLambda(final Object value) {
+ return getLambdaName(value)
+ .orElse(LAMBDA);
+ }
+
+ private String renderSimple(final Object value) {
+ if (value instanceof CharSequence || value instanceof Character) {
+ return "\"" + ObjectUtils.toString(value) + "\"";
+ }
+ if (value instanceof Enum) {
+ return ((Enum>) value).name();
+ }
+ return ObjectUtils.toString(value);
+ }
+
+ private String simpleClassName(final Object value) {
+ final Class> type = value.getClass();
+ if (type.isAnonymousClass()) {
+ return type.getSuperclass().getSimpleName();
+ }
+ return type.getSimpleName();
+ }
+}
diff --git a/allure-assertj/src/main/resources/META-INF/services/io.qameta.allure.listener.FixtureLifecycleListener b/allure-assertj/src/main/resources/META-INF/services/io.qameta.allure.listener.FixtureLifecycleListener
new file mode 100644
index 00000000..65aaf3ec
--- /dev/null
+++ b/allure-assertj/src/main/resources/META-INF/services/io.qameta.allure.listener.FixtureLifecycleListener
@@ -0,0 +1 @@
+io.qameta.allure.assertj.AssertJLifecycleListener
diff --git a/allure-assertj/src/main/resources/META-INF/services/io.qameta.allure.listener.TestLifecycleListener b/allure-assertj/src/main/resources/META-INF/services/io.qameta.allure.listener.TestLifecycleListener
new file mode 100644
index 00000000..65aaf3ec
--- /dev/null
+++ b/allure-assertj/src/main/resources/META-INF/services/io.qameta.allure.listener.TestLifecycleListener
@@ -0,0 +1 @@
+io.qameta.allure.assertj.AssertJLifecycleListener
diff --git a/allure-assertj/src/test/java/io/qameta/allure/assertj/AllureAspectJTest.java b/allure-assertj/src/test/java/io/qameta/allure/assertj/AllureAspectJTest.java
index 957657b1..71e14e7c 100644
--- a/allure-assertj/src/test/java/io/qameta/allure/assertj/AllureAspectJTest.java
+++ b/allure-assertj/src/test/java/io/qameta/allure/assertj/AllureAspectJTest.java
@@ -15,18 +15,27 @@
*/
package io.qameta.allure.assertj;
+import io.qameta.allure.model.Parameter;
+import io.qameta.allure.model.Status;
+import io.qameta.allure.model.StatusDetails;
import io.qameta.allure.model.StepResult;
import io.qameta.allure.model.TestResult;
import io.qameta.allure.test.AllureFeatures;
import io.qameta.allure.test.AllureResults;
+import org.assertj.core.api.AbstractStringAssert;
+import org.assertj.core.api.InstanceOfAssertFactories;
import org.assertj.core.api.SoftAssertions;
import org.junit.jupiter.api.Test;
+import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.function.Function;
import static io.qameta.allure.test.RunUtils.runWithinTestContext;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.tuple;
/**
* @author charlie (Dmitry Baev).
@@ -35,90 +44,461 @@ class AllureAspectJTest {
@AllureFeatures.Steps
@Test
- void shouldCreateStepsForAsserts() {
+ void shouldCreateSemanticChainForScalarAssert() {
final AllureResults results = runWithinTestContext(() -> {
assertThat("Data")
.hasSize(4);
}, AllureAspectJ::setLifecycle);
- assertThat(results.getTestResults())
- .flatExtracting(TestResult::getSteps)
+
+ final TestResult result = assertOnlyOneResult(results);
+ assertThat(result.getSteps())
+ .extracting(StepResult::getName, StepResult::getStatus)
+ .containsExactly(tuple("assert \"Data\"", Status.PASSED));
+ assertThat(result.getSteps())
+ .flatExtracting(StepResult::getSteps)
.extracting(StepResult::getName)
- .containsExactly(
- "assertThat 'Data'",
- "hasSize '4'"
- );
+ .containsExactly("has size 4");
+ assertThat(result.getSteps())
+ .flatExtracting(StepResult::getSteps)
+ .flatExtracting(StepResult::getParameters)
+ .isEmpty();
}
@AllureFeatures.Steps
@Test
- void shouldHandleNullableObject() {
+ void shouldUseAssertDescriptionAsChainName() {
final AllureResults results = runWithinTestContext(() -> {
assertThat((Object) null)
.as("Nullable object")
.isNull();
}, AllureAspectJ::setLifecycle);
- assertThat(results.getTestResults())
- .flatExtracting(TestResult::getSteps)
+ final TestResult result = assertOnlyOneResult(results);
+ assertThat(result.getSteps())
+ .extracting(StepResult::getName)
+ .containsExactly("assert Nullable object");
+ assertThat(result.getSteps())
+ .flatExtracting(StepResult::getSteps)
+ .extracting(StepResult::getName)
+ .containsExactly("described as \"Nullable object\"", "is null");
+ }
+
+ @AllureFeatures.Steps
+ @Test
+ void shouldRenderByteArraysWithoutPayload() {
+ final String value = "some string";
+ final AllureResults results = runWithinTestContext(() -> {
+ assertThat(value.getBytes(StandardCharsets.UTF_8))
+ .as("Byte array object")
+ .isEqualTo(value.getBytes(StandardCharsets.UTF_8));
+ }, AllureAspectJ::setLifecycle);
+
+ final TestResult result = assertOnlyOneResult(results);
+ assertThat(result.getSteps())
+ .extracting(StepResult::getName)
+ .containsExactly("assert Byte array object");
+ assertThat(result.getSteps())
+ .flatExtracting(StepResult::getSteps)
+ .extracting(StepResult::getName)
+ .containsExactly("described as \"Byte array object\"", "is equal to ");
+ }
+
+ @AllureFeatures.Steps
+ @Test
+ void shouldRenderCollectionsAsSubjectsAndExpectedValuesAsValues() {
+ final AllureResults results = runWithinTestContext(() -> {
+ assertThat(Arrays.asList("a", "b"))
+ .containsExactly("a", "b");
+ }, AllureAspectJ::setLifecycle);
+
+ final TestResult result = assertOnlyOneResult(results);
+ assertThat(result.getSteps())
+ .extracting(StepResult::getName)
+ .containsExactly("assert [\"a\", \"b\"]");
+ assertThat(result.getSteps())
+ .flatExtracting(StepResult::getSteps)
+ .extracting(StepResult::getName)
+ .containsExactly("contains exactly [\"a\", \"b\"]");
+ assertThat(result.getSteps())
+ .flatExtracting(StepResult::getSteps)
+ .flatExtracting(StepResult::getParameters)
+ .isEmpty();
+ }
+
+ @AllureFeatures.Steps
+ @Test
+ void shouldRenderSmallArraysAsValues() {
+ final AllureResults results = runWithinTestContext(() -> {
+ assertThat(new int[]{1, 2})
+ .containsExactly(1, 2);
+
+ assertThat(new String[]{"alpha", "bravo"})
+ .containsExactly("alpha", "bravo");
+ }, AllureAspectJ::setLifecycle);
+
+ final TestResult result = assertOnlyOneResult(results);
+ assertThat(result.getSteps())
+ .extracting(StepResult::getName)
+ .containsExactly("assert [1, 2]", "assert [\"alpha\", \"bravo\"]");
+ assertThat(result.getSteps())
+ .flatExtracting(StepResult::getSteps)
+ .extracting(StepResult::getName)
+ .containsExactly("contains exactly [1, 2]", "contains exactly [\"alpha\", \"bravo\"]");
+ }
+
+ @AllureFeatures.Steps
+ @Test
+ void shouldRenderTuplesAsValues() {
+ final AllureResults results = runWithinTestContext(() -> {
+ assertThat(
+ Arrays.asList(
+ tuple("first", Status.PASSED),
+ tuple("second", Status.FAILED)
+ )
+ )
+ .containsExactly(
+ tuple("first", Status.PASSED),
+ tuple("second", Status.FAILED)
+ );
+ }, AllureAspectJ::setLifecycle);
+
+ final TestResult result = assertOnlyOneResult(results);
+ assertThat(result.getSteps())
+ .extracting(StepResult::getName)
+ .containsExactly("assert [(\"first\", PASSED), (\"second\", FAILED)]");
+ assertThat(result.getSteps())
+ .flatExtracting(StepResult::getSteps)
+ .extracting(StepResult::getName)
+ .containsExactly("contains exactly [(\"first\", PASSED), (\"second\", FAILED)]");
+ assertThat(result.getSteps())
+ .flatExtracting(StepResult::getSteps)
+ .flatExtracting(StepResult::getParameters)
+ .isEmpty();
+ }
+
+ @AllureFeatures.Steps
+ @Test
+ void shouldRenderFieldOrPropertyValueAssertions() {
+ final StatusDetails details = new StatusDetails()
+ .setMessage("Make the test failed");
+
+ final AllureResults results = runWithinTestContext(() -> {
+ assertThat(details)
+ .hasFieldOrPropertyWithValue("message", "Make the test failed");
+ }, AllureAspectJ::setLifecycle);
+
+ final TestResult result = assertOnlyOneResult(results);
+ assertThat(result.getSteps())
+ .extracting(StepResult::getName)
+ .containsExactly("assert StatusDetails");
+ assertThat(result.getSteps())
+ .flatExtracting(StepResult::getSteps)
+ .extracting(StepResult::getName)
+ .containsExactly("has field or property \"message\" with value \"Make the test failed\"");
+ assertThat(result.getSteps())
+ .flatExtracting(StepResult::getSteps)
+ .flatExtracting(StepResult::getParameters)
+ .isEmpty();
+ }
+
+ @AllureFeatures.Steps
+ @Test
+ void shouldTruncateLongStepNamesAndAddOnlyTruncatedValuesAsParameters() {
+ final String value = String.join("", Collections.nCopies(1200, "a"));
+
+ final AllureResults results = runWithinTestContext(() -> {
+ assertThat(value)
+ .isEqualTo(value);
+ }, AllureAspectJ::setLifecycle);
+
+ final TestResult result = assertOnlyOneResult(results);
+ assertThat(result.getSteps())
+ .extracting(StepResult::getName)
+ .singleElement()
+ .asString()
+ .hasSize(1000)
+ .startsWith("assert \"")
+ .endsWith("...");
+ assertThat(result.getSteps())
+ .flatExtracting(StepResult::getSteps)
+ .extracting(StepResult::getName)
+ .singleElement()
+ .asString()
+ .hasSize(1000)
+ .startsWith("is equal to \"")
+ .endsWith("...");
+ assertThat(result.getSteps())
+ .flatExtracting(StepResult::getSteps)
+ .flatExtracting(StepResult::getParameters)
+ .extracting(Parameter::getName, Parameter::getValue)
+ .containsExactly(tuple("expected", "\"" + value + "\""));
+ }
+
+ @AllureFeatures.Steps
+ @Test
+ void shouldCreateSeparateChainsForMultipleAssertThatCalls() {
+ final AllureResults results = runWithinTestContext(() -> {
+ assertThat("Data")
+ .hasSize(4);
+
+ assertThat(42)
+ .isPositive()
+ .isEqualTo(42);
+
+ assertThat(Arrays.asList("a", "b"))
+ .hasSize(2)
+ .contains("a");
+ }, AllureAspectJ::setLifecycle);
+
+ final TestResult result = assertOnlyOneResult(results);
+ assertThat(result.getSteps())
+ .extracting(StepResult::getName, StepResult::getStatus)
+ .containsExactly(
+ tuple("assert \"Data\"", Status.PASSED),
+ tuple("assert 42", Status.PASSED),
+ tuple("assert [\"a\", \"b\"]", Status.PASSED)
+ );
+ assertThat(result.getSteps())
+ .flatExtracting(StepResult::getSteps)
.extracting(StepResult::getName)
.containsExactly(
- "assertThat 'null'",
- "as 'Nullable object []'",
- "isNull"
+ "has size 4",
+ "is positive",
+ "is equal to 42",
+ "has size 2",
+ "contains \"a\""
);
}
@AllureFeatures.Steps
@Test
- void shouldHandleByteArrayObject() {
- final String s = "some string";
+ void shouldAttachOperationsToStoredAssertionInstances() {
+ final String targetA = "alpha";
+ final String targetB = "bravo";
+
final AllureResults results = runWithinTestContext(() -> {
- assertThat(s.getBytes(StandardCharsets.UTF_8))
- .as("Byte array object")
- .isEqualTo(s.getBytes(StandardCharsets.UTF_8));
+ final AbstractStringAssert> a = assertThat(targetA);
+ final AbstractStringAssert> b = assertThat(targetB);
+
+ a.isEqualTo("alpha");
+ b.isEqualTo("bravo");
+ }, AllureAspectJ::setLifecycle);
+
+ final TestResult result = assertOnlyOneResult(results);
+ assertThat(result.getSteps())
+ .extracting(StepResult::getName, StepResult::getStatus)
+ .containsExactly(
+ tuple("assert \"alpha\"", Status.PASSED),
+ tuple("assert \"bravo\"", Status.PASSED)
+ );
+ assertThat(result.getSteps())
+ .filteredOn("name", "assert \"alpha\"")
+ .flatExtracting(StepResult::getSteps)
+ .extracting(StepResult::getName)
+ .containsExactly("is equal to \"alpha\"");
+ assertThat(result.getSteps())
+ .filteredOn("name", "assert \"bravo\"")
+ .flatExtracting(StepResult::getSteps)
+ .extracting(StepResult::getName)
+ .containsExactly("is equal to \"bravo\"");
+ }
+
+ @AllureFeatures.Steps
+ @Test
+ void shouldAvoidVerboseModelToStringPayloads() {
+ final TestResult model = new TestResult()
+ .setUuid("uid")
+ .setName("testPassed")
+ .setFullName("other.PassingTest.testPassed");
+
+ final AllureResults results = runWithinTestContext(() -> {
+ assertThat(Collections.singletonList(model))
+ .hasSize(1)
+ .containsExactly(model);
+ }, AllureAspectJ::setLifecycle);
+
+ final TestResult result = assertOnlyOneResult(results);
+ assertThat(result.getSteps())
+ .extracting(StepResult::getName)
+ .containsExactly("assert 1 TestResult item");
+ assertThat(result.getSteps())
+ .flatExtracting(StepResult::getSteps)
+ .extracting(StepResult::getName)
+ .containsExactly("has size 1", "contains exactly [TestResult]");
+ assertThat(result.getSteps())
+ .extracting(StepResult::getName)
+ .noneMatch(name -> name.contains("fullName="))
+ .noneMatch(name -> name.contains("other.PassingTest.testPassed"));
+ assertThat(result.getSteps())
+ .flatExtracting(StepResult::getSteps)
+ .extracting(StepResult::getName)
+ .noneMatch(name -> name.contains("fullName="))
+ .noneMatch(name -> name.contains("other.PassingTest.testPassed"));
+ }
+
+ @AllureFeatures.Steps
+ @Test
+ void shouldKeepNavigationInsideTheSameChain() {
+ final TestResult model = new TestResult()
+ .setFullName("my.company.Test.testOne");
+
+ final AllureResults results = runWithinTestContext(() -> {
+ assertThat(Collections.singletonList(model))
+ .extracting(TestResult::getFullName)
+ .containsExactly("my.company.Test.testOne");
+
+ assertThat(Collections.singletonList("alpha"))
+ .first(InstanceOfAssertFactories.STRING)
+ .startsWith("al");
+
+ assertThat(Collections.singletonList("bravo"))
+ .singleElement(InstanceOfAssertFactories.STRING)
+ .endsWith("vo");
+
+ assertThat((Object) "charlie")
+ .asInstanceOf(InstanceOfAssertFactories.STRING)
+ .contains("har");
+
+ assertThat(Collections.singletonList(Collections.singletonList("delta")))
+ .flatExtracting(value -> value)
+ .containsExactly("delta");
}, AllureAspectJ::setLifecycle);
- assertThat(results.getTestResults())
- .flatExtracting(TestResult::getSteps)
+ final TestResult result = assertOnlyOneResult(results);
+ assertThat(result.getSteps())
+ .hasSize(5);
+ assertThat(result.getSteps())
+ .flatExtracting(StepResult::getSteps)
.extracting(StepResult::getName)
.containsExactly(
- "assertThat ''",
- "describedAs 'Byte array object'",
- "isEqualTo ''"
+ "extracts -> [\"my.company.Test.testOne\"]",
+ "contains exactly [\"my.company.Test.testOne\"]",
+ "first element as InstanceOfAssertFactory -> \"alpha\"",
+ "starts with \"al\"",
+ "single element as InstanceOfAssertFactory -> \"bravo\"",
+ "ends with \"vo\"",
+ "as instance of InstanceOfAssertFactory -> \"charlie\"",
+ "contains \"har\"",
+ "flat extracts -> [\"delta\"]",
+ "contains exactly [\"delta\"]"
);
}
@AllureFeatures.Steps
@Test
- void shouldHandleCollections() {
+ void shouldRenderSerializedLambdaMethodReferences() {
+ final TestResult model = new TestResult()
+ .setFullName("my.company.Test.testOne");
+
final AllureResults results = runWithinTestContext(() -> {
- assertThat(Arrays.asList("a", "b"))
- .containsExactly("a", "b");
+ assertThat(Collections.singletonList(model))
+ .extracting((Function & Serializable) TestResult::getFullName)
+ .containsExactly("my.company.Test.testOne");
+ }, AllureAspectJ::setLifecycle);
+
+ final TestResult result = assertOnlyOneResult(results);
+ assertThat(result.getSteps())
+ .flatExtracting(StepResult::getSteps)
+ .extracting(StepResult::getName)
+ .containsExactly(
+ "extracts TestResult::getFullName -> [\"my.company.Test.testOne\"]",
+ "contains exactly [\"my.company.Test.testOne\"]"
+ );
+ }
+
+ @AllureFeatures.Steps
+ @Test
+ void shouldMarkTheFailedHardAssertionOperation() {
+ final AllureResults results = runWithinTestContext(() -> {
+ assertThat("Data")
+ .hasSize(5);
}, AllureAspectJ::setLifecycle);
- assertThat(results.getTestResults())
- .flatExtracting(TestResult::getSteps)
- .extracting(StepResult::getName)
- .containsExactly(
- "assertThatList '[a, b]'",
- "containsExactly '[a, b]'"
- );
+ final TestResult result = assertOnlyOneResult(results);
+ assertThat(result.getSteps())
+ .extracting(StepResult::getName, StepResult::getStatus)
+ .containsExactly(tuple("assert \"Data\"", Status.FAILED));
+ assertThat(result.getSteps())
+ .flatExtracting(StepResult::getSteps)
+ .extracting(StepResult::getName, StepResult::getStatus)
+ .containsExactly(tuple("has size 5", Status.FAILED));
+ assertThat(result.getSteps())
+ .flatExtracting(StepResult::getSteps)
+ .filteredOn("name", "has size 5")
+ .extracting(step -> step.getStatusDetails().getMessage())
+ .singleElement()
+ .asString()
+ .contains("size");
}
@AllureFeatures.Steps
@Test
- void softAssertions() {
+ void shouldMarkTheFailedSoftAssertionOperationBeforeAssertAll() {
final AllureResults results = runWithinTestContext(() -> {
final SoftAssertions soft = new SoftAssertions();
soft.assertThat(25)
- .as("Test description")
+ .as("Age")
.isEqualTo(26);
soft.assertAll();
}, AllureAspectJ::setLifecycle);
- assertThat(results.getTestResults())
- .flatExtracting(TestResult::getSteps)
+ final TestResult result = assertOnlyOneResult(results);
+ assertThat(result.getSteps())
+ .extracting(StepResult::getName, StepResult::getStatus)
+ .containsExactly(tuple("assert Age", Status.FAILED));
+ assertThat(result.getSteps())
+ .flatExtracting(StepResult::getSteps)
+ .extracting(StepResult::getName, StepResult::getStatus)
+ .containsExactly(
+ tuple("described as \"Age\"", Status.PASSED),
+ tuple("is equal to 26", Status.FAILED)
+ );
+ assertThat(result.getSteps())
+ .flatExtracting(StepResult::getSteps)
+ .filteredOn("name", "is equal to 26")
+ .extracting(step -> step.getStatusDetails().getMessage())
+ .singleElement()
+ .asString()
+ .contains("expected: 26");
+ }
+
+ @AllureFeatures.Steps
+ @Test
+ void shouldAttachNestedAssertionsUnderCallbackOperations() {
+ final AllureResults results = runWithinTestContext(() -> {
+ assertThat("alpha")
+ .satisfies(
+ value -> assertThat(value)
+ .startsWith("al")
+ .endsWith("ha")
+ );
+ }, AllureAspectJ::setLifecycle);
+
+ final TestResult result = assertOnlyOneResult(results);
+ assertThat(result.getSteps())
+ .extracting(StepResult::getName)
+ .containsExactly("assert \"alpha\"");
+ assertThat(result.getSteps())
+ .flatExtracting(StepResult::getSteps)
.extracting(StepResult::getName)
- .contains("as 'Test description []'", "isEqualTo '26'");
+ .containsExactly("satisfies ");
+ assertThat(result.getSteps())
+ .flatExtracting(StepResult::getSteps)
+ .filteredOn("name", "satisfies ")
+ .flatExtracting(StepResult::getSteps)
+ .extracting(StepResult::getName)
+ .containsExactly("assert \"alpha\"");
+ assertThat(result.getSteps())
+ .flatExtracting(StepResult::getSteps)
+ .filteredOn("name", "satisfies ")
+ .flatExtracting(StepResult::getSteps)
+ .flatExtracting(StepResult::getSteps)
+ .extracting(StepResult::getName)
+ .containsExactly("starts with \"al\"", "ends with \"ha\"");
+ }
+
+ private TestResult assertOnlyOneResult(final AllureResults results) {
+ assertThat(results.getTestResults()).hasSize(1);
+ return results.getTestResults().get(0);
}
}
diff --git a/allure-assertj/src/test/resources/allure.properties b/allure-assertj/src/test/resources/allure.properties
index 9c0b0a2d..c881472e 100644
--- a/allure-assertj/src/test/resources/allure.properties
+++ b/allure-assertj/src/test/resources/allure.properties
@@ -1,2 +1,3 @@
allure.results.directory=build/allure-results
allure.label.epic=#project.description#
+allure.label.module=allure-assertj
diff --git a/allure-attachments/src/main/java/io/qameta/allure/attachment/AttachmentRenderer.java b/allure-attachments/src/main/java/io/qameta/allure/attachment/AttachmentRenderer.java
index 15423fc8..44029cc8 100644
--- a/allure-attachments/src/main/java/io/qameta/allure/attachment/AttachmentRenderer.java
+++ b/allure-attachments/src/main/java/io/qameta/allure/attachment/AttachmentRenderer.java
@@ -19,7 +19,6 @@
* @param the type of attachment data
* @author charlie (Dmitry Baev).
*/
-@SuppressWarnings("PMD.AvoidUncheckedExceptionsInSignatures")
@FunctionalInterface
public interface AttachmentRenderer {
diff --git a/allure-attachments/src/test/java/io/qameta/allure/attachment/FreemarkerAttachmentRendererTest.java b/allure-attachments/src/test/java/io/qameta/allure/attachment/FreemarkerAttachmentRendererTest.java
index 4f0c235e..3c37c53a 100644
--- a/allure-attachments/src/test/java/io/qameta/allure/attachment/FreemarkerAttachmentRendererTest.java
+++ b/allure-attachments/src/test/java/io/qameta/allure/attachment/FreemarkerAttachmentRendererTest.java
@@ -35,7 +35,6 @@ class FreemarkerAttachmentRendererTest {
private static final String FILE_EXTENSION = "fileExtension";
private static final String HTML = ".html";
-
@AllureFeatures.Attachments
@Test
void shouldRenderRequestAttachment() {
diff --git a/allure-attachments/src/test/resources/allure.properties b/allure-attachments/src/test/resources/allure.properties
index 9c0b0a2d..b47a01f6 100644
--- a/allure-attachments/src/test/resources/allure.properties
+++ b/allure-attachments/src/test/resources/allure.properties
@@ -1,2 +1,3 @@
allure.results.directory=build/allure-results
allure.label.epic=#project.description#
+allure.label.module=allure-attachments
diff --git a/allure-awaitility/src/main/java/io/qameta/allure/awaitility/AllureAwaitilityListener.java b/allure-awaitility/src/main/java/io/qameta/allure/awaitility/AllureAwaitilityListener.java
index 451e3fd9..a19e5e31 100644
--- a/allure-awaitility/src/main/java/io/qameta/allure/awaitility/AllureAwaitilityListener.java
+++ b/allure-awaitility/src/main/java/io/qameta/allure/awaitility/AllureAwaitilityListener.java
@@ -77,8 +77,7 @@ public class AllureAwaitilityListener implements ConditionEvaluationListener LIFECYCLE
- = new InheritableThreadLocal() {
+ private static final InheritableThreadLocal LIFECYCLE = new InheritableThreadLocal() {
@Override
protected AllureLifecycle initialValue() {
return Allure.getLifecycle();
@@ -225,7 +224,8 @@ public void exceptionIgnored(final IgnoredException ignoredException) {
getLifecycle().updateStep(awaitilityCondition -> {
final String currentExceptionIgnoredStepUUID = UUID.randomUUID().toString();
final String message = String.format(
- onExceptionStepTextPattern, ignoredException.getThrowable().getMessage());
+ onExceptionStepTextPattern, ignoredException.getThrowable().getMessage()
+ );
final StringWriter stringWriter = new StringWriter();
ignoredException.getThrowable().printStackTrace(new PrintWriter(stringWriter));
final String stackTrace = stringWriter.toString();
@@ -239,7 +239,8 @@ public void exceptionIgnored(final IgnoredException ignoredException) {
);
getLifecycle().addAttachment(
ignoredException.getThrowable().getMessage(), "text/plain", ".txt",
- stackTrace.getBytes(StandardCharsets.UTF_8));
+ stackTrace.getBytes(StandardCharsets.UTF_8)
+ );
getLifecycle().stopStep(currentExceptionIgnoredStepUUID);
});
}
diff --git a/allure-awaitility/src/test/java/io/qameta/allure/awaitility/ConditionListenersPositiveTest.java b/allure-awaitility/src/test/java/io/qameta/allure/awaitility/ConditionListenersPositiveTest.java
index 59207847..9eb6d7d6 100644
--- a/allure-awaitility/src/test/java/io/qameta/allure/awaitility/ConditionListenersPositiveTest.java
+++ b/allure-awaitility/src/test/java/io/qameta/allure/awaitility/ConditionListenersPositiveTest.java
@@ -58,27 +58,27 @@ static void setup() {
@TestFactory
Stream globalSettingsAwaitWoAliasCheckTopLevelPassedStep() {
final List testResult = runWithinTestContext(() -> {
- final AtomicInteger atomicInteger = new AtomicInteger(0);
- await().with()
- .conditionEvaluationListener(new AllureAwaitilityListener())
- .atMost(Duration.of(1000, ChronoUnit.MILLIS))
- .pollInterval(Duration.of(50, ChronoUnit.MILLIS))
- .until(atomicInteger::getAndIncrement, is(3));
- },
+ final AtomicInteger atomicInteger = new AtomicInteger(0);
+ await().with()
+ .conditionEvaluationListener(new AllureAwaitilityListener())
+ .atMost(Duration.of(1000, ChronoUnit.MILLIS))
+ .pollInterval(Duration.of(50, ChronoUnit.MILLIS))
+ .until(atomicInteger::getAndIncrement, is(3));
+ },
AllureAwaitilityListener::setLifecycle
).getTestResults();
return Stream.of(
- DynamicTest.dynamicTest("Exactly 1 top level step for 1 awaitility condition", () ->
- assertThat(testResult.get(0).getSteps())
+ DynamicTest.dynamicTest(
+ "Exactly 1 top level step for 1 awaitility condition", () -> assertThat(testResult.get(0).getSteps())
.hasSize(1)
),
- DynamicTest.dynamicTest("Top level step has passed status", () ->
- assertThat(testResult.get(0).getSteps())
+ DynamicTest.dynamicTest(
+ "Top level step has passed status", () -> assertThat(testResult.get(0).getSteps())
.allMatch(step -> Status.PASSED.equals(step.getStatus()))
),
- DynamicTest.dynamicTest("Top level step has default name because await() wo alias", () ->
- assertThat(testResult.get(0).getSteps())
+ DynamicTest.dynamicTest(
+ "Top level step has default name because await() wo alias", () -> assertThat(testResult.get(0).getSteps())
.extracting(StepResult::getName)
.containsExactly("Awaitility: Starting evaluation")
)
@@ -96,13 +96,13 @@ Stream globalSettingsAwaitWoAliasCheckTopLevelPassedStep() {
@Test
void globalSettingsAwaitWithAliasCheckTopLevelPassedStep() {
final List testResult = runWithinTestContext(() -> {
- final AtomicInteger atomicInteger = new AtomicInteger(0);
- await("Counter should be at least 3").with()
- .conditionEvaluationListener(new AllureAwaitilityListener())
- .atMost(Duration.of(1000, ChronoUnit.MILLIS))
- .pollInterval(Duration.of(50, ChronoUnit.MILLIS))
- .until(atomicInteger::getAndIncrement, is(3));
- },
+ final AtomicInteger atomicInteger = new AtomicInteger(0);
+ await("Counter should be at least 3").with()
+ .conditionEvaluationListener(new AllureAwaitilityListener())
+ .atMost(Duration.of(1000, ChronoUnit.MILLIS))
+ .pollInterval(Duration.of(50, ChronoUnit.MILLIS))
+ .until(atomicInteger::getAndIncrement, is(3));
+ },
AllureAwaitilityListener::setLifecycle
).getTestResults();
assertEquals(
@@ -125,51 +125,51 @@ void globalSettingsAwaitWithAliasCheckTopLevelPassedStep() {
@TestFactory
Stream globalSettingsCheckAwaitWoAliasSecondLevelPassedSteps() {
final List testResult = runWithinTestContext(() -> {
- final AtomicInteger atomicInteger = new AtomicInteger(0);
- await().with()
- .conditionEvaluationListener(new AllureAwaitilityListener())
- .atMost(Duration.of(1000, ChronoUnit.MILLIS))
- .pollInterval(Duration.of(50, ChronoUnit.MILLIS))
- .until(atomicInteger::getAndIncrement, is(3));
- },
+ final AtomicInteger atomicInteger = new AtomicInteger(0);
+ await().with()
+ .conditionEvaluationListener(new AllureAwaitilityListener())
+ .atMost(Duration.of(1000, ChronoUnit.MILLIS))
+ .pollInterval(Duration.of(50, ChronoUnit.MILLIS))
+ .until(atomicInteger::getAndIncrement, is(3));
+ },
AllureAwaitilityListener::setLifecycle
).getTestResults();
return Stream.of(
- dynamicTest("Exactly 4 second level steps for 4 polling iterations", () ->
- assertThat(testResult.get(0).getSteps().get(0).getSteps())
+ dynamicTest(
+ "Exactly 4 second level steps for 4 polling iterations", () -> assertThat(testResult.get(0).getSteps().get(0).getSteps())
.hasSize(4)
),
- dynamicTest("All second level steps has passed statuses", () ->
- assertThat(testResult.get(0).getSteps().get(0).getSteps())
+ dynamicTest(
+ "All second level steps has passed statuses", () -> assertThat(testResult.get(0).getSteps().get(0).getSteps())
.allMatch(x -> x.getStatus().equals(Status.PASSED))
),
- dynamicTest("Second level step 1 name", () ->
- assertThat(testResult.get(0).getSteps().get(0).getSteps().get(0).getName())
+ dynamicTest(
+ "Second level step 1 name", () -> assertThat(testResult.get(0).getSteps().get(0).getSteps().get(0).getName())
.contains("io.qameta.allure.awaitility.ConditionListenersPositiveTest")
.contains("expected <3> but was <0>")
.contains("elapsed time")
.contains("remaining time")
.contains("last poll interval was")
),
- dynamicTest("Second level step 2 name", () ->
- assertThat(testResult.get(0).getSteps().get(0).getSteps().get(1).getName())
+ dynamicTest(
+ "Second level step 2 name", () -> assertThat(testResult.get(0).getSteps().get(0).getSteps().get(1).getName())
.contains("io.qameta.allure.awaitility.ConditionListenersPositiveTest")
.contains("expected <3> but was <1>")
.contains("elapsed time")
.contains("remaining time")
.contains("last poll interval was")
),
- dynamicTest("Second level step 3 name", () ->
- assertThat(testResult.get(0).getSteps().get(0).getSteps().get(2).getName())
+ dynamicTest(
+ "Second level step 3 name", () -> assertThat(testResult.get(0).getSteps().get(0).getSteps().get(2).getName())
.contains("io.qameta.allure.awaitility.ConditionListenersPositiveTest")
.contains("expected <3> but was <2>")
.contains("elapsed time")
.contains("remaining time")
.contains("last poll interval was")
),
- dynamicTest("Second level step 4 name", () ->
- assertThat(testResult.get(0).getSteps().get(0).getSteps().get(3).getName())
+ dynamicTest(
+ "Second level step 4 name", () -> assertThat(testResult.get(0).getSteps().get(0).getSteps().get(3).getName())
.contains("io.qameta.allure.awaitility.ConditionListenersPositiveTest")
.contains("reached its end value of <3> after")
.contains("remaining time")
diff --git a/allure-awaitility/src/test/java/io/qameta/allure/awaitility/GlobalSettingsNegativeTest.java b/allure-awaitility/src/test/java/io/qameta/allure/awaitility/GlobalSettingsNegativeTest.java
index b01c36cd..c00fcf77 100644
--- a/allure-awaitility/src/test/java/io/qameta/allure/awaitility/GlobalSettingsNegativeTest.java
+++ b/allure-awaitility/src/test/java/io/qameta/allure/awaitility/GlobalSettingsNegativeTest.java
@@ -63,12 +63,12 @@ void setup() {
@Test
void globalSettingsAwaitWoAliasCheckTopLevelBrokenStep() {
final List testResult = runWithinTestContext(() -> {
- final AtomicInteger atomicInteger = new AtomicInteger(0);
- await().with()
- .atMost(Duration.of(1000, ChronoUnit.MILLIS))
- .pollInterval(Duration.of(500, ChronoUnit.MILLIS))
- .until(atomicInteger::getAndIncrement, is(3));
- },
+ final AtomicInteger atomicInteger = new AtomicInteger(0);
+ await().with()
+ .atMost(Duration.of(1000, ChronoUnit.MILLIS))
+ .pollInterval(Duration.of(500, ChronoUnit.MILLIS))
+ .until(atomicInteger::getAndIncrement, is(3));
+ },
AllureAwaitilityListener::setLifecycle
).getTestResults();
assertEquals(
@@ -90,37 +90,42 @@ void globalSettingsAwaitWoAliasCheckTopLevelBrokenStep() {
@TestFactory
Stream globalSettingsCheckAwaitWoAliasSecondLevelTimeoutStep() {
final List testResult = runWithinTestContext(() -> {
- final AtomicInteger atomicInteger = new AtomicInteger(0);
- await().with()
- .atMost(Duration.of(1000, ChronoUnit.MILLIS))
- .pollInterval(Duration.of(500, ChronoUnit.MILLIS))
- .until(atomicInteger::getAndIncrement, is(3));
- },
+ final AtomicInteger atomicInteger = new AtomicInteger(0);
+ await().with()
+ .atMost(Duration.of(1000, ChronoUnit.MILLIS))
+ .pollInterval(Duration.of(500, ChronoUnit.MILLIS))
+ .until(atomicInteger::getAndIncrement, is(3));
+ },
AllureAwaitilityListener::setLifecycle
).getTestResults();
return Stream.of(
- dynamicTest("Second level steps count", () ->
- assertThat(testResult.get(0).getSteps().get(0).getSteps())
+ dynamicTest(
+ "Second level steps count", () -> assertThat(testResult.get(0).getSteps().get(0).getSteps())
.as("Exactly 2 second level steps for 2 polling iterations")
- .hasSize(2)),
- dynamicTest("Second level step 1 name", () ->
- assertThat(testResult.get(0).getSteps().get(0).getSteps().get(0).getName())
+ .hasSize(2)
+ ),
+ dynamicTest(
+ "Second level step 1 name", () -> assertThat(testResult.get(0).getSteps().get(0).getSteps().get(0).getName())
.contains("io.qameta.allure.awaitility.GlobalSettingsNegativeTest")
.contains("expected <3> but was <0>")
.contains("elapsed time")
.contains("remaining time")
- .contains("last poll interval was")),
- dynamicTest("Second level step 1 status", () ->
- assertThat(testResult.get(0).getSteps().get(0).getSteps().get(0).getStatus())
- .isEqualTo(Status.PASSED)),
- dynamicTest("Second level step 2 name", () ->
- assertThat(testResult.get(0).getSteps().get(0).getSteps().get(1).getName())
+ .contains("last poll interval was")
+ ),
+ dynamicTest(
+ "Second level step 1 status", () -> assertThat(testResult.get(0).getSteps().get(0).getSteps().get(0).getStatus())
+ .isEqualTo(Status.PASSED)
+ ),
+ dynamicTest(
+ "Second level step 2 name", () -> assertThat(testResult.get(0).getSteps().get(0).getSteps().get(1).getName())
.contains("Condition timeout.")
- .contains("io.qameta.allure.awaitility.GlobalSettingsNegativeTest")),
- dynamicTest("Second level step 2 status", () ->
- assertThat(testResult.get(0).getSteps().get(0).getSteps().get(1).getStatus())
- .isEqualTo(Status.BROKEN))
+ .contains("io.qameta.allure.awaitility.GlobalSettingsNegativeTest")
+ ),
+ dynamicTest(
+ "Second level step 2 status", () -> assertThat(testResult.get(0).getSteps().get(0).getSteps().get(1).getStatus())
+ .isEqualTo(Status.BROKEN)
+ )
);
}
diff --git a/allure-awaitility/src/test/java/io/qameta/allure/awaitility/GlobalSettingsPositiveTest.java b/allure-awaitility/src/test/java/io/qameta/allure/awaitility/GlobalSettingsPositiveTest.java
index df782ada..dfb3ff3b 100644
--- a/allure-awaitility/src/test/java/io/qameta/allure/awaitility/GlobalSettingsPositiveTest.java
+++ b/allure-awaitility/src/test/java/io/qameta/allure/awaitility/GlobalSettingsPositiveTest.java
@@ -65,29 +65,29 @@ void setup() {
@Test
Stream globalSettingsAwaitWoAliasCheckTopLevelPassedStep() {
final List testResult = runWithinTestContext(() -> {
- final AtomicInteger atomicInteger = new AtomicInteger(0);
- await().with()
- .atMost(Duration.of(1000, ChronoUnit.MILLIS))
- .pollInterval(Duration.of(50, ChronoUnit.MILLIS))
- .until(atomicInteger::getAndIncrement, is(3));
- },
+ final AtomicInteger atomicInteger = new AtomicInteger(0);
+ await().with()
+ .atMost(Duration.of(1000, ChronoUnit.MILLIS))
+ .pollInterval(Duration.of(50, ChronoUnit.MILLIS))
+ .until(atomicInteger::getAndIncrement, is(3));
+ },
AllureAwaitilityListener::setLifecycle
).getTestResults();
return Stream.of(
- dynamicTest("Steps count", () ->
- assertThat(testResult.get(0).getSteps())
+ dynamicTest(
+ "Steps count", () -> assertThat(testResult.get(0).getSteps())
.as("Exactly 1 top level step for 1 awaitility condition")
.hasSize(1)
),
- dynamicTest("Top level step status", () ->
- assertEquals(
+ dynamicTest(
+ "Top level step status", () -> assertEquals(
Status.PASSED, testResult.get(0).getSteps().get(0).getStatus(),
"Top level step has passed status"
)
),
- dynamicTest("Top level step name", () ->
- assertEquals(
+ dynamicTest(
+ "Top level step name", () -> assertEquals(
"Awaitility: Starting evaluation",
testResult.get(0).getSteps().get(0).getName(),
"Top level step has default name because await() wo alias"
@@ -107,12 +107,12 @@ Stream globalSettingsAwaitWoAliasCheckTopLevelPassedStep() {
@Test
void globalSettingsAwaitWithAliasCheckTopLevelPassedStep() {
final List testResult = runWithinTestContext(() -> {
- final AtomicInteger atomicInteger = new AtomicInteger(0);
- await("Counter should be at least 3").with()
- .atMost(Duration.of(1000, ChronoUnit.MILLIS))
- .pollInterval(Duration.of(50, ChronoUnit.MILLIS))
- .until(atomicInteger::getAndIncrement, is(3));
- },
+ final AtomicInteger atomicInteger = new AtomicInteger(0);
+ await("Counter should be at least 3").with()
+ .atMost(Duration.of(1000, ChronoUnit.MILLIS))
+ .pollInterval(Duration.of(50, ChronoUnit.MILLIS))
+ .until(atomicInteger::getAndIncrement, is(3));
+ },
AllureAwaitilityListener::setLifecycle
).getTestResults();
assertEquals(
@@ -135,24 +135,24 @@ void globalSettingsAwaitWithAliasCheckTopLevelPassedStep() {
@Test
Stream globalSettingsCheckAwaitWoAliasSecondLevelPassedSteps() {
final List testResult = runWithinTestContext(() -> {
- final AtomicInteger atomicInteger = new AtomicInteger(0);
- await().with()
- .atMost(Duration.of(1000, ChronoUnit.MILLIS))
- .pollInterval(Duration.of(50, ChronoUnit.MILLIS))
- .until(atomicInteger::getAndIncrement, is(3));
- },
+ final AtomicInteger atomicInteger = new AtomicInteger(0);
+ await().with()
+ .atMost(Duration.of(1000, ChronoUnit.MILLIS))
+ .pollInterval(Duration.of(50, ChronoUnit.MILLIS))
+ .until(atomicInteger::getAndIncrement, is(3));
+ },
AllureAwaitilityListener::setLifecycle
).getTestResults();
return Stream.of(
- dynamicTest("Second level steps count", () ->
- assertEquals(
+ dynamicTest(
+ "Second level steps count", () -> assertEquals(
4, testResult.get(0).getSteps().get(0).getSteps().size(),
"Exactly 4 second level steps for 4 polling iterations"
)
),
- dynamicTest("Second level steps all passed", () ->
- assertThat(testResult.get(0).getSteps().get(0).getSteps())
+ dynamicTest(
+ "Second level steps all passed", () -> assertThat(testResult.get(0).getSteps().get(0).getSteps())
.extracting(StepResult::getStatus)
.containsExactlyInAnyOrder(
Status.PASSED,
@@ -161,32 +161,32 @@ Stream globalSettingsCheckAwaitWoAliasSecondLevelPassedSteps() {
Status.PASSED
)
),
- dynamicTest("Second level step 1 name", () ->
- assertThat(testResult.get(0).getSteps().get(0).getSteps().get(0).getName())
+ dynamicTest(
+ "Second level step 1 name", () -> assertThat(testResult.get(0).getSteps().get(0).getSteps().get(0).getName())
.contains("io.qameta.allure.awaitility.GlobalSettingsPositiveTest")
.contains("expected <3> but was <0>")
.contains("elapsed time")
.contains("remaining time")
.contains("last poll interval was")
),
- dynamicTest("Second level step 2 name", () ->
- assertThat(testResult.get(0).getSteps().get(0).getSteps().get(1).getName())
+ dynamicTest(
+ "Second level step 2 name", () -> assertThat(testResult.get(0).getSteps().get(0).getSteps().get(1).getName())
.contains("io.qameta.allure.awaitility.GlobalSettingsPositiveTest")
.contains("expected <3> but was <1>")
.contains("elapsed time")
.contains("remaining time")
.contains("last poll interval was")
),
- dynamicTest("Second level step 3 name", () ->
- assertThat(testResult.get(0).getSteps().get(0).getSteps().get(2).getName())
+ dynamicTest(
+ "Second level step 3 name", () -> assertThat(testResult.get(0).getSteps().get(0).getSteps().get(2).getName())
.contains("io.qameta.allure.awaitility.GlobalSettingsPositiveTest")
.contains("expected <3> but was <2>")
.contains("elapsed time")
.contains("remaining time")
.contains("last poll interval was")
),
- dynamicTest("Second level step 4 name", () ->
- assertThat(testResult.get(0).getSteps().get(0).getSteps().get(3).getName())
+ dynamicTest(
+ "Second level step 4 name", () -> assertThat(testResult.get(0).getSteps().get(0).getSteps().get(3).getName())
.contains("io.qameta.allure.awaitility.GlobalSettingsPositiveTest")
.contains("java.util.concurrent.atomic.AtomicInteger:")
.contains("reached its end value of <3> after")
diff --git a/allure-awaitility/src/test/java/io/qameta/allure/awaitility/MultipleConditionsTest.java b/allure-awaitility/src/test/java/io/qameta/allure/awaitility/MultipleConditionsTest.java
index 8acb2782..6b2fff5f 100644
--- a/allure-awaitility/src/test/java/io/qameta/allure/awaitility/MultipleConditionsTest.java
+++ b/allure-awaitility/src/test/java/io/qameta/allure/awaitility/MultipleConditionsTest.java
@@ -43,24 +43,24 @@ void setup() {
@TestFactory
Stream bothAwaitilityStepsShouldAppearTest() {
final List testResult = runWithinTestContext(() -> {
- await().with()
- .alias("First waiting")
- .until(() -> true);
- await().with()
- .alias("Second waiting")
- .until(() -> true);
- },
+ await().with()
+ .alias("First waiting")
+ .until(() -> true);
+ await().with()
+ .alias("Second waiting")
+ .until(() -> true);
+ },
AllureAwaitilityListener::setLifecycle
).getTestResults();
return Stream.of(
- DynamicTest.dynamicTest("Exactly 2 top level step for 2 awaitility condition", () ->
- assertThat(testResult.get(0).getSteps())
+ DynamicTest.dynamicTest(
+ "Exactly 2 top level step for 2 awaitility condition", () -> assertThat(testResult.get(0).getSteps())
.describedAs("Allure TestResult contains exactly 2 top level step for 2 awaitility condition")
.hasSize(2)
),
- DynamicTest.dynamicTest("All top level step for all awaitility condition has PASSED", () ->
- assertThat(testResult.get(0).getSteps())
+ DynamicTest.dynamicTest(
+ "All top level step for all awaitility condition has PASSED", () -> assertThat(testResult.get(0).getSteps())
.describedAs("Allure TestResult contains all top level step for all awaitility with PASSED condition")
.allMatch(step -> Status.PASSED.equals(step.getStatus()))
)
diff --git a/allure-awaitility/src/test/resources/allure.properties b/allure-awaitility/src/test/resources/allure.properties
new file mode 100644
index 00000000..0486d8a7
--- /dev/null
+++ b/allure-awaitility/src/test/resources/allure.properties
@@ -0,0 +1,3 @@
+allure.results.directory=build/allure-results
+allure.label.epic=#project.description#
+allure.label.module=allure-awaitility
diff --git a/allure-bom/build.gradle.kts b/allure-bom/build.gradle.kts
index 9593300a..0d83d95e 100644
--- a/allure-bom/build.gradle.kts
+++ b/allure-bom/build.gradle.kts
@@ -8,6 +8,8 @@ dependencies {
constraints {
rootProject.subprojects.sorted()
.forEach { api("${it.group}:${it.name}:${it.version}") }
+ api("io.qameta.allure:allure-junit5:${project.version}")
+ api("io.qameta.allure:allure-junit5-assert:${project.version}")
}
}
diff --git a/allure-citrus/src/main/java/io/qameta/allure/citrus/AllureCitrus.java b/allure-citrus/src/main/java/io/qameta/allure/citrus/AllureCitrus.java
index d501b3a2..b50ec45d 100644
--- a/allure-citrus/src/main/java/io/qameta/allure/citrus/AllureCitrus.java
+++ b/allure-citrus/src/main/java/io/qameta/allure/citrus/AllureCitrus.java
@@ -59,6 +59,7 @@
import static io.qameta.allure.util.ResultsUtils.createParameter;
import static io.qameta.allure.util.ResultsUtils.createSuiteLabel;
import static io.qameta.allure.util.ResultsUtils.createThreadLabel;
+import static io.qameta.allure.util.ResultsUtils.createTitlePath;
import static io.qameta.allure.util.ResultsUtils.getProvidedLabels;
/**
@@ -161,25 +162,31 @@ public void onTestActionSkipped(final TestCase testCase, final TestAction testAc
private void startTestCase(final TestCase testCase) {
final String uuid = createUuid(testCase);
+ final Optional extends Class>> testClass = Optional.ofNullable(testCase.getTestClass());
final TestResult result = new TestResult()
.setUuid(uuid)
.setName(testCase.getName())
+ .setTitlePath(
+ testClass
+ .map(ResultsUtils::createTitlePathFromJavaClass)
+ .orElseGet(() -> createTitlePath(testCase.getName()))
+ )
.setStage(Stage.RUNNING);
result.getLabels().addAll(getProvidedLabels());
-
- final Optional extends Class>> testClass = Optional.ofNullable(testCase.getTestClass());
testClass.map(this::getLabels).ifPresent(result.getLabels()::addAll);
testClass.map(this::getLinks).ifPresent(result.getLinks()::addAll);
- result.getLabels().addAll(Arrays.asList(
- createHostLabel(),
- createThreadLabel(),
- createFrameworkLabel("citrus"),
- createLanguageLabel("java")
- ));
+ result.getLabels().addAll(
+ Arrays.asList(
+ createHostLabel(),
+ createThreadLabel(),
+ createFrameworkLabel("citrus"),
+ createLanguageLabel("java")
+ )
+ );
testClass.ifPresent(aClass -> {
final String suiteName = aClass.getCanonicalName();
@@ -217,7 +224,6 @@ private void stopTestCase(final TestCase testCase,
getLifecycle().writeTestCase(uuid);
}
-
private String createUuid(final TestCase testCase) {
final String uuid = UUID.randomUUID().toString();
try {
@@ -265,7 +271,8 @@ private List getLinks(final AnnotatedElement annotatedElement) {
return Stream.of(
getAnnotations(annotatedElement, io.qameta.allure.Link.class).map(ResultsUtils::createLink),
getAnnotations(annotatedElement, io.qameta.allure.Issue.class).map(ResultsUtils::createLink),
- getAnnotations(annotatedElement, io.qameta.allure.TmsLink.class).map(ResultsUtils::createLink))
+ getAnnotations(annotatedElement, io.qameta.allure.TmsLink.class).map(ResultsUtils::createLink)
+ )
.reduce(Stream::concat).orElseGet(Stream::empty).collect(Collectors.toList());
}
diff --git a/allure-citrus/src/test/java/io/qameta/allure/citrus/AllureCitrusTest.java b/allure-citrus/src/test/java/io/qameta/allure/citrus/AllureCitrusTest.java
index bf12e299..eb4b05d2 100644
--- a/allure-citrus/src/test/java/io/qameta/allure/citrus/AllureCitrusTest.java
+++ b/allure-citrus/src/test/java/io/qameta/allure/citrus/AllureCitrusTest.java
@@ -61,6 +61,8 @@ void shouldSetName() {
assertThat(results.getTestResults())
.extracting(TestResult::getName)
.containsExactly("Simple test");
+ assertThat(results.getTestResults().get(0).getTitlePath())
+ .containsExactly("com", "consol", "citrus", "dsl", "design", "DefaultTestDesigner");
}
@AllureFeatures.PassedTests
diff --git a/allure-citrus/src/test/resources/allure.properties b/allure-citrus/src/test/resources/allure.properties
index 9c0b0a2d..0833b8e0 100644
--- a/allure-citrus/src/test/resources/allure.properties
+++ b/allure-citrus/src/test/resources/allure.properties
@@ -1,2 +1,3 @@
allure.results.directory=build/allure-results
allure.label.epic=#project.description#
+allure.label.module=allure-citrus
diff --git a/allure-cucumber4-jvm/src/main/java/io/qameta/allure/cucumber4jvm/AllureCucumber4Jvm.java b/allure-cucumber4-jvm/src/main/java/io/qameta/allure/cucumber4jvm/AllureCucumber4Jvm.java
index 967ee1a3..7cfdc67e 100644
--- a/allure-cucumber4-jvm/src/main/java/io/qameta/allure/cucumber4jvm/AllureCucumber4Jvm.java
+++ b/allure-cucumber4-jvm/src/main/java/io/qameta/allure/cucumber4jvm/AllureCucumber4Jvm.java
@@ -71,6 +71,8 @@
import static cucumber.api.HookType.Before;
import static io.qameta.allure.util.ResultsUtils.createParameter;
+import static io.qameta.allure.util.ResultsUtils.createTitlePath;
+import static io.qameta.allure.util.ResultsUtils.createTitlePathFromSourcePath;
import static io.qameta.allure.util.ResultsUtils.getStatus;
import static io.qameta.allure.util.ResultsUtils.getStatusDetails;
import static io.qameta.allure.util.ResultsUtils.md5;
@@ -78,11 +80,14 @@
/**
* Allure plugin for Cucumber JVM 4.0.
*/
-@SuppressWarnings({
- "ClassDataAbstractionCoupling",
- "ClassFanOutComplexity",
- "MultipleStringLiterals",
-})
+@SuppressWarnings(
+ {
+ "ClassDataAbstractionCoupling",
+ "ClassFanOutComplexity",
+ "MultipleStringLiterals",
+ "PMD.GodClass",
+ }
+)
public class AllureCucumber4Jvm implements ConcurrentEventListener {
private static final String COLON = ":";
@@ -144,11 +149,11 @@ private void handleTestCaseStarted(final TestCaseStarted event) {
final String name = testCase.getName();
-
// the same way full name is generated for
// org.junit.platform.engine.support.descriptor.ClasspathResourceSource
// to support io.qameta.allure.junitplatform.AllurePostDiscoveryFilter
- final String fullName = String.format("%s:%d",
+ final String fullName = String.format(
+ "%s:%d",
getTestCaseUri(testCase),
testCase.getLine()
);
@@ -156,20 +161,23 @@ private void handleTestCaseStarted(final TestCaseStarted event) {
final String testCaseUuid = testCaseUuids
.computeIfAbsent(testCase, tc -> UUID.randomUUID().toString());
+ final List titlePath = createTitlePathFromSourcePath(getTestCaseUri(testCase));
+ titlePath.addAll(createTitlePath(feature.getName()));
+
final TestResult result = new TestResult()
.setUuid(testCaseUuid)
.setTestCaseId(getTestCaseId(testCase))
.setHistoryId(getHistoryId(testCase))
.setFullName(fullName)
+ .setTitlePath(titlePath)
.setName(name)
.setLabels(labelBuilder.getScenarioLabels())
.setLinks(labelBuilder.getScenarioLinks());
- final ScenarioDefinition scenarioDefinition =
- testSources.getScenarioDefinition(
- testCase.getUri(),
- testCase.getLine()
- );
+ final ScenarioDefinition scenarioDefinition = testSources.getScenarioDefinition(
+ testCase.getUri(),
+ testCase.getLine()
+ );
if (scenarioDefinition instanceof ScenarioOutline) {
result.setParameters(
@@ -209,9 +217,10 @@ private void handleTestCaseFinished(final TestCaseFinished event) {
.setMuted(tagParser.isMuted())
.setKnown(tagParser.isKnown());
- lifecycle.updateTestCase(uuid, testResult -> testResult
- .setStatus(status)
- .setStatusDetails(statusDetails)
+ lifecycle.updateTestCase(
+ uuid, testResult -> testResult
+ .setStatus(status)
+ .setStatusDetails(statusDetails)
);
lifecycle.stopTestCase(uuid);
@@ -297,13 +306,13 @@ private void handleStartFixtureHook(final TestCase testCase,
return;
}
-
final String containerUuid = hookStepContainerUuid
.computeIfAbsent(hook, unused -> UUID.randomUUID().toString());
- lifecycle.startTestContainer(new TestResultContainer()
- .setUuid(containerUuid)
- .setChildren(Collections.singletonList(uuid))
+ lifecycle.startTestContainer(
+ new TestResultContainer()
+ .setUuid(containerUuid)
+ .setChildren(Collections.singletonList(uuid))
);
final FixtureResult hookResult = new FixtureResult()
@@ -402,15 +411,14 @@ private Status translateTestCaseStatus(final Result testCaseResult) {
}
private List getExamplesAsParameters(
- final ScenarioOutline scenario,
- final TestCase localCurrentTestCase) {
- final Optional maybeExample =
- scenario.getExamples().stream()
- .filter(example -> example.getTableBody().stream()
- .anyMatch(row -> row.getLocation().getLine()
- == localCurrentTestCase.getLine())
- )
- .findFirst();
+ final ScenarioOutline scenario,
+ final TestCase localCurrentTestCase) {
+ final Optional maybeExample = scenario.getExamples().stream()
+ .filter(
+ example -> example.getTableBody().stream()
+ .anyMatch(row -> row.getLocation().getLine() == localCurrentTestCase.getLine())
+ )
+ .findFirst();
if (!maybeExample.isPresent()) {
return Collections.emptyList();
@@ -449,8 +457,10 @@ private void createDataTableAttachment(final PickleTable pickleTable) {
}
final String attachmentSource = lifecycle
.prepareAttachment("Data table", "text/tab-separated-values", "csv");
- lifecycle.writeAttachment(attachmentSource,
- new ByteArrayInputStream(dataTableCsv.toString().getBytes(StandardCharsets.UTF_8)));
+ lifecycle.writeAttachment(
+ attachmentSource,
+ new ByteArrayInputStream(dataTableCsv.toString().getBytes(StandardCharsets.UTF_8))
+ );
}
private void handleStopHookStep(final Result eventResult,
@@ -471,9 +481,10 @@ private void handleStopHookStep(final Result eventResult,
final StatusDetails statusDetails = getStatusDetails(eventResult.getError())
.orElseGet(StatusDetails::new);
- lifecycle.updateFixture(uuid, result -> result
- .setStatus(status)
- .setStatusDetails(statusDetails)
+ lifecycle.updateFixture(
+ uuid, result -> result
+ .setStatus(status)
+ .setStatusDetails(statusDetails)
);
lifecycle.stopFixture(uuid);
@@ -494,8 +505,7 @@ private void handleStopStep(final TestCase testCase,
final Status stepStatus = translateTestCaseStatus(eventResult);
- final StatusDetails statusDetails
- = eventResult.getStatus() == Result.Type.UNDEFINED
+ final StatusDetails statusDetails = eventResult.getStatus() == Result.Type.UNDEFINED
? new StatusDetails().setMessage("Undefined Step. Please add step definition")
: getStatusDetails(eventResult.getError())
.orElse(new StatusDetails());
diff --git a/allure-cucumber4-jvm/src/main/java/io/qameta/allure/cucumber4jvm/LabelBuilder.java b/allure-cucumber4-jvm/src/main/java/io/qameta/allure/cucumber4jvm/LabelBuilder.java
index 0424e6e1..1f54ea8f 100644
--- a/allure-cucumber4-jvm/src/main/java/io/qameta/allure/cucumber4jvm/LabelBuilder.java
+++ b/allure-cucumber4-jvm/src/main/java/io/qameta/allure/cucumber4jvm/LabelBuilder.java
@@ -49,7 +49,7 @@
/**
* Scenario labels and links builder.
*/
-@SuppressWarnings({"CyclomaticComplexity", "MultipleStringLiterals"})
+@SuppressWarnings({"CyclomaticComplexity", "MultipleStringLiterals", "PMD.CognitiveComplexity"})
final class LabelBuilder {
private static final Logger LOGGER = LoggerFactory.getLogger(LabelBuilder.class);
private static final String COMPOSITE_TAG_DELIMITER = "=";
@@ -116,17 +116,19 @@ final class LabelBuilder {
final String uri = scenario.getUri();
scenarioLabels.addAll(ResultsUtils.getProvidedLabels());
- scenarioLabels.addAll(Arrays.asList(
- createHostLabel(),
- createThreadLabel(),
- createFeatureLabel(featureName),
- createStoryLabel(scenario.getName()),
- createSuiteLabel(featureName),
- createTestClassLabel(scenario.getName()),
- createFrameworkLabel("cucumber4jvm"),
- createLanguageLabel("java"),
- createLabel("gherkin_uri", uri)
- ));
+ scenarioLabels.addAll(
+ Arrays.asList(
+ createHostLabel(),
+ createThreadLabel(),
+ createFeatureLabel(featureName),
+ createStoryLabel(scenario.getName()),
+ createSuiteLabel(featureName),
+ createTestClassLabel(scenario.getName()),
+ createFrameworkLabel("cucumber4jvm"),
+ createLanguageLabel("java"),
+ createLabel("gherkin_uri", uri)
+ )
+ );
featurePackage(uri, featureName)
.map(ResultsUtils::createPackageLabel)
@@ -159,8 +161,10 @@ private void tryHandleNamedLink(final String tagString) {
final String name = tagString.split(COMPOSITE_TAG_DELIMITER)[1];
scenarioLinks.add(ResultsUtils.createLink(null, name, null, type));
} else {
- LOGGER.warn("Composite named tag {} does not match regex {}. Skipping", tagString,
- namedLinkPatternString);
+ LOGGER.warn(
+ "Composite named tag {} does not match regex {}. Skipping", tagString,
+ namedLinkPatternString
+ );
}
}
@@ -178,10 +182,12 @@ private Optional featurePackage(final String uriString, final String fea
final String schemeSpecificPart = uri.normalize().getSchemeSpecificPart();
final Stream folders = Stream.of(schemeSpecificPart.replaceAll("\\.", "_").split("/"));
final Stream name = Stream.of(featureName);
- return Optional.of(Stream.concat(folders, name)
- .filter(Objects::nonNull)
- .filter(s -> !s.isEmpty())
- .collect(Collectors.joining(".")));
+ return Optional.of(
+ Stream.concat(folders, name)
+ .filter(Objects::nonNull)
+ .filter(s -> !s.isEmpty())
+ .collect(Collectors.joining("."))
+ );
}
private static Optional safeUri(final String uri) {
diff --git a/allure-cucumber4-jvm/src/main/java/io/qameta/allure/cucumber4jvm/TagParser.java b/allure-cucumber4-jvm/src/main/java/io/qameta/allure/cucumber4jvm/TagParser.java
index 4839d4ea..cc4b120d 100644
--- a/allure-cucumber4-jvm/src/main/java/io/qameta/allure/cucumber4jvm/TagParser.java
+++ b/allure-cucumber4-jvm/src/main/java/io/qameta/allure/cucumber4jvm/TagParser.java
@@ -52,9 +52,9 @@ public boolean isKnown() {
private boolean getStatusDetailByTag(final String tagName) {
return scenario.getTags().stream()
- .anyMatch(tag -> tag.getName().equalsIgnoreCase(tagName))
- || feature.getTags().stream()
- .anyMatch(tag -> tag.getName().equalsIgnoreCase(tagName));
+ .anyMatch(tag -> tag.getName().equalsIgnoreCase(tagName))
+ || feature.getTags().stream()
+ .anyMatch(tag -> tag.getName().equalsIgnoreCase(tagName));
}
public boolean isResultTag(final PickleTag tag) {
diff --git a/allure-cucumber4-jvm/src/test/java/io/qameta/allure/cucumber4jvm/AllureCucumber4JvmTest.java b/allure-cucumber4-jvm/src/test/java/io/qameta/allure/cucumber4jvm/AllureCucumber4JvmTest.java
index 85a1d093..640148a1 100644
--- a/allure-cucumber4-jvm/src/test/java/io/qameta/allure/cucumber4jvm/AllureCucumber4JvmTest.java
+++ b/allure-cucumber4-jvm/src/test/java/io/qameta/allure/cucumber4jvm/AllureCucumber4JvmTest.java
@@ -77,6 +77,8 @@ void shouldSetName() {
assertThat(testResults)
.extracting(TestResult::getName)
.containsExactlyInAnyOrder("Add a to b");
+ assertThat(testResults.get(0).getTitlePath())
+ .containsExactly("src", "test", "resources", "features", "simple.feature", "Simple feature");
}
@AllureFeatures.PassedTests
@@ -180,7 +182,7 @@ void shouldSetDescription() {
final AllureResults results = runFeature("features/description.feature");
final String expected = "This is description for current feature.\n"
- + "It should appear on each scenario in report";
+ + "It should appear on each scenario in report";
final List testResults = results.getTestResults();
assertThat(testResults)
@@ -197,7 +199,7 @@ void shouldSetScenarioDescription() {
final AllureResults results = runFeature("features/scenario_description.feature");
final String expected = "This is description for current feature.\n"
- + "It should appear on each scenario in report";
+ + "It should appear on each scenario in report";
final List testResults = results.getTestResults();
assertThat(testResults)
@@ -235,11 +237,12 @@ void shouldAddDataTableAttachment() {
final String attachmentContent = new String(bytes, StandardCharsets.UTF_8);
assertThat(attachmentContent)
- .isEqualTo("""
- name\tlogin\temail
- Viktor\tclicman\tclicman@ya.ru
- Viktor2\tclicman2\tclicman2@ya.ru
+ .isEqualTo(
"""
+ name\tlogin\temail
+ Viktor\tclicman\tclicman@ya.ru
+ Viktor2\tclicman2\tclicman2@ya.ru
+ """
);
}
@@ -378,8 +381,14 @@ void shouldAddTags() {
@AllureFeatures.Links
@ExtendWith(SystemPropertyExtension.class)
- @SystemProperty(name = "allure.link.issue.pattern", value = "https://example.org/issue/{}")
- @SystemProperty(name = "allure.link.tms.pattern", value = "https://example.org/tms/{}")
+ @SystemProperty(
+ name = "allure.link.issue.pattern",
+ value = "https://example.org/issue/{}"
+ )
+ @SystemProperty(
+ name = "allure.link.tms.pattern",
+ value = "https://example.org/tms/{}"
+ )
@Test
void shouldAddLinks() {
final AllureResults results = runFeature("features/tags.feature");
@@ -508,8 +517,10 @@ void shouldSupportDryRunForSimpleFeatures() {
@AllureFeatures.Base
@Test
void shouldSupportDryRunForHooks() {
- final AllureResults results = runFeature("features/hooks.feature", "--dry-run", "-t",
- "@WithHooks or @BeforeHookWithException or @AfterHookWithException");
+ final AllureResults results = runFeature(
+ "features/hooks.feature", "--dry-run", "-t",
+ "@WithHooks or @BeforeHookWithException or @AfterHookWithException"
+ );
final TestResult tr1 = results.getTestResultByName("Simple scenario with Before and After hooks");
@@ -649,8 +660,10 @@ void shouldProcessScenariosInParallelMode() {
@AllureFeatures.Stages
@Test
void shouldDisplayHooksAsStages() {
- final AllureResults results = runFeature("features/hooks.feature", "-t",
- "@WithHooks or @BeforeHookWithException or @AfterHookWithException");
+ final AllureResults results = runFeature(
+ "features/hooks.feature", "-t",
+ "@WithHooks or @BeforeHookWithException or @AfterHookWithException"
+ );
final TestResult tr1 = results.getTestResultByName("Simple scenario with Before and After hooks");
final TestResult tr2 = results.getTestResultByName("Simple scenario with Before hook with Exception");
@@ -688,7 +701,6 @@ void shouldDisplayHooksAsStages() {
tuple("Then result is 15", Status.SKIPPED)
);
-
assertThat(results.getTestResultContainersForTestResult(tr2))
.flatExtracting(TestResultContainer::getBefores)
.extracting(FixtureResult::getName, FixtureResult::getStatus)
@@ -734,8 +746,14 @@ void shouldHandleAmbigiousStepsExceptions() {
);
}
- @ResourceLock(value = SYSTEM_PROPERTIES, mode = READ_WRITE)
- @SystemProperty(name = "allure.label.x-provided", value = "cucumberjvm5-test-provided")
+ @ResourceLock(
+ value = SYSTEM_PROPERTIES,
+ mode = READ_WRITE
+ )
+ @SystemProperty(
+ name = "allure.label.x-provided",
+ value = "cucumberjvm5-test-provided"
+ )
@Test
void shouldSupportProvidedLabels() {
final AllureResults results = runFeature("features/simple.feature");
@@ -780,7 +798,10 @@ void shouldSupportRuntimeApiInStepsWhenHooksAreUsed() {
);
}
- @SystemProperty(name = "cucumber.junit-platform.naming-strategy", value = "long")
+ @SystemProperty(
+ name = "cucumber.junit-platform.naming-strategy",
+ value = "long"
+ )
@Step
private AllureResults runFeature(final String featureResource,
final String... moreOptions) {
@@ -788,17 +809,18 @@ private AllureResults runFeature(final String featureResource,
final AllureCucumber4Jvm cucumber4Jvm = new AllureCucumber4Jvm(lifecycle);
final ClassLoader classLoader = currentThread().getContextClassLoader();
final ResourceLoader resourceLoader = new MultiLoader(classLoader);
- final List opts = new ArrayList<>(Arrays.asList(
- "--glue", "io.qameta.allure.cucumber4jvm.samples",
- "--plugin", "null_summary"
- ));
+ final List opts = new ArrayList<>(
+ Arrays.asList(
+ "--glue", "io.qameta.allure.cucumber4jvm.samples",
+ "--plugin", "null_summary"
+ )
+ );
opts.addAll(Arrays.asList(moreOptions));
final FeatureWithLines featureWithLines = FeatureWithLines.parse("src/test/resources/" + featureResource);
final RuntimeOptions options = new CommandlineOptionsParser()
.parse(opts.toArray(new String[]{})).addFeature(featureWithLines).build();
- final FeaturePathFeatureSupplier supplier
- = new FeaturePathFeatureSupplier(new FeatureLoader(resourceLoader), options);
+ final FeaturePathFeatureSupplier supplier = new FeaturePathFeatureSupplier(new FeatureLoader(resourceLoader), options);
final Runtime runtime = Runtime.builder()
.withClassLoader(classLoader)
@@ -811,5 +833,4 @@ private AllureResults runFeature(final String featureResource,
});
}
-
}
diff --git a/allure-cucumber4-jvm/src/test/java/io/qameta/allure/cucumber4jvm/samples/AmbigiousSteps.java b/allure-cucumber4-jvm/src/test/java/io/qameta/allure/cucumber4jvm/samples/AmbigiousSteps.java
index 429e6290..07de408c 100644
--- a/allure-cucumber4-jvm/src/test/java/io/qameta/allure/cucumber4jvm/samples/AmbigiousSteps.java
+++ b/allure-cucumber4-jvm/src/test/java/io/qameta/allure/cucumber4jvm/samples/AmbigiousSteps.java
@@ -34,7 +34,7 @@ public void ambigious_2() {
}
@Then("^something bad should happen")
- public void somethingBadStep(){
+ public void somethingBadStep() {
//nothing here
}
}
diff --git a/allure-cucumber4-jvm/src/test/java/io/qameta/allure/cucumber4jvm/samples/AttachmentSteps.java b/allure-cucumber4-jvm/src/test/java/io/qameta/allure/cucumber4jvm/samples/AttachmentSteps.java
index d8307110..75b44942 100644
--- a/allure-cucumber4-jvm/src/test/java/io/qameta/allure/cucumber4jvm/samples/AttachmentSteps.java
+++ b/allure-cucumber4-jvm/src/test/java/io/qameta/allure/cucumber4jvm/samples/AttachmentSteps.java
@@ -19,25 +19,21 @@
import io.cucumber.java.Before;
import io.cucumber.java.en.Given;
-public class AttachmentSteps
-{
+public class AttachmentSteps {
private Scenario scenario;
@Before("@attachments")
- public void setup(Scenario scenario)
- {
+ public void setup(Scenario scenario) {
this.scenario = scenario;
}
@Given("step with scenario write")
- public void stepWithScenarioWrite()
- {
+ public void stepWithScenarioWrite() {
scenario.write("text attachment");
}
@Given("step with scenario embed")
- public void stepWithScenarioEmbed()
- {
+ public void stepWithScenarioEmbed() {
scenario.embed("image attachment".getBytes(), "image/png");
}
}
diff --git a/allure-cucumber4-jvm/src/test/java/io/qameta/allure/cucumber4jvm/samples/DatatableFeatureSteps.java b/allure-cucumber4-jvm/src/test/java/io/qameta/allure/cucumber4jvm/samples/DatatableFeatureSteps.java
index 158b5ece..235af032 100644
--- a/allure-cucumber4-jvm/src/test/java/io/qameta/allure/cucumber4jvm/samples/DatatableFeatureSteps.java
+++ b/allure-cucumber4-jvm/src/test/java/io/qameta/allure/cucumber4jvm/samples/DatatableFeatureSteps.java
@@ -15,8 +15,8 @@
*/
package io.qameta.allure.cucumber4jvm.samples;
-import io.cucumber.java.en.Given;
import io.cucumber.datatable.DataTable;
+import io.cucumber.java.en.Given;
/**
* @author charlie (Dmitry Baev).
diff --git a/allure-cucumber4-jvm/src/test/java/io/qameta/allure/cucumber4jvm/samples/HookSteps.java b/allure-cucumber4-jvm/src/test/java/io/qameta/allure/cucumber4jvm/samples/HookSteps.java
index 2aa7f01f..9c4666fa 100644
--- a/allure-cucumber4-jvm/src/test/java/io/qameta/allure/cucumber4jvm/samples/HookSteps.java
+++ b/allure-cucumber4-jvm/src/test/java/io/qameta/allure/cucumber4jvm/samples/HookSteps.java
@@ -25,22 +25,22 @@
public class HookSteps {
@Before("@WithHooks")
- public void beforeHook(){
+ public void beforeHook() {
// nothing
}
@After("@WithHooks")
- public void afterHook(){
+ public void afterHook() {
// nothing
}
@Before("@BeforeHookWithException")
- public void beforeHookWithException(){
+ public void beforeHookWithException() {
Assertions.fail("Exception in Hook step");
}
@After("@AfterHookWithException")
- public void afterHookWithException(){
+ public void afterHookWithException() {
Assertions.fail("Exception in Hook step");
}
diff --git a/allure-cucumber4-jvm/src/test/java/io/qameta/allure/cucumber4jvm/samples/RuntimeApiSteps.java b/allure-cucumber4-jvm/src/test/java/io/qameta/allure/cucumber4jvm/samples/RuntimeApiSteps.java
index 4c768742..16ac1320 100644
--- a/allure-cucumber4-jvm/src/test/java/io/qameta/allure/cucumber4jvm/samples/RuntimeApiSteps.java
+++ b/allure-cucumber4-jvm/src/test/java/io/qameta/allure/cucumber4jvm/samples/RuntimeApiSteps.java
@@ -27,12 +27,12 @@
public class RuntimeApiSteps {
@Before("@beforeScenario")
- public void beforeScenario(){
+ public void beforeScenario() {
// nothing
}
@Before("@beforeFeature")
- public void beforeFeature(){
+ public void beforeFeature() {
// nothing
}
diff --git a/allure-cucumber4-jvm/src/test/resources/allure.properties b/allure-cucumber4-jvm/src/test/resources/allure.properties
index dbfefee4..e026a61f 100644
--- a/allure-cucumber4-jvm/src/test/resources/allure.properties
+++ b/allure-cucumber4-jvm/src/test/resources/allure.properties
@@ -1,3 +1,4 @@
allure.model.indentOutput=true
allure.results.directory=build/allure-results
allure.label.epic=#project.description#
+allure.label.module=allure-cucumber4-jvm
diff --git a/allure-cucumber5-jvm/src/main/java/io/qameta/allure/cucumber5jvm/AllureCucumber5Jvm.java b/allure-cucumber5-jvm/src/main/java/io/qameta/allure/cucumber5jvm/AllureCucumber5Jvm.java
index e1e9debb..682cc378 100644
--- a/allure-cucumber5-jvm/src/main/java/io/qameta/allure/cucumber5jvm/AllureCucumber5Jvm.java
+++ b/allure-cucumber5-jvm/src/main/java/io/qameta/allure/cucumber5jvm/AllureCucumber5Jvm.java
@@ -67,6 +67,8 @@
import java.util.stream.Stream;
import static io.qameta.allure.util.ResultsUtils.createParameter;
+import static io.qameta.allure.util.ResultsUtils.createTitlePath;
+import static io.qameta.allure.util.ResultsUtils.createTitlePathFromSourcePath;
import static io.qameta.allure.util.ResultsUtils.getStatus;
import static io.qameta.allure.util.ResultsUtils.getStatusDetails;
import static io.qameta.allure.util.ResultsUtils.md5;
@@ -74,11 +76,14 @@
/**
* Allure plugin for Cucumber JVM 5.0.
*/
-@SuppressWarnings({
- "ClassDataAbstractionCoupling",
- "ClassFanOutComplexity",
- "MultipleStringLiterals",
-})
+@SuppressWarnings(
+ {
+ "ClassDataAbstractionCoupling",
+ "ClassFanOutComplexity",
+ "MultipleStringLiterals",
+ "PMD.GodClass",
+ }
+)
public class AllureCucumber5Jvm implements ConcurrentEventListener {
private static final String COLON = ":";
@@ -139,31 +144,34 @@ private void handleTestCaseStarted(final TestCaseStarted event) {
final String name = testCase.getName();
-
// the same way full name is generated for
// org.junit.platform.engine.support.descriptor.ClasspathResourceSource
// to support io.qameta.allure.junitplatform.AllurePostDiscoveryFilter
- final String fullName = String.format("%s:%d",
+ final String fullName = String.format(
+ "%s:%d",
getTestCaseUri(testCase),
testCase.getLine()
);
final String testCaseUuid = testCase.getId().toString();
+ final List titlePath = createTitlePathFromSourcePath(getTestCaseUri(testCase));
+ titlePath.addAll(createTitlePath(feature.getName()));
+
final TestResult result = new TestResult()
.setUuid(testCaseUuid)
.setTestCaseId(getTestCaseId(testCase))
.setHistoryId(getHistoryId(testCase))
.setFullName(fullName)
+ .setTitlePath(titlePath)
.setName(name)
.setLabels(labelBuilder.getScenarioLabels())
.setLinks(labelBuilder.getScenarioLinks());
- final ScenarioDefinition scenarioDefinition =
- testSources.getScenarioDefinition(
- testCase.getUri(),
- testCase.getLine()
- );
+ final ScenarioDefinition scenarioDefinition = testSources.getScenarioDefinition(
+ testCase.getUri(),
+ testCase.getLine()
+ );
if (scenarioDefinition instanceof ScenarioOutline) {
result.setParameters(
@@ -199,9 +207,10 @@ private void handleTestCaseFinished(final TestCaseFinished event) {
.setMuted(tagParser.isMuted())
.setKnown(tagParser.isKnown());
- lifecycle.updateTestCase(uuid, testResult -> testResult
- .setStatus(status)
- .setStatusDetails(statusDetails)
+ lifecycle.updateTestCase(
+ uuid, testResult -> testResult
+ .setStatus(status)
+ .setStatusDetails(statusDetails)
);
lifecycle.stopTestCase(uuid);
@@ -269,9 +278,10 @@ private void handleStartFixtureHook(final TestCase testCase,
final String containerUuid = hookStepContainerUuid
.computeIfAbsent(hook, unused -> UUID.randomUUID().toString());
- lifecycle.startTestContainer(new TestResultContainer()
- .setUuid(containerUuid)
- .setChildren(Collections.singletonList(uuid))
+ lifecycle.startTestContainer(
+ new TestResultContainer()
+ .setUuid(containerUuid)
+ .setChildren(Collections.singletonList(uuid))
);
final FixtureResult hookResult = new FixtureResult()
@@ -354,15 +364,14 @@ private Status translateTestCaseStatus(final Result testCaseResult) {
}
private List getExamplesAsParameters(
- final ScenarioOutline scenario,
- final TestCase localCurrentTestCase) {
- final Optional maybeExample =
- scenario.getExamples().stream()
- .filter(example -> example.getTableBody().stream()
- .anyMatch(row -> row.getLocation().getLine()
- == localCurrentTestCase.getLine())
- )
- .findFirst();
+ final ScenarioOutline scenario,
+ final TestCase localCurrentTestCase) {
+ final Optional maybeExample = scenario.getExamples().stream()
+ .filter(
+ example -> example.getTableBody().stream()
+ .anyMatch(row -> row.getLocation().getLine() == localCurrentTestCase.getLine())
+ )
+ .findFirst();
if (!maybeExample.isPresent()) {
return Collections.emptyList();
@@ -400,8 +409,10 @@ private void createDataTableAttachment(final DataTableArgument dataTableArgument
}
final String attachmentSource = lifecycle
.prepareAttachment("Data table", "text/tab-separated-values", "csv");
- lifecycle.writeAttachment(attachmentSource,
- new ByteArrayInputStream(dataTableCsv.toString().getBytes(StandardCharsets.UTF_8)));
+ lifecycle.writeAttachment(
+ attachmentSource,
+ new ByteArrayInputStream(dataTableCsv.toString().getBytes(StandardCharsets.UTF_8))
+ );
}
private void handleStopHookStep(final Result eventResult,
@@ -422,9 +433,10 @@ private void handleStopHookStep(final Result eventResult,
final StatusDetails statusDetails = getStatusDetails(eventResult.getError())
.orElseGet(StatusDetails::new);
- lifecycle.updateFixture(uuid, result -> result
- .setStatus(status)
- .setStatusDetails(statusDetails)
+ lifecycle.updateFixture(
+ uuid, result -> result
+ .setStatus(status)
+ .setStatusDetails(statusDetails)
);
lifecycle.stopFixture(uuid);
@@ -445,8 +457,7 @@ private void handleStopStep(final TestCase testCase,
final Status stepStatus = translateTestCaseStatus(eventResult);
- final StatusDetails statusDetails
- = eventResult.getStatus() == io.cucumber.plugin.event.Status.UNDEFINED
+ final StatusDetails statusDetails = eventResult.getStatus() == io.cucumber.plugin.event.Status.UNDEFINED
? new StatusDetails().setMessage("Undefined Step. Please add step definition")
: getStatusDetails(eventResult.getError())
.orElse(new StatusDetails());
diff --git a/allure-cucumber5-jvm/src/main/java/io/qameta/allure/cucumber5jvm/LabelBuilder.java b/allure-cucumber5-jvm/src/main/java/io/qameta/allure/cucumber5jvm/LabelBuilder.java
index 73321148..31d7d53f 100644
--- a/allure-cucumber5-jvm/src/main/java/io/qameta/allure/cucumber5jvm/LabelBuilder.java
+++ b/allure-cucumber5-jvm/src/main/java/io/qameta/allure/cucumber5jvm/LabelBuilder.java
@@ -15,8 +15,8 @@
*/
package io.qameta.allure.cucumber5jvm;
-import io.cucumber.plugin.event.TestCase;
import gherkin.ast.Feature;
+import io.cucumber.plugin.event.TestCase;
import io.qameta.allure.model.Label;
import io.qameta.allure.model.Link;
import io.qameta.allure.util.ResultsUtils;
@@ -48,7 +48,7 @@
/**
* Scenario labels and links builder.
*/
-@SuppressWarnings({"CyclomaticComplexity", "MultipleStringLiterals"})
+@SuppressWarnings({"CyclomaticComplexity", "MultipleStringLiterals", "PMD.CognitiveComplexity"})
class LabelBuilder {
private static final Logger LOGGER = LoggerFactory.getLogger(LabelBuilder.class);
private static final String COMPOSITE_TAG_DELIMITER = "=";
@@ -113,17 +113,19 @@ class LabelBuilder {
final URI uri = scenario.getUri();
scenarioLabels.addAll(ResultsUtils.getProvidedLabels());
- scenarioLabels.addAll(Arrays.asList(
- createHostLabel(),
- createThreadLabel(),
- createFeatureLabel(featureName),
- createStoryLabel(scenario.getName()),
- createSuiteLabel(featureName),
- createTestClassLabel(scenario.getName()),
- createFrameworkLabel("cucumber5jvm"),
- createLanguageLabel("java"),
- createLabel("gherkin_uri", uri.toString())
- ));
+ scenarioLabels.addAll(
+ Arrays.asList(
+ createHostLabel(),
+ createThreadLabel(),
+ createFeatureLabel(featureName),
+ createStoryLabel(scenario.getName()),
+ createSuiteLabel(featureName),
+ createTestClassLabel(scenario.getName()),
+ createFrameworkLabel("cucumber5jvm"),
+ createLanguageLabel("java"),
+ createLabel("gherkin_uri", uri.toString())
+ )
+ );
featurePackage(uri.toString(), featureName)
.map(ResultsUtils::createPackageLabel)
@@ -156,8 +158,10 @@ private void tryHandleNamedLink(final String tagString) {
final String name = tagString.split(COMPOSITE_TAG_DELIMITER)[1];
scenarioLinks.add(ResultsUtils.createLink(null, name, null, type));
} else {
- LOGGER.warn("Composite named tag {} does not match regex {}. Skipping", tagString,
- namedLinkPatternString);
+ LOGGER.warn(
+ "Composite named tag {} does not match regex {}. Skipping", tagString,
+ namedLinkPatternString
+ );
}
}
@@ -175,10 +179,12 @@ private Optional featurePackage(final String uriString, final String fea
final String schemeSpecificPart = uri.normalize().getSchemeSpecificPart();
final Stream folders = Stream.of(schemeSpecificPart.replaceAll("\\.", "_").split("/"));
final Stream name = Stream.of(featureName);
- return Optional.of(Stream.concat(folders, name)
- .filter(Objects::nonNull)
- .filter(s -> !s.isEmpty())
- .collect(Collectors.joining(".")));
+ return Optional.of(
+ Stream.concat(folders, name)
+ .filter(Objects::nonNull)
+ .filter(s -> !s.isEmpty())
+ .collect(Collectors.joining("."))
+ );
}
private static Optional safeUri(final String uri) {
diff --git a/allure-cucumber5-jvm/src/main/java/io/qameta/allure/cucumber5jvm/TagParser.java b/allure-cucumber5-jvm/src/main/java/io/qameta/allure/cucumber5jvm/TagParser.java
index 5e980a98..e2e31d22 100644
--- a/allure-cucumber5-jvm/src/main/java/io/qameta/allure/cucumber5jvm/TagParser.java
+++ b/allure-cucumber5-jvm/src/main/java/io/qameta/allure/cucumber5jvm/TagParser.java
@@ -51,9 +51,9 @@ public boolean isKnown() {
private boolean getStatusDetailByTag(final String tagName) {
return scenario.getTags().stream()
- .anyMatch(tag -> tag.equalsIgnoreCase(tagName))
- || feature.getTags().stream()
- .anyMatch(tag -> tag.getName().equalsIgnoreCase(tagName));
+ .anyMatch(tag -> tag.equalsIgnoreCase(tagName))
+ || feature.getTags().stream()
+ .anyMatch(tag -> tag.getName().equalsIgnoreCase(tagName));
}
public boolean isResultTag(final String tag) {
diff --git a/allure-cucumber5-jvm/src/main/java/io/qameta/allure/cucumber5jvm/testsourcemodel/TestSourcesModel.java b/allure-cucumber5-jvm/src/main/java/io/qameta/allure/cucumber5jvm/testsourcemodel/TestSourcesModel.java
index 356783cb..7249c97a 100644
--- a/allure-cucumber5-jvm/src/main/java/io/qameta/allure/cucumber5jvm/testsourcemodel/TestSourcesModel.java
+++ b/allure-cucumber5-jvm/src/main/java/io/qameta/allure/cucumber5jvm/testsourcemodel/TestSourcesModel.java
@@ -28,6 +28,7 @@
import gherkin.ast.Step;
import gherkin.ast.TableRow;
import io.cucumber.plugin.event.TestSourceRead;
+
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
@@ -38,7 +39,8 @@ public final class TestSourcesModel {
private final Map> pathToNodeMap = new HashMap<>();
public static ScenarioDefinition getScenarioDefinition(final AstNode astNode) {
- return astNode.node instanceof ScenarioDefinition ? (ScenarioDefinition) astNode.node
+ return astNode.node instanceof ScenarioDefinition
+ ? (ScenarioDefinition) astNode.node
: (ScenarioDefinition) astNode.parent.parent.node;
}
@@ -73,8 +75,10 @@ private void parseGherkinSource(final URI path) {
final Parser parser = new Parser<>(new AstBuilder());
final TokenMatcher matcher = new TokenMatcher();
try {
- final GherkinDocument gherkinDocument = parser.parse(pathToReadEventMap.get(path).getSource(),
- matcher);
+ final GherkinDocument gherkinDocument = parser.parse(
+ pathToReadEventMap.get(path).getSource(),
+ matcher
+ );
pathToAstMap.put(path, gherkinDocument);
final Map nodeMap = new HashMap<>();
final AstNode currentParent = new AstNode(gherkinDocument.getFeature(), null);
@@ -83,8 +87,10 @@ private void parseGherkinSource(final URI path) {
}
pathToNodeMap.put(path, nodeMap);
} catch (ParserException e) {
- throw new IllegalStateException("You are using a plugin that only supports till Gherkin 5.\n"
- + "Please check if the Gherkin provided follows the standard of Gherkin 5\n", e
+ throw new IllegalStateException(
+ "You are using a plugin that only supports till Gherkin 5.\n"
+ + "Please check if the Gherkin provided follows the standard of Gherkin 5\n",
+ e
);
}
}
diff --git a/allure-cucumber5-jvm/src/test/java/io/qameta/allure/cucumber5jvm/AllureCucumber5JvmTest.java b/allure-cucumber5-jvm/src/test/java/io/qameta/allure/cucumber5jvm/AllureCucumber5JvmTest.java
index 7456106a..11f28271 100644
--- a/allure-cucumber5-jvm/src/test/java/io/qameta/allure/cucumber5jvm/AllureCucumber5JvmTest.java
+++ b/allure-cucumber5-jvm/src/test/java/io/qameta/allure/cucumber5jvm/AllureCucumber5JvmTest.java
@@ -80,6 +80,8 @@ void shouldSetName() {
assertThat(testResults)
.extracting(TestResult::getName)
.containsExactlyInAnyOrder("Add a to b");
+ assertThat(testResults.get(0).getTitlePath())
+ .containsExactly("src", "test", "resources", "features", "simple.feature", "Simple feature");
}
@AllureFeatures.PassedTests
@@ -183,7 +185,7 @@ void shouldSetDescription() {
final AllureResults results = runFeature("features/description.feature");
final String expected = "This is description for current feature.\n"
- + "It should appear on each scenario in report";
+ + "It should appear on each scenario in report";
final List testResults = results.getTestResults();
assertThat(testResults)
@@ -200,7 +202,7 @@ void shouldSetScenarioDescription() {
final AllureResults results = runFeature("features/scenario_description.feature");
final String expected = "This is description for current feature.\n"
- + "It should appear on each scenario in report";
+ + "It should appear on each scenario in report";
final List testResults = results.getTestResults();
assertThat(testResults)
@@ -238,11 +240,12 @@ void shouldAddDataTableAttachment() {
final String attachmentContent = new String(bytes, StandardCharsets.UTF_8);
assertThat(attachmentContent)
- .isEqualTo("""
- name\tlogin\temail
- Viktor\tclicman\tclicman@ya.ru
- Viktor2\tclicman2\tclicman2@ya.ru
+ .isEqualTo(
"""
+ name\tlogin\temail
+ Viktor\tclicman\tclicman@ya.ru
+ Viktor2\tclicman2\tclicman2@ya.ru
+ """
);
}
@@ -381,8 +384,14 @@ void shouldAddTags() {
@AllureFeatures.Links
@ExtendWith(SystemPropertyExtension.class)
- @SystemProperty(name = "allure.link.issue.pattern", value = "https://example.org/issue/{}")
- @SystemProperty(name = "allure.link.tms.pattern", value = "https://example.org/tms/{}")
+ @SystemProperty(
+ name = "allure.link.issue.pattern",
+ value = "https://example.org/issue/{}"
+ )
+ @SystemProperty(
+ name = "allure.link.tms.pattern",
+ value = "https://example.org/tms/{}"
+ )
@Test
void shouldAddLinks() {
final AllureResults results = runFeature("features/tags.feature");
@@ -511,8 +520,10 @@ void shouldSupportDryRunForSimpleFeatures() {
@AllureFeatures.Base
@Test
void shouldSupportDryRunForHooks() {
- final AllureResults results = runFeature("features/hooks.feature", "--dry-run", "-t",
- "@WithHooks or @BeforeHookWithException or @AfterHookWithException");
+ final AllureResults results = runFeature(
+ "features/hooks.feature", "--dry-run", "-t",
+ "@WithHooks or @BeforeHookWithException or @AfterHookWithException"
+ );
final TestResult tr1 = results.getTestResultByName("Simple scenario with Before and After hooks");
@@ -652,8 +663,10 @@ void shouldProcessScenariosInParallelMode() {
@AllureFeatures.Stages
@Test
void shouldDisplayHooksAsStages() {
- final AllureResults results = runFeature("features/hooks.feature", "-t",
- "@WithHooks or @BeforeHookWithException or @AfterHookWithException");
+ final AllureResults results = runFeature(
+ "features/hooks.feature", "-t",
+ "@WithHooks or @BeforeHookWithException or @AfterHookWithException"
+ );
final TestResult tr1 = results.getTestResultByName("Simple scenario with Before and After hooks");
final TestResult tr2 = results.getTestResultByName("Simple scenario with Before hook with Exception");
@@ -691,7 +704,6 @@ void shouldDisplayHooksAsStages() {
tuple("Then result is 15", Status.SKIPPED)
);
-
assertThat(results.getTestResultContainersForTestResult(tr2))
.flatExtracting(TestResultContainer::getBefores)
.extracting(FixtureResult::getName, FixtureResult::getStatus)
@@ -737,8 +749,14 @@ void shouldHandleAmbigiousStepsExceptions() {
);
}
- @ResourceLock(value = SYSTEM_PROPERTIES, mode = READ_WRITE)
- @SystemProperty(name = "allure.label.x-provided", value = "cucumberjvm5-test-provided")
+ @ResourceLock(
+ value = SYSTEM_PROPERTIES,
+ mode = READ_WRITE
+ )
+ @SystemProperty(
+ name = "allure.label.x-provided",
+ value = "cucumberjvm5-test-provided"
+ )
@Test
void shouldSupportProvidedLabels() {
final AllureResults results = runFeature("features/simple.feature");
@@ -783,17 +801,22 @@ void shouldSupportRuntimeApiInStepsWhenHooksAreUsed() {
);
}
- @SystemProperty(name = "cucumber.junit-platform.naming-strategy", value = "long")
+ @SystemProperty(
+ name = "cucumber.junit-platform.naming-strategy",
+ value = "long"
+ )
@Step
private AllureResults runFeature(final String featureResource,
final String... moreOptions) {
return RunUtils.runTests(lifecycle -> {
final AllureCucumber5Jvm cucumber5jvm = new AllureCucumber5Jvm(lifecycle);
final Supplier classLoader = ClassLoaders::getDefaultClassLoader;
- final List opts = new ArrayList<>(Arrays.asList(
- "--glue", "io.qameta.allure.cucumber5jvm.samples",
- "--plugin", "null_summary"
- ));
+ final List opts = new ArrayList<>(
+ Arrays.asList(
+ "--glue", "io.qameta.allure.cucumber5jvm.samples",
+ "--plugin", "null_summary"
+ )
+ );
opts.addAll(Arrays.asList(moreOptions));
final FeatureWithLines featureWithLines = FeatureWithLines.parse("src/test/resources/" + featureResource);
final RuntimeOptions options = new CommandlineOptionsParser()
@@ -801,8 +824,7 @@ private AllureResults runFeature(final String featureResource,
final EventBus bus = new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID);
final FeatureParser parser = new FeatureParser(bus::generateId);
- final FeaturePathFeatureSupplier supplier
- = new FeaturePathFeatureSupplier(classLoader, options, parser);
+ final FeaturePathFeatureSupplier supplier = new FeaturePathFeatureSupplier(classLoader, options, parser);
final Runtime runtime = Runtime.builder()
.withClassLoader(classLoader)
@@ -815,5 +837,4 @@ private AllureResults runFeature(final String featureResource,
});
}
-
}
diff --git a/allure-cucumber5-jvm/src/test/java/io/qameta/allure/cucumber5jvm/samples/AmbigiousSteps.java b/allure-cucumber5-jvm/src/test/java/io/qameta/allure/cucumber5jvm/samples/AmbigiousSteps.java
index 3e605bb6..fe41e55b 100644
--- a/allure-cucumber5-jvm/src/test/java/io/qameta/allure/cucumber5jvm/samples/AmbigiousSteps.java
+++ b/allure-cucumber5-jvm/src/test/java/io/qameta/allure/cucumber5jvm/samples/AmbigiousSteps.java
@@ -34,7 +34,7 @@ public void ambigious_2() {
}
@Then("^something bad should happen")
- public void somethingBadStep(){
+ public void somethingBadStep() {
//nothing here
}
}
diff --git a/allure-cucumber5-jvm/src/test/java/io/qameta/allure/cucumber5jvm/samples/AttachmentSteps.java b/allure-cucumber5-jvm/src/test/java/io/qameta/allure/cucumber5jvm/samples/AttachmentSteps.java
index a4489b08..1310cb9d 100644
--- a/allure-cucumber5-jvm/src/test/java/io/qameta/allure/cucumber5jvm/samples/AttachmentSteps.java
+++ b/allure-cucumber5-jvm/src/test/java/io/qameta/allure/cucumber5jvm/samples/AttachmentSteps.java
@@ -15,29 +15,25 @@
*/
package io.qameta.allure.cucumber5jvm.samples;
-import io.cucumber.java.Scenario;
import io.cucumber.java.Before;
+import io.cucumber.java.Scenario;
import io.cucumber.java.en.Given;
-public class AttachmentSteps
-{
+public class AttachmentSteps {
private Scenario scenario;
@Before("@attachments")
- public void setup(Scenario scenario)
- {
+ public void setup(Scenario scenario) {
this.scenario = scenario;
}
@Given("step with scenario write")
- public void stepWithScenarioWrite()
- {
+ public void stepWithScenarioWrite() {
scenario.write("text attachment");
}
@Given("step with scenario embed")
- public void stepWithScenarioEmbed()
- {
- scenario.embed("image attachment".getBytes(), "image/png","ImageAttachment");
+ public void stepWithScenarioEmbed() {
+ scenario.embed("image attachment".getBytes(), "image/png", "ImageAttachment");
}
}
diff --git a/allure-cucumber5-jvm/src/test/java/io/qameta/allure/cucumber5jvm/samples/HookSteps.java b/allure-cucumber5-jvm/src/test/java/io/qameta/allure/cucumber5jvm/samples/HookSteps.java
index 775d9086..8c7162e7 100644
--- a/allure-cucumber5-jvm/src/test/java/io/qameta/allure/cucumber5jvm/samples/HookSteps.java
+++ b/allure-cucumber5-jvm/src/test/java/io/qameta/allure/cucumber5jvm/samples/HookSteps.java
@@ -25,22 +25,22 @@
public class HookSteps {
@Before("@WithHooks")
- public void beforeHook(){
+ public void beforeHook() {
// nothing
}
@After("@WithHooks")
- public void afterHook(){
+ public void afterHook() {
// nothing
}
@Before("@BeforeHookWithException")
- public void beforeHookWithException(){
+ public void beforeHookWithException() {
Assertions.fail("Exception in Hook step");
}
@After("@AfterHookWithException")
- public void afterHookWithException(){
+ public void afterHookWithException() {
Assertions.fail("Exception in Hook step");
}
diff --git a/allure-cucumber5-jvm/src/test/java/io/qameta/allure/cucumber5jvm/samples/RuntimeApiSteps.java b/allure-cucumber5-jvm/src/test/java/io/qameta/allure/cucumber5jvm/samples/RuntimeApiSteps.java
index 581b2afe..f507b64a 100644
--- a/allure-cucumber5-jvm/src/test/java/io/qameta/allure/cucumber5jvm/samples/RuntimeApiSteps.java
+++ b/allure-cucumber5-jvm/src/test/java/io/qameta/allure/cucumber5jvm/samples/RuntimeApiSteps.java
@@ -27,12 +27,12 @@
public class RuntimeApiSteps {
@Before("@beforeScenario")
- public void beforeScenario(){
+ public void beforeScenario() {
// nothing
}
@Before("@beforeFeature")
- public void beforeFeature(){
+ public void beforeFeature() {
// nothing
}
diff --git a/allure-cucumber5-jvm/src/test/resources/allure.properties b/allure-cucumber5-jvm/src/test/resources/allure.properties
index dbfefee4..8e296072 100644
--- a/allure-cucumber5-jvm/src/test/resources/allure.properties
+++ b/allure-cucumber5-jvm/src/test/resources/allure.properties
@@ -1,3 +1,4 @@
allure.model.indentOutput=true
allure.results.directory=build/allure-results
allure.label.epic=#project.description#
+allure.label.module=allure-cucumber5-jvm
diff --git a/allure-cucumber6-jvm/src/main/java/io/qameta/allure/cucumber6jvm/AllureCucumber6Jvm.java b/allure-cucumber6-jvm/src/main/java/io/qameta/allure/cucumber6jvm/AllureCucumber6Jvm.java
index e8c71e51..c7384b9b 100644
--- a/allure-cucumber6-jvm/src/main/java/io/qameta/allure/cucumber6jvm/AllureCucumber6Jvm.java
+++ b/allure-cucumber6-jvm/src/main/java/io/qameta/allure/cucumber6jvm/AllureCucumber6Jvm.java
@@ -65,6 +65,8 @@
import java.util.stream.Stream;
import static io.qameta.allure.util.ResultsUtils.createParameter;
+import static io.qameta.allure.util.ResultsUtils.createTitlePath;
+import static io.qameta.allure.util.ResultsUtils.createTitlePathFromSourcePath;
import static io.qameta.allure.util.ResultsUtils.getStatus;
import static io.qameta.allure.util.ResultsUtils.getStatusDetails;
import static io.qameta.allure.util.ResultsUtils.md5;
@@ -72,13 +74,16 @@
/**
* Allure plugin for Cucumber JVM 6.0.
*/
-@SuppressWarnings({
- "ClassDataAbstractionCoupling",
- "ClassFanOutComplexity",
-})
+@SuppressWarnings(
+ {
+ "ClassDataAbstractionCoupling",
+ "ClassFanOutComplexity",
+ }
+)
public class AllureCucumber6Jvm implements ConcurrentEventListener {
private static final String COLON = ":";
+ private static final String NEW_LINE = "\n";
private final AllureLifecycle lifecycle;
@@ -134,31 +139,34 @@ private void handleTestCaseStarted(final TestCaseStarted event) {
final String name = testCase.getName();
-
// the same way full name is generated for
// org.junit.platform.engine.support.descriptor.ClasspathResourceSource
// to support io.qameta.allure.junitplatform.AllurePostDiscoveryFilter
- final String fullName = String.format("%s:%d",
+ final String fullName = String.format(
+ "%s:%d",
getTestCaseUri(testCase),
testCase.getLocation().getLine()
);
final String testCaseUuid = testCase.getId().toString();
+ final List titlePath = createTitlePathFromSourcePath(getTestCaseUri(testCase));
+ titlePath.addAll(createTitlePath(feature.getName()));
+
final TestResult result = new TestResult()
.setUuid(testCaseUuid)
.setTestCaseId(getTestCaseId(testCase))
.setHistoryId(getHistoryId(testCase))
.setFullName(fullName)
+ .setTitlePath(titlePath)
.setName(name)
.setLabels(labelBuilder.getScenarioLabels())
.setLinks(labelBuilder.getScenarioLinks());
- final Scenario scenarioDefinition =
- testSources.getScenarioDefinition(
- testCase.getUri(),
- testCase.getLocation().getLine()
- );
+ final Scenario scenarioDefinition = testSources.getScenarioDefinition(
+ testCase.getUri(),
+ testCase.getLocation().getLine()
+ );
if (scenarioDefinition.getExamplesList() != null) {
result.setParameters(
@@ -169,7 +177,7 @@ private void handleTestCaseStarted(final TestCaseStarted event) {
final String description = Stream.of(feature.getDescription(), scenarioDefinition.getDescription())
.filter(Objects::nonNull)
.filter(s -> !s.isEmpty())
- .collect(Collectors.joining("\n"));
+ .collect(Collectors.joining(NEW_LINE));
if (!description.isEmpty()) {
result.setDescription(description);
@@ -194,9 +202,10 @@ private void handleTestCaseFinished(final TestCaseFinished event) {
.setMuted(tagParser.isMuted())
.setKnown(tagParser.isKnown());
- lifecycle.updateTestCase(uuid, testResult -> testResult
- .setStatus(status)
- .setStatusDetails(statusDetails)
+ lifecycle.updateTestCase(
+ uuid, testResult -> testResult
+ .setStatus(status)
+ .setStatusDetails(statusDetails)
);
lifecycle.stopTestCase(uuid);
@@ -256,9 +265,10 @@ private void handleStartFixtureHook(final TestCase testCase,
final String containerUuid = hookStepContainerUuid
.computeIfAbsent(hookId, unused -> UUID.randomUUID().toString());
- lifecycle.startTestContainer(new TestResultContainer()
- .setUuid(containerUuid)
- .setChildren(Collections.singletonList(uuid))
+ lifecycle.startTestContainer(
+ new TestResultContainer()
+ .setUuid(containerUuid)
+ .setChildren(Collections.singletonList(uuid))
);
final FixtureResult hookResult = new FixtureResult()
@@ -339,15 +349,14 @@ private Status translateTestCaseStatus(final Result testCaseResult) {
}
private List getExamplesAsParameters(
- final Scenario scenario,
- final TestCase localCurrentTestCase) {
- final Optional maybeExample =
- scenario.getExamplesList().stream()
- .filter(example -> example.getTableBodyList().stream()
- .anyMatch(row -> row.getLocation().getLine()
- == localCurrentTestCase.getLocation().getLine())
- )
- .findFirst();
+ final Scenario scenario,
+ final TestCase localCurrentTestCase) {
+ final Optional maybeExample = scenario.getExamplesList().stream()
+ .filter(
+ example -> example.getTableBodyList().stream()
+ .anyMatch(row -> row.getLocation().getLine() == localCurrentTestCase.getLocation().getLine())
+ )
+ .findFirst();
if (!maybeExample.isPresent()) {
return Collections.emptyList();
@@ -379,14 +388,16 @@ private void createDataTableAttachment(final DataTableArgument dataTableArgument
final StringBuilder dataTableCsv = new StringBuilder();
for (List columns : rowsInTable) {
if (!columns.isEmpty()) {
- final String rowValue = columns.stream().collect(Collectors.joining("\t", "", "\n"));
+ final String rowValue = columns.stream().collect(Collectors.joining("\t", "", NEW_LINE));
dataTableCsv.append(rowValue);
}
}
final String attachmentSource = lifecycle
.prepareAttachment("Data table", "text/tab-separated-values", "csv");
- lifecycle.writeAttachment(attachmentSource,
- new ByteArrayInputStream(dataTableCsv.toString().getBytes(StandardCharsets.UTF_8)));
+ lifecycle.writeAttachment(
+ attachmentSource,
+ new ByteArrayInputStream(dataTableCsv.toString().getBytes(StandardCharsets.UTF_8))
+ );
}
private void handleStopHookStep(final Result eventResult,
@@ -403,9 +414,10 @@ private void handleStopHookStep(final Result eventResult,
final StatusDetails statusDetails = getStatusDetails(eventResult.getError())
.orElseGet(StatusDetails::new);
- lifecycle.updateFixture(uuid, result -> result
- .setStatus(status)
- .setStatusDetails(statusDetails)
+ lifecycle.updateFixture(
+ uuid, result -> result
+ .setStatus(status)
+ .setStatusDetails(statusDetails)
);
lifecycle.stopFixture(uuid);
@@ -420,8 +432,7 @@ private void handleStopStep(final TestCase testCase,
final Status stepStatus = translateTestCaseStatus(eventResult);
- final StatusDetails statusDetails
- = eventResult.getStatus() == io.cucumber.plugin.event.Status.UNDEFINED
+ final StatusDetails statusDetails = eventResult.getStatus() == io.cucumber.plugin.event.Status.UNDEFINED
? new StatusDetails().setMessage("Undefined Step. Please add step definition")
: getStatusDetails(eventResult.getError())
.orElse(new StatusDetails());
diff --git a/allure-cucumber6-jvm/src/main/java/io/qameta/allure/cucumber6jvm/LabelBuilder.java b/allure-cucumber6-jvm/src/main/java/io/qameta/allure/cucumber6jvm/LabelBuilder.java
index 9684b9d5..794b78af 100644
--- a/allure-cucumber6-jvm/src/main/java/io/qameta/allure/cucumber6jvm/LabelBuilder.java
+++ b/allure-cucumber6-jvm/src/main/java/io/qameta/allure/cucumber6jvm/LabelBuilder.java
@@ -48,7 +48,7 @@
/**
* Scenario labels and links builder.
*/
-@SuppressWarnings({"CyclomaticComplexity", "MultipleStringLiterals"})
+@SuppressWarnings({"CyclomaticComplexity", "MultipleStringLiterals", "PMD.CognitiveComplexity"})
class LabelBuilder {
private static final Logger LOGGER = LoggerFactory.getLogger(LabelBuilder.class);
private static final String COMPOSITE_TAG_DELIMITER = "=";
@@ -113,17 +113,19 @@ class LabelBuilder {
final URI uri = scenario.getUri();
scenarioLabels.addAll(ResultsUtils.getProvidedLabels());
- scenarioLabels.addAll(Arrays.asList(
- createHostLabel(),
- createThreadLabel(),
- createFeatureLabel(featureName),
- createStoryLabel(scenario.getName()),
- createSuiteLabel(featureName),
- createTestClassLabel(scenario.getName()),
- createFrameworkLabel("cucumber6jvm"),
- createLanguageLabel("java"),
- createLabel("gherkin_uri", uri.toString())
- ));
+ scenarioLabels.addAll(
+ Arrays.asList(
+ createHostLabel(),
+ createThreadLabel(),
+ createFeatureLabel(featureName),
+ createStoryLabel(scenario.getName()),
+ createSuiteLabel(featureName),
+ createTestClassLabel(scenario.getName()),
+ createFrameworkLabel("cucumber6jvm"),
+ createLanguageLabel("java"),
+ createLabel("gherkin_uri", uri.toString())
+ )
+ );
featurePackage(uri.toString(), featureName)
.map(ResultsUtils::createPackageLabel)
@@ -156,8 +158,10 @@ private void tryHandleNamedLink(final String tagString) {
final String name = tagString.split(COMPOSITE_TAG_DELIMITER)[1];
scenarioLinks.add(ResultsUtils.createLink(null, name, null, type));
} else {
- LOGGER.warn("Composite named tag {} does not match regex {}. Skipping", tagString,
- namedLinkPatternString);
+ LOGGER.warn(
+ "Composite named tag {} does not match regex {}. Skipping", tagString,
+ namedLinkPatternString
+ );
}
}
@@ -175,10 +179,12 @@ private Optional featurePackage(final String uriString, final String fea
final String schemeSpecificPart = uri.normalize().getSchemeSpecificPart();
final Stream folders = Stream.of(schemeSpecificPart.replaceAll("\\.", "_").split("/"));
final Stream name = Stream.of(featureName);
- return Optional.of(Stream.concat(folders, name)
- .filter(Objects::nonNull)
- .filter(s -> !s.isEmpty())
- .collect(Collectors.joining(".")));
+ return Optional.of(
+ Stream.concat(folders, name)
+ .filter(Objects::nonNull)
+ .filter(s -> !s.isEmpty())
+ .collect(Collectors.joining("."))
+ );
}
private static Optional safeUri(final String uri) {
diff --git a/allure-cucumber6-jvm/src/main/java/io/qameta/allure/cucumber6jvm/TagParser.java b/allure-cucumber6-jvm/src/main/java/io/qameta/allure/cucumber6jvm/TagParser.java
index 3d31fdef..66644299 100644
--- a/allure-cucumber6-jvm/src/main/java/io/qameta/allure/cucumber6jvm/TagParser.java
+++ b/allure-cucumber6-jvm/src/main/java/io/qameta/allure/cucumber6jvm/TagParser.java
@@ -51,9 +51,9 @@ public boolean isKnown() {
private boolean getStatusDetailByTag(final String tagName) {
return scenario.getTags().stream()
- .anyMatch(tag -> tag.equalsIgnoreCase(tagName))
- || feature.getTagsList().stream()
- .anyMatch(tag -> tag.getName().equalsIgnoreCase(tagName));
+ .anyMatch(tag -> tag.equalsIgnoreCase(tagName))
+ || feature.getTagsList().stream()
+ .anyMatch(tag -> tag.getName().equalsIgnoreCase(tagName));
}
public boolean isResultTag(final String tag) {
diff --git a/allure-cucumber6-jvm/src/main/java/io/qameta/allure/cucumber6jvm/testsourcemodel/TestSourcesModel.java b/allure-cucumber6-jvm/src/main/java/io/qameta/allure/cucumber6jvm/testsourcemodel/TestSourcesModel.java
index 2a338e95..2fa406ea 100644
--- a/allure-cucumber6-jvm/src/main/java/io/qameta/allure/cucumber6jvm/testsourcemodel/TestSourcesModel.java
+++ b/allure-cucumber6-jvm/src/main/java/io/qameta/allure/cucumber6jvm/testsourcemodel/TestSourcesModel.java
@@ -73,14 +73,16 @@ private void parseGherkinSource(final URI path) {
final String source = pathToReadEventMap.get(path).getSource();
final List sources = singletonList(
- makeSourceEnvelope(source, path.toString()));
+ makeSourceEnvelope(source, path.toString())
+ );
final List envelopes = Gherkin.fromSources(
sources,
true,
true,
true,
- () -> String.valueOf(UUID.randomUUID())).collect(toList());
+ () -> String.valueOf(UUID.randomUUID())
+ ).collect(toList());
final GherkinDocument gherkinDocument = envelopes.stream()
.filter(Messages.Envelope::hasGherkinDocument)
@@ -99,7 +101,7 @@ private void parseGherkinSource(final URI path) {
}
private void processFeatureDefinition(
- final Map nodeMap, final FeatureChild child, final AstNode currentParent) {
+ final Map nodeMap, final FeatureChild child, final AstNode currentParent) {
if (child.hasBackground()) {
processBackgroundDefinition(nodeMap, child.getBackground(), currentParent);
} else if (child.hasScenario()) {
@@ -114,8 +116,7 @@ private void processFeatureDefinition(
}
private void processBackgroundDefinition(
- final Map nodeMap, final Background background, final AstNode currentParent
- ) {
+ final Map nodeMap, final Background background, final AstNode currentParent) {
final AstNode childNode = createAstNode(background, currentParent);
nodeMap.put(background.getLocation().getLine(), childNode);
for (Step step : background.getStepsList()) {
@@ -124,7 +125,7 @@ private void processBackgroundDefinition(
}
private void processScenarioDefinition(
- final Map nodeMap, final Scenario child, final AstNode currentParent) {
+ final Map nodeMap, final Scenario child, final AstNode currentParent) {
final AstNode childNode = createAstNode(child, currentParent);
nodeMap.put(child.getLocation().getLine(), childNode);
for (Step step : child.getStepsList()) {
@@ -136,7 +137,7 @@ private void processScenarioDefinition(
}
private void processRuleDefinition(
- final Map nodeMap, final RuleChild child, final AstNode currentParent) {
+ final Map nodeMap, final RuleChild child, final AstNode currentParent) {
if (child.hasBackground()) {
processBackgroundDefinition(nodeMap, child.getBackground(), currentParent);
} else if (child.hasScenario()) {
@@ -145,8 +146,7 @@ private void processRuleDefinition(
}
private void processScenarioOutlineExamples(
- final Map nodeMap, final Scenario scenarioOutline, final AstNode parent
- ) {
+ final Map nodeMap, final Scenario scenarioOutline, final AstNode parent) {
for (Examples examples : scenarioOutline.getExamplesList()) {
final AstNode examplesNode = createAstNode(examples, parent);
final TableRow headerRow = examples.getTableHeader();
diff --git a/allure-cucumber6-jvm/src/test/java/io/qameta/allure/cucumber6jvm/AllureCucumber6JvmTest.java b/allure-cucumber6-jvm/src/test/java/io/qameta/allure/cucumber6jvm/AllureCucumber6JvmTest.java
index 75cc8495..6788dc6c 100644
--- a/allure-cucumber6-jvm/src/test/java/io/qameta/allure/cucumber6jvm/AllureCucumber6JvmTest.java
+++ b/allure-cucumber6-jvm/src/test/java/io/qameta/allure/cucumber6jvm/AllureCucumber6JvmTest.java
@@ -80,6 +80,8 @@ void shouldSetName() {
assertThat(testResults)
.extracting(TestResult::getName)
.containsExactlyInAnyOrder("Add a to b");
+ assertThat(testResults.get(0).getTitlePath())
+ .containsExactly("src", "test", "resources", "features", "simple.feature", "Simple feature");
}
@AllureFeatures.PassedTests
@@ -183,7 +185,7 @@ void shouldSetDescription() {
final AllureResults results = runFeature("features/description.feature");
final String expected = "This is description for current feature.\n"
- + "It should appear on each scenario in report";
+ + "It should appear on each scenario in report";
final List testResults = results.getTestResults();
assertThat(testResults)
@@ -200,7 +202,7 @@ void shouldSetScenarioDescription() {
final AllureResults results = runFeature("features/scenario_description.feature");
final String expected = "This is description for current feature.\n"
- + "It should appear on each scenario in report";
+ + "It should appear on each scenario in report";
final List testResults = results.getTestResults();
assertThat(testResults)
@@ -238,11 +240,12 @@ void shouldAddDataTableAttachment() {
final String attachmentContent = new String(bytes, StandardCharsets.UTF_8);
assertThat(attachmentContent)
- .isEqualTo("""
- name\tlogin\temail
- Viktor\tclicman\tclicman@ya.ru
- Viktor2\tclicman2\tclicman2@ya.ru
+ .isEqualTo(
"""
+ name\tlogin\temail
+ Viktor\tclicman\tclicman@ya.ru
+ Viktor2\tclicman2\tclicman2@ya.ru
+ """
);
}
@@ -381,8 +384,14 @@ void shouldAddTags() {
@AllureFeatures.Links
@ExtendWith(SystemPropertyExtension.class)
- @SystemProperty(name = "allure.link.issue.pattern", value = "https://example.org/issue/{}")
- @SystemProperty(name = "allure.link.tms.pattern", value = "https://example.org/tms/{}")
+ @SystemProperty(
+ name = "allure.link.issue.pattern",
+ value = "https://example.org/issue/{}"
+ )
+ @SystemProperty(
+ name = "allure.link.tms.pattern",
+ value = "https://example.org/tms/{}"
+ )
@Test
void shouldAddLinks() {
final AllureResults results = runFeature("features/tags.feature");
@@ -511,8 +520,10 @@ void shouldSupportDryRunForSimpleFeatures() {
@AllureFeatures.Base
@Test
void shouldSupportDryRunForHooks() {
- final AllureResults results = runFeature("features/hooks.feature", "--dry-run", "-t",
- "@WithHooks or @BeforeHookWithException or @AfterHookWithException");
+ final AllureResults results = runFeature(
+ "features/hooks.feature", "--dry-run", "-t",
+ "@WithHooks or @BeforeHookWithException or @AfterHookWithException"
+ );
final TestResult tr1 = results.getTestResultByName("Simple scenario with Before and After hooks");
@@ -652,8 +663,10 @@ void shouldProcessScenariosInParallelMode() {
@AllureFeatures.Stages
@Test
void shouldDisplayHooksAsStages() {
- final AllureResults results = runFeature("features/hooks.feature", "-t",
- "@WithHooks or @BeforeHookWithException or @AfterHookWithException");
+ final AllureResults results = runFeature(
+ "features/hooks.feature", "-t",
+ "@WithHooks or @BeforeHookWithException or @AfterHookWithException"
+ );
final TestResult tr1 = results.getTestResultByName("Simple scenario with Before and After hooks");
final TestResult tr2 = results.getTestResultByName("Simple scenario with Before hook with Exception");
@@ -691,7 +704,6 @@ void shouldDisplayHooksAsStages() {
tuple("Then result is 15", Status.SKIPPED)
);
-
assertThat(results.getTestResultContainersForTestResult(tr2))
.flatExtracting(TestResultContainer::getBefores)
.extracting(FixtureResult::getName, FixtureResult::getStatus)
@@ -737,8 +749,14 @@ void shouldHandleAmbigiousStepsExceptions() {
);
}
- @ResourceLock(value = SYSTEM_PROPERTIES, mode = READ_WRITE)
- @SystemProperty(name = "allure.label.x-provided", value = "cucumberjvm6-test-provided")
+ @ResourceLock(
+ value = SYSTEM_PROPERTIES,
+ mode = READ_WRITE
+ )
+ @SystemProperty(
+ name = "allure.label.x-provided",
+ value = "cucumberjvm6-test-provided"
+ )
@Test
void shouldSupportProvidedLabels() {
final AllureResults results = runFeature("features/simple.feature");
@@ -783,17 +801,22 @@ void shouldSupportRuntimeApiInStepsWhenHooksAreUsed() {
);
}
- @SystemProperty(name = "cucumber.junit-platform.naming-strategy", value = "long")
+ @SystemProperty(
+ name = "cucumber.junit-platform.naming-strategy",
+ value = "long"
+ )
@Step
private AllureResults runFeature(final String featureResource,
final String... moreOptions) {
return RunUtils.runTests(lifecycle -> {
final AllureCucumber6Jvm cucumber6jvm = new AllureCucumber6Jvm(lifecycle);
final Supplier classLoader = ClassLoaders::getDefaultClassLoader;
- final List opts = new ArrayList<>(Arrays.asList(
- "--glue", "io.qameta.allure.cucumber6jvm.samples",
- "--plugin", "null_summary"
- ));
+ final List opts = new ArrayList<>(
+ Arrays.asList(
+ "--glue", "io.qameta.allure.cucumber6jvm.samples",
+ "--plugin", "null_summary"
+ )
+ );
opts.addAll(Arrays.asList(moreOptions));
final FeatureWithLines featureWithLines = FeatureWithLines.parse("src/test/resources/" + featureResource);
final RuntimeOptions options = new CommandlineOptionsParser(System.out)
@@ -801,8 +824,7 @@ private AllureResults runFeature(final String featureResource,
final EventBus bus = new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID);
final FeatureParser parser = new FeatureParser(bus::generateId);
- final FeaturePathFeatureSupplier supplier
- = new FeaturePathFeatureSupplier(classLoader, options, parser);
+ final FeaturePathFeatureSupplier supplier = new FeaturePathFeatureSupplier(classLoader, options, parser);
final Runtime runtime = Runtime.builder()
.withClassLoader(classLoader)
diff --git a/allure-cucumber6-jvm/src/test/java/io/qameta/allure/cucumber6jvm/samples/AmbigiousSteps.java b/allure-cucumber6-jvm/src/test/java/io/qameta/allure/cucumber6jvm/samples/AmbigiousSteps.java
index 5a2e5a12..22a70c07 100644
--- a/allure-cucumber6-jvm/src/test/java/io/qameta/allure/cucumber6jvm/samples/AmbigiousSteps.java
+++ b/allure-cucumber6-jvm/src/test/java/io/qameta/allure/cucumber6jvm/samples/AmbigiousSteps.java
@@ -34,7 +34,7 @@ public void ambigious_2() {
}
@Then("^something bad should happen")
- public void somethingBadStep(){
+ public void somethingBadStep() {
//nothing here
}
}
diff --git a/allure-cucumber6-jvm/src/test/java/io/qameta/allure/cucumber6jvm/samples/AttachmentSteps.java b/allure-cucumber6-jvm/src/test/java/io/qameta/allure/cucumber6jvm/samples/AttachmentSteps.java
index 6a9393a8..2b72de70 100644
--- a/allure-cucumber6-jvm/src/test/java/io/qameta/allure/cucumber6jvm/samples/AttachmentSteps.java
+++ b/allure-cucumber6-jvm/src/test/java/io/qameta/allure/cucumber6jvm/samples/AttachmentSteps.java
@@ -15,8 +15,8 @@
*/
package io.qameta.allure.cucumber6jvm.samples;
-import io.cucumber.java.Scenario;
import io.cucumber.java.Before;
+import io.cucumber.java.Scenario;
import io.cucumber.java.en.Given;
public class AttachmentSteps {
diff --git a/allure-cucumber6-jvm/src/test/java/io/qameta/allure/cucumber6jvm/samples/HookSteps.java b/allure-cucumber6-jvm/src/test/java/io/qameta/allure/cucumber6jvm/samples/HookSteps.java
index d7fe2a8e..5ca9cc61 100644
--- a/allure-cucumber6-jvm/src/test/java/io/qameta/allure/cucumber6jvm/samples/HookSteps.java
+++ b/allure-cucumber6-jvm/src/test/java/io/qameta/allure/cucumber6jvm/samples/HookSteps.java
@@ -25,22 +25,22 @@
public class HookSteps {
@Before("@WithHooks")
- public void beforeHook(){
+ public void beforeHook() {
// nothing
}
@After("@WithHooks")
- public void afterHook(){
+ public void afterHook() {
// nothing
}
@Before("@BeforeHookWithException")
- public void beforeHookWithException(){
+ public void beforeHookWithException() {
Assertions.fail("Exception in Hook step");
}
@After("@AfterHookWithException")
- public void afterHookWithException(){
+ public void afterHookWithException() {
Assertions.fail("Exception in Hook step");
}
diff --git a/allure-cucumber6-jvm/src/test/java/io/qameta/allure/cucumber6jvm/samples/RuntimeApiSteps.java b/allure-cucumber6-jvm/src/test/java/io/qameta/allure/cucumber6jvm/samples/RuntimeApiSteps.java
index 7438ea19..4fd9281d 100644
--- a/allure-cucumber6-jvm/src/test/java/io/qameta/allure/cucumber6jvm/samples/RuntimeApiSteps.java
+++ b/allure-cucumber6-jvm/src/test/java/io/qameta/allure/cucumber6jvm/samples/RuntimeApiSteps.java
@@ -27,12 +27,12 @@
public class RuntimeApiSteps {
@Before("@beforeScenario")
- public void beforeScenario(){
+ public void beforeScenario() {
// nothing
}
@Before("@beforeFeature")
- public void beforeFeature(){
+ public void beforeFeature() {
// nothing
}
diff --git a/allure-cucumber6-jvm/src/test/resources/allure.properties b/allure-cucumber6-jvm/src/test/resources/allure.properties
index dbfefee4..6b16969f 100644
--- a/allure-cucumber6-jvm/src/test/resources/allure.properties
+++ b/allure-cucumber6-jvm/src/test/resources/allure.properties
@@ -1,3 +1,4 @@
allure.model.indentOutput=true
allure.results.directory=build/allure-results
allure.label.epic=#project.description#
+allure.label.module=allure-cucumber6-jvm
diff --git a/allure-cucumber7-jvm/src/main/java/io/qameta/allure/cucumber7jvm/AllureCucumber7Jvm.java b/allure-cucumber7-jvm/src/main/java/io/qameta/allure/cucumber7jvm/AllureCucumber7Jvm.java
index 60883143..ac4ca986 100644
--- a/allure-cucumber7-jvm/src/main/java/io/qameta/allure/cucumber7jvm/AllureCucumber7Jvm.java
+++ b/allure-cucumber7-jvm/src/main/java/io/qameta/allure/cucumber7jvm/AllureCucumber7Jvm.java
@@ -66,6 +66,8 @@
import java.util.stream.Stream;
import static io.qameta.allure.util.ResultsUtils.createParameter;
+import static io.qameta.allure.util.ResultsUtils.createTitlePath;
+import static io.qameta.allure.util.ResultsUtils.createTitlePathFromSourcePath;
import static io.qameta.allure.util.ResultsUtils.getStatus;
import static io.qameta.allure.util.ResultsUtils.getStatusDetails;
import static io.qameta.allure.util.ResultsUtils.md5;
@@ -73,13 +75,16 @@
/**
* Allure plugin for Cucumber JVM 7.0.
*/
-@SuppressWarnings({
- "ClassDataAbstractionCoupling",
- "ClassFanOutComplexity",
-})
+@SuppressWarnings(
+ {
+ "ClassDataAbstractionCoupling",
+ "ClassFanOutComplexity",
+ }
+)
public class AllureCucumber7Jvm implements ConcurrentEventListener {
private static final String COLON = ":";
+ private static final String NEW_LINE = "\n";
private final AllureLifecycle lifecycle;
@@ -135,31 +140,34 @@ private void handleTestCaseStarted(final TestCaseStarted event) {
final String name = testCase.getName();
-
// the same way full name is generated for
// org.junit.platform.engine.support.descriptor.ClasspathResourceSource
// to support io.qameta.allure.junitplatform.AllurePostDiscoveryFilter
- final String fullName = String.format("%s:%d",
+ final String fullName = String.format(
+ "%s:%d",
getTestCaseUri(testCase),
testCase.getLocation().getLine()
);
final String testCaseUuid = testCase.getId().toString();
+ final List titlePath = createTitlePathFromSourcePath(getTestCaseUri(testCase));
+ titlePath.addAll(createTitlePath(feature.getName()));
+
final TestResult result = new TestResult()
.setUuid(testCaseUuid)
.setTestCaseId(getTestCaseId(testCase))
.setHistoryId(getHistoryId(testCase))
.setFullName(fullName)
+ .setTitlePath(titlePath)
.setName(name)
.setLabels(labelBuilder.getScenarioLabels())
.setLinks(labelBuilder.getScenarioLinks());
- final Scenario scenarioDefinition =
- testSources.getScenarioDefinition(
- testCase.getUri(),
- testCase.getLocation().getLine()
- );
+ final Scenario scenarioDefinition = testSources.getScenarioDefinition(
+ testCase.getUri(),
+ testCase.getLocation().getLine()
+ );
if (scenarioDefinition.getExamples() != null) {
result.setParameters(
@@ -170,7 +178,7 @@ private void handleTestCaseStarted(final TestCaseStarted event) {
final String description = Stream.of(feature.getDescription(), scenarioDefinition.getDescription())
.filter(Objects::nonNull)
.filter(s -> !s.isEmpty())
- .collect(Collectors.joining("\n"));
+ .collect(Collectors.joining(NEW_LINE));
if (!description.isEmpty()) {
result.setDescription(description);
@@ -195,9 +203,10 @@ private void handleTestCaseFinished(final TestCaseFinished event) {
.setMuted(tagParser.isMuted())
.setKnown(tagParser.isKnown());
- lifecycle.updateTestCase(uuid, testResult -> testResult
- .setStatus(status)
- .setStatusDetails(statusDetails)
+ lifecycle.updateTestCase(
+ uuid, testResult -> testResult
+ .setStatus(status)
+ .setStatusDetails(statusDetails)
);
lifecycle.stopTestCase(uuid);
@@ -257,9 +266,10 @@ private void handleStartFixtureHook(final TestCase testCase,
final String containerUuid = hookStepContainerUuid
.computeIfAbsent(hookId, unused -> UUID.randomUUID().toString());
- lifecycle.startTestContainer(new TestResultContainer()
- .setUuid(containerUuid)
- .setChildren(Collections.singletonList(uuid))
+ lifecycle.startTestContainer(
+ new TestResultContainer()
+ .setUuid(containerUuid)
+ .setChildren(Collections.singletonList(uuid))
);
final FixtureResult hookResult = new FixtureResult()
@@ -340,16 +350,15 @@ private Status translateTestCaseStatus(final Result testCaseResult) {
}
private List getExamplesAsParameters(
- final Scenario scenario,
- final TestCase localCurrentTestCase) {
+ final Scenario scenario,
+ final TestCase localCurrentTestCase) {
- final Optional maybeExample =
- scenario.getExamples().stream()
- .filter(example -> example.getTableBody().stream()
- .anyMatch(row -> row.getLocation().getLine()
- == localCurrentTestCase.getLocation().getLine())
- )
- .findFirst();
+ final Optional maybeExample = scenario.getExamples().stream()
+ .filter(
+ example -> example.getTableBody().stream()
+ .anyMatch(row -> row.getLocation().getLine() == localCurrentTestCase.getLocation().getLine())
+ )
+ .findFirst();
if (!maybeExample.isPresent()) {
return Collections.emptyList();
@@ -370,9 +379,10 @@ private List getExamplesAsParameters(
final List headerNames = examples.getTableHeader()
.map(TableRow::getCells)
- .map(rows -> rows.stream()
- .map(TableCell::getValue)
- .collect(Collectors.toList())
+ .map(
+ rows -> rows.stream()
+ .map(TableCell::getValue)
+ .collect(Collectors.toList())
)
.orElse(null);
@@ -392,14 +402,16 @@ private void createDataTableAttachment(final DataTableArgument dataTableArgument
final StringBuilder dataTableCsv = new StringBuilder();
for (List columns : rowsInTable) {
if (!columns.isEmpty()) {
- final String rowValue = columns.stream().collect(Collectors.joining("\t", "", "\n"));
+ final String rowValue = columns.stream().collect(Collectors.joining("\t", "", NEW_LINE));
dataTableCsv.append(rowValue);
}
}
final String attachmentSource = lifecycle
.prepareAttachment("Data table", "text/tab-separated-values", "csv");
- lifecycle.writeAttachment(attachmentSource,
- new ByteArrayInputStream(dataTableCsv.toString().getBytes(StandardCharsets.UTF_8)));
+ lifecycle.writeAttachment(
+ attachmentSource,
+ new ByteArrayInputStream(dataTableCsv.toString().getBytes(StandardCharsets.UTF_8))
+ );
}
private void handleStopHookStep(final Result eventResult,
@@ -416,9 +428,10 @@ private void handleStopHookStep(final Result eventResult,
final StatusDetails statusDetails = getStatusDetails(eventResult.getError())
.orElseGet(StatusDetails::new);
- lifecycle.updateFixture(uuid, result -> result
- .setStatus(status)
- .setStatusDetails(statusDetails)
+ lifecycle.updateFixture(
+ uuid, result -> result
+ .setStatus(status)
+ .setStatusDetails(statusDetails)
);
lifecycle.stopFixture(uuid);
@@ -433,8 +446,7 @@ private void handleStopStep(final TestCase testCase,
final Status stepStatus = translateTestCaseStatus(eventResult);
- final StatusDetails statusDetails
- = eventResult.getStatus() == io.cucumber.plugin.event.Status.UNDEFINED
+ final StatusDetails statusDetails = eventResult.getStatus() == io.cucumber.plugin.event.Status.UNDEFINED
? new StatusDetails().setMessage("Undefined Step. Please add step definition")
: getStatusDetails(eventResult.getError())
.orElse(new StatusDetails());
diff --git a/allure-cucumber7-jvm/src/main/java/io/qameta/allure/cucumber7jvm/LabelBuilder.java b/allure-cucumber7-jvm/src/main/java/io/qameta/allure/cucumber7jvm/LabelBuilder.java
index e4d96d5f..62123bdf 100644
--- a/allure-cucumber7-jvm/src/main/java/io/qameta/allure/cucumber7jvm/LabelBuilder.java
+++ b/allure-cucumber7-jvm/src/main/java/io/qameta/allure/cucumber7jvm/LabelBuilder.java
@@ -48,7 +48,7 @@
/**
* Scenario labels and links builder.
*/
-@SuppressWarnings({"CyclomaticComplexity", "MultipleStringLiterals"})
+@SuppressWarnings({"CyclomaticComplexity", "MultipleStringLiterals", "PMD.CognitiveComplexity"})
class LabelBuilder {
private static final Logger LOGGER = LoggerFactory.getLogger(LabelBuilder.class);
private static final String COMPOSITE_TAG_DELIMITER = "=";
@@ -117,17 +117,19 @@ class LabelBuilder {
final URI uri = scenario.getUri();
scenarioLabels.addAll(ResultsUtils.getProvidedLabels());
- scenarioLabels.addAll(Arrays.asList(
- createHostLabel(),
- createThreadLabel(),
- createFeatureLabel(featureName),
- createStoryLabel(scenario.getName()),
- createSuiteLabel(featureName),
- createTestClassLabel(scenario.getName()),
- createFrameworkLabel("cucumber4jvm"),
- createLanguageLabel("java"),
- createLabel("gherkin_uri", uri.toString())
- ));
+ scenarioLabels.addAll(
+ Arrays.asList(
+ createHostLabel(),
+ createThreadLabel(),
+ createFeatureLabel(featureName),
+ createStoryLabel(scenario.getName()),
+ createSuiteLabel(featureName),
+ createTestClassLabel(scenario.getName()),
+ createFrameworkLabel("cucumber4jvm"),
+ createLanguageLabel("java"),
+ createLabel("gherkin_uri", uri.toString())
+ )
+ );
featurePackage(uri.toString(), featureName)
.map(ResultsUtils::createPackageLabel)
@@ -160,8 +162,10 @@ private void tryHandleNamedLink(final String tagString) {
final String name = tagString.split(COMPOSITE_TAG_DELIMITER)[1];
scenarioLinks.add(ResultsUtils.createLink(null, name, null, type));
} else {
- LOGGER.warn("Composite named tag {} does not match regex {}. Skipping", tagString,
- namedLinkPatternString);
+ LOGGER.warn(
+ "Composite named tag {} does not match regex {}. Skipping", tagString,
+ namedLinkPatternString
+ );
}
}
@@ -179,10 +183,12 @@ private Optional featurePackage(final String uriString, final String fea
final String schemeSpecificPart = uri.normalize().getSchemeSpecificPart();
final Stream folders = Stream.of(schemeSpecificPart.replaceAll("\\.", "_").split("/"));
final Stream name = Stream.of(featureName);
- return Optional.of(Stream.concat(folders, name)
- .filter(Objects::nonNull)
- .filter(s -> !s.isEmpty())
- .collect(Collectors.joining(".")));
+ return Optional.of(
+ Stream.concat(folders, name)
+ .filter(Objects::nonNull)
+ .filter(s -> !s.isEmpty())
+ .collect(Collectors.joining("."))
+ );
}
private static Optional safeUri(final String uri) {
diff --git a/allure-cucumber7-jvm/src/main/java/io/qameta/allure/cucumber7jvm/TagParser.java b/allure-cucumber7-jvm/src/main/java/io/qameta/allure/cucumber7jvm/TagParser.java
index ea7e10ef..faca7e91 100644
--- a/allure-cucumber7-jvm/src/main/java/io/qameta/allure/cucumber7jvm/TagParser.java
+++ b/allure-cucumber7-jvm/src/main/java/io/qameta/allure/cucumber7jvm/TagParser.java
@@ -51,9 +51,9 @@ public boolean isKnown() {
private boolean getStatusDetailByTag(final String tagName) {
return scenario.getTags().stream()
- .anyMatch(tag -> tag.equalsIgnoreCase(tagName))
- || feature.getTags().stream()
- .anyMatch(tag -> tag.getName().equalsIgnoreCase(tagName));
+ .anyMatch(tag -> tag.equalsIgnoreCase(tagName))
+ || feature.getTags().stream()
+ .anyMatch(tag -> tag.getName().equalsIgnoreCase(tagName));
}
public boolean isResultTag(final String tag) {
diff --git a/allure-cucumber7-jvm/src/main/java/io/qameta/allure/cucumber7jvm/testsourcemodel/TestSourcesModel.java b/allure-cucumber7-jvm/src/main/java/io/qameta/allure/cucumber7jvm/testsourcemodel/TestSourcesModel.java
index 479ada73..91e4e185 100644
--- a/allure-cucumber7-jvm/src/main/java/io/qameta/allure/cucumber7jvm/testsourcemodel/TestSourcesModel.java
+++ b/allure-cucumber7-jvm/src/main/java/io/qameta/allure/cucumber7jvm/testsourcemodel/TestSourcesModel.java
@@ -73,7 +73,8 @@ private void parseGherkinSource(final URI path) {
.build();
final Stream envelopes = parser.parse(
- Envelope.of(new Source(path.toString(), source, SourceMediaType.TEXT_X_CUCUMBER_GHERKIN_PLAIN)));
+ Envelope.of(new Source(path.toString(), source, SourceMediaType.TEXT_X_CUCUMBER_GHERKIN_PLAIN))
+ );
// TODO: What about empty gherkin docs?
final GherkinDocument gherkinDocument = envelopes
@@ -97,7 +98,7 @@ private void parseGherkinSource(final URI path) {
}
private void processFeatureDefinition(
- final Map nodeMap, final FeatureChild child, final AstNode currentParent) {
+ final Map nodeMap, final FeatureChild child, final AstNode currentParent) {
child.getBackground().ifPresent(background -> processBackgroundDefinition(nodeMap, background, currentParent));
child.getScenario().ifPresent(scenario -> processScenarioDefinition(nodeMap, scenario, currentParent));
child.getRule().ifPresent(rule -> {
@@ -108,8 +109,7 @@ private void processFeatureDefinition(
}
private void processBackgroundDefinition(
- final Map nodeMap, final Background background, final AstNode currentParent
- ) {
+ final Map nodeMap, final Background background, final AstNode currentParent) {
final AstNode childNode = createAstNode(background, currentParent);
nodeMap.put(background.getLocation().getLine(), childNode);
for (Step step : background.getSteps()) {
@@ -118,7 +118,7 @@ private void processBackgroundDefinition(
}
private void processScenarioDefinition(
- final Map nodeMap, final Scenario child, final AstNode currentParent) {
+ final Map nodeMap, final Scenario child, final AstNode currentParent) {
final AstNode childNode = createAstNode(child, currentParent);
nodeMap.put(child.getLocation().getLine(), childNode);
for (Step step : child.getSteps()) {
@@ -130,14 +130,13 @@ private void processScenarioDefinition(
}
private void processRuleDefinition(
- final Map nodeMap, final RuleChild child, final AstNode currentParent) {
+ final Map nodeMap, final RuleChild child, final AstNode currentParent) {
child.getBackground().ifPresent(background -> processBackgroundDefinition(nodeMap, background, currentParent));
child.getScenario().ifPresent(scenario -> processScenarioDefinition(nodeMap, scenario, currentParent));
}
private void processScenarioOutlineExamples(
- final Map nodeMap, final Scenario scenarioOutline, final AstNode parent
- ) {
+ final Map nodeMap, final Scenario scenarioOutline, final AstNode parent) {
for (Examples examples : scenarioOutline.getExamples()) {
final AstNode examplesNode = createAstNode(examples, parent);
// TODO: Can tables without headers even exist?
diff --git a/allure-cucumber7-jvm/src/test/java/io/qameta/allure/cucumber7jvm/AllureCucumber7JvmTest.java b/allure-cucumber7-jvm/src/test/java/io/qameta/allure/cucumber7jvm/AllureCucumber7JvmTest.java
index 0d8a20a7..ea50e510 100644
--- a/allure-cucumber7-jvm/src/test/java/io/qameta/allure/cucumber7jvm/AllureCucumber7JvmTest.java
+++ b/allure-cucumber7-jvm/src/test/java/io/qameta/allure/cucumber7jvm/AllureCucumber7JvmTest.java
@@ -80,6 +80,8 @@ void shouldSetName() {
assertThat(testResults)
.extracting(TestResult::getName)
.containsExactlyInAnyOrder("Add a to b");
+ assertThat(testResults.get(0).getTitlePath())
+ .containsExactly("src", "test", "resources", "features", "simple.feature", "Simple feature");
}
@AllureFeatures.PassedTests
@@ -183,7 +185,7 @@ void shouldSetDescription() {
final AllureResults results = runFeature("features/description.feature");
final String expected = "This is description for current feature.\n"
- + "It should appear on each scenario in report";
+ + "It should appear on each scenario in report";
final List testResults = results.getTestResults();
assertThat(testResults)
@@ -200,7 +202,7 @@ void shouldSetScenarioDescription() {
final AllureResults results = runFeature("features/scenario_description.feature");
final String expected = "This is description for current feature.\n"
- + "It should appear on each scenario in report";
+ + "It should appear on each scenario in report";
final List testResults = results.getTestResults();
assertThat(testResults)
@@ -238,11 +240,12 @@ void shouldAddDataTableAttachment() {
final String attachmentContent = new String(bytes, StandardCharsets.UTF_8);
assertThat(attachmentContent)
- .isEqualTo("""
- name\tlogin\temail
- Viktor\tclicman\tclicman@ya.ru
- Viktor2\tclicman2\tclicman2@ya.ru
+ .isEqualTo(
"""
+ name\tlogin\temail
+ Viktor\tclicman\tclicman@ya.ru
+ Viktor2\tclicman2\tclicman2@ya.ru
+ """
);
}
@@ -381,8 +384,14 @@ void shouldAddTags() {
@AllureFeatures.Links
@ExtendWith(SystemPropertyExtension.class)
- @SystemProperty(name = "allure.link.issue.pattern", value = "https://example.org/issue/{}")
- @SystemProperty(name = "allure.link.tms.pattern", value = "https://example.org/tms/{}")
+ @SystemProperty(
+ name = "allure.link.issue.pattern",
+ value = "https://example.org/issue/{}"
+ )
+ @SystemProperty(
+ name = "allure.link.tms.pattern",
+ value = "https://example.org/tms/{}"
+ )
@Test
void shouldAddLinks() {
final AllureResults results = runFeature("features/tags.feature");
@@ -511,8 +520,10 @@ void shouldSupportDryRunForSimpleFeatures() {
@AllureFeatures.Base
@Test
void shouldSupportDryRunForHooks() {
- final AllureResults results = runFeature("features/hooks.feature", "--dry-run", "-t",
- "@WithHooks or @BeforeHookWithException or @AfterHookWithException");
+ final AllureResults results = runFeature(
+ "features/hooks.feature", "--dry-run", "-t",
+ "@WithHooks or @BeforeHookWithException or @AfterHookWithException"
+ );
final TestResult tr1 = results.getTestResultByName("Simple scenario with Before and After hooks");
@@ -652,8 +663,10 @@ void shouldProcessScenariosInParallelMode() {
@AllureFeatures.Stages
@Test
void shouldDisplayHooksAsStages() {
- final AllureResults results = runFeature("features/hooks.feature", "-t",
- "@WithHooks or @BeforeHookWithException or @AfterHookWithException");
+ final AllureResults results = runFeature(
+ "features/hooks.feature", "-t",
+ "@WithHooks or @BeforeHookWithException or @AfterHookWithException"
+ );
final TestResult tr1 = results.getTestResultByName("Simple scenario with Before and After hooks");
final TestResult tr2 = results.getTestResultByName("Simple scenario with Before hook with Exception");
@@ -691,7 +704,6 @@ void shouldDisplayHooksAsStages() {
tuple("Then result is 15", Status.SKIPPED)
);
-
assertThat(results.getTestResultContainersForTestResult(tr2))
.flatExtracting(TestResultContainer::getBefores)
.extracting(FixtureResult::getName, FixtureResult::getStatus)
@@ -737,8 +749,14 @@ void shouldHandleAmbigiousStepsExceptions() {
);
}
- @ResourceLock(value = SYSTEM_PROPERTIES, mode = READ_WRITE)
- @SystemProperty(name = "allure.label.x-provided", value = "cucumberjvm7-test-provided")
+ @ResourceLock(
+ value = SYSTEM_PROPERTIES,
+ mode = READ_WRITE
+ )
+ @SystemProperty(
+ name = "allure.label.x-provided",
+ value = "cucumberjvm7-test-provided"
+ )
@Test
void shouldSupportProvidedLabels() {
final AllureResults results = runFeature("features/simple.feature");
@@ -783,7 +801,10 @@ void shouldSupportRuntimeApiInStepsWhenHooksAreUsed() {
);
}
- @SystemProperty(name = "cucumber.junit-platform.naming-strategy", value = "long")
+ @SystemProperty(
+ name = "cucumber.junit-platform.naming-strategy",
+ value = "long"
+ )
@Step
private AllureResults runFeature(final String featureResource,
final String... moreOptions) {
@@ -791,10 +812,12 @@ private AllureResults runFeature(final String featureResource,
return RunUtils.runTests(lifecycle -> {
final AllureCucumber7Jvm cucumber7jvm = new AllureCucumber7Jvm(lifecycle);
final Supplier classLoader = ClassLoaders::getDefaultClassLoader;
- final List opts = new ArrayList<>(Arrays.asList(
- "--glue", "io.qameta.allure.cucumber7jvm.samples",
- "--no-summary"
- ));
+ final List opts = new ArrayList<>(
+ Arrays.asList(
+ "--glue", "io.qameta.allure.cucumber7jvm.samples",
+ "--no-summary"
+ )
+ );
opts.addAll(Arrays.asList(moreOptions));
final FeatureWithLines featureWithLines = FeatureWithLines.parse("src/test/resources/" + featureResource);
final RuntimeOptions options = new CommandlineOptionsParser(System.out)
@@ -802,8 +825,7 @@ private AllureResults runFeature(final String featureResource,
final EventBus bus = new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID);
final FeatureParser parser = new FeatureParser(bus::generateId);
- final FeaturePathFeatureSupplier supplier
- = new FeaturePathFeatureSupplier(classLoader, options, parser);
+ final FeaturePathFeatureSupplier supplier = new FeaturePathFeatureSupplier(classLoader, options, parser);
final Runtime runtime = Runtime.builder()
.withClassLoader(classLoader)
diff --git a/allure-cucumber7-jvm/src/test/java/io/qameta/allure/cucumber7jvm/samples/AmbigiousSteps.java b/allure-cucumber7-jvm/src/test/java/io/qameta/allure/cucumber7jvm/samples/AmbigiousSteps.java
index 0406db2f..b5fec46c 100644
--- a/allure-cucumber7-jvm/src/test/java/io/qameta/allure/cucumber7jvm/samples/AmbigiousSteps.java
+++ b/allure-cucumber7-jvm/src/test/java/io/qameta/allure/cucumber7jvm/samples/AmbigiousSteps.java
@@ -34,7 +34,7 @@ public void ambigious_2() {
}
@Then("^something bad should happen")
- public void somethingBadStep(){
+ public void somethingBadStep() {
//nothing here
}
}
diff --git a/allure-cucumber7-jvm/src/test/java/io/qameta/allure/cucumber7jvm/samples/AttachmentSteps.java b/allure-cucumber7-jvm/src/test/java/io/qameta/allure/cucumber7jvm/samples/AttachmentSteps.java
index 455aaffe..bd1fb2a4 100644
--- a/allure-cucumber7-jvm/src/test/java/io/qameta/allure/cucumber7jvm/samples/AttachmentSteps.java
+++ b/allure-cucumber7-jvm/src/test/java/io/qameta/allure/cucumber7jvm/samples/AttachmentSteps.java
@@ -15,8 +15,8 @@
*/
package io.qameta.allure.cucumber7jvm.samples;
-import io.cucumber.java.Scenario;
import io.cucumber.java.Before;
+import io.cucumber.java.Scenario;
import io.cucumber.java.en.Given;
public class AttachmentSteps {
diff --git a/allure-cucumber7-jvm/src/test/java/io/qameta/allure/cucumber7jvm/samples/HookSteps.java b/allure-cucumber7-jvm/src/test/java/io/qameta/allure/cucumber7jvm/samples/HookSteps.java
index b85751af..46ca418a 100644
--- a/allure-cucumber7-jvm/src/test/java/io/qameta/allure/cucumber7jvm/samples/HookSteps.java
+++ b/allure-cucumber7-jvm/src/test/java/io/qameta/allure/cucumber7jvm/samples/HookSteps.java
@@ -25,22 +25,22 @@
public class HookSteps {
@Before("@WithHooks")
- public void beforeHook(){
+ public void beforeHook() {
// nothing
}
@After("@WithHooks")
- public void afterHook(){
+ public void afterHook() {
// nothing
}
@Before("@BeforeHookWithException")
- public void beforeHookWithException(){
+ public void beforeHookWithException() {
Assertions.fail("Exception in Hook step");
}
@After("@AfterHookWithException")
- public void afterHookWithException(){
+ public void afterHookWithException() {
Assertions.fail("Exception in Hook step");
}
diff --git a/allure-cucumber7-jvm/src/test/java/io/qameta/allure/cucumber7jvm/samples/RuntimeApiSteps.java b/allure-cucumber7-jvm/src/test/java/io/qameta/allure/cucumber7jvm/samples/RuntimeApiSteps.java
index e76d71ac..50eabea7 100644
--- a/allure-cucumber7-jvm/src/test/java/io/qameta/allure/cucumber7jvm/samples/RuntimeApiSteps.java
+++ b/allure-cucumber7-jvm/src/test/java/io/qameta/allure/cucumber7jvm/samples/RuntimeApiSteps.java
@@ -16,9 +16,9 @@
package io.qameta.allure.cucumber7jvm.samples;
import io.cucumber.java.Before;
+import io.cucumber.java.en.And;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
-import io.cucumber.java.en.And;
import io.qameta.allure.Allure;
/**
@@ -27,12 +27,12 @@
public class RuntimeApiSteps {
@Before("@beforeScenario")
- public void beforeScenario(){
+ public void beforeScenario() {
// nothing
}
@Before("@beforeFeature")
- public void beforeFeature(){
+ public void beforeFeature() {
// nothing
}
diff --git a/allure-cucumber7-jvm/src/test/resources/allure.properties b/allure-cucumber7-jvm/src/test/resources/allure.properties
index dbfefee4..20fde5c1 100644
--- a/allure-cucumber7-jvm/src/test/resources/allure.properties
+++ b/allure-cucumber7-jvm/src/test/resources/allure.properties
@@ -1,3 +1,4 @@
allure.model.indentOutput=true
allure.results.directory=build/allure-results
allure.label.epic=#project.description#
+allure.label.module=allure-cucumber7-jvm
diff --git a/allure-descriptions-javadoc/src/main/java/io/qameta/allure/description/JavaDocDescriptionRenderer.java b/allure-descriptions-javadoc/src/main/java/io/qameta/allure/description/JavaDocDescriptionRenderer.java
index 6546dbb4..1985af5f 100644
--- a/allure-descriptions-javadoc/src/main/java/io/qameta/allure/description/JavaDocDescriptionRenderer.java
+++ b/allure-descriptions-javadoc/src/main/java/io/qameta/allure/description/JavaDocDescriptionRenderer.java
@@ -47,6 +47,7 @@
* description path suitable for open source projects where comments may evolve over time and where
* security is more important than pixel-perfect parity with the standard doclet.
*/
+@SuppressWarnings({"PMD.GodClass", "PMD.TooManyMethods"})
final class JavaDocDescriptionRenderer {
private static final String PARAGRAPH_BREAK = "\n\n";
@@ -58,6 +59,7 @@ final class JavaDocDescriptionRenderer {
private static final String CODE_TAG = "code";
private static final String HTML_TAG_END = ">";
private static final String CLOSING_TAG_PREFIX = "";
+ private static final String NEW_LINE = "\n";
/**
* Converts raw JavaDoc comment text into the description format stored by the annotation
@@ -84,7 +86,7 @@ String render(final String rawDocComment) {
}
private String extractDescriptionBody(final String rawDocComment) {
- final String[] lines = normalize(rawDocComment).split("\n", -1);
+ final String[] lines = normalize(rawDocComment).split(NEW_LINE, -1);
final StringBuilder body = new StringBuilder();
int inlineTagDepth = 0;
@@ -107,7 +109,7 @@ private boolean startsBlockTag(final String line) {
return trimmed.length() > 1 && trimmed.charAt(0) == '@' && Character.isJavaIdentifierStart(trimmed.charAt(1));
}
- @SuppressWarnings("checkstyle:CyclomaticComplexity")
+ @SuppressWarnings({"checkstyle:CyclomaticComplexity", "PMD.CognitiveComplexity"})
private void renderFragment(final String fragment, final StringBuilder rendered) {
int index = 0;
while (index < fragment.length()) {
@@ -182,10 +184,13 @@ private int renderInlineTag(final String fragment, final int start, final String
return end + 1;
}
- @SuppressWarnings({
- "checkstyle:CyclomaticComplexity",
- "checkstyle:NPathComplexity",
- "checkstyle:ReturnCount"})
+ @SuppressWarnings(
+ {
+ "checkstyle:CyclomaticComplexity",
+ "checkstyle:NPathComplexity",
+ "PMD.CognitiveComplexity",
+ "checkstyle:ReturnCount"}
+ )
private int renderHtmlTag(final String fragment, final int start, final StringBuilder rendered) {
if (start + 1 >= fragment.length() || Character.isWhitespace(fragment.charAt(start + 1))) {
return start;
@@ -317,7 +322,7 @@ private void appendLineBreak(final StringBuilder rendered) {
}
private String cleanup(final String rendered) {
- final String[] lines = normalize(rendered).split("\n", -1);
+ final String[] lines = normalize(rendered).split(NEW_LINE, -1);
final StringBuilder cleaned = new StringBuilder();
boolean blankLinePending = false;
@@ -331,7 +336,7 @@ private String cleanup(final String rendered) {
}
if (cleaned.length() > 0) {
- cleaned.append(blankLinePending ? PARAGRAPH_BREAK : "\n");
+ cleaned.append(blankLinePending ? PARAGRAPH_BREAK : NEW_LINE);
}
cleaned.append(trimmed);
blankLinePending = false;
@@ -341,7 +346,7 @@ private String cleanup(final String rendered) {
}
private String trimBlankLines(final String value) {
- final String[] lines = normalize(value).split("\n", -1);
+ final String[] lines = normalize(value).split(NEW_LINE, -1);
int start = 0;
int end = lines.length;
@@ -452,7 +457,7 @@ private String trimTrailingWhitespace(final String line) {
}
private String normalize(final String value) {
- return value.replace("\r\n", "\n").replace('\r', '\n');
+ return value.replace("\r\n", NEW_LINE).replace('\r', '\n');
}
private boolean isBlank(final String value) {
@@ -479,10 +484,12 @@ private int renderEntityReference(final String fragment, final int start, final
return end + 1;
}
- @SuppressWarnings({
- "checkstyle:CyclomaticComplexity",
- "checkstyle:NPathComplexity",
- "checkstyle:ReturnCount"})
+ @SuppressWarnings(
+ {
+ "checkstyle:CyclomaticComplexity",
+ "checkstyle:NPathComplexity",
+ "checkstyle:ReturnCount"}
+ )
private String decodeEntity(final String entity) {
if (entity.isEmpty()) {
return null;
diff --git a/allure-descriptions-javadoc/src/main/java/io/qameta/allure/description/JavaDocDescriptionsProcessor.java b/allure-descriptions-javadoc/src/main/java/io/qameta/allure/description/JavaDocDescriptionsProcessor.java
index acc1d555..289c5f43 100644
--- a/allure-descriptions-javadoc/src/main/java/io/qameta/allure/description/JavaDocDescriptionsProcessor.java
+++ b/allure-descriptions-javadoc/src/main/java/io/qameta/allure/description/JavaDocDescriptionsProcessor.java
@@ -31,6 +31,7 @@
import javax.tools.Diagnostic;
import javax.tools.FileObject;
import javax.tools.StandardLocation;
+
import java.io.IOException;
import java.io.Writer;
import java.math.BigInteger;
@@ -96,14 +97,18 @@ public boolean process(final Set extends TypeElement> annotations, final Round
method.getEnclosingElement().toString(), name, typeParams
);
try {
- final FileObject file = filer.createResource(StandardLocation.CLASS_OUTPUT, "",
- ALLURE_DESCRIPTIONS_FOLDER + hash);
+ final FileObject file = filer.createResource(
+ StandardLocation.CLASS_OUTPUT, "",
+ ALLURE_DESCRIPTIONS_FOLDER + hash
+ );
try (Writer writer = file.openWriter()) {
writer.write(docs);
}
} catch (IOException e) {
- messager.printMessage(Diagnostic.Kind.WARNING,
- "Unable to create resource from docs comment of method " + name + typeParams);
+ messager.printMessage(
+ Diagnostic.Kind.WARNING,
+ "Unable to create resource from docs comment of method " + name + typeParams
+ );
}
});
diff --git a/allure-descriptions-javadoc/src/test/java/io/qameta/allure/description/JavaDocDescriptionRendererTest.java b/allure-descriptions-javadoc/src/test/java/io/qameta/allure/description/JavaDocDescriptionRendererTest.java
index 180d2b03..b82f4c47 100644
--- a/allure-descriptions-javadoc/src/test/java/io/qameta/allure/description/JavaDocDescriptionRendererTest.java
+++ b/allure-descriptions-javadoc/src/test/java/io/qameta/allure/description/JavaDocDescriptionRendererTest.java
@@ -29,10 +29,10 @@ class JavaDocDescriptionRendererTest {
void shouldRenderPlainTextAndTrimBlankLines() {
final String rendered = renderer.render(
"\r\n"
- + " First line \r\n"
- + "\r\n"
- + " Second line\t\r\n"
- + "\r\n"
+ + " First line \r\n"
+ + "\r\n"
+ + " Second line\t\r\n"
+ + "\r\n"
);
assertThat(rendered)
@@ -43,7 +43,7 @@ void shouldRenderPlainTextAndTrimBlankLines() {
void shouldReturnEmptyStringWhenBodyContainsOnlyBlockTags() {
final String rendered = renderer.render(
"@param value description\n"
- + "@throws Exception description"
+ + "@throws Exception description"
);
assertThat(rendered)
@@ -54,9 +54,9 @@ void shouldReturnEmptyStringWhenBodyContainsOnlyBlockTags() {
void shouldIgnoreBlockTagsAndEverythingAfterThem() {
final String rendered = renderer.render(
"Summary paragraph.\n"
- + "\n"
- + "@param value Description of the value.\n"
- + "Continuation that should also be ignored."
+ + "\n"
+ + "@param value Description of the value.\n"
+ + "Continuation that should also be ignored."
);
assertThat(rendered)
@@ -95,7 +95,7 @@ void shouldIgnoreStandardBlockTagsAfterMainDescription() {
void shouldNotTreatAtSignsInsideTextAsBlockTags() {
final String rendered = renderer.render(
"Email support@example.com\n"
- + "Use @smoke in prose."
+ + "Use @smoke in prose."
);
assertThat(rendered)
@@ -106,7 +106,7 @@ void shouldNotTreatAtSignsInsideTextAsBlockTags() {
void shouldDecodeEscapedAtEntityBeforeBlockTags() {
final String rendered = renderer.render(
"@version stays in prose.\n"
- + "@version 2.4.0"
+ + "@version 2.4.0"
);
assertThat(rendered)
@@ -135,9 +135,9 @@ void shouldDecodeSupportedNamedAndNumericEntities() {
void shouldRenderSupportedInlineTags() {
final String rendered = renderer.render(
"Use {@code a < b}, {@literal }, "
- + "{@link java.lang.String}, "
- + "{@linkplain java.lang.String#valueOf(Object)}, "
- + "{@link java.util.List list docs}."
+ + "{@link java.lang.String}, "
+ + "{@linkplain java.lang.String#valueOf(Object)}, "
+ + "{@link java.util.List list docs}."
);
assertThat(rendered)
@@ -158,9 +158,9 @@ void shouldSupportBalancedBracesInsideInlineTags() {
void shouldNotTreatAtLinesInsideBalancedInlineTagsAsBlockTags() {
final String rendered = renderer.render(
"Summary {@literal first line\n"
- + "@notATag\n"
- + "last line}\n"
- + "@param ignored"
+ + "@notATag\n"
+ + "last line}\n"
+ + "@param ignored"
);
assertThat(rendered)
@@ -181,14 +181,14 @@ void shouldRenderNestedInlineTagsInsideLinkLabels() {
void shouldSafelyDegradeUnsupportedStandardInlineTags() {
final String rendered = renderer.render(
"Fallbacks: {@docRoot}, {@inheritDoc}, {@index release}, "
- + "{@summary quick summary}, {@systemProperty user.home}, "
- + "{@value java.lang.Integer#MAX_VALUE}."
+ + "{@summary quick summary}, {@systemProperty user.home}, "
+ + "{@value java.lang.Integer#MAX_VALUE}."
);
assertThat(rendered)
.isEqualTo(
"Fallbacks: docRoot, inheritDoc, index release, summary quick summary, "
- + "systemProperty user.home, value java.lang.Integer#MAX_VALUE."
+ + "systemProperty user.home, value java.lang.Integer#MAX_VALUE."
);
}
@@ -196,9 +196,9 @@ void shouldSafelyDegradeUnsupportedStandardInlineTags() {
void shouldSafelyDegradeSnippetTags() {
final String rendered = renderer.render(
"Snippet {@snippet :\n"
- + "int answer = 42;\n"
- + "@highlight substring=\"answer\"\n"
- + "}."
+ + "int answer = 42;\n"
+ + "@highlight substring=\"answer\"\n"
+ + "}."
);
assertThat(rendered)
@@ -301,28 +301,28 @@ void shouldDropUnknownHtmlTagsButKeepTheirTextContentEscaped() {
void shouldRenderComplexModernJavadocExampleSafely() {
final String rendered = renderer.render(
"Fetches release metadata for the current build.\n"
- + "\n"
- + "
Use {@link java.net.URI URIs} for endpoint configuration.
\n"
- + "
\n"
- + "
Supports café, Привет, 東京, and λ.
\n"
- + "
See the Javadoc specification "
- + "and {@linkplain java.lang.String#formatted(Object...) formatted examples}.