2012年04月16日

jQueryMobile+iPhone で QUnit を使う。

jQuery Monile 1.1.0 に対応した JQMDP-1.0rc2 を googlecode に置きました。
本題。
JavaScriptのテストコードってどうすれば良いかと調べると QUnit って言うのが見付かった。
使い方はほぼ JUnit と同じ。
特殊なのは JavaScript はスレッドが使えないので非同期処理をするための 仕掛けが用意されている事くらい。

参考サイトは非常に良くまとめてくれるいるので先に参照して欲しい。

で、現実的なテストの場合、アプリとテストコードは分離したいので 別Window(又は iframe)でテストアプリを起動してやる必要がある。

参考サイトにはこの辺の解説が無かったので自分で調べてみた。

本家サイトのデモを見ると基本系はこんな感じだが

module("Module name"); test("test1", function() { equal( true, true, "passing test" ); }); test("test2", function() { equal( true, false, "failing test" ); });

module()関数の第2引数にオプションを指定すると開始と終了をフックできる。

module("Module name", { setup: function(){ 開始処理 }, teardown: function{ 終了処理 } }); test("test1", function() { // ここで this は module()第2引数になる。 });

なのでおおざっぱに考えると

  • setup() で window.open("アプリURL");
    • onload でテスト起動
  • teardown() で window.close();
してやれば良さげ。

具体的にはこうなった。

var Sandbox = function(url){ this.url = url; this.win = null; this.onload = []; this.autoClose = true; } Sandbox.prototype = { setup: function() { var self = this; this.win = window.open(this.url, "_blank"); this._onload = function(){ var $handle = $(self.win.document); for (var i=0; i<self.onload.length; i++) { self.onload[i]($handle, self); } }; function checker() { if (self.win.document.readyState == "complete") { self._onload(); } else { setTimeout(checker, 50); } } setTimeout(checker, 100); }, teardown: function() { if (this.autoClose) this.win.close() }, load: function(callback){ this.onload.push(callback); } }; module("module name", new Sandbox("アプリURL")); test("test1", function() { stop(); this.load(function($sandbox){ // Sandbox.load() start(); same($sandbox.find("#id").val(), "正解", "項目詳細"); }); });

IEは別 Window の onload が取れないのでしかたなくポーリングにした。 IEいらないなら this.win.onload = function(){...} で良いはず。

Sandbox.load()で設定したコールバックが別Windowの onload で呼ばれるので、 引数の $sandbox (別Windowの$(document))から値を引っ張り出してチェックすれば良い。

PC の Chrome, IE, Safari で動作が確認できた。
が、残念ながら iPhone では動かなかったので iframe に変更してみた。

$(function(){ $(document.body).append($("<iframe id='sandbox'></iframe>")); }) var Sandbox = function(url){ this.url = url; this.win = null; this.onload = []; this.autoClose = true; } Sandbox.prototype = { setup: function() { var self = this; var ifr = document.getElementById("sandbox"); this.win = ifr.contentWindow; function onload(){ var $handle = $(self.win.document); for (var i=0; i<self.onload.length; i++) { self.onload[i]($handle, self); } }; ifr.onload = onload; // for IE ifr.onreadystatechange = function(){ if (this.readyState == "complete") { onload(); } }; ifr.src = this.url; }, teardown: function() { }, load: function(callback){ this.onload.push(callback); } };

iframe だとちゃんと iPhone でも QUnit が動いた。(iOS5、他未確認)

デバックは別Windowの方がやり易いので切替えられるようにしたいかな。

あと、普通のWebアプリのテストにも使えそうなので環境が面倒な Selenium の代わりになりそうに思います。
 

Posted by kotemaru at 13:38  |Comments(0)TrackBack(0) | JavaScript , jQueryMobile

2012年04月15日

jQuery Mobile 1.1.0 Final がリリースされてた

昨日、jQuery Monile 1.1.0 の正式版がリリースされていたようです。

rc2 で試してましたが iPhone でもスクロール時にフッターが
ガクガクしなくなったのでそれだけでも乗り換えの価値有りです。

JQMDP の移行では内部APIを使っていた所以外は変更無かったので
通常は差し替えだけでいけるんじゃないかな。

 
Posted by kotemaru at 09:00  |Comments(0)TrackBack(0) | JavaScript , jQueryMobile

2011年12月11日

JQMDPのIE8対応したんでメモ。

JQMDPをIE8対応して rc1 で公開した。
  • http://code.google.com/p/jqmdp/downloads/list
  • IE8 で必要になった修正点をメモ。

    • === undefined が効かない
      • == null か typeof === "undefined" に変更。
    • $.attr(key,val) が jQuery 内でエラー
      • $.attr({key:val}) に変更。
      • これは jQuery のバグっぽい気がするので将来的には大丈夫かも。
    • Array.indexOf が無い
      • 毎回忘れるw
      • エラーメッセージに indexOf って単語が現れないから気が付くのに時間がかかる注意。
    • td で text-align: center; が効かない
      • margin: auto; に変更
    • window変数がクロージャ内で見えない
      • 引数で渡すなりする。
    • jQuery でエラー
      • jquery-1.6.4.js: 4581行でエラー。
      • 原因はDOMのフラグメントが渡った時にトップノードに getAttribute() が無いから
      • jquery-1.6.4.jsを変更するならこれで解決。
        4585:   (elem.getAttribute ? elem.getAttribute( name ) : null),
      • jquery-1.6.4.jsを変更できないならこれ
        if ($.browser.msie) {
        	var org_ATTR = $.expr.filter.ATTR;
        	$.expr.filter.ATTR = function(elem, match) {
        		try {
        			return org_ATTR(elem, match);
        		} catch (e) {
        			console.error(e);
        		}
        		return false;
        	}
        }

    window変数がクロージャ内で見えない件は IE9 でも必要だったが、 それ以外は IE9 では修正不要。
    IE がほぼ普通のブラウザになったのは喜ばしい事だが XP 非対応なので普及には時間がかかりそう。
    当面は IE8 には対応は必須だ orz
     

    Posted by kotemaru at 11:47  |Comments(0)TrackBack(0) | JQMDP , JavaScript , jQueryMobile

    2011年12月05日

    JQMDP 1.0b2 公開します。

    jQuery Mobileのプラグインを作ってみた。」 で作った物を仕様をちゃんとして作り直してみました。 Chrome,Firefox,Safari は動きますが IE が動きません。

    いつもの 「オブジェクトでサポートされていないプロパティまたはメソッドです」 攻撃で意味不明です。(T_T)

    なんでそのプロパティ名をメッセージに含めるというだけの改修が IE になされないのか、本当に疑問です。
     

    Posted by kotemaru at 13:53  |Comments(0)TrackBack(0) | jQueryMobile , JavaScript

    2011年11月22日

    jQuery Mobileのプラグインを作ってみた。

    GAEの値上がりでテンションだだ下がりなので ちょっと気分を変えて jQuery Mobile のノウハウを整理してプラグイン化してみた。 ちょうど 1.0正式版がリリースされたし。

    P.S. 「 JQMDP 1.0b2 公開します。」で正式版公開しました。

    JQM でスマホのネイティブアプリっぽい事をやろうとすると 動的ページをJavaScriptでごりごり書かないといけないのだが JQM はあまりその辺を助けてくれる機能が無い。

    そんなに複雑な事でなくテンプレ内のテキスト、画像、リンクの 差し替え位の事がしたいだけなのだが結構めんどくさい。

    と言うわけでざっくり プラグインの機能をまとめると

    • テキスト、画像、リンクとかの差し替えがしたい。
    • 配列なんかを勝手にリストに変換してほしい。
    • 部品化したテンプレをあちこちで参照したい。
    • id="〜"なんか衝突しちゃうから使いたくない。
    てな感じだ。 4番目は少し規模が大きくなると結構大事だ。

    詳細な仕様/デモ/Downloadはこちら。

    とりあえず実装方法としては data-dp-〜 と言う拡張属性を定義して そこに小さいJavaScriptを書くといい感じに処理してくれるって事にした。

    具体的にはこうだ

    <div>Date: <span data-dp-text="(new Date())"></span></div><div>Date: <span>Mon Nov 21 2011 22:25:20 GMT+0900 (JST)</span></div> になる。 いい感じじゃね?

    リストはこうする

    <div data-dp-scope="({list:window.location, key:0})"> <ul data-dp-for="(key in list)" data-role="listview" > <span data-dp-text="(key)"></span> = <span data-dp-text="(list[key])"></span> </ul> </div>
    • 見たまま、HTMLの中に JavaScript を書いている感じ。
    • data-dp-scope はこのタグの中だけで使えるローカル変数を定義している。
    • ループ引数の key を data-dp-scope で定義させるのがちょっとダサイが現状やむ無し。
    <div> <ul data-role="listview" > <li><span>hostname</span> = <span>localhost</span><li> <li><span>port</span> = <span>8080</span><li> <li><span>hash</span> = <span>#attrs2</span><li> : </ul> </div> になる。

    部品参照はこう、(☆評価サンプル)

    <script src="js/parts/rating/Rating.js"></script> <div data-dp-scope="(new Rating($this))"></div> <div data-dp-scope="(new Rating($this))"></div>
    • Rating()のコンストラクタが実行されると Rating.html の内容が埋め込まれるようになっている。
    • 2つ有るが其々が値を保持できる。

    実行結果(これは画像、動くのはここ):

    部品コード定義:(js/parts/rating/Rating.jsに配置) function Rating(){this.initialize.apply(this, arguments)}; (function(Class){ var This = Class.prototype; // リソースの相対パスを絶対パスに変換している。 var TEMPL = $.jqmdp.absPath("Rating.html"); var IMG_ON = $.jqmdp.absPath("star-1.0.png"); var IMG_OFF = $.jqmdp.absPath("star-0.0.png"); // コンストラクタ This.initialize = function($this) { this.$this = $this; this.value = 0; // Rating.htmlをテンプレート適用。 $.jqmdp.exTemplate($this, TEMPL); } // 値を変更して再描画 This.val = function(v){ this.value = v; $.jqmdp.refresh(this.$this); } // on/off の☆の画像を帰す This.star = function(v){ return (this.value>=v) ? IMG_ON : IMG_OFF; } })(Rating);

    部品テンプレート定義:(js/parts/rating/Rating.htmlに配置)

    <div> <img data-dp-src="(star(1))" onclick="$(this).jqmdp('scope').val(1)" /> <img data-dp-src="(star(2))" onclick="$(this).jqmdp('scope').val(2)" /> <img data-dp-src="(star(3))" onclick="$(this).jqmdp('scope').val(3)" /> <img data-dp-src="(star(4))" onclick="$(this).jqmdp('scope').val(4)" /> <img data-dp-src="(star(5))" onclick="$(this).jqmdp('scope').val(5)" /> </div>
    • $(this).jqmdp('scope') は参照元の data-dp-scope="(new Rating($this))" の値を帰す。 したがって、onclick は Rating.val() が呼ばれる。
    • data-dp-src="(star(n))" は参照元の data-dp-scope="(new Rating($this))" のスコープで評価されるので省略しても Rating.star() が呼ばれる。
    • data-dp-scope を使う事で id の使用を回避できている。

    と言うような感じで動いている。

    簡単にやっているように書いたが実は結構大変だった。 JQMは挙動が良く分からない所が有ったりするし HTML上にローカルスコープが有るように見せかけるのは結構トリッキーな実装になっている。

    それと属性名が長い。 HTML5の規約で data- が必要なんだけど衝突を考慮するとさらに プレフィックスを付けて data-dp- にしなきゃいけない。 実は、dp- だけで試して見たのだが普通に動いた (^^;。 実装依存になりそうだったので諦めたが。

    尚、Chrome,Firefox,iPhone でしか動作確認していない :-P

    参考サイト:


     
    Posted by kotemaru at 11:21  |Comments(0)TrackBack(0) | jQueryMobile , JavaScript

    2011年08月31日

    jQTouchとjQueryMobile

    最近、スマホ向けWebアプリを作っている。

    スマホ向けWebアプリのフレームワークとして jQTouchとjQueryMobile が有るのだがどちらを使うか悩ましいところ。

    両方、使ってみたので所感をまとめてみる。

    * jQTouch

    • 良い所
      • 安定している。
      • jQueryMobileよりは軽い
    • 悪い所
      • 開発が止まっている。(*1)
      • 機能が足りない。
      • ドキュメントが無い。
      • Android/2.1でこけた。
    (*1)jQTouch は sencha.com の SenchaTouch に吸収されているようで 基本的にやる気が無いらしい。 SenchaTouch は商用/GPL3ライセンスの選択。

    * jQueryMobile

    • 良い所
      • jQuery の正式モジュール
      • 機能は大体揃ってる。
      • 対応機種が多い。
      • 日本語のドキュメントが有る。
    • 悪い所
      • まだβ版でバグも多い。
      • 重い。

    結局、jQueryMobile を選んだ。
    ロードマップが不明なのが不安なのだが jQTouch には将来性はまったく無さそうだし、 使うなら SenchaTouch を GPL3 で使うほうがよさげ。

    参考URL:

    関連記事:
     
    Posted by kotemaru at 18:18  |Comments(0)TrackBack(0) | JavaScript , android , jQueryMobile