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    }