2013-05-29 14 views
24

Użytkownicy kliknij ten link:jak czekać na widoczności elementem phantomjs

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

Teraz jestem symulację kliknięcia w phantomjs:

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

wyjścia Phantom przed powolnym funkcja kończy swoje wykonanie i DIV staje się widoczny. Jak mogę wdrożyć czekanie?

+0

głupie rozwiązanie jest setTimeout() – igor

Odpowiedz

7

Moje podejście do tego scenariusza polega na poczekać, aż "coś" zostanie wykonane lub będzie prawdziwe. Wysoce sugeruję przetestować 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); 
     } 
    }); 
} 

Ten skrypt ocenić $('#thediv').is(':visible') (klasyczny kod jQuery) co 500 ms, aby sprawdzić, czy div jest widoczny.

+0

Niesamowite rozwiązanie. Nie wiem, jak zastosować to do węzła 'phantom', ze względu na wzór' Promise' @see https://github.com/amir20/phantomjs-node/issues/431 – loretoparisi

22

Herezje spin odpowiedzi Cybermaxs za:

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); 
} 

Przykład zastosowania:

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 
}); 

To trochę łatwiejsze, kiedy użyć zmiennej config.

+0

THANKYOU! Spędziłem dwa dni próbując pół tuzina innych sposobów niezawodnego sprawdzenia, czy formularz został złożony (a nie na ślepo nadziei "setTimeout") i to jest jedyny, który działał bezbłędnie dla mnie. +1 – indextwo

14

PhantomJS działa asynchronicznie domyślnie, co powoduje problemy, takie jak ta, którą opisujemy powyżej (gdzie skrypt kończy zanim wyniki są gotowe)

Jednak nic nie powstrzyma cię używania go w sposób synchroniczny.

Po prostu użyj phantom.page.sendEvent('mousemove') w pętli while. Zapętli się przez pompę zdarzeń, dopóki silnik webkitów nie załaduje strony lub nie przetworzy koniecznych zdarzeń przeglądarki.

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'); 

Zauważ, że page.loading może być także każdy inny stan logiczną, na przykład:

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

odkryłem to podejście podczas pracy nad projektem triflejs.org (wersja Internet Explorer od fantomu) próbuje naśladować połączeń do trifle.wait(ms) w środowisku PhantomJS.

+0

Czy 'phantom.page.sendEvent ('mousemove')' działa na stronie mobilnej? –

+0

@FengY pewnością. 'mousemove' jest właściwym wydarzeniem do użycia. Phantom (podobnie jak Twoja przeglądarka na komputer) nie zna strony mobilnej. Wszystko, co wie, to otwarcie strony internetowej i jej renderowanie. –

+0

Czy mogę usunąć 'page.sendEvent ('mousemove')' in while loop? –

0

Wewnątrz page.evaluate(), użyj self.loading własności do testowania doneness ....

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(); 
});