Проверка JavaScript AJAX загруженных ресурсов с помощью Mink / Zombie в PHP?

Я пытаюсь проверить страницу, которая в противном случае использует массивные асинхронно загруженные ресурсы JavaScript, с PHP-скриптом, который использует Mink и Zombie. К сожалению, этот процесс терпит неудачу (оставляя за собой зависающие процессы ). Мне удалось наконец смоделировать эту ошибку на минимальном примере, который я выложу здесь.

Первая страница – проверяемая страница – это в основном автономный файл , который имитирует вызовы 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> 

Итак, когда страница загружается первой, нет никаких действий; и как только нажимается кнопка: сначала возвращается какой-то HTML-контент, содержащий <script> (это ?getone ) через вызов AJAX; то этот скрипт сам загружается «автоматически» из ?gettwo . Я знаю, что это, вероятно, не совсем то, что нужно сделать (см. JavaScript console.log вызывает ошибку: «Синхронный XMLHttpRequest в основном потоке устарел …» ), но на фактической странице я есть аналогичная организация пытаясь проверить.

Чтобы запустить эту страницу, вы можете просто получить версию php версии 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 

Btw, ошибка « TypeError: ... is not a function » – это то же самое, что я получаю от Mink / Zombie с фактической страницей, которую я пытаюсь проверить; хотя я не думаю, что сама страница демонстрирует эту ошибку, когда она называется автономной, как она здесь.

Теперь давайте попробуем проверить эту страницу с помощью Mink (установить как на nodejs невозможно найти модуль «зомби» с PHP норкой ):

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"; ?> 

Запуск этого из другой оболочки терминала (в то время как первый по-прежнему запускает процесс php-сервера, который обслуживает test_JSload.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)'); ', который проверяет для browser.statusCode внутри JavaScript; Я знаю, что есть также:

 $returnstring = $session->evaluateScript('document.readyState'); 

… который также может запрашивать данные из JavaScript. Таким образом, что-то вроде этого – в принципе – может быть использовано для «ожидания», пока не будут загружены заданные ресурсы.

Но проблема здесь в том, что переменная ptest , которая раскрывает проблему, находится в локальной области функции OnGetdata() , и поэтому я понятия не имею, как построить оператор, который «проверял» его на PHP, где я ожидаю только глобальные переменные, такие как browser и document должны быть доступны.

Итак, кто-нибудь теперь, как я мог бы использовать PHP-скрипт Mink / Zombie «ждать» для такой «вложенной» загрузки JavaScript, чтобы я мог завершить загрузку страницы после виртуального щелчка без ошибки?


EDIT: удалось уменьшить эту проблему до простого файла JavaScript Zombie:

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 просто не может ждать синхронных запросов.

Solutions Collecting From Web of "Проверка JavaScript AJAX загруженных ресурсов с помощью Mink / Zombie в PHP?"

Попробуйте это тоже:

 $session->wait(50000, "document.readyState === 'complete'"); 

Или попробуйте другое условие ожидания, например, сделать, пока ждать объекта, также, пожалуйста, не то, что findButton может возвращать значение null, если элемент не найден / загружен, поэтому вы можете попробовать сделать-в течение нескольких секунд и если кнопка! == null, тогда перерыв / возврат кнопки.

Я всегда использую wait для элемента, который возвращает объект или генерирует исключение, и нажимает, например:

 $this->waitForElement('selector')->click(); 

Если вам нужен пример waitForElement, пожалуйста, дайте мне знать.

Вот некоторые условия ajax, которые вы можете попробовать: как сделать-behat-wait-for-a-ajax-call