CasperJS'de 'O Zaman' Gerçekten Ne Anlama Geliyor?


97

Bir web sitesi aracılığıyla bir dizi tıklamayı, doldurulmuş formları, ayrıştırmayı vb. Otomatikleştirmek için CasperJS kullanıyorum.

Casper, thenifadeler biçiminde önceden belirlenmiş adımların bir listesi halinde düzenlenmiş gibi görünmektedir (örneğine bakın: http://casperjs.org/quickstart.html ), ancak bir sonraki ifadeyi gerçekten çalıştırmak için neyin tetiklediği belirsizdir.

Örneğin, thenbekleyen tüm isteklerin tamamlanmasını bekliyor mu? injectJSBekleyen bir istek olarak sayılır mı ? thenİç içe geçmiş bir ifadem varsa - bir openifadenin sonuna zincirlenmişse ne olur ?

casper.thenOpen('http://example.com/list', function(){
    casper.page.injectJs('/libs/jquery.js');
    casper.evaluate(function(){
        var id = jQuery("span:contains('"+itemName+"')").closest("tr").find("input:first").val();
        casper.open("http://example.com/show/"+id); //what if 'then' was added here?
    });
});

casper.then(function(){
    //parse the 'show' page
});

CasperJS'de akışın nasıl çalıştığına dair teknik bir açıklama arıyorum. Benim özel sorunum, son thenifademin (yukarıda) ifademden önce çalışması casper.openve nedenini bilmiyorum.


1
Hala flowcasperjs generali hakkında bir açıklama arıyorum, ancak temelde bir evaluateçağrı içinden casper'a başvuramayacağınızı keşfettim . (yani yeni bir url, günlük, yankı vb. açamazsınız). Benim durumumda değerlendirme çağrılıyordu ama dış dünyayla hiçbir şekilde etkileşime girmiyordu.
bendytree

1
Tam olarak aynı şeyleri merak ediyordum ama sormak için çok tembelim. İyi soru!
Nathan

4
evaluate()phantomjs'in göz attığı sayfanın DOM'sindeki "tarayıcıda" çalışan kod içindir. Yani orada yok casper.openama jQuery olabilir. Yani örneğiniz bir anlam ifade etmiyor, ama yine de ne then()işe yaradığını merak ediyorum .
Nathan

Yanıtlar:


93

then()temelde bir yığına yeni bir gezinme adımı ekler. Adım, iki farklı şey yapabilen bir javascript işlevidir:

  1. önceki adımı bekliyor - varsa - yürütülüyor
  2. istenen bir url ve ilgili sayfanın yüklenmesi bekleniyor

Basit bir gezinme senaryosunu ele alalım:

var casper = require('casper').create();

casper.start();

casper.then(function step1() {
    this.echo('this is step one');
});

casper.then(function step2() {
    this.echo('this is step two');
});

casper.thenOpen('http://google.com/', function step3() {
    this.echo('this is step 3 (google.com is loaded)');
});

Yığın içinde oluşturulan tüm adımları şu şekilde yazdırabilirsiniz:

require('utils').dump(casper.steps.map(function(step) {
    return step.toString();
}));

Bu verir:

$ casperjs test-steps.js
[
    "function step1() { this.echo('this is step one'); }",
    "function step2() { this.echo('this is step two'); }",
    "function _step() { this.open(location, settings); }",
    "function step3() { this.echo('this is step 3 (google.com is loaded)'); }"
]

_step()URL'yi bizim için yüklemek için CasperJS tarafından otomatik olarak eklenen işleve dikkat edin ; url yüklendiğinde, yığındaki bir sonraki adım - yani step3()- çağrılır.

Gezinme adımlarınızı tanımladığınızda, run()bunları sırayla birer birer yürütür:

casper.run();

Dipnot: geri çağırma / dinleyici şeyler, Promise modelinin bir uygulamasıdır .


Casperjs 1.0.0-RC1'de, "test-steps.js", işlev tanımlama dizeleri koleksiyonu yerine [nesne DOMWindow] koleksiyonunu görüntülüyor.
starlocke

[Nesne DOMWindow] koleksiyonu hala 1.0.0-RC4 sonucudur; Bu işlev tanımlarının nereye gittiğini merak ediyorum ...
starlocke

1
Başlangıçta CasperJS'nin işlevleri DOMWindows'a dönüştürmek için yeni bir hile yaptığını düşünmüştüm, ancak sorun gerçekten "this.toString ()" ve "return step.toString ()" idi - yanıt için bir düzenleme gönderdim.
starlocke

5
Sözde 'yığın' aslında bir kuyruk değil mi? Adımlar sırayla yürütülür, bir yığın olsaydı 3. adım, 2. adım, 1. adım beklemiyor muyduk?
Reut Sharabani

1
Sanırım şöyle olmalı: Bir yığın adımınız var. Bir adımı atıp değerlendiriyorsunuz. Boş bir sıra oluşturursunuz. Geçerli adımın işlenmesi nedeniyle oluşturulan tüm adımlar bu kuyruğa alınır. Adım değerlendirmeyi bitirdiğinde, kuyrukta oluşturulan tüm adımlar yığının en üstüne yerleştirilir, ancak sıraları içindeki sıraları korunur. (Yığını ters sırada itmekle aynı).
Mark

33

then() yalnızca bir dizi adımı kaydeder.

run() ve koşucu işlevleri, geri çağırmalar ve dinleyiciler ailesi, aslında her adımı yürütme işini yapan şeydir.

Bir adım tamamlandığında zaman, CasperJS 3 bayrakları karşı kontrol eder: pendingWait, loadInProgress, ve navigationRequested. Bu işaretlerden herhangi biri doğruysa, hiçbir şey yapmayın, daha sonraki bir zamana kadar boşta kalın ( setIntervalstil). Bu işaretlerin hiçbiri doğru değilse, bir sonraki adım yürütülür.

CasperJS 1.0.0-RC4'ten itibaren, belirli zamana dayalı koşullar altında, CasperJS, loadInProgressveya navigationRequestedişaretlerinden birini yükseltmek için zamana sahip olmadan önce "bir sonraki adımı yapmayı dene" yönteminin tetiklendiği bir kusur mevcuttur . Çözüm, bu bayrakların yükseltilmesi beklenen herhangi bir adımdan ayrılmadan önce bu bayraklardan birini yükseltmektir (örn: a için sormadan önce veya sonra bir bayrak kaldırın casper.click()), belki de şöyle:

(Not: Bu yalnızca açıklayıcıdır, uygun CasperJS formundan çok psuedocode gibidir ...)

step_one = function(){
    casper.click(/* something */);
    do_whatever_you_want()
    casper.click(/* something else */); // Click something else, why not?
    more_magic_that_you_like()
    here_be_dragons()
    // Raise a flag before exiting this "step"
    profit()
}

Bu çözümü tek satırlık bir koda dönüştürmek için, blockStep()bu github çekme isteğine ekledim , genişleterek click()ve clickLabel()kullanırken beklenen davranışı elde etmemizi garantilemeye yardımcı olacak bir araç olarak then(). Daha fazla bilgi, kullanım modelleri ve minimum test dosyaları için isteği inceleyin.


1
Çok yararlı ve büyük anlayış ve üzerinde öneri blockStepIMHO
Brian M. Hunt

Hâlâ "nihai cevap" çözümünü tartışıyoruz ... Umarım "küresel varsayılanlar" özelliğini uyguladığımda, CasperJS çeker.
starlocke

1
Yani evet, ona bir göz atın. :)
starlocke

Bunun için herhangi bir çözümümüz var mı? Eğer evet ise, bu nedir ?
Surender Singh Malik

Bunu açıkladığınız için çok teşekkürler. Ajax ağırlıklı bir uygulama için Casper işlevsel testlerim her zaman rastgele başarısız olduğu için, bu davranış beni bir yıldan uzun süredir öldürüyor.
brettjonesdev

0

Göre CasperJS Belgeler :

then()

İmza: then(Function then)

Bu yöntem, basit bir işlev sağlayarak yığına yeni bir gezinme adımı eklemenin standart yoludur:

casper.start('http://google.fr/');

casper.then(function() {
  this.echo('I\'m in your google.');
});

casper.then(function() {
  this.echo('Now, let me write something');
});

casper.then(function() {
  this.echo('Oh well.');
});

casper.run();

İhtiyaç duyduğunuz kadar adım ekleyebilirsiniz. Geçerli Casperörneğin thisanahtar kelimeyi sizin için adım işlevlerinde otomatik olarak bağladığını unutmayın.

Tanımladığınız tüm adımları çalıştırmak için run()yöntemi çağırın ve tamamlayın .

Not: Sen gerekir start()sırayla Casper örneği kullanmak için then()yöntem.

Uyarı: Eklenen adım işlevleri then()iki farklı durumda işlenir:

  1. önceki adım işlevi yürütüldüğünde,
  2. önceki ana HTTP isteği yürütüldüğünde ve sayfa yüklendiğinde ;

Yüklenen sayfanın tek bir tanımı olmadığını unutmayın ; DOMReady olayı tetiklendiğinde mi? "Tüm istekler tamamlanıyor" mu? "Tüm uygulama mantığı gerçekleştiriliyor" mu? Veya "işlenen tüm öğeler"? Cevap her zaman içeriğe bağlıdır. Bu nedenle, waitFor()gerçekte ne beklediğiniz üzerinde açık bir kontrol sağlamak için her zaman aile yöntemlerini kullanmaya teşvik ediliyorsunuz .

Yaygın bir numara kullanmaktır waitForSelector():

casper.start('http://my.website.com/');

casper.waitForSelector('#plop', function() {
  this.echo('I\'m sure #plop is available in the DOM');
});

casper.run();

Perde arkasında, kaynak koduCasper.prototype.then aşağıda gösterilmiştir:

/**
 * Schedules the next step in the navigation process.
 *
 * @param  function  step  A function to be called as a step
 * @return Casper
 */
Casper.prototype.then = function then(step) {
    "use strict";
    this.checkStarted();
    if (!utils.isFunction(step)) {
        throw new CasperError("You can only define a step as a function");
    }
    // check if casper is running
    if (this.checker === null) {
        // append step to the end of the queue
        step.level = 0;
        this.steps.push(step);
    } else {
        // insert substep a level deeper
        try {
            step.level = this.steps[this.step - 1].level + 1;
        } catch (e) {
            step.level = 0;
        }
        var insertIndex = this.step;
        while (this.steps[insertIndex] && step.level === this.steps[insertIndex].level) {
            insertIndex++;
        }
        this.steps.splice(insertIndex, 0, step);
    }
    this.emit('step.added', step);
    return this;
};

Açıklama:

Diğer bir deyişle, then()navigasyon sürecindeki bir sonraki adımı planlar.

Çağrıldığında then(), adım olarak çağrılacak bir parametre olarak bir fonksiyon geçirilir.

Bir örneğin başlatılıp başlatılmadığını kontrol eder ve başlamadıysa aşağıdaki hatayı görüntüler:

CasperError: Casper is not started, can't execute `then()`.

Ardından, pagenesnenin olup olmadığını kontrol eder null.

Koşul doğruysa, Casper yeni bir pagenesne oluşturur .

Bundan sonra, bir işlev olup olmadığını kontrol etmek then()için stepparametreyi doğrular .

Parametre bir işlev değilse, aşağıdaki hatayı görüntüler:

CasperError: You can only define a step as a function

Ardından, işlev Casper'ın çalışıp çalışmadığını kontrol eder.

Casper çalışmıyorsa, then()adımı kuyruğun sonuna ekler.

Aksi takdirde, Casper çalışıyorsa, önceki adımdan bir seviye daha derin bir alt adım ekler.

Son olarak, then()işlev bir step.addedolay yayınlayarak sona erer ve Casper nesnesini döndürür.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.