text:
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The SFC licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

goog.require('goog.array');
goog.require('goog.string');
goog.require('goog.testing.jsunit');
goog.require('goog.userAgent');
goog.require('webdriver.promise');
goog.require('webdriver.stacktrace.Snapshot');
goog.require('webdriver.stacktrace');
goog.require('webdriver.test.testutil');

// Aliases for readability.
var StubError = webdriver.test.testutil.StubError,
    throwStubError = webdriver.test.testutil.throwStubError,
    assertIsStubError = webdriver.test.testutil.assertIsStubError,
    assertingMessages = webdriver.test.testutil.assertingMessages,
    callbackHelper = webdriver.test.testutil.callbackHelper,
    callbackPair = webdriver.test.testutil.callbackPair;

var flow, flowHistory, uncaughtExceptions;

function shouldRunTests() {
  return !goog.userAgent.IE || goog.userAgent.isVersionOrHigher(10);
}

function setUp() {
  webdriver.promise.LONG_STACK_TRACES = false;
  flow = new webdriver.promise.ControlFlow();
  webdriver.promise.setDefaultFlow(flow);
  webdriver.test.testutil.messages = [];
  flowHistory = [];

  uncaughtExceptions = [];
  flow.on(webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION,
          onUncaughtException);
}

function tearDown() {
  flow.removeAllListeners(
      webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION);
  assertArrayEquals('There were uncaught exceptions', [], uncaughtExceptions);
  flow.reset();
  webdriver.promise.LONG_STACK_TRACES = false;
}

function onUncaughtException(e) {
  uncaughtExceptions.push(e);
}

function waitForAbort(opt_flow) {
  var theFlow = opt_flow || flow;
  theFlow.removeAllListeners(
      webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION);
  return new goog.Promise(function(fulfill, reject) {
    theFlow.once(webdriver.promise.ControlFlow.EventType.IDLE, function() {
      reject(Error('expected flow to report an unhandled error'));
    });
    theFlow.once(
        webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION,
        fulfill);
  });
}

function waitForIdle(opt_flow) {
  var theFlow = opt_flow || flow;
  return new goog.Promise(function(fulfill, reject) {
    theFlow.once(webdriver.promise.ControlFlow.EventType.IDLE, fulfill);
    theFlow.once(
        webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, reject);
  });
}

function timeout(ms) {
  return new goog.Promise(function(fulfill) {
    setTimeout(fulfill, ms);
  });
}

function schedule(msg, opt_return) {
  return scheduleAction(msg, function() {
    return opt_return;
  });
}

/**
 * @param {string} value The value to push.
 * @param {webdriver.promise.Promise=} opt_taskPromise Promise to return from
 *     the task.
 * @return {!webdriver.promise.Promise} The result.
 */
function schedulePush(value, opt_taskPromise) {
  return scheduleAction(value, function() {
    webdriver.test.testutil.messages.push(value);
    return opt_taskPromise;
  });
}

/**
 * @param {string} msg Debug message.
 * @param {!Function} actionFn The function.
 * @return {!webdriver.promise.Promise} The function result.
 */
function scheduleAction(msg, actionFn) {
  return webdriver.promise.controlFlow().execute(function() {
    flowHistory.push(msg);
    return actionFn();
  }, msg);
}

/**
 * @param {!Function} condition The condition function.
 * @param {number} timeout The timeout.
 * @param {string=} opt_message Optional message.
 * @return {!webdriver.promise.Promise} The wait result.
 */
function scheduleWait(condition, timeout, opt_message) {
  var msg = opt_message || '';
  // It's not possible to hook into when the wait itself is scheduled, so
  // we record each iteration of the wait loop.
  var count = 0;
  return webdriver.promise.controlFlow().wait(function() {
    flowHistory.push((count++) + ': ' + msg);
    return condition();
  }, timeout, msg);
}

function assertFlowHistory(var_args) {
  var expected = goog.array.slice(arguments, 0);
  assertArrayEquals(expected, flowHistory);
}

/**
 * @param {string=} opt_description A description of the task for debugging.
 * @return {!webdriver.promise.Task_} The new task.
 */
function createTask(opt_description) {
  return new webdriver.promise.Task_(
      webdriver.promise.controlFlow(),
      goog.nullFunction,
      opt_description || '',
      new webdriver.stacktrace.Snapshot());
}

/**
 * @return {!webdriver.promise.Frame_}
 */
function createFrame() {
  return new webdriver.promise.Frame_(webdriver.promise.controlFlow());
}

function testScheduling_aSimpleFunction() {
  schedule('go');
  return waitForIdle().then(function() {
    assertFlowHistory('go');
  });
}

function testScheduling_aSimpleFunctionWithANonPromiseReturnValue() {
  schedule('go', 123).then(function(value) {
    assertEquals(123, value);
  });
  return waitForIdle().then(function() {
    assertFlowHistory('go');
  });
}

function testScheduling_aSimpleSequence() {
  schedule('a');
  schedule('b');
  schedule('c');
  return waitForIdle().then(function() {
    assertFlowHistory('a', 'b', 'c');
  });
}

function testScheduling_invokesCallbacksWhenTaskIsDone() {
  var d = new webdriver.promise.Deferred();
  var called = false;
  var done = schedule('a', d.promise).then(function(value) {
    called = true;
    assertEquals(123, value);
  });
  return timeout(5).then(function() {
    assertFalse(called);
    d.fulfill(123);
    return done;
  }).
  then(waitForIdle).
  then(function() {
    assertFlowHistory('a');
  });
}

function testScheduling_blocksUntilPromiseReturnedByTaskIsResolved() {
  var done = webdriver.promise.defer();
  schedulePush('a', done.promise);
  schedulePush('b');
  setTimeout(function() {
    done.fulfill();
    webdriver.test.testutil.messages.push('c');
  }, 25);
  return waitForIdle().then(assertingMessages('a', 'c', 'b'));
}

function testScheduling_waitsForReturnedPromisesToResolve() {
  var d1 = new webdriver.promise.Deferred();
  var d2 = new webdriver.promise.Deferred();

  var callback;
  schedule('a', d1.promise).then(callback = callbackHelper(function(value) {
    assertEquals('fluffy bunny', value);
  }));

  return timeout(5).then(function() {
    callback.assertNotCalled('d1 not resolved yet');
    d1.fulfill(d2);
    return timeout(5);
  }).then(function() {
    callback.assertNotCalled('d2 not resolved yet');
    d2.fulfill('fluffy bunny');
    return waitForIdle();
  }).then(function() {
    callback.assertCalled('d2 has been resolved');
    assertFlowHistory('a');
  });
}

function testScheduling_executesTasksInAFutureTurnAfterTheyAreScheduled() {
  var count = 0;
  function incr() { count++; }

  scheduleAction('', incr);
  assertEquals(0, count);
  return waitForIdle().then(function() {
    assertEquals(1, count);
  });
}

function testScheduling_executesOneTaskPerTurnOfTheEventLoop() {
  var order = [];
  function go() {
    order.push(order.length / 2);
    goog.async.run(function() {
      order.push('-');
    });
  }

  scheduleAction('', go);
  scheduleAction('', go);
  return waitForIdle().then(function() {
    assertArrayEquals([0, '-', 1, '-'], order);
  })
}

function testScheduling_firstScheduledTaskIsWithinACallback() {
  webdriver.promise.fulfilled().then(function() {
    schedule('a');
    schedule('b');
    schedule('c');
  }).then(function() {
    assertFlowHistory('a', 'b', 'c');
  });
  return waitForIdle();
}

function testScheduling_newTasksAddedWhileWaitingOnTaskReturnedPromise() {
  scheduleAction('a', function() {
    var d = webdriver.promise.defer();
    setTimeout(function() {
      schedule('c');
      d.fulfill();
    }, 10);
    return d.promise;
  });
  schedule('b');
  return waitForIdle().then(function() {
    assertFlowHistory('a', 'c', 'b');
  });
}

function testFraming_callbacksRunInANewFrame() {
  schedule('a').then(function() {
    schedule('c');
  });
  schedule('b');
  return waitForIdle().then(function() {
    assertFlowHistory('a', 'c', 'b');
  });
}

function testFraming_lotsOfNesting() {
  schedule('a').then(function() {
    schedule('c').then(function() {
      schedule('e').then(function() {
        schedule('g');
      });
      schedule('f');
    });
    schedule('d');
  });
  schedule('b');

  return waitForIdle().then(function() {
    assertFlowHistory('a', 'c', 'e', 'g', 'f', 'd', 'b');
  });
}

function testFrame_callbackReturnsPromiseThatDependsOnATask_1() {
  schedule('a').then(function() {
    schedule('b');
    return webdriver.promise.delayed(5).then(function() {
      return schedule('c');
    });
  });
  schedule('d');

  return waitForIdle().then(function() {
    assertFlowHistory('a', 'b', 'c', 'd');
  });
}

function testFrame_callbackReturnsPromiseThatDependsOnATask_2() {
  schedule('a').then(function() {
    schedule('b');
    return webdriver.promise.delayed(5).
        then(function() { return webdriver.promise.delayed(5) }).
        then(function() { return webdriver.promise.delayed(5) }).
        then(function() { return webdriver.promise.delayed(5) }).
        then(function() { return schedule('c'); });
  });
  schedule('d');

  return waitForIdle().then(function() {
    assertFlowHistory('a', 'b', 'c', 'd');
  });
}

function testFraming_eachCallbackWaitsForAllScheduledTasksToComplete() {
  schedule('a').
      then(function() {
        schedule('b');
        schedule('c');
      }).
      then(function() {
        schedule('d');
      });
  schedule('e');

  return waitForIdle().then(function() {
    assertFlowHistory('a', 'b', 'c', 'd', 'e');
  });
}

function testFraming_eachCallbackWaitsForReturnTasksToComplete() {
  schedule('a').
      then(function() {
        schedule('b');
        return schedule('c');
      }).
      then(function() {
        schedule('d');
      });
  schedule('e');

  return waitForIdle().then(function() {
    assertFlowHistory('a', 'b', 'c', 'd', 'e');
  });
}

function testFraming_callbacksOnAResolvedPromiseInsertIntoTheCurrentFlow() {
  webdriver.promise.fulfilled().then(function() {
    schedule('b');
  });
  schedule('a');

  return waitForIdle().then(function() {
    assertFlowHistory('b', 'a');
  });
}

function testFraming_callbacksInterruptTheFlowWhenPromiseIsResolved() {
  schedule('a').then(function() {
    schedule('c');
  })
  schedule('b');

  return waitForIdle().then(function() {
    assertFlowHistory('a', 'c', 'b');
  });
}

function testFraming_allCallbacksInAFrameAreScheduledWhenPromiseIsResolved() {
  var a = schedule('a');
  a.then(function() { schedule('b'); });
  schedule('c');
  a.then(function() { schedule('d'); });
  schedule('e');

  return waitForIdle().then(function() {
    assertFlowHistory('a', 'b', 'd', 'c', 'e');
  });
}

function testFraming_tasksScheduledInInActiveFrameDoNotGetPrecedence() {
  var d = webdriver.promise.fulfilled();
  schedule('a');
  schedule('b');
  d.then(function() { schedule('c'); });

  return waitForIdle().then(function() {
    assertFlowHistory('a', 'b', 'c');
  });
}

function testFraming_tasksScheduledInAFrameGetPrecedence_1() {
  var a = schedule('a');
  schedule('b').then(function() {
    a.then(function() {
      schedule('c');
      schedule('d');
    });
    var e = schedule('e');
    a.then(function() {
      // When this function runs, |e| will not be resolved yet, so |f| and
      // |h| will be resolved first.  After |e| is resolved, |g| will be
      // scheduled in a new frame, resulting in: [j][f, h, i][g], so |g| is
      // expected to execute first.
      schedule('f');
      e.then(function() {
        schedule('g');
      });
      schedule('h');
    });
    schedule('i');
  });
  schedule('j');

  return waitForIdle().then(function() {
    assertFlowHistory('a', 'b', 'c', 'd', 'e', 'g', 'f', 'h', 'i', 'j');
  });
}

function testErrorHandling_thrownErrorsArePassedToTaskErrback() {
  scheduleAction('function that throws', throwStubError).
      then(fail, assertIsStubError);
  return waitForIdle();
}

function testErrorHandling_thrownErrorsPropagateThroughPromiseChain() {
  scheduleAction('function that throws', throwStubError).
      then(fail).
      then(fail, assertIsStubError);
  return waitForIdle();
}

function testErrorHandling_catchesErrorsFromFailedTasksInAFrame() {
  schedule('a').then(function() {
    schedule('b');
    scheduleAction('function that throws', throwStubError);
  }).
  then(fail, assertIsStubError);
  return waitForIdle();
}

function testErrorHandling_abortsIfOnlyTaskReturnsAnUnhandledRejection() {
  scheduleAction('function that returns rejected promise', function() {
    return webdriver.promise.rejected(new StubError);
  });
  return waitForAbort().then(assertIsStubError);
}

function testErrorHandling_abortsIfThereIsAnUnhandledRejection() {
  webdriver.promise.rejected(new StubError);
  schedule('this should not run');
  return waitForAbort().
      then(assertIsStubError).
      then(function() {
        assertFlowHistory(/* none */);
      });
}

function testErrorHandling_abortsSequenceIfATaskFails() {
  schedule('a');
  schedule('b');
  scheduleAction('c', throwStubError);
  schedule('d');  // Should never execute.

  return waitForAbort().
      then(assertIsStubError).
      then(function() {
        assertFlowHistory('a', 'b', 'c');
      });
}

function testErrorHandling_abortsFromUnhandledFramedTaskFailures_1() {
  schedule('outer task').then(function() {
    scheduleAction('inner task', throwStubError);
  });
  schedule('this should not run');
  return waitForAbort().
      then(assertIsStubError).
      then(function() {
        assertFlowHistory('outer task', 'inner task');
      });
}

function testErrorHandling_abortsFromUnhandledFramedTaskFailures_2() {
  schedule('a').then(function() {
    schedule('b').then(function() {
      scheduleAction('c', throwStubError);
      // This should not execute.
      schedule('d');
    });
  });

  return waitForAbort().
      then(assertIsStubError).
      then(function() {
        assertFlowHistory('a', 'b', 'c');
      });
}

function testErrorHandling_abortsWhenErrorBubblesUpFromFullyResolvingAnObject() {
  var callback = callbackHelper(function() {
    return webdriver.promise.rejected('rejected 2');
  });

  scheduleAction('', function() {
    var obj = {'foo': webdriver.promise.rejected(new StubError)};
    return webdriver.promise.fullyResolved(obj).then(callback);
  });

  return waitForAbort().
      then(assertIsStubError).
      then(callback.assertNotCalled);
}

function testErrorHandling_abortsWhenErrorBubblesUpFromFullyResolvingAnObject_withCallback() {
  var callback1 = callbackHelper(function() {
    return webdriver.promise.rejected('rejected 2');
  });
  var callback2 = callbackHelper();

  scheduleAction('', function() {
    var obj = {'foo': webdriver.promise.rejected(new StubError)};
    return webdriver.promise.fullyResolved(obj).then(callback1);
  }).then(callback2);

  return waitForAbort().
      then(assertIsStubError).
      then(callback1.assertNotCalled).
      then(callback2.assertNotCalled);
}

function testErrorHandling_canCatchErrorsFromNestedTasks() {
  var errback;
  schedule('a').
      then(function() {
        return scheduleAction('b', throwStubError);
      }).
      thenCatch(errback = callbackHelper(assertIsStubError));
  return waitForIdle().then(errback.assertCalled);
}

function testErrorHandling_nestedCommandFailuresCanBeCaughtAndSuppressed() {
  var errback;
  schedule('a').then(function() {
    return schedule('b').then(function() {
      return schedule('c').then(function() {
        throw new StubError;
      });
    });
  }).thenCatch(errback = callbackHelper(assertIsStubError));
  schedule('d');
  return waitForIdle().
      then(errback.assertCalled).
      then(function() {
        assertFlowHistory('a', 'b', 'c', 'd');
      });
}

function testErrorHandling_aTaskWithAnUnhandledPromiseRejection() {
  schedule('a');
  scheduleAction('sub-tasks', function() {
    webdriver.promise.rejected(new StubError);
  });
  schedule('should never run');

  return waitForAbort().
      then(assertIsStubError).
      then(function() {
        assertFlowHistory('a', 'sub-tasks');
      });
}

function testErrorHandling_aTaskThatReutrnsARejectedPromise() {
  schedule('a');
  scheduleAction('sub-tasks', function() {
    return webdriver.promise.rejected(new StubError);
  });
  schedule('should never run');

  return waitForAbort().
      then(assertIsStubError).
      then(function() {
        assertFlowHistory('a', 'sub-tasks');
      });
}

function testErrorHandling_discardsSubtasksIfTaskThrows() {
  var pair = callbackPair(null, assertIsStubError);
  scheduleAction('a', function() {
    schedule('b');
    schedule('c');
    throwStubError();
  }).then(pair.callback, pair.errback);
  schedule('d');

  return waitForIdle().
      then(pair.assertErrback).
      then(function() {
        assertFlowHistory('a', 'd');
      });
}

function testErrorHandling_discardsRemainingSubtasksIfASubtaskFails() {
  var pair = callbackPair(null, assertIsStubError);
  scheduleAction('a', function() {
    schedule('b');
    scheduleAction('c', throwStubError);
    schedule('d');
  }).then(pair.callback, pair.errback);
  schedule('e');

  return waitForIdle().
      then(pair.assertErrback).
      then(function() {
        assertFlowHistory('a', 'b', 'c', 'e');
      });
}

function testTryFinally_happyPath() {
  /* Model:
     try {
       doFoo();
       doBar();
     } finally {
       doBaz();
     }
   */
  schedulePush('foo').
      then(goog.partial(schedulePush, 'bar')).
      thenFinally(goog.partial(schedulePush, 'baz'));
  return waitForIdle().then(assertingMessages('foo', 'bar', 'baz'));
}

function testTryFinally_firstTryFails() {
  /* Model:
     try {
       doFoo();
       doBar();
     } finally {
       doBaz();
     }
   */

  scheduleAction('doFoo and throw', function() {
    webdriver.test.testutil.messages.push('foo');
    throw new StubError;
  }).
  then(function() { schedulePush('bar'); }).
  thenFinally(function() { schedulePush('baz'); });

  return waitForAbort().
      then(assertIsStubError).
      then(assertingMessages('foo', 'baz'));
}

function testTryFinally_secondTryFails() {
  /* Model:
     try {
       doFoo();
       doBar();
     } finally {
       doBaz();
     }
   */

  schedulePush('foo').
      then(function() {
        return scheduleAction('doBar and throw', function() {
          webdriver.test.testutil.messages.push('bar');
          throw new StubError;
        });
      }).
      thenFinally(function() {
        return schedulePush('baz');
      });
  return waitForAbort().
      then(assertIsStubError).
      then(assertingMessages('foo', 'bar', 'baz'));
}

function testTaskCallbacksInterruptFlow() {
  schedule('a').then(function() {
    schedule('b');
  });
  schedule('c');
  return waitForIdle().then(function() {
    assertFlowHistory('a', 'b', 'c');
  });
}

function
testTaskCallbacksInterruptFlow_taskDependsOnImmediatelyFulfilledPromise() {
  scheduleAction('a', function() {
    return webdriver.promise.fulfilled();
  }).then(function() {
    schedule('b');
  });
  schedule('c');
  return waitForIdle().then(function() {
    assertFlowHistory('a', 'b', 'c');
  });
}

function testTaskCallbacksInterruptFlow_taskDependsOnPreviouslyFulfilledPromise() {
  var promise = webdriver.promise.fulfilled(123);
  scheduleAction('a', function() {
    return promise;
  }).then(function(value) {
    assertEquals(123, value);
    schedule('b');
  });
  schedule('c');
  return waitForIdle().then(function() {
    assertFlowHistory('a', 'b', 'c');
  });
}

function testTaskCallbacksInterruptFlow_taskDependsOnAsyncPromise() {
  scheduleAction('a', function() {
    return webdriver.promise.delayed(25);
  }).then(function() {
    schedule('b');
  });
  schedule('c');
  return waitForIdle().then(function() {
    assertFlowHistory('a', 'b', 'c');
  });
}

function testPromiseChainedToTaskInterruptFlow() {
  schedule('a').then(function() {
    return webdriver.promise.fulfilled();
  }).then(function() {
    return webdriver.promise.fulfilled();
  }).then(function() {
    schedule('b');
  });
  schedule('c');
  return waitForIdle().then(function() {
    assertFlowHistory('a', 'b', 'c');
  });
}

function testNestedTaskCallbacksInterruptFlowWhenResolved() {
  schedule('a').then(function() {
    schedule('b').then(function() {
      schedule('c');
    });
  });
  schedule('d');
  return waitForIdle().then(function() {
    assertFlowHistory('a', 'b', 'c', 'd');
  });
}

function testDelayedNesting_1() {
  var a = schedule('a');
  schedule('b').then(function() {
    a.then(function() { schedule('c'); });
    schedule('d');
  });
  schedule('e');

  return waitForIdle().then(function() {
    assertFlowHistory('a', 'b', 'c', 'd', 'e');
  });
}

function testDelayedNesting_2() {
  var a = schedule('a');
  schedule('b').then(function() {
    a.then(function() { schedule('c'); });
    schedule('d');
    a.then(function() { schedule('e'); });
  });
  schedule('f');

  return waitForIdle().then(function() {
    assertFlowHistory('a', 'b', 'c', 'd', 'e', 'f');
  });
}

function testDelayedNesting_3() {
  var a = schedule('a');
  schedule('b').then(function() {
    a.then(function() { schedule('c'); });
    a.then(function() { schedule('d'); });
  });
  schedule('e');

  return waitForIdle().then(function() {
    assertFlowHistory('a', 'b', 'c', 'd', 'e');
  });
}

function testDelayedNesting_4() {
  var a = schedule('a');
  schedule('b').then(function() {
    a.then(function() { schedule('c'); }).then(function() {
      schedule('d');
    });
    a.then(function() { schedule('e'); });
  });
  schedule('f');

  return waitForIdle().then(function() {
    assertFlowHistory('a', 'b', 'c', 'd', 'e', 'f');
  });
}

function testDelayedNesting_5() {
  var a = schedule('a');
  schedule('b').then(function() {
    var c;
    a.then(function() { c = schedule('c'); }).then(function() {
      schedule('d');
      a.then(function() { schedule('e'); });
      c.then(function() { schedule('f'); });
      schedule('g');
    });
    a.then(function() { schedule('h'); });
  });
  schedule('i');

  return waitForIdle().then(function() {
    assertFlowHistory('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i');
  });
}

function testWaiting_onAConditionThatIsAlwaysTrue() {
  scheduleWait(function() { return true;}, 0, 'waiting on true');
  return waitForIdle().then(function() {
    assertFlowHistory('0: waiting on true');
  });
}

function testWaiting_aSimpleCountingCondition() {
  var count = 0;
  scheduleWait(function() {
    return ++count == 3;
  }, 200, 'counting to 3');

  return waitForIdle().then(function() {
    assertEquals(3, count);
  });
}

function testWaiting_aConditionThatReturnsAPromise() {
  var d = new webdriver.promise.Deferred();
  var count = 0;

  scheduleWait(function() {
    count += 1;
    return d.promise;
  }, 0, 'waiting for promise');

  return timeout(50).then(function() {
    assertEquals(1, count);
    d.fulfill(123);
    return waitForIdle();
  });
}

function testWaiting_aConditionThatReturnsAPromise_2() {
  var count = 0;
  scheduleWait(function() {
    return webdriver.promise.fulfilled(++count == 3);
  }, 200, 'waiting for promise');

  return waitForIdle().then(function() {
    assertEquals(3, count);
  });
}

function testWaiting_aConditionThatReturnsATaskResult() {
  var count = 0;
  scheduleWait(function() {
    return scheduleAction('increment count', function() {
      return ++count == 3;
    });
  }, 200, 'counting to 3');
  schedule('post wait');

  return waitForIdle().then(function() {
    assertEquals(3, count);
    assertFlowHistory(
        '0: counting to 3', 'increment count',
        '1: counting to 3', 'increment count',
        '2: counting to 3', 'increment count',
        'post wait');
  });
}

function testWaiting_conditionContainsASubtask() {
  var count = 0;
  scheduleWait(function() {
    schedule('sub task');
    return ++count == 3;
  }, 200, 'counting to 3');
  schedule('post wait');

  return waitForIdle().then(function() {
    assertEquals(3, count);
    assertFlowHistory(
        '0: counting to 3', 'sub task',
        '1: counting to 3', 'sub task',
        '2: counting to 3', 'sub task',
        'post wait');
  });
}

function testWaiting_cancelsWaitIfScheduledTaskFails() {
  var pair = callbackPair(null, assertIsStubError);
  scheduleWait(function() {
    scheduleAction('boom', throwStubError);
    schedule('this should not run');
    return true;
  }, 200, 'waiting to go boom').then(pair.callback, pair.errback);
  schedule('post wait');

  return waitForIdle().
      then(pair.assertErrback).
      then(function() {
        assertFlowHistory(
            '0: waiting to go boom', 'boom',
            'post wait');
      });
}

function testWaiting_failsIfConditionThrows() {
  var callbacks = callbackPair(null, assertIsStubError);
  scheduleWait(throwStubError, 0, 'goes boom').
      then(callbacks.callback, callbacks.errback);
  schedule('post wait');

  return waitForIdle().
      then(callbacks.assertErrback).
      then(function() {
        assertFlowHistory('0: goes boom', 'post wait');
      });
}

function testWaiting_failsIfConditionReturnsARejectedPromise() {
  var callbacks = callbackPair(null, assertIsStubError);
  scheduleWait(function() {
    return webdriver.promise.rejected(new StubError);
  }, 0, 'goes boom').then(callbacks.callback, callbacks.errback);
  schedule('post wait');

  return waitForIdle().
      then(callbacks.assertErrback).
      then(function() {
        assertFlowHistory('0: goes boom', 'post wait');
      });
}

function testWaiting_failsIfConditionHasUnhandledRejection() {
  var callbacks = callbackPair(null, assertIsStubError);
  scheduleWait(function() {
    webdriver.promise.controlFlow().execute(throwStubError);
  }, 0, 'goes boom').then(callbacks.callback, callbacks.errback);
  schedule('post wait');

  return waitForIdle().
      then(callbacks.assertErrback).
      then(function() {
        assertFlowHistory('0: goes boom', 'post wait');
      });
}

function testWaiting_failsIfConditionHasAFailedSubtask() {
  var callbacks = callbackPair(null, assertIsStubError);
  var count = 0;
  scheduleWait(function() {
    scheduleAction('maybe throw', function() {
      if (++count == 2) {
        throw new StubError;
      }
    });
  }, 200, 'waiting').then(callbacks.callback, callbacks.errback);
  schedule('post wait');

  return waitForIdle().then(function() {
    assertEquals(2, count);
    assertFlowHistory(
        '0: waiting', 'maybe throw',
        '1: waiting', 'maybe throw',
        'post wait');
  });
}

function testWaiting_pollingLoopWaitsForAllScheduledTasksInCondition() {
  var count = 0;
  scheduleWait(function() {
    scheduleAction('increment count', function() { ++count; });
    return count >= 3;
  }, 350, 'counting to 3');
  schedule('post wait');

  return waitForIdle().then(function() {
    assertEquals(4, count);
    assertFlowHistory(
        '0: counting to 3', 'increment count',
        '1: counting to 3', 'increment count',
        '2: counting to 3', 'increment count',
        '3: counting to 3', 'increment count',
        'post wait');
  });
}

function testWaiting_waitsForeverOnAZeroTimeout() {
  var done = false;
  setTimeout(function() {
    done = true;
  }, 500);
  var waitResult = scheduleWait(function() {
    return done;
  }, 0);

  return timeout(250).then(function() {
    assertFalse(done);
    return timeout(300);
  }).then(function() {
    assertTrue(done);
    return waitResult;
  });
}

function testWaiting_waitsForeverIfTimeoutOmitted() {
  var done = false;
  setTimeout(function() {
    done = true;
  }, 500);
  var waitResult = scheduleWait(function() {
    return done;
  });

  return timeout(250).then(function() {
    assertFalse(done);
    return timeout(300);
  }).then(function() {
    assertTrue(done);
    return waitResult;
  });
}

function testWaiting_timesOut_nonZeroTimeout() {
  var count = 0;
  scheduleWait(function() {
    count += 1;
    var ms = count === 2 ? 65 : 5;
    var start = goog.now();
    return webdriver.promise.delayed(ms).then(function() {
      return false;
    });
  }, 60, 'counting to 3');
  return waitForAbort().then(function(e) {
    switch (count) {
      case 1:
        assertFlowHistory('0: counting to 3');
        break;
      case 2:
        assertFlowHistory('0: counting to 3', '1: counting to 3');
        break;
      default:
        fail('unexpected polling count: ' + count);
    }
    assertRegExp(/^counting to 3\nWait timed out after \d+ms$/, e.message);
  });
}

function testWaiting_shouldFailIfConditionReturnsARejectedPromise() {
  var count = 0;
  scheduleWait(function() {
    return webdriver.promise.rejected(new StubError);
  }, 100, 'counting to 3');
  return waitForAbort().then(assertIsStubError);
}

function testWaiting_scheduleWithIntermittentWaits() {
  schedule('a');
  scheduleWait(function() { return true; }, 0, 'wait 1');
  schedule('b');
  scheduleWait(function() { return true; }, 0, 'wait 2');
  schedule('c');
  scheduleWait(function() { return true; }, 0, 'wait 3');

  return waitForIdle().then(function() {
    assertFlowHistory('a', '0: wait 1', 'b', '0: wait 2', 'c', '0: wait 3');
  });
}

function testWaiting_scheduleWithIntermittentAndNestedWaits() {
  schedule('a');
  scheduleWait(function() { return true; }, 0, 'wait 1').
      then(function() {
        schedule('d');
        scheduleWait(function() { return true; }, 0, 'wait 2');
        schedule('e');
      });
  schedule('b');
  scheduleWait(function() { return true; }, 0, 'wait 3');
  schedule('c');
  scheduleWait(function() { return true; }, 0, 'wait 4');

  return waitForIdle().then(function() {
    assertFlowHistory(
        'a', '0: wait 1', 'd', '0: wait 2', 'e', 'b', '0: wait 3', 'c',
        '0: wait 4');
  });
}

function testWait_requiresConditionToBeAPromiseOrFunction() {
  assertThrows(function() {
    flow.wait(1234, 0);
  });
  flow.wait(function() { return true;}, 0);
  flow.wait(webdriver.promise.fulfilled(), 0);
  return waitForIdle();
}

function testWait_promiseThatDoesNotResolveBeforeTimeout() {
  var d = webdriver.promise.defer();
  flow.wait(d.promise, 5).then(fail, function(e) {
    assertRegExp(/Timed out waiting for promise to resolve after \d+ms/,
        e.message);
  });
  return waitForIdle().then(function() {
    assertTrue('Promise should not be cancelled', d.promise.isPending());
  });
}

function testWait_unboundedWaitOnPromiseResolution() {
  var messages = [];
  var d = webdriver.promise.defer();
  var waitResult = flow.wait(d.promise).then(function(value) {
    messages.push('b');
    assertEquals(1234, value);
  });
  setTimeout(function() {
    messages.push('a');
  }, 5);

  webdriver.promise.delayed(10).then(function() {
    assertArrayEquals(['a'], messages);
    assertTrue(waitResult.isPending());
    d.fulfill(1234);
    return waitResult;
  }).then(function(value) {
    assertArrayEquals(['a', 'b'], messages);
  });

  return waitForIdle();
}

function testSubtasks() {
  schedule('a');
  scheduleAction('sub-tasks', function() {
    schedule('c');
    schedule('d');
  });
  schedule('b');

  return waitForIdle().then(function() {
    assertFlowHistory('a', 'sub-tasks', 'c', 'd', 'b');
  });
}

function testSubtasks_nesting() {
  schedule('a');
  scheduleAction('sub-tasks', function() {
    schedule('b');
    scheduleAction('sub-sub-tasks', function() {
      schedule('c');
      schedule('d');
    });
    schedule('e');
  });
  schedule('f');

  return waitForIdle().then(function() {
    assertFlowHistory(
        'a', 'sub-tasks', 'b', 'sub-sub-tasks', 'c', 'd', 'e', 'f');
  });
}

function testSubtasks_taskReturnsSubTaskResult_1() {
  schedule('a');
  scheduleAction('sub-tasks', function() {
    return schedule('c');
  });
  schedule('b');

  return waitForIdle().then(function() {
    assertFlowHistory('a', 'sub-tasks', 'c', 'b');
  });
}

function testSubtasks_taskReturnsSubTaskResult_2() {
  var callback;
  schedule('a');
  schedule('sub-tasks', webdriver.promise.fulfilled(123)).
      then(callback = callbackHelper(function(value) {
        assertEquals(123, value);
      }));
  schedule('b');

  return waitForIdle().then(function() {
    assertFlowHistory('a', 'sub-tasks','b');
    callback.assertCalled();
  });
}

function testSubtasks_taskReturnsPromiseThatDependsOnSubtask_1() {
  scheduleAction('a', function() {
    return webdriver.promise.delayed(10).then(function() {
      schedule('b');
    });
  });
  schedule('c');
  return waitForIdle().then(function() {
    assertFlowHistory('a', 'b', 'c');
  });
}

function testSubtasks_taskReturnsPromiseThatDependsOnSubtask_2() {
  scheduleAction('a', function() {
    return webdriver.promise.fulfilled().then(function() {
      schedule('b');
    });
  });
  schedule('c');
  return waitForIdle().then(function() {
    assertFlowHistory('a', 'b', 'c');
  });
}

function testSubtasks_taskReturnsPromiseThatDependsOnSubtask_3() {
  scheduleAction('a', function() {
    return webdriver.promise.delayed(10).then(function() {
      return schedule('b');
    });
  });
  schedule('c');
  return waitForIdle().then(function() {
    assertFlowHistory('a', 'b', 'c');
  });
}

function testSubtasks_taskReturnsPromiseThatDependsOnSubtask_4() {
  scheduleAction('a', function() {
    return webdriver.promise.delayed(5).then(function() {
      return webdriver.promise.delayed(5).then(function() {
        return schedule('b');
      });
    });
  });
  schedule('c');
  return waitForIdle().then(function() {
    assertFlowHistory('a', 'b', 'c');
  });
}

function testSubtasks_taskReturnsPromiseThatDependsOnSubtask_5() {
  scheduleAction('a', function() {
    return webdriver.promise.delayed(5).then(function() {
      return webdriver.promise.delayed(5).then(function() {
        return webdriver.promise.delayed(5).then(function() {
          return webdriver.promise.delayed(5).then(function() {
            return schedule('b');
          });
        });
      });
    });
  });
  schedule('c');
  return waitForIdle().then(function() {
    assertFlowHistory('a', 'b', 'c');
  });
}

function testSubtasks_taskReturnsPromiseThatDependsOnSubtask_6() {
  scheduleAction('a', function() {
    return webdriver.promise.delayed(5).
        then(function() { return webdriver.promise.delayed(5) }).
        then(function() { return webdriver.promise.delayed(5) }).
        then(function() { return webdriver.promise.delayed(5) }).
        then(function() { return schedule('b'); });
  });
  schedule('c');
  return waitForIdle().then(function() {
    assertFlowHistory('a', 'b', 'c');
  });
}

function testSubtasks_subTaskFails_1() {
  schedule('a');
  scheduleAction('sub-tasks', function() {
    scheduleAction('sub-task that fails', throwStubError);
  });
  schedule('should never execute');

  return waitForAbort().
      then(assertIsStubError).
      then(function() {
        assertFlowHistory('a', 'sub-tasks', 'sub-task that fails');
      });
}

function testSubtasks_subTaskFails_2() {
  schedule('a');
  scheduleAction('sub-tasks', function() {
    return webdriver.promise.rejected(new StubError);
  });
  schedule('should never execute');

  return waitForAbort().
      then(assertIsStubError).
      then(function() {
        assertFlowHistory('a', 'sub-tasks');
      });
}

function testSubtasks_subTaskFails_3() {
  var callbacks = callbackPair(null, assertIsStubError);

  schedule('a');
  scheduleAction('sub-tasks', function() {
    return webdriver.promise.rejected(new StubError);
  }).then(callbacks.callback, callbacks.errback);
  schedule('b');

  return waitForIdle().
      then(function() {
        assertFlowHistory('a', 'sub-tasks', 'b');
        callbacks.assertErrback();
      });
}

function testEventLoopWaitsOnPendingPromiseRejections_oneRejection() {
  var d = new webdriver.promise.Deferred;
  scheduleAction('one', function() {
    return d.promise;
  });
  scheduleAction('two', goog.nullFunction);

  return timeout(50).then(function() {
    assertFlowHistory('one');
    d.reject(new StubError);
    return waitForAbort();
  }).
  then(assertIsStubError).
  then(function() {
    assertFlowHistory('one');
  });
}

function testEventLoopWaitsOnPendingPromiseRejections_multipleRejections() {
  var once = Error('once');
  var twice = Error('twice');
  var seen = [];

  scheduleAction('one', function() {
    webdriver.promise.rejected(once);
    webdriver.promise.rejected(twice);
  });
  var twoResult = scheduleAction('two', goog.nullFunction);

  flow.removeAllListeners(
      webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION);
  return new goog.Promise(function(fulfill, reject) {
    setTimeout(function() {
      reject(Error('Should have reported the two errors by now: ' + seen));
    }, 500);
    flow.on(
        webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION,
        function(e) {
          seen.push(e);
          if (seen.length === 2) {
            fulfill();
          }
        });
  }).then(function() {
    seen.sort();
    assertArrayEquals([once, twice], seen);
    assertFlowHistory('one');
    assertFalse('Did not cancel the second task', twoResult.isPending());
  });
}

function testCancelsPromiseReturnedByCallbackIfFrameFails_promiseCallback() {
  var chainPair = callbackPair(null, assertIsStubError);
  var deferredPair = callbackPair(null, function(e) {
    assertEquals('callback result should be cancelled',
        'CancellationError: StubError',
        e.toString());
  });

  var d = new webdriver.promise.Deferred();
  d.then(deferredPair.callback, deferredPair.errback);

  webdriver.promise.fulfilled().
      then(function() {
        scheduleAction('boom', throwStubError);
        schedule('this should not run');
        return d.promise;
      }).
      then(chainPair.callback, chainPair.errback);

  return waitForIdle().then(function() {
    assertFlowHistory('boom');
    chainPair.assertErrback('chain errback not invoked');
    deferredPair.assertErrback('deferred errback not invoked');
  });
}

function testCancelsPromiseReturnedByCallbackIfFrameFails_taskCallback() {
  var chainPair = callbackPair(null, assertIsStubError);
  var deferredPair = callbackPair(null, function(e) {
    assertEquals('callback result should be cancelled',
        'CancellationError: StubError',
        e.toString());
  });

  var d = new webdriver.promise.Deferred();
  d.then(deferredPair.callback, deferredPair.errback);

  schedule('a').
      then(function() {
        scheduleAction('boom', throwStubError);
        schedule('this should not run');
        return d.promise;
      }).
      then(chainPair.callback, chainPair.errback);

  return waitForIdle().then(function() {
    assertFlowHistory('a', 'boom');
    chainPair.assertErrback('chain errback not invoked');
    deferredPair.assertErrback('deferred errback not invoked');
  });
}

function testMaintainsOrderInCallbacksWhenATaskReturnsAPromise() {
  schedule('__start__', webdriver.promise.fulfilled()).
      then(function() {
        webdriver.test.testutil.messages.push('a');
        schedulePush('b');
        webdriver.test.testutil.messages.push('c');
      }).
      then(function() {
        webdriver.test.testutil.messages.push('d');
      });
  schedulePush('e');

  return waitForIdle().then(function() {
    assertFlowHistory('__start__', 'b', 'e');
    webdriver.test.testutil.assertMessages('a', 'c', 'b', 'd', 'e');
  });
}

function assertFlowIs(flow) {
  assertEquals(flow, webdriver.promise.controlFlow());
}

function testOwningFlowIsActivatedForExecutingTasks() {
  var defaultFlow = webdriver.promise.controlFlow();
  var order = [];

  webdriver.promise.createFlow(function(flow) {
    assertFlowIs(flow);
    order.push(0);

    defaultFlow.execute(function() {
      assertFlowIs(defaultFlow);
      order.push(1);
    });
  });

  return waitForIdle().then(function() {
    assertFlowIs(defaultFlow);
    assertArrayEquals([0, 1], order);
  });
}

function testCreateFlowReturnsPromisePairedWithCreatedFlow() {
  return new goog.Promise(function(fulfill, reject) {
    var newFlow;
    webdriver.promise.createFlow(function(flow) {
      newFlow = flow;
      assertFlowIs(newFlow);
    }).then(function() {
      assertFlowIs(newFlow);
      waitForIdle(newFlow).then(fulfill, reject);
    });
  });
}

function testDeferredFactoriesCreateForActiveFlow_defaultFlow() {
  var e = Error();
  var defaultFlow = webdriver.promise.controlFlow();
  webdriver.promise.fulfilled().then(function() {
    assertFlowIs(defaultFlow);
  });
  webdriver.promise.rejected(e).then(null, function(err) {
    assertEquals(e, err);
    assertFlowIs(defaultFlow);
  });
  webdriver.promise.defer().then(function() {
    assertFlowIs(defaultFlow);
  });

  return waitForIdle();
}

function testDeferredFactoriesCreateForActiveFlow_newFlow() {
  var e = Error();
  var newFlow = new webdriver.promise.ControlFlow;
  newFlow.execute(function() {
    webdriver.promise.fulfilled().then(function() {
      assertFlowIs(newFlow);
    });

    webdriver.promise.rejected(e).then(null, function(err) {
      assertEquals(e, err);
      assertFlowIs(newFlow);
    });

    webdriver.promise.defer().then(function() {
      assertFlowIs(newFlow);
    });
  }).then(function() {
    assertFlowIs(newFlow);
  });

  return waitForIdle(newFlow);
}

function testFlowsSynchronizeWithThemselvesNotEachOther() {
  var defaultFlow = webdriver.promise.controlFlow();
  schedulePush('a', 'a');
  webdriver.promise.controlFlow().timeout(250);
  schedulePush('b', 'b');

  webdriver.promise.createFlow(function() {
    schedulePush('c', 'c');
    schedulePush('d', 'd');
  });

  return waitForIdle().then(function() {
    webdriver.test.testutil.assertMessages('a', 'c', 'd', 'b');
  });
}

function testUnhandledErrorsAreReportedToTheOwningFlow() {
  var error1 = Error('e1');
  var error2 = Error('e2');

  var defaultFlow = webdriver.promise.controlFlow();
  defaultFlow.removeAllListeners('uncaughtException');

  var flow1Error = goog.Promise.withResolver();
  flow1Error.promise.then(function(value) {
    assertEquals(error2, value);
  });

  var flow2Error = goog.Promise.withResolver();
  flow2Error.promise.then(function(value) {
    assertEquals(error1, value);
  });

  webdriver.promise.createFlow(function(flow) {
    flow.once('uncaughtException', flow2Error.resolve);
    webdriver.promise.rejected(error1);

    defaultFlow.once('uncaughtException', flow1Error.resolve);
    defaultFlow.execute(function() {
      webdriver.promise.rejected(error2);
    });
  });

  return goog.Promise.all([flow1Error.promise, flow2Error.promise]);
}

function testCanSynchronizeFlowsByReturningPromiseFromOneToAnother() {
  var flow1 = new webdriver.promise.ControlFlow;
  var flow1Done = goog.Promise.withResolver();
  flow1.once('idle', flow1Done.resolve);
  flow1.once('uncaughtException', flow1Done.reject);

  var flow2 = new webdriver.promise.ControlFlow;
  var flow2Done = goog.Promise.withResolver();
  flow2.once('idle', flow2Done.resolve);
  flow2.once('uncaughtException', flow2Done.reject);

  flow1.execute(function() {
    schedulePush('a', 'a');
    return webdriver.promise.delayed(25);
  }, 'start flow 1');

  flow2.execute(function() {
    schedulePush('b', 'b');
    schedulePush('c', 'c');
    flow2.execute(function() {
      return flow1.execute(function() {
        schedulePush('d', 'd');
      }, 'flow 1 task');
    }, 'inject flow1 result into flow2');
    schedulePush('e', 'e');
  }, 'start flow 2');

  return goog.Promise.all([flow1Done.promise, flow2Done.promise]).
      then(function() {
        webdriver.test.testutil.assertMessages('a', 'b', 'c', 'd', 'e');
      });
}

function testFramesWaitToCompleteForPendingRejections() {
  return new goog.Promise(function(fulfill, reject) {

    webdriver.promise.controlFlow().execute(function() {
      webdriver.promise.rejected(new StubError);
    }).then(fulfill, reject);

  }).
  then(goog.partial(fail, 'expected to fail'), assertIsStubError).
  then(waitForIdle);
}

function testSynchronizeErrorsPropagateToOuterFlow() {
  var outerFlow = new webdriver.promise.ControlFlow;
  var innerFlow = new webdriver.promise.ControlFlow;

  var block = goog.Promise.withResolver();
  innerFlow.execute(function() {
    return block.promise;
  }, 'block inner flow');

  outerFlow.execute(function() {
    block.resolve();
    return innerFlow.execute(function() {
      webdriver.promise.rejected(new StubError);
    }, 'trigger unhandled rejection error');
  }, 'run test');

  return goog.Promise.all([
    waitForIdle(innerFlow),
    waitForAbort(outerFlow).then(assertIsStubError)
  ]);
}

function testFailsIfErrbackThrows() {
  webdriver.promise.rejected('').then(null, throwStubError);
  return waitForAbort().then(assertIsStubError);
}

function testFailsIfCallbackReturnsRejectedPromise() {
  webdriver.promise.fulfilled().then(function() {
    return webdriver.promise.rejected(new StubError);
  });
  return waitForAbort().then(assertIsStubError);
}

function testAbortsFrameIfTaskFails() {
  webdriver.promise.fulfilled().then(function() {
    webdriver.promise.controlFlow().execute(throwStubError);
  });
  return waitForAbort().then(assertIsStubError);
}

function testAbortsFramePromisedChainedFromTaskIsNotHandled() {
  webdriver.promise.fulfilled().then(function() {
    webdriver.promise.controlFlow().execute(goog.nullFunction).
        then(throwStubError);
  });
  return waitForAbort().then(assertIsStubError);
}

function testTrapsChainedUnhandledRejectionsWithinAFrame() {
  var pair = callbackPair(null, assertIsStubError);
  webdriver.promise.fulfilled().then(function() {
    webdriver.promise.controlFlow().execute(goog.nullFunction).
        then(throwStubError);
  }).then(pair.callback, pair.errback);

  return waitForIdle().then(pair.assertErrback);
}

function testCancelsRemainingTasksIfFrameThrowsDuringScheduling() {
  var task1, task2;
  var pair = callbackPair(null, assertIsStubError);
  var flow = webdriver.promise.controlFlow();
  flow.execute(function() {
    task1 = flow.execute(goog.nullFunction);
    task2 = flow.execute(goog.nullFunction);
    throw new StubError;
  }).then(pair.callback, pair.errback);

  return waitForIdle().
      then(pair.assertErrback).
      then(function() {
        assertFalse(task1.isPending());
        pair = callbackPair();
        return task1.then(pair.callback, pair.errback);
      }).
      then(function() {
        pair.assertErrback();
        assertFalse(task2.isPending());
        pair = callbackPair();
        return task2.then(pair.callback, pair.errback);
      }).
      then(function() {
        pair.assertErrback();
      });
}

function testCancelsRemainingTasksInFrameIfATaskFails() {
  var task;
  var pair = callbackPair(null, assertIsStubError);
  var flow = webdriver.promise.controlFlow();
  flow.execute(function() {
    flow.execute(throwStubError);
    task = flow.execute(goog.nullFunction);
  }).then(pair.callback, pair.errback);

  return waitForIdle().then(pair.assertErrback).then(function() {
    assertFalse(task.isPending());
    pair = callbackPair();
    task.then(pair.callback, pair.errback);
  }).then(function() {
    pair.assertErrback();
  });
}

function testDoesNotModifyRejectionErrorIfPromiseNotInsideAFlow() {
  var error = Error('original message');
  var originalStack = error.stack;
  var originalStr = error.toString();

  var pair = callbackPair(null, function(e) {
    assertEquals(error, e);
    assertEquals('original message', e.message);
    assertEquals(originalStack, e.stack);
    assertEquals(originalStr, e.toString());
  });

  webdriver.promise.rejected(error).then(pair.callback, pair.errback);
  return waitForIdle().then(pair.assertErrback);
}

/** See https://github.com/SeleniumHQ/selenium/issues/444 */
function testMaintainsOrderWithPromiseChainsCreatedWithinAForeach_1() {
  var messages = [];
  flow.execute(function() {
    return webdriver.promise.fulfilled(['a', 'b', 'c', 'd']);
  }, 'start').then(function(steps) {
    steps.forEach(function(step) {
      webdriver.promise.fulfilled(step)
      .then(function() {
        messages.push(step + '.1');
      }).then(function() {
        messages.push(step + '.2');
      });
    })
  });
  return waitForIdle().then(function() {
    assertArrayEquals(
        ['a.1', 'b.1', 'c.1', 'd.1', 'a.2', 'b.2', 'c.2', 'd.2'],
        messages);
  });
}

/** See https://github.com/SeleniumHQ/selenium/issues/444 */
function testMaintainsOrderWithPromiseChainsCreatedWithinAForeach_2() {
  var messages = [];
  flow.execute(function() {
    return webdriver.promise.fulfilled(['a', 'b', 'c', 'd']);
  }, 'start').then(function(steps) {
    steps.forEach(function(step) {
      webdriver.promise.fulfilled(step)
      .then(function() {
        messages.push(step + '.1');
      }).then(function() {
        flow.execute(function() {}, step + '.2').then(function(text) {
          messages.push(step + '.2');
        });
      });
    })
  });
  return waitForIdle().then(function() {
    assertArrayEquals(
        ['a.1', 'b.1', 'c.1', 'd.1', 'a.2', 'b.2', 'c.2', 'd.2'],
        messages);
  });
}

/** See https://github.com/SeleniumHQ/selenium/issues/444 */
function testMaintainsOrderWithPromiseChainsCreatedWithinAForeach_3() {
  var messages = [];
  flow.execute(function() {
    return webdriver.promise.fulfilled(['a', 'b', 'c', 'd']);
  }, 'start').then(function(steps) {
    steps.forEach(function(step) {
      webdriver.promise.fulfilled(step)
      .then(function(){})
      .then(function() {
        messages.push(step + '.1');
        return flow.execute(function() {}, step + '.1');
      }).then(function() {
        flow.execute(function() {}, step + '.2').then(function(text) {
          messages.push(step + '.2');
        });
      });
    })
  });
  return waitForIdle().then(function() {
    assertArrayEquals(
        ['a.1', 'b.1', 'c.1', 'd.1', 'a.2', 'b.2', 'c.2', 'd.2'],
        messages);
  });
}


text:
<?php

namespace spec\Jojo1981\Tree\BinaryTree;

use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Jojo1981\Tree\BinaryTree\Node;

/**
 * @category spec
 * @package Jojo1981
 * @subpackage Tree
 * @author Joost Nijhuis <jnijhuis81@gmail.com>
 * @copyright Copyright (c) 2014, Joost Nijhuis
 * @license MIT
 *
 * spec\Jojo1981\Tree\BinaryTree\NodeSpec
 *
 * @mixin Node
 */
class NodeSpec extends ObjectBehavior
{
    function let()
    {
        $this->beConstructedWith('+');
    }

    function it_is_initializable()
    {
        $this->shouldHaveType('Jojo1981\Tree\BinaryTree\Node');
    }

    function it_should_return_the_left_node(Node $leftNode)
    {
        $this->setLeft($leftNode);
        $this->getLeft()->shouldReturn($leftNode);
    }

    function it_should_return_the_right_node(Node $rightNode)
    {
        $this->setRight($rightNode);
        $this->getRight()->shouldReturn($rightNode);
    }

    function it_should_return_the_label_which_is_set_when_constructing()
    {
        $this->getLabel()->shouldReturn('+');
    }

    function it_should_return_null_when_the_node_does_not_have_parent_node()
    {
        $this->getParent()->shouldReturn(null);
    }

    function it_should_return_the_parent_node_because_this_node_is_child_node(Node $rootNode)
    {
        $this->setParent($rootNode);
        $this->getParent()->shouldReturn($rootNode);
    }

    function it_should_return_true_because_this_node_is_the_root_node_without_children()
    {
        $this->isRootNode()->shouldReturn(true);
    }

    function it_should_return_true_because_this_node_is_the_root_node_with_children(
        Node $leftNode,
        Node $rightNode
    ) {
        $this->setLeft($leftNode);
        $this->setRight($rightNode);
        $this->isRootNode()->shouldReturn(true);
    }

    function it_should_return_false_because_this_node_is_a_childNode(Node $rootNode)
    {
        $this->setParent($rootNode);
        $this->isRootNode()->shouldReturn(false);
    }

    function it_should_return_false_because_this_node_is_not_a_leave_node_and_has_a_left_child(
        Node $leftNode
    ) {
        $this->setLeft($leftNode);
        $this->isLeaveNode()->shouldReturn(false);
    }

    function it_should_return_false_because_this_node_is_not_a_leave_node_and_has_a_right_child(
        Node $rightNode
    ) {
        $this->setRight($rightNode);
        $this->isLeaveNode()->shouldReturn(false);
    }

    function it_should_return_false_because_this_node_is_not_a_leave_node_and_has_childs(
        Node $leftNode,
        Node $rightNode
    )
    {
        $this->setLeft($leftNode);
        $this->setRight($rightNode);
        $this->isLeaveNode()->shouldReturn(false);
    }

    function it_should_return_true_because_this_root_node_is_a_leave_node_becaue_it_does_not_have_any_child()
    {
        $this->isLeaveNode()->shouldReturn(true);
    }

    function it_should_return_true_because_this_child_node_is_a_leave_node_becaue_it_does_not_have_any_child(
        Node $rootNode
    ) {
        $this->setParent($rootNode);
        $this->isLeaveNode()->shouldReturn(true);
    }
}

redpajama_set_name: RedPajamaGithub


text:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#3F51B5</color>
    <color name="colorPrimaryDark">#303F9F</color>
    <color name="colorAccent">#FF4081</color>

    <!-- Array of ColorStateList -->
    <array name="colors">
        <item>@color/fab_mini</item>
        <item>@color/fab_mini</item>
        <item>@color/fab_mini</item>
        <item>@color/fab_mini</item>
        <item>@color/fab_mini</item>
    </array>

    <array name="white_array">
        <item>@android:color/white</item>
        <item>@android:color/white</item>
        <item>@android:color/white</item>
        <item>@android:color/white</item>
        <item>@android:color/white</item>
    </array>

    <array name="black_array">
        <item>@android:color/black</item>
        <item>@android:color/black</item>
        <item>@android:color/black</item>
        <item>@android:color/black</item>
        <item>@android:color/black</item>
    </array>
</resources>


text:
package org.xson.common.object;

import java.math.BigInteger;

public class BigIntegerField implements IField {

	private static final long	serialVersionUID	= 4848636595224033221L;

	protected String			name;

	private BigInteger			value;

	public BigIntegerField(String name, BigInteger value) {
		this.name = name;
		this.value = value;
	}

	@Override
	public Object getValue() {
		return value;
	}

	@Override
	public Object getValue(int dataType) {
		if (dataType == BIGINTEGER_TYPE) {
			return value;
		}
		throw new XCOException("Type mismatch for field: " + this.name);
	}

	@Override
	public void toXMLString(StringBuilder builder) {
		builder.append("<K " + PROPERTY_K + "=\"");
		builder.append(this.name);
		builder.append("\" " + PROPERTY_V + "=\"");
		builder.append(this.value.toString());
		builder.append("\"/>");
	}

	@Override
	public void toJSONString(StringBuilder builder) {
		// builder.append("\"").append(this.name).append("\"").append(":").append(this.value.toString());
		builder.append("\"").append(this.name).append("\"").append(":\"").append(this.value.toString()).append("\"");
	}

	@Override
	public IField cloneSelf() {
		return new BigIntegerField(name, new BigInteger(value.toString()));
	}
}


text:
- Added optional support for receiving remote files and uploading local files via the `SupportsFiles` typeclass (see issue [#17](https://github.com/JustusAdam/marvin/issues/17) and [#14](https://github.com/JustusAdam/marvin/issues/14)
- Added files support to the adapters
    - Shell
    - Slack
    - Telegram
- Changed bot runner in `IsAdapter` to be simpler to implement.
- Changed the IsAdapter API.
    - The new API is lens based. Adapter dependent types are constrained via the requirement for certain lens class instances.
    - Deprecated `getUsername` and `getChannelName`, these are now accessible via the lens class instances.
- Changed Util API
    - `Marvin.Util.HTTP` has been removed (was empty) until an API has been found
    - `Marvin.Util.Mutable` has been removed as BotReacting now has a `MonadBase` and `MonadBaseControl` instance meaning the functions from `lifted-base` can be used without modification.
- Fixed issue [#7](https://github.com/JustusAdam/marvin/issues/7) where initializer overwrites files
- Preprocessor now uses the new `external-scripts.json` spec from issue [#8](https://github.com/JustusAdam/marvin/issues/8)
- Added some generic handlers for file down/upload and version
- Configuration is now abstract and based on a typeclass
    - Added lower level run functions so the user can specify logging and config implementation
- Switched to hpack for package management
- Exporting internal data constructors for adapters per request from [@jchildren](https://github.com/jchildren) in [#21](https://github.com/JustusAdam/marvin/issues/21)
- Changed Adapter API, channel names are now optional
- Changed slack channel implementation, channel topic is now optional

# 0.2.4 - 23.09.2017

- Fixed an old bug when getting information about unnamed channels

# 0.2.3 - 05.03.2017

- A big thanks to [@lubomir](https://github.com/lubomir) for testing the IRC adapter and contributing the following changes
    - Better command recognition in IRC adapter
    - Bot now properly registers in IRC
    - Added config setting to IRC for channels which the bot should join

# 0.2.2 - 05.03.2017

- Removed the integration tests from the cabal package config

# 0.2.1 - 04.03.2017

- Fixed the templates for the initializer
- added tests to ensure integration test bot and project created by initializer actually compile
- Fixed the travis configurations

# 0.2.0 - 25.02.2017

- Implemented an adapter for IRC
- Added manual lens class instances to expose the datatypes and instances using them.

# 0.1.2, 0.1.3, 0.1.4, 0.1.5 - 17.02.2017

- Added manual version bounds to be compatible with stackage and the hackage build (sorry for the version spam)

# 0.1.1 - 17.02.2017

- Added changelog
- Added version bounds


text:
package org.ohdsi.webapi.check.checker.criteria;

import org.ohdsi.circe.cohortdefinition.Criteria;
import org.ohdsi.circe.cohortdefinition.DateRange;
import org.ohdsi.circe.cohortdefinition.NumericRange;
import org.ohdsi.circe.cohortdefinition.ProcedureOccurrence;
import org.ohdsi.webapi.check.builder.DateRangeValidatorBuilder;
import org.ohdsi.webapi.check.builder.NumericRangeValidatorBuilder;
import org.ohdsi.webapi.check.builder.ValidatorGroupBuilder;

public class ProcedureOccurrenceHelper {
    public static ValidatorGroupBuilder<Criteria, ProcedureOccurrence> prepareProcedureOccurrenceBuilder() {
        ValidatorGroupBuilder<Criteria, ProcedureOccurrence> builder =
                new ValidatorGroupBuilder<Criteria, ProcedureOccurrence>()
                        .attrName("procedure occurrence")
                        .conditionGetter(t -> t instanceof ProcedureOccurrence)
                        .valueGetter(t -> (ProcedureOccurrence) t)
                        .groups(
                                prepareAgeBuilder(),
                                prepareStartDateBuilder(),
                                prepareQuantityBuilder()
                        );
        return builder;
    }

    private static ValidatorGroupBuilder<ProcedureOccurrence, NumericRange> prepareAgeBuilder() {
        ValidatorGroupBuilder<ProcedureOccurrence, NumericRange> builder =
                new ValidatorGroupBuilder<ProcedureOccurrence, NumericRange>()
                        .attrName("age")
                        .valueGetter(t -> t.age)
                        .validators(
                                new NumericRangeValidatorBuilder<>()
                        );
        return builder;
    }

    private static ValidatorGroupBuilder<ProcedureOccurrence, DateRange> prepareStartDateBuilder() {
        ValidatorGroupBuilder<ProcedureOccurrence, DateRange> builder =
                new ValidatorGroupBuilder<ProcedureOccurrence, DateRange>()
                        .attrName("occurrence start date")
                        .valueGetter(t -> t.occurrenceStartDate)
                        .validators(
                                new DateRangeValidatorBuilder<>()
                        );
        return builder;
    }

    private static ValidatorGroupBuilder<ProcedureOccurrence, NumericRange> prepareQuantityBuilder() {
        ValidatorGroupBuilder<ProcedureOccurrence, NumericRange> builder =
                new ValidatorGroupBuilder<ProcedureOccurrence, NumericRange>()
                        .attrName("quantity")
                        .valueGetter(t -> t.quantity)
                        .validators(
                                new NumericRangeValidatorBuilder<>()
                        );
        return builder;
    }
}


text:
define(
({
	"pasteFromWord": "Pegar desde Word",
	"instructions": "Pegue el contenido de Word en el siguiente recuadro de texto. Cuando esté satisfecho con el contenido que se debe insertar, pulse el botón pegar. Para abortar la inserción de texto, pulse el botón cancelar."
})
);


text:
def delete_bucket bucket_name:
  # The ID of your GCS bucket
  # bucket_name = "your-unique-bucket-name"

  require "google/cloud/storage"

  storage = Google::Cloud::Storage.new
  bucket  = storage.bucket bucket_name, skip_lookup: true

  bucket.delete

  puts "Deleted bucket: #{bucket.name}"
end
# [END storage_delete_bucket]

compose_file bucket_name: ARGV.shift if $PROGRAM_NAME == __FILE__


text:
/*!
 * \file
 * \brief OpenGL ES 3.0 Test Package Entry Point.
 *//*--------------------------------------------------------------------*/

#include "tes3TestPackage.hpp"

// Register package to test executor.

static tcu::TestPackage* createTestPackage (tcu::TestContext& testCtx)
{
	return new deqp::gles3::TestPackage(testCtx);
}

tcu::TestPackageDescriptor g_gles3PackageDescriptor("dEQP-GLES3", createTestPackage);


text:
const ArthurDeGreefCard = {
  title: "The peculiar story of Arthur De Greef",
  type: "card",
  date: "28/04/2012 10:00",
  tags: ["Art", "Culture", "Music"],
  img:
    "https://drive.google.com/uc?export=view&id=1N9Ed6a_CDa8SEMZeLaxULF4FtkHBQf4Feg",
  xpPoints: 100,
  // TODO: remove in future to component
  closeHandler: () => null,
  caption: "A statue of Arthur de Greef",
  description:
    "Pianist Arthur De Greef born in Louvain. He was a pupil of Frank Liszt. I do not why there is statue is placed here. There is music school with the same name not far.",
  location: { latitude: 50.828797, longitude: 4.352191 },
  place: "Park next to my Home",
  creator: "Jan",
  media: [
    {
      type: "photo",
      name: "franz-liszt---the-first-rock-star",
      src: ""
    },
    {
      type: "hyperlink",
      name: "franz-liszt---the-first-rock-star",
      src: ""
    },
    {
      type: "game",
      name: "franz-liszt---the-first-rock-star",
      src: ""
    }
  ],
  friends: [
    {
      user: "Nils",
      comment: "I did not know that he was such a famous composer"
    },
    {
      user: "Babba",
      comment: "What a nice park, strange, that they put a mask on his face!"
    }
  ],
  rating: [
    {
      user: "Nils",
      value: 4
    }
  ],
  cardSets: ["european_composers"],
  linkedCards: ["Frank Liszt", "Music school Arthur de Greef"]
};

const ArthurDeGreefCard2 = {
  title: "The peculiar story of Arthur De Greef",
  type: "card",
  date: "28/04/2012 10:00",
  tags: ["Art", "Culture", "Music"],
  img:
    "https://drive.google.com/uc?export=view&id=1N9Ed6a_CDa8SEMZeLaxULF4FtkHBQf4Feg",
  xpPoints: 100,
  // TODO: remove in future to component
  closeHandler: () => null,
  caption: "A statue of Arthur de Greef",
  description:
    "Pianist Arthur De Greef born in Louvain. He was a pupil of Frank Liszt. I do not why there is statue is placed here. There is music school with the same name not far.",
  location: { latitude: 50.828797, longitude: 4.352191 },
  place: "Park next to my Home",
  creator: "Jan",
  media: [
    {
      type: "photo",
      name: "franz-liszt---the-first-rock-star",
      src: ""
    },
    {
      type: "hyperlink",
      name: "franz-liszt---the-first-rock-star",
      src: ""
    },
    {
      type: "game",
      name: "franz-liszt---the-first-rock-star",
      src: ""
    }
  ],
  friends: [
    {
      user: "Nils",
      comment: "I did not know that he was such a famous composer"
    },
    {
      user: "Babba",
      comment: "What a nice park, strange, that they put a mask on his face!"
    }
  ],
  rating: [
    {
      user: "Nils",
      value: 4
    }
  ],
  cardSets: ["european_composers"],
  linkedCards: ["Frank Liszt", "Music school Arthur de Greef"]
};

const ArthurDeGreefCard3 = {
  title: "The peculiar story of Arthur De Greef",
  type: "card",
  date: "28/04/2012 10:00",
  tags: ["Art", "Culture", "Music"],
  img:
    "https://drive.google.com/uc?export=view&id=1N9Ed6a_CDa8SEMZeLaxULF4FtkHBQf4Feg",
  xpPoints: 100,
  // TODO: remove in future to component
  closeHandler: () => null,
  caption: "A statue of Arthur de Greef",
  description:
    "Pianist Arthur De Greef born in Louvain. He was a pupil of Frank Liszt. I do not why there is statue is placed here. There is music school with the same name not far.",
  location: { latitude: 50.828797, longitude: 4.352191 },
  place: "Park next to my Home",
  creator: "Jan",
  media: [
    {
      type: "photo",
      name: "franz-liszt---the-first-rock-star",
      src: ""
    },
    {
      type: "hyperlink",
      name: "franz-liszt---the-first-rock-star",
      src: ""
    },
    {
      type: "game",
      name: "franz-liszt---the-first-rock-star",
      src: ""
    }
  ],
  friends: [
    {
      user: "Nils",
      comment: "I did not know that he was such a famous composer"
    },
    {
      user: "Babba",
      comment: "What a nice park, strange, that they put a mask on his face!"
    }
  ],
  rating: [
    {
      user: "Nils",
      value: 4
    }
  ],
  cardSets: ["european_composers"],
  linkedCards: ["Frank Liszt", "Music school Arthur de Greef"]
};

const MapEvent = {
  title: "an event on a location on a map",
  type: "card",
  date: "27/04/2012 10:00",
  // tags: ['Art', 'Culture', 'Music'],
  // img: 'https://drive.google.com/uc?export=view&id=1N9Ed6a_CDa8SEMZeLaxULF4FtkHBQf4Feg',
  xpPoints: 100,
  // TODO: remove in future to component
  // closeHandler: () => (null),
  // caption: 'A statue of Arthur de Greef',
  location: { latitude: 50.828797, longitude: 4.352191 },
  place: "Park next to my Home",
  tags: ["Art", "Culture", "classic", "something", "composer", "classic"],
  // creator: 'Jan',
  cardSets: [],
  linkedCards: []
};

const FranzLisztCard = {
  title: "The life of Frank Liszt",
  date: "24/04/2012 10:00",
  type: "card",
  tags: ["Art", "Culture", "classic", "music"],
  img:
    "http://www.wagner-heavymetal.com/uploads/4/4/7/3/44734409/5792753_orig.gif",
  caption: "Frank Liszt meets Heavy Metal",
  xpPoints: 100,
  // TODO: remove in future to component
  closeHandler: () => null,
  description:
    "The first pop star of classical music. He made girls cry with his virtuosic piano play. Much like Jimi hendrix he redefined the way to play his instrument",
  location: { latitude: 50.843018, longitude: 4.35908 },
  place: "Bayreuth, a city with a long history of classical music",
  creator: "Jan",
  media: [
    {
      type: "hyperlink",
      name: "franz-liszt---the-first-rock-star",
      src:
        "http://www.wagner-heavymetal.com/franz-liszt---the-first-rock-star.html"
    }
  ],
  friends: [
    {
      user: "Nils",
      comment: "the band Phoenix dedicated an album to Liszt"
    }
  ],
  rating: [
    {
      user: "Nils",
      value: 4
    }
  ],
  cardSets: ["european_composers", "Music challenge"],
  linkedCards: [
    "Jimi Hendrix",
    "Music instruments museum",
    "HardRock Cafe Brussels"
  ]
};

const jimiHendrixCard = {
  title: "Jimi Hendrix in Belgium",
  type: "card",
  date: "01/04/2012 10:00",
  tags: ["Rock 'n ' Roll", "Culture", "music"],
  img:
    "http://www.memoire60-70.be/Images/Chronique_1966_1972/Jimi_Hendrix_Belgium_67.jpg",
  caption: "Jimi Hendrix au Twenty Club - 5 mars 1967",
  xpPoints: 50,
  // TODO: remove in future to component
  closeHandler: () => null,
  description:
    "The first pop star of classical music. He made girls cry with his virtuosic piano play. Much like Jimi hendrix he redefined the way to play his instrument",
  location: { latitude: 50.893793, longitude: 4.352083 },
  place: "Amerikaans Theater",
  creator: "Jan",
  media: [
    {
      type: "hyperlink",
      name: "Le Twenty Club",
      src:
        "http://www.memoire60-70.be/Chronique_1960_1965/Twenty_Club_Relais_de_la_Poste.htm"
    },
    {
      type: "video",
      name: "Live at woodstock",
      src: "https://www.youtube.com/watch?v=_PVjcIO4MT4"
    }
  ],
  friends: [
    {
      user: "Nils",
      comment: "jimi yeah"
    },
    {
      user: "Kiran",
      comment: "My hero, I want to have his haircut"
    },
    {
      user: "babba",
      comment: "all along the watch tower!"
    },
    {
      user: "gab",
      comment: "BEER"
    }
  ],
  rating: [
    {
      user: "Nils",
      value: 4
    }
  ],
  cardSets: ["european_composers", "Music challenge"],
  linkedCards: ["Frank Liszt", "Music school Arthur de Greef"]
};
const jimiHendrixCard2 = {
  title: "Jimi Hendrix in Belgium",
  type: "card",
  date: "01/04/2012 10:00",
  tags: ["Rock 'n ' Roll", "Culture", "music"],
  img:
    "http://www.memoire60-70.be/Images/Chronique_1966_1972/Jimi_Hendrix_Belgium_67.jpg",
  caption: "Jimi Hendrix au Twenty Club - 5 mars 1967",
  xpPoints: 50,
  // TODO: remove in future to component
  closeHandler: () => null,
  description:
    "The first pop star of classical music. He made girls cry with his virtuosic piano play. Much like Jimi hendrix he redefined the way to play his instrument",
  location: { latitude: 50.893793, longitude: 4.352083 },
  place: "Amerikaans Theater",
  creator: "Jan",
  media: [
    {
      type: "hyperlink",
      name: "Le Twenty Club",
      src:
        "http://www.memoire60-70.be/Chronique_1960_1965/Twenty_Club_Relais_de_la_Poste.htm"
    },
    {
      type: "video",
      name: "Live at woodstock",
      src: "https://www.youtube.com/watch?v=_PVjcIO4MT4"
    }
  ],
  friends: [
    {
      user: "Nils",
      comment: "jimi yeah"
    },
    {
      user: "Kiran",
      comment: "My hero, I want to have his haircut"
    },
    {
      user: "babba",
      comment: "all along the watch tower!"
    },
    {
      user: "gab",
      comment: "BEER"
    }
  ],
  rating: [
    {
      user: "Nils",
      value: 4
    }
  ],
  cardSets: ["european_composers", "Music challenge"],
  linkedCards: ["Frank Liszt", "Music school Arthur de Greef"]
};

const notification = {
  title: "TestCard",
  type: "notification",
  date: "02/04/2012 10:00",
  tags: ["Rock 'n ' Roll", "Culture", "music"],
  img: "https://www.w3schools.com/images/w3schools_green.jpg",
  caption: "blalblalal",
  xpPoints: 50,
  // TODO: remove in future to component
  closeHandler: () => null,
  description: "more descriptive text",
  location: { latitude: 50.893793, longitude: 4.352083 },
  place: "Amerikaans Theater",
  creator: "Jan",
  cardSets: ["european_composers", "Music challenge"],
  linkedCards: ["Frank Liszt", "Music school Arthur de Greef"]
};

const notification2 = {
  title: "TestCard",
  type: "notification",
  date: "02/04/2012 10:00",
  tags: ["Rock 'n ' Roll", "Culture", "music"],
  img: "https://www.w3schools.com/images/w3schools_green.jpg",
  caption: "blalblalal",
  xpPoints: 50,
  // TODO: remove in future to component
  closeHandler: () => null,
  description: "more descriptive text",
  location: { latitude: 50.893793, longitude: 4.352083 },
  place: "Amerikaans Theater",
  creator: "Jan",
  cardSets: ["european_composers", "Music challenge"],
  linkedCards: ["Frank Liszt", "Music school Arthur de Greef"]
};

const VUBcard = {
  title: "Vrije Universiteit Brussel",
  type: "card",
  date: "28/04/2012 10:00",
  tags: ["Uni", "education"],
  img:
    "https://drive.google.com/uc?export=view&id=1N9Ed6a_CDa8SEMZeLaxULF4FtkHBQf4Feg",
  caption: "Main Entrance VUB",
  xpPoints: 50,
  // TODO: remove in future to component
  closeHandler: () => null,
  description:
    "The Vrije Universiteit Brussel is the only Flemish university that has incorporated the principle of 'free inquiry' in its statutes. This principle is based on a text by the French mathematician and natural philosopher Henri Poincaré (1854-1912)",
  location: { latitude: 50.821705, longitude: 4.395165 },
  place: "Pleinlaan 2 - 1050 BRUSSEL",
  creator: "Jan",
  media: [
    {
      type: "hyperlink",
      name: "Website",
      src: "http://we.vub.ac.be"
    },
    {
      type: "video",
      name: "Some of the VUB's international students",
      src: "https://www.youtube.com/watch?v=YFCzlOqQW7M"
    }
  ],
  friends: [
    {
      user: "Chauncey",
      comment: "here I succeeded my Master studies."
    },
    {
      user: "Jan",
      comment: "Now, I finally earn money as PhD student at the VUB!"
    }
  ],
  rating: [
    {
      user: "Nils",
      value: 4
    }
  ],
  cardSets: ["scavenger_hunt_vub", "Brussels_city_tour"],
  linkedCards: ["Sport_centre_vub", "ULB_brussels"]
};

const VubCard2 = {
  title: "Vrije Universiteit Brussel",
  type: "card",
  date: "28/04/2012 10:00",
  tags: ["Uni", "education"],
  img:
    "https://drive.google.com/uc?export=view&id=1N9Ed6a_CDa8SEMZeLaxULF4FtkHBQf4Feg",
  caption: "Main Entrance VUB",
  xpPoints: 50,
  // TODO: remove in future to component
  closeHandler: () => null,
  description:
    "The Vrije Universiteit Brussel is the only Flemish university that has incorporated the principle of 'free inquiry' in its statutes. This principle is based on a text by the French mathematician and natural philosopher Henri Poincaré (1854-1912)",
  location: { latitude: 50.821705, longitude: 4.395165 },
  place: "Pleinlaan 2 - 1050 BRUSSEL",
  creator: "Jan",
  media: [
    {
      type: "hyperlink",
      name: "Website",
      src: "http://we.vub.ac.be"
    },
    {
      type: "video",
      name: "Some of the VUB's international students",
      src: "https://www.youtube.com/watch?v=YFCzlOqQW7M"
    }
  ],
  friends: [
    {
      user: "Chauncey",
      comment: "here I succeeded my Master studies."
    },
    {
      user: "Jan",
      comment: "Now, I finally earn money as PhD student at the VUB!"
    }
  ],
  rating: [
    {
      user: "Nils",
      value: 4
    }
  ],
  cardSets: ["scavenger_hunt_vub", "Brussels_city_tour"],
  linkedCards: ["Sport_centre_vub", "ULB_brussels"]
};
const VUBSports = {
  title: "Sport center VUB",
  type: "card",
  key: 4,
  date: "04/04/2012 10:00",
  tags: ["Uni", "sports"],
  img:
    "http://uabsport.be/wp-content/uploads/2015/10/817SOap_binnenzicht_01.jpg",
  caption: "The future swimming pool",
  xpPoints: 100,
  // TODO: remove in future to component
  description:
    "At a university, you set your mind in movement. At VUB, you do the same for your body. And no less intense, for that matter. There is a swimming pool. A running field. A complete Fit-O-Run course. You can partake in no less than 17 different sporting disciplines, individually or under guidance. You can work out in a Basic Fit hall or sign up for a student's sporting club and aim for victory in competitions. Starting is as simple as contacting our Sports & Movement service. It's the only thing that involves no sweat whatsoever.",
  location: { latitude: 50.824228, longitude: 4.395328 },
  place: "Pleinlaan 2 - 1050 BRUSSEL",
  creator: "Jan",
  media: [
    {
      type: "hyperlink",
      name: "Website",
      src: "http://www.vub.ac.be/en/sport"
    }
  ],
  friends: [
    {
      user: "Lars",
      comment: "they have two squash courts and they are not expensive"
    },
    {
      user: "Jan",
      comment:
        "In the basic fit you can work out with many different equipment!"
    }
  ],
  rating: [
    {
      user: "Nils",
      value: 4
    }
  ],
  cardSets: ["Fitness_Brussels", "Squash_brussels"],
  linkedCards: ["VUB", "ULB_brussels"]
};

const VUBSports2 = {
  title: "Sport center VUB",
  type: "card",
  key: 4,
  date: "04/04/2012 10:00",
  tags: ["Uni", "sports"],
  img:
    "http://uabsport.be/wp-content/uploads/2015/10/817SOap_binnenzicht_01.jpg",
  caption: "The future swimming pool",
  xpPoints: 100,
  // TODO: remove in future to component
  description:
    "At a university, you set your mind in movement. At VUB, you do the same for your body. And no less intense, for that matter. There is a swimming pool. A running field. A complete Fit-O-Run course. You can partake in no less than 17 different sporting disciplines, individually or under guidance. You can work out in a Basic Fit hall or sign up for a student's sporting club and aim for victory in competitions. Starting is as simple as contacting our Sports & Movement service. It's the only thing that involves no sweat whatsoever.",
  location: { latitude: 50.824228, longitude: 4.395328 },
  place: "Pleinlaan 2 - 1050 BRUSSEL",
  creator: "Jan",
  media: [
    {
      type: "hyperlink",
      name: "Website",
      src: "http://www.vub.ac.be/en/sport"
    }
  ],
  friends: [
    {
      user: "Lars",
      comment: "they have two squash courts and they are not expensive"
    },
    {
      user: "Jan",
      comment:
        "In the basic fit you can work out with many different equipment!"
    }
  ],
  rating: [
    {
      user: "Nils",
      value: 4
    }
  ],
  cardSets: ["Fitness_Brussels", "Squash_brussels"],
  linkedCards: ["VUB", "ULB_brussels"]
};

const arr = [
  notification,
  notification2,
  MapEvent,
  jimiHendrixCard,
  ArthurDeGreefCard,
  ArthurDeGreefCard2,
  ArthurDeGreefCard3,
  FranzLisztCard,
  VUBcard,
  VUBSports,
  VUBSports2,
  VubCard2
];

// arr.forEach((d, i) => d.key = i);

module.exports = arr;


text:
/**
 * filetree.css
 * */

:root {
  font-family: "Hiragino Kaku Gothic ProN", Meiryo, sans-serif;
}

.filetree input[type="checkbox"] {
  display: none;
}

.filetree ul {
  height: 0;
  overflow: hidden;
}

.filetree > li input:checked ~ ul {
  height: auto;
}

.filetree li {
  list-style: none;
}

.filetree label {
  padding-left: 33px;
  line-height: 33px;
  display: inline-block;
}

/**
 * Icon
 * */
.filetree label {
  background-image: url('./icon/sprite/icon.png');
  background-repeat: no-repeat;
}

.filetree input[type="checkbox"] + label {
  background-position: 0 0;
  width: 33px;
  height: 33px;
}

.filetree input[type="checkbox"]:checked + label {
  background-position: 0 -33px;
  width: 30px;
  height: 30px;
}

@media screen and (-webkit-min-device-pixel-ratio: 1.0), screen and (min--moz-device-pixel-ratio: 1.0), screen and (-o-min-device-pixel-ratio: 100/100), screen and (min-device-pixel-ratio: 1.0), screen and (min-resolution: 1.0dppx) {
   filetree label {
      background-image: url('./icon/sprite/icon.png');
    -webkit-background-size: 33px 63px;
    -moz-background-size: 33px 63px;
    background-size: 33px 63px;
  }
}