Bug 1430970 - Fix FxAccounts internal mocking. draft
authorEdouard Oger <eoger@fastmail.com>
Tue, 16 Jan 2018 22:24:17 -0500
changeset 721335 ac1e1eb69c47058a12a5a9d89f143d760a804fa7
parent 713998 5b1fdaa14d35ddf1a638c9422786ede707cacf1f
child 746304 f709859004ccd56c12f89db4fbe9be49414621e8
push id95805
push userbmo:eoger@fastmail.com
push dateWed, 17 Jan 2018 03:27:49 +0000
bugs1430970
milestone59.0a1
Bug 1430970 - Fix FxAccounts internal mocking. MozReview-Commit-ID: 7ATwZTJ4w5K
services/fxaccounts/FxAccounts.jsm
--- a/services/fxaccounts/FxAccounts.jsm
+++ b/services/fxaccounts/FxAccounts.jsm
@@ -284,78 +284,71 @@ AccountState.prototype = {
 };
 
 /* Given an array of scopes, make a string key by normalizing. */
 function getScopeKey(scopeArray) {
   let normalizedScopes = scopeArray.map(item => item.toLowerCase());
   return normalizedScopes.sort().join("|");
 }
 
+function getPropertyDescriptor(obj, prop) {
+  return Object.getOwnPropertyDescriptor(obj, prop) ||
+         getPropertyDescriptor(Object.getPrototypeOf(obj), prop);
+}
+
 /**
  * Copies properties from a given object to another object.
  *
  * @param from (object)
  *        The object we read property descriptors from.
  * @param to (object)
  *        The object that we set property descriptors on.
- * @param options (object) (optional)
- *        {keys: [...]}
- *          Lets the caller pass the names of all properties they want to be
- *          copied. Will copy all properties of the given source object by
- *          default.
- *        {bind: object}
- *          Lets the caller specify the object that will be used to .bind()
- *          all function properties we find to. Will bind to the given target
- *          object by default.
+ * @param thisObj (object)
+ *        The object that will be used to .bind() all function properties we find to.
+ * @param keys ([...])
+ *        The names of all properties to be copied.
  */
-function copyObjectProperties(from, to, opts = {}) {
-  let keys = (opts && opts.keys) || Object.keys(from);
-  let thisArg = (opts && opts.bind) || to;
-
+function copyObjectProperties(from, to, thisObj, keys) {
   for (let prop of keys) {
-    let desc = Object.getOwnPropertyDescriptor(from, prop);
+    // Look for the prop in the prototype chain.
+    let desc = getPropertyDescriptor(from, prop);
 
     if (typeof(desc.value) == "function") {
-      desc.value = desc.value.bind(thisArg);
+      desc.value = desc.value.bind(thisObj);
     }
 
     if (desc.get) {
-      desc.get = desc.get.bind(thisArg);
+      desc.get = desc.get.bind(thisObj);
     }
 
     if (desc.set) {
-      desc.set = desc.set.bind(thisArg);
+      desc.set = desc.set.bind(thisObj);
     }
 
     Object.defineProperty(to, prop, desc);
   }
 }
 
 function urlsafeBase64Encode(key) {
   return ChromeUtils.base64URLEncode(new Uint8Array(key), { pad: false });
 }
 
 /**
  * The public API's constructor.
  */
 this.FxAccounts = function(mockInternal) {
-  let internal = new FxAccountsInternal();
   let external = {};
+  let internal;
 
-  // Copy all public properties to the 'external' object.
-  let prototype = FxAccountsInternal.prototype;
-  let options = {keys: publicProperties, bind: internal};
-  copyObjectProperties(prototype, external, options);
-
-  // Copy all of the mock's properties to the internal object.
-  if (mockInternal && !mockInternal.onlySetInternal) {
-    copyObjectProperties(mockInternal, internal);
-  }
-
-  if (mockInternal) {
+  if (!mockInternal) {
+    internal = new FxAccountsInternal();
+    copyObjectProperties(FxAccountsInternal.prototype, external, internal, publicProperties);
+  } else {
+    internal = Object.create(FxAccountsInternal.prototype, Object.getOwnPropertyDescriptors(mockInternal));
+    copyObjectProperties(internal, external, internal, publicProperties);
     // Exposes the internal object for testing only.
     external.internal = internal;
   }
 
   if (!internal.fxaPushService) {
     // internal.fxaPushService option is used in testing.
     // Otherwise we load the service lazily.
     XPCOMUtils.defineLazyGetter(internal, "fxaPushService", function() {