Điều gì thực sự có nghĩa là 'sau đó' trong CasperJS


97

Tôi đang sử dụng CasperJS để tự động hóa một loạt nhấp chuột, biểu mẫu đã hoàn thành, phân tích dữ liệu, v.v. thông qua một trang web.

Casper dường như được sắp xếp thành danh sách các bước đặt trước dưới dạng thencâu lệnh (xem ví dụ của họ ở đây: http://casperjs.org/quickstart.html ) nhưng không rõ điều gì kích hoạt câu lệnh tiếp theo thực sự chạy.

Ví dụ: có thenđợi hoàn thành tất cả các yêu cầu đang chờ xử lý không? Có injectJStính là một yêu cầu đang chờ xử lý không? Điều gì sẽ xảy ra nếu tôi có một thencâu lệnh lồng nhau - được xâu chuỗi vào cuối một opencâu lệnh?

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

Tôi đang tìm lời giải thích kỹ thuật về cách hoạt động của luồng trong CasperJS. Vấn đề cụ thể của tôi là thencâu lệnh cuối cùng của tôi (ở trên) chạy trước casper.opencâu lệnh của tôi và tôi không biết tại sao.


1
Tôi vẫn đang tìm kiếm lời giải thích về tổng thể flowcủa casperj, nhưng tôi đã phát hiện ra rằng về cơ bản bạn không thể tham chiếu casper từ bên trong evaluatecuộc gọi. (tức là bạn không thể mở url mới, nhật ký, tiếng vang, v.v.). Vì vậy, trong trường hợp của tôi, đánh giá đã được gọi nhưng không có cách nào để tương tác với thế giới bên ngoài.
bentytree

1
Tôi đã tự hỏi chính xác những điều tương tự nhưng quá lười để hỏi. Câu hỏi hay!
Nathan

4
evaluate()là cho mã chạy trong "trình duyệt", trong DOM của trang phantomjs đang duyệt. Vì vậy, không casper.opencó ở đó, nhưng có thể có jQuery. Vì vậy, ví dụ của bạn không có ý nghĩa, nhưng tôi vẫn tự hỏi những gì then()thực sự làm.
Nathan

Câu trả lời:


93

then()về cơ bản là thêm một bước điều hướng mới trong ngăn xếp. Một bước là một hàm javascript có thể thực hiện hai việc khác nhau:

  1. chờ bước trước đó - nếu có - được thực hiện
  2. đang đợi url được yêu cầu và trang liên quan tải

Hãy xem một kịch bản điều hướng đơn giản:

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

Bạn có thể in ra tất cả các bước đã tạo trong ngăn xếp như sau:

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

Điều đó mang lại:

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

Lưu ý _step()chức năng đã được CasperJS tự động thêm vào để tải url cho chúng tôi; khi url được tải, bước tiếp theo có sẵn trong ngăn xếp - chính là step3()- được gọi.

Khi bạn đã xác định các bước điều hướng của mình, hãy run()thực hiện lần lượt từng bước một:

casper.run();

Chú thích cuối trang: nội dung gọi lại / người nghe là một triển khai của mẫu Lời hứa .


Trong casperjs 1.0.0-RC1, "test-steps.js" đang hiển thị một tập hợp [đối tượng DOMWindow], thay vì một tập hợp các chuỗi định nghĩa hàm.
starlocke

Bộ sưu tập [object DOMWindow] vẫn là kết quả trong 1.0.0-RC4; Tôi tự hỏi nơi những định nghĩa chức năng đi ...
starlocke

1
Ban đầu tôi nghĩ rằng CasperJS đang thực hiện một thủ thuật mới để chuyển đổi các hàm thành DOMWindows, nhưng vấn đề thực sự là "return this.toString ()" so với "return step.toString ()" - Tôi đã gửi một bản chỉnh sửa cho câu trả lời.
starlocke

5
Không phải cái gọi là 'ngăn xếp' thực sự là một hàng đợi sao? Các bước được thực hiện theo thứ tự, nếu nó là một ngăn xếp, chúng ta sẽ không mong đợi bước 3, bước 2, bước 1?
Reut Sharabani

1
Tôi nghĩ nó phải như thế này: Bạn có một chồng các bước. Bạn bật ra một bước và đánh giá nó. Bạn tạo một hàng đợi trống. Bất kỳ bước nào được tạo ra do quá trình xử lý bước hiện tại sẽ được đưa vào hàng đợi này. Khi đánh giá xong bước, tất cả các bước được tạo trong hàng đợi sẽ được đưa lên đầu ngăn xếp, nhưng vẫn giữ nguyên thứ tự của chúng trong hàng đợi của chúng. (Tương tự như đẩy lên ngăn xếp theo thứ tự ngược lại).
Đánh dấu

33

then() chỉ đăng ký một loạt các bước.

run() và họ các hàm chạy, lệnh gọi lại và bộ nghe của nó, là tất cả những gì thực sự thực hiện công việc thực hiện mỗi bước.

Bất cứ khi nào một bước hoàn tất, CasperJS sẽ kiểm tra chống lại 3 lá cờ: pendingWait, loadInProgress, và navigationRequested. Nếu bất kỳ cờ nào trong số đó là đúng, thì không làm gì cả, hãy ở chế độ nhàn rỗi cho đến một thời gian sau ( setIntervalkiểu). Nếu không có cờ nào trong số đó là đúng, thì bước tiếp theo sẽ được thực thi.

Kể từ CasperJS 1.0.0-RC4, một lỗ hổng tồn tại, trong đó, trong các trường hợp dựa trên thời gian nhất định, phương pháp "thử làm bước tiếp theo" sẽ được kích hoạt trước khi CasperJS có thời gian để nâng một trong các loadInProgresshoặc navigationRequestedcờ. Giải pháp là nâng một trong những lá cờ đó trước khi rời khỏi bất kỳ bước nào mà những lá cờ đó dự kiến ​​sẽ được nâng lên (ví dụ: nâng một lá cờ trước hoặc sau khi yêu cầu a casper.click()), có thể như vậy:

(Lưu ý: Đây chỉ mang tính chất minh họa, giống mã psuedoc hơn là dạng CasperJS thích hợp ...)

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

Để gói giải pháp đó thành một dòng mã duy nhất, tôi đã giới thiệu blockStep()trong yêu cầu kéo github này , mở rộng click()clickLabel()như một phương tiện để giúp đảm bảo rằng chúng ta có được hành vi mong đợi khi sử dụng then(). Kiểm tra yêu cầu để biết thêm thông tin, cách sử dụng và các tệp thử nghiệm tối thiểu.


1
rất hữu ích và thông tin chi tiết và đề xuất tuyệt vời về blockStep, IMHO
Brian M. Hunt

Chúng tôi vẫn đang thảo luận về giải pháp "câu trả lời cuối cùng" ... Tôi hy vọng rằng một khi tôi triển khai khía cạnh "mặc định toàn cầu", CasperJS sẽ thực hiện được.
starlocke

1
Vì vậy, hãy để ý đến nó. :)
starlocke

Chúng tôi có bất kỳ giải pháp cho điều này? nếu có, nó là cái gì ?
Surender Singh Malik

Cảm ơn rất nhiều vì đã giải thích điều này. Hành vi này đã giết chết tôi hơn một năm nay khi các bài kiểm tra chức năng Casper của tôi cho một ứng dụng nặng Ajax luôn thất bại một cách ngẫu nhiên.
brettjonesdev

0

Theo Tài liệu CasperJS :

then()

Chữ ký: then(Function then)

Phương pháp này là cách tiêu chuẩn để thêm một bước điều hướng mới vào ngăn xếp, bằng cách cung cấp một chức năng đơn giản:

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

Bạn có thể thêm bao nhiêu bước tùy ý. Lưu ý rằng phiên bản hiện tại Caspertự động liên kết thistừ khóa cho bạn trong các hàm bước.

Để chạy tất cả các bước bạn đã xác định, hãy gọi run()phương thức và thì đấy.

Lưu ý: Bạn phải thể start()hiện casper để sử dụng then()phương thức này.

Cảnh báo: Các hàm bước được thêm vào then()được xử lý trong hai trường hợp khác nhau:

  1. khi chức năng bước trước đã được thực thi,
  2. khi yêu cầu HTTP chính trước đó đã được thực thi và trang được tải ;

Lưu ý rằng không có định nghĩa duy nhất về trang được tải ; đó là khi sự kiện DOMReady đã được kích hoạt? Nó có phải là "tất cả các yêu cầu đang được hoàn thành"? Nó có phải là "tất cả logic ứng dụng đang được thực hiện" không? Hay "tất cả các phần tử đang được kết xuất"? Câu trả lời luôn phụ thuộc vào ngữ cảnh. Do đó, tại sao bạn được khuyến khích luôn sử dụng các waitFor()phương pháp gia đình để kiểm soát rõ ràng những gì bạn thực sự mong đợi.

Một thủ thuật phổ biến là sử dụng waitForSelector():

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

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

casper.run();

Phía sau, mã nguồn củaCasper.prototype.then được hiển thị bên dưới:

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

Giải trình:

Nói cách khác, lập then()lịch cho bước tiếp theo trong quá trình điều hướng.

Khi then()được gọi, nó được truyền vào một hàm như một tham số sẽ được gọi như một bước.

Nó kiểm tra xem một phiên bản đã bắt đầu chưa, và nếu chưa, nó sẽ hiển thị lỗi sau:

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

Tiếp theo, nó kiểm tra xem pageđối tượng có null.

Nếu điều kiện là đúng, Casper tạo một pageđối tượng mới .

Sau đó, then()xác thực steptham số để kiểm tra xem nó có phải là một hàm hay không.

Nếu tham số không phải là một hàm, nó sẽ hiển thị lỗi sau:

CasperError: You can only define a step as a function

Sau đó, chức năng kiểm tra xem Casper có đang chạy hay không.

Nếu Casper không chạy, hãy then()thêm bước vào cuối hàng đợi.

Ngược lại, nếu Casper đang chạy, nó sẽ chèn một bước phụ sâu hơn bước trước đó.

Cuối cùng, then()hàm kết thúc bằng cách tạo ra một step.addedsự kiện và trả về đối tượng Casper.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.