设法将这个问题减少为一个简单的Zombie JavaScript文件: test_JSload_zombie.js // call with:// DEBUG=zombie NODE_PATH=/home/USERNAME/.nvm/versions/node/v4.0.0/lib/node_modules node test_JSload_zombie.jsvar Browser = require("zombie");browser = new Browser({waitDuration: 30*1000, runScripts: true});browser.visit("http://127.0.0.1:8080/test_JSload.php", function () { browser.pressButton('Get Data!'); //, done); console.log(' person, post-click pre-wait:', browser.evaluate('typeof(person)'), browser.evaluate('$("script").length') ); // $("script").length is 2 var checkCondition = function () { var ret =browser.evaluate("(typeof(person) != 'undefined')"); console.log("ret is " + ret); return ret; }; browser.wait({function: checkCondition, duration: 100000}, function() { console.log(' person, post-wait:', browser.evaluate('typeof(person)'), browser.evaluate('$("script").length')); // $("script").length is 3 /// browser.dump(); // not too much info });}); 运行此文件,记录完全相同的错误: zombie Opened window http://127.0.0.1:8080/test_JSload.php +0ms zombie GET http://127.0.0.1:8080/test_JSload.php => 200 +198ms zombie Loaded document http://127.0.0.1:8080/test_JSload.php +233ms zombie GET http://code.jquery.com/jquery-1.12.4.min.js => 200 +207ms zombie Event loop is empty +476msOnGetdata; loading ?getone via AJAX call ptest pre ajax is Object {} zombie XHR readystatechange http://127.0.0.1:8080/test_JSload.php?getone +54ms zombie XHR loadstart http://127.0.0.1:8080/test_JSload.php?getone +3ms zombie TypeError: ptest.changeName is not a function at OnGetdata (http://127.0.0.1:8080/test_JSload.php:script:24:9) at null.<anonymous> (http://127.0.0.1:8080/test_JSload.php:script:30:5) at n.event.dispatch (http://code.jquery.com/jquery-1.12.4.min.js:3:12444) at r.handle (http://code.jquery.com/jquery-1.12.4.min.js:3:9173) at callListeners (/home/USERNAME/.nvm/versions/node/v4.0.0/lib/node_modules/zombie/node_modules/jsdom/lib/jsdom/events/EventTarget.js:170:34) at dispatchPhase (/home/USERNAME/.nvm/versions/node/v4.0.0/lib/node_modules/zombie/node_modules/jsdom/lib/jsdom/events/EventTarget.js:159:7) at EventTarget.dispatchEvent (/home/USERNAME/.nvm/versions/node/v4.0.0/lib/node_modules/zombie/node_modules/jsdom/lib/jsdom/events/EventTarget.js:115:3) at DOM.EventTarget.dispatchEvent (/home/USERNAME/.nvm/versions/node/v4.0.0/lib/node_modules/zombie/lib/dom/jsdom_patches.js:155:31) at define.proto.dispatchEvent (/home/USERNAME/.nvm/versions/node/v4.0.0/lib/node_modules/zombie/node_modules/jsdom/lib/jsdom/level2/html.js:365:55) at Browser.fire (/home/USERNAME/.nvm/versions/node/v4.0.0/lib/node_modules/zombie/lib/index.js:424:14) in http://127.0.0.1:8080/test_JSload.php +24ms person, post-click pre-wait: undefined 2ret is false zombie GET http://127.0.0.1:8080/test_JSload.php?getone => 200 +36ms zombie XHR readystatechange http://127.0.0.1:8080/test_JSload.php?getone +7ms zombie XHR readystatechange http://127.0.0.1:8080/test_JSload.php?getone +0msret is falsegot getone data zombie ReferenceError: person is not defined at Object.OnGetdata.$.ajax.success (http://127.0.0.1:8080/test_JSload.php:script:16:19) at i (http://code.jquery.com/jquery-1.12.4.min.js:2:27449) at Object.j.fireWith [as resolveWith] (http://code.jquery.com/jquery-1.12.4.min.js:2:28213) at y (http://code.jquery.com/jquery-1.12.4.min.js:4:22721) at XMLHttpRequest.c (http://code.jquery.com/jquery-1.12.4.min.js:4:26925) at callListeners (/home/USERNAME/.nvm/versions/node/v4.0.0/lib/node_modules/zombie/node_modules/jsdom/lib/jsdom/events/EventTarget.js:170:34) at dispatchPhase (/home/USERNAME/.nvm/versions/node/v4.0.0/lib/node_modules/zombie/node_modules/jsdom/lib/jsdom/events/EventTarget.js:159:7) at XMLHttpRequest.EventTarget.dispatchEvent (/home/USERNAME/.nvm/versions/node/v4.0.0/lib/node_modules/zombie/node_modules/jsdom/lib/jsdom/events/EventTarget.js:115:3) at XMLHttpRequest.DOM.EventTarget.dispatchEvent (/home/USERNAME/.nvm/versions/node/v4.0.0/lib/node_modules/zombie/lib/dom/jsdom_patches.js:155:31) at XMLHttpRequest._fire (/home/USERNAME/.nvm/versions/node/v4.0.0/lib/node_modules/zombie/lib/xhr.js:242:12) in http://127.0.0.1:8080/test_JSload.php +73ms person, post-wait: undefined 3 zombie XHR readystatechange http://127.0.0.1:8080/test_JSload.php?getone +4ms zombie XHR progress http://127.0.0.1:8080/test_JSload.php?getone +0ms zombie XHR load http://127.0.0.1:8080/test_JSload.php?getone +1ms zombie XHR loadend http://127.0.0.1:8080/test_JSload.php?getone +0ms zombie GET http://127.0.0.1:8080/test_JSload.php?gettwo => 200 +18ms 所以,我想如果这在Zombie/JS级别上得到解决,那么最终可以在Mink/PHP级别上解决...但是请注意,对?gettwo的调用仅在错误被排除之后才发生.发出(并且是日志中的最后一个条目)-似乎Zombie根本无法等待同步请求.解决方案也尝试一下:$session->wait(50000, "document.readyState === 'complete'");或尝试其他等待条件,例如在等待对象时执行do,另外请不要因为如果未找到/加载元素,则findButton可能会返回null,因此您可以尝试执行do-while几秒钟,并且if按钮!== null,然后断开/返回按钮.我总是使用等待返回对象或引发异常并单击的元素,例如:$this->waitForElement('selector')->click();如果您需要一个waitForElement示例,请告诉我.以下是一些ajax条件,您可以尝试 how-制作要等待的ajax呼叫 I'm trying to test a page, which otherwise uses massive asynchronously loaded JavaScript resources, with PHP script that uses Mink and Zombie. Unfortunately, this process fails (leaving behind hanging processes). I have managed finally to simulate this error on a minimal example, which I'll post here.The first page - the page to be tested - is basically a self-contained file, that simulates the AJAX calls by calling itself:test_JSload.php<?phpif (array_key_exists("QUERY_STRING", $_SERVER)) { if ($_SERVER["QUERY_STRING"] == "getone") { echo "<!doctype html> <html> <head> <script src='test_JSload.php?gettwo'></script> </head> </html> "; exit; } if ($_SERVER["QUERY_STRING"] == "gettwo") { header('Content-Type: application/javascript'); echo " function person(firstName) { this.firstName = firstName; this.changeName = function (name) { this.firstName = name; }; } "; exit; }}?><html><head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <style type="text/css">.my_btn { background-color:yellow; } </style> <script src="http://code.jquery.com/jquery-1.12.4.min.js"></script> <script type="text/javascript">var thishref = window.location.href.slice(0, window.location.href.indexOf('?')+1);var qstr = window.location.href.slice(window.location.href.indexOf('?')+1);function OnGetdata(inbtn) { console.log("OnGetdata; loading ?getone via AJAX call"); //~ $.ajax(thishref + "?getone", { // works var ptest = {}; // init as empty object console.log(" ptest pre ajax is ", ptest); $.ajax({url: thishref + "?getone", async: true, // still "Synchronous XMLHttpRequest on the main thread is deprecated", because we load a script; https://stackoverflow.com/q/24639335 success: function(data) { console.log("got getone data "); //, data); $("#dataholder").html(data); ptest = new person("AHA"); console.log(" ptest post getone is ", ptest); }, error: function(xhr, ajaxOptions, thrownError) { console.log("getone error " + thishref + " : " + xhr.status + " / " + thrownError); } }); ptest.changeName("Somename"); console.log(" ptest post ajax is ", ptest);}ondocready = function() { $("#getdatabtn").click(function(){ OnGetdata(this); });}$(document).ready(ondocready); </script></head><body> <h1>Hello World!</h1> <button type="button" id="getdatabtn" class="my_btn">Get Data!</button> <div id="dataholder"></div></body></html>So, when the page first loads, there's no action; and as soon as the button is pressed: first some HTML content, which contains <script>, is returned (this is ?getone) through an AJAX call; then this script itself loads "automatically" from ?gettwo. I'm aware this is probably not the right thing to do (see JavaScript console.log causes error: "Synchronous XMLHttpRequest on the main thread is deprecated..."), but there is a similar organization in the actual page I'm trying to test. To run this page, you can just have the command-line php version > 5.3, and run in the same directory of the file:php -S localhost:8080... and then, in a browser, go to http://127.0.0.1:8080/test_JSload.php.In any case, once I click the button, Firefox console shows:OnGetdata; loading ?getone via AJAX call test_JSload.php:13:3 ptest pre ajax is Object { } test_JSload.php:16:3TypeError: ptest.changeName is not a function test_JSload.php:31:3got getone data test_JSload.php:21:7Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help http://xhr.spec.whatwg.org/ jquery-1.12.4.min.js:4:26272 ptest post getone is Object { firstName: "AHA", changeName: person/this.changeName(name) } test_JSload.php:24:7Btw, the error 'TypeError: ... is not a function' is the exact same I get from Mink/Zombie with the actual page I'm trying to inspect; although I don't think the page itself exhibits that error when called standalone, as it does here.Now, let's try inspect this page with Mink (install as on nodejs cannot find module 'zombie' with PHP mink):test_JSload_mink.php<?php$nodeModPath = "/home/USERNAME/.nvm/versions/node/v4.0.0/lib/node_modules";# composer autoload:require_once __DIR__ . '/vendor/autoload.php';$zsrv = new \Behat\Mink\Driver\NodeJS\Server\ZombieServer();$zsrv->setNodeModulesPath($nodeModPath . "/"); # needs to end with a trailing '/'$driver = new \Behat\Mink\Driver\ZombieDriver( $zsrv );$session = new \Behat\Mink\Session($driver);// start the session$session->start();$session->visit("http://127.0.0.1:8080/test_JSload.php");$session->wait(20000, '(browser.statusCode > 0)'); ### THIS makes things work?!$statcode = $session->getStatusCode();echo " current URL: " . $session->getCurrentUrl() ."\n";echo " status code: " . $statcode ."\n";$page = $session->getPage();$el_button = $page->findButton('getdatabtn');echo "Check: el_b " . gettype($el_button) . "\n";echo " pressing/clicking the button\n";$el_button->click();echo "Page URL after click: ". $session->getCurrentUrl() . "\n";?>Running this from another terminal shell (while the first one still runs the php server process that serves test_JSload.php) results with:$ php test_JSload_mink.php current URL: http://127.0.0.1:8080/test_JSload.php status code: 200Check: el_b object pressing/clicking the buttonPHP Fatal error: Uncaught exception 'Behat\Mink\Exception\DriverException' with message 'Error while processing event 'click': "ReferenceError: person is not defined\n at Object.OnGetdata.$.ajax.success (http://127.0.0.1:8080/test_JSload.php:script:16:19)\n at i (http://code.jquery.com/jquery-1.12.4.min.js:2:27449)\n at Object.j.fireWith [as resolveWith] (http://code.jquery.com/jquery-1.12.4.min.js:2:28213)\n at y (http://code.jquery.com/jquery-1.12.4.min.js:4:22721)\n at XMLHttpRequest.c (http://code.jquery.com/jquery-1.12.4.min.js:4:26925)\n at callListeners (/home/USERNAME/.nvm/versions/node/v4.0.0/lib/node_modules/zombie/node_modules/jsdom/lib/jsdom/events/EventTarget.js:170:34)\n at dispatchPhase (/home/USERNAME/.nvm/versions/node/v4.0.0/lib/node_modules/zombie/node_modules/jsdom/lib/jsdom/events/EventTarget.js:159:7)\n at XMLHttpRequest.EventTarget.dispatchEvent (/home/USERNAME/.nvm/versions/node/v4.0.0/lib/node_modules/zombie/node_modules/jsdom/lib/jsdom/events/EventTarget.js:115: in /media/Data1/work/bbs/gits/econdk-vis-01_git/test/test_php_mink/vendor/behat/mink-zombie-driver/src/ZombieDriver.php on line 880Now, here the error from Mink/Zombie is 'ReferenceError: person is not defined', whereas in my actual page inspection, it is 'TypeError: ... is not a function' - but I think they are similar enough.The question is - how can I handle errors like this?For instance, here there is already '$session->wait(20000, '(browser.statusCode > 0)');' used, which checks for browser.statusCode inside JavaScript; I'm aware there is also:$returnstring = $session->evaluateScript('document.readyState');... which can also ask for data from JavaScript. So something like this - in principle - could be used to "wait" until given resources are loaded.But the problem here is that the ptest variable, which exposes the problem, is in the local scope of function OnGetdata(), and so I have no idea how to construct a statement that would "check" it from PHP - where I expect only global variables like browser and document to be accessible.So, does anyone now how could I have a Mink/Zombie PHP script "wait" for such "nested" JavaScript loading - so I could complete the loading of the page, after the virtual click, without an error?EDIT: Managed to reduce this problem to a plain Zombie JavaScript file:test_JSload_zombie.js// call with:// DEBUG=zombie NODE_PATH=/home/USERNAME/.nvm/versions/node/v4.0.0/lib/node_modules node test_JSload_zombie.jsvar Browser = require("zombie");browser = new Browser({waitDuration: 30*1000, runScripts: true});browser.visit("http://127.0.0.1:8080/test_JSload.php", function () { browser.pressButton('Get Data!'); //, done); console.log(' person, post-click pre-wait:', browser.evaluate('typeof(person)'), browser.evaluate('$("script").length') ); // $("script").length is 2 var checkCondition = function () { var ret =browser.evaluate("(typeof(person) != 'undefined')"); console.log("ret is " + ret); return ret; }; browser.wait({function: checkCondition, duration: 100000}, function() { console.log(' person, post-wait:', browser.evaluate('typeof(person)'), browser.evaluate('$("script").length')); // $("script").length is 3 /// browser.dump(); // not too much info });});Running this file, logs the exact same error: zombie Opened window http://127.0.0.1:8080/test_JSload.php +0ms zombie GET http://127.0.0.1:8080/test_JSload.php => 200 +198ms zombie Loaded document http://127.0.0.1:8080/test_JSload.php +233ms zombie GET http://code.jquery.com/jquery-1.12.4.min.js => 200 +207ms zombie Event loop is empty +476msOnGetdata; loading ?getone via AJAX call ptest pre ajax is Object {} zombie XHR readystatechange http://127.0.0.1:8080/test_JSload.php?getone +54ms zombie XHR loadstart http://127.0.0.1:8080/test_JSload.php?getone +3ms zombie TypeError: ptest.changeName is not a function at OnGetdata (http://127.0.0.1:8080/test_JSload.php:script:24:9) at null.<anonymous> (http://127.0.0.1:8080/test_JSload.php:script:30:5) at n.event.dispatch (http://code.jquery.com/jquery-1.12.4.min.js:3:12444) at r.handle (http://code.jquery.com/jquery-1.12.4.min.js:3:9173) at callListeners (/home/USERNAME/.nvm/versions/node/v4.0.0/lib/node_modules/zombie/node_modules/jsdom/lib/jsdom/events/EventTarget.js:170:34) at dispatchPhase (/home/USERNAME/.nvm/versions/node/v4.0.0/lib/node_modules/zombie/node_modules/jsdom/lib/jsdom/events/EventTarget.js:159:7) at EventTarget.dispatchEvent (/home/USERNAME/.nvm/versions/node/v4.0.0/lib/node_modules/zombie/node_modules/jsdom/lib/jsdom/events/EventTarget.js:115:3) at DOM.EventTarget.dispatchEvent (/home/USERNAME/.nvm/versions/node/v4.0.0/lib/node_modules/zombie/lib/dom/jsdom_patches.js:155:31) at define.proto.dispatchEvent (/home/USERNAME/.nvm/versions/node/v4.0.0/lib/node_modules/zombie/node_modules/jsdom/lib/jsdom/level2/html.js:365:55) at Browser.fire (/home/USERNAME/.nvm/versions/node/v4.0.0/lib/node_modules/zombie/lib/index.js:424:14) in http://127.0.0.1:8080/test_JSload.php +24ms person, post-click pre-wait: undefined 2ret is false zombie GET http://127.0.0.1:8080/test_JSload.php?getone => 200 +36ms zombie XHR readystatechange http://127.0.0.1:8080/test_JSload.php?getone +7ms zombie XHR readystatechange http://127.0.0.1:8080/test_JSload.php?getone +0msret is falsegot getone data zombie ReferenceError: person is not defined at Object.OnGetdata.$.ajax.success (http://127.0.0.1:8080/test_JSload.php:script:16:19) at i (http://code.jquery.com/jquery-1.12.4.min.js:2:27449) at Object.j.fireWith [as resolveWith] (http://code.jquery.com/jquery-1.12.4.min.js:2:28213) at y (http://code.jquery.com/jquery-1.12.4.min.js:4:22721) at XMLHttpRequest.c (http://code.jquery.com/jquery-1.12.4.min.js:4:26925) at callListeners (/home/USERNAME/.nvm/versions/node/v4.0.0/lib/node_modules/zombie/node_modules/jsdom/lib/jsdom/events/EventTarget.js:170:34) at dispatchPhase (/home/USERNAME/.nvm/versions/node/v4.0.0/lib/node_modules/zombie/node_modules/jsdom/lib/jsdom/events/EventTarget.js:159:7) at XMLHttpRequest.EventTarget.dispatchEvent (/home/USERNAME/.nvm/versions/node/v4.0.0/lib/node_modules/zombie/node_modules/jsdom/lib/jsdom/events/EventTarget.js:115:3) at XMLHttpRequest.DOM.EventTarget.dispatchEvent (/home/USERNAME/.nvm/versions/node/v4.0.0/lib/node_modules/zombie/lib/dom/jsdom_patches.js:155:31) at XMLHttpRequest._fire (/home/USERNAME/.nvm/versions/node/v4.0.0/lib/node_modules/zombie/lib/xhr.js:242:12) in http://127.0.0.1:8080/test_JSload.php +73ms person, post-wait: undefined 3 zombie XHR readystatechange http://127.0.0.1:8080/test_JSload.php?getone +4ms zombie XHR progress http://127.0.0.1:8080/test_JSload.php?getone +0ms zombie XHR load http://127.0.0.1:8080/test_JSload.php?getone +1ms zombie XHR loadend http://127.0.0.1:8080/test_JSload.php?getone +0ms zombie GET http://127.0.0.1:8080/test_JSload.php?gettwo => 200 +18msSo, I guess if this gets solved on a Zombie/JS level, then it can eventually be solved on a Mink/PHP level... Note however, that the call to ?gettwo happens only after the error has been emitted (and is the final entry in the log) - it seems that Zombie simply cannot wait for synchronous requests.. 解决方案 Try this too:$session->wait(50000, "document.readyState === 'complete'");Or try other wait condition, for example a do while to wait for an object, also please not that findButton may return null if the element is not found/loaded so you can try a do-while for some seconds and if button !== null then break/return the button.I always use wait for element that returns the object or throws an exception and click, for example something like:$this->waitForElement('selector')->click();If you need an example of waitForElement please let me know.Here are some ajax conditions that you can try how-to-make-behat-wait-for-an-ajax-call 这篇关于在PHP中使用Mink/Zombie检查JavaScript AJAX加载的资源?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 10-23 07:07