<!--
  Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
  <title>Test for Promise.all, Promise.race</title>
  <script src="/tests/SimpleTest/SimpleTest.js"></script>
  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none">

</div>
<pre id="test">
<script type="application/javascript"><!--

function promiseUtilitiesDefined() {
  ok(Promise.all, "Promise.all must be defined when Promise is enabled.");
  ok(Promise.race, "Promise.race must be defined when Promise is enabled.");
  runTest();
}

function promiseAllEmptyArray() {
  var p = Promise.all([]);
  ok(p instanceof Promise, "Return value of Promise.all should be a Promise.");
  p.then(function(values) {
    ok(Array.isArray(values), "Resolved value should be an array.");
    is(values.length, 0, "Resolved array length should match iterable's length.");
    runTest();
  }, function() {
    ok(false, "Promise.all shouldn't fail when iterable has no rejected Promises.");
    runTest();
  });
}

function promiseAllArray() {
  var p = Promise.all([1, new Date(), Promise.resolve("firefox")]);
  ok(p instanceof Promise, "Return value of Promise.all should be a Promise.");
  p.then(function(values) {
    ok(Array.isArray(values), "Resolved value should be an array.");
    is(values.length, 3, "Resolved array length should match iterable's length.");
    is(values[0], 1, "Array values should match.");
    ok(values[1] instanceof Date, "Array values should match.");
    is(values[2], "firefox", "Array values should match.");
    runTest();
  }, function() {
    ok(false, "Promise.all shouldn't fail when iterable has no rejected Promises.");
    runTest();
  });
}

function promiseAllIterable() {
  function* promiseGen() {
    var i = 3;
    while (--i) {
      yield Promise.resolve(i);
    }

    yield new Promise(function(resolve) {
      setTimeout(resolve, 10);
    });
  }

  Promise.all(promiseGen()).then(function(values) {
    is(values.length, 3, "Resolved array length should match iterable's length.");
    is(values[0], 2, "Array values should match.");
    is(values[1], 1, "Array values should match.");
    is(values[2], undefined, "Array values should match.");
    runTest();
  }, function(e) {
    ok(false, "Promise.all shouldn't fail when an iterable is passed.");
    runTest();
  });
}

function promiseAllWaitsForAllPromises() {
  var arr = [
    new Promise(function(resolve) {
      setTimeout(resolve.bind(undefined, 1), 50);
    }),
    new Promise(function(resolve) {
      setTimeout(resolve.bind(undefined, 2), 10);
    }),
    new Promise(function(resolve) {
      setTimeout(resolve.bind(undefined, new Promise(function(resolve2) {
        resolve2(3);
      })), 10);
    }),
    new Promise(function(resolve) {
      setTimeout(resolve.bind(undefined, 4), 20);
    }),
  ];

  var p = Promise.all(arr);
  p.then(function(values) {
    ok(Array.isArray(values), "Resolved value should be an array.");
    is(values.length, 4, "Resolved array length should match iterable's length.");
    is(values[0], 1, "Array values should match.");
    is(values[1], 2, "Array values should match.");
    is(values[2], 3, "Array values should match.");
    is(values[3], 4, "Array values should match.");
    runTest();
  }, function() {
    ok(false, "Promise.all shouldn't fail when iterable has no rejected Promises.");
    runTest();
  });
}

function promiseAllRejectFails() {
  var arr = [
    new Promise(function(resolve) {
      setTimeout(resolve.bind(undefined, 1), 50);
    }),
    new Promise(function(resolve, reject) {
      setTimeout(reject.bind(undefined, 2), 10);
    }),
    new Promise(function(resolve) {
      setTimeout(resolve.bind(undefined, 3), 10);
    }),
    new Promise(function(resolve) {
      setTimeout(resolve.bind(undefined, 4), 20);
    }),
  ];

  var p = Promise.all(arr);
  p.then(function(values) {
    ok(false, "Promise.all shouldn't resolve when iterable has rejected Promises.");
    runTest();
  }, function(e) {
    ok(true, "Promise.all should reject when iterable has rejected Promises.");
    is(e, 2, "Rejection value should match.");
    runTest();
  });
}

function promiseAllCastError() {
  var p = Promise.all([Promise.resolve(2), { then() {
    throw new ReferenceError("placeholder for nonexistent function call");
  } }]);
  ok(p instanceof Promise, "Should cast to a Promise.");
  p.then(function(v) {
    ok(false, "promiseAllCastError: should've rejected.");
    runTest();
  }, function(e) {
    ok(e instanceof ReferenceError, "promiseCastThenableError");
    runTest();
  });
}

// Check that the resolved array is enumerable.
function promiseAllEnumerable() {
  var p = Promise.all([1, new Date(), Promise.resolve("firefox")]);
  p.then(function(v) {
    var count = 0;
    for (let key in v) {
      ++count;
      ok(v[key] === 1 || v[key] instanceof Date || v[key] === "firefox",
         "Enumerated properties don't match.");
    }
    is(count, 3, "Resolved array from Promise.all should be enumerable");
    runTest();
  }, function(e) {
    ok(false, "promiseAllEnumerable: should've resolved.");
    runTest();
  });
}

function promiseRaceEmpty() {
  var p = Promise.race([]);
  ok(p instanceof Promise, "Should return a Promise.");
  p.then(function() {
    ok(false, "Should not resolve");
  }, function() {
    ok(false, "Should not reject");
  });
  // Per spec, An empty race never resolves or rejects.
  setTimeout(function() {
    ok(true);
    runTest();
  }, 50);
}

function promiseRaceValuesArray() {
  var p = Promise.race([true, new Date(), 3]);
  ok(p instanceof Promise, "Should return a Promise.");
  p.then(function(winner) {
    is(winner, true, "First value should win.");
    runTest();
  }, function(err) {
    ok(false, "Should not fail " + err + ".");
    runTest();
  });
}

function promiseRacePromiseArray() {
  var arr = [
    new Promise(function(resolve) {
      resolve("first");
    }),
    Promise.resolve("second"),
    new Promise(function() {}),
    new Promise(function(resolve) {
      setTimeout(function() {
        setTimeout(function() {
          resolve("fourth");
        }, 0);
      }, 0);
    }),
  ];

  var p = Promise.race(arr);
  p.then(function(winner) {
    is(winner, "first", "First queued resolution should win the race.");
    runTest();
  });
}

function promiseRaceIterable() {
  function* participants() {
    yield new Promise(function(resolve) {
      setTimeout(resolve, 10, 10);
    });
    yield new Promise(function(resolve) {
      setTimeout(resolve, 20, 20);
    });
  }

  Promise.race(participants()).then(function(winner) {
    is(winner, 10, "Winner should be the one that finished earlier.");
    runTest();
  }, function(e) {
    ok(false, "Promise.race shouldn't throw when an iterable is passed!");
    runTest();
  });
}

function promiseRaceReject() {
  var p = Promise.race([
    Promise.reject(new Error("Fail bad!")),
    new Promise(function(resolve) {
      setTimeout(resolve, 0);
    }),
  ]);

  p.then(function() {
    ok(false, "Should not resolve when winning Promise rejected.");
    runTest();
  }, function(e) {
    ok(true, "Should be rejected");
    ok(e instanceof Error, "Should reject with Error.");
    ok(e.message == "Fail bad!", "Message should match.");
    runTest();
  });
}

function promiseRaceThrow() {
  var p = Promise.race([
    new Promise(function(resolve) {
      throw new ReferenceError("placeholder for nonexistent function call");
    }),
    new Promise(function(resolve) {
      setTimeout(resolve, 0);
    }),
  ]);

  p.then(function() {
    ok(false, "Should not resolve when winning Promise had an error.");
    runTest();
  }, function(e) {
    ok(true, "Should be rejected");
    ok(e instanceof ReferenceError, "Should reject with ReferenceError for function nonExistent().");
    runTest();
  });
}

var tests = [
              promiseUtilitiesDefined,
              promiseAllEmptyArray,
              promiseAllArray,
              promiseAllIterable,
              promiseAllWaitsForAllPromises,
              promiseAllRejectFails,
              promiseAllCastError,
              promiseAllEnumerable,

              promiseRaceEmpty,
              promiseRaceValuesArray,
              promiseRacePromiseArray,
              promiseRaceIterable,
              promiseRaceReject,
              promiseRaceThrow,
            ];

function runTest() {
  if (!tests.length) {
    SimpleTest.finish();
    return;
  }

  var test = tests.shift();
  test();
}

SimpleTest.waitForExplicitFinish();
SimpleTest.requestFlakyTimeout("untriaged");
runTest();
// -->
</script>
</pre>
</body>
</html>
