Что лучше (более проверяемым) способ структурировать эту страницу-конкретно в JS?


Я создал это (очень) пример, который демонстрирует, как сегодня мы организуем специальную страницу JS в наше приложение. В основном каждая страница имеет соответствующие JS файл, который использует модуль шаблон подложить странице конкретного поведения. Каждый модуль имеет метод init() функцию, которая принимает идентификаторов элементов управления и проводов мероприятий и т. д.

Это работает fairely хорошо для нас, однако теперь мы хотим добавить модульных тестов. Поскольку многие из наших модулей хранения в закрытых переменных наши модульные тесты заканчивают топать все над друг друга.

Я думаю, нам нужно прекратить использовать эти синглтон модули и использовать вместо экземпляров, так что мы можем лучше писать тесты. Каким образом мы можем перестроить этот тип кода, так что было бы проверяемым?

В JS скрипку здесь...

HTML-код

<div>
  <input type="file" /><br/>
  <input type="file" /><br/>
  <input type="file" />
</div>
<p>
  <br/>
  <input id="btnUpload" type="button" value="Upload Files" />
  or <a href="#" id="btnClear">Clear files</a>
</p>

<script>
    MyPage.init({
      clearLink: '#btnClear',
      uploadBtn: '#btnUpload',
      fileCtrls: "input[type='file']"
    });
</script>

Яш

MyPage = (function() {
    // private members
    var _files = [];
    var _initOptions;

    // public members
    return {
        init: function(options) {
            _initOptions = options;

            $(function() {
                // wire up the events
                $(options.clearLink).click(MyPage.clear);
                $(options.uploadBtn).click(MyPage.doUpload);
                $(options.fileCtrls).change(MyPage.addFile);
            });
        },

        addFile: function() {
            _files.push($(this).val());
        },

        doUpload: function() {
            alert('uploading ' + _files.length + ' files...');
        },

        clear: function() {
            _files = [];
            $(_initOptions.fileCtrls).val('');
        }
    }
})();


Комментарии
4 ответа

Вот мой подход к вашему сценарию. Еще несколько вещей; однако, это должно дать вам общее представление.


  1. Новый дом может быть введен в
    тестирование, так что мне не нужно полагаться
    на истинную форму, если я не
    хочу такой необходимости.

  2. Объект дом в целом может быть
    абстрагирован в собственное пространство имен это
    для повторного использования селектора на страницах.
    (Я еще играл с этой идеей).

  3. Подход добавляет
    объект/пространство имен глобальных
    область.

Тестирование в формате HTML

<!DOCTYPE html>
<html>
<head>
<script src="http://code.jquery.com/jquery-1.6.1.min.js"></script>
<script src="Script.js"></script>
<script>
var testDom = {
uploadButton: $("<input type='button'>")
};
$(function(){
var page = new MyPage.Behaviors(testDom);
page.init();

testDom.uploadButton.trigger("click");
});
</script>
</head>
<body>
</body>
</html>

Пример Скрипта

var MyPage = function($) {
var _files
, Dom = (function($){
return {
clearLink: $("#btnClear")
, uploadButton: $("#btnUpload")
, filesControl: $("input[type=file]")
};
}($))

, Behaviors = function(dom) {
if (typeof dom === 'undefined'){
dom = Dom;
}

function clear() {
_files = [];
dom.fileControl.val("");
}

function doUpload(){
alert('uploading ' + _files.length + ' files...');
}

function addFile(e){
_files.push(e.target.value);
}

function bindElement ($element, trigger, callback){
if (typeof $element !== 'undefined'){
$element.bind(trigger, callback);
}
}

function init() {
_files = [];
bindElement(dom.clearLink, "click", clear);
bindElement(dom.uploadButton, "click", doUpload);
bindElement(dom.fileControl, "click", addFile);
}

return {
clear: clear
, init: init
, doUpload: doUpload
, addFile: addFile
};
}

return {
Behaviors: Behaviors
};
}(jQuery);

На нагрузки, поведение инициализируется мой тест дом, а затем щелкните событие uploadButton запускается, чтобы проверить результат.

Это работает при тестировании в браузере, но не со скрипкой в настоящее время.

Я уверен, что есть лучший способ; однако, это позволит вам, чтобы проверить ваш код JavaScript в более контролируемый метод, а также удаляет зависимость от фактической разметки если вы используете что-то вроде, а именно jstestdriver.

1
ответ дан 28 июня 2011 в 04:06 Источник Поделиться

MyPage = (function() {
var Page = {
addFile: function(e) {
this._files.push($(e.target).val());
},

doUpload: function() {
alert('uploading ' + this._files.length + ' files...');
},

clear: function() {
this._files = [];
$(this.options.fileCtrls).val('');
}
}

// public members
return {
init: function(options) {
var o = Object.create(Page, {
options: { value: options },
_files: { value: [] }
});

$(function() {
// wire up the events
options.clearLink.click($.proxy(o.clear, o));
options.uploadBtn.click($.proxy(o.doUpload, o));
options.fileCtrls.change($.proxy(o.addFile, o));
});
},

}
})();

Используя Объект.создать

Живой Пример

В ES5-shim и для поддержки браузера.

А для тестирования использовать любую старую систему тестирования вам нравится. Использовать в jQuery.суб() имитация на jQuery

1
ответ дан 28 июня 2011 в 03:06 Источник Поделиться

Вы должны спроектировать приложение, чтобы быть проверяемой. Автоматизированное тестирование не означает "автоматической" проверки. Все что мы можем сделать с основы модульного тестирования (например, помощью QUnit) делает утверждения о том, что можно подняться на рамки.

Любой "метод" вы хотите модульного тестирования должны быть доступны за пределами его закрытия или объекта-прототипа; (т. е.) следует свойство объекта, а не переменная с областью действия в пределах своего определения функции. После вызова одного из этих методов в модульном тесте вы можете сравнить возвращаемое значение или состояние приложения против некоторым ожидаемым значением или условием. Снова состояние приложения вы хотите осмотреть должен быть представлен общественности области свойства тоже.

Оригинальный пример сценария в значительной степени опирается на события пользовательского интерфейса поведение. Вы могли бы вызвать функцию jQuery, чтобы повторить эти события. Вот переделанный вариант приложения с возможным методом с помощью QUnit комплекте тестов: (объявление XML и XHTML тип документа опущен редактором)

<html>
<head>
<title>Refactored App with QUnit Example</title>
<link href="http://code.jquery.com/qunit/git/qunit.css" media="screen" type="text/css" rel="stylesheet" />
</head>
<body>

<!-- original app markup -->
<div>
<input type="file" /><br/>
<input type="file" /><br/>
<input type="file" />
</div>
<p>
<br/>
<input id="btnUpload" type="button" value="Upload Files" />
or <a href="#" id="btnClear">Clear files</a>
</p>

<!-- QUnit boilerplate: -->
<h1 id="qunit-header">Example QUnit Test Suite</h1>
<h2 id="qunit-banner"></h2>
<div id="qunit-testrunner-toolbar"></div>
<h2 id="qunit-userAgent"></h2>
<ol id="qunit-tests"></ol>
<div id="qunit-fixture"></div>

<script type="text/javascript" src="http://code.jquery.com/jquery-latest.js"></script>
<script type="text/javascript" src="http://code.jquery.com/qunit/git/qunit.js"></script>
<script type="text/javascript">

// original app, refactored:

var App = (function (jquery) {

var $ = jquery,
app = this;

app.files = [];

app.initOptions = null;

app.addFile = function () {
app.files.push($(this).val());
};

app.doUpload = function () {
alert('uploading ' + app.files.length.toString() + ' files...');
};

app.clear = function () {
app.files = [];
app.initOptions.fileCtrls.val('');
};

app.init = function (options) {
app.initOptions = options;

// wire up the events
options.clearLink.click(app.clear);
options.uploadBtn.click(app.doUpload);
options.fileCtrls.change(app.addFile);
};

return app;

}($));

$(document).ready(function () {
var i;

// initialization call on the app:

App.init({
clearLink: $('#btnClear'),
uploadBtn: $('#btnUpload'),
fileCtrls: $("input[type='file']")
});

// QUnit test examples:

test("Set File Input", function () {
for(i = 0; i < App.initOptions.fileCtrls.length; i++) {
App.initOptions.fileCtrls[i].value = 'C:/fakepath/document--add.png';
$(App.initOptions.fileCtrls[i]).trigger('change');
}
equals(App.files.length, i, 'files array has ' + i.toString() + ' files');
});

test("Clear Files", function () {
$('#btnClear').trigger('click');
ok((App.files.length == 0), 'files array is empty');
for(i = 0; i < App.initOptions.fileCtrls.length; i++)
{
equals(App.initOptions.fileCtrls[i].value, '', 'file input ' + i.toString() + " value is empty");
}
});
});
</script>
</body>

0
ответ дан 29 июня 2011 в 04:06 Источник Поделиться

//оригинальный код:

/*
функция дисп файла mypage = ($) {
ВАР _FILES больше
, Дом = (функция($){
возвращение {
clearLink: $("#btnClear")
, uploadButton: $("#btnUpload")
, filesControl: $("вход[тип=файл]")
};
}($))
*/
Эта часть может быть очень грязно, если вы не обезьянничает в дом как то.. может вызвать серьезные проблемы в вашем браузере

        //something like this will do the trick
var the_Form= $('form:eq(0)'); //find our form

var MyPage= jQuery(function($){
var _files,
Dom= (function($){
return{
//rediscover the elements, I would have done it with a class and checked on hasClass('class') for better testing from the frontend part
clearLink: the_Form.find('a#btnClear'),
uploadButton: the_Form.find('input[type="submit"]#btnUpload'),
filesControl: the_Form.find("input[type='file']")

... продолжить с вашей программой.. Я считаю, что вы должны иметь способ, чтобы уничтожить элемент, если вы не перезагрузите страницу, или хотите сделать некоторые реального времени взаимодействие с AJAX, возможно, для загрузки файлов.. как там может быть некоторые из тех объектов, существующих впоследствии..

0
ответ дан 25 ноября 2012 в 08:11 Источник Поделиться

Другие вопросы по теме
кнопки пользовательского интерфейса jQuery и некоторые расчеты
28 июня 2011 в 11:06
Делая ул списка элементов анимации при наведении
27 июня 2011 в 07:06
Манипуляции со строками в JavaScript с определенная логика в этом
29 июня 2011 в 09:06
В JavaScript динамически