読者です 読者をやめる 読者になる 読者になる

クロコめも2。

ただのめもー

Nightmareさんとこをざっくり訳

javascript

英語わかんねっす。

Nightmare

Nightmareはハイレベルな自動ページ表示ライブラリーです。

達成したいことは、ネストが深く分かり難いコールバック地獄なしに下のことを実現する

  • シンプルに書ける
  • 同期処理っぽく書ける

これは、APIを持っていないサイトのE2Eテスト用に設計されています。

Electronベースでできてます。PhantomJSに似てますが、もっと早くてモダンです。

Daydream

Daydreamは@stevenmiller888さんによって作られた、Nightmareを使う際のお助けツール
chromeのエクステンションで、ブラウザ操作でNightmare用スクリプトを生成するものです。

Examples

yahooをサーチしてみよう

var Nightmare = require('nightmare');
var vo = require('vo');

vo(function* () {
    var nightmare = Nightmare({ show: true });
    var link = yield nightmare
        .goto('http://yahoo.com')
        .type('input[title="Search"]', 'github nightmare')
        .click('.searchsubmit')
        .wait('.ac-21th')
        .evaluate(function () {
            return document.getElementsByClassName('ac-21th')[0].href;
        });
    yield nightmare.end();
    return link;
})(function (err, result) {
    if (err) return console.log(err);
    console.log(result);
});

以下のコマンドで実行できます

npm install nightmare vo
node --harmony yahoo.js

もしくは mochaのテストを実行してみましょう:

var Nightmare = require('nightmare');
var expect = require('chai').expect; // jshint ignore:line

describe('test yahoo search results', function() {
    it('should find the nightmare github link first', function*() {
        var nightmare = Nightmare()
        var link = yield nightmare
            .goto('http://yahoo.com')
            .type('input[title="Search"]', 'github nightmare')
            .click('.searchsubmit')
            .wait('.ac-21th')
            .evaluate(function () {
                return document.getElementsByClassName('ac-21th')[0].href;
            });
        expect(link).to.equal('https://github.com/segmentio/nightmare');
    });
});

全機能の例はこっち
https://github.com/segmentio/nightmare/blob/master/test/index.js
※この例はmocha-generatorsを使ってるから注意してね
動かすには

  1. npm install
  2. npm test

API

Nightmare(options)

Web参照できる新しいインスタンスを生成します。
指定できるオプションはこれ
https://github.com/atom/electron/blob/master/docs/api/browser-window.md#new-browserwindowoptions

加えてnightmareのオプションとして

waitTimeout

.wait()が指定の時間までにtrueを返さないとexceptionを投げます。

paths

Electronが使用するデフォルトシステムパス。
下のようにして上書き可能

var nightmare = Nightmare({
    paths: {
        userData: '/user/data'
    }
});

switches

Chromeで使えるコマンドラインスイッチ Electronでサポートしているのは下の通り
https://github.com/atom/electron/blob/master/docs/api/chrome-command-line-switches.md

electronPath

Electronのバイナリを指定。別バージョンのElectronでテストしたいときに使う。

.useragent(useragent)

Electronで使われるuseragentを指定する

.end()

キューを完了し、Electronのプロセスを切断、終了します。

ページ操作

.goto(url

指定したURLのページをロードします。

.back()

バックします。

.forward()

進みます

.refresh()

ページをリフレッシュします。

.click(selector)

指定したセレクタでクリックイベントを発生させます。

.mousedown(selector)

指定したセレクタでマウスダウンイベントを発生させます。

.type(selector[, text])

指定したセレクタに文字入力する。空文字やfalseを設定すると対象のセレクタの値がクリアされる

.check(selector)

指定したチェックボックスセレクタのトグルを切り替える

.select(selector, option)

指定したdropdownセレクタのオプションを変える

.scrollTo(top, left)

スクロールする

.inject(type, file)

指定したフィアルをページにロードする。ロードできるのはjsかcss

.evaluate(fn[, arg1, arg2,...])

指定したfnを指定した引数で実行する。引数はオプショナル。実行終了後にfnの戻り値が戻り値となる。
情報を抜き取るときに便利

var selector = 'h1';
var text = yield nightmare
    .evaluate(function (selector) {
        // now we're executing inside the browser scope.
        return document.querySelector(selector).innerText;
    }, selector); // <-- that's how you pass parameters from Node scope to browser scope

.wait(ms)

指定したミリ秒待つ

.wait(selector)

指定したセレクタが存在するまで待つ(.wait('#pay-button'

.wait(fn[, arg1, arg2,...])

指定したfnがtrueを返すまで待つ

ページから情報を抜き取る

.exists(selector)

指定したselectorが存在するかどうか

.visible(selector)

指定したselectorが表示状態か非表示状態か

.on(event, callback)

ページのイベントを関ししてコールバックを呼ぶ。
.goto()を呼ぶ前に読んでおかないといけない
サポートしているイベントはここ
http://electron.atom.io/docs/v0.30.0/api/browser-window/#class-webcontents

追加されているページイベント

.on('page', function(type="error", message, stack))
このイベントはページで何かしらjavascriptのエラーが起きたときに発火します。
しかし、挿入されたjavascriptコードでは発火しません。(例 .evaluate())そういうときはただの例外

ページのイベント

window.addEventListener('error'), alert(...),prompt(...) とかconfirm(...)を監視します

.on('page', function(type="alert", message))

Nightmareではデフォルトでポップアップのwindow.alertが無効です。
しかし監視できます。

.on('page', function(type="prompt", message, response))

Nightmareではデフォルトでポップアップのwindow.promptが無効です。 しかし監視できます。
もし、異なるものをハンドリングしたい場合は独自のプリロードスクリプトを用意する必要があります。???なんのこっちゃ

.on('page', function(type="confirm", message, response))

Nightmareではデフォルトでポップアップのwindow.promptが無効です。 しかし監視できます。
もし、異なるものをハンドリングしたい場合は独自のプリロードスクリプトを用意する必要があります。???なんのこっちゃ

.on('console', function(type [, arguments, ...]))

typeにはlog,warnもしくはerror、そしてargumantsはコンソールから渡されてくるものです。

.on('console', function(type, errorMessage, errorStack))

このイベントはconsole.logがページ内で使われているときに発火します。しかし、injectされたscriptでは発火しません(例 console.log()の中で.evalute()

.screenshot([path][, clip])

現在ページのスクリーンショットを取ります。デバッグに便利。
出力はpngです。引数はどっちともオプショナルです。
pathが指定された場合、画像を指定のディレクトリに保存します。指定されてないと画像データのバッファを返します。
clipが指定された場合、下にもあるようにイメージが矩形にクリップされます。
https://github.com/atom/electron/blob/master/docs/api/browser-window.md#wincapturepagerect-callback

.pdf(path, options)

指定されたパスにpdfファイルを保存します。細かいオプションについての情報は下
https://github.com/atom/electron/blob/v0.35.2/docs/api/web-contents.md#webcontentsprinttopdfoptions-callback 

.title()

現在ページのタイトルを返却します。

.url()

現在ページのurlを返却します。

クッキー

.cookies.get(name)

指定した名前のクッキーを取得します。対象のurlは現在ページのurlです。

.cookies.get(query)

クエリを使って複数のクッキーを取得します。もしquery.nameが指定された場合、最初に見つかった値、もしくは配列を返します。   query.urlが指定されていない場合は現在urlになります

例)

// get all google cookies that are secure
// and have the path `/query`
var cookies = yield nightmare
    .goto('http://google.com')
    .cookies.get({
        path: '/query',
        secure: true
})

   使えるプロパティは下
https://github.com/atom/electron/blob/master/docs/api/session.md#sescookiesgetdetails-callback

.cookies.get()

現在ページのurlをすべて取得します。

.cookies.set(name, value)

現在ページのクッキーをセットします。

.cookies.set(cookie)

クッキーをセットします。もしcookie.urlが指定されてない場合、現在ページにセットします。
例)

yield nightmare
    .goto('http://google.com')
    .cookies.set({
        name: 'token',
        value: 'some token',
        path: '/query',
        secure: true
})

使えるプロパティ一覧は下
https://github.com/atom/electron/blob/master/docs/api/session.md#sescookiessetdetails-callback

.cookies.set(cookies)

一度に複数cookieをセットします。cookiescookieの配列です。   詳しくは.cookies.set(cookie)

Nightmareの拡張

Nightmare.action(name, action|namespace)

下の例のようにカスタムアクションをNightmareのプロトタイプに追加できます。

例)

Nightmare.action('size', function (done) {
    this.evaluate_now(function() {
        var w = Math.max(document.documentElement.clientWidth, window.innerWidth || 0)
        var h = Math.max(document.documentElement.clientHeight, window.innerHeight || 0)
        return {
            height: h,
            width: w
        }
    }, done)
})

var size = yield Nightmare()
    .goto('http://cnn.com')
    .size()

注意:これはstatic classに追加してます。Nightmareのインスタンスに追加してるわけじゃないです。
evaluate_nowという処理を読んでいます。これはnightmare.evaluateとは違います。
これは即時に実行されますが、nightmare.evaluateはキューにたまります。

忘れないための簡単な方法は:evaluateについて疑問におもったとき、もしカスタムアクションを作ってるときならevaluate_nowを使ってください
なぜならすでにキューイングされたものを処理している最中に、その処理をまたキューにためてはいけないからです

またカスタムのネームスペースを作ることもできます。
nightmare.cookies.get,nightmare.cookies.setなどで内部でも使っているように
アクションをまとめるのに便利です。しかしnightmareオブジェクトが乱雑になります。

例)

Nightmare.action('style', {
    background: function (done) {
        this.evaluate_now(function () {
            return window.getComputedStyle(document.body, null).backgroundColor
        }, done)
    }
})

var background = yield Nightmare()
    .goto('http://google.com')
    .style.background()

.use(plugin)

nightmare.useは一つのインスタンスでタスクを再利用するのに便利です。
nightmare-swiftlyでサンプルをチェックしてください
https://github.com/segmentio/nightmare-swiftly

Custom preload script

ウィンドウ環境起動時になにかしたいときは、プリロードスクリプトを定義できます。
どうやるかっていうと

var nightmare = Nightmare({
    webPreferences: {
        preload: custom-script.js
    }
})

このプリロードするスクリプトは下の記述を入れておく

window.__nightmare = {};
__nightmare.ipc = require('ipc');

使い方

インストール

NightmareはNodeのmoduleなのでNode.jsは入れておく。
インストールはnpm install で行う

$npm install --save nightmare

実行

Nightmareはnodeのスクリプトやmoduleから利用できる。

例)

var Nightmare = require('../nightmare');
var vo = require('vo');

vo(run)(function(err, result) {
    if (err) throw err;
});

function *run() {
    var nightmare = Nightmare();
    var title = yield nightmare
        .goto('http://cnn.com')
        .evaluate(function() {
            return document.title;
        });
    console.log(title);
    yield nightmare.end();
}

これはcnn.jsとして保存したら下のように実行する

npm install vo nightmare
node --harmony cnn.js

デバッグ

内部で何が起きてるかをしるために3つの方法があります。 1. DEBUG=*デバッグフラグ 1. Nightmareコンストラクタ{show:true}を渡すと実際にウィンドウを表示する 1. specific eventsを監視する(下URL参照)

https://github.com/segmentio/nightmare#onevent-callback

同じテストをデバッグモードで動かすときはDEBUG=nightmare node --harmony cnn.js
windowsなら(set DEBUG=nightmare & node cnn.js

そうすると追加情報が出力されます。

テスト

MochaとChaiを使ってテストします。どっちともnpmでインストールします。
nightmareのテストはmake testで実行します

テストできたら下のように見えます。

make test
    ․․․․․․․․․․․․․․․․․․
    18 passing (1m)