domMock: implement event propagation
This commit is contained in:
parent
8dc21f4c48
commit
12d3540d47
2 changed files with 648 additions and 21 deletions
|
|
@ -37,6 +37,30 @@ module.exports = function(options) {
|
||||||
function isModernEvent(type) {
|
function isModernEvent(type) {
|
||||||
return type === "transitionstart" || type === "transitionend" || type === "animationstart" || type === "animationend"
|
return type === "transitionstart" || type === "transitionend" || type === "animationstart" || type === "animationend"
|
||||||
}
|
}
|
||||||
|
function dispatchEvent(e) {
|
||||||
|
var stopped = false
|
||||||
|
e.stopImmediatePropagation = function() {
|
||||||
|
e.stopPropagation()
|
||||||
|
stopped = true
|
||||||
|
}
|
||||||
|
e.currentTarget = this
|
||||||
|
if (this._events[e.type] != null) {
|
||||||
|
for (var i = 0; i < this._events[e.type].handlers.length; i++) {
|
||||||
|
var useCapture = this._events[e.type].options[i].capture
|
||||||
|
if (useCapture && e.eventPhase < 3 || !useCapture && e.eventPhase > 1) {
|
||||||
|
var handler = this._events[e.type].handlers[i]
|
||||||
|
if (typeof handler === "function") try {handler.call(this, e)} catch(e) {setTimeout(function(){throw e})}
|
||||||
|
else try {handler.handleEvent(e)} catch(e) {setTimeout(function(){throw e})}
|
||||||
|
if (stopped) return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// this is inaccurate. Normally the event fires in definition order, including legacy events
|
||||||
|
// this would require getters/setters for each of them though and we haven't gotten around to
|
||||||
|
// adding them since it would be at a high perf cost or would entail some heavy refactoring of
|
||||||
|
// the mocks (prototypes instead of closures).
|
||||||
|
if (e.eventPhase > 1 && typeof this["on" + e.type] === "function" && !isModernEvent(e.type)) try {this["on" + e.type](e)} catch(e) {setTimeout(function(){throw e})}
|
||||||
|
}
|
||||||
function appendChild(child) {
|
function appendChild(child) {
|
||||||
var ancestor = this
|
var ancestor = this
|
||||||
while (ancestor !== child && ancestor !== null) ancestor = ancestor.parentNode
|
while (ancestor !== child && ancestor !== null) ancestor = ancestor.parentNode
|
||||||
|
|
@ -260,35 +284,99 @@ module.exports = function(options) {
|
||||||
else this.setAttribute("class", value)
|
else this.setAttribute("class", value)
|
||||||
},
|
},
|
||||||
focus: function() {activeElement = this},
|
focus: function() {activeElement = this},
|
||||||
addEventListener: function(type, callback) {
|
addEventListener: function(type, handler, options) {
|
||||||
if (events[type] == null) events[type] = [callback]
|
if (arguments.length > 2) {
|
||||||
else events[type].push(callback)
|
if (typeof options === "object" && options != null) throw new TypeError("NYI: addEventListener options")
|
||||||
|
else if (typeof options !== "boolean") throw new TypeError("boolean expected for useCapture")
|
||||||
|
else options = {capture: options}
|
||||||
|
} else {
|
||||||
|
options = {capture: false}
|
||||||
|
}
|
||||||
|
if (events[type] == null) events[type] = {handlers: [handler], options: [options]}
|
||||||
|
else {
|
||||||
|
var found = false
|
||||||
|
for (var i = 0; i < events[type].handlers.length; i++) {
|
||||||
|
if (events[type].handlers[i] === handler && events[type].options[i].capture === options.capture) {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
events[type].handlers.push(handler)
|
||||||
|
events[type].options.push(options)
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
removeEventListener: function(type, callback) {
|
removeEventListener: function(type, handler, options) {
|
||||||
|
if (arguments.length > 2) {
|
||||||
|
if (typeof options === "object" && options != null) throw new TypeError("NYI: addEventListener options")
|
||||||
|
else if (typeof options !== "boolean") throw new TypeError("boolean expected for useCapture")
|
||||||
|
else options = {capture: options}
|
||||||
|
} else {
|
||||||
|
options = {capture: false}
|
||||||
|
}
|
||||||
if (events[type] != null) {
|
if (events[type] != null) {
|
||||||
var index = events[type].indexOf(callback)
|
for (var i = 0; i < events[type].handlers.length; i++) {
|
||||||
if (index > -1) events[type].splice(index, 1)
|
if (events[type].handlers[i] === handler && events[type].options[i].capture === options.capture) {
|
||||||
|
events[type].handlers.splice(i, 1)
|
||||||
|
events[type].options.splice(i, 1)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
dispatchEvent: function(e) {
|
dispatchEvent: function(e) {
|
||||||
if (this.nodeName === "INPUT" && this.attributes["type"] != null && this.attributes["type"].value === "checkbox" && e.type === "click") {
|
var parents = []
|
||||||
this.checked = !this.checked
|
if (this.parentNode != null) {
|
||||||
|
var parent = this.parentNode
|
||||||
|
do {
|
||||||
|
parents.push(parent)
|
||||||
|
parent = parent.parentNode
|
||||||
|
} while (parent != null)
|
||||||
}
|
}
|
||||||
|
|
||||||
e.target = this
|
e.target = this
|
||||||
if (events[e.type] != null) {
|
var prevented = false
|
||||||
for (var i = 0; i < events[e.type].length; i++) {
|
e.preventDefault = function() {
|
||||||
var handler = events[e.type][i]
|
prevented = true
|
||||||
if (typeof handler === "function") handler.call(this, e)
|
}
|
||||||
else handler.handleEvent(e)
|
var stopped = false
|
||||||
|
e.stopPropagation = function() {
|
||||||
|
stopped = true
|
||||||
|
}
|
||||||
|
e.eventPhase = 1
|
||||||
|
try {
|
||||||
|
for (var i = parents.length - 1; 0 <= i; i--) {
|
||||||
|
dispatchEvent.call(parents[i], e)
|
||||||
|
if (stopped) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
e.eventPhase = 2
|
||||||
|
dispatchEvent.call(this, e)
|
||||||
|
if (stopped) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
e.eventPhase = 3
|
||||||
|
for (var i = 0; i < parents.length; i++) {
|
||||||
|
dispatchEvent.call(parents[i], e)
|
||||||
|
if (stopped) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
throw e
|
||||||
|
} finally {
|
||||||
|
e.eventPhase = 0
|
||||||
|
if (!prevented) {
|
||||||
|
if (this.nodeName === "INPUT" && this.attributes["type"] != null && this.attributes["type"].value === "checkbox" && e.type === "click") {
|
||||||
|
this.checked = !this.checked
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
e.preventDefault = function() {
|
|
||||||
// TODO: should this do something?
|
|
||||||
}
|
|
||||||
if (typeof this["on" + e.type] === "function" && !isModernEvent(e.type)) this["on" + e.type](e)
|
|
||||||
},
|
},
|
||||||
onclick: null,
|
onclick: null,
|
||||||
|
_events: events
|
||||||
}
|
}
|
||||||
|
|
||||||
if (element.nodeName === "A") {
|
if (element.nodeName === "A") {
|
||||||
|
|
@ -515,7 +603,8 @@ module.exports = function(options) {
|
||||||
},
|
},
|
||||||
createEvent: function() {
|
createEvent: function() {
|
||||||
return {
|
return {
|
||||||
initEvent: function(type) {this.type = type},
|
eventPhase: 0,
|
||||||
|
initEvent: function(type) {this.type = type}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
get activeElement() {return activeElement},
|
get activeElement() {return activeElement},
|
||||||
|
|
|
||||||
|
|
@ -611,13 +611,57 @@ o.spec("domMock", function() {
|
||||||
o(spy.args[0].type).equals("click")
|
o(spy.args[0].type).equals("click")
|
||||||
o(spy.args[0].target).equals(div)
|
o(spy.args[0].target).equals(div)
|
||||||
})
|
})
|
||||||
o("removeEventListener works", function(done) {
|
o("removeEventListener works (bubbling phase)", function() {
|
||||||
div.addEventListener("click", spy, false)
|
div.addEventListener("click", spy, false)
|
||||||
div.removeEventListener("click", spy, false)
|
div.removeEventListener("click", spy, false)
|
||||||
div.dispatchEvent(e)
|
div.dispatchEvent(e)
|
||||||
|
|
||||||
o(spy.callCount).equals(0)
|
o(spy.callCount).equals(0)
|
||||||
done()
|
})
|
||||||
|
o("removeEventListener works (capture phase)", function() {
|
||||||
|
div.addEventListener("click", spy, true)
|
||||||
|
div.removeEventListener("click", spy, true)
|
||||||
|
div.dispatchEvent(e)
|
||||||
|
|
||||||
|
o(spy.callCount).equals(0)
|
||||||
|
})
|
||||||
|
o("removeEventListener is selective (bubbling phase)", function() {
|
||||||
|
var other = o.spy()
|
||||||
|
div.addEventListener("click", spy, false)
|
||||||
|
div.addEventListener("click", other, false)
|
||||||
|
div.removeEventListener("click", spy, false)
|
||||||
|
div.dispatchEvent(e)
|
||||||
|
|
||||||
|
o(spy.callCount).equals(0)
|
||||||
|
o(other.callCount).equals(1)
|
||||||
|
})
|
||||||
|
o("removeEventListener is selective (capture phase)", function() {
|
||||||
|
var other = o.spy()
|
||||||
|
div.addEventListener("click", spy, true)
|
||||||
|
div.addEventListener("click", other, true)
|
||||||
|
div.removeEventListener("click", spy, true)
|
||||||
|
div.dispatchEvent(e)
|
||||||
|
|
||||||
|
o(spy.callCount).equals(0)
|
||||||
|
o(other.callCount).equals(1)
|
||||||
|
})
|
||||||
|
o("removeEventListener only removes the handler related to a given phase (1/2)", function() {
|
||||||
|
spy = o.spy(function(e) {o(e.eventPhase).equals(3)})
|
||||||
|
$document.body.addEventListener("click", spy, true)
|
||||||
|
$document.body.addEventListener("click", spy, false)
|
||||||
|
$document.body.removeEventListener("click", spy, true)
|
||||||
|
div.dispatchEvent(e)
|
||||||
|
|
||||||
|
o(spy.callCount).equals(1)
|
||||||
|
})
|
||||||
|
o("removeEventListener only removes the handler related to a given phase (2/2)", function() {
|
||||||
|
spy = o.spy(function(e) {o(e.eventPhase).equals(1)})
|
||||||
|
$document.body.addEventListener("click", spy, true)
|
||||||
|
$document.body.addEventListener("click", spy, false)
|
||||||
|
$document.body.removeEventListener("click", spy, false)
|
||||||
|
div.dispatchEvent(e)
|
||||||
|
|
||||||
|
o(spy.callCount).equals(1)
|
||||||
})
|
})
|
||||||
o("click fires onclick", function() {
|
o("click fires onclick", function() {
|
||||||
div.onclick = spy
|
div.onclick = spy
|
||||||
|
|
@ -655,6 +699,488 @@ o.spec("domMock", function() {
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
o.spec("capture and bubbling phases", function() {
|
||||||
|
var div, e
|
||||||
|
o.beforeEach(function() {
|
||||||
|
div = $document.createElement("div")
|
||||||
|
e = $document.createEvent("MouseEvents")
|
||||||
|
e.initEvent("click", true, true)
|
||||||
|
|
||||||
|
$document.body.appendChild(div)
|
||||||
|
})
|
||||||
|
o.afterEach(function() {
|
||||||
|
$document.body.removeChild(div)
|
||||||
|
})
|
||||||
|
o("capture and bubbling events both fire on the target in the order they were defined, regardless of the phase", function () {
|
||||||
|
var sequence = []
|
||||||
|
var capture = o.spy(function(ev){
|
||||||
|
sequence.push("capture")
|
||||||
|
|
||||||
|
o(ev).equals(e)
|
||||||
|
o(ev.eventPhase).equals(2)
|
||||||
|
o(ev.target).equals(div)
|
||||||
|
o(ev.currentTarget).equals(div)
|
||||||
|
})
|
||||||
|
var bubble = o.spy(function(ev){
|
||||||
|
sequence.push("bubble")
|
||||||
|
|
||||||
|
o(ev).equals(e)
|
||||||
|
o(ev.eventPhase).equals(2)
|
||||||
|
o(ev.target).equals(div)
|
||||||
|
o(ev.currentTarget).equals(div)
|
||||||
|
})
|
||||||
|
|
||||||
|
div.addEventListener("click", bubble, false)
|
||||||
|
div.addEventListener("click", capture, true)
|
||||||
|
div.dispatchEvent(e)
|
||||||
|
|
||||||
|
o(capture.callCount).equals(1)
|
||||||
|
o(bubble.callCount).equals(1)
|
||||||
|
o(sequence).deepEquals(["bubble", "capture"])
|
||||||
|
})
|
||||||
|
o("capture and bubbling events both fire on the parent", function () {
|
||||||
|
var sequence = []
|
||||||
|
var capture = o.spy(function(ev){
|
||||||
|
sequence.push("capture")
|
||||||
|
|
||||||
|
o(ev).equals(e)
|
||||||
|
o(ev.eventPhase).equals(1)
|
||||||
|
o(ev.target).equals(div)
|
||||||
|
o(ev.currentTarget).equals($document.body)
|
||||||
|
})
|
||||||
|
var bubble = o.spy(function(ev){
|
||||||
|
sequence.push("bubble")
|
||||||
|
|
||||||
|
o(ev).equals(e)
|
||||||
|
o(ev.eventPhase).equals(3)
|
||||||
|
o(ev.target).equals(div)
|
||||||
|
o(ev.currentTarget).equals($document.body)
|
||||||
|
})
|
||||||
|
|
||||||
|
$document.body.addEventListener("click", bubble, false)
|
||||||
|
$document.body.addEventListener("click", capture, true)
|
||||||
|
div.dispatchEvent(e)
|
||||||
|
|
||||||
|
o(capture.callCount).equals(1)
|
||||||
|
o(bubble.callCount).equals(1)
|
||||||
|
o(sequence).deepEquals(["capture", "bubble"])
|
||||||
|
})
|
||||||
|
o("useCapture defaults to false", function () {
|
||||||
|
var sequence = []
|
||||||
|
var parent = o.spy(function(ev){
|
||||||
|
sequence.push("parent")
|
||||||
|
|
||||||
|
o(ev).equals(e)
|
||||||
|
o(ev.eventPhase).equals(3)
|
||||||
|
o(ev.target).equals(div)
|
||||||
|
o(ev.currentTarget).equals($document.body)
|
||||||
|
})
|
||||||
|
var target = o.spy(function(ev){
|
||||||
|
sequence.push("target")
|
||||||
|
|
||||||
|
o(ev).equals(e)
|
||||||
|
o(ev.eventPhase).equals(2)
|
||||||
|
o(ev.target).equals(div)
|
||||||
|
o(ev.currentTarget).equals(div)
|
||||||
|
})
|
||||||
|
|
||||||
|
$document.body.addEventListener("click", parent)
|
||||||
|
div.addEventListener("click", target)
|
||||||
|
div.dispatchEvent(e)
|
||||||
|
|
||||||
|
o(parent.callCount).equals(1)
|
||||||
|
o(target.callCount).equals(1)
|
||||||
|
o(sequence).deepEquals(["target", "parent"])
|
||||||
|
})
|
||||||
|
o("legacy handlers fire on the bubbling phase", function () {
|
||||||
|
var sequence = []
|
||||||
|
var parent = o.spy(function(ev){
|
||||||
|
sequence.push("parent")
|
||||||
|
|
||||||
|
o(ev).equals(e)
|
||||||
|
o(ev.eventPhase).equals(3)
|
||||||
|
o(ev.target).equals(div)
|
||||||
|
o(ev.currentTarget).equals($document.body)
|
||||||
|
})
|
||||||
|
var target = o.spy(function(ev){
|
||||||
|
sequence.push("target")
|
||||||
|
|
||||||
|
o(ev).equals(e)
|
||||||
|
o(ev.eventPhase).equals(2)
|
||||||
|
o(ev.target).equals(div)
|
||||||
|
o(ev.currentTarget).equals(div)
|
||||||
|
})
|
||||||
|
|
||||||
|
$document.body.addEventListener("click", parent)
|
||||||
|
$document.body.onclick = parent
|
||||||
|
div.addEventListener("click", target)
|
||||||
|
div.dispatchEvent(e)
|
||||||
|
|
||||||
|
o(parent.callCount).equals(2)
|
||||||
|
o(target.callCount).equals(1)
|
||||||
|
o(sequence).deepEquals(["target", "parent", "parent"])
|
||||||
|
})
|
||||||
|
o("events do not propagate to child nodes", function() {
|
||||||
|
var target = o.spy(function(ev){
|
||||||
|
o(ev).equals(e)
|
||||||
|
o(ev.eventPhase).equals(2)
|
||||||
|
o(ev.target).equals($document.body)
|
||||||
|
o(ev.currentTarget).equals($document.body)
|
||||||
|
})
|
||||||
|
var child = o.spy(function(){
|
||||||
|
})
|
||||||
|
|
||||||
|
$document.body.addEventListener("click", target)
|
||||||
|
div.addEventListener("click", child)
|
||||||
|
$document.body.dispatchEvent(e)
|
||||||
|
|
||||||
|
o(target.callCount).equals(1)
|
||||||
|
o(child.callCount).equals(0)
|
||||||
|
})
|
||||||
|
o("e.stopPropagation 1/6", function () {
|
||||||
|
var capParent = o.spy(function(e){e.stopPropagation()})
|
||||||
|
var capTarget = o.spy()
|
||||||
|
var bubTarget = o.spy()
|
||||||
|
var legacyTarget = o.spy()
|
||||||
|
var bubParent = o.spy()
|
||||||
|
var legacyParent = o.spy()
|
||||||
|
|
||||||
|
$document.body.addEventListener("click", capParent, true)
|
||||||
|
$document.body.addEventListener("click", bubParent, false)
|
||||||
|
$document.body.onclick = legacyParent
|
||||||
|
|
||||||
|
div.addEventListener("click", capTarget, true)
|
||||||
|
div.addEventListener("click", bubTarget, false)
|
||||||
|
div.onclick = legacyTarget
|
||||||
|
|
||||||
|
div.dispatchEvent(e)
|
||||||
|
|
||||||
|
o(capParent.callCount).equals(1)
|
||||||
|
o(capTarget.callCount).equals(0)
|
||||||
|
o(bubTarget.callCount).equals(0)
|
||||||
|
o(legacyTarget.callCount).equals(0)
|
||||||
|
o(bubParent.callCount).equals(0)
|
||||||
|
o(legacyParent.callCount).equals(0)
|
||||||
|
})
|
||||||
|
o("e.stopPropagation 2/6", function () {
|
||||||
|
var capParent = o.spy()
|
||||||
|
var capTarget = o.spy(function(e){e.stopPropagation()})
|
||||||
|
var bubTarget = o.spy()
|
||||||
|
var legacyTarget = o.spy()
|
||||||
|
var bubParent = o.spy()
|
||||||
|
var legacyParent = o.spy()
|
||||||
|
|
||||||
|
$document.body.addEventListener("click", capParent, true)
|
||||||
|
$document.body.addEventListener("click", bubParent, false)
|
||||||
|
$document.body.onclick = legacyParent
|
||||||
|
|
||||||
|
div.addEventListener("click", capTarget, true)
|
||||||
|
div.addEventListener("click", bubTarget, false)
|
||||||
|
div.onclick = legacyTarget
|
||||||
|
|
||||||
|
div.dispatchEvent(e)
|
||||||
|
|
||||||
|
o(capParent.callCount).equals(1)
|
||||||
|
o(capTarget.callCount).equals(1)
|
||||||
|
o(bubTarget.callCount).equals(1)
|
||||||
|
o(legacyTarget.callCount).equals(1)
|
||||||
|
o(bubParent.callCount).equals(0)
|
||||||
|
o(legacyParent.callCount).equals(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
o("e.stopPropagation 3/6", function () {
|
||||||
|
var capParent = o.spy()
|
||||||
|
var capTarget = o.spy()
|
||||||
|
var bubTarget = o.spy(function(e){e.stopPropagation()})
|
||||||
|
var legacyTarget = o.spy()
|
||||||
|
var bubParent = o.spy()
|
||||||
|
var legacyParent = o.spy()
|
||||||
|
|
||||||
|
$document.body.addEventListener("click", capParent, true)
|
||||||
|
$document.body.addEventListener("click", bubParent, false)
|
||||||
|
$document.body.onclick = legacyParent
|
||||||
|
|
||||||
|
div.addEventListener("click", capTarget, true)
|
||||||
|
div.addEventListener("click", bubTarget, false)
|
||||||
|
div.onclick = legacyTarget
|
||||||
|
|
||||||
|
div.dispatchEvent(e)
|
||||||
|
|
||||||
|
o(capParent.callCount).equals(1)
|
||||||
|
o(capTarget.callCount).equals(1)
|
||||||
|
o(bubTarget.callCount).equals(1)
|
||||||
|
o(legacyTarget.callCount).equals(1)
|
||||||
|
o(bubParent.callCount).equals(0)
|
||||||
|
o(legacyParent.callCount).equals(0)
|
||||||
|
})
|
||||||
|
o("e.stopPropagation 4/6", function () {
|
||||||
|
var capParent = o.spy()
|
||||||
|
var capTarget = o.spy()
|
||||||
|
var bubTarget = o.spy()
|
||||||
|
var legacyTarget = o.spy(function(e){e.stopPropagation()})
|
||||||
|
var bubParent = o.spy()
|
||||||
|
var legacyParent = o.spy()
|
||||||
|
|
||||||
|
$document.body.addEventListener("click", capParent, true)
|
||||||
|
$document.body.addEventListener("click", bubParent, false)
|
||||||
|
$document.body.onclick = legacyParent
|
||||||
|
|
||||||
|
div.addEventListener("click", capTarget, true)
|
||||||
|
div.addEventListener("click", bubTarget, false)
|
||||||
|
div.onclick = legacyTarget
|
||||||
|
|
||||||
|
div.dispatchEvent(e)
|
||||||
|
|
||||||
|
o(capParent.callCount).equals(1)
|
||||||
|
o(capTarget.callCount).equals(1)
|
||||||
|
o(bubTarget.callCount).equals(1)
|
||||||
|
o(legacyTarget.callCount).equals(1)
|
||||||
|
o(bubParent.callCount).equals(0)
|
||||||
|
o(legacyParent.callCount).equals(0)
|
||||||
|
})
|
||||||
|
o("e.stopPropagation 5/6", function () {
|
||||||
|
var capParent = o.spy()
|
||||||
|
var capTarget = o.spy()
|
||||||
|
var bubTarget = o.spy()
|
||||||
|
var legacyTarget = o.spy()
|
||||||
|
var bubParent = o.spy(function(e){e.stopPropagation()})
|
||||||
|
var legacyParent = o.spy()
|
||||||
|
|
||||||
|
$document.body.addEventListener("click", capParent, true)
|
||||||
|
$document.body.addEventListener("click", bubParent, false)
|
||||||
|
$document.body.onclick = legacyParent
|
||||||
|
|
||||||
|
div.addEventListener("click", capTarget, true)
|
||||||
|
div.addEventListener("click", bubTarget, false)
|
||||||
|
div.onclick = legacyTarget
|
||||||
|
|
||||||
|
div.dispatchEvent(e)
|
||||||
|
|
||||||
|
o(capParent.callCount).equals(1)
|
||||||
|
o(capTarget.callCount).equals(1)
|
||||||
|
o(bubTarget.callCount).equals(1)
|
||||||
|
o(legacyTarget.callCount).equals(1)
|
||||||
|
o(bubParent.callCount).equals(1)
|
||||||
|
o(legacyParent.callCount).equals(1)
|
||||||
|
})
|
||||||
|
o("e.stopPropagation 6/6", function () {
|
||||||
|
var capParent = o.spy()
|
||||||
|
var capTarget = o.spy()
|
||||||
|
var legacyTarget = o.spy()
|
||||||
|
var bubTarget = o.spy()
|
||||||
|
var bubParent = o.spy()
|
||||||
|
var legacyParent = o.spy(function(e){e.stopPropagation()})
|
||||||
|
|
||||||
|
$document.body.addEventListener("click", capParent, true)
|
||||||
|
$document.body.addEventListener("click", bubParent, false)
|
||||||
|
$document.body.onclick = legacyParent
|
||||||
|
|
||||||
|
div.addEventListener("click", capTarget, true)
|
||||||
|
div.addEventListener("click", bubTarget, false)
|
||||||
|
div.onclick = legacyTarget
|
||||||
|
|
||||||
|
div.dispatchEvent(e)
|
||||||
|
|
||||||
|
o(capParent.callCount).equals(1)
|
||||||
|
o(capTarget.callCount).equals(1)
|
||||||
|
o(bubTarget.callCount).equals(1)
|
||||||
|
o(legacyTarget.callCount).equals(1)
|
||||||
|
o(bubParent.callCount).equals(1)
|
||||||
|
o(legacyParent.callCount).equals(1)
|
||||||
|
})
|
||||||
|
o("e.stopImmediatePropagation 1/6", function () {
|
||||||
|
var capParent = o.spy(function(e){e.stopImmediatePropagation()})
|
||||||
|
var capTarget = o.spy()
|
||||||
|
var bubTarget = o.spy()
|
||||||
|
var legacyTarget = o.spy()
|
||||||
|
var bubParent = o.spy()
|
||||||
|
var legacyParent = o.spy()
|
||||||
|
|
||||||
|
$document.body.addEventListener("click", capParent, true)
|
||||||
|
$document.body.addEventListener("click", bubParent, false)
|
||||||
|
$document.body.onclick = legacyParent
|
||||||
|
|
||||||
|
div.addEventListener("click", capTarget, true)
|
||||||
|
div.addEventListener("click", bubTarget, false)
|
||||||
|
div.onclick = legacyTarget
|
||||||
|
|
||||||
|
div.dispatchEvent(e)
|
||||||
|
|
||||||
|
o(capParent.callCount).equals(1)
|
||||||
|
o(capTarget.callCount).equals(0)
|
||||||
|
o(bubTarget.callCount).equals(0)
|
||||||
|
o(legacyTarget.callCount).equals(0)
|
||||||
|
o(bubParent.callCount).equals(0)
|
||||||
|
o(legacyParent.callCount).equals(0)
|
||||||
|
})
|
||||||
|
o("e.stopImmediatePropagation 2/6", function () {
|
||||||
|
var capParent = o.spy()
|
||||||
|
var capTarget = o.spy(function(e){e.stopImmediatePropagation()})
|
||||||
|
var bubTarget = o.spy()
|
||||||
|
var legacyTarget = o.spy()
|
||||||
|
var bubParent = o.spy()
|
||||||
|
var legacyParent = o.spy()
|
||||||
|
|
||||||
|
$document.body.addEventListener("click", capParent, true)
|
||||||
|
$document.body.addEventListener("click", bubParent, false)
|
||||||
|
$document.body.onclick = legacyParent
|
||||||
|
|
||||||
|
div.addEventListener("click", capTarget, true)
|
||||||
|
div.addEventListener("click", bubTarget, false)
|
||||||
|
div.onclick = legacyTarget
|
||||||
|
|
||||||
|
div.dispatchEvent(e)
|
||||||
|
|
||||||
|
o(capParent.callCount).equals(1)
|
||||||
|
o(capTarget.callCount).equals(1)
|
||||||
|
o(bubTarget.callCount).equals(0)
|
||||||
|
o(legacyTarget.callCount).equals(0)
|
||||||
|
o(bubParent.callCount).equals(0)
|
||||||
|
o(legacyParent.callCount).equals(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
o("e.stopImmediatePropagation 3/6", function () {
|
||||||
|
var capParent = o.spy()
|
||||||
|
var capTarget = o.spy()
|
||||||
|
var bubTarget = o.spy(function(e){e.stopImmediatePropagation()})
|
||||||
|
var legacyTarget = o.spy()
|
||||||
|
var bubParent = o.spy()
|
||||||
|
var legacyParent = o.spy()
|
||||||
|
|
||||||
|
$document.body.addEventListener("click", capParent, true)
|
||||||
|
$document.body.addEventListener("click", bubParent, false)
|
||||||
|
$document.body.onclick = legacyParent
|
||||||
|
|
||||||
|
div.addEventListener("click", capTarget, true)
|
||||||
|
div.addEventListener("click", bubTarget, false)
|
||||||
|
div.onclick = legacyTarget
|
||||||
|
|
||||||
|
div.dispatchEvent(e)
|
||||||
|
|
||||||
|
o(capParent.callCount).equals(1)
|
||||||
|
o(capTarget.callCount).equals(1)
|
||||||
|
o(bubTarget.callCount).equals(1)
|
||||||
|
o(legacyTarget.callCount).equals(0)
|
||||||
|
o(bubParent.callCount).equals(0)
|
||||||
|
o(legacyParent.callCount).equals(0)
|
||||||
|
})
|
||||||
|
o("e.stopImmediatePropagation 4/6", function () {
|
||||||
|
var capParent = o.spy()
|
||||||
|
var capTarget = o.spy()
|
||||||
|
var bubTarget = o.spy()
|
||||||
|
var legacyTarget = o.spy(function(e){e.stopImmediatePropagation()})
|
||||||
|
var bubParent = o.spy()
|
||||||
|
var legacyParent = o.spy()
|
||||||
|
|
||||||
|
$document.body.addEventListener("click", capParent, true)
|
||||||
|
$document.body.addEventListener("click", bubParent, false)
|
||||||
|
$document.body.onclick = legacyParent
|
||||||
|
|
||||||
|
div.addEventListener("click", capTarget, true)
|
||||||
|
div.addEventListener("click", bubTarget, false)
|
||||||
|
div.onclick = legacyTarget
|
||||||
|
|
||||||
|
div.dispatchEvent(e)
|
||||||
|
|
||||||
|
o(capParent.callCount).equals(1)
|
||||||
|
o(capTarget.callCount).equals(1)
|
||||||
|
o(bubTarget.callCount).equals(1)
|
||||||
|
o(legacyTarget.callCount).equals(1)
|
||||||
|
o(bubParent.callCount).equals(0)
|
||||||
|
o(legacyParent.callCount).equals(0)
|
||||||
|
})
|
||||||
|
o("e.stopImmediatePropagation 5/6", function () {
|
||||||
|
var capParent = o.spy()
|
||||||
|
var capTarget = o.spy()
|
||||||
|
var bubTarget = o.spy()
|
||||||
|
var legacyTarget = o.spy()
|
||||||
|
var bubParent = o.spy(function(e){e.stopImmediatePropagation()})
|
||||||
|
var legacyParent = o.spy()
|
||||||
|
|
||||||
|
$document.body.addEventListener("click", capParent, true)
|
||||||
|
$document.body.addEventListener("click", bubParent, false)
|
||||||
|
$document.body.onclick = legacyParent
|
||||||
|
|
||||||
|
div.addEventListener("click", capTarget, true)
|
||||||
|
div.addEventListener("click", bubTarget, false)
|
||||||
|
div.onclick = legacyTarget
|
||||||
|
|
||||||
|
div.dispatchEvent(e)
|
||||||
|
|
||||||
|
o(capParent.callCount).equals(1)
|
||||||
|
o(capTarget.callCount).equals(1)
|
||||||
|
o(bubTarget.callCount).equals(1)
|
||||||
|
o(legacyTarget.callCount).equals(1)
|
||||||
|
o(bubParent.callCount).equals(1)
|
||||||
|
o(legacyParent.callCount).equals(0)
|
||||||
|
})
|
||||||
|
o("e.stopImmediatePropagation 6/6", function () {
|
||||||
|
var capParent = o.spy()
|
||||||
|
var capTarget = o.spy()
|
||||||
|
var legacyTarget = o.spy()
|
||||||
|
var bubTarget = o.spy()
|
||||||
|
var bubParent = o.spy()
|
||||||
|
var legacyParent = o.spy(function(e){e.stopImmediatePropagation()})
|
||||||
|
|
||||||
|
$document.body.addEventListener("click", capParent, true)
|
||||||
|
$document.body.addEventListener("click", bubParent, false)
|
||||||
|
$document.body.onclick = legacyParent
|
||||||
|
|
||||||
|
div.addEventListener("click", capTarget, true)
|
||||||
|
div.addEventListener("click", bubTarget, false)
|
||||||
|
div.onclick = legacyTarget
|
||||||
|
|
||||||
|
div.dispatchEvent(e)
|
||||||
|
|
||||||
|
o(capParent.callCount).equals(1)
|
||||||
|
o(capTarget.callCount).equals(1)
|
||||||
|
o(bubTarget.callCount).equals(1)
|
||||||
|
o(legacyTarget.callCount).equals(1)
|
||||||
|
o(bubParent.callCount).equals(1)
|
||||||
|
o(legacyParent.callCount).equals(1)
|
||||||
|
})
|
||||||
|
o("errors thrown in handlers don't interrupt the chain", function(done) {
|
||||||
|
var errMsg = "The presence of these six errors in the log is expected in non-Node.js environments"
|
||||||
|
var handler = o.spy(function(){throw errMsg})
|
||||||
|
|
||||||
|
$document.body.addEventListener("click", handler, true)
|
||||||
|
$document.body.addEventListener("click", handler, false)
|
||||||
|
$document.body.onclick = handler
|
||||||
|
|
||||||
|
div.addEventListener("click", handler, true)
|
||||||
|
div.addEventListener("click", handler, false)
|
||||||
|
div.onclick = handler
|
||||||
|
|
||||||
|
div.dispatchEvent(e)
|
||||||
|
|
||||||
|
o(handler.callCount).equals(6)
|
||||||
|
|
||||||
|
// Swallow the async errors in NodeJS
|
||||||
|
if (typeof process !== "undefined" && typeof process.once === "function"){
|
||||||
|
process.once("uncaughtException", function(e) {
|
||||||
|
if (e !== errMsg) throw e
|
||||||
|
process.once("uncaughtException", function(e) {
|
||||||
|
if (e !== errMsg) throw e
|
||||||
|
process.once("uncaughtException", function(e) {
|
||||||
|
if (e !== errMsg) throw e
|
||||||
|
process.once("uncaughtException", function(e) {
|
||||||
|
if (e !== errMsg) throw e
|
||||||
|
process.once("uncaughtException", function(e) {
|
||||||
|
if (e !== errMsg) throw e
|
||||||
|
process.once("uncaughtException", function(e) {
|
||||||
|
if (e !== errMsg) throw e
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
done()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
o.spec("attributes", function() {
|
o.spec("attributes", function() {
|
||||||
o.spec("a[href]", function() {
|
o.spec("a[href]", function() {
|
||||||
|
|
@ -731,6 +1257,18 @@ o.spec("domMock", function() {
|
||||||
|
|
||||||
o(input.checked).equals(true)
|
o(input.checked).equals(true)
|
||||||
})
|
})
|
||||||
|
o("doesn't toggle on click when preventDefault() is used", function() {
|
||||||
|
var input = $document.createElement("input")
|
||||||
|
input.setAttribute("type", "checkbox")
|
||||||
|
input.checked = false
|
||||||
|
input.onclick = function(e) {e.preventDefault()}
|
||||||
|
|
||||||
|
var e = $document.createEvent("MouseEvents")
|
||||||
|
e.initEvent("click", true, true)
|
||||||
|
input.dispatchEvent(e)
|
||||||
|
|
||||||
|
o(input.checked).equals(false)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
o.spec("input[value]", function() {
|
o.spec("input[value]", function() {
|
||||||
o("only exists in input elements", function() {
|
o("only exists in input elements", function() {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue