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