[Перевод] Обработка окон и вкладок браузера в Selenium PHP
Один из распространённых сценариев работы с веб-приложениями заключается в открывании нового окна (или вкладки) браузера после выполнения пользователем определённого действия. Многие веб-разработчики используют HTML-тэг __blank
, приказывающий браузеру при нажатии на ссылку открыть новое окно (или вкладку, это зависит от выбранных пользователем настроек). Работа с окнами в Selenium при помощи PHP может использоваться для автоматизации взаимодействия с окнами, вкладками и даже всплывающими окнами браузера.
Всплывающие окна могут иногда раздражать посетителей веб-сайта, однако зачастую у разработчика нет иного выхода, кроме как использовать их. Как Selenium различает окна и вкладки? Как можно использовать автоматизацию для беспроблемного переключения между окнами или вкладками браузера? Как реализовать работу с окнами в Selenium?
В этой статье мы подробно рассмотрим применение Selenium для автоматизации взаимодействия с браузерами, вкладками и всплывающими окнами. Для демонстрации работы с окнами в Selenium PHP мы воспользуемся PHPUnit — фреймворком юнит-тестирования для PHP.
Что такое дескриптор окна?
Дескриптор окна (window handle) — это уникальный идентификатор, основная задача которого заключается в хранении адресов всех окон. При создании экземпляра Selenium WebDriver окну назначается алфавитно-цифровой ID. Этот уникальный ID называется дескриптором окна — указателем на окно, позволяющим идентифицировать окно браузера.
Для каждого окна/вкладки/всплывающего окна дескриптор окна (или ID) уникален. Для переключения между окнами (или вкладками) Selenium WebDriver использует функции дескрипторов окон в Selenium PHP.
Уникальный ID сохраняется до закрытия сессии Selenium WebDriver (выполняемого через API WebDriver.Quit или WebDriver.Close). Функции дескрипторов окон используются для получения подробной информации о дескрипторах всех окон. Фундаментальные основы работы с окнами в Selenium остаются неизменными, вне зависимости от того, используете ли вы Selenium с PHP или Selenium с другими языками программирования (например, Python, Java, и т. п.).
Вот некоторые из самых распространённых сценариев, в которых приходится иметь дело с множественными окнами (или вкладками):
- Формы, требующие выбора даты в новом открытом окне
- Нажатия кнопок, открывающие новую вкладку (или окно)
- Всплывающие окна, используемые для показа конечному пользователю каких-либо предложений; эту стратегию часто используют порталы с вакансиями
- Работа с окнами, отображающими рекламу
Пример сценария, в котором нажатие на родительское (или базовое) окно открывает два «дочерних окна». Каждое из них имеет уникальный дескриптор окна, и этот дескриптор хранится, пока окна не будут уничтожены (т. е. закрыты). Общее количество окон становится равным трём (parent + child-1 + child-2). При нажатии кнопки/ссылки в «Child-1» и «Child-2», соответственно, откроется окно «Grand Child-1» и «Grand Child-2». После этого суммарно будет открыто пять окон, и каждое из них будет иметь уникальный дескриптор окна, который можно использовать для автоматизации операций с окном.
Команды для работы с окнами в Selenium с PHP
Selenium предоставляет различные методы для работы с несколькими окнами. Вы можете также изучить наше руководство по работе с несколькими окнами при помощи Selenium и Protractor. Ниже представлены самые часто используемые при тестировании Selenium PHP команды для переключения окон браузера и работы со всплывающими окнами.
Если вы специалист по PHP, то вы можете получить сертификат специализации в программировании на базовом PHP и расширить свои возможности для развития своей карьеры в сфере автоматического тестирования PHP.
▍ SwitchTo Window
Команда SwitchTo
используется для переключения фокуса на новое окно или вкладку браузера. Для переключения фокуса на новое окно в качестве аргумента команде передаётся дескриптор окна соответствующего окна браузера.
/* $wHandle - это дескриптор окна (или ID), на которое нужно выполнить переключение */
$this->webDriver->switchTo()->window($wHandle);
/* Переключение также можно выполнять получением количества окон при помощи getWindowHandles или getWindowHandle */
$this->webDriver->switchTo()->window($HandleCount[win-number]);
▍ getWindowHandle
Метод getWindowHandle
в Selenium PHP возвращает Window ID (уникальный алфавитно-цифровой идентификатор окна) для текущего активного (или находящегося в фокусе) окна.
$wHandle = $this->webDriver->getWindowHandle();
В показанном выше примере $wHandle
— это ID окна, полученный при помощи метода getWindowHandle
.
▍ getWindowHandles
Это важный метод для работы с окнами в Selenium с PHP. Метод getWindowHandles
возвращает множество дескрипторов окон (и вкладок), открытых одним экземпляром драйвера, в том числе родительских и дочерних окон. Например, если операция «щелчок на кнопке» в родительском окне открывает новую вкладку, то метод getWindowHandles
вернёт дескрипторы родительского и дочернего окна (например, вкладки). Если затем к множеству, возвращённому методом getWindowHandles
, применить оператор sizeof
, то он вернёт размер множества (в данном случае 2).
Аналогично, если открытая в родительском окне веб-страница открывает 8 всплывающих окон, то количество дескрипторов, полученных применением оператора sizeof
для множества, возвращённого getWindowHandles
, будет 9.
Каждое из окон будет иметь уникальный дескриптор окна (или идентификатор) для удобства идентификации окна.
/* В случае веб-страницы, открывающей новую вкладку, getWindowHandles возвращает массив, состоящий из двух дескрипторов окон (т. е. дескрипторов родительского и дочернего окон) */
$HandleCount = $this->webDriver->getWindowHandles();
/* Возвращает размер массива дескрипторов окон. В данном примере он будет равен 2 */
echo ("\n Total number of window handles are " . sizeof($HandleCount));
/* Печать дескриптора окна в родительском окне */
echo ("\n Window 0: " . $HandleCount[0]);
/* Печать дескриптора окна в дочернем окне */
echo ("\n Window 0: " . $HandleCount[1]);
Как работать с несколькими окнами, вкладками и всплывающими окнами в Selenium PHP
Мы продемонстрируем сценарии тестов работы с окнами в Selenium PHP при помощи PHPUnit в облачном Selenium Grid сервиса LambdaTest. Кросс-браузерное тестирование при помощи PHPUnit в облачном Selenium Grid помогает в тестировании на различных сочетаниях браузеров, платформ и эмуляторов устройств.
Для начала создадим аккаунт LambdaTest и обратим внимание, что user-name и access-key доступны на странице профиля. Тесты выполняются на сочетании Chrome 85.0 + Windows 10. Возможности браузера сгенерированы при помощи генератора возможностей LambdaTest.
Для установки фреймворка PHPUnit мы создаём для этого проекта файл composer.json:
{
"require":{
"php":">=7.1",
"phpunit/phpunit":"^9",
"phpunit/phpunit-selenium": "*",
"php-webdriver/webdriver":"1.8.0",
"symfony/symfony":"4.4",
"brianium/paratest": "dev-master"
}
}
Введём команду composer require и дважды нажмём на кнопку «Enter», чтобы приступить к установке фреймворка PHPUnit. После завершения фреймворк PHPUnit (версии 9.3) будет установлен.
Файл composer.lock содержит информацию о зависимостях, а в папке vendor содержатся все зависимости.
Файл vendor/autoload.php будет использоваться в коде тестов, чтобы классы (и их методы), предоставленные этими библиотеками, можно было использовать в реализации.
Теперь настало время продемонстрировать различные сценарии работы с окнами в Selenium с PHP.
▍ Работа с несколькими окнами браузера в Selenium PHP
Для демонстрации работы с несколькими окнами в Selenium PHP мы используем следующий тестовый сценарий:
- Открываем страницу LambdaTest в браузере Chrome.
- Получаем дескриптор текущего окна в фокусе.
- Открываем страницу блога LambdaTest в новом окне при помощи HTML-атрибута __blank.
- Печатаем соответствующие дескрипторы окон.
- Переключаемся на окно, где открыт второй URL.
- Добавляем утверждение на случай, если заголовок окна не соответствует ожидаемому заголовку.
- Закрываем окно в фокусе.
- Переключаемся на первое окно и добавляем утверждение на случай, если заголовок окна не соответствует ожидаемому заголовку.
- Закрываем окно браузера.
❒ Реализация
"[PHP] Window Switching with Chrome on Windows 10",
"name" => "[PHP] Window Switching with Chrome on Windows 10",
"platform" => "Windows 10",
"browserName" => "Chrome",
"version" => "85.0"
);
return $capabilities;
}
public function setUp(): void
{
$url = "https://". $GLOBALS['LT_USERNAME'] .":" . $GLOBALS['LT_APPKEY'] ."@hub.lambdatest.com/wd/hub";
$capabilities = $this->build_browser_capabilities();
/* Скачать Selenium Server 3.141.59 с
https://selenium-release.storage.googleapis.com/3.141/selenium-server-standalone-3.141.59.jar
*/
/* $this->webDriver = RemoteWebDriver::create('http://localhost:4444/wd/hub', $capabilities); */
$this->webDriver = RemoteWebDriver::create($url, $capabilities);
}
public function tearDown(): void
{
$this->webDriver->quit();
}
/*
* @test
*/
public function test_SwitchToNewWindow()
{
$test_url_1 = "https://www.lambdatest.com";
$title_1 = "Most Powerful Cross Browser Testing Tool Online | LambdaTest";
$test_url_2 = "https://www.lambdatest.com/blog/";
$title_2 = "LambdaTest | A Cross Browser Testing Blog";
$this->webDriver->get($test_url_1);
$this->webDriver->manage()->window()->maximize();
$wHandle = $this->webDriver->getWindowHandle();
/* echo ("\n Primary Window Handle is " . $wHandle ); */
sleep(5);
/* Открываем второе окно */
/* $link = "window.open('https://www.lambdatest.com/blog/', '_blank', 'toolbar=yes,scrollbars=yes,resizable=yes,width=800,height=800')"; */
$link = "window.open('". $test_url_2 ."', '_blank', 'toolbar=yes,scrollbars=yes,resizable=yes,width=1200,height=1200')";
$this->webDriver->executeScript($link);
/* $this->webDriver->manage()->window()->maximize(); */
/* Фокус теперь на втором окне */
/* Количество дескрипторов будет равным двум */
$HandleCount = $this->webDriver->getWindowHandles();
echo ("\n Total number of window handles are " . sizeof($HandleCount));
echo ("\n Window 0: " . $HandleCount[0]);
echo ("\n Window 1: " . $HandleCount[1]);
sleep(10);
/* Создаём утверждение на случай, если количество окон не равно 2 */
$this->assertEquals(2, sizeof($HandleCount));
/* Проверяем соответствие заголовков окон */
$this->webDriver->switchTo()->window($HandleCount[1]);
$win_title_2 = $this->webDriver->getTitle();
echo ("\n Title of the window 1 is " . $win_title_2);
sleep(10);
$this->assertEquals($win_title_2, $title_2);
/* Закрываем новое открытое окно и возвращаемся к старому окну */
$this->webDriver->close();
sleep(10);
/* Возвращаемся к окну с дескриптором = 0 */
$this->webDriver->switchTo()->window($wHandle);
/* Проверяем, совпадают ли заголовки окон */
$win_title_1 = $this->webDriver->getTitle();
echo ("\n Title of the window 0 is " . $win_title_1);
$this->assertEquals($win_title_1, $title_1);
sleep(10);
}
}
?>
❒ Разбор кода
Существенная часть реализации в этой части туториала по Selenium PHP остаётся той же, которая была использована для работы с окнами в Selenium с PHP.
1. При открытиитестового URL для получения дескрипторов открытых окон браузера используется метод getWindowHandles
WebDriver.
public function test_SwitchToNewTab()
{
$test_url = "http://automationpractice.com/index.php";
$title_1 = "My Store";
$title_2 = "Selenium Framework - YouTube";
......................................
......................................
$this->webDriver->get($test_url);
$this->webDriver->manage()->window()->maximize();
......................................
......................................
$HandleCount = $this->webDriver->getWindowHandles();
2. В конце страницы размещается веб-элемент с гиперссылкой на YouTube-канал сайта. При помощи метода executeScript
, предоставленного JavaScriptExecutor, исполняется метод window.scrollTo
JavaScript. Он выполняет переход к концу страницы.
$link = "window.scrollTo(0, document.body.scrollHeight)";
$this->webDriver->executeScript($link);
3. Для получения информации о требуемом веб-элементе мы используем расширение POM Builder для Chrome, чтобы получить информацию локатора (т. е. XPath).
Для получения информации о веб-элементе [with XPath – //a[contains(.,’Youtube’)]
используется метод findElement класса WebDriverBy
.
$browser_button = $this->webDriver->findElement(WebDriverBy::XPath("//a[contains(.,'Youtube')]"));
4. Для найденного веб-элемента вызывается метод click
.
$browser_button->click();
5. Метод getWindowHandles
возвращает алфавитно-цифровой массив (или множество), содержащий дескрипторы (или ID) текущих открытых окон (или вкладок) браузера. В нашем случае количество открытых окон будет равным 2.
Следовательно, после применения оператора sizeof
к массиву (или множеству), возвращённому getWindowHandles
, вернёт 2.
$HandleCount = $this->webDriver->getWindowHandles();
echo ("\n Total number of window handles are " . sizeof($HandleCount));
echo ("\n Window 0: " . $HandleCount[0]);
echo ("\n Window 1: " . $HandleCount[1]);
6. Для переключения на второе окно (т. е. $HandleCount[1]
) используется метод switchTo
Selenium WebDriver. Если заголовок окна не соответствует ожидаемому заголовку, срабатывает утверждение.
$this->webDriver->switchTo()->window($HandleCount[1]);
$win_title_2 = $this->webDriver->getTitle();
$this->assertEquals($win_title_2, $title_2);
7. Метод close
закрывает окно в фокусе (т. е. вкладку, где открыт YouTube-канал).
$this->webDriver->close();
8. Выполняется переключение на родительское окно (т. е. $HandleCount[0]
). Если заголовки не совпадают, срабатывает утверждение.
$this->webDriver->switchTo()->window($HandleCount[0]);
$win_title_1 = $this->webDriver->getTitle();
$this->assertEquals($win_title_1, $title_1);
❒ Исполнение
Вот дескрипторы двух окон браузера, созданных при тестировании.
Ниже показан снэпшот исполнения автоматизированного теста Selenium:
Как видно из снэпшота, дескрипторы родительского окна и вкладки уникальны. Когда окно и вкладка открыты, размер массива дескрипторов окон равен 2.
Теперь, когда мы увидели реализацию и исполнение работы с окнами и несколькими вкладками в Selenium с PHP, давайте подробнее узнаем о работе с несколькими всплывающими окнами браузера.
▍ Работа с несколькими всплывающими окнами браузера в Selenium с PHP
Для демонстрации работы со всплывающими окнами браузера в Selenium PHP мы используем следующий тестовый сценарий:
- Откроем www.popuptest.com/popuptest1.html в браузере Chrome.
- Закроем все всплывающие окна в обратном хронологическом порядке.
- Проверим, соответствует ли заголовок родительского окна ожидаемому заголовку.
Если тот же тест выполняется в локальном Selenium Grid, то нужно убедиться, что для Google Chrome включены всплывающие окна. Для включения всплывающих окон для Chrome на локальной машине перейдите по адресу chrome://settings/ -> Privacy and security -> Site Settings -> Pop-ups and redirects
. Отключите опцию Block для www.popuptest.com:80.
❒ Реализация
"[PHP] Pop Up Testing with Chrome on Windows 10",
"name" => "[PHP] Pop Up Testing with Chrome on Windows 10",
"platform" => "Windows 10",
"browserName" => "Chrome",
"version" => "85.0"
);
return $capabilities;
}
public function setUp(): void
{
$url = "https://". $GLOBALS['LT_USERNAME'] .":" . $GLOBALS['LT_APPKEY'] ."@hub.lambdatest.com/wd/hub";
$capabilities = $this->build_browser_capabilities();
/* Скачать Selenium Server 3.141.59 с
https://selenium-release.storage.googleapis.com/3.141/selenium-server-standalone-3.141.59.jar
*/
/* $this->webDriver = RemoteWebDriver::create('http://localhost:4444/wd/hub', $capabilities); */
$this->webDriver = RemoteWebDriver::create($url, $capabilities);
}
public function tearDown(): void
{
$this->webDriver->quit();
}
/*
* @test
*/
public function test_SwitchToNewWindow()
{
$test_url = "http://www.popuptest.com/popuptest1.html";
$title = "PopupTest 1 - test your popup killer software";
$this->webDriver->get($test_url);
sleep(5);
/* Это откроет основное окно и шесть всплывающих окон */
/* После загрузки страницы общее количество страниц будет равным 7 */
$HandleCount = $this->webDriver->getWindowHandles();
/* Это ID родительского окна */
$mainHandle = $HandleCount[0];
echo ("\n Total number of window handles are " . sizeof($HandleCount));
echo ("\n Window 0: " . $HandleCount[0]);
$win_title = $this->webDriver->getTitle();
echo ("\n Title of the parent window is " . $win_title);
foreach( $HandleCount as $handle)
{
if($handle != $mainHandle)
{
echo ("\n Window handle of the current window: " . $handle);
$this->webDriver->switchTo()->window($handle);
echo ("\n Title of the current window: " . $this->webDriver->getTitle());
/* Закрываем всплывающее окно и возвращаемся к старому окну */
$this->webDriver->close();
sleep(2);
}
}
$this->webDriver->switchTo()->window($mainHandle);
$this->webDriver->manage()->window()->maximize();
sleep(5);
$curr_window_title = $this->webDriver->getTitle();
echo ("\n\n Title of the only left window: " . $curr_window_title);
$this->assertEquals($curr_window_title, $title);
sleep(5);
}
}
?>
❒ Разбор кода
1. Так как для тестирования используется Selenium Grid на LambdaTest, user-name и access key хранятся в глобальных переменных. Того же результата можно получить, перейдя на страницу профиля на LambdaTest.
$GLOBALS['LT_USERNAME'] = "user-name";
# accessKey: AccessKey можно сгенерировать в панели автоматизации или в разделе профиля
$GLOBALS['LT_APPKEY'] = "access-key";
2. При помощи LambdaTest Capabilities Generator генерируются возможности браузера.
$capabilities = array(
"build" => "[PHP] Window Switching with Chrome on Windows 10",
"name" => "[PHP] Window Switching with Chrome on Windows 10",
"platform" => "Windows 10",
"browserName" => "Chrome",
"version" => "85.0"
);
3. Для доступа к Selenium Grid on LambdaTest (@hub.lambdatest.com/wd/hub
) используется сочетание глобальных переменных, в которых хранятся user-name и access-key. Метод create
в классе RemoteWebDriver
получает в качестве первого параметра URL Selenium Grid, а в качестве второго — возможности браузера.
$url = "https://". $GLOBALS['LT_USERNAME'] .":" . $GLOBALS['LT_APPKEY'] ."@hub.lambdatest.com/wd/hub";
$capabilities = $this->build_browser_capabilities();
$this->webDriver = RemoteWebDriver::create($url, $capabilities);
4. В тестовом методе test_SwitchToNewWindow
открывается тестовый URL https://www.lambdatest.com
и используется предоставляемый Selenium WebDriver метод getWindowHandle
для получения дескриптора текущего окна.
public function test_SwitchToNewWindow()
{
$test_url_1 = "https://www.lambdatest.com";
$title_1 = "Most Powerful Cross Browser Testing Tool Online | LambdaTest";
...............................................
...............................................
$this->webDriver->get($test_url_1);
$wHandle = $this->webDriver->getWindowHandle();
...............................................
}
5. Для открытия нового вторичного окна браузера используется метод JavaScript window.open
. Методу также передаются ширина и высота окна. Для исполнения сформированного кода JavaScript в контексте текущего открытого окна используется метод executeScript
, предоставляемый JavaScriptExecutor в Selenium PHP.
$link = "window.open('". $test_url_2 ."', '_blank', 'toolbar=yes,scrollbars=yes,resizable=yes,width=1200,height=1200')";
$this->webDriver->executeScript($link);
6. Метод getWindowHandles
возвращает алфавитно-цифровой массив, содержащий ID (или дескриптор) текущих открытых окон. Оператор sizeof
, применяемый к массиву, возвращённому getWindowHandles
, возвращает количество открытых окон (т. е. в нашем случае 2).
$HandleCount = $this->webDriver->getWindowHandles();
echo ("\n Total number of window handles are " . sizeof($HandleCount));
7. При помощи утверждения проверяем, не меньше ли двух общее количество дескрипторов окон.
$this->assertEquals(2, sizeof($HandleCount));
8. Для переключения на второе окно (которое было открыто при помощи метода window.open
) используется метод switchTo
Selenium WebDriver. Если заголовок окна не соответствует ожидаемому заголовку, срабатывает утверждение.
$this->webDriver->switchTo()->window($HandleCount[1]);
$win_title_2 = $this->webDriver->getTitle();
$this->assertEquals($win_title_2, $title_2);
9. Текущее окно закрывается при помощи метода Close()
.
$this->webDriver->close();
10. Теперь количество дескрипторов окон будет равно одному (так как открыто только родительское окно). Для переключения на родительское окно используется метод switchTo
, которому в качестве параметра передаётся дескриптор этого окна (т. е. $wHandle
). Если заголовок окна не соответствует ожидаемому заголовку, срабатывает утверждение.
$test_url_1 = "https://www.lambdatest.com";
$title_1 = "Most Powerful Cross Browser Testing Tool Online | LambdaTest";
...........................................
$this->webDriver->get($test_url_1);
...........................................
...........................................
$wHandle = $this->webDriver->getWindowHandle();
/* Возвращаемся к окну с дескриптором = 0 */
$this->webDriver->switchTo()->window($wHandle);
/* Проверяем, совпадают ли заголовки окон */
$win_title_1 = $this->webDriver->getTitle();
$this->assertEquals($win_title_1, $title_1);
11. Как часть tearDown
вызывается метод quit
Selenium WebDriver на PHP.
public function tearDown(): void
{
$this->webDriver->quit();
}
❒ Исполнение
На скриншоте ниже отмечены дескрипторы всплывающих окон:
После закрытия всех всплывающих окон остаётся только родительское окно, которое закрывается после исполнения теста.
Как показано на снэпшоте исполнения, сделанном на вкладке автоматизации платформы LambdaTest, всплывающие окна открываются, а затем закрываются в обратном хронологическом порядке.
Заключение
Окна браузера, в том числе вкладки и всплывающие окна, идентифицируются по дескрипторам окон. Эти дескрипторы используются в качестве ID окон, они уникальны для каждого окна браузера. В этом туториале по Selenium WebDriver PHP мы узнали, как происходит работа с окнами в Selenium с PHP при помощи таких методов, как switchTo
, getWindowHandle
и getWindowHandles
. Эти методы играют важную роль в работе с окнами для автоматизации тестов Selenium с PHP.