summaryrefslogtreecommitdiffstats
path: root/dom/svg/test/MutationEventChecker.js
diff options
context:
space:
mode:
Diffstat (limited to 'dom/svg/test/MutationEventChecker.js')
-rw-r--r--dom/svg/test/MutationEventChecker.js245
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;
+ }
+ }
+}