Skip to content

Commit c659614

Browse files
SergeyPirogovbaev
authored andcommitted
support for custom link annotations (via allure-framework#354)
1 parent 36ee626 commit c659614

5 files changed

Lines changed: 125 additions & 5 deletions

File tree

allure-java-commons/src/main/java/io/qameta/allure/Link.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
*/
1616
package io.qameta.allure;
1717

18+
import io.qameta.allure.util.ResultsUtils;
19+
1820
import java.lang.annotation.Documented;
1921
import java.lang.annotation.ElementType;
2022
import java.lang.annotation.Inherited;
@@ -66,5 +68,5 @@
6668
*
6769
* @return the link type.
6870
*/
69-
String type() default "custom";
71+
String type() default ResultsUtils.CUSTOM_LINK_TYPE;
7072
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright 2019 Qameta Software OÜ
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.qameta.allure;
17+
18+
import java.lang.annotation.Documented;
19+
import java.lang.annotation.ElementType;
20+
import java.lang.annotation.Inherited;
21+
import java.lang.annotation.Retention;
22+
import java.lang.annotation.RetentionPolicy;
23+
import java.lang.annotation.Target;
24+
25+
import static io.qameta.allure.util.ResultsUtils.CUSTOM_LINK_TYPE;
26+
27+
/**
28+
* Marker annotation. Annotations marked by this annotation will be discovered
29+
* by Allure and added to test results as a link.
30+
*
31+
* @see Link
32+
* @see TmsLink
33+
* @see Issue
34+
*/
35+
@Documented
36+
@Inherited
37+
@Retention(RetentionPolicy.RUNTIME)
38+
@Target({ElementType.ANNOTATION_TYPE})
39+
public @interface LinkAnnotation {
40+
41+
/**
42+
* The value of link. In not specified will take value from <code>value()</code>
43+
* method of target annotation.
44+
*
45+
* @return the value of the link to add.
46+
*/
47+
String value() default "";
48+
49+
/**
50+
* This type is used for create an icon for link. Also there is few reserved types such as issue and tms.
51+
*
52+
* @return the link type.
53+
*/
54+
String type() default CUSTOM_LINK_TYPE;
55+
}

allure-java-commons/src/main/java/io/qameta/allure/util/AnnotationUtils.java

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package io.qameta.allure.util;
1717

1818
import io.qameta.allure.LabelAnnotation;
19+
import io.qameta.allure.LinkAnnotation;
1920
import io.qameta.allure.model.Label;
2021
import io.qameta.allure.model.Link;
2122
import org.slf4j.Logger;
@@ -26,17 +27,18 @@
2627
import java.lang.reflect.AnnotatedElement;
2728
import java.lang.reflect.InvocationTargetException;
2829
import java.lang.reflect.Method;
29-
import java.util.ArrayList;
30-
import java.util.Arrays;
3130
import java.util.Collection;
3231
import java.util.Collections;
3332
import java.util.List;
33+
import java.util.ArrayList;
3434
import java.util.Objects;
3535
import java.util.Set;
3636
import java.util.function.Function;
3737
import java.util.stream.Collectors;
3838
import java.util.stream.Stream;
3939

40+
import static java.util.Arrays.asList;
41+
4042
/**
4143
* Collection of utils used by Allure integration to extract meta information from
4244
* test cases via reflection.
@@ -64,6 +66,7 @@ public static List<Link> getLinks(final AnnotatedElement annotatedElement) {
6466
result.addAll(extractLinks(annotatedElement, io.qameta.allure.Link.class, ResultsUtils::createLink));
6567
result.addAll(extractLinks(annotatedElement, io.qameta.allure.Issue.class, ResultsUtils::createLink));
6668
result.addAll(extractLinks(annotatedElement, io.qameta.allure.TmsLink.class, ResultsUtils::createLink));
69+
result.addAll(extractCustomLinks(asList(annotatedElement.getDeclaredAnnotations())));
6770
return result;
6871
}
6972

@@ -74,7 +77,7 @@ public static List<Link> getLinks(final AnnotatedElement annotatedElement) {
7477
* @return discovered links.
7578
*/
7679
public static List<Link> getLinks(final Annotation... annotations) {
77-
return getLinks(Arrays.asList(annotations));
80+
return getLinks(asList(annotations));
7881
}
7982

8083
/**
@@ -88,6 +91,7 @@ public static List<Link> getLinks(final Collection<Annotation> annotations) {
8891
result.addAll(extractLinks(annotations, io.qameta.allure.Link.class, ResultsUtils::createLink));
8992
result.addAll(extractLinks(annotations, io.qameta.allure.Issue.class, ResultsUtils::createLink));
9093
result.addAll(extractLinks(annotations, io.qameta.allure.TmsLink.class, ResultsUtils::createLink));
94+
result.addAll(extractCustomLinks(annotations));
9195
return result;
9296
}
9397

@@ -109,7 +113,7 @@ public static Set<Label> getLabels(final AnnotatedElement annotatedElement) {
109113
* @return discovered labels.
110114
*/
111115
public static Set<Label> getLabels(final Annotation... annotations) {
112-
return getLabels(Arrays.asList(annotations));
116+
return getLabels(asList(annotations));
113117
}
114118

115119
/**
@@ -129,6 +133,7 @@ public static Set<Label> getLabels(final Collection<Annotation> annotations) {
129133
private static <T extends Annotation> Set<Link> extractLinks(final AnnotatedElement element,
130134
final Class<T> annotationType,
131135
final Function<T, Link> mapper) {
136+
132137
return Stream.of(element.getAnnotationsByType(annotationType))
133138
.map(mapper)
134139
.collect(Collectors.toSet());
@@ -146,6 +151,32 @@ private static <T extends Annotation> Set<Link> extractLinks(final Collection<An
146151
.collect(Collectors.toSet());
147152
}
148153

154+
private static Collection<? extends Link> extractCustomLinks(final Collection<Annotation> annotations) {
155+
return annotations.stream()
156+
.flatMap(AnnotationUtils::extractRepeatable)
157+
.filter(annotation -> annotation.annotationType().isAnnotationPresent(LinkAnnotation.class))
158+
.flatMap(annotation -> AnnotationUtils.toLink(annotation).stream())
159+
.collect(Collectors.toSet());
160+
}
161+
162+
private static Set<Link> toLink(final Annotation annotation) {
163+
final LinkAnnotation linkAnnotation = annotation.annotationType().getAnnotation(LinkAnnotation.class);
164+
165+
try {
166+
final Method method = annotation.annotationType().getMethod(VALUE_METHOD_NAME);
167+
final Object object = method.invoke(annotation);
168+
return objectToStringStream(object)
169+
.map(value -> ResultsUtils.createLink("", value, "", linkAnnotation.type()))
170+
.collect(Collectors.toSet());
171+
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
172+
LOGGER.error(
173+
"Invalid annotation {}: marker annotations should contains value() method",
174+
annotation
175+
);
176+
}
177+
return Collections.emptySet();
178+
}
179+
149180
private static Set<Label> getMarks(final Annotation annotation) {
150181
return Stream.of(annotation.annotationType().getAnnotationsByType(LabelAnnotation.class))
151182
.map(marker -> getLabel(annotation, marker))

allure-java-commons/src/main/java/io/qameta/allure/util/ResultsUtils.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ public final class ResultsUtils {
7575

7676
public static final String ISSUE_LINK_TYPE = "issue";
7777
public static final String TMS_LINK_TYPE = "tms";
78+
public static final String CUSTOM_LINK_TYPE = "custom";
7879

7980
public static final String ALLURE_ID_LABEL_NAME = "AS_ID";
8081
public static final String SUITE_LABEL_NAME = "suite";

allure-java-commons/src/test/java/io/qameta/allure/util/AnnotationUtilsTest.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import io.qameta.allure.Issues;
2424
import io.qameta.allure.LabelAnnotation;
2525
import io.qameta.allure.Link;
26+
import io.qameta.allure.LinkAnnotation;
2627
import io.qameta.allure.Links;
2728
import io.qameta.allure.Story;
2829
import io.qameta.allure.TmsLink;
@@ -192,6 +193,18 @@ void shouldExtractLinksFromAnnotationList() {
192193
);
193194
}
194195

196+
@ResourceLock(value = SYSTEM_PROPERTIES, mode = READ_WRITE)
197+
@SystemProperty(name = "allure.link.custom.pattern", value = "https://example.org/custom/{}")
198+
@SystemProperty(name = "allure.link.tms.pattern", value = "https://tms.com/custom/{}")
199+
@Test
200+
void shouldExtractCustomLinks() {
201+
assertThat(getLinks(WithCustomLink.class.getDeclaredAnnotations()))
202+
.extracting(io.qameta.allure.model.Link::getUrl)
203+
.containsOnly("https://example.org/custom/LINK-2",
204+
"https://example.org/custom/LINK-1",
205+
"https://tms.com/custom/ISSUE-1");
206+
}
207+
195208
@Epic("e1")
196209
@Feature("f1")
197210
@Story("s1")
@@ -277,4 +290,22 @@ class WithLinks {
277290
public @interface CustomMultiLabel {
278291
}
279292

293+
@Retention(RetentionPolicy.RUNTIME)
294+
@Target({ElementType.METHOD, ElementType.TYPE})
295+
@LinkAnnotation
296+
public @interface CustomLink {
297+
String value() default "";
298+
}
299+
300+
@Retention(RetentionPolicy.RUNTIME)
301+
@Target({ElementType.METHOD, ElementType.TYPE})
302+
@LinkAnnotation(type = "tms")
303+
public @interface CustomIssue {
304+
String value();
305+
}
306+
307+
@CustomLink("LINK-2")
308+
@Link("LINK-1")
309+
@CustomIssue("ISSUE-1")
310+
class WithCustomLink { }
280311
}

0 commit comments

Comments
 (0)