diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 319290ead..311c48e21 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -25,42 +25,38 @@ body: description: Please select the Allure integration you options: - allure-assertj - - allure-attachments - allure-awaitility + - allure-bom - allure-citrus - - allure-cucumber2-jvm - - allure-cucumber3-jvm - - allure-cucumber4-jvm - - allure-cucumber5-jvm - - allure-cucumber6-jvm - allure-cucumber7-jvm - allure-descriptions-javadoc - allure-grpc - allure-hamcrest - allure-httpclient + - allure-httpclient5 - allure-java-commons + - allure-java-commons-test - allure-jax-rs - - allure-jbehave - allure-jbehave5 + - allure-jooq - allure-jsonunit - allure-junit-platform - allure-junit4 + - allure-junit4-aspect - allure-jupiter - allure-jupiter-assert - - allure-junit5 - - allure-junit5-assert - allure-karate - - allure-okhttp + - allure-model - allure-okhttp3 - - allure-reader + - allure-playwright - allure-rest-assured - allure-scalatest + - allure-selenium-bidi - allure-selenide - allure-servlet-api - allure-spock - allure-spock2 - allure-spring-web - - allure-test-filter - allure-testng validations: required: true diff --git a/.github/labeler.yml b/.github/labeler.yml index 367271a00..13fd84e53 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -11,20 +11,30 @@ "theme:assertj": - "allure-assertj/**" -"theme:attachments": - - "allure-attachments/**" +"theme:awaitility": + - "allure-awaitility/**" + +"theme:bom": + - "allure-bom/**" "theme:citrus": - "allure-citrus/**" "theme:cucumber-jvm": - - "allure-cucumber*-jvm/**" + - "allure-cucumber7-jvm/**" "theme:descriptions-javadoc": - "allure-descriptions-javadoc/**" +"theme:grpc": + - "allure-grpc/**" + +"theme:hamcrest": + - "allure-hamcrest/**" + "theme:httpclient": - "allure-httpclient/**" + - "allure-httpclient5/**" "theme:model": - "allure-model/**" @@ -32,13 +42,15 @@ "theme:core": - "allure-java-commons/**" - "allure-java-commons-test/**" - - "allure-test-filter/**" "theme:jax-rs": - "allure-jax-rs/**" "theme:jbehave": - - "allure-jbehave*/**" + - "allure-jbehave5/**" + +"theme:jooq": + - "allure-jooq/**" "theme:jsonunit": - "allure-jsonunit/**" @@ -56,15 +68,20 @@ - "allure-karate/**" "theme:okhttp": - - "allure-okhttp/**" - "allure-okhttp3/**" +"theme:playwright": + - "allure-playwright/**" + "theme:rest-assured": - "allure-rest-assured/**" "theme:scalatest": - "allure-scalatest/**" +"theme:selenium-bidi": + - "allure-selenium-bidi/**" + "theme:selenide": - "allure-selenide/**" @@ -73,12 +90,10 @@ "theme:spock": - "allure-spock/**" + - "allure-spock2/**" "theme:spring": - "allure-spring-web/**" "theme:testng": - "allure-testng/**" - -"theme:hamcrest": - - "allure-hamcrest/**" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 341788f5e..bf608b29f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,11 +21,15 @@ concurrency: jobs: build: - name: "Build" + name: "Build (JDK ${{ matrix.java }})" runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + java: [17, 25] env: - ALLURE_MATRIX_ENV: ubuntu-jdk-21 - ALLURE_TEST_DUMP_NAME: allure-results-test-jdk-21 + ALLURE_MATRIX_ENV: ubuntu-jdk-${{ matrix.java }} + ALLURE_TEST_DUMP_NAME: allure-results-test-jdk-${{ matrix.java }} steps: - uses: actions/checkout@v6 @@ -37,7 +41,7 @@ jobs: uses: actions/setup-java@v5 with: distribution: 'zulu' - java-version: 21 + java-version: ${{ matrix.java }} - name: "Setup Gradle" uses: gradle/actions/setup-gradle@v6 diff --git a/README.md b/README.md index 2c6f44a12..08bd79020 100644 --- a/README.md +++ b/README.md @@ -1,341 +1,146 @@ -[license]: http://www.apache.org/licenses/LICENSE-2.0 "Apache License 2.0" -[blog]: https://qameta.io/blog -[gitter]: https://gitter.im/allure-framework/allure-core -[gitter-ru]: https://gitter.im/allure-framework/allure-ru -[twitter]: https://twitter.com/QametaSoftware "Qameta Software" -[twitter-team]: https://twitter.com/QametaSoftware/lists/team/members "Team" - -[CONTRIBUTING.md]: .github/CONTRIBUTING.md -[docs]: https://allurereport.org/docs/ - -# Allure Java Integrations - -[![Build](https://github.com/allure-framework/allure-java/actions/workflows/build.yml/badge.svg)](https://github.com/allure-framework/allure-java/actions/workflows/build.yml) -[![Allure Java](https://img.shields.io/github/release/allure-framework/allure-java.svg)](https://github.com/allure-framework/allure-java/releases/latest) - -> The repository contains new versions of adaptors for JVM-based test frameworks. - -[Allure Report logo](https://allurereport.org "Allure Report") - -- Learn more about Allure Report at [https://allurereport.org](https://allurereport.org) -- 📚 [Documentation](https://allurereport.org/docs/) – discover official documentation for Allure Report -- ❓ [Questions and Support](https://github.com/orgs/allure-framework/discussions/categories/questions-support) – get help from the team and community -- 📢 [Official announcements](https://github.com/orgs/allure-framework/discussions/categories/announcements) – stay updated with our latest news and updates -- 💬 [General Discussion](https://github.com/orgs/allure-framework/discussions/categories/general-discussion) – engage in casual conversations, share insights and ideas with the community -- 🖥️ [Live Demo](https://demo.allurereport.org/) — explore a live example of Allure Report in action - ---- -## TestNG - -- 🚀 Documentation — https://allurereport.org/docs/testng/ -- 📚 Example project — https://github.com/allure-examples?q=topic%3Atestng -- ✅ Generate a project in 10 seconds via Allure Start - https://allurereport.org/start/ - -## JUnit 4 - -- 🚀 Documentation — work in progress -- 📚 Example project — https://github.com/allure-examples?q=topic%3Ajunit4 -- ✅ Generate a project in 10 seconds via Allure Start - https://allurereport.org/start/ -- -## JUnit Jupiter (JUnit 5 and 6) +# Allure Java -- 🚀 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 relocation coordinate during migration. - -## Cucumber JVM - -- 🚀 Documentation — https://allurereport.org/docs/cucumberjvm/ -- 📚 Example project — https://github.com/allure-examples?q=cucumber&type=all&language=java -- ✅ Generate a project in 10 seconds via Allure Start - https://allurereport.org/start/ - -## Spock - -- 🚀 Documentation — https://allurereport.org/docs/spock/ -- 📚 Example project — https://github.com/allure-examples?q=topic%3Aspock -- ✅ Generate a project in 10 seconds via Allure Start - https://allurereport.org/start/ - -## Selenide - -Listener for Selenide, that logging steps for Allure: - -```xml - - io.qameta.allure - allure-selenide - $LATEST_VERSION - -``` - -Usage example: -``` -SelenideLogger.addListener("AllureSelenide", new AllureSelenide().screenshots(true).savePageSource(false)); - -Capture selenium logs: -SelenideLogger.addListener("AllureSelenide", new AllureSelenide().enableLogs(LogType.BROWSER, Level.ALL)); -https://github.com/SeleniumHQ/selenium/wiki/Logging -``` - -## Playwright Java +Allure Java is the JVM integration family for [Allure Report](https://allurereport.org/). It contains test framework adapters, runtime APIs, HTTP exchange integrations, browser tooling, assertion integrations, and internal support modules used to write Allure result files from Java, Groovy, Scala, Kotlin, and other JVM test suites. -AspectJ-based integration for Playwright Java that reports browser actions as Allure steps and attaches -Playwright screenshots automatically: +## Requirements -```xml - - io.qameta.allure - allure-playwright - $LATEST_VERSION - -``` +- Allure Java 3.x targets Java 17 and newer. +- Use one framework adapter per test runtime, for example `allure-jupiter`, `allure-testng`, or `allure-cucumber7-jvm`. +- Prefer `allure-bom` to keep Allure module versions aligned. +- `allure-junit5` and `allure-junit5-assert` are no longer published aliases; use `allure-jupiter` and `allure-jupiter-assert`. +- HTTP client integrations now write a single structured HTTP exchange attachment with content type `application/vnd.allure.http+json`. -Enable the AspectJ weaver for automatic action steps: -``` --javaagent:/path/to/aspectjweaver.jar -``` +## Quick Start -Usage example with Playwright Java JUnit fixtures: -```java -@UsePlaywright -class UiTest { +Gradle: - @Test - void shouldOpenPage(Page page) { - page.navigate("https://playwright.dev"); - page.screenshot(); - } +```kotlin +dependencies { + testImplementation(platform("io.qameta.allure:allure-bom:")) + testImplementation("io.qameta.allure:allure-jupiter") } -``` -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(); +tasks.test { + useJUnitPlatform() } ``` -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 +Maven: -Filter for rest-assured http client, that generates attachment for allure. - -```xml - - io.qameta.allure - allure-rest-assured - $LATEST_VERSION - -``` - -Usage example: -``` -.filter(new AllureRestAssured()) -``` -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")) +```xml + + + + io.qameta.allure + allure-bom + ${allure.version} + pom + import + + + + + + + io.qameta.allure + allure-jupiter + test + + ``` -## Spring Web +Run your tests, then generate or serve the report from the produced Allure results directory, usually `build/allure-results` for Gradle or `target/allure-results` for Maven. + +## Module Catalog + +### Test Frameworks + +| Module | Use When | Registration | +| --- | --- | --- | +| [`allure-jupiter`](allure-jupiter/README.md) | JUnit Jupiter on JUnit 5 or 6 | JUnit Platform service loader | +| [`allure-junit-platform`](allure-junit-platform/README.md) | Building a custom JUnit Platform integration | JUnit Platform listener and filter services | +| [`allure-junit4`](allure-junit4/README.md) | JUnit 4 runner where listeners can be configured | Register `io.qameta.allure.junit4.AllureJunit4` | +| [`allure-junit4-aspect`](allure-junit4-aspect/README.md) | Gradle built-in JUnit 4 execution | Enable AspectJ weaver | +| [`allure-testng`](allure-testng/README.md) | TestNG 7 suites | TestNG service loader or listener registration | +| [`allure-spock2`](allure-spock2/README.md) | Spock 2 specifications | Spock global extension service | +| [`allure-spock`](allure-spock/README.md) | Legacy Spock 1 specifications | Spock global extension service | +| [`allure-scalatest`](allure-scalatest/README.md) | ScalaTest suites | ScalaTest reporter | +| [`allure-cucumber7-jvm`](allure-cucumber7-jvm/README.md) | Cucumber JVM 7 | Cucumber plugin | +| [`allure-jbehave5`](allure-jbehave5/README.md) | JBehave 5 stories | JBehave story reporter | +| [`allure-karate`](allure-karate/README.md) | Karate runtime hooks | Karate runtime hook | +| [`allure-citrus`](allure-citrus/README.md) | Citrus tests | Citrus listeners | + +### HTTP, Browser, And Client Integrations + +| Module | Use When | Output | +| --- | --- | --- | +| [`allure-rest-assured`](allure-rest-assured/README.md) | REST Assured filters | HTTP exchange attachment | +| [`allure-httpclient5`](allure-httpclient5/README.md) | Apache HttpClient 5 interceptors | HTTP exchange attachment | +| [`allure-httpclient`](allure-httpclient/README.md) | Apache HttpClient 4 interceptors | HTTP exchange attachment | +| [`allure-okhttp3`](allure-okhttp3/README.md) | OkHttp 3 or 4 interceptors | HTTP exchange attachment | +| [`allure-spring-web`](allure-spring-web/README.md) | Spring `RestTemplate` interceptors | HTTP exchange attachment | +| [`allure-jax-rs`](allure-jax-rs/README.md) | Jakarta RESTful Web Services / JAX-RS client filters | HTTP exchange attachment | +| [`allure-servlet-api`](allure-servlet-api/README.md) | Jakarta Servlet request/response conversion | HTTP exchange request/response model | +| [`allure-grpc`](allure-grpc/README.md) | gRPC client interceptors | HTTP exchange attachment with gRPC stream metadata | +| [`allure-selenide`](allure-selenide/README.md) | Selenide UI tests | UI steps, screenshots, page source, logs | +| [`allure-selenium-bidi`](allure-selenium-bidi/README.md) | Selenium WebDriver BiDi sessions | Browser logs and network attachments | +| [`allure-playwright`](allure-playwright/README.md) | Playwright Java actions | AspectJ action steps and screenshots | +| [`allure-jooq`](allure-jooq/README.md) | jOOQ execution listener | SQL execution steps | + +### Assertions And Utilities + +| Module | Use When | Notes | +| --- | --- | --- | +| [`allure-assertj`](allure-assertj/README.md) | AssertJ assertions should appear as Allure steps | Requires AspectJ | +| [`allure-hamcrest`](allure-hamcrest/README.md) | Hamcrest assertions should appear as Allure steps | Requires AspectJ | +| [`allure-jupiter-assert`](allure-jupiter-assert/README.md) | JUnit Jupiter assertions should appear as Allure steps | Requires AspectJ | +| [`allure-awaitility`](allure-awaitility/README.md) | Awaitility polling should appear as Allure steps | Register condition listener | +| [`allure-jsonunit`](allure-jsonunit/README.md) | JsonUnit diffs should be attached to Allure | Provides JSON matcher/listener helpers | + +### Core And Support Modules + +| Module | Purpose | +| --- | --- | +| [`allure-bom`](allure-bom/README.md) | Maven/Gradle dependency alignment | +| [`allure-java-commons`](allure-java-commons/README.md) | Runtime API, lifecycle, annotations, aspects, HTTP exchange model, test-plan filtering | +| [`allure-model`](allure-model/README.md) | Serializable Allure result model | +| [`allure-descriptions-javadoc`](allure-descriptions-javadoc/README.md) | Annotation processor for JavaDoc-based test descriptions | +| [`allure-java-commons-test`](allure-java-commons-test/README.md) | Internal test utilities for Allure Java modules | + +## Common Runtime APIs + +Most adapters depend on `allure-java-commons`, which provides the high-level API: -Interceptor for Spring synchronous HTTP clients, that generates attachments for allure. +```java +import io.qameta.allure.Allure; -```xml - - io.qameta.allure - allure-spring-web - $LATEST_VERSION - +Allure.step("Create order", () -> { + Allure.attachment("request-id", "42"); +}); ``` -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. +It also provides the HTTP exchange model: -`RestTemplate` remains supported: -``` -RestTemplate restTemplate = new RestTemplate( - new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()) -); -restTemplate.setInterceptors(Collections.singletonList(new AllureRestTemplate())); -``` +```java +import io.qameta.allure.Allure; +import io.qameta.allure.http.HttpExchange; + +var exchange = HttpExchange.builder() + .redactHeader("X-Api-Key") + .redactCookie("SESSION") + .setMaxBodySize(64 * 1024) + .request("GET", "https://example.test/orders", request -> request + .addHeader("X-Api-Key", "secret")) + .response(response -> response + .setStatus(200)) + .build(); -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") +Allure.addHttpExchange("HTTP exchange", exchange); ``` -## OkHttp +## Maintainer References -Interceptor for OkHttp client, that generates attachment for allure. - -```xml - - io.qameta.allure - allure-okhttp3 - $LATEST_VERSION - -``` - -Usage example: -``` -.addInterceptor(new AllureOkHttp3()) -``` -You can specify custom templates, which should be placed in src/main/resources/tpl folder: -``` -.addInterceptor(new AllureOkHttp3() - .withRequestTemplate("custom-http-request.ftl") - .withResponseTemplate("custom-http-response.ftl")) - -``` - -## gRPC - -Interceptor for gRPC stubs, that generates attachment for allure. - -```xml - - io.qameta.allure - allure-grpc - $LATEST_VERSION - -``` - -Usage example: -``` -.newBlockingStub(channel).withInterceptors(new AllureGrpc()); -``` -You can enable interception of response metadata (disabled by default) -``` -.withInterceptors(new AllureGrpc() - .interceptResponseMetadata(true)) -``` -By default, a step will be marked as failed in case that response contains any statuses except 0(OK). -You can change this behavior, for example, for negative scenarios -``` -.withInterceptors(new AllureGrpc() - .markStepFailedOnNonZeroCode(false)) -``` -You can specify custom templates, which should be placed in src/main/resources/tpl folder: -``` -.withInterceptors(new AllureGrpc() - .setRequestTemplate("custom-http-request.ftl") - .setResponseTemplate("custom-http-response.ftl")) -``` - -## Http client - -Interceptors for Apache HTTP client, that generates attachment for allure. - -```xml - - io.qameta.allure - allure-httpclient - $LATEST_VERSION - -``` - -Usage example: -``` -.addInterceptorFirst(new AllureHttpClientRequest()) -.addInterceptorLast(new AllureHttpClientResponse()); -``` - -## Http client 5 -Interceptors for Apache [httpclient5](https://hc.apache.org/httpcomponents-client-5.2.x/index.html). -Additional info can be found in module `allure-httpclient5` - -```xml - - io.qameta.allure - allure-httpclient5 - $LATEST_VERSION - -``` - -Usage example: -```java -final HttpClientBuilder builder = HttpClientBuilder.create() - .addRequestInterceptorFirst(new AllureHttpClient5Request("your-request-template-attachment.ftl")) - .addResponseInterceptorLast(new AllureHttpClient5Response("your-response-template-attachment.ftl")); -``` - -## JAX-RS Filter - -Filter that can be used with JAX-RS compliant clients such as RESTeasy and Jersey - -```xml - - io.qameta.allure - allure-jax-rs - $LATEST_VERSION - -``` - -Usage example: -``` -.register(AllureJaxRs.class) -``` - -## JsonUnit -JsonPatchMatcher is extension of JsonUnit matcher, that generates pretty html attachment for differences based on [json diff patch](https://github.com/benjamine/jsondiffpatch/blob/master/docs/deltas.md). - -```xml - - io.qameta.allure - allure-jsonunit - $LATEST_VERSION - -``` - -## Awaitility -Extended logging for poling and ignored exceptions for [awaitility](https://github.com/awaitility/awaitility). For -more usage example look into module `allure-awaitility` - -```xml - - io.qameta.allure - allure-awaitility - $LATEST_VERSION - -``` - -Usage example: -``` -Awaitility.setDefaultConditionEvaluationListener(new AllureAwaitilityListener()); -``` - +- [Allure Report documentation](https://allurereport.org/docs/) +- [Test framework integration reference](docs/test-framework-integration-reference.md) +- [Allure Agent Mode guide](docs/allure-agent-mode.md) +- [3.0 follow-up audit](docs/3.0-audit.md) diff --git a/allure-assertj/README.md b/allure-assertj/README.md new file mode 100644 index 000000000..1855cb60b --- /dev/null +++ b/allure-assertj/README.md @@ -0,0 +1,34 @@ +# allure-assertj + +AssertJ assertion step integration for Allure Java. + +## Coordinates + +`io.qameta.allure:allure-assertj` + +```kotlin +val aspectjAgent by configurations.creating + +dependencies { + testImplementation(platform("io.qameta.allure:allure-bom:")) + testImplementation("io.qameta.allure:allure-assertj") + testRuntimeOnly("org.aspectj:aspectjrt:") + aspectjAgent("org.aspectj:aspectjweaver:") +} + +tasks.test { + doFirst { + jvmArgs("-javaagent:${aspectjAgent.singleFile}") + } +} +``` + +## Use + +Enable the AspectJ weaver for the test JVM. The module weaves AssertJ calls and reports assertion chains as Allure steps. + +## Notes + +- Works with AssertJ `Assertions.assertThat(...)` and BDD `then(...)` entry points. +- Failed assertions update the corresponding Allure step status. +- For large classpaths, limit weaving with `META-INF/aop.xml`. diff --git a/allure-assertj/build.gradle.kts b/allure-assertj/build.gradle.kts index e38cca0ea..10005f9eb 100644 --- a/allure-assertj/build.gradle.kts +++ b/allure-assertj/build.gradle.kts @@ -10,6 +10,7 @@ dependencies { testImplementation(project(":allure-java-commons-test")) testImplementation(project(":allure-junit-platform")) testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") + testRuntimeOnly("org.junit.platform:junit-platform-launcher") } tasks.jar { diff --git a/allure-attachments/build.gradle.kts b/allure-attachments/build.gradle.kts deleted file mode 100644 index c5010a3c0..000000000 --- a/allure-attachments/build.gradle.kts +++ /dev/null @@ -1,26 +0,0 @@ -description = "Allure Attachments" - -dependencies { - api(project(":allure-java-commons")) - implementation("org.freemarker:freemarker") - testImplementation("org.apache.commons:commons-lang3") - testImplementation("org.assertj:assertj-core") - testImplementation("org.junit.jupiter:junit-jupiter-api") - testImplementation("org.mockito:mockito-core") - testImplementation("org.slf4j:slf4j-simple") - testImplementation(project(":allure-java-commons-test")) - testImplementation(project(":allure-junit-platform")) - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") -} - -tasks.jar { - manifest { - attributes(mapOf( - "Automatic-Module-Name" to "io.qameta.allure.attachment" - )) - } -} - -tasks.test { - useJUnitPlatform() -} diff --git a/allure-attachments/src/main/java/io/qameta/allure/attachment/AttachmentContent.java b/allure-attachments/src/main/java/io/qameta/allure/attachment/AttachmentContent.java deleted file mode 100644 index f3c57536b..000000000 --- a/allure-attachments/src/main/java/io/qameta/allure/attachment/AttachmentContent.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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.attachment; - -/** - * Defines the attachment content contract used by Allure attachment support. - * - *

Implement this interface when custom code needs to participate in the same integration flow as the built-in Allure adapter components.

- */ -public interface AttachmentContent { - - /** - * Returns the content. - * - * @return the content - */ - String getContent(); - - /** - * Returns the content type. - * - * @return the content type - */ - String getContentType(); - - /** - * Returns the file extension. - * - * @return the file extension - */ - String getFileExtension(); - -} diff --git a/allure-attachments/src/main/java/io/qameta/allure/attachment/AttachmentRenderException.java b/allure-attachments/src/main/java/io/qameta/allure/attachment/AttachmentRenderException.java deleted file mode 100644 index b097a0e16..000000000 --- a/allure-attachments/src/main/java/io/qameta/allure/attachment/AttachmentRenderException.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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.attachment; - -/** - * Supports Allure attachment integration with Allure reporting. - * - *

Use this type through the module that owns it when translating framework execution, result metadata, or attachments into Allure report data.

- */ -public class AttachmentRenderException extends RuntimeException { - - /** - * Creates an attachment render exception with the supplied values. - * - * @param message the message - * @param cause the failure cause reported by the framework - */ - public AttachmentRenderException(final String message, final Throwable cause) { - super(message, cause); - } -} diff --git a/allure-attachments/src/main/java/io/qameta/allure/attachment/DefaultAttachmentContent.java b/allure-attachments/src/main/java/io/qameta/allure/attachment/DefaultAttachmentContent.java deleted file mode 100644 index fb70c14f3..000000000 --- a/allure-attachments/src/main/java/io/qameta/allure/attachment/DefaultAttachmentContent.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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.attachment; - -/** - * Supports Allure attachment integration with Allure reporting. - * - *

Use this type through the module that owns it when translating framework execution, result metadata, or attachments into Allure report data.

- */ -public class DefaultAttachmentContent implements AttachmentContent { - - private final String content; - - private final String contentType; - - private final String fileExtension; - - /** - * Creates a default attachment content with the supplied values. - * - * @param content the attachment content - * @param contentType the attachment content type - * @param fileExtension the attachment file extension - */ - public DefaultAttachmentContent(final String content, - final String contentType, - final String fileExtension) { - this.content = content; - this.contentType = contentType; - this.fileExtension = fileExtension; - } - - /** - * {@inheritDoc} - */ - @Override - public String getContent() { - return content; - } - - /** - * {@inheritDoc} - */ - @Override - public String getContentType() { - return contentType; - } - - /** - * {@inheritDoc} - */ - @Override - public String getFileExtension() { - return fileExtension; - } -} diff --git a/allure-attachments/src/main/java/io/qameta/allure/attachment/DefaultAttachmentProcessor.java b/allure-attachments/src/main/java/io/qameta/allure/attachment/DefaultAttachmentProcessor.java deleted file mode 100644 index 3bfc71a43..000000000 --- a/allure-attachments/src/main/java/io/qameta/allure/attachment/DefaultAttachmentProcessor.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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.attachment; - -import io.qameta.allure.Allure; -import io.qameta.allure.AllureLifecycle; - -import java.nio.charset.StandardCharsets; - -/** - * Supports Allure attachment integration with Allure reporting. - * - *

Use this type through the module that owns it when translating framework execution, result metadata, or attachments into Allure report data.

- */ -public class DefaultAttachmentProcessor implements AttachmentProcessor { - - private final AllureLifecycle lifecycle; - - /** - * Creates a default attachment processor with default configuration. - */ - public DefaultAttachmentProcessor() { - this(Allure.getLifecycle()); - } - - /** - * Creates a default attachment processor with the supplied values. - * - * @param lifecycle the Allure lifecycle to use - */ - public DefaultAttachmentProcessor(final AllureLifecycle lifecycle) { - this.lifecycle = lifecycle; - } - - /** - * {@inheritDoc} - */ - @Override - public void addAttachment(final AttachmentData attachmentData, - final AttachmentRenderer renderer) { - final AttachmentContent content = renderer.render(attachmentData); - lifecycle.addAttachment( - attachmentData.getName(), - content.getContentType(), - content.getFileExtension(), - content.getContent().getBytes(StandardCharsets.UTF_8) - ); - } -} diff --git a/allure-attachments/src/main/java/io/qameta/allure/attachment/FreemarkerAttachmentRenderer.java b/allure-attachments/src/main/java/io/qameta/allure/attachment/FreemarkerAttachmentRenderer.java deleted file mode 100644 index 6442cb62f..000000000 --- a/allure-attachments/src/main/java/io/qameta/allure/attachment/FreemarkerAttachmentRenderer.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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.attachment; - -import freemarker.template.Configuration; -import freemarker.template.Template; -import freemarker.template.TemplateExceptionHandler; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.StringWriter; -import java.io.Writer; -import java.util.Collections; - -/** - * Supports Allure attachment integration with Allure reporting. - * - *

Use this type through the module that owns it when translating framework execution, result metadata, or attachments into Allure report data.

- */ -public class FreemarkerAttachmentRenderer implements AttachmentRenderer { - - private static final Logger LOGGER = LoggerFactory.getLogger(FreemarkerAttachmentRenderer.class); - - private final Configuration configuration; - - private final String templateName; - - /** - * Creates a freemarker attachment renderer with the supplied values. - * - * @param templateName the template name - */ - public FreemarkerAttachmentRenderer(final String templateName) { - this.templateName = templateName; - this.configuration = new Configuration(Configuration.VERSION_2_3_23); - this.configuration.setLocalizedLookup(false); - this.configuration.setTemplateUpdateDelayMilliseconds(0); - this.configuration.setTemplateExceptionHandler(TemplateExceptionHandler.HTML_DEBUG_HANDLER); - this.configuration.setClassLoaderForTemplateLoading(getClass().getClassLoader(), "tpl"); - } - - /** - * {@inheritDoc} - */ - @Override - public DefaultAttachmentContent render(final AttachmentData data) { - try (Writer writer = new StringWriter()) { - final Template template = configuration.getTemplate(templateName); - template.process(Collections.singletonMap("data", data), writer); - return new DefaultAttachmentContent(writer.toString(), "text/html", ".html"); - } catch (Exception e) { - LOGGER.debug(data.toString()); - throw new AttachmentRenderException("Could't render http attachment file", e); - } - } - -} diff --git a/allure-attachments/src/main/java/io/qameta/allure/attachment/http/HttpRequestAttachment.java b/allure-attachments/src/main/java/io/qameta/allure/attachment/http/HttpRequestAttachment.java deleted file mode 100644 index b58f9c18b..000000000 --- a/allure-attachments/src/main/java/io/qameta/allure/attachment/http/HttpRequestAttachment.java +++ /dev/null @@ -1,406 +0,0 @@ -/* - * 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.attachment.http; - -import io.qameta.allure.attachment.AttachmentData; -import io.qameta.allure.util.ObjectUtils; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; - -/** - * Describes an HTTP or RPC request attachment rendered in an Allure report. - * - *

Use this model to carry request metadata and body content from client interceptors to attachment renderers and processors.

- */ -public class HttpRequestAttachment implements AttachmentData { - - private final String name; - - private final String url; - - private final String method; - - private final String body; - - private final String curl; - - private final Map headers; - - private final Map cookies; - - private final Map formParams; - - /** - * Creates an HTTP request attachment with the supplied values. - * - * @param name the display name or logical name to use - * @param url the request URL or service method name - * @param method the framework or Java method to inspect - * @param body the attachment body - * @param curl the curl - * @param headers the headers - * @param cookies the cookies - */ - public HttpRequestAttachment(final String name, final String url, final String method, - final String body, final String curl, final Map headers, - final Map cookies) { - this(name, url, method, body, curl, headers, cookies, Collections.emptyMap()); - } - - /** - * Creates an HTTP request attachment with the supplied values. - * - * @param name the display name or logical name to use - * @param url the request URL or service method name - * @param method the framework or Java method to inspect - * @param body the attachment body - * @param curl the curl - * @param headers the headers - * @param cookies the cookies - * @param formParams the form params - */ - @SuppressWarnings("checkstyle:parameternumber") - public HttpRequestAttachment(final String name, final String url, final String method, - final String body, final String curl, final Map headers, - final Map cookies, final Map formParams) { - this.name = name; - this.url = url; - this.method = method; - this.body = body; - this.curl = curl; - this.headers = headers; - this.cookies = cookies; - this.formParams = formParams; - } - - /** - * Returns the url. - * - * @return the url - */ - public String getUrl() { - return url; - } - - /** - * Returns the method. - * - * @return the method - */ - public String getMethod() { - return method; - } - - /** - * Returns the body. - * - * @return the body - */ - public String getBody() { - return body; - } - - /** - * Returns the headers. - * - * @return the headers - */ - public Map getHeaders() { - return headers; - } - - /** - * Returns the cookies. - * - * @return the cookies - */ - public Map getCookies() { - return cookies; - } - - /** - * Returns the form params. - * - * @return the form params - */ - public Map getFormParams() { - return formParams; - } - - /** - * Returns the curl. - * - * @return the curl - */ - public String getCurl() { - return curl; - } - - /** - * {@inheritDoc} - */ - @Override - public String getName() { - return name; - } - - /** - * {@inheritDoc} - */ - @Override - public String toString() { - return "HttpRequestAttachment(" - + "\n\tname=" + this.name - + ",\n\turl=" + this.url - + ",\n\tbody=" + this.body - + ",\n\theaders=" + ObjectUtils.mapToString(this.headers) - + ",\n\tcookies=" + ObjectUtils.mapToString(this.cookies) - + ",\n\tformParams=" + ObjectUtils.mapToString(this.formParams) - + "\n)"; - } - - /** - * Builder for HttpRequestAttachment. - */ - public static final class Builder { - - private final String name; - - private final String url; - - private String method; - - private String body; - - private final Map headers = new HashMap<>(); - - private final Map cookies = new HashMap<>(); - - private final Map formParams = new HashMap<>(); - - private Builder(final String name, final String url) { - Objects.requireNonNull(name, "Name must not be null value"); - Objects.requireNonNull(url, "Url must not be null value"); - this.name = name; - this.url = url; - } - - /** - * Creates a builder for a builder. - * - * @param attachmentName the attachment display name - * @param url the request URL or service method name - * @return a new builder instance - */ - public static Builder create(final String attachmentName, final String url) { - return new Builder(attachmentName, url); - } - - /** - * Sets the method. - * - * @param method the framework or Java method to inspect - * @return this instance for method chaining - */ - public Builder setMethod(final String method) { - Objects.requireNonNull(method, "Method must not be null value"); - this.method = method; - return this; - } - - /** - * Sets the header. - * - * @param name the display name or logical name to use - * @param value the value to set - * @return this instance for method chaining - */ - public Builder setHeader(final String name, final String value) { - Objects.requireNonNull(name, "Header name must not be null value"); - Objects.requireNonNull(value, "Header value must not be null value"); - this.headers.put(name, value); - return this; - } - - /** - * Sets the headers. - * - * @param headers the headers - * @return this instance for method chaining - */ - public Builder setHeaders(final Map headers) { - Objects.requireNonNull(headers, "Headers must not be null value"); - this.headers.putAll(headers); - return this; - } - - /** - * Sets the cookie. - * - * @param name the display name or logical name to use - * @param value the value to set - * @return this instance for method chaining - */ - public Builder setCookie(final String name, final String value) { - Objects.requireNonNull(name, "Cookie name must not be null value"); - Objects.requireNonNull(value, "Cookie value must not be null value"); - this.cookies.put(name, value); - return this; - } - - /** - * Sets the cookies. - * - * @param cookies the cookies - * @return this instance for method chaining - */ - public Builder setCookies(final Map cookies) { - Objects.requireNonNull(cookies, "Cookies must not be null value"); - this.cookies.putAll(cookies); - return this; - } - - /** - * Sets the body. - * - * @param body the attachment body - * @return this instance for method chaining - */ - public Builder setBody(final String body) { - Objects.requireNonNull(body, "Body should not be null value"); - this.body = body; - return this; - } - - /** - * Sets the form params. - * - * @param formParams the form params - * @return this instance for method chaining - */ - public Builder setFormParams(final Map formParams) { - Objects.requireNonNull(formParams, "Form params must not be null value"); - this.formParams.putAll(formParams); - return this; - } - - /** - * Use setter method instead. - * @deprecated scheduled for removal in 3.0 release - */ - @Deprecated - public Builder withMethod(final String method) { - return setMethod(method); - } - - /** - * Use setter method instead. - * @deprecated scheduled for removal in 3.0 release - */ - @Deprecated - public Builder withHeader(final String name, final String value) { - return setHeader(name, value); - } - - /** - * Use setter method instead. - * @deprecated scheduled for removal in 3.0 release - */ - @Deprecated - public Builder withHeaders(final Map headers) { - return setHeaders(headers); - } - - /** - * Use setter method instead. - * @deprecated scheduled for removal in 3.0 release - */ - @Deprecated - public Builder withCookie(final String name, final String value) { - return setCookie(name, value); - } - - /** - * Use setter method instead. - * @deprecated scheduled for removal in 3.0 release - */ - @Deprecated - public Builder withCookies(final Map cookies) { - return setCookies(cookies); - } - - /** - * Use setter method instead. - * @deprecated scheduled for removal in 3.0 release - */ - @Deprecated - public Builder withBody(final String body) { - return setBody(body); - } - - /** - * Builds a builder from the configured values. - * - * @return the built attachment model - */ - public HttpRequestAttachment build() { - return new HttpRequestAttachment(name, url, method, body, getCurl(), headers, cookies, formParams); - } - - private String getCurl() { - final StringBuilder builder = new StringBuilder("curl -v"); - if (Objects.nonNull(method)) { - builder.append(" -X ").append(method); - } - builder.append(" '").append(url).append('\''); - headers.forEach((key, value) -> appendHeader(builder, key, value)); - cookies.forEach((key, value) -> appendCookie(builder, key, value)); - formParams.forEach((key, value) -> appendFormParams(builder, key, value)); - - if (Objects.nonNull(body)) { - builder.append(" -d '").append(body).append('\''); - } - return builder.toString(); - } - - private static void appendHeader(final StringBuilder builder, final String key, final String value) { - builder.append(" -H '") - .append(key) - .append(": ") - .append(value) - .append('\''); - } - - private static void appendCookie(final StringBuilder builder, final String key, final String value) { - builder.append(" -b '") - .append(key) - .append('=') - .append(value) - .append('\''); - } - - private static void appendFormParams(final StringBuilder builder, final String key, final String value) { - builder.append(" --form '") - .append(key) - .append('=') - .append(value) - .append('\''); - } - } -} diff --git a/allure-attachments/src/main/java/io/qameta/allure/attachment/http/HttpResponseAttachment.java b/allure-attachments/src/main/java/io/qameta/allure/attachment/http/HttpResponseAttachment.java deleted file mode 100644 index 4a956dba9..000000000 --- a/allure-attachments/src/main/java/io/qameta/allure/attachment/http/HttpResponseAttachment.java +++ /dev/null @@ -1,331 +0,0 @@ -/* - * 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.attachment.http; - -import io.qameta.allure.attachment.AttachmentData; -import io.qameta.allure.util.ObjectUtils; - -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; - -/** - * Describes an HTTP or RPC response attachment rendered in an Allure report. - * - *

Use this model to carry response metadata and body content from client interceptors to attachment renderers and processors.

- */ -public class HttpResponseAttachment implements AttachmentData { - - private final String name; - - private final String url; - - private final String body; - - private final int responseCode; - - private final Map headers; - - private final Map cookies; - - /** - * Creates an HTTP response attachment with the supplied values. - * - * @param name the display name or logical name to use - * @param url the request URL or service method name - * @param body the attachment body - * @param responseCode the response code - * @param headers the headers - * @param cookies the cookies - */ - public HttpResponseAttachment(final String name, final String url, - final String body, final int responseCode, - final Map headers, final Map cookies) { - this.name = name; - this.url = url; - this.body = body; - this.responseCode = responseCode; - this.headers = headers; - this.cookies = cookies; - } - - /** - * {@inheritDoc} - */ - @Override - public String getName() { - return name; - } - - /** - * Returns the url. - * - * @return the url - */ - public String getUrl() { - return url; - } - - /** - * Returns the body. - * - * @return the body - */ - public String getBody() { - return body; - } - - /** - * Returns the response code. - * - * @return the response code - */ - public int getResponseCode() { - return responseCode; - } - - /** - * Returns the headers. - * - * @return the headers - */ - public Map getHeaders() { - return headers; - } - - /** - * Returns the cookies. - * - * @return the cookies - */ - public Map getCookies() { - return cookies; - } - - /** - * {@inheritDoc} - */ - @Override - public String toString() { - return "HttpResponseAttachment(" - + "\n\tname=" + this.name - + ",\n\turl=" + this.url - + ",\n\tbody=" + this.body - + ",\n\tresponseCode=" + this.responseCode - + ",\n\theaders=" + ObjectUtils.mapToString(this.headers) - + ",\n\tcookies=" + ObjectUtils.mapToString(this.cookies) - + "\n)"; - } - - /** - * Builder for HttpRequestAttachment. - */ - public static final class Builder { - - private final String name; - - private String url; - - private int responseCode; - - private String body; - - private final Map headers = new HashMap<>(); - - private final Map cookies = new HashMap<>(); - - private Builder(final String name) { - Objects.requireNonNull(name, "Name must not be null value"); - this.name = name; - } - - /** - * Creates a builder for a builder. - * - * @param attachmentName the attachment display name - * @return a new builder instance - */ - public static Builder create(final String attachmentName) { - return new Builder(attachmentName); - } - - /** - * Sets the url. - * - * @param url the request URL or service method name - * @return this instance for method chaining - */ - public Builder setUrl(final String url) { - Objects.requireNonNull(url, "Url must not be null value"); - this.url = url; - return this; - } - - /** - * Sets the response code. - * - * @param responseCode the response code - * @return this instance for method chaining - */ - public Builder setResponseCode(final int responseCode) { - this.responseCode = responseCode; - return this; - } - - /** - * Sets the header. - * - * @param name the display name or logical name to use - * @param value the value to set - * @return this instance for method chaining - */ - public Builder setHeader(final String name, final String value) { - Objects.requireNonNull(name, "Header name must not be null value"); - Objects.requireNonNull(value, "Header value must not be null value"); - this.headers.put(name, value); - return this; - } - - /** - * Sets the headers. - * - * @param headers the headers - * @return this instance for method chaining - */ - public Builder setHeaders(final Map headers) { - Objects.requireNonNull(headers, "Headers must not be null value"); - this.headers.putAll(headers); - return this; - } - - /** - * Sets the cookie. - * - * @param name the display name or logical name to use - * @param value the value to set - * @return this instance for method chaining - */ - public Builder setCookie(final String name, final String value) { - Objects.requireNonNull(name, "Cookie name must not be null value"); - Objects.requireNonNull(value, "Cookie value must not be null value"); - this.cookies.put(name, value); - return this; - } - - /** - * Sets the cookies. - * - * @param cookies the cookies - * @return this instance for method chaining - */ - public Builder setCookies(final Map cookies) { - Objects.requireNonNull(cookies, "Cookies must not be null value"); - this.cookies.putAll(cookies); - return this; - } - - /** - * Sets the body. - * - * @param body the attachment body - * @return this instance for method chaining - */ - public Builder setBody(final String body) { - Objects.requireNonNull(body, "Body should not be null value"); - this.body = body; - return this; - } - - /** - * Use setter method instead. - * - * @deprecated scheduled for removal in 3.0 release - */ - @Deprecated - public Builder withUrl(final String url) { - return setUrl(url); - } - - /** - * Use setter method instead. - * - * @deprecated scheduled for removal in 3.0 release - */ - @Deprecated - public Builder withResponseCode(final int responseCode) { - return setResponseCode(responseCode); - } - - /** - * Use setter method instead. - * - * @deprecated scheduled for removal in 3.0 release - */ - @Deprecated - public Builder withHeader(final String name, final String value) { - return setHeader(name, value); - } - - /** - * Use setter method instead. - * - * @deprecated scheduled for removal in 3.0 release - */ - @Deprecated - public Builder withHeaders(final Map headers) { - return setHeaders(headers); - } - - /** - * Use setter method instead. - * - * @deprecated scheduled for removal in 3.0 release - */ - @Deprecated - public Builder withCookie(final String name, final String value) { - return setCookie(name, value); - } - - /** - * Use setter method instead. - * - * @deprecated scheduled for removal in 3.0 release - */ - @Deprecated - public Builder withCookies(final Map cookies) { - return setCookies(cookies); - } - - /** - * Use setter method instead. - * - * @deprecated scheduled for removal in 3.0 release - */ - @Deprecated - public Builder withBody(final String body) { - return setBody(body); - } - - /** - * Builds a builder from the configured values. - * - * @return the built attachment model - */ - public HttpResponseAttachment build() { - return new HttpResponseAttachment(name, url, body, responseCode, headers, cookies); - } - } -} diff --git a/allure-attachments/src/main/resources/tpl/http-request.ftl b/allure-attachments/src/main/resources/tpl/http-request.ftl deleted file mode 100644 index 935c1c91a..000000000 --- a/allure-attachments/src/main/resources/tpl/http-request.ftl +++ /dev/null @@ -1,47 +0,0 @@ -<#ftl output_format="HTML"> -<#-- @ftlvariable name="data" type="io.qameta.allure.attachment.http.HttpRequestAttachment" --> -
<#if data.method??>${data.method}<#else>GET to <#if data.url??>${data.url}<#else>Unknown
- -<#if data.body??> -

Body

-
-
-    <#t>${data.body}
-    
-
- - -<#if (data.headers)?has_content> -

Headers

-
- <#list data.headers as name, value> -
${name}: ${value!"null"}
- -
- - - -<#if (data.cookies)?has_content> -

Cookies

-
- <#list data.cookies as name, value> -
${name}: ${value!"null"}
- -
- - -<#if data.curl??> -

Curl

-
- ${data.curl} -
- - -<#if (data.formParams)?has_content> -

FormParams

-
- <#list data.formParams as name, value> -
${name}: ${value!"null"}
- -
- diff --git a/allure-attachments/src/main/resources/tpl/http-response.ftl b/allure-attachments/src/main/resources/tpl/http-response.ftl deleted file mode 100644 index 91fed7287..000000000 --- a/allure-attachments/src/main/resources/tpl/http-response.ftl +++ /dev/null @@ -1,32 +0,0 @@ -<#ftl output_format="HTML"> -<#-- @ftlvariable name="data" type="io.qameta.allure.attachment.http.HttpResponseAttachment" --> -
Status code <#if data.responseCode??>${data.responseCode} <#else>Unknown
-<#if data.url??>
${data.url}
- -<#if data.body??> -

Body

-
-
-    <#t>${data.body}
-    
-
- - -<#if (data.headers)?has_content> -

Headers

-
- <#list data.headers as name, value> -
${name}: ${value!"null"}
- -
- - - -<#if (data.cookies)?has_content> -

Cookies

-
- <#list data.cookies as name, value> -
${name}: ${value!"null"}
- -
- diff --git a/allure-attachments/src/test/java/io/qameta/allure/attachment/DefaultAttachmentProcessorTest.java b/allure-attachments/src/test/java/io/qameta/allure/attachment/DefaultAttachmentProcessorTest.java deleted file mode 100644 index f1defa786..000000000 --- a/allure-attachments/src/test/java/io/qameta/allure/attachment/DefaultAttachmentProcessorTest.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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.attachment; - -import io.qameta.allure.AllureLifecycle; -import io.qameta.allure.attachment.http.HttpRequestAttachment; -import io.qameta.allure.test.AllureFeatures; -import org.junit.jupiter.api.Test; - -import java.nio.charset.StandardCharsets; - -import static io.qameta.allure.attachment.testdata.TestData.randomAttachmentContent; -import static io.qameta.allure.attachment.testdata.TestData.randomHttpRequestAttachment; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -class DefaultAttachmentProcessorTest { - - @SuppressWarnings("unchecked") - @AllureFeatures.Attachments - @Test - void shouldProcessAttachments() { - final HttpRequestAttachment attachment = randomHttpRequestAttachment(); - final AllureLifecycle lifecycle = mock(AllureLifecycle.class); - final AttachmentRenderer renderer = mock(AttachmentRenderer.class); - final AttachmentContent content = randomAttachmentContent(); - doReturn(content) - .when(renderer) - .render(attachment); - - new DefaultAttachmentProcessor(lifecycle) - .addAttachment(attachment, renderer); - - verify(renderer, times(1)).render(attachment); - verify(lifecycle, times(1)) - .addAttachment( - eq(attachment.getName()), - eq(content.getContentType()), - eq(content.getFileExtension()), - eq(content.getContent().getBytes(StandardCharsets.UTF_8)) - ); - } -} 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 deleted file mode 100644 index 956080c3b..000000000 --- a/allure-attachments/src/test/java/io/qameta/allure/attachment/FreemarkerAttachmentRendererTest.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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.attachment; - -import io.qameta.allure.attachment.http.HttpRequestAttachment; -import io.qameta.allure.attachment.http.HttpResponseAttachment; -import io.qameta.allure.test.AllureFeatures; -import org.junit.jupiter.api.Test; - -import static io.qameta.allure.attachment.testdata.TestData.randomHttpRequestAttachment; -import static io.qameta.allure.attachment.testdata.TestData.randomHttpResponseAttachment; -import static org.assertj.core.api.Assertions.assertThat; -class FreemarkerAttachmentRendererTest { - - private static final String CONTENT = "content"; - private static final String CONTENT_TYPE = "contentType"; - private static final String TEXT_HTML = "text/html"; - private static final String FILE_EXTENSION = "fileExtension"; - private static final String HTML = ".html"; - - @AllureFeatures.Attachments - @Test - void shouldRenderRequestAttachment() { - final HttpRequestAttachment data = randomHttpRequestAttachment(); - final DefaultAttachmentContent content = new FreemarkerAttachmentRenderer("http-request.ftl") - .render(data); - - assertThat(content) - .hasFieldOrPropertyWithValue(CONTENT_TYPE, TEXT_HTML) - .hasFieldOrPropertyWithValue(FILE_EXTENSION, HTML) - .hasFieldOrProperty(CONTENT); - } - - @AllureFeatures.Attachments - @Test - void shouldRenderResponseAttachment() { - final HttpResponseAttachment data = randomHttpResponseAttachment(); - final DefaultAttachmentContent content = new FreemarkerAttachmentRenderer("http-response.ftl") - .render(data); - - assertThat(content) - .hasFieldOrPropertyWithValue(CONTENT_TYPE, TEXT_HTML) - .hasFieldOrPropertyWithValue(FILE_EXTENSION, HTML) - .hasFieldOrProperty(CONTENT); - } -} diff --git a/allure-attachments/src/test/java/io/qameta/allure/attachment/NegativeFreemarkerAttachmentRendererTest.java b/allure-attachments/src/test/java/io/qameta/allure/attachment/NegativeFreemarkerAttachmentRendererTest.java deleted file mode 100644 index 5f5ba6d4f..000000000 --- a/allure-attachments/src/test/java/io/qameta/allure/attachment/NegativeFreemarkerAttachmentRendererTest.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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.attachment; - -import io.qameta.allure.attachment.http.HttpRequestAttachment; -import io.qameta.allure.test.AllureFeatures; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; -import java.io.UnsupportedEncodingException; -import java.nio.charset.StandardCharsets; - -import static io.qameta.allure.attachment.testdata.TestData.negativeHttpRequestAttachment; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; -class NegativeFreemarkerAttachmentRendererTest { - - private static final String TEMPLATE_FOR_EXCEPTION = "body-npe-non-safe-attachment.ftl"; - - private PrintStream realSysOut; - private ByteArrayOutputStream sysOutBuffer; - - @BeforeEach - void setUpSysOut() throws UnsupportedEncodingException { - realSysOut = System.err; - sysOutBuffer = new ByteArrayOutputStream(); - System.setErr(new PrintStream(sysOutBuffer, false, StandardCharsets.UTF_8.toString())); - } - - @AfterEach - void rollBackSysOut() { - System.setErr(realSysOut); - } - - @AllureFeatures.Attachments - @Test - void shouldThrowExceptionalSituationsForFreeMarketRendererWithIncorrectAttachmentData() { - assertThrows(AttachmentRenderException.class, () -> { - final HttpRequestAttachment data = negativeHttpRequestAttachment(); - new FreemarkerAttachmentRenderer(TEMPLATE_FOR_EXCEPTION).render(data); - }); - } - - @AllureFeatures.Attachments - @Test - void shouldExplainExceptionalSituationsForFreeMarketRenderer() throws UnsupportedEncodingException { - try { - final HttpRequestAttachment data = negativeHttpRequestAttachment(); - new FreemarkerAttachmentRenderer(TEMPLATE_FOR_EXCEPTION).render(data); - } catch (Exception ignored) { - // for test purposes - } - assertThat(sysOutBuffer.toString(StandardCharsets.UTF_8.toString())) - .contains("SEVERE: Error executing FreeMarker template") - .contains("FreeMarker template error:") - .contains("The following has evaluated to null or missing:") - .contains("==> data.body") - .contains("[in template \"body-npe-non-safe-attachment.ftl\" at line 8, column 11]") - .contains("\t- Failed at: ${data.body.size}") - .contains("io.qameta.allure.attachment.FreemarkerAttachmentRenderer - HttpRequestAttachment") - .contains("\tname=null,") - .contains("\turl=null,") - .contains("\tbody=null,") - .contains("\theaders={},") - .contains("\tcookies={}"); - } -} diff --git a/allure-attachments/src/test/java/io/qameta/allure/attachment/testdata/TestData.java b/allure-attachments/src/test/java/io/qameta/allure/attachment/testdata/TestData.java deleted file mode 100644 index 3ef9c4c63..000000000 --- a/allure-attachments/src/test/java/io/qameta/allure/attachment/testdata/TestData.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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.attachment.testdata; - -import io.qameta.allure.attachment.AttachmentContent; -import io.qameta.allure.attachment.DefaultAttachmentContent; -import io.qameta.allure.attachment.http.HttpRequestAttachment; -import io.qameta.allure.attachment.http.HttpResponseAttachment; -import org.apache.commons.lang3.RandomStringUtils; - -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.ThreadLocalRandom; -public final class TestData { - - private TestData() { - throw new IllegalStateException("Do not instance"); - } - - public static String randomString() { - return RandomStringUtils.randomAlphabetic(10); - } - - public static HttpRequestAttachment randomHttpRequestAttachment() { - return new HttpRequestAttachment( - randomString(), - randomString(), - randomString(), - randomString(), - randomString(), - randomMap(), - randomMap() - ); - } - - public static HttpResponseAttachment randomHttpResponseAttachment() { - return new HttpResponseAttachment( - randomString(), - randomString(), - randomString(), - ThreadLocalRandom.current().nextInt(), - randomMap(), - randomMap() - ); - } - - public static AttachmentContent randomAttachmentContent() { - return new DefaultAttachmentContent(randomString(), randomString(), randomString()); - } - - public static Map randomMap() { - final Map map = new HashMap<>(); - map.put(randomString(), randomString()); - map.put(randomString(), randomString()); - map.put(randomString(), null); - return map; - } - - public static HttpRequestAttachment negativeHttpRequestAttachment() { - return new HttpRequestAttachment( - null, - null, - null, - null, - null, - null, - null - ); - } - -} diff --git a/allure-attachments/src/test/resources/allure.properties b/allure-attachments/src/test/resources/allure.properties deleted file mode 100644 index b47a01f60..000000000 --- a/allure-attachments/src/test/resources/allure.properties +++ /dev/null @@ -1,3 +0,0 @@ -allure.results.directory=build/allure-results -allure.label.epic=#project.description# -allure.label.module=allure-attachments diff --git a/allure-attachments/src/test/resources/tpl/body-npe-non-safe-attachment.ftl b/allure-attachments/src/test/resources/tpl/body-npe-non-safe-attachment.ftl deleted file mode 100644 index 813425567..000000000 --- a/allure-attachments/src/test/resources/tpl/body-npe-non-safe-attachment.ftl +++ /dev/null @@ -1,36 +0,0 @@ -<#ftl output_format="HTML"> -<#-- @ftlvariable name="data" type="io.qameta.allure.attachment.http.HttpRequestAttachment" --> -
<#if data.method??>${data.method}<#else>GET to <#if data.url??>${data.url}<#else>Unknown
- -

Body

-
-
-    <#t>${data.body.size}
-    
-
- -<#if (data.headers)?has_content> -

Headers

-
- <#list data.headers as name, value> -
${name}: ${value!"null"}
- -
- - - -<#if (data.cookies)?has_content> -

Cookies

-
- <#list data.cookies as name, value> -
${name}: ${value!"null"}
- -
- - -<#if data.curl??> -

Curl

-
- ${data.curl} -
- diff --git a/allure-awaitility/README.md b/allure-awaitility/README.md new file mode 100644 index 000000000..fd4cc0e1a --- /dev/null +++ b/allure-awaitility/README.md @@ -0,0 +1,30 @@ +# allure-awaitility + +Awaitility condition listener integration for Allure Java. + +## Coordinates + +`io.qameta.allure:allure-awaitility` + +```kotlin +dependencies { + testImplementation(platform("io.qameta.allure:allure-bom:")) + testImplementation("io.qameta.allure:allure-awaitility") +} +``` + +## Use + +Register `io.qameta.allure.awaitility.AllureAwaitilityListener` as an Awaitility condition listener: + +```java +await() + .conditionEvaluationListener(new AllureAwaitilityListener()) + .untilAsserted(() -> assertThat(service.isReady()).isTrue()); +``` + +## Captured Data + +- Await start, poll, satisfaction, timeout, and exception events. +- Poll attempts as nested Allure steps. +- Timing information rendered through `TemporalDuration`. diff --git a/allure-awaitility/build.gradle.kts b/allure-awaitility/build.gradle.kts index 3c9ed6c79..d16fca3ec 100644 --- a/allure-awaitility/build.gradle.kts +++ b/allure-awaitility/build.gradle.kts @@ -8,7 +8,7 @@ dependencies { agent("org.aspectj:aspectjweaver") api(project(":allure-java-commons")) compileOnly("org.awaitility:awaitility:$awaitilityVersion") - testImplementation("javax.annotation:javax.annotation-api") + testImplementation("jakarta.annotation:jakarta.annotation-api") testImplementation("org.assertj:assertj-core") testImplementation("org.awaitility:awaitility:$awaitilityVersion") testImplementation("org.junit.jupiter:junit-jupiter-api") @@ -16,6 +16,7 @@ dependencies { testImplementation(project(":allure-java-commons-test")) testImplementation(project(":allure-junit-platform")) testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") + testRuntimeOnly("org.junit.platform:junit-platform-launcher") } tasks.jar { diff --git a/allure-awaitility/readme.md b/allure-awaitility/readme.md deleted file mode 100644 index 1fcb34691..000000000 --- a/allure-awaitility/readme.md +++ /dev/null @@ -1,71 +0,0 @@ -## Allure-awaitility -Extended logging for poling and ignored exceptions for [awaitility](https://github.com/awaitility/awaitility) - - -## Wiki -For more information about awaitility highly recommended look into [awaitility usage guide](https://github.com/awaitility/awaitility/wiki/Usage) - - -### Configuration examples -Single line for all awaitility conditions in project -```java -Awaitility.setDefaultConditionEvaluationListener(new AllureAwaitilityListener()); -``` - -And another line to prevent breaking allure lifecycle for Steps inside Awaitility evaluations -```java -Awaitility.pollInSameThread(); -``` - -Moreover, it's possible logging only few unstable conditions with method `.conditionEvaluationListener()` -```java -final AtomicInteger atomicInteger = new AtomicInteger(0); -await().with() - .conditionEvaluationListener(new AllureAwaitilityListener()) - .alias("Checking that important counter reached value around 3") - .atMost(Duration.of(1000, ChronoUnit.MILLIS)) - .pollInterval(Duration.of(50, ChronoUnit.MILLIS)) - .until(atomicInteger::getAndIncrement, is(3)); -``` - -How it looks like: -1. Top-level step with condition evaluation definition such as alias or default information. -2. Second-level steps with poling process information -3. Optional second-level step with timeout information -4. Optional second-level steps with ignored information - - -### TimeUnit -Most awaitility users count time as milliseconds, but you can feel free to change print poll information. -```java -Awaitility.setDefaultConditionEvaluationListener(new AllureAwaitilityListener().setUnit(TimeUnit.SECONDS)); -``` - - -### Exceptions handling -By default, it's not possible to handle and log any exceptions, but you can try to -[ignore](https://github.com/awaitility/awaitility/wiki/Usage#ignoring-exceptions) and log ignored exceptions. - -```java -final AtomicInteger atomicInteger = new AtomicInteger(0); -await().with() - .ignoreExceptions() //required - .atMost(Duration.of(1000, ChronoUnit.MILLIS)) - .pollInterval(Duration.of(500, ChronoUnit.MILLIS)) - .until(() -> { - if (atomicInteger.getAndIncrement() != 3) { - //this exception will be ignored by awaitility, but logged into Allure - throw new RuntimeException("Something wrong happens"); - } else { - return true; - } - }); -``` - -Then, if you are not impressed with the large volume of logged exceptions, there is the way to disable logging for -ignored exceptions globally. - -```java -Awaitility.ignoreExceptionsByDefault(); -Awaitility.setDefaultConditionEvaluationListener(new AllureAwaitilityListener().setLogIgnoredExceptions(false)); -``` \ No newline at end of file diff --git a/allure-bom/README.md b/allure-bom/README.md new file mode 100644 index 000000000..c42bbe992 --- /dev/null +++ b/allure-bom/README.md @@ -0,0 +1,37 @@ +# allure-bom + +Bill of materials for Allure Java artifacts. + +## Coordinates + +`io.qameta.allure:allure-bom` + +Gradle: + +```kotlin +dependencies { + testImplementation(platform("io.qameta.allure:allure-bom:")) + testImplementation("io.qameta.allure:allure-jupiter") + testImplementation("io.qameta.allure:allure-rest-assured") +} +``` + +Maven: + +```xml + + + + io.qameta.allure + allure-bom + ${allure.version} + pom + import + + + +``` + +## Use + +Import the BOM once, then omit versions from individual Allure Java module dependencies. This keeps framework adapters, runtime APIs, and support modules on the same release line. diff --git a/allure-bom/build.gradle.kts b/allure-bom/build.gradle.kts index 0d83d95e9..9593300aa 100644 --- a/allure-bom/build.gradle.kts +++ b/allure-bom/build.gradle.kts @@ -8,8 +8,6 @@ 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/README.md b/allure-citrus/README.md new file mode 100644 index 000000000..0c27be603 --- /dev/null +++ b/allure-citrus/README.md @@ -0,0 +1,24 @@ +# allure-citrus + +Citrus listener integration for Allure Java. + +## Coordinates + +`io.qameta.allure:allure-citrus` + +```kotlin +dependencies { + testImplementation(platform("io.qameta.allure:allure-bom:")) + testImplementation("io.qameta.allure:allure-citrus") +} +``` + +## Use + +Register `io.qameta.allure.citrus.AllureCitrus` with Citrus as a test, test suite, and test action listener. The listener translates Citrus suite, test case, and action events into Allure tests and steps. + +## Captured Data + +- Citrus test cases and suite lifecycle. +- Test actions as Allure steps. +- Standard Allure labels, links, parameters, status, and status details. diff --git a/allure-citrus/build.gradle.kts b/allure-citrus/build.gradle.kts index dbad67a9c..cfbbf6332 100644 --- a/allure-citrus/build.gradle.kts +++ b/allure-citrus/build.gradle.kts @@ -16,6 +16,7 @@ dependencies { testImplementation(project(":allure-java-commons-test")) testImplementation(project(":allure-junit-platform")) testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") + testRuntimeOnly("org.junit.platform:junit-platform-launcher") } tasks.jar { diff --git a/allure-cucumber4-jvm/build.gradle.kts b/allure-cucumber4-jvm/build.gradle.kts deleted file mode 100644 index 26b244ee0..000000000 --- a/allure-cucumber4-jvm/build.gradle.kts +++ /dev/null @@ -1,31 +0,0 @@ -description = "Allure CucumberJVM 4.0" - -val cucumberVersion = "4.8.0" - -dependencies { - api(project(":allure-java-commons")) - compileOnly("io.cucumber:cucumber-core:$cucumberVersion") - compileOnly("io.cucumber:cucumber-java:$cucumberVersion") - testImplementation("commons-io:commons-io") - testImplementation("io.cucumber:cucumber-core:$cucumberVersion") - testImplementation("io.cucumber:cucumber-java:$cucumberVersion") - testImplementation("io.github.glytching:junit-extensions") - testImplementation("org.assertj:assertj-core") - testImplementation("org.junit.jupiter:junit-jupiter-api") - testImplementation("org.slf4j:slf4j-simple") - testImplementation(project(":allure-java-commons-test")) - testImplementation(project(":allure-junit-platform")) - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") -} - -tasks.jar { - manifest { - attributes(mapOf( - "Automatic-Module-Name" to "io.qameta.allure.cucumber4jvm" - )) - } -} - -tasks.test { - useJUnitPlatform() -} diff --git a/allure-cucumber4-jvm/src/main/java/cucumber/runtime/formatter/TestSourcesModelProxy.java b/allure-cucumber4-jvm/src/main/java/cucumber/runtime/formatter/TestSourcesModelProxy.java deleted file mode 100644 index 1c19d47b3..000000000 --- a/allure-cucumber4-jvm/src/main/java/cucumber/runtime/formatter/TestSourcesModelProxy.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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 cucumber.runtime.formatter; - -import cucumber.api.event.TestSourceRead; -import gherkin.ast.Feature; -import gherkin.ast.ScenarioDefinition; - -/** - * Compatibility proxy around Cucumber feature source storage. - * - *

The proxy hides version-specific Cucumber source model APIs from the reporting plugin. Integrations use it to add source-read events and resolve feature, scenario, and step keyword metadata during execution.

- */ -public class TestSourcesModelProxy { - - private final TestSourcesModel testSources; - - /** - * Creates a test sources model proxy with default configuration. - */ - public TestSourcesModelProxy() { - this.testSources = new TestSourcesModel(); - } - - /** - * Adds the test source read event. - * - * @param path the path to read from or write to - * @param event the framework event to process - */ - public void addTestSourceReadEvent(final String path, final TestSourceRead event) { - testSources.addTestSourceReadEvent(path, event); - } - - /** - * Returns the feature. - * - * @param path the path to read from or write to - * @return the feature - */ - public Feature getFeature(final String path) { - return testSources.getFeature(path); - } - - /** - * Returns the scenario definition. - * - * @param path the path to read from or write to - * @param line the source line number to resolve - * @return the scenario definition - */ - public ScenarioDefinition getScenarioDefinition(final String path, final int line) { - return testSources.getScenarioDefinition(path, line); - } - - /** - * Returns the keyword from source. - * - * @param uri the feature file URI - * @param stepLine the feature file line number of the step - * @return the keyword from source - */ - public String getKeywordFromSource(final String uri, final int stepLine) { - return testSources.getKeywordFromSource(uri, stepLine); - } -} 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 deleted file mode 100644 index cc291892d..000000000 --- a/allure-cucumber4-jvm/src/main/java/io/qameta/allure/cucumber4jvm/AllureCucumber4Jvm.java +++ /dev/null @@ -1,540 +0,0 @@ -/* - * 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.cucumber4jvm; - -import cucumber.api.HookTestStep; -import cucumber.api.HookType; -import cucumber.api.PickleStepTestStep; -import cucumber.api.Result; -import cucumber.api.TestCase; -import cucumber.api.TestStep; -import cucumber.api.event.ConcurrentEventListener; -import cucumber.api.event.EmbedEvent; -import cucumber.api.event.EventHandler; -import cucumber.api.event.EventPublisher; -import cucumber.api.event.TestCaseFinished; -import cucumber.api.event.TestCaseStarted; -import cucumber.api.event.TestSourceRead; -import cucumber.api.event.TestStepFinished; -import cucumber.api.event.TestStepStarted; -import cucumber.api.event.WriteEvent; -import cucumber.runtime.formatter.TestSourcesModelProxy; -import gherkin.ast.Examples; -import gherkin.ast.Feature; -import gherkin.ast.ScenarioDefinition; -import gherkin.ast.ScenarioOutline; -import gherkin.ast.TableRow; -import gherkin.pickles.PickleCell; -import gherkin.pickles.PickleRow; -import gherkin.pickles.PickleStep; -import gherkin.pickles.PickleTable; -import gherkin.pickles.PickleTag; -import io.qameta.allure.Allure; -import io.qameta.allure.AllureLifecycle; -import io.qameta.allure.model.FixtureResult; -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.model.TestResultContainer; - -import java.io.ByteArrayInputStream; -import java.net.URI; -import java.nio.charset.StandardCharsets; -import java.nio.file.Paths; -import java.util.Collections; -import java.util.Deque; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; -import java.util.stream.IntStream; -import java.util.stream.Stream; - -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; - -/** - * Reports Cucumber JVM 4 execution to Allure. - * - *

Add this plugin to the Cucumber runtime so feature, scenario, step, hook, and attachment events are converted into Allure results. Use the default lifecycle for normal runs or pass one explicitly for embedded runners and tests.

- */ -@SuppressWarnings( - { - "ClassDataAbstractionCoupling", - "ClassFanOutComplexity", - "MultipleStringLiterals", - "PMD.GodClass", - } -) -public class AllureCucumber4Jvm implements ConcurrentEventListener { - - private static final String COLON = ":"; - - private final AllureLifecycle lifecycle; - - private final TestSourcesModelProxy testSources = new TestSourcesModelProxy(); - - private final EventHandler featureStartedHandler = this::handleFeatureStartedHandler; - private final EventHandler caseStartedHandler = this::handleTestCaseStarted; - private final EventHandler caseFinishedHandler = this::handleTestCaseFinished; - private final EventHandler stepStartedHandler = this::handleTestStepStarted; - private final EventHandler stepFinishedHandler = this::handleTestStepFinished; - private final EventHandler writeEventHandler = this::handleWriteEvent; - private final EventHandler embedEventHandler = this::handleEmbedEvent; - - private final Map hookStepContainerUuid = new ConcurrentHashMap<>(); - private final Map testCaseUuids = new ConcurrentHashMap<>(); - private final Map stepUuids = new ConcurrentHashMap<>(); - private final Map fixtureUuids = new ConcurrentHashMap<>(); - - private static final String TXT_EXTENSION = ".txt"; - private static final String TEXT_PLAIN = "text/plain"; - private static final String CUCUMBER_WORKING_DIR = Paths.get("").toUri().getSchemeSpecificPart(); - - /** - * Creates an Allure cucumber4 jvm with default configuration. - */ - @SuppressWarnings("unused") - public AllureCucumber4Jvm() { - this(Allure.getLifecycle()); - } - - /** - * Creates an Allure cucumber4 jvm with the supplied values. - * - * @param lifecycle the Allure lifecycle to use - */ - public AllureCucumber4Jvm(final AllureLifecycle lifecycle) { - this.lifecycle = lifecycle; - } - - /** - * {@inheritDoc} - */ - @Override - public void setEventPublisher(final EventPublisher publisher) { - publisher.registerHandlerFor(TestSourceRead.class, featureStartedHandler); - - publisher.registerHandlerFor(TestCaseStarted.class, caseStartedHandler); - publisher.registerHandlerFor(TestCaseFinished.class, caseFinishedHandler); - - publisher.registerHandlerFor(TestStepStarted.class, stepStartedHandler); - publisher.registerHandlerFor(TestStepFinished.class, stepFinishedHandler); - - publisher.registerHandlerFor(WriteEvent.class, writeEventHandler); - publisher.registerHandlerFor(EmbedEvent.class, embedEventHandler); - } - - private void handleFeatureStartedHandler(final TestSourceRead event) { - testSources.addTestSourceReadEvent(event.uri, event); - } - - private void handleTestCaseStarted(final TestCaseStarted event) { - final TestCase testCase = event.getTestCase(); - final Feature feature = testSources.getFeature(testCase.getUri()); - - final Deque tags = new LinkedList<>(testCase.getTags()); - final LabelBuilder labelBuilder = new LabelBuilder(feature, testCase, tags); - - 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", - getTestCaseUri(testCase), - testCase.getLine() - ); - - 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() - ); - - if (scenarioDefinition instanceof ScenarioOutline) { - result.setParameters( - getExamplesAsParameters((ScenarioOutline) scenarioDefinition, testCase) - ); - } - - final String description = Stream.of(feature.getDescription(), scenarioDefinition.getDescription()) - .filter(Objects::nonNull) - .filter(s -> !s.isEmpty()) - .collect(Collectors.joining("\n")); - - if (!description.isEmpty()) { - result.setDescription(description); - } - - lifecycle.scheduleTestCase(result); - lifecycle.startTestCase(testCaseUuid); - } - - private void handleTestCaseFinished(final TestCaseFinished event) { - final TestCase testCase = event.getTestCase(); - final String uuid = testCaseUuids.get(testCase); - if (Objects.isNull(uuid)) { - return; - } - - final Feature feature = testSources.getFeature(testCase.getUri()); - final Result result = event.result; - final Status status = translateTestCaseStatus(result); - final StatusDetails statusDetails = getStatusDetails(result.getError()) - .orElseGet(StatusDetails::new); - - final TagParser tagParser = new TagParser(feature, testCase); - statusDetails - .setFlaky(tagParser.isFlaky()) - .setMuted(tagParser.isMuted()) - .setKnown(tagParser.isKnown()); - - lifecycle.updateTestCase( - uuid, testResult -> testResult - .setStatus(status) - .setStatusDetails(statusDetails) - ); - - lifecycle.stopTestCase(uuid); - lifecycle.writeTestCase(uuid); - } - - private void handleTestStepStarted(final TestStepStarted event) { - final TestCase testCase = event.getTestCase(); - if (event.testStep instanceof HookTestStep) { - final HookTestStep hook = (HookTestStep) event.testStep; - - if (isFixtureHook(hook)) { - handleStartFixtureHook(testCase, hook); - } else { - handleStartStepHook(testCase, hook); - } - } else if (event.testStep instanceof PickleStepTestStep) { - handleStartPickleStep(testCase, (PickleStepTestStep) event.testStep); - } - } - - private void handleStartPickleStep(final TestCase testCase, - final PickleStepTestStep pickleStep) { - final String uuid = testCaseUuids.get(testCase); - if (Objects.isNull(uuid)) { - return; - } - - final PickleStep step = pickleStep.getPickleStep(); - final String stepKeyword = Optional - .ofNullable( - testSources.getKeywordFromSource( - testCase.getUri(), - pickleStep.getStepLine() - ) - ) - .orElse(""); - - final StepResult stepResult = new StepResult() - .setName(stepKeyword + step.getText()) - .setStart(System.currentTimeMillis()); - - final String stepUuid = stepUuids.computeIfAbsent( - pickleStep, - cl -> UUID.randomUUID().toString() - ); - - lifecycle.setCurrentTestCase(uuid); - lifecycle.startStep(uuid, stepUuid, stepResult); - - pickleStep.getStepArgument() - .stream() - .filter(PickleTable.class::isInstance) - .map(PickleTable.class::cast) - .findFirst() - .ifPresent(this::createDataTableAttachment); - - } - - private void handleStartStepHook(final TestCase testCase, - final HookTestStep hook) { - final String uuid = testCaseUuids.get(testCase); - if (Objects.isNull(uuid)) { - return; - } - - final StepResult stepResult = new StepResult() - .setName(hook.getCodeLocation()) - .setStart(System.currentTimeMillis()); - - final String stepUuid = stepUuids.computeIfAbsent( - hook, unused -> UUID.randomUUID().toString() - ); - - lifecycle.setCurrentTestCase(uuid); - lifecycle.startStep(uuid, stepUuid, stepResult); - } - - private void handleStartFixtureHook(final TestCase testCase, - final HookTestStep hook) { - final String uuid = testCaseUuids.get(testCase); - if (Objects.isNull(uuid)) { - return; - } - - final String containerUuid = hookStepContainerUuid - .computeIfAbsent(hook, unused -> UUID.randomUUID().toString()); - - lifecycle.startTestContainer( - new TestResultContainer() - .setUuid(containerUuid) - .setChildren(Collections.singletonList(uuid)) - ); - - final FixtureResult hookResult = new FixtureResult() - .setName(hook.getCodeLocation()); - - final String fixtureUuid = fixtureUuids.computeIfAbsent( - hook, unused -> UUID.randomUUID().toString() - ); - if (hook.getHookType() == Before) { - lifecycle.startPrepareFixture(containerUuid, fixtureUuid, hookResult); - } else { - lifecycle.startTearDownFixture(containerUuid, fixtureUuid, hookResult); - } - } - - private void handleTestStepFinished(final TestStepFinished event) { - if (event.testStep instanceof HookTestStep) { - final HookTestStep hook = (HookTestStep) event.testStep; - if (isFixtureHook(hook)) { - handleStopHookStep(event.result, hook); - } else { - handleStopStep(event.getTestCase(), event.result, hook); - } - } else if (event.testStep instanceof PickleStepTestStep) { - final PickleStepTestStep pickleStep = (PickleStepTestStep) event.testStep; - handleStopStep(event.getTestCase(), event.result, pickleStep); - } - } - - private static boolean isFixtureHook(final HookTestStep hook) { - return hook.getHookType() == Before || hook.getHookType() == HookType.After; - } - - private void handleWriteEvent(final WriteEvent event) { - lifecycle.addAttachment( - "Text output", - TEXT_PLAIN, - TXT_EXTENSION, - Objects.toString(event.text).getBytes(StandardCharsets.UTF_8) - ); - } - - private void handleEmbedEvent(final EmbedEvent event) { - lifecycle.addAttachment( - Objects.isNull(event.name) - ? "Embedding" - : event.name, - event.mimeType, - null, - new ByteArrayInputStream(event.data) - ); - } - - private String getHistoryId(final TestCase testCase) { - final String testCaseLocation = getTestCaseUri(testCase) + COLON + testCase.getLine(); - return md5(testCaseLocation); - } - - private String getTestCaseId(final TestCase testCase) { - final String testCaseId = getTestCaseUri(testCase) + COLON + testCase.getName(); - return md5(testCaseId); - } - - private String getTestCaseUri(final TestCase testCase) { - final String testCaseUri = getUriWithoutScheme(testCase); - - if (testCaseUri.startsWith(CUCUMBER_WORKING_DIR)) { - return testCaseUri.substring(CUCUMBER_WORKING_DIR.length()); - } - return testCaseUri; - } - - private static String getUriWithoutScheme(final TestCase testCase) { - try { - return URI.create(testCase.getUri()).getSchemeSpecificPart(); - } catch (Exception ignored) { - return testCase.getUri(); - } - } - - private Status translateTestCaseStatus(final Result testCaseResult) { - switch (testCaseResult.getStatus()) { - case FAILED: - return getStatus(testCaseResult.getError()) - .orElse(Status.FAILED); - case PASSED: - return Status.PASSED; - case SKIPPED: - case PENDING: - return Status.SKIPPED; - case AMBIGUOUS: - case UNDEFINED: - default: - return null; - } - } - - 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(); - - if (!maybeExample.isPresent()) { - return Collections.emptyList(); - } - - final Examples examples = maybeExample.get(); - - final Optional maybeRow = examples.getTableBody().stream() - .filter(example -> example.getLocation().getLine() == localCurrentTestCase.getLine()) - .findFirst(); - - if (!maybeRow.isPresent()) { - return Collections.emptyList(); - } - - final TableRow row = maybeRow.get(); - - return IntStream.range(0, examples.getTableHeader().getCells().size()) - .mapToObj(index -> { - final String name = examples.getTableHeader().getCells().get(index).getValue(); - final String value = row.getCells().get(index).getValue(); - return createParameter(name, value); - }) - .collect(Collectors.toList()); - } - - private void createDataTableAttachment(final PickleTable pickleTable) { - final List rows = pickleTable.getRows(); - - final StringBuilder dataTableCsv = new StringBuilder(); - for (PickleRow row : rows) { - final String rowString = row.getCells().stream() - .map(PickleCell::getValue) - .collect(Collectors.joining("\t", "", "\n")); - dataTableCsv.append(rowString); - } - final String attachmentSource = lifecycle - .prepareAttachment("Data table", "text/tab-separated-values", "csv"); - lifecycle.writeAttachment( - attachmentSource, - new ByteArrayInputStream(dataTableCsv.toString().getBytes(StandardCharsets.UTF_8)) - ); - } - - private void handleStopHookStep(final Result eventResult, - final HookTestStep hook) { - final String containerUuid = hookStepContainerUuid.get(hook); - if (Objects.isNull(containerUuid)) { - // maybe throw an exception? - return; - } - - final String uuid = fixtureUuids.get(hook); - if (Objects.isNull(uuid)) { - // maybe throw an exception? - return; - } - - final Status status = translateTestCaseStatus(eventResult); - final StatusDetails statusDetails = getStatusDetails(eventResult.getError()) - .orElseGet(StatusDetails::new); - - lifecycle.updateFixture( - uuid, result -> result - .setStatus(status) - .setStatusDetails(statusDetails) - ); - lifecycle.stopFixture(uuid); - - lifecycle.stopTestContainer(containerUuid); - lifecycle.writeTestContainer(containerUuid); - } - - private void handleStopStep(final TestCase testCase, - final Result eventResult, - final TestStep step) { - final String stepUuid = stepUuids.get(step); - if (Objects.isNull(stepUuid)) { - // maybe exception? - return; - } - - final Feature feature = testSources.getFeature(testCase.getUri()); - - final Status stepStatus = translateTestCaseStatus(eventResult); - - final StatusDetails statusDetails = eventResult.getStatus() == Result.Type.UNDEFINED - ? new StatusDetails().setMessage("Undefined Step. Please add step definition") - : getStatusDetails(eventResult.getError()) - .orElse(new StatusDetails()); - - final TagParser tagParser = new TagParser(feature, testCase); - statusDetails - .setFlaky(tagParser.isFlaky()) - .setMuted(tagParser.isMuted()) - .setKnown(tagParser.isKnown()); - - lifecycle.updateStep( - stepUuid, - stepResult -> stepResult - .setStatus(stepStatus) - .setStatusDetails(statusDetails) - ); - lifecycle.stopStep(stepUuid); - } -} 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 deleted file mode 100644 index 1f54ea8f9..000000000 --- a/allure-cucumber4-jvm/src/main/java/io/qameta/allure/cucumber4jvm/LabelBuilder.java +++ /dev/null @@ -1,201 +0,0 @@ -/* - * 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.cucumber4jvm; - -import cucumber.api.TestCase; -import gherkin.ast.Feature; -import gherkin.pickles.PickleTag; -import io.qameta.allure.model.Label; -import io.qameta.allure.model.Link; -import io.qameta.allure.util.ResultsUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.File; -import java.net.URI; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Deque; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.regex.Pattern; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static io.qameta.allure.util.ResultsUtils.createFeatureLabel; -import static io.qameta.allure.util.ResultsUtils.createFrameworkLabel; -import static io.qameta.allure.util.ResultsUtils.createHostLabel; -import static io.qameta.allure.util.ResultsUtils.createLabel; -import static io.qameta.allure.util.ResultsUtils.createLanguageLabel; -import static io.qameta.allure.util.ResultsUtils.createStoryLabel; -import static io.qameta.allure.util.ResultsUtils.createSuiteLabel; -import static io.qameta.allure.util.ResultsUtils.createTestClassLabel; -import static io.qameta.allure.util.ResultsUtils.createThreadLabel; - -/** - * Scenario labels and links builder. - */ -@SuppressWarnings({"CyclomaticComplexity", "MultipleStringLiterals", "PMD.CognitiveComplexity"}) -final class LabelBuilder { - private static final Logger LOGGER = LoggerFactory.getLogger(LabelBuilder.class); - private static final String COMPOSITE_TAG_DELIMITER = "="; - - private static final String SEVERITY = "@SEVERITY"; - private static final String ISSUE_LINK = "@ISSUE"; - private static final String TMS_LINK = "@TMSLINK"; - private static final String PLAIN_LINK = "@LINK"; - - private final List