001/**
002 * Copyright 2005-2018 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 org.kuali.rice.testtools.selenium;
017
018import org.apache.commons.lang.RandomStringUtils;
019import org.kuali.rice.testtools.common.JiraAwareFailable;
020import org.kuali.rice.testtools.common.JiraAwareFailureUtils;
021
022import java.io.BufferedReader;
023import java.io.InputStreamReader;
024import java.io.PrintWriter;
025import java.io.StringWriter;
026import java.net.HttpURLConnection;
027import java.net.URL;
028import java.net.URLEncoder;
029import java.util.Calendar;
030
031/**
032 * For Rice specific sampleapp testing code.
033 * <ol>
034 *   <li>Keep test framework methods (WebDriver, Assert) dependencies out of this class, those should be in
035 *   {@link WebDriverUtils}</li>
036 *   <li>Move JiraAware calls out of this class, those should be in {@see JiraAwareFailureUtils} or modified to be failed
037 *   in another class</li>
038 * </ol>
039 * @author Kuali Rice Team (rice.collab@kuali.org)
040 */
041public class AutomatedFunctionalTestUtils {
042
043    /**
044     * Calendar.getInstance().getTimeInMillis() + ""
045     */
046    public static final String DTS = Calendar.getInstance().getTimeInMillis() + "";
047
048    /**
049     * Calendar.getInstance().getTimeInMillis() + "" + RandomStringUtils.randomAlphabetic(2).toLowerCase()
050     * @Deprecated {@link AutomatedFunctionalTestUtils#createUniqueDtsPlusTwoRandomChars()}
051     */
052    public static final String DTS_TWO = Calendar.getInstance().getTimeInMillis() + "" + RandomStringUtils.randomAlphabetic(2).toLowerCase();
053
054    /**
055     *  &hideReturnLink=true
056     */
057    public static final String HIDE_RETURN_LINK =  "&hideReturnLink=true";
058
059    /**
060     *  &hideReturnLink=false
061     */
062    public static final String HIDE_RETURN_LINK_FALSE =  "&hideReturnLink=false";
063
064    /**
065     * /kr-krad/lookup?methodToCall=start&dataObjectClassName=
066     */
067    public static final String KRAD_LOOKUP_METHOD =  "/kr-krad/lookup?methodToCall=start&dataObjectClassName=";
068
069    /**
070     * /kr/lookup.do?methodToCall=start&businessObjectClassName=
071     */
072    public static final String KNS_LOOKUP_METHOD =  "/kr/lookup.do?methodToCall=start&businessObjectClassName=";
073
074    /**
075     * /kr-krad/kradsampleapp?viewId=KradSampleAppHome
076     */
077    public static final String KRAD_PORTAL = "/kr-krad/kradsampleapp?viewId=KradSampleAppHome";
078
079    /**
080     * /kr-krad/kradsampleapp?viewId=KradSampleAppHome
081     */
082    public static final String KRAD_PORTAL_URL = WebDriverUtils.getBaseUrlString() + KRAD_PORTAL;
083
084    /**
085     * /kr-krad/labs?viewId=LabsMenuView
086     */
087    public static final  String LABS = "/kr-krad/labs?viewId=LabsMenuView";
088
089    /**
090     * WebDriverUtils.getBaseUrlString() + LABS
091     */
092    public static final String LABS_URL = WebDriverUtils.getBaseUrlString() + LABS;
093
094    /**
095     * /portal.do
096     */
097    public static final String PORTAL = "/portal.do";
098
099    /**
100     * WebDriverUtils.getBaseUrlString() + ITUtil.PORTAL
101     */
102    public static final String PORTAL_URL =  WebDriverUtils.getBaseUrlString() + AutomatedFunctionalTestUtils.PORTAL;
103
104    /**
105     * URLEncoder.encode(PORTAL_URL)
106     */
107    public static final String PORTAL_URL_ENCODED = URLEncoder.encode(PORTAL_URL);
108
109    /**
110     *  &showMaintenanceLinks=true
111     */
112    public static final String SHOW_MAINTENANCE_LINKS =  "&showMaintenanceLinks=true";
113
114    /**
115     * KRAD
116     */
117    public static final String REMOTE_UIF_KRAD = "KRAD";
118
119    /**
120     * KNS
121     */
122    public static final String REMOTE_UIF_KNS  = "KNS";
123
124    /**
125     * &docFormKey=
126     */
127    public static final String DOC_FORM_KEY = "&docFormKey=";
128    
129    /**
130     * Creates a 13 digit time stamp with two random characters appended for use with fields that require unique values.
131     *
132     * Some fields have built in validation to not allow 9 continuous digits for those cases {@see #createUniqueDtsPlusTwoRandomCharsNot9Digits}
133     * @return
134     */
135    public static String createUniqueDtsPlusTwoRandomChars() {
136        return Calendar.getInstance().getTimeInMillis() + "" + RandomStringUtils.randomAlphabetic(2).toLowerCase();
137    }
138
139    /**
140     * Creates a 13 digit time stamp with two random characters inserted into it to avoid the 9 continuous digit verification some fields use.
141     *
142     * @return
143     */
144    public static String createUniqueDtsPlusTwoRandomCharsNot9Digits() {
145        String dtsTwo = AutomatedFunctionalTestUtils.createUniqueDtsPlusTwoRandomChars();
146        dtsTwo = dtsTwo.substring(0, 5) + dtsTwo.substring(13, 15) + dtsTwo.substring(6, 12);
147        return dtsTwo;
148    }
149
150    protected static void checkForIncidentReport(String contents, String linkLocator, String message, JiraAwareFailable failable) {
151        if (contents == null) { //guard clause
152            return;
153        }
154
155        String errorMessage = incidentReportMessage(contents, linkLocator, message);
156
157        if (errorMessage != null) {
158            if (message != null && !message.isEmpty()) {
159                failable.jiraAwareFail(errorMessage, message);
160            } else {
161                failable.jiraAwareFail(errorMessage, contents);
162            }
163        }
164    }
165
166    protected static String incidentReportMessage(String contents, String linkLocator, String message) {
167        if (incidentReported(contents)) {
168            try {
169                return processIncidentReport(contents, linkLocator, message);
170            } catch (IndexOutOfBoundsException e) {
171                return "\nIncident report detected "
172                                + message
173                                + " but there was an exception during processing: "
174                                + e.getMessage()
175                                + "\nStack Trace from processing exception"
176                                + stackTrace(e)
177                                + "\nContents that triggered exception: "
178                                + deLinespace(contents);
179            }
180        }
181
182        if (contents.contains("HTTP Status 404")) {
183            return "HTTP Status 404 contents: " + contents;
184        }
185
186        if (contents.contains("HTTP Status 500")) {
187            return "\nHTTP Status 500 stacktrace: " + extract500Exception(contents);
188        }
189
190        // freemarker exception
191        if (contents.contains("Java backtrace for programmers:")
192                || contents.contains("Java stack trace (for programmers):")
193                || contents.contains("FreeMarker template error:")) {
194            try {
195                return freemarkerExceptionMessage(contents, linkLocator, message);
196            } catch (IndexOutOfBoundsException e) {
197                return "\nFreemarker exception detected "
198                        + message
199                        + " but there was an exception during processing: "
200                        + e.getMessage()
201                        + "\nStack Trace from processing exception"
202                        + stackTrace(e)
203                        + "\nContents that triggered exception: "
204                        + deLinespace(contents);
205            }
206        }
207
208        if (contents.contains("Document Expired")) { // maybe Firefox specific
209            return "Document Expired message.";
210        }
211
212        return null;
213    }
214
215    public static String deLinespace(String contents) {
216        while (contents.contains("\n\n")) {
217            contents = contents.replaceAll("\n\n", "\n");
218        }
219        return contents;
220    }
221
222    private static String extractIncidentReportInfo(String contents, String linkLocator, String message) {
223        String chunk =  contents.substring(contents.indexOf("Incident Feedback"), contents.lastIndexOf("</div>") );
224        String docId = chunk.substring(chunk.lastIndexOf("Document Id"), chunk.indexOf("View Id"));
225        docId = docId.substring(0, docId.indexOf("</div>"));
226        docId = docId.substring(docId.lastIndexOf(">") + 2, docId.length()).trim();
227
228        String viewId = chunk.substring(chunk.lastIndexOf("View Id"), chunk.indexOf("Error Message"));
229        viewId = viewId.substring(0, viewId.indexOf("</div>"));
230        viewId = viewId.substring(viewId.lastIndexOf(">") + 2, viewId.length()).trim();
231
232        String stackTrace = chunk.substring(chunk.lastIndexOf("(only in dev mode)"), chunk.length());
233        stackTrace = stackTrace.substring(stackTrace.indexOf("<pre") + 4, stackTrace.length());
234        stackTrace = stackTrace.substring(stackTrace.indexOf(">") + 1, stackTrace.indexOf("</"));
235
236        return "\nIncident report "
237                + message
238                + " navigating to "
239                + linkLocator
240                + " : View Id: "
241                + viewId.trim()
242                + " Doc Id: "
243                + docId.trim()
244                + "\nStackTrace: "
245                + stackTrace.trim();
246    }
247
248    private static String extractIncidentReportKim(String contents, String linkLocator, String message) {
249        if (contents.indexOf("id=\"headerarea\"") > -1) {
250            String chunk =  contents.substring(contents.indexOf("id=\"headerarea\""), contents.lastIndexOf("</div>") );
251            String docIdPre = "type=\"hidden\" value=\"";
252            String docId = chunk.substring(chunk.indexOf(docIdPre) + docIdPre.length(), chunk.indexOf("\" name=\"documentId\""));
253
254            String stackTrace = chunk.substring(chunk.lastIndexOf("name=\"displayMessage\""), chunk.length());
255            String stackTracePre = "value=\"";
256            stackTrace = stackTrace.substring(stackTrace.indexOf(stackTracePre) + stackTracePre.length(), stackTrace.indexOf("name=\"stackTrace\"") - 2);
257
258            return "\nIncident report "
259                    + message
260                    + " navigating to "
261                    + linkLocator
262                    + " Doc Id: "
263                    + docId.trim()
264                    + "\nStackTrace: "
265                    + stackTrace.trim();
266        }
267        return "\nIncident report detected for " + linkLocator + " but not able to parse.  " + message;
268    }
269
270    public static void failOnInvalidUserName(String userName, String contents, JiraAwareFailable failable) {
271        if (contents.indexOf("Invalid") > -1) {
272            failable.fail("Invalid Login " + userName);
273        }
274    }
275/*
276    public static void failOnMatchedJira(String contents) {
277        Iterator<String> iter = jiraMatches.keySet().iterator();
278        String key = null;
279
280        while (iter.hasNext()) {
281            key = iter.next();
282            if (contents.contains(key)) {
283                SeleneseTestBase.fail(JIRA_BROWSE_URL + jiraMatches.get(key));
284            }
285        }
286    }
287*/
288
289    private static void failWithInfo(String contents, String linkLocator, JiraAwareFailable failable, String message) {
290        JiraAwareFailureUtils.failOnMatchedJira(contents, linkLocator, failable);
291        failable.fail(contents);
292    }
293
294    private static void failWithReportInfo(String contents, String linkLocator, JiraAwareFailable failable, String message) {
295        final String incidentReportInformation = extractIncidentReportInfo(contents, linkLocator, message);
296        JiraAwareFailureUtils.failOnMatchedJira(incidentReportInformation, failable);
297        failWithInfo(incidentReportInformation, linkLocator, failable, message);
298    }
299
300    /*
301    private static void failWithReportInfoForKim(String contents, String linkLocator, String message) {
302        final String kimIncidentReport = extractIncidentReportKim(contents, linkLocator, message);
303        SeleneseTestBase.fail(kimIncidentReport);
304    }
305*/
306    private static void failWithReportInfoForKim(String contents, String linkLocator, JiraAwareFailable failable, String message) {
307        final String kimIncidentReport = extractIncidentReportKim(contents, linkLocator, message);
308        JiraAwareFailureUtils.failOnMatchedJira(kimIncidentReport, failable);
309        failable.fail(kimIncidentReport);
310    }
311
312    public static String getHTML(String urlToRead) {
313        URL url;
314        HttpURLConnection conn;
315        BufferedReader rd;
316        String line;
317        String result = "";
318
319        try {
320            url = new URL(urlToRead);
321            conn = (HttpURLConnection) url.openConnection();
322            conn.setRequestMethod("GET");
323            rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
324            while ((line = rd.readLine()) != null) {
325                result += line;
326            }
327            rd.close();
328        } catch (Exception e) {
329            e.printStackTrace();
330        }
331
332        return result;
333    }
334
335    private static boolean incidentReported(String contents) {
336        return contents != null &&
337                contents.contains("Incident Report") &&
338                !contents.contains("portal.do?channelTitle=Incident%20Report") && // Incident Report link on sampleapp KRAD tab
339                !contents.contains("portal.do?channelTitle=Incident Report") &&   // Incident Report link on sampleapp KRAD tab IE8
340                !contents.contains("uitest?viewId=Travel-testView2") &&
341                !contents.contains("SeleniumException"); // selenium timeouts have Incident Report in them
342    }
343
344    /**
345     * Append http:// if not present, remove trailing /.
346     *
347     * @param baseUrl
348     * @return
349     */
350    public static String prettyHttp(String baseUrl) {
351
352        if (baseUrl.endsWith("/")) {
353            baseUrl = baseUrl.substring(0, baseUrl.length() - 1);
354        }
355
356        if (!baseUrl.startsWith("http")) {
357            baseUrl = "http://" + baseUrl;
358        }
359
360        return baseUrl;
361    }
362
363    private static String extract500Exception(String contents) {
364        return contents.substring(contents.indexOf("<b>exception</b> </p><pre>") +26,
365                                  contents.indexOf("</pre><p></p><p><b>note</b>"));
366    }
367
368    private static void processFreemarkerException(String contents, String linkLocator, JiraAwareFailable failable, String message) {
369        JiraAwareFailureUtils.failOnMatchedJira(contents, failable);
370        String errorMessage = freemarkerExceptionMessage(contents, linkLocator, message);
371
372        JiraAwareFailureUtils.failOnMatchedJira(errorMessage, linkLocator, failable);
373        failable.fail(errorMessage);
374    }
375
376    protected static String freemarkerExceptionMessage(String contents, String linkLocator, String message) {
377        String ftlStackTrace = null;
378        if (contents.contains("more<")) {
379            ftlStackTrace = contents.substring(contents.indexOf("----------"), contents.indexOf("more<") - 1);
380        } else if (contents.contains("at java.lang.Thread.run(Thread.java:")) {
381            if (contents.indexOf("Error: on line") > -1) {
382                ftlStackTrace = contents.substring(contents.indexOf("Error: on line"), contents.indexOf("at java.lang.Thread.run(Thread.java:") + 39 );
383            } else {
384                ftlStackTrace = contents.substring(contents.indexOf("FreeMarker template error:"), contents.indexOf("at java.lang.Thread.run(Thread.java:") + 39 );
385            }
386        }
387        return "\nFreemarker Exception " + message + " navigating to " + linkLocator + "\nStackTrace: " + ftlStackTrace.trim();
388    }
389
390/*
391    private static void processIncidentReport(String contents, String linkLocator, String message) {
392        failOnMatchedJira(contents);
393
394        if (contents.indexOf("Incident Feedback") > -1) {
395            failWithReportInfo(contents, linkLocator, message);
396        }
397
398        if (contents.indexOf("Incident Report") > -1) { // KIM incident report
399            failWithReportInfoForKim(contents, linkLocator, message);
400        }
401
402        SeleneseTestBase.fail("\nIncident report detected " + message + "\n Unable to parse out details for the contents that triggered exception: " + deLinespace(
403                contents));
404    }
405
406    private static void failWithReportInfo(String contents, String linkLocator, String message) {
407        final String incidentReportInformation = extractIncidentReportInfo(contents, linkLocator, message);
408        SeleneseTestBase.fail(incidentReportInformation);
409    }
410*/
411
412    protected static void processIncidentReport(String contents, String linkLocator, JiraAwareFailable failable, String message) {
413
414        if (contents.indexOf("Incident Feedback") > -1) {
415            failWithReportInfo(contents, linkLocator, failable, message);
416        }
417
418        if (contents.indexOf("Incident Report") > -1) { // KIM incident report
419            failWithReportInfoForKim(contents, linkLocator, failable, message);
420        }
421
422        JiraAwareFailureUtils.failOnMatchedJira(contents, failable);
423        failable.fail("\nIncident report detected "
424                + message
425                + "\n Unable to parse out details for the contents that triggered exception: "
426                + deLinespace(contents));
427    }
428
429    protected static String processIncidentReport(String contents, String linkLocator, String message) {
430
431        if (contents.indexOf("Incident Feedback") > -1) {
432            return extractIncidentReportInfo(contents, linkLocator, message);
433        }
434
435        if (contents.indexOf("Incident Report") > -1) { // KIM incident report
436            return extractIncidentReportKim(contents, linkLocator, message);
437        }
438
439        return "\nIncident report detected "
440                + message
441                + "\n Unable to parse out details for the contents that triggered exception: "
442                + deLinespace(contents);
443    }
444
445    /**
446     * Write the given stack trace into a String remove the ats in an attempt to not cause Jenkins problems.
447     * @param throwable whose stack trace to return
448     * @return String of the given throwable's stack trace.
449     */
450    public static String stackTrace(Throwable throwable) {
451        StringWriter wrt = new StringWriter();
452        PrintWriter pw = new PrintWriter(wrt);
453        throwable.printStackTrace(pw);
454        pw.flush();
455        return wrt.toString();
456    }
457
458    /**
459     * <p>
460     * Use the KRAD Login Screen or the old KNS Login Screen
461     * </p>
462     *
463     * @return true if Krad login
464     */
465    public static boolean isKradLogin(){
466        // check system property, default to KRAD
467        String loginUif = System.getProperty(WebDriverUtils.REMOTE_LOGIN_UIF);
468        if (loginUif == null) {
469            loginUif = REMOTE_UIF_KRAD;
470        }
471
472        return (REMOTE_UIF_KRAD.equalsIgnoreCase(loginUif));
473    }
474}