diff options
| -rw-r--r-- | src/scenario/DSL.js | 55 | ||||
| -rw-r--r-- | src/scenario/Future.js | 13 | ||||
| -rw-r--r-- | src/scenario/Matcher.js | 21 | ||||
| -rw-r--r-- | src/scenario/Runner.js | 43 | ||||
| -rw-r--r-- | test/scenario/DSLSpec.js | 46 | ||||
| -rw-r--r-- | test/scenario/RunnerSpec.js | 161 | ||||
| -rw-r--r-- | test/scenario/TestContext.js | 15 |
7 files changed, 199 insertions, 155 deletions
diff --git a/src/scenario/DSL.js b/src/scenario/DSL.js index b713cfd6..ef2f5553 100644 --- a/src/scenario/DSL.js +++ b/src/scenario/DSL.js @@ -1,13 +1,14 @@ angular.scenario.dsl.browser = { navigateTo: function(url){ - $scenario.addStep('Navigate to: ' + url, function(done){ + $scenario.addFuture('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 = + bind(self.$browser, self.$browser.notifyWhenNoOutstandingRequests); self.notifyWhenNoOutstandingRequests(done); }); if (this.testFrame.attr('src') == url) { @@ -19,20 +20,23 @@ angular.scenario.dsl.browser = { } }; +function future(name, behavior) { + return new Future(name, behavior); +}; + angular.scenario.dsl.input = function(selector) { + var namePrefix = "input '" + 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(); + enter: function(value) { + return future(namePrefix + " enter '" + 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){ + select: function(value) { + return future(namePrefix + " select '" + value + "'", function(done) { var input = this.testDocument. find(':radio[name$=@' + selector + '][value=' + value + ']'); jqLiteWrap(input[0]).trigger('click'); @@ -41,22 +45,15 @@ angular.scenario.dsl.input = function(selector) { }); } }; -}; +}, -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(); - }); - } - } - }; - } +angular.scenario.dsl.repeater = function(selector) { + var namePrefix = "repeater '" + selector + "'"; + return { + count: function() { + return future(namePrefix + ' count', function(done) { + done(this.testDocument.find(selector).size()); + }); + } + }; }; diff --git a/src/scenario/Future.js b/src/scenario/Future.js new file mode 100644 index 00000000..d70e8e6e --- /dev/null +++ b/src/scenario/Future.js @@ -0,0 +1,13 @@ +function Future(name, behavior) { + this.name = name; + this.behavior = behavior; + this.fulfilled = false; + this.value = undefined; +} + +Future.prototype = { + fulfill: function(value) { + this.fulfilled = true; + this.value = value; + } +}; diff --git a/src/scenario/Matcher.js b/src/scenario/Matcher.js new file mode 100644 index 00000000..b9787050 --- /dev/null +++ b/src/scenario/Matcher.js @@ -0,0 +1,21 @@ +function Matcher(scope, future, logger) { + var self = scope.$scenario = this; + this.logger = logger; + this.future = future; +} + +Matcher.addMatcher = function(name, matcher) { + Matcher.prototype[name] = function(expected) { + var future = this.future; + $scenario.addFuture( + 'expect ' + future.name + ' ' + name + ' ' + expected, + function(done){ + if (matcher(future.value, expected)) + throw "Expected " + expected + ' but was ' + future.value; + done(); + } + ); + }; +}; + +Matcher.addMatcher('toEqual', function(a,b) { return a == b; }); diff --git a/src/scenario/Runner.js b/src/scenario/Runner.js index 13ba5af0..13dfbe7d 100644 --- a/src/scenario/Runner.js +++ b/src/scenario/Runner.js @@ -8,6 +8,7 @@ angular.scenario.Runner = function(scope, jQuery){ this.scope.$testrun = {done: false, results: []}; var specs = this.specs = {}; + this.currentSpec = {name: '', futures: []}; var path = []; this.scope.describe = function(name, body){ path.push(name); @@ -22,17 +23,20 @@ angular.scenario.Runner = function(scope, jQuery){ this.scope.afterEach = function(body) { afterEach = body; }; + this.scope.expect = function(future) { + return new Matcher(self, future, self.logger); + }; this.scope.it = function(name, body) { var specName = path.join(' ') + ': it ' + name; self.currentSpec = specs[specName] = { name: specName, - steps:[] + futures: [] }; try { beforeEach(); body(); } catch(err) { - self.addStep(err.message || 'ERROR', function(){ + self.addFuture(err.message || 'ERROR', function(){ throw err; }); } finally { @@ -107,14 +111,16 @@ angular.scenario.Runner.prototype = { callback(); }, - addStep: function(name, step) { - this.currentSpec.steps.push({name:name, fn:step}); + addFuture: function(name, behavior) { + var future = new Future(name, behavior); + this.currentSpec.futures.push(future); + return future; }, execute: function(name, callback) { var spec = this.specs[name], self = this, - stepsDone = [], + futuresFulfilled = [], result = { passed: false, failed: false, @@ -132,29 +138,32 @@ angular.scenario.Runner.prototype = { testWindow: this.testWindow }, angularService, {}); this.self = specThis; - var stepLogger = this.logger('spec', name); - spec.nextStepIndex = 0; + var futureLogger = this.logger('spec', name); + spec.nextFutureIndex = 0; function done() { result.finished = true; - stepLogger.close(); + futureLogger.close(); self.self = null; (callback||noop).call(specThis); } - function next(){ - var step = spec.steps[spec.nextStepIndex]; + function next(value){ + if (spec.nextFutureIndex > 0) { + spec.futures[spec.nextFutureIndex - 1].fulfill(value); + } + var future = spec.futures[spec.nextFutureIndex]; (result.log || {close:noop}).close(); result.log = null; - if (step) { - spec.nextStepIndex ++; - result.log = stepLogger('step', step.name); - stepsDone.push(step.name); + if (future) { + spec.nextFutureIndex ++; + result.log = futureLogger('future', future.name); + futuresFulfilled.push(future.name); try { - step.fn.call(specThis, next); + future.behavior.call(specThis, next); } catch (e) { console.error(e); result.fail(e); self.scope.$testrun.results.push( - {name: name, passed: false, error: e, steps: stepsDone}); + {name: name, passed: false, error: e, steps: futuresFulfilled}); done(); } } else { @@ -163,7 +172,7 @@ angular.scenario.Runner.prototype = { name: name, passed: !result.failed, error: result.error, - steps: stepsDone}); + steps: futuresFulfilled}); done(); } }; diff --git a/test/scenario/DSLSpec.js b/test/scenario/DSLSpec.js index 5aac9752..533d34ac 100644 --- a/test/scenario/DSLSpec.js +++ b/test/scenario/DSLSpec.js @@ -1,39 +1,36 @@ describe("DSL", function() { - var lastStep, executeStep, lastDocument; + var lastDocument, executeFuture, Expect; beforeEach(function() { - lastStep = null; - $scenario = { - addStep: function(name, stepFunction) { - lastStep = { name:name, fn: stepFunction}; - } - }; - executeStep = function(step, html, callback) { + setUpContext(); + executeFuture = function(future, html, callback) { lastDocument =_jQuery('<div>' + html + '</div>'); _jQuery(document.body).append(lastDocument); var specThis = { testWindow: window, testDocument: lastDocument }; - step.fn.call(specThis, callback || noop); + future.behavior.call(specThis, callback || noop); }; + Expect = scenario.expect; }); describe("input", function() { var input = angular.scenario.dsl.input; + it('should enter', function() { - input('name').enter('John'); - expect(lastStep.name).toEqual("Set input text of 'name' to 'John'"); - executeStep(lastStep, '<input type="text" name="name" />'); + var future = input('name').enter('John'); + expect(future.name).toEqual("input 'name' enter 'John'"); + executeFuture(future, '<input type="text" name="name" />'); expect(lastDocument.find('input').val()).toEqual('John'); }); it('should select', function() { - input('gender').select('female'); - expect(lastStep.name).toEqual("Select radio 'gender' to 'female'"); - executeStep(lastStep, + var future = input('gender').select('female'); + expect(future.name).toEqual("input 'gender' select 'female'"); + executeFuture(future, '<input type="radio" name="0@gender" value="male" checked/>' + '<input type="radio" name="0@gender" value="female"/>'); expect(lastDocument.find(':radio:checked').length).toEqual(1); @@ -41,15 +38,16 @@ describe("DSL", function() { }); }); - describe('expect', function() { - var dslExpect = angular.scenario.dsl.expect; - describe('repeater', function() { - it('should check the count of repeated elements', function() { - dslExpect.repeater('.repeater-row').count.toEqual(2); - expect(lastStep.name).toEqual("Expect that there are 2 items in Repeater with selector '.repeater-row'"); - var html = "<div class='repeater-row'>a</div><div class='repeater-row'>b</div>"; - executeStep(lastStep, html); - }); + describe('repeater', function() { + + var repeater = angular.scenario.dsl.repeater; + + it('should fetch the count of repeated elements', function() { + var future = repeater('.repeater-row').count(); + expect(future.name).toEqual("repeater '.repeater-row' count"); + executeFuture(future, "<div class='repeater-row'>a</div>" + + "<div class='repeater-row'>b</div>"); + Expect(future).toEqual(2); }); }); }); diff --git a/test/scenario/RunnerSpec.js b/test/scenario/RunnerSpec.js index bbdd9e8c..2eb13f7f 100644 --- a/test/scenario/RunnerSpec.js +++ b/test/scenario/RunnerSpec.js @@ -1,40 +1,30 @@ -describe('Runner', function(){ - var scenario, runner, log, Describe, It, $scenario, body; - - function logger(text) { - return function(done){ - log += text; - (done||noop)(); - }; - } - - beforeEach(function(){ - log = ''; - scenario = {}; - body = _jQuery('<div></div>'); - runner = new angular.scenario.Runner(scenario, _jQuery); +describe('Runner', function() { + + var Describe, It, BeforeEach, AfterEach, body; + + beforeEach(function() { + setUpContext(); Describe = scenario.describe; + It = scenario.it; BeforeEach = scenario.beforeEach; AfterEach = scenario.afterEach; - It = scenario.it; - $scenario = scenario.$scenario; + body = _jQuery('<div></div>'); }); - describe('describe', function(){ - it('should consume the describe functions', function(){ + 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(){ + 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.futures).toEqual([]); expect(spec.name).toEqual('describe name: it should text'); }); @@ -42,18 +32,18 @@ describe('Runner', function(){ // WRITE ME!!!! }); - it('should create a failing step if there is a javascript error', function(){ + it('should create a failing future if there is a javascript error', function() { var spec; - Describe('D1', function(){ - It('I1', function(){ + Describe('D1', function() { + It('I1', function() { spec = $scenario.currentSpec; throw {message: 'blah'}; }); }); - var step = spec.steps[0]; - expect(step.name).toEqual('blah'); + var future = spec.futures[0]; + expect(future.name).toEqual('blah'); try { - step.fn(); + future.behavior(); fail(); } catch (e) { expect(e.message).toEqual('blah'); @@ -63,7 +53,7 @@ describe('Runner', function(){ describe('beforeEach', function() { it('should execute beforeEach before every it', function() { - Describe('describe name', function(){ + Describe('describe name', function() { BeforeEach(logger('before;')); It('should text', logger('body;')); It('should text2', logger('body2;')); @@ -73,7 +63,7 @@ describe('Runner', function(){ }); describe('afterEach', function() { it('should execute afterEach after every it', function() { - Describe('describe name', function(){ + Describe('describe name', function() { AfterEach(logger('after;')); It('should text1', logger('body1;')); It('should text2', logger('body2;')); @@ -82,7 +72,7 @@ describe('Runner', function(){ }); it('should always execute afterEach after every it', function() { - Describe('describe name', function(){ + Describe('describe name', function() { AfterEach(logger('after;')); It('should text', function() { logger('body1;')(); @@ -95,62 +85,63 @@ describe('Runner', function(){ it('should report an error if afterEach fails', function() { var next; - Describe('describe name', function(){ + Describe('describe name', function() { AfterEach(function() { - $scenario.addStep('afterEachLog', logger('after;')); - $scenario.addStep('afterEachThrow', function() { + $scenario.addFuture('afterEachLog', logger('after;')); + $scenario.addFuture('afterEachThrow', function() { throw "AfterError"; }); }); It('should text1', function() { - $scenario.addStep('step1', logger('step1;')); + $scenario.addFuture('future1', logger('future1;')); }); It('should text2', function() { - $scenario.addStep('step2', logger('step2;')); + $scenario.addFuture('future2', logger('future2;')); }); }); $scenario.run(body); - expect(log).toEqual('step1;after;step2;after;'); + expect(log).toEqual('future1;after;future2;after;'); expect(scenario.$testrun.results).toEqual([ { name : 'describe name: it should text1', passed : false, error : 'AfterError', - steps : [ 'step1', 'afterEachLog', 'afterEachThrow' ] }, + steps : [ 'future1', 'afterEachLog', 'afterEachThrow' ] }, { name : 'describe name: it should text2', passed : false, error : 'AfterError', - steps : [ 'step2', 'afterEachLog', 'afterEachThrow' ] }]); + steps : [ 'future2', 'afterEachLog', 'afterEachThrow' ] }]); }); }); }); - describe('steps building', function(){ - it('should queue steps', function(){ - function step(){}; - Describe('name', function(){ - It('should', function(){ - $scenario.addStep('stepname', step); + describe('future building', function() { + it('should queue futures', function() { + function behavior(){}; + Describe('name', function() { + It('should', function() { + $scenario.addFuture('futureName', behavior); }); }); - expect($scenario.specs['name: it should'].steps).toEqual([{name:'stepname', fn:step}]); + expect($scenario.specs['name: it should'].futures[0].name). + toEqual('futureName'); }); }); - describe('execution', function(){ - it('should execute the queued steps', function(){ + describe('execution', function() { + it('should execute the queued futures', 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; - }} + futures: [ + new Future('future1', function(done) { + next = done; + log += 'first;'; + firstThis = this; + }), + new Future('future2', function(done) { + next = done; + log += 'second;'; + secondThis = this; + }) ] }; @@ -174,18 +165,18 @@ describe('Runner', function(){ expect(spec.result.passed).toEqual(true); }); - it('should handle exceptions in a step', function(){ + it('should handle exceptions in a future', function() { $scenario.specs['spec'] = { - steps: [ - {name: 'first step', fn: function(done) { + futures: [ + new Future('first future', function(done) { done(); - }}, - {name:'error', fn:function(done) { + }), + new Future('error', function(done) { throw "MyError"; - }}, - {name: 'should not execute', fn: function(done) { + }), + new Future('should not execute', function(done) { done(); - }} + }) ] }; @@ -199,26 +190,26 @@ describe('Runner', function(){ name: 'spec', passed: false, error: 'MyError', - steps: ['first step', 'error']}]); + steps: ['first future', 'error']}]); }); }); - describe('run', function(){ + describe('run', function() { var next; beforeEach(function() { - Describe('d1', function(){ - It('it1', function(){ $scenario.addStep('s1', logger('s1,')); }); - It('it2', function(){ - $scenario.addStep('s2', logger('s2,')); - $scenario.addStep('s2.2', function(done){ next = done; }); + Describe('d1', function() { + It('it1', function() { $scenario.addFuture('s1', logger('s1,')); }); + It('it2', function() { + $scenario.addFuture('s2', logger('s2,')); + $scenario.addFuture('s2.2', function(done){ next = done; }); }); }); - Describe('d2', function(){ - It('it3', function(){ $scenario.addStep('s3', logger('s3,')); }); - It('it4', function(){ $scenario.addStep('s4', logger('s4,')); }); + Describe('d2', function() { + It('it3', function() { $scenario.addFuture('s3', logger('s3,')); }); + It('it4', function() { $scenario.addFuture('s4', logger('s4,')); }); }); }); - it('should execute all specs', function(){ + it('should execute all specs', function() { $scenario.run(body); expect(log).toEqual('s1,s2,'); @@ -231,15 +222,15 @@ describe('Runner', function(){ $scenario.run(body); expect(scenario.$testrun.done).toBeFalsy(); expect(scenario.$testrun.results).toEqual([ - {name: 'd1: it it1', passed: true, steps: ['s1']} + {name: 'd1: it it1', passed: true, error: undefined, steps: ['s1']} ]); next(); expect(scenario.$testrun.done).toBeTruthy(); expect(scenario.$testrun.results).toEqual([ - {name: 'd1: it it1', passed: true, steps: ['s1']}, - {name: 'd1: it it2', passed: true, steps: ['s2', 's2.2']}, - {name: 'd2: it it3', passed: true, steps: ['s3']}, - {name: 'd2: it it4', passed: true, steps: ['s4']} + {name: 'd1: it it1', passed: true, error: undefined, steps: ['s1']}, + {name: 'd1: it it2', passed: true, error: undefined, steps: ['s2', 's2.2']}, + {name: 'd2: it it3', passed: true, error: undefined, steps: ['s3']}, + {name: 'd2: it it4', passed: true, error: undefined, steps: ['s4']} ]); }); }); diff --git a/test/scenario/TestContext.js b/test/scenario/TestContext.js new file mode 100644 index 00000000..ebb40b95 --- /dev/null +++ b/test/scenario/TestContext.js @@ -0,0 +1,15 @@ +var scenario, runner, log, $scenario; + +function logger(text) { + return function(done){ + log += text; + (done||noop)(); + }; +} + +function setUpContext() { + scenario = {}; + runner = new angular.scenario.Runner(scenario, _jQuery); + $scenario = scenario.$scenario; + log = ''; +} |
