Проверка 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

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

Теперь давайте попробуем проверить эту страницу с помощью 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 быть доступным.

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


РЕДАКТИРОВАТЬ: удалось уменьшить эту проблему до простого файла 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 просто не может ждать синхронных запросов ..

1

Решение

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

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

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

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

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

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

Вот некоторые условия ajax, которые вы можете попробовать как к подпиточной behat-ждать-за-ан-АЯКС-вызова

0

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]