1

2 つの RequireJS モジュールがあります。1 つは外部サービスからデータをフェッチするためのもので、もう 1 つはコールバックを最初のモジュールに渡すためのものです。

最初の非常に基本的なモジュールは次のとおりです。

define(["jquery"], function($) {

    return {
        /**
         * Retrieves all the companies that do not employs the provided employee
         * @param employeeId ID of the employee
         * @param successCallback callback executed on successful request completion
         * @return matching companies
         */
        fetchCompanies: function(employeeId, successCallback) {
            var url = '/employees/' + employeeId + '/nonEmployers';
            return $.getJSON(url, successCallback);
        }
    };
});

そして最も興味深いのは、新しいドロップダウンを生成し、それを指定された DOM 要素に挿入するものです (これはテスト対象の要素です)。

define([
    'jquery',
    'vendor/underscore',
    'modules/non-employers',
    'text!tpl/employeeOption.tpl'], function($, _, nonEmployers, employeeTemplate) {

    var updateCompanies = function(selectedEmployeeId, companyDropDownSelector) {
        nonEmployers.fetchCompanies(selectedEmployeeId, function(data) {
            var template = _.template(employeeTemplate),
                newContents = _.reduce(data, function(string,element) {
                    return string + template({
                        value: element.id,
                        display: element.name
                    });
                }, "<option value='-1'>select a client...</option>\n");
            $(companyDropDownSelector).html(newContents);
        });
    };

    return {
        /**
         * Updates the dropdown identified by companyDropDownSelector
         * with the companies that are non employing the selected employee
         * @param employeeDropDownSelector selector of the employee dropdown
         * @param companyDropDownSelector selector of the company dropdown
         */
        observeEmployees: function(employeeDropDownSelector, companyDropDownSelector) {
            $(employeeDropDownSelector).change(function() {
                var selectedEmployeeId = $(employeeDropDownSelector + " option:selected").val();
                if (selectedEmployeeId > 0) {
                    updateCompanies(selectedEmployeeId, companyDropDownSelector);
                }
            });
        }
    };
});

Jasmine-fixtures と waitsFor を使用して、この最後のモジュールをテストし、セットアップされたテスト DOM 構造が変更されたことを非同期的に確認しようとしています。ただし、タイムアウトには常に到達します。

次のテストで何が問題なのかを見つけることができれば、私は非常に感謝しています (要旨: https://gist.github.com/fbiville/6223bb346476ca88f55d ):

define(["jquery", "modules/non-employers", "modules/pages/activities"], function($, nonEmployers, activities) {
    describe("activities test suite", function() {
        var $form, $employeesDropDown, $companiesDropDown;

        beforeEach(function() {
            $form = affix('form[id=testForm]');
            $employeesDropDown = $form.affix('select[id=employees]');
            $employeesDropDown.affix('option[selected=selected]');
            $employeesDropDown.affix('option[value=1]');
            $companiesDropDown = $form.affix('select[id=companies]');
            $companiesDropDown.affix('option');
        });

        it("should update the company dropdown", function() {
            spyOn(nonEmployers, "fetchCompanies").andCallFake(function(employeeId, callback) {
                callback([{id: 42, name: "ACME"}, {id: 100, name: "OUI"}]);
            });

            activities.observeEmployees('#employees', '#companies');
            $('#employees').trigger('change');

            waitsFor(function() {
                var companiesContents = $('#companies').html(),
                    result = expect(companiesContents).toContain('<option value="42">ACME</option>');

                return result && expect(companiesContents).toContain('<option value="100">OUI</option>');
            }, 'DOM has never been updated', 10000);
        });
    });
});

前もって感謝します!

ロルフ

PS:呼び出し (および) を domReady で置換および/またはラップする$(employeeDropDownSelector).changeと、同じ結果が得られます。$(employeeDropDownSelector).on('change',activities.observeEmployees$('#employees').trigger('change');

PPS: このエラーが原因です -> SEVERE: runtimeError: message=[An invalid or illegal selector was specified (selector: '[id='employees'] :selected' error: Invalid selector: *[id="employees"] *:selected).] sourceName=[http://localhost:59811/src/vendor/require-jquery.js] line=[6002] lineSource=[null] lineOffset=[0].

PPPS: HtmlUnit は CSS3 セレクター (WTF?) をサポートしていないようで、jasmine-maven-plugin 依存関係として最新の公開バージョンを強制しても何も変わりません...

ジャスミンプラグインランナーを変更する方法はありますか?

4

2 に答える 2

1

この方法でモジュールを作成するのは非常に困難です。フィクスチャを使用せず、実際にはどこにもレンダリングしないことをお勧めします。代わりに、切り離された DOM 要素を使用してすべての作業を行う方がはるかに簡単です。

あなたのコードがこれに近いと想像してみてください:

define([
    'jquery',
    'vendor/underscore',
    'modules/non-employers',
    'text!tpl/employeeOption.tpl'], function($, _, nonEmployers, employeeTemplate) {

    return {
        init: function() {
            this.$companies = $('<select class="js-companies"></select>');
        },
        render: function(data) {
            var template = _.template(employeeTemplate),
                newContents = _.reduce(data, function(string,element) {
                    return string + template({
                        value: element.id,
                        display: element.name
                    });
                }, "<option value='-1'>select a client...</option>\n");
            this.$companies.empty().append(newContents);
            return this;
        });
        observeEmployees: function(employeeDropDownSelector) {
            $(employeeDropDownSelector).change(function() {
                var selectedEmployeeId = $(employeeDropDownSelector + " option:selected").val();
                if (selectedEmployeeId > 0) {
                    nonEmployers.fetchCompanies(selectedEmployeeId, function(data) {
                        this.render(data);
                    }
                }
            });
        }
    };
});

上記は完全ではありません。問題にアプローチするための別の方法のアイデアを提供するだけです。フィクスチャの代わりに、検査するだけです。this.$companiesそしてあなたは終わります。ただし、主な問題は、関数が十分に単純ではないことだと思います。各機能の関心は、非常に具体的でなければなりません。あなたの updateCompanies 関数は、テンプレートの作成、データのフェッチ、スパイできない匿名関数への渡しなどを行っています。その匿名関数はオブジェクトを反復処理し、既存の DOM 要素を変更します。それは疲れるね。関数が行うべきことは、オブジェクトを送信するプリコンパイル済みテンプレートを確認することだけです。テンプレートは、{{each}} を使用してオブジェクトをループしてから戻る必要があります。次に、関数は newContents を空にして追加し、それ自体を返すので、次の関数の下で this.$companies をどう処理するかを選択できます。または、this.$companies が既にページに追加されている場合は、何もする必要はありません。

于 2013-04-19T03:14:39.437 に答える
1

わかりました。

見つかった解決策:

  1. jasmine-maven-plugin v1.3.1.1 (またはそれ以降) に (まだアップグレードしていない場合) アップグレードします。
  2. このくだらないHtmlUnitの代わりにphantomjsを構成してください(プロジェクトにPhantomJSバイナリを追加してください)
  3. コードで ':focus' セレクターを使用している場合は、このバグに注意して、$(mySelector).get(0) == document.activeElement
  4. run(function() { /* expect */ })また、コードブロックが後に配置され、条件に依存する場合は、コードブロックをラップすることを忘れないでくださいwaitsFor

最後に、すべてがうまくいくはずです。

テストの様子をご覧ください:

define(["jquery",
    "modules/nonEmployers",
    "modules/pages/activities"], function($, nonEmployers, activities) {

    describe("activities test suite", function() {
        var $form, $employeesDropDown, $companiesDropDown;

        beforeEach(function() {
            $form = affix('form[id=testForm]');
            $employeesDropDown = $form.affix('select[id=employees]');
            $employeesDropDown.affix('option[selected=selected]');
            $employeesDropDown.affix('option[value=1]');
            $companiesDropDown = $form.affix('select[id=companies]');
            $companiesDropDown.affix('option');

            spyOn(nonEmployers, "fetchCompanies").andCallFake(function(employeeId, callback) {
                callback([{id: 42, name: "ACME"}, {id: 100, name: "OUI"}]);
            });
        });


        it("should update the company dropdown", function() {

            $(document).ready(function() {
                activities.observeEmployees('#employees', '#companies');
                $('#employees option[selected=selected]').removeAttr("selected");
                $('#employees option[value=1]').attr("selected", "selected");
                $('#employees').trigger('change');

                waitsFor(function() {
                    var dropDown = $('#companies').html();
                    return dropDown.indexOf('ACME') > 0 && dropDown.indexOf('OUI') > 0;
                }, 'DOM has never been updated', 500);

                runs(function() {
                    var dropDown = $('#companies').html();
                    expect(dropDown).toContain('<option value="42">ACME</option>');
                    expect(dropDown).toContain('<option value="100">OUI</option>');
                });
            });
        });
    });
});
于 2013-04-18T17:36:14.627 に答える