diff --git a/tests/resources/testharness.js b/tests/resources/testharness.js --- a/tests/resources/testharness.js +++ b/tests/resources/testharness.js @@ -233,16 +233,33 @@ policies and contribution forms [3]. * assert_inherits(object, property_name, description) * assert that object does not have an own property named property_name * but that property_name is present in the prototype chain for object * * assert_idl_attribute(object, attribute_name, description) * assert that an object that is an instance of some interface has the * attribute attribute_name following the conditions specified by WebIDL * + * assert_interface(interface_name, inherits, description) + * assert that window[interface_name] is an interface named interface_name + * per WebIDL, which inherits from the interface named inherits. If the + * interface doesn't inherit from anything, inherits should be "Object" (or + * you can omit it or leave it blank), except if the interface is ArrayClass, + * in which case it should be "Array". Example: + * assert_interface("NodeList", ""); + * + * assert_operation(object, operation_name, max_args, description) + * assert that object[operation_name] corresponds to a WebIDL operation named + * operation_name, which takes at most max_args arguments. object should be + * the interface prototype object for regular operations, and the interface + * object for static operations -- it should *not* be an object implementing + * the interface. Examples: + * assert_operation(Document.prototype, "createElement", 1); + * INCORRECT: assert_operation(document, "createElement", 1); + * * assert_readonly(object, property_name, description) * assert that property property_name on object is readonly * * assert_throws(code, func, description) * code - a DOMException/RangeException code as a string, e.g. "HIERARCHY_REQUEST_ERR" * func - a function that should throw * * assert that func throws a DOMException or RangeException (as appropriate) @@ -679,16 +696,149 @@ policies and contribution forms [3]. name, description, "property ${p} not found in prototype chain", {p:property_name}); }; } expose(_assert_inherits("assert_inherits"), "assert_inherits"); expose(_assert_inherits("assert_idl_attribute"), "assert_idl_attribute"); + function assert_interface(name, inherits, description) + { + //"For every interface that is not declared with the + //[NoInterfaceObject] extended attribute, a corresponding property must + //exist on the interface’s relevant namespace object. The name of the + //property is the identifier of the interface, and its value is an + //object called the interface object. The property has the attributes { + //[[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true }." + //TODO: Should we test here that the property is actually writable + //etc., or trust getOwnPropertyDescriptor? + assert(window.hasOwnProperty(name), + "assert_interface", description, + "window does not have own property ${name}", {name:name}); + assert(Object.getOwnPropertyDescriptor(window, name).writable, + "assert_interface", description, + "window's property ${name} is not writable", {name:name}); + assert(!Object.getOwnPropertyDescriptor(window, name).enumerable, + "assert_interface", description, + "window's property ${name} is enumerable", {name:name}); + assert(Object.getOwnPropertyDescriptor(window, name).configurable, + "assert_interface", description, + "window's property ${name} is not configurable", {name:name}); + + //"Interface objects are always function objects." + //"If an object is defined to be a function object, then it has + //characteristics as follows:" + //"Its [[Prototype]] internal property is the Function prototype + //object." + //FIXME: The spec is wrong, has to be Object.prototype and not + //Function.prototype. I test for how browsers actually behave, + //assuming the bug will be fixed: + //http://www.w3.org/Bugs/Public/show_bug.cgi?id=14813 + assert(Object.prototype.isPrototypeOf(window[name]), + "assert_interface", description, + "prototype is window's property ${name} is not Object.prototype", {name:name}); + //"Its [[Get]] internal property is set as described in ECMA-262 + //section 15.3.5.4." + //"Its [[Construct]] internal property is set as described in ECMA-262 + //section 13.2.2." + //TODO: I'm not sure how to test these. + //"Its [[HasInstance]] internal property is set as described in + //ECMA-262 section 15.3.5.3, unless otherwise specified." + //This needs to be tested in some other assertion that takes an object + //that's supposed to be an instance of the interface. + + //"The [[Class]] property of the interface object must be the + //identifier of the interface." + assert(String(window[name]) === "[object " + name + "]", + "assert_interface", description, + "String(" + name + ') must be "[object ' + name + ']", got ${result}', {result:String(window[name])}); + + //"The interface object must also have a property named “prototype” + //with attributes { [[Writable]]: false, [[Enumerable]]: false, + //[[Configurable]]: false } whose value is an object called the + //interface prototype object. This object has properties that + //correspond to the attributes and operations defined on the interface, + //and is described in more detail in section 4.5.3 below." + assert(window[name].hasOwnProperty("prototype"), + "assert_interface", description, + 'interface ${name} does not have own property "prototype"', {name:name}); + assert(!Object.getOwnPropertyDescriptor(window[name], "prototype").writable, + "assert_interface", description, + name + ".prototype is writable", {name:name}); + assert(!Object.getOwnPropertyDescriptor(window[name], "prototype").enumerable, + "assert_interface", description, + name + ".prototype is enumerable", {name:name}); + assert(!Object.getOwnPropertyDescriptor(window[name], "prototype").configurable, + "assert_interface", description, + name + ".prototype is configurable", {name:name}); + + //"If the [NoInterfaceObject] extended attribute was not specified on + //the interface, then the interface prototype object must also have a + //property named “constructor” with attributes { [[Writable]]: true, + //[[Enumerable]]: false, [[Configurable]]: true } whose value is a + //reference to the interface object for the interface." + assert(window[name].prototype.hasOwnProperty("constructor"), + "assert_interface", description, + name + '.prototype does not have own property "constructor"'); + assert(Object.getOwnPropertyDescriptor(window[name].prototype, "constructor").writable, + "assert_interface", description, + name + ".prototype.constructor is not writable", {name:name}); + assert(!Object.getOwnPropertyDescriptor(window[name].prototype, "constructor").enumerable, + "assert_interface", description, + name + ".prototype.constructor is enumerable", {name:name}); + assert(Object.getOwnPropertyDescriptor(window[name].prototype, "constructor").configurable, + "assert_interface", description, + name + ".prototype.constructor in not configurable", {name:name}); + assert(window[name].prototype.constructor === window[name], + "assert_interface", description, + name + '.prototype.constructor is not the same object as ' + name); + + if (!inherits) + { + //Assume Object, which is the default for anything except + //ArrayClass + inherits = "Object"; + } + assert(window[inherits].prototype.isPrototypeOf(window[name].prototype), + "assert_interface", description, + 'prototype of ' + name + '.prototype is not ' + inherits + '.prototype'); + } + expose(assert_interface, "assert_interface"); + + function assert_operation(object, name, max_args, description) + { + assert(object.hasOwnProperty(name), + "assert_operation", description, + "object does not have own property ${name}", {name:name}); + //"The property has attributes { [[Writable]]: true, [[Enumerable]]: + //true, [[Configurable]]: true }." + assert(Object.getOwnPropertyDescriptor(object, name).writable, + "assert_operation", description, + "property ${name} is not writable", {name:name}); + assert(Object.getOwnPropertyDescriptor(object, name).enumerable, + "assert_operation", description, + "property ${name} is not enumerable", {name:name}); + assert(Object.getOwnPropertyDescriptor(object, name).configurable, + "assert_operation", description, + "property ${name} is not configurable", {name:name}); + //"The value of the property is a Function object . . ." + assert(typeof object[name] == "function", + "assert_operation", description, + "property ${name} is not a Function object", {name:name}); + //"The value of the Function object’s “length” property is a Number + //determined as follows: . . . Return the maximum argument list length + //of the functions in the entries of S." + assert(object[name].length === max_args, + "assert_operation", description, + "property ${name}'s length property must be ${max_args}, got ${actual}", + {name:name, max_args:max_args, actual:object[name].length}); + } + expose(assert_operation, "assert_operation"); + function assert_readonly(object, property_name, description) { var initial_value = object[property_name]; try { assert(delete object[property_name] === false, "assert_readonly", description, "deleting property ${p} succeeded", {p:property_name}); assert(object[property_name] === initial_value,