Що означає "тоді" насправді означає CasperJS


97

Я використовую CasperJS для автоматизації серії кліків, заповнених форм, розбору даних тощо через веб-сайт.

Каспер, схоже, організований у список заданих кроків у вигляді thenвисловлювань (див. Їхній приклад тут: http://casperjs.org/quickstart.html ), але незрозуміло, що викликає насправді наступне твердження.

Наприклад, чи thenчекає завершення всіх очікуваних запитів? Чи injectJSвраховується як очікуваний запит? Що станеться, якщо у мене thenоператор вкладений - прикутий до кінця openоператора?

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. Моя конкретна проблема полягає в тому, що моє останнє thenтвердження (вище) працює перед моїм casper.openтвердженням, і я не знаю чому.


1
Я все ще шукаю пояснення генерала flowcasperjs, але я виявив, що ви, по суті, не можете посилатися на каспера в межах evaluateдзвінка. (тобто ви не можете відкрити новий URL, журнал, ехо тощо). Тож у моєму випадку оцінювали, але не було можливості взаємодіяти із зовнішнім світом.
bendytree

1
Я дивувався точно таким самим речам, але лінувався запитати. Гарне питання!
Натан

4
evaluate()призначений для коду, який працює у "браузері", у DOM сторінки переглядає phantomjs. Так що його немає casper.open, але може бути jQuery. Тож ваш приклад не має сенсу, але мені все одно цікаво, що then()насправді робить.
Натан

Відповіді:


93

then()в основному додає новий крок навігації в стек. Крок - це функція javascript, яка може робити дві різні речі:

  1. чекаючи виконання попереднього кроку - якщо такий є -
  2. очікуючи завантаження запитуваного URL-адреси та пов’язаної сторінки

Візьмемо простий сценарій навігації:

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

Ви можете роздрукувати всі створені етапи в стеку так:

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

Це дає:

$ 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()функцію, яку автоматично додав CasperJS для завантаження для нас URL-адреси; коли URL-адреса завантажується, наступний крок, доступний у стеці - який є step3()- викликається.

Визначивши кроки навігації, виконайте run()їх один за одним послідовно:

casper.run();

Виноска: інформація про зворотний виклик / слухача - це реалізація схеми Обіцяння .


У casperjs 1.0.0-RC1 "test-steps.js" відображає колекцію [object DOMWindow], а не колекцію рядків визначення функції.
starlocke

Колекція [object DOMWindow] все ще є результатом в 1.0.0-RC4; Цікаво, куди пішли ці визначення функцій ...
starlocke

1
Спочатку я думав, що CasperJS робить новий трюк з перетворення функцій у DOMWindows, але проблема була насправді "return this.toString ()" проти "return step.toString ()" - я надіслав редагування для відповіді.
starlocke

5
Чи не так звана "стека" насправді черга? Етапи виконуються в порядку, якби це був стек, чи не очікували б нас 3, крок 2, крок 1?
Reut Sharabani

1
Я думаю, що повинно бути так: у вас є степка кроків. Ви вискакуєте крок і оцінюєте його. Ви створюєте порожню чергу. Будь-які кроки, створені внаслідок обробки поточного кроку, ставлять у цю чергу. Після завершення етапу оцінювання всі згенеровані кроки в черзі ставлять над стеком, але зберігаючи їх порядок у черзі. (Те саме, що натискання на стек у зворотному порядку).
Позначте

33

then() просто реєструє ряд кроків.

run() та його сімейство функцій бігуна, зворотні дзвінки та слухачі - це все, що насправді виконує роботу з виконання кожного кроку.

Всякий раз , коли крок завершено, CasperJS перевірятиме проти 3 прапорів: pendingWait, loadInProgress, і navigationRequested. Якщо будь-який із цих прапорів істинний, тоді нічого не робіть, не працюйте до подальшого часу ( setIntervalстиль). Якщо жоден із цих прапорів не відповідає дійсності, наступний крок буде виконаний.

Станом на CasperJS 1.0.0-RC4, недолік існує, де при певних почасових обставин «спробувати зробити наступний крок» метод буде викликаний , перш ніж CasperJS встиг підняти жоден з loadInProgressабо navigationRequestedпрапорів. Рішення полягає в тому, щоб підняти один із цих прапорів перед тим, як залишити будь-який крок, на якому очікується підняття цих прапорів (наприклад: підняти прапор перед або після запиту на a casper.click()), можливо так:

(Примітка. Це лише наочно, більше нагадує psuedocode, ніж належну форму CasperJS ...)

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

Щоб завершити це рішення в однорядковий код, я вніс blockStep()цей запит на тягу github , що розширює click()і clickLabel()як засіб, що допомагає гарантувати, що ми отримаємо очікувану поведінку при використанні then(). Перегляньте запит на отримання додаткової інформації, моделей використання та мінімальних тестових файлів.


1
дуже корисні та чудові прозріння та пропозиції щодо blockStep, ІМХО
Брайан М. Хант

Ми все ще обговорюємо рішення "остаточної відповіді" ... Я сподіваюся, що як тільки я застосую аспект "глобальних значень за замовчуванням", CasperJS зробить тяжкий результат.
starlocke

1
Так що так, слідкуйте за цим. :)
starlocke

Чи є у нас рішення для цього? якщо так, що це?
Surender Singh Malik

Дуже дякую за пояснення цього. Така поведінка вбиває мене вже більше року, оскільки мої функціональні тести Casper для додатків Ajax-важких постійно постійно виходять з ладу.
brettjonesdev

0

Відповідно до Документації CasperJS :

then()

Підпис: then(Function then)

Цей метод є стандартним способом додавання нового кроку навігації до стеку, надаючи просту функцію:

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

Ви можете додати стільки кроків, скільки вам потрібно. Зауважте, що поточний Casperекземпляр автоматично пов'язує thisключове слово для вас у межах функцій кроку.

Щоб виконати всі визначені вами кроки, зателефонуйте run()методу та voila.

Примітка.start() Щоб скористатися then()методом, ви повинні використовувати екземпляр каспер .

Попередження: Додані функції крок then()обробляються у двох різних випадках:

  1. коли функція попереднього кроку була виконана,
  2. коли попередній основний HTTP-запит виконаний і сторінка завантажена ;

Зауважте, що не існує єдиного визначення завантаженої сторінки ; це коли спрацьовує подія DOMReady? Це "всі запити закінчені"? Це "виконується вся логіка програми"? Або "всі елементи, що надаються"? Відповідь завжди залежить від контексту. Отже, чому вам пропонується завжди використовувати waitFor()сімейні методи, щоб чітко контролювати те, що ви насправді очікуєте.

Поширений трюк - використовувати waitForSelector():

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

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

casper.run();

Позаду, вихідний код дляCasper.prototype.then наведено нижче:

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

Пояснення:

Іншими словами, then()намічає наступний крок у процесі навігації.

Коли then()викликається, передається функція як параметр, який повинен бути названий як крок.

Він перевіряє, чи запущений екземпляр, і якщо він не, він відображає таку помилку:

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

Далі він перевіряє, чи є pageоб’єкт null.

Якщо умова справжня, Каспер створює новий pageоб’єкт.

Після цього then()перевіряє stepпараметр, щоб перевірити, чи він не є функцією.

Якщо параметр не є функцією, він відображає таку помилку:

CasperError: You can only define a step as a function

Потім функція перевіряє, чи працює Casper.

Якщо Casper не працює, then()додає крок до кінця черги.

В іншому випадку, якщо Casper запущений, він вставляє підступень на рівень глибший за попередній крок.

Нарешті, then()функція завершується випуском step.addedподії та повертає об'єкт Casper.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.