diff options
Diffstat (limited to 'dom/svg/test/MutationEventChecker.js')
-rw-r--r-- | dom/svg/test/MutationEventChecker.js | 245 |
1 files changed, 245 insertions, 0 deletions
diff --git a/dom/svg/test/MutationEventChecker.js b/dom/svg/test/MutationEventChecker.js new file mode 100644 index 000000000..1394b6ca1 --- /dev/null +++ b/dom/svg/test/MutationEventChecker.js @@ -0,0 +1,245 @@ +// Helper class to check DOM MutationEvents +// +// Usage: +// +// * Create a new event checker: +// var eventChecker = new MutationEventChecker; +// * Set the attribute to watch +// eventChecker.watchAttr(<DOM element>, "<attribute name>"); +// * Set the events to expect (0..n) +// eventChecker.expect("add", "modify"); +// OR +// eventChecker.expect("add modify"); +// OR +// eventChecker.expect(MutationEvent.ADDITION, MutationEvent.MODIFICATION); +// +// An empty string or empty set of arguments is also fine as a way of checking +// that all expected events have been received and indicating no events are +// expected from the following code, e.g. +// +// eventChecker.expect(""); +// // changes that are not expected to generate events +// eventChecker.expect("modify"); +// // change that is expected to generate an event +// ... +// +// * Either finish listening or set the next attribute to watch +// eventChecker.finish(); +// eventChecker.watchAttr(element, "nextAttribute"); +// +// In either case a check is performed that all expected events have been +// received. +// +// * Event checking can be temporarily disabled with ignoreEvents(). The next +// call to expect() will cause it to resume. + +function MutationEventChecker() +{ + this.expectedEvents = []; + + this.watchAttr = function(element, attr) + { + if (this.attr) { + this.finish(); + } + + this.expectedEvents = []; + this.element = element; + this.attr = attr; + this.oldValue = element.getAttribute(attr); + this.giveUp = false; + this.ignore = false; + + this.element.addEventListener('DOMAttrModified', this._listener, false); + } + + this.expect = function() + { + if (this.giveUp) { + return; + } + + ok(this.expectedEvents.length == 0, + "Expecting new events for " + this.attr + + " but the following previously expected events have still not been " + + "received: " + this._stillExpecting()); + if (this.expectedEvents.length != 0) { + this.giveUp = true; + return; + } + + this.ignore = false; + + if (arguments.length == 0 || + arguments.length == 1 && arguments[0] == "") { + return; + } + + // Turn arguments object into an array + var args = Array.prototype.slice.call(arguments); + // Check for whitespace separated keywords + if (args.length == 1 && typeof args[0] === 'string' && + args[0].indexOf(' ') > 0) { + args = args[0].split(' '); + } + // Convert strings to event Ids + this.expectedEvents = args.map(this._argToEventId); + } + + // Temporarily disable event checking + this.ignoreEvents = function() + { + // Check all events have been received + ok(this.giveUp || this.expectedEvents.length == 0, + "Going to ignore subsequent events on " + this.attr + + " attribute, but we're still expecting the following events: " + + this._stillExpecting()); + + this.ignore = true; + } + + this.finish = function() + { + // Check all events have been received + ok(this.giveUp || this.expectedEvents.length == 0, + "Finishing listening to " + this.attr + + " attribute, but we're still expecting the following events: " + + this._stillExpecting()); + + this.element.removeEventListener('DOMAttrModified', this._listener, false); + this.attr = ""; + } + + this._receiveEvent = function(e) + { + if (this.giveUp || this.ignore) { + this.oldValue = e.newValue; + return; + } + + // Make sure we're expecting something at all + if (this.expectedEvents.length == 0) { + ok(false, 'Unexpected ' + this._eventToName(e.attrChange) + + ' event when none expected on ' + this.attr + ' attribute.'); + return; + } + + var expectedEvent = this.expectedEvents.shift(); + + // Make sure we got the event we expected + if (e.attrChange != expectedEvent) { + ok(false, 'Unexpected ' + this._eventToName(e.attrChange) + + ' on ' + this.attr + ' attribute. Expected ' + + this._eventToName(expectedEvent) + ' (followed by: ' + + this._stillExpecting() + ")"); + // If we get events out of sequence, it doesn't make sense to do any + // further testing since we don't really know what to expect + this.giveUp = true; + return; + } + + // Common param checking + is(e.target, this.element, + 'Unexpected node for mutation event on ' + this.attr + ' attribute'); + is(e.attrName, this.attr, 'Unexpected attribute name for mutation event'); + + // Don't bother testing e.relatedNode since Attr nodes are on the way + // out anyway (but then, so are mutation events...) + + // Event-specific checking + if (e.attrChange == MutationEvent.MODIFICATION) { + ok(this.element.hasAttribute(this.attr), + 'Attribute not set after modification'); + is(e.prevValue, this.oldValue, + 'Unexpected old value for modification to ' + this.attr + + ' attribute'); + isnot(e.newValue, this.oldValue, + 'Unexpected new value for modification to ' + this.attr + + ' attribute'); + } else if (e.attrChange == MutationEvent.REMOVAL) { + ok(!this.element.hasAttribute(this.attr), 'Attribute set after removal'); + is(e.prevValue, this.oldValue, + 'Unexpected old value for removal of ' + this.attr + + ' attribute'); + // DOM 3 Events doesn't say what value newValue will be for a removal + // event but generally empty strings are used for other events when an + // attribute isn't relevant + ok(e.newValue === "", + 'Unexpected new value for removal of ' + this.attr + + ' attribute'); + } else if (e.attrChange == MutationEvent.ADDITION) { + ok(this.element.hasAttribute(this.attr), + 'Attribute not set after addition'); + // DOM 3 Events doesn't say what value prevValue will be for an addition + // event but generally empty strings are used for other events when an + // attribute isn't relevant + ok(e.prevValue === "", + 'Unexpected old value for addition of ' + this.attr + + ' attribute'); + ok(typeof(e.newValue) == 'string' && e.newValue !== "", + 'Unexpected new value for addition of ' + this.attr + + ' attribute'); + } else { + ok(false, 'Unexpected mutation event type: ' + e.attrChange); + this.giveUp = true; + } + this.oldValue = e.newValue; + } + this._listener = this._receiveEvent.bind(this); + + this._stillExpecting = function() + { + if (this.expectedEvents.length == 0) { + return "(nothing)"; + } + var eventNames = []; + for (var i=0; i < this.expectedEvents.length; i++) { + eventNames.push(this._eventToName(this.expectedEvents[i])); + } + return eventNames.join(", "); + } + + this._eventToName = function(evtId) + { + switch (evtId) + { + case MutationEvent.MODIFICATION: + return "modification"; + case MutationEvent.ADDITION: + return "addition"; + case MutationEvent.REMOVAL: + return "removal"; + } + } + + this._argToEventId = function(arg) + { + if (typeof arg === 'number') + return arg; + + if (typeof arg !== 'string') { + ok(false, "Unexpected event type: " + arg); + return 0; + } + + switch (arg.toLowerCase()) + { + case "mod": + case "modify": + case "modification": + return MutationEvent.MODIFICATION; + + case "add": + case "addition": + return MutationEvent.ADDITION; + + case "removal": + case "remove": + return MutationEvent.REMOVAL; + + default: + ok(false, "Unexpected event name: " + arg); + return 0; + } + } +} |