001/**
002 * Copyright 2005-2015 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.junit.Assert;
019import org.kuali.rice.testtools.common.JiraAwareFailable;
020import org.kuali.rice.testtools.common.JiraAwareFailureUtils;
021import org.openqa.selenium.By;
022import org.openqa.selenium.WebDriver;
023import org.openqa.selenium.WebElement;
024
025import java.util.Arrays;
026import java.util.List;
027
028/**
029 * <p>
030 * Jira Aware Automated Functional Test Base.
031 * </p><p>
032 *     <img src="https://wiki.kuali.org/download/attachments/330268881/JiraAwareFailure.png"/>
033 * <ul>
034 *     <li>{@see JiraAwareWebDriverUtils}</li>
035 *     <li>{@see JiraAwareFailable}</li>
036 *     <li>{@see JiraAwareFailure}</li>
037 * </ul>
038 * TODO: promote the various jiraAware methods from WebDriverLegacyITBase
039 * </p>
040 *
041 * @author Kuali Rice Team (rice.collab@kuali.org)
042 */
043public abstract class JiraAwareAftBase extends AutomatedFunctionalTestBase implements JiraAwareFailable {
044
045    /**
046     * Test state, used for Saucelabs REST API call to set test state via @{see SauceLabsWebDriverHelper#tearDown}.
047     */
048    private boolean passed = false;
049
050    /**
051     * Implement to check for Incident Report or other on screen errors, should call {@see JiraAwareFailable#fail} to fail,
052     * without calling any of the jiraAwareFail methods to avoid an infinite loop.
053     *
054     * @param locator used in failure message if there is an incident report can be blank
055     * @param message used in failure message if there is an incident report can be blank
056     */
057    protected abstract void checkForIncidentReport(String locator, String message);
058
059    /**
060     * WebDriver used in fail and pass to display jGrowl messages.
061     *
062     * @return WebDriver used to display jGrowl messages on fail and pass
063     */
064    protected abstract WebDriver getDriver();
065
066    /**
067     * {@see WebDriverUtils#assertButtonDisabledByText}
068     *
069     * @param buttonText of button to assert is disabled
070     */
071    protected void assertButtonDisabledByText(String buttonText) {
072        JiraAwareWebDriverUtils.assertButtonDisabledByText(getDriver(), buttonText, this);
073    }
074
075    /**
076     * {@see WebDriverUtils.assertButtonEnabledByText}.
077     *
078     * @param buttonText of button to assert is disabled
079     */
080    protected void assertButtonEnabledByText(String buttonText) {
081        JiraAwareWebDriverUtils.assertButtonEnabledByText(getDriver(), buttonText, this);
082    }
083
084    protected void assertDataTableContains(String[][] data) throws InterruptedException {
085        boolean dataPresent = true;
086        String missingMessage = "";
087        String dataTableRow;
088        for (int i = 0, s = data.length; i < s; i++) {
089            dataTableRow = findDataTableRow(data[i][0]).getText();
090            for (int j = 1, t = data[i].length; j < t; j++) {
091                if (!dataTableRow.contains(data[i][j])) {
092                    dataPresent = false;
093                    missingMessage += data[i][j] + " not present in data table row containing " + data[i][0] + ". ";
094                }
095            }
096            WebDriverUtils.jGrowl(getDriver(), "Assert DataTable Row", false, "Assert datatable row '" + dataTableRow
097                    + "' contains '" + Arrays.toString(data[i]) + "' " + dataPresent);
098        }
099        if (!dataPresent) {
100            jiraAwareFail(missingMessage);
101        }
102    }
103
104    protected void assertDataTableContains(String[][] data, String tableClass) throws InterruptedException {
105        boolean dataPresent = true;
106        String missingMessage = "";
107        String dataTableRow;
108        for (int i = 0, s = data.length; i < s; i++) {
109            dataTableRow = findDataTableRow(data[i][0], tableClass).getText();
110            for (int j = 1, t = data[i].length; j < t; j++) {
111                if (!dataTableRow.contains(data[i][j])) {
112                    dataPresent = false;
113                    missingMessage += data[i][j] + " not present in data table row containing " + data[i][0] + ". ";
114                }
115            }
116        }
117        if (!dataPresent) {
118            jiraAwareFail(missingMessage);
119        }
120    }
121
122    protected void assertElementPresentByName(String name) {
123        assertElementPresentByName(name, this.getClass().toString());
124    }
125
126    protected void assertElementPresentByName(String name, String message) {
127        try {
128            findElement(By.name(name));
129        } catch (Throwable t) {
130            jiraAwareFail(name + " not present " + message);
131        }
132    }
133
134    protected void assertElementPresentByXpath(String locator) {
135        assertElementPresentByXpath(locator, this.getClass().toString());
136    }
137
138    protected void assertElementPresent(By by) {
139        assertElementPresent(by, this.getClass().toString());
140    }
141
142    protected void assertElementPresent(By by, String message) {
143        try {
144            findElement(by);
145        } catch (Throwable t) {
146            jiraAwareFail(by, message, t);
147        }
148    }
149
150    protected void assertElementPresentByXpath(String locator, String message) {
151        try {
152            findElement(By.xpath(locator));
153        } catch (Throwable t) {
154            jiraAwareFail(By.xpath(locator), message, t);
155        }
156    }
157
158    protected void assertElementPresentByLinkText(String linkText) {
159        try {
160            findElement(By.linkText(linkText));
161        } catch (Throwable t) {
162            jiraAwareFail(By.cssSelector(linkText), this.getClass().toString(), t);
163        }
164
165    }
166
167    protected void assertElementPresent(String locator) {
168        try {
169            findElement(By.cssSelector(locator));
170        } catch (Throwable t) {
171            jiraAwareFail(By.cssSelector(locator), this.getClass().toString(), t);
172        }
173    }
174
175    protected void assertEquals(boolean expected, boolean actual) {
176        if (expected != actual) {
177            jiraAwareFail("Expected \"" + expected + "\" but saw \"" + actual + "\" instead");
178        }
179    }
180
181    protected void assertEquals(int expected, int actual) {
182        if (expected != actual) {
183            jiraAwareFail("Expected \"" + expected + "\" but saw \"" + actual + "\" instead");
184        }
185    }
186
187    protected void assertEquals(String message, int expected, int actual) {
188        if (expected != actual) {
189            jiraAwareFail("Expected \"" + expected + "\" but saw \"" + actual + "\" instead " + message);
190        }
191    }
192
193
194    protected void assertEquals(String expected, String actual) {
195        if (!expected.equals(actual)) {
196            jiraAwareFail("Expected \"" + expected + "\" but saw \"" + actual + "\" instead in " + getClass().toString());
197        }
198    }
199
200    protected void assertEquals(String message, String expected, String actual) {
201        if (!expected.equals(actual)) {
202            jiraAwareFail("Expected \"" + expected + "\" but saw \"" + actual + "\" instead " + message);
203        }
204    }
205
206    /**
207     * If booleanToAssertFalse is true call {@see jiraAwareFail}.
208     *
209     * @param booleanToAssertFalse
210     */
211    protected void assertFalse(boolean booleanToAssertFalse) {
212        JiraAwareWebDriverUtils.assertFalse(booleanToAssertFalse, this);
213    }
214
215    /**
216     * If booleanToAssertFalse is true call {@see jiraAwareFail}.
217     *
218     * @param message to include if booleanToAssertTrue is true
219     * @param booleanToAssertFalse
220     */
221    protected void assertFalse(String message, boolean booleanToAssertFalse) {
222        JiraAwareWebDriverUtils.assertFalse(message, booleanToAssertFalse, this);
223    }
224
225    protected void assertIsVisible(String locator) {
226        if (!isVisible(locator)) {
227            jiraAwareFail(locator + " is not visible and should be");
228        }
229    }
230
231    protected void assertIsVisible(By by, String message) {
232        if (!isVisible(by)) {
233            jiraAwareFail(by + " not visible " + message);
234        }
235    }
236
237    protected void assertIsVisibleById(String id) {
238        if (!isVisibleById(id)) {
239            jiraAwareFail(id + " is not visible and should be");
240        }
241    }
242
243    protected void assertIsVisibleByXpath(String xpath, String message) {
244        if (!isVisibleByXpath(xpath)) {
245            jiraAwareFail(xpath + " not visible " + message);
246        }
247    }
248
249    protected void assertIsNotVisible(By by) {
250        assertIsNotVisible(by, this.getClass().toString());
251    }
252
253    protected void assertIsNotVisible(By by, String message) {
254        if (isVisible(by)) {
255            jiraAwareFail(by + " is visible and should not be " + message);
256        }
257    }
258
259    protected void assertIsNotVisible(String locator) {
260        if (isVisible(locator)) {
261            jiraAwareFail(locator + " is visible and should not be");
262        }
263    }
264
265    protected void assertIsNotVisibleByXpath(String xpath) {
266        if (isVisible(By.xpath(xpath))) {
267            jiraAwareFail(xpath + " is visible and should not be");
268        }
269    }
270
271    protected void assertIsNotVisibleByXpath(String xpath, String message) {
272        if (isVisibleByXpath(xpath)) {
273            jiraAwareFail(xpath + " visible and should not be " + message);
274        }
275    }
276
277    protected void assertLabeledTextNotPresent(String[][] labeledText) {
278        boolean allLabeledTextNotPresent = true;
279        String missingMessage = "";
280        for (int i = 0, s = labeledText.length; i < s; i++) {
281            if (isLabeledTextPresent(labeledText[i][0], labeledText[i][1])) {
282                allLabeledTextNotPresent = false;
283                missingMessage += "Text: " + labeledText[i][1] + " labeled by: " + labeledText[i][0] + " present. ";
284            }
285        }
286        if (!allLabeledTextNotPresent) {
287            jiraAwareFail(missingMessage);
288        }
289    }
290
291    protected void assertLabeledTextPresent(String[][] labeledText) {
292        boolean allLabeledTextPresent = true;
293        String missingMessage = "";
294        for (int i = 0, s = labeledText.length; i < s; i++) {
295            if (!isLabeledTextPresent(labeledText[i][0], labeledText[i][1])) {
296                allLabeledTextPresent = false;
297                missingMessage += "Text: " + labeledText[i][1] + " labeled by: " + labeledText[i][0] + " not present. ";
298            }
299        }
300        if (!allLabeledTextPresent) {
301            jiraAwareFail(missingMessage);
302        }
303    }
304
305    /**
306     * Looks for values in input or select
307     * @param labeledText
308     */
309    protected void assertLabeledInputTextPresent(String[][] labeledText) {
310        boolean allLabeledTextPresent = true;
311        String missingMessage = "";
312        for (int i = 0, s = labeledText.length; i < s; i++) {
313            if (!isLabeledInputTextPresent(labeledText[i][0], labeledText[i][1])) {
314                allLabeledTextPresent = false;
315                missingMessage += "Text: " + labeledText[i][1] + " labeled by: " + labeledText[i][0] + " not present. ";
316            }
317        }
318        if (!allLabeledTextPresent) {
319            jiraAwareFail(missingMessage);
320        }
321    }
322
323    protected void assertLabeledTextPresent(String label, String text) {
324        if (!isLabeledTextPresent(label, text)) {
325            jiraAwareFail("Text: " + text + " labeled by: " + label + " not present");
326        }
327    }
328
329    protected void assertResultCount(String count) throws InterruptedException {
330        jiraAwareWaitFor(By.cssSelector("div.dataTables_info"), "result count for " + this.getClass().toString());
331        assertTextPresent("of " + count + " entries", "div.dataTables_info", this.getClass().toString());
332    }
333
334    /**
335     * <b>WARNING:</b> this only does a check against the page source.  The form url can have random character that match
336     * simple text.  A narrowly scoped locator for {@see #assertTextPresent(String String String)}
337     *
338     * @param text
339     */
340    protected void assertTextPresent(String text) {
341        assertTextPresent(text, this.getClass().toString());
342    }
343
344    /**
345     * <b>WARNING:</b> this only does a check against the page source.  The form url can have random character that match simple text
346     * @param text
347     */
348    protected void assertTextPresent(String text, String message) {
349        WebDriverUtils.jGrowl(getDriver(), "Assert Text Present", false, "Assert text '" + text + "' is present.");
350        String pageSource = getDriver().getPageSource();
351        if (!pageSource.contains(text)) {
352            jiraAwareFail(text + " not present " + message);
353        }
354        WebDriverUtils.highlightElement(getDriver(), By.xpath("//*[contains(text(), '" + text + "')]"));
355    }
356
357    /**
358     * @param text
359     */
360    protected void assertTextPresent(String text, String cssSelector, String message){
361        WebElement element = findElement(By.cssSelector(cssSelector));
362        if (!element.getText().contains(text)){
363            jiraAwareFail(text + " for " + cssSelector + " not present " + message);
364        }
365    }
366
367    /**
368     * Asset that the given text does not occur in the page
369     * Warning, this only does a check against the page source.  The form url can have random character that match simple text
370     * @param text the text to search for
371     */
372    protected void assertTextNotPresent(String text) {
373        assertTextNotPresent(text, this.getClass().toString());
374    }
375
376    /**
377     * Assert that the given text does not occur in the page, and add an additional message to the failure
378     * @param text the text to search for
379     * @param message the message to add to the failure
380     */
381    protected void assertTextNotPresent(String text, String message) {
382        String contents = getDriver().getPageSource();
383        if (contents.contains(text)) {
384            jiraAwareFail(text + " is present and should not be " + message);
385        }
386    }
387
388    /**
389     * @param text
390     */
391    protected void assertTextNotPresent(String text, String cssSelector, String message){
392        WebElement element = findElement(By.cssSelector(cssSelector));
393        if (element.getText().contains(text)){
394            jiraAwareFail(text + " for " + cssSelector + " is present and shouldn't be " + message);
395        }
396    }
397
398    /**
399     * If booleanToAssertTrue is false call {@see jiraAwareFail}.
400     *
401     * @param booleanToAssertTrue
402     */
403    protected void assertTrue(boolean booleanToAssertTrue) {
404        JiraAwareWebDriverUtils.assertTrue(getClass().toString(), booleanToAssertTrue, this);
405    }
406
407    /**
408     * If booleanToAssertTrue is false call {@see jiraAwareFail}.
409     *
410     * @param message to include if booleanToAssertTrue is false
411     * @param booleanToAssertTrue
412     */
413    protected void assertTrue(String message, boolean booleanToAssertTrue) {
414        JiraAwareWebDriverUtils.assertTrue(message, booleanToAssertTrue, this);
415    }
416
417    /**
418     * {@inheritDoc}
419     * <p>
420     * Set passed to false, call jGrowl sticky with the given message, then fails using  {@see JiraAwareFailable#fail}.
421     * </p>
422     * @param message to display with failure
423     */
424    @Override
425    public void fail(String message) {
426        passed = false;
427        WebDriverUtils.jGrowl(getDriver(), "Failure " + getClass().getSimpleName(), true, message);
428        Assert.fail(message); // The final fail that JiraAwareFailure calls, do not change this to a JiraAwareFailure.
429    }
430
431    protected WebElement findDataTableRow(String keyText) throws InterruptedException {
432        return findDataTableRow(keyText, "dataTable");
433    }
434
435    protected WebElement findDataTableRow(String keyText, String className) throws InterruptedException {
436        jiraAwareWaitFor(By.className(className));
437        WebElement element = findElement(By.className(className));
438        return findElement(By.xpath("./*/tr//*[contains(text(), '" + keyText + "')]/ancestor::tr"), element);
439    }
440
441    /**
442     * {@see WebDriverUtils#findElement}.
443     *
444     * @param by to find element with
445     * @return WebElement found with given by
446     */
447    protected WebElement findElement(By by) {
448        try {
449            return WebDriverUtils.findElement(getDriver(), by);
450        } catch (Throwable t) {
451            checkForIncidentReport(by.toString(), t.getMessage());
452            jiraAwareFail(by.toString(), t.getMessage(), t);
453        }
454        return null; // required by compiler, never reached
455    }
456
457    protected WebElement findElement(By by, WebElement elementToFindOn) {
458        try {
459            WebElement found = elementToFindOn.findElement(by);
460            WebDriverUtils.highlightElement(getDriver(), found);
461            return found;
462        } catch (Throwable t) {
463            checkForIncidentReport(by.toString(), t.getMessage());
464            jiraAwareFail(by.toString(), t.getMessage() + " " + this.getClass().toString(), t);
465        }
466        return null; // required by compiler, never reached
467    }
468
469    protected boolean isLabeledTextPresent(String label, String text) {
470        WebElement element = findElement(By.xpath("//tr/th/label[contains(text(), '" + label + "')]/ancestor::tr/td"));
471        String labeledText = element.getText().trim();
472        WebDriverUtils.jGrowl(getDriver(), "Is Labeled Text Present", false, "Is text '" + text + "' present for label '"
473                + label + "'? " + labeledText.contains(text) + " saw " + labeledText);
474        return labeledText.contains(text);
475    }
476
477    protected boolean isLabeledInputTextPresent(String label, String text) {
478        String labeledText = "no input or select found no text returned";
479        List<WebElement> inputs = getDriver().findElements(By.xpath("//tr/th/label[contains(text(), '" + label + "')]/ancestor::tr/td/div/input"));
480
481        if (inputs.size() > 1) {
482            System.out.println("found more elements in labeled input than expected");
483        }
484
485        if (inputs.size() == 1) {
486            labeledText = inputs.get(0).getAttribute("value").trim();
487            // seeing this on Agenda Context select, value is getting uppercased somewhere...
488            if (labeledText.equals(text.toUpperCase())) {
489                text = text.toUpperCase();
490            }
491
492        } else {
493            inputs = getDriver().findElements(By.xpath("//tr/th/label[contains(text(), '" + label + "')]/ancestor::tr/td/div/select"));
494
495            if (inputs.size() > 1) {
496                System.out.println("found more elements in labeled input than expected");
497            }
498
499            if (inputs.size() == 1) {
500                List<WebElement> options = inputs.get(0).findElements(By.tagName("option"));
501                for (WebElement option : options) {
502                    if (option.getAttribute("selected") != null) {
503                        labeledText = option.getText().trim();
504                    }
505                }
506            }
507        }
508
509        WebDriverUtils.jGrowl(getDriver(), "Is Labeled Text Present", false, "Is text '" + text + "' present for label '"
510                + label + "'? " + labeledText.contains(text) + " saw " + labeledText);
511        return labeledText.contains(text);
512    }
513
514    protected boolean isVisible(String locator) {
515        return isVisible(By.cssSelector(locator));
516    }
517
518    protected boolean isVisible(By by) {
519        List<WebElement> elements = getDriver().findElements(by);
520        for (WebElement element: elements) {
521            try {
522                if (element.isDisplayed()) {
523                    return true;
524                }
525            } catch (Throwable t) {
526                // don't fail
527            }
528        }
529        return false;
530    }
531
532    protected boolean isVisibleById(String id) {
533        return isVisible(By.id(id));
534    }
535
536    protected boolean isVisibleByXpath(String locator) {
537        return isVisible(By.xpath(locator));
538    }
539
540    protected WebElement jiraAwareClearType(By by, String text) {
541        return jiraAwareClearAndType(by, text, this.getClass().toString().replace("class ", ""));
542    }
543
544    protected WebElement jiraAwareClearAndType(By by, String text, String failureMessage) {
545        findElement(by).clear();
546        return jiraAwareType(by, text, failureMessage);
547    }
548
549    protected WebElement jiraAwareClearAndTypeByName(String name, String text) {
550        return jiraAwareClearAndType(By.name(name), text, this.getClass().toString().replace("class ", ""));
551    }
552
553    protected WebElement jiraAwareClearAndTypeByName(String name, String text, String failureMessage) {
554        return jiraAwareClearAndType(By.name(name), text, failureMessage);
555    }
556
557    /**
558     * {@inheritDoc}
559     * {@see #checkForIncidentReport} and {@see JiraAwareFailureUtils#fail}.
560     *
561     * @param message to check for a Jira match and fail with.
562     */
563    @Override
564    public void jiraAwareFail(String message) {
565        jiraAwareFail("", message, null, this);
566    }
567
568    /**
569     * {@inheritDoc}
570     * {@see #checkForIncidentReport} and {@see JiraAwareFailureUtils#fail}.
571     *
572     * @param contents to check for a Jira match
573     * @param message to check for a Jira match and fail with.
574     */
575    @Override
576    public void jiraAwareFail(String contents, String message) {
577        jiraAwareFail(contents, message, null, this);
578    }
579
580    /**
581     * {@see #checkForIncidentReport} and {@see JiraAwareFailureUtils#fail}.
582     *
583     * @param by to check for a Jira match
584     * @param message to check for a Jira match and fail with.
585     * @param throwable to check for a Jira match
586     */
587    protected void jiraAwareFail(By by, String message, Throwable throwable) {
588        jiraAwareFail(by.toString(), message, throwable, this);
589    }
590
591    /**
592     * {@inheritDoc}
593     * {@see #checkForIncidentReport} and {@see JiraAwareFailureUtils#fail}.
594     *
595     * @param contents to check for a Jira match
596     * @param message to check for a Jira match and fail with.
597     * @param throwable to check for a Jira match
598     */
599    @Override
600    public void jiraAwareFail(String contents, String message, Throwable throwable) {
601        jiraAwareFail(contents, message, throwable, this);
602    }
603
604    public void jiraAwareFail(String pageSource, By by, String message, Throwable t) {
605
606    }
607
608
609    /**
610     * {@see #checkForIncidentReport} and {@see JiraAwareFailureUtils#fail}.
611     *
612     * @param contents to check for a Jira match
613     * @param message to check for a Jira match and fail with.
614     * @param throwable to check for a Jira match
615     * @param failable to call fail on
616     */
617    protected void jiraAwareFail(String contents, String message, Throwable throwable, JiraAwareFailable failable) {
618        passed = false;
619
620        if (message == null) {
621            message = "";
622        }
623
624        String javascriptErrors = WebDriverUtils.javascriptErrorsToString(WebDriverUtils.javascriptErrors(getDriver()));
625        message = javascriptErrors + message;
626
627        if (contents == null) {
628            contents = getDriver().getPageSource();
629        }
630
631        if (!contents.startsWith("\nIncident report") && !message.startsWith("\nIncident report")) {
632            String errorMessage = AutomatedFunctionalTestUtils.incidentReportMessage(getDriver().getPageSource(), "", message);
633            if (errorMessage != null) {
634                JiraAwareFailureUtils.failOnMatchedJira(errorMessage, message, failable);
635                JiraAwareFailureUtils.fail(errorMessage, message, throwable, failable);
636            }
637        }
638
639        JiraAwareFailureUtils.fail(contents, message, throwable, failable);
640    }
641
642    /**
643     * {@see #checkForIncidentReport} and {@see JiraAwareFailureUtils#fail}.
644     *
645     * @param by to click on
646     * @param message on failure
647     * @throws InterruptedException
648     */
649    protected void jiraAwareWaitAndClick(By by, String message) throws InterruptedException {
650        jiraAwareWaitAndClick(by, message, this);
651    }
652
653    protected WebElement jiraAwareType(By by, String text) {
654        return jiraAwareType(by, text, this.getClass().toString().replace("class ", ""));
655    }
656
657    protected WebElement jiraAwareType(By by, String text, String failureMessage) {
658        try {
659            return type(by, text);
660        } catch (Throwable t) {
661            JiraAwareFailureUtils.failOnMatchedJira(by.toString(), failureMessage, this);
662            jiraAwareFail(t.getMessage()
663                    + " "
664                    + by.toString()
665                    + "  unable to type text '"
666                    + text
667                    + "'  "
668                    + failureMessage
669                    + " current url "
670                    + getDriver().getCurrentUrl()
671                    + "\n"
672                    + AutomatedFunctionalTestUtils.deLinespace(getDriver().getPageSource()));
673        }
674        return null;
675    }
676
677    protected WebElement jiraAwareTypeByName(String name, String text) {
678        return jiraAwareType(By.name(name), text, this.getClass().toString().replace("class ", ""));
679    }
680
681    protected WebElement jiraAwareTypeByName(String name, String text, String failureMessage) {
682        return jiraAwareType(By.name(name), text, failureMessage);
683    }
684
685    /**
686     * {@see #jiraAwareWaitFor}
687     *
688     * @param by to click on
689     * @param message on failure
690     * @param failable to fail on if not found
691     * @throws InterruptedException
692     */
693    protected void jiraAwareWaitAndClick(By by, String message, JiraAwareFailable failable) throws InterruptedException {
694        jiraAwareWaitAndClick(by, WebDriverUtils.configuredImplicityWait(), message, failable);
695    }
696
697    protected void jiraAwareWaitAndClick(By by, int waitSeconds, String message, JiraAwareFailable failable) throws InterruptedException {
698        try {
699            jiraAwareWaitFor(by, waitSeconds, message, failable);
700            findElement(by).click();
701            // possible future code of outputting clicked components in a more generic way, but need to look into duplicates, don't delete
702            //            WebElement element = findElement(by);
703            //            String jgrowl = element.getAttribute("name");
704            //            if (jgrowl == null || "".equals(jgrowl)) {
705            //                jgrowl = element.getAttribute("id");
706            //            }
707            //            if (jgrowl == null || "".equals(jgrowl)) {
708            //                jgrowl = by.toString();
709            //            }
710            //            WebDriverUtils.jGrowl(getDriver(), "Click " + jgrowl, false, "Click " + jgrowl);
711            //            element.click();
712        } catch (Throwable t) {
713            failable.jiraAwareFail(by.toString(), message, t);
714        }
715    }
716
717    /**
718     * {@see WebDriverUtils#waitFor}.
719     *
720     * @param by to find
721     * @return WebElement found with given by
722     * @throws InterruptedException
723     */
724    protected WebElement jiraAwareWaitFor(By by) throws InterruptedException {
725        return jiraAwareWaitFor(by, this.getClass().toString());
726    }
727
728    /**
729     * {@see WebDriverUtils#waitFor}.
730     *
731     * @param by to find
732     * @param message on failure
733     * @return WebElement found with given by
734     * @throws InterruptedException
735     */
736    protected WebElement jiraAwareWaitFor(By by, String message) throws InterruptedException {
737        try {
738            return WebDriverUtils.waitFor(getDriver(), WebDriverUtils.configuredImplicityWait(), by, message);
739        } catch (Throwable t) {
740            jiraAwareFail(by, message + " " + this.getClass().toString(), t);
741        }
742        return null; // required, but the jiraAwareFail will will end test before this statement is reached
743    }
744
745    /**
746     * {@see WebDriverUtils#waitFor}.
747     *
748     * @param by to find
749     * @param message on failure
750     * @throws InterruptedException
751     */
752    protected void jiraAwareWaitFors(By by, String message) throws InterruptedException {
753        try {
754            WebDriverUtils.waitFors(getDriver(), WebDriverUtils.configuredImplicityWait(), by, message);
755        } catch (Throwable t) {
756            jiraAwareFail(by, message, t);
757        }
758    }
759
760    /**
761     * {@see WebDriverUtils#waitFor}.
762     *
763     * @param by to find
764     * @param message on failure
765     * @param failable to fail if given by is not found
766     * @throws InterruptedException
767     */
768    protected void jiraAwareWaitFor(By by, String message, JiraAwareFailable failable) throws InterruptedException {
769        jiraAwareWaitFor(by, WebDriverUtils.configuredImplicityWait(), message, failable);
770    }
771
772    protected void jiraAwareWaitFor(By by, int waitSeconds, String message, JiraAwareFailable failable) throws InterruptedException {
773        try {
774            WebDriverUtils.waitFor(getDriver(), waitSeconds, by, message);
775        } catch (Throwable t) {
776            jiraAwareFail(by.toString(), message, t, failable);
777        }
778    }
779
780    /**
781     * {@see WebDriverUtils#waitFor}.
782     *
783     * @param by to find
784     * @param seconds to wait
785     * @param message on failure
786     * @return WebElement found with given by
787     * @throws InterruptedException
788     */
789    protected WebElement jiraAwareWaitFor(By by, int seconds, String message) throws InterruptedException {
790        try {
791            return WebDriverUtils.waitFor(getDriver(), seconds, by, message);
792        } catch (Throwable t) {
793            jiraAwareFail(by, message, t);
794        }
795        return null; // required, but the jiraAwareFail will will end test before this statement is reached
796    }
797
798    /**
799     * @return passed
800     */
801    public boolean isPassed() {
802        return passed;
803    }
804
805    protected void selectOptionByName(String name, String optionValue) throws InterruptedException {
806        selectOption(By.name(name), optionValue);
807    }
808
809    protected void selectOptionByXpath(String locator, String optionValue) throws InterruptedException {
810        selectOption(By.name(locator), optionValue);
811    }
812
813    /**
814     * Uses Selenium's findElements method which does not throw a test exception if not found.
815     * @param by
816     * @param optionValue
817     * @throws InterruptedException
818     */
819    protected void selectOption(By by, String optionValue) throws InterruptedException {
820        WebElement select1 = findElement(by);
821        List<WebElement> options = select1.findElements(By.tagName("option"));
822
823        String name = select1.getAttribute("name");
824
825        if (options == null || options.size() == 0) {
826            jiraAwareFail("No options for select "
827                    + select1.toString()
828                    + " was looking for value "
829                    + optionValue
830                    + " using "
831                    + by.toString());
832        }
833
834        for (WebElement option : options) {
835            if (option.getAttribute("value").equals(optionValue)) {
836                WebDriverUtils.jGrowl(getDriver(), "Select " + option.getText(), false, "Select " + option.getText() + " from " + name);
837                option.click();
838                break;
839            }
840        }
841    }
842
843    /**
844     * Uses Selenium's findElements method which does not throw a test exception if not found.
845     * @param by
846     * @param optionText
847     * @throws InterruptedException
848     */
849    protected void selectOptionText(By by, String optionText) throws InterruptedException {
850        WebElement select1 = findElement(by);
851        List<WebElement> options = select1.findElements(By.tagName("option"));
852
853        String name = select1.getAttribute("name");
854
855        if (options == null || options.size() == 0) {
856            jiraAwareFail("No options for select "
857                    + select1.toString()
858                    + " was looking for text "
859                    + optionText
860                    + " using "
861                    + by.toString());
862        }
863
864        for (WebElement option : options) {
865            if (option.getText().equals(optionText)) {
866                WebDriverUtils.jGrowl(getDriver(), "Select " + option.getText(), false, "Select " + option.getText() + " from " + name);
867                option.click();
868                break;
869            }
870        }
871    }
872
873    private WebElement type(By by, String text) {
874        WebElement element = findElement(by);
875        String name = element.getAttribute("name");
876        WebDriverUtils.jGrowl(getDriver(), "Type", false, "Type into " + name + " the text: " + text);
877        WebDriverUtils.highlightElement(getDriver(), element);
878        element.sendKeys(text);
879        return element;
880    }
881
882    private WebElement typeByName(String name, String text) {
883        return type(By.name(name), text);
884    }
885
886    /**
887     * <p>
888     * Set the test state to passed, call jGrowl sticky with success, required to be called at the conclusion of a test
889     * for the saucelabs state of a test to be updated to passed.
890     * </p>
891     */
892    protected void passed() {
893        if (passed == true) {
894            WebDriverUtils.jGrowl(getDriver(), "Passed has been called more than once " + getClass().getSimpleName(), true, "Passed");
895        }
896        passed = true;
897        WebDriverUtils.jGrowl(getDriver(), "Success " + getClass().getSimpleName(), true, "Passed");
898    }
899
900    protected WebElement waitAndType(By by, String text, String message) throws InterruptedException {
901        try {
902            jiraAwareWaitFor(by, message);
903            return type(by, text);
904        } catch (Throwable t) {
905            checkForIncidentReport(by.toString(), message);
906            JiraAwareFailureUtils.failOnMatchedJira(by.toString(), message, this);
907            jiraAwareFail(t.getMessage()
908                    + " "
909                    + by.toString()
910                    + "  unable to type text '"
911                    + text
912                    + "'  "
913                    + message
914                    + " current url "
915                    + getDriver().getCurrentUrl()
916                    + "\n"
917                    + AutomatedFunctionalTestUtils.deLinespace(getDriver().getPageSource()));
918        }
919        return null;
920    }
921}