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 }