使用Laravel Dusk测试PayPal支付时出现No such window错误
问题:Laravel Dusk测试PayPal标准结账时无法切换回原窗口
我正在编写集成测试以测试PayPal标准结账功能,点击PayPal按钮会弹出一个窗口。通过php artisan dusk --browse本地运行测试时,能成功切换到PayPal弹窗,填写输入框并点击最终支付按钮。但切换回原窗口对支付确认页进行断言时,出现如下错误:
Facebook\WebDriver\Exception\NoSuchWindowException: no such window: target window already closed from unknown error: web view not found
为何无法切换回原窗口?
我的Laravel Dusk测试代码如下:
public function test_paypal_payments_are_working() { $this->browse(function (Browser $browser) { $browser->visit("/products/payment") ->withinFrame('#paypal-button-container iframe', function($browser) { $browser ->waitFor(".paypal-button") ->click(".paypal-button"); }); $mainWindow = $browser->driver->getWindowHandle(); // 获取当前窗口 $paypalWindow = collect($browser->driver->getWindowHandles())->last(); // 获取最新打开的窗口 $browser->driver->switchTo()->window($paypalWindow); // 切换到PayPal窗口 $browser ->waitFor("#email", 7) ->keys('#email', "paypal-sandbox@personal.example.com") ->click("#btnNext") ->waitFor("#password") ->keys('#password', "pass1") ->click("#btnLogin") ->waitFor("#payment-submit-btn") ->click("#payment-submit-btn") ->waitFor("@payment-header") ->driver->switchTo()->window($mainWindow); // 此处失败 $browser->assertSeeIn("@payment-header", "Payment was successful"); }); }
原因分析与解决方案
核心原因
- 主窗口句柄获取时机错误:你在打开PayPal弹窗后才获取主窗口句柄,此时浏览器窗口上下文可能已变化,拿到的句柄不准确;后续支付流程可能导致原窗口被关闭或刷新,使句柄直接失效。
- 切换窗口时机过早:点击支付按钮后,PayPal窗口的流程尚未完全结束(比如跳转、关闭),此时原窗口可能还在加载确认页,或已被关闭,导致切换失败。
修复方案
1. 提前获取主窗口句柄
将获取主窗口句柄的代码移到打开PayPal弹窗之前,确保拿到的是未受弹窗影响的原始主窗口句柄。
2. 等待PayPal窗口完成流程
点击支付按钮后,等待PayPal窗口关闭(或从窗口列表中消失),再切换回主窗口,避免在流程未结束时操作。
3. 切换后主窗口等待页面加载
切换回主窗口后,先等待确认页的元素加载完成,再执行断言,避免页面未就绪导致的元素找不到问题。
修改后的测试代码
public function test_paypal_payments_are_working() { $this->browse(function (Browser $browser) { // 提前获取主窗口句柄,确保是打开弹窗前的原始窗口 $mainWindow = $browser->driver->getWindowHandle(); $browser->visit("/products/payment") ->withinFrame('#paypal-button-container iframe', function($browser) { $browser ->waitFor(".paypal-button") ->click(".paypal-button"); }); // 获取PayPal弹窗的句柄 $paypalWindow = collect($browser->driver->getWindowHandles())->last(); $browser->driver->switchTo()->window($paypalWindow); // 完成PayPal登录与支付流程 $browser ->waitFor("#email", 7) ->keys('#email', "paypal-sandbox@personal.example.com") ->click("#btnNext") ->waitFor("#password") ->keys('#password', "pass1") ->click("#btnLogin") ->waitFor("#payment-submit-btn") ->click("#payment-submit-btn"); // 等待PayPal窗口关闭,确保支付流程完全结束 $browser->driver->wait()->until(function ($driver) use ($paypalWindow) { return !in_array($paypalWindow, $driver->getWindowHandles()); }); // 切换回主窗口 $browser->driver->switchTo()->window($mainWindow); // 等待支付确认页加载完成后,执行断言 $browser->waitFor("@payment-header") ->assertSeeIn("@payment-header", "Payment was successful"); }); }
额外注意事项
- 如果支付完成后主窗口发生跳转,原句柄依然有效,但必须等待页面加载完成才能进行断言。
- 若主窗口被意外关闭,可以通过
$browser->driver->getWindowHandles()获取当前所有窗口,选择第一个(或对应主窗口的句柄)进行切换。
内容的提问来源于stack exchange,提问作者Hashim Aziz
相关产品推荐
相关产品推荐

