001/*
002 * (C) Copyright 2015-2016 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Anahide Tchertchian
018 */
019package org.nuxeo.functionaltests;
020
021import static org.junit.Assert.fail;
022
023import java.util.ArrayList;
024import java.util.Arrays;
025import java.util.List;
026
027import org.openqa.selenium.WebDriver;
028
029import net.jsourcerer.webdriver.jserrorcollector.JavaScriptError;
030
031/**
032 * Helper class to collect JavaScript errors on a page.
033 *
034 * @since 8.1
035 */
036public class JavaScriptErrorCollector {
037
038    protected final WebDriver driver;
039
040    protected List<JavaScriptErrorIgnoreRule> ignores;
041
042    public JavaScriptErrorCollector(WebDriver driver) {
043        super();
044        this.driver = driver;
045        ignores = new ArrayList<>();
046        // Add this error per default which are actually a warning for FF 42
047        ignore(JavaScriptErrorIgnoreRule.startsWith("mutating the [[Prototype]] of an object"));
048    }
049
050    /**
051     * @since 9.3
052     */
053    public static JavaScriptErrorCollector from(WebDriver driver) {
054        return new JavaScriptErrorCollector(driver);
055    }
056
057    /**
058     * @since 9.3
059     */
060    public JavaScriptErrorCollector ignore(JavaScriptErrorIgnoreRule... ignores) {
061        this.ignores.addAll(Arrays.asList(ignores));
062        return this;
063    }
064
065    /**
066     * Throws an {@link AssertionError} when JavaScript errors are detected on current page.
067     */
068    public void checkForErrors() {
069        if (driver == null) {
070            return;
071        }
072
073        List<JavaScriptError> jsErrors = JavaScriptError.readErrors(driver);
074        if (jsErrors != null && !jsErrors.isEmpty()) {
075            StringBuilder msg = new StringBuilder();
076            int i = 0;
077            for (JavaScriptError jsError : jsErrors) {
078                // skip errors that match an ignored rule
079                if (ignores.stream().anyMatch(e -> e.isIgnored(jsError))) {
080                    continue;
081                }
082                if (i != 0) {
083                    msg.append(", ");
084                }
085                i++;
086                msg.append("\"").append(jsError.getErrorMessage()).append("\"");
087                msg.append(" at ").append(jsError.getSourceName());
088                msg.append(" line ").append(jsError.getLineNumber());
089            }
090            if (i > 0) {
091                msg.append("]");
092                msg.insert(0, jsErrors.size() + " Javascript error(s) detected: " + "[");
093                fail(msg.toString());
094            }
095        }
096    }
097
098    /**
099     * @since 9.3
100     */
101    public static class JavaScriptErrorIgnoreRule {
102        protected String text = "";
103
104        protected String source = "";
105
106        protected JavaScriptErrorIgnoreRule(String text) {
107            this.text = text;
108        }
109
110        /**
111         * Ensure that error has to be ignored or not.
112         */
113        protected boolean isIgnored(JavaScriptError error) {
114            if (error.getErrorMessage().startsWith(text)) {
115                return error.getSourceName().startsWith(source);
116            }
117
118            return false;
119        }
120
121        public JavaScriptErrorIgnoreRule withSource(String source) {
122            this.source = source;
123            return this;
124        }
125
126        public static JavaScriptErrorIgnoreRule startsWith(String text) {
127            return new JavaScriptErrorIgnoreRule(text);
128        }
129
130        public static JavaScriptErrorIgnoreRule fromSource(String source) {
131            return new JavaScriptErrorIgnoreRule("").withSource(source);
132        }
133    }
134}