PHP Mink/Zombie - handling hanging processes after a fatal exception?
Asked Answered
F

2

0

I'm using the PHP script with Mink+Zombie driver (install as on nodejs cannot find module 'zombie' with PHP mink) from here: PHP Mink/Zombie - page visit returns status code 0? ; re-posting for completeness:

<?php
$nodeModPath = "/home/USERNAME/.nvm/versions/node/v4.0.0/lib/node_modules";

# composer autoload:
require_once __DIR__ . '/vendor/autoload.php';

$URL = "https://demo.centreon.com/centreon";
$USERNAME = "admin";
$PASSWORD = "centreon";
$LFID = "useralias";
$PFID = "password";
$BFID = "submitLogin";


$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->setRequestHeader('User-Agent', 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.36');

$statcode = 0;
while ($statcode != 200) {
  $isleep = rand(2, 7); echo "sleeping $isleep sec...\n";
  sleep($isleep);
  $session->visit($URL);
  // $session->wait(20000, '(0 === jQuery.active)'); # Uncaught exception 'RuntimeException' with message 'Could not establish connection: Connection refused (111)'
  $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_login = $page->findField($LFID);
$el_password = $page->findField($PFID);
$el_button = $page->find('xpath', '//*[@name="'.$BFID.'"]');//findById($BFID);//findField($BFID);

$el_login->setValue($USERNAME);
$el_password->setValue($PASSWORD);

echo "  pressing/clicking login button\n";
$el_button->click();

echo "Page URL after click: ". $session->getCurrentUrl() . "\n";
$page = $session->getPage();
?>

The problem is, when the script runs, and does a login click, some JavaScript libraries that the page uses cannot be detected by Mink, and an exception is raised:

$ php test_php_mink.php 
sleeping 4 sec...
  current URL: https://demo.centreon.com/centreon/
  status code: 200
  pressing/clicking login button
PHP Fatal error:  Uncaught exception 'Behat\Mink\Exception\DriverException' with message 'Error while processing event 'click': "ReferenceError: Effect is not defined\n    at https://demo.centreon.com/centreon/include/common/javascript/modalbox.js:517:1\n    at Object.exports.runInContext (vm.js:44:17)\n    at window._evaluate (/home/USERNAME/.nvm/versions/node/v4.0.0/lib/node_modules/zombie/lib/document.js:253:75)\n    at Object.DOM.languageProcessors.javascript (/home/USERNAME/.nvm/versions/node/v4.0.0/lib/node_modules/zombie/lib/dom/scripts.js:26:12)\n    at define.proto._eval (/home/USERNAME/.nvm/versions/node/v4.0.0/lib/node_modules/zombie/node_modules/jsdom/lib/jsdom/level2/html.js:1477:47)\n    at /home/USERNAME/.nvm/versions/node/v4.0.0/lib/node_modules/zombie/node_modules/jsdom/lib/jsdom/browser/resource-loader.js:32:22\n    at Object.item.check (/home/USERNAME/.nvm/versions/node/v4.0.0/lib/node_modules/zombie/node_modules/jsdom/lib/jsdom/level2/html.js:188:11)\n    at Object.item.check (/home in /path/to/test_php_mink/vendor/behat/mink-zombie-driver/src/ZombieDriver.php on line 880

The exception is ReferenceError: Effect is not defined, and it is due to the page using this javascript:

<script type="text/javascript" src="./include/common/javascript/scriptaculous/scriptaculous.js?load=effects,dragdrop"></script>
...
    new Effect.toggle('header', 'appear', { afterFinish: function() {

However, the even bigger problem is that after this crash happens, then when I run the script for a second time, it crashes almost immediately, this time with Error: listen EADDRINUSE 127.0.0.1:8124:

$ php test_php_mink.php 
PHP Fatal error:  Uncaught exception 'RuntimeException' with message 'Server process has been terminated: (1) [events.js:141
      throw er; // Unhandled 'error' event
      ^

Error: listen EADDRINUSE 127.0.0.1:8124
    at Object.exports._errnoException (util.js:837:11)
    at exports._exceptionWithHostPort (util.js:860:20)
    at Server._listen2 (net.js:1231:14)
    at listen (net.js:1267:10)
    at net.js:1376:9
    at doNTCallback3 (node.js:440:9)
    at process._tickCallback (node.js:346:17)
    at Function.Module.runMain (module.js:473:11)
    at startup (node.js:117:18)
    at node.js:951:3
]' in /path/to/test_php_mink/vendor/behat/mink-zombie-driver/src/NodeJS/Server.php:413
Stack trace:
#0 /path/to/test_php_mink/vendor/behat/mink-zombie-driver/src/NodeJS/Server.php(306): Behat\Mink\Driver\NodeJS\Server->checkAvailability()
#1 /path/to/test_php_mink/vendor/behat/mink-zombie-driver/src/ZombieDriver. in /path/to/test_php_mink/vendor/behat/mink-zombie-driver/src/NodeJS/Server.php on line 413

It seems that after the first crash, there are still mink processes hanging around - and indeed, there are:

$ pgrep -fl mink
7659 sh
7660 node

$ ps axf | grep mink
 7687 pts/0    S+     0:00  |       \_ grep --color=tty mink
 7659 pts/0    S      0:00 sh -c 'node' '/path/to/test_php_mink/vendor/behat/mink-zombie-driver/bin/mink-zombie-server.js'
 7660 pts/0    Sl     0:03  \_ node /path/to/test_php_mink/vendor/behat/mink-zombie-driver/bin/mink-zombie-server.js

At this point, if I kill these processes - say, with pkill -f mink - then they disappear from the process tree, and then I can run the script again, with the same results as the first call in the OP (and then I have to kill processes again, etc).

What would be the best/recommended way to handle this, so I don't have to manually kill processes, each time the script reaches a fatal exception? Is there something in the Mink library that would allow for this?

Faretheewell answered 27/5, 2016 at 11:55 Comment(1)
Note, I tested this with zombie v4.2.1 with nodejs versions iojs-v3.3.1, 4.0.0, 4.4.5, 6.2.0 - and they all fail with this script in the exactly same way.Faretheewell
R
1

First thing to do in order to avoid fatal exceptions is to implement/use methods with proper error handling. You can create a method to check if the element is not null and if it is to throw a custom exception.

For example in your case findField method will return null if the element is not found, in this case setValue will e used on a non-object resulting in a fatal error.

Ragi answered 31/5, 2016 at 15:2 Comment(6)
Thanks for that, @Ragi - I'm still not clear on exactly how to implement a method with proper error handling, but I'll look it up... Cheers!Faretheewell
OK, I did a bit more testing, and I did echo "Check: el_l: " . gettype($el_login) . " el_p: " . gettype($el_password) . " el_b " . gettype($el_button) . "\n"; after the findField commands, and none of the objects is null: Check: el_l: object el_p: object el_b object. So, the exception does not happen because setValue() or click() is called on a non-object - as stated in the OP, the exception happens somewhere inside the click method, due to an exter JS class not being found. So, the question is still open...Faretheewell
How do you set $BFID? Do you need to click or you have a slider or something like that? If this is a special case and click action is working in other cases have you tried to use executeScript for this case?Ragi
It is possible that the element would not receive the click? Can you post the html code with element you are trying to click?, it would be helpful.Ragi
$BFID is set at top of the OP script: $BFID = "submitLogin"; - it's just a string. Btw, the entire PHP file is self-contained, if you have installed mink/zombie - you can just copy paste into a file and run it with php fromk php-cli. And as the script says, the page loaded is $URL = "https://demo.centreon.com/centreon";.Faretheewell
Sorry i missed that. I see the button is clicked if execute the following javascript in the browser: document.querySelector("[name*='submitLogin']").click();Ragi
R
0

An example of exception handling is:

public function fillWith($locator, $text){
        $element = $this->getSession()->getPage()->find("xpath", $locator);

        if($element === null){
            throw new Exception("Element $locator not found");
        }else{
            $element->setValue($text);
        }
    }
Ragi answered 1/6, 2016 at 9:15 Comment(1)
Thanks @Ragi - you should have edited your old answer to put this information in; but it still doesn't change the fact that non-object exception handling is not the problem here...Faretheewell

© 2022 - 2024 — McMap. All rights reserved.