aboutsummaryrefslogtreecommitdiffstats
path: root/src/scenario/SpecRunner.js
blob: 26fa9b918afb89c9730d67b0e2eb59e2c7e216bf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
/**
 * This class is the "this" of the it/beforeEach/afterEach method.
 * Responsibilities:
 *   - "this" for it/beforeEach/afterEach
 *   - keep state for single it/beforeEach/afterEach execution
 *   - keep track of all of the futures to execute
 *   - run single spec (execute each future)
 */
angular.scenario.SpecRunner = function() {
  this.futures = [];
};

/**
 * Executes a spec which is an it block with associated before/after functions
 * based on the describe nesting.
 *
 * @param {Object} An angular.scenario.UI implementation
 * @param {Object} A spec object
 * @param {Object} An angular.scenario.Application instance
 * @param {Function} Callback function that is called when the  spec finshes.
 */
angular.scenario.SpecRunner.prototype.run = function(ui, spec, specDone) {
  var specUI = ui.addSpec(spec);

  try {
    spec.fn.call(this);
  } catch (e) {
    specUI.error(e);
    specDone();
    return;
  }

  asyncForEach(
    this.futures,
    function(future, futureDone) {
      var stepUI = specUI.addStep(future.name);
      try {
        future.execute(function(error) {
          stepUI.finish(error);
          futureDone(error);
        });
      } catch (e) {
        stepUI.error(e);
        throw e;
      }
    },
    function(e) {
      specUI.finish(e);
      specDone();
    }
  );
};

/**
 * Adds a new future action.
 *
 * @param {String} Name of the future
 * @param {Function} Behavior of the future
 */
angular.scenario.SpecRunner.prototype.addFuture = function(name, behavior) {
  var future = new angular.scenario.Future(name, angular.bind(this, behavior));
  this.futures.push(future);
  return future;
};

/**
 * Adds a new future action to be executed on the application window.
 *
 * @param {String} Name of the future
 * @param {Function} Behavior of the future
 */
angular.scenario.SpecRunner.prototype.addFutureAction = function(name, behavior) {
  return this.addFuture(name, function(done) {
    this.application.executeAction(angular.bind(this, function($window, $document) {

      $document.elements = angular.bind(this, function(selector) {
        var args = Array.prototype.slice.call(arguments, 1);
        if (this.selector) {
          selector = this.selector + ' ' + (selector || '');
        }
        angular.foreach(args, function(value, index) {
          selector = selector.replace('$' + (index + 1), value);
        });
        var result = $document.find(selector);
        if (!result.length) {
          throw {
            type: 'selector',
            message: 'Selector ' + selector + ' did not match any elements.'
          };
        }

        return result;
      });

      try {
        behavior.call(this, $window, $document, done);
      } catch(e) {
        if (e.type && e.type === 'selector') {
          done(e.message);
        } else {
          throw e;
        }
      }
    }));
  });
};