web-dev-qa-db-fra.com

comment attendre la visibilité des éléments dans phantomjs

Les utilisateurs cliquent sur ce lien:

<span onclick="slow_function_that_fills_the_panel(); $('#panel').show();">

Maintenant, je simule le clic dans phantomjs:

page.evaluate(
  function() { $("#panel").click(); }
);
console.log('SUCCESS');
phantom.exit();

Phantom se ferme avant que la fonction lente ne termine son exécution et que le DIV ne devienne visible. Comment puis-je implémenter l'attente?

26
yegor256

Voici un aperçu de la réponse de Cybermaxs:

function waitFor ($config) {
    $config._start = $config._start || new Date();

    if ($config.timeout && new Date - $config._start > $config.timeout) {
        if ($config.error) $config.error();
        if ($config.debug) console.log('timedout ' + (new Date - $config._start) + 'ms');
        return;
    }

    if ($config.check()) {
        if ($config.debug) console.log('success ' + (new Date - $config._start) + 'ms');
        return $config.success();
    }

    setTimeout(waitFor, $config.interval || 0, $config);
}

Exemple d'utilisation:

waitFor({
    debug: true,  // optional
    interval: 0,  // optional
    timeout: 1000,  // optional
    check: function () {
        return page.evaluate(function() {
            return $('#thediv').is(':visible');
        });
    },
    success: function () {
        // we have what we want
    },
    error: function () {} // optional
});

C'est un peu plus facile lorsque vous utilisez une variable de configuration.

23
Chad Scira

PhantomJS s'exécute de manière asynchrone par défaut, provoquant des problèmes comme celui que vous décrivez ci-dessus (où le script se termine avant que vos résultats ne soient prêts)

Cependant, rien ne vous empêche de l'utiliser de manière synchrone.

Utilisez simplement phantom.page.sendEvent('mousemove') dans une boucle while. Cela continuera à parcourir la pompe d'événements jusqu'à ce que le moteur du kit Web charge votre page ou traite les événements de navigateur nécessaires.

var page = require('webpage').create();

// Step 1: View item
page.open('http://localhost/item3324.php');
do { phantom.page.sendEvent('mousemove'); } while (page.loading);
page.render('step1-viewitem.png');

// Step 2: Add to cart
page.evaluate(function() {$('#add-to-cart').click(); });
do { phantom.page.sendEvent('mousemove'); } while (page.loading);
page.render('step2-viewcart.png');

// Step 3: Confirm contents
page.evaluate(function() {$('#confirm-cart').click(); });
do { phantom.page.sendEvent('mousemove'); } while (page.loading);
page.render('step3-confirm.png');

Notez que page.loading Peut également être toute autre condition booléenne, par exemple:

do { phantom.page.sendEvent('mousemove'); } 
while (page.evaluate(function() {return $("#panel").is(":visible");}));

J'ai découvert cette approche en travaillant sur le projet triflejs.org (la version Internet Explorer de fantôme) en essayant d'émuler des appels à trifle.wait(ms) dans l'environnement PhantomJS.

15
Steven de Salas

Mon approche pour ce scénario est d'attendre que "quelque chose" soit fait ou vrai. Je vous suggère fortement de tester waitfor.js .

demo.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <script src="http://code.jquery.com/jquery-1.9.1.js"></script>
    <title>Test</title>
</head>
<body id="body">

    <div id="thediv">Hello World !</div>

    <script type="text/javascript">
        $('#thediv').hide();
        setTimeout(function () {
            $('#thediv').show();
        }, 3000);

    </script>
</body>
</html>

demoscript.js

var page = require('webpage').create();
var system = require('system');

function waitFor(testFx, onReady, timeOutMillis) {
    var maxtimeOutMillis = timeOutMillis ? timeOutMillis : 5000, //< Default Max Timout is 5s
        start = new Date().getTime(),
        condition = false,
        interval = setInterval(function () {
            if ((new Date().getTime() - start < maxtimeOutMillis) && !condition) {
                // If not time-out yet and condition not yet fulfilled
                condition = (typeof (testFx) === "string" ? eval(testFx) : testFx()); //< defensive code
            } else {
                if (!condition) {
                    // If condition still not fulfilled (timeout but condition is 'false')
                    //console.log("'waitFor()' timeout");
                    typeof (onReady) === "string" ? eval(onReady) : onReady();
                    clearInterval(interval);
                    //phantom.exit(1);
                } else {
                    // Condition fulfilled (timeout and/or condition is 'true')
                    console.log("'waitFor()' finished in " + (new Date().getTime() - start) + "ms.");
                    typeof (onReady) === "string" ? eval(onReady) : onReady(); //< Do what it's supposed to do once the condition is fulfilled
                    clearInterval(interval); //< Stop this interval
                }
            }
        }, 500); //< repeat check every 500ms
};

if (system.args.length != 1) {
    console.log('invalid call');
    phantom.exit(1);
} else {
    //adapt the url to your context
    page.open('http://localhost:40772/demo.html', function (status) {
        if (status !== 'success') {
            console.log('Unable to load the address!');
            phantom.exit();
        } else {
            waitFor(
                function () {
                    return page.evaluate(function () {
                        return $('#thediv').is(':visible');
                    });
                },
                function () {
                    page.render('page.png');
                    phantom.exit();
                }, 5000);
        }
    });
}

Ce script évalue $('#thediv').is(':visible') (code Jquery classique) toutes les 500 ms pour vérifier si le div est visible.

7
Cybermaxs

À l'intérieur de page.evaluate (), utilisez la propriété self.loading pour tester la cuisson ....

var fs = require('fs');
path = '/path/to/file.html';
address = 'http://google.com';    

page.open(address, function (status) {
    if (status !== 'success') {
        console.log('Unable to access page');
    } else {
        var p = page.evaluate(function () {
            if(!self.loading){ // ah, such beauty
              return document.documentElement.outerHTML;
            }
        });
    fs.write(path, p, 'w');
    }
    phantom.exit();
});     
1
Chris Fortune