From 39c6c5975bedf6e1610f7328a088acda9ab3406a Mon Sep 17 00:00:00 2001 From: Adam Abrons Date: Mon, 15 Mar 2010 17:02:54 -0700 Subject: get scenarios running again - open Runner.html in a browser to run them --- example/calculator-bootstrap.html | 3 +- scenario/Runner.html | 10 +++ scenario/datastore-scenarios.js | 19 +++++ scenario/datastore.html | 12 +++ scenario/widgets-scenarios.js | 49 +++++++++++ scenario/widgets.html | 58 +++++++++++++ src/scenario/Runner.js | 171 ++++++++++++++++++++++++++++++++++++++ src/scenario/Steps.js | 57 +++++++++++++ src/scenario/_namespace.js | 6 ++ src/scenario/bootstrap.js | 47 +++++++++++ src/test/Runner.js | 162 ------------------------------------ src/test/Steps.js | 57 ------------- src/test/_namespace.js | 5 -- test/scenario/StepsTest.js | 7 ++ test/test/StepsTest.js | 7 -- 15 files changed, 438 insertions(+), 232 deletions(-) create mode 100644 scenario/Runner.html create mode 100644 scenario/datastore-scenarios.js create mode 100644 scenario/datastore.html create mode 100644 scenario/widgets-scenarios.js create mode 100644 scenario/widgets.html create mode 100644 src/scenario/Runner.js create mode 100644 src/scenario/Steps.js create mode 100644 src/scenario/_namespace.js create mode 100644 src/scenario/bootstrap.js delete mode 100644 src/test/Runner.js delete mode 100644 src/test/Steps.js delete mode 100644 src/test/_namespace.js create mode 100644 test/scenario/StepsTest.js delete mode 100644 test/test/StepsTest.js diff --git a/example/calculator-bootstrap.html b/example/calculator-bootstrap.html index e37fe02d..c72837dc 100644 --- a/example/calculator-bootstrap.html +++ b/example/calculator-bootstrap.html @@ -10,7 +10,8 @@ scope.set('a', 3); scope.updateView(); }); - + + Quantity: * diff --git a/scenario/Runner.html b/scenario/Runner.html new file mode 100644 index 00000000..fb4b0f8f --- /dev/null +++ b/scenario/Runner.html @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/scenario/datastore-scenarios.js b/scenario/datastore-scenarios.js new file mode 100644 index 00000000..6038070b --- /dev/null +++ b/scenario/datastore-scenarios.js @@ -0,0 +1,19 @@ +angular.scenarioDef.datastore = { + $before:[ + {Given:"dataset", + dataset:{ + Book:[{$id:'moby', name:"Moby Dick"}, + {$id:'gadsby', name:'Great Gadsby'}] + } + }, + {Given:"browser", at:"datastore.html#book=moby"}, + ], + checkLoadBook:[ + {Then:"drainRequestQueue"}, + + {Then:"text", at:"{{book.$id}}", should_be:"moby"}, + {Then:"text", at:"li[$index=0] {{book.name}}", should_be:"Great Gahdsby"}, + {Then:"text", at:"li[$index=0] {{book.name}}", should_be:"Moby Dick"}, + + ] +}; diff --git a/scenario/datastore.html b/scenario/datastore.html new file mode 100644 index 00000000..355d151c --- /dev/null +++ b/scenario/datastore.html @@ -0,0 +1,12 @@ + + + + + + +

{{book.$id}}

+
  • +
  • {{book.name}}
  • + + + diff --git a/scenario/widgets-scenarios.js b/scenario/widgets-scenarios.js new file mode 100644 index 00000000..663b06da --- /dev/null +++ b/scenario/widgets-scenarios.js @@ -0,0 +1,49 @@ +angular.scenarioDef.widgets = { + $before:[ + {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:"{{gender}}", should_be:"male"}, + + {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"}, + + {Then:"text", at:"{{multiple}}", should_be:"[]"}, + {When:"select", at:"select[name=multiple]", option:"A"}, + {Then:"text", at:"{{multiple}}", should_be:["A"]}, + {When:"select", at:"select[name=multiple]", option:"B"}, + {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"}, + {When:"enter", text:"reset", at:":input[name=password]"}, + {Then:"text", at:"{{password}}", should_be:"reset"}, + ], + checkNewWidgetEmpty:[ + {Then:"text", at:"{{name}}", should_be:""}, + ] +}; diff --git a/scenario/widgets.html b/scenario/widgets.html new file mode 100644 index 00000000..cb28e78c --- /dev/null +++ b/scenario/widgets.html @@ -0,0 +1,58 @@ + + + + + + + + + +

    + name: name={{name}}
    +

    +

    + Female + Male + gender={{gender}} +

    +

    + tea={{tea}}
    + coffee={{coffee}}
    +

    +

    +

    + + + + action + count={{count}} +
    +

    +

    + + select={{select}} +

    +

    + + multiple={{multiple}} +

    +

    + + Hidden field = {{hidden}} +

    +

    + + Password field = {{password}} +

    + + diff --git a/src/scenario/Runner.js b/src/scenario/Runner.js new file mode 100644 index 00000000..7caddc98 --- /dev/null +++ b/src/scenario/Runner.js @@ -0,0 +1,171 @@ +var scenario = angular.scenario; +scenario.SuiteRunner = function(scenarios, body) { + this.scenarios = scenarios; + this.body = body; +}; + +scenario.SuiteRunner.prototype = { + run:function(){ + this.setUpUI(); + this.runScenarios(); + }, + + + setUpUI:function(){ + this.body.html( + '
    ' + + '
    ' + + '
    ' + + '
    ' + + '' + + '
    '); + 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('
    ') + }; + current.run = current.scenario.append( + '
    ' + + '.' + + '.' + + '.' + + '').find(".run"); + current.log = current.scenario.append('
    ').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('').text(current.error); + current.scenario.find(".log").hide(); + }, + 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('
    '); + 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); + }, + + + 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); + }; + }, + + + 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)); + } + } +}; diff --git a/src/scenario/Steps.js b/src/scenario/Steps.js new file mode 100644 index 00000000..f8ac173f --- /dev/null +++ b/src/scenario/Steps.js @@ -0,0 +1,57 @@ +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.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 new file mode 100644 index 00000000..7da3a5d8 --- /dev/null +++ b/src/scenario/_namespace.js @@ -0,0 +1,6 @@ +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 new file mode 100644 index 00000000..1d40b9d0 --- /dev/null +++ b/src/scenario/bootstrap.js @@ -0,0 +1,47 @@ +(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(''); + }; + function addCSS(path) { + document.write(''); + }; + 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(); + }); + (onLoadDelegate||function(){})(); + }; + addCSS("../../css/angular-scenario.css"); + addScript("../../lib/underscore/underscore.js"); + addScript("../../lib/jquery/jquery-1.3.2.js"); + addScript("../angular-bootstrap.js"); + addScript("_namespace.js"); + addScript("Steps.js"); + addScript("Runner.js"); +})(window.onload); + diff --git a/src/test/Runner.js b/src/test/Runner.js deleted file mode 100644 index c6684951..00000000 --- a/src/test/Runner.js +++ /dev/null @@ -1,162 +0,0 @@ -if (typeof test == 'undefined') test = {}; - -test.ScenarioRunner = function(scenarios, body) { - this.scenarios = scenarios; - this.body = body; -}; - -test.ScenarioRunner.prototype = { - run:function(){ - this.setUpUI(); - this.runScenarios(); - }, - setUpUI:function(){ - this.body.html( - '
    ' + - '
    ' + - '
    ' + - '
    ' + - '' + - '
    '); - 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 test.Runner(this.console, this.testFrame); - _.stepper(this.scenarios, function(next, scenario, name){ - new test.Scenario(name, scenario).run(runner, next); - }, function(){ - } - ); - } -}; - -test.Runner = function(console, frame){ - this.console = console; - this.current = null; - this.tests = []; - this.frame = frame; -}; -test.Runner.prototype = { - start:function(name){ - var current = this.current = { - name:name, - start:new Date().getTime(), - scenario:jQuery('
    ') - }; - current.run = current.scenario.append( - '
    ' + - '.' + - '.' + - '.' + - '').find(".run"); - current.log = current.scenario.append('
    ').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('').text(current.error); - current.scenario.find(".log").hide(); - }, - 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('
    '); - log.text(buf.join(" ")); - this.current.log.append(log); - this.console.scrollTop(this.console[0].scrollHeight); - if (level == "error") - this.current.error = buf.join(" "); - } -}; - -test.Scenario = function(name, scenario){ - this.name = name; - this.scenario = scenario; -}; -test.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); - }, - verb:function(step){ - var fn = null; - if (!step) fn = function (){ throw "Step is null!"; }; - else if (step.Given) fn = angular.test.GIVEN[step.Given]; - else if (step.When) fn = angular.test.WHEN[step.When]; - else if (step.Then) fn = angular.test.THEN[step.Then]; - return fn || function (){ - throw "ERROR: Need Given/When/Then got: " + toJson(step); - }; - }, - 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)); - } - } -}; diff --git a/src/test/Steps.js b/src/test/Steps.js deleted file mode 100644 index cc9ff549..00000000 --- a/src/test/Steps.js +++ /dev/null @@ -1,57 +0,0 @@ -angular.test.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.test.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.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.test.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/test/_namespace.js b/src/test/_namespace.js deleted file mode 100644 index e29ae72a..00000000 --- a/src/test/_namespace.js +++ /dev/null @@ -1,5 +0,0 @@ -if (!angular) var angular = window['angular'] = {}; -if (!angular['test']) var angularTest = angular['test'] = {}; -if (!angular['test']['GIVEN']) angularTest['GIVEN'] = {}; -if (!angular['test']['WHEN']) angularTest['WHEN'] = {}; -if (!angular['test']['THEN']) angularTest['THEN'] = {}; diff --git a/test/scenario/StepsTest.js b/test/scenario/StepsTest.js new file mode 100644 index 00000000..9d64d0a9 --- /dev/null +++ b/test/scenario/StepsTest.js @@ -0,0 +1,7 @@ +StepsTest = TestCase("StepsTest"); + +StepsTest.prototype.testGivenDataset=function(){ + var self = {frame:{}, dataset:[]}; + angular.test.GIVEN.dataset.call(self); + assertEquals('$DATASET:{"dataset":[]}', self.frame.name); +}; diff --git a/test/test/StepsTest.js b/test/test/StepsTest.js deleted file mode 100644 index 9d64d0a9..00000000 --- a/test/test/StepsTest.js +++ /dev/null @@ -1,7 +0,0 @@ -StepsTest = TestCase("StepsTest"); - -StepsTest.prototype.testGivenDataset=function(){ - var self = {frame:{}, dataset:[]}; - angular.test.GIVEN.dataset.call(self); - assertEquals('$DATASET:{"dataset":[]}', self.frame.name); -}; -- cgit v1.2.3