A Demo project for MSFT
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

/*
* 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
}
}
}
}