1
+ package io .swaglabs .portal .qa .utils ;
2
+
3
+ import com .microsoft .playwright .Locator ;
4
+ import com .microsoft .playwright .Page ;
5
+ import io .restassured .RestAssured ;
6
+ import io .restassured .response .Response ;
7
+ import lombok .AccessLevel ;
8
+ import lombok .NoArgsConstructor ;
9
+ import lombok .extern .slf4j .Slf4j ;
10
+ import org .apache .http .HttpStatus ;
11
+
12
+ import java .util .List ;
13
+ import java .util .Objects ;
14
+ import java .util .Set ;
15
+ import java .util .concurrent .ExecutorService ;
16
+ import java .util .concurrent .Executors ;
17
+ import java .util .concurrent .Future ;
18
+ import java .util .stream .Collectors ;
19
+
20
+ /**
21
+ * Utility class to validate all <a href> links on a web page using Playwright and RestAssured.
22
+ * Collects all anchor tags, resolves their full URLs, checks for broken links (4xx/5xx), and logs the results.
23
+ * Usage:
24
+ * BrokenLinkValidatorUtils.validateAllLinks(page, "MethodNameXYZ");
25
+ */
26
+ @ Slf4j
27
+ @ NoArgsConstructor (access = AccessLevel .PRIVATE )
28
+ public class BrokenLinkValidatorUtils {
29
+
30
+ /**
31
+ * Extracts and validates all unique anchor tags (<a href>) on the page.
32
+ *
33
+ * @param page The Playwright page instance.
34
+ * @param methodName Name of the test method (for logging context).
35
+ */
36
+ public static void validateAllLinks (Page page , String methodName ) {
37
+ Objects .requireNonNull (page , "Playwright page cannot be null!" );
38
+ Set <String > uniqueLinks = extractUniqueLinks (page );
39
+ log .info ("Total unique links found in method [{}]: {}" , methodName , uniqueLinks .size ());
40
+ ExecutorService executor = Executors .newFixedThreadPool (10 ); // Tune this based on infra
41
+ List <Future <String >> futures = uniqueLinks .stream ()
42
+ .map (link -> executor .submit (() -> {
43
+ String resolvedUrl = resolveAbsoluteUrl (page .url (), link );
44
+ try {
45
+ Response response = RestAssured .given ()
46
+ .relaxedHTTPSValidation ()
47
+ .when ()
48
+ .head (resolvedUrl );
49
+ int statusCode = response .getStatusCode ();
50
+ if (statusCode >= HttpStatus .SC_BAD_REQUEST ) {
51
+ log .info ("BROKEN LINK: {} --> HTTP {}" , resolvedUrl , statusCode );
52
+ return resolvedUrl + " --> HTTP " + statusCode ;
53
+ } else {
54
+ log .debug ("Valid Link: {} --> HTTP {}" , resolvedUrl , statusCode );
55
+ return null ;
56
+ }
57
+ } catch (Exception e ) {
58
+ log .info ("BROKEN LINK: {} --> Exception: {}" , resolvedUrl , e .getMessage ());
59
+ return resolvedUrl + " --> Exception: " + e .getMessage ();
60
+ }
61
+ }))
62
+ .toList ();
63
+ List <String > brokenLinks = futures .stream ()
64
+ .map (future -> {
65
+ try {
66
+ return future .get (); // blocking until complete
67
+ } catch (Exception e ) {
68
+ String errMsg = "Future execution failed --> " + e .getMessage ();
69
+ log .info (errMsg );
70
+ return errMsg ;
71
+ }
72
+ })
73
+ .filter (Objects ::nonNull )
74
+ .toList ();
75
+ executor .shutdown ();
76
+ if (brokenLinks .isEmpty ()) {
77
+ log .info ("No broken links found on page: {}" , page .url ());
78
+ } else {
79
+ log .info ("Total broken links found on page [{}]: {}" , page .url (), brokenLinks .size ());
80
+ brokenLinks .forEach (link -> log .info ("Broken --> {}" , link ));
81
+ }
82
+ }
83
+
84
+
85
+ /**
86
+ * Extracts all unique href values from <a> tags on the page.
87
+ *
88
+ * @param page Playwright Page
89
+ * @return Set of unique link hrefs
90
+ */
91
+ private static Set <String > extractUniqueLinks (Page page ) {
92
+ Locator anchors = page .locator ("a[href]" );
93
+ return anchors .all ().stream ()
94
+ .map (element -> element .getAttribute ("href" ))
95
+ .filter (Objects ::nonNull )
96
+ .map (String ::trim )
97
+ .filter (href -> !href .isEmpty ())
98
+ .filter (href -> !href .startsWith ("javascript:" ))
99
+ .filter (href -> !href .startsWith ("#" ))
100
+ .collect (Collectors .toSet ());
101
+ }
102
+
103
+ /**
104
+ * Resolves relative URLs against the base page URL.
105
+ *
106
+ * @param baseUrl The current page URL
107
+ * @param hrefValue The raw href
108
+ * @return Absolute URL
109
+ */
110
+ private static String resolveAbsoluteUrl (String baseUrl , String hrefValue ) {
111
+ if (hrefValue .startsWith ("http" )) {
112
+ return hrefValue ;
113
+ }
114
+ if (hrefValue .startsWith ("/" )) {
115
+ return baseUrl .replaceAll ("(?<=https?://[^/]+).*" , "" ) + hrefValue ;
116
+ }
117
+ // Fallback for relative paths
118
+ return baseUrl + "/" + hrefValue ;
119
+ }
120
+ }
0 commit comments