From d0a6f2b5561a56bc8febcbb4f6e8273a09f51bc8 Mon Sep 17 00:00:00 2001 From: Wouter Groeneveld Date: Wed, 29 Jun 2011 10:24:32 +0200 Subject: [PATCH 1/4] hack envjs element css style parsing --- src/test/javascript/lib/env.utils.js | 29 ++++++++++++++-- src/test/javascript/specs/envUtilsSpec.js | 41 ++++++++++++++++++++++- 2 files changed, 66 insertions(+), 4 deletions(-) diff --git a/src/test/javascript/lib/env.utils.js b/src/test/javascript/lib/env.utils.js index c7f50a1..f8735f4 100755 --- a/src/test/javascript/lib/env.utils.js +++ b/src/test/javascript/lib/env.utils.js @@ -55,7 +55,8 @@ * Envjs specific hacks * 1) Fix Envjs relative path system to work with Windows path systems * 2) Fix window.setTimeout() using Rhino specific functions - * 3) Fix CSS2Properties support: all properties have the same objmaps, wtf? + * 3) Fix CSS2Properties support for parsing style attributes: get from raw node context. + * 4) Fix CSS2Properties support for setting values: all properties have the same objmaps, wtf? */ (function() { @@ -74,6 +75,29 @@ }); }; + (function(Element) { + + var style = "style"; + function lookupStyleInNodeAttributes(el) { + if(el.attributes) { + for(var i = 0; i < el.attributes.length; i++) { + if(el.attributes[i].nodeName === style) { + return el.attributes[i].nodeValue; + } + } + } + } + + var styleSetFn = Element.__lookupGetter__(style); + Element.__defineGetter__(style, function() { + if(!this.cssText) { + this.cssText = lookupStyleInNodeAttributes(this); + } + return styleSetFn.apply(this); + }); + + })(HTMLElement.prototype); + (function(css) { var setCssProperty = css.prototype.setProperty; @@ -82,9 +106,8 @@ if(Object.keys(Object.getPrototypeOf(this.styleIndex)).length === 0) { this.styleIndex = Object.create(this.styleIndex); } - + return setCssProperty.call(this, name, value); } })(CSS2Properties); - })(); diff --git a/src/test/javascript/specs/envUtilsSpec.js b/src/test/javascript/specs/envUtilsSpec.js index d0ff318..cd3e508 100755 --- a/src/test/javascript/specs/envUtilsSpec.js +++ b/src/test/javascript/specs/envUtilsSpec.js @@ -1,7 +1,46 @@ +importPackage(org.apache.commons.io); +importPackage(java.io); + +var loadFixtures = function() { + this.fixturesPath = "src/test/javascript/specs/fixtures"; + + var read = ""; + for(var i = 0; i < arguments.length; i++) { + read += FileUtils.readFileToString(new File(this.fixturesPath, arguments[i])); + } + + document.body.innerHTML = document.body.innerHTML + read; +} + +var cleanupFixtures = function() { + document.body.innerHTML = ""; +} + describe("envjs fixes", function() { - describe("CSS2 style property support", function() { + describe("CSS2 style property support for parsing style attributes", function() { + beforeEach(function() { + loadFixtures("styleAttributes.html"); + }); + + afterEach(cleanupFixtures); + + it("should get a style attribute from a static DOM element", function() { + var div = document.getElementById("div"); + expect(div.style.color).toBe("blue"); + }); + + it("should get a style attribute with dashes using camelCasing properties", function() { + var spanStyle = document.getElementById("span").style; + + expect(spanStyle.backgroundColor).toBe("green"); + expect(spanStyle.fontSize).toBe("8pt"); + expect(spanStyle.fontFamily).toBe("verdana"); + }); + }); + + describe("CSS2 style property support for setting values", function() { var someColor = "#FFFFFF"; var someFont = "12px 'Bitstream Vera Sans Mono','Courier',monospace"; From 1868b831d4c940f6d232c33fb74401f84ad2c457 Mon Sep 17 00:00:00 2001 From: Wouter Groeneveld Date: Wed, 29 Jun 2011 10:38:04 +0200 Subject: [PATCH 2/4] added jasmine jquery matchers, modified for envjs/rhino support --- README.md | 28 ++- src/test/java/be/klak/env/EnvUtilsTest.java | 9 +- {bin => src/test/javascript}/jquery-1.6.1.js | 0 .../lib/jasmine-1.0.2/jasmine-jquery-rhino.js | 193 ++++++++++++++++++ src/test/javascript/specs/envUtilsSpec.js | 22 +- .../specs/fixtures/styleAttributes.html | 8 + 6 files changed, 237 insertions(+), 23 deletions(-) rename {bin => src/test/javascript}/jquery-1.6.1.js (100%) create mode 100755 src/test/javascript/lib/jasmine-1.0.2/jasmine-jquery-rhino.js create mode 100755 src/test/javascript/specs/fixtures/styleAttributes.html diff --git a/README.md b/README.md index f010eb8..5c451a6 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,9 @@ So let's rephrase: * Talks like a duck-erhm, any other Junit Java test. Just use a custom annotation (see below) * Executes super-fast. No browser required. Hocus-pocus. (Rhino + Envjs magic) -## Does this thing generate Junit XML? +## Does this thing support ... + +### Generation of Junit XML Results? Yes and no. Not explicitly using the Jasmine Junit XML Reporter, but since it's a Java Junit Result, your build process will do that for you. Maven surefire plugins will generate the needed result files, for Jenkins to pick up. Your stacktrace/failure message will be something like: @@ -49,6 +51,30 @@ Maven surefire plugins will generate the needed result files, for Jenkins to pic > Expected x to be y (zz.js, #458) Just like the default Jasmine HTML reporter. +(So, to answer the question: yes!) + +### GUI Testing with Envjs? + +Yes! It allows you to test your jQuery plugins or your spaghetti GUI+Logic code, neatly woven together. +You can use jasmine-jquery matchers. I've modified `jasmine.Fixtures` to support Envjs+Rhino. This means you can test stuff like this: + +```javascript +beforeEach(function() { + loadFixtures("myFixture.html"); +}); + +it("should be visible and blue", function() { + var div = $('#myDivInFixtureHtml'); + expect(div).toBeVisible(); + expect(div.css('color')).toBe('blue'); +}); +``` + +Fixtures are automatically cleaned up. See src/test/javascript/lib/jasminedir/jasmine-jquery-rhino.js + +#### But wait, CSS Style Parsing does not work in Envjs 1.2, how come this does? + +See env.utils.js. Cover your eyes - hacks present. ## What Do I need to do? diff --git a/src/test/java/be/klak/env/EnvUtilsTest.java b/src/test/java/be/klak/env/EnvUtilsTest.java index 3e0d63f..5782666 100755 --- a/src/test/java/be/klak/env/EnvUtilsTest.java +++ b/src/test/java/be/klak/env/EnvUtilsTest.java @@ -1,12 +1,19 @@ package be.klak.env; +import org.junit.Before; import org.junit.runner.RunWith; import be.klak.junit.jasmine.JasmineSuite; import be.klak.junit.jasmine.JasmineTestRunner; +import be.klak.rhino.RhinoContext; @RunWith(JasmineTestRunner.class) -@JasmineSuite +@JasmineSuite(sources = "jquery-1.6.1.js", sourcesRootDir = "src/test/javascript") public class EnvUtilsTest { + @Before + public void loadJasmineJQueryMatchers(RhinoContext context) { + context.load("src/test/javascript/lib/jasmine-1.0.2/jasmine-jquery-rhino.js"); + } + } diff --git a/bin/jquery-1.6.1.js b/src/test/javascript/jquery-1.6.1.js similarity index 100% rename from bin/jquery-1.6.1.js rename to src/test/javascript/jquery-1.6.1.js diff --git a/src/test/javascript/lib/jasmine-1.0.2/jasmine-jquery-rhino.js b/src/test/javascript/lib/jasmine-1.0.2/jasmine-jquery-rhino.js new file mode 100755 index 0000000..8e8d4fe --- /dev/null +++ b/src/test/javascript/lib/jasmine-1.0.2/jasmine-jquery-rhino.js @@ -0,0 +1,193 @@ +importPackage(org.apache.commons.io); +importPackage(java.io); + +var loadFixtures = function() { + jasmine.getFixtures().load.apply(jasmine.getFixtures(), arguments); +}; + +var appendToBody = function(el) { + $('body').append(el); +}; + +jasmine.getFixtures = function() { + return jasmine.currentFixtures_ = jasmine.currentFixtures_ || new jasmine.Fixtures(); +}; + +jasmine.Fixtures = function() { + this.fixturesPath = 'src/test/javascript/specs/fixtures'; +}; + +jasmine.Fixtures.prototype.load = function() { + appendToBody(this.read.apply(this, arguments)); +}; + +jasmine.Fixtures.prototype.read = function() { + var read = ""; + for(var i = 0; i < arguments.length; i++) { + read += FileUtils.readFileToString(new File(this.fixturesPath, arguments[i])); + } + return read; +} + +jasmine.Fixtures.prototype.cleanUp = function() { + $('body').children().remove(); +}; + +jasmine.JQuery = function() {}; + +jasmine.JQuery.browserTagCaseIndependentHtml = function(html) { + return $('
').append(html).html(); +}; + +jasmine.JQuery.elementToString = function(element) { + return $('
').append(element.clone()).html(); +}; + +jasmine.JQuery.matchersClass = {}; + +(function(namespace) { + var data = { + spiedEvents: {}, + handlers: [] + }; + + namespace.events = { + spyOn: function(selector, eventName) { + var handler = function(e) { + data.spiedEvents[[selector, eventName]] = e; + }; + $(selector).bind(eventName, handler); + data.handlers.push(handler); + }, + + wasTriggered: function(selector, eventName) { + return !!(data.spiedEvents[[selector, eventName]]); + }, + + cleanUp: function() { + data.spiedEvents = {}; + data.handlers = []; + } + } +})(jasmine.JQuery); + +(function(){ + var jQueryMatchers = { + toHaveClass: function(className) { + return this.actual.hasClass(className); + }, + + toBeVisible: function() { + return this.actual.is(':visible'); + }, + + toBeHidden: function() { + return this.actual.is(':hidden'); + }, + + toBeSelected: function() { + return this.actual.is(':selected'); + }, + + toBeChecked: function() { + return this.actual.is(':checked'); + }, + + toBeEmpty: function() { + return this.actual.is(':empty'); + }, + + toExist: function() { + return this.actual.size() > 0; + }, + + toHaveAttr: function(attributeName, expectedAttributeValue) { + return hasProperty(this.actual.attr(attributeName), expectedAttributeValue); + }, + + toHaveId: function(id) { + return this.actual.attr('id') == id; + }, + + toHaveHtml: function(html) { + return this.actual.html() == jasmine.JQuery.browserTagCaseIndependentHtml(html); + }, + + toHaveText: function(text) { + if (text && jQuery.isFunction(text.test)) { + return text.test(this.actual.text()); + } else { + return this.actual.text() == text; + } + }, + + toHaveValue: function(value) { + return this.actual.val() == value; + }, + + toHaveData: function(key, expectedValue) { + return hasProperty(this.actual.data(key), expectedValue); + }, + + toBe: function(selector) { + return this.actual.is(selector); + }, + + toContain: function(selector) { + return this.actual.find(selector).size() > 0; + }, + + toBeDisabled: function(selector){ + return this.actual.attr("disabled") == true; + } + }; + + var hasProperty = function(actualValue, expectedValue) { + if (expectedValue === undefined) { + return actualValue !== undefined; + } + return actualValue == expectedValue; + }; + + var bindMatcher = function(methodName) { + var builtInMatcher = jasmine.Matchers.prototype[methodName]; + + jasmine.JQuery.matchersClass[methodName] = function() { + if (this.actual instanceof jQuery) { + var result = jQueryMatchers[methodName].apply(this, arguments); + this.actual = jasmine.JQuery.elementToString(this.actual); + return result; + } + + if (builtInMatcher) { + return builtInMatcher.apply(this, arguments); + } + + return false; + }; + }; + + for(var methodName in jQueryMatchers) { + bindMatcher(methodName); + } +})(); + +beforeEach(function() { + this.addMatchers(jasmine.JQuery.matchersClass); + this.addMatchers({ + toHaveBeenTriggeredOn: function(selector) { + this.message = function() { + return [ + "Expected event " + this.actual + " to have been triggered on" + selector, + "Expected event " + this.actual + " not to have been triggered on" + selector + ]; + }; + return jasmine.JQuery.events.wasTriggered(selector, this.actual); + } + }) +}); + +afterEach(function() { + jasmine.getFixtures().cleanUp(); + jasmine.JQuery.events.cleanUp(); +}); diff --git a/src/test/javascript/specs/envUtilsSpec.js b/src/test/javascript/specs/envUtilsSpec.js index cd3e508..13ddb6e 100755 --- a/src/test/javascript/specs/envUtilsSpec.js +++ b/src/test/javascript/specs/envUtilsSpec.js @@ -1,31 +1,11 @@ -importPackage(org.apache.commons.io); -importPackage(java.io); - -var loadFixtures = function() { - this.fixturesPath = "src/test/javascript/specs/fixtures"; - - var read = ""; - for(var i = 0; i < arguments.length; i++) { - read += FileUtils.readFileToString(new File(this.fixturesPath, arguments[i])); - } - - document.body.innerHTML = document.body.innerHTML + read; -} - -var cleanupFixtures = function() { - document.body.innerHTML = ""; -} - describe("envjs fixes", function() { describe("CSS2 style property support for parsing style attributes", function() { beforeEach(function() { loadFixtures("styleAttributes.html"); }); - - afterEach(cleanupFixtures); - + it("should get a style attribute from a static DOM element", function() { var div = document.getElementById("div"); expect(div.style.color).toBe("blue"); diff --git a/src/test/javascript/specs/fixtures/styleAttributes.html b/src/test/javascript/specs/fixtures/styleAttributes.html new file mode 100755 index 0000000..99c28c4 --- /dev/null +++ b/src/test/javascript/specs/fixtures/styleAttributes.html @@ -0,0 +1,8 @@ +
+ This is blue and in Verdana. Cool? Right! +
+ + + Right makes might! (They say) + +
From aa3de3f54f4faca34832029e5de08288322560ef Mon Sep 17 00:00:00 2001 From: Wouter Groeneveld Date: Wed, 29 Jun 2011 10:44:42 +0200 Subject: [PATCH 3/4] updated readme --- README.md | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 5c451a6..90f8277 100644 --- a/README.md +++ b/README.md @@ -76,16 +76,26 @@ Fixtures are automatically cleaned up. See src/test/javascript/lib/jasminedir/ja See env.utils.js. Cover your eyes - hacks present. -## What Do I need to do? +### Debugging 'n stuff? + +Yes! When the debug mode flag in `@JasmineSuite` has been set to `true`, you can use the Rhino Debugger to set breakpoints. +After pressing "GO", the tests will run and you can inspect stuff and step through the code. + +#### What about integrated debugging inside my IDE? + +Tough luck. I've tried to get JSDT working but no avail. +You can still use Firebug to debug when generating a specRunner HTML file (see below). + +## Excellent! What Do I need to do? 1. Fork this project. 2. Create some Jasmine specs, place them in some folder. -3. Create a Junit test class, annotate it with _@RunWith(JasmineTestRunner.class)_ -4. Fill in the blanks using @JasmineSuite +3. Create a Junit test class, annotate it with `@RunWith(JasmineTestRunner.class)` +4. Fill in the blanks using `@JasmineSuite` ## More options -_@JasmineSuite_ allows you to set these options: +`@JasmineSuite` allows you to set these options: * debug: use the built-in Rhino debugger (gives you the chance to set a breakpoint before firing the test suite) * jsRootDir: the javascript install root dir. Jasmine and other should be installed here (see source) @@ -100,7 +110,7 @@ Currently, Jasmine Junit Runner relies on Rhino 1.7R2 (+ es5-shim) & Envjs 1.2 t ### Dependencies Overview -See the _pom.xml_ (Maven2) - you can build the whole thing using: +See the `pom.xml` (Maven2) - you can build the whole thing using: > mvn clean install @@ -139,7 +149,7 @@ describe("my awesome code", function() { }); ``` -### Using Junit's _@Before_ and _@After_ +### Using Junit's `@Before` and `@After_` It's possible to do some extra work before and after each spec run: @@ -168,8 +178,8 @@ public class MyAwesomeTest { What's happening? -* You can define n number of _PUBLIC_ methods annotated with @Before or @After -* You can, but don't have to, take the _RhinoContext_ object as the only parameter. This allows you to set stuff up in JS space before running the spec. +* You can define n number of _PUBLIC_ methods annotated with `@Before` or `@After` +* You can, but don't have to, take the `RhinoContext` object as the only parameter. This allows you to set stuff up in JS space before running the spec. ### Generating a spec runner @@ -201,10 +211,3 @@ Your awesome test (example 1) would for instance generate this html file: ``` You can inspect the output using firefox, or debug in your spec file using firebug. - -### Debugging in Java - -When the debug mode flag has been set to _true_, you can use the Rhino Debugger to set breakpoints. -After pressing "GO", the tests will run and you can inspect stuff and step through the code. - -Integrated debugging into for example Eclipse does not work for the moment. From 0d84493b056deb0d4f97f2fee46cd213c2ad4852 Mon Sep 17 00:00:00 2001 From: Wouter Groeneveld Date: Wed, 29 Jun 2011 10:52:22 +0200 Subject: [PATCH 4/4] modified jquery include --- bin/bootstrap.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/bootstrap.js b/bin/bootstrap.js index a851590..2b92dc7 100755 --- a/bin/bootstrap.js +++ b/bin/bootstrap.js @@ -2,7 +2,7 @@ load("./../src/test/javascript/lib/env.rhino.1.2.js"); load("./../src/test/javascript/lib/env.utils.js"); load("./../src/test/javascript/envJsOptions.js"); -load("jquery-1.6.1.js"); +load("./../src/test/javascript/jquery-1.6.1.js"); function append(itm) { $('body').append($(itm));