diff options
Diffstat (limited to 'src/scenario')
| -rw-r--r-- | src/scenario/DSL.js | 63 | ||||
| -rw-r--r-- | src/scenario/Runner.js | 161 | ||||
| -rw-r--r-- | src/scenario/angular.prefix | 30 | ||||
| -rw-r--r-- | src/scenario/angular.suffix | 11 | ||||
| -rw-r--r-- | src/scenario/bootstrap.js | 44 |
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); + |
