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    
029    /**
030     * TODO:
031     * <ol>
032     *   <li>Keep WebDriver dependencies out of this class, those should be in {@link WebDriverUtil}</li>
033     *   <li>Keep JUnit or TestNG dependencies out of in this class.</li>
034     *   <li>Extract Hub specific logic?/li>
035     *   <li>Rename to SmokeTestUtil or such</li>
036     * </ol>
037     * @author Kuali Rice Team (rice.collab@kuali.org)
038     */
039    public class ITUtil {
040    
041        /**
042         * http://localhost:8080/kr-dev
043         */
044        public static final String DEFAULT_BASE_URL = "http://localhost:8080/kr-dev";
045    
046        /**
047         * //div[@class='error']"
048         */
049        public static final String DIV_ERROR_LOCATOR = "//div[@class='error']";
050    
051        /**
052         * //div[@class='msg-excol']
053         */
054        public static final String DIV_EXCOL_LOCATOR = "//div[@class='msg-excol']";
055    
056        /**
057         * remote.driver.dontTearDown
058         */
059        public static final String DONT_TEAR_DOWN_PROPERTY = "remote.driver.dontTearDown";
060    
061        /**
062         * Calendar.getInstance().getTimeInMillis() + ""
063         */
064        public static final String DTS = Calendar.getInstance().getTimeInMillis() + "";
065    
066        /**
067         * Calendar.getInstance().getTimeInMillis() + "" + RandomStringUtils.randomAlphabetic(2).toLowerCase()
068         * @Deprecated {@link ITUtil#createUniqueDtsPlusTwoRandomChars()}
069         */
070        public static final String DTS_TWO = Calendar.getInstance().getTimeInMillis() + "" + RandomStringUtils.randomAlphabetic(2).toLowerCase();
071    
072        /**
073         *  &hideReturnLink=true
074         */
075        public static final String HIDE_RETURN_LINK =  "&hideReturnLink=true";
076    
077        /**
078         * remote.public.hub
079         */
080        public static final String HUB_PROPERTY = "remote.public.hub";
081    
082        /**
083         * remote.public.driver
084         */
085        public static final String HUB_DRIVER_PROPERTY = "remote.public.driver";
086    
087        /**
088         * http://localhost:4444/wd/hub
089         */
090        public static final String HUB_URL_PROPERTY = "http://localhost:4444/wd/hub";
091    
092        /**
093         * /kr-krad/lookup?methodToCall=start&dataObjectClassName=
094         */
095        public static final String KRAD_LOOKUP_METHOD =  "/kr-krad/lookup?methodToCall=start&dataObjectClassName=";
096    
097        /**
098         * /kr/lookup.do?methodToCall=start&businessObjectClassName=
099         */
100        public static final String KNS_LOOKUP_METHOD =  "/kr/lookup.do?methodToCall=start&businessObjectClassName=";
101    
102        /**
103         * /kr-krad/kradsampleapp?viewId=KradSampleAppHome
104         */
105        public static final String KRAD_PORTAL = "/kr-krad/kradsampleapp?viewId=KradSampleAppHome";
106    
107        /**
108         * /kr-krad/kradsampleapp?viewId=KradSampleAppHome
109         */
110        public static final String KRAD_PORTAL_URL = ITUtil.getBaseUrlString() + KRAD_PORTAL;
111    
112        /**
113         * /kr-krad/labs?viewId=LabsMenuView
114         */
115        public static final  String LABS = "/kr-krad/labs?viewId=LabsMenuView";
116    
117        /**
118         * ITUtil.getBaseUrlString() + LABS
119         */
120        public static final String LABS_URL = ITUtil.getBaseUrlString() + LABS;
121    
122        /**
123         * /portal.do
124         */
125        public static final String PORTAL = "/portal.do";
126    
127        /**
128         * ITUtil.getBaseUrlString() + ITUtil.PORTAL
129         */
130        public static final String PORTAL_URL =  ITUtil.getBaseUrlString() + ITUtil.PORTAL;
131    
132        /**
133         * URLEncoder.encode(PORTAL_URL)
134         */
135        public static final String PORTAL_URL_ENCODED = URLEncoder.encode(PORTAL_URL);
136    
137        /**
138         *  &showMaintenanceLinks=true
139         */
140        public static final String SHOW_MAINTENANCE_LINKS =  "&showMaintenanceLinks=true";
141    
142        /**
143         * remote.public.url
144         */
145        public static final String REMOTE_PUBLIC_URL_PROPERTY = "remote.public.url";
146    
147        /**
148         * remote.autologin
149         */
150        public static final String REMOTE_AUTOLOGIN_PROPERTY = "remote.autologin";
151    
152        /**
153         * KRAD
154         */
155        public static final String REMOTE_UIF_KRAD = "KRAD";
156    
157        /**
158         * KNS
159         */
160        public static final String REMOTE_UIF_KNS  = "KNS";
161    
162        /**
163         * &docFormKey=
164         */
165        public static final String DOC_FORM_KEY = "&docFormKey=";
166        
167    
168        public static String blanketApprovalCleanUpErrorText(String errorText) {
169            errorText = errorText.replace("* required field", "").replace("\n", " ").trim(); // bit of extra ui text we don't care about
170            return errorText;
171        }
172    
173        public static String createUniqueDtsPlusTwoRandomChars() {
174            return Calendar.getInstance().getTimeInMillis() + "" + RandomStringUtils.randomAlphabetic(2).toLowerCase();
175        }
176    
177        // The Document Description contains 9 continuous digits or 9 digits grouped in the following pattern: ###-##-####, which may represent a Tax Number.
178        // The Document Description is not secure and its contents may be viewed by other application users. Please revise the Document Description to not contain digits in those patterns.
179        public static String createUniqueDtsPlusTwoRandomCharsNot9Digits() {
180            String dtsTwo = ITUtil.createUniqueDtsPlusTwoRandomChars();
181            dtsTwo = dtsTwo.substring(0, 5) + dtsTwo.substring(13, 14) + dtsTwo.substring(6, 12);
182            return dtsTwo;
183        }
184    
185        protected static void checkForIncidentReport(String contents, String linkLocator, Failable failable, String message) {
186            if (contents == null) { //guard clause
187                return;
188            }
189    
190            if (incidentReported(contents)) {
191                try {
192                    processIncidentReport(contents, linkLocator, failable, message);
193                } catch (IndexOutOfBoundsException e) {
194                    failable.fail(
195                            "\nIncident report detected "
196                                    + message
197                                    + " but there was an exception during processing: "
198                                    + e.getMessage()
199                                    + "\nStack Trace from processing exception"
200                                    + stackTrace(e)
201                                    + "\nContents that triggered exception: "
202                                    + deLinespace(contents));
203                }
204            }
205    
206            if (contents.contains("HTTP Status 404")) {
207                failable.fail("\nHTTP Status 404 " + linkLocator + " " + message + " " + "\ncontents:" + contents);
208            }
209    
210            if (contents.contains("Java backtrace for programmers:")) { // freemarker exception
211                try {
212                    processFreemarkerException(contents, linkLocator, failable, message);
213                } catch (IndexOutOfBoundsException e) {
214                    failable.fail("\nFreemarker exception detected "
215                            + message
216                            + " but there was an exception during processing: "
217                            + e.getMessage()
218                            + "\nStack Trace from processing exception"
219                            + stackTrace(e)
220                            + "\nContents that triggered exception: "
221                            + deLinespace(contents));
222                }
223            }
224    
225            if (contents.contains("Document Expired")) { // maybe Firefox specific
226                failable.fail("Document Expired message.");
227            }
228        }
229    
230        public static String deLinespace(String contents) {
231            while (contents.contains("\n\n")) {
232                contents = contents.replaceAll("\n\n", "\n");
233            }
234            return contents;
235        }
236    
237        /**
238         * 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.
239         * 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.
240         * {@code }
241         * @return true if the dontTearDownProperty is not set.
242         */
243        public static boolean dontTearDownPropertyNotSet() {
244            return System.getProperty(DONT_TEAR_DOWN_PROPERTY) == null ||
245                    "f".startsWith(System.getProperty(DONT_TEAR_DOWN_PROPERTY).toLowerCase()) ||
246                    "n".startsWith(System.getProperty(DONT_TEAR_DOWN_PROPERTY).toLowerCase());
247        }
248    
249        private static String extractIncidentReportInfo(String contents, String linkLocator, String message) {
250            String chunk =  contents.substring(contents.indexOf("Incident Feedback"), contents.lastIndexOf("</div>") );
251            String docId = chunk.substring(chunk.lastIndexOf("Document Id"), chunk.indexOf("View Id"));
252            docId = docId.substring(0, docId.indexOf("</span>"));
253            docId = docId.substring(docId.lastIndexOf(">") + 2, docId.length());
254    
255            String viewId = chunk.substring(chunk.lastIndexOf("View Id"), chunk.indexOf("Error Message"));
256            viewId = viewId.substring(0, viewId.indexOf("</span>"));
257            viewId = viewId.substring(viewId.lastIndexOf(">") + 2, viewId.length());
258    
259            String stackTrace = chunk.substring(chunk.lastIndexOf("(only in dev mode)"), chunk.length());
260            stackTrace = stackTrace.substring(stackTrace.indexOf("<pre>") + 5, stackTrace.length());
261            stackTrace = stackTrace.substring(0, stackTrace.indexOf("</"));
262    
263            //            System.out.println(docId);
264            //            System.out.println(viewId);
265            //            System.out.println(stackTrace);
266            return "\nIncident report "
267                    + message
268                    + " navigating to "
269                    + linkLocator
270                    + " : View Id: "
271                    + viewId.trim()
272                    + " Doc Id: "
273                    + docId.trim()
274                    + "\nStackTrace: "
275                    + stackTrace.trim().replace(" at ", "");
276        }
277    
278        private static String extractIncidentReportKim(String contents, String linkLocator, String message) {
279            String chunk =  contents.substring(contents.indexOf("id=\"headerarea\""), contents.lastIndexOf("</div>") );
280            String docIdPre = "type=\"hidden\" value=\"";
281            String docId = chunk.substring(chunk.indexOf(docIdPre) + docIdPre.length(), chunk.indexOf("\" name=\"documentId\""));
282    
283            String stackTrace = chunk.substring(chunk.lastIndexOf("name=\"displayMessage\""), chunk.length());
284            String stackTracePre = "value=\"";
285            stackTrace = stackTrace.substring(stackTrace.indexOf(stackTracePre) + stackTracePre.length(), stackTrace.indexOf("name=\"stackTrace\"") - 2);
286    
287            return "\nIncident report "
288                    + message
289                    + " navigating to "
290                    + linkLocator
291                    + " Doc Id: "
292                    + docId.trim()
293                    + "\nStackTrace: "
294                    + stackTrace.trim().replace(" at ", "");
295        }
296    
297        public static void failOnInvalidUserName(String userName, String contents, Failable failable) {
298            if (contents.indexOf("Invalid username") > -1) {
299                failable.fail("Invalid username " + userName);
300            }
301        }
302    /*
303        public static void failOnMatchedJira(String contents) {
304            Iterator<String> iter = jiraMatches.keySet().iterator();
305            String key = null;
306    
307            while (iter.hasNext()) {
308                key = iter.next();
309                if (contents.contains(key)) {
310                    SeleneseTestBase.fail(JIRA_BROWSE_URL + jiraMatches.get(key));
311                }
312            }
313        }
314    */
315    
316        private static void failWithReportInfo(String contents, String linkLocator, Failable failable, String message) {
317            final String incidentReportInformation = extractIncidentReportInfo(contents, linkLocator, message);
318            failable.fail(incidentReportInformation);
319        }
320    
321    /*
322        private static void failWithReportInfoForKim(String contents, String linkLocator, String message) {
323            final String kimIncidentReport = extractIncidentReportKim(contents, linkLocator, message);
324            SeleneseTestBase.fail(kimIncidentReport);
325        }
326    */
327        private static void failWithReportInfoForKim(String contents, String linkLocator, Failable failable, String message) {
328            final String kimIncidentReport = extractIncidentReportKim(contents, linkLocator, message);
329            failable.fail(kimIncidentReport);
330        }
331    
332        /**
333         * In order to run as a smoke test the ability to set the baseUrl via the JVM arg remote.public.url is required.
334         * Trailing slashes are trimmed.  If the remote.public.url does not start with http:// it will be added.
335         * @return http://localhost:8080/kr-dev by default else the value of remote.public.url
336         */
337        public static String getBaseUrlString() {
338            String baseUrl = System.getProperty(REMOTE_PUBLIC_URL_PROPERTY);
339            if (baseUrl == null) {
340                baseUrl = DEFAULT_BASE_URL;
341            }
342            baseUrl = prettyHttp(baseUrl);
343            return baseUrl;
344        }
345    
346        public static String getHTML(String urlToRead) {
347            URL url;
348            HttpURLConnection conn;
349            BufferedReader rd;
350            String line;
351            String result = "";
352    
353            try {
354                url = new URL(urlToRead);
355                conn = (HttpURLConnection) url.openConnection();
356                conn.setRequestMethod("GET");
357                rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
358                while ((line = rd.readLine()) != null) {
359                    result += line;
360                }
361                rd.close();
362            } catch (Exception e) {
363                e.printStackTrace();
364            }
365    
366            return result;
367        }
368    
369        /**
370         * 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.
371         * Trailing slashes are trimmed.  If the remote.public.hub does not start with http:// it will be added.
372         * @return http://localhost:4444/wd/hub by default else the value of remote.public.hub
373         */
374        public static String getHubUrlString() {
375            String hubUrl = System.getProperty(HUB_PROPERTY);
376            if (hubUrl == null) {
377                hubUrl = HUB_URL_PROPERTY;
378            }
379            hubUrl = prettyHttp(hubUrl);
380            if (!hubUrl.endsWith("/wd/hub")) {
381                hubUrl = hubUrl + "/wd/hub";
382            }
383            return hubUrl;
384        }
385    
386        private static boolean incidentReported(String contents) {
387            return contents != null &&
388                    contents.contains("Incident Report") &&
389                    !contents.contains("portal.do?channelTitle=Incident%20Report") && // Incident Report link on sampleapp KRAD tab
390                    !contents.contains("portal.do?channelTitle=Incident Report") &&   // Incident Report link on sampleapp KRAD tab IE8
391                    !contents.contains("uitest?viewId=Travel-testView2") &&
392                    !contents.contains("SeleniumException"); // selenium timeouts have Incident Report in them
393        }
394    
395        /**
396         * Append http:// if not present.  Remove trailing /
397         * @param baseUrl
398         * @return
399         */
400        public static String prettyHttp(String baseUrl) {
401    
402            if (baseUrl.endsWith("/")) {
403                baseUrl = baseUrl.substring(0, baseUrl.length() - 1);
404            }
405    
406            if (!baseUrl.startsWith("http")) {
407                baseUrl = "http://" + baseUrl;
408            }
409    
410            return baseUrl;
411        }
412    
413        private static void processFreemarkerException(String contents, String linkLocator, Failable failable, String message) {
414            JiraAwareFailureUtil.failOnMatchedJira(contents, failable);
415            String stackTrace = contents.substring(contents.indexOf("Error: on line"), contents.indexOf("more<") - 1);
416            failable.fail(
417                    "\nFreemarker Exception " + message + " navigating to " + linkLocator + "\nStackTrace: " + stackTrace
418                            .trim().replace(" at ", ""));
419        }
420    
421    /*
422        private static void processIncidentReport(String contents, String linkLocator, String message) {
423            failOnMatchedJira(contents);
424    
425            if (contents.indexOf("Incident Feedback") > -1) {
426                failWithReportInfo(contents, linkLocator, message);
427            }
428    
429            if (contents.indexOf("Incident Report") > -1) { // KIM incident report
430                failWithReportInfoForKim(contents, linkLocator, message);
431            }
432    
433            SeleneseTestBase.fail("\nIncident report detected " + message + "\n Unable to parse out details for the contents that triggered exception: " + deLinespace(
434                    contents));
435        }
436    
437        private static void failWithReportInfo(String contents, String linkLocator, String message) {
438            final String incidentReportInformation = extractIncidentReportInfo(contents, linkLocator, message);
439            SeleneseTestBase.fail(incidentReportInformation);
440        }
441    */
442    
443        protected static void processIncidentReport(String contents, String linkLocator, Failable failable, String message) {
444            JiraAwareFailureUtil.failOnMatchedJira(contents, failable);
445    
446            if (contents.indexOf("Incident Feedback") > -1) {
447                failWithReportInfo(contents, linkLocator, failable, message);
448            }
449    
450            if (contents.indexOf("Incident Report") > -1) { // KIM incident report
451                failWithReportInfoForKim(contents, linkLocator, failable, message);
452            }
453    
454            failable.fail("\nIncident report detected "
455                    + message
456                    + "\n Unable to parse out details for the contents that triggered exception: "
457                    + deLinespace(contents));
458        }
459    
460        /**
461         * Write the given stack trace into a String remove the ats in an attempt to not cause Jenkins problems.
462         * @param throwable whose stack trace to return
463         * @return String of the given throwable's stack trace.
464         */
465        public static String stackTrace(Throwable throwable) {
466            StringWriter wrt = new StringWriter();
467            PrintWriter pw = new PrintWriter(wrt);
468            throwable.printStackTrace(pw);
469            pw.flush();
470            return wrt.toString().replace(" at " ,"");
471        }
472    }