在Selenium PHP中处理多个浏览器窗口和标签
了解在必须处理多个窗口、标签和弹出式窗口时,如何使用Selenium PHP自动化作为Selenium中窗口处理的替代品。
网络应用程序(或网络应用)中的一个常见场景是,一旦用户执行了一个特定的活动,就会打开一个新的浏览器窗口(或标签)。许多Web开发者使用HTML标签"__blank",当点击一个链接时,通知浏览器打开一个新窗口(或标签,取决于用户的设置偏好)。用PHP处理Selenium中的窗口,可用于自动处理与浏览器窗口、标签、甚至弹出式窗口的交互。
弹出式窗口 "有时 "会让网站访问者感到厌烦,但有时,除了使用同样的东西,你没有其他选择。Selenium是如何区分不同的窗口或标签的?如何用自动化的方式在不同的浏览器窗口或标签之间无缝切换?如何在Selenium中管理窗口处理?
在这篇博客中,我们深入探讨了Selenium在自动化与浏览器、标签和弹出式窗口交互方面的用法。我们使用PHPUnit,PHP的单元测试框架,来演示Selenium PHP中的窗口处理。
什么是窗口句柄?
窗口句柄是一个唯一的标识符,其主要职责是保存所有窗口的地址。当Selenium WebDriver实例被实例化时,一个字母数字的ID被分配给窗口。这个唯一的ID被称为窗口句柄,是一个指向窗口的指针,有助于识别浏览器窗口。
无论是新窗口/标签/弹出窗口,每个窗口的句柄(或ID)都是唯一的。Selenium WebDriver使用Selenium PHP中的窗口句柄函数在不同的窗口(或标签)之间切换。
就窗口句柄的保留而言,唯一的ID会保留到Selenium WebDriver会话结束(即通过WebDriver.Quit或WebDriver.Close APIs)。窗口处理函数用于获取所有窗口处理的细节。在Selenium中处理窗口的基本原理是相同的,无论你是用PHP在Selenium中处理窗口还是用其他编程语言(如Python、Java等)使用Selenium。
下面是一些你会遇到多个窗口(或标签)的常见情况:
- 需要从一个新打开的窗口中选择日期的表格
- 点击按钮,打开一个新的标签(或窗口)。
- 用于向终端用户展示一些优惠的弹出式窗口;工作门户网站通常使用这种策略
- 处理显示广告的窗口
上面显示的是一个示例场景,在父窗口(或基础窗口)中的点击操作会打开两个 "子窗口"。每个窗口都有唯一的窗口句柄,句柄被保留到窗口没有被销毁(即关闭)。这样一来,窗口的总数就变成了三个(父窗口+子窗口1+子窗口2)。点击 "孩子-1 "和 "孩子-2 "的按钮/链接,分别打开 "孙子-1 "和 "孙子-2"。这样一来,窗口的总数为5个,每个窗口都有独特的窗口句柄,可以用来对它们进行自动化操作。
用PHP在Selenium中处理窗口的命令
Selenium提供了不同的方法来处理多个窗口。你也可以参考我们的指南,用Selenium和Protractor处理多个窗口。这里有一些在Selenium PHP测试中广泛使用的命令,用于切换浏览器窗口和处理弹出窗口。
如果你是一个PHP专家,你可以获得一个专门研究PHP核心编程技能的基准行业认证,并扩大你在PHP自动化测试中的职业发展机会。
切换到窗口
SwitchTo
命令用于将焦点切换到一个新的浏览器窗口或标签。为了将焦点切换到一个新的窗口,所需的浏览器窗口的句柄被作为参数传给该命令。
/* $wHandle is the Window Handle (or ID) of the Window to which the switch has to be performed */
$this->webDriver->switchTo()->window($wHandle);
/* Switch can also be done by obtaining the number of windows using getWindowHandles or getWindowHandle */
$this->webDriver->switchTo()->window($HandleCount[win-number]);
getWindowHandle
Selenium PHP中的getWindowHandle
方法返回当前活动(或焦点)窗口的窗口ID(这是一个唯一的字母数字标识符)。
$wHandle = $this->webDriver->getWindowHandle();
在上面的例子中,$wHandle
是用getWindowHandle
方法得到的窗口ID。
getWindowHandles
这是在Selenium中用PHP处理窗口的一个重要方法。getWindowHandles
方法返回一组由同一驱动实例打开的窗口(和标签)句柄,包括父、子窗口。例如,如果对父窗口中的按钮进行点击操作,会打开一个新的标签,getWindowHandles
方法将返回父窗口和子窗口(即标签)的句柄。在这种情况下,sizeof
操作符在应用于由getWindowHandles
返回的集合时,会返回该集合的大小(即2)。
在类似的情况下,如果在父窗口中打开的网页打开了8个弹出窗口,在getWindowHandles
方法返回的集合上应用sizeof
操作符得到的处理数是9。
每个窗口都会有一个独特的窗口句柄(或标识符),以便于识别窗口。
/* For a web page that opens a new tab, getWindowHandles returns an array of comprising of 2 window handles (i.e. handle of parent and child window) */
$HandleCount = $this->webDriver->getWindowHandles();
/* Returns the size of the Window Handle array. In the current example, it will be 2 */
echo ("\n Total number of window handles are " . sizeof($HandleCount));
/* Print the Window Handle of the Parent Window */
echo ("\n Window 0: " . $HandleCount[0]);
/* Print the Window Handle of the Child Window */
echo ("\n Window 0: " . $HandleCount[1]);
如何在Selenium PHP中处理多个浏览器窗口、标签和弹出窗口
我们在LambdaTest的基于云的Selenium Grid上使用PHPUnit演示Selenium PHP中的窗口处理的测试方案。在基于云的Selenium Grid上使用PHPUnit进行跨浏览器测试有助于在不同的浏览器、平台和设备模拟器组合中进行测试。
为了开始测试,我们在LambdaTest上创建了一个账户,并在个人资料页面上注明了用户名和访问密钥。测试是在(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中的多窗口处理,我们使用以下测试场景:
- 在Chrome浏览器中打开LambdaTest页面。
- 获取当前焦点窗口的窗口句柄。
- 使用HTML属性__blank在新窗口中打开LambdaTest博客页面。
- 打印各自的窗口句柄。
- 切换到打开第二个URL的窗口。
- 如果窗口的标题与预期的标题不一致,则断言。
- 关闭焦点下的窗口。
- 切换到第一个窗口,如果窗口的标题与预期的标题不一致,则断言。
- 关闭浏览器窗口。
实施
<?php
require 'vendor/autoload.php';
use PHPUnit\Framework\TestCase;
use Facebook\WebDriver\Remote\DesiredCapabilities;
use Facebook\WebDriver\Remote\RemoteWebDriver;
$GLOBALS['LT_USERNAME'] = "user-name";
# accessKey: AccessKey can be generated from automation dashboard or profile section
$GLOBALS['LT_APPKEY'] = "access-key";
class WindowSwitchTest extends TestCase
{
protected $webDriver;
public function build_browser_capabilities(){
/* $capabilities = DesiredCapabilities::chrome(); */
$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"
);
return $capabilities;
}
public function setUp(): void
{
$url = "https://". $GLOBALS['LT_USERNAME'] .":" . $GLOBALS['LT_APPKEY'] ."@hub.lambdatest.com/wd/hub";
$capabilities = $this->build_browser_capabilities();
/* Download the Selenium Server 3.141.59 from
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);
/* Open the second window */
/* $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(); */
/* The focus is now on the second window */
/* The Handle count will be two */
$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);
/* Assert if the Window Count is not 2 */
$this->assertEquals(2, sizeof($HandleCount));
/* Check if the Window titles match */
$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);
/* Close the newly opened Window and return to the old window */
$this->webDriver->close();
sleep(10);
/* Return to the window with handle = 0 */
$this->webDriver->switchTo()->window($wHandle);
/* Check if the Window titles match */
$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时,WebDriver的getWindowHandles
方法被用来获取打开的浏览器窗口的手柄。
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频道的超链接的网页元素位于页面的最后。使用JavaScriptExecutor提供的executeScript
方法执行JavaScript中的window.scrollTo
方法。这将导航到页面的末端。
$link = "window.scrollTo(0, document.body.scrollHeight)";
$this->webDriver->executeScript($link);
3.为了获得所需网络元素的信息,我们使用Chrome中的POM Builder扩展来获得定位器信息(即XPath)。
WebDriverBy
类的 findElement 方法被用来获取有关网络元素的详细信息[with XPath – //a[contains(.,’Youtube’)]
。
$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.Selenium WebDriver的switchTo
方法被用来切换到第二个窗口(即$HandleCount[1]
)。如果窗口的标题与预期的标题不一致,则提出断言。
$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);
用PHP在Selenium中处理多个浏览器弹出窗口
为了演示在Selenium PHP中处理浏览器弹出窗口,我们使用以下测试场景。
- 在Chrome浏览器中打开www.popuptest.com/popuptest1.…
- 按时间顺序反向关闭所有的弹出窗口。
- 检查父窗口的标题是否符合预期的标题。
如果在本地Selenium网格上进行同样的测试,你必须确保为谷歌浏览器启用弹出窗口。要在本地机器上启用Chrome浏览器的弹出式窗口,请进入chrome://settings/ -> Privacy and security -> Site Settings -> Pop-ups and redirects。禁用www.popuptest.com:80 的阻止选项。
<?php
require 'vendor/autoload.php';
use PHPUnit\Framework\TestCase;
use Facebook\WebDriver\Remote\DesiredCapabilities;
use Facebook\WebDriver\Remote\RemoteWebDriver;
use Facebook\WebDriver\WebDriverBy;
$GLOBALS['LT_USERNAME'] = "user-name";
# accessKey: AccessKey can be generated from automation dashboard or profile section
$GLOBALS['LT_APPKEY'] = "access-key";
class PopUpTest extends TestCase
{
protected $webDriver;
public function build_browser_capabilities(){
/* $capabilities = DesiredCapabilities::chrome(); */
$capabilities = array(
"build" => "[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();
/* Download the Selenium Server 3.141.59 from
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);
/* This will open-up main window and six pop-up windows */
/* Once the page is loaded, the total window count will be 7 */
$HandleCount = $this->webDriver->getWindowHandles();
/* This is the ID of the parent window */
$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());
/* Close the pop-up window and return to the old window */
$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.由于LambdaTest上的Selenium网格被用于测试,用户名和访问密钥被存储在全局变量中。同样的内容可以通过访问LambdaTest的配置文件页面获得。
$GLOBALS['LT_USERNAME'] = "user-name";
# accessKey: AccessKey can be generated from automation dashboard or profile section
$GLOBALS['LT_APPKEY'] = "access-key";
2.使用LambdaTest能力生成器来生成浏览器能力。
$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.持有用户名和访问密钥的全局变量的组合被用来访问LambdaTest的Selenium网格(@hub.lambdatest.com/wd/hub
)。RemoteWebDriver
类中的创建方法首先接受参数 Selenium Grid URL,第二个参数是浏览器能力。
$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
中,测试URLhttps://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
,用于创建一个新的二级浏览器窗口。窗口的高度和宽度尺寸也被传递给该方法。Selenium PHP中的JavaScriptExecutor提供的executeScript
方法被用来在当前打开的窗口中执行新形成的JavaScript代码。
$link = "window.open('". $test_url_2 ."', '_blank', 'toolbar=yes,scrollbars=yes,resizable=yes,width=1200,height=1200')";
$this->webDriver->executeScript($link);
6.getWindowHandles
方法返回一个包含当前打开窗口的Window ID(或句柄)的字母数字数组。在getWindowHandles
返回的数组上的sizeof
运算符返回已打开窗口的数量(即在我们的例子中,是2)。
$HandleCount = $this->webDriver->getWindowHandles();
echo ("\n Total number of window handles are " . sizeof($HandleCount));
7.如果窗口句柄的总数小于2,则断言:
$this->assertEquals(2, sizeof($HandleCount));
- Selenium WebDriver的
switchTo
方法被用来切换到第二个窗口(即用window.open
方法打开的窗口)。如果窗口的标题与预期的标题不一致,则提出断言。
$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.现在窗口句柄计数为1(因为只有父窗口被打开)。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();
/* Return to the window with handle = 0 */
$this->webDriver->switchTo()->window($wHandle);
/* Check if the Window titles match */
$win_title_1 = $this->webDriver->getTitle();
$this->assertEquals($win_title_1, $title_1);
11.在PHP中Selenium WebDriver的退出方法作为tearDown
的一部分被调用。
public function tearDown(): void
{
$this->webDriver->quit();
}
执行
从访问LambdaTest平台上的自动化标签所获得的执行快照中可以看出,我们注意到弹出窗口是按照相反的时间顺序打开并随后关闭的。
结论
浏览器窗口,包括标签和弹出式窗口,都是用窗口句柄来识别的。这些句柄作为窗口ID,对每个浏览器窗口都是唯一的。在这个Selenium WebDriver PHP教程中,我们看了在Selenium中用PHP处理窗口的方法,如switchTo
、getWindowHandle
、getWindowHandles
。这些方法在用PHP处理Selenium测试自动化中的窗口方面很有用。