diff --git a/maqs-accessibility/pom.xml b/maqs-accessibility/pom.xml
index 78a45cf89..5121edd95 100644
--- a/maqs-accessibility/pom.xml
+++ b/maqs-accessibility/pom.xml
@@ -18,7 +18,7 @@
4.4.1
1.9
- 1.14.3
+ 1.15.2
@@ -26,11 +26,20 @@
com.cognizantsoftvision.maqs.selenium
maqs-selenium
+
+ com.cognizantsoftvision.maqs.playwright
+ maqs-playwright
+
com.deque.html.axe-core
selenium
${deque.axe.version}
+
+ com.deque.html.axe-core
+ playwright
+ ${deque.axe.version}
+
io.github.bonigarcia
webdrivermanager
diff --git a/maqs-accessibility/src/main/java/com/cognizantsoftvision/maqs/accessibility/HtmlReporter.java b/maqs-accessibility/src/main/java/com/cognizantsoftvision/maqs/accessibility/HtmlReporter.java
index c8ebb7ba4..48a026d72 100644
--- a/maqs-accessibility/src/main/java/com/cognizantsoftvision/maqs/accessibility/HtmlReporter.java
+++ b/maqs-accessibility/src/main/java/com/cognizantsoftvision/maqs/accessibility/HtmlReporter.java
@@ -552,7 +552,7 @@ private static String getDataImageString(SearchContext context) {
* @return The timestamp as a specified date formatted string
* @throws ParseException If parse exception occurs
*/
- private static String getDateFormat(String timestamp) throws ParseException {
+ static String getDateFormat(String timestamp) throws ParseException {
Date date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").parse(timestamp);
return new SimpleDateFormat("dd-MMM-yy HH:mm:ss Z").format(date);
}
@@ -562,7 +562,7 @@ private static String getDateFormat(String timestamp) throws ParseException {
* @return the javascript file script as a string
* @throws IOException if an exception is thrown
*/
- private static String getJavascriptFileToString() throws IOException {
+ public static String getJavascriptFileToString() throws IOException {
return String.valueOf(Files.readAllLines(Paths.get(RESOURCES_FILE + "htmlReporterElements.js")));
}
}
\ No newline at end of file
diff --git a/maqs-accessibility/src/main/java/com/cognizantsoftvision/maqs/accessibility/PlaywrightReporter.java b/maqs-accessibility/src/main/java/com/cognizantsoftvision/maqs/accessibility/PlaywrightReporter.java
new file mode 100644
index 000000000..ef0b311b2
--- /dev/null
+++ b/maqs-accessibility/src/main/java/com/cognizantsoftvision/maqs/accessibility/PlaywrightReporter.java
@@ -0,0 +1,542 @@
+/*
+ * Copyright 2022 (C) Cognizant SoftVision, All rights Reserved
+ */
+
+package com.cognizantsoftvision.maqs.accessibility;
+
+
+import com.deque.html.axecore.playwright.AxeBuilder;
+import com.deque.html.axecore.selenium.ResultType;
+import com.deque.html.axecore.utilities.axeresults.AxeResults;
+import com.deque.html.axecore.utilities.axeresults.Check;
+import com.deque.html.axecore.utilities.axeresults.CheckedNode;
+import com.deque.html.axecore.utilities.axeresults.Rule;
+import com.microsoft.playwright.Page;
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.text.ParseException;
+import java.util.Base64;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import org.apache.commons.io.FileUtils;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.DataNode;
+import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Element;
+import org.openqa.selenium.WebElement;
+
+/**
+ * The HTML reporter class.
+ */
+public class PlaywrightReporter extends HtmlReporter {
+
+ /**
+ * Placeholder for wrap one tag string type.
+ */
+ private static final String WRAP_ONE = "wrapOne";
+
+ /**
+ * File path to resources java resources folder.
+ */
+ private static final String RESOURCES_FILE = "../maqs-accessibility/src/main/resources/";
+
+ /**
+ * Class constructor.
+ */
+ protected PlaywrightReporter() {
+ }
+
+ /**
+ * Create an HTML accessibility report for an entire web page.
+ * @param page the web driver used in the scan
+ * @param destination the destination file the html report will go to
+ * @throws IOException if an IO exception is thrown
+ * @throws ParseException if a parse exception is thrown
+ */
+ public static void createAxeHtmlReport(Page page, String destination)
+ throws IOException, ParseException {
+ createAxeHtmlReport(page, destination, EnumSet.allOf(ResultType.class));
+ }
+
+ /**
+ * Create an HTML accessibility report for an entire web page with specific Result types.
+ * @param page the web driver used in the scan
+ * @param destination the destination file the html report will go to
+ * @param requestedResults the specified result types to include in the report
+ * @throws IOException if an IO exception is thrown
+ * @throws ParseException if a parse exception is thrown
+ */
+ public static void createAxeHtmlReport(Page page, String destination, Set requestedResults)
+ throws IOException, ParseException {
+ createAxeHtmlReport(page, new AxeBuilder(page).analyze(), destination, requestedResults);
+ }
+
+ /**
+ * Create an HTML accessibility report for a specific element.
+ * @param page the web driver used in the scan
+ * @param element the element to be scanned
+ * @param destination the destination file the html report will go to
+ * @throws IOException if an IO exception is thrown
+ * @throws ParseException if a parse exception is thrown
+ */
+ public static void createAxeHtmlReport(Page page, WebElement element, String destination)
+ throws IOException, ParseException {
+ createAxeHtmlReport(page, element, destination, EnumSet.allOf(ResultType.class));
+ }
+
+ /**
+ * Create an HTML accessibility report for a specific element and specified result types.
+ * @param page the web driver used in the scan
+ * @param element the element to be scanned
+ * @param destination the destination file the html report will go to
+ * @param requestedResults the specified result types to include in the report
+ * @throws IOException if an IO exception is thrown
+ * @throws ParseException if a parse exception is thrown
+ */
+ public static void createAxeHtmlReport(Page page, WebElement element, String destination,
+ Set requestedResults) throws IOException, ParseException {
+ // createAxeHtmlReport(page, new AxeBuilder(page).analyze(element), destination, requestedResults);
+ }
+
+ /**
+ * Create an HTML accessibility report for an entire web page with already scanned results.
+ * @param page the web driver used in the scan
+ * @param results the results type variable used after scanning the web page
+ * @param destination the destination file the html report will go to
+ * @throws IOException if an IO exception is thrown
+ * @throws ParseException if a parse exception is thrown
+ */
+ public static void createAxeHtmlReport(Page page, AxeResults results, String destination)
+ throws IOException, ParseException {
+ createAxeHtmlReport(page, results, destination, EnumSet.allOf(ResultType.class));
+ }
+
+ /**
+ * Create an HTML accessibility report for an entire web page with specified Result types
+ * and inputted already scanned results.
+ * @param page the web driver used in the scan
+ * @param results the results object created after scanning the web page
+ * @param destination the destination file the html report will go to
+ * @param requestedResults the specified result types to include in the report
+ * @throws IOException if an IO exception is thrown
+ * @throws ParseException if a parse exception is thrown
+ */
+ public static void createAxeHtmlReport(Page page, AxeResults results, String destination,
+ Set requestedResults) throws IOException, ParseException {
+ createAxeHtmlReportFile(page, results, destination, requestedResults);
+ }
+
+ /**
+ * Creates an HTML accessibility report.
+ * @param context the Search Context to be used in the scan
+ * @param results the results object created after scanning the web page
+ * @param destination the destination file the html report will go to
+ * @param requestedResults the specified result types to include in the report
+ * @throws IOException if an IO exception is thrown
+ * @throws ParseException if a parse exception is thrown
+ */
+ private static void createAxeHtmlReportFile(Page context, AxeResults results, String destination,
+ Set requestedResults) throws IOException, ParseException {
+ final int violationCount = getCount(results.getViolations());
+ final int incompleteCount = getCount(results.getIncomplete());
+ final int passCount = getCount(results.getPasses());
+ final int inapplicableCount = getCount(results.getInapplicable());
+
+ String stringBuilder = String.valueOf(Files.readString(Paths.get(RESOURCES_FILE + "htmlReporterTags.html")));
+ stringBuilder = stringBuilder.replace(System.lineSeparator(), "");
+
+ Document doc = Jsoup.parse(stringBuilder);
+
+ doc.select("style").append(getCss(context));
+
+ Element contentArea = doc.select("content").first();
+
+ Element reportTitle = new Element("h1");
+ reportTitle.text("Accessibility Check");
+ Objects.requireNonNull(contentArea).appendChild(reportTitle);
+
+ Element metaFlex = new Element("div");
+ metaFlex.attributes().put("id", "metadata");
+ contentArea.appendChild(metaFlex);
+
+ Element contextGroup = new Element("div");
+ contextGroup.attributes().put("id", "context");
+ metaFlex.appendChild(contextGroup);
+
+ Element contextHeader = new Element("h3");
+ contextHeader.text("Context:");
+ contextGroup.appendChild(contextHeader);
+
+ Element contextContent = new Element("div");
+ contextContent.addClass("emOne");
+ contextContent.attributes().put("id", "reportContext");
+ getContextContent(results, contextContent);
+ contextGroup.appendChild(contextContent);
+
+ Element imgGroup = new Element("div");
+ imgGroup.attributes().put("id", "image");
+ metaFlex.appendChild(imgGroup);
+
+ Element imageHeader = new Element("h3");
+ imageHeader.appendText("Image:");
+ imgGroup.appendChild(imageHeader);
+
+ Element imageContent = new Element("img");
+ imageContent.addClass("thumbnail");
+ imageContent.attributes().put("id", "screenshotThumbnail");
+ imageContent.attributes().put("alt", "A Screenshot of the page");
+ imageContent.attributes().put("width", "33%");
+ imageContent.attributes().put("height", "auto");
+ imgGroup.appendChild(imageContent);
+
+ Element countsGroup = new Element("div");
+ countsGroup.attributes().put("id", "counts");
+ metaFlex.appendChild(countsGroup);
+
+ Element countsHeader = new Element("h3");
+ countsHeader.appendText("Counts:");
+ countsGroup.appendChild(countsHeader);
+
+ Element countsContent = new Element("div");
+ countsContent.addClass("emOne");
+ getCountContent(violationCount, incompleteCount, passCount, inapplicableCount, requestedResults, countsContent);
+ countsGroup.appendChild(countsContent);
+
+ Element resultsFlex = new Element("div");
+ resultsFlex.attributes().put("id", "results");
+ contentArea.appendChild(resultsFlex);
+
+ if (results.isErrored()) {
+ Element errorHeader = new Element("h2");
+ errorHeader.appendText("SCAN ERRORS:");
+ contentArea.appendChild(errorHeader);
+
+ Element errorContent = new Element("div");
+ errorContent.attributes().put("id", "ErrorMessage");
+ errorContent.appendText(results.getErrorMessage());
+ contentArea.appendChild(errorContent);
+ }
+
+ if (violationCount > 0 && requestedResults.contains(ResultType.Violations)) {
+ getReadableAxeResults(results.getViolations(), ResultType.Violations, resultsFlex);
+ }
+
+ if (incompleteCount > 0 && requestedResults.contains(ResultType.Incomplete)) {
+ getReadableAxeResults(results.getIncomplete(), ResultType.Incomplete, resultsFlex);
+ }
+
+ if (passCount > 0 && requestedResults.contains(ResultType.Passes)) {
+ getReadableAxeResults(results.getPasses(), ResultType.Passes, resultsFlex);
+ }
+
+ if (inapplicableCount > 0 && requestedResults.contains(ResultType.Inapplicable)) {
+ getReadableAxeResults(results.getInapplicable(), ResultType.Inapplicable, resultsFlex);
+ }
+
+ Element modal = new Element("div");
+ modal.attributes().put("id", "modal");
+ contentArea.appendChild(modal);
+
+ Element modalClose = new Element("div");
+ modalClose.text("X");
+ modalClose.attributes().put("id", "modalclose");
+ modal.appendChild(modalClose);
+
+ Element modalImage = new Element("img");
+ modalImage.attributes().put("id", "modalimage");
+ modal.appendChild(modalImage);
+
+ Element script = doc.select("script").first();
+ Objects.requireNonNull(script).appendChild(new DataNode(HtmlReporter.getJavascriptFileToString()));
+
+ FileUtils.writeStringToFile(new File(destination), doc.outerHtml(), StandardCharsets.UTF_8);
+ }
+
+ /**
+ * Sets up the results into html elements for the report.
+ * @param results A list of the Rule results found
+ * @param type The result type that is being created
+ * @param body The main html page element body
+ */
+ private static void getReadableAxeResults(List results, ResultType type, Element body) {
+ Element resultWrapper = new Element("div");
+ resultWrapper.addClass("resultWrapper");
+ body.appendChild(resultWrapper);
+
+ Element sectionButton = new Element("button");
+ sectionButton.addClass("sectionbutton active");
+ resultWrapper.appendChild(sectionButton);
+
+ Element sectionButtonHeader = new Element("h2");
+ sectionButtonHeader.addClass("buttonInfoText");
+ sectionButtonHeader.text(type.name() + ": " + getCount(results));
+ sectionButton.appendChild(sectionButtonHeader);
+
+ Element sectionButtonExpando = new Element("h2");
+ sectionButtonExpando.addClass("buttonExpandoText");
+ sectionButtonExpando.text("-");
+ sectionButton.appendChild(sectionButtonExpando);
+
+ Element section = new Element("div");
+ section.addClass("majorSection");
+ section.attributes().put("id", type.name() + "Section");
+ resultWrapper.appendChild(section);
+
+ int loops = 1;
+
+ for (Rule element : results) {
+ Element childEl = new Element("div");
+ childEl.addClass("findings");
+ childEl.appendText(loops++ + ": " + element.getHelp());
+ section.appendChild(childEl);
+
+ Element content = new Element("div");
+ content.addClass("emTwo");
+ content.text("Description: " + element.getDescription());
+ content.appendChild(new Element("br"));
+ content.appendText("Help: " + element.getHelp());
+ content.appendChild(new Element("br"));
+ content.appendText("Help URL: ");
+
+ Element link = new Element("a");
+ link.attributes().put("href", element.getHelpUrl());
+ link.text(element.getHelpUrl());
+
+ content.appendChild(link);
+ content.appendChild(new Element("br"));
+
+ if (!element.getImpact().isEmpty()) {
+ content.appendText("Impact: " + element.getImpact());
+ content.appendChild(new Element("br"));
+ }
+
+ content.appendText("Tags: ").append(String.join(", ", element.getTags()));
+ content.appendChild(new Element("br"));
+
+ if (!element.getNodes().isEmpty()) {
+ content.appendText("Element(s):");
+ }
+
+ Element childEl2 = new Element("div");
+ childEl2.addClass("emTwo");
+ childEl.appendChild(content);
+
+ for (CheckedNode item : element.getNodes()) {
+ Element elementNodes = new Element("div");
+ elementNodes.addClass("htmlTable");
+ childEl.appendChild(elementNodes);
+
+ Element htmlAndSelectorWrapper = new Element("div");
+ htmlAndSelectorWrapper.addClass("emThree");
+ htmlAndSelectorWrapper.text("Html:");
+ htmlAndSelectorWrapper.appendChild(new Element("br"));
+ elementNodes.appendChild(htmlAndSelectorWrapper);
+
+ Element htmlAndSelector = new Element("p");
+ htmlAndSelector.addClass(WRAP_ONE);
+ htmlAndSelector.html(item.getHtml());
+ htmlAndSelector.text(item.getHtml());
+ htmlAndSelectorWrapper.appendChild(htmlAndSelector);
+ htmlAndSelectorWrapper.appendText("Selector:");
+
+ htmlAndSelector = new Element("p");
+ htmlAndSelector.addClass("wrapTwo");
+
+ for (Object target : Collections.singletonList(item.getTarget())) {
+ String targetString = target.toString().replace("[", "").replace("]", "");
+ htmlAndSelector.text(targetString);
+ htmlAndSelector.html(targetString);
+ }
+
+ htmlAndSelectorWrapper.appendChild(htmlAndSelector);
+ addFixes(item, type, htmlAndSelectorWrapper);
+ }
+ }
+ }
+
+ /**
+ * Add the fixes for the specified result type.
+ * @param resultsNode The fixes from the results in this specific result type
+ * @param type The result type fixes
+ * @param htmlAndSelectorWrapper The element that the fixes will be appended to
+ */
+ private static void addFixes(CheckedNode resultsNode, ResultType type, Element htmlAndSelectorWrapper) {
+ Element htmlAndSelector = new Element("div");
+
+ List anyCheckResults = resultsNode.getAny();
+ List allCheckResults = resultsNode.getAll();
+ List noneCheckResults = resultsNode.getNone();
+
+ int checkResultsCount = anyCheckResults.size() + allCheckResults.size() + noneCheckResults.size();
+
+ // Add fixes if this is for violations
+ if (ResultType.Violations.equals(type) && checkResultsCount > 0) {
+ htmlAndSelector.text("To solve:");
+ htmlAndSelectorWrapper.appendChild(htmlAndSelector);
+
+ htmlAndSelector = new Element("p");
+ htmlAndSelector.addClass("wrapTwo");
+ htmlAndSelectorWrapper.appendChild(htmlAndSelector);
+
+ if (!allCheckResults.isEmpty() || !noneCheckResults.isEmpty()) {
+ fixAllIssues(htmlAndSelectorWrapper, allCheckResults, noneCheckResults);
+ }
+
+ if (!anyCheckResults.isEmpty()) {
+ fixAnyIssues(htmlAndSelectorWrapper, anyCheckResults);
+ }
+ }
+ }
+
+ /**
+ * Adds the issues in the all category in the list of Checks.
+ * @param htmlAndSelectorWrapper The element that all the content will be appended to
+ * @param allCheckResults A list of the all check results
+ * @param noneCheckResults A list of the none check results
+ */
+ private static void fixAllIssues(Element htmlAndSelectorWrapper,
+ List allCheckResults, List noneCheckResults) {
+ Element htmlAndSelector = new Element("p");
+ htmlAndSelector.addClass(WRAP_ONE);
+ htmlAndSelector.text("Fix at least one of the following issues:");
+
+ Element htmlSet = new Element("ul");
+
+ for (var checkResult : allCheckResults) {
+ Element bulletPoints = new Element("li");
+ bulletPoints.text(checkResult.getImpact().toUpperCase() + ": " + checkResult.getMessage());
+ htmlSet.appendChild(bulletPoints);
+ }
+
+ for (var checkResult : noneCheckResults) {
+ Element bulletPoints = new Element("li");
+ bulletPoints.text(checkResult.getImpact().toUpperCase() + ": " + checkResult.getMessage());
+ htmlSet.appendChild(bulletPoints);
+ }
+
+ htmlAndSelector.appendChild(htmlSet);
+ htmlAndSelectorWrapper.appendChild(htmlAndSelector);
+ }
+
+ /**
+ * Adds the issues in the Any category in the list of Checks.
+ * @param htmlAndSelectorWrapper The element that all the content will be appended to
+ * @param anyCheckResults A list of the Any check results
+ */
+ private static void fixAnyIssues(Element htmlAndSelectorWrapper, List anyCheckResults) {
+ Element htmlAndSelector = new Element("p");
+ htmlAndSelector.addClass(WRAP_ONE);
+ htmlAndSelector.text("Fix at least one of the following issues:");
+
+ Element htmlSet = new Element("ul");
+
+ for (var checkResult : anyCheckResults) {
+ Element bulletPoints = new Element("li");
+ bulletPoints.text(checkResult.getImpact().toUpperCase() + ": " + checkResult.getMessage());
+ htmlSet.appendChild(bulletPoints);
+ }
+
+ htmlAndSelector.appendChild(htmlSet);
+ htmlAndSelectorWrapper.appendChild(htmlAndSelector);
+ }
+
+ /**
+ * Sets up the Context content and adds it to the element.
+ * @param results the results to be used for the report
+ * @param element the element that the content will be added to
+ * @throws ParseException if an exception is thrown
+ */
+ private static void getContextContent(AxeResults results, Element element) throws ParseException {
+ element.text("Url: " + results.getUrl());
+ element.appendChild(new Element("br"));
+ element.appendText("Orientation: " + results.getTestEnvironment().getOrientationType());
+ element.appendChild(new Element("br"));
+ element.appendText("Size: " + results.getTestEnvironment().getwindowWidth() + " x "
+ + results.getTestEnvironment().getWindowHeight());
+ element.appendChild(new Element("br"));
+ element.appendText("Time: " + HtmlReporter.getDateFormat(results.getTimestamp()));
+ element.appendChild(new Element("br"));
+ element.appendText("User agent: " + results.getTestEnvironment().getUserAgent());
+ element.appendChild(new Element("br"));
+ element.appendText("Using: " + results.getTestEngine().getName() + " ("
+ + results.getTestEngine().getVersion() + ")");
+ }
+
+ /**
+ * Gets the count of the number of rules that came up in the scan.
+ * @param results The list of rules to be looped through
+ * @return The count of all the rules
+ */
+ private static int getCount(List results) {
+ int count = 0;
+ for (Rule item : results) {
+ count += item.getNodes().size();
+
+ // Still add one if no targets are included
+ if (item.getNodes().isEmpty()) {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ /**
+ * Sets up the count content for the html report.
+ * @param violationCount The count for the violations in the scan
+ * @param incompleteCount The count for incomplete in the scan
+ * @param passCount The count for passes in the scan
+ * @param inapplicableCount The count for inapplicable in the scan
+ * @param requestedResults The result types that will be included on the html report
+ * @param element The element that all the content will be appended to
+ */
+ private static void getCountContent(int violationCount, int incompleteCount, int passCount,
+ int inapplicableCount, Set requestedResults, Element element) {
+ if (requestedResults.contains(ResultType.Violations)) {
+ element.text(" Violations: " + violationCount);
+ element.appendChild(new Element("br"));
+ }
+
+ if (requestedResults.contains(ResultType.Incomplete)) {
+ element.appendText(" Incomplete: " + incompleteCount);
+ element.appendChild(new Element("br"));
+ }
+
+ if (requestedResults.contains(ResultType.Passes)) {
+ element.appendText(" Passes: " + passCount);
+ element.appendChild(new Element("br"));
+ }
+
+ if (requestedResults.contains(ResultType.Inapplicable)) {
+ element.appendText(" Inapplicable: " + inapplicableCount);
+ }
+ }
+
+ /**
+ * Gets the CSS file into a string format.
+ * @return the CSS file script as a string
+ * @throws IOException if an exception is thrown
+ */
+ private static String getCss(Page context) throws IOException {
+ String css = String.valueOf(Files.readAllLines(
+ Paths.get(RESOURCES_FILE + "htmlReporter.css")));
+ return css.replace("url('", "url('" + getDataImageString(context));
+ }
+
+ /**
+ * Gets the data image as a base 64 string.
+ * @param context The web driver or element to take a screenshot of
+ * @return the base 64 data image as a string
+ */
+ private static String getDataImageString(Page context) {
+ //TakesScreenshot newScreen = (TakesScreenshot) context;
+ //return "data:image/png;base64," + newScreen.getScreenshotAs(OutputType.BASE64);
+ byte[] bytes = context.screenshot();
+ return "data:image/png;base64," + Base64.getEncoder().encodeToString(bytes);
+ }
+}
\ No newline at end of file
diff --git a/maqs-accessibility/src/main/java/com/cognizantsoftvision/maqs/accessibility/SeleniumReporter.java b/maqs-accessibility/src/main/java/com/cognizantsoftvision/maqs/accessibility/SeleniumReporter.java
new file mode 100644
index 000000000..129be71ab
--- /dev/null
+++ b/maqs-accessibility/src/main/java/com/cognizantsoftvision/maqs/accessibility/SeleniumReporter.java
@@ -0,0 +1,546 @@
+/*
+ * Copyright 2022 (C) Cognizant SoftVision, All rights Reserved
+ */
+
+package com.cognizantsoftvision.maqs.accessibility;
+
+import com.deque.html.axecore.results.Check;
+import com.deque.html.axecore.results.CheckedNode;
+import com.deque.html.axecore.results.Results;
+import com.deque.html.axecore.results.Rule;
+import com.deque.html.axecore.selenium.AxeBuilder;
+import com.deque.html.axecore.selenium.ResultType;
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.text.ParseException;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import org.apache.commons.io.FileUtils;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.DataNode;
+import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Element;
+import org.openqa.selenium.OutputType;
+import org.openqa.selenium.SearchContext;
+import org.openqa.selenium.TakesScreenshot;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.WrapsElement;
+
+/**
+ * The HTML reporter class.
+ */
+public class SeleniumReporter extends HtmlReporter {
+
+ /**
+ * Placeholder for wrap one tag string type.
+ */
+ private static final String WRAP_ONE = "wrapOne";
+
+ /**
+ * File path to resources java resources folder.
+ */
+ private static final String RESOURCES_FILE = "../maqs-accessibility/src/main/resources/";
+
+ /**
+ * Class constructor.
+ */
+ protected SeleniumReporter() {
+ }
+
+ /**
+ * Create an HTML accessibility report for an entire web page.
+ * @param webDriver the web driver used in the scan
+ * @param destination the destination file the html report will go to
+ * @throws IOException if an IO exception is thrown
+ * @throws ParseException if a parse exception is thrown
+ */
+ public static void createAxeHtmlReport(WebDriver webDriver, String destination)
+ throws IOException, ParseException {
+ createAxeHtmlReport(webDriver, destination, EnumSet.allOf(ResultType.class));
+ }
+
+ /**
+ * Create an HTML accessibility report for an entire web page with specific Result types.
+ * @param webDriver the web driver used in the scan
+ * @param destination the destination file the html report will go to
+ * @param requestedResults the specified result types to include in the report
+ * @throws IOException if an IO exception is thrown
+ * @throws ParseException if a parse exception is thrown
+ */
+ public static void createAxeHtmlReport(WebDriver webDriver, String destination, Set requestedResults)
+ throws IOException, ParseException {
+ createAxeHtmlReport(webDriver, new AxeBuilder().analyze(webDriver), destination, requestedResults);
+ }
+
+ /**
+ * Create an HTML accessibility report for a specific element.
+ * @param webDriver the web driver used in the scan
+ * @param element the element to be scanned
+ * @param destination the destination file the html report will go to
+ * @throws IOException if an IO exception is thrown
+ * @throws ParseException if a parse exception is thrown
+ */
+ public static void createAxeHtmlReport(WebDriver webDriver, WebElement element, String destination)
+ throws IOException, ParseException {
+ createAxeHtmlReport(webDriver, element, destination, EnumSet.allOf(ResultType.class));
+ }
+
+ /**
+ * Create an HTML accessibility report for a specific element and specified result types.
+ * @param webDriver the web driver used in the scan
+ * @param element the element to be scanned
+ * @param destination the destination file the html report will go to
+ * @param requestedResults the specified result types to include in the report
+ * @throws IOException if an IO exception is thrown
+ * @throws ParseException if a parse exception is thrown
+ */
+ public static void createAxeHtmlReport(WebDriver webDriver, WebElement element, String destination,
+ Set requestedResults) throws IOException, ParseException {
+ createAxeHtmlReport(webDriver, new AxeBuilder().analyze(webDriver, element), destination, requestedResults);
+ }
+
+ /**
+ * Create an HTML accessibility report for an entire web page with already scanned results.
+ * @param webDriver the web driver used in the scan
+ * @param results the results type variable used after scanning the web page
+ * @param destination the destination file the html report will go to
+ * @throws IOException if an IO exception is thrown
+ * @throws ParseException if a parse exception is thrown
+ */
+ public static void createAxeHtmlReport(WebDriver webDriver, Results results, String destination)
+ throws IOException, ParseException {
+ createAxeHtmlReport(webDriver, results, destination, EnumSet.allOf(ResultType.class));
+ }
+
+ /**
+ * Create an HTML accessibility report for an entire web page with specified Result types
+ * and inputted already scanned results.
+ * @param webDriver the web driver used in the scan
+ * @param results the results object created after scanning the web page
+ * @param destination the destination file the html report will go to
+ * @param requestedResults the specified result types to include in the report
+ * @throws IOException if an IO exception is thrown
+ * @throws ParseException if a parse exception is thrown
+ */
+ public static void createAxeHtmlReport(WebDriver webDriver, Results results, String destination,
+ Set requestedResults) throws IOException, ParseException {
+ createAxeHtmlReportFile(webDriver, results, destination, requestedResults);
+ }
+
+ /**
+ * Creates an HTML accessibility report.
+ * @param context the Search Context to be used in the scan
+ * @param results the results object created after scanning the web page
+ * @param destination the destination file the html report will go to
+ * @param requestedResults the specified result types to include in the report
+ * @throws IOException if an IO exception is thrown
+ * @throws ParseException if a parse exception is thrown
+ */
+ private static void createAxeHtmlReportFile(SearchContext context, Results results, String destination,
+ Set requestedResults) throws IOException, ParseException {
+ // Get the unwrapped element if we are using a wrapped element
+ context = (context instanceof WrapsElement)
+ ? ((WrapsElement) context).getWrappedElement() : context;
+
+ final int violationCount = getCount(results.getViolations());
+ final int incompleteCount = getCount(results.getIncomplete());
+ final int passCount = getCount(results.getPasses());
+ final int inapplicableCount = getCount(results.getInapplicable());
+
+ String stringBuilder = String.valueOf(Files.readString(Paths.get(RESOURCES_FILE + "htmlReporterTags.html")));
+ stringBuilder = stringBuilder.replace(System.lineSeparator(), "");
+
+ Document doc = Jsoup.parse(stringBuilder);
+
+ doc.select("style").append(getCss(context));
+
+ Element contentArea = doc.select("content").first();
+
+ Element reportTitle = new Element("h1");
+ reportTitle.text("Accessibility Check");
+ Objects.requireNonNull(contentArea).appendChild(reportTitle);
+
+ Element metaFlex = new Element("div");
+ metaFlex.attributes().put("id", "metadata");
+ contentArea.appendChild(metaFlex);
+
+ Element contextGroup = new Element("div");
+ contextGroup.attributes().put("id", "context");
+ metaFlex.appendChild(contextGroup);
+
+ Element contextHeader = new Element("h3");
+ contextHeader.text("Context:");
+ contextGroup.appendChild(contextHeader);
+
+ Element contextContent = new Element("div");
+ contextContent.addClass("emOne");
+ contextContent.attributes().put("id", "reportContext");
+ getContextContent(results, contextContent);
+ contextGroup.appendChild(contextContent);
+
+ Element imgGroup = new Element("div");
+ imgGroup.attributes().put("id", "image");
+ metaFlex.appendChild(imgGroup);
+
+ Element imageHeader = new Element("h3");
+ imageHeader.appendText("Image:");
+ imgGroup.appendChild(imageHeader);
+
+ Element imageContent = new Element("img");
+ imageContent.addClass("thumbnail");
+ imageContent.attributes().put("id", "screenshotThumbnail");
+ imageContent.attributes().put("alt", "A Screenshot of the page");
+ imageContent.attributes().put("width", "33%");
+ imageContent.attributes().put("height", "auto");
+ imgGroup.appendChild(imageContent);
+
+ Element countsGroup = new Element("div");
+ countsGroup.attributes().put("id", "counts");
+ metaFlex.appendChild(countsGroup);
+
+ Element countsHeader = new Element("h3");
+ countsHeader.appendText("Counts:");
+ countsGroup.appendChild(countsHeader);
+
+ Element countsContent = new Element("div");
+ countsContent.addClass("emOne");
+ getCountContent(violationCount, incompleteCount, passCount, inapplicableCount, requestedResults, countsContent);
+ countsGroup.appendChild(countsContent);
+
+ Element resultsFlex = new Element("div");
+ resultsFlex.attributes().put("id", "results");
+ contentArea.appendChild(resultsFlex);
+
+ if (results.isErrored()) {
+ Element errorHeader = new Element("h2");
+ errorHeader.appendText("SCAN ERRORS:");
+ contentArea.appendChild(errorHeader);
+
+ Element errorContent = new Element("div");
+ errorContent.attributes().put("id", "ErrorMessage");
+ errorContent.appendText(results.getErrorMessage());
+ contentArea.appendChild(errorContent);
+ }
+
+ if (violationCount > 0 && requestedResults.contains(ResultType.Violations)) {
+ getReadableAxeResults(results.getViolations(), ResultType.Violations, resultsFlex);
+ }
+
+ if (incompleteCount > 0 && requestedResults.contains(ResultType.Incomplete)) {
+ getReadableAxeResults(results.getIncomplete(), ResultType.Incomplete, resultsFlex);
+ }
+
+ if (passCount > 0 && requestedResults.contains(ResultType.Passes)) {
+ getReadableAxeResults(results.getPasses(), ResultType.Passes, resultsFlex);
+ }
+
+ if (inapplicableCount > 0 && requestedResults.contains(ResultType.Inapplicable)) {
+ getReadableAxeResults(results.getInapplicable(), ResultType.Inapplicable, resultsFlex);
+ }
+
+ Element modal = new Element("div");
+ modal.attributes().put("id", "modal");
+ contentArea.appendChild(modal);
+
+ Element modalClose = new Element("div");
+ modalClose.text("X");
+ modalClose.attributes().put("id", "modalclose");
+ modal.appendChild(modalClose);
+
+ Element modalImage = new Element("img");
+ modalImage.attributes().put("id", "modalimage");
+ modal.appendChild(modalImage);
+
+ Element script = doc.select("script").first();
+ Objects.requireNonNull(script).appendChild(new DataNode(HtmlReporter.getJavascriptFileToString()));
+
+ FileUtils.writeStringToFile(new File(destination), doc.outerHtml(), StandardCharsets.UTF_8);
+ }
+
+ /**
+ * Sets up the results into html elements for the report.
+ * @param results A list of the Rule results found
+ * @param type The result type that is being created
+ * @param body The main html page element body
+ */
+ private static void getReadableAxeResults(List results, ResultType type, Element body) {
+ Element resultWrapper = new Element("div");
+ resultWrapper.addClass("resultWrapper");
+ body.appendChild(resultWrapper);
+
+ Element sectionButton = new Element("button");
+ sectionButton.addClass("sectionbutton active");
+ resultWrapper.appendChild(sectionButton);
+
+ Element sectionButtonHeader = new Element("h2");
+ sectionButtonHeader.addClass("buttonInfoText");
+ sectionButtonHeader.text(type.name() + ": " + getCount(results));
+ sectionButton.appendChild(sectionButtonHeader);
+
+ Element sectionButtonExpando = new Element("h2");
+ sectionButtonExpando.addClass("buttonExpandoText");
+ sectionButtonExpando.text("-");
+ sectionButton.appendChild(sectionButtonExpando);
+
+ Element section = new Element("div");
+ section.addClass("majorSection");
+ section.attributes().put("id", type.name() + "Section");
+ resultWrapper.appendChild(section);
+
+ int loops = 1;
+
+ for (Rule element : results) {
+ Element childEl = new Element("div");
+ childEl.addClass("findings");
+ childEl.appendText(loops++ + ": " + element.getHelp());
+ section.appendChild(childEl);
+
+ Element content = new Element("div");
+ content.addClass("emTwo");
+ content.text("Description: " + element.getDescription());
+ content.appendChild(new Element("br"));
+ content.appendText("Help: " + element.getHelp());
+ content.appendChild(new Element("br"));
+ content.appendText("Help URL: ");
+
+ Element link = new Element("a");
+ link.attributes().put("href", element.getHelpUrl());
+ link.text(element.getHelpUrl());
+
+ content.appendChild(link);
+ content.appendChild(new Element("br"));
+
+ if (!element.getImpact().isEmpty()) {
+ content.appendText("Impact: " + element.getImpact());
+ content.appendChild(new Element("br"));
+ }
+
+ content.appendText("Tags: ").append(String.join(", ", element.getTags()));
+ content.appendChild(new Element("br"));
+
+ if (!element.getNodes().isEmpty()) {
+ content.appendText("Element(s):");
+ }
+
+ Element childEl2 = new Element("div");
+ childEl2.addClass("emTwo");
+ childEl.appendChild(content);
+
+ for (CheckedNode item : element.getNodes()) {
+ Element elementNodes = new Element("div");
+ elementNodes.addClass("htmlTable");
+ childEl.appendChild(elementNodes);
+
+ Element htmlAndSelectorWrapper = new Element("div");
+ htmlAndSelectorWrapper.addClass("emThree");
+ htmlAndSelectorWrapper.text("Html:");
+ htmlAndSelectorWrapper.appendChild(new Element("br"));
+ elementNodes.appendChild(htmlAndSelectorWrapper);
+
+ Element htmlAndSelector = new Element("p");
+ htmlAndSelector.addClass(WRAP_ONE);
+ htmlAndSelector.html(item.getHtml());
+ htmlAndSelector.text(item.getHtml());
+ htmlAndSelectorWrapper.appendChild(htmlAndSelector);
+ htmlAndSelectorWrapper.appendText("Selector:");
+
+ htmlAndSelector = new Element("p");
+ htmlAndSelector.addClass("wrapTwo");
+
+ for (Object target : Collections.singletonList(item.getTarget())) {
+ String targetString = target.toString().replace("[", "").replace("]", "");
+ htmlAndSelector.text(targetString);
+ htmlAndSelector.html(targetString);
+ }
+
+ htmlAndSelectorWrapper.appendChild(htmlAndSelector);
+ addFixes(item, type, htmlAndSelectorWrapper);
+ }
+ }
+ }
+
+ /**
+ * Add the fixes for the specified result type.
+ * @param resultsNode The fixes from the results in this specific result type
+ * @param type The result type fixes
+ * @param htmlAndSelectorWrapper The element that the fixes will be appended to
+ */
+ private static void addFixes(CheckedNode resultsNode, ResultType type, Element htmlAndSelectorWrapper) {
+ Element htmlAndSelector = new Element("div");
+
+ List anyCheckResults = resultsNode.getAny();
+ List allCheckResults = resultsNode.getAll();
+ List noneCheckResults = resultsNode.getNone();
+
+ int checkResultsCount = anyCheckResults.size() + allCheckResults.size() + noneCheckResults.size();
+
+ // Add fixes if this is for violations
+ if (ResultType.Violations.equals(type) && checkResultsCount > 0) {
+ htmlAndSelector.text("To solve:");
+ htmlAndSelectorWrapper.appendChild(htmlAndSelector);
+
+ htmlAndSelector = new Element("p");
+ htmlAndSelector.addClass("wrapTwo");
+ htmlAndSelectorWrapper.appendChild(htmlAndSelector);
+
+ if (!allCheckResults.isEmpty() || !noneCheckResults.isEmpty()) {
+ fixAllIssues(htmlAndSelectorWrapper, allCheckResults, noneCheckResults);
+ }
+
+ if (!anyCheckResults.isEmpty()) {
+ fixAnyIssues(htmlAndSelectorWrapper, anyCheckResults);
+ }
+ }
+ }
+
+ /**
+ * Adds the issues in the all category in the list of Checks.
+ * @param htmlAndSelectorWrapper The element that all the content will be appended to
+ * @param allCheckResults A list of the all check results
+ * @param noneCheckResults A list of the none check results
+ */
+ private static void fixAllIssues(Element htmlAndSelectorWrapper,
+ List allCheckResults, List noneCheckResults) {
+ Element htmlAndSelector = new Element("p");
+ htmlAndSelector.addClass(WRAP_ONE);
+ htmlAndSelector.text("Fix at least one of the following issues:");
+
+ Element htmlSet = new Element("ul");
+
+ for (var checkResult : allCheckResults) {
+ Element bulletPoints = new Element("li");
+ bulletPoints.text(checkResult.getImpact().toUpperCase() + ": " + checkResult.getMessage());
+ htmlSet.appendChild(bulletPoints);
+ }
+
+ for (var checkResult : noneCheckResults) {
+ Element bulletPoints = new Element("li");
+ bulletPoints.text(checkResult.getImpact().toUpperCase() + ": " + checkResult.getMessage());
+ htmlSet.appendChild(bulletPoints);
+ }
+
+ htmlAndSelector.appendChild(htmlSet);
+ htmlAndSelectorWrapper.appendChild(htmlAndSelector);
+ }
+
+ /**
+ * Adds the issues in the Any category in the list of Checks.
+ * @param htmlAndSelectorWrapper The element that all the content will be appended to
+ * @param anyCheckResults A list of the Any check results
+ */
+ private static void fixAnyIssues(Element htmlAndSelectorWrapper, List anyCheckResults) {
+ Element htmlAndSelector = new Element("p");
+ htmlAndSelector.addClass(WRAP_ONE);
+ htmlAndSelector.text("Fix at least one of the following issues:");
+
+ Element htmlSet = new Element("ul");
+
+ for (var checkResult : anyCheckResults) {
+ Element bulletPoints = new Element("li");
+ bulletPoints.text(checkResult.getImpact().toUpperCase() + ": " + checkResult.getMessage());
+ htmlSet.appendChild(bulletPoints);
+ }
+
+ htmlAndSelector.appendChild(htmlSet);
+ htmlAndSelectorWrapper.appendChild(htmlAndSelector);
+ }
+
+ /**
+ * Sets up the Context content and adds it to the element.
+ * @param results the results to be used for the report
+ * @param element the element that the content will be added to
+ * @throws ParseException if an exception is thrown
+ */
+ private static void getContextContent(Results results, Element element) throws ParseException {
+ element.text("Url: " + results.getUrl());
+ element.appendChild(new Element("br"));
+ element.appendText("Orientation: " + results.getTestEnvironment().getOrientationType());
+ element.appendChild(new Element("br"));
+ element.appendText("Size: " + results.getTestEnvironment().getwindowWidth() + " x "
+ + results.getTestEnvironment().getWindowHeight());
+ element.appendChild(new Element("br"));
+ element.appendText("Time: " + HtmlReporter.getDateFormat(results.getTimestamp()));
+ element.appendChild(new Element("br"));
+ element.appendText("User agent: " + results.getTestEnvironment().getUserAgent());
+ element.appendChild(new Element("br"));
+ element.appendText("Using: " + results.getTestEngine().getName() + " ("
+ + results.getTestEngine().getVersion() + ")");
+ }
+
+ /**
+ * Gets the count of the number of rules that came up in the scan.
+ * @param results The list of rules to be looped through
+ * @return The count of all the rules
+ */
+ private static int getCount(List results) {
+ int count = 0;
+ for (Rule item : results) {
+ count += item.getNodes().size();
+
+ // Still add one if no targets are included
+ if (item.getNodes().isEmpty()) {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ /**
+ * Sets up the count content for the html report.
+ * @param violationCount The count for the violations in the scan
+ * @param incompleteCount The count for incomplete in the scan
+ * @param passCount The count for passes in the scan
+ * @param inapplicableCount The count for inapplicable in the scan
+ * @param requestedResults The result types that will be included on the html report
+ * @param element The element that all the content will be appended to
+ */
+ private static void getCountContent(int violationCount, int incompleteCount, int passCount,
+ int inapplicableCount, Set requestedResults, Element element) {
+ if (requestedResults.contains(ResultType.Violations)) {
+ element.text(" Violations: " + violationCount);
+ element.appendChild(new Element("br"));
+ }
+
+ if (requestedResults.contains(ResultType.Incomplete)) {
+ element.appendText(" Incomplete: " + incompleteCount);
+ element.appendChild(new Element("br"));
+ }
+
+ if (requestedResults.contains(ResultType.Passes)) {
+ element.appendText(" Passes: " + passCount);
+ element.appendChild(new Element("br"));
+ }
+
+ if (requestedResults.contains(ResultType.Inapplicable)) {
+ element.appendText(" Inapplicable: " + inapplicableCount);
+ }
+ }
+
+ /**
+ * Gets the CSS file into a string format.
+ * @return the CSS file script as a string
+ * @throws IOException if an exception is thrown
+ */
+ private static String getCss(SearchContext context) throws IOException {
+ String css = String.valueOf(Files.readAllLines(
+ Paths.get(RESOURCES_FILE + "htmlReporter.css")));
+ return css.replace("url('", "url('" + getDataImageString(context));
+ }
+
+ /**
+ * Gets the data image as a base 64 string.
+ * @param context The web driver or element to take a screenshot of
+ * @return the base 64 data image as a string
+ */
+ private static String getDataImageString(SearchContext context) {
+ TakesScreenshot newScreen = (TakesScreenshot) context;
+ return "data:image/png;base64," + newScreen.getScreenshotAs(OutputType.BASE64);
+ }
+}
\ No newline at end of file
diff --git a/maqs-accessibility/src/test/java/com/cognizantsoftvision/maqs/accessibility/htmlReporter/PlaywrightUnitTest.java b/maqs-accessibility/src/test/java/com/cognizantsoftvision/maqs/accessibility/htmlReporter/PlaywrightUnitTest.java
new file mode 100644
index 000000000..2ff40a602
--- /dev/null
+++ b/maqs-accessibility/src/test/java/com/cognizantsoftvision/maqs/accessibility/htmlReporter/PlaywrightUnitTest.java
@@ -0,0 +1,343 @@
+package com.cognizantsoftvision.maqs.accessibility.htmlReporter;
+
+import com.cognizantsoftvision.maqs.accessibility.PlaywrightReporter;
+import com.cognizantsoftvision.maqs.playwright.BasePlaywrightTest;
+import com.cognizantsoftvision.maqs.utilities.helper.TestCategories;
+import com.deque.html.axecore.playwright.AxeBuilder;
+import com.deque.html.axecore.selenium.ResultType;
+import com.deque.html.axecore.utilities.axeresults.AxeResults;
+import com.deque.html.axecore.utilities.axeresults.Check;
+import com.deque.html.axecore.utilities.axeresults.CheckedNode;
+import com.deque.html.axecore.utilities.axeresults.Rule;
+import com.deque.html.axecore.utilities.axerunoptions.AxeRunOptions;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.microsoft.playwright.options.LoadState;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.text.ParseException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Objects;
+import java.util.UUID;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.jsoup.select.Elements;
+import org.testng.Assert;
+import org.testng.annotations.Ignore;
+import org.testng.annotations.Test;
+
+/**
+ * Accessibility HTML Playwright Reporter unit tests.
+ */
+public class PlaywrightUnitTest extends BasePlaywrightTest {
+
+ /**
+ * The file to be opened in the browser.
+ */
+ private static final File integrationTestTargetSimpleFile = new File(
+ "src/test/resources/testFiles/integration-test-target.html");
+
+ /**
+ * The url to be opened in the browser.
+ */
+ private static final String integrationTestTargetSimpleUrl = integrationTestTargetSimpleFile.getAbsolutePath();
+
+ /**
+ * The file to be opened in the browser.
+ */
+ private static final File integrationTestTargetComplexFile = new File(
+ "src/test/resources/testFiles/integration-test-target-complex.html");
+
+ /**
+ * The url to be opened in the browser.
+ */
+ private static final String integrationTestTargetComplexUrl = integrationTestTargetComplexFile.getAbsolutePath();
+
+ /**
+ * The file to be converted into a result type.
+ */
+ private static final File integrationTestJsonResultFile = new File(
+ "src/test/resources/testFiles/sampleResults.json");
+
+ /**
+ * The path to the file converted into a result type.
+ */
+ private static final String integrationTestJsonResultUrl = integrationTestJsonResultFile.getAbsolutePath();
+
+ /**
+ * String value of main element selector.
+ */
+ private static final String mainElementSelector = "main";
+
+ private void loadTestPage(String testPage) {
+ this.getPage().getAsyncPage().navigate("file:///" + new File(testPage).getAbsolutePath());
+ this.getPage().getAsyncPage().waitForLoadState(LoadState.DOMCONTENTLOADED);
+ this.getPage().getAsyncPage().isVisible(mainElementSelector);
+ }
+
+ @Test(groups = TestCategories.ACCESSIBILITY)
+ public void runScanOnPage() {
+ loadTestPage(integrationTestTargetSimpleUrl);
+
+ //var timeBeforeScan = DateTime.Now;
+
+ AxeRunOptions axeRunOptions = new AxeRunOptions();
+ axeRunOptions.setXPath(true);
+
+ AxeBuilder builder = new AxeBuilder(this.getPage().getAsyncPage());
+ builder.options(axeRunOptions);
+ builder.withTags(Arrays.asList("wcag2a", "wcag2aa"));
+ builder.disableRules(Collections.singletonList("color-contrast"));
+ // builder.withOutputFile("./raw-axe-results.json");
+
+ AxeResults results = builder.analyze();
+
+ Assert.assertEquals(results.getViolations().size(), 2);
+
+ for (Rule violations : results.getViolations()) {
+ Assert.assertFalse(violations.getId().contains("color-contrast"));
+
+ // results.Violations.FirstOrDefault(v => !v.Tags.Contains("wcag2a") && !v.Tags.Contains("wcag2aa")).Should().BeNull();
+ Assert.assertTrue(violations.getTags().contains("wcag2a"));
+ // Assert.assertTrue(violations.getTags().contains("wcag2aa"));
+ }
+
+ Assert.assertNotNull(results.getViolations().get(0).getNodes().get(0));
+ }
+
+ @Ignore
+ @Test(groups = TestCategories.ACCESSIBILITY)
+ public void runScanOnGivenElement()
+ throws IOException, ParseException {
+ loadTestPage(integrationTestTargetSimpleUrl);
+ String path = createReportPath();
+ // HtmlPlaywrightReporter.createAxeHtmlReport(this.getPage().getAsyncPage(),
+ // this.getPageDriver().waitForSelector(mainElementSelector), path);
+ validateReport(path, 3, 14, 0, 75);
+
+ deleteFile(new File(path));
+ }
+
+ @Test(groups = TestCategories.ACCESSIBILITY)
+ public void reportFullPage() throws IOException, ParseException {
+ loadTestPage(integrationTestTargetSimpleUrl);
+ String path = createReportPath();
+ PlaywrightReporter.createAxeHtmlReport(this.getPageDriver().getAsyncPage(), path);
+ validateReport(path, 4, 26, 0, 69);
+
+ deleteFile(new File(path));
+ }
+
+ @Test(groups = TestCategories.ACCESSIBILITY)
+ public void reportFullPageViolationsOnly()
+ throws IOException, ParseException {
+ loadTestPage(integrationTestTargetSimpleUrl);
+ String path = createReportPath();
+ PlaywrightReporter.createAxeHtmlReport(this.getPageDriver().getAsyncPage(), path, EnumSet.of(
+ ResultType.Violations));
+
+ // Check violations
+ validateReport(path, 4, 0, 0, 0);
+ validateResultNotWritten(path,
+ EnumSet.of(ResultType.Passes, ResultType.Inapplicable, ResultType.Incomplete));
+
+ deleteFile(new File(path));
+ }
+
+ @Test(groups = TestCategories.ACCESSIBILITY)
+ public void reportFullPagePassesInapplicableViolationsOnly()
+ throws IOException, ParseException {
+ loadTestPage(integrationTestTargetSimpleUrl);
+ String path = createReportPath();
+ PlaywrightReporter.createAxeHtmlReport(this.getPageDriver().getAsyncPage(), path,
+ EnumSet.of(ResultType.Passes, ResultType.Inapplicable, ResultType.Violations));
+
+ // Check Passes
+ validateReport(path, 4, 26, 0, 69);
+ validateResultNotWritten(path, EnumSet.of(ResultType.Incomplete));
+
+ deleteFile(new File(path));
+ }
+
+ @Test(groups = TestCategories.ACCESSIBILITY)
+ @Ignore
+ public void reportOnElement() throws IOException, ParseException {
+ loadTestPage(integrationTestTargetSimpleUrl);
+ String path = createReportPath();
+
+ var mainElement = this.getPageDriver().getAsyncPage().waitForSelector(mainElementSelector);
+ // HtmlPlaywrightReporter.createAxeHtmlReport(this.getPageDriver().getAsyncPage(), mainElement, path);
+
+ validateReport(path, 3, 14, 0, 75);
+ deleteFile(new File(path));
+ }
+
+ @Test(groups = TestCategories.ACCESSIBILITY)
+ public void reportRespectRules() throws IOException, ParseException {
+ loadTestPage(integrationTestTargetSimpleUrl);
+ String path = createReportPath();
+
+ var builder = new AxeBuilder(this.getPageDriver().getAsyncPage()).disableRules(Collections.singletonList("color-contrast"));
+ PlaywrightReporter.createAxeHtmlReport(this.getPageDriver().getAsyncPage(), builder.analyze(), path);
+
+ validateReport(path, 3, 21, 0, 69);
+ deleteFile(new File(path));
+ }
+
+ @Test(groups = TestCategories.ACCESSIBILITY)
+ public void reportSampleResults() throws IOException, ParseException {
+ String path = createReportPath();
+ AxeResults results = new ObjectMapper().readValue(new File(integrationTestJsonResultUrl), AxeResults.class);
+
+ PlaywrightReporter.createAxeHtmlReport(this.getPageDriver().getAsyncPage(), results, path);
+ validateReport(path, 3, 5, 2, 4);
+
+ String text = new String(Files.readAllBytes(Paths.get(path)));
+ Document doc = Jsoup.parse(text);
+
+ String errorMessage = Objects.requireNonNull(doc.selectFirst("#ErrorMessage")).text();
+ Assert.assertEquals(errorMessage, "java.lang.Exception: AutomationError");
+
+ String reportContext = Objects.requireNonNull(doc.selectFirst("#reportContext")).text();
+ Assert.assertTrue(reportContext.contains("Url: https://www.google.com/"), "URL is not in the document");
+ Assert.assertTrue(reportContext.contains("Orientation: landscape-primary"), "Orientation is not in the document");
+ Assert.assertTrue(reportContext.contains("Size: 1200 x 646"), "Size is not in the document");
+ Assert.assertTrue(reportContext.contains("Time: 14-Apr-20 01:33:59"), "Time is not in the document: " + reportContext);
+ Assert.assertTrue(reportContext.contains("User agent: AutoAgent"), "User Agent is not in the document");
+ Assert.assertTrue(reportContext.contains("Using: axe-core (3.4.1)"), "Using is not in the document");
+
+ deleteFile(new File(path));
+ }
+
+ @Test(groups = TestCategories.ACCESSIBILITY)
+ public void reportRespectsIframeImplicitTrue() throws IOException, ParseException {
+ loadTestPage(integrationTestTargetComplexUrl);
+ String path = createReportPath();
+
+ PlaywrightReporter.createAxeHtmlReport(this.getPageDriver().getAsyncPage(), path);
+ validateReport(path, 4, 43, 0, 64);
+
+ deleteFile(new File(path));
+ }
+
+ @Test(groups = TestCategories.ACCESSIBILITY)
+ public void ReportRespectsIframeTrue() throws IOException, ParseException {
+ loadTestPage(integrationTestTargetComplexUrl);
+ String path = createReportPath();
+
+ AxeRunOptions runOptions = new AxeRunOptions();
+ runOptions.setIFrames(true);
+
+ var builder = new AxeBuilder(getPageDriver().getAsyncPage()).options(runOptions);
+
+ PlaywrightReporter.createAxeHtmlReport(this.getPageDriver().getAsyncPage(), builder.analyze(), path);
+ validateReport(path, 4, 43, 0, 64);
+
+ deleteFile(new File(path));
+ }
+
+ @Test(groups = TestCategories.ACCESSIBILITY)
+ public void reportRespectsIframeFalse() throws IOException, ParseException {
+ loadTestPage(integrationTestTargetComplexUrl);
+ String path = createReportPath();
+
+ AxeRunOptions runOptions = new AxeRunOptions();
+ runOptions.setIFrames(false);
+
+ var builder = new AxeBuilder(getPageDriver().getAsyncPage()).options(runOptions);
+ PlaywrightReporter.createAxeHtmlReport(getPageDriver().getAsyncPage(), builder.analyze(), path);
+ validateReport(path, 4, 43, 0, 64);
+
+ deleteFile(new File(path));
+ }
+
+ @Test(groups = TestCategories.ACCESSIBILITY)
+ public void runSiteThatReturnsMultipleTargets() {
+ loadTestPage(integrationTestTargetComplexUrl);
+
+ AxeResults axeResult = new AxeBuilder(getPageDriver().getAsyncPage()).analyze();
+ // .withOutputFile("./raw-axe-results.json").analyze();
+
+ Rule colorContrast = null;
+
+ for (Rule rule : axeResult.getViolations()) {
+ if (rule.getId().equals("color-contrast")) {
+ colorContrast = rule;
+ break;
+ }
+ }
+
+ Assert.assertNotNull(colorContrast);
+
+ for (CheckedNode checkedNode : colorContrast.getNodes()) {
+ for (Check check : checkedNode.getAny()) {
+ if (check.getId().equals("color-contrast")) {
+ Assert.assertNotNull(checkedNode.getAny());
+ Assert.assertEquals(checkedNode.getAny().size(), 1);
+ break;
+ }
+ }
+ }
+ }
+
+ private String createReportPath() {
+ return FileSystems.getDefault().getPath("target" + File.separator + "logs")
+ + File.separator + UUID.randomUUID() + ".html";
+ }
+
+ private void validateReport(String path, int violationCount, int passCount, int incompleteCount, int inapplicableCount)
+ throws IOException {
+ String text = String.valueOf(Files.readString(Paths.get(path)));
+ Document doc = Jsoup.parse(text);
+
+ // Check the Element count for each result type
+ validateElementCount(doc, violationCount, ResultType.Violations);
+ validateElementCount(doc, passCount, ResultType.Passes);
+ validateElementCount(doc, inapplicableCount, ResultType.Inapplicable);
+ validateElementCount(doc, incompleteCount, ResultType.Incomplete);
+
+ // Check header data
+ Assert.assertTrue(text.contains("Using: axe-core"), "Expected to find 'Using: axe-core'");
+
+ // Check the result count for each result type
+ validateResultCount(text, violationCount, ResultType.Violations);
+ validateResultCount(text, incompleteCount, ResultType.Incomplete);
+ validateResultCount(text, passCount, ResultType.Passes);
+ validateResultCount(text, inapplicableCount, ResultType.Inapplicable);
+ }
+
+ private void validateElementCount(Document doc, int count, ResultType resultType) {
+ String ending = resultType.equals(ResultType.Inapplicable) ? "div.findings" : "div > div.htmlTable";
+ String xpath = "#" + resultType + "Section > " + ending;
+ Elements liNodes = !doc.select(xpath).isEmpty() ? doc.select(xpath) : new Elements();
+ Assert.assertEquals(liNodes.size(), count, "Expected " + count + " " + resultType);
+ }
+
+ private void validateResultCount(String text, int count, ResultType resultType) {
+ if (count != 0) {
+ Assert.assertTrue(text.contains(resultType + ": " + count),
+ "Expected to find '" + resultType + ": " + count);
+ }
+ }
+
+ private void validateResultNotWritten(String path, EnumSet resultTypeArray) throws IOException {
+ loadTestPage(integrationTestTargetSimpleUrl);
+ String text = String.valueOf(Files.readAllLines(Paths.get(path)));
+
+ for (ResultType resultType : resultTypeArray) {
+ Assert.assertFalse(text.contains(resultType + ": "),
+ "Expected to not find '" + resultType + ": '");
+ }
+ }
+
+ private void deleteFile(File file) {
+ if (file.exists()) {
+ Assert.assertTrue(file.delete(), "File was not deleted");
+ Assert.assertFalse(file.exists(), "file still exists");
+ }
+ }
+}
diff --git a/maqs-accessibility/src/test/java/com/cognizantsoftvision/maqs/accessibility/HTMLReporterUnitTest.java b/maqs-accessibility/src/test/java/com/cognizantsoftvision/maqs/accessibility/htmlReporter/SeleniumUnitTest.java
similarity index 92%
rename from maqs-accessibility/src/test/java/com/cognizantsoftvision/maqs/accessibility/HTMLReporterUnitTest.java
rename to maqs-accessibility/src/test/java/com/cognizantsoftvision/maqs/accessibility/htmlReporter/SeleniumUnitTest.java
index bd6615663..aff209343 100644
--- a/maqs-accessibility/src/test/java/com/cognizantsoftvision/maqs/accessibility/HTMLReporterUnitTest.java
+++ b/maqs-accessibility/src/test/java/com/cognizantsoftvision/maqs/accessibility/htmlReporter/SeleniumUnitTest.java
@@ -2,8 +2,9 @@
* Copyright 2022 (C) Cognizant SoftVision, All rights Reserved
*/
-package com.cognizantsoftvision.maqs.accessibility;
+package com.cognizantsoftvision.maqs.accessibility.htmlReporter;
+import com.cognizantsoftvision.maqs.accessibility.SeleniumReporter;
import com.deque.html.axecore.axeargs.AxeRunOptions;
import com.deque.html.axecore.results.Check;
import com.deque.html.axecore.results.CheckedNode;
@@ -36,7 +37,7 @@
/**
* Accessibility HTML Reporter unit tests.
*/
-public class HTMLReporterUnitTest extends BaseSeleniumTest {
+public class SeleniumUnitTest extends BaseSeleniumTest {
/**
* The file to be opened in the browser.
@@ -119,7 +120,7 @@ public void runScanOnGivenElement()
throws IOException, ParseException {
loadTestPage(integrationTestTargetSimpleUrl);
String path = createReportPath();
- HtmlReporter.createAxeHtmlReport(this.getWebDriver(),
+ SeleniumReporter.createAxeHtmlReport(this.getWebDriver(),
this.getWebDriver().findElement(By.cssSelector(mainElementSelector)), path);
validateReport(path, 3, 14, 0, 75);
@@ -130,7 +131,7 @@ public void runScanOnGivenElement()
public void reportFullPage() throws IOException, ParseException {
loadTestPage(integrationTestTargetSimpleUrl);
String path = createReportPath();
- HtmlReporter.createAxeHtmlReport(this.getWebDriver(), path);
+ SeleniumReporter.createAxeHtmlReport(this.getWebDriver(), path);
validateReport(path, 4, 26, 0, 69);
deleteFile(new File(path));
@@ -141,7 +142,7 @@ public void reportFullPageViolationsOnly()
throws IOException, ParseException {
loadTestPage(integrationTestTargetSimpleUrl);
String path = createReportPath();
- HtmlReporter.createAxeHtmlReport(this.getWebDriver(), path, EnumSet.of(ResultType.Violations));
+ SeleniumReporter.createAxeHtmlReport(this.getWebDriver(), path, EnumSet.of(ResultType.Violations));
// Check violations
validateReport(path, 4, 0, 0, 0);
@@ -156,7 +157,7 @@ public void reportFullPagePassesInapplicableViolationsOnly()
throws IOException, ParseException {
loadTestPage(integrationTestTargetSimpleUrl);
String path = createReportPath();
- HtmlReporter.createAxeHtmlReport(this.getWebDriver(), path,
+ SeleniumReporter.createAxeHtmlReport(this.getWebDriver(), path,
EnumSet.of(ResultType.Passes, ResultType.Inapplicable, ResultType.Violations));
// Check Passes
@@ -172,7 +173,7 @@ public void reportOnElement() throws IOException, ParseException {
String path = createReportPath();
var mainElement = this.getWebDriver().findElement(By.cssSelector(mainElementSelector));
- HtmlReporter.createAxeHtmlReport(this.getWebDriver(), mainElement, path);
+ SeleniumReporter.createAxeHtmlReport(this.getWebDriver(), mainElement, path);
validateReport(path, 3, 14, 0, 75);
deleteFile(new File(path));
@@ -184,7 +185,7 @@ public void reportRespectRules() throws IOException, ParseException {
String path = createReportPath();
var builder = new AxeBuilder().disableRules(Collections.singletonList("color-contrast"));
- HtmlReporter.createAxeHtmlReport(this.getWebDriver(), builder.analyze(this.getWebDriver()), path);
+ SeleniumReporter.createAxeHtmlReport(this.getWebDriver(), builder.analyze(this.getWebDriver()), path);
validateReport(path, 3, 21, 0, 69);
deleteFile(new File(path));
@@ -195,7 +196,7 @@ public void reportSampleResults() throws IOException, ParseException {
String path = createReportPath();
Results results = new ObjectMapper().readValue(new File(integrationTestJsonResultUrl), Results.class);
- HtmlReporter.createAxeHtmlReport(this.getWebDriver(), results, path);
+ SeleniumReporter.createAxeHtmlReport(this.getWebDriver(), results, path);
validateReport(path, 3, 5, 2, 4);
String text = new String(Files.readAllBytes(Paths.get(path)));
@@ -220,7 +221,7 @@ public void reportRespectsIframeImplicitTrue() throws IOException, ParseExceptio
loadTestPage(integrationTestTargetComplexUrl);
String path = createReportPath();
- HtmlReporter.createAxeHtmlReport(this.getWebDriver(), path);
+ SeleniumReporter.createAxeHtmlReport(this.getWebDriver(), path);
validateReport(path, 4, 43, 0, 64);
deleteFile(new File(path));
@@ -236,7 +237,7 @@ public void ReportRespectsIframeTrue() throws IOException, ParseException {
var builder = new AxeBuilder().withOptions(runOptions);
- HtmlReporter.createAxeHtmlReport(this.getWebDriver(), builder.analyze(this.getWebDriver()), path);
+ SeleniumReporter.createAxeHtmlReport(this.getWebDriver(), builder.analyze(this.getWebDriver()), path);
validateReport(path, 4, 43, 0, 64);
deleteFile(new File(path));
@@ -251,7 +252,7 @@ public void reportRespectsIframeFalse() throws IOException, ParseException {
runOptions.setIFrames(false);
var builder = new AxeBuilder().withOptions(runOptions);
- HtmlReporter.createAxeHtmlReport(this.getWebDriver(), builder.analyze(this.getWebDriver()), path);
+ SeleniumReporter.createAxeHtmlReport(this.getWebDriver(), builder.analyze(this.getWebDriver()), path);
validateReport(path, 4, 43, 0, 64);
deleteFile(new File(path));
diff --git a/maqs-playwright/pom.xml b/maqs-playwright/pom.xml
index 5ec8c6aa8..53a9175f0 100644
--- a/maqs-playwright/pom.xml
+++ b/maqs-playwright/pom.xml
@@ -16,7 +16,7 @@
${revision}
- 1.21.0
+ 1.25.0