aboutsummaryrefslogtreecommitdiffstats
path: root/src/scenario
diff options
context:
space:
mode:
Diffstat (limited to 'src/scenario')
-rw-r--r--src/scenario/DSL.js63
-rw-r--r--src/scenario/Runner.js161
-rw-r--r--src/scenario/angular.prefix30
-rw-r--r--src/scenario/angular.suffix11
-rw-r--r--src/scenario/bootstrap.js44
5 files changed, 309 insertions, 0 deletions
diff --git a/src/scenario/DSL.js b/src/scenario/DSL.js
new file mode 100644
index 00000000..194a28d6
--- /dev/null
+++ b/src/scenario/DSL.js
@@ -0,0 +1,63 @@
+angular.scenario.dsl.browser = {
+ navigateTo: function(url){
+ $scenario.addStep('Navigate to: ' + url, function(done){
+ var self = this;
+ this.testFrame.load(function(){
+ self.testFrame.unbind();
+ self.testWindow = self.testFrame[0].contentWindow;
+ self.testDocument = jQuery(self.testWindow.document);
+ self.$browser = self.testWindow.angular.service.$browser();
+ self.notifyWhenNoOutstandingRequests = bind(self.$browser, self.$browser.notifyWhenNoOutstandingRequests);
+ self.notifyWhenNoOutstandingRequests(done);
+ });
+ if (this.testFrame.attr('src') == url) {
+ this.testFrame[0].contentWindow.location.reload();
+ } else {
+ this.testFrame.attr('src', url);
+ }
+ });
+ }
+};
+
+angular.scenario.dsl.input = function(selector) {
+ return {
+ enter: function(value){
+ $scenario.addStep("Set input text of '" + selector + "' to '" +
+ value + "'", function(done){
+ var input = this.testDocument.find('input[name=' + selector + ']');
+ input.val(value);
+ this.testWindow.angular.element(input[0]).trigger('change');
+ done();
+ });
+ },
+ select: function(value){
+ $scenario.addStep("Select radio '" + selector + "' to '" +
+ value + "'", function(done){
+ var input = this.testDocument.
+ find(':radio[name$=@' + selector + '][value=' + value + ']');
+ var event = this.testWindow.document.createEvent('MouseEvent');
+ event.initMouseEvent('click', true, true, this.testWindow, 0,0,0,0,0, false, false, false, false, 0, null);
+ input[0].dispatchEvent(event);
+ done();
+ });
+ }
+ };
+};
+
+angular.scenario.dsl.expect = {
+ repeater: function(selector) {
+ return {
+ count: {
+ toEqual: function(number) {
+ $scenario.addStep("Expect that there are " + number + " items in Repeater with selector '" + selector + "'", function(done) {
+ var items = this.testDocument.find(selector);
+ if (items.length != number) {
+ this.result.fail("Expected " + number + " but was " + items.length);
+ }
+ done();
+ });
+ }
+ }
+ };
+ }
+};
diff --git a/src/scenario/Runner.js b/src/scenario/Runner.js
new file mode 100644
index 00000000..8e0cc909
--- /dev/null
+++ b/src/scenario/Runner.js
@@ -0,0 +1,161 @@
+angular['scenario'] = angular['scenario'] || (angular['scenario'] = {});
+angular.scenario['dsl'] = angular.scenario['dsl'] || (angular.scenario['dsl'] = {});
+
+angular.scenario.Runner = function(scope, jQuery){
+ var self = scope.$scenario = this;
+ this.scope = scope;
+ this.jQuery = jQuery;
+
+ var specs = this.specs = {};
+ var path = [];
+ this.scope.describe = function(name, body){
+ path.push(name);
+ body();
+ path.pop();
+ };
+ var beforeEach = noop;
+ var afterEach = noop;
+ this.scope.beforeEach = function(body) {
+ beforeEach = body;
+ };
+ this.scope.afterEach = function(body) {
+ afterEach = body;
+ };
+ this.scope.it = function(name, body) {
+ var specName = path.join(' ') + ': it ' + name;
+ self.currentSpec = specs[specName] = {
+ name: specName,
+ steps:[]
+ };
+ try {
+ beforeEach();
+ body();
+ } catch(err) {
+ self.addStep(err.message || 'ERROR', function(){
+ throw err;
+ });
+ } finally {
+ afterEach();
+ }
+ self.currentSpec = null;
+ };
+ this.logger = function returnNoop(){
+ return extend(returnNoop, {close:noop, fail:noop});;
+ };
+};
+
+angular.scenario.Runner.prototype = {
+ run: function(body){
+ var jQuery = this.jQuery;
+ body.append(
+ '<div id="runner">' +
+ '<div class="console"></div>' +
+ '</div>' +
+ '<div id="testView">' +
+ '<iframe></iframe>' +
+ '</div>');
+ var console = body.find('#runner .console');
+ console.find('li').live('click', function(){
+ jQuery(this).toggleClass('collapsed');
+ });
+ this.testFrame = body.find('#testView iframe');
+ function logger(parent) {
+ var container;
+ return function(type, text) {
+ if (!container) {
+ container = jQuery('<ul></ul>');
+ parent.append(container);
+ }
+ var element = jQuery('<li class="running '+type+'"><span></span></li>');
+ element.find('span').text(text);
+ container.append(element);
+ return extend(logger(element), {
+ close: function(){
+ element.removeClass('running');
+ if(!element.hasClass('fail'))
+ element.addClass('collapsed');
+ console.scrollTop(console[0].scrollHeight);
+ },
+ fail: function(){
+ element.removeClass('running');
+ var current = element;
+ while (current[0] != console[0]) {
+ if (current.is('li'))
+ current.addClass('fail');
+ current = current.parent();
+ }
+ }
+ });
+ };
+ }
+ this.logger = logger(console);
+ var specNames = [];
+ foreach(this.specs, function(spec, name){
+ specNames.push(name);
+ }, this);
+ specNames.sort();
+ var self = this;
+ function callback(){
+ var next = specNames.shift();
+ if(next) {
+ self.execute(next, callback);
+ }
+ };
+ callback();
+ },
+
+ addStep: function(name, step) {
+ this.currentSpec.steps.push({name:name, fn:step});
+ },
+
+ execute: function(name, callback) {
+ var spec = this.specs[name],
+ self = this,
+ result = {
+ passed: false,
+ failed: false,
+ finished: false,
+ fail: function(error) {
+ result.passed = false;
+ result.failed = true;
+ result.error = error;
+ result.log('fail', isString(error) ? error : toJson(error)).fail();
+ }
+ },
+ specThis = createScope({
+ result: result,
+ testFrame: this.testFrame,
+ testWindow: this.testWindow
+ }, angularService, {});
+ this.self = specThis;
+ var stepLogger = this.logger('spec', name);
+ spec.nextStepIndex = 0;
+ function done() {
+ result.finished = true;
+ stepLogger.close();
+ self.self = null;
+ (callback||noop).call(specThis);
+ }
+ function next(){
+ var step = spec.steps[spec.nextStepIndex];
+ (result.log || {close:noop}).close();
+ result.log = null;
+ if (step) {
+ spec.nextStepIndex ++;
+ result.log = stepLogger('step', step.name);
+ try {
+ step.fn.call(specThis, next);
+ } catch (e) {
+ console.error(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/angular.prefix b/src/scenario/angular.prefix
new file mode 100644
index 00000000..5b44e17c
--- /dev/null
+++ b/src/scenario/angular.prefix
@@ -0,0 +1,30 @@
+/**
+ * The MIT License
+ *
+ * Copyright (c) 2010 Adam Abrons and Misko Hevery http://getangular.com
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+(function(window, document, previousOnLoad){
+ window.angular = {
+ scenario: {
+ dsl: window
+ }
+ };
+
diff --git a/src/scenario/angular.suffix b/src/scenario/angular.suffix
new file mode 100644
index 00000000..fc861cbf
--- /dev/null
+++ b/src/scenario/angular.suffix
@@ -0,0 +1,11 @@
+
+ var $scenarioRunner = new angular.scenario.Runner(window, jQuery);
+
+ window.onload = function(){
+ try {
+ if (previousOnLoad) previousOnLoad();
+ } catch(e) {}
+ $scenarioRunner.run(jQuery(window.document.body));
+ };
+
+})(window, document, window.onload);
diff --git a/src/scenario/bootstrap.js b/src/scenario/bootstrap.js
new file mode 100644
index 00000000..694d0e97
--- /dev/null
+++ b/src/scenario/bootstrap.js
@@ -0,0 +1,44 @@
+(function(onLoadDelegate){
+ var prefix = (function(){
+ var filename = /(.*\/)bootstrap.js(#(.*))?/;
+ var scripts = document.getElementsByTagName("script");
+ for(var j = 0; j < scripts.length; j++) {
+ var src = scripts[j].src;
+ if (src && src.match(filename)) {
+ var parts = src.match(filename);
+ return parts[1];
+ }
+ }
+ })();
+ function addScript(path) {
+ document.write('<script type="text/javascript" src="' + prefix + path + '"></script>');
+ }
+
+ function addCSS(path) {
+ document.write('<link rel="stylesheet" type="text/css" href="' + prefix + path + '"/>');
+ }
+
+ window.angular = {
+ scenario: {
+ dsl: window
+ }
+ };
+
+ window.onload = function(){
+ _.defer(function(){
+ $scenarioRunner.run(jQuery(window.document.body));
+ });
+ (onLoadDelegate||function(){})();
+ };
+ addCSS("../../css/angular-scenario.css");
+ addScript("../../lib/underscore/underscore.js");
+ addScript("../../lib/jquery/jquery-1.4.2.js");
+ addScript("Runner.js");
+ addScript("../Angular.js");
+ addScript("../JSON.js");
+ addScript("DSL.js");
+ document.write('<script type="text/javascript">' +
+ '$scenarioRunner = new angular.scenario.Runner(window, jQuery);' +
+ '</script>');
+})(window.onload);
+