View Javadoc

1   /**
2    * Copyright 2005-2012 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */package edu.samplu.common;
16  
17  import com.thoughtworks.selenium.Selenium;
18  import org.apache.commons.lang.RandomStringUtils;
19  import org.junit.Assert;
20  import org.openqa.selenium.By;
21  import org.openqa.selenium.WebDriver;
22  import org.openqa.selenium.chrome.ChromeDriver;
23  import org.openqa.selenium.firefox.FirefoxDriver;
24  import org.openqa.selenium.firefox.FirefoxProfile;
25  import org.openqa.selenium.remote.DesiredCapabilities;
26  import org.openqa.selenium.remote.RemoteWebDriver;
27  
28  import java.io.PrintWriter;
29  import java.io.StringWriter;
30  import java.net.MalformedURLException;
31  import java.net.URL;
32  import java.util.Calendar;
33  import java.util.HashMap;
34  import java.util.Iterator;
35  import java.util.Map;
36  
37  import static com.thoughtworks.selenium.SeleneseTestBase.fail;
38  import static org.junit.Assert.assertEquals;
39  
40  /**
41   * Common selenium test methods that should be reused rather than recreated for each test.
42   * @author Kuali Rice Team (rice.collab@kuali.org)
43   */
44  
45  public class ITUtil {
46  
47      public static final String KUALI_PORTAL_TITLE = "Kuali Portal Index";
48      public final static String PORTAL = "/portal.do";
49      public static final String DTS = Calendar.getInstance().getTimeInMillis() + "";
50      public static final String DTS_TWO = Calendar.getInstance().getTimeInMillis() + "" + RandomStringUtils.randomAlphabetic(2).toLowerCase();
51      public static String WAIT_TO_END_TEST = "5000";
52      public static final String DIV_ERROR_LOCATOR = "//div[@class='error']";
53      public static final String DIV_EXCOL_LOCATOR = "//div[@class='msg-excol']";
54      public static final int WAIT_DEFAULT_SECONDS = 60;
55      public static final String DEFAULT_WAIT_FOR_PAGE_TO_LOAD_TIMEOUT = "30000";
56      static Map<String, String> jiraMatches;
57      public static final String REMOTE_AUTOLOGIN_PROPERTY = "remote.autologin";
58  
59      static {
60          jiraMatches = new HashMap<String, String>();
61          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?",
62                  "KULRICE-8137 Agenda Rule edit Incident report Invalid property 'refreshWhenChanged'");
63  
64          jiraMatches.put("org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase.processAddCollectionLineBusinessRules(MaintenanceDocumentRuleBase.",
65                  "KULRICE-8142 NPE in MaintenanceDocumentRuleBase.processAddCollectionLineBusinessRules");
66  
67          jiraMatches.put("at org.kuali.rice.krad.rules.DocumentRuleBase.isDocumentOverviewValid(DocumentRuleBase.",
68                  "KULRICE-8134 NPE in DocumentRuleBase.isDocumentOverviewValid(DocumentRuleBase");
69  
70          jiraMatches.put("org.kuali.rice.krad.uif.layout.TableLayoutManager.buildLine(TableLayoutManager.",
71                  "KULRICE-8160 NPE at TableLayoutManager.buildLine(TableLayoutManager");
72  
73          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?",
74                  "KULRICE-8173 Bean property 'configFileLocations' is not writable or has an invalid setter method");
75  
76          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?",
77                  "KULRICE-8182 JDK7 Bean property 'componentSecurity' is not readable...");
78  
79          jiraMatches.put("java.sql.SQLSyntaxErrorException: ORA-00904: \"ROUTEHEADERID\": invalid identifier",
80                  "KULRICE-8277 Several ITs fail with OJB operation; bad SQL grammar []; nested exception is java.sql.SQLException: ORA-00904: \"ROUTEHEADERID\": invalid identifier");
81  //        jiraMatches.put("",
82  //                "");
83  
84      }
85  
86      /**
87       * "FINAL", selenium.getText("//table[@id='row']/tbody/tr[1]/td[4]"
88       * @param selenium
89       * @param docId
90       */
91      public static void assertDocFinal(Selenium selenium, String docId) {
92          docId= "link=" + docId;
93          if(selenium.isElementPresent(docId)){
94              assertEquals("FINAL", selenium.getText("//table[@id='row']/tbody/tr[1]/td[4]"));
95          }else{
96              assertEquals(docId, selenium.getText("//table[@id='row']/tbody/tr[1]/td[1]"));
97              assertEquals("FINAL", selenium.getText("//table[@id='row']/tbody/tr[1]/td[4]"));
98          }
99      }
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 }