001    /**
002     * Copyright 2005-2012 The Kuali Foundation
003     *
004     * Licensed under the Educational Community 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.opensource.org/licenses/ecl2.php
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     */package edu.samplu.common;
016    
017    import com.thoughtworks.selenium.Selenium;
018    import org.apache.commons.lang.RandomStringUtils;
019    import org.junit.Assert;
020    import org.openqa.selenium.By;
021    import org.openqa.selenium.WebDriver;
022    import org.openqa.selenium.chrome.ChromeDriver;
023    import org.openqa.selenium.firefox.FirefoxDriver;
024    import org.openqa.selenium.firefox.FirefoxProfile;
025    import org.openqa.selenium.remote.DesiredCapabilities;
026    import org.openqa.selenium.remote.RemoteWebDriver;
027    
028    import java.io.PrintWriter;
029    import java.io.StringWriter;
030    import java.net.MalformedURLException;
031    import java.net.URL;
032    import java.util.Calendar;
033    import java.util.HashMap;
034    import java.util.Iterator;
035    import java.util.Map;
036    
037    import static com.thoughtworks.selenium.SeleneseTestBase.fail;
038    import static org.junit.Assert.assertEquals;
039    
040    /**
041     * Common selenium test methods that should be reused rather than recreated for each test.
042     * @author Kuali Rice Team (rice.collab@kuali.org)
043     */
044    
045    public class ITUtil {
046    
047        public static final String KUALI_PORTAL_TITLE = "Kuali Portal Index";
048        public final static String PORTAL = "/portal.do";
049        public static final String DTS = Calendar.getInstance().getTimeInMillis() + "";
050        public static final String DTS_TWO = Calendar.getInstance().getTimeInMillis() + "" + RandomStringUtils.randomAlphabetic(2).toLowerCase();
051        public static String WAIT_TO_END_TEST = "5000";
052        public static final String DIV_ERROR_LOCATOR = "//div[@class='error']";
053        public static final String DIV_EXCOL_LOCATOR = "//div[@class='msg-excol']";
054        public static final int WAIT_DEFAULT_SECONDS = 60;
055        public static final String DEFAULT_WAIT_FOR_PAGE_TO_LOAD_TIMEOUT = "30000";
056        static Map<String, String> jiraMatches;
057        public static final String REMOTE_AUTOLOGIN_PROPERTY = "remote.autologin";
058    
059        static {
060            jiraMatches = new HashMap<String, String>();
061            jiraMatches.put("Error setting property values; nested exception is org.springframework.beans.NotWritablePropertyException: Invalid property 'refreshWhenChanged' of bean class [org.kuali.rice.krad.uif.element.Action]: Bean property 'refreshWhenChanged' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?",
062                    "KULRICE-8137 Agenda Rule edit Incident report Invalid property 'refreshWhenChanged'");
063    
064            jiraMatches.put("org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase.processAddCollectionLineBusinessRules(MaintenanceDocumentRuleBase.",
065                    "KULRICE-8142 NPE in MaintenanceDocumentRuleBase.processAddCollectionLineBusinessRules");
066    
067            jiraMatches.put("at org.kuali.rice.krad.rules.DocumentRuleBase.isDocumentOverviewValid(DocumentRuleBase.",
068                    "KULRICE-8134 NPE in DocumentRuleBase.isDocumentOverviewValid(DocumentRuleBase");
069    
070            jiraMatches.put("org.kuali.rice.krad.uif.layout.TableLayoutManager.buildLine(TableLayoutManager.",
071                    "KULRICE-8160 NPE at TableLayoutManager.buildLine(TableLayoutManager");
072    
073            jiraMatches.put("Bean property 'configFileLocations' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?",
074                    "KULRICE-8173 Bean property 'configFileLocations' is not writable or has an invalid setter method");
075    
076            jiraMatches.put("Bean property 'componentSecurity' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?",
077                    "KULRICE-8182 JDK7 Bean property 'componentSecurity' is not readable...");
078    
079            jiraMatches.put("java.sql.SQLSyntaxErrorException: ORA-00904: \"ROUTEHEADERID\": invalid identifier",
080                    "KULRICE-8277 Several ITs fail with OJB operation; bad SQL grammar []; nested exception is java.sql.SQLException: ORA-00904: \"ROUTEHEADERID\": invalid identifier");
081    //        jiraMatches.put("",
082    //                "");
083    
084        }
085    
086        /**
087         * "FINAL", selenium.getText("//table[@id='row']/tbody/tr[1]/td[4]"
088         * @param selenium
089         * @param docId
090         */
091        public static void assertDocFinal(Selenium selenium, String docId) {
092            docId= "link=" + docId;
093            if(selenium.isElementPresent(docId)){
094                assertEquals("FINAL", selenium.getText("//table[@id='row']/tbody/tr[1]/td[4]"));
095            }else{
096                assertEquals(docId, selenium.getText("//table[@id='row']/tbody/tr[1]/td[1]"));
097                assertEquals("FINAL", selenium.getText("//table[@id='row']/tbody/tr[1]/td[4]"));
098            }
099        }
100    
101        protected static String blanketApprovalCleanUpErrorText(String errorText) {
102            errorText = errorText.replace("* required field", "").replace("\n", " ").trim(); // bit of extra ui text we don't care about
103            return errorText;
104        }
105    
106        /**
107         * Generic blanket approve behavior
108         * @param selenium
109         * @throws InterruptedException
110         */
111        public static void blanketApprove(Selenium selenium) throws InterruptedException {
112            ITUtil.checkForIncidentReport(selenium, "methodToCall.blanketApprove");
113            ITUtil.waitAndClick(selenium, "methodToCall.blanketApprove");
114            selenium.waitForPageToLoad(DEFAULT_WAIT_FOR_PAGE_TO_LOAD_TIMEOUT);
115            Thread.sleep(2000);
116    
117            if (selenium.isElementPresent(DIV_ERROR_LOCATOR)) {
118                String errorText = selenium.getText(DIV_ERROR_LOCATOR);
119                if (errorText != null && errorText.contains("error(s) found on page.")) {
120                    errorText = blanketApprovalCleanUpErrorText(errorText);
121                    if (selenium.isElementPresent(DIV_EXCOL_LOCATOR)) { // not present if errors are at the bottom of the page (see left-errmsg below)
122                        errorText = blanketApprovalCleanUpErrorText(selenium.getText(DIV_EXCOL_LOCATOR));// + "\n" + selenium.getHtmlSource()); // replacing errorText as DIV_EXCOL_LOCATOR includes the error count
123                    }
124                    if (selenium.isElementPresent("//div[@class='left-errmsg-tab']/div/div")) {
125                        errorText = errorText + blanketApprovalCleanUpErrorText(selenium.getText("//div[@class='left-errmsg-tab']/div/div"));
126                    }
127    
128                    //                if (selenium.isElementPresent("//div[@class='left-errmsg']/div")) {
129                    //                    errorText = errorText + " " + selenium.getText("//div[@class='left-errmsg']/div/div[1]");
130                    //                }
131                    Assert.fail(errorText);
132                }
133            }
134            ITUtil.checkForIncidentReport(selenium, "//img[@alt='doc search']");
135            waitAndClick(selenium, "//img[@alt='doc search']");
136            selenium.waitForPageToLoad(DEFAULT_WAIT_FOR_PAGE_TO_LOAD_TIMEOUT);
137            assertEquals("Kuali Portal Index", selenium.getTitle());
138            selenium.selectFrame("iframeportlet");
139            selenium.click("//input[@name='methodToCall.search' and @value='search']");
140            selenium.waitForPageToLoad(DEFAULT_WAIT_FOR_PAGE_TO_LOAD_TIMEOUT);
141        }
142    
143        /**
144         * "//li[@class='uif-errorMessageItem']"
145         * @param selenium
146         * @param message
147         */
148        public static void checkErrorMessageItem(Selenium selenium, String message) {
149            final String error_locator = "//li[@class='uif-errorMessageItem']";
150            if (selenium.isElementPresent(error_locator)) {
151                String errorText = selenium.getText(error_locator);
152                if (errorText != null && errorText.contains("errors")) {
153                    Assert.fail(errorText + message);
154                }
155            }
156        }
157    
158    
159        /**
160         * In order to run as a smoke test the ability to set the baseUrl via the JVM arg remote.public.url is required.
161         * Trailing slashes are trimmed.  If the remote.public.url does not start with http:// it will be added.
162         * @return http://localhost:8080/kr-dev by default else the value of remote.public.url
163         */
164        public static String getBaseUrlString() {
165            String baseUrl = System.getProperty("remote.public.url");
166            if (baseUrl == null) {
167                baseUrl = "http://localhost:8080/kr-dev";
168            }
169            baseUrl = prettyHttp(baseUrl);
170            return baseUrl;
171        }
172    
173        /**
174         * Append http:// if not present.  Remove trailing /
175         * @param baseUrl
176         * @return
177         */
178        public static String prettyHttp(String baseUrl) {
179            if (baseUrl.endsWith("/")) {
180                baseUrl = baseUrl.substring(0, baseUrl.length() - 1);
181            }
182            if (!baseUrl.startsWith("http")) {
183                baseUrl = "http://" + baseUrl;
184            }
185            return baseUrl;
186        }
187    
188        /**
189         * In order to run as a smoke test under selenium grid the ability to set the hubUrl via the JVM arg remote.public.hub is required.
190         * Trailing slashes are trimmed.  If the remote.public.hub does not start with http:// it will be added.
191         * @return http://localhost:4444/wd/hub by default else the value of remote.public.hub
192         */
193        public static String getHubUrlString() {
194            String hubUrl = System.getProperty("remote.public.hub");
195            if (hubUrl == null) {
196                hubUrl = "http://localhost:4444/wd/hub";
197            }
198            hubUrl = prettyHttp(hubUrl);
199            if (!hubUrl.endsWith("/wd/hub")) {
200                hubUrl = hubUrl + "/wd/hub";
201            }
202            return hubUrl;
203        }
204    
205        /**
206         * remote.public.driver set to chrome or firefox (null assumes firefox)
207         * if remote.public.hub is set a RemoteWebDriver is created (Selenium Grid)
208         * @return WebDriver or null if unable to create
209         */
210        public static WebDriver getWebDriver() {
211            String driverParam = System.getProperty("remote.public.driver");
212            String hubParam = System.getProperty("remote.public.hub");
213            if (hubParam == null) {
214                if (driverParam == null || "firefox".equalsIgnoreCase(driverParam)) {
215                    FirefoxProfile profile = new FirefoxProfile();
216                    profile.setEnableNativeEvents(false);
217                    return new FirefoxDriver(profile);
218                } else if ("chrome".equalsIgnoreCase(driverParam)) {
219                    return new ChromeDriver();
220                }
221            } else {
222                try {
223                    if (driverParam == null || "firefox".equalsIgnoreCase(driverParam)) {
224                        return new RemoteWebDriver(new URL(ITUtil.getHubUrlString()), DesiredCapabilities.firefox());
225                    } else if ("chrome".equalsIgnoreCase(driverParam)) {
226                        return new RemoteWebDriver(new URL(ITUtil.getHubUrlString()), DesiredCapabilities.chrome());
227                    }
228                } catch (MalformedURLException mue) {
229                    System.out.println(ITUtil.getHubUrlString() + " " + mue.getMessage());
230                    mue.printStackTrace();
231                }
232            }
233            return null;
234        }
235    
236        /**
237         * If the JVM arg remote.autologin is set, auto login as admin will not be done.
238         * @param selenium to login with
239         */
240        public static void loginSe(Selenium selenium) {
241            loginSe(selenium, "admin");
242        }
243    
244        /**
245         * If the JVM arg remote.autologin is set, auto login as admin will not be done.
246         * @param driver
247         * @param userName
248         * @throws InterruptedException
249         */
250        public static void login(WebDriver driver, String userName) throws InterruptedException {
251            if (System.getProperty(REMOTE_AUTOLOGIN_PROPERTY) == null) {
252                driver.findElement(By.name("__login_user")).clear();
253                driver.findElement(By.name("__login_user")).sendKeys(userName);
254                driver.findElement(By.cssSelector("input[type=\"submit\"]")).click();
255                Thread.sleep(1000);
256                if (driver.getPageSource().indexOf("<strong>Invalid username</strong>") > -1) {
257                    Assert.fail("Invalid username " + userName);
258                }
259            }
260        }
261    
262        /**
263         * If the JVM arg remote.autologin is set, auto login as admin will not be done.
264         * @param selenium to login with
265         */
266        public static void loginSe(Selenium selenium, String user) {
267            if (System.getProperty(REMOTE_AUTOLOGIN_PROPERTY) == null) {
268                try {
269                    selenium.waitForPageToLoad(DEFAULT_WAIT_FOR_PAGE_TO_LOAD_TIMEOUT);
270                } catch (Exception e) {
271                    Assert.fail("Login page not loaded app started?");
272                }
273                if (!"Login".equals(selenium.getTitle())) {
274                    fail("Title is not Login as expected, but " + selenium.getTitle());
275                }
276                selenium.type("__login_user", user);
277                selenium.click("//input[@type='submit']"); //using css selector fails
278                selenium.waitForPageToLoad(DEFAULT_WAIT_FOR_PAGE_TO_LOAD_TIMEOUT);
279                if (selenium.getHtmlSource().indexOf("<strong>Invalid username</strong>") > -1) {
280                    Assert.fail("Invalid username " + user);
281                }
282            }
283        }
284    
285        /**
286         * Write the given stack trace into a String
287         * @param throwable whose stack trace to return
288         * @return String of the given throwable's stack trace.
289         */
290        public static String stackTrace(Throwable throwable) {
291            StringWriter wrt=new StringWriter();
292            PrintWriter pw=new PrintWriter(wrt);
293            throwable.printStackTrace(pw);
294            pw.flush();
295            return wrt.toString();
296        }
297    
298        /**
299         * Setting the JVM arg remote.driver.dontTearDown to y or t leaves the browser window open when the test has completed.  Valuable when debugging, updating, or creating new tests.
300         * When implementing your own tearDown method rather than an inherited one, it is a common courtesy to include this check and not stop and shutdown the browser window to make it easy debug or update your test.
301         * {@code }
302         * @return true if the dontTearDownProperty is not set.
303         */
304        public static boolean dontTearDownPropertyNotSet() {
305            return System.getProperty("remote.driver.dontTearDown") == null ||
306                    "f".startsWith(System.getProperty("remote.driver.dontTearDown").toLowerCase()) ||
307                    "n".startsWith(System.getProperty("remote.driver.dontTearDown").toLowerCase());
308        }
309    
310        /**
311         * Wait 60 seconds for the elementLocator to be present or fail.  Click if present
312         * @param selenium
313         * @param elementLocator
314         * @throws InterruptedException
315         */
316        public static void waitAndClick(Selenium selenium, String elementLocator) throws InterruptedException {
317            waitAndClick(selenium, elementLocator, WAIT_DEFAULT_SECONDS);
318        }
319    
320        /**
321         * Wait 60 seconds for the elementLocator to be present or fail.  Click if present
322         * @param selenium
323         * @param elementLocator
324         * @param message
325         * @throws InterruptedException
326         */
327        public static void waitAndClick(Selenium selenium, String elementLocator, String message) throws InterruptedException {
328            waitAndClick(selenium, elementLocator, WAIT_DEFAULT_SECONDS, message);
329        }
330    
331        /**
332         * Wait the given seconds for the elementLocator to be present or fail
333         * @param selenium
334         * @param elementLocator
335         * @param seconds
336         * @throws InterruptedException
337         */
338        public static void waitAndClick(Selenium selenium, String elementLocator, int seconds) throws InterruptedException {
339            waitAndClick(selenium, elementLocator, seconds, "");
340        }
341    
342        /**
343         * Wait the given seconds for the elementLocator to be present or fail
344         * @param selenium
345         * @param elementLocator
346         * @param seconds
347         * @param message
348         * @throws InterruptedException
349         */
350        public static void waitAndClick(Selenium selenium, String elementLocator, int seconds, String message) throws InterruptedException {
351            waitForElement(selenium, elementLocator, seconds, message);
352            selenium.click(elementLocator);
353            Thread.sleep(1000);
354            ITUtil.checkForIncidentReport(selenium, elementLocator, message);
355        }
356    
357        /**
358         * Wait the 60 seconds for the elementLocator to be present or fail, when present type the text.
359         * @param selenium
360         * @param elementLocator
361         * @param text
362         * @throws InterruptedException
363         */
364        public static void waitAndType(Selenium selenium, String elementLocator, String text) throws InterruptedException {
365            waitAndType(selenium, elementLocator, text, "");
366        }
367    
368        /**
369         * Wait the 60 seconds for the elementLocator to be present or fail, when present type the text.  Include failure message on fail.
370         * @param selenium
371         * @param elementLocator
372         * @param text
373         * @param message
374         * @throws InterruptedException
375         */
376        public static void waitAndType(Selenium selenium, String elementLocator, String text, String message) throws InterruptedException {
377            waitAndType(selenium, elementLocator, WAIT_DEFAULT_SECONDS, text, message);
378        }
379    
380        /**
381         * Wait the given seconds for the elementLocator to be present or fail, when present type the text.
382         * @param selenium
383         * @param elementLocator
384         * @param seconds
385         * @param text
386         * @param message
387         * @throws InterruptedException
388         */
389        public static void waitAndType(Selenium selenium, String elementLocator, int seconds, String text, String message) throws InterruptedException {
390            waitForElement(selenium, elementLocator, seconds, message);
391            selenium.type(elementLocator, text);
392            Thread.sleep(1000);
393        }
394    
395        /**
396         * Wait 60 seconds for the elementLocator to be present or fail
397         * @param selenium
398         * @param elementLocator
399         * @throws InterruptedException
400         */
401        public static void waitForElement(Selenium selenium, String elementLocator) throws InterruptedException {
402            waitForElement(selenium, elementLocator, WAIT_DEFAULT_SECONDS);
403        }
404    
405        /**
406         * Wait 60 seconds for the elementLocator to be present or fail
407         * @param selenium
408         * @param elementLocator
409         * @param message
410         * @throws InterruptedException
411         */
412        public static void waitForElement(Selenium selenium, String elementLocator, String message) throws InterruptedException {
413            waitForElement(selenium, elementLocator, WAIT_DEFAULT_SECONDS, message);
414        }
415    
416        /**
417         * Wait the given seconds for the elementLocator to be present or fail
418         * @param selenium
419         * @param elementLocator
420         * @param seconds
421         * @throws InterruptedException
422         */
423        public static void waitForElement(Selenium selenium, String elementLocator, int seconds) throws InterruptedException {
424            waitForElement(selenium, elementLocator, WAIT_DEFAULT_SECONDS, "");
425        }
426    
427        /**
428         * Wait the given seconds for the elementLocator to be present or fail
429         * @param selenium
430         * @param elementLocator
431         * @param seconds
432         * @param message
433         * @throws InterruptedException
434         */
435        public static void waitForElement(Selenium selenium, String elementLocator, int seconds, String message) throws InterruptedException {
436            boolean failed = false;
437            for (int second = 0;; second++) {
438                if (second >= seconds) failed = true;
439                try { if (failed || selenium.isElementPresent(elementLocator)) break; } catch (Exception e) {}
440                Thread.sleep(1000);
441            }
442            ITUtil.checkForIncidentReport(selenium, elementLocator); // after timeout to be sure page is loaded
443            if (failed) fail("timeout of " + seconds + " seconds waiting for " + elementLocator + " " + message);
444        }
445    
446        /**
447         * Wait the given seconds for the elementLocator to be present or fail
448         * @param selenium
449         * @param elementLocator
450         * @throws InterruptedException
451         */
452        public static void waitForElementVisible(Selenium selenium, String elementLocator) throws InterruptedException {
453            waitForElementVisible(selenium, elementLocator, WAIT_DEFAULT_SECONDS, "");
454        }
455    
456        /**
457         * Wait 60 seconds for the elementLocator to be present or fail including the given message
458         * @param selenium
459         * @param elementLocator
460         * @param message
461         * @throws InterruptedException
462         */
463        public static void waitForElementVisible(Selenium selenium, String elementLocator, String message) throws InterruptedException {
464            waitForElementVisible(selenium, elementLocator, WAIT_DEFAULT_SECONDS, message);
465        }
466    
467        /**
468         * Wait the given seconds for the elementLocator to be present or fail
469         * @param selenium
470         * @param elementLocator
471         * @param seconds
472         * @throws InterruptedException
473         */
474        public static void waitForElementVisible(Selenium selenium, String elementLocator, int seconds, String message) throws InterruptedException {
475            for (int second = 0;; second++) {
476                if (second >= seconds) fail("timeout of " + seconds + " seconds waiting for " + elementLocator + " " + message);
477                try { if (selenium.isVisible(elementLocator)) break; } catch (Exception e) {}
478                Thread.sleep(1000);
479            }
480        }
481    
482        /**
483         * Wait for 60 seconds for the selenium.getTitle to match the given title then fail.
484         * @param selenium
485         * @param title
486         * @throws InterruptedException
487         */
488        public static void waitForTitleToEqual(Selenium selenium, String title) throws InterruptedException {
489            waitForTitleToEqual(selenium, title, "");
490        }
491    
492        /**
493         * Wait for 60 seconds for the selenium.getTitle to match the given title then fail including the given message.
494         * @param selenium
495         * @param title
496         * @param message
497         * @throws InterruptedException
498         */
499        public static void waitForTitleToEqual(Selenium selenium, String title, String message) throws InterruptedException {
500            for (int second = 0;; second++) {
501                if (second >= WAIT_DEFAULT_SECONDS) fail(("timeout of " + WAIT_DEFAULT_SECONDS + " seconds waiting for title to equal " + title + " " + message).trim());
502                try { if (title.equals(selenium.getTitle())) break; } catch (Exception e) {}
503                Thread.sleep(1000);
504            }
505        }
506    
507        /**
508         * Check the selenium contents for an Incident Report failure with Incident Report Details
509         * @param selenium
510         * @param linkLocator
511         */
512        public static void checkForIncidentReport(Selenium selenium, String linkLocator) {
513            checkForIncidentReport(selenium, linkLocator, "");
514        }
515    
516        /**
517         * Fails if a Incident Report is detected, extracting and reporting the View Id, Document Id, and StackTrace
518         * @param selenium
519         * @param linkLocator used only in the failure message
520         */
521        public static void checkForIncidentReport(Selenium selenium, String linkLocator, String message) {
522            selenium.waitForPageToLoad(DEFAULT_WAIT_FOR_PAGE_TO_LOAD_TIMEOUT);
523            String contents = selenium.getHtmlSource();
524            checkForIncidentReport(contents, linkLocator, message);
525        }
526    
527        protected static void checkForIncidentReport(String contents, String linkLocator, String message) {
528            if (contents != null &&
529                    contents.contains("Incident Report") &&
530                    !contents.contains("portal.do?channelTitle=Incident%20Report&amp;") && // Incident Report link on sampleapp KRAD tab
531                    !contents.contains("SeleniumException")) { // selenium timeouts have Incident Report in them
532                try {
533                    if (contents.indexOf("Incident Feedback") > -1) {
534                        Iterator<String> iter = jiraMatches.keySet().iterator();
535                        String key = null;
536                        while (iter.hasNext()) {
537                            key = iter.next();
538                            if (contents.contains(key)) {
539                                Assert.fail("https://jira.kuali.org/browse/" + jiraMatches.get(key));
540                            }
541                        }
542    
543                        String chunk =  contents.substring(contents.indexOf("Incident Feedback"), contents.lastIndexOf("</div>") );
544                        String docId = chunk.substring(chunk.lastIndexOf("Document Id"), chunk.indexOf("View Id"));
545                        docId = docId.substring(0, docId.indexOf("</span>"));
546                        docId = docId.substring(docId.lastIndexOf(">") + 2, docId.length());
547    
548                        String viewId = chunk.substring(chunk.lastIndexOf("View Id"), chunk.indexOf("Error Message"));
549                        viewId = viewId.substring(0, viewId.indexOf("</span>"));
550                        viewId = viewId.substring(viewId.lastIndexOf(">") + 2, viewId.length());
551    
552                        String stackTrace = chunk.substring(chunk.lastIndexOf("(only in dev mode)"), chunk.length());
553                        stackTrace = stackTrace.substring(stackTrace.indexOf("<span id=\"") + 3, stackTrace.length());
554                        stackTrace = stackTrace.substring(stackTrace.indexOf("\">") + 2, stackTrace.indexOf("</span>"));
555    
556                        //            System.out.println(docId);
557                        //            System.out.println(viewId);
558                        //            System.out.println(stackTrace);
559                        Assert.fail("\nIncident report " + message + " navigating to "
560                                + linkLocator
561                                + " : View Id: "
562                                + viewId.trim()
563                                + " Doc Id: "
564                                + docId.trim()
565                                + "\nStackTrace: "
566                                + stackTrace.trim());
567                    } else {
568                        Assert.fail("\nIncident report detected " + message + "\nContents that triggered exception: " + deLinespace(contents));
569                    }
570                } catch (IndexOutOfBoundsException e) {
571                    Assert.fail("\nIncident report detected " + message + " but there was an exception during processing: " + e.getMessage() + "\nStack Trace from processing exception" + stackTrace(e) + "\nContents that triggered exception: " + deLinespace(
572                            contents));
573                }
574            } else {
575                if (contents.contains("HTTP Status 404")) {
576                    Assert.fail("\nHTTP Status 404 " + linkLocator + " " + message + " " + "\ncontents:" + contents);
577                }
578            }
579        }
580    
581        private static String deLinespace(String contents) {
582            while (contents.contains("\n\n")) {
583                contents = contents.replace("\n\n", "\n");
584            }
585            return contents;
586        }
587    }