From ed687dcc396469be0368210f67add5174fa07955 Mon Sep 17 00:00:00 2001 From: "Jose R. Borja" Date: Fri, 11 Apr 2014 11:48:48 -0500 Subject: [PATCH] Finish implementing the myQuery library - all tests pass --- SpecRunner.html | 1 + spec/myquery-spec.js | 32 ++++++--- spec/myqueryfull-spec.js | 150 +++++++++++++++++++++++++++++++++++++++ spec/spec-helper.js | 30 ++++++++ src/myquery.js | 89 ++++++++++++++++++++++- 5 files changed, 291 insertions(+), 11 deletions(-) create mode 100644 spec/myqueryfull-spec.js diff --git a/SpecRunner.html b/SpecRunner.html index f8b2f34..9c6c948 100755 --- a/SpecRunner.html +++ b/SpecRunner.html @@ -21,6 +21,7 @@ + diff --git a/spec/myquery-spec.js b/spec/myquery-spec.js index 40d6c5d..84a8691 100755 --- a/spec/myquery-spec.js +++ b/spec/myquery-spec.js @@ -22,7 +22,7 @@ describe("myQuery", function () { }); describe("General each function", function () { - xit("iterates through an array", function () { + it("iterates through an array", function () { var testResult = []; var someArray = [10, 20, 30]; $.each(someArray, function (number) { @@ -38,31 +38,31 @@ describe("myQuery", function () { describe("Selectors", function () { - xit("selects an element by id", function() { + it("selects an element by id", function() { var elem = $('#profile').get(0); expect(elem.className).toEqual('noice'); }); - xit("selects elements by class name", function() { + it("selects elements by class name", function() { var buttons = $('.button'); expect(buttons.get(0).className).toMatch(/first/); expect(buttons.get(1).className).toMatch(/second/); }); - xit("selects elements by tag name", function() { + it("selects elements by tag name", function() { var anchors = $('a'); - expect(anchors.length).toEqual(2) + expect(anchors.length).toEqual(2); expect(anchors.get(0).className).toEqual("button second"); expect(anchors.get(1).className).toEqual("straggler"); var images = $('img'); - expect(images.length).toEqual(1) + expect(images.length).toEqual(1); expect(images.get(0).className).toEqual("avatar"); }); }); describe("Selected elements each function", function () { - xit("iterates through all selected elements", function() { + it("iterates through all selected elements", function() { var testResult = []; $('.button').each(function (elem, i) { testResult.push(elem.className + ' ' + i); @@ -76,16 +76,26 @@ describe("myQuery", function () { describe("Show and Hide", function () { // TODO: Write tests for .show() and .hide() + it("hides or shows a selected element", function() { + $('.button').hide(); + expect( $('.button').get(0).style.display ).toEqual('none'); + $('.button').show(); + expect( $('.button').get(0).style.display ).toEqual('block'); + }); }); describe("addClass", function () { // TODO: Write tests for addClass // HINT: Test using .toMatch() like the selector test + it("adds a class to a selected element", function() { + $('.button').addClass('active'); + expect( $('.button').get(0).className ).toMatch(/active/); + }); }); describe("Modifying CSS", function () { - xit("can set a single property", function() { + it("can set a single property", function() { // Ensure they're not already hidden expect( $('.button').get(0).style.display ).toEqual(''); expect( $('.button').get(1).style.display ).toEqual(''); @@ -97,7 +107,11 @@ describe("myQuery", function () { }); // TODO: (`it` without a function are pending tests) - it("can set multiple properties in one call"); + it("can set multiple properties in one call", function() { + $('.button').css({'color': 'red', 'display': 'none'}); + expect( $('.button').get(0).style.color ).toEqual('red'); + expect( $('.button').get(0).style.display ).toEqual('none'); + }); }); }); diff --git a/spec/myqueryfull-spec.js b/spec/myqueryfull-spec.js new file mode 100644 index 0000000..e177a0b --- /dev/null +++ b/spec/myqueryfull-spec.js @@ -0,0 +1,150 @@ +describe("myQuery", function () { + + beforeEach(function () { + // `setFixtures` comes from the jasmine-jquery plugin. + // Although *you* are not using jQuery, we use this plugin to + // help us create HTML elements for testing. + // + // Key point: The HTML elements we create here are available + // for our tests to select. They also get destroyed after each test. + setFixtures( + '
' + + '
' + + '' + + '' + + '' + + '
' + ); + }); + + it("has a version of value 'beta'", function() { + expect($.version).toEqual('beta'); + }); + + describe("General each function", function () { + it("iterates through an array", function () { + var testResult = []; + var someArray = [10, 20, 30]; + $.each(someArray, function (number) { + testResult.push(number * number); + }); + + expect(testResult.length).toEqual(3); + expect(testResult[0]).toEqual(100); + expect(testResult[1]).toEqual(400); + expect(testResult[2]).toEqual(900); + }); + }); + + describe("Selectors", function () { + + it("selects an element by id", function() { + var elem = $('#profile').get(0); + expect(elem.className).toEqual('noice'); + }); + + it("selects elements by class name", function() { + var buttons = $('.button'); + expect(buttons.get(0).className).toMatch(/first/); + expect(buttons.get(1).className).toMatch(/second/); + }); + + it("selects elements by tag name", function() { + var anchors = $('a'); + expect(anchors.length).toEqual(2) + expect(anchors.get(0).className).toEqual("button second"); + expect(anchors.get(1).className).toEqual("straggler"); + + var images = $('img'); + expect(images.length).toEqual(1) + expect(images.get(0).className).toEqual("avatar"); + }); + }); + + describe("Selected elements each function", function () { + it("iterates through all selected elements", function() { + var testResult = []; + $('.button').each(function (elem, i) { + testResult.push(elem.className + ' ' + i); + }); + + expect(testResult.length).toEqual(2); + expect(testResult[0]).toEqual("button first 0"); + expect(testResult[1]).toEqual("button second 1"); + }); + }) + + describe("Show and Hide", function () { + // TODO: Write tests for .show() and .hide() + it("hides an element", function() { + var button = $('.button').hide(); + expect($('.button').get(0).style.display).toEqual("none"); + expect($('.button').get(1).style.display).toEqual("none"); + }); + + it("shows an element", function() { + $('.button').hide(); + var button = $('.button').show(); + expect($('.button').get(0).style.display).toEqual("block"); + expect($('.button').get(1).style.display).toEqual("block"); + }); + }); + + describe("addClass", function () { + // TODO: Write tests for addClass + // HINT: Test using .toMatch() like the selector test + it("adds a class to an element", function() { + $('.button').addClass('fuzzles'); + expect($('.button').get(0).className).toMatch(/fuzzles/); + }); + + it("does not allow two classes of the same name on an element", function () { + $('.button').addClass('pancake'); + $('.button').addClass('pancake'); + expect($('.button').get(0).className).not.toMatch(/pancake pancake/); + }) + }); + + describe("Modifying CSS", function () { + + it("can set a single property", function() { + // Ensure they're not already hidden + expect( $('.button').get(0).style.display ).toEqual(''); + expect( $('.button').get(1).style.display ).toEqual(''); + + // Now make sure displays have updated + $('.button').css('display', 'none'); + expect( $('.button').get(0).style.display ).toEqual('none'); + expect( $('.button').get(1).style.display ).toEqual('none'); + }); + + it("can set multiple properties in one call", function() { + $('.button').css({"border": "1px solid red", "height": "100px"}); + + expect( $('.button').get(0).style.border ).toEqual("1px solid red"); + expect( $('.button').get(0).style.height ).toEqual("100px"); + + expect( $('.button').get(1).style.border ).toEqual("1px solid red"); + expect( $('.button').get(1).style.height ).toEqual("100px"); + }); + }); + + describe("Chaining", function () { + it("can chain multiple calls", function() { + $('.button').show().hide().show().hide(); + expect($('.button').get(0).style.display).toEqual("none"); + expect($('.button').get(1).style.display).toEqual("none"); + }); + }); + + describe("Software Design Requirements", function () { + it("does not use jQuery", function() { + expect('' + $).not.toMatch(/jQuery/); + }); + + it("does not use querySelector or querySelectorAll", function() { + expect('' + $).not.toMatch(/querySelector(All)?/); + }); + }); + +}); \ No newline at end of file diff --git a/spec/spec-helper.js b/spec/spec-helper.js index 38a8220..b4319f1 100755 --- a/spec/spec-helper.js +++ b/spec/spec-helper.js @@ -1 +1,31 @@ // Any test helpers go here + + +// /** +// * Hide element(s) from DOM +// * @returns {*} +// */ +// hide: function () { +// var len = this.length; +// // Here we simply loop through our object (this) and set the css to display none. +// //If you got more that 1 node from DOM selected with querySelectorAll, you would hide them all. +// while (len--) { +// this[len].style.display = 'none'; +// } + +// // It's important to return this if you want to chain methods! +// return this; +// }, + +// /** +// * Show element(s) from DOM +// * @returns {*} +// */ +// show: function () { +// var len = this.length; +// while (len--) { +// this[len].style.display = 'block'; +// } + +// return this; +// } \ No newline at end of file diff --git a/src/myquery.js b/src/myquery.js index 047fdff..dc1a9bb 100755 --- a/src/myquery.js +++ b/src/myquery.js @@ -2,12 +2,97 @@ var QueryWrapper = function (elems) { // TODO + this.get = function(index) { + return elems[index]; + }; + this.length = elems.length; + this.each = function(func) { + for(var i = 0; i < elems.length; i++) { + func(elems[i], i); + } + }; + this.hide = function() { + this.each(function(elems) { + elems.style.display = "none"; + }); + return this; + }; + this.show = function() { + this.each(function(elems) { + elems.style.display = "block"; + }); + return this; + }; + this.addClass = function(className) { + this.each(function(elems) { + elems.className += className; + }); + return this; + }; + this.css = function(object, property) { + + // if (arguments.length === 2) { + if (typeof object == 'string' || object instanceof String) { + this.each(function(elems) { + elems.style[object] = property; + }); + } else { + this.each(function(elems) { + for (var prop in object) { + elems.style[prop] = object[prop] + } + }); + }; + return this; + }; }; var myQuery = function (selector) { - // TODO + if (selector[0] == '#') { + selector = selector.slice(1) + elements = [document.getElementById(selector)] + } + else if (selector[0] == '.') { + class_selector = selector.slice(1) + elements = document.getElementsByClassName(class_selector) + } + else { + elements = document.getElementsByTagName(selector) + } + return new QueryWrapper(elements) }; - window.$ = myQuery; + window.$ = myQuery; + $.version = "beta" + + $.each = function(array, func) { + for(var i = 0; i < array.length; i++) { + func(array[i]); + } + }; })(); + +// FOR EXTENSIONS +// var rexClass = /(\.[_a-z]+[_a-z0-9-:\\]*)/ig; +// var rexId = /(#[a-z]+[_a-z0-9-:\\]*)/ig; +// var rexTag = /([a-z]+[_a-z0-9-:\\]*)/ig; +// var elements = []; + +// // for (var i = 0; i < selector.length; i++) { + +// if ( selector[i].match(rexId) == selector[i] ) { +// result = document.getElementById(...); +// elements.push(result); +// } +// else if (selector[i].match(rexClass) == selector[i] ) { +// result = document.getElementByClassName(selector[i]); +// elements.push(result); +// } +// else if (selector[i].match(rexTag) == selector[i] ) { +// result = document.getElementByTagName(selector[i]); +// elements.push(result); +// } + // }; + +