Cosa significa veramente "Allora" in CasperJS


97

Sto usando CasperJS per automatizzare una serie di clic, moduli completati, analisi dei dati, ecc. Attraverso un sito web.

Casper sembra essere organizzato in un elenco di passaggi preimpostati sotto forma di thenistruzioni (vedere il loro esempio qui: http://casperjs.org/quickstart.html ) ma non è chiaro cosa fa scattare effettivamente l'esecuzione dell'istruzione successiva.

Ad esempio, thenattende il completamento di tutte le richieste in sospeso? Non injectJSconta come una richiesta in sospeso? Cosa succede se ho thenun'istruzione annidata, concatenata alla fine di openun'istruzione?

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

Sto cercando una spiegazione tecnica di come funziona il flusso in CasperJS. Il mio problema specifico è che la mia ultima thenaffermazione (sopra) viene eseguita prima della mia casper.opendichiarazione e non so perché.


1
Sto ancora cercando una spiegazione del generale flowdi casperjs, ma ho scoperto che fondamentalmente non puoi fare riferimento a casper dall'interno di una evaluatechiamata. (cioè non puoi aprire un nuovo URL, log, eco, ecc.). Quindi, nel mio caso, Value veniva chiamato ma senza alcun modo di interagire con il mondo esterno.
bendytree

1
Mi chiedevo esattamente le stesse cose, ma troppo pigro per chiedere. Buona domanda!
Nathan

4
evaluate()è per il codice che gira nel "browser", nel DOM della pagina che phantomjs sta navigando. Quindi non casper.openc'è, ma potrebbe esserci jQuery. Quindi il tuo esempio non ha senso, ma mi chiedo ancora cosa then()fa effettivamente.
Nathan

Risposte:


93

then()fondamentalmente aggiunge un nuovo passaggio di navigazione in uno stack. Un passaggio è una funzione javascript che può fare due cose diverse:

  1. in attesa che il passaggio precedente, se presente, venga eseguito
  2. in attesa del caricamento di un URL richiesto e della relativa pagina

Facciamo un semplice scenario di navigazione:

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

Puoi stampare tutti i passaggi creati all'interno dello stack in questo modo:

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

Ciò dà:

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

Notare la _step()funzione che è stata aggiunta automaticamente da CasperJS per caricare l'URL per noi; quando l'URL viene caricato, step3()viene chiamato il passaggio successivo disponibile nello stack, ovvero .

Dopo aver definito i passaggi di navigazione, run()li esegue uno per uno in sequenza:

casper.run();

Nota a piè di pagina: la funzione callback / listener è un'implementazione del modello Promise .


In casperjs 1.0.0-RC1, "test-steps.js" mostra una raccolta di [oggetto DOMWindow], invece di una raccolta di stringhe di definizione di funzione.
Starlocke

La raccolta [object DOMWindow] è ancora il risultato in 1.0.0-RC4; Mi chiedo dove siano finite quelle definizioni di funzione ...
starlocke

1
Inizialmente pensavo che CasperJS stesse facendo un nuovo trucco per convertire le funzioni in DOMWindows, ma il problema era davvero "return this.toString ()" vs "return step.toString ()" - Ho inviato una modifica per la risposta.
Starlocke

5
Il cosiddetto "stack" non è effettivamente una coda? I passaggi vengono eseguiti in ordine, se fosse stato uno stack non ci saremmo aspettati passaggio 3, passaggio 2, passaggio 1?
Reut Sharabani

1
Penso che debba essere così: hai una pila di passaggi. Fai un salto e valutalo. Crei una coda vuota. Tutti i passaggi generati a causa dell'elaborazione del passaggio corrente vengono inseriti in questa coda. Quando il passaggio ha terminato la valutazione, tutti i passaggi generati nella coda vengono messi in cima alla pila, ma preservando il loro ordine all'interno della coda. (Lo stesso che spingere sulla pila in ordine inverso).
Segna il

33

then() registra semplicemente una serie di passaggi.

run() e la sua famiglia di funzioni runner, callback e listener, sono tutti ciò che effettivamente fa il lavoro di esecuzione di ogni passaggio.

Ogni volta che un passaggio è completato, CasperJS controllerà contro 3 bandiere: pendingWait, loadInProgress, e navigationRequested. Se uno qualsiasi di questi flag è vero, non fare nulla, restare inattivo fino a un momento successivo ( setIntervalstile). Se nessuno di questi flag è vero, il passaggio successivo verrà eseguito.

A partire da CasperJS 1.0.0-RC4, esiste un difetto in cui, in determinate circostanze basate sul tempo, il metodo "prova a fare il passaggio successivo" verrà attivato prima che CasperJS abbia il tempo di sollevare uno dei flag loadInProgresso navigationRequested. La soluzione è alzare una di queste bandiere prima di lasciare qualsiasi passaggio in cui ci si aspetta che vengano alzate (es: alzare una bandiera prima o dopo aver chiesto uncasper.click() ), forse in questo modo:

(Nota: questo è solo illustrativo, più simile a psuedocode che al modulo CasperJS appropriato ...)

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

Per racchiudere quella soluzione in una singola riga di codice, ho introdotto blockStep()in questa richiesta pull github , estendendola click()e clickLabel()come mezzo per garantire che otteniamo il comportamento previsto durante l'utilizzo then(). Controlla la richiesta per ulteriori informazioni, modelli di utilizzo e file di test minimi.


1
molto utile e grande intuizione e suggerimento su blockStep, IMHO
Brian M. Hunt

Stiamo ancora discutendo la soluzione "risposta finale" ... Spero che una volta implementato l'aspetto "impostazioni predefinite globali", CasperJS farà il pull.
Starlocke

1
Quindi sì, tienilo d'occhio. :)
starlocke

Abbiamo qualche soluzione per questo? se si che cos'è?
Surender Singh Malik

Grazie mille per aver spiegato questo. Questo comportamento mi sta uccidendo da più di un anno, poiché i miei test funzionali Casper per un'applicazione pesante Ajax falliscono in modo casuale tutto il tempo.
brettjonesdev

0

Secondo la documentazione di CasperJS :

then()

Firma: then(Function then)

Questo metodo è il modo standard per aggiungere un nuovo passaggio di navigazione allo stack, fornendo una semplice funzione:

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

Puoi aggiungere tutti i passaggi di cui hai bisogno. Tieni presente che l' Casperistanza corrente associa automaticamente il filethis parola chiave all'interno delle funzioni passo.

Per eseguire tutti i passaggi definiti, chiama il file run() metodo e voilà.

Nota: è necessario start()l'istanza casper per utilizzare l'estensionethen() metodo.

Avvertimento: funzioni Step aggiunte then()vengono elaborate in due diversi casi:

  1. quando è stata eseguita la funzione passo precedente,
  2. quando la precedente richiesta HTTP principale è stata eseguita e la pagina caricata ;

Nota che non esiste una definizione unica di pagina caricata ; è quando è stato attivato l'evento DOMReady? "Tutte le richieste sono state completate"? È "tutta la logica dell'applicazione in esecuzione"? O "tutti gli elementi vengono renderizzati"? La risposta dipende sempre dal contesto. Ecco perché sei incoraggiato a usare sempre il filewaitFor() metodi familiari per mantenere un controllo esplicito su ciò che ti aspetti effettivamente.

Un trucco comune è usare waitForSelector():

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

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

casper.run();

Dietro le quinte, il codice sorgente diCasper.prototype.then è mostrato di seguito:

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

Spiegazione:

In altre parole, then() pianifica il passaggio successivo del processo di navigazione.

quando then() viene chiamato, viene passata una funzione come parametro che deve essere chiamato come passo.

Controlla se un'istanza è stata avviata e, in caso contrario, visualizza il seguente errore:

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

Successivamente, controlla se l' pageoggetto è null.

Se la condizione è vera, Casper crea un nuovo pageoggetto.

Successivamente, then()convalida il filestep parametro per verificare se non è una funzione.

Se il parametro non è una funzione, viene visualizzato il seguente errore:

CasperError: You can only define a step as a function

Quindi, la funzione controlla se Casper è in esecuzione.

Se Casper non è in esecuzione, then() aggiunge il passaggio alla fine della coda.

Altrimenti, se Casper è in esecuzione, inserisce un sottopasso di un livello più profondo rispetto al passaggio precedente.

Infine, la then()funzione si conclude emettendo un step.addedevento e restituisce l'oggetto Casper.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.