diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..7715e64 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "qunit"] + path = qunit + url = git://github.com/jquery/qunit.git diff --git a/collection.js b/collection.js new file mode 100644 index 0000000..fa814a3 --- /dev/null +++ b/collection.js @@ -0,0 +1,69 @@ +(function (exports) { + "use strict"; + +exports.Collection = function (items) { + + this.items = []; + var key; + + for (key in items) { + if (items.hasOwnProperty(key)) { + this.items.push(items[key]); + } + } +}; + +Collection.prototype.constructor = exports.Collection; + +/** + * Добавляет в коллекцию объект + * + * @param {object} model + * + * @return {Collection} * @example + * + */ +Collection.prototype.add = function (model) { + + var temp = new this.constructor(this.items); + temp.items.push(model); + return temp; +}; + +/** + * @param {Function} selector + * + * @see https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/filter + * + * @example + * new Collection().filter(function (item) { + * return item.get('attendee').indexOf("me") !== -1; + * }); + * @return {Collection} + */ +Collection.prototype.filter = function (selector) { + + if (typeof selector !== "function") { + throw new Error('Argument must be function'); + } + + return new this.constructor(this.items.filter(selector)); +}; + +/** + * Принимает функцию сортировки и сортирует на основе ее + * + * @param {function} selector + * + * @return {Collection} * @example + * + */ +Collection.prototype.sort = function (selector) { + + if (typeof selector !== "function") { + throw new Error('Argument must be function'); + } + + return new this.constructor(this.items.sort(selector)); +}; +}(window)); \ No newline at end of file diff --git a/event.js b/event.js new file mode 100644 index 0000000..dc67f0c --- /dev/null +++ b/event.js @@ -0,0 +1,81 @@ +(function (exports) { + "use strict"; + +function isDate(date) { + + if (typeof date === 'undefined') { + return false; + } + if (typeof date.getMonth !== 'function') { + return false; + } + return true; +} + +exports.inherits = function (constructor, superconstructor) { + + var Func = function () { }; + + Func.prototype = superconstructor.prototype; + constructor.prototype = new Func(); +} + +exports.Event = function (data) { + + Model.apply(this, arguments); +}; + +inherits(Event, Model); + +/** + * Валидирует объект event, либо undefined, если в объекте отсутвуют обязательные поля + * eventObject{ + * name - название события + * start - начало + * end - окончание + * location - место + * remindTime - за сколько минут до события напомнить + * description - описание + * } + + * @param {object} obj Объект + * @example + * Event({ + * name: "Пара по веб-технологиям", + * start: new Date("2012-10-20 10:00:00"), + * end: new Date("2012-10-20 12:50:00"), + * location: "5 этаж", + * remindTime: 10, + * description: "Взять бумагу и ручку, не брать бук!" + * }) + * + * @return {Object} + */ +Event.prototype.validate = function () { + + var remindTime = this.remindTime || 0; + this.raiting = this.raiting || 0; + + if (!isDate(this.get("start"))) { + throw new Error('Field "start" must be Date format'); + } + + if (!isDate(this.end)) { + this.end = this.start; + } + + if (this.end < this.start) { + this.end = this.start; + } + + return { + "name": this.name || "(Нет темы)", + "start": this.start, + "end": this.end, + "location": this.location || "", + "remindTime": remindTime, + "description": this.description || "(отсутствует)", + "raiting": this.raiting + }; +}; +}(window)); \ No newline at end of file diff --git a/events.js b/events.js new file mode 100644 index 0000000..fe1ed51 --- /dev/null +++ b/events.js @@ -0,0 +1,125 @@ +(function (exports) { + "use strict"; + +exports.Events = function (data) { + + Collection.apply(this, arguments); +}; + +inherits(Events, Collection); + +Events.prototype.constructor = exports.Events; + +/** + * Возвращает прошедшие события, из items отсортированной по дате начала + * + * @param {events} - коллекция объектов типа event + * @return {Collection} - коллекция объектов типа event +*/ +Events.prototype.past = function () { + + return this.filter(function (events) { + return events.start < new Date(); + }); +}; + +/** + * Возвращает предстоящие события, + * из items, отсортированной по дате начала + * + * @return {Collection} - коллекция объектов типа event +*/ +Events.prototype.coming = function () { + + return this.filter(function (events) { + return events.start > new Date(); + }); +}; + +/** + * Возвращает события, которые произойдут через опр период времени, + * из items, отсортированной по дате начала + * + * @param {number} days - период (в днях) времени + * + * @return коллекция объектов типа event +*/ +Events.prototype.comeThrough = function (days) { + + var now = new Date(); + now.setDate(now.getDate() + days); + + var result = this.coming() + .filter(function (events) { + return events.start < now; + }); + + return result; +}; + +Events.prototype.byEndTime = function () { + + return this.sort(function (a, b) { + return a.end - b.end; + }); +}; + +Events.prototype.byRaiting = function () { + + return this.sort(function (a, b) { + return a.raiting - b.raiting; + }); +}; + +Events.prototype.byStartTime = function () { + + return this.sort(function (a, b) { + return a.start - b.start; + }); +}; + +/** + * Возвращает события,из items отсортированной по дате начала по возр/убыв + * от старых обытий к новым / наоборот. + * По умолчанию сортирует в порядке возрастания + * + * @param {bool} isAscending - необязательный параметр - указывает порядок сортировки. + * при отсутсвии сортируется по возрастанию. + * + * @return {Collection} - Новый объект типа Collection +*/ +Events.prototype.sortByTime = function (isAscending) { + + isAscending = isAscending || false; + + if (isAscending) { + return this + .byStartTime(); + } + return this.byStartTime() + .reverse(); +}; + +/** + * Возвращает события, из items, отсортированной по рейтингу по убыв/возрастанию + * от событий с более высоким рейтингом к самому низко приоритетному / наоборот. + * По умолчанию сортирует в порядке убывания + * + * @param {bool} isAscending - необязательный параметр - указывает порядок сортировки. + * при отсутствии сортируется по убыванию. + * + * @return {COllection} - Новый объект типа Collection +*/ +Events.prototype.sortByRaiting = function (isAscending) { + + isAscending = isAscending || false; + + if (isAscending) { + return this + .byRaiting(); + } + return this + .byRaiting() + .reverse(); +}; +}(window)); \ No newline at end of file diff --git a/examples.js b/examples.js new file mode 100644 index 0000000..8aa58ae --- /dev/null +++ b/examples.js @@ -0,0 +1,23 @@ +var examples = [ + { name: "Пара по веб-технологиям", start: new Date("2012-10-20 10:00:00"), end: new Date("2012-10-20 12:50:00"), location: "5 этаж", remindTime: 10, description: "Взять бумагу и ручку, не брать бук!" }, + { name: "День зимы", start: new Date("2012-10-27 06:00:00"), end: new Date("2012-10-27 12:00:00"), location: "Скандинавия", description: "Кататься ^_^" }, + { name: "День инженера механика", start: new Date("2012-10-29 10:00:00"), end: new Date("2012-10-29 15:00:00"), location: "9 этаж", remindTime: 10 }, + { name: "День вегана", start: new Date("2012-11-1 10:00:00"), end: new Date("2012-10-1 23:00"), location: "Дома", description: "Be vegan =)" }, + { name: "День журналисты", start: new Date("2012-11-8 10:00:00"), location: "Китай", remindTime: 10, description: "Поздравить Олежу" }, + { name: "Всемирный день борьбы с диабетом", start: new Date("2012-11-14 12:00") }, + { name: "Международный день отказа от курения", start: new Date("2012-11-15 12:00"), description: "Поздравить Сашку)" }, + { name: "День защиты черных котов", start: new Date("2012-11-17 14:00:00"), location: "Италия", remindTime: 50, description: "Котэ" }, + { name: "Всемирный день туалетов", start: new Date("2012-11-19 15:00:00"), location: "МИр", description: "о_О" }, + { name: "День революции", start: new Date("2012-11-20 12:00:00"), location: "Мексика"}, + { name: "День сладостей", start: new Date("2012-10-20 15:00:00"), location: "США", remindTime: 10, description: "Приготовить вкусняшки" }, + { name: "Ерофеев день", start: new Date("2012-10-17 16:00:00"), location: "Россия", description: "Лисики" }, + { name: "Утиный фестиваль", start: new Date("2012-10-13 12:00:00"), location: "Италия", description: "Все в Италию!" }, + { name: "Дент ребенка", start: new Date("2012-10-12 14:00:00"), location: "Бразилия" }, + { name: "День физкультуры", start: new Date("2012-10-8 12:00:00"), location: "Япония"}, + { name: "Всемирный день животных", start: new Date("2012-10-4 12:00:00 ")}, + { name: "День сакэ в Японии", start: new Date("2012-10-1 14:00:00") }, + { name: "День моря", start: new Date("2012-09-27 15:00:00") }, + { name: "День комиксов", start: new Date("2012-09-25 15:00:00"), location: "США"}, + { name: "День почитания пожилых людей", start: new Date("2012-09-17 16:00:00")}, + { name: "Международный жень демократии", start: new Date("2012-09-15 17:00:00")} +]; \ No newline at end of file diff --git a/model.js b/model.js new file mode 100644 index 0000000..963a925 --- /dev/null +++ b/model.js @@ -0,0 +1,62 @@ +(function (exports) { + "use strict"; + +/** + * Абстрактный конструктор, принимает объект и создает абстрактный объект для работы + * + * @param {Object} attributes + * + * @example + * item.set({title: "March 20", content: "In his eyes she eclipses..."}); + */ +exports.Model = function (data) { + + var key; + + for (key in data) { + if (data.hasOwnProperty(key)) { + this[key] = data[key]; + } + } +}; + + +/** + * Сеттер - устанавливает аттрибуты и значения атрибутов, в соответсвии с принятым в качестве параметра объектом + * + * @param {Object} attributes + * + * @example + * item.set({title: "March 20", content: "In his eyes she eclipses..."}); + */ +Model.prototype.set = function (attributes) { + + var key; + + for (key in attributes) { + if (attributes.hasOwnProperty(key)) { + this[key] = attributes[key]; + } + } +}; + +/** + * Геттер - возвращает запрашиваемое свойство у объекта + * + * @param {Object} attributes + */ +Model.prototype.get = function (attribute) { + + if (this.hasOwnProperty(attribute)) { + return this[attribute]; + } + + return undefined; +}; +/** + * @param {Object} attributes + */ +Model.prototype.validate = function (attributes) { + throw new Error('this is Abstract method'); +}; +}(window)); \ No newline at end of file diff --git a/qunit b/qunit new file mode 160000 index 0000000..515de77 --- /dev/null +++ b/qunit @@ -0,0 +1 @@ +Subproject commit 515de778ca756e181cc56c23b0c2841ef4eda8fa diff --git a/tests.js b/tests.js new file mode 100644 index 0000000..07fcc1b --- /dev/null +++ b/tests.js @@ -0,0 +1,56 @@ +test("Testing qunit", function() { + "use strict"; + + ok("1" === "1", "Passed!"); +}); + +test("Creating absract object Model", function () { + "use strict"; + + var item = new Model({title: "March 20", content: "In his eyes she eclipses..."}); + ok(item.title === "March 20", "Passed!"); +}); + + +test("Testing setter for Model", function () { + "use strict"; + + var item = new Model(); + item.set({title: "March 20", content: "In his eyes she eclipses..."}); + ok(item.title === "March 20", "Passed!"); +}); + +test("Testing getter for Model", function () { + "use strict"; + + var item = new Model(); + item.set({title: "March 20", content: "In his eyes she eclipses..."}); + + ok(item.get("title") === "March 20", "Passed!"); +}); + +test("Create object Event using Model constructor", function () { + "use strict"; + + var item = new Event({title: "March 20", content: "In his eyes she eclipses..."}); + + ok(item.title === "March 20", "Passed!"); +}); + +test("Testing inherits Event from Model", function () { + "use strict"; + + var item = new Event({title: "March 20", content: "In his eyes she eclipses..."}); + + ok(item.get("title") === "March 20", "Passed!"); +}); + +test("Test for validate method", function () { + "use strict"; + + var item = new Event({name: "hello", start: new Date("2012-10-20 10:00:00")}); + var valide = item.validate(); + + ok(valide.name === "hello", "Passed!"); +}); + diff --git a/tests/EventsTest.html b/tests/EventsTest.html new file mode 100644 index 0000000..dbafb9e --- /dev/null +++ b/tests/EventsTest.html @@ -0,0 +1,18 @@ + + + + + Тестирование класса Events (Collection) + + + + + + + + + + +
+ + diff --git a/tests/EventsTest.js b/tests/EventsTest.js new file mode 100644 index 0000000..9549c6c --- /dev/null +++ b/tests/EventsTest.js @@ -0,0 +1,78 @@ +function getString(event) { + "use strict"; + + return "\n" + event.name + "\n" + " начало: " + event.start + "\n" + + " конец: " + event.end + "\n" + + " место события: " + event.location + " напомнить за " + event.remindTime + " минут" + "\n" + + " описание: " + event.description + "\n"; +} + +test("Testing inherits Events from Collection", function () { + "use strict"; + + var collection = new Events(examples), + element = {title: "March 20", content: "In his eyes she eclipses..."}, + result = collection.add(element); + + examples.push(element); + + deepEqual(result.items, examples, "Two objects can be the same in value"); +}); + +test("Testing past in Events", function () { + "use strict"; + + var collection = new Events(examples), + result = collection.past(); + + console.log("Result: " + result.items.map(function (event) { + return getString(event); + })); + + console.log("--------------\n"); + ok("1" === "1", "Two objects can be the same in value"); +}); + +test("Testing coming in Events", function () { + "use strict"; + + var collection = new Events(examples), + result = collection.coming(); + + console.log("Result: " + result.items.map(function(event) { + return getString(event); + })); + + console.log("--------------\n"); + ok("1" === "1", "Two objects can be the same in value"); +}); + +test("Testing comeThrough in Events", function () { + "use strict"; + + var collection = new Events(examples), + result = collection.comeThrough(10); + + console.log("Result: " + result.items.map(function (event) { + return getString(event); + })); + + console.log("--------------\n"); + ok("1" === "1", "Two objects can be the same in value"); +}); + +test("Testing sortBytime in Events", function () { + "use strict"; + + var collection = new Events(examples), + result = collection.byStartTime(); + + console.log("Result: " + result.items.map(function (event) { + return getString(event); + })); + + console.log("--------------\n"); + ok("1" === "1", "Two objects can be the same in value"); +}); + + diff --git a/tests/collectionTest.html b/tests/collectionTest.html new file mode 100644 index 0000000..1ae6ec6 --- /dev/null +++ b/tests/collectionTest.html @@ -0,0 +1,17 @@ + + + + + Тестирование класса Collection + + + + + + + + + +
+ + diff --git a/tests/collectionTest.js b/tests/collectionTest.js new file mode 100644 index 0000000..131b6c1 --- /dev/null +++ b/tests/collectionTest.js @@ -0,0 +1,49 @@ +test("Create object Collection", function () { + "use strict"; + + var result = new Collection(examples); + + ok(result.items[0].name == examples[0].name, "Passed!"); +}); + +test("Testing for creating new Collection in constructor", function () { + "use strict"; + + var result = new Collection(examples); + result.items.pop(); + + ok(result.items.length !== examples.length, "Passed!"); +}); + +test("Add func in Collection", function () { + "use strict"; + + var collection = new Collection(examples), + element = {title: "March 20", content: "In his eyes she eclipses..."}, + result = collection.add(element); + + examples.push(element); + + deepEqual(result.items, examples, "Two objects can be the same in value"); +}); + +test("Test Collection", function () { + "use strict"; + + var collection = new Collection(), + element = {title: "March 20", content: "In his eyes she eclipses..."}, + result = collection.add(element); + + deepEqual(result.items, [element], "Two objects can be the same in value"); +}); + +test("Filter func in Collection", function () { + "use strict"; + + var collection = new Collection(examples), + result = collection.filter(function (item) { + return item.name === "День зимы"; + }); + + ok(result.items.length !== examples.length, "Passed"); +}); diff --git a/tests/eventTest.html b/tests/eventTest.html new file mode 100644 index 0000000..d8284b1 --- /dev/null +++ b/tests/eventTest.html @@ -0,0 +1,16 @@ + + + + + Тестирование класса Event и наследования от класса Model + + + + + + + + +
+ + diff --git a/tests/eventTest.js b/tests/eventTest.js new file mode 100644 index 0000000..0787a89 --- /dev/null +++ b/tests/eventTest.js @@ -0,0 +1,24 @@ +test("Create object Event using Model constructor", function () { + "use strict"; + + var item = new Event({title: "March 20", content: "In his eyes she eclipses..."}); + + ok(item.title === "March 20", "Passed!"); +}); + +test("Testing inherits Event from Model", function () { + "use strict"; + + var item = new Event({title: "March 20", content: "In his eyes she eclipses..."}); + + ok(item.get("title") === "March 20", "Passed!"); +}); + +test("Test for validate method", function () { + "use strict"; + + var item = new Event({name: "hello", start: new Date("2012-10-20 10:00:00")}), + valide = item.validate(); + + ok(valide.name === "hello", "Passed!"); +}); diff --git a/tests/modelTest.html b/tests/modelTest.html new file mode 100644 index 0000000..978bab0 --- /dev/null +++ b/tests/modelTest.html @@ -0,0 +1,16 @@ + + + + + Тестирование класса Model + + + + + + + + +
+ + diff --git a/tests/modelTest.js b/tests/modelTest.js new file mode 100644 index 0000000..c322f9e --- /dev/null +++ b/tests/modelTest.js @@ -0,0 +1,30 @@ +test("Testing qunit", function () { + "use strict"; + + ok( "1" == "1", "Passed!"); +}); + +test("Creating absract object Model", function () { + "use strict"; + + var item = new Model({title: "March 20", content: "In his eyes she eclipses..."}); + ok(item.title === "March 20", "Passed!"); +}); + + +test("Testing setter for Model", function () { + "use strict"; + + var item = new Model(); + item.set({title: "March 20", content: "In his eyes she eclipses..."}); + ok(item.title === "March 20", "Passed!"); +}); + +test("Testing getter for Model", function () { + "use strict"; + + var item = new Model(); + item.set({title: "March 20", content: "In his eyes she eclipses..."}); + + ok(item.get("title") === "March 20", "Passed!"); +}); \ No newline at end of file diff --git a/tests/runner.js b/tests/runner.js new file mode 100644 index 0000000..cea3719 --- /dev/null +++ b/tests/runner.js @@ -0,0 +1,98 @@ +/* + * Qt+WebKit powered headless test runner using Phantomjs + * + * Phantomjs installation: http://code.google.com/p/phantomjs/wiki/BuildInstructions + * + * Run with: + * phantomjs runner.js [url-of-your-qunit-testsuite] + * + * E.g. + * phantomjs runner.js http://localhost/qunit/test + */ + +/*jshint latedef:false */ +/*global phantom:true require:true console:true */ +var url = phantom.args[0], + page = require('webpage').create(); + +// Route "console.log()" calls from within the Page context to the main Phantom context (i.e. current "this") +page.onConsoleMessage = function(msg) { + console.log(msg); +}; + +page.onInitialized = function() { + page.evaluate(addLogging); +}; +page.open(url, function(status){ + if (status !== "success") { + console.log("Unable to access network: " + status); + phantom.exit(1); + } else { + // page.evaluate(addLogging); + var interval = setInterval(function() { + if (finished()) { + clearInterval(interval); + onfinishedTests(); + } + }, 500); + } +}); + +function finished() { + return page.evaluate(function(){ + return !!window.qunitDone; + }); +} + +function onfinishedTests() { + var output = page.evaluate(function() { + return JSON.stringify(window.qunitDone); + }); + phantom.exit(JSON.parse(output).failed > 0 ? 1 : 0); +} + +function addLogging() { + window.document.addEventListener( "DOMContentLoaded", function() { + var current_test_assertions = []; + + QUnit.testDone(function(result) { + var i, + name = result.module + ': ' + result.name; + + if (result.failed) { + console.log('Assertion Failed: ' + name); + + for (i = 0; i < current_test_assertions.length; i++) { + console.log(' ' + current_test_assertions[i]); + } + } + + current_test_assertions = []; + }); + + QUnit.log(function(details) { + var response; + + if (details.result) { + return; + } + + response = details.message || ''; + + if (typeof details.expected !== 'undefined') { + if (response) { + response += ', '; + } + + response += 'expected: ' + details.expected + ', but was: ' + details.actual; + } + + current_test_assertions.push('Failed assertion: ' + response); + }); + + QUnit.done(function(result){ + console.log('Took ' + result.runtime + 'ms to run ' + result.total + ' tests. ' + result.passed + ' passed, ' + result.failed + ' failed.'); + window.qunitDone = result; + }); + }, false ); +}