aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--css/angular-scenario.css29
-rw-r--r--jsTestDriver-jquery.conf1
-rw-r--r--jsTestDriver.conf1
-rw-r--r--scenario/Runner.html3
-rw-r--r--scenario/widgets-scenario2.js54
-rw-r--r--scenario/widgets-scenarios.js22
-rw-r--r--scenario/widgets.html3
-rw-r--r--src/scenario/Runner.js250
-rw-r--r--src/scenario/Steps.js57
-rw-r--r--src/scenario/_namespace.js6
-rw-r--r--src/scenario/bootstrap.js19
-rw-r--r--test/scenario/RunnerSpec.js105
-rw-r--r--test/scenario/StepsTest.js7
13 files changed, 284 insertions, 273 deletions
diff --git a/css/angular-scenario.css b/css/angular-scenario.css
index ad263d21..70256a5a 100644
--- a/css/angular-scenario.css
+++ b/css/angular-scenario.css
@@ -2,18 +2,25 @@
/* CSS Document */
#runner {
- position: absolute;
- top:5px;
- left:10px;
+ position: absolute;
+ top:5px;
+ left:10px;
right:10px;
height: 200px;
}
+.console {
+ display: block;
+ overflow: scroll;
+ height: 200px;
+ border: 1px solid black;
+}
+
#testView {
- position: absolute;
- bottom:10px;
- top:210px;
- left:10px;
+ position: absolute;
+ bottom:10px;
+ top:230px;
+ left:10px;
right:10px;
}
@@ -22,12 +29,8 @@
height: 100%;
}
-.console {
- display: block;
- overflow: scroll;
- height: 200px;
- border: 1px solid black;
-}
+
+///////////////
.collapsed .log {
display: none;
diff --git a/jsTestDriver-jquery.conf b/jsTestDriver-jquery.conf
index 196d282f..dde88e80 100644
--- a/jsTestDriver-jquery.conf
+++ b/jsTestDriver-jquery.conf
@@ -6,7 +6,6 @@ load:
- lib/jquery/jquery-1.4.2.js
- src/Angular.js
- src/*.js
- - src/scenario/_namespace.js
- src/scenario/*.js
- test/testabilityPatch.js
- test/angular-mocks.js
diff --git a/jsTestDriver.conf b/jsTestDriver.conf
index e2820ddb..6f959b8f 100644
--- a/jsTestDriver.conf
+++ b/jsTestDriver.conf
@@ -6,7 +6,6 @@ load:
# - lib/jquery/jquery-1.4.2.js
- src/Angular.js
- src/*.js
- - src/scenario/_namespace.js
- src/scenario/*.js
- test/testabilityPatch.js
- test/angular-mocks.js
diff --git a/scenario/Runner.html b/scenario/Runner.html
index c5eb6205..5502283a 100644
--- a/scenario/Runner.html
+++ b/scenario/Runner.html
@@ -2,8 +2,7 @@
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<script type="text/javascript" src="../src/scenario/bootstrap.js"></script>
- <script type="text/javascript" src="widgets-scenarios.js"></script>
- <script type="text/javascript" src="datastore-scenarios.js"></script>
+ <script type="text/javascript" src="widgets-scenario2.js"></script>
</head>
<body>
</body>
diff --git a/scenario/widgets-scenario2.js b/scenario/widgets-scenario2.js
new file mode 100644
index 00000000..e24cabad
--- /dev/null
+++ b/scenario/widgets-scenario2.js
@@ -0,0 +1,54 @@
+browser = {
+ navigateTo: function(url){
+ $scenario.addStep('Navigate to: ' + url, function(done){
+ var self = this;
+ self.testFrame.load(function(){
+ self.testFrame.unbind();
+ self.testDocument = self.testWindow.angular.element(self.testWindow.document);
+ done();
+ });
+ if (this.testFrame.attr('src') == url) {
+ this.testWindow.location.reload();
+ } else {
+ this.testFrame.attr('src', url);
+ }
+ });
+ }
+};
+
+function input(selector) {
+ return {
+ enter: function(value){
+ $scenario.addStep("Set input text of '" + selector + "' to value '" + value + "'", function(done){
+ var input = this.testDocument.find('input[name=' + selector + ']');
+ input.val(value);
+ input.trigger('change');
+ done();
+ });
+ }
+ };
+}
+
+function expect(selector) {
+ return {
+ toEqual: function(expected) {
+ $scenario.addStep("Expect that " + selector + " equals '" + expected + "'", function(done){
+ var attrName = selector.substring(2, selector.length - 2);
+ var binding = this.testDocument.find('span[ng-bind=' + attrName + ']');
+ if (binding.text() != expected) {
+ this.result.fail("Expected '" + expected + "' but was '" + binding.text() + "'");
+ }
+ done();
+ });
+ }
+ };
+}
+
+describe('widgets', function(){
+ it('should verify that basic widgets work', function(){
+ browser.navigateTo('widgets.html');
+ expect('{{text.basic}}').toEqual('');
+ input('text.basic').enter('John');
+ expect('{{text.basic}}').toEqual('JohnXX');
+ });
+});
diff --git a/scenario/widgets-scenarios.js b/scenario/widgets-scenarios.js
index 663b06da..a1e6c0ed 100644
--- a/scenario/widgets-scenarios.js
+++ b/scenario/widgets-scenarios.js
@@ -3,28 +3,28 @@ angular.scenarioDef.widgets = {
{Given:"browser", at:"widgets.html"}
],
checkWidgetBinding:[
- {Then:"text", at:"{{name}}", should_be:""},
- {When:"enter", text:"John", at:":input[name=name]"},
- {Then:"text", at:"{{name}}", should_be:"John"},
-
- {Then:"text", at:"{{gender}}", should_be:""},
- {When:"click", at:"input:radio[value=male]"},
+ {Then:"text", at:"{{text.basic}}", should_be:""},
+ {When:"enter", text:"John", at:":input[name=text.basic]"},
+ {Then:"text", at:"{{text.basic}}", should_be:"John"},
+
{Then:"text", at:"{{gender}}", should_be:"male"},
-
+ {When:"click", at:"input:radio[value=female]"},
+ {Then:"text", at:"{{gender}}", should_be:"female"},
+
{Then:"text", at:"{{tea}}", should_be:"on"},
{When:"click", at:"input[name=tea]"},
{Then:"text", at:"{{tea}}", should_be:""},
-
+
{Then:"text", at:"{{coffee}}", should_be:""},
{When:"click", at:"input[name=coffee]"},
{Then:"text", at:"{{coffee}}", should_be:"on"},
-
+
{Then:"text", at:"{{count}}", should_be:0},
{When:"click", at:"form :button"},
{When:"click", at:"form :submit"},
{When:"click", at:"form :image"},
{Then:"text", at:"{{count}}", should_be:3},
-
+
{Then:"text", at:"{{select}}", should_be:"A"},
{When:"select", at:"select[name=select]", option:"B"},
{Then:"text", at:"{{select}}", should_be:"B"},
@@ -36,7 +36,7 @@ angular.scenarioDef.widgets = {
{Then:"text", at:"{{multiple}}", should_be:["A", "B"]},
{When:"select", at:"select[name=multiple]", option:"A"},
{Then:"text", at:"{{multiple}}", should_be:["B"]},
-
+
{Then:"text", at:"{{hidden}}", should_be:"hiddenValue"},
{Then:"text", at:"{{password}}", should_be:"passwordValue"},
diff --git a/scenario/widgets.html b/scenario/widgets.html
index 242fd9e6..5c3afa21 100644
--- a/scenario/widgets.html
+++ b/scenario/widgets.html
@@ -16,8 +16,7 @@
<tr>
<td>basic</td>
<td>
- <input type="text" name="text.basic" ng-required ng-validate="number" ng-format="number"/>
- <input type="text" name="text.basic" ng-format="number"/>
+ <input type="text" name="text.basic"/>
</td>
<td>text.basic={{text.basic}}</td>
</tr>
diff --git a/src/scenario/Runner.js b/src/scenario/Runner.js
index 7caddc98..eeb4330d 100644
--- a/src/scenario/Runner.js
+++ b/src/scenario/Runner.js
@@ -1,171 +1,109 @@
-var scenario = angular.scenario;
-scenario.SuiteRunner = function(scenarios, body) {
- this.scenarios = scenarios;
- this.body = body;
-};
+angular['scenario'] = (angular['scenario'] = {});
-scenario.SuiteRunner.prototype = {
- run:function(){
- this.setUpUI();
- this.runScenarios();
- },
+angular.scenario.Runner = function(scope){
+ var self = scope.$scenario = this;
+ this.scope = scope;
+ var specs = this.specs = {};
+ var path = [];
+ this.scope.describe = function describe(name, body){
+ path.push(name);
+ body();
+ path.pop();
+ };
+ this.scope.it = function it(name, body) {
+ var specName = path.join(' ') + ': it ' + name;
+ self.currentSpec = specs[specName] = {
+ name: specName,
+ steps:[]
+ };
+ body();
+ self.currentSpec = null;
+ };
+ this.beginSpec = function returnNoop(){
+ return returnNoop;
+ };
+};
- setUpUI:function(){
- this.body.html(
+angular.scenario.Runner.prototype = {
+ run: function(body){
+ body.append(
'<div id="runner">' +
- '<div class="console"></div>' +
+ '<ul class="console"></ul>' +
'</div>' +
'<div id="testView">' +
'<iframe></iframe>' +
'</div>');
- this.console = this.body.find(".console");
- this.testFrame = this.body.find("iframe");
- this.console.find(".run").live("click", function(){
- jQuery(this).parent().find('.log').toggle();
- });
- },
-
-
- runScenarios:function(){
- var runner = new scenario.Runner(this.console, this.testFrame);
- _.stepper(this.scenarios, function(next, scenarioObj, name){
- new scenario.Scenario(name, scenarioObj).run(runner, next);
- }, function(){
- }
- );
- }
-};
-
-scenario.Runner = function(console, frame){
- this.console = console;
- this.current = null;
- this.tests = [];
- this.frame = frame;
-};
-scenario.Runner.prototype = {
- start:function(name){
- var current = this.current = {
- name:name,
- start:new Date().getTime(),
- scenario:jQuery('<div class="scenario"></div>')
+ var console = body.find('#runner .console');
+ this.testFrame = body.find('#testView iframe');
+ this.testWindow = this.testFrame[0].contentWindow;
+ this.beginSpec = function(name){
+ var specElement = jQuery('<li class="spec"></li>');
+ var stepContainer = jQuery('<ul class="step"></ul>');
+ console.append(specElement);
+ specElement.text(name);
+ specElement.append(stepContainer);
+ return function(name){
+ var stepElement = jQuery('<li class="step"></li>');
+ var logContainer = jQuery('<ul class="log"></ul>');
+ stepContainer.append(stepElement);
+ stepElement.text(name);
+ stepElement.append(logContainer);
+ return function(message) {
+ var logElement = jQuery('<li class="log"></li>');
+ logContainer.append(logElement);
+ logElement.text(message);
+ };
+ };
};
- current.run = current.scenario.append(
- '<div class="run">' +
- '<span class="name">.</span>' +
- '<span class="time">.</span>' +
- '<span class="state">.</span>' +
- '</run>').find(".run");
- current.log = current.scenario.append('<div class="log"></div>').find(".log");
- current.run.find(".name").text(name);
- this.tests.push(current);
- this.console.append(current.scenario);
- },
- end:function(name){
- var current = this.current;
- var run = current.run;
- this.current = null;
- current.end = new Date().getTime();
- current.time = current.end - current.start;
- run.find(".time").text(current.time);
- run.find(".state").text(current.error ? "FAIL" : "PASS");
- run.addClass(current.error ? "fail" : "pass");
- if (current.error)
- run.find(".run").append('<span div="error"></span>').text(current.error);
- current.scenario.find(".log").hide();
+ this.execute("widgets: it should verify that basic widgets work");
},
- log:function(level) {
- var buf = [];
- for ( var i = 1; i < arguments.length; i++) {
- var arg = arguments[i];
- buf.push(typeof arg == "string" ?arg:toJson(arg));
- }
- var log = jQuery('<div class="' + level + '"></div>');
- log.text(buf.join(" "));
- this.current.log.append(log);
- this.console.scrollTop(this.console[0].scrollHeight);
- if (level == "error")
- this.current.error = buf.join(" ");
- }
-};
-scenario.Scenario = function(name, scenario){
- this.name = name;
- this.scenario = scenario;
-};
-scenario.Scenario.prototype = {
- run:function(runner, callback) {
- var self = this;
- _.stepper(this.scenario, function(next, steps, name){
- if (name.charAt(0) == '$') {
- next();
- } else {
- runner.start(self.name + "::" + name);
- var allSteps = (self.scenario.$before||[]).concat(steps);
- _.stepper(allSteps, function(next, step){
- self.executeStep(runner, step, next);
- }, function(){
- runner.end();
- next();
- });
- }
- }, callback);
+ addStep: function(name, step) {
+ this.currentSpec.steps.push({name:name, fn:step});
},
-
- verb:function(step){
- var fn = null;
- if (!step) fn = function (){ throw "Step is null!"; };
- else if (step.Given) fn = scenario.GIVEN[step.Given];
- else if (step.When) fn = scenario.WHEN[step.When];
- else if (step.Then) fn = scenario.THEN[step.Then];
- return fn || function (){
- throw "ERROR: Need Given/When/Then got: " + toJson(step);
+ execute: function(name, callback) {
+ var spec = this.specs[name],
+ result = {
+ passed: false,
+ failed: false,
+ finished: false,
+ fail: function(error) {
+ result.passed = false;
+ result.failed = true;
+ result.error = error;
+ result.log(angular.isString(error) ? error : angular.toJson(error));
+ }
+ };
+ specThis = {
+ result: result,
+ testWindow: this.testWindow,
+ testFrame: this.testFrame
};
- },
-
-
- context: function(runner) {
- var frame = runner.frame;
- var window = frame[0].contentWindow;
- var document;
- if (window.jQuery)
- document = window.jQuery(window.document);
- var context = {
- frame:frame,
- window:window,
- log:_.bind(runner.log, runner, "info"),
- document:document,
- assert:function(element, path){
- if (element.size() != 1) {
- throw "Expected to find '1' found '"+
- element.size()+"' for '"+path+"'.";
- }
- return element;
- },
- element:function(path){
- var exp = path.replace("{{","[ng-bind=").replace("}}", "]");
- var element = document.find(exp);
- return context.assert(element, path);
- }
- };
- return context;
- },
-
-
- executeStep:function(runner, step, callback) {
- if (!step) {
- callback();
- return;
- }
- runner.log("info", toJson(step));
- var fn = this.verb(step);
- var context = this.context(runner);
- _.extend(context, step);
- try {
- (fn.call(context)||function(c){c();})(callback);
- } catch (e) {
- runner.log("error", "ERROR: " + toJson(e));
- }
+ var beginStep = this.beginSpec(name);
+ spec.nextStepIndex = 0;
+ function done() {
+ result.finished = true;
+ (callback||angular.noop).call(specThis);
+ }
+ function next(){
+ var step = spec.steps[spec.nextStepIndex];
+ if (step) {
+ spec.nextStepIndex ++;
+ result.log = beginStep(step.name);
+ try {
+ step.fn.call(specThis, next);
+ } catch (e) {
+ result.fail(e);
+ done();
+ }
+ } else {
+ result.passed = !result.failed;
+ done();
+ }
+ };
+ next();
+ return specThis;
}
-};
+}; \ No newline at end of file
diff --git a/src/scenario/Steps.js b/src/scenario/Steps.js
deleted file mode 100644
index ffe75933..00000000
--- a/src/scenario/Steps.js
+++ /dev/null
@@ -1,57 +0,0 @@
-angular.scenario.GIVEN = {
- browser:function(){
- var self = this;
- if (jQuery.browser.safari && this.frame.attr('src') == this.at) {
- this.window.location.reload();
- } else {
- this.frame.attr('src', this.at);
- }
- return function(done){
- self.frame.load(function(){
- self.frame.unbind();
- done();
- });
- };
- },
- dataset:function(){
- this.frame.name="$DATASET:" + toJson({dataset:this.dataset});
- }
-};
-angular.scenario.WHEN = {
- enter:function(){
- var element = this.element(this.at);
- element.attr('value', this.text);
- element.change();
- },
- click:function(){
- var element = this.element(this.at);
- var input = element[0];
- // emulate the browser behavior which causes it
- // to be overridden at the end.
- var checked = input.checked = !input.checked;
- element.trigger('click');
- input.checked = checked;
- },
- select:function(){
- var element = this.element(this.at);
- var path = "option[value=" + this.option + "]";
- var option = this.assert(element.find(path));
- option[0].selected = !option[0].selected;
- element.change();
- }
-};
-angular.scenario.THEN = {
- text:function(){
- var element = this.element(this.at);
- if (typeof this.should_be != undefined ) {
- var should_be = this.should_be;
- if (_.isArray(this.should_be))
- should_be = JSON.stringify(should_be);
- if (element.text() != should_be)
- throw "Expected " + should_be +
- " but was " + element.text() + ".";
- }
- },
- drainRequestQueue:function(){
- }
-};
diff --git a/src/scenario/_namespace.js b/src/scenario/_namespace.js
deleted file mode 100644
index 7da3a5d8..00000000
--- a/src/scenario/_namespace.js
+++ /dev/null
@@ -1,6 +0,0 @@
-if (!angular) var angular = window['angular'] = {};
-if (!angular['scenario']) var angularScenario = angular['scenario'] = {};
-if (!angular['scenarioDef']) var scenarioDef = angular['scenarioDef'] = {};
-if (!angular['scenario']['GIVEN']) angularScenario['GIVEN'] = {};
-if (!angular['scenario']['WHEN']) angularScenario['WHEN'] = {};
-if (!angular['scenario']['THEN']) angularScenario['THEN'] = {};
diff --git a/src/scenario/bootstrap.js b/src/scenario/bootstrap.js
index 169f1860..81272bdd 100644
--- a/src/scenario/bootstrap.js
+++ b/src/scenario/bootstrap.js
@@ -19,22 +19,8 @@
}
window.onload = function(){
- if (!_.stepper) {
- _.stepper = function(collection, iterator, done){
- var keys = _.keys(collection);
- function next() {
- if (keys.length) {
- var key = keys.shift();
- iterator(next, collection[key], key);
- } else {
- (done||_.identity)();
- }
- }
- next();
- };
- }
_.defer(function(){
- new angular.scenario.SuiteRunner(angular.scenarioDef, jQuery(document.body)).run();
+ $scenarioRunner.run(jQuery(document.body));
});
(onLoadDelegate||function(){})();
};
@@ -42,8 +28,7 @@
addScript("../../lib/underscore/underscore.js");
addScript("../../lib/jquery/jquery-1.4.2.js");
addScript("../angular-bootstrap.js");
- addScript("_namespace.js");
- addScript("Steps.js");
addScript("Runner.js");
+ document.write('<script type="text/javascript">$scenarioRunner = new angular.scenario.Runner(window);</script>');
})(window.onload);
diff --git a/test/scenario/RunnerSpec.js b/test/scenario/RunnerSpec.js
new file mode 100644
index 00000000..bd7c0599
--- /dev/null
+++ b/test/scenario/RunnerSpec.js
@@ -0,0 +1,105 @@
+describe('Runner', function(){
+ var scenario, runner, log, Describe, It, $scenario;
+
+ function logger(text) {
+ return function(){log += text;};
+ }
+
+ beforeEach(function(){
+ log = '';
+ scenario = {};
+ runner = new angular.scenario.Runner(scenario);
+ Describe = scenario.describe;
+ It = scenario.it;
+ $scenario = scenario.$scenario;
+ });
+
+ describe('describe', function(){
+ it('should consume the describe functions', function(){
+ Describe('describe name', logger('body'));
+
+ expect(log).toEqual('body');
+ });
+
+ describe('it', function(){
+ it('should consume it', function(){
+ Describe('describe name', function(){
+ It('should text', logger('body'));
+ });
+ expect(log).toEqual('body');
+ var spec = $scenario.specs['describe name: it should text'];
+ expect(spec.steps).toEqual([]);
+ expect(spec.name).toEqual('describe name: it should text');
+ });
+ });
+ });
+
+ describe('steps building', function(){
+ it('should queue steps', function(){
+ function step(){};
+ Describe('name', function(){
+ It('should', function(){
+ $scenario.addStep('stepname', step);
+ });
+ });
+ expect($scenario.specs['name: it should'].steps).toEqual([{name:'stepname', fn:step}]);
+ });
+ });
+
+ describe('execution', function(){
+ it('should execute the queued steps', function(){
+ var next, firstThis, secondThis, doneThis, spec;
+ $scenario.specs['spec'] = {
+ steps: [
+ {name:'step1', fn: function(done) {
+ next = done;
+ log += 'first;';
+ firstThis = this;
+ }},
+ {name:'step2', fn:function(done){
+ next = done;
+ log += 'second;';
+ secondThis = this;
+ }}
+ ]
+ };
+
+ spec = $scenario.execute('spec', function(done){
+ log += 'done;';
+ doneThis = this;
+ });
+ expect(log).toEqual('first;');
+ next();
+ expect(log).toEqual('first;second;');
+ next();
+ expect(log).toEqual('first;second;done;');
+ expect(spec).not.toEqual(window);
+ expect(spec).toEqual(firstThis);
+ expect(spec).toEqual(secondThis);
+ expect(spec).toEqual(doneThis);
+
+ expect(spec.result.failed).toEqual(false);
+ expect(spec.result.finished).toEqual(true);
+ expect(spec.result.error).toBeUndefined();
+ expect(spec.result.passed).toEqual(true);
+ });
+
+ it('should handle exceptions in a step', function(){
+ $scenario.specs['spec'] = {
+ steps: [
+ {name:'error', fn:function(done) {
+ throw "MyError";
+ }}
+ ]
+ };
+
+ var spec = $scenario.execute('spec');
+
+ expect(spec.result.passed).toEqual(false);
+ expect(spec.result.failed).toEqual(true);
+ expect(spec.result.finished).toEqual(true);
+ expect(spec.result.error).toEqual("MyError");
+ });
+ });
+
+}); \ No newline at end of file
diff --git a/test/scenario/StepsTest.js b/test/scenario/StepsTest.js
deleted file mode 100644
index 32ef637d..00000000
--- a/test/scenario/StepsTest.js
+++ /dev/null
@@ -1,7 +0,0 @@
-StepsTest = TestCase("StepsTest");
-
-StepsTest.prototype.testGivenDataset=function(){
- var self = {frame:{}, dataset:[]};
- angular.scenario.GIVEN.dataset.call(self);
- assertEquals('$DATASET:{"dataset":[]}', self.frame.name);
-};