6
6
import org .apache .commons .io .FilenameUtils ;
7
7
import org .apache .commons .lang3 .time .StopWatch ;
8
8
import org .junit .jupiter .api .Assertions ;
9
- import org .openqa .selenium .By ;
10
- import org .openqa .selenium .JavascriptExecutor ;
11
- import org .openqa .selenium .WebDriver ;
12
- import org .openqa .selenium .WebElement ;
13
- import org .openqa .selenium .chrome .ChromeDriver ;
14
- import org .openqa .selenium .chrome .ChromeDriverService ;
15
- import org .openqa .selenium .chrome .ChromeOptions ;
16
- import org .openqa .selenium .edge .EdgeDriver ;
17
- import org .openqa .selenium .edge .EdgeDriverService ;
18
- import org .openqa .selenium .edge .EdgeOptions ;
19
- import org .openqa .selenium .ie .InternetExplorerDriver ;
20
- import org .openqa .selenium .ie .InternetExplorerDriverLogLevel ;
21
- import org .openqa .selenium .ie .InternetExplorerDriverService ;
22
- import org .openqa .selenium .ie .InternetExplorerOptions ;
23
9
10
+ import javax .management .RuntimeMBeanException ;
24
11
import javax .swing .text .Utilities ;
25
12
import java .io .BufferedReader ;
26
13
import java .io .File ;
27
14
import java .io .InputStreamReader ;
15
+ import java .lang .annotation .ElementType ;
16
+ import java .lang .annotation .Retention ;
17
+ import java .lang .annotation .RetentionPolicy ;
18
+ import java .lang .annotation .Target ;
19
+ import java .net .ConnectException ;
28
20
import java .nio .file .Files ;
29
21
import java .nio .file .Path ;
30
22
import java .nio .file .Paths ;
34
26
import java .util .List ;
35
27
import java .util .concurrent .TimeUnit ;
36
28
29
+ import org .openqa .selenium .*;
30
+ import org .openqa .selenium .chrome .ChromeDriver ;
31
+ import org .openqa .selenium .chrome .ChromeDriverService ;
32
+ import org .openqa .selenium .chrome .ChromeOptions ;
33
+ import org .openqa .selenium .edge .EdgeDriver ;
34
+ import org .openqa .selenium .edge .EdgeDriverService ;
35
+ import org .openqa .selenium .edge .EdgeOptions ;
36
+ import org .openqa .selenium .ie .InternetExplorerDriver ;
37
+ import org .openqa .selenium .ie .InternetExplorerDriverLogLevel ;
38
+ import org .openqa .selenium .ie .InternetExplorerDriverService ;
39
+ import org .openqa .selenium .ie .InternetExplorerOptions ;
37
40
38
41
public class SeleniumDriver {
39
42
// CONSTANT FIELDS
@@ -51,7 +54,6 @@ public class SeleniumDriver {
51
54
52
55
private WebDriver webDriver ;
53
56
54
-
55
57
private Duration _findTimeout = null ;
56
58
private Duration _pollInterval = null ;
57
59
private Duration _pageLoadTimeout = null ;
@@ -155,6 +157,7 @@ public Duration setPageLoadTimeout(Duration pageLoadTimeout) {
155
157
public Browsers setDevice (Devices device ) { _device =device ; return getDevice ();}
156
158
157
159
160
+
158
161
public HTMLElement findElement (ObjectMapping objectMapping ) { return findElement (null ,objectMapping , false , false , getElementFindTimeout (), getPollInterval (), false );}
159
162
public HTMLElement findElement (ObjectMapping objectMapping ,boolean waitUntilStable ) { return findElement (null ,objectMapping , false , false , getElementFindTimeout (), getPollInterval (), waitUntilStable );}
160
163
public HTMLElement findElement (ObjectMapping objectMapping ,Duration timeout ) { return findElement (null ,objectMapping , false , false , timeout , getPollInterval (), false );}
@@ -185,7 +188,6 @@ public HTMLElement findElement(HTMLElement parentElement,ObjectMapping objectMap
185
188
boolean multiLogShown =false ;
186
189
boolean showMultiMatches =true ;
187
190
boolean isStable =false ;
188
-
189
191
//
190
192
// We look after our own implicitWait (timeout) and poll interval rather that Selenium's as we are using our FindElements to find the element we want. We use our
191
193
// FindElements so that we can control if ONLY a single match is allowed (Selenium FindElement allows multiple matches silently - see By class implementation) and extra debug logging.
@@ -195,7 +197,7 @@ public HTMLElement findElement(HTMLElement parentElement,ObjectMapping objectMap
195
197
196
198
Logger .WriteLine (Logger .LogLevels .TestDebug , "Timeout - %s" , durationFormatted (timeout ));
197
199
198
- while (waitUntilStable && ! isStable ) {
200
+ while (true ) {
199
201
200
202
StopWatch timer = StopWatch .createStarted ();
201
203
@@ -234,6 +236,7 @@ public HTMLElement findElement(HTMLElement parentElement,ObjectMapping objectMap
234
236
throw new RuntimeException (errorText );
235
237
}
236
238
239
+ // At this point we have a single match OR multiple matches
237
240
if (showMultiMatches && !waitUntilSingle ) {
238
241
Logger .WriteLine (Logger .LogLevels .TestDebug , "From [%s], find [%s (%s)] returned %d matches (%s multiple matches)." ,
239
242
(parentElement == null ) ? "DOM Top Level" : parentElement .getMappingDetails ().getFriendlyName (),
@@ -252,7 +255,7 @@ public HTMLElement findElement(HTMLElement parentElement,ObjectMapping objectMap
252
255
objectMapping .getFriendlyName (),
253
256
clauseResults .size (),
254
257
(allowMultipleMatches ) ? "Allowing" : "Not" );
255
- return clauseResults . get ( 0 ) ;
258
+ break ;
256
259
} else {
257
260
if (timer .getTime ()>=totalTimeoutMillis ) {
258
261
Logger .WriteLine (Logger .LogLevels .Error , "From [%s], find [%s (%s)] returned %d matches (%s multiple matches). We must wait until stable, element 0 is NOT stable and timeout reached so throwing" ,
@@ -277,17 +280,15 @@ public HTMLElement findElement(HTMLElement parentElement,ObjectMapping objectMap
277
280
(allowMultipleMatches ) ? "Allowing" : "Not" );
278
281
}
279
282
}
280
-
281
283
}
282
284
return clauseResults .get (0 );
283
285
}
284
286
285
287
public List <HTMLElement > findElements (HTMLElement parentElement , ObjectMapping mapping ) {
286
288
287
- List <WebElement > foundElements ;
289
+ List <WebElement > foundElements = null ;
288
290
List <HTMLElement > returnElements = new ArrayList <HTMLElement >();
289
291
290
-
291
292
if (mapping ==null ) {
292
293
Logger .WriteLine (Logger .LogLevels .Error ,"ObjectMapping = null!" );
293
294
throw new RuntimeException ("SeleniumDriver.FindElements called with mapping null!" );
@@ -302,10 +303,14 @@ public List<HTMLElement> findElements(HTMLElement parentElement, ObjectMapping m
302
303
Logger .WriteLine (Logger .LogLevels .FrameworkDebug ,"Calling Selenium WebDriver findElements with By = [%s]" ,seleniumFindBy .toString ());
303
304
foundElements = (parentElement ==null ) ? webDriver .findElements (seleniumFindBy ) : parentElement .getSeleniumnWebElement ().findElements (seleniumFindBy );
304
305
}
306
+ catch (WebDriverException e )
307
+ {
308
+ checkIfConnectionIssue (e );
309
+ }
305
310
catch (Exception e ) {
306
311
if (parentElement ==null ) {
307
312
Logger .WriteLine (Logger .LogLevels .Error , "Selenium error finding elements using find logic [%s] ([%s)]: %s" , seleniumFindBy .toString (), mapping .getFriendlyName (), e .toString ());
308
- throw new RuntimeException (String .format ("Selenium error finding elements using find logic [%s] ([%s)]" , seleniumFindBy .toString (), mapping .getFriendlyName ()));
313
+ throw new RuntimeException (String .format ("Selenium error finding elements using find logic [%s] ([%s)]" , seleniumFindBy .toString (), mapping .getFriendlyName ()), e );
309
314
}
310
315
else {
311
316
Logger .WriteLine (Logger .LogLevels .Error , "Selenium error finding elements offset from [%s (%s)] using find logic [%s] ([%s)]: %s" ,parentElement .getMappingDetails ().getFriendlyName (),parentElement .getMappingDetails ().getActualFindLogic (),seleniumFindBy .toString (), mapping .getFriendlyName (), e .toString ());
@@ -344,11 +349,14 @@ public List<HTMLElement> findElements(HTMLElement parentElement, ObjectMapping m
344
349
/// of executing Javascript in automated tests.
345
350
public <T > T executeJavaScript (Class <T > type , String script , Object ... args )
346
351
{
347
- Object result ;
352
+ Object result = null ;
348
353
try
349
354
{
350
355
result = ((JavascriptExecutor )webDriver ).executeScript (script , args );
351
- return type .cast (result );
356
+ }
357
+ catch (WebDriverException e )
358
+ {
359
+ checkIfConnectionIssue (e );
352
360
}
353
361
catch (Exception ex )
354
362
{
@@ -364,16 +372,36 @@ public <T> T executeJavaScript(Class<T> type, String script, Object... args)
364
372
exceptionString = String .format ("executeJavaScript(\" %s\" ): %s" ,script , exceptionString );
365
373
throw new RuntimeException (exceptionString ,ex );
366
374
}
375
+ return type .cast (result );
367
376
}
368
377
369
378
/// <summary>Injects and executes Javascript in the currently active Selenium browser. If Selenium throws an error, test is aborted.</summary>
370
379
/// <param name="script">Javascript that will be injected into the DOM and executed.</param>
371
380
/// <param name="args">Any arguments passed in to the Javascript</param>
372
381
public void executeJavaScriptNoReturnData (String script , Object [] args )
373
382
{
374
- Object dummy = executeJavaScript (Object .class ,script ,args );
383
+ Object dummy = executeJavaScript (Object .class ,script ,args );
384
+ }
385
+
386
+ public void quit () {
387
+ try {
388
+ if (webDriver != null ) {
389
+ webDriver .quit ();
390
+ }
391
+ }
392
+ catch (Exception e ) {};
375
393
}
376
394
395
+ public void finalize () {
396
+ //
397
+ // We use finalize here in full awareness that it MAY NOT be called and that it may be called after a full clean-up! We are only doing this to prevent
398
+ // a ruckload of browsers accumulating! As finalize is a terrible method to use, we use it VERY carefully!
399
+ //
400
+ try {
401
+ webDriver .quit ();
402
+ }
403
+ catch (Exception e ) {}
404
+ }
377
405
378
406
private void startOrConnectToSeleniumServer (boolean killFirst ) {
379
407
if (isLocalSelenium ) {
@@ -497,7 +525,6 @@ private List<HTMLElement> getHtmlElements(HTMLElement parentElement, ObjectMappi
497
525
return clauseResults ;
498
526
}
499
527
500
-
501
528
private void setPathToDriverIfExistsAndIsExecutable (final String pathToDriver , final String driverExeProperty ,String executable ) {
502
529
final File driver = new File (pathToDriver ,executable );
503
530
if (driver .exists () && driver .canExecute ()) {
@@ -607,7 +634,6 @@ private void killAllProcesses(String name) {
607
634
}
608
635
}
609
636
610
-
611
637
private int getProcessCount (String name ) {
612
638
int count =0 ;
613
639
List <String []> processInstanceCount = getProcessList ();
@@ -639,5 +665,30 @@ private List<String[]> getProcessList() {
639
665
return list ;
640
666
}
641
667
668
+
669
+ private String CallingMethodDetails (StackTraceElement methodBase )
670
+ {
671
+ String methodName ="" ;
672
+ if (methodBase != null )
673
+ {
674
+ methodName = methodBase .getMethodName ();
675
+ if (methodName ==null ) methodName = "<Unknown>" ;
676
+ }
677
+ return String .format ("%s" ,methodName );
678
+ }
679
+
680
+
681
+ private void checkIfConnectionIssue (WebDriverException e ) throws RuntimeException {
682
+ StackTraceElement [] stackTraceElements = Thread .currentThread ().getStackTrace ();
683
+ StackTraceElement caller = stackTraceElements [2 ];
684
+ if (e .getCause ().getClass ()==ConnectException .class ) {
685
+ throw new RuntimeException (String .format ("Selenium Driver method [%s] called but Selenium WebDriver not connected!" ,CallingMethodDetails (caller )),e );
686
+ }
687
+ else
688
+ {
689
+ throw new RuntimeException (String .format ("Selenium Driver method [%s] error using Selenium. See inner exception." ,CallingMethodDetails (caller )),e );
690
+ }
691
+ }
692
+
642
693
}
643
694
0 commit comments