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