Skip to content

Commit ccd1e95

Browse files
committed
Refactor FormInteractionTrait for improved dropdown handling
- Cleaned up dropdown selection logic to enhance readability and maintainability. - Updated XPath queries for dropdown elements to improve accuracy. - Enhanced error handling and logging for dropdown interactions. - Improved search functionality within dropdowns to ensure better user experience.
1 parent 9b8dbc2 commit ccd1e95

File tree

1 file changed

+114
-115
lines changed

1 file changed

+114
-115
lines changed

tests/AdminCabinet/Lib/Traits/FormInteractionTrait.php

+114-115
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ function () use ($xpath) {
206206
}
207207
}
208208

209-
209+
210210

211211
/**
212212
* Private helper methods
@@ -226,12 +226,12 @@ protected function selectDropdownItem(string $name, string $value, bool $skipIfN
226226
$this->logTestAction("Select dropdown", ['name' => $name, 'value' => $value]);
227227

228228
try {
229-
// Get semantic UI dropdown element
229+
// Находим dropdown
230230
$dropdownXpath = sprintf(
231231
'//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"]]',
235235
$name
236236
);
237237

@@ -245,82 +245,89 @@ protected function selectDropdownItem(string $name, string $value, bool $skipIfN
245245
return null;
246246
}
247247

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);
251250

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) {
258252
$currentValue = $selectedItem->getAttribute('data-value');
259253
$currentText = $selectedItem->getText();
260254

261-
// If current value or text matches desired value, no need to proceed
262255
if ($currentValue === $value || $currentText === $value || stripos($currentText, $value) !== false) {
263-
return $currentValue ?: $selectedItem->getAttribute('data-text') ?: $currentText;
256+
return $currentValue ?: $currentText;
264257
}
265-
} catch (\Exception $e) {
266-
// No selection found or error getting current selection - proceed with new selection
267258
}
268259

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"]',
284283
$value
285284
);
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"]',
294292
$value
295293
);
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+
}
298309
}
299-
300-
if (!$menuItem && !$skipIfNotExist) {
301-
// Close dropdown before throwing exception
302-
try {
310+
311+
if (!$itemFound && !$skipIfNotExist) {
312+
if ($isDropdownVisible) {
303313
$dropdown->click();
304-
} catch (\Exception $e) {
305-
// Ignore errors on closing
306314
}
307315
throw new RuntimeException("Menu item '{$value}' not found in dropdown '{$name}'");
308316
}
309-
310-
if ($menuItem) {
317+
318+
if ($itemFound) {
311319
$dataValue = $menuItem->getAttribute('data-value');
312-
313-
// Scroll and click the found menu item
320+
314321
$this->scrollIntoView($menuItem);
315322
$menuItem->click();
316323
$this->waitForAjax();
317-
318-
return $dataValue ?: $menuItem->getAttribute('data-text') ?: $menuItem->getText();
324+
325+
return $dataValue ?: $menuItem->getText();
319326
}
320-
327+
321328
return null;
322329
} 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);
324331
return null;
325332
}
326333
}
@@ -337,79 +344,72 @@ protected function checkIfElementExistOnDropdownMenu(string $name, string $value
337344
$this->logTestAction("Check dropdown element", ['name' => $name, 'value' => $value]);
338345

339346
try {
340-
// Find Semantic UI dropdown
341-
$xpath = sprintf(
347+
$dropdownXpath = sprintf(
342348
'//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"]]',
346352
$name
347353
);
348354

349-
$dropdown = $this->findElementSafely($xpath);
350-
355+
$dropdown = $this->findElementSafely($dropdownXpath);
356+
351357
if (!$dropdown) {
352358
return false;
353359
}
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) {
361365
$currentValue = $selectedItem->getAttribute('data-value');
362366
$currentText = $selectedItem->getText();
363-
364-
// If already selected, return true
367+
365368
if ($currentValue === $value || $currentText === $value || stripos($currentText, $value) !== false) {
366369
return true;
367370
}
368-
} catch (\Exception $e) {
369-
// Not selected, continue checking
370371
}
371372

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"]',
386384
$value
387385
);
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-
}
401386

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+
}
408408
}
409409

410-
return $menuItem !== null;
410+
return $itemExists;
411411
} catch (\Exception $e) {
412-
self::annotate("Element check failed: " . $e->getMessage(), 'warning');
412+
$this->handleActionError('check dropdown element', "{$name} with value {$value}", $e);
413413
return false;
414414
}
415415
}
@@ -424,9 +424,9 @@ private function fillDropdownSearch(string $name, string $value): void
424424
{
425425
$xpath = sprintf(
426426
'//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")]',
430430
$name
431431
);
432432

@@ -436,7 +436,7 @@ private function fillDropdownSearch(string $name, string $value): void
436436
$searchInput->click();
437437
$searchInput->clear();
438438
$searchInput->sendKeys($value);
439-
439+
440440
// Small delay to allow filtering to occur
441441
usleep(300000); // 300ms
442442
} catch (\Exception $e) {
@@ -459,5 +459,4 @@ private function waitForDropdownMenu(): void
459459
)
460460
);
461461
}
462-
463462
}

0 commit comments

Comments
 (0)