diff --git a/CHANGELOG.md b/CHANGELOG.md index 197219038..95b627b53 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,11 @@ Tweak to in-line script processing to fix a problem no one reported. Fire `focus` and `blur` events when filling fields, selecting fields, pressing button and switching windows. +Both `focus` and `blur` methods now work and you can get the `activeElement`. + + 513 tests + 12.9 sec to complete + ## Version 1.1.5 2012-05-24 diff --git a/lib/zombie/browser.coffee b/lib/zombie/browser.coffee index 1efafb0a6..ac4efcc33 100644 --- a/lib/zombie/browser.coffee +++ b/lib/zombie/browser.coffee @@ -521,7 +521,7 @@ class Browser extends EventEmitter throw new Error("This INPUT field is disabled") if field.getAttribute("readonly") throw new Error("This INPUT field is readonly") - @window._focused = field + field.focus() field.value = value @fire "change", field, callback return this @@ -613,7 +613,7 @@ class Browser extends EventEmitter if option && !option.getAttribute("selected") select = @xpath("./ancestor::select", option).value[0] option.setAttribute("selected", "selected") - @window._focused = select + select.focus() @fire "change", select, callback else if callback process.nextTick -> @@ -645,7 +645,7 @@ class Browser extends EventEmitter unless select.multiple throw new Error("Cannot unselect in single select") option.removeAttribute("selected") - @window._focused = select + select.focus() @fire "change", select, callback else if callback process.nextTick -> @@ -670,7 +670,7 @@ class Browser extends EventEmitter field.files ||= [] field.files.push file field.value = filename - @window._focused = field + field.focus() @fire "change", field, callback return this @@ -703,7 +703,7 @@ class Browser extends EventEmitter throw new Error("No BUTTON '#{selector}'") if button.getAttribute("disabled") throw new Error("This button is disabled") - @window._focused = button + button.focus() return @fire("click", button, callback) # ### browser.focused => element diff --git a/lib/zombie/forms.coffee b/lib/zombie/forms.coffee index f75db3d58..898ff5773 100644 --- a/lib/zombie/forms.coffee +++ b/lib/zombie/forms.coffee @@ -123,13 +123,13 @@ HTML.HTMLInputElement.prototype._eventDefaults = # ignore all other clicks. We need those other clicks to occur, so we're going # to dispatch them all. HTML.HTMLInputElement.prototype.click = -> + focus(@ownerDocument, this) + # First event we fire is click event click = => event = @ownerDocument.createEvent("HTMLEvents") event.initEvent "click", true, true cancelled = @ownerDocument.parentWindow.browser.dispatchEvent(this, event) - unless cancelled - @ownerDocument.parentWindow._focused = this return !cancelled # If that works out, we follow with a change event @@ -187,3 +187,28 @@ HTML.Document.prototype._elementBuilders["button"] = (doc, s)-> button = new HTML.HTMLButtonElement(doc, s) button.type ||= "submit" return button + + +# The element in focus. +HTML.HTMLDocument.prototype.__defineGetter__ "activeElement", -> + return document._focused + +# Change the current element in focus +focus = (document, element)-> + unless element == document._focused + if document._focused + onblur = document.createEvent("HTMLEvents") + onblur.initEvent "blur", false, false + document._focused.dispatchEvent onblur + if element + onfocus = document.createEvent("HTMLEvents") + onfocus.initEvent "focus", false, false + element.dispatchEvent onfocus + document._focused = element + +for element in [HTML.HTMLInputElement, HTML.HTMLSelectElement, HTML.HTMLTextAreaElement, HTML.HTMLButtonElement, HTML.HTMLAnchorElement] + element.prototype.focus = -> + focus @ownerDocument, this + element.prototype.blur = -> + focus @ownerDocument, null + diff --git a/lib/zombie/windows.coffee b/lib/zombie/windows.coffee index 4576d4a69..9e1d3fd1e 100644 --- a/lib/zombie/windows.coffee +++ b/lib/zombie/windows.coffee @@ -223,35 +223,17 @@ class Windows # -- Focusing -- - # Handle change to in-focus element - focused = null - Object.defineProperty window, "_focused", - get: -> - return focused - set: (element)-> - unless element == focused - if focused - onblur = window.document.createEvent("HTMLEvents") - onblur.initEvent "blur", false, false - previous = focused - previous.dispatchEvent onblur - if element - onfocus = window.document.createEvent("HTMLEvents") - onfocus.initEvent "focus", false, false - element.dispatchEvent onfocus - focused = element - # If window goes in/out of focus, notify focused input field window.addEventListener "focus", (event)-> - if window._focused + if window.document.activeElement onfocus = window.document.createEvent("HTMLEvents") onfocus.initEvent "focus", false, false - window._focused.dispatchEvent onfocus + window.document.activeElement.dispatchEvent onfocus window.addEventListener "blur", (event)-> - if window._focused + if window.document.activeElement onblur = window.document.createEvent("HTMLEvents") onblur.initEvent "blur", false, false - window._focused.dispatchEvent onblur + window.document.activeElement.dispatchEvent onblur # -- JavaScript evaluation diff --git a/test/forms_test.coffee b/test/forms_test.coffee index b64d6d211..e12875e8b 100644 --- a/test/forms_test.coffee +++ b/test/forms_test.coffee @@ -218,29 +218,28 @@ describe "Forms", -> assert.throws -> @browser.fill @browser.querySelector("#readonly_input_field"), "yeahh" - @browser.fill @browser.querySelector("#field-email3"), "headchomper@example.com", done - - it "should fire the callback", -> - assert.equal @browser.querySelector("#field-email3").value, "headchomper@example.com" - describe "any field", -> - before -> - @field1 = @browser.querySelector("#field-email2") - @field2 = @browser.querySelector("#field-email3") - it "should fire focus event on selected field", (done)-> - @browser.fill @field1, "something" - @field2.addEventListener "focus", -> - done() - done = null - @browser.fill @field2, "else" + browser = new Browser() + browser.visit("http://localhost:3003/forms/form") + .then -> + field1 = browser.querySelector("#field-email2") + field2 = browser.querySelector("#field-email3") + browser.fill field1, "something" + field2.addEventListener "focus", -> + done() + browser.fill field2, "else" it "should fire blur event on previous field", (done)-> - @browser.fill @field1, "something" - @field1.addEventListener "blur", -> - done() - done = null - @browser.fill @field2, "else" + browser = new Browser() + browser.visit("http://localhost:3003/forms/form") + .then -> + field1 = browser.querySelector("#field-email2") + field2 = browser.querySelector("#field-email3") + browser.fill field1, "something" + field1.addEventListener "blur", -> + done() + browser.fill field2, "else" describe "check box", -> @@ -337,25 +336,29 @@ describe "Forms", -> assert.deepEqual @values, [false, true, false] describe "any checkbox", -> - before -> - @field1 = @browser.querySelector("#field-check") - @field2 = @browser.querySelector("#field-uncheck") - it "should fire focus event on selected field", (done)-> - @browser.uncheck @field1 - @browser.check @field1 - @field2.addEventListener "focus", -> - done() - done = null - @browser.check @field2 + browser = new Browser() + browser.visit("http://localhost:3003/forms/form") + .then -> + field1 = browser.querySelector("#field-check") + field2 = browser.querySelector("#field-uncheck") + browser.uncheck field1 + browser.check field1 + field2.addEventListener "focus", -> + done() + browser.check field2 it "should fire blur event on previous field", (done)-> - @browser.uncheck @field1 - @browser.check @field1 - @field1.addEventListener "blur", -> - done() - done = null - @browser.check @field2 + browser = new Browser() + browser.visit("http://localhost:3003/forms/form") + .then -> + field1 = browser.querySelector("#field-check") + field2 = browser.querySelector("#field-uncheck") + browser.uncheck field1 + browser.check field1 + field1.addEventListener "blur", -> + done() + browser.check field2 describe "radio buttons", -> @@ -506,22 +509,28 @@ describe "Forms", -> describe "any selection", -> before -> - @field1 = @browser.querySelector("#field-email2") - @field2 = @browser.querySelector("#field-kills") it "should fire focus event on selected field", (done)-> - @browser.fill @field1, "something" - @field2.addEventListener "focus", -> - done() - done = null - @browser.select @field2, "Five" + browser = new Browser() + browser.visit("http://localhost:3003/forms/form") + .then -> + field1 = browser.querySelector("#field-email2") + field2 = browser.querySelector("#field-kills") + browser.fill field1, "something" + field2.addEventListener "focus", -> + done() + browser.select field2, "Five" it "should fire blur event on previous field", (done)-> - @browser.fill @field1, "something" - @field1.addEventListener "blur", -> - done() - done = null - @browser.select @field2, "Five" + browser = new Browser() + browser.visit("http://localhost:3003/forms/form") + .then -> + field1 = browser.querySelector("#field-email2") + field2 = browser.querySelector("#field-kills") + browser.fill field1, "something" + field1.addEventListener "blur", -> + done() + browser.select field2, "Five" describe "multiple select option", -> @@ -752,27 +761,25 @@ describe "Forms", -> assert.equal @browser.text("#image_clicked"), "undefined" describe "pressButton", -> - before (done)-> - @browser = new Browser() - @browser.visit("http://localhost:3003/forms/form") - .then => - @field1 = @browser.querySelector("#field-email2") - return - .then(done, done) - it "should fire focus event on button", (done)-> - @browser.fill @field1, "something" - @browser.button("Hit Me").addEventListener "focus", -> - done() - done = null - @browser.pressButton("Hit Me") + browser = new Browser() + browser.visit("http://localhost:3003/forms/form") + .then -> + field = browser.querySelector("#field-email2") + browser.fill field, "something" + browser.button("Hit Me").addEventListener "focus", -> + done() + browser.pressButton("Hit Me") it "should fire blur event on previous field", (done)-> - @browser.fill @field1, "something" - @field1.addEventListener "blur", -> - done() - done = null - @browser.pressButton("Hit Me") + browser = new Browser() + browser.visit("http://localhost:3003/forms/form") + .then => + field = browser.querySelector("#field-email2") + browser.fill field, "something" + field.addEventListener "blur", -> + done() + browser.pressButton("Hit Me") describe "by clicking image button", ->