001 /** 002 * Copyright 2005-2013 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 */ 016 package edu.samplu.common; 017 018 import org.apache.commons.lang.RandomStringUtils; 019 020 import java.io.BufferedReader; 021 import java.io.InputStreamReader; 022 import java.io.PrintWriter; 023 import java.io.StringWriter; 024 import java.net.HttpURLConnection; 025 import java.net.URL; 026 import java.net.URLEncoder; 027 import java.util.Calendar; 028 import java.util.HashMap; 029 import java.util.Iterator; 030 import java.util.Map; 031 032 /** 033 * TODO: 034 * <ol> 035 * <li>Keep JUnit or TestNG dependencies out of in this class.</li> 036 * <li>Once and Only Once WAIT_DEFAULT_SECONDS and WebDriverLegacyITBase.DEFAULT_WAIT_SEC</li> 037 * <li>Rename to SmokeTestUtil or such</li> 038 * <li>Extract jiraMatches data to property file</li> 039 * </ol> 040 * @author Kuali Rice Team (rice.collab@kuali.org) 041 */ 042 043 public class ITUtil { 044 045 /** 046 * http://localhost:8080/kr-dev 047 */ 048 public static final String DEFAULT_BASE_URL = "http://localhost:8080/kr-dev"; 049 050 /** 051 * /portal.do 052 */ 053 public static final String PORTAL = "/portal.do"; 054 055 /** 056 * ITUtil.getBaseUrlString() + ITUtil.PORTAL 057 */ 058 public static final String PORTAL_URL = ITUtil.getBaseUrlString() + ITUtil.PORTAL; 059 060 /** 061 * URLEncoder.encode(PORTAL_URL) 062 */ 063 public static final String PORTAL_URL_ENCODED = URLEncoder.encode(PORTAL_URL); 064 065 /** 066 * Calendar.getInstance().getTimeInMillis() + "" 067 */ 068 public static final String DTS = Calendar.getInstance().getTimeInMillis() + ""; 069 070 /** 071 * Calendar.getInstance().getTimeInMillis() + "" + RandomStringUtils.randomAlphabetic(2).toLowerCase() 072 */ 073 public static final String DTS_TWO = Calendar.getInstance().getTimeInMillis() + "" + RandomStringUtils.randomAlphabetic(2).toLowerCase(); 074 075 /** 076 * //div[@class='error']" 077 */ 078 public static final String DIV_ERROR_LOCATOR = "//div[@class='error']"; 079 080 /** 081 * //div[@class='msg-excol'] 082 */ 083 public static final String DIV_EXCOL_LOCATOR = "//div[@class='msg-excol']"; 084 085 /** 086 * 60 087 */ 088 public static final int WAIT_DEFAULT_SECONDS = 60; 089 090 /** 091 * "30000" 092 */ 093 public static final String DEFAULT_WAIT_FOR_PAGE_TO_LOAD_TIMEOUT = "30000"; 094 095 /** 096 * remote.public.url 097 */ 098 public static final String REMOTE_PUBLIC_URL_PROPERTY = "remote.public.url"; 099 100 /** 101 * remote.autologin 102 */ 103 public static final String REMOTE_AUTOLOGIN_PROPERTY = "remote.autologin"; 104 105 /** 106 * remote.public.hub 107 */ 108 public static final String HUB_PROPERTY = "remote.public.hub"; 109 110 /** 111 * remote.public.driver 112 */ 113 public static final String HUB_DRIVER_PROPERTY = "remote.public.driver"; 114 115 /** 116 * http://localhost:4444/wd/hub 117 */ 118 public static final String HUB_URL_PROPERTY = "http://localhost:4444/wd/hub"; 119 120 /** 121 * remote.driver.dontTearDown 122 */ 123 public static final String DONT_TEAR_DOWN_PROPERTY = "remote.driver.dontTearDown"; 124 125 /** 126 * https://jira.kuali.org/browse/ 127 */ 128 public static final String JIRA_BROWSE_URL = "https://jira.kuali.org/browse/"; 129 static Map<String, String> jiraMatches; 130 static { 131 jiraMatches = new HashMap<String, String>(); 132 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?", 133 "KULRICE-8137 Agenda Rule edit Incident report Invalid property 'refreshWhenChanged'"); 134 135 jiraMatches.put("org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase.processAddCollectionLineBusinessRules(MaintenanceDocumentRuleBase.", 136 "KULRICE-8142 NPE in MaintenanceDocumentRuleBase.processAddCollectionLineBusinessRules"); 137 138 jiraMatches.put("at org.kuali.rice.krad.rules.DocumentRuleBase.isDocumentOverviewValid(DocumentRuleBase.", 139 "KULRICE-8134 NPE in DocumentRuleBase.isDocumentOverviewValid(DocumentRuleBase"); 140 141 jiraMatches.put("org.kuali.rice.krad.uif.layout.TableLayoutManager.buildLine(TableLayoutManager.", 142 "KULRICE-8160 NPE at TableLayoutManager.buildLine(TableLayoutManager"); 143 144 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?", 145 "KULRICE-8173 Bean property 'configFileLocations' is not writable or has an invalid setter method"); 146 147 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?", 148 "KULRICE-8182 JDK7 Bean property 'componentSecurity' is not readable..."); 149 150 jiraMatches.put("java.sql.SQLSyntaxErrorException: ORA-00904: \"ROUTEHEADERID\": invalid identifier", 151 "KULRICE-8277 Several ITs fail with OJB operation; bad SQL grammar []; nested exception is java.sql.SQLException: ORA-00904: \"ROUTEHEADERID\": invalid identifier"); 152 153 jiraMatches.put("By.xpath: //button[@data-loadingmessage='Adding Line...']", 154 "KULRICE-9044 KRAD \"stacked\" collection elements are not rendering add/delete buttons "); 155 156 jiraMatches.put("Error: on line 135, column 39 in krad/WEB-INF/ftl/lib/grid.ftl", 157 "KULRICE-9047 Term maintenance freemarker exception "); 158 159 // jiraMatches.put("", 160 // ""); 161 162 } 163 164 public static String blanketApprovalCleanUpErrorText(String errorText) { 165 errorText = errorText.replace("* required field", "").replace("\n", " ").trim(); // bit of extra ui text we don't care about 166 return errorText; 167 } 168 169 protected static void checkForIncidentReport(String contents, String linkLocator, Failable failable, String message) { 170 if (contents == null) { //guard clause 171 return; 172 } 173 174 if (incidentReported(contents)) { 175 try { 176 processIncidentReport(contents, linkLocator, failable, message); 177 } catch (IndexOutOfBoundsException e) { 178 failable.fail( 179 "\nIncident report detected " 180 + message 181 + " but there was an exception during processing: " 182 + e.getMessage() 183 + "\nStack Trace from processing exception" 184 + stackTrace(e) 185 + "\nContents that triggered exception: " 186 + deLinespace(contents)); 187 } 188 } 189 190 if (contents.contains("HTTP Status 404")) { 191 failable.fail("\nHTTP Status 404 " + linkLocator + " " + message + " " + "\ncontents:" + contents); 192 } 193 194 if (contents.contains("Java backtrace for programmers:")) { // freemarker exception 195 try { 196 processFreemarkerException(contents, linkLocator, failable, message); 197 } catch (IndexOutOfBoundsException e) { 198 failable.fail("\nFreemarker exception detected " 199 + message 200 + " but there was an exception during processing: " 201 + e.getMessage() 202 + "\nStack Trace from processing exception" 203 + stackTrace(e) 204 + "\nContents that triggered exception: " 205 + deLinespace(contents)); 206 } 207 208 } 209 } 210 211 public static String deLinespace(String contents) { 212 while (contents.contains("\n\n")) { 213 contents = contents.replaceAll("\n\n", "\n"); 214 } 215 return contents; 216 } 217 218 /** 219 * 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. 220 * 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. 221 * {@code } 222 * @return true if the dontTearDownProperty is not set. 223 */ 224 public static boolean dontTearDownPropertyNotSet() { 225 return System.getProperty(DONT_TEAR_DOWN_PROPERTY) == null || 226 "f".startsWith(System.getProperty(DONT_TEAR_DOWN_PROPERTY).toLowerCase()) || 227 "n".startsWith(System.getProperty(DONT_TEAR_DOWN_PROPERTY).toLowerCase()); 228 } 229 230 private static String extractIncidentReportInfo(String contents, String linkLocator, String message) { 231 String chunk = contents.substring(contents.indexOf("Incident Feedback"), contents.lastIndexOf("</div>") ); 232 String docId = chunk.substring(chunk.lastIndexOf("Document Id"), chunk.indexOf("View Id")); 233 docId = docId.substring(0, docId.indexOf("</span>")); 234 docId = docId.substring(docId.lastIndexOf(">") + 2, docId.length()); 235 236 String viewId = chunk.substring(chunk.lastIndexOf("View Id"), chunk.indexOf("Error Message")); 237 viewId = viewId.substring(0, viewId.indexOf("</span>")); 238 viewId = viewId.substring(viewId.lastIndexOf(">") + 2, viewId.length()); 239 240 String stackTrace = chunk.substring(chunk.lastIndexOf("(only in dev mode)"), chunk.length()); 241 stackTrace = stackTrace.substring(stackTrace.indexOf("<span id=\"") + 3, stackTrace.length()); 242 stackTrace = stackTrace.substring(stackTrace.indexOf("\">") + 2, stackTrace.indexOf("</span>")); 243 244 // System.out.println(docId); 245 // System.out.println(viewId); 246 // System.out.println(stackTrace); 247 return "\nIncident report " 248 + message 249 + " navigating to " 250 + linkLocator 251 + " : View Id: " 252 + viewId.trim() 253 + " Doc Id: " 254 + docId.trim() 255 + "\nStackTrace: " 256 + stackTrace.trim(); 257 } 258 259 private static String extractIncidentReportKim(String contents, String linkLocator, String message) { 260 String chunk = contents.substring(contents.indexOf("id=\"headerarea\""), contents.lastIndexOf("</div>") ); 261 String docIdPre = "type=\"hidden\" value=\""; 262 String docId = chunk.substring(chunk.indexOf(docIdPre) + docIdPre.length(), chunk.indexOf("\" name=\"documentId\"")); 263 264 String stackTrace = chunk.substring(chunk.lastIndexOf("name=\"displayMessage\""), chunk.length()); 265 String stackTracePre = "value=\""; 266 stackTrace = stackTrace.substring(stackTrace.indexOf(stackTracePre) + stackTracePre.length(), stackTrace.indexOf("name=\"stackTrace\"") - 2); 267 268 return "\nIncident report " 269 + message 270 + " navigating to " 271 + linkLocator 272 + " Doc Id: " 273 + docId.trim() 274 + "\nStackTrace: " 275 + stackTrace.trim(); 276 } 277 278 public static void failOnInvalidUserName(String userName, String contents, Failable failable) { 279 if (contents.indexOf("Invalid username") > -1) { 280 failable.fail("Invalid username " + userName); 281 } 282 } 283 /* 284 public static void failOnMatchedJira(String contents) { 285 Iterator<String> iter = jiraMatches.keySet().iterator(); 286 String key = null; 287 288 while (iter.hasNext()) { 289 key = iter.next(); 290 if (contents.contains(key)) { 291 SeleneseTestBase.fail(JIRA_BROWSE_URL + jiraMatches.get(key)); 292 } 293 } 294 } 295 */ 296 public static void failOnMatchedJira(String contents, Failable failable) { 297 Iterator<String> iter = jiraMatches.keySet().iterator(); 298 String key = null; 299 300 while (iter.hasNext()) { 301 key = iter.next(); 302 if (contents.contains(key)) { 303 failable.fail(JIRA_BROWSE_URL + jiraMatches.get(key)); 304 } 305 } 306 } 307 308 private static void failWithReportInfo(String contents, String linkLocator, Failable failable, String message) { 309 final String incidentReportInformation = extractIncidentReportInfo(contents, linkLocator, message); 310 failable.fail(incidentReportInformation); 311 } 312 313 /* 314 private static void failWithReportInfoForKim(String contents, String linkLocator, String message) { 315 final String kimIncidentReport = extractIncidentReportKim(contents, linkLocator, message); 316 SeleneseTestBase.fail(kimIncidentReport); 317 } 318 */ 319 private static void failWithReportInfoForKim(String contents, String linkLocator, Failable failable, String message) { 320 final String kimIncidentReport = extractIncidentReportKim(contents, linkLocator, message); 321 failable.fail(kimIncidentReport); 322 } 323 324 /** 325 * In order to run as a smoke test the ability to set the baseUrl via the JVM arg remote.public.url is required. 326 * Trailing slashes are trimmed. If the remote.public.url does not start with http:// it will be added. 327 * @return http://localhost:8080/kr-dev by default else the value of remote.public.url 328 */ 329 public static String getBaseUrlString() { 330 String baseUrl = System.getProperty(REMOTE_PUBLIC_URL_PROPERTY); 331 if (baseUrl == null) { 332 baseUrl = DEFAULT_BASE_URL; 333 } 334 baseUrl = prettyHttp(baseUrl); 335 return baseUrl; 336 } 337 338 public static String getHTML(String urlToRead) { 339 URL url; 340 HttpURLConnection conn; 341 BufferedReader rd; 342 String line; 343 String result = ""; 344 345 try { 346 url = new URL(urlToRead); 347 conn = (HttpURLConnection) url.openConnection(); 348 conn.setRequestMethod("GET"); 349 rd = new BufferedReader(new InputStreamReader(conn.getInputStream())); 350 while ((line = rd.readLine()) != null) { 351 result += line; 352 } 353 rd.close(); 354 } catch (Exception e) { 355 e.printStackTrace(); 356 } 357 358 return result; 359 } 360 361 /** 362 * 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. 363 * Trailing slashes are trimmed. If the remote.public.hub does not start with http:// it will be added. 364 * @return http://localhost:4444/wd/hub by default else the value of remote.public.hub 365 */ 366 public static String getHubUrlString() { 367 String hubUrl = System.getProperty(HUB_PROPERTY); 368 if (hubUrl == null) { 369 hubUrl = HUB_URL_PROPERTY; 370 } 371 hubUrl = prettyHttp(hubUrl); 372 if (!hubUrl.endsWith("/wd/hub")) { 373 hubUrl = hubUrl + "/wd/hub"; 374 } 375 return hubUrl; 376 } 377 378 private static boolean incidentReported(String contents) { 379 return contents != null && 380 contents.contains("Incident Report") && 381 !contents.contains("portal.do?channelTitle=Incident%20Report") && // Incident Report link on sampleapp KRAD tab 382 !contents.contains("portal.do?channelTitle=Incident Report") && // Incident Report link on sampleapp KRAD tab IE8 383 !contents.contains("uitest?viewId=Travel-testView2") && 384 !contents.contains("SeleniumException"); // selenium timeouts have Incident Report in them 385 } 386 387 /** 388 * Append http:// if not present. Remove trailing / 389 * @param baseUrl 390 * @return 391 */ 392 public static String prettyHttp(String baseUrl) { 393 if (baseUrl.endsWith("/")) { 394 baseUrl = baseUrl.substring(0, baseUrl.length() - 1); 395 } 396 if (!baseUrl.startsWith("http")) { 397 baseUrl = "http://" + baseUrl; 398 } 399 return baseUrl; 400 } 401 402 private static void processFreemarkerException(String contents, String linkLocator, Failable failable, String message) { 403 failOnMatchedJira(contents, failable); 404 String stackTrace = contents.substring(contents.indexOf("Error: on line"), contents.indexOf("more<") - 1); 405 failable.fail( 406 "\nFreemarker Exception " + message + " navigating to " + linkLocator + "\nStackTrace: " + stackTrace 407 .trim()); 408 } 409 410 /* 411 private static void processIncidentReport(String contents, String linkLocator, String message) { 412 failOnMatchedJira(contents); 413 414 if (contents.indexOf("Incident Feedback") > -1) { 415 failWithReportInfo(contents, linkLocator, message); 416 } 417 418 if (contents.indexOf("Incident Report") > -1) { // KIM incident report 419 failWithReportInfoForKim(contents, linkLocator, message); 420 } 421 422 SeleneseTestBase.fail("\nIncident report detected " + message + "\n Unable to parse out details for the contents that triggered exception: " + deLinespace( 423 contents)); 424 } 425 426 private static void failWithReportInfo(String contents, String linkLocator, String message) { 427 final String incidentReportInformation = extractIncidentReportInfo(contents, linkLocator, message); 428 SeleneseTestBase.fail(incidentReportInformation); 429 } 430 */ 431 432 protected static void processIncidentReport(String contents, String linkLocator, Failable failable, String message) { 433 failOnMatchedJira(contents, failable); 434 435 if (contents.indexOf("Incident Feedback") > -1) { 436 failWithReportInfo(contents, linkLocator, failable, message); 437 } 438 439 if (contents.indexOf("Incident Report") > -1) { // KIM incident report 440 failWithReportInfoForKim(contents, linkLocator, failable, message); 441 } 442 443 failable.fail("\nIncident report detected " 444 + message 445 + "\n Unable to parse out details for the contents that triggered exception: " 446 + deLinespace(contents)); 447 } 448 449 /** 450 * Write the given stack trace into a String 451 * @param throwable whose stack trace to return 452 * @return String of the given throwable's stack trace. 453 */ 454 public static String stackTrace(Throwable throwable) { 455 StringWriter wrt = new StringWriter(); 456 PrintWriter pw = new PrintWriter(wrt); 457 throwable.printStackTrace(pw); 458 pw.flush(); 459 return wrt.toString(); 460 } 461 462 }