Ich versuche eine Seite zu testen, die ansonsten massive asynchron geladene JavaScript-Ressourcen verwendet, mit PHP-Skript, das Mink und Zombie verwendet. Leider schlägt dieser Prozess fehl (hanging processes). Ich habe es endlich geschafft, diesen Fehler an einem minimalen Beispiel zu simulieren, das ich hier veröffentlichen werde.JavaScript überprüfen AJAX geladene Ressourcen mit Mink/Zombie in PHP?
Die erste Seite - die Seite getestet wird - ist im Grunde ein self-contained file, dass das AJAX simuliert ruft selbst Aufruf:
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>
Also, wenn die Seite geladen wird, es gibt keine Aktion; und sobald die Schaltfläche gedrückt wird: zuerst wird ein HTML-Inhalt, der <script>
enthält, durch einen AJAX-Aufruf (dies ist ?getone
) zurückgegeben; dann lädt dieses Skript selbst "automatisch" von . Ich bin mir bewusst, das ist wahrscheinlich nicht das Richtige zu tun (siehe JavaScript console.log causes error: "Synchronous XMLHttpRequest on the main thread is deprecated..."), aber es gibt eine ähnliche Organisation in der aktuellen Seite, die ich versuche zu testen.
Diese Seite ausführen zu können, können Sie die Befehlszeilen php
Version haben nur> 5,3, und im gleichen Verzeichnis der Datei auszuführen:
php -S localhost:8080
... und dann, in einem Browser, gehen zu http://127.0.0.1:8080/test_JSload.php
.
Auf jeden Fall, wenn ich auf die Schaltfläche klicken, zeigt Firefox Konsole:
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
Btw, der Fehler ‚TypeError: ... is not a function
‘ genau das gleiche, das ich von Mink/Zombie mit der aktuellen Seite erhalten Ich versuche, inspizieren; obwohl ich glaube nicht, dass die Seite selbst diesen Fehler zeigt, wenn sie als eigenständig bezeichnet wird, wie hier.
Jetzt wollen wir versuchen, diese Seite mit Mink inspizieren (installieren wie auf 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";
?>
das Lauf von einem anderen Terminal-Shell (während die erste läuft noch die PHP-Server Prozess, der test_JSload.php
liefert) Ergebnisse mit:
$ 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
Nun, hier ist der Fehler von Mink/Zombie 'ReferenceError: person is not defined
', während in meiner tatsächlichen Seiteninspektion ist es 'TypeError: ... is not a function
' - aber ich denke, sie sind ähnlich genug.
Die Frage ist - wie kann ich mit solchen Fehlern umgehen?
Zum Beispiel, hier ist bereits '$session->wait(20000, '(browser.statusCode > 0)');
' verwendet, die für browser.statusCode
innerhalb JavaScript überprüft; Ich bin mir bewusst, es gibt auch:
... die auch für Daten aus JavaScript fragen können. So etwas - im Prinzip - könnte verwendet werden, um zu "warten", bis bestimmte Ressourcen geladen sind.
Aber das Problem ist, dass die ptest
Variable, das das Problem aussetzt, in dem lokal sind Umfang der Funktion OnGetdata()
, und so habe ich keine Ahnung, wie man eine Aussage zu konstruieren, die „check“ es von PHP würde - wo ich erwarte, dass nur globale Variablen wie browser
und document
zugänglich sind.
Also, hat jemand jetzt wie könnte ich ein Mink/Zombie PHP-Skript "warten" für solche "geschachtelte" JavaScript laden - so konnte ich das Laden der Seite nach dem virtuellen Klick, ohne einen Fehler abschließen?
EDIT: Managed dieses Problem auf eine Ebene Zombie JavaScript-Datei zu reduzieren:
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
});
});
Ausführen dieser Datei protokolliert exakt die gleichen Fehler:
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
Also, ich denke, wenn das auf einem gelöst wird Zombie/JS-Level, dann kann es eventuell auf einer Mink/PHP-Ebene gelöst werden ... Beachten Sie jedoch, dass der Aufruf an ?gettwo
erst dann erfolgt, nachdem der Fehler ausgegeben wurde (und der letzte Eintrag im Log ist) - so scheint es Zombie kann nicht einfach für synchrone Anforderungen warten ..
Haben Sie basiert auf AJAX-Anfragen einen Wartezustand zu verwenden versucht, wie versuchen: Session-> $ wait ('numberOfSeconds', ‚(0 === Ajax.activeRequestCount) "); ? – lauda
Danke dafür, @lauda - habe es gerade ausprobiert, aber ich bekomme: 'ReferenceError: Ajax ist nicht definiert'. Ich habe es ein bisschen nachgeschaut, und auf dieser Seite: https://github.com/facebook/php-webdriver/wiki/How-to-work-with-jQuery,-Prototype-Dojo-AJAX kann ich das sehen Ajax.activeRequestCount' ist auf die Verwendung von 'prototypejs' zurückzuführen, die ich nirgendwo in diesem Beispiel verwende. Ich benutze jQuery, und ich versuchte '$ session-> wait (20000, '(0 === jQuery.active)');' vor dem 'click()' stattdessen, aber es schlägt nur das gleiche wie in OP beschrieben . – sdbbs
Versuchen Sie auch dies: $ session-> wait (50000, "document.readyState === 'complete'"); Oder versuchen Sie eine andere Wartebedingung, zum Beispiel eine Weile, um auf ein Objekt zu warten, auch nicht, dass findButton Null zurückgibt, wenn das Element nicht gefunden/geladen wird, so dass Sie einige Sekunden lang versuchen können, und wenn Taste! == null dann brechen/zurück den Knopf. Ich benutze immer warten auf Element, das das Objekt zurückgibt oder löst eine Ausnahme und klicken Sie auf, zum Beispiel wie $ this-> waitForElement ('Selektor') -> click(); Wenn Sie ein Beispiel für waitForElement benötigen, lassen Sie es mich bitte wissen. – lauda