/*
 * This file is meant to provide common infrastructure for several consumers.
 * The consumer is expected to define the following things:
 *
 * 1) An verifyPromiseGlobal function which does whatever test the consumer
 *    wants.
 * 2) An isXrayArgumentTest global boolean, because some of these tests act
 *    differenly based on that boolean.
 * 3) A function named getPromise.  This function is given a global object and a
 *    single argument to use for getting the promise.  The getPromise function
 *    is expected to trigger the canonical Promise.resolve for the given global
 *    with the given argument in some way that depends on the test, and return
 *    the result.
 * 4) A subframe (frames[0]) which can be used as a second global for creating
 *    promises.
 */
var label = parent;

function passBasicPromise() {
  var p1 = Promise.resolve();
  verifyPromiseGlobal(p1, window, "Promise.resolve return value 1");
  var p2 = getPromise(window, p1);
  is(p1, p2, "Basic promise should just pass on through");
  return p2;
}

function passPrimitive(global) {
  var p = getPromise(global, 5);
  verifyPromiseGlobal(p, global, "Promise wrapping primitive");
  return p.then(function(arg) {
    is(arg, 5, "Should have the arg we passed in");
  });
}

function passThenable(global) {
  var called = false;
  var thenable = {
    then: function(f) {
      called = true;
      f(7);
    }
  };
  var p = getPromise(global, thenable);
  verifyPromiseGlobal(p, global, "Promise wrapping thenable");
  return p.then(function(arg) {
    ok(called, "Thenable should have been called");
    is(arg, 7, "Should have the arg our thenable passed in");
  });
}

function passWrongPromiseWithMatchingConstructor() {
  var p1 = Promise.resolve();
  verifyPromiseGlobal(p1, window, "Promise.resolve() return value 2");
  p1.constructor = frames[0].Promise;
  var p2 = getPromise(frames[0], p1);
  // The behavior here will depend on whether we're touching frames[0] via Xrays
  // or not.  If we are not, the current compartment while getting our promise
  // will be that of frames[0].  If we are, it will be our window's compartment.
  if (isXrayArgumentTest) {
    isnot(p1, p2, "Should have wrapped the Promise in a new promise, because its constructor is not matching the current-compartment Promise constructor");
    verifyPromiseGlobal(p2, window, "Promise wrapping xrayed promise with therefore non-matching constructor");
  } else {
    is(p1, p2, "Should have left the Promise alone because its constructor matched");
  }
  return p2;
}

function passCorrectPromiseWithMismatchedConstructor() {
  var p1 = Promise.resolve(9);
  verifyPromiseGlobal(p1, window, "Promise.resolve() return value 3");
  p1.constructor = frames[0].Promise;
  var p2 = getPromise(window, p1);
  isnot(p1, p2,
        "Should have wrapped promise in a new promise, since its .constructor was wrong");
  verifyPromiseGlobal(p2, window,
                      "Promise wrapping passed-in promise with mismatched constructor");
  return p2.then(function(arg) {
    is(arg, 9, "Should have propagated along our resolution value");
  });
}

function passPromiseToOtherGlobal() {
  var p1 = Promise.resolve();
  verifyPromiseGlobal(p1, window, "Promise.resolve() return value 4");
  var p2 = getPromise(frames[0], p1);
  // The behavior here will depend on whether we're touching frames[0] via Xrays
  // or not.  If we are not, the current compartment while getting our promise
  // will be that of frames[0].  If we are, it will be our window's compartment.
  if (isXrayArgumentTest) {
    is(p1, p2, "Should have left the Promise alone, because its constructor matches the current compartment's constructor");
  } else {
    isnot(p1, p2, "Should have wrapped promise in a promise from the other global");
    verifyPromiseGlobal(p2, frames[0],
                        "Promise wrapping passed-in basic promise");
  }
  return p2;
}

function passPromiseSubclass() {
  class PromiseSubclass extends Promise {
    constructor(func) {
      super(func);
    }
  }

  var p1 = PromiseSubclass.resolve(11);
  verifyPromiseGlobal(p1, window, "PromiseSubclass.resolve() return value");
  var p2 = getPromise(window, p1);
  isnot(p1, p2,
        "Should have wrapped promise subclass in a new promise");
  verifyPromiseGlobal(p2, window,
                      "Promise wrapping passed-in promise subclass");
  return p2.then(function(arg) {
    is(arg, 11, "Should have propagated along our resolution value from subclass");
  });
}

function runPromiseArgumentTests(finishFunc) {
  Promise.resolve()
    .then(passBasicPromise)
    .then(passPrimitive.bind(undefined, window))
    .then(passPrimitive.bind(undefined, frames[0]))
    .then(passThenable.bind(undefined, window))
    .then(passThenable.bind(undefined, frames[0]))
    .then(passWrongPromiseWithMatchingConstructor)
    .then(passCorrectPromiseWithMismatchedConstructor)
    .then(passPromiseToOtherGlobal)
    .then(passPromiseSubclass)
    .then(finishFunc)
    .catch(function(e) {
      ok(false, `Exception thrown: ${e}@${location.pathname}:${e.lineNumber}:${e.columnNumber}`);
      finishFunc();
    });
}
