我正在尝试使用Mink和Zombie的PHP脚本测试一个页面,否则该页面使用大量异步加载的JavaScript资源。不幸的是,这个过程失败了(留下hanging processes)。我最终设法在一个最小的例子上模拟这个错误,我将在这里发布。
第一页 - 要测试的页面 - 基本上是self-contained file,它通过调用自身来模拟AJAX调用:
test_JSload.php
<?php
if (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>
因此,当页面首次加载时,没有动作;按下按钮后:首先通过AJAX调用返回一些包含<script>
的HTML内容(这是?getone
);然后这个脚本本身从?gettwo
“自动”加载。我知道这可能不是正确的事情(参见JavaScript console.log causes error: "Synchronous XMLHttpRequest on the main thread is deprecated..."),但在我试图测试的实际页面中有一个类似的组织。
要运行此页面,您只需拥有命令行php
版本&gt; 5.3,并在文件的同一目录中运行:
php -S localhost:8080
...然后,在浏览器中,转到http://127.0.0.1:8080/test_JSload.php
。
无论如何,一旦我点击按钮,Firefox控制台就会显示:
OnGetdata; loading ?getone via AJAX call test_JSload.php:13:3
ptest pre ajax is Object { } test_JSload.php:16:3
TypeError: ptest.changeName is not a function test_JSload.php:31:3
got getone data test_JSload.php:21:7
Synchronous 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:7
顺便说一句,错误'TypeError: ... is not a function
'与我从Mink / Zombie得到的错误与我正在尝试检查的实际页面完全相同;虽然我不认为页面本身在独立调用时会出现错误,但就像在这里一样。
现在,让我们尝试使用Mink检查此页面(在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";
?>
从另一个终端shell运行它(当第一个仍运行服务于test_JSload.php
的php服务器进程时)结果为:
$ php test_JSload_mink.php
current URL: http://127.0.0.1:8080/test_JSload.php
status code: 200
Check: el_b object
pressing/clicking the button
PHP 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 880
现在,来自Mink / Zombie的错误是'ReferenceError: person is not defined
',而在我的实际页面检查中,它是'TypeError: ... is not a function
' - 但我认为它们足够相似。
问题是 - 我该如何处理这样的错误?
例如,此处已使用“$session->wait(20000, '(browser.statusCode > 0)');
”,用于检查JavaScript中的browser.statusCode
;我知道还有:
$returnstring = $session->evaluateScript('document.readyState');
...也可以从JavaScript请求数据。所以这样的东西 - 原则上 - 可以用来“等待”,直到加载给定的资源。
但问题是,ptest
变量暴露了问题,它位于函数OnGetdata()
的局部范围内,因此我不知道如何构造一个从PHP“检查”它的语句 - 我希望只能访问browser
和document
这样的全局变量。
那么,有没有人现在怎么能有一个Mink / Zombie PHP脚本“等待”这样的“嵌套”JavaScript加载 - 所以我可以在虚拟点击后完成页面的加载,没有错误?
编辑:管理将此问题减少为简单的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.js
var 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 +476ms
OnGetdata; 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 2
ret 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 +0ms
ret is false
got 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根本无法等待同步请求。
答案 0 :(得分:0)
试试这个:
$session->wait(50000, "document.readyState === 'complete'");
或尝试其他等待条件,例如等待一个对象,同时请不要指望如果找不到元素,findButton可能会返回null,这样你可以尝试一段时间的do-while和if按钮!== null然后中断/返回按钮。
我总是使用等待返回对象的元素或抛出异常并单击,例如:
$this->waitForElement('selector')->click();
如果您需要waitForElement的示例,请告诉我。
以下是一些ajax条件,您可以尝试how-to-make-behat-wait-for-an-ajax-call