Apa Artinya 'Lalu' di CasperJS

97

Saya menggunakan CasperJS untuk mengotomatiskan serangkaian klik, formulir yang telah diisi, penguraian data, dll melalui situs web.

Casper tampaknya diatur ke dalam daftar langkah prasetel dalam bentuk thenpernyataan (lihat contohnya di sini: http://casperjs.org/quickstart.html ) tetapi tidak jelas apa yang memicu pernyataan berikutnya untuk benar-benar dijalankan.

Misalnya, apakah thenmenunggu semua permintaan yang tertunda selesai? Apakah injectJSdihitung sebagai permintaan yang menunggu keputusan? Apa yang terjadi jika saya memiliki thenpernyataan bersarang - dirantai di akhir openpernyataan?

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

Saya mencari penjelasan teknis tentang cara kerja aliran di CasperJS. Masalah khusus saya adalah bahwa thenpernyataan terakhir saya (di atas) berjalan sebelum casper.openpernyataan saya & saya tidak tahu mengapa.

bendytree.dll
sumber
1
Saya masih mencari penjelasan tentang jenderal flowcasperjs, tetapi saya telah menemukan bahwa pada dasarnya Anda tidak dapat merujuk casper dari dalam evaluatepanggilan. (yaitu Anda tidak dapat membuka url baru, log, echo, dll). Jadi dalam kasus saya, evaluasi dipanggil tetapi tidak ada cara untuk berinteraksi dengan dunia luar.
bendytree
1
Saya bertanya-tanya hal yang persis sama tetapi terlalu malas untuk bertanya. Pertanyaan bagus!
Nathan
4
evaluate()adalah untuk kode yang berjalan di "browser", di DOM halaman phantomjs sedang menjelajah. Jadi tidak ada di casper.opensana, tetapi mungkin ada jQuery. Jadi teladan Anda tidak masuk akal, tapi saya masih bertanya-tanya apa yang then()sebenarnya terjadi.
Nathan

Jawaban:

93

then()pada dasarnya menambahkan langkah navigasi baru dalam tumpukan. Langkah adalah fungsi javascript yang dapat melakukan dua hal berbeda:

  1. menunggu langkah sebelumnya - jika ada - dijalankan
  2. menunggu url yang diminta dan halaman terkait untuk dimuat

Mari kita ambil skenario navigasi sederhana:

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

Anda dapat mencetak semua langkah yang dibuat dalam tumpukan seperti ini:

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

Itu memberi:

$ 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)'); }"
]

Perhatikan _step()fungsi yang telah ditambahkan secara otomatis oleh CasperJS untuk memuat url untuk kita; ketika url dimuat, langkah selanjutnya yang tersedia di tumpukan - yang step3()- dipanggil.

Ketika Anda telah menentukan langkah-langkah navigasi Anda, run()jalankan satu per satu secara berurutan:

casper.run();

Catatan kaki: item panggilan balik / pendengar adalah implementasi dari pola Janji .

NiKo
sumber
Dalam casperjs 1.0.0-RC1, "test-steps.js" menampilkan kumpulan [object DOMWindow], bukan kumpulan string definisi fungsi.
starlocke
Koleksi [object DOMWindow] masih merupakan hasil dalam 1.0.0-RC4; Aku ingin tahu kemana definisi fungsi itu pergi ...
starlocke
1
Awalnya saya mengira CasperJS sedang melakukan trik baru untuk mengubah fungsi menjadi DOMWindows, tetapi masalahnya sebenarnya adalah "return this.toString ()" vs "return step.toString ()" - Saya mengirimkan hasil edit untuk jawabannya.
starlocke
5
Bukankah yang disebut 'tumpukan' sebenarnya adalah antrian? Langkah-langkahnya dieksekusi secara berurutan, seandainya itu tumpukan, bukankah kita mengharapkan langkah 3, langkah 2, langkah 1?
Reut Sharabani
1
Saya pikir pasti seperti ini: Anda memiliki setumpuk anak tangga. Anda keluar dari satu langkah dan mengevaluasinya. Anda membuat antrian kosong. Setiap langkah yang dihasilkan karena pemrosesan langkah saat ini dimasukkan ke dalam antrean ini. Ketika langkah selesai mengevaluasi, semua langkah yang dihasilkan dalam antrian akan diletakkan di atas tumpukan, tetapi mempertahankan urutannya dalam antriannya. (Sama seperti mendorong tumpukan dengan urutan terbalik).
Tandai
33

then() hanya mendaftarkan serangkaian langkah.

run() dan rangkaian fungsi runner, callback, dan listenernya, semuanya melakukan tugas melaksanakan setiap langkah.

Setiap kali langkah selesai, CasperJS akan memeriksa terhadap 3 bendera: pendingWait, loadInProgress, dan navigationRequested. Jika salah satu dari flag tersebut benar, maka tidak melakukan apa-apa, diam sampai nanti ( setIntervalgaya). Jika tidak ada satu pun dari tanda tersebut yang benar, maka langkah selanjutnya akan dijalankan.

Pada CasperJS 1.0.0-RC4, terdapat cacat, di mana, dalam keadaan berbasis waktu tertentu, metode "coba lakukan langkah berikutnya" akan dipicu sebelum CasperJS punya waktu untuk menaikkan salah satu dari loadInProgressatau navigationRequestedbendera. Solusinya adalah menaikkan salah satu bendera tersebut sebelum meninggalkan langkah apa pun di mana bendera tersebut diharapkan akan dinaikkan (misal: menaikkan bendera sebelum atau setelah meminta a casper.click()), mungkin seperti ini:

(Catatan: Ini hanya ilustrasi, lebih mirip psuedocode daripada bentuk CasperJS yang tepat ...)

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

Untuk membungkus solusi itu menjadi satu baris kode, saya perkenalkan blockStep()dalam permintaan tarik github ini , memperluas click()dan clickLabel()sebagai sarana untuk membantu menjamin bahwa kami mendapatkan perilaku yang diharapkan saat menggunakan then(). Lihat permintaan untuk info lebih lanjut, pola penggunaan, dan file pengujian minimum.

starlocke
sumber
1
wawasan & saran yang sangat membantu dan hebat tentang blockStep, IMHO
Brian M. Hunt
Kami masih mendiskusikan solusi "jawaban akhir" ... Saya harap setelah saya menerapkan aspek "default global", CasperJS akan menariknya.
starlocke
1
Jadi ya, perhatikan itu. :)
starlocke
Apakah kita punya solusi untuk ini? Jika ya, apa itu ?
Surender Singh Malik
Terima kasih banyak telah menjelaskan hal ini. Perilaku ini telah membunuh saya selama lebih dari setahun sekarang karena tes fungsional Casper saya untuk aplikasi Ajax-heavy gagal secara acak sepanjang waktu.
brettjonesdev
0

Menurut Dokumentasi CasperJS :

then()

Tanda tangan: then(Function then)

Metode ini adalah cara standar untuk menambahkan langkah navigasi baru ke tumpukan, dengan menyediakan fungsi sederhana:

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

Anda dapat menambahkan langkah sebanyak yang Anda butuhkan. Perhatikan bahwa Caspercontoh saat ini secara otomatis mengikat thiskata kunci untuk Anda dalam fungsi langkah.

Untuk menjalankan semua langkah yang Anda tentukan, panggil run()metode, dan voila.

Catatan: Anda harus start()contoh casper untuk menggunakan then()metode ini.

Peringatan: Fungsi langkah yang ditambahkan ke then()diproses dalam dua kasus berbeda:

  1. ketika fungsi langkah sebelumnya telah dijalankan,
  2. ketika permintaan HTTP utama sebelumnya telah dijalankan dan halaman dimuat ;

Perhatikan bahwa tidak ada definisi tunggal tentang halaman yang dimuat ; apakah itu ketika acara DOMReady telah dipicu? Apakah ini "semua permintaan diselesaikan"? Apakah itu "semua logika aplikasi sedang dilakukan"? Atau "semua elemen sedang dirender"? Jawabannya selalu tergantung pada konteksnya. Oleh karena itu mengapa Anda didorong untuk selalu menggunakan waitFor()metode keluarga untuk tetap mengontrol secara eksplisit apa yang sebenarnya Anda harapkan.

Trik yang umum digunakan adalah waitForSelector():

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

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

casper.run();

Di balik layar, kode sumber untukCasper.prototype.then ditampilkan di bawah ini:

/**
 * 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;
};

Penjelasan:

Dengan kata lain, then()menjadwalkan langkah berikutnya dalam proses navigasi.

Ketika then()dipanggil, itu melewati fungsi sebagai parameter yang akan disebut sebagai langkah.

Ia memeriksa apakah sebuah instance telah dimulai, dan jika belum, ini akan menampilkan kesalahan berikut:

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

Selanjutnya, ia memeriksa apakah pageobjeknya null.

Jika kondisinya benar, Casper membuat pageobjek baru .

Setelah itu, then()validasi stepparameter untuk memeriksa apakah itu bukan fungsi.

Jika parameter bukan fungsi, ini akan menampilkan kesalahan berikut:

CasperError: You can only define a step as a function

Kemudian, fungsi tersebut memeriksa apakah Casper sedang berjalan.

Jika Casper tidak berjalan, then()menambahkan langkah ke akhir antrian.

Jika tidak, jika Casper sedang berjalan, Casper akan memasukkan sub-langkah yang lebih dalam dari langkah sebelumnya.

Terakhir, then()fungsi tersebut diakhiri dengan memancarkan step.addedperistiwa, dan mengembalikan objek Casper.

Berikan Miller
sumber