@@ -206,7 +206,7 @@ function () use ($xpath) {
206
206
}
207
207
}
208
208
209
-
209
+
210
210
211
211
/**
212
212
* Private helper methods
@@ -226,12 +226,12 @@ protected function selectDropdownItem(string $name, string $value, bool $skipIfN
226
226
$ this ->logTestAction ("Select dropdown " , ['name ' => $ name , 'value ' => $ value ]);
227
227
228
228
try {
229
- // Get semantic UI dropdown element
229
+ // Находим dropdown
230
230
$ dropdownXpath = sprintf (
231
231
'//select[@name="%1$s"]/ancestor::div[contains(@class, "dropdown")] | ' .
232
- '//input[@name="%1$s"]/ancestor::div[contains(@class, "dropdown")] | ' .
233
- '//div[contains(@class, "dropdown")][@id="%1$s"] | ' .
234
- '//div[contains(@class, "dropdown")][.//select[@name="%1$s"]] ' ,
232
+ '//input[@name="%1$s"]/ancestor::div[contains(@class, "dropdown")] | ' .
233
+ '//div[contains(@class, "dropdown")][@id="%1$s"] | ' .
234
+ '//div[contains(@class, "dropdown")][.//select[@name="%1$s"]] ' ,
235
235
$ name
236
236
);
237
237
@@ -245,82 +245,89 @@ protected function selectDropdownItem(string $name, string $value, bool $skipIfN
245
245
return null ;
246
246
}
247
247
248
- if ($ dropdown ) {
249
- $ elementSource = $ dropdown ->getAttribute ('outerHTML ' );
250
- }
248
+ $ selectedItemXpath = './/div[contains(@class, "item") and contains(@class, "active") and contains(@class, "selected")] ' ;
249
+ $ selectedItem = $ this ->findElementSafely ($ selectedItemXpath , $ dropdown );
251
250
252
- // Check if the desired value is already selected
253
- try {
254
- $ selectedItem = $ dropdown ->findElement (
255
- WebDriverBy::xpath ('.//div[contains(@class, "item") and contains(@class, "active selected")] ' )
256
- );
257
-
251
+ if ($ selectedItem ) {
258
252
$ currentValue = $ selectedItem ->getAttribute ('data-value ' );
259
253
$ currentText = $ selectedItem ->getText ();
260
254
261
- // If current value or text matches desired value, no need to proceed
262
255
if ($ currentValue === $ value || $ currentText === $ value || stripos ($ currentText , $ value ) !== false ) {
263
- return $ currentValue ?: $ selectedItem -> getAttribute ( ' data-text ' ) ?: $ currentText ;
256
+ return $ currentValue ?: $ currentText ;
264
257
}
265
- } catch (\Exception $ e ) {
266
- // No selection found or error getting current selection - proceed with new selection
267
258
}
268
259
269
-
270
- // Click to open dropdown
271
- $ this ->scrollIntoView ($ dropdown );
272
- $ dropdown ->click ();
273
-
274
- // Wait for dropdown menu to be visible
275
- $ this ->waitForDropdownMenu ();
276
-
277
- // Try to use search input if available
278
- $ this ->fillDropdownSearch ($ name , $ value );
279
-
280
- // First try to find exact match by data-value or text()
281
- $ exactMenuXpath = sprintf (
282
- '//div[contains(@class, "menu") and contains(@class, "visible")] ' .
283
- '//div[contains(@class, "item") and (@data-value="%1$s" or normalize-space(text())="%1$s")] ' ,
260
+ $ inputXpath = sprintf ('.//input[@name="%s" and @type="hidden"] ' , $ name );
261
+ $ input = $ this ->findElementSafely ($ inputXpath , $ dropdown );
262
+
263
+ if ($ input ) {
264
+ $ inputValue = $ input ->getAttribute ('value ' );
265
+ if ($ inputValue === $ value ) {
266
+ return $ inputValue ;
267
+ }
268
+ }
269
+
270
+ $ isDropdownVisible = strpos ($ dropdown ->getAttribute ('class ' ), 'active visible ' ) !== false ;
271
+ if (!$ isDropdownVisible ) {
272
+ $ this ->scrollIntoView ($ dropdown );
273
+ $ dropdown ->click ();
274
+ $ this ->waitForDropdownMenu ();
275
+ }
276
+
277
+ $ this ->fillDropdownSearch ($ dropdown , $ value );
278
+
279
+ $ itemFound = false ;
280
+
281
+ $ exactValueXpath = sprintf (
282
+ './/div[contains(@class, "menu")]//div[contains(@class, "item") and @data-value="%s"] ' ,
284
283
$ value
285
284
);
286
-
287
- $ menuItem = $ this ->findElementSafely ($ exactMenuXpath );
288
-
289
- // If exact match not found, try partial text match
290
- if (!$ menuItem ) {
291
- $ partialMenuXpath = sprintf (
292
- '//div[contains(@class, "menu") and contains(@class, "visible")] ' .
293
- '//div[contains(@class, "item") and contains(normalize-space(text()),"%s")] ' ,
285
+ $ menuItem = $ this ->findElementSafely ($ exactValueXpath , $ dropdown );
286
+
287
+ if ($ menuItem ) {
288
+ $ itemFound = true ;
289
+ } else {
290
+ $ exactTextXpath = sprintf (
291
+ './/div[contains(@class, "menu")]//div[contains(@class, "item") and normalize-space(text())="%s"] ' ,
294
292
$ value
295
293
);
296
-
297
- $ menuItem = $ this ->findElementSafely ($ partialMenuXpath );
294
+ $ menuItem = $ this ->findElementSafely ($ exactTextXpath , $ dropdown );
295
+
296
+ if ($ menuItem ) {
297
+ $ itemFound = true ;
298
+ } else {
299
+ $ partialTextXpath = sprintf (
300
+ './/div[contains(@class, "menu")]//div[contains(@class, "item") and contains(normalize-space(text()),"%s")] ' ,
301
+ $ value
302
+ );
303
+ $ menuItem = $ this ->findElementSafely ($ partialTextXpath , $ dropdown );
304
+
305
+ if ($ menuItem ) {
306
+ $ itemFound = true ;
307
+ }
308
+ }
298
309
}
299
-
300
- if (!$ menuItem && !$ skipIfNotExist ) {
301
- // Close dropdown before throwing exception
302
- try {
310
+
311
+ if (!$ itemFound && !$ skipIfNotExist ) {
312
+ if ($ isDropdownVisible ) {
303
313
$ dropdown ->click ();
304
- } catch (\Exception $ e ) {
305
- // Ignore errors on closing
306
314
}
307
315
throw new RuntimeException ("Menu item ' {$ value }' not found in dropdown ' {$ name }' " );
308
316
}
309
-
310
- if ($ menuItem ) {
317
+
318
+ if ($ itemFound ) {
311
319
$ dataValue = $ menuItem ->getAttribute ('data-value ' );
312
-
313
- // Scroll and click the found menu item
320
+
314
321
$ this ->scrollIntoView ($ menuItem );
315
322
$ menuItem ->click ();
316
323
$ this ->waitForAjax ();
317
-
318
- return $ dataValue ?: $ menuItem ->getAttribute ( ' data-text ' ) ?: $ menuItem -> getText ();
324
+
325
+ return $ dataValue ?: $ menuItem ->getText ();
319
326
}
320
-
327
+
321
328
return null ;
322
329
} catch (\Exception $ e ) {
323
- $ this ->handleActionError ('select dropdown item ' , "{$ name } with value {$ value }" , $ e, $ elementSource ?? '' );
330
+ $ this ->handleActionError ('select dropdown item ' , "{$ name } with value {$ value }" , $ e );
324
331
return null ;
325
332
}
326
333
}
@@ -337,79 +344,72 @@ protected function checkIfElementExistOnDropdownMenu(string $name, string $value
337
344
$ this ->logTestAction ("Check dropdown element " , ['name ' => $ name , 'value ' => $ value ]);
338
345
339
346
try {
340
- // Find Semantic UI dropdown
341
- $ xpath = sprintf (
347
+ $ dropdownXpath = sprintf (
342
348
'//select[@name="%1$s"]/ancestor::div[contains(@class, "dropdown")] | ' .
343
- '//input[@name="%1$s"]/ancestor::div[contains(@class, "dropdown")] | ' .
344
- '//div[contains(@class, "dropdown")][@id="%1$s"] | ' .
345
- '//div[contains(@class, "dropdown")][.//select[@name="%1$s"]] ' ,
349
+ '//input[@name="%1$s"]/ancestor::div[contains(@class, "dropdown")] | ' .
350
+ '//div[contains(@class, "dropdown")][@id="%1$s"] | ' .
351
+ '//div[contains(@class, "dropdown")][.//select[@name="%1$s"]] ' ,
346
352
$ name
347
353
);
348
354
349
- $ dropdown = $ this ->findElementSafely ($ xpath );
350
-
355
+ $ dropdown = $ this ->findElementSafely ($ dropdownXpath );
356
+
351
357
if (!$ dropdown ) {
352
358
return false ;
353
359
}
354
-
355
- // First check if it's already selected
356
- try {
357
- $ selectedItem = $ dropdown ->findElement (
358
- WebDriverBy::xpath ('.//div[contains(@class, "item") and contains(@class, "active selected")] ' )
359
- );
360
-
360
+
361
+ $ selectedItemXpath = './/div[contains(@class, "item") and contains(@class, "active") and contains(@class, "selected")] ' ;
362
+ $ selectedItem = $ this ->findElementSafely ($ selectedItemXpath , $ dropdown );
363
+
364
+ if ($ selectedItem ) {
361
365
$ currentValue = $ selectedItem ->getAttribute ('data-value ' );
362
366
$ currentText = $ selectedItem ->getText ();
363
-
364
- // If already selected, return true
367
+
365
368
if ($ currentValue === $ value || $ currentText === $ value || stripos ($ currentText , $ value ) !== false ) {
366
369
return true ;
367
370
}
368
- } catch (\Exception $ e ) {
369
- // Not selected, continue checking
370
371
}
371
372
372
- // Click to open dropdown
373
- $ this ->scrollIntoView ($ dropdown );
374
- $ dropdown ->click ();
375
-
376
- // Wait for dropdown menu to be visible
377
- $ this ->waitForDropdownMenu ();
378
-
379
- // Try to use search input if available
380
- $ this ->fillDropdownSearch ($ name , $ value );
381
-
382
- // Look for item by both data-value and text content
383
- $ exactMenuXpath = sprintf (
384
- '//div[contains(@class, "menu") and contains(@class, "visible")] ' .
385
- '//div[contains(@class, "item") and (@data-value="%1$s" or normalize-space(text())="%1$s")] ' ,
373
+ $ isDropdownVisible = strpos ($ dropdown ->getAttribute ('class ' ), 'active visible ' ) !== false ;
374
+ if (!$ isDropdownVisible ) {
375
+ $ this ->scrollIntoView ($ dropdown );
376
+ $ dropdown ->click ();
377
+ $ this ->waitForDropdownMenu ();
378
+ }
379
+
380
+ $ this ->fillDropdownSearch ($ dropdown , $ value );
381
+
382
+ $ exactValueXpath = sprintf (
383
+ './/div[contains(@class, "menu")]//div[contains(@class, "item") and @data-value="%s"] ' ,
386
384
$ value
387
385
);
388
-
389
- $ menuItem = $ this ->findElementSafely ($ exactMenuXpath );
390
-
391
- // If exact match not found, try partial text match
392
- if (!$ menuItem ) {
393
- $ partialMenuXpath = sprintf (
394
- '//div[contains(@class, "menu") and contains(@class, "visible")] ' .
395
- '//div[contains(@class, "item") and contains(normalize-space(text()),"%s")] ' ,
396
- $ value
397
- );
398
-
399
- $ menuItem = $ this ->findElementSafely ($ partialMenuXpath );
400
- }
401
386
402
- // Close dropdown after check regardless of result
403
- try {
404
- $ dropdown ->click ();
405
- } catch (\Exception $ e ) {
406
- // Ignore errors on closing
407
- self ::annotate ("Failed to close dropdown: " . $ e ->getMessage (), 'warning ' );
387
+ $ exactTextXpath = sprintf (
388
+ './/div[contains(@class, "menu")]//div[contains(@class, "item") and normalize-space(text())="%s"] ' ,
389
+ $ value
390
+ );
391
+
392
+ $ partialTextXpath = sprintf (
393
+ './/div[contains(@class, "menu")]//div[contains(@class, "item") and contains(normalize-space(text()),"%s")] ' ,
394
+ $ value
395
+ );
396
+
397
+ $ itemExists = (
398
+ $ this ->findElementSafely ($ exactValueXpath , $ dropdown ) !== null ||
399
+ $ this ->findElementSafely ($ exactTextXpath , $ dropdown ) !== null ||
400
+ $ this ->findElementSafely ($ partialTextXpath , $ dropdown ) !== null
401
+ );
402
+
403
+ if ($ isDropdownVisible ) {
404
+ try {
405
+ $ dropdown ->click ();
406
+ } catch (\Exception $ e ) {
407
+ }
408
408
}
409
409
410
- return $ menuItem !== null ;
410
+ return $ itemExists ;
411
411
} catch (\Exception $ e ) {
412
- self :: annotate ( " Element check failed: " . $ e -> getMessage (), ' warning ' );
412
+ $ this -> handleActionError ( ' check dropdown element ' , "{ $ name } with value { $ value }" , $ e );
413
413
return false ;
414
414
}
415
415
}
@@ -424,9 +424,9 @@ private function fillDropdownSearch(string $name, string $value): void
424
424
{
425
425
$ xpath = sprintf (
426
426
'//select[@name="%1$s"]/ancestor::div[contains(@class, "dropdown")]/input[contains(@class,"search")] | ' .
427
- '//input[@name="%1$s"]/ancestor::div[contains(@class, "dropdown")]/input[contains(@class,"search")] | ' .
428
- '//div[@id="%1$s"]/input[contains(@class,"search")] | ' .
429
- '//div[contains(@class, "dropdown")][.//select[@name="%1$s"]]/input[contains(@class,"search")] ' ,
427
+ '//input[@name="%1$s"]/ancestor::div[contains(@class, "dropdown")]/input[contains(@class,"search")] | ' .
428
+ '//div[@id="%1$s"]/input[contains(@class,"search")] | ' .
429
+ '//div[contains(@class, "dropdown")][.//select[@name="%1$s"]]/input[contains(@class,"search")] ' ,
430
430
$ name
431
431
);
432
432
@@ -436,7 +436,7 @@ private function fillDropdownSearch(string $name, string $value): void
436
436
$ searchInput ->click ();
437
437
$ searchInput ->clear ();
438
438
$ searchInput ->sendKeys ($ value );
439
-
439
+
440
440
// Small delay to allow filtering to occur
441
441
usleep (300000 ); // 300ms
442
442
} catch (\Exception $ e ) {
@@ -459,5 +459,4 @@ private function waitForDropdownMenu(): void
459
459
)
460
460
);
461
461
}
462
-
463
462
}
0 commit comments