You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
207 lines
7.8 KiB
207 lines
7.8 KiB
/* |
|
* Copyright 2020 Adobe Systems Incorporated |
|
* |
|
* 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 com.mcaps.it.tests; |
|
|
|
import com.adobe.cq.testing.client.CQClient; |
|
import org.apache.http.impl.client.CloseableHttpClient; |
|
import org.apache.sling.testing.clients.ClientException; |
|
import org.apache.sling.testing.clients.SlingClientConfig; |
|
import org.htmlunit.DefaultCssErrorHandler; |
|
import org.htmlunit.WebClient; |
|
import org.htmlunit.WebClientOptions; |
|
import org.htmlunit.html.DomNode; |
|
import org.htmlunit.html.HtmlPage; |
|
import org.slf4j.LoggerFactory; |
|
import org.w3c.dom.Node; |
|
|
|
import java.io.IOException; |
|
import java.net.URI; |
|
import java.net.URISyntaxException; |
|
import java.net.URL; |
|
import java.util.ArrayList; |
|
import java.util.List; |
|
import java.util.logging.Level; |
|
import java.util.logging.Logger; |
|
|
|
import static org.junit.Assert.fail; |
|
|
|
/** |
|
* AEM client that maintains a WebClient instance from HTMLUnit framework |
|
*/ |
|
public class HtmlUnitClient extends CQClient { |
|
|
|
|
|
private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(HtmlUnitClient.class); |
|
|
|
private final WebClient webClient = new WebClient(); |
|
|
|
/** Extracts references to external resources used by the specified page. |
|
* This method extracts references from script, img, meta and link tags. |
|
* @param path path to the page. |
|
* @return list of URIs resolved against the pages baseURL |
|
* @throws IOException when IO error occurs |
|
* @throws URISyntaxException if malformed URL reference is found. |
|
*/ |
|
public List<URI> getResourceRefs(String path) throws IOException, URISyntaxException { |
|
HtmlPage page = getPage(path, false); |
|
List<URI> result = new ArrayList<>(); |
|
result.addAll(getRefs(page, "script", "src")); |
|
result.addAll(getRefs(page, "img", "src")); |
|
result.addAll(getRefs(page, "meta", "href")); |
|
result.addAll(getRefs(page, "link", "href")); |
|
result.addAll(getCoreComponentImageRenditions(page)); |
|
return result; |
|
} |
|
|
|
/** |
|
* Loads html page specified by path. |
|
* @param path path to the page |
|
* @param javaScriptEnabled whether to execute javascript |
|
* @return parsed page. |
|
* @throws IOException if IO error occurs. |
|
*/ |
|
public HtmlPage getPage(String path, boolean javaScriptEnabled) throws IOException { |
|
WebClientOptions options = webClient.getOptions(); |
|
boolean wasJsEnabled = options.isJavaScriptEnabled(); |
|
try { |
|
options.setJavaScriptEnabled(javaScriptEnabled); |
|
return getPage(webClient, getUrl(path).toURL()); |
|
} finally { |
|
options.setJavaScriptEnabled(wasJsEnabled); |
|
} |
|
} |
|
|
|
//********************************************* |
|
// Creation |
|
//********************************************* |
|
|
|
public HtmlUnitClient(CloseableHttpClient http, SlingClientConfig config) throws ClientException { |
|
super(http, config); |
|
webClient.setCredentialsProvider(this.getCredentialsProvider()); |
|
} |
|
|
|
public HtmlUnitClient(URI url, String user, String password) throws ClientException { |
|
super(url, user, password); |
|
webClient.setCredentialsProvider(this.getCredentialsProvider()); |
|
} |
|
|
|
@Override |
|
public void close() throws IOException { |
|
try { |
|
webClient.close(); |
|
} finally { |
|
super.close(); |
|
} |
|
} |
|
|
|
//********************************************* |
|
// Internals |
|
//********************************************* |
|
|
|
private static List<URI> getRefs(HtmlPage page, String tag, String refAttr) throws URISyntaxException { |
|
URI baseUri = new URI(page.getBaseURI()); |
|
List<URI> result = new ArrayList<>(); |
|
for (DomNode child : page.getElementsByTagName(tag)) { |
|
URI uriRef = getNamedItemAsUri(child, refAttr); |
|
if (uriRef != null) { |
|
URI uri = baseUri.resolve(uriRef); |
|
result.add(uri); |
|
} |
|
} |
|
return result; |
|
} |
|
|
|
/** |
|
* Extract all image core components and their references from the page. |
|
* @param page the page to scan |
|
* @return all renditions of all image core components |
|
* @throws URISyntaxException |
|
*/ |
|
private static List<URI> getCoreComponentImageRenditions (HtmlPage page) throws URISyntaxException { |
|
URI baseUri = new URI(page.getBaseURI()); |
|
List<URI> result = new ArrayList<>(); |
|
|
|
// detect images core components based on the CSS class name |
|
List<DomNode> coreComponents = page.getByXPath("//div[contains(@class, 'cmp-image')]"); |
|
for (DomNode child: coreComponents) { |
|
String src = null; |
|
String width = null; |
|
if (child.getAttributes().getNamedItem("data-cmp-src") != null) { |
|
src = child.getAttributes().getNamedItem("data-cmp-src").getNodeValue(); |
|
} |
|
if (child.getAttributes().getNamedItem("data-cmp-widths") != null) { |
|
width = child.getAttributes().getNamedItem("data-cmp-widths").getNodeValue(); |
|
} |
|
if (src != null && width != null) { |
|
String[] widths = width.split(","); |
|
for (String w: widths) { |
|
String ref = src.replace("{.width}", "."+w); |
|
result.add(baseUri.resolve(ref)); |
|
} |
|
} else if (src != null && width == null) { |
|
// happens with SVG and GIFs |
|
String ref = src.replace("{.width}", ""); |
|
result.add(baseUri.resolve(ref)); |
|
} |
|
} |
|
return result; |
|
} |
|
|
|
|
|
/** |
|
* Loads requested page while suppressing CSS errors (logged as warnings) |
|
* @param webClient web client to use for loading |
|
* @param url page URL |
|
* @return loaded HtmlPage instance. |
|
* @throws IOException when error occurs |
|
*/ |
|
private static HtmlPage getPage(WebClient webClient, URL url) throws IOException { |
|
Logger logger = Logger.getLogger(DefaultCssErrorHandler.class.getName()); |
|
Level originalLevel = logger.getLevel(); |
|
try { |
|
logger.setLevel(Level.SEVERE); |
|
return webClient.getPage(url); |
|
} finally { |
|
logger.setLevel(originalLevel); |
|
} |
|
} |
|
|
|
/** |
|
* Extracts URI reference from specified element and converts it to URI |
|
* This method will trigger junit assertion if refAttr value cannot be parsed as URI |
|
* providing comprehensive error message. |
|
* @param node - html element from which to extract reference |
|
* @param refAttr - name of the attribute containing corresponding value |
|
* @return refAttr value as URI or <code>null</code> if attribute does not exist |
|
*/ |
|
private static URI getNamedItemAsUri(DomNode node, String refAttr) { |
|
Node src = node.getAttributes().getNamedItem(refAttr); |
|
if (src == null) { |
|
return null; |
|
} else { |
|
try { |
|
String href = src.getNodeValue(); |
|
return new URI(href); |
|
} catch (URISyntaxException e) { |
|
fail("Invalid URI value in [" + refAttr + "] attribute in: [" + node + "].\n" + |
|
" Page URL: [" + node.getPage().getUrl() + "]\n" + |
|
" XPath: [" + node.getCanonicalXPath() + "]\n" + |
|
" Caused by: [" + e.getMessage() + "]"); |
|
throw new AssertionError(); // must never happen |
|
} |
|
} |
|
} |
|
}
|
|
|