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 */
016package edu.samplu.common;
017
018import org.apache.commons.lang.RandomStringUtils;
019
020import java.io.BufferedReader;
021import java.io.InputStreamReader;
022import java.io.PrintWriter;
023import java.io.StringWriter;
024import java.net.HttpURLConnection;
025import java.net.URL;
026import java.net.URLEncoder;
027import 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 */
039public 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}