michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: "use strict"; michael@0: michael@0: var BaseAssert = require("sdk/test/assert").Assert; michael@0: michael@0: /** michael@0: * Whether or not given property descriptors are equivalent. They are michael@0: * equivalent either if both are marked as "conflict" or "required" property michael@0: * or if all the properties of descriptors are equal. michael@0: * @param {Object} actual michael@0: * @param {Object} expected michael@0: */ michael@0: function equivalentDescriptors(actual, expected) { michael@0: return (actual.conflict && expected.conflict) || michael@0: (actual.required && expected.required) || michael@0: equalDescriptors(actual, expected); michael@0: } michael@0: michael@0: function equalDescriptors(actual, expected) { michael@0: return actual.get === expected.get && michael@0: actual.set === expected.set && michael@0: actual.value === expected.value && michael@0: !!actual.enumerable === !!expected.enumerable && michael@0: !!actual.configurable === !!expected.configurable && michael@0: !!actual.writable === !!expected.writable; michael@0: } michael@0: michael@0: /** michael@0: * Whether or not given `target` array contains all the element michael@0: * from a given `source` array. michael@0: */ michael@0: function containsSet(source, target) { michael@0: return source.some(function(element) { michael@0: return 0 > target.indexOf(element); michael@0: }); michael@0: } michael@0: michael@0: /** michael@0: * Whether or not given two arrays contain all elements from another. michael@0: */ michael@0: function equivalentSets(source, target) { michael@0: return containsSet(source, target) && containsSet(target, source); michael@0: } michael@0: michael@0: /** michael@0: * Finds name of the property from `source` property descriptor map, that michael@0: * is not equivalent of the name named property in the `target` property michael@0: * descriptor map. If not found `null` is returned instead. michael@0: */ michael@0: function findNonEquivalentPropertyName(source, target) { michael@0: var value = null; michael@0: Object.getOwnPropertyNames(source).some(function(key) { michael@0: var areEquivalent = false; michael@0: if (!equivalentDescriptors(source[key], target[key])) { michael@0: value = key; michael@0: areEquivalent = true; michael@0: } michael@0: return areEquivalent; michael@0: }); michael@0: return value; michael@0: } michael@0: michael@0: var AssertDescriptor = { michael@0: equalTraits: { michael@0: value: function equivalentTraits(actual, expected, message) { michael@0: var difference; michael@0: var actualKeys = Object.getOwnPropertyNames(actual); michael@0: var expectedKeys = Object.getOwnPropertyNames(expected); michael@0: michael@0: if (equivalentSets(actualKeys, expectedKeys)) { michael@0: this.fail({ michael@0: operator: "equalTraits", michael@0: message: "Traits define different properties", michael@0: actual: actualKeys.sort().join(","), michael@0: expected: expectedKeys.sort().join(","), michael@0: }); michael@0: } michael@0: else if ((difference = findNonEquivalentPropertyName(actual, expected))) { michael@0: this.fail({ michael@0: operator: "equalTraits", michael@0: message: "Traits define non-equivalent property `" + difference + "`", michael@0: actual: actual[difference], michael@0: expected: expected[difference] michael@0: }); michael@0: } michael@0: else { michael@0: this.pass(message || "Traits are equivalent."); michael@0: } michael@0: } michael@0: } michael@0: }; michael@0: michael@0: exports.Assert = function Assert() { michael@0: return Object.create(BaseAssert.apply(null, arguments), AssertDescriptor); michael@0: };